1 import type { PublicKeyReference } from '@proton/crypto';
2 import { CryptoProxy } from '@proton/crypto';
3 import { arrayToBinaryString, binaryStringToArray, decodeBase64, encodeBase64 } from '@proton/crypto/lib/utils';
4 import isTruthy from '@proton/utils/isTruthy';
6 import { MIME_TYPES, PGP_SCHEMES } from '../constants';
7 import type { MimeTypeVcard, PinnedKeysConfig } from '../interfaces';
8 import type { VCardContact, VCardProperty } from '../interfaces/contacts/VCard';
9 import { compareVCardPropertyByPref, createContactPropertyUid } from './properties';
12 * The only values allowed for a PGP scheme stored in a vCard are
13 * '' for default PGP scheme (meaning we should use the PGPScheme from mailSettings when composing email)
14 * 'pgp-mime' for PGP-Inline scheme
15 * 'pgp-mime' for PGP-MIME scheme
17 export const getPGPSchemeVcard = (scheme: string): PGP_SCHEMES | undefined => {
18 // ugly code; typescript to be blamed
19 if (Object.values(PGP_SCHEMES).includes(scheme as PGP_SCHEMES)) {
20 return scheme as PGP_SCHEMES;
25 * The only values allowed for a MIME type stored in a vCard are
26 * '' for automatic format (meaning we should use DraftMIMEType from mailSettings when composing email)
27 * 'text/plain' for plain text format
29 export const getMimeTypeVcard = (mimeType: string): MimeTypeVcard | undefined => {
30 return mimeType === MIME_TYPES.PLAINTEXT ? mimeType : undefined;
33 export const getKeyVCard = async (keyValue: string): Promise<PublicKeyReference | undefined> => {
34 const [, base64 = ''] = keyValue.split(',');
35 const key = binaryStringToArray(decodeBase64(base64));
38 const publicKey = await CryptoProxy.importPublicKey({ binaryKey: key });
44 * Given an array of vCard properties, extract the keys and key-related fields relevant for an email address
46 export const getKeyInfoFromProperties = async (
47 vCardContact: VCardContact,
49 ): Promise<Omit<PinnedKeysConfig, 'isContactSignatureVerified' | 'isContact'>> => {
50 const getByGroup = <T>(properties: VCardProperty<T>[] = []): VCardProperty<T> | undefined =>
51 properties.find(({ group }) => group === emailGroup);
53 const pinnedKeyPromises = (vCardContact.key || [])
54 .filter(({ group }) => group === emailGroup)
55 .sort(compareVCardPropertyByPref)
56 .map(async ({ value }) => getKeyVCard(value));
57 const pinnedKeys = (await Promise.all(pinnedKeyPromises)).filter(isTruthy);
59 const encryptToPinned =
60 'x-pm-encrypt' in vCardContact ? getByGroup(vCardContact['x-pm-encrypt'])?.value : undefined;
61 const encryptToUntrusted =
62 'x-pm-encrypt-untrusted' in vCardContact
63 ? getByGroup(vCardContact['x-pm-encrypt-untrusted'])?.value
66 const scheme = getByGroup(vCardContact['x-pm-scheme'])?.value;
67 const mimeType = getByGroup(vCardContact['x-pm-mimetype'])?.value;
68 const sign = getByGroup(vCardContact['x-pm-sign'])?.value;
70 return { pinnedKeys, encryptToPinned, encryptToUntrusted, scheme, mimeType, sign };
73 interface VcardPublicKey {
74 publicKey: PublicKeyReference;
80 * Transform a key into a vCard property
82 export const toKeyProperty = async ({ publicKey, group, index }: VcardPublicKey): Promise<VCardProperty<string>> => {
83 const binaryKey = await CryptoProxy.exportPublicKey({ key: publicKey, format: 'binary' });
86 value: `data:application/pgp-keys;base64,${encodeBase64(arrayToBinaryString(binaryKey))}`,
88 params: { pref: String(index + 1) }, // order is important
89 uid: createContactPropertyUid(),