1 import { useCallback } from 'react';
3 import { useGetAddressKeys } from '@proton/account/addressKeys/hooks';
4 import { useGetAddresses } from '@proton/account/addresses/hooks';
5 import { useGetUserKeys } from '@proton/account/userKeys/hooks';
6 import { useGetMailSettings } from '@proton/mail/mailSettings/hooks';
7 import getPublicKeysVcardHelper from '@proton/shared/lib/api/helpers/getPublicKeysVcardHelper';
8 import { MINUTE, RECIPIENT_TYPES } from '@proton/shared/lib/constants';
9 import { getSelfSendAddresses } from '@proton/shared/lib/helpers/address';
10 import { canonicalizeEmail, canonicalizeInternalEmail } from '@proton/shared/lib/helpers/email';
11 import type { ApiKeysConfig, PinnedKeysConfig, SelfSend } from '@proton/shared/lib/interfaces';
12 import { KT_VERIFICATION_STATUS } from '@proton/shared/lib/interfaces';
13 import type { GetEncryptionPreferences } from '@proton/shared/lib/interfaces/hooks/GetEncryptionPreferences';
14 import { getKeyHasFlagsToEncrypt } from '@proton/shared/lib/keys';
15 import { getActiveAddressKeys } from '@proton/shared/lib/keys/getActiveKeys';
16 import { splitKeys } from '@proton/shared/lib/keys/keys';
17 import { getContactPublicKeyModel, getKeyEncryptionCapableStatus } from '@proton/shared/lib/keys/publicKeys';
18 import extractEncryptionPreferences from '@proton/shared/lib/mail/encryptionPreferences';
20 import useApi from './useApi';
21 import useCache from './useCache';
22 import { getPromiseValue } from './useCachedModelResult';
23 import useGetPublicKeysForInbox from './useGetPublicKeysForInbox';
25 export const CACHE_KEY = 'ENCRYPTION_PREFERENCES';
27 const DEFAULT_LIFETIME = 5 * MINUTE;
30 * Given an email address and the user mail settings, return the encryption preferences for sending to that email.
31 * The logic for how those preferences are determined is laid out in the
32 * Confluence document 'Encryption preferences for outgoing email'.
33 * NB: the current logic does not handle internal address keys belonging to external accounts, since these keys are not used by Inbox.
35 const useGetEncryptionPreferences = () => {
37 const cache = useCache();
38 const getAddresses = useGetAddresses();
39 const getUserKeys = useGetUserKeys();
40 const getAddressKeys = useGetAddressKeys();
41 const getPublicKeysForInbox = useGetPublicKeysForInbox();
42 const getMailSettings = useGetMailSettings();
44 const getEncryptionPreferences = useCallback<GetEncryptionPreferences>(
45 async ({ email, lifetime, contactEmailsMap, intendedForEmail = true }) => {
46 const [addresses, mailSettings] = await Promise.all([getAddresses(), getMailSettings()]);
47 const canonicalEmail = canonicalizeInternalEmail(email);
48 const selfAddress = getSelfSendAddresses(addresses).find(
49 ({ Email }) => canonicalizeInternalEmail(Email) === canonicalEmail
51 let selfSend: SelfSend | undefined;
52 let apiKeysConfig: ApiKeysConfig;
53 let pinnedKeysConfig: PinnedKeysConfig;
55 // we do not trust the public keys in ownAddress (they will be deprecated in the API response soon anyway)
56 const selfAddressKeys = await getAddressKeys(selfAddress.ID);
57 const primaryAddressKey = (
58 await getActiveAddressKeys(
60 selfAddress.SignedKeyList,
65 const selfPublicKey = primaryAddressKey?.publicKey;
66 const canEncrypt = selfPublicKey ? await getKeyEncryptionCapableStatus(selfPublicKey) : undefined;
67 const canSend = canEncrypt && getKeyHasFlagsToEncrypt(primaryAddressKey.flags);
68 selfSend = { address: selfAddress, publicKey: selfPublicKey, canSend };
69 // For own addresses, we use the decrypted keys in selfSend and do not fetch any data from the API
72 RecipientType: RECIPIENT_TYPES.TYPE_INTERNAL,
73 ktVerificationResult: { status: KT_VERIFICATION_STATUS.VERIFIED_KEYS },
75 pinnedKeysConfig = { pinnedKeys: [], isContact: false };
77 const { publicKeys } = splitKeys(await getUserKeys());
78 apiKeysConfig = await getPublicKeysForInbox({
80 internalKeysOnly: intendedForEmail === false,
81 includeInternalKeysWithE2EEDisabledForMail: intendedForEmail === false,
84 const isInternal = apiKeysConfig.RecipientType === RECIPIENT_TYPES.TYPE_INTERNAL;
85 pinnedKeysConfig = await getPublicKeysVcardHelper(api, email, publicKeys, isInternal, contactEmailsMap);
87 const publicKeyModel = await getContactPublicKeyModel({
92 return extractEncryptionPreferences(publicKeyModel, mailSettings, selfSend);
94 [api, getAddressKeys, getAddresses, getPublicKeysForInbox, getMailSettings]
97 return useCallback<GetEncryptionPreferences>(
98 ({ email, lifetime = DEFAULT_LIFETIME, contactEmailsMap, intendedForEmail }) => {
99 if (!cache.has(CACHE_KEY)) {
100 cache.set(CACHE_KEY, new Map());
102 const subCache = cache.get(CACHE_KEY);
103 // By normalizing email here, we consider that it could not exist different encryption preferences
104 // For 2 addresses identical but for the cases.
105 // If a provider does different one day, this would have to evolve.
106 const canonicalEmail = canonicalizeEmail(email);
108 getEncryptionPreferences({
109 email: canonicalEmail,
114 return getPromiseValue(subCache, canonicalEmail, miss, lifetime);
116 [cache, getEncryptionPreferences]
120 export default useGetEncryptionPreferences;