11import { NativeModules } from 'react-native' ;
22import { Encryptor } from './Encryptor' ;
3- import { ENCRYPTION_LIBRARY } from './constants' ;
3+ import {
4+ ENCRYPTION_LIBRARY ,
5+ DEFAULT_DERIVATION_PARAMS ,
6+ KeyDerivationIteration ,
7+ } from './constants' ;
48
59const Aes = NativeModules . Aes ;
610const AesForked = NativeModules . AesForked ;
@@ -9,10 +13,32 @@ describe('Encryptor', () => {
913 let encryptor : Encryptor ;
1014
1115 beforeEach ( ( ) => {
12- encryptor = new Encryptor ( ) ;
16+ encryptor = new Encryptor ( { derivationParams : DEFAULT_DERIVATION_PARAMS } ) ;
17+ } ) ;
18+
19+ describe ( 'constructor' , ( ) => {
20+ it ( 'throws an error if the provided iterations do not meet the minimum required' , ( ) => {
21+ expect (
22+ ( ) =>
23+ new Encryptor ( {
24+ derivationParams : {
25+ algorithm : 'PBKDF2' ,
26+ params : {
27+ iterations : 100 ,
28+ } ,
29+ } ,
30+ } ) ,
31+ ) . toThrowError (
32+ `Invalid key derivation iterations: 100. Recommended number of iterations is ${ KeyDerivationIteration . Default } . Minimum required is ${ KeyDerivationIteration . Minimum } .` ,
33+ ) ;
34+ } ) ;
1335 } ) ;
1436
1537 describe ( 'encrypt' , ( ) => {
38+ afterEach ( ( ) => {
39+ jest . clearAllMocks ( ) ;
40+ } ) ;
41+
1642 it ( 'should encrypt an object correctly' , async ( ) => {
1743 const password = 'testPassword' ;
1844 const objectToEncrypt = { key : 'value' } ;
@@ -31,52 +57,187 @@ describe('Encryptor', () => {
3157 } ) ;
3258
3359 describe ( 'decrypt' , ( ) => {
60+ let decryptAesSpy : jest . SpyInstance ,
61+ pbkdf2AesSpy : jest . SpyInstance ,
62+ decryptAesForkedSpy : jest . SpyInstance ,
63+ pbkdf2AesForkedSpy : jest . SpyInstance ;
64+
65+ beforeEach ( ( ) => {
66+ decryptAesSpy = jest
67+ . spyOn ( Aes , 'decrypt' )
68+ . mockResolvedValue ( '{"mockData": "mockedPlainText"}' ) ;
69+ pbkdf2AesSpy = jest
70+ . spyOn ( Aes , 'pbkdf2' )
71+ . mockResolvedValue ( 'mockedAesKey' ) ;
72+ decryptAesForkedSpy = jest
73+ . spyOn ( AesForked , 'decrypt' )
74+ . mockResolvedValue ( '{"mockData": "mockedPlainText"}' ) ;
75+ pbkdf2AesForkedSpy = jest
76+ . spyOn ( AesForked , 'pbkdf2' )
77+ . mockResolvedValue ( 'mockedAesForkedKey' ) ;
78+ } ) ;
79+
80+ afterEach ( ( ) => {
81+ decryptAesSpy . mockRestore ( ) ;
82+ pbkdf2AesSpy . mockRestore ( ) ;
83+ decryptAesForkedSpy . mockRestore ( ) ;
84+ pbkdf2AesForkedSpy . mockRestore ( ) ;
85+ } ) ;
86+
3487 it . each ( [
3588 {
3689 lib : ENCRYPTION_LIBRARY . original ,
37- expectedDecryptFunction : 'decrypt' ,
38- expectedKey : 'mockedKey' ,
39- description : 'with original library' ,
90+ expectedKey : 'mockedAesKey' ,
91+ expectedPBKDF2Args : [ 'testPassword' , 'mockedSalt' , 900000 , 256 ] ,
92+ description :
93+ 'with original library and default iterations number for key generation' ,
94+ keyMetadata : DEFAULT_DERIVATION_PARAMS ,
95+ } ,
96+ {
97+ lib : ENCRYPTION_LIBRARY . original ,
98+ expectedKey : 'mockedAesKey' ,
99+ expectedPBKDF2Args : [ 'testPassword' , 'mockedSalt' , 5000 , 256 ] ,
100+ description :
101+ 'with original library and old iterations number for key generation' ,
102+ } ,
103+ {
104+ lib : 'random-lib' , // Assuming not using "original" should lead to AesForked
105+ expectedKey : 'mockedAesForkedKey' ,
106+ expectedPBKDF2Args : [ 'testPassword' , 'mockedSalt' ] ,
107+ description :
108+ 'with library different to "original" and default iterations number for key generation' ,
109+ keyMetadata : DEFAULT_DERIVATION_PARAMS ,
40110 } ,
41111 {
42112 lib : 'random-lib' , // Assuming not using "original" should lead to AesForked
43- expectedDecryptFunction : 'decrypt' ,
44- expectedKey : 'mockedKeyForked' ,
45- description : 'with library different to "original"' ,
113+ expectedKey : 'mockedAesForkedKey' ,
114+ expectedPBKDF2Args : [ 'testPassword' , 'mockedSalt' ] ,
115+ description :
116+ 'with library different to "original" and old iterations number for key generation' ,
46117 } ,
47118 ] ) (
48119 'decrypts a string correctly $description' ,
49- async ( { lib, expectedDecryptFunction , expectedKey } ) => {
120+ async ( { lib, expectedKey , expectedPBKDF2Args , keyMetadata } ) => {
50121 const password = 'testPassword' ;
51- const encryptedString = {
122+ const mockVault = {
52123 cipher : 'mockedCipher' ,
53124 iv : 'mockedIV' ,
54125 salt : 'mockedSalt' ,
55126 lib,
56127 } ;
57128
58- // Determine which AES module to spy on based on the lib value
59- const aesModuleToSpyOn =
60- lib === ENCRYPTION_LIBRARY . original ? Aes : AesForked ;
61- const decryptSpy = jest . spyOn (
62- aesModuleToSpyOn ,
63- expectedDecryptFunction ,
64- ) ;
65-
66129 const decryptedObject = await encryptor . decrypt (
67130 password ,
68- JSON . stringify ( encryptedString ) ,
131+ JSON . stringify (
132+ keyMetadata !== undefined
133+ ? { ...mockVault , keyMetadata }
134+ : mockVault ,
135+ ) ,
69136 ) ;
70137
71138 expect ( decryptedObject ) . toEqual ( expect . any ( Object ) ) ;
72- expect ( decryptSpy ) . toHaveBeenCalledWith (
73- encryptedString . cipher ,
74- expectedKey ,
75- encryptedString . iv ,
76- ) ;
77-
78- decryptSpy . mockRestore ( ) ;
139+ expect (
140+ lib === ENCRYPTION_LIBRARY . original
141+ ? decryptAesSpy
142+ : decryptAesForkedSpy ,
143+ ) . toHaveBeenCalledWith ( mockVault . cipher , expectedKey , mockVault . iv ) ;
144+ expect (
145+ lib === ENCRYPTION_LIBRARY . original
146+ ? pbkdf2AesSpy
147+ : pbkdf2AesForkedSpy ,
148+ ) . toHaveBeenCalledWith ( ...expectedPBKDF2Args ) ;
79149 } ,
80150 ) ;
81151 } ) ;
152+
153+ describe ( 'isVaultUpdated' , ( ) => {
154+ it ( 'returns true if a vault has the correct format' , ( ) => {
155+ expect (
156+ encryptor . isVaultUpdated (
157+ JSON . stringify ( {
158+ cipher : 'mockedCipher' ,
159+ iv : 'mockedIV' ,
160+ salt : 'mockedSalt' ,
161+ lib : 'original' ,
162+ keyMetadata : DEFAULT_DERIVATION_PARAMS ,
163+ } ) ,
164+ ) ,
165+ ) . toBe ( true ) ;
166+ } ) ;
167+
168+ it ( 'returns false if a vault has the incorrect format' , ( ) => {
169+ expect (
170+ encryptor . isVaultUpdated (
171+ JSON . stringify ( {
172+ cipher : 'mockedCipher' ,
173+ iv : 'mockedIV' ,
174+ salt : 'mockedSalt' ,
175+ lib : 'original' ,
176+ } ) ,
177+ ) ,
178+ ) . toBe ( false ) ;
179+ } ) ;
180+ } ) ;
181+
182+ describe ( 'updateVault' , ( ) => {
183+ let encryptSpy : jest . SpyInstance , decryptSpy : jest . SpyInstance ;
184+ const expectedKeyMetadata = DEFAULT_DERIVATION_PARAMS ;
185+
186+ beforeEach ( ( ) => {
187+ encryptSpy = jest
188+ . spyOn ( Aes , 'encrypt' )
189+ . mockResolvedValue ( ( ) => Promise . resolve ( 'mockedCipher' ) ) ;
190+ decryptSpy = jest
191+ . spyOn ( Aes , 'decrypt' )
192+ . mockResolvedValue ( '{"mockData": "mockedPlainText"}' ) ;
193+ } ) ;
194+
195+ afterEach ( ( ) => {
196+ encryptSpy . mockRestore ( ) ;
197+ decryptSpy . mockRestore ( ) ;
198+ } ) ;
199+
200+ it ( 'updates a vault correctly if keyMetadata is not present' , async ( ) => {
201+ const mockVault = {
202+ cipher : 'mockedCipher' ,
203+ iv : 'mockedIV' ,
204+ salt : 'mockedSalt' ,
205+ lib : 'original' ,
206+ } ;
207+
208+ const updatedVault = await encryptor . updateVault (
209+ JSON . stringify ( mockVault ) ,
210+ 'mockPassword' ,
211+ ) ;
212+
213+ const vault = JSON . parse ( updatedVault ) ;
214+
215+ expect ( encryptSpy ) . toBeCalledTimes ( 1 ) ;
216+ expect ( decryptSpy ) . toBeCalledTimes ( 1 ) ;
217+ expect ( vault ) . toHaveProperty ( 'keyMetadata' ) ;
218+ expect ( vault . keyMetadata ) . toStrictEqual ( expectedKeyMetadata ) ;
219+ } ) ;
220+
221+ it ( 'does not update a vault if algorithm is PBKDF2 and the number of iterations is 900000' , async ( ) => {
222+ const mockVault = {
223+ cipher : 'mockedCipher' ,
224+ iv : 'mockedIV' ,
225+ salt : 'mockedSalt' ,
226+ lib : 'original' ,
227+ keyMetadata : DEFAULT_DERIVATION_PARAMS ,
228+ } ;
229+
230+ const updatedVault = await encryptor . updateVault (
231+ JSON . stringify ( mockVault ) ,
232+ 'mockPassword' ,
233+ ) ;
234+
235+ const vault = JSON . parse ( updatedVault ) ;
236+
237+ expect ( encryptSpy ) . toBeCalledTimes ( 0 ) ;
238+ expect ( decryptSpy ) . toBeCalledTimes ( 0 ) ;
239+ expect ( vault ) . toHaveProperty ( 'keyMetadata' ) ;
240+ expect ( vault . keyMetadata ) . toStrictEqual ( expectedKeyMetadata ) ;
241+ } ) ;
242+ } ) ;
82243} ) ;
0 commit comments