1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-07-31 15:24:23 +03:00

Element-R: Basic implementation of SAS verification (#3490)

* Return uploaded keys from `/keys/query`

* Basic implementation of SAS verification in Rust

* Update the `verifier` *before* emitting `erificationRequestEvent.Change`

* remove dead code
This commit is contained in:
Richard van der Hoff
2023-06-26 09:48:44 +01:00
committed by GitHub
parent f16a6bc654
commit 48c4127035
9 changed files with 558 additions and 31 deletions

View File

@ -55,7 +55,7 @@
],
"dependencies": {
"@babel/runtime": "^7.12.5",
"@matrix-org/matrix-sdk-crypto-js": "^0.1.0-alpha.10",
"@matrix-org/matrix-sdk-crypto-js": "^0.1.0-alpha.11",
"another-json": "^0.2.0",
"bs58": "^5.0.0",
"content-type": "^1.0.4",

View File

@ -16,6 +16,7 @@ limitations under the License.
import fetchMock from "fetch-mock-jest";
import { MockResponse } from "fetch-mock";
import "fake-indexeddb/auto";
import { createClient, CryptoEvent, MatrixClient } from "../../../src";
import {
@ -41,6 +42,7 @@ import {
} from "../../test-utils/test-data";
import { mockInitialApiRequests } from "../../test-utils/mockEndpoints";
import { E2EKeyResponder } from "../../test-utils/E2EKeyResponder";
import { E2EKeyReceiver } from "../../test-utils/E2EKeyReceiver";
// The verification flows use javascript timers to set timeouts. We tell jest to use mock timer implementations
// to ensure that we don't end up with dangling timeouts.
@ -48,7 +50,7 @@ jest.useFakeTimers();
let previousCrypto: Crypto | undefined;
beforeAll(() => {
beforeAll(async () => {
// Stub out global.crypto
previousCrypto = global["crypto"];
@ -60,6 +62,9 @@ beforeAll(() => {
},
},
});
// we use the libolm primitives in the test, so init the Olm library
await global.Olm.init();
});
// restore the original global.crypto
@ -105,6 +110,9 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
/** an object which intercepts `/keys/query` requests from {@link #aliceClient} */
let e2eKeyResponder: E2EKeyResponder;
/** an object which intercepts `/keys/upload` requests from {@link #aliceClient} */
let e2eKeyReceiver: E2EKeyReceiver;
beforeEach(async () => {
// anything that we don't have a specific matcher for silently returns a 404
fetchMock.catch(404);
@ -121,7 +129,10 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
await initCrypto(aliceClient);
e2eKeyReceiver = new E2EKeyReceiver(aliceClient.getHomeserverUrl());
e2eKeyResponder = new E2EKeyResponder(aliceClient.getHomeserverUrl());
e2eKeyResponder.addKeyReceiver(TEST_USER_ID, e2eKeyReceiver);
syncResponder = new SyncResponder(aliceClient.getHomeserverUrl());
mockInitialApiRequests(aliceClient.getHomeserverUrl());
await aliceClient.startClient();
@ -129,6 +140,10 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
afterEach(async () => {
await aliceClient.stopClient();
// Allow in-flight things to complete before we tear down the test
await jest.runAllTimersAsync();
fetchMock.mockReset();
});
@ -138,7 +153,9 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
e2eKeyResponder.addDeviceKeys(TEST_USER_ID, TEST_DEVICE_ID, SIGNED_TEST_DEVICE_DATA);
});
oldBackendOnly("can verify via SAS", async () => {
it("can verify another device via SAS", async () => {
await waitForDeviceList();
// have alice initiate a verification. She should send a m.key.verification.request
let [requestBody, request] = await Promise.all([
expectSendToDeviceMessage("m.key.verification.request"),
@ -189,22 +206,29 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
short_authentication_string: ["decimal", "emoji"],
},
});
await waitForVerificationRequestChanged(request);
// as soon as the Changed event arrives, `verifier` should be defined
const verifier = await new Promise<Verifier>((resolve) => {
function onChange() {
expect(request.phase).toEqual(VerificationPhase.Started);
expect(request.otherPartySupportsMethod("m.sas.v1")).toBe(true);
expect(request.chosenMethod).toEqual("m.sas.v1");
// there should now be a verifier
const verifier: Verifier = request.verifier!;
expect(verifier).toBeDefined();
expect(verifier.getShowSasCallbacks()).toBeNull();
resolve(verifier);
}
request.once(VerificationRequestEvent.Change, onChange);
});
// start off the verification process: alice will send an `accept`
const sendToDevicePromise = expectSendToDeviceMessage("m.key.verification.accept");
const verificationPromise = verifier.verify();
// advance the clock, because the devicelist likes to sleep for 5ms during key downloads
jest.advanceTimersByTime(10);
requestBody = await expectSendToDeviceMessage("m.key.verification.accept");
requestBody = await sendToDevicePromise;
toDeviceMessage = requestBody.messages[TEST_USER_ID][TEST_DEVICE_ID];
expect(toDeviceMessage.key_agreement_protocol).toEqual("curve25519-hkdf-sha256");
expect(toDeviceMessage.short_authentication_string).toEqual(["decimal", "emoji"]);
@ -281,15 +305,9 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
});
oldBackendOnly("can verify another via QR code with an untrusted cross-signing key", async () => {
e2eKeyResponder.addCrossSigningData(SIGNED_CROSS_SIGNING_KEYS_DATA);
// QRCode fails if we don't yet have the cross-signing keys, so make sure we have them now.
//
// Completing the initial sync will make the device list download outdated device lists (of which our own
// user will be one).
syncResponder.sendOrQueueSyncResponse({});
// DeviceList has a sleep(5) which we need to make happen
await jest.advanceTimersByTimeAsync(10);
e2eKeyResponder.addCrossSigningData(SIGNED_CROSS_SIGNING_KEYS_DATA);
await waitForDeviceList();
expect(aliceClient.getStoredCrossSigningForUser(TEST_USER_ID)).toBeTruthy();
// have alice initiate a verification. She should send a m.key.verification.request
@ -377,7 +395,9 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
expect(request.phase).toEqual(VerificationPhase.Done);
});
oldBackendOnly("can cancel during the SAS phase", async () => {
it("can cancel during the SAS phase", async () => {
await waitForDeviceList();
// have alice initiate a verification. She should send a m.key.verification.request
const [, request] = await Promise.all([
expectSendToDeviceMessage("m.key.verification.request"),
@ -419,12 +439,13 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
expect(verifier.hasBeenCancelled).toBe(false);
// start off the verification process: alice will send an `accept`
const sendToDevicePromise = expectSendToDeviceMessage("m.key.verification.accept");
const verificationPromise = verifier.verify();
// advance the clock, because the devicelist likes to sleep for 5ms during key downloads
jest.advanceTimersByTime(10);
await expectSendToDeviceMessage("m.key.verification.accept");
await sendToDevicePromise;
// now we unceremoniously cancel
// now we unceremoniously cancel. We expect the verificatationPromise to reject.
const requestPromise = expectSendToDeviceMessage("m.key.verification.cancel");
verifier.cancel(new Error("blah"));
await requestPromise;
@ -479,6 +500,19 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
});
});
/** make sure that the client knows about the dummy device */
async function waitForDeviceList(): Promise<void> {
// Completing the initial sync will make the device list download outdated device lists (of which our own
// user will be one).
syncResponder.sendOrQueueSyncResponse({});
// DeviceList has a sleep(5) which we need to make happen
await jest.advanceTimersByTimeAsync(10);
// The client should now know about the dummy device
const devices = await aliceClient.getCrypto()!.getUserDeviceInfo([TEST_USER_ID]);
expect(devices.get(TEST_USER_ID)!.keys()).toContain(TEST_DEVICE_ID);
}
function returnToDeviceMessageFromSync(ev: { type: string; content: object; sender?: string }): void {
ev.sender ??= TEST_USER_ID;
syncResponder.sendOrQueueSyncResponse({ to_device: { events: [ev] } });

View File

@ -145,6 +145,13 @@ export class E2EKeyReceiver implements IE2EKeyReceiver {
return this.deviceKeys.keys[keyIds[0]];
}
/**
* If the device keys have already been uploaded, return them. Else return null.
*/
public getUploadedDeviceKeys(): IDeviceKeys | null {
return this.deviceKeys;
}
/**
* If one-time keys have already been uploaded, return them. Otherwise,
* set up an expectation that the keys will be uploaded, and wait for

View File

@ -19,12 +19,14 @@ import fetchMock from "fetch-mock-jest";
import { MapWithDefault } from "../../src/utils";
import { IDownloadKeyResult } from "../../src";
import { IDeviceKeys } from "../../src/@types/crypto";
import { E2EKeyReceiver } from "./E2EKeyReceiver";
/**
* An object which intercepts `/keys/query` fetches via fetch-mock.
*/
export class E2EKeyResponder {
private deviceKeysByUserByDevice = new MapWithDefault<string, Map<string, any>>(() => new Map());
private e2eKeyReceiversByUser = new Map<string, E2EKeyReceiver>();
private masterKeysByUser: Record<string, any> = {};
private selfSigningKeysByUser: Record<string, any> = {};
private userSigningKeysByUser: Record<string, any> = {};
@ -61,6 +63,16 @@ export class E2EKeyResponder {
if (userKeys !== undefined) {
response.device_keys[user] = Object.fromEntries(userKeys.entries());
}
const e2eKeyReceiver = this.e2eKeyReceiversByUser.get(user);
if (e2eKeyReceiver !== undefined) {
const deviceKeys = e2eKeyReceiver.getUploadedDeviceKeys();
if (deviceKeys !== null) {
response.device_keys[user] ??= {};
response.device_keys[user][deviceKeys.device_id] = deviceKeys;
}
}
if (this.masterKeysByUser.hasOwnProperty(user)) {
response.master_keys[user] = this.masterKeysByUser[user];
}
@ -96,4 +108,16 @@ export class E2EKeyResponder {
Object.assign(this.selfSigningKeysByUser, data.self_signing_keys);
Object.assign(this.userSigningKeysByUser, data.user_signing_keys);
}
/**
* Add an E2EKeyReceiver to poll for uploaded keys
*
* Any keys which have been uploaded to the given `E2EKeyReceiver` at the time of the `/keys/query` request will
* be added to the response.
*
* @param e2eKeyReceiver
*/
public addKeyReceiver(userId: string, e2eKeyReceiver: E2EKeyReceiver) {
this.e2eKeyReceiversByUser.set(userId, e2eKeyReceiver);
}
}

View File

@ -494,6 +494,15 @@ describe("RustCrypto", () => {
expect(deviceMap.has(testData.TEST_DEVICE_ID)).toBe(true);
rustCrypto.stop();
});
describe("requestDeviceVerification", () => {
it("throws an error if the device is unknown", async () => {
const rustCrypto = await makeTestRustCrypto();
await expect(() => rustCrypto.requestDeviceVerification(TEST_USER, "unknown")).rejects.toThrow(
"Not a known device",
);
});
});
});
/** build a basic RustCrypto instance for testing

View File

@ -2238,6 +2238,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
this.secretStorage,
this.cryptoCallbacks,
);
rustCrypto.supportedVerificationMethods = this.verificationMethods;
this.cryptoBackend = rustCrypto;
// attach the event listeners needed by RustCrypto

View File

@ -51,6 +51,7 @@ import { secretStorageContainsCrossSigningKeys } from "./secret-storage";
import { keyFromPassphrase } from "../crypto/key_passphrase";
import { encodeRecoveryKey } from "../crypto/recoverykey";
import { crypto } from "../crypto/crypto";
import { RustVerificationRequest, verificationMethodIdentifierToMethod } from "./verification";
/**
* An implementation of {@link CryptoBackend} using the Rust matrix-sdk-crypto.
@ -562,6 +563,13 @@ export class RustCrypto implements CryptoBackend {
return;
}
/**
* The verification methods we offer to the other side during an interactive verification.
*
* If `undefined`, we will offer all the methods supported by the Rust SDK.
*/
public supportedVerificationMethods: string[] | undefined;
/**
* Send a verification request to our other devices.
*
@ -571,7 +579,7 @@ export class RustCrypto implements CryptoBackend {
*
* @returns a VerificationRequest when the request has been sent to the other party.
*/
public requestOwnUserVerification(): Promise<VerificationRequest> {
public async requestOwnUserVerification(): Promise<VerificationRequest> {
throw new Error("not implemented");
}
@ -587,8 +595,22 @@ export class RustCrypto implements CryptoBackend {
*
* @returns a VerificationRequest when the request has been sent to the other party.
*/
public requestDeviceVerification(userId: string, deviceId: string): Promise<VerificationRequest> {
throw new Error("not implemented");
public async requestDeviceVerification(userId: string, deviceId: string): Promise<VerificationRequest> {
const device: RustSdkCryptoJs.Device | undefined = await this.olmMachine.getDevice(
new RustSdkCryptoJs.UserId(userId),
new RustSdkCryptoJs.DeviceId(deviceId),
);
if (!device) {
throw new Error("Not a known device");
}
const [request, outgoingRequest]: [RustSdkCryptoJs.VerificationRequest, RustSdkCryptoJs.ToDeviceRequest] =
await device.requestVerification(
this.supportedVerificationMethods?.map(verificationMethodIdentifierToMethod),
);
await this.outgoingRequestProcessor.makeOutgoingRequest(outgoingRequest);
return new RustVerificationRequest(request, this.outgoingRequestProcessor);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,429 @@
/*
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 * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-js";
import { Emoji } from "@matrix-org/matrix-sdk-crypto-js";
import {
ShowQrCodeCallbacks,
ShowSasCallbacks,
VerificationPhase,
VerificationRequest,
VerificationRequestEvent,
VerificationRequestEventHandlerMap,
Verifier,
VerifierEvent,
VerifierEventHandlerMap,
} from "../crypto-api/verification";
import { TypedEventEmitter } from "../models/typed-event-emitter";
import { OutgoingRequest, OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
/**
* An incoming, or outgoing, request to verify a user or a device via cross-signing.
*/
export class RustVerificationRequest
extends TypedEventEmitter<VerificationRequestEvent, VerificationRequestEventHandlerMap>
implements VerificationRequest
{
private _verifier: Verifier | undefined;
public constructor(
private readonly inner: RustSdkCryptoJs.VerificationRequest,
outgoingRequestProcessor: OutgoingRequestProcessor,
) {
super();
const onChange = async (): Promise<void> => {
// if we now have a `Verification` where we lacked one before, wrap it.
// TODO: QR support
if (this._verifier === undefined) {
const verification: RustSdkCryptoJs.Qr | RustSdkCryptoJs.Sas | undefined = this.inner.getVerification();
if (verification instanceof RustSdkCryptoJs.Sas) {
this._verifier = new RustSASVerifier(verification, this, outgoingRequestProcessor);
}
}
this.emit(VerificationRequestEvent.Change);
};
inner.registerChangesCallback(onChange);
}
/**
* Unique ID for this verification request.
*
* An ID isn't assigned until the first message is sent, so this may be `undefined` in the early phases.
*/
public get transactionId(): string | undefined {
return this.inner.flowId;
}
/**
* For an in-room verification, the ID of the room.
*
* For to-device verifications, `undefined`.
*/
public get roomId(): string | undefined {
return this.inner.roomId?.toString();
}
/**
* True if this request was initiated by the local client.
*
* For in-room verifications, the initiator is who sent the `m.key.verification.request` event.
* For to-device verifications, the initiator is who sent the `m.key.verification.start` event.
*/
public get initiatedByMe(): boolean {
return this.inner.weStarted();
}
/** The user id of the other party in this request */
public get otherUserId(): string {
return this.inner.otherUserId.toString();
}
/** For verifications via to-device messages: the ID of the other device. Otherwise, undefined. */
public get otherDeviceId(): string | undefined {
return this.inner.otherDeviceId?.toString();
}
/** True if the other party in this request is one of this user's own devices. */
public get isSelfVerification(): boolean {
return this.inner.isSelfVerification();
}
/** current phase of the request. */
public get phase(): VerificationPhase {
const phase = this.inner.phase();
switch (phase) {
case RustSdkCryptoJs.VerificationRequestPhase.Created:
case RustSdkCryptoJs.VerificationRequestPhase.Requested:
return VerificationPhase.Requested;
case RustSdkCryptoJs.VerificationRequestPhase.Ready:
return VerificationPhase.Ready;
case RustSdkCryptoJs.VerificationRequestPhase.Transitioned:
return VerificationPhase.Started;
case RustSdkCryptoJs.VerificationRequestPhase.Done:
return VerificationPhase.Done;
case RustSdkCryptoJs.VerificationRequestPhase.Cancelled:
return VerificationPhase.Cancelled;
}
throw new Error(`Unknown verification phase ${phase}`);
}
/** True if the request has sent its initial event and needs more events to complete
* (ie it is in phase `Requested`, `Ready` or `Started`).
*/
public get pending(): boolean {
throw new Error("not implemented");
}
/**
* True if we have started the process of sending an `m.key.verification.ready` (but have not necessarily received
* the remote echo which causes a transition to {@link VerificationPhase.Ready}.
*/
public get accepting(): boolean {
throw new Error("not implemented");
}
/**
* True if we have started the process of sending an `m.key.verification.cancel` (but have not necessarily received
* the remote echo which causes a transition to {@link VerificationPhase.Cancelled}).
*/
public get declining(): boolean {
throw new Error("not implemented");
}
/**
* The remaining number of ms before the request will be automatically cancelled.
*
* `null` indicates that there is no timeout
*/
public get timeout(): number | null {
throw new Error("not implemented");
}
/** once the phase is Started (and !initiatedByMe) or Ready: common methods supported by both sides */
public get methods(): string[] {
throw new Error("not implemented");
}
/** the method picked in the .start event */
public get chosenMethod(): string | null {
const verification: RustSdkCryptoJs.Qr | RustSdkCryptoJs.Sas | undefined = this.inner.getVerification();
// TODO: this isn't quite right. The existence of a Verification doesn't prove that we have .started.
if (verification instanceof RustSdkCryptoJs.Sas) {
return "m.sas.v1";
} else {
return null;
}
}
/**
* Checks whether the other party supports a given verification method.
* This is useful when setting up the QR code UI, as it is somewhat asymmetrical:
* if the other party supports SCAN_QR, we should show a QR code in the UI, and vice versa.
* For methods that need to be supported by both ends, use the `methods` property.
*
* @param method - the method to check
* @returns true if the other party said they supported the method
*/
public otherPartySupportsMethod(method: string): boolean {
const theirMethods: RustSdkCryptoJs.VerificationMethod[] | undefined = this.inner.theirSupportedMethods;
if (theirMethods === undefined) {
// no message from the other side yet
return false;
}
const requiredMethod = verificationMethodsByIdentifier[method];
return theirMethods.some((m) => m === requiredMethod);
}
/**
* Accepts the request, sending a .ready event to the other party
*
* @returns Promise which resolves when the event has been sent.
*/
public accept(): Promise<void> {
throw new Error("not implemented");
}
/**
* Cancels the request, sending a cancellation to the other party
*
* @param params - Details for the cancellation, including `reason` (defaults to "User declined"), and `code`
* (defaults to `m.user`).
*
* @returns Promise which resolves when the event has been sent.
*/
public cancel(params?: { reason?: string; code?: string }): Promise<void> {
throw new Error("not implemented");
}
/**
* Create a {@link Verifier} to do this verification via a particular method.
*
* If a verifier has already been created for this request, returns that verifier.
*
* This does *not* send the `m.key.verification.start` event - to do so, call {@link Verifier#verifier} on the
* returned verifier.
*
* If no previous events have been sent, pass in `targetDevice` to set who to direct this request to.
*
* @param method - the name of the verification method to use.
* @param targetDevice - details of where to send the request to.
*
* @returns The verifier which will do the actual verification.
*/
public beginKeyVerification(method: string, targetDevice?: { userId?: string; deviceId?: string }): Verifier {
throw new Error("not implemented");
}
/**
* The verifier which is doing the actual verification, once the method has been established.
* Only defined when the `phase` is Started.
*/
public get verifier(): Verifier | undefined {
return this._verifier;
}
/**
* Get the data for a QR code allowing the other device to verify this one, if it supports it.
*
* Only set after a .ready if the other party can scan a QR code, otherwise undefined.
*/
public getQRCodeBytes(): Buffer | undefined {
// TODO
return undefined;
}
/**
* If this request has been cancelled, the cancellation code (e.g `m.user`) which is responsible for cancelling
* this verification.
*/
public get cancellationCode(): string | null {
throw new Error("not implemented");
}
/**
* The id of the user that cancelled the request.
*
* Only defined when phase is Cancelled
*/
public get cancellingUserId(): string | undefined {
throw new Error("not implemented");
}
}
export class RustSASVerifier extends TypedEventEmitter<VerifierEvent, VerifierEventHandlerMap> implements Verifier {
/** A promise which completes when the verification completes (or rejects when it is cancelled/fails) */
private readonly completionPromise: Promise<void>;
private callbacks: ShowSasCallbacks | null = null;
public constructor(
private readonly inner: RustSdkCryptoJs.Sas,
_verificationRequest: RustVerificationRequest,
private readonly outgoingRequestProcessor: OutgoingRequestProcessor,
) {
super();
this.completionPromise = new Promise<void>((resolve, reject) => {
const onChange = async (): Promise<void> => {
this.updateCallbacks();
if (this.inner.isDone()) {
resolve(undefined);
} else if (this.inner.isCancelled()) {
const cancelInfo = this.inner.cancelInfo()!;
reject(
new Error(
`Verification cancelled by ${
cancelInfo.cancelledbyUs() ? "us" : "them"
} with code ${cancelInfo.cancelCode()}: ${cancelInfo.reason()}`,
),
);
}
};
inner.registerChangesCallback(onChange);
});
// stop the runtime complaining if nobody catches a failure
this.completionPromise.catch(() => null);
}
/** if we can now show the callbacks, do so */
private updateCallbacks(): void {
if (this.callbacks === null) {
const emoji: Array<Emoji> | undefined = this.inner.emoji();
const decimal = this.inner.decimals() as [number, number, number] | undefined;
if (emoji === undefined && decimal === undefined) {
return;
}
this.callbacks = {
sas: {
decimal: decimal,
emoji: emoji?.map((e) => [e.symbol, e.description]),
},
confirm: async (): Promise<void> => {
const requests: Array<OutgoingRequest> = await this.inner.confirm();
for (const m of requests) {
await this.outgoingRequestProcessor.makeOutgoingRequest(m);
}
},
mismatch: (): void => {
throw new Error("impl");
},
cancel: (): void => {
throw new Error("impl");
},
};
this.emit(VerifierEvent.ShowSas, this.callbacks);
}
}
/**
* Returns true if the verification has been cancelled, either by us or the other side.
*/
public get hasBeenCancelled(): boolean {
return this.inner.isCancelled();
}
/**
* The ID of the other user in the verification process.
*/
public get userId(): string {
return this.inner.otherUserId.toString();
}
/**
* Start the key verification, if it has not already been started.
*
* This means sending a `m.key.verification.start` if we are the first responder, or a `m.key.verification.accept`
* if the other side has already sent a start event.
*
* @returns Promise which resolves when the verification has completed, or rejects if the verification is cancelled
* or times out.
*/
public async verify(): Promise<void> {
const req: undefined | OutgoingRequest = this.inner.accept();
if (req) {
await this.outgoingRequestProcessor.makeOutgoingRequest(req);
}
await this.completionPromise;
}
/**
* Cancel a verification.
*
* We will send an `m.key.verification.cancel` if the verification is still in flight. The verification promise
* will reject, and a {@link Crypto.VerifierEvent#Cancel} will be emitted.
*
* @param e - the reason for the cancellation.
*/
public cancel(e: Error): void {
// TODO: something with `e`
const req: undefined | OutgoingRequest = this.inner.cancel();
if (req) {
this.outgoingRequestProcessor.makeOutgoingRequest(req);
}
}
/**
* Get the details for an SAS verification, if one is in progress
*
* Returns `null`, unless this verifier is for a SAS-based verification and we are waiting for the user to confirm
* the SAS matches.
*/
public getShowSasCallbacks(): ShowSasCallbacks | null {
return this.callbacks;
}
/**
* Get the details for reciprocating QR code verification, if one is in progress
*
* Returns `null`, unless this verifier is for reciprocating a QR-code-based verification (ie, the other user has
* already scanned our QR code), and we are waiting for the user to confirm.
*/
public getReciprocateQrCodeCallbacks(): ShowQrCodeCallbacks | null {
return null;
}
}
/** For each specced verification method, the rust-side `VerificationMethod` corresponding to it */
const verificationMethodsByIdentifier: Record<string, RustSdkCryptoJs.VerificationMethod> = {
"m.sas.v1": RustSdkCryptoJs.VerificationMethod.SasV1,
"m.qr_code.scan.v1": RustSdkCryptoJs.VerificationMethod.QrCodeScanV1,
"m.qr_code.show.v1": RustSdkCryptoJs.VerificationMethod.QrCodeShowV1,
"m.reciprocate.v1": RustSdkCryptoJs.VerificationMethod.ReciprocateV1,
};
/**
* Convert a specced verification method identifier into a rust-side `VerificationMethod`.
*
* @param method - specced method identifier, for example `m.sas.v1`.
* @returns Rust-side `VerificationMethod` corresponding to `method`.
* @throws An error if the method is unknown.
*/
export function verificationMethodIdentifierToMethod(method: string): RustSdkCryptoJs.VerificationMethod {
const meth = verificationMethodsByIdentifier[method];
if (meth === undefined) {
throw new Error(`Unknown verification method ${method}`);
}
return meth;
}

View File

@ -1426,10 +1426,10 @@
dependencies:
lodash "^4.17.21"
"@matrix-org/matrix-sdk-crypto-js@^0.1.0-alpha.10":
version "0.1.0-alpha.10"
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.10.tgz#b6a6395cffd3197ae2e0a88f4eeae8b315571fd2"
integrity sha512-8V2NKuzGOFzEZeZVgF2is7gmuopdRbMZ064tzPDE0vN34iX6s3O8A4oxIT7SA3qtymwm3t1yEvTnT+0gfbmh4g==
"@matrix-org/matrix-sdk-crypto-js@^0.1.0-alpha.11":
version "0.1.0-alpha.11"
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.11.tgz#24d705318c3159ef7dbe43bca464ac2bdd11e45d"
integrity sha512-HD3rskPkqrUUSaKzGLg97k/bN+OZrkcX7ODB/pNBs/jqq+/A0wDKqsszJotzFwsQcDPpWn78BmMyvBo4tLxKjw==
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz":
version "3.2.14"