From 683e055757075373a0236a9a96a6e4f50cc65b41 Mon Sep 17 00:00:00 2001 From: RMidhunSuresh Date: Wed, 1 Mar 2023 16:59:24 +0530 Subject: [PATCH] WIP --- src/matrix/verification/CrossSigning.ts | 1 + .../verification/SAS/SASVerification.ts | 27 ++-- .../verification/SAS/channel/Channel.ts | 135 ++++++++++++++++-- src/matrix/verification/SAS/channel/types.ts | 20 +++ .../SAS/stages/BaseSASVerificationStage.ts | 2 + ...onStage.ts => RequestVerificationStage.ts} | 17 ++- .../stages/SelectVerificationMethodStage.ts | 93 ++++++++++++ ...tage.ts => SendAcceptVerificationStage.ts} | 44 +++--- .../verification/SAS/stages/SendKeyStage.ts | 33 +++-- .../verification/SAS/stages/constants.ts | 14 ++ 10 files changed, 308 insertions(+), 78 deletions(-) create mode 100644 src/matrix/verification/SAS/channel/types.ts rename src/matrix/verification/SAS/stages/{StartVerificationStage.ts => RequestVerificationStage.ts} (78%) create mode 100644 src/matrix/verification/SAS/stages/SelectVerificationMethodStage.ts rename src/matrix/verification/SAS/stages/{AcceptVerificationStage.ts => SendAcceptVerificationStage.ts} (68%) create mode 100644 src/matrix/verification/SAS/stages/constants.ts diff --git a/src/matrix/verification/CrossSigning.ts b/src/matrix/verification/CrossSigning.ts index c5f22786..83b95052 100644 --- a/src/matrix/verification/CrossSigning.ts +++ b/src/matrix/verification/CrossSigning.ts @@ -129,6 +129,7 @@ export class CrossSigning { otherUserId: userId, platform: this.platform, deviceMessageHandler: this.deviceMessageHandler, + log }); return new SASVerification({ room, diff --git a/src/matrix/verification/SAS/SASVerification.ts b/src/matrix/verification/SAS/SASVerification.ts index 960e63a6..aa19a6e7 100644 --- a/src/matrix/verification/SAS/SASVerification.ts +++ b/src/matrix/verification/SAS/SASVerification.ts @@ -13,10 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import {StartVerificationStage} from "./stages/StartVerificationStage"; -import {WaitForIncomingMessageStage} from "./stages/WaitForIncomingMessageStage"; -import {AcceptVerificationStage} from "./stages/AcceptVerificationStage"; -import {SendKeyStage} from "./stages/SendKeyStage"; +import {RequestVerificationStage} from "./stages/RequestVerificationStage"; import type {ILogItem} from "../../../logging/types"; import type {Room} from "../../room/Room.js"; import type {Platform} from "../../../platform/web/Platform.js"; @@ -48,23 +45,23 @@ export class SASVerification { // channel.send("m.key.verification.request", {}, log); try { const options = { room, ourUser, otherUserId, log, olmSas, olmUtil, channel }; - let stage: BaseSASVerificationStage = new StartVerificationStage(options); + let stage: BaseSASVerificationStage = new RequestVerificationStage(options); this.startStage = stage; - stage.setNextStage(new WaitForIncomingMessageStage("m.key.verification.ready", options)); - stage = stage.nextStage; + // stage.setNextStage(new WaitForIncomingMessageStage("m.key.verification.ready", options)); + // stage = stage.nextStage; - stage.setNextStage(new WaitForIncomingMessageStage("m.key.verification.start", options)); - stage = stage.nextStage; + // stage.setNextStage(new WaitForIncomingMessageStage("m.key.verification.start", options)); + // stage = stage.nextStage; - stage.setNextStage(new AcceptVerificationStage(options)); - stage = stage.nextStage; + // stage.setNextStage(new AcceptVerificationStage(options)); + // stage = stage.nextStage; - stage.setNextStage(new WaitForIncomingMessageStage("m.key.verification.key", options)); - stage = stage.nextStage; + // stage.setNextStage(new WaitForIncomingMessageStage("m.key.verification.key", options)); + // stage = stage.nextStage; - stage.setNextStage(new SendKeyStage(options)); - stage = stage.nextStage; + // stage.setNextStage(new SendKeyStage(options)); + // stage = stage.nextStage; console.log("startStage", this.startStage); } finally { diff --git a/src/matrix/verification/SAS/channel/Channel.ts b/src/matrix/verification/SAS/channel/Channel.ts index e2de538a..3b390616 100644 --- a/src/matrix/verification/SAS/channel/Channel.ts +++ b/src/matrix/verification/SAS/channel/Channel.ts @@ -20,21 +20,38 @@ import type {ILogItem} from "../../../../logging/types"; import type {Platform} from "../../../../platform/web/Platform.js"; import type {DeviceMessageHandler} from "../../../DeviceMessageHandler.js"; import {makeTxnId} from "../../../common.js"; +import {CancelTypes, VerificationEventTypes} from "./types"; + +const messageFromErrorType = { + [CancelTypes.UserCancelled]: "User cancelled this request.", + [CancelTypes.InvalidMessage]: "Invalid Message.", + [CancelTypes.KeyMismatch]: "Key Mismatch.", + [CancelTypes.OtherUserAccepted]: "Another device has accepted this request.", + [CancelTypes.TimedOut]: "Timed Out", + [CancelTypes.UnexpectedMessage]: "Unexpected Message.", + [CancelTypes.UnknownMethod]: "Unknown method.", + [CancelTypes.UnknownTransaction]: "Unknown Transaction.", + [CancelTypes.UserMismatch]: "User Mismatch", +} const enum ChannelType { MessageEvent, ToDeviceMessage, } -const enum VerificationEventTypes { - Request = "m.key.verification.request", - Ready = "m.key.verification.ready", -} - export interface IChannel { send(eventType: string, content: any, log: ILogItem): Promise; - waitForEvent(eventType: string): any; + waitForEvent(eventType: string): Promise; type: ChannelType; + id: string; + sentMessages: Map; + receivedMessages: Map; + localMessages: Map; + setStartMessage(content: any): void; + setInitiatedByUs(value: boolean): void; + initiatedByUs: boolean; + startMessage: any; + cancelVerification(cancellationType: CancelTypes): Promise; } type Options = { @@ -43,6 +60,7 @@ type Options = { otherUserId: string; platform: Platform; deviceMessageHandler: DeviceMessageHandler; + log: ILogItem; } export class ToDeviceChannel implements IChannel { @@ -51,15 +69,22 @@ export class ToDeviceChannel implements IChannel { private readonly otherUserId: string; private readonly platform: Platform; private readonly deviceMessageHandler: DeviceMessageHandler; - private readonly sentMessages: Map = new Map(); - private readonly receivedMessages: Map = new Map(); + public readonly sentMessages: Map = new Map(); + public readonly receivedMessages: Map = new Map(); + public readonly localMessages: Map = new Map(); private readonly waitMap: Map}> = new Map(); + private readonly log: ILogItem; + private otherUserDeviceId: string; + public startMessage: any; + public id: string; + private _initiatedByUs: boolean; constructor(options: Options) { this.hsApi = options.hsApi; this.deviceTracker = options.deviceTracker; this.otherUserId = options.otherUserId; this.platform = options.platform; + this.log = options.log; this.deviceMessageHandler = options.deviceMessageHandler; // todo: find a way to dispose this subscription this.deviceMessageHandler.on("message", ({unencrypted}) => this.handleDeviceMessage(unencrypted)) @@ -74,17 +99,28 @@ export class ToDeviceChannel implements IChannel { if (eventType === VerificationEventTypes.Request) { // Handle this case specially await this.handleRequestEventSpecially(eventType, content, log); + this.sentMessages.set(eventType, content); return; } + Object.assign(content, { transaction_id: this.id }); + const payload = { + messages: { + [this.otherUserId]: { + // check if the following is undefined? + [this.otherUserDeviceId]: content + } + } + } + await this.hsApi.sendToDevice(eventType, payload, this.id, { log }).response(); + this.sentMessages.set(eventType, content); }); } async handleRequestEventSpecially(eventType: string, content: any, log: ILogItem) { await log.wrap("ToDeviceChannel.handleRequestEventSpecially", async () => { - const devices = await this.deviceTracker.devicesForUsers([this.otherUserId], this.hsApi, log); - console.log("devices", devices); const timestamp = this.platform.clock.now(); const txnId = makeTxnId(); + this.id = txnId; Object.assign(content, { timestamp, transaction_id: txnId }); const payload = { messages: { @@ -93,14 +129,64 @@ export class ToDeviceChannel implements IChannel { } } } - this.hsApi.sendToDevice(eventType, payload, txnId, { log }); + await this.hsApi.sendToDevice(eventType, payload, txnId, { log }).response(); }); } - handleDeviceMessage(event) { - console.log("event", event); - this.resolveAnyWaits(event); - this.receivedMessages.set(event.type, event); + + private handleDeviceMessage(event) { + this.log.wrap("ToDeviceChannel.handleDeviceMessage", (log) => { + console.log("event", event); + log.set("event", event); + this.resolveAnyWaits(event); + this.receivedMessages.set(event.type, event); + if (event.type === VerificationEventTypes.Ready) { + this.handleReadyMessage(event, log); + } + }); + } + + private async handleReadyMessage(event, log: ILogItem) { + try { + const fromDevice = event.content.from_device; + this.otherUserDeviceId = fromDevice; + // We need to send cancel messages to all other devices + const devices = await this.deviceTracker.devicesForUsers([this.otherUserId], this.hsApi, log); + const otherDevices = devices.filter(device => device.deviceId !== fromDevice); + const cancelMessage = { + code: CancelTypes.OtherUserAccepted, + reason: "An user already accepted this request!", + transaction_id: this.id, + }; + const deviceMessages = otherDevices.reduce((acc, device) => { acc[device.deviceId] = cancelMessage; return acc; }, {}); + const payload = { + messages: { + [this.otherUserId]: deviceMessages + } + } + await this.hsApi.sendToDevice(VerificationEventTypes.Cancel, payload, this.id, { log }).response(); + } + catch (e) { + console.log(e); + // Do something here + } + } + + async cancelVerification(cancellationType: CancelTypes) { + await this.log.wrap("Channel.cancelVerification", async log => { + const payload = { + messages: { + [this.otherUserId]: { + [this.otherUserDeviceId]: { + code: cancellationType, + reason: messageFromErrorType[cancellationType], + transaction_id: this.id, + } + } + } + } + await this.hsApi.sendToDevice(VerificationEventTypes.Cancel, payload, this.id, { log }).response(); + }); } private resolveAnyWaits(event) { @@ -113,15 +199,34 @@ export class ToDeviceChannel implements IChannel { } waitForEvent(eventType: string): Promise { + // Check if we already received the message + const receivedMessage = this.receivedMessages.get(eventType); + if (receivedMessage) { + return Promise.resolve(receivedMessage); + } + // Check if we're already waiting for this message const existingWait = this.waitMap.get(eventType); if (existingWait) { return existingWait.promise; } let resolve; + // Add to wait map const promise = new Promise(r => { resolve = r; }); this.waitMap.set(eventType, { resolve, promise }); return promise; } + + setStartMessage(event) { + this.startMessage = event; + } + + setInitiatedByUs(value: boolean): void { + this._initiatedByUs = value; + } + + get initiatedByUs(): boolean { + return this._initiatedByUs; + }; } diff --git a/src/matrix/verification/SAS/channel/types.ts b/src/matrix/verification/SAS/channel/types.ts new file mode 100644 index 00000000..68c5bb89 --- /dev/null +++ b/src/matrix/verification/SAS/channel/types.ts @@ -0,0 +1,20 @@ +export const enum VerificationEventTypes { + Request = "m.key.verification.request", + Ready = "m.key.verification.ready", + Start = "m.key.verification.start", + Accept = "m.key.verification.accept", + Key = "m.key.verification.key", + Cancel = "m.key.verification.cancel", +} + +export const enum CancelTypes { + UserCancelled = "m.user", + TimedOut = "m.timeout", + UnknownTransaction = "m.unknown_transaction", + UnknownMethod = "m.unknown_method", + UnexpectedMessage = "m.unexpected_message", + KeyMismatch = "m.key_mismatch", + UserMismatch = "m.user_mismatch", + InvalidMessage = "m.invalid_message", + OtherUserAccepted = "m.accepted", +} diff --git a/src/matrix/verification/SAS/stages/BaseSASVerificationStage.ts b/src/matrix/verification/SAS/stages/BaseSASVerificationStage.ts index 8d275f4c..31ed908f 100644 --- a/src/matrix/verification/SAS/stages/BaseSASVerificationStage.ts +++ b/src/matrix/verification/SAS/stages/BaseSASVerificationStage.ts @@ -47,9 +47,11 @@ export abstract class BaseSASVerificationStage extends Disposables { protected previousResult: undefined | any; protected _nextStage: BaseSASVerificationStage; protected channel: IChannel; + protected options: Options; constructor(options: Options) { super(); + this.options = options; this.room = options.room; this.ourUser = options.ourUser; this.otherUserId = options.otherUserId; diff --git a/src/matrix/verification/SAS/stages/StartVerificationStage.ts b/src/matrix/verification/SAS/stages/RequestVerificationStage.ts similarity index 78% rename from src/matrix/verification/SAS/stages/StartVerificationStage.ts rename to src/matrix/verification/SAS/stages/RequestVerificationStage.ts index c31346a6..fe00649b 100644 --- a/src/matrix/verification/SAS/stages/StartVerificationStage.ts +++ b/src/matrix/verification/SAS/stages/RequestVerificationStage.ts @@ -15,8 +15,10 @@ limitations under the License. */ import {BaseSASVerificationStage} from "./BaseSASVerificationStage"; import {FragmentBoundaryEntry} from "../../../room/timeline/entries/FragmentBoundaryEntry.js"; +import {SelectVerificationMethodStage} from "./SelectVerificationMethodStage"; +import {VerificationEventTypes} from "../channel/types"; -export class StartVerificationStage extends BaseSASVerificationStage { +export class RequestVerificationStage extends BaseSASVerificationStage { async completeStage() { await this.log.wrap("StartVerificationStage.completeStage", async (log) => { @@ -27,13 +29,14 @@ export class StartVerificationStage extends BaseSASVerificationStage { // "msgtype": "m.key.verification.request", // "to": this.otherUserId, }; - const promise = this.trackEventId(); + // const promise = this.trackEventId(); // await this.room.sendEvent("m.room.message", content, null, log); - await this.channel.send("m.key.verification.request", content, log); - const c = await this.channel.waitForEvent("m.key.verification.ready"); - const eventId = await promise; - console.log("eventId", eventId); - this.setRequestEventId(eventId); + await this.channel.send(VerificationEventTypes.Request, content, log); + this._nextStage = new SelectVerificationMethodStage(this.options); + const readyContent = await this.channel.waitForEvent("m.key.verification.ready"); + // const eventId = await promise; + // console.log("eventId", eventId); + // this.setRequestEventId(eventId); this.dispose(); }); } diff --git a/src/matrix/verification/SAS/stages/SelectVerificationMethodStage.ts b/src/matrix/verification/SAS/stages/SelectVerificationMethodStage.ts new file mode 100644 index 00000000..2807abd6 --- /dev/null +++ b/src/matrix/verification/SAS/stages/SelectVerificationMethodStage.ts @@ -0,0 +1,93 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import {BaseSASVerificationStage} from "./BaseSASVerificationStage"; +import {KEY_AGREEMENT_LIST, HASHES_LIST, MAC_LIST, SAS_LIST} from "./constants"; +import {CancelTypes, VerificationEventTypes} from "../channel/types"; +import type {ILogItem} from "../../../../logging/types"; +import {SendAcceptVerificationStage} from "./SendAcceptVerificationStage"; + +export class SelectVerificationMethodStage extends BaseSASVerificationStage { + private hasSentStartMessage = false; + // should somehow emit something that tells the ui to hide the select option + private allowSelection = true; + + async completeStage() { + await this.log.wrap("SelectVerificationMethodStage.completeStage", async (log) => { + const startMessage = this.channel.waitForEvent(VerificationEventTypes.Start); + const acceptMessage = this.channel.waitForEvent(VerificationEventTypes.Accept); + const { content } = await Promise.race([startMessage, acceptMessage]); + if (content.method) { + // We received the start message + this.allowSelection = false; + if (this.hasSentStartMessage) { + await this.resolveStartConflict(); + } + else { + this.channel.setStartMessage(this.channel.receivedMessages.get(VerificationEventTypes.Start)); + this.channel.setInitiatedByUs(false); + } + } + else { + // We received the accept message + this.channel.setStartMessage(this.channel.sentMessages.get(VerificationEventTypes.Start)); + this.channel.setInitiatedByUs(true); + } + if (!this.channel.initiatedByUs) { + // We need to send the accept message next + this.setNextStage(new SendAcceptVerificationStage(this.options)); + } + this.dispose(); + }); + } + + async resolveStartConflict() { + const receivedStartMessage = this.channel.receivedMessages.get(VerificationEventTypes.Start); + const sentStartMessage = this.channel.sentMessages.get(VerificationEventTypes.Start); + if (receivedStartMessage.content.method !== sentStartMessage.content.method) { + await this.channel.cancelVerification(CancelTypes.UnexpectedMessage); + return; + } + // todo: what happens if we are verifying devices? user-ids would be the same in that case! + // In the case of conflict, the lexicographically smaller id wins + if (this.ourUser.userId < this.otherUserId) { + // use our stat message + this.channel.setStartMessage(sentStartMessage); + this.channel.setInitiatedByUs(true); + } + else { + this.channel.setStartMessage(receivedStartMessage); + this.channel.setInitiatedByUs(false); + } + } + + async selectEmojiMethod(log: ILogItem) { + if (!this.allowSelection) { return; } + const content = { + method: "m.sas.v1", + from_device: this.ourUser.deviceId, + key_agreement_protocols: KEY_AGREEMENT_LIST, + hashes: HASHES_LIST, + message_authentication_codes: MAC_LIST, + short_authentication_string: SAS_LIST, + }; + await this.channel.send(VerificationEventTypes.Start, content, log); + this.hasSentStartMessage = true; + } + + get type() { + return "m.key.verification.request"; + } +} diff --git a/src/matrix/verification/SAS/stages/AcceptVerificationStage.ts b/src/matrix/verification/SAS/stages/SendAcceptVerificationStage.ts similarity index 68% rename from src/matrix/verification/SAS/stages/AcceptVerificationStage.ts rename to src/matrix/verification/SAS/stages/SendAcceptVerificationStage.ts index a8d32046..9b1b2fcc 100644 --- a/src/matrix/verification/SAS/stages/AcceptVerificationStage.ts +++ b/src/matrix/verification/SAS/stages/SendAcceptVerificationStage.ts @@ -15,30 +15,18 @@ limitations under the License. */ import {BaseSASVerificationStage} from "./BaseSASVerificationStage"; import anotherjson from "another-json"; - -// From element-web -type KeyAgreement = "curve25519-hkdf-sha256" | "curve25519"; -type MacMethod = "hkdf-hmac-sha256.v2" | "org.matrix.msc3783.hkdf-hmac-sha256" | "hkdf-hmac-sha256" | "hmac-sha256"; - -const KEY_AGREEMENT_LIST: KeyAgreement[] = ["curve25519-hkdf-sha256", "curve25519"]; -const HASHES_LIST = ["sha256"]; -const MAC_LIST: MacMethod[] = [ - "hkdf-hmac-sha256.v2", - "org.matrix.msc3783.hkdf-hmac-sha256", - "hkdf-hmac-sha256", - "hmac-sha256", -]; -const SAS_LIST = ["decimal", "emoji"]; -const SAS_SET = new Set(SAS_LIST); - -export class AcceptVerificationStage extends BaseSASVerificationStage { +import type { KeyAgreement, MacMethod } from "./constants"; +import {HASHES_LIST, MAC_LIST, SAS_SET, KEY_AGREEMENT_LIST} from "./constants"; +import { VerificationEventTypes } from "../channel/types"; +import { SendKeyStage } from "./SendKeyStage"; +export class SendAcceptVerificationStage extends BaseSASVerificationStage { async completeStage() { - await this.log.wrap("AcceptVerificationStage.completeStage", async (log) => { - const event = this.previousResult["m.key.verification.start"]; + await this.log.wrap("SAcceptVerificationStage.completeStage", async (log) => { + const event = this.channel.startMessage; const content = { ...event.content, - "m.relates_to": event.relation, + // "m.relates_to": event.relation, }; console.log("content from event", content); const keyAgreement = intersection(KEY_AGREEMENT_LIST, new Set(content.key_agreement_protocols))[0]; @@ -63,12 +51,16 @@ export class AcceptVerificationStage extends BaseSASVerificationStage { rel_type: "m.reference", } }; - await this.room.sendEvent("m.key.verification.accept", contentToSend, null, log); - this.nextStage?.setResultFromPreviousStage({ - ...this.previousResult, - [this.type]: contentToSend, - "our_pub_key": ourPubKey, - }); + // await this.room.sendEvent("m.key.verification.accept", contentToSend, null, log); + await this.channel.send(VerificationEventTypes.Accept, contentToSend, log); + this.channel.localMessages.set("our_pub_key", ourPubKey); + await this.channel.waitForEvent(VerificationEventTypes.Key); + this._nextStage = new SendKeyStage(this.options); + // this.nextStage?.setResultFromPreviousStage({ + // ...this.previousResult, + // [this.type]: contentToSend, + // "our_pub_key": ourPubKey, + // }); this.dispose(); }); } diff --git a/src/matrix/verification/SAS/stages/SendKeyStage.ts b/src/matrix/verification/SAS/stages/SendKeyStage.ts index f4e6d743..199ec5d6 100644 --- a/src/matrix/verification/SAS/stages/SendKeyStage.ts +++ b/src/matrix/verification/SAS/stages/SendKeyStage.ts @@ -16,6 +16,7 @@ limitations under the License. import {BaseSASVerificationStage} from "./BaseSASVerificationStage"; import {generateEmojiSas} from "../generator"; import {ILogItem} from "../../../../lib"; +import { VerificationEventTypes } from "../channel/types"; // From element-web type KeyAgreement = "curve25519-hkdf-sha256" | "curve25519"; @@ -41,31 +42,30 @@ type SASUserInfo = { type SASUserInfoCollection = { our: SASUserInfo; their: SASUserInfo; - requestId: string; + id: string; + initiatedByMe: boolean; }; const calculateKeyAgreement = { // eslint-disable-next-line @typescript-eslint/naming-convention "curve25519-hkdf-sha256": function (sas: SASUserInfoCollection, olmSAS: Olm.SAS, bytes: number): Uint8Array { - console.log("sas.requestId", sas.requestId); + console.log("sas.requestId", sas.id); const ourInfo = `${sas.our.userId}|${sas.our.deviceId}|` + `${sas.our.publicKey}|`; const theirInfo = `${sas.their.userId}|${sas.their.deviceId}|${sas.their.publicKey}|`; console.log("ourInfo", ourInfo); console.log("theirInfo", theirInfo); - const initiatedByMe = false; const sasInfo = "MATRIX_KEY_VERIFICATION_SAS|" + - (initiatedByMe ? ourInfo + theirInfo : theirInfo + ourInfo) + sas.requestId; + (sas.initiatedByMe ? ourInfo + theirInfo : theirInfo + ourInfo) + sas.id; console.log("sasInfo", sasInfo); return olmSAS.generate_bytes(sasInfo, bytes); }, "curve25519": function (sas: SASUserInfoCollection, olmSAS: Olm.SAS, bytes: number): Uint8Array { const ourInfo = `${sas.our.userId}${sas.our.deviceId}`; const theirInfo = `${sas.their.userId}${sas.their.deviceId}`; - const initiatedByMe = false; const sasInfo = "MATRIX_KEY_VERIFICATION_SAS" + - (initiatedByMe ? ourInfo + theirInfo : theirInfo + ourInfo) + sas.requestId; + (sas.initiatedByMe ? ourInfo + theirInfo : theirInfo + ourInfo) + sas.id; return olmSAS.generate_bytes(sasInfo, bytes); }, } as const; @@ -87,17 +87,18 @@ export class SendKeyStage extends BaseSASVerificationStage { private async sendKey(key: string, log: ILogItem): Promise { const contentToSend = { key, - "m.relates_to": { - event_id: this.requestEventId, - rel_type: "m.reference", - }, + // "m.relates_to": { + // event_id: this.requestEventId, + // rel_type: "m.reference", + // }, }; - await this.room.sendEvent("m.key.verification.key", contentToSend, null, log); + await this.channel.send(VerificationEventTypes.Key, contentToSend, log); + // await this.room.sendEvent("m.key.verification.key", contentToSend, null, log); } private generateSASBytes(): Uint8Array { - const keyAgreement = this.previousResult["m.key.verification.accept"].key_agreement_protocol; - const otherUserDeviceId = this.previousResult["m.key.verification.start"].content.from_device; + const keyAgreement = this.channel.sentMessages.get(VerificationEventTypes.Accept).key_agreement_protocol; + const otherUserDeviceId = this.channel.startMessage.content.from_device; const sasBytes = calculateKeyAgreement[keyAgreement]({ our: { userId: this.ourUser.userId, @@ -109,7 +110,8 @@ export class SendKeyStage extends BaseSASVerificationStage { deviceId: otherUserDeviceId, publicKey: this.theirKey, }, - requestId: this.requestEventId, + id: this.channel.id, + initiatedByMe: this.channel.initiatedByUs, }, this.olmSAS, 6); return sasBytes; } @@ -119,7 +121,8 @@ export class SendKeyStage extends BaseSASVerificationStage { } get theirKey(): string { - return this.previousResult["m.key.verification.key"].content.key; + const { content } = this.channel.receivedMessages.get(VerificationEventTypes.Key); + return content.key; } } diff --git a/src/matrix/verification/SAS/stages/constants.ts b/src/matrix/verification/SAS/stages/constants.ts new file mode 100644 index 00000000..8112ce44 --- /dev/null +++ b/src/matrix/verification/SAS/stages/constants.ts @@ -0,0 +1,14 @@ +// From element-web +export type KeyAgreement = "curve25519-hkdf-sha256" | "curve25519"; +export type MacMethod = "hkdf-hmac-sha256.v2" | "org.matrix.msc3783.hkdf-hmac-sha256" | "hkdf-hmac-sha256" | "hmac-sha256"; + +export const KEY_AGREEMENT_LIST: KeyAgreement[] = ["curve25519-hkdf-sha256", "curve25519"]; +export const HASHES_LIST = ["sha256"]; +export const MAC_LIST: MacMethod[] = [ + "hkdf-hmac-sha256.v2", + "org.matrix.msc3783.hkdf-hmac-sha256", + "hkdf-hmac-sha256", + "hmac-sha256", +]; +export const SAS_LIST = ["decimal", "emoji"]; +export const SAS_SET = new Set(SAS_LIST);