-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
aes-ctr-web.ts
59 lines (52 loc) · 1.76 KB
/
aes-ctr-web.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
import type { Keystore } from './aes-ctr';
import { bufferFromString, stringFromBuffer, keyFromPassword } from './aes-ctr';
import { randomBytes } from './randomBytes';
import { crypto } from './universal-crypto';
const ALGORITHM = 'AES-CTR';
/**
* Encrypts a data object that can be any serializable value using
* a provided password.
*
* @returns Promise<Keystore> object
*/
export async function encrypt<T>(password: string, data: T): Promise<Keystore> {
const iv = randomBytes(16);
const salt = randomBytes(32);
const secret = keyFromPassword(password, salt);
const dataBuffer = Uint8Array.from(Buffer.from(JSON.stringify(data), 'utf-8'));
const alg = {
name: ALGORITHM,
counter: iv,
length: 64,
};
const key = await crypto.subtle.importKey('raw', secret, alg, false, ['encrypt']);
const encBuffer = await crypto.subtle.encrypt(alg, key, dataBuffer);
return {
data: stringFromBuffer(encBuffer),
iv: stringFromBuffer(iv),
salt: stringFromBuffer(salt),
};
}
/**
* Given a password and a keystore object, decrypts the text and returns
* the resulting value
*/
export async function decrypt<T>(password: string, keystore: Keystore): Promise<T> {
const iv = bufferFromString(keystore.iv);
const salt = bufferFromString(keystore.salt);
const secret = keyFromPassword(password, salt);
const encryptedText = bufferFromString(keystore.data);
const alg = {
name: ALGORITHM,
counter: iv,
length: 64,
};
const key = await crypto.subtle.importKey('raw', secret, alg, false, ['decrypt']);
const ptBuffer = await crypto.subtle.decrypt(alg, key, encryptedText);
const decryptedData = new TextDecoder().decode(ptBuffer);
try {
return JSON.parse(decryptedData);
} catch {
throw new Error('Invalid credentials');
}
}