-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathaes.ts
59 lines (51 loc) · 1.58 KB
/
aes.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
const ALGORITHM = "AES-GCM";
const encoder = new TextEncoder();
const decoder = new TextDecoder();
export async function encrypt(
plaintext: string,
password: string,
): Promise<string> {
const key = await generateKey(password);
const iv = await crypto.getRandomValues(new Uint8Array(12));
const ciphertext = await crypto.subtle.encrypt(
{ name: ALGORITHM, iv: iv },
key,
encoder.encode(plaintext),
);
return arrayToBase64(
Array.from(iv).concat(Array.from(new Uint8Array(ciphertext))),
);
}
export async function decrypt(
ciphertext: string,
password: string,
): Promise<string> {
const key = await generateKey(password);
const decoded = base64ToArray(ciphertext);
const iv = decoded.slice(0, 12);
const cipher = decoded.slice(12);
return decoder.decode(
await crypto.subtle.decrypt({ name: ALGORITHM, iv: iv }, key, cipher),
);
}
async function generateKey(password: string): Promise<CryptoKey> {
const keyData = await crypto.subtle.digest(
"SHA-256",
encoder.encode(password),
);
return await crypto.subtle.importKey("raw", keyData, ALGORITHM, false, [
"encrypt",
"decrypt",
]);
}
function base64ToArray(data: string) {
return Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
}
/**
* Previously this function performed `String.fromCharCode(...data)` which blew through the maximum call stack size,
* as the spread operator was building up a stack. Let's make sure we don't do that again.
* @param data
*/
function arrayToBase64(data: number[]) {
return btoa(data.reduce((acc, it) => acc + String.fromCharCode(it), ""));
}