2026-09-13 14:14:38 -04:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
|
|
|
value: true
|
|
|
|
|
});
|
|
|
|
|
exports.RustVerificationRequest = exports.RustSASVerifier = exports.RustQrCodeVerifier = void 0;
|
|
|
|
|
exports.verificationMethodIdentifierToMethod = verificationMethodIdentifierToMethod;
|
|
|
|
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
|
|
|
var RustSdkCryptoJs = _interopRequireWildcard(require("@matrix-org/matrix-sdk-crypto-wasm"));
|
|
|
|
|
var _verification = require("../crypto-api/verification");
|
|
|
|
|
var _typedEventEmitter = require("../models/typed-event-emitter");
|
|
|
|
|
var _ReEmitter = require("../ReEmitter");
|
|
|
|
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
|
|
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
2026-09-12 23:57:45 -04:00
|
|
|
/*
|
|
|
|
|
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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* An incoming, or outgoing, request to verify a user or a device via cross-signing.
|
|
|
|
|
*
|
|
|
|
|
* @internal
|
|
|
|
|
*/
|
2026-09-13 14:14:38 -04:00
|
|
|
class RustVerificationRequest extends _typedEventEmitter.TypedEventEmitter {
|
2026-09-12 23:57:45 -04:00
|
|
|
/**
|
|
|
|
|
* Construct a new RustVerificationRequest to wrap the rust-level `VerificationRequest`.
|
|
|
|
|
*
|
2026-09-13 14:14:38 -04:00
|
|
|
* @param inner - VerificationRequest from the Rust SDK
|
|
|
|
|
* @param outgoingRequestProcessor - `OutgoingRequestProcessor` to use for making outgoing HTTP requests
|
|
|
|
|
* @param supportedVerificationMethods - Verification methods to use when `accept()` is called
|
2026-09-12 23:57:45 -04:00
|
|
|
*/
|
2026-09-13 14:14:38 -04:00
|
|
|
constructor(inner, outgoingRequestProcessor, supportedVerificationMethods) {
|
2026-09-12 23:57:45 -04:00
|
|
|
super();
|
|
|
|
|
this.inner = inner;
|
|
|
|
|
this.outgoingRequestProcessor = outgoingRequestProcessor;
|
|
|
|
|
this.supportedVerificationMethods = supportedVerificationMethods;
|
2026-09-13 14:14:38 -04:00
|
|
|
/** a reëmitter which relays VerificationRequestEvent.Changed events emitted by the verifier */
|
|
|
|
|
(0, _defineProperty2.default)(this, "reEmitter", void 0);
|
|
|
|
|
/** Are we in the process of sending an `m.key.verification.ready` event? */
|
|
|
|
|
(0, _defineProperty2.default)(this, "_accepting", false);
|
|
|
|
|
/** Are we in the process of sending an `m.key.verification.cancellation` event? */
|
|
|
|
|
(0, _defineProperty2.default)(this, "_cancelling", false);
|
|
|
|
|
(0, _defineProperty2.default)(this, "_verifier", void 0);
|
|
|
|
|
this.reEmitter = new _ReEmitter.TypedReEmitter(this);
|
|
|
|
|
const onChange = async () => {
|
|
|
|
|
const verification = this.inner.getVerification();
|
|
|
|
|
|
|
|
|
|
// If we now have a `Verification` where we lacked one before, or we have transitioned from QR to SAS,
|
|
|
|
|
// wrap the new rust Verification as a js-sdk Verifier.
|
|
|
|
|
if (verification instanceof RustSdkCryptoJs.Sas) {
|
|
|
|
|
if (this._verifier === undefined || this._verifier instanceof RustQrCodeVerifier) {
|
|
|
|
|
this.setVerifier(new RustSASVerifier(verification, this, outgoingRequestProcessor));
|
|
|
|
|
}
|
|
|
|
|
} else if (verification instanceof RustSdkCryptoJs.Qr && this._verifier === undefined) {
|
|
|
|
|
this.setVerifier(new RustQrCodeVerifier(verification, outgoingRequestProcessor));
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
2026-09-13 14:14:38 -04:00
|
|
|
this.emit(_verification.VerificationRequestEvent.Change);
|
|
|
|
|
};
|
|
|
|
|
inner.registerChangesCallback(onChange);
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
setVerifier(verifier) {
|
|
|
|
|
// if we already have a verifier, unsubscribe from its events
|
|
|
|
|
if (this._verifier) {
|
2026-09-13 14:14:38 -04:00
|
|
|
this.reEmitter.stopReEmitting(this._verifier, [_verification.VerificationRequestEvent.Change]);
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
this._verifier = verifier;
|
2026-09-13 14:14:38 -04:00
|
|
|
this.reEmitter.reEmit(this._verifier, [_verification.VerificationRequestEvent.Change]);
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
get transactionId() {
|
|
|
|
|
return this.inner.flowId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* For an in-room verification, the ID of the room.
|
|
|
|
|
*
|
|
|
|
|
* For to-device verifications, `undefined`.
|
|
|
|
|
*/
|
|
|
|
|
get roomId() {
|
2026-09-13 14:14:38 -04:00
|
|
|
var _this$inner$roomId;
|
|
|
|
|
return (_this$inner$roomId = this.inner.roomId) === null || _this$inner$roomId === void 0 ? void 0 : _this$inner$roomId.toString();
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
get initiatedByMe() {
|
|
|
|
|
return this.inner.weStarted();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** The user id of the other party in this request */
|
|
|
|
|
get otherUserId() {
|
|
|
|
|
return this.inner.otherUserId.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** For verifications via to-device messages: the ID of the other device. Otherwise, undefined. */
|
|
|
|
|
get otherDeviceId() {
|
2026-09-13 14:14:38 -04:00
|
|
|
var _this$inner$otherDevi;
|
|
|
|
|
return (_this$inner$otherDevi = this.inner.otherDeviceId) === null || _this$inner$otherDevi === void 0 ? void 0 : _this$inner$otherDevi.toString();
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** True if the other party in this request is one of this user's own devices. */
|
|
|
|
|
get isSelfVerification() {
|
|
|
|
|
return this.inner.isSelfVerification();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** current phase of the request. */
|
|
|
|
|
get phase() {
|
|
|
|
|
const phase = this.inner.phase();
|
|
|
|
|
switch (phase) {
|
|
|
|
|
case RustSdkCryptoJs.VerificationRequestPhase.Created:
|
|
|
|
|
case RustSdkCryptoJs.VerificationRequestPhase.Requested:
|
2026-09-13 14:14:38 -04:00
|
|
|
return _verification.VerificationPhase.Requested;
|
2026-09-12 23:57:45 -04:00
|
|
|
case RustSdkCryptoJs.VerificationRequestPhase.Ready:
|
|
|
|
|
// if we're still sending the `m.key.verification.ready`, that counts as "Requested" in the js-sdk's
|
|
|
|
|
// parlance.
|
2026-09-13 14:14:38 -04:00
|
|
|
return this._accepting ? _verification.VerificationPhase.Requested : _verification.VerificationPhase.Ready;
|
2026-09-12 23:57:45 -04:00
|
|
|
case RustSdkCryptoJs.VerificationRequestPhase.Transitioned:
|
|
|
|
|
if (!this._verifier) {
|
|
|
|
|
// this shouldn't happen, because the onChange handler should have created a _verifier.
|
|
|
|
|
throw new Error("VerificationRequest: inner phase == Transitioned but no verifier!");
|
|
|
|
|
}
|
|
|
|
|
return this._verifier.verificationPhase;
|
|
|
|
|
case RustSdkCryptoJs.VerificationRequestPhase.Done:
|
2026-09-13 14:14:38 -04:00
|
|
|
return _verification.VerificationPhase.Done;
|
2026-09-12 23:57:45 -04:00
|
|
|
case RustSdkCryptoJs.VerificationRequestPhase.Cancelled:
|
2026-09-13 14:14:38 -04:00
|
|
|
return _verification.VerificationPhase.Cancelled;
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
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`).
|
|
|
|
|
*/
|
|
|
|
|
get pending() {
|
|
|
|
|
if (this.inner.isPassive()) return false;
|
|
|
|
|
const phase = this.phase;
|
2026-09-13 14:14:38 -04:00
|
|
|
return phase !== _verification.VerificationPhase.Done && phase !== _verification.VerificationPhase.Cancelled;
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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}.
|
|
|
|
|
*/
|
|
|
|
|
get accepting() {
|
|
|
|
|
return this._accepting;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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}).
|
|
|
|
|
*/
|
|
|
|
|
get declining() {
|
|
|
|
|
return this._cancelling;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The remaining number of ms before the request will be automatically cancelled.
|
|
|
|
|
*
|
|
|
|
|
* `null` indicates that there is no timeout
|
|
|
|
|
*/
|
|
|
|
|
get timeout() {
|
|
|
|
|
return this.inner.timeRemainingMillis();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** once the phase is Started (and !initiatedByMe) or Ready: common methods supported by both sides */
|
|
|
|
|
get methods() {
|
|
|
|
|
throw new Error("not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** the method picked in the .start event */
|
|
|
|
|
get chosenMethod() {
|
2026-09-13 14:14:38 -04:00
|
|
|
if (this.phase !== _verification.VerificationPhase.Started) return null;
|
2026-09-12 23:57:45 -04:00
|
|
|
const verification = this.inner.getVerification();
|
|
|
|
|
if (verification instanceof RustSdkCryptoJs.Sas) {
|
2026-09-13 14:14:38 -04:00
|
|
|
return "m.sas.v1";
|
2026-09-12 23:57:45 -04:00
|
|
|
} else if (verification instanceof RustSdkCryptoJs.Qr) {
|
2026-09-13 14:14:38 -04:00
|
|
|
return "m.reciprocate.v1";
|
2026-09-12 23:57:45 -04:00
|
|
|
} 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
|
|
|
|
|
*/
|
|
|
|
|
otherPartySupportsMethod(method) {
|
|
|
|
|
const theirMethods = 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.
|
|
|
|
|
*/
|
|
|
|
|
async accept() {
|
|
|
|
|
if (this.inner.phase() !== RustSdkCryptoJs.VerificationRequestPhase.Requested || this._accepting) {
|
|
|
|
|
throw new Error(`Cannot accept a verification request in phase ${this.phase}`);
|
|
|
|
|
}
|
|
|
|
|
this._accepting = true;
|
|
|
|
|
try {
|
|
|
|
|
const req = this.inner.acceptWithMethods(this.supportedVerificationMethods.map(verificationMethodIdentifierToMethod));
|
|
|
|
|
if (req) {
|
|
|
|
|
await this.outgoingRequestProcessor.makeOutgoingRequest(req);
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
this._accepting = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// phase may have changed, so emit a 'change' event
|
2026-09-13 14:14:38 -04:00
|
|
|
this.emit(_verification.VerificationRequestEvent.Change);
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
async cancel(params) {
|
|
|
|
|
if (this._cancelling) {
|
|
|
|
|
// already cancelling; do nothing
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this._cancelling = true;
|
|
|
|
|
try {
|
|
|
|
|
const req = this.inner.cancel();
|
|
|
|
|
if (req) {
|
|
|
|
|
await this.outgoingRequestProcessor.makeOutgoingRequest(req);
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
this._cancelling = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
beginKeyVerification(method, targetDevice) {
|
|
|
|
|
throw new Error("not implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Send an `m.key.verification.start` event to start verification via a particular method.
|
|
|
|
|
*
|
|
|
|
|
* Implementation of {@link Crypto.VerificationRequest#startVerification}.
|
|
|
|
|
*
|
|
|
|
|
* @param method - the name of the verification method to use.
|
|
|
|
|
*/
|
|
|
|
|
async startVerification(method) {
|
2026-09-13 14:14:38 -04:00
|
|
|
if (method !== "m.sas.v1") {
|
2026-09-12 23:57:45 -04:00
|
|
|
throw new Error(`Unsupported verification method ${method}`);
|
|
|
|
|
}
|
|
|
|
|
const res = await this.inner.startSas();
|
|
|
|
|
if (res) {
|
|
|
|
|
const [, req] = res;
|
|
|
|
|
await this.outgoingRequestProcessor.makeOutgoingRequest(req);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// this should have triggered the onChange callback, and we should now have a verifier
|
|
|
|
|
if (!this._verifier) {
|
|
|
|
|
throw new Error("Still no verifier after startSas() call");
|
|
|
|
|
}
|
|
|
|
|
return this._verifier;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Start a QR code verification by providing a scanned QR code for this verification flow.
|
|
|
|
|
*
|
|
|
|
|
* Implementation of {@link Crypto.VerificationRequest#scanQRCode}.
|
|
|
|
|
*
|
|
|
|
|
* @param qrCodeData - the decoded QR code.
|
|
|
|
|
* @returns A verifier; call `.verify()` on it to wait for the other side to complete the verification flow.
|
|
|
|
|
*/
|
|
|
|
|
async scanQRCode(uint8Array) {
|
2026-09-13 14:14:38 -04:00
|
|
|
const scan = RustSdkCryptoJs.QrCodeScan.fromBytes(new Uint8ClampedArray(uint8Array));
|
2026-09-12 23:57:45 -04:00
|
|
|
const verifier = await this.inner.scanQrCode(scan);
|
|
|
|
|
|
|
|
|
|
// this should have triggered the onChange callback, and we should now have a verifier
|
|
|
|
|
if (!this._verifier) {
|
|
|
|
|
throw new Error("Still no verifier after scanQrCode() call");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we can immediately trigger the reciprocate request
|
|
|
|
|
const req = verifier.reciprocate();
|
|
|
|
|
if (req) {
|
|
|
|
|
await this.outgoingRequestProcessor.makeOutgoingRequest(req);
|
|
|
|
|
}
|
|
|
|
|
return this._verifier;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The verifier which is doing the actual verification, once the method has been established.
|
|
|
|
|
* Only defined when the `phase` is Started.
|
|
|
|
|
*/
|
|
|
|
|
get verifier() {
|
|
|
|
|
// It's possible for us to have a Verifier before a method has been chosen (in particular,
|
|
|
|
|
// if we are showing a QR code which the other device has not yet scanned. At that point, we could
|
|
|
|
|
// still switch to SAS).
|
|
|
|
|
//
|
|
|
|
|
// In that case, we should not return it to the application yet, since the application will not expect the
|
|
|
|
|
// Verifier to be replaced during the lifetime of the VerificationRequest.
|
2026-09-13 14:14:38 -04:00
|
|
|
return this.phase === _verification.VerificationPhase.Started ? this._verifier : undefined;
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Stub implementation of {@link Crypto.VerificationRequest#getQRCodeBytes}.
|
|
|
|
|
*/
|
|
|
|
|
getQRCodeBytes() {
|
|
|
|
|
throw new Error("getQRCodeBytes() unsupported in Rust Crypto; use generateQRCode() instead.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generate the data for a QR code allowing the other device to verify this one, if it supports it.
|
|
|
|
|
*
|
|
|
|
|
* Implementation of {@link Crypto.VerificationRequest#generateQRCode}.
|
|
|
|
|
*/
|
|
|
|
|
async generateQRCode() {
|
|
|
|
|
const innerVerifier = await this.inner.generateQrCode();
|
2026-09-13 14:14:38 -04:00
|
|
|
return Buffer.from(innerVerifier.toBytes());
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* If this request has been cancelled, the cancellation code (e.g `m.user`) which is responsible for cancelling
|
|
|
|
|
* this verification.
|
|
|
|
|
*/
|
|
|
|
|
get cancellationCode() {
|
2026-09-13 14:14:38 -04:00
|
|
|
throw new Error("not implemented");
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The id of the user that cancelled the request.
|
|
|
|
|
*
|
|
|
|
|
* Only defined when phase is Cancelled
|
|
|
|
|
*/
|
|
|
|
|
get cancellingUserId() {
|
2026-09-13 14:14:38 -04:00
|
|
|
throw new Error("not implemented");
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Common base class for `Verifier` implementations which wrap rust classes.
|
|
|
|
|
*
|
|
|
|
|
* The generic parameter `InnerType` is the type of the rust Verification class which we wrap.
|
|
|
|
|
*
|
|
|
|
|
* @internal
|
|
|
|
|
*/
|
2026-09-13 14:14:38 -04:00
|
|
|
exports.RustVerificationRequest = RustVerificationRequest;
|
|
|
|
|
class BaseRustVerifer extends _typedEventEmitter.TypedEventEmitter {
|
2026-09-12 23:57:45 -04:00
|
|
|
constructor(inner, outgoingRequestProcessor) {
|
|
|
|
|
super();
|
|
|
|
|
this.inner = inner;
|
|
|
|
|
this.outgoingRequestProcessor = outgoingRequestProcessor;
|
2026-09-13 14:14:38 -04:00
|
|
|
/** A promise which completes when the verification completes (or rejects when it is cancelled/fails) */
|
|
|
|
|
(0, _defineProperty2.default)(this, "completionPromise", void 0);
|
|
|
|
|
this.completionPromise = new Promise((resolve, reject) => {
|
|
|
|
|
const onChange = async () => {
|
|
|
|
|
this.onChange();
|
|
|
|
|
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()}`));
|
|
|
|
|
}
|
|
|
|
|
this.emit(_verification.VerificationRequestEvent.Change);
|
|
|
|
|
};
|
|
|
|
|
inner.registerChangesCallback(onChange);
|
|
|
|
|
});
|
2026-09-12 23:57:45 -04:00
|
|
|
// stop the runtime complaining if nobody catches a failure
|
2026-09-13 14:14:38 -04:00
|
|
|
this.completionPromise.catch(() => null);
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook which is called when the underlying rust class notifies us that there has been a change.
|
|
|
|
|
*
|
2026-09-13 14:14:38 -04:00
|
|
|
* Can be overridden by subclasses to see if we can notify the application about an update.
|
2026-09-12 23:57:45 -04:00
|
|
|
*/
|
2026-09-13 14:14:38 -04:00
|
|
|
onChange() {}
|
2026-09-12 23:57:45 -04:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns true if the verification has been cancelled, either by us or the other side.
|
|
|
|
|
*/
|
|
|
|
|
get hasBeenCancelled() {
|
|
|
|
|
return this.inner.isCancelled();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The ID of the other user in the verification process.
|
|
|
|
|
*/
|
|
|
|
|
get userId() {
|
|
|
|
|
return this.inner.otherUserId.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
cancel(e) {
|
|
|
|
|
// TODO: something with `e`
|
|
|
|
|
const req = 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.
|
|
|
|
|
*/
|
|
|
|
|
getShowSasCallbacks() {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
getReciprocateQrCodeCallbacks() {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** A Verifier instance which is used to show and/or scan a QR code. */
|
2026-09-13 14:14:38 -04:00
|
|
|
class RustQrCodeVerifier extends BaseRustVerifer {
|
2026-09-12 23:57:45 -04:00
|
|
|
constructor(inner, outgoingRequestProcessor) {
|
|
|
|
|
super(inner, outgoingRequestProcessor);
|
2026-09-13 14:14:38 -04:00
|
|
|
(0, _defineProperty2.default)(this, "callbacks", null);
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
onChange() {
|
|
|
|
|
// if the other side has scanned our QR code and sent us a "reciprocate" message, it is now time for the
|
|
|
|
|
// application to prompt the user to confirm their side.
|
|
|
|
|
if (this.callbacks === null && this.inner.hasBeenScanned()) {
|
|
|
|
|
this.callbacks = {
|
2026-09-13 14:14:38 -04:00
|
|
|
confirm: () => this.confirmScanning(),
|
2026-09-12 23:57:45 -04:00
|
|
|
cancel: () => this.cancel()
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Start the key verification, if it has not already been started.
|
|
|
|
|
*
|
|
|
|
|
* @returns Promise which resolves when the verification has completed, or rejects if the verification is cancelled
|
|
|
|
|
* or times out.
|
|
|
|
|
*/
|
|
|
|
|
async verify() {
|
|
|
|
|
// Some applications (hello, matrix-react-sdk) may not check if there is a `ShowQrCodeCallbacks` and instead
|
|
|
|
|
// register a `ShowReciprocateQr` listener which they expect to be called once `.verify` is called.
|
|
|
|
|
if (this.callbacks !== null) {
|
2026-09-13 14:14:38 -04:00
|
|
|
this.emit(_verification.VerifierEvent.ShowReciprocateQr, this.callbacks);
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
// Nothing to do here but wait.
|
2026-09-13 14:14:38 -04:00
|
|
|
await this.completionPromise;
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calculate an appropriate VerificationPhase for a VerificationRequest where this is the verifier.
|
|
|
|
|
*
|
|
|
|
|
* This is abnormally complicated because a rust-side QR Code verifier can span several verification phases.
|
|
|
|
|
*/
|
|
|
|
|
get verificationPhase() {
|
|
|
|
|
switch (this.inner.state()) {
|
2026-09-13 14:14:38 -04:00
|
|
|
case RustSdkCryptoJs.QrState.Created:
|
2026-09-12 23:57:45 -04:00
|
|
|
// we have created a QR for display; neither side has yet sent an `m.key.verification.start`.
|
2026-09-13 14:14:38 -04:00
|
|
|
return _verification.VerificationPhase.Ready;
|
|
|
|
|
case RustSdkCryptoJs.QrState.Scanned:
|
2026-09-12 23:57:45 -04:00
|
|
|
// other side has scanned our QR and sent an `m.key.verification.start` with `m.reciprocate.v1`
|
2026-09-13 14:14:38 -04:00
|
|
|
return _verification.VerificationPhase.Started;
|
|
|
|
|
case RustSdkCryptoJs.QrState.Confirmed:
|
2026-09-12 23:57:45 -04:00
|
|
|
// we have confirmed the other side's scan and sent an `m.key.verification.done`.
|
2026-09-13 14:14:38 -04:00
|
|
|
return _verification.VerificationPhase.Done;
|
|
|
|
|
case RustSdkCryptoJs.QrState.Reciprocated:
|
2026-09-12 23:57:45 -04:00
|
|
|
// although the rust SDK doesn't immediately send the `m.key.verification.start` on transition into this
|
|
|
|
|
// state, `RustVerificationRequest.scanQrCode` immediately calls `reciprocate()` and does so, so in practice
|
|
|
|
|
// we can treat the two the same.
|
2026-09-13 14:14:38 -04:00
|
|
|
return _verification.VerificationPhase.Started;
|
|
|
|
|
case RustSdkCryptoJs.QrState.Done:
|
|
|
|
|
return _verification.VerificationPhase.Done;
|
|
|
|
|
case RustSdkCryptoJs.QrState.Cancelled:
|
|
|
|
|
return _verification.VerificationPhase.Cancelled;
|
2026-09-12 23:57:45 -04:00
|
|
|
default:
|
|
|
|
|
throw new Error(`Unknown qr code state ${this.inner.state()}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
getReciprocateQrCodeCallbacks() {
|
|
|
|
|
return this.callbacks;
|
|
|
|
|
}
|
|
|
|
|
async confirmScanning() {
|
|
|
|
|
const req = this.inner.confirmScanning();
|
|
|
|
|
if (req) {
|
|
|
|
|
await this.outgoingRequestProcessor.makeOutgoingRequest(req);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** A Verifier instance which is used if we are exchanging emojis */
|
2026-09-13 14:14:38 -04:00
|
|
|
exports.RustQrCodeVerifier = RustQrCodeVerifier;
|
|
|
|
|
class RustSASVerifier extends BaseRustVerifer {
|
2026-09-12 23:57:45 -04:00
|
|
|
constructor(inner, _verificationRequest, outgoingRequestProcessor) {
|
|
|
|
|
super(inner, outgoingRequestProcessor);
|
2026-09-13 14:14:38 -04:00
|
|
|
(0, _defineProperty2.default)(this, "callbacks", null);
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
async verify() {
|
|
|
|
|
const req = this.inner.accept();
|
|
|
|
|
if (req) {
|
|
|
|
|
await this.outgoingRequestProcessor.makeOutgoingRequest(req);
|
|
|
|
|
}
|
2026-09-13 14:14:38 -04:00
|
|
|
await this.completionPromise;
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** if we can now show the callbacks, do so */
|
|
|
|
|
onChange() {
|
|
|
|
|
if (this.callbacks === null) {
|
|
|
|
|
const emoji = this.inner.emoji();
|
|
|
|
|
const decimal = this.inner.decimals();
|
|
|
|
|
if (emoji === undefined && decimal === undefined) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.callbacks = {
|
2026-09-13 14:14:38 -04:00
|
|
|
sas: {
|
|
|
|
|
decimal: decimal,
|
|
|
|
|
emoji: emoji === null || emoji === void 0 ? void 0 : emoji.map(e => [e.symbol, e.description])
|
|
|
|
|
},
|
2026-09-12 23:57:45 -04:00
|
|
|
confirm: async () => {
|
|
|
|
|
const requests = await this.inner.confirm();
|
|
|
|
|
for (const m of requests) {
|
|
|
|
|
await this.outgoingRequestProcessor.makeOutgoingRequest(m);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
mismatch: () => {
|
2026-09-13 14:14:38 -04:00
|
|
|
throw new Error("impl");
|
2026-09-12 23:57:45 -04:00
|
|
|
},
|
|
|
|
|
cancel: () => {
|
2026-09-13 14:14:38 -04:00
|
|
|
throw new Error("impl");
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
};
|
2026-09-13 14:14:38 -04:00
|
|
|
this.emit(_verification.VerifierEvent.ShowSas, this.callbacks);
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calculate an appropriate VerificationPhase for a VerificationRequest where this is the verifier.
|
|
|
|
|
*/
|
|
|
|
|
get verificationPhase() {
|
2026-09-13 14:14:38 -04:00
|
|
|
return _verification.VerificationPhase.Started;
|
2026-09-12 23:57:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
getShowSasCallbacks() {
|
|
|
|
|
return this.callbacks;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** For each specced verification method, the rust-side `VerificationMethod` corresponding to it */
|
2026-09-13 14:14:38 -04:00
|
|
|
exports.RustSASVerifier = RustSASVerifier;
|
2026-09-12 23:57:45 -04:00
|
|
|
const verificationMethodsByIdentifier = {
|
2026-09-13 14:14:38 -04:00
|
|
|
"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
|
2026-09-12 23:57:45 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* @internal
|
|
|
|
|
*/
|
2026-09-13 14:14:38 -04:00
|
|
|
function verificationMethodIdentifierToMethod(method) {
|
2026-09-12 23:57:45 -04:00
|
|
|
const meth = verificationMethodsByIdentifier[method];
|
|
|
|
|
if (meth === undefined) {
|
|
|
|
|
throw new Error(`Unknown verification method ${method}`);
|
|
|
|
|
}
|
|
|
|
|
return meth;
|
|
|
|
|
}
|
|
|
|
|
//# sourceMappingURL=verification.js.map
|