Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3002352
D12671.id42937.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Referenced Files
None
Subscribers
None
D12671.id42937.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D12671: Implement notification encryption on web
Attached
Detach File
Event Timeline
Log In to Comment