-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
aes-ctr-node.ts
51 lines (44 loc) · 1.66 KB
/
aes-ctr-node.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
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-256-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 cipher = crypto.createCipheriv(ALGORITHM, secret, iv);
let cipherData = cipher.update(dataBuffer);
cipherData = Buffer.concat([cipherData, cipher.final()]);
return {
data: stringFromBuffer(cipherData),
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 decipher = crypto.createDecipheriv(ALGORITHM, secret, iv);
const decrypted = decipher.update(encryptedText);
const deBuff = Buffer.concat([decrypted, decipher.final()]);
const decryptedData = Buffer.from(deBuff).toString('utf-8');
try {
return JSON.parse(decryptedData);
} catch {
throw new Error('Invalid credentials');
}
}