1 import type { PrivateKeyReference } from '@proton/crypto';
2 import { CryptoProxy } from '@proton/crypto';
3 import { computeKeyPassword, generateKeySalt } from '@proton/srp';
5 import type { UpgradeAddressKeyPayload } from '../api/keys';
6 import { upgradeKeysRoute } from '../api/keys';
7 import { getOrganizationKeys } from '../api/organization';
8 import { USER_ROLES } from '../constants';
9 import { toMap } from '../helpers/object';
12 CachedOrganizationKey,
15 KeyMigrationKTVerifier,
16 KeyTransparencyVerify,
22 OrganizationKey as tsOrganizationKey,
24 } from '../interfaces';
25 import { srpVerify } from '../srp';
26 import { generateAddressKeyTokens, reformatAddressKey } from './addressKeys';
27 import { getDecryptedAddressKeysHelper } from './getDecryptedAddressKeys';
28 import { getCachedOrganizationKey } from './getDecryptedOrganizationKey';
29 import { getDecryptedUserKeysHelper } from './getDecryptedUserKeys';
30 import { getHasMigratedAddressKeys } from './keyMigration';
31 import { reformatOrganizationKey } from './organizationKeys';
32 import { createSignedKeyListForMigration } from './signedKeyList';
33 import { USER_KEY_USERID } from './userKeys';
35 export const getV2KeyToUpgrade = (Key: tsKey) => {
36 return Key.Version < 3;
39 export const getV2KeysToUpgrade = (Keys?: tsKey[]) => {
43 return Keys.filter(getV2KeyToUpgrade);
46 export const getHasV2KeysToUpgrade = (User: tsUser, Addresses: tsAddress[]) => {
48 getV2KeysToUpgrade(User.Keys).length > 0 ||
49 Addresses.some((Address) => getV2KeysToUpgrade(Address.Keys).length > 0)
53 const reEncryptOrReformatKey = async (privateKey: PrivateKeyReference, Key: Key, email: string, passphrase: string) => {
54 if (Key && getV2KeyToUpgrade(Key)) {
55 return reformatAddressKey({
61 const privateKeyArmored = await CryptoProxy.exportPrivateKey({ privateKey: privateKey, passphrase });
62 return { privateKey, privateKeyArmored };
65 const getReEncryptedKeys = (keys: DecryptedKey[], Keys: Key[], email: string, passphrase: string) => {
66 const keysMap = Keys.reduce<{ [key: string]: Key }>((acc, Key) => {
71 keys.map(async ({ privateKey, ID }) => {
72 const { privateKeyArmored } = await reEncryptOrReformatKey(privateKey, keysMap[ID], email, passphrase);
75 PrivateKey: privateKeyArmored,
81 const getReformattedAddressKeysV2 = (
85 userKey: PrivateKeyReference
87 const keysMap = Keys.reduce<{ [key: string]: Key }>((acc, Key) => {
92 keys.map(async ({ ID, privateKey: originalPrivateKey }) => {
93 const { token, encryptedToken, signature } = await generateAddressKeyTokens(userKey);
94 const { privateKey, privateKeyArmored } = await reEncryptOrReformatKey(
100 const publicKey = await CryptoProxy.importPublicKey({
101 binaryKey: await CryptoProxy.exportPublicKey({ key: privateKey, format: 'binary' }),
111 PrivateKey: privateKeyArmored,
112 Token: encryptedToken,
113 Signature: signature,
120 interface UpgradeV2KeysLegacyArgs {
121 loginPassword: string;
122 clearKeyPassword: string;
124 isOnePasswordMode?: boolean;
126 userKeys: DecryptedKey[];
127 organizationKey?: CachedOrganizationKey;
130 keys: DecryptedKey[];
134 export const upgradeV2KeysLegacy = async ({
143 }: UpgradeV2KeysLegacyArgs) => {
144 const keySalt = generateKeySalt();
145 const newKeyPassword = await computeKeyPassword(clearKeyPassword, keySalt);
147 const [reformattedUserKeys, reformattedAddressesKeys, reformattedOrganizationKey] = await Promise.all([
148 getReEncryptedKeys(userKeys, user.Keys, USER_KEY_USERID, newKeyPassword),
150 addressesKeys.map(({ address, keys }) => {
151 return getReEncryptedKeys(keys, address.Keys, address.Email, newKeyPassword);
154 organizationKey?.privateKey ? reformatOrganizationKey(organizationKey.privateKey, newKeyPassword) : undefined,
157 const reformattedKeys = [...reformattedUserKeys, ...reformattedAddressesKeys.flat()];
159 const config = upgradeKeysRoute({
161 Keys: reformattedKeys,
162 OrganizationKey: reformattedOrganizationKey?.privateKeyArmored,
165 if (isOnePasswordMode) {
168 credentials: { password: loginPassword },
171 return newKeyPassword;
175 return newKeyPassword;
178 interface UpgradeV2KeysArgs extends UpgradeV2KeysLegacyArgs {
179 keyTransparencyVerify: KeyTransparencyVerify;
180 keyMigrationKTVerifier: KeyMigrationKTVerifier;
183 export const upgradeV2KeysV2 = async ({
192 keyTransparencyVerify,
193 keyMigrationKTVerifier,
194 }: UpgradeV2KeysArgs) => {
195 if (!userKeys.length) {
198 const keySalt = generateKeySalt();
199 const newKeyPassword: string = await computeKeyPassword(clearKeyPassword, keySalt);
200 const [reformattedUserKeys, reformattedOrganizationKey] = await Promise.all([
201 getReEncryptedKeys(userKeys, user.Keys, USER_KEY_USERID, newKeyPassword),
202 organizationKey?.privateKey ? reformatOrganizationKey(organizationKey.privateKey, newKeyPassword) : undefined,
205 const primaryUserKey = await CryptoProxy.importPrivateKey({
206 armoredKey: reformattedUserKeys[0].PrivateKey,
207 passphrase: newKeyPassword,
210 const reformattedAddressesKeys = await Promise.all(
211 addressesKeys.map(async ({ address, keys }) => {
212 const reformattedAddressKeys = await getReformattedAddressKeysV2(
218 const [decryptedKeys, addressKeys] = reformattedAddressKeys.reduce<
219 [DecryptedKey[], UpgradeAddressKeyPayload[]]
222 acc[0].push(cur.decryptedKey);
223 acc[1].push(cur.Key);
228 const [signedKeyList, onSKLPublishSuccess] = await createSignedKeyListForMigration({
232 keyTransparencyVerify,
233 keyMigrationKTVerifier,
238 signedKeyList: signedKeyList,
239 onSKLPublishSuccess: onSKLPublishSuccess,
244 const AddressKeys = reformattedAddressesKeys.map(({ addressKeys }) => addressKeys).flat();
245 const SignedKeyLists = reformattedAddressesKeys.reduce<{ [id: string]: SignedKeyList }>(
246 (acc, { address, signedKeyList }) => {
248 acc[address.ID] = signedKeyList;
255 const config = upgradeKeysRoute({
257 UserKeys: reformattedUserKeys,
259 OrganizationKey: reformattedOrganizationKey?.privateKeyArmored,
264 reformattedAddressesKeys.map(({ onSKLPublishSuccess }) =>
265 onSKLPublishSuccess ? onSKLPublishSuccess() : Promise.resolve()
269 if (isOnePasswordMode) {
272 credentials: { password: loginPassword },
275 return newKeyPassword;
279 return newKeyPassword;
282 interface UpgradeV2KeysHelperArgs {
283 addresses: tsAddress[];
285 loginPassword: string;
286 clearKeyPassword: string;
289 isOnePasswordMode?: boolean;
290 preAuthKTVerify: PreAuthKTVerify;
291 keyMigrationKTVerifier: KeyMigrationKTVerifier;
294 export const upgradeV2KeysHelper = async ({
303 keyMigrationKTVerifier,
304 }: UpgradeV2KeysHelperArgs) => {
305 const userKeys = await getDecryptedUserKeysHelper(user, keyPassword);
307 const addressesKeys = await Promise.all(
308 addresses.map(async (address) => {
311 keys: await getDecryptedAddressKeysHelper(address.Keys, user, userKeys, keyPassword),
316 const organizationKey =
317 user.Role === USER_ROLES.ADMIN_ROLE
318 ? await api<tsOrganizationKey>(getOrganizationKeys()).then((Key) => {
319 return getCachedOrganizationKey({ keyPassword, Key, userKeys });
323 if (!clearKeyPassword || !loginPassword) {
324 throw new Error('Password required');
326 // Not allowed signed into member
327 if (user.OrganizationPrivateKey) {
331 const userKeyMap = toMap(user.Keys, 'ID');
332 const hasDecryptedUserKeysToUpgrade = userKeys.some(({ privateKey, ID }) => {
333 const Key = userKeyMap[ID];
334 return Key && privateKey && getV2KeyToUpgrade(Key);
336 const hasDecryptedAddressKeyToUpgrade = addressesKeys.some(({ address, keys }) => {
337 const addressKeyMap = toMap(address.Keys, 'ID');
338 return keys.some(({ privateKey, ID }) => {
339 const Key = addressKeyMap[ID];
340 return Key && privateKey && getV2KeyToUpgrade(Key);
344 if (!hasDecryptedUserKeysToUpgrade && !hasDecryptedAddressKeyToUpgrade) {
348 if (getHasMigratedAddressKeys(addresses)) {
349 const keyTransparencyVerify = preAuthKTVerify(userKeys);
351 return upgradeV2KeysV2({
360 keyTransparencyVerify,
361 keyMigrationKTVerifier,
365 return upgradeV2KeysLegacy({