Page MenuHomePhabricator

D12671.id42937.diff
No OneTemporary

D12671.id42937.diff

diff --git a/lib/types/crypto-types.js b/lib/types/crypto-types.js
--- a/lib/types/crypto-types.js
+++ b/lib/types/crypto-types.js
@@ -151,6 +151,10 @@
deviceID: string,
messageID: string,
) => Promise<EncryptedData>,
+ +encryptNotification: (
+ payload: string,
+ deviceID: string,
+ ) => Promise<EncryptedData>,
+decrypt: (encryptedData: EncryptedData, deviceID: string) => Promise<string>,
+decryptAndPersist: (
encryptedData: EncryptedData,
diff --git a/lib/utils/__mocks__/config.js b/lib/utils/__mocks__/config.js
--- a/lib/utils/__mocks__/config.js
+++ b/lib/utils/__mocks__/config.js
@@ -17,6 +17,7 @@
getUserPublicKey: jest.fn(),
encrypt: jest.fn(),
encryptAndPersist: jest.fn(),
+ encryptNotification: jest.fn(),
decrypt: jest.fn(),
decryptAndPersist: jest.fn(),
contentInboundSessionCreator: jest.fn(),
diff --git a/native/crypto/olm-api.js b/native/crypto/olm-api.js
--- a/native/crypto/olm-api.js
+++ b/native/crypto/olm-api.js
@@ -21,6 +21,7 @@
getUserPublicKey: commCoreModule.getUserPublicKey,
encrypt: commCoreModule.encrypt,
encryptAndPersist: commCoreModule.encryptAndPersist,
+ encryptNotification: commCoreModule.encryptNotification,
decrypt: commCoreModule.decrypt,
decryptAndPersist: commCoreModule.decryptAndPersist,
async contentInboundSessionCreator(
diff --git a/native/push/encrypted-notif-utils-api.js b/native/push/encrypted-notif-utils-api.js
--- a/native/push/encrypted-notif-utils-api.js
+++ b/native/push/encrypted-notif-utils-api.js
@@ -1,8 +1,9 @@
// @flow
import type { EncryptedNotifUtilsAPI } from 'lib/types/notif-types.js';
+import { getConfig } from 'lib/utils/config.js';
-import { commUtilsModule, commCoreModule } from '../native-modules.js';
+import { commUtilsModule } from '../native-modules.js';
const encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI = {
encryptSerializedNotifPayload: async (
@@ -13,8 +14,12 @@
type: '1' | '0',
) => boolean,
) => {
- const { message: body, messageType: type } =
- await commCoreModule.encryptNotification(unencryptedPayload, cryptoID);
+ const { initializeCryptoAccount, encryptNotification } = getConfig().olmAPI;
+ await initializeCryptoAccount();
+ const { message: body, messageType: type } = await encryptNotification(
+ unencryptedPayload,
+ cryptoID,
+ );
return {
encryptedData: { body, type },
sizeLimitViolated: encryptedPayloadSizeValidator
diff --git a/web/crypto/olm-api.js b/web/crypto/olm-api.js
--- a/web/crypto/olm-api.js
+++ b/web/crypto/olm-api.js
@@ -46,6 +46,7 @@
getUserPublicKey: proxyToWorker('getUserPublicKey'),
encrypt: proxyToWorker('encrypt'),
encryptAndPersist: proxyToWorker('encryptAndPersist'),
+ encryptNotification: proxyToWorker('encryptNotification'),
decrypt: proxyToWorker('decrypt'),
decryptAndPersist: proxyToWorker('decryptAndPersist'),
contentInboundSessionCreator: proxyToWorker('contentInboundSessionCreator'),
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
@@ -1,6 +1,7 @@
// @flow
import type { EncryptedNotifUtilsAPI } from 'lib/types/notif-types.js';
+import { getConfig } from 'lib/utils/config.js';
const encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI = {
encryptSerializedNotifPayload: async (
@@ -11,16 +12,16 @@
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 { initializeCryptoAccount, encryptNotification } = getConfig().olmAPI;
+ await initializeCryptoAccount();
+ const { message: body, messageType: 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,
@@ -86,9 +88,9 @@
displayErrorMessage: staffCanSee,
};
}
- const { olmDataContentKey, encryptionKeyDBKey } = olmDBKeys;
+ const { olmDataKey, encryptionKeyDBKey } = olmDBKeys;
const [encryptedOlmData, encryptionKey] = await Promise.all([
- localforage.getItem<EncryptedData>(olmDataContentKey),
+ localforage.getItem<EncryptedData>(olmDataKey),
retrieveEncryptionKey(encryptionKeyDBKey),
]);
@@ -105,7 +107,7 @@
const decryptedNotification = await commonDecrypt<PlainTextWebNotification>(
encryptedOlmData,
- olmDataContentKey,
+ olmDataKey,
encryptionKey,
encryptedPayload,
);
@@ -132,15 +134,15 @@
staffCanSee: boolean,
keyserverID?: string,
): Promise<{ +[string]: mixed }> {
- let encryptedOlmData, encryptionKey, olmDataContentKey;
+ let encryptedOlmData, encryptionKey, olmDataKey;
try {
- const { olmDataContentKey: olmDataContentKeyValue, encryptionKeyDBKey } =
+ const { olmDataKey: olmDataKeyValue, encryptionKeyDBKey } =
await getNotifsOlmSessionDBKeys(keyserverID);
- olmDataContentKey = olmDataContentKeyValue;
+ olmDataKey = olmDataKeyValue;
[encryptedOlmData, encryptionKey] = await Promise.all([
- localforage.getItem<EncryptedData>(olmDataContentKey),
+ localforage.getItem<EncryptedData>(olmDataKey),
retrieveEncryptionKey(encryptionKeyDBKey),
initOlm(),
]);
@@ -162,7 +164,7 @@
try {
decryptedNotification = await commonDecrypt<{ +[string]: mixed }>(
encryptedOlmData,
- olmDataContentKey,
+ olmDataKey,
encryptionKey,
encryptedPayload,
);
@@ -197,7 +199,7 @@
async function commonDecrypt<T>(
encryptedOlmData: EncryptedData,
- olmDataContentKey: string,
+ olmDataKey: string,
encryptionKey: CryptoKey,
encryptedPayload: string,
): Promise<T> {
@@ -257,7 +259,7 @@
encryptionKey,
);
- await localforage.setItem(olmDataContentKey, updatedEncryptedSession);
+ await localforage.setItem(olmDataKey, updatedEncryptedSession);
return decryptedNotification;
}
@@ -309,6 +311,87 @@
}
}
+async function encryptNotification(
+ payload: string,
+ deviceID: string,
+): Promise<EncryptResult> {
+ const olmDataKey = getOlmDataKeyForDeviceID(deviceID);
+ const olmEncryptionKeyDBLabel =
+ getOlmEncryptionKeyDBLabelForDeviceID(deviceID);
+
+ let encryptedOlmData, encryptionKey;
+ try {
+ [encryptedOlmData, encryptionKey] = await Promise.all([
+ localforage.getItem<EncryptedData>(olmDataKey),
+ 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,
+ olmDataKey,
+ encryptionKey,
+ );
+ } catch (e) {
+ throw new Error(
+ `Failed encrypt notification for device: ${deviceID}. Details: ${
+ getMessageForException(e) ?? ''
+ }`,
+ );
+ }
+ return encryptedNotification;
+}
+
+async function encryptNotificationWithOlmSession(
+ payload: string,
+ encryptedOlmData: EncryptedData,
+ olmDataKey: string,
+ encryptionKey: CryptoKey,
+): Promise<EncryptResult> {
+ 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(olmDataKey, updatedEncryptedSession);
+ return encryptedNotification;
+}
+
async function retrieveEncryptionKey(
encryptionKeyDBLabel: string,
): Promise<?CryptoKey> {
@@ -326,10 +409,10 @@
}
async function getNotifsOlmSessionDBKeys(keyserverID?: string): Promise<{
- +olmDataContentKey: string,
+ +olmDataKey: string,
+encryptionKeyDBKey: string,
}> {
- const olmDataContentKeyForKeyserverPrefix = getOlmDataContentKeyForCookie(
+ const olmDataKeyForKeyserverPrefix = getOlmDataKeyForCookie(
undefined,
keyserverID,
);
@@ -338,8 +421,8 @@
getOlmEncryptionKeyDBLabelForCookie(undefined, keyserverID);
const dbKeys = await localforage.keys();
- const olmDataContentKeys = sortOlmDBKeysArray(
- dbKeys.filter(key => key.startsWith(olmDataContentKeyForKeyserverPrefix)),
+ const olmDataKeys = sortOlmDBKeysArray(
+ dbKeys.filter(key => key.startsWith(olmDataKeyForKeyserverPrefix)),
);
const encryptionKeyDBLabels = sortOlmDBKeysArray(
dbKeys.filter(key =>
@@ -347,38 +430,36 @@
),
);
- if (olmDataContentKeys.length === 0 || encryptionKeyDBLabels.length === 0) {
+ if (olmDataKeys.length === 0 || encryptionKeyDBLabels.length === 0) {
throw new Error(
'Received encrypted notification but olm session was not created',
);
}
- const latestDataContentKey =
- olmDataContentKeys[olmDataContentKeys.length - 1];
+ const latestDataKey = olmDataKeys[olmDataKeys.length - 1];
const latestEncryptionKeyDBKey =
encryptionKeyDBLabels[encryptionKeyDBLabels.length - 1];
- const latestDataContentCookieID =
- getCookieIDFromOlmDBKey(latestDataContentKey);
+ const latestDataCookieID = getCookieIDFromOlmDBKey(latestDataKey);
const latestEncryptionKeyCookieID = getCookieIDFromOlmDBKey(
latestEncryptionKeyDBKey,
);
- if (latestDataContentCookieID !== latestEncryptionKeyCookieID) {
+ if (latestDataCookieID !== latestEncryptionKeyCookieID) {
throw new Error(
'Olm sessions and their encryption keys out of sync. Latest cookie ' +
- `id for olm sessions ${latestDataContentCookieID}. Latest cookie ` +
+ `id for olm sessions ${latestDataCookieID}. Latest cookie ` +
`id for olm session encryption keys ${latestEncryptionKeyCookieID}`,
);
}
const olmDBKeys = {
- olmDataContentKey: latestDataContentKey,
+ olmDataKey: latestDataKey,
encryptionKeyDBKey: latestEncryptionKeyDBKey,
};
const keysToDelete: $ReadOnlyArray<string> = [
- ...olmDataContentKeys.slice(0, olmDataContentKeys.length - 1),
+ ...olmDataKeys.slice(0, olmDataKeys.length - 1),
...encryptionKeyDBLabels.slice(0, encryptionKeyDBLabels.length - 1),
];
@@ -386,29 +467,26 @@
return olmDBKeys;
}
-function getOlmDataContentKeyForCookie(
- cookie: ?string,
- keyserverID?: string,
-): string {
- let olmDataContentKeyBase;
+function getOlmDataKeyForCookie(cookie: ?string, keyserverID?: string): string {
+ let olmDataKeyBase;
if (keyserverID) {
- olmDataContentKeyBase = [
+ olmDataKeyBase = [
INDEXED_DB_KEYSERVER_PREFIX,
keyserverID,
NOTIFICATIONS_OLM_DATA_CONTENT,
].join(INDEXED_DB_KEY_SEPARATOR);
} else {
- olmDataContentKeyBase = NOTIFICATIONS_OLM_DATA_CONTENT;
+ olmDataKeyBase = NOTIFICATIONS_OLM_DATA_CONTENT;
}
if (!cookie) {
- return olmDataContentKeyBase;
+ return olmDataKeyBase;
}
const cookieID = getCookieIDFromCookie(cookie);
- return [olmDataContentKeyBase, cookieID].join(INDEXED_DB_KEY_SEPARATOR);
+ return [olmDataKeyBase, cookieID].join(INDEXED_DB_KEY_SEPARATOR);
}
-function getOlmDataContentKeyForDeviceID(deviceID: string): string {
+function getOlmDataKeyForDeviceID(deviceID: string): string {
return [
INDEXED_DB_DEVICE_PREFIX,
deviceID,
@@ -480,7 +558,7 @@
let keyToInsert;
if (key.startsWith(NOTIFICATIONS_OLM_DATA_CONTENT)) {
const cookieID = getCookieIDFromOlmDBKey(key);
- keyToInsert = getOlmDataContentKeyForCookie(
+ keyToInsert = getOlmDataKeyForCookie(
cookieID,
ASHOAT_KEYSERVER_ID_USED_ONLY_FOR_MIGRATION_FROM_LEGACY_NOTIF_STORAGE,
);
@@ -557,9 +635,10 @@
export {
decryptWebNotification,
decryptDesktopNotification,
- getOlmDataContentKeyForCookie,
+ encryptNotification,
+ getOlmDataKeyForCookie,
getOlmEncryptionKeyDBLabelForCookie,
- getOlmDataContentKeyForDeviceID,
+ getOlmDataKeyForDeviceID,
getOlmEncryptionKeyDBLabelForDeviceID,
migrateLegacyOlmNotificationsSessions,
updateNotifsUnreadCountStorage,
diff --git a/web/shared-worker/worker/worker-crypto.js b/web/shared-worker/worker/worker-crypto.js
--- a/web/shared-worker/worker/worker-crypto.js
+++ b/web/shared-worker/worker/worker-crypto.js
@@ -50,10 +50,11 @@
generateCryptoKey,
} from '../../crypto/aes-gcm-crypto-utils.js';
import {
- getOlmDataContentKeyForCookie,
+ getOlmDataKeyForCookie,
getOlmEncryptionKeyDBLabelForCookie,
- getOlmDataContentKeyForDeviceID,
+ getOlmDataKeyForDeviceID,
getOlmEncryptionKeyDBLabelForDeviceID,
+ encryptNotification,
} from '../../push-notif/notif-crypto-utils.js';
import {
type WorkerRequestMessage,
@@ -439,16 +440,13 @@
cookie,
keyserverID,
),
- notifsOlmDataContentKey: getOlmDataContentKeyForCookie(
- cookie,
- keyserverID,
- ),
+ notifsOlmDataContentKey: getOlmDataKeyForCookie(cookie, keyserverID),
};
} else {
return {
notifsOlmDataEncryptionKeyDBLabel:
getOlmEncryptionKeyDBLabelForCookie(cookie),
- notifsOlmDataContentKey: getOlmDataContentKeyForCookie(cookie),
+ notifsOlmDataContentKey: getOlmDataKeyForCookie(cookie),
};
}
}
@@ -567,6 +565,16 @@
return result;
},
+ async encryptNotification(
+ payload: string,
+ deviceID: string,
+ ): Promise<EncryptedData> {
+ const { body: message, type: messageType } = await encryptNotification(
+ payload,
+ deviceID,
+ );
+ return { message, messageType };
+ },
async decrypt(
encryptedData: EncryptedData,
deviceID: string,
@@ -728,7 +736,7 @@
notificationsIdentityKeys: OLMIdentityKeys,
notificationsInitializationInfo: OlmSessionInitializationInfo,
): Promise<EncryptedData> {
- const dataPersistenceKey = getOlmDataContentKeyForDeviceID(deviceID);
+ const dataPersistenceKey = getOlmDataKeyForDeviceID(deviceID);
const dataEncryptionKeyDBLabel =
getOlmEncryptionKeyDBLabelForDeviceID(deviceID);
return createAndPersistNotificationsOutboundSession(

File Metadata

Mime Type
text/plain
Expires
Sat, Oct 19, 7:36 AM (22 h, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2321902
Default Alt Text
D12671.id42937.diff (15 KB)

Event Timeline