Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / shared / test / mail / encryptionPreferences.spec.ts
blob07922f2f6f0a16703802e498ca9b82be97856063
1 import type { PublicKeyReference } from '@proton/crypto';
2 import { PACKAGE_TYPE, SIGN } from '@proton/shared/lib/mail/mailSettings';
4 import type { CONTACT_MIME_TYPES } from '../../lib/constants';
5 import { MIME_TYPES, MIME_TYPES_MORE, PGP_SCHEMES, PGP_SCHEMES_MORE } from '../../lib/constants';
6 import type { MailSettings, SelfSend } from '../../lib/interfaces';
7 import { KT_VERIFICATION_STATUS } from '../../lib/interfaces';
8 import extractEncryptionPreferences, { ENCRYPTION_PREFERENCES_ERROR_TYPES } from '../../lib/mail/encryptionPreferences';
10 const fakeKey1: PublicKeyReference = {
11     getFingerprint() {
12         return 'fakeKey1';
13     },
14     getUserIDs: () => ['<user@pm.me>'],
15 } as any;
16 const pinnedFakeKey1: PublicKeyReference = {
17     getFingerprint() {
18         return 'fakeKey1';
19     },
20     getUserIDs: () => ['<user@pm.me>'],
21 } as any;
22 const fakeKey2: PublicKeyReference = {
23     getFingerprint() {
24         return 'fakeKey2';
25     },
26     getUserIDs: () => ['<user@tatoo.me>'],
27 } as any;
28 const pinnedFakeKey2: PublicKeyReference = {
29     getFingerprint() {
30         return 'fakeKey2';
31     },
32     getUserIDs: () => ['<user@tatoo.me>'],
33 } as any;
34 const fakeKey3: PublicKeyReference = {
35     getFingerprint() {
36         return 'fakeKey3';
37     },
38 } as any;
39 const pinnedFakeKey3: PublicKeyReference = {
40     getFingerprint() {
41         return 'fakeKey3';
42     },
43 } as any;
45 describe('extractEncryptionPreferences for an internal user', () => {
46     const ktVerificationResult = { status: KT_VERIFICATION_STATUS.VERIFIED_KEYS };
47     const model = {
48         emailAddress: 'user@pm.me',
49         publicKeys: { apiKeys: [], pinnedKeys: [] },
50         scheme: PGP_SCHEMES_MORE.GLOBAL_DEFAULT,
51         mimeType: MIME_TYPES_MORE.AUTOMATIC,
52         isInternalWithDisabledE2EEForMail: false,
53         trustedFingerprints: new Set([]),
54         encryptionCapableFingerprints: new Set([]),
55         obsoleteFingerprints: new Set([]),
56         compromisedFingerprints: new Set([]),
57         isPGPExternal: false,
58         isPGPInternal: true,
59         isPGPExternalWithExternallyFetchedKeys: false,
60         isPGPExternalWithoutExternallyFetchedKeys: false,
61         pgpAddressDisabled: false,
62         isContact: true,
63         isContactSignatureVerified: true,
64         contactSignatureTimestamp: new Date(0),
65         ktVerificationResult,
66     };
67     const mailSettings = {
68         Sign: SIGN.ENABLED,
69         PGPScheme: PACKAGE_TYPE.SEND_PGP_MIME,
70         DraftMIMEType: MIME_TYPES.DEFAULT,
71     } as MailSettings;
73     it('should extract the primary API key when the email address does not belong to any contact', () => {
74         const apiKeys = [fakeKey1, fakeKey2, fakeKey3];
75         const pinnedKeys = [] as PublicKeyReference[];
76         const verifyingPinnedKeys = [] as PublicKeyReference[];
77         const publicKeyModel = {
78             ...model,
79             isContact: false,
80             isContactSignatureVerified: undefined,
81             contactSignatureTimestamp: undefined,
82             publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
83             encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey3']),
84             obsoleteFingerprints: new Set(['fakeKey3']),
85         };
86         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
88         expect(result).toEqual({
89             encrypt: true,
90             sign: true,
91             mimeType: MIME_TYPES_MORE.AUTOMATIC,
92             scheme: PGP_SCHEMES.PGP_MIME,
93             isInternalWithDisabledE2EEForMail: false,
94             sendKey: fakeKey1,
95             isSendKeyPinned: false,
96             apiKeys,
97             pinnedKeys,
98             verifyingPinnedKeys,
99             isInternal: true,
100             hasApiKeys: true,
101             hasPinnedKeys: false,
102             warnings: [],
103             isContact: false,
104             isContactSignatureVerified: undefined,
105             contactSignatureTimestamp: undefined,
106             emailAddressWarnings: undefined,
107             ktVerificationResult,
108         });
109     });
111     it('should extract the primary API key when there are no pinned keys', () => {
112         const apiKeys = [fakeKey1, fakeKey2, fakeKey3];
113         const pinnedKeys = [] as PublicKeyReference[];
114         const verifyingPinnedKeys = [] as PublicKeyReference[];
115         const publicKeyModel = {
116             ...model,
117             publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
118             encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey3']),
119             obsoleteFingerprints: new Set(['fakeKey3']),
120         };
121         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
123         expect(result).toEqual({
124             encrypt: true,
125             sign: true,
126             mimeType: MIME_TYPES_MORE.AUTOMATIC,
127             scheme: PGP_SCHEMES.PGP_MIME,
128             isInternalWithDisabledE2EEForMail: false,
129             sendKey: fakeKey1,
130             isSendKeyPinned: false,
131             apiKeys,
132             pinnedKeys,
133             verifyingPinnedKeys,
134             isInternal: true,
135             hasApiKeys: true,
136             hasPinnedKeys: false,
137             warnings: [],
138             isContact: true,
139             isContactSignatureVerified: true,
140             contactSignatureTimestamp: new Date(0),
141             emailAddressWarnings: undefined,
142             ktVerificationResult,
143         });
144     });
146     it('should pick the pinned key (and not the API one)', () => {
147         const apiKeys = [fakeKey1, fakeKey2, fakeKey3];
148         const pinnedKeys = [pinnedFakeKey2, pinnedFakeKey1];
149         const verifyingPinnedKeys = [pinnedFakeKey2];
150         const publicKeyModel = {
151             ...model,
152             publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
153             trustedFingerprints: new Set(['fakeKey1', 'fakeKey2']),
154             encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey3']),
155             obsoleteFingerprints: new Set(['fakeKey3']),
156         };
157         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
159         expect(result).toEqual({
160             encrypt: true,
161             sign: true,
162             mimeType: MIME_TYPES_MORE.AUTOMATIC,
163             scheme: PGP_SCHEMES.PGP_MIME,
164             isInternalWithDisabledE2EEForMail: false,
165             sendKey: pinnedFakeKey1,
166             isSendKeyPinned: true,
167             apiKeys,
168             pinnedKeys,
169             verifyingPinnedKeys,
170             isInternal: true,
171             hasApiKeys: true,
172             hasPinnedKeys: true,
173             warnings: [],
174             isContact: true,
175             isContactSignatureVerified: true,
176             contactSignatureTimestamp: new Date(0),
177             emailAddressWarnings: undefined,
178             ktVerificationResult,
179         });
180     });
182     it('should give a warning for keyid mismatch', () => {
183         const apiKeys = [fakeKey2, fakeKey3];
184         const pinnedKeys = [pinnedFakeKey2];
185         const verifyingPinnedKeys = [] as PublicKeyReference[];
186         const publicKeyModel = {
187             ...model,
188             publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
189             trustedFingerprints: new Set(['fakeKey2']),
190             encryptionCapableFingerprints: new Set(['fakeKey2', 'fakeKey3']),
191         };
192         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
194         expect(result.warnings?.length).toEqual(1);
195     });
197     it('should give an error when the API gave emailAddress errors', () => {
198         const apiKeys = [fakeKey1, fakeKey2, fakeKey3];
199         const pinnedKeys = [pinnedFakeKey1];
200         const verifyingPinnedKeys = [pinnedFakeKey1] as PublicKeyReference[];
201         const publicKeyModel = {
202             ...model,
203             publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
204             encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey2', 'fakeKey3']),
205             obsoleteFingerprints: new Set(['fakeKey1']),
206             emailAddressErrors: ['Recipient could not be found'],
207         };
208         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
210         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.EMAIL_ADDRESS_ERROR);
211     });
213     it('should give an error when there are no pinned keys and the primary key is not valid for sending', () => {
214         const apiKeys = [fakeKey1, fakeKey2, fakeKey3];
215         const publicKeyModel = {
216             ...model,
217             publicKeys: { apiKeys, pinnedKeys: [], verifyingPinnedKeys: [] },
218             encryptionCapableFingerprints: new Set(['fakeKey2', 'fakeKey3']),
219             compromisedFingerprints: new Set(['fakeKey1']),
220         };
221         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
223         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.PRIMARY_CANNOT_SEND);
224     });
226     it('should give an error when the preferred pinned key is not valid for sending', () => {
227         const apiKeys = [fakeKey1, fakeKey2, fakeKey3];
228         const pinnedKeys = [pinnedFakeKey1];
229         const verifyingPinnedKeys = [pinnedFakeKey1];
230         const publicKeyModel = {
231             ...model,
232             publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
233             encryptionCapableFingerprints: new Set(['fakeKey2', 'fakeKey3']),
234             obsoleteFingerprints: new Set(['fakeKey1']),
235         };
236         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
238         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.PRIMARY_CANNOT_SEND);
239     });
241     it('should give an error when the preferred pinned key is not among the keys returned by the API', () => {
242         const apiKeys = [fakeKey1, fakeKey2];
243         const pinnedKeys = [pinnedFakeKey3];
244         const verifyingPinnedKeys = [pinnedFakeKey3];
245         const publicKeyModel = {
246             ...model,
247             publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
248             encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey2']),
249             trustedFingerprints: new Set(['fakeKey3']),
250         };
251         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
253         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.PRIMARY_NOT_PINNED);
254     });
256     it('should give an error if the API returned no keys', () => {
257         const apiKeys = [] as PublicKeyReference[];
258         const pinnedKeys = [pinnedFakeKey1];
259         const verifyingPinnedKeys = [pinnedFakeKey1];
260         const publicKeyModel = {
261             ...model,
262             publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
263             trustedFingerprints: new Set(['fakeKey1']),
264             encryptionCapableFingerprints: new Set(['fakeKey1']),
265         };
266         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
268         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.INTERNAL_USER_NO_API_KEY);
269     });
271     it('should give an error if the API returned no keys valid for sending', () => {
272         const apiKeys = [fakeKey1, fakeKey2, fakeKey3];
273         const pinnedKeys = [pinnedFakeKey1];
274         const verifyingPinnedKeys = [] as PublicKeyReference[];
275         const publicKeyModel = {
276             ...model,
277             publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
278             trustedFingerprints: new Set(['fakeKey1']),
279             encryptionCapableFingerprints: new Set(['fakeKey3']),
280             obsoleteFingerprints: new Set(['fakeKey3']),
281         };
282         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
284         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.PRIMARY_CANNOT_SEND);
285     });
287     it('should give an error if there are pinned keys but the contact signature could not be verified', () => {
288         const apiKeys = [fakeKey1, fakeKey2, fakeKey3];
289         const pinnedKeys = [pinnedFakeKey1];
290         const verifyingPinnedKeys = [pinnedFakeKey1];
291         const publicKeyModel = {
292             ...model,
293             isContactSignatureVerified: false,
294             publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
295             encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey2', 'fakeKey3']),
296         };
297         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
299         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.CONTACT_SIGNATURE_NOT_VERIFIED);
300     });
302     it('should give an error if key transparency returned an error', () => {
303         const publicKeyModel = {
304             ...model,
305             publicKeys: { apiKeys: [], pinnedKeys: [], verifyingPinnedKeys: [] },
306             emailAddressErrors: ['Key verification error'],
307         };
308         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
309         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.EMAIL_ADDRESS_ERROR);
310     });
312     it('should give an error if key transparency returned an error, and ignore pinned keys', () => {
313         const pinnedKeys = [pinnedFakeKey2, pinnedFakeKey1];
314         const verifyingPinnedKeys = [pinnedFakeKey2];
315         const publicKeyModel = {
316             ...model,
317             publicKeys: { apiKeys: [], pinnedKeys, verifyingPinnedKeys },
318             trustedFingerprints: new Set(['fakeKey1', 'fakeKey2']),
319             emailAddressErrors: ['Key verification error'],
320         };
321         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
322         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.EMAIL_ADDRESS_ERROR);
323     });
326 const testExtractEncryptionPreferencesWithWKD = (encrypt: boolean) =>
327     describe(`extractEncryptionPreferences for an external user with WKD keys (encrypt: ${encrypt})`, () => {
328         const ktVerificationResult = { status: KT_VERIFICATION_STATUS.UNVERIFIED_KEYS };
329         const model = {
330             encrypt,
331             emailAddress: 'user@pm.me',
332             publicKeys: { apiKeys: [], pinnedKeys: [], verifyingPinnedKeys: [] },
333             scheme: PGP_SCHEMES.PGP_INLINE,
334             mimeType: MIME_TYPES.PLAINTEXT as CONTACT_MIME_TYPES,
335             isInternalWithDisabledE2EEForMail: false,
336             trustedFingerprints: new Set([]),
337             encryptionCapableFingerprints: new Set([]),
338             obsoleteFingerprints: new Set([]),
339             compromisedFingerprints: new Set([]),
340             isPGPExternal: true,
341             isPGPInternal: false,
342             isPGPExternalWithExternallyFetchedKeys: true,
343             isPGPExternalWithoutExternallyFetchedKeys: false,
344             pgpAddressDisabled: false,
345             isContact: true,
346             isContactSignatureVerified: true,
347             contactSignatureTimestamp: new Date(0),
348             ktVerificationResult,
349         };
350         const mailSettings = {
351             Sign: SIGN.DISABLED,
352             PGPScheme: PACKAGE_TYPE.SEND_PGP_MIME,
353             DraftMIMEType: MIME_TYPES.DEFAULT,
354         } as MailSettings;
356         it('should extract the primary API key when the email address does not belong to any contact', () => {
357             const apiKeys = [fakeKey1, fakeKey2, fakeKey3];
358             const pinnedKeys = [] as PublicKeyReference[];
359             const verifyingPinnedKeys = [] as PublicKeyReference[];
360             const publicKeyModel = {
361                 ...model,
362                 isContact: false,
363                 isContactSignatureVerified: undefined,
364                 contactSignatureTimestamp: undefined,
365                 publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
366                 encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey3']),
367                 obsoleteFingerprints: new Set(['fakeKey3']),
368             };
369             const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
371             expect(result).toEqual({
372                 encrypt,
373                 sign: encrypt,
374                 mimeType: MIME_TYPES.PLAINTEXT,
375                 scheme: PGP_SCHEMES.PGP_INLINE,
376                 isInternalWithDisabledE2EEForMail: false,
377                 sendKey: fakeKey1,
378                 isSendKeyPinned: false,
379                 apiKeys,
380                 pinnedKeys,
381                 verifyingPinnedKeys,
382                 isInternal: false,
383                 hasApiKeys: true,
384                 hasPinnedKeys: false,
385                 warnings: [],
386                 isContact: false,
387                 isContactSignatureVerified: undefined,
388                 emailAddressWarnings: undefined,
389                 contactSignatureTimestamp: undefined,
390                 ktVerificationResult,
391             });
392         });
394         it('should extract the primary API key when there are no pinned keys', () => {
395             const apiKeys = [fakeKey1, fakeKey2, fakeKey3];
396             const pinnedKeys = [] as PublicKeyReference[];
397             const verifyingPinnedKeys = [] as PublicKeyReference[];
398             const publicKeyModel = {
399                 ...model,
400                 publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
401                 encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey3']),
402                 obsoleteFingerprints: new Set(['fakeKey3']),
403             };
404             const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
406             expect(result).toEqual({
407                 encrypt,
408                 sign: encrypt,
409                 mimeType: MIME_TYPES.PLAINTEXT,
410                 scheme: PGP_SCHEMES.PGP_INLINE,
411                 isInternalWithDisabledE2EEForMail: false,
412                 sendKey: fakeKey1,
413                 isSendKeyPinned: false,
414                 apiKeys,
415                 pinnedKeys,
416                 verifyingPinnedKeys,
417                 isInternal: false,
418                 hasApiKeys: true,
419                 hasPinnedKeys: false,
420                 warnings: [],
421                 isContact: true,
422                 isContactSignatureVerified: true,
423                 contactSignatureTimestamp: new Date(0),
424                 emailAddressWarnings: undefined,
425                 ktVerificationResult,
426             });
427         });
429         it('should pick the pinned key (and not the API one)', () => {
430             const apiKeys = [fakeKey1, fakeKey2, fakeKey3];
431             const pinnedKeys = [pinnedFakeKey2, pinnedFakeKey1];
432             const verifyingPinnedKeys = [pinnedFakeKey1];
433             const publicKeyModel = {
434                 ...model,
435                 publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
436                 trustedFingerprints: new Set(['fakeKey1', 'fakeKey2']),
437                 encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey3']),
438                 obsoleteFingerprints: new Set(['fakeKey3']),
439             };
440             const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
442             expect(result).toEqual({
443                 encrypt,
444                 sign: encrypt,
445                 mimeType: MIME_TYPES.PLAINTEXT,
446                 scheme: PGP_SCHEMES.PGP_INLINE,
447                 isInternalWithDisabledE2EEForMail: false,
448                 sendKey: pinnedFakeKey1,
449                 isSendKeyPinned: true,
450                 apiKeys,
451                 pinnedKeys,
452                 verifyingPinnedKeys,
453                 isInternal: false,
454                 hasApiKeys: true,
455                 hasPinnedKeys: true,
456                 warnings: [],
457                 isContact: true,
458                 isContactSignatureVerified: true,
459                 contactSignatureTimestamp: new Date(0),
460                 emailAddressWarnings: undefined,
461                 ktVerificationResult,
462             });
463         });
465         it('should give a warning for keyid mismatch', () => {
466             const apiKeys = [fakeKey2, fakeKey3];
467             const pinnedKeys = [pinnedFakeKey2];
468             const verifyingPinnedKeys = [pinnedFakeKey2];
469             const publicKeyModel = {
470                 ...model,
471                 publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
472                 trustedFingerprints: new Set(['fakeKey2']),
473                 encryptionCapableFingerprints: new Set(['fakeKey2', 'fakeKey3']),
474             };
475             const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
477             expect(result.warnings?.length).toEqual(1);
478         });
480         it('should give an error when the API gave emailAddress errors', () => {
481             const publicKeyModel = {
482                 ...model,
483                 publicKeys: {
484                     apiKeys: [fakeKey1, fakeKey2, fakeKey3],
485                     pinnedKeys: [pinnedFakeKey1],
486                     verifyingPinnedKeys: [],
487                 },
488                 encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey2', 'fakeKey3']),
489                 obsoleteFingerprints: new Set(['fakeKey1']),
490                 emailAddressErrors: ['Recipient could not be found'],
491             };
492             const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
494             expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.EMAIL_ADDRESS_ERROR);
495         });
497         it('should give an error when the preferred pinned key is not valid for sending', () => {
498             const publicKeyModel = {
499                 ...model,
500                 publicKeys: {
501                     apiKeys: [fakeKey1, fakeKey2, fakeKey3],
502                     pinnedKeys: [pinnedFakeKey1],
503                     verifyingPinnedKeys: [pinnedFakeKey1],
504                 },
505                 encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey2', 'fakeKey3']),
506                 obsoleteFingerprints: new Set(['fakeKey1']),
507             };
508             const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
510             expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.PRIMARY_NOT_PINNED);
511         });
513         it('should give an error when the preferred pinned key is not among the keys returned by the API', () => {
514             const publicKeyModel = {
515                 ...model,
516                 publicKeys: {
517                     apiKeys: [fakeKey1, fakeKey2],
518                     pinnedKeys: [pinnedFakeKey3],
519                     verifyingPinnedKeys: [pinnedFakeKey3],
520                 },
521                 encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey2', 'fakeKey3']),
522                 trustedFingerprints: new Set(['fakeKey3']),
523             };
524             const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
526             expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.PRIMARY_NOT_PINNED);
527         });
529         it('should give an error if the API returned no keys valid for sending', () => {
530             const publicKeyModel = {
531                 ...model,
532                 publicKeys: {
533                     apiKeys: [fakeKey1, fakeKey2, fakeKey3],
534                     pinnedKeys: [pinnedFakeKey1],
535                     verifyingPinnedKeys: [],
536                 },
537                 trustedFingerprints: new Set(['fakeKey1']),
538                 encryptionCapableFingerprints: new Set(['fakeKey3']),
539                 obsoleteFingerprints: new Set(['fakeKey3']),
540             };
541             const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
543             expect(result?.error?.type).toEqual(
544                 ENCRYPTION_PREFERENCES_ERROR_TYPES.EXTERNAL_USER_NO_VALID_EXTERNALLY_FETCHED_KEY
545             );
546         });
548         it('should give an error if there are pinned keys but the contact signature could not be verified', () => {
549             const apiKeys = [fakeKey1, fakeKey2, fakeKey3];
550             const pinnedKeys = [pinnedFakeKey1];
551             const verifyingPinnedKeys = [pinnedFakeKey1];
552             const publicKeyModel = {
553                 ...model,
554                 isContactSignatureVerified: false,
555                 publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
556                 encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey2', 'fakeKey3']),
557             };
558             const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
560             expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.CONTACT_SIGNATURE_NOT_VERIFIED);
561         });
562     });
564 testExtractEncryptionPreferencesWithWKD(true);
565 testExtractEncryptionPreferencesWithWKD(false);
567 describe('extractEncryptionPreferences for an external user without WKD keys', () => {
568     const ktVerificationResult = { status: KT_VERIFICATION_STATUS.UNVERIFIED_KEYS };
569     const model = {
570         emailAddress: 'user@tatoo.me',
571         publicKeys: { apiKeys: [], pinnedKeys: [], verifyingPinnedKeys: [] },
572         encrypt: false,
573         sign: false,
574         scheme: PGP_SCHEMES_MORE.GLOBAL_DEFAULT,
575         mimeType: MIME_TYPES_MORE.AUTOMATIC,
576         isInternalWithDisabledE2EEForMail: false,
577         trustedFingerprints: new Set([]),
578         encryptionCapableFingerprints: new Set([]),
579         obsoleteFingerprints: new Set([]),
580         compromisedFingerprints: new Set([]),
581         isPGPExternal: true,
582         isPGPInternal: false,
583         isPGPExternalWithExternallyFetchedKeys: false,
584         isPGPExternalWithoutExternallyFetchedKeys: true,
585         pgpAddressDisabled: false,
586         isContact: true,
587         isContactSignatureVerified: true,
588         contactSignatureTimestamp: new Date(0),
589         ktVerificationResult,
590     };
591     const mailSettings = {
592         Sign: SIGN.ENABLED,
593         PGPScheme: PACKAGE_TYPE.SEND_PGP_MIME,
594         DraftMIMEType: MIME_TYPES.PLAINTEXT,
595     } as MailSettings;
597     it('should take into account the mail Settings', () => {
598         const modelWithoutSign = { ...model, encrypt: undefined, sign: undefined };
599         const result = extractEncryptionPreferences(modelWithoutSign, mailSettings);
601         expect(result).toEqual({
602             encrypt: false,
603             sign: true,
604             mimeType: MIME_TYPES_MORE.AUTOMATIC,
605             scheme: PGP_SCHEMES.PGP_MIME,
606             isInternalWithDisabledE2EEForMail: false,
607             apiKeys: [],
608             pinnedKeys: [],
609             verifyingPinnedKeys: [],
610             isInternal: false,
611             hasApiKeys: false,
612             hasPinnedKeys: false,
613             isContact: true,
614             isContactSignatureVerified: true,
615             contactSignatureTimestamp: new Date(0),
616             emailAddressWarnings: undefined,
617             ktVerificationResult,
618         });
619     });
621     it('should pick no key when the email address does not belong to any contact', () => {
622         const result = extractEncryptionPreferences(
623             {
624                 ...model,
625                 isContact: false,
626                 isContactSignatureVerified: undefined,
627                 contactSignatureTimestamp: undefined,
628             },
629             mailSettings
630         );
632         expect(result).toEqual({
633             encrypt: false,
634             sign: false,
635             mimeType: MIME_TYPES_MORE.AUTOMATIC,
636             scheme: PGP_SCHEMES.PGP_MIME,
637             isInternalWithDisabledE2EEForMail: false,
638             apiKeys: [],
639             pinnedKeys: [],
640             verifyingPinnedKeys: [],
641             isInternal: false,
642             hasApiKeys: false,
643             hasPinnedKeys: false,
644             isContact: false,
645             isContactSignatureVerified: undefined,
646             contactSignatureTimestamp: undefined,
647             emailAddressWarnings: undefined,
648             ktVerificationResult,
649         });
650     });
652     it('should pick no key when there are no pinned keys', () => {
653         const result = extractEncryptionPreferences(model, mailSettings);
655         expect(result).toEqual({
656             encrypt: false,
657             sign: false,
658             mimeType: MIME_TYPES_MORE.AUTOMATIC,
659             scheme: PGP_SCHEMES.PGP_MIME,
660             isInternalWithDisabledE2EEForMail: false,
661             apiKeys: [],
662             pinnedKeys: [],
663             verifyingPinnedKeys: [],
664             isInternal: false,
665             hasApiKeys: false,
666             hasPinnedKeys: false,
667             isContact: true,
668             isContactSignatureVerified: true,
669             contactSignatureTimestamp: new Date(0),
670             emailAddressWarnings: undefined,
671             ktVerificationResult,
672         });
673     });
675     it('should pick the first pinned key', () => {
676         const apiKeys = [] as PublicKeyReference[];
677         const pinnedKeys = [pinnedFakeKey2, pinnedFakeKey3];
678         const verifyingPinnedKeys = [pinnedFakeKey2, pinnedFakeKey3];
679         const publicKeyModel = {
680             ...model,
681             encrypt: true,
682             sign: true,
683             publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
684             trustedFingerprints: new Set(['fakeKey2', 'fakeKey3']),
685             encryptionCapableFingerprints: new Set(['fakeKey2', 'fakeKey3']),
686         };
687         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
689         expect(result).toEqual({
690             encrypt: true,
691             sign: true,
692             mimeType: MIME_TYPES_MORE.AUTOMATIC,
693             scheme: PGP_SCHEMES.PGP_MIME,
694             isInternalWithDisabledE2EEForMail: false,
695             sendKey: pinnedFakeKey2,
696             isSendKeyPinned: true,
697             apiKeys,
698             pinnedKeys,
699             verifyingPinnedKeys,
700             isInternal: false,
701             hasApiKeys: false,
702             hasPinnedKeys: true,
703             warnings: [],
704             isContact: true,
705             isContactSignatureVerified: true,
706             contactSignatureTimestamp: new Date(0),
707             emailAddressWarnings: undefined,
708             ktVerificationResult,
709         });
710     });
712     it('should give a warning for keyid mismatch', () => {
713         const publicKeyModel = {
714             ...model,
715             encrypt: true,
716             sign: true,
717             publicKeys: { apiKeys: [], pinnedKeys: [pinnedFakeKey1], verifyingPinnedKeys: [] },
718             trustedFingerprints: new Set(['fakeKey1']),
719             encryptionCapableFingerprints: new Set(['fakeKey1']),
720         };
721         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
723         expect(result.warnings?.length).toEqual(1);
724     });
726     it('should give an error when the preferred pinned key is not valid for sending', () => {
727         const publicKeyModel = {
728             ...model,
729             encrypt: true,
730             sign: true,
731             publicKeys: {
732                 apiKeys: [],
733                 pinnedKeys: [pinnedFakeKey2, pinnedFakeKey1],
734                 verifyingPinnedKeys: [pinnedFakeKey1],
735             },
736             encryptionCapableFingerprints: new Set([]),
737         };
738         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
740         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.EXTERNAL_USER_NO_VALID_PINNED_KEY);
741     });
743     it('should give an error if there are pinned keys but the contact signature could not be verified', () => {
744         const publicKeyModel = {
745             ...model,
746             isContactSignatureVerified: false,
747             publicKeys: {
748                 apiKeys: [],
749                 pinnedKeys: [pinnedFakeKey2, pinnedFakeKey1],
750                 verifyingPinnedKeys: [pinnedFakeKey2],
751             },
752             encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey2']),
753         };
754         const result = extractEncryptionPreferences(publicKeyModel, mailSettings);
756         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.CONTACT_SIGNATURE_NOT_VERIFIED);
757     });
760 describe('extractEncryptionPreferences for an own address', () => {
761     const apiKeys = [fakeKey1, fakeKey2];
762     const pinnedKeys = [] as PublicKeyReference[];
763     const verifyingPinnedKeys = [] as PublicKeyReference[];
764     const ktVerificationResult = { status: KT_VERIFICATION_STATUS.VERIFIED_KEYS };
765     const model = {
766         emailAddress: 'user@pm.me',
767         publicKeys: { apiKeys, pinnedKeys, verifyingPinnedKeys },
768         mimeType: MIME_TYPES_MORE.AUTOMATIC,
769         scheme: PGP_SCHEMES_MORE.GLOBAL_DEFAULT,
770         isInternalWithDisabledE2EEForMail: false,
771         trustedFingerprints: new Set([]),
772         encryptionCapableFingerprints: new Set(['fakeKey1', 'fakeKey2']),
773         obsoleteFingerprints: new Set([]),
774         compromisedFingerprints: new Set([]),
775         isPGPExternal: false,
776         isPGPInternal: true,
777         isPGPExternalWithExternallyFetchedKeys: false,
778         isPGPExternalWithoutExternallyFetchedKeys: false,
779         pgpAddressDisabled: false,
780         isContact: false,
781         emailAddressWarnings: undefined,
782         ktVerificationResult,
783     };
784     const mailSettings = {
785         Sign: SIGN.ENABLED,
786         PGPScheme: PACKAGE_TYPE.SEND_PGP_MIME,
787         DraftMIMEType: MIME_TYPES.PLAINTEXT,
788     } as MailSettings;
790     it('should not pick the public key from the keys in selfSend.address', () => {
791         const selfSend: SelfSend = {
792             address: {
793                 HasKeys: 1,
794                 Receive: 1,
795             },
796             publicKey: pinnedFakeKey1,
797             canSend: true,
798         } as any;
799         const result = extractEncryptionPreferences(model, mailSettings, selfSend);
801         expect(result).toEqual({
802             encrypt: true,
803             sign: true,
804             mimeType: MIME_TYPES_MORE.AUTOMATIC,
805             scheme: PGP_SCHEMES.PGP_MIME,
806             isInternalWithDisabledE2EEForMail: false,
807             sendKey: pinnedFakeKey1,
808             isSendKeyPinned: false,
809             apiKeys,
810             pinnedKeys,
811             verifyingPinnedKeys,
812             isInternal: true,
813             hasApiKeys: true,
814             hasPinnedKeys: false,
815             warnings: [],
816             isContact: false,
817             isContactSignatureVerified: undefined,
818             contactSignatureTimestamp: undefined,
819             emailAddressWarnings: undefined,
820             ktVerificationResult,
821         });
822     });
824     it('should give a warning for keyid mismatch', () => {
825         const selfSend: SelfSend = {
826             address: {
827                 HasKeys: 1,
828                 Receive: 1,
829             },
830             publicKey: pinnedFakeKey2,
831             canSend: true,
832         } as any;
833         const result = extractEncryptionPreferences(model, mailSettings, selfSend);
835         expect(result.warnings?.length).toEqual(1);
836     });
838     it('should give an error when the address is disabled', () => {
839         const selfSend: SelfSend = {
840             address: {
841                 HasKeys: 1,
842                 Receive: 0,
843             },
844             publicKey: pinnedFakeKey1,
845             canSend: true,
846         } as any;
847         const result = extractEncryptionPreferences(model, mailSettings, selfSend);
849         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.INTERNAL_USER_DISABLED);
850     });
852     it('should give an error when the API returned no keys for the address', () => {
853         const selfSend: SelfSend = {
854             address: {
855                 HasKeys: 0,
856                 Receive: 1,
857             },
858             publicKey: pinnedFakeKey1,
859             canSend: true,
860         } as any;
861         const result = extractEncryptionPreferences(model, mailSettings, selfSend);
863         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.INTERNAL_USER_NO_API_KEY);
864     });
866     it('should give an error if the primary key is compromised', () => {
867         const selfSend: SelfSend = {
868             address: {
869                 HasKeys: 0,
870                 Receive: 1,
871             },
872             publicKey: pinnedFakeKey1,
873             canSend: false,
874         } as any;
875         const result = extractEncryptionPreferences(model, mailSettings, selfSend);
877         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.INTERNAL_USER_NO_API_KEY);
878     });
880     it('should give an error when no public key (from the decypted private key) was received', () => {
881         const selfSend: SelfSend = {
882             address: {
883                 HasKeys: 1,
884                 Receive: 1,
885             },
886         } as any;
887         const result = extractEncryptionPreferences(model, mailSettings, selfSend);
889         expect(result?.error?.type).toEqual(ENCRYPTION_PREFERENCES_ERROR_TYPES.PRIMARY_CANNOT_SEND);
890     });