Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / shared / lib / keys / reactivation / reactivateKeyHelper.ts
blob22039ec28be8a21c09e42c34c595f0e097ac023b
1 import type { PrivateKeyReference } from '@proton/crypto';
2 import { CryptoProxy } from '@proton/crypto';
3 import unique from '@proton/utils/unique';
5 import type {
6     DecryptedKey,
7     Key,
8     KeyPair,
9     KeyTransparencyVerify,
10     SignedKeyList,
11     Address as tsAddress,
12     User as tsUser,
13 } from '../../interfaces';
14 import { getActiveKeys, getNormalizedActiveKeys, getReactivatedKeyFlag } from '../getActiveKeys';
15 import { getDecryptedAddressKeysHelper } from '../getDecryptedAddressKeys';
16 import type { OnSKLPublishSuccess } from '../signedKeyList';
17 import { getSignedKeyListWithDeferredPublish } from '../signedKeyList';
19 interface GetReactivatedAddressKeys {
20     address: tsAddress;
21     oldUserKeys: KeyPair[];
22     newUserKeys: KeyPair[];
23     user: tsUser;
24     keyPassword: string;
25     keyTransparencyVerify: KeyTransparencyVerify;
28 type GetReactivateAddressKeysReturnValue =
29     | {
30           address: tsAddress;
31           reactivatedKeys?: undefined;
32           signedKeyList?: undefined;
33           onSKLPublishSuccess?: undefined;
34       }
35     | {
36           address: tsAddress;
37           reactivatedKeys: DecryptedKey[];
38           signedKeyList: SignedKeyList;
39           onSKLPublishSuccess: OnSKLPublishSuccess;
40       };
42 export const getReactivatedAddressKeys = async ({
43     address,
44     user,
45     oldUserKeys,
46     newUserKeys,
47     keyPassword,
48     keyTransparencyVerify,
49 }: GetReactivatedAddressKeys): Promise<GetReactivateAddressKeysReturnValue> => {
50     const empty = {
51         address,
52         reactivatedKeys: undefined,
53         signedKeyList: undefined,
54     } as const;
56     const oldDecryptedAddressKeys = await getDecryptedAddressKeysHelper(address.Keys, user, oldUserKeys, keyPassword);
58     // All keys were able to decrypt previously, can just return.
59     if (oldDecryptedAddressKeys.length === address.Keys.length) {
60         return empty;
61     }
62     const newDecryptedAddressKeys = await getDecryptedAddressKeysHelper(address.Keys, user, newUserKeys, keyPassword);
64     // No difference in how many keys were able to get decrypted
65     if (oldDecryptedAddressKeys.length === newDecryptedAddressKeys.length) {
66         return empty;
67     }
68     if (newDecryptedAddressKeys.length < oldDecryptedAddressKeys.length) {
69         throw new Error('More old decryptable keys than new, should never happen');
70     }
72     // New keys were able to get decrypted
73     const oldDecryptedAddressKeysSet = new Set<string>(oldDecryptedAddressKeys.map(({ ID }) => ID));
74     const reactivatedKeys = newDecryptedAddressKeys.filter(({ ID }) => !oldDecryptedAddressKeysSet.has(ID));
75     const reactivatedKeysSet = new Set<string>(reactivatedKeys.map(({ ID }) => ID));
77     if (!reactivatedKeysSet.size) {
78         return empty;
79     }
81     const oldAddressKeysMap = new Map<string, Key>(address.Keys.map((Key) => [Key.ID, Key]));
82     const newActiveKeys = await getActiveKeys(address, address.SignedKeyList, address.Keys, newDecryptedAddressKeys);
83     const newActiveKeysFormatted = getNormalizedActiveKeys(
84         address,
85         newActiveKeys.map((activeKey) => {
86             if (!reactivatedKeysSet.has(activeKey.ID)) {
87                 return activeKey;
88             }
89             return {
90                 ...activeKey,
91                 flags: getReactivatedKeyFlag(address, oldAddressKeysMap.get(activeKey.ID)?.Flags),
92             };
93         })
94     );
95     const [signedKeyList, onSKLPublishSuccess] = await getSignedKeyListWithDeferredPublish(
96         newActiveKeysFormatted,
97         address,
98         keyTransparencyVerify
99     );
100     return {
101         address,
102         reactivatedKeys,
103         signedKeyList: signedKeyList,
104         onSKLPublishSuccess: onSKLPublishSuccess,
105     };
108 export const getAddressReactivationPayload = (results: GetReactivateAddressKeysReturnValue[]) => {
109     const AddressKeyFingerprints = unique(
110         results.reduce<KeyPair[]>((acc, { reactivatedKeys }) => {
111             if (!reactivatedKeys) {
112                 return acc;
113             }
114             return acc.concat(reactivatedKeys);
115         }, [])
116     ).map(({ privateKey }) => privateKey.getFingerprint());
118     const SignedKeyLists = results.reduce<{ [key: string]: SignedKeyList }>((acc, result) => {
119         if (!result.reactivatedKeys?.length || !result.signedKeyList) {
120             throw new Error('Missing reactivated keys');
121         }
122         acc[result.address.ID] = result.signedKeyList;
123         return acc;
124     }, {});
126     return {
127         AddressKeyFingerprints,
128         SignedKeyLists,
129     };
132 interface GetReactivatedAddressesKeys {
133     addresses: tsAddress[];
134     oldUserKeys: KeyPair[];
135     newUserKeys: KeyPair[];
136     user: tsUser;
137     keyPassword: string;
138     keyTransparencyVerify: KeyTransparencyVerify;
141 export const getReactivatedAddressesKeys = async ({
142     addresses,
143     oldUserKeys,
144     newUserKeys,
145     user,
146     keyPassword,
147     keyTransparencyVerify,
148 }: GetReactivatedAddressesKeys) => {
149     const promises = addresses.map((address) =>
150         getReactivatedAddressKeys({
151             address,
152             oldUserKeys,
153             newUserKeys,
154             user,
155             keyPassword,
156             keyTransparencyVerify,
157         })
158     );
159     const results = await Promise.all(promises);
160     return results.filter(({ reactivatedKeys }) => {
161         if (!reactivatedKeys) {
162             return false;
163         }
164         return reactivatedKeys.length > 0;
165     });
168 export const resetUserId = async (Key: Key, reactivatedKey: PrivateKeyReference) => {
169     // Before the new key format imposed after key migration, the address and user key were the same key.
170     // Users may have exported one of the two. Upon reactivation the fingerprint could match a user key
171     // to the corresponding address key or vice versa. For that reason, the userids are reset to the userids
172     // of the old key.
173     const inactiveKey = await CryptoProxy.importPublicKey({ armoredKey: Key.PrivateKey });
174     // Warning: This function mutates the target key.
175     await CryptoProxy.replaceUserIDs({ sourceKey: inactiveKey, targetKey: reactivatedKey });