Page MenuHomePhabricator

D11304.id38223.diff
No OneTemporary

D11304.id38223.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
@@ -8,6 +8,10 @@
import { initialEncryptedMessageContent } from 'lib/shared/crypto-utils.js';
import { OlmSessionCreatorContext } from 'lib/shared/olm-session-creator-context.js';
+import {
+ hasMinCodeVersion,
+ NEXT_CODE_VERSION,
+} from 'lib/shared/version-utils.js';
import type {
SignedIdentityKeysBlob,
CryptoStore,
@@ -18,6 +22,7 @@
} from 'lib/types/crypto-types.js';
import { type IdentityDeviceKeyUpload } from 'lib/types/identity-service-types.js';
import type { OlmSessionInitializationInfo } from 'lib/types/request-types.js';
+import { getConfig } from 'lib/utils/config.js';
import { retrieveAccountKeysSet } from 'lib/utils/olm-utils.js';
import { useDispatch } from 'lib/utils/redux-utils.js';
@@ -237,6 +242,7 @@
function OlmSessionCreatorProvider(props: Props): React.Node {
const getOrCreateCryptoStore = useGetOrCreateCryptoStore();
const currentCryptoStore = useSelector(state => state.cryptoStore);
+ const platformDetails = getConfig().platformDetails;
const createNewNotificationsSession = React.useCallback(
async (
@@ -282,12 +288,25 @@
encryptionKey,
);
- const notifsOlmDataEncryptionKeyDBLabel =
- getOlmEncryptionKeyDBLabelForCookie(cookie, keyserverID);
- const notifsOlmDataContentKey = getOlmDataContentKeyForCookie(
- cookie,
- keyserverID,
- );
+ let notifsOlmDataContentKey;
+ let notifsOlmDataEncryptionKeyDBLabel;
+
+ if (
+ hasMinCodeVersion(platformDetails, { majorDesktop: NEXT_CODE_VERSION })
+ ) {
+ notifsOlmDataEncryptionKeyDBLabel = getOlmEncryptionKeyDBLabelForCookie(
+ cookie,
+ keyserverID,
+ );
+ notifsOlmDataContentKey = getOlmDataContentKeyForCookie(
+ cookie,
+ keyserverID,
+ );
+ } else {
+ notifsOlmDataEncryptionKeyDBLabel =
+ getOlmEncryptionKeyDBLabelForCookie(cookie);
+ notifsOlmDataContentKey = getOlmDataContentKeyForCookie(cookie);
+ }
const persistEncryptionKeyPromise = (async () => {
let cryptoKeyPersistentForm;
@@ -312,7 +331,7 @@
return initialNotificationsEncryptedMessage;
},
- [getOrCreateCryptoStore],
+ [getOrCreateCryptoStore, platformDetails],
);
const createNewContentSession = React.useCallback(
@@ -348,7 +367,10 @@
[getOrCreateCryptoStore],
);
- const notificationsSessionPromise = React.useRef<?Promise<string>>(null);
+ const perKeyserverNotificationsSessionPromises = React.useRef<{
+ [keyserverID: string]: ?Promise<string>,
+ }>({});
+
const createNotificationsSession = React.useCallback(
async (
cookie: ?string,
@@ -356,8 +378,8 @@
notificationsInitializationInfo: OlmSessionInitializationInfo,
keyserverID: string,
) => {
- if (notificationsSessionPromise.current) {
- return notificationsSessionPromise.current;
+ if (perKeyserverNotificationsSessionPromises.current[keyserverID]) {
+ return perKeyserverNotificationsSessionPromises.current[keyserverID];
}
const newNotificationsSessionPromise = (async () => {
@@ -369,12 +391,14 @@
keyserverID,
);
} catch (e) {
- notificationsSessionPromise.current = undefined;
+ perKeyserverNotificationsSessionPromises.current[keyserverID] =
+ undefined;
throw e;
}
})();
- notificationsSessionPromise.current = newNotificationsSessionPromise;
+ perKeyserverNotificationsSessionPromises.current[keyserverID] =
+ newNotificationsSessionPromise;
return newNotificationsSessionPromise;
},
[createNewNotificationsSession],
@@ -383,7 +407,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,22 @@
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 ASHOAT_KEYSERVER_ID_USED_ONLY_FOR_MIGRATION_FROM_LEGACY_NOTIF_STORAGE =
+ '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 +73,7 @@
let olmDBKeys;
try {
- olmDBKeys = await getNotifsOlmSessionDBKeys();
+ olmDBKeys = await getNotifsOlmSessionDBKeys(keyserverID);
} catch (e) {
return {
id,
@@ -107,13 +118,12 @@
async function decryptDesktopNotification(
encryptedPayload: string,
staffCanSee: boolean,
- // eslint-disable-next-line no-unused-vars
keyserverID?: string,
): Promise<{ +[string]: mixed }> {
let encryptedOlmData, encryptionKey, olmDataContentKey;
try {
const { olmDataContentKey: olmDataContentKeyValue, encryptionKeyDBKey } =
- await getNotifsOlmSessionDBKeys();
+ await getNotifsOlmSessionDBKeys(keyserverID);
olmDataContentKey = olmDataContentKeyValue;
@@ -281,16 +291,26 @@
return await importJWKKey(persistedCryptoKey);
}
-async function getNotifsOlmSessionDBKeys(): Promise<{
+async function getNotifsOlmSessionDBKeys(keyserverID?: string): Promise<{
+olmDataContentKey: string,
+encryptionKeyDBKey: string,
}> {
+ const olmDataContentKeyForKeyserverPrefix = getOlmDataContentKeyForCookie(
+ undefined,
+ keyserverID,
+ );
+
+ const olmEncryptionKeyDBLabelForKeyserverPrefix =
+ getOlmEncryptionKeyDBLabelForCookie(undefined, 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) {
@@ -334,30 +354,55 @@
function getOlmDataContentKeyForCookie(
cookie: ?string,
- // eslint-disable-next-line no-unused-vars
- keyserverID: string,
+ keyserverID?: string,
): string {
+ let olmDataContentKeyBase;
+ if (keyserverID) {
+ olmDataContentKeyBase = [
+ INDEXED_DB_KEYSERVER_PREFIX,
+ keyserverID,
+ NOTIFICATIONS_OLM_DATA_CONTENT,
+ ].join(INDEXED_DB_KEY_SEPARATOR);
+ } else {
+ olmDataContentKeyBase = NOTIFICATIONS_OLM_DATA_CONTENT;
+ }
+
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,
+ keyserverID?: string,
): string {
+ let olmEncryptionKeyDBLabelBase;
+ if (keyserverID) {
+ olmEncryptionKeyDBLabelBase = [
+ INDEXED_DB_KEYSERVER_PREFIX,
+ keyserverID,
+ NOTIFICATIONS_OLM_DATA_ENCRYPTION_KEY,
+ ].join(INDEXED_DB_KEY_SEPARATOR);
+ } else {
+ olmEncryptionKeyDBLabelBase = NOTIFICATIONS_OLM_DATA_ENCRYPTION_KEY;
+ }
+
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 one of the following formats:
+ // KEYSERVER:<id>:(OLM_CONTENT | OLM_ENCRYPTION_KEY):<cookie id>
+ // or legacy (OLM_CONTENT | OLM_ENCRYPTION_KEY):<cookie id>.
+ // Legacy format may be used in case a new version of the web app
+ // is running on a old desktop version that uses legacy key format.
+ const cookieID = olmDBKey.split(INDEXED_DB_KEY_SEPARATOR).slice(-1)[0];
return cookieID ?? '0';
}
@@ -376,9 +421,52 @@
.map(({ key }) => key);
}
+async function migrateLegacyOlmNotificationsSessions() {
+ const keyValuePairsToInsert: { [key: string]: EncryptedData | CryptoKey } =
+ {};
+ const keysToDelete = [];
+
+ await localforage.iterate((value: EncryptedData | CryptoKey, key) => {
+ let keyToInsert;
+ if (key.startsWith(NOTIFICATIONS_OLM_DATA_CONTENT)) {
+ const cookieID = getCookieIDFromOlmDBKey(key);
+ keyToInsert = getOlmDataContentKeyForCookie(
+ cookieID,
+ ASHOAT_KEYSERVER_ID_USED_ONLY_FOR_MIGRATION_FROM_LEGACY_NOTIF_STORAGE,
+ );
+ } else if (key.startsWith(NOTIFICATIONS_OLM_DATA_ENCRYPTION_KEY)) {
+ const cookieID = getCookieIDFromOlmDBKey(key);
+ keyToInsert = getOlmEncryptionKeyDBLabelForCookie(
+ cookieID,
+ ASHOAT_KEYSERVER_ID_USED_ONLY_FOR_MIGRATION_FROM_LEGACY_NOTIF_STORAGE,
+ );
+ } else {
+ return undefined;
+ }
+
+ keyValuePairsToInsert[keyToInsert] = value;
+ keysToDelete.push(key);
+ return undefined;
+ });
+
+ const insertionPromises = Object.entries(keyValuePairsToInsert).map(
+ ([key, value]) =>
+ (async () => {
+ await localforage.setItem(key, value);
+ })(),
+ );
+
+ 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
@@ -8,6 +8,12 @@
} from 'lib/actions/device-actions.js';
import { useModalContext } from 'lib/components/modal-provider.react.js';
import { isLoggedIn } from 'lib/selectors/user-selectors.js';
+import {
+ hasMinCodeVersion,
+ NEXT_CODE_VERSION,
+} from 'lib/shared/version-utils.js';
+import { isDesktopPlatform } from 'lib/types/device-types.js';
+import { getConfig } from 'lib/utils/config.js';
import { convertNonPendingIDToNewSchema } from 'lib/utils/migration-utils.js';
import {
shouldSkipPushPermissionAlert,
@@ -16,7 +22,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 electron from '../electron.js';
import PushNotifModal from '../modals/push-notif-modal.react.js';
@@ -29,6 +38,22 @@
const dispatchActionPromise = useDispatchActionPromise();
const callSetDeviceToken = useSetDeviceTokenFanout();
const staffCanSee = useStaffCanSee();
+ const [notifsOlmSessionMigrated, setNotifsSessionsMigrated] =
+ React.useState<boolean>(false);
+ const platformDetails = getConfig().platformDetails;
+
+ React.useEffect(() => {
+ if (
+ !isDesktopPlatform(platformDetails.platform) ||
+ !hasMinCodeVersion(platformDetails, { majorDesktop: NEXT_CODE_VERSION })
+ ) {
+ return;
+ }
+ void (async () => {
+ await migrateLegacyOlmNotificationsSessions();
+ setNotifsSessionsMigrated(true);
+ })();
+ }, [platformDetails]);
React.useEffect(
() =>
@@ -45,26 +70,31 @@
electron?.fetchDeviceToken?.();
}, []);
- React.useEffect(
- () =>
- electron?.onEncryptedNotification?.(
- async ({
+ React.useEffect(() => {
+ if (
+ hasMinCodeVersion(platformDetails, { majorDesktop: NEXT_CODE_VERSION }) &&
+ !notifsOlmSessionMigrated
+ ) {
+ return undefined;
+ }
+
+ return electron?.onEncryptedNotification?.(
+ async ({
+ encryptedPayload,
+ keyserverID,
+ }: {
+ encryptedPayload: string,
+ keyserverID?: string,
+ }) => {
+ const decryptedPayload = await decryptDesktopNotification(
encryptedPayload,
+ staffCanSee,
keyserverID,
- }: {
- encryptedPayload: string,
- keyserverID?: string,
- }) => {
- const decryptedPayload = await decryptDesktopNotification(
- encryptedPayload,
- staffCanSee,
- keyserverID,
- );
- electron?.showDecryptedNotification(decryptedPayload);
- },
- ),
- [staffCanSee],
- );
+ );
+ electron?.showDecryptedNotification(decryptedPayload);
+ },
+ );
+ }, [staffCanSee, notifsOlmSessionMigrated, platformDetails]);
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, 3:50 PM (7 m, 59 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2677872
Default Alt Text
D11304.id38223.diff (14 KB)

Event Timeline