Use same lock values as mobile clients
[ProtonMail-WebClient.git] / packages / shared / lib / api / helpers / getPublicKeysVcardHelper.ts
blobe6a34e08a17ca8d3ff93611f1678adaa5d85b8e8
1 import type { PublicKeyReference } from '@proton/crypto';
3 import { CONTACT_CARD_TYPE } from '../../constants';
4 import { CRYPTO_PROCESSING_TYPES } from '../../contacts/constants';
5 import { readSigned } from '../../contacts/decrypt';
6 import { getKeyInfoFromProperties } from '../../contacts/keyProperties';
7 import { parseToVCard } from '../../contacts/vcard';
8 import { CANONICALIZE_SCHEME, canonicalizeEmail } from '../../helpers/email';
9 import type { Api, PinnedKeysConfig } from '../../interfaces';
10 import type { ContactEmail, Contact as tsContact } from '../../interfaces/contacts';
11 import { getContact, queryContactEmails } from '../contacts';
13 const getContactEmail = async (
14     emailAddress: string,
15     contactEmailsMap: { [email: string]: ContactEmail | undefined } = {},
16     api: Api
17 ) => {
18     // Simple normalize here, internal version is to aggressive relative to contacts emails
19     const canonicalEmail = canonicalizeEmail(emailAddress);
20     if (contactEmailsMap[canonicalEmail]) {
21         return contactEmailsMap[canonicalEmail];
22     }
23     const { ContactEmails = [] } = await api<{ ContactEmails: ContactEmail[] }>(
24         queryContactEmails({ Email: canonicalEmail })
25     );
26     return ContactEmails[0];
29 /**
30  * Get the public keys stored in the vcard of a contact associated to a certain email address.
31  * Verify the signature on the contact in the process with the public keys provided
32  */
33 const getPublicKeysVcardHelper = async (
34     api: Api,
35     emailAddress: string,
36     publicKeys: PublicKeyReference[],
37     isInternal?: boolean,
38     contactEmailsMap: { [email: string]: ContactEmail | undefined } = {}
39 ): Promise<PinnedKeysConfig> => {
40     let isContact = false;
41     let isContactSignatureVerified;
42     let contactSignatureTimestamp;
43     try {
44         const ContactEmail = await getContactEmail(emailAddress, contactEmailsMap, api);
45         if (ContactEmail === undefined) {
46             return { pinnedKeys: [], isContact };
47         }
48         isContact = true;
49         // ContactEmail.Defaults flag informs if there is specific configuration in the contact for this email
50         if (ContactEmail.Defaults === 1) {
51             return { pinnedKeys: [], isContact };
52         }
53         // pick the first contact with the desired email. The API returns them ordered by decreasing priority already
54         const { Contact } = await api<{ Contact: tsContact }>(getContact(ContactEmail.ContactID));
55         // all the info we need is in the signed part
56         const signedCard = Contact.Cards.find(({ Type }) => Type === CONTACT_CARD_TYPE.SIGNED);
57         if (!signedCard) {
58             // contacts created by the server are not signed
59             return { pinnedKeys: [], isContact: !!Contact.Cards.length };
60         }
61         const { type, data: signedVcard, signatureTimestamp } = await readSigned(signedCard, { publicKeys });
62         isContactSignatureVerified = type === CRYPTO_PROCESSING_TYPES.SUCCESS;
63         contactSignatureTimestamp = signatureTimestamp;
64         const vCardContact = parseToVCard(signedVcard);
65         const emailProperty = (vCardContact.email || []).find(({ field, value }) => {
66             const scheme = isInternal ? CANONICALIZE_SCHEME.PROTON : CANONICALIZE_SCHEME.DEFAULT;
67             return (
68                 field === 'email' &&
69                 canonicalizeEmail(value as string, scheme) === canonicalizeEmail(emailAddress, scheme)
70             );
71         });
72         if (!emailProperty || !emailProperty.group) {
73             throw new Error('Invalid vcard');
74         }
75         return {
76             ...(await getKeyInfoFromProperties(vCardContact, emailProperty.group)),
77             isContact,
78             isContactSignatureVerified,
79             contactSignatureTimestamp,
80         };
81     } catch (error: any) {
82         return { pinnedKeys: [], isContact, isContactSignatureVerified, contactSignatureTimestamp, error };
83     }
86 export default getPublicKeysVcardHelper;