Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / components / hooks / useGetEncryptionPreferences.ts
blob6db9ee8d45c6c2731571d68a12a18aca445f4fd7
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;
29 /**
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.
34  */
35 const useGetEncryptionPreferences = () => {
36     const api = useApi();
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
50             );
51             let selfSend: SelfSend | undefined;
52             let apiKeysConfig: ApiKeysConfig;
53             let pinnedKeysConfig: PinnedKeysConfig;
54             if (selfAddress) {
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(
59                         selfAddress,
60                         selfAddress.SignedKeyList,
61                         selfAddress.Keys,
62                         selfAddressKeys
63                     )
64                 ).v4[0];
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
70                 apiKeysConfig = {
71                     publicKeys: [],
72                     RecipientType: RECIPIENT_TYPES.TYPE_INTERNAL,
73                     ktVerificationResult: { status: KT_VERIFICATION_STATUS.VERIFIED_KEYS },
74                 };
75                 pinnedKeysConfig = { pinnedKeys: [], isContact: false };
76             } else {
77                 const { publicKeys } = splitKeys(await getUserKeys());
78                 apiKeysConfig = await getPublicKeysForInbox({
79                     email,
80                     internalKeysOnly: intendedForEmail === false,
81                     includeInternalKeysWithE2EEDisabledForMail: intendedForEmail === false,
82                     lifetime,
83                 });
84                 const isInternal = apiKeysConfig.RecipientType === RECIPIENT_TYPES.TYPE_INTERNAL;
85                 pinnedKeysConfig = await getPublicKeysVcardHelper(api, email, publicKeys, isInternal, contactEmailsMap);
86             }
87             const publicKeyModel = await getContactPublicKeyModel({
88                 emailAddress: email,
89                 apiKeysConfig,
90                 pinnedKeysConfig,
91             });
92             return extractEncryptionPreferences(publicKeyModel, mailSettings, selfSend);
93         },
94         [api, getAddressKeys, getAddresses, getPublicKeysForInbox, getMailSettings]
95     );
97     return useCallback<GetEncryptionPreferences>(
98         ({ email, lifetime = DEFAULT_LIFETIME, contactEmailsMap, intendedForEmail }) => {
99             if (!cache.has(CACHE_KEY)) {
100                 cache.set(CACHE_KEY, new Map());
101             }
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);
107             const miss = () =>
108                 getEncryptionPreferences({
109                     email: canonicalEmail,
110                     intendedForEmail,
111                     lifetime,
112                     contactEmailsMap,
113                 });
114             return getPromiseValue(subCache, canonicalEmail, miss, lifetime);
115         },
116         [cache, getEncryptionPreferences]
117     );
120 export default useGetEncryptionPreferences;