1
1
/**
2
2
* AES-GCM Encryption
3
3
* (c) 2017 Chris Veness - MIT Licence
4
+ * Adapted for TypeScript and buffers
4
5
* https://gist.github.com/chrisveness/43bcda93af9f646d083fad678071b90a
5
6
*/
6
- namespace AESGCMEncryption {
7
+ namespace AESGCMEncryption {
7
8
export async function encrypt ( plaintext : string , password : string ) : Promise < string > {
8
- const passwordUtf8 = new TextEncoder ( ) . encode ( password )
9
- const passwordHash = await crypto . subtle . digest ( 'SHA-256' , passwordUtf8 )
10
- const iv = crypto . getRandomValues ( new Uint8Array ( 12 ) )
11
- const algorithm = { name : 'AES-GCM' , iv : iv }
12
- const key = await crypto . subtle . importKey ( 'raw' , passwordHash , algorithm , false , [ 'encrypt' ] )
13
- const ptUint8 = new TextEncoder ( ) . encode ( plaintext )
14
- const ctBuffer = await crypto . subtle . encrypt ( algorithm , key , ptUint8 )
15
- const ctArray = Array . from ( new Uint8Array ( ctBuffer ) )
16
- const ctStr = ctArray . map ( byte => String . fromCharCode ( byte ) ) . join ( '' )
17
- const ctBase64 = btoa ( ctStr )
18
- const ivHex = Array . from ( iv ) . map ( b => ( '00' + b . toString ( 16 ) ) . slice ( - 2 ) ) . join ( '' )
19
- return ivHex + ctBase64
9
+ const sha256 = await crypto . subtle . digest ( 'SHA-256' , new TextEncoder ( ) . encode ( password ) ) ;
10
+ const algorithm = { name : 'AES-GCM' , iv : crypto . getRandomValues ( new Uint8Array ( 12 ) ) } ;
11
+ const key = await crypto . subtle . importKey ( 'raw' , sha256 , algorithm , false , [ 'encrypt' ] ) ;
12
+ const ctBuffer = await crypto . subtle . encrypt ( algorithm , key , new TextEncoder ( ) . encode ( plaintext ) ) ;
13
+ const ctBase64 = btoa ( Array . from ( new Uint8Array ( ctBuffer ) ) . map ( byte => String . fromCharCode ( byte ) ) . join ( '' ) ) ;
14
+ const ivHex = Array . from ( algorithm . iv ) . map ( b => b . toString ( 16 ) . padStart ( 2 , '0' ) ) . join ( '' ) ;
15
+ return ivHex + ctBase64 ;
20
16
}
21
17
22
18
export async function decrypt ( ciphertext : string , password : string ) : Promise < string > {
23
- const passwordUtf8 = new TextEncoder ( ) . encode ( password )
24
- const passwordHash = await crypto . subtle . digest ( 'SHA-256' , passwordUtf8 )
25
- const iv = ciphertext . slice ( 0 , 24 ) . match ( / .{ 2 } / g) ! . map ( byte => parseInt ( byte , 16 ) )
26
- const algorithm = { name : 'AES-GCM' , iv : new Uint8Array ( iv ) }
27
- const key = await crypto . subtle . importKey ( 'raw' , passwordHash , algorithm , false , [ 'decrypt' ] )
28
- const ctStr = atob ( ciphertext . slice ( 24 ) )
29
- const ctUint8 = new Uint8Array ( ctStr . match ( / [ \s \S ] / g) ! . map ( ch => ch . charCodeAt ( 0 ) ) )
30
- const plainBuffer = await crypto . subtle . decrypt ( algorithm , key , ctUint8 )
31
- const plaintext = new TextDecoder ( ) . decode ( plainBuffer )
32
- return plaintext
19
+ const sha256 = await crypto . subtle . digest ( 'SHA-256' , new TextEncoder ( ) . encode ( password ) ) ;
20
+ const algorithm = { name : 'AES-GCM' , iv : new Uint8Array ( ciphertext . slice ( 0 , 24 ) . match ( / .{ 2 } / g) ! . map ( byte => parseInt ( byte , 16 ) ) ) } ;
21
+ const key = await crypto . subtle . importKey ( 'raw' , sha256 , algorithm , false , [ 'decrypt' ] ) ;
22
+ const ctStr = atob ( ciphertext . slice ( 24 ) ) ;
23
+ const ctUint8 = new Uint8Array ( ctStr . match ( / [ \s \S ] / g) ! . map ( ch => ch . charCodeAt ( 0 ) ) ) ;
24
+ return new TextDecoder ( ) . decode ( await crypto . subtle . decrypt ( algorithm , key , ctUint8 ) ) ;
25
+ }
26
+
27
+ function concatBuffers ( left : ArrayBuffer , right : ArrayBuffer ) : ArrayBuffer {
28
+ const x = new Uint8Array ( left . byteLength + right . byteLength ) ;
29
+ x . set ( new Uint8Array ( left ) , 0 ) ;
30
+ x . set ( new Uint8Array ( right ) , left . byteLength ) ;
31
+ return x ;
32
+ }
33
+
34
+ export async function encryptBuffer ( plaintext : ArrayBuffer , password : string ) : Promise < ArrayBuffer > {
35
+ const sha256 = await crypto . subtle . digest ( 'SHA-256' , new TextEncoder ( ) . encode ( password ) ) ;
36
+ const algorithm = { name : 'AES-GCM' , iv : crypto . getRandomValues ( new Uint8Array ( 12 ) ) } ;
37
+ const key = await crypto . subtle . importKey ( 'raw' , sha256 , algorithm , false , [ 'encrypt' ] ) ;
38
+ return concatBuffers ( algorithm . iv , await crypto . subtle . encrypt ( algorithm , key , plaintext ) ) ;
39
+ }
40
+
41
+ export async function decryptBuffer ( ciphertext : ArrayBuffer , password : string ) : Promise < ArrayBuffer > {
42
+ const sha256 = await crypto . subtle . digest ( 'SHA-256' , new TextEncoder ( ) . encode ( password ) ) ;
43
+ const algorithm = { name : 'AES-GCM' , iv : new Uint8Array ( ciphertext . slice ( 0 , 12 ) ) } ;
44
+ const key = await crypto . subtle . importKey ( 'raw' , sha256 , algorithm , false , [ 'decrypt' ] ) ;
45
+ return await crypto . subtle . decrypt ( algorithm , key , ciphertext . slice ( 12 ) ) ;
33
46
}
34
47
}
0 commit comments