112 lines
2.4 KiB
TypeScript
112 lines
2.4 KiB
TypeScript
|
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);
|
||
|
}
|