generated from TBD54566975/tbd-project-template
/
encryption.ts
145 lines (118 loc) · 4.4 KB
/
encryption.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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import * as crypto from 'crypto';
import * as eciesjs from 'eciesjs';
import { Readable } from 'readable-stream';
// compress publicKey for message encryption
eciesjs.ECIES_CONFIG.isEphemeralKeyCompressed = true;
/**
* Utility class for performing common, non-DWN specific encryption operations.
*/
export class Encryption {
/**
* Encrypts the given plaintext stream using AES-256-CTR algorithm.
*/
public static async aes256CtrEncrypt(key: Uint8Array, initializationVector: Uint8Array, plaintextStream: Readable): Promise<Readable> {
const cipher = crypto.createCipheriv('aes-256-ctr', key, initializationVector);
const cipherStream = new Readable({
read(): void { }
});
plaintextStream.on('data', (chunk) => {
const encryptedChunk = cipher.update(chunk);
cipherStream.push(encryptedChunk);
});
plaintextStream.on('end', () => {
const finalChunk = cipher.final();
cipherStream.push(finalChunk);
cipherStream.push(null);
});
plaintextStream.on('error', (err) => {
cipherStream.emit('error', err);
});
return cipherStream;
}
/**
* Decrypts the given cipher stream using AES-256-CTR algorithm.
*/
public static async aes256CtrDecrypt(key: Uint8Array, initializationVector: Uint8Array, cipherStream: Readable): Promise<Readable> {
const decipher = crypto.createDecipheriv('aes-256-ctr', key, initializationVector);
const plaintextStream = new Readable({
read(): void { }
});
cipherStream.on('data', (chunk) => {
const decryptedChunk = decipher.update(chunk);
plaintextStream.push(decryptedChunk);
});
cipherStream.on('end', () => {
const finalChunk = decipher.final();
plaintextStream.push(finalChunk);
plaintextStream.push(null);
});
cipherStream.on('error', (err) => {
plaintextStream.emit('error', err);
});
return plaintextStream;
}
/**
* Encrypts the given plaintext using ECIES (Elliptic Curve Integrated Encryption Scheme)
* with SECP256K1 for the asymmetric calculations, HKDF as the key-derivation function,
* and AES-GCM for the symmetric encryption and MAC algorithms.
*/
public static async eciesSecp256k1Encrypt(publicKeyBytes: Uint8Array, plaintext: Uint8Array): Promise<EciesEncryptionOutput> {
// underlying library requires Buffer as input
const publicKey = Buffer.from(publicKeyBytes);
const plaintextBuffer = Buffer.from(plaintext);
const cryptogram = eciesjs.encrypt(publicKey, plaintextBuffer);
// split cryptogram returned into constituent parts
let start = 0;
let end = Encryption.isEphemeralKeyCompressed ? 33 : 65;
const ephemeralPublicKey = cryptogram.subarray(start, end);
start = end;
end += eciesjs.ECIES_CONFIG.symmetricNonceLength;
const initializationVector = cryptogram.subarray(start, end);
start = end;
end += 16; // eciesjs.consts.AEAD_TAG_LENGTH
const messageAuthenticationCode = cryptogram.subarray(start, end);
const ciphertext = cryptogram.subarray(end);
return {
ciphertext,
ephemeralPublicKey,
initializationVector,
messageAuthenticationCode
};
}
/**
* Decrypt the given plaintext using ECIES (Elliptic Curve Integrated Encryption Scheme)
* with SECP256K1 for the asymmetric calculations, HKDF as the key-derivation function,
* and AES-GCM for the symmetric encryption and MAC algorithms.
*/
public static async eciesSecp256k1Decrypt(input: EciesEncryptionInput): Promise<Uint8Array> {
// underlying library requires Buffer as input
const privateKeyBuffer = Buffer.from(input.privateKey);
const eciesEncryptionOutput = Buffer.concat([
input.ephemeralPublicKey,
input.initializationVector,
input.messageAuthenticationCode,
input.ciphertext
]);
const plaintext = eciesjs.decrypt(privateKeyBuffer, eciesEncryptionOutput);
return plaintext;
}
/**
* Expose eciesjs library configuration
*/
static get isEphemeralKeyCompressed():boolean {
return eciesjs.ECIES_CONFIG.isEphemeralKeyCompressed;
}
}
export type EciesEncryptionOutput = {
initializationVector: Uint8Array;
ephemeralPublicKey: Uint8Array;
ciphertext: Uint8Array;
messageAuthenticationCode: Uint8Array;
};
export type EciesEncryptionInput = EciesEncryptionOutput & {
privateKey: Uint8Array;
};
export enum EncryptionAlgorithm {
Aes256Ctr = 'A256CTR',
EciesSecp256k1 = 'ECIES-ES256K'
}