Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / shared / lib / keys / add / addKeysProcess.ts
blob4b143ede47dd417288467fef0d6fa1b2c7ebec3a
1 import { CryptoProxy } from '@proton/crypto';
2 import { activatePasswordlessKey } from '@proton/shared/lib/api/members';
3 import {
4     getIsPasswordless,
5     getReplacedAddressKeyTokens,
6     reencryptOrganizationToken,
7     splitKeys,
8 } from '@proton/shared/lib/keys';
9 import noop from '@proton/utils/noop';
11 import { createUserKeyRoute, replaceAddressTokens } from '../../api/keys';
12 import { DEFAULT_KEYGEN_TYPE, KEYGEN_CONFIGS } from '../../constants';
13 import type {
14     Address,
15     Api,
16     CachedOrganizationKey,
17     DecryptedKey,
18     KeyGenConfig,
19     KeyGenConfigV6,
20     KeyTransparencyVerify,
21     UserModel,
22 } from '../../interfaces';
23 import { storeDeviceRecovery } from '../../recoveryFile/deviceRecovery';
24 import { getActiveAddressKeys } from '../getActiveKeys';
25 import { getHasMigratedAddressKeys } from '../keyMigration';
26 import { generateUserKey } from '../userKeys';
27 import { createAddressKeyLegacy, createAddressKeyV2 } from './addAddressKeyHelper';
29 interface AddAddressKeysProcessArguments {
30     api: Api;
31     addressKeys: DecryptedKey[];
32     userKeys: DecryptedKey[];
33     address: Address;
34     addresses: Address[];
35     keyGenConfig: KeyGenConfig;
36     keyPassword: string;
37     keyTransparencyVerify: KeyTransparencyVerify;
40 export const addAddressKeysProcess = async ({
41     api,
42     keyGenConfig,
43     addresses,
44     address,
45     addressKeys,
46     userKeys,
47     keyPassword,
48     keyTransparencyVerify,
49 }: AddAddressKeysProcessArguments) => {
50     const hasMigratedAddressKeys = getHasMigratedAddressKeys(addresses);
52     const activeKeys = await getActiveAddressKeys(address, address.SignedKeyList, address.Keys, addressKeys);
54     if (hasMigratedAddressKeys) {
55         return createAddressKeyV2({
56             api,
57             userKeys,
58             keyGenConfig,
59             activeKeys,
60             address,
61             keyTransparencyVerify,
62         });
63     }
65     return createAddressKeyLegacy({
66         api,
67         address,
68         keyGenConfig,
69         passphrase: keyPassword,
70         activeKeys,
71         keyTransparencyVerify,
72     });
75 interface AddUserKeysProcessArguments {
76     api: Api;
77     keyGenConfig?: KeyGenConfig | KeyGenConfigV6;
78     user: UserModel;
79     userKeys: DecryptedKey[];
80     addresses: Address[];
81     passphrase: string;
82     isDeviceRecoveryAvailable?: boolean;
83     isDeviceRecoveryEnabled?: boolean;
84     organizationKey?: CachedOrganizationKey;
87 export const addUserKeysProcess = async ({
88     api,
89     keyGenConfig = KEYGEN_CONFIGS[DEFAULT_KEYGEN_TYPE],
90     user,
91     userKeys,
92     addresses,
93     passphrase,
94     organizationKey,
95     isDeviceRecoveryAvailable,
96     isDeviceRecoveryEnabled,
97 }: AddUserKeysProcessArguments) => {
98     const { privateKey, privateKeyArmored } = await generateUserKey({
99         passphrase,
100         keyGenConfig,
101     });
103     await api(
104         createUserKeyRoute({
105             Primary: 1,
106             PrivateKey: privateKeyArmored,
107         })
108     );
110     const splitUserKeys = splitKeys(userKeys);
112     if (getHasMigratedAddressKeys(addresses)) {
113         const replacedResult = await getReplacedAddressKeyTokens({
114             privateKeys: splitUserKeys.privateKeys,
115             addresses,
116             privateKey,
117         });
118         if (replacedResult.AddressKeyTokens.length) {
119             await api(replaceAddressTokens(replacedResult)).catch(/*ignore failures */ noop);
120         }
121     }
123     if (getIsPasswordless(organizationKey?.Key) && organizationKey?.privateKey) {
124         const result = await reencryptOrganizationToken({
125             Token: organizationKey.Key.Token,
126             encryptionKey: privateKey,
127             signingKey: privateKey,
128             decryptionKeys: splitUserKeys.privateKeys,
129         });
130         await api(
131             activatePasswordlessKey({
132                 TokenKeyPacket: result.keyPacket,
133                 Signature: result.signature,
134             })
135         ).catch(/*ignore failures */ noop);
136     }
138     // Store a new device recovery immediately to avoid having the storing trigger asynchronously which would cause red notification flashes
139     if (isDeviceRecoveryAvailable && isDeviceRecoveryEnabled) {
140         const publicKey = await CryptoProxy.importPublicKey({
141             binaryKey: await CryptoProxy.exportPublicKey({ key: privateKey, format: 'binary' }),
142         });
143         await storeDeviceRecovery({
144             api,
145             user,
146             userKeys: [{ ID: 'tmp-id', privateKey, publicKey }, ...userKeys],
147         });
148     }
150     return privateKey;