Use same lock values as mobile clients
[ProtonMail-WebClient.git] / packages / shared / lib / calendar / serialize.ts
bloba14b1ac8f93364fa5807825abd4c643d93a75557
1 import type { PrivateKeyReference, PublicKeyReference, SessionKey } from '@proton/crypto';
3 import type { SimpleMap } from '../interfaces';
4 import type { VcalVeventComponent } from '../interfaces/calendar';
5 import { CALENDAR_CARD_TYPE } from './constants';
6 import {
7     createSessionKey,
8     encryptPart,
9     getEncryptedSessionKey,
10     getEncryptedSessionKeysMap,
11     signPart,
12 } from './crypto/encrypt';
13 import { formatData } from './formatData';
14 import { getVeventParts } from './veventHelper';
16 const { ENCRYPTED_AND_SIGNED, SIGNED, CLEAR_TEXT } = CALENDAR_CARD_TYPE;
18 /**
19  * Split the properties of the component into parts.
20  */
21 const getParts = (eventComponent: VcalVeventComponent) => {
22     return getVeventParts(eventComponent);
25 /**
26  * Create a calendar event by encrypting and serializing an internal vcal component.
27  */
28 interface CreateCalendarEventArguments {
29     eventComponent: VcalVeventComponent;
30     cancelledOccurrenceVevent?: VcalVeventComponent;
31     publicKey: PublicKeyReference;
32     privateKey: PrivateKeyReference;
33     sharedSessionKey?: SessionKey;
34     calendarSessionKey?: SessionKey;
35     isCreateEvent: boolean;
36     isSwitchCalendar: boolean;
37     hasDefaultNotifications: boolean;
38     isAttendee?: boolean;
39     removedAttendeesEmails?: string[];
40     addedAttendeesPublicKeysMap?: SimpleMap<PublicKeyReference>;
42 export const createCalendarEvent = async ({
43     eventComponent,
44     cancelledOccurrenceVevent,
45     publicKey,
46     privateKey,
47     sharedSessionKey: oldSharedSessionKey,
48     calendarSessionKey: oldCalendarSessionKey,
49     isCreateEvent,
50     isSwitchCalendar,
51     hasDefaultNotifications,
52     isAttendee,
53     removedAttendeesEmails = [],
54     addedAttendeesPublicKeysMap,
55 }: CreateCalendarEventArguments) => {
56     const { sharedPart, calendarPart, notificationsPart, attendeesPart } = getParts(eventComponent);
57     const cancelledOccurrenceSharedPart = cancelledOccurrenceVevent
58         ? getParts(cancelledOccurrenceVevent).sharedPart
59         : undefined;
61     const isCreateOrSwitchCalendar = isCreateEvent || isSwitchCalendar;
62     const isAttendeeSwitchingCalendar = isSwitchCalendar && isAttendee;
63     // If there is no encrypted calendar part, a calendar session key is not needed.
64     const shouldHaveCalendarKey = !!calendarPart[ENCRYPTED_AND_SIGNED];
66     const [calendarSessionKey, sharedSessionKey] = await Promise.all([
67         shouldHaveCalendarKey ? oldCalendarSessionKey || createSessionKey(publicKey) : undefined,
68         oldSharedSessionKey || createSessionKey(publicKey),
69     ]);
71     const [
72         encryptedCalendarSessionKey,
73         encryptedSharedSessionKey,
74         sharedSignedPart,
75         sharedEncryptedPart,
76         calendarSignedPart,
77         calendarEncryptedPart,
78         attendeesEncryptedPart,
79         attendeesEncryptedSessionKeysMap,
80         cancelledOccurrenceSignedPart,
81     ] = await Promise.all([
82         // If we're updating an event (but not switching calendar), no need to encrypt again the session keys
83         isCreateOrSwitchCalendar && calendarSessionKey
84             ? getEncryptedSessionKey(calendarSessionKey, publicKey)
85             : undefined,
86         isCreateOrSwitchCalendar ? getEncryptedSessionKey(sharedSessionKey, publicKey) : undefined,
87         // attendees are not allowed to change the SharedEventContent, so they shouldn't send it (API will complain otherwise)
88         isAttendeeSwitchingCalendar ? undefined : signPart(sharedPart[SIGNED], privateKey),
89         isAttendeeSwitchingCalendar
90             ? undefined
91             : encryptPart(sharedPart[ENCRYPTED_AND_SIGNED], privateKey, sharedSessionKey),
92         signPart(calendarPart[SIGNED], privateKey),
93         calendarSessionKey
94             ? encryptPart(calendarPart[ENCRYPTED_AND_SIGNED], privateKey, calendarSessionKey)
95             : undefined,
96         // attendees are not allowed to change the SharedEventContent, so they shouldn't send it (API will complain otherwise)
97         isAttendeeSwitchingCalendar
98             ? undefined
99             : encryptPart(attendeesPart[ENCRYPTED_AND_SIGNED], privateKey, sharedSessionKey),
100         getEncryptedSessionKeysMap(sharedSessionKey, addedAttendeesPublicKeysMap),
101         cancelledOccurrenceSharedPart ? signPart(cancelledOccurrenceSharedPart[SIGNED], privateKey) : undefined,
102     ]);
104     return formatData({
105         sharedSignedPart,
106         sharedEncryptedPart,
107         cancelledOccurrenceSignedPart,
108         sharedSessionKey: encryptedSharedSessionKey,
109         calendarSignedPart,
110         calendarEncryptedPart,
111         calendarSessionKey: encryptedCalendarSessionKey,
112         notificationsPart: hasDefaultNotifications ? undefined : notificationsPart,
113         colorPart: eventComponent.color?.value,
114         attendeesEncryptedPart,
115         attendeesClearPart: isAttendeeSwitchingCalendar ? undefined : attendeesPart[CLEAR_TEXT],
116         removedAttendeesEmails,
117         attendeesEncryptedSessionKeysMap,
118     });