diff --git a/web/push-notif/encrypted-notif-utils-api.js b/web/push-notif/encrypted-notif-utils-api.js --- a/web/push-notif/encrypted-notif-utils-api.js +++ b/web/push-notif/encrypted-notif-utils-api.js @@ -2,6 +2,8 @@ import type { EncryptedNotifUtilsAPI } from 'lib/types/notif-types.js'; +import { encryptNotification } from './notif-crypto-utils.js'; + const encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI = { encryptSerializedNotifPayload: async ( cryptoID: string, @@ -11,16 +13,14 @@ type: '1' | '0', ) => boolean, ) => { - // The "mock" implementation below will be replaced with proper - // implementation after olm notif sessions initialization is - // implemented. for now it is actually beneficial to return - // original string as encrypted string since it allows for - // better testing as we can verify which data are encrypted - // and which aren't. + const { body, type } = await encryptNotification( + unencryptedPayload, + cryptoID, + ); return { - encryptedData: { body: unencryptedPayload, type: 1 }, + encryptedData: { body, type }, sizeLimitViolated: encryptedPayloadSizeValidator - ? !encryptedPayloadSizeValidator(unencryptedPayload, '1') + ? !encryptedPayloadSizeValidator(body, type ? '1' : '0') : false, }; }, diff --git a/web/push-notif/notif-crypto-utils.js b/web/push-notif/notif-crypto-utils.js --- a/web/push-notif/notif-crypto-utils.js +++ b/web/push-notif/notif-crypto-utils.js @@ -1,6 +1,7 @@ // @flow import olm from '@commapp/olm'; +import type { EncryptResult } from '@commapp/olm'; import invariant from 'invariant'; import localforage from 'localforage'; @@ -13,6 +14,7 @@ EncryptedWebNotification, } from 'lib/types/notif-types.js'; import { getCookieIDFromCookie } from 'lib/utils/cookie-utils.js'; +import { getMessageForException } from 'lib/utils/errors.js'; import { type EncryptedData, @@ -309,6 +311,87 @@ } } +async function encryptNotification( + payload: string, + deviceID: string, +): Promise { + const olmDataContentKey = getOlmDataContentKeyForDeviceID(deviceID); + const olmEncryptionKeyDBLabel = + getOlmEncryptionKeyDBLabelForDeviceID(deviceID); + + let encryptedOlmData, encryptionKey; + try { + [encryptedOlmData, encryptionKey] = await Promise.all([ + localforage.getItem(olmDataContentKey), + retrieveEncryptionKey(olmEncryptionKeyDBLabel), + initOlm(), + ]); + } catch (e) { + throw new Error( + `Failed to fetch olm session from IndexedDB for device: ${deviceID}. Details: ${ + getMessageForException(e) ?? '' + }`, + ); + } + + if (!encryptionKey || !encryptedOlmData) { + throw new Error(`Session with device: ${deviceID} not initialized.`); + } + + let encryptedNotification; + try { + encryptedNotification = await encryptNotificationWithOlmSession( + payload, + encryptedOlmData, + olmDataContentKey, + encryptionKey, + ); + } catch (e) { + throw new Error( + `Failed encrypt notification for device: ${deviceID}. Details: ${ + getMessageForException(e) ?? '' + }`, + ); + } + return encryptedNotification; +} + +async function encryptNotificationWithOlmSession( + payload: string, + encryptedOlmData: EncryptedData, + olmDataContentKey: string, + encryptionKey: CryptoKey, +): Promise { + const serializedOlmData = await decryptData(encryptedOlmData, encryptionKey); + const { + mainSession, + picklingKey, + pendingSessionUpdate, + updateCreationTimestamp, + }: NotificationsOlmDataType = JSON.parse( + new TextDecoder().decode(serializedOlmData), + ); + + const session = new olm.Session(); + session.unpickle(picklingKey, pendingSessionUpdate); + const encryptedNotification = session.encrypt(payload); + + const newPendingSessionUpdate = session.pickle(picklingKey); + const updatedOlmData: NotificationsOlmDataType = { + mainSession, + pendingSessionUpdate: newPendingSessionUpdate, + picklingKey, + updateCreationTimestamp, + }; + const updatedEncryptedSession = await encryptData( + new TextEncoder().encode(JSON.stringify(updatedOlmData)), + encryptionKey, + ); + + await localforage.setItem(olmDataContentKey, updatedEncryptedSession); + return encryptedNotification; +} + async function retrieveEncryptionKey( encryptionKeyDBLabel: string, ): Promise { @@ -557,6 +640,7 @@ export { decryptWebNotification, decryptDesktopNotification, + encryptNotification, getOlmDataContentKeyForCookie, getOlmEncryptionKeyDBLabelForCookie, getOlmDataContentKeyForDeviceID,