Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / shared / test / keys / upgradeKeys.spec.ts
blob69728ac62c431f1c5a91d2857285da99510a7e70
1 import type { PrivateKeyReference } from '@proton/crypto';
2 import { CryptoProxy } from '@proton/crypto';
4 import type { Address as tsAddress, User as tsUser } from '../../lib/interfaces';
5 import { getDecryptedAddressKeysHelper, getDecryptedUserKeysHelper } from '../../lib/keys';
6 import { upgradeV2KeysHelper } from '../../lib/keys/upgradeKeysV2';
7 import { Modulus } from '../authentication/login.data';
8 import { getAddressKey, getUserKey } from './keyDataHelper';
10 const DEFAULT_EMAIL = 'test@test.com';
11 const DEFAULT_KEYPASSWORD = '1';
13 const getKey = async (email = DEFAULT_EMAIL, keyPassword = DEFAULT_KEYPASSWORD) => {
14     const privateKey = await CryptoProxy.generateKey({
15         userIDs: [{ name: email, email }],
16     });
18     return {
19         privateKey,
20         privateKeyArmored: await CryptoProxy.exportPrivateKey({
21             privateKey,
22             passphrase: keyPassword,
23         }),
24     };
27 describe('upgrade keys v2', () => {
28     describe('do v2 upgrade', () => {
29         it('should upgrade v2 keys', async () => {
30             const keyPassword = DEFAULT_KEYPASSWORD;
31             const [userKey1, userKey2] = await Promise.all([
32                 getUserKey('a', keyPassword, 2),
33                 getUserKey('b', keyPassword, 2),
34             ]);
35             const User = {
36                 Keys: [userKey1.Key, userKey2.Key],
37             } as tsUser;
38             const keys = await Promise.all([
39                 getAddressKey('c', userKey1.key.privateKey, 'test@test.com', 2),
40                 getAddressKey('d', userKey1.key.privateKey, 'test@test.com', 2),
41                 getAddressKey('e', userKey2.key.privateKey, 'test2@test.com', 2),
42             ]);
44             const Addresses = [
45                 {
46                     Email: 'test@test.com',
47                     Keys: [keys[0].Key, keys[1].Key],
48                 },
49                 {
50                     Email: 'test2@test.com',
51                     Keys: [keys[2].Key],
52                 },
53             ] as tsAddress[];
54             const api = jasmine.createSpy('api').and.returnValues(Promise.resolve({ Modulus }), Promise.resolve());
55             const newKeyPassword = await upgradeV2KeysHelper({
56                 user: User,
57                 addresses: Addresses,
58                 loginPassword: keyPassword,
59                 keyPassword,
60                 clearKeyPassword: keyPassword,
61                 isOnePasswordMode: true,
62                 api,
63                 preAuthKTVerify: () => async () => {},
64                 keyMigrationKTVerifier: async () => {},
65             });
66             if (!newKeyPassword) {
67                 throw new Error('Missing new password');
68             }
69             expect(api.calls.all().length).toBe(2);
70             const newKeysArgs = api.calls.all()[1].args[0];
71             const decryptedUserKeys = await getDecryptedUserKeysHelper(
72                 { ...User, Keys: newKeysArgs.data.UserKeys },
73                 newKeyPassword
74             );
75             const decryptedAddressesKeys = await getDecryptedAddressKeysHelper(
76                 newKeysArgs.data.AddressKeys,
77                 User,
78                 decryptedUserKeys,
79                 ''
80             );
81             expect(decryptedUserKeys.every((key) => key.privateKey.isPrivate())).toBe(true);
82             expect(decryptedUserKeys.length).toBe(2 as any);
83             expect(decryptedAddressesKeys.every((key) => key.privateKey.isPrivate())).toBe(true);
84             expect(decryptedAddressesKeys.length).toBe(3 as any);
85             expect(newKeysArgs).toEqual({
86                 url: 'core/v4/keys/private/upgrade',
87                 method: 'post',
88                 data: jasmine.objectContaining({
89                     KeySalt: jasmine.any(String),
90                     Auth: jasmine.any(Object),
91                     UserKeys: jasmine.any(Array),
92                     AddressKeys: jasmine.any(Array),
93                     SignedKeyLists: jasmine.any(Object),
94                 }),
95             });
96             expect(newKeyPassword).toEqual(jasmine.any(String));
97         });
98     });
100     describe('do legacy upgrade', () => {
101         it('should upgrade v2 keys', async () => {
102             const keyPassword = DEFAULT_KEYPASSWORD;
103             const [userKey1, userKey2, addressKey1, addressKey2, addressKey3] = await Promise.all([
104                 getKey(),
105                 getKey(),
106                 getKey(),
107                 getKey(),
108                 getKey(),
109             ]);
110             const User = {
111                 Keys: [
112                     {
113                         ID: 'a',
114                         PrivateKey: userKey1.privateKeyArmored,
115                         Version: 2,
116                     },
117                     {
118                         ID: 'b',
119                         PrivateKey: userKey2.privateKeyArmored,
120                         Version: 2,
121                     },
122                 ],
123             } as tsUser;
124             const Addresses = [
125                 {
126                     Email: 'test@test.com',
127                     Keys: [
128                         {
129                             ID: 'c',
130                             PrivateKey: addressKey1.privateKeyArmored,
131                             Version: 2,
132                         },
133                         {
134                             ID: 'd',
135                             PrivateKey: addressKey2.privateKeyArmored,
136                             Version: 2,
137                         },
138                     ],
139                 },
140                 {
141                     Email: 'test2@test.com',
142                     Keys: [
143                         {
144                             ID: 'e',
145                             PrivateKey: addressKey3.privateKeyArmored,
146                             Version: 2,
147                         },
148                     ],
149                 },
150             ] as tsAddress[];
151             const api = jasmine.createSpy('api').and.returnValues(Promise.resolve({ Modulus }), Promise.resolve());
152             const newKeyPassword = await upgradeV2KeysHelper({
153                 user: User,
154                 addresses: Addresses,
155                 loginPassword: keyPassword,
156                 keyPassword,
157                 clearKeyPassword: keyPassword,
158                 isOnePasswordMode: true,
159                 api,
160                 preAuthKTVerify: () => async () => {},
161                 keyMigrationKTVerifier: async () => {},
162             });
163             if (!newKeyPassword) {
164                 throw new Error('Missing new password');
165             }
166             expect(api.calls.all().length).toBe(2);
167             const newKeysArgs = api.calls.all()[1].args[0];
168             const decryptedKeys: PrivateKeyReference[] = await Promise.all(
169                 newKeysArgs.data.Keys.map(({ PrivateKey }: any) => {
170                     return CryptoProxy.importPrivateKey({ armoredKey: PrivateKey, passphrase: newKeyPassword });
171                 })
172             );
173             expect(decryptedKeys.every((key) => key.isPrivate())).toBe(true);
174             expect(decryptedKeys.length).toBe(5);
175             expect(newKeysArgs.data.Keys[0].PrivateKey);
176             expect(newKeysArgs).toEqual({
177                 url: 'core/v4/keys/private/upgrade',
178                 method: 'post',
179                 data: jasmine.objectContaining({
180                     KeySalt: jasmine.any(String),
181                     Auth: jasmine.any(Object),
182                     Keys: jasmine.any(Array),
183                 }),
184             });
185             expect(newKeyPassword).toEqual(jasmine.any(String));
186         });
188         it('should upgrade v2 keys in two password mode', async () => {
189             const keyPassword = DEFAULT_KEYPASSWORD;
190             const [userKey1, userKey2, addressKey1, addressKey2] = await Promise.all([
191                 getKey(),
192                 getKey(),
193                 getKey(),
194                 getKey(),
195             ]);
196             const User = {
197                 Keys: [
198                     {
199                         ID: 'a',
200                         PrivateKey: userKey1.privateKeyArmored,
201                         Version: 2,
202                     },
203                     {
204                         ID: 'b',
205                         PrivateKey: userKey2.privateKeyArmored,
206                         Version: 2,
207                     },
208                 ],
209             } as tsUser;
210             const Addresses = [
211                 {
212                     Email: 'test@test.com',
213                     Keys: [
214                         {
215                             ID: 'c',
216                             PrivateKey: addressKey1.privateKeyArmored,
217                             Version: 2,
218                         },
219                         {
220                             ID: 'd',
221                             PrivateKey: addressKey2.privateKeyArmored,
222                             Version: 2,
223                         },
224                     ],
225                 },
226             ] as tsAddress[];
227             const api = jasmine.createSpy('api').and.returnValues(Promise.resolve());
228             const newKeyPassword = await upgradeV2KeysHelper({
229                 user: User,
230                 addresses: Addresses,
231                 loginPassword: '123',
232                 keyPassword,
233                 clearKeyPassword: keyPassword,
234                 isOnePasswordMode: false,
235                 api,
236                 preAuthKTVerify: () => async () => {},
237                 keyMigrationKTVerifier: async () => {},
238             });
239             expect(api.calls.all().length).toBe(1);
240             if (!newKeyPassword) {
241                 throw new Error('Missing password');
242             }
243             const newKeysArgs = api.calls.all()[0].args[0];
244             const decryptedKeys: PrivateKeyReference[] = await Promise.all(
245                 newKeysArgs.data.Keys.map(({ PrivateKey }: any) => {
246                     return CryptoProxy.importPrivateKey({ armoredKey: PrivateKey, passphrase: newKeyPassword });
247                 })
248             );
249             expect(decryptedKeys.length).toBe(4);
250             expect(decryptedKeys.every((key) => key.isPrivate())).toBe(true);
251             expect(newKeysArgs.data.Keys[0].PrivateKey);
252             expect(newKeysArgs).toEqual({
253                 url: 'core/v4/keys/private/upgrade',
254                 method: 'post',
255                 data: jasmine.objectContaining({
256                     KeySalt: jasmine.any(String),
257                     Keys: jasmine.any(Array),
258                 }),
259             });
260             expect(newKeyPassword).toEqual(jasmine.any(String));
261         });
263         it('should upgrade v2 and v3 keys mixed', async () => {
264             const keyPassword = DEFAULT_KEYPASSWORD;
265             const [userKey1, userKey2, addressKey1, addressKey2] = await Promise.all([
266                 getKey(),
267                 getKey(),
268                 getKey(),
269                 getKey(),
270             ]);
271             const User = {
272                 Keys: [
273                     {
274                         ID: 'a',
275                         PrivateKey: userKey1.privateKeyArmored,
276                         Version: 3,
277                     },
278                     {
279                         ID: 'b',
280                         PrivateKey: userKey2.privateKeyArmored,
281                         Version: 2,
282                     },
283                 ],
284             } as tsUser;
285             const Addresses = [
286                 {
287                     Email: 'test@test.com',
288                     Keys: [
289                         {
290                             ID: 'c',
291                             PrivateKey: addressKey1.privateKeyArmored,
292                             Version: 3,
293                         },
294                         {
295                             ID: 'd',
296                             PrivateKey: addressKey2.privateKeyArmored,
297                             Version: 2,
298                         },
299                     ],
300                 },
301             ] as tsAddress[];
302             const api = jasmine.createSpy('api').and.returnValues(Promise.resolve());
303             const newKeyPassword = await upgradeV2KeysHelper({
304                 user: User,
305                 addresses: Addresses,
306                 loginPassword: '123',
307                 keyPassword,
308                 clearKeyPassword: keyPassword,
309                 isOnePasswordMode: false,
310                 api,
311                 preAuthKTVerify: () => async () => {},
312                 keyMigrationKTVerifier: async () => {},
313             });
314             if (!newKeyPassword) {
315                 throw new Error('Missing password');
316             }
317             expect(api.calls.all().length).toBe(1);
318             const newKeysArgs = api.calls.all()[0].args[0];
319             const decryptedKeys: PrivateKeyReference[] = await Promise.all(
320                 newKeysArgs.data.Keys.map(({ PrivateKey }: any) => {
321                     return CryptoProxy.importPrivateKey({ armoredKey: PrivateKey, passphrase: newKeyPassword });
322                 })
323             );
324             expect(decryptedKeys.length).toBe(4);
325             expect(decryptedKeys.every((key) => key.isPrivate())).toBe(true);
326             expect(newKeysArgs.data.Keys[0].PrivateKey);
327             expect(newKeysArgs).toEqual({
328                 url: 'core/v4/keys/private/upgrade',
329                 method: 'post',
330                 data: jasmine.objectContaining({
331                     KeySalt: jasmine.any(String),
332                     Keys: jasmine.any(Array),
333                 }),
334             });
335             expect(newKeyPassword).toEqual(jasmine.any(String));
336         });
337     });
339     describe('do not upgrade', () => {
340         it('should not upgrade if the v2 keys cannot be decrypted', async () => {
341             const email = 'test@test.com';
342             const keyPassword = '1';
343             const [userKey1, addressKey1, addressKey2] = await Promise.all([
344                 getKey(),
345                 getKey(),
346                 getKey('test@test.com', '123'),
347             ]);
348             const User = {
349                 Keys: [
350                     {
351                         ID: 'a',
352                         PrivateKey: userKey1.privateKeyArmored,
353                         Version: 3,
354                     },
355                 ],
356             } as tsUser;
357             const Addresses = [
358                 {
359                     Email: email,
360                     Keys: [
361                         {
362                             ID: 'b',
363                             PrivateKey: addressKey1.privateKeyArmored,
364                             Version: 3,
365                         },
366                         {
367                             ID: 'c',
368                             PrivateKey: addressKey2.privateKeyArmored,
369                             Version: 2,
370                         },
371                     ],
372                 },
373             ] as tsAddress[];
374             const api = jasmine.createSpy('api').and.returnValues(Promise.resolve());
375             const newKeyPassword = await upgradeV2KeysHelper({
376                 user: User,
377                 addresses: Addresses,
378                 loginPassword: keyPassword,
379                 keyPassword,
380                 clearKeyPassword: keyPassword,
381                 isOnePasswordMode: false,
382                 api,
383                 preAuthKTVerify: () => async () => {},
384                 keyMigrationKTVerifier: async () => {},
385             });
386             expect(api.calls.all().length).toBe(0);
387             expect(newKeyPassword).toBeUndefined();
388         });
390         it('should not upgrade if there are no v2 keys', async () => {
391             const email = 'test@test.com';
392             const keyPassword = '1';
393             const [userKey1, addressKey1, addressKey2] = await Promise.all([getKey(), getKey(), getKey()]);
394             const User = {
395                 Keys: [
396                     {
397                         ID: 'a',
398                         PrivateKey: userKey1.privateKeyArmored,
399                         Version: 3,
400                     },
401                 ],
402             } as tsUser;
403             const Addresses = [
404                 {
405                     Email: email,
406                     Keys: [
407                         {
408                             ID: 'c',
409                             PrivateKey: addressKey1.privateKeyArmored,
410                             Version: 3,
411                         },
412                         {
413                             ID: 'd',
414                             PrivateKey: addressKey2.privateKeyArmored,
415                             Version: 3,
416                         },
417                     ],
418                 },
419             ] as tsAddress[];
420             const api = jasmine.createSpy('api').and.returnValues(Promise.resolve({ Modulus }), Promise.resolve());
421             const newKeyPassword = await upgradeV2KeysHelper({
422                 user: User,
423                 addresses: Addresses,
424                 loginPassword: keyPassword,
425                 keyPassword,
426                 clearKeyPassword: keyPassword,
427                 isOnePasswordMode: true,
428                 api,
429                 preAuthKTVerify: () => async () => {},
430                 keyMigrationKTVerifier: async () => {},
431             });
432             expect(api.calls.all().length).toBe(0);
433             expect(newKeyPassword).toBeUndefined();
434         });
435     });