AES convenience functions for encryption
This commit is contained in:
parent
79a1120198
commit
839a11bfd7
33
common/crypto/aes.test.ts
Normal file
33
common/crypto/aes.test.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { assertEquals } from "../../test_deps.ts";
|
||||
import {
|
||||
decryptAES,
|
||||
decryptPath,
|
||||
deriveKeyFromPassword,
|
||||
encryptAES,
|
||||
encryptPath,
|
||||
} from "./aes.ts";
|
||||
|
||||
Deno.test("AES encryption and decryption", async () => {
|
||||
const password = "YourPassword";
|
||||
const salt = "UniquePerUserSalt";
|
||||
const message = "Hello, World!";
|
||||
|
||||
const key = await deriveKeyFromPassword(password, salt);
|
||||
const encrypted = await encryptAES(key, message);
|
||||
|
||||
const decrypted = await decryptAES(key, encrypted);
|
||||
assertEquals(decrypted, message);
|
||||
|
||||
// Test that checks if a path is encrypted the same way every time and can be unencrypted
|
||||
const path =
|
||||
"this/is/a/long/path/that/needs/to/be/encrypted because that's what we do.md";
|
||||
const encryptedPath = await encryptPath(key, path);
|
||||
const encryptedPath2 = await encryptPath(key, path);
|
||||
// Assure two runs give the same result
|
||||
assertEquals(encryptedPath, encryptedPath2);
|
||||
|
||||
// Ensure decryption works
|
||||
const decryptedPath = await decryptPath(key, encryptedPath);
|
||||
console.log(encryptedPath);
|
||||
assertEquals(decryptedPath, path);
|
||||
});
|
111
common/crypto/aes.ts
Normal file
111
common/crypto/aes.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import {
|
||||
base64Decode,
|
||||
base64Encode,
|
||||
} from "../../plugos/asset_bundle/base64.ts";
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
export async function deriveKeyFromPassword(
|
||||
password: string,
|
||||
salt: string,
|
||||
): Promise<CryptoKey> {
|
||||
const baseKey = encoder.encode(password);
|
||||
const importedKey = await window.crypto.subtle.importKey(
|
||||
"raw",
|
||||
baseKey,
|
||||
{ name: "PBKDF2" },
|
||||
false,
|
||||
["deriveKey"],
|
||||
);
|
||||
return crypto.subtle.deriveKey(
|
||||
{
|
||||
name: "PBKDF2",
|
||||
salt: encoder.encode(salt),
|
||||
iterations: 10000,
|
||||
hash: "SHA-256",
|
||||
},
|
||||
importedKey,
|
||||
{
|
||||
name: "AES-GCM",
|
||||
length: 256,
|
||||
},
|
||||
true,
|
||||
["encrypt", "decrypt"],
|
||||
);
|
||||
}
|
||||
|
||||
export async function encryptAES(
|
||||
key: CryptoKey,
|
||||
message: string,
|
||||
): Promise<ArrayBuffer> {
|
||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||
const encodedMessage = encoder.encode(message);
|
||||
const ciphertext = await window.crypto.subtle.encrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv: iv,
|
||||
},
|
||||
key,
|
||||
encodedMessage,
|
||||
);
|
||||
return appendBuffer(iv, ciphertext);
|
||||
}
|
||||
|
||||
export async function decryptAES(
|
||||
key: CryptoKey,
|
||||
data: ArrayBuffer,
|
||||
): Promise<string> {
|
||||
const iv = data.slice(0, 12);
|
||||
const ciphertext = data.slice(12);
|
||||
const decrypted = await window.crypto.subtle.decrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv: iv,
|
||||
},
|
||||
key,
|
||||
ciphertext,
|
||||
);
|
||||
return decoder.decode(decrypted);
|
||||
}
|
||||
|
||||
function appendBuffer(buffer1: ArrayBuffer, buffer2: ArrayBuffer): ArrayBuffer {
|
||||
const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
|
||||
tmp.set(new Uint8Array(buffer1), 0);
|
||||
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
|
||||
return tmp.buffer;
|
||||
}
|
||||
|
||||
// This is against security recommendations, but we need a way to always generate the same encrypted path for the same path and password
|
||||
const pathIv = new Uint8Array(12); // 12 bytes of 0
|
||||
|
||||
export async function encryptPath(
|
||||
key: CryptoKey,
|
||||
path: string,
|
||||
): Promise<string> {
|
||||
const encodedMessage = encoder.encode(path);
|
||||
const ciphertext = await crypto.subtle.encrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv: pathIv,
|
||||
},
|
||||
key,
|
||||
encodedMessage,
|
||||
);
|
||||
return base64Encode(new Uint8Array(ciphertext));
|
||||
}
|
||||
|
||||
export async function decryptPath(
|
||||
key: CryptoKey,
|
||||
data: string,
|
||||
): Promise<string> {
|
||||
const decrypted = await crypto.subtle.decrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv: pathIv,
|
||||
},
|
||||
key,
|
||||
base64Decode(data),
|
||||
);
|
||||
return decoder.decode(decrypted);
|
||||
}
|
Loading…
Reference in New Issue
Block a user