Page MenuHomePhabricator

D11929.id40066.diff
No OneTemporary

D11929.id40066.diff

diff --git a/lib/shared/device-list-utils.js b/lib/shared/device-list-utils.js
new file mode 100644
--- /dev/null
+++ b/lib/shared/device-list-utils.js
@@ -0,0 +1,121 @@
+// @flow
+
+import type {
+ IdentityServiceClient,
+ RawDeviceList,
+ SignedDeviceList,
+} from '../types/identity-service-types.js';
+import { getConfig } from '../utils/config.js';
+import { rawDeviceListFromSignedList } from '../utils/device-list-utils.js';
+
+export type DeviceListVerificationResult =
+ | { +valid: true, +deviceList: RawDeviceList }
+ | DeviceListVerificationFailure;
+
+type DeviceListVerificationFailure =
+ | { +valid: false, +reason: 'empty_device_list_history' }
+ | { +valid: false, +reason: 'empty_device_list_update', +timestamp: number }
+ | { +valid: false, +reason: 'invalid_timestamp_order', +timestamp: number }
+ | {
+ +valid: false,
+ +reason: 'invalid_cur_primary_signature',
+ +timestamp: number,
+ }
+ | {
+ +valid: false,
+ +reason: 'invalid_last_primary_signature',
+ +timestamp: number,
+ };
+
+// Verifies all device list updates for given `userID` since
+// last known (and valid) device list. The updates are fetched
+// from Identity Service. If `lastKnownDeviceList` is not provided,
+// the whole device list history will be verified.
+// Returns latest device list from Identity Service.
+async function verifyAndGetDeviceList(
+ identityClient: IdentityServiceClient,
+ userID: string,
+ lastKnownDeviceList: ?SignedDeviceList,
+): Promise<DeviceListVerificationResult> {
+ let since;
+ if (lastKnownDeviceList) {
+ const rawList = rawDeviceListFromSignedList(lastKnownDeviceList);
+ since = rawList.timestamp;
+ }
+
+ const history = await identityClient.getDeviceListHistoryForUser(
+ userID,
+ since,
+ );
+ if (history.length < 1) {
+ return { valid: false, reason: 'empty_device_list_history' };
+ }
+
+ const [firstUpdate, ...updates] = history;
+ const deviceListUpdates = lastKnownDeviceList ? history : updates;
+ let previousDeviceList = lastKnownDeviceList ?? firstUpdate;
+
+ const { olmAPI } = getConfig();
+ for (const deviceList of deviceListUpdates) {
+ const currentPayload = rawDeviceListFromSignedList(deviceList);
+ const previousPayload = rawDeviceListFromSignedList(previousDeviceList);
+
+ // verify timestamp order
+ const { timestamp } = currentPayload;
+ if (previousPayload.timestamp >= timestamp) {
+ return {
+ valid: false,
+ reason: 'invalid_timestamp_order',
+ timestamp,
+ };
+ }
+
+ const currentPrimaryDeviceID = currentPayload.devices[0];
+ const previousPrimaryDeviceID = previousPayload.devices[0];
+ if (!currentPrimaryDeviceID || !previousPrimaryDeviceID) {
+ return { valid: false, reason: 'empty_device_list_update', timestamp };
+ }
+
+ // verify signatures
+ if (deviceList.curPrimarySignature) {
+ // verify signature using previous primary device signature
+ const signatureValid = await olmAPI.verifyMessage(
+ deviceList.rawDeviceList,
+ deviceList.curPrimarySignature,
+ currentPrimaryDeviceID,
+ );
+ if (!signatureValid) {
+ return {
+ valid: false,
+ reason: 'invalid_cur_primary_signature',
+ timestamp,
+ };
+ }
+ }
+ if (
+ currentPrimaryDeviceID !== previousPrimaryDeviceID &&
+ deviceList.lastPrimarySignature
+ ) {
+ // verify signature using previous primary device signature
+ const signatureValid = await olmAPI.verifyMessage(
+ deviceList.rawDeviceList,
+ deviceList.lastPrimarySignature,
+ previousPrimaryDeviceID,
+ );
+ if (!signatureValid) {
+ return {
+ valid: false,
+ reason: 'invalid_last_primary_signature',
+ timestamp,
+ };
+ }
+ }
+
+ previousDeviceList = deviceList;
+ }
+
+ const deviceList = rawDeviceListFromSignedList(previousDeviceList);
+ return { valid: true, deviceList };
+}
+
+export { verifyAndGetDeviceList };
diff --git a/lib/utils/device-list-utils.js b/lib/utils/device-list-utils.js
--- a/lib/utils/device-list-utils.js
+++ b/lib/utils/device-list-utils.js
@@ -2,6 +2,8 @@
import { assertWithValidator } from './validation-utils.js';
import type {
+ RawDeviceList,
+ SignedDeviceList,
UsersRawDeviceLists,
UsersSignedDeviceLists,
} from '../types/identity-service-types.js';
@@ -14,13 +16,22 @@
for (const userID in signedDeviceLists) {
usersRawDeviceLists = {
...usersRawDeviceLists,
- [userID]: assertWithValidator(
- JSON.parse(signedDeviceLists[userID].rawDeviceList),
- rawDeviceListValidator,
- ),
+ [userID]: rawDeviceListFromSignedList(signedDeviceLists[userID]),
};
}
return usersRawDeviceLists;
}
-export { convertSignedDeviceListsToRawDeviceLists };
+function rawDeviceListFromSignedList(
+ signedDeviceList: SignedDeviceList,
+): RawDeviceList {
+ return assertWithValidator(
+ JSON.parse(signedDeviceList.rawDeviceList),
+ rawDeviceListValidator,
+ );
+}
+
+export {
+ convertSignedDeviceListsToRawDeviceLists,
+ rawDeviceListFromSignedList,
+};

File Metadata

Mime Type
text/plain
Expires
Mon, Nov 18, 2:49 AM (21 h, 14 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2533427
Default Alt Text
D11929.id40066.diff (5 KB)

Event Timeline