Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / shared / lib / keys / memberKeys.ts
blob2f180562bbd3978c9a705c6994d93faff692eeb8
1 import type { PrivateKeyReference } from '@proton/crypto';
2 import { CryptoProxy } from '@proton/crypto';
3 import { getDecryptedAddressKeys } from '@proton/shared/lib/keys/getDecryptedAddressKeys';
4 import { getDecryptedUserKeys } from '@proton/shared/lib/keys/getDecryptedUserKeys';
5 import { getPrimaryKey } from '@proton/shared/lib/keys/getPrimaryKey';
6 import { getDefaultKeyFlags } from '@proton/shared/lib/keys/keyFlags';
7 import isTruthy from '@proton/utils/isTruthy';
9 import { createMemberKeyRoute, setupMemberKeyRoute } from '../api/memberKeys';
10 import { MEMBER_PRIVATE } from '../constants';
11 import type {
12     Api,
13     CachedOrganizationKey,
14     DecryptedKey,
15     KeyGenConfig,
16     KeyPair,
17     KeyTransparencyVerify,
18     UserModel,
19     Address as tsAddress,
20     Key as tsKey,
21     Member as tsMember,
22 } from '../interfaces';
23 import { srpVerify } from '../srp';
24 import { generateAddressKey, generateAddressKeyTokens } from './addressKeys';
25 import { getActiveKeyObject, getActiveKeys, getNormalizedActiveKeys, getPrimaryFlag } from './getActiveKeys';
26 import { generateKeySaltAndPassphrase } from './keys';
27 import { decryptMemberToken, encryptMemberToken, generateMemberToken } from './memberToken';
28 import { generateMemberAddressKey } from './organizationKeys';
29 import { getSignedKeyListWithDeferredPublish } from './signedKeyList';
30 import { generateUserKey } from './userKeys';
32 export const getDecryptedMemberKey = async (
33     { Token, PrivateKey }: tsKey,
34     organizationKey: PrivateKeyReference
35 ): Promise<PrivateKeyReference> => {
36     if (!Token) {
37         throw new Error('Member token invalid');
38     }
39     const decryptedToken = await decryptMemberToken(Token, [organizationKey], [organizationKey]);
40     return CryptoProxy.importPrivateKey({ armoredKey: PrivateKey, passphrase: decryptedToken });
43 interface SetupMemberKeySharedArguments {
44     api: Api;
45     member: tsMember;
46     memberAddresses: tsAddress[];
47     password: string;
48     organizationKey: PrivateKeyReference;
49     keyGenConfig: KeyGenConfig;
50     keyTransparencyVerify: KeyTransparencyVerify;
53 export const setupMemberKeyV2 = async ({
54     api,
55     member,
56     memberAddresses,
57     password,
58     organizationKey,
59     keyGenConfig,
60     keyTransparencyVerify,
61 }: SetupMemberKeySharedArguments) => {
62     const { salt: keySalt, passphrase: memberKeyPassword } = await generateKeySaltAndPassphrase(password);
64     const { privateKey: userPrivateKey, privateKeyArmored: userPrivateKeyArmored } = await generateUserKey({
65         passphrase: memberKeyPassword,
66         keyGenConfig,
67     });
68     const memberKeyToken = generateMemberToken();
69     const privateKeyArmoredOrganization = await CryptoProxy.exportPrivateKey({
70         privateKey: userPrivateKey,
71         passphrase: memberKeyToken,
72     });
73     const organizationToken = await encryptMemberToken(memberKeyToken, organizationKey);
75     const AddressKeysWithOnSKLPublish = await Promise.all(
76         memberAddresses.map(async (address) => {
77             const { token, signature, organizationSignature, encryptedToken } = await generateAddressKeyTokens(
78                 userPrivateKey,
79                 organizationKey
80             );
82             const { privateKey: addressPrivateKey, privateKeyArmored: addressPrivateKeyArmored } =
83                 await generateAddressKey({
84                     email: address.Email,
85                     passphrase: token,
86                     keyGenConfig,
87                 });
89             const newActiveKey = await getActiveKeyObject(addressPrivateKey, {
90                 ID: 'tmp',
91                 primary: 1,
92                 flags: getDefaultKeyFlags(address),
93             });
94             const updatedActiveKeys = getNormalizedActiveKeys(address, [newActiveKey]);
95             const [SignedKeyList, onSKLPublishSuccess] = await getSignedKeyListWithDeferredPublish(
96                 updatedActiveKeys,
97                 address,
98                 keyTransparencyVerify
99             );
101             return {
102                 addressKey: {
103                     AddressID: address.ID,
104                     SignedKeyList,
105                     PrivateKey: addressPrivateKeyArmored,
106                     Token: encryptedToken,
107                     Signature: signature,
108                     OrgSignature: organizationSignature,
109                 },
110                 onSKLPublishSuccess,
111             };
112         })
113     );
114     const AddressKeys = AddressKeysWithOnSKLPublish.map(({ addressKey }) => addressKey);
116     const { Member } = await srpVerify<{ Member: tsMember }>({
117         api,
118         credentials: { password },
119         config: setupMemberKeyRoute({
120             MemberID: member.ID,
121             AddressKeys,
122             UserKey: {
123                 PrivateKey: userPrivateKeyArmored,
124                 OrgPrivateKey: privateKeyArmoredOrganization,
125                 OrgToken: organizationToken,
126             },
127             KeySalt: keySalt,
128         }),
129     });
131     await Promise.all(
132         AddressKeysWithOnSKLPublish.map(({ onSKLPublishSuccess }) =>
133             onSKLPublishSuccess ? onSKLPublishSuccess() : Promise.resolve()
134         )
135     );
137     return { Member, userPrivateKey };
140 export const getCanGenerateMemberKeys = (member: tsMember | undefined) => {
141     return member?.Private === MEMBER_PRIVATE.READABLE && !member.SSO;
144 export const getShouldSetupMemberKeys = (member: tsMember | undefined) => {
145     return !member?.Self && member?.Keys.length === 0 && getCanGenerateMemberKeys(member);
148 export const getCanGenerateMemberKeysPermissions = (
149     user: UserModel,
150     organizationKey: CachedOrganizationKey | undefined
151 ) => {
152     return !!organizationKey?.privateKey && user.isAdmin && !user.isSubUser;
155 export const getCanGenerateMemberAddressKeys = ({
156     user,
157     member,
158     organizationKey,
159     address,
160 }: {
161     user: UserModel;
162     member: tsMember | undefined;
163     organizationKey: CachedOrganizationKey | undefined;
164     address: tsAddress;
165 }) => {
166     /*
167      * Keys can be generated if the organisation key is decrypted, and you are an admin,
168      * and the member is readable, you're not an admin signed in to a readable member.
169      */
170     return (
171         getCanGenerateMemberKeysPermissions(user, organizationKey) &&
172         getCanGenerateMemberKeys(member) &&
173         !address.HasKeys
174     );
177 interface SetupMemberKeyArguments extends SetupMemberKeySharedArguments {
178     ownerAddresses: tsAddress[];
181 export const setupMemberKeys = async ({ ownerAddresses, ...rest }: SetupMemberKeyArguments) => {
182     return setupMemberKeyV2(rest);
185 interface CreateMemberAddressKeysLegacyArguments {
186     api: Api;
187     member: tsMember;
188     memberAddress: tsAddress;
189     memberAddressKeys: DecryptedKey[];
190     memberUserKey: PrivateKeyReference;
191     organizationKey: PrivateKeyReference;
192     keyGenConfig: KeyGenConfig;
193     keyTransparencyVerify: KeyTransparencyVerify;
196 export const createMemberAddressKeysLegacy = async ({
197     api,
198     member,
199     memberAddress,
200     memberAddressKeys,
201     memberUserKey,
202     organizationKey,
203     keyGenConfig,
204     keyTransparencyVerify,
205 }: CreateMemberAddressKeysLegacyArguments) => {
206     const { privateKey, activationToken, privateKeyArmored, privateKeyArmoredOrganization, organizationToken } =
207         await generateMemberAddressKey({
208             email: memberAddress.Email,
209             primaryKey: memberUserKey,
210             organizationKey,
211             keyGenConfig,
212         });
214     const activeKeys = await getActiveKeys(
215         memberAddress,
216         memberAddress.SignedKeyList,
217         memberAddress.Keys,
218         memberAddressKeys
219     );
220     const newActiveKey = await getActiveKeyObject(privateKey, {
221         ID: 'tmp',
222         primary: getPrimaryFlag(activeKeys),
223         flags: getDefaultKeyFlags(memberAddress),
224     });
225     const updatedActiveKeys = getNormalizedActiveKeys(memberAddress, [...activeKeys, newActiveKey]);
226     const [SignedKeyList, onSKLPublishSuccess] = await getSignedKeyListWithDeferredPublish(
227         updatedActiveKeys,
228         memberAddress,
229         keyTransparencyVerify
230     );
232     const { primary } = newActiveKey;
234     const { MemberKey } = await api(
235         createMemberKeyRoute({
236             MemberID: member.ID,
237             AddressID: memberAddress.ID,
238             Activation: activationToken,
239             UserKey: privateKeyArmored,
240             Token: organizationToken,
241             MemberKey: privateKeyArmoredOrganization,
242             Primary: primary,
243             SignedKeyList,
244         })
245     );
247     await onSKLPublishSuccess();
249     newActiveKey.ID = MemberKey.ID;
251     return updatedActiveKeys;
254 interface CreateMemberAddressKeysV2Arguments {
255     api: Api;
256     member: tsMember;
257     memberAddress: tsAddress;
258     memberAddressKeys: DecryptedKey[];
259     memberUserKey: PrivateKeyReference;
260     organizationKey: PrivateKeyReference;
261     keyGenConfig: KeyGenConfig;
262     keyTransparencyVerify: KeyTransparencyVerify;
265 export const createMemberAddressKeysV2 = async ({
266     api,
267     member,
268     memberAddress,
269     memberAddressKeys,
270     memberUserKey,
271     organizationKey,
272     keyGenConfig,
273     keyTransparencyVerify,
274 }: CreateMemberAddressKeysV2Arguments) => {
275     const { token, signature, organizationSignature, encryptedToken } = await generateAddressKeyTokens(
276         memberUserKey,
277         organizationKey
278     );
280     const { privateKey: addressPrivateKey, privateKeyArmored: addressPrivateKeyArmored } = await generateAddressKey({
281         email: memberAddress.Email,
282         passphrase: token,
283         keyGenConfig,
284     });
286     const activeKeys = await getActiveKeys(
287         memberAddress,
288         memberAddress.SignedKeyList,
289         memberAddress.Keys,
290         memberAddressKeys
291     );
292     const newActiveKey = await getActiveKeyObject(addressPrivateKey, {
293         ID: 'tmp',
294         primary: getPrimaryFlag(activeKeys),
295         flags: getDefaultKeyFlags(memberAddress),
296     });
297     const updatedActiveKeys = getNormalizedActiveKeys(memberAddress, [...activeKeys, newActiveKey]);
298     const [SignedKeyList, onSKLPublishSuccess] = await getSignedKeyListWithDeferredPublish(
299         updatedActiveKeys,
300         memberAddress,
301         keyTransparencyVerify
302     );
304     const { primary } = newActiveKey;
306     const { MemberKey } = await api(
307         createMemberKeyRoute({
308             MemberID: member.ID,
309             AddressID: memberAddress.ID,
310             PrivateKey: addressPrivateKeyArmored,
311             Token: encryptedToken,
312             Signature: signature,
313             OrgSignature: organizationSignature,
314             Primary: primary,
315             SignedKeyList,
316         })
317     );
319     await onSKLPublishSuccess();
321     newActiveKey.ID = MemberKey.ID;
323     return updatedActiveKeys;
326 export const getMemberKeys = async ({
327     member,
328     memberAddresses,
329     organizationKey,
330 }: {
331     member: Pick<tsMember, 'Keys'>;
332     memberAddresses: tsAddress[];
333     organizationKey: KeyPair;
334 }) => {
335     const memberUserKeys = await getDecryptedUserKeys(member.Keys, '', organizationKey);
336     const memberUserKeyPrimary = getPrimaryKey(memberUserKeys)?.privateKey;
337     if (!memberUserKeyPrimary) {
338         throw new Error('Not able to decrypt the primary member user key');
339     }
341     const memberAddressesKeys = (
342         await Promise.all(
343             memberAddresses.map(async (address) => {
344                 const result = {
345                     address,
346                     keys: await getDecryptedAddressKeys(address.Keys, memberUserKeys, '', organizationKey),
347                 };
348                 // Some non-private members don't have keys generated
349                 if (!result.keys.length) {
350                     return;
351                 }
352                 return result;
353             })
354         )
355     ).filter(isTruthy);
357     return {
358         memberUserKeyPrimary,
359         memberUserKeys,
360         memberAddressesKeys,
361     };