Page MenuHomePhabricator

D12395.id41551.diff
No OneTemporary

D12395.id41551.diff

diff --git a/keyserver/src/push/crypto.js b/keyserver/src/push/crypto.js
--- a/keyserver/src/push/crypto.js
+++ b/keyserver/src/push/crypto.js
@@ -11,13 +11,13 @@
PlainTextWebNotificationPayload,
WebNotification,
PlainTextWNSNotification,
- PlainTextWNSNotificationPayload,
WNSNotification,
AndroidVisualNotification,
AndroidVisualNotificationPayload,
AndroidBadgeOnlyNotification,
AndroidNotificationRescind,
NotificationTargetDevice,
+ SenderDeviceDescriptor,
} from 'lib/types/notif-types.js';
import { toBase64URL } from 'lib/utils/base64.js';
@@ -27,6 +27,7 @@
async function encryptAPNsNotification(
cookieID: string,
+ senderDeviceID: SenderDeviceDescriptor,
notification: apn.Notification,
codeVersion?: ?number,
notificationSizeValidator?: apn.Notification => boolean,
@@ -58,8 +59,7 @@
encryptedNotification.pushType = 'alert';
encryptedNotification.mutableContent = true;
- const { id, keyserverID, ...payloadSansUnencryptedData } =
- notification.payload;
+ const { id, ...payloadSansUnencryptedData } = notification.payload;
const unencryptedPayload = {
...payloadSansUnencryptedData,
badge: notification.aps.badge.toString(),
@@ -95,6 +95,10 @@
);
encryptedNotification.payload.encryptedPayload = serializedPayload.body;
+ encryptedNotification.payload = {
+ ...senderDeviceID,
+ ...encryptedNotification.payload,
+ };
if (codeVersion && codeVersion >= 254 && codeVersion % 2 === 0) {
encryptedNotification.aps = {
@@ -137,10 +141,15 @@
async function encryptAndroidNotificationPayload<T>(
cookieID: string,
+ senderDeviceID: SenderDeviceDescriptor,
unencryptedPayload: T,
- payloadSizeValidator?: (T | { +encryptedPayload: string }) => boolean,
+ payloadSizeValidator?: (
+ T | $ReadOnly<{ ...SenderDeviceDescriptor, +encryptedPayload: string }>,
+ ) => boolean,
): Promise<{
- +resultPayload: T | { +encryptedPayload: string },
+ +resultPayload:
+ | T
+ | $ReadOnly<{ ...SenderDeviceDescriptor, +encryptedPayload: string }>,
+payloadSizeExceeded: boolean,
+encryptionOrder?: number,
}> {
@@ -161,7 +170,11 @@
serializedPayload,
}: {
+[string]: EncryptResult,
- }) => payloadSizeValidator({ encryptedPayload: serializedPayload.body });
+ }) =>
+ payloadSizeValidator({
+ encryptedPayload: serializedPayload.body,
+ ...senderDeviceID,
+ });
}
const {
@@ -177,7 +190,10 @@
dbPersistCondition,
);
return {
- resultPayload: { encryptedPayload: serializedPayload.body },
+ resultPayload: {
+ encryptedPayload: serializedPayload.body,
+ ...senderDeviceID,
+ },
payloadSizeExceeded: !!dbPersistConditionViolated,
encryptionOrder,
};
@@ -197,6 +213,7 @@
}
async function encryptAndroidVisualNotification(
+ senderDeviceID: SenderDeviceDescriptor,
cookieID: string,
notification: AndroidVisualNotification,
notificationSizeValidator?: AndroidVisualNotification => boolean,
@@ -206,11 +223,11 @@
+payloadSizeExceeded: boolean,
+encryptionOrder?: number,
}> {
- const { id, keyserverID, ...rest } = notification.data;
+ const { id, ...rest } = notification.data;
- let unencryptedData = { keyserverID };
+ let unencryptedData = {};
if (id) {
- unencryptedData = { ...unencryptedData, id };
+ unencryptedData = { id };
}
let unencryptedPayload = rest;
@@ -221,7 +238,9 @@
let payloadSizeValidator;
if (notificationSizeValidator) {
payloadSizeValidator = (
- payload: AndroidVisualNotificationPayload | { +encryptedPayload: string },
+ payload:
+ | AndroidVisualNotificationPayload
+ | $ReadOnly<{ ...SenderDeviceDescriptor, +encryptedPayload: string }>,
) => {
return notificationSizeValidator({
data: { ...unencryptedData, ...payload },
@@ -231,6 +250,7 @@
const { resultPayload, payloadSizeExceeded, encryptionOrder } =
await encryptAndroidNotificationPayload(
cookieID,
+ senderDeviceID,
unencryptedPayload,
payloadSizeValidator,
);
@@ -248,31 +268,32 @@
async function encryptAndroidSilentNotification(
cookieID: string,
+ senderDeviceID: SenderDeviceDescriptor,
notification: AndroidNotificationRescind | AndroidBadgeOnlyNotification,
): Promise<AndroidNotificationRescind | AndroidBadgeOnlyNotification> {
// We don't validate payload size for rescind
// since they are expected to be small and
// never exceed any FCM limit
- const { keyserverID, ...unencryptedPayload } = notification.data;
+ const { ...unencryptedPayload } = notification.data;
const { resultPayload } = await encryptAndroidNotificationPayload(
cookieID,
+ senderDeviceID,
unencryptedPayload,
);
if (resultPayload.encryptedPayload) {
return {
- data: { keyserverID, ...resultPayload },
+ data: { ...resultPayload },
};
}
if (resultPayload.rescind) {
return {
- data: { keyserverID, ...resultPayload },
+ data: { ...resultPayload },
};
}
return {
data: {
- keyserverID,
...resultPayload,
},
};
@@ -280,9 +301,14 @@
async function encryptBasicPayload<T>(
cookieID: string,
+ senderDeviceID: SenderDeviceDescriptor,
basicPayload: T,
): Promise<
- | { +encryptedPayload: string, +encryptionOrder?: number }
+ | $ReadOnly<{
+ ...SenderDeviceDescriptor,
+ +encryptedPayload: string,
+ +encryptionOrder?: number,
+ }>
| { ...T, +encryptionFailed: '1' },
> {
const unencryptedSerializedPayload = JSON.stringify(basicPayload);
@@ -300,6 +326,7 @@
});
return {
+ ...senderDeviceID,
encryptedPayload: serializedPayload.body,
encryptionOrder,
};
@@ -314,37 +341,42 @@
async function encryptWebNotification(
cookieID: string,
+ senderDeviceID: SenderDeviceDescriptor,
notification: PlainTextWebNotification,
): Promise<{ +notification: WebNotification, +encryptionOrder?: number }> {
- const { id, keyserverID, ...payloadSansId } = notification;
+ const { id, ...payloadSansId } = notification;
const { encryptionOrder, ...encryptionResult } =
await encryptBasicPayload<PlainTextWebNotificationPayload>(
cookieID,
+ senderDeviceID,
payloadSansId,
);
+
return {
- notification: { id, keyserverID, ...encryptionResult },
+ notification: { id, ...encryptionResult },
encryptionOrder,
};
}
async function encryptWNSNotification(
cookieID: string,
+ senderDeviceID: SenderDeviceDescriptor,
notification: PlainTextWNSNotification,
): Promise<{ +notification: WNSNotification, +encryptionOrder?: number }> {
- const { keyserverID, ...payloadSansKeyserverID } = notification;
const { encryptionOrder, ...encryptionResult } =
- await encryptBasicPayload<PlainTextWNSNotificationPayload>(
+ await encryptBasicPayload<PlainTextWNSNotification>(
cookieID,
- payloadSansKeyserverID,
+ senderDeviceID,
+ notification,
);
return {
- notification: { keyserverID, ...encryptionResult },
+ notification: { ...encryptionResult },
encryptionOrder,
};
}
function prepareEncryptedAPNsNotifications(
+ senderDeviceID: SenderDeviceDescriptor,
devices: $ReadOnlyArray<NotificationTargetDevice>,
notification: apn.Notification,
codeVersion?: ?number,
@@ -363,6 +395,7 @@
async ({ cookieID, deviceToken, blobHolder }) => {
const notif = await encryptAPNsNotification(
cookieID,
+ senderDeviceID,
notification,
codeVersion,
notificationSizeValidator,
@@ -375,6 +408,7 @@
}
function prepareEncryptedIOSNotificationRescind(
+ senderDeviceID: SenderDeviceDescriptor,
devices: $ReadOnlyArray<NotificationTargetDevice>,
notification: apn.Notification,
codeVersion?: ?number,
@@ -389,6 +423,7 @@
async ({ deviceToken, cookieID }) => {
const { notification: notif } = await encryptAPNsNotification(
cookieID,
+ senderDeviceID,
notification,
codeVersion,
);
@@ -399,6 +434,7 @@
}
function prepareEncryptedAndroidVisualNotifications(
+ senderDeviceID: SenderDeviceDescriptor,
devices: $ReadOnlyArray<NotificationTargetDevice>,
notification: AndroidVisualNotification,
notificationSizeValidator?: (
@@ -416,6 +452,7 @@
const notificationPromises = devices.map(
async ({ deviceToken, cookieID, blobHolder }) => {
const notif = await encryptAndroidVisualNotification(
+ senderDeviceID,
cookieID,
notification,
notificationSizeValidator,
@@ -428,6 +465,7 @@
}
function prepareEncryptedAndroidSilentNotifications(
+ senderDeviceID: SenderDeviceDescriptor,
devices: $ReadOnlyArray<NotificationTargetDevice>,
notification: AndroidNotificationRescind | AndroidBadgeOnlyNotification,
): Promise<
@@ -442,6 +480,7 @@
async ({ deviceToken, cookieID }) => {
const notif = await encryptAndroidSilentNotification(
cookieID,
+ senderDeviceID,
notification,
);
return { deviceToken, cookieID, notification: notif };
@@ -451,6 +490,7 @@
}
function prepareEncryptedWebNotifications(
+ senderDeviceID: SenderDeviceDescriptor,
devices: $ReadOnlyArray<NotificationTargetDevice>,
notification: PlainTextWebNotification,
): Promise<
@@ -462,7 +502,11 @@
> {
const notificationPromises = devices.map(
async ({ deviceToken, cookieID }) => {
- const notif = await encryptWebNotification(cookieID, notification);
+ const notif = await encryptWebNotification(
+ cookieID,
+ senderDeviceID,
+ notification,
+ );
return { ...notif, deviceToken };
},
);
@@ -470,6 +514,7 @@
}
function prepareEncryptedWNSNotifications(
+ senderDeviceID: SenderDeviceDescriptor,
devices: $ReadOnlyArray<NotificationTargetDevice>,
notification: PlainTextWNSNotification,
): Promise<
@@ -481,7 +526,11 @@
> {
const notificationPromises = devices.map(
async ({ deviceToken, cookieID }) => {
- const notif = await encryptWNSNotification(cookieID, notification);
+ const notif = await encryptWNSNotification(
+ cookieID,
+ senderDeviceID,
+ notification,
+ );
return { ...notif, deviceToken };
},
);
diff --git a/keyserver/src/push/rescind.js b/keyserver/src/push/rescind.js
--- a/keyserver/src/push/rescind.js
+++ b/keyserver/src/push/rescind.js
@@ -9,6 +9,7 @@
import type {
NotificationTargetDevice,
TargetedAndroidNotification,
+ SenderDeviceDescriptor,
} from 'lib/types/notif-types.js';
import { threadSubscriptions } from 'lib/types/subscription-types.js';
import { threadPermissions } from 'lib/types/thread-permission-types.js';
@@ -274,10 +275,12 @@
}
async function conditionallyEncryptNotification<T>(
+ senderDeviceID: SenderDeviceDescriptor,
notification: T,
codeVersion: ?number,
devices: $ReadOnlyArray<NotificationTargetDevice>,
encryptCallback: (
+ senderDeviceID: SenderDeviceDescriptor,
devices: $ReadOnlyArray<NotificationTargetDevice>,
notification: T,
codeVersion?: ?number,
@@ -298,6 +301,7 @@
}));
}
const notifications = await encryptCallback(
+ senderDeviceID,
devices,
notification,
codeVersion,
@@ -350,6 +354,7 @@
},
};
return await conditionallyEncryptNotification(
+ { keyserverID },
notification,
codeVersion,
devices,
@@ -375,10 +380,10 @@
rescindID: notifID,
setUnreadStatus: 'true',
threadID,
- keyserverID,
},
};
const targetedRescinds = await conditionallyEncryptNotification(
+ { keyserverID },
notification,
codeVersion,
devices,
diff --git a/keyserver/src/push/send.js b/keyserver/src/push/send.js
--- a/keyserver/src/push/send.js
+++ b/keyserver/src/push/send.js
@@ -999,7 +999,6 @@
notification.pushType = 'alert';
notification.payload.id = uniqueID;
notification.payload.threadID = threadID;
- notification.payload.keyserverID = keyserverID;
if (platformDetails.codeVersion && platformDetails.codeVersion > 198) {
notification.mutableContent = true;
@@ -1040,6 +1039,7 @@
if (platformDetails.platform === 'macos') {
const macOSNotifsWithoutMessageInfos =
await prepareEncryptedAPNsNotifications(
+ { keyserverID },
devices,
notification,
platformDetails.codeVersion,
@@ -1053,6 +1053,7 @@
}
const notifsWithMessageInfos = await prepareEncryptedAPNsNotifications(
+ { keyserverID },
devices,
copyWithMessageInfos,
platformDetails.codeVersion,
@@ -1061,7 +1062,10 @@
const devicesWithExcessiveSizeNoHolders = notifsWithMessageInfos
.filter(({ payloadSizeExceeded }) => payloadSizeExceeded)
- .map(({ deviceToken, cookieID }) => ({ deviceToken, cookieID }));
+ .map(({ deviceToken, cookieID }) => ({
+ deviceToken,
+ cookieID,
+ }));
if (devicesWithExcessiveSizeNoHolders.length === 0) {
return notifsWithMessageInfos.map(
@@ -1119,6 +1123,7 @@
}
const notifsWithoutMessageInfos = await prepareEncryptedAPNsNotifications(
+ { keyserverID },
devicesWithExcessiveSize,
notification,
platformDetails.codeVersion,
@@ -1208,7 +1213,6 @@
const { merged, ...rest } = notifTexts;
const notification = {
data: {
- keyserverID,
badge: unreadCount.toString(),
...rest,
threadID,
@@ -1265,6 +1269,7 @@
const notifsWithMessageInfos =
await prepareEncryptedAndroidVisualNotifications(
+ { keyserverID },
devices,
copyWithMessageInfos,
notificationsSizeValidator,
@@ -1326,6 +1331,7 @@
const notifsWithoutMessageInfos =
await prepareEncryptedAndroidVisualNotifications(
+ { keyserverID },
devicesWithExcessiveSize,
notification,
);
@@ -1385,7 +1391,6 @@
unreadCount,
id,
threadID,
- keyserverID,
};
const shouldBeEncrypted = hasMinCodeVersion(convertedData.platformDetails, {
@@ -1396,7 +1401,11 @@
return devices.map(({ deviceToken }) => ({ deviceToken, notification }));
}
- return prepareEncryptedWebNotifications(devices, notification);
+ return prepareEncryptedWebNotifications(
+ { keyserverID },
+ devices,
+ notification,
+ );
}
type WNSNotifInputData = {
@@ -1428,7 +1437,6 @@
...rest,
unreadCount,
threadID,
- keyserverID,
};
if (
@@ -1448,7 +1456,11 @@
notification,
}));
}
- return await prepareEncryptedWNSNotifications(devices, notification);
+ return await prepareEncryptedWNSNotifications(
+ { keyserverID },
+ devices,
+ notification,
+ );
}
type NotificationInfo =
@@ -1797,11 +1809,11 @@
});
notification.badge = unreadCount;
notification.pushType = 'alert';
- notification.payload.keyserverID = keyserverID;
const preparePromise: Promise<PreparePushResult[]> = (async () => {
let targetedNotifications: $ReadOnlyArray<TargetedAPNsNotification>;
if (codeVersion > 222) {
const notificationsArray = await prepareEncryptedAPNsNotifications(
+ { keyserverID },
deviceInfos,
notification,
codeVersion,
@@ -1845,7 +1857,7 @@
badgeOnly: '1',
};
const notification = {
- data: { ...notificationData, keyserverID },
+ data: { ...notificationData },
};
const preparePromise: Promise<PreparePushResult[]> = (async () => {
let targetedNotifications: $ReadOnlyArray<TargetedAndroidNotification>;
@@ -1853,6 +1865,7 @@
if (codeVersion > 222) {
const notificationsArray =
await prepareEncryptedAndroidSilentNotifications(
+ { keyserverID },
deviceInfos,
notification,
);
@@ -1910,6 +1923,7 @@
let targetedNotifications: $ReadOnlyArray<TargetedAPNsNotification>;
if (shouldBeEncrypted) {
const notificationsArray = await prepareEncryptedAPNsNotifications(
+ { keyserverID },
deviceInfos,
notification,
codeVersion,
diff --git a/lib/types/notif-types.js b/lib/types/notif-types.js
--- a/lib/types/notif-types.js
+++ b/lib/types/notif-types.js
@@ -26,6 +26,10 @@
prefix: t.maybe(t.String),
});
+export type SenderDeviceDescriptor =
+ | { +keyserverID: string }
+ | { +senderDeviceID: string };
+
export type PlainTextWebNotificationPayload = {
+body: string,
+prefix?: string,
@@ -35,23 +39,22 @@
+encryptionFailed?: '1',
};
-export type PlainTextWebNotification = {
+export type PlainTextWebNotification = $ReadOnly<{
+id: string,
- +keyserverID: string,
...PlainTextWebNotificationPayload,
-};
+}>;
-export type EncryptedWebNotification = {
+export type EncryptedWebNotification = $ReadOnly<{
+ ...SenderDeviceDescriptor,
+id: string,
- +keyserverID: string,
+encryptedPayload: string,
-};
+}>;
export type WebNotification =
| PlainTextWebNotification
| EncryptedWebNotification;
-export type PlainTextWNSNotificationPayload = {
+export type PlainTextWNSNotification = {
+body: string,
+prefix?: string,
+title: string,
@@ -60,15 +63,10 @@
+encryptionFailed?: '1',
};
-export type PlainTextWNSNotification = {
- +keyserverID: string,
- ...PlainTextWNSNotificationPayload,
-};
-
-export type EncryptedWNSNotification = {
- +keyserverID: string,
+export type EncryptedWNSNotification = $ReadOnly<{
+ ...SenderDeviceDescriptor,
+encryptedPayload: string,
-};
+}>;
export type WNSNotification =
| PlainTextWNSNotification
@@ -85,39 +83,44 @@
+encryptionFailed?: '1',
}>;
-export type AndroidVisualNotificationPayload = $ReadOnly<
- | {
- ...AndroidVisualNotificationPayloadBase,
- +messageInfos?: string,
- }
- | {
- ...AndroidVisualNotificationPayloadBase,
- +blobHash: string,
- +encryptionKey: string,
- },
->;
+type AndroidSmallVisualNotificationPayload = $ReadOnly<{
+ ...AndroidVisualNotificationPayloadBase,
+ +messageInfos?: string,
+}>;
+
+type AndroidLargeVisualNotificationPayload = $ReadOnly<{
+ ...AndroidVisualNotificationPayloadBase,
+ +blobHash: string,
+ +encryptionKey: string,
+}>;
+
+export type AndroidVisualNotificationPayload =
+ | AndroidSmallVisualNotificationPayload
+ | AndroidLargeVisualNotificationPayload;
+
+type EncryptedThinThreadPayload = {
+ +keyserverID: string,
+ +encryptedPayload: string,
+};
+
+type EncryptedThickThreadPayload = {
+ +senderDeviceID: string,
+ +encryptedPayload: string,
+};
export type AndroidVisualNotification = {
+data: $ReadOnly<{
+id?: string,
- +keyserverID: string,
...
- | {
- ...AndroidVisualNotificationPayloadBase,
- +messageInfos?: string,
- }
- | {
- ...AndroidVisualNotificationPayloadBase,
- +blobHash: string,
- +encryptionKey: string,
- }
- | { +encryptedPayload: string },
+ | AndroidSmallVisualNotificationPayload
+ | AndroidLargeVisualNotificationPayload
+ | EncryptedThinThreadPayload
+ | EncryptedThickThreadPayload,
}>,
};
export type AndroidNotificationRescind = {
+data: $ReadOnly<{
- +keyserverID: string,
...
| {
+badge: string,
@@ -127,20 +130,21 @@
+threadID: string,
+encryptionFailed?: string,
}
- | { +encryptedPayload: string },
+ | EncryptedThinThreadPayload
+ | EncryptedThickThreadPayload,
}>,
};
export type AndroidBadgeOnlyNotification = {
+data: $ReadOnly<{
- +keyserverID: string,
...
| {
+badge: string,
+badgeOnly: '1',
+encryptionFailed?: string,
}
- | { +encryptedPayload: string },
+ | EncryptedThinThreadPayload
+ | EncryptedThickThreadPayload,
}>,
};
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 invariant from 'invariant';
import localforage from 'localforage';
import {
@@ -64,6 +65,7 @@
encryptedNotification: EncryptedWebNotification,
): Promise<PlainTextWebNotification | WebNotifDecryptionError> {
const { id, keyserverID, encryptedPayload } = encryptedNotification;
+ invariant(keyserverID, 'KeyserverID must be present to decrypt a notif');
const utilsData = await localforage.getItem<WebNotifsServiceUtilsData>(
WEB_NOTIFS_SERVICE_UTILS_KEY,
);
@@ -108,6 +110,8 @@
);
const { unreadCount } = decryptedNotification;
+
+ invariant(keyserverID, 'Keyserver ID must be set to update badge counts');
await updateNotifsUnreadCountStorage({
[keyserverID]: unreadCount,
});

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 29, 10:03 AM (4 h, 36 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2596631
Default Alt Text
D12395.id41551.diff (20 KB)

Event Timeline