Page MenuHomePhabricator

D11304.id38054.diff
No OneTemporary

D11304.id38054.diff

diff --git a/web/account/account-hooks.js b/web/account/account-hooks.js
--- a/web/account/account-hooks.js
+++ b/web/account/account-hooks.js
@@ -288,10 +288,10 @@
);
const notifsOlmDataEncryptionKeyDBLabel =
- getOlmEncryptionKeyDBLabelForCookie(cookie, keyserverID);
+ getOlmEncryptionKeyDBLabelForCookie(keyserverID, cookie);
const notifsOlmDataContentKey = getOlmDataContentKeyForCookie(
- cookie,
keyserverID,
+ cookie,
);
const persistEncryptionKeyPromise = (async () => {
@@ -355,7 +355,10 @@
[getOrCreateCryptoStore],
);
- const notificationsSessionPromise = React.useRef<?Promise<string>>(null);
+ const perKeyserverNotificationsSessionPromises = React.useRef<{
+ [keyserverID: string]: ?Promise<string>,
+ }>({});
+
const createNotificationsSession = React.useCallback(
async (
cookie: ?string,
@@ -363,8 +366,8 @@
notificationsInitializationInfo: OlmSessionInitializationInfo,
keyserverID: string,
) => {
- if (notificationsSessionPromise.current) {
- return notificationsSessionPromise.current;
+ if (perKeyserverNotificationsSessionPromises.current[keyserverID]) {
+ return perKeyserverNotificationsSessionPromises.current[keyserverID];
}
const newNotificationsSessionPromise = (async () => {
@@ -376,12 +379,14 @@
keyserverID,
);
} catch (e) {
- notificationsSessionPromise.current = undefined;
+ perKeyserverNotificationsSessionPromises.current[keyserverID] =
+ undefined;
throw e;
}
})();
- notificationsSessionPromise.current = newNotificationsSessionPromise;
+ perKeyserverNotificationsSessionPromises.current[keyserverID] =
+ newNotificationsSessionPromise;
return newNotificationsSessionPromise;
},
[createNewNotificationsSession],
@@ -390,7 +395,7 @@
const isCryptoStoreSet = !!currentCryptoStore;
React.useEffect(() => {
if (!isCryptoStoreSet) {
- notificationsSessionPromise.current = undefined;
+ perKeyserverNotificationsSessionPromises.current = {};
}
}, [isCryptoStoreSet]);
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
@@ -46,11 +46,21 @@
export const WEB_NOTIFS_SERVICE_UTILS_KEY = 'webNotifsServiceUtils';
const SESSION_UPDATE_MAX_PENDING_TIME = 10 * 1000;
+const INDEXED_DB_KEYSERVER_PREFIX = 'keyserver';
+const INDEXED_DB_KEY_SEPARATOR = ':';
+
+// This constant is only used to migrate the existing notifications
+// session with production keyserver to new IndexedDB key format. This
+// migration will fire when user updates the app. It will also fire
+// on dev env provided old keyserver set up is used. Developers willing
+// to use new keyserver set up must log out before updating the app.
+// Do not introduce new usages of this constant in the code!!!
+const AUTHORITATIVE_KEYSERVER_ID = '256';
async function decryptWebNotification(
encryptedNotification: EncryptedWebNotification,
): Promise<PlainTextWebNotification | WebNotifDecryptionError> {
- const { id, encryptedPayload } = encryptedNotification;
+ const { id, keyserverID, encryptedPayload } = encryptedNotification;
const utilsData = await localforage.getItem<WebNotifsServiceUtilsData>(
WEB_NOTIFS_SERVICE_UTILS_KEY,
);
@@ -62,7 +72,7 @@
let olmDBKeys;
try {
- olmDBKeys = await getNotifsOlmSessionDBKeys();
+ olmDBKeys = await getNotifsOlmSessionDBKeys(keyserverID);
} catch (e) {
return {
id,
@@ -112,7 +122,7 @@
let encryptedOlmData, encryptionKey, olmDataContentKey;
try {
const { olmDataContentKey: olmDataContentKeyValue, encryptionKeyDBKey } =
- await getNotifsOlmSessionDBKeys();
+ await getNotifsOlmSessionDBKeys(keyserverID);
olmDataContentKey = olmDataContentKeyValue;
@@ -280,16 +290,24 @@
return await importJWKKey(persistedCryptoKey);
}
-async function getNotifsOlmSessionDBKeys(): Promise<{
+async function getNotifsOlmSessionDBKeys(keyserverID: string): Promise<{
+olmDataContentKey: string,
+encryptionKeyDBKey: string,
}> {
+ const olmDataContentKeyForKeyserverPrefix =
+ getOlmDataContentKeyForCookie(keyserverID);
+
+ const olmEncryptionKeyDBLabelForKeyserverPrefix =
+ getOlmEncryptionKeyDBLabelForCookie(keyserverID);
+
const dbKeys = await localforage.keys();
const olmDataContentKeys = sortOlmDBKeysArray(
- dbKeys.filter(key => key.startsWith(NOTIFICATIONS_OLM_DATA_CONTENT)),
+ dbKeys.filter(key => key.startsWith(olmDataContentKeyForKeyserverPrefix)),
);
const encryptionKeyDBLabels = sortOlmDBKeysArray(
- dbKeys.filter(key => key.startsWith(NOTIFICATIONS_OLM_DATA_ENCRYPTION_KEY)),
+ dbKeys.filter(key =>
+ key.startsWith(olmEncryptionKeyDBLabelForKeyserverPrefix),
+ ),
);
if (olmDataContentKeys.length === 0 || encryptionKeyDBLabels.length === 0) {
@@ -332,31 +350,51 @@
}
function getOlmDataContentKeyForCookie(
- cookie: ?string,
- // eslint-disable-next-line no-unused-vars
keyserverID: string,
+ cookie: ?string,
): string {
+ const olmDataContentKeyBase = [
+ INDEXED_DB_KEYSERVER_PREFIX,
+ keyserverID,
+ NOTIFICATIONS_OLM_DATA_CONTENT,
+ ].join(INDEXED_DB_KEY_SEPARATOR);
+
if (!cookie) {
- return NOTIFICATIONS_OLM_DATA_CONTENT;
+ return olmDataContentKeyBase;
}
const cookieID = getCookieIDFromCookie(cookie);
- return `${NOTIFICATIONS_OLM_DATA_CONTENT}:${cookieID}`;
+ return [olmDataContentKeyBase, cookieID].join(INDEXED_DB_KEY_SEPARATOR);
}
function getOlmEncryptionKeyDBLabelForCookie(
- cookie: ?string,
- // eslint-disable-next-line no-unused-vars
keyserverID: string,
+ cookie: ?string,
): string {
+ const olmEncryptionKeyDBLabelBase = [
+ INDEXED_DB_KEYSERVER_PREFIX,
+ keyserverID,
+ NOTIFICATIONS_OLM_DATA_ENCRYPTION_KEY,
+ ].join(INDEXED_DB_KEY_SEPARATOR);
+
if (!cookie) {
- return NOTIFICATIONS_OLM_DATA_ENCRYPTION_KEY;
+ return olmEncryptionKeyDBLabelBase;
}
const cookieID = getCookieIDFromCookie(cookie);
- return `${NOTIFICATIONS_OLM_DATA_ENCRYPTION_KEY}:${cookieID}`;
+ return [olmEncryptionKeyDBLabelBase, cookieID].join(INDEXED_DB_KEY_SEPARATOR);
}
function getCookieIDFromOlmDBKey(olmDBKey: string): string | '0' {
- const cookieID = olmDBKey.split(':')[1];
+ // Olm DB keys comply to the following format:
+ // KEYSERVER:<id>:(OLM_CONTENT | OLM_ENCRYPTION_KEY):<cookie id>
+ const cookieID = olmDBKey.split(INDEXED_DB_KEY_SEPARATOR)[3];
+ return cookieID ?? '0';
+}
+
+function getCookieIDFromLegacyOlmDBKey(olmDBKey: string): string | '0' {
+ // Legacy (prior to multi-keyserver) olm DB keys
+ // complied to the following format:
+ // (OLM_CONTENT | OLM_ENCRYPTION_KEY):<cookie id>
+ const cookieID = olmDBKey.split(INDEXED_DB_KEY_SEPARATOR)[1];
return cookieID ?? '0';
}
@@ -375,9 +413,51 @@
.map(({ key }) => key);
}
+async function migrateLegacyOlmNotificationsSessions() {
+ const keysToInsert = [];
+ const valuesToInsert = [];
+ const keysToDelete = [];
+
+ await localforage.iterate((value: EncryptedData | CryptoKey, key) => {
+ if (key.startsWith(NOTIFICATIONS_OLM_DATA_CONTENT)) {
+ const cookieID = getCookieIDFromLegacyOlmDBKey(key);
+ keysToInsert.push(
+ getOlmDataContentKeyForCookie(AUTHORITATIVE_KEYSERVER_ID, cookieID),
+ );
+ } else if (key.startsWith(NOTIFICATIONS_OLM_DATA_ENCRYPTION_KEY)) {
+ const cookieID = getCookieIDFromLegacyOlmDBKey(key);
+ keysToInsert.push(
+ getOlmEncryptionKeyDBLabelForCookie(
+ AUTHORITATIVE_KEYSERVER_ID,
+ cookieID,
+ ),
+ );
+ } else {
+ return undefined;
+ }
+
+ valuesToInsert.push(value);
+ keysToDelete.push(key);
+ return undefined;
+ });
+
+ const insertionPromises = keysToInsert.map((key, idx) =>
+ (async () => {
+ await localforage.setItem(key, valuesToInsert[idx]);
+ })(),
+ );
+
+ const deletionPromises = keysToDelete.map(key =>
+ (async () => await localforage.removeItem(key))(),
+ );
+
+ await Promise.all([...insertionPromises, ...deletionPromises]);
+}
+
export {
decryptWebNotification,
decryptDesktopNotification,
getOlmDataContentKeyForCookie,
getOlmEncryptionKeyDBLabelForCookie,
+ migrateLegacyOlmNotificationsSessions,
};
diff --git a/web/push-notif/push-notifs-handler.js b/web/push-notif/push-notifs-handler.js
--- a/web/push-notif/push-notifs-handler.js
+++ b/web/push-notif/push-notifs-handler.js
@@ -16,7 +16,10 @@
import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js';
import { useDispatch } from 'lib/utils/redux-utils.js';
-import { decryptDesktopNotification } from './notif-crypto-utils.js';
+import {
+ decryptDesktopNotification,
+ migrateLegacyOlmNotificationsSessions,
+} from './notif-crypto-utils.js';
import { authoritativeKeyserverID } from '../authoritative-keyserver.js';
import {
WORKERS_MODULES_DIR_PATH,
@@ -35,6 +38,15 @@
const dispatchActionPromise = useDispatchActionPromise();
const callSetDeviceToken = useSetDeviceTokenFanout();
const staffCanSee = useStaffCanSee();
+ const [notifsOlmSessionMigrated, setNotifsSessionsMigrated] =
+ React.useState<boolean>(false);
+
+ React.useEffect(() => {
+ void (async () => {
+ await migrateLegacyOlmNotificationsSessions();
+ setNotifsSessionsMigrated(true);
+ })();
+ }, []);
React.useEffect(
() =>
@@ -51,26 +63,28 @@
electron?.fetchDeviceToken?.();
}, []);
- React.useEffect(
- () =>
- electron?.onEncryptedNotification?.(
- async ({
+ React.useEffect(() => {
+ if (!notifsOlmSessionMigrated) {
+ return undefined;
+ }
+
+ return electron?.onEncryptedNotification?.(
+ async ({
+ keyserverID,
+ encryptedPayload,
+ }: {
+ keyserverID: string,
+ encryptedPayload: string,
+ }) => {
+ const decryptedPayload = await decryptDesktopNotification(
keyserverID,
encryptedPayload,
- }: {
- keyserverID: string,
- encryptedPayload: string,
- }) => {
- const decryptedPayload = await decryptDesktopNotification(
- keyserverID,
- encryptedPayload,
- staffCanSee,
- );
- electron?.showDecryptedNotification(decryptedPayload);
- },
- ),
- [staffCanSee],
- );
+ staffCanSee,
+ );
+ electron?.showDecryptedNotification(decryptedPayload);
+ },
+ );
+ }, [staffCanSee, notifsOlmSessionMigrated]);
const dispatch = useDispatch();
diff --git a/web/push-notif/service-worker.js b/web/push-notif/service-worker.js
--- a/web/push-notif/service-worker.js
+++ b/web/push-notif/service-worker.js
@@ -10,6 +10,7 @@
import {
decryptWebNotification,
+ migrateLegacyOlmNotificationsSessions,
WEB_NOTIFS_SERVICE_UTILS_KEY,
type WebNotifsServiceUtilsData,
type WebNotifDecryptionError,
@@ -78,6 +79,8 @@
WEB_NOTIFS_SERVICE_UTILS_KEY,
webNotifsServiceUtils,
);
+
+ await migrateLegacyOlmNotificationsSessions();
})(),
);
});

File Metadata

Mime Type
text/plain
Expires
Thu, Dec 19, 4:28 PM (40 m, 2 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2677993
Default Alt Text
D11304.id38054.diff (11 KB)

Event Timeline