From 74fe7427af784caa9c3976ed3fcd96f4b2ac70ed Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Thu, 30 Mar 2023 14:39:39 +0200 Subject: [PATCH 1/3] sign device or user when mac check out during sas --- src/matrix/verification/CrossSigning.ts | 1 + .../verification/SAS/SASVerification.ts | 4 +++- .../SAS/stages/BaseSASVerificationStage.ts | 2 ++ .../verification/SAS/stages/VerifyMacStage.ts | 23 +++++++++++++------ 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/matrix/verification/CrossSigning.ts b/src/matrix/verification/CrossSigning.ts index a6fb3091..d9725c9a 100644 --- a/src/matrix/verification/CrossSigning.ts +++ b/src/matrix/verification/CrossSigning.ts @@ -202,6 +202,7 @@ export class CrossSigning { deviceTracker: this.deviceTracker, hsApi: this.hsApi, clock: this.platform.clock, + crossSigning: this, }); return this.sasVerificationInProgress; } diff --git a/src/matrix/verification/SAS/SASVerification.ts b/src/matrix/verification/SAS/SASVerification.ts index fff697f2..14e52007 100644 --- a/src/matrix/verification/SAS/SASVerification.ts +++ b/src/matrix/verification/SAS/SASVerification.ts @@ -29,6 +29,7 @@ import {SelectVerificationMethodStage} from "./stages/SelectVerificationMethodSt import {VerificationCancelledError} from "./VerificationCancelledError"; import {EventEmitter} from "../../../utils/EventEmitter"; import {SASProgressEvents} from "./types"; +import type {CrossSigning} from "../CrossSigning"; type Olm = typeof OlmNamespace; @@ -44,6 +45,7 @@ type Options = { deviceTracker: DeviceTracker; hsApi: HomeServerApi; clock: Clock; + crossSigning: CrossSigning } export class SASVerification extends EventEmitter { @@ -60,7 +62,7 @@ export class SASVerification extends EventEmitter { this.olmSas = olmSas; this.channel = channel; this.setupCancelAfterTimeout(clock); - const stageOptions = {...options, olmSas, eventEmitter: this}; + const stageOptions = {...options, olmSas, eventEmitter: this, crossSigning: options.crossSigning}; if (channel.getReceivedMessage(VerificationEventType.Start)) { this.startStage = new SelectVerificationMethodStage(stageOptions); } diff --git a/src/matrix/verification/SAS/stages/BaseSASVerificationStage.ts b/src/matrix/verification/SAS/stages/BaseSASVerificationStage.ts index 0eb26a02..1c2506e0 100644 --- a/src/matrix/verification/SAS/stages/BaseSASVerificationStage.ts +++ b/src/matrix/verification/SAS/stages/BaseSASVerificationStage.ts @@ -16,6 +16,7 @@ limitations under the License. import type {ILogItem} from "../../../../logging/types"; import type {Account} from "../../../e2ee/Account.js"; import type {DeviceTracker} from "../../../e2ee/DeviceTracker.js"; +import type {CrossSigning} from "../../CrossSigning"; import {IChannel} from "../channel/Channel"; import {HomeServerApi} from "../../../net/HomeServerApi"; import {SASProgressEvents} from "../types"; @@ -33,6 +34,7 @@ export type Options = { deviceTracker: DeviceTracker; hsApi: HomeServerApi; eventEmitter: EventEmitter + crossSigning: CrossSigning } export abstract class BaseSASVerificationStage { diff --git a/src/matrix/verification/SAS/stages/VerifyMacStage.ts b/src/matrix/verification/SAS/stages/VerifyMacStage.ts index 6d635cce..7fa66cc5 100644 --- a/src/matrix/verification/SAS/stages/VerifyMacStage.ts +++ b/src/matrix/verification/SAS/stages/VerifyMacStage.ts @@ -21,7 +21,7 @@ import {SendDoneStage} from "./SendDoneStage"; import {KeyUsage, getKeyEd25519Key} from "../../CrossSigning"; import {getDeviceEd25519Key} from "../../../e2ee/common"; -export type KeyVerifier = (keyId: string, device: any, keyInfo: string) => void; +export type KeyVerifier = (keyId: string, publicKey: string, keyInfo: string) => boolean; export class VerifyMacStage extends BaseSASVerificationStage { async completeStage() { @@ -54,11 +54,12 @@ export class VerifyMacStage extends BaseSASVerificationStage { await this.verifyKeys(content.mac, (keyId, key, keyInfo) => { const calculatedMAC = calculateMAC(key, baseInfo + keyId, log); - if (keyInfo !== calculatedMAC) { + const matches = keyInfo === calculatedMAC; + if (!matches) { log.log({ l: "Mac verification failed for key", keyMac: keyInfo, calculatedMAC, keyId, key }); this.channel.cancelVerification(CancelReason.KeyMismatch); - return; } + return matches; }, log); } @@ -68,8 +69,12 @@ export class VerifyMacStage extends BaseSASVerificationStage { const deviceIdOrMSK = keyId.split(":", 2)[1]; const device = await this.deviceTracker.deviceForId(userId, deviceIdOrMSK, this.hsApi, log); if (device) { - verifier(keyId, getDeviceEd25519Key(device), keyInfo); - // todo: mark device as verified here + if (verifier(keyId, getDeviceEd25519Key(device), keyInfo)) { + await log.wrap("signing device", async log => { + const signedKey = await this.options.crossSigning.signDevice(device.device_id, log); + log.set("success", !!signedKey); + }); + } } else { // If we were not able to find the device, then deviceIdOrMSK is actually the MSK! const key = await this.deviceTracker.getCrossSigningKeyForUser(userId, KeyUsage.Master, this.hsApi, log); @@ -78,8 +83,12 @@ export class VerifyMacStage extends BaseSASVerificationStage { throw new Error("Fetching MSK for user failed!"); } const masterKey = getKeyEd25519Key(key); - verifier(keyId, masterKey, keyInfo); - // todo: mark user as verified here + if(masterKey && verifier(keyId, masterKey, keyInfo)) { + await log.wrap("signing user", async log => { + const signedKey = await this.options.crossSigning.signUser(userId, log); + log.set("success", !!signedKey); + }); + } } } } From c2b6c44a68dd58eba1dd7b231066579d78b873e3 Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Thu, 30 Mar 2023 14:40:58 +0200 Subject: [PATCH 2/3] actually, don't need to pass this, it's already in options --- src/matrix/verification/SAS/SASVerification.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/matrix/verification/SAS/SASVerification.ts b/src/matrix/verification/SAS/SASVerification.ts index 14e52007..52ea4ca5 100644 --- a/src/matrix/verification/SAS/SASVerification.ts +++ b/src/matrix/verification/SAS/SASVerification.ts @@ -62,7 +62,7 @@ export class SASVerification extends EventEmitter { this.olmSas = olmSas; this.channel = channel; this.setupCancelAfterTimeout(clock); - const stageOptions = {...options, olmSas, eventEmitter: this, crossSigning: options.crossSigning}; + const stageOptions = {...options, olmSas, eventEmitter: this}; if (channel.getReceivedMessage(VerificationEventType.Start)) { this.startStage = new SelectVerificationMethodStage(stageOptions); } From ab65745b07867c3906111ce42a1087b18385f010 Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Thu, 30 Mar 2023 14:45:59 +0200 Subject: [PATCH 3/3] fix tests --- src/matrix/verification/SAS/SASVerification.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/matrix/verification/SAS/SASVerification.ts b/src/matrix/verification/SAS/SASVerification.ts index 52ea4ca5..b6265db5 100644 --- a/src/matrix/verification/SAS/SASVerification.ts +++ b/src/matrix/verification/SAS/SASVerification.ts @@ -128,7 +128,6 @@ import {SendDoneStage} from "./stages/SendDoneStage"; import {SendAcceptVerificationStage} from "./stages/SendAcceptVerificationStage"; export function tests() { - async function createSASRequest( ourUserId: string, ourDeviceId: string, @@ -190,6 +189,7 @@ export function tests() { olm, startingMessage, ); + const crossSigning = new MockCrossSigning() as unknown as CrossSigning; const clock = new MockClock(); const logger = new NullLogger(); return logger.run("log", (log) => { @@ -207,6 +207,7 @@ export function tests() { ourUserId, ourUserDeviceId: ourDeviceId, log, + crossSigning }); // @ts-ignore channel.setOlmSas(sas.olmSas); @@ -217,6 +218,16 @@ export function tests() { }); } + class MockCrossSigning { + signDevice(deviceId: string, log: ILogItem) { + return Promise.resolve({}); // device keys, means signing succeeded + } + + signUser(userId: string, log: ILogItem) { + return Promise.resolve({}); // cross-signing keys, means signing succeeded + } + } + return { "Order of stages created matches expected order when I sent request, they sent start": async (assert) => { const ourDeviceId = "ILQHOACESQ";