@@ -14,137 +14,47 @@ See the License for the specific language governing permissions and
1414limitations under the License.
1515*/
1616
17- import type { BinaryLike } from "crypto" ;
18- import { getCrypto } from '../utils' ;
1917import { decodeBase64 , encodeBase64 } from './olmlib' ;
20-
21- const subtleCrypto = ( typeof window !== "undefined" && window . crypto ) ?
22- ( window . crypto . subtle || window . crypto . webkitSubtle ) : null ;
18+ import { subtleCrypto , crypto , TextEncoder } from "./crypto" ;
2319
2420// salt for HKDF, with 8 bytes of zeros
2521const zeroSalt = new Uint8Array ( 8 ) ;
2622
2723export interface IEncryptedPayload {
2824 [ key : string ] : any ; // extensible
29- iv ? : string ;
30- ciphertext ? : string ;
31- mac ? : string ;
25+ iv : string ;
26+ ciphertext : string ;
27+ mac : string ;
3228}
3329
3430/**
35- * encrypt a string in Node.js
31+ * encrypt a string
3632 *
3733 * @param {string } data the plaintext to encrypt
3834 * @param {Uint8Array } key the encryption key to use
3935 * @param {string } name the name of the secret
4036 * @param {string } ivStr the initialization vector to use
4137 */
42- async function encryptNode ( data : string , key : Uint8Array , name : string , ivStr ?: string ) : Promise < IEncryptedPayload > {
43- const crypto = getCrypto ( ) ;
44- if ( ! crypto ) {
45- throw new Error ( "No usable crypto implementation" ) ;
46- }
47-
48- let iv ;
49- if ( ivStr ) {
50- iv = decodeBase64 ( ivStr ) ;
51- } else {
52- iv = crypto . randomBytes ( 16 ) ;
53-
54- // clear bit 63 of the IV to stop us hitting the 64-bit counter boundary
55- // (which would mean we wouldn't be able to decrypt on Android). The loss
56- // of a single bit of iv is a price we have to pay.
57- iv [ 8 ] &= 0x7f ;
58- }
59-
60- const [ aesKey , hmacKey ] = deriveKeysNode ( key , name ) ;
61-
62- const cipher = crypto . createCipheriv ( "aes-256-ctr" , aesKey , iv ) ;
63- const ciphertext = Buffer . concat ( [
64- cipher . update ( data , "utf8" ) ,
65- cipher . final ( ) ,
66- ] ) ;
67-
68- const hmac = crypto . createHmac ( "sha256" , hmacKey )
69- . update ( ciphertext ) . digest ( "base64" ) ;
70-
71- return {
72- iv : encodeBase64 ( iv ) ,
73- ciphertext : ciphertext . toString ( "base64" ) ,
74- mac : hmac ,
75- } ;
76- }
77-
78- /**
79- * decrypt a string in Node.js
80- *
81- * @param {object } data the encrypted data
82- * @param {string } data.ciphertext the ciphertext in base64
83- * @param {string } data.iv the initialization vector in base64
84- * @param {string } data.mac the HMAC in base64
85- * @param {Uint8Array } key the encryption key to use
86- * @param {string } name the name of the secret
87- */
88- async function decryptNode ( data : IEncryptedPayload , key : Uint8Array , name : string ) : Promise < string > {
89- const crypto = getCrypto ( ) ;
90- if ( ! crypto ) {
91- throw new Error ( "No usable crypto implementation" ) ;
92- }
93-
94- const [ aesKey , hmacKey ] = deriveKeysNode ( key , name ) ;
95-
96- const hmac = crypto . createHmac ( "sha256" , hmacKey )
97- . update ( Buffer . from ( data . ciphertext , "base64" ) )
98- . digest ( "base64" ) . replace ( / = + $ / g, '' ) ;
99-
100- if ( hmac !== data . mac . replace ( / = + $ / g, '' ) ) {
101- throw new Error ( `Error decrypting secret ${ name } : bad MAC` ) ;
102- }
103-
104- const decipher = crypto . createDecipheriv (
105- "aes-256-ctr" , aesKey , decodeBase64 ( data . iv ) ,
106- ) ;
107- return decipher . update ( data . ciphertext , "base64" , "utf8" )
108- + decipher . final ( "utf8" ) ;
109- }
110-
111- function deriveKeysNode ( key : BinaryLike , name : string ) : [ Buffer , Buffer ] {
112- const crypto = getCrypto ( ) ;
113- const prk = crypto . createHmac ( "sha256" , zeroSalt ) . update ( key ) . digest ( ) ;
114-
115- const b = Buffer . alloc ( 1 , 1 ) ;
116- const aesKey = crypto . createHmac ( "sha256" , prk )
117- . update ( name , "utf8" ) . update ( b ) . digest ( ) ;
118- b [ 0 ] = 2 ;
119- const hmacKey = crypto . createHmac ( "sha256" , prk )
120- . update ( aesKey ) . update ( name , "utf8" ) . update ( b ) . digest ( ) ;
121-
122- return [ aesKey , hmacKey ] ;
123- }
124-
125- /**
126- * encrypt a string in Node.js
127- *
128- * @param {string } data the plaintext to encrypt
129- * @param {Uint8Array } key the encryption key to use
130- * @param {string } name the name of the secret
131- * @param {string } ivStr the initialization vector to use
132- */
133- async function encryptBrowser ( data : string , key : Uint8Array , name : string , ivStr ?: string ) : Promise < IEncryptedPayload > {
134- let iv ;
38+ export async function encryptAES (
39+ data : string ,
40+ key : Uint8Array ,
41+ name : string ,
42+ ivStr ?: string ,
43+ ) : Promise < IEncryptedPayload > {
44+ let iv : Uint8Array ;
13545 if ( ivStr ) {
13646 iv = decodeBase64 ( ivStr ) ;
13747 } else {
13848 iv = new Uint8Array ( 16 ) ;
139- window . crypto . getRandomValues ( iv ) ;
49+ crypto . getRandomValues ( iv ) ;
14050
14151 // clear bit 63 of the IV to stop us hitting the 64-bit counter boundary
14252 // (which would mean we wouldn't be able to decrypt on Android). The loss
14353 // of a single bit of iv is a price we have to pay.
14454 iv [ 8 ] &= 0x7f ;
14555 }
14656
147- const [ aesKey , hmacKey ] = await deriveKeysBrowser ( key , name ) ;
57+ const [ aesKey , hmacKey ] = await deriveKeys ( key , name ) ;
14858 const encodedData = new TextEncoder ( ) . encode ( data ) ;
14959
15060 const ciphertext = await subtleCrypto . encrypt (
@@ -171,7 +81,7 @@ async function encryptBrowser(data: string, key: Uint8Array, name: string, ivStr
17181}
17282
17383/**
174- * decrypt a string in the browser
84+ * decrypt a string
17585 *
17686 * @param {object } data the encrypted data
17787 * @param {string } data.ciphertext the ciphertext in base64
@@ -180,8 +90,8 @@ async function encryptBrowser(data: string, key: Uint8Array, name: string, ivStr
18090 * @param {Uint8Array } key the encryption key to use
18191 * @param {string } name the name of the secret
18292 */
183- async function decryptBrowser ( data : IEncryptedPayload , key : Uint8Array , name : string ) : Promise < string > {
184- const [ aesKey , hmacKey ] = await deriveKeysBrowser ( key , name ) ;
93+ export async function decryptAES ( data : IEncryptedPayload , key : Uint8Array , name : string ) : Promise < string > {
94+ const [ aesKey , hmacKey ] = await deriveKeys ( key , name ) ;
18595
18696 const ciphertext = decodeBase64 ( data . ciphertext ) ;
18797
@@ -207,7 +117,7 @@ async function decryptBrowser(data: IEncryptedPayload, key: Uint8Array, name: st
207117 return new TextDecoder ( ) . decode ( new Uint8Array ( plaintext ) ) ;
208118}
209119
210- async function deriveKeysBrowser ( key : Uint8Array , name : string ) : Promise < [ CryptoKey , CryptoKey ] > {
120+ async function deriveKeys ( key : Uint8Array , name : string ) : Promise < [ CryptoKey , CryptoKey ] > {
211121 const hkdfkey = await subtleCrypto . importKey (
212122 'raw' ,
213123 key ,
@@ -253,14 +163,6 @@ async function deriveKeysBrowser(key: Uint8Array, name: string): Promise<[Crypto
253163 return Promise . all ( [ aesProm , hmacProm ] ) ;
254164}
255165
256- export function encryptAES ( data : string , key : Uint8Array , name : string , ivStr ?: string ) : Promise < IEncryptedPayload > {
257- return subtleCrypto ? encryptBrowser ( data , key , name , ivStr ) : encryptNode ( data , key , name , ivStr ) ;
258- }
259-
260- export function decryptAES ( data : IEncryptedPayload , key : Uint8Array , name : string ) : Promise < string > {
261- return subtleCrypto ? decryptBrowser ( data , key , name ) : decryptNode ( data , key , name ) ;
262- }
263-
264166// string of zeroes, for calculating the key check
265167const ZERO_STR = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" ;
266168
0 commit comments