1
0
silverbullet/common/crypto/aes.ts

112 lines
2.4 KiB
TypeScript
Raw Normal View History

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);
}