1 import { CryptoProxy } from '@proton/crypto';
2 import { activatePasswordlessKey } from '@proton/shared/lib/api/members';
5 getReplacedAddressKeyTokens,
6 reencryptOrganizationToken,
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';
16 CachedOrganizationKey,
20 KeyTransparencyVerify,
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 {
31 addressKeys: DecryptedKey[];
32 userKeys: DecryptedKey[];
35 keyGenConfig: KeyGenConfig;
37 keyTransparencyVerify: KeyTransparencyVerify;
40 export const addAddressKeysProcess = async ({
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({
61 keyTransparencyVerify,
65 return createAddressKeyLegacy({
69 passphrase: keyPassword,
71 keyTransparencyVerify,
75 interface AddUserKeysProcessArguments {
77 keyGenConfig?: KeyGenConfig | KeyGenConfigV6;
79 userKeys: DecryptedKey[];
82 isDeviceRecoveryAvailable?: boolean;
83 isDeviceRecoveryEnabled?: boolean;
84 organizationKey?: CachedOrganizationKey;
87 export const addUserKeysProcess = async ({
89 keyGenConfig = KEYGEN_CONFIGS[DEFAULT_KEYGEN_TYPE],
95 isDeviceRecoveryAvailable,
96 isDeviceRecoveryEnabled,
97 }: AddUserKeysProcessArguments) => {
98 const { privateKey, privateKeyArmored } = await generateUserKey({
106 PrivateKey: privateKeyArmored,
110 const splitUserKeys = splitKeys(userKeys);
112 if (getHasMigratedAddressKeys(addresses)) {
113 const replacedResult = await getReplacedAddressKeyTokens({
114 privateKeys: splitUserKeys.privateKeys,
118 if (replacedResult.AddressKeyTokens.length) {
119 await api(replaceAddressTokens(replacedResult)).catch(/*ignore failures */ noop);
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,
131 activatePasswordlessKey({
132 TokenKeyPacket: result.keyPacket,
133 Signature: result.signature,
135 ).catch(/*ignore failures */ noop);
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' }),
143 await storeDeviceRecovery({
146 userKeys: [{ ID: 'tmp-id', privateKey, publicKey }, ...userKeys],