feat: complete Ancestor quote bot with production-ready Docker support
This commit is contained in:
16
node_modules/matrix-js-sdk/lib/rust-crypto/CrossSigningIdentity.d.ts
generated
vendored
16
node_modules/matrix-js-sdk/lib/rust-crypto/CrossSigningIdentity.d.ts
generated
vendored
@@ -1,18 +1,20 @@
|
||||
import { type OlmMachine } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { type BootstrapCrossSigningOpts } from "../crypto-api/index.ts";
|
||||
import { type Logger } from "../logger.ts";
|
||||
import { type OutgoingRequestProcessor } from "./OutgoingRequestProcessor.ts";
|
||||
import { type ServerSideSecretStorage } from "../secret-storage.ts";
|
||||
import { OlmMachine } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { BootstrapCrossSigningOpts } from "../crypto-api";
|
||||
import { OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
|
||||
import { ServerSideSecretStorage } from "../secret-storage";
|
||||
/** Manages the cross-signing keys for our own user.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export declare class CrossSigningIdentity {
|
||||
private readonly logger;
|
||||
private readonly olmMachine;
|
||||
private readonly outgoingRequestProcessor;
|
||||
private readonly secretStorage;
|
||||
constructor(logger: Logger, olmMachine: OlmMachine, outgoingRequestProcessor: OutgoingRequestProcessor, secretStorage: ServerSideSecretStorage);
|
||||
/** Called if the cross signing keys are imported from the secret storage */
|
||||
private readonly onCrossSigningKeysImport;
|
||||
constructor(olmMachine: OlmMachine, outgoingRequestProcessor: OutgoingRequestProcessor, secretStorage: ServerSideSecretStorage,
|
||||
/** Called if the cross signing keys are imported from the secret storage */
|
||||
onCrossSigningKeysImport: () => void);
|
||||
/**
|
||||
* Initialise our cross-signing keys by creating new keys if they do not exist, and uploading to the server
|
||||
*/
|
||||
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/CrossSigningIdentity.d.ts.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/CrossSigningIdentity.d.ts.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"CrossSigningIdentity.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/CrossSigningIdentity.ts"],"names":[],"mappings":"AAgBA,OAAO,EACH,KAAK,UAAU,EAGlB,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAE9E,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAEpE;;;GAGG;AACH,qBAAa,oBAAoB;IAEzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,wBAAwB;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAJlC,YACqB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,EACtB,wBAAwB,EAAE,wBAAwB,EAClD,aAAa,EAAE,uBAAuB,EACvD;IAEJ;;OAEG;IACU,qBAAqB,CAAC,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqFjF;IAED;;;;;;OAMG;YACW,iBAAiB;IA+B/B;;;;OAIG;YACW,+BAA+B;CAoBhD"}
|
||||
{"version":3,"file":"CrossSigningIdentity.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/CrossSigningIdentity.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,UAAU,EAAsB,MAAM,oCAAoC,CAAC;AAGpF,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE1D,OAAO,EAAmB,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAEvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAE5D;;;GAGG;AACH,qBAAa,oBAAoB;IAEzB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,wBAAwB;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,4EAA4E;IAC5E,OAAO,CAAC,QAAQ,CAAC,wBAAwB;gBAJxB,UAAU,EAAE,UAAU,EACtB,wBAAwB,EAAE,wBAAwB,EAClD,aAAa,EAAE,uBAAuB;IACvD,4EAA4E;IAC3D,wBAAwB,EAAE,MAAM,IAAI;IAGzD;;OAEG;IACU,qBAAqB,CAAC,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;IAiElF;;;;;;OAMG;YACW,iBAAiB;IAU/B;;;;OAIG;YACW,+BAA+B;CAGhD"}
|
||||
109
node_modules/matrix-js-sdk/lib/rust-crypto/CrossSigningIdentity.js
generated
vendored
109
node_modules/matrix-js-sdk/lib/rust-crypto/CrossSigningIdentity.js
generated
vendored
@@ -1,3 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.CrossSigningIdentity = void 0;
|
||||
var _logger = require("../logger");
|
||||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
@@ -18,12 +25,13 @@ limitations under the License.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class CrossSigningIdentity {
|
||||
constructor(logger, olmMachine, outgoingRequestProcessor, secretStorage) {
|
||||
this.logger = logger;
|
||||
class CrossSigningIdentity {
|
||||
constructor(olmMachine, outgoingRequestProcessor, secretStorage, /** Called if the cross signing keys are imported from the secret storage */
|
||||
onCrossSigningKeysImport) {
|
||||
this.olmMachine = olmMachine;
|
||||
this.outgoingRequestProcessor = outgoingRequestProcessor;
|
||||
this.secretStorage = secretStorage;
|
||||
this.onCrossSigningKeysImport = onCrossSigningKeysImport;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,54 +52,35 @@ export class CrossSigningIdentity {
|
||||
const olmDeviceHasKeys = olmDeviceStatus.hasMaster && olmDeviceStatus.hasUserSigning && olmDeviceStatus.hasSelfSigning;
|
||||
|
||||
// Log all relevant state for easier parsing of debug logs.
|
||||
this.logger.debug("bootstrapCrossSigning: starting", {
|
||||
_logger.logger.log("bootStrapCrossSigning: starting", {
|
||||
setupNewCrossSigning: opts.setupNewCrossSigning,
|
||||
olmDeviceHasMaster: olmDeviceStatus.hasMaster,
|
||||
olmDeviceHasUserSigning: olmDeviceStatus.hasUserSigning,
|
||||
olmDeviceHasSelfSigning: olmDeviceStatus.hasSelfSigning,
|
||||
privateKeysInSecretStorage
|
||||
});
|
||||
if (olmDeviceHasKeys) {
|
||||
if (!(await this.secretStorage.hasKey())) {
|
||||
this.logger.warn("bootstrapCrossSigning: Olm device has private keys, but secret storage is not yet set up; doing nothing for now.");
|
||||
// the keys should get uploaded to 4S once that is set up.
|
||||
} else if (!privateKeysInSecretStorage) {
|
||||
// the device has the keys but they are not in 4S, so update it
|
||||
this.logger.debug("bootstrapCrossSigning: Olm device has private keys: exporting to secret storage");
|
||||
await this.exportCrossSigningKeysToStorage();
|
||||
} else {
|
||||
this.logger.debug("bootstrapCrossSigning: Olm device has private keys and they are saved in secret storage; doing nothing");
|
||||
}
|
||||
} /* (!olmDeviceHasKeys) */else {
|
||||
if (privateKeysInSecretStorage) {
|
||||
// they are in 4S, so import from there
|
||||
this.logger.debug("bootstrapCrossSigning: Cross-signing private keys not found locally, but they are available " + "in secret storage, reading storage and caching locally");
|
||||
const status = await this.olmMachine.importCrossSigningKeys(masterKeyFromSecretStorage, selfSigningKeyFromSecretStorage, userSigningKeyFromSecretStorage);
|
||||
if (!olmDeviceHasKeys && !privateKeysInSecretStorage) {
|
||||
_logger.logger.log("bootStrapCrossSigning: Cross-signing private keys not found locally or in secret storage, creating new keys");
|
||||
await this.resetCrossSigning(opts.authUploadDeviceSigningKeys);
|
||||
} else if (olmDeviceHasKeys) {
|
||||
_logger.logger.log("bootStrapCrossSigning: Olm device has private keys: exporting to secret storage");
|
||||
await this.exportCrossSigningKeysToStorage();
|
||||
} else if (privateKeysInSecretStorage) {
|
||||
_logger.logger.log("bootStrapCrossSigning: Cross-signing private keys not found locally, but they are available " + "in secret storage, reading storage and caching locally");
|
||||
await this.olmMachine.importCrossSigningKeys(masterKeyFromSecretStorage, selfSigningKeyFromSecretStorage, userSigningKeyFromSecretStorage);
|
||||
|
||||
// Check that `importCrossSigningKeys` worked correctly (for example, it will fail silently if the
|
||||
// public keys are not available).
|
||||
if (!status.hasMaster || !status.hasSelfSigning || !status.hasUserSigning) {
|
||||
throw new Error("importCrossSigningKeys failed to import the keys");
|
||||
}
|
||||
// Get the current device
|
||||
const device = await this.olmMachine.getDevice(this.olmMachine.userId, this.olmMachine.deviceId);
|
||||
|
||||
// Get the current device
|
||||
const device = await this.olmMachine.getDevice(this.olmMachine.userId, this.olmMachine.deviceId);
|
||||
try {
|
||||
// Sign the device with our cross-signing key and upload the signature
|
||||
const request = await device.verify();
|
||||
await this.outgoingRequestProcessor.makeOutgoingRequest(request);
|
||||
} finally {
|
||||
device.free();
|
||||
}
|
||||
} else {
|
||||
this.logger.debug("bootstrapCrossSigning: Cross-signing private keys not found locally or in secret storage, creating new keys");
|
||||
await this.resetCrossSigning(opts.authUploadDeviceSigningKeys);
|
||||
}
|
||||
// Sign the device with our cross-signing key and upload the signature
|
||||
const request = await device.verify();
|
||||
await this.outgoingRequestProcessor.makeOutgoingRequest(request);
|
||||
this.onCrossSigningKeysImport();
|
||||
}
|
||||
|
||||
// TODO: we might previously have bootstrapped cross-signing but not completed uploading the keys to the
|
||||
// server -- in which case we should call OlmDevice.bootstrap_cross_signing. How do we know?
|
||||
this.logger.debug("bootstrapCrossSigning: complete");
|
||||
_logger.logger.log("bootStrapCrossSigning: complete");
|
||||
}
|
||||
|
||||
/** Reset our cross-signing keys
|
||||
@@ -102,27 +91,12 @@ export class CrossSigningIdentity {
|
||||
* * Upload the private keys to SSSS, if it is set up
|
||||
*/
|
||||
async resetCrossSigning(authUploadDeviceSigningKeys) {
|
||||
// XXX: We must find a way to make this atomic, currently if the user does not remember his account password
|
||||
// or 4S passphrase/key the process will fail in a bad state, with keys rotated but not uploaded or saved in 4S.
|
||||
const outgoingRequests = await this.olmMachine.bootstrapCrossSigning(true);
|
||||
|
||||
// If 4S is configured we need to update it.
|
||||
if (!(await this.secretStorage.hasKey())) {
|
||||
this.logger.warn("resetCrossSigning: Secret storage is not yet set up; not exporting keys to secret storage yet.");
|
||||
// the keys should get uploaded to 4S once that is set up.
|
||||
} else {
|
||||
// Update 4S before uploading cross-signing keys, to stay consistent with legacy that asks
|
||||
// 4S passphrase before asking for account password.
|
||||
// Ultimately should be made atomic and resistant to forgotten password/passphrase.
|
||||
this.logger.debug("resetCrossSigning: exporting private keys to secret storage");
|
||||
await this.exportCrossSigningKeysToStorage();
|
||||
}
|
||||
this.logger.debug("resetCrossSigning: publishing public keys to server");
|
||||
for (const req of [outgoingRequests.uploadKeysRequest, outgoingRequests.uploadSigningKeysRequest, outgoingRequests.uploadSignaturesRequest]) {
|
||||
if (req) {
|
||||
await this.outgoingRequestProcessor.makeOutgoingRequest(req, authUploadDeviceSigningKeys);
|
||||
}
|
||||
_logger.logger.log("bootStrapCrossSigning: publishing keys to server");
|
||||
for (const req of outgoingRequests) {
|
||||
await this.outgoingRequestProcessor.makeOutgoingRequest(req, authUploadDeviceSigningKeys);
|
||||
}
|
||||
await this.exportCrossSigningKeysToStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,23 +105,8 @@ export class CrossSigningIdentity {
|
||||
* (If secret storage is *not* configured, we assume that the export will happen when it is set up)
|
||||
*/
|
||||
async exportCrossSigningKeysToStorage() {
|
||||
const exported = await this.olmMachine.exportCrossSigningKeys();
|
||||
/* istanbul ignore else (this function is only called when we know the olm machine has keys) */
|
||||
if (exported?.masterKey) {
|
||||
await this.secretStorage.store("m.cross_signing.master", exported.masterKey);
|
||||
} else {
|
||||
this.logger.error(`Cannot export MSK to secret storage, private key unknown`);
|
||||
}
|
||||
if (exported?.self_signing_key) {
|
||||
await this.secretStorage.store("m.cross_signing.self_signing", exported.self_signing_key);
|
||||
} else {
|
||||
this.logger.error(`Cannot export SSK to secret storage, private key unknown`);
|
||||
}
|
||||
if (exported?.userSigningKey) {
|
||||
await this.secretStorage.store("m.cross_signing.user_signing", exported.userSigningKey);
|
||||
} else {
|
||||
this.logger.error(`Cannot export USK to secret storage, private key unknown`);
|
||||
}
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
exports.CrossSigningIdentity = CrossSigningIdentity;
|
||||
//# sourceMappingURL=CrossSigningIdentity.js.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/CrossSigningIdentity.js.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/CrossSigningIdentity.js.map
generated
vendored
File diff suppressed because one or more lines are too long
118
node_modules/matrix-js-sdk/lib/rust-crypto/DehydratedDeviceManager.d.ts
generated
vendored
118
node_modules/matrix-js-sdk/lib/rust-crypto/DehydratedDeviceManager.d.ts
generated
vendored
@@ -1,118 +0,0 @@
|
||||
import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { type OutgoingRequestProcessor } from "./OutgoingRequestProcessor.ts";
|
||||
import { type IHttpOpts, type MatrixHttpApi } from "../http-api/index.ts";
|
||||
import { type ServerSideSecretStorage } from "../secret-storage.ts";
|
||||
import { type Logger } from "../logger.ts";
|
||||
import { CryptoEvent, type CryptoEventHandlerMap, type StartDehydrationOpts } from "../crypto-api/index.ts";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
|
||||
/**
|
||||
* The unstable URL prefix for dehydrated device endpoints
|
||||
*/
|
||||
export declare const UnstablePrefix = "/_matrix/client/unstable/org.matrix.msc3814.v1";
|
||||
/**
|
||||
* Manages dehydrated devices
|
||||
*
|
||||
* We have one of these per `RustCrypto`. It's responsible for
|
||||
*
|
||||
* * determining server support for dehydrated devices
|
||||
* * creating new dehydrated devices when requested, including periodically
|
||||
* replacing the dehydrated device with a new one
|
||||
* * rehydrating a device when requested, and when present
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export declare class DehydratedDeviceManager extends TypedEventEmitter<DehydratedDevicesEvents, DehydratedDevicesEventMap> {
|
||||
private readonly logger;
|
||||
private readonly olmMachine;
|
||||
private readonly http;
|
||||
private readonly outgoingRequestProcessor;
|
||||
private readonly secretStorage;
|
||||
/** the ID of the interval for periodically replacing the dehydrated device */
|
||||
private intervalId?;
|
||||
constructor(logger: Logger, olmMachine: RustSdkCryptoJs.OlmMachine, http: MatrixHttpApi<IHttpOpts & {
|
||||
onlyData: true;
|
||||
}>, outgoingRequestProcessor: OutgoingRequestProcessor, secretStorage: ServerSideSecretStorage);
|
||||
private cacheKey;
|
||||
/**
|
||||
* Return whether the server supports dehydrated devices.
|
||||
*/
|
||||
isSupported(): Promise<boolean>;
|
||||
/**
|
||||
* Start using device dehydration.
|
||||
*
|
||||
* - Rehydrates a dehydrated device, if one is available and `opts.rehydrate`
|
||||
* is `true`.
|
||||
* - Creates a new dehydration key, if necessary, and stores it in Secret
|
||||
* Storage.
|
||||
* - If `opts.createNewKey` is set to true, always creates a new key.
|
||||
* - If a dehydration key is not available, creates a new one.
|
||||
* - Creates a new dehydrated device, and schedules periodically creating
|
||||
* new dehydrated devices.
|
||||
*
|
||||
* @param opts - options for device dehydration. For backwards compatibility
|
||||
* with old code, a boolean can be given here, which will be treated as
|
||||
* the `createNewKey` option. However, this is deprecated.
|
||||
*/
|
||||
start(opts?: StartDehydrationOpts | boolean): Promise<void>;
|
||||
/**
|
||||
* Return whether the dehydration key is stored in Secret Storage.
|
||||
*/
|
||||
isKeyStored(): Promise<boolean>;
|
||||
/**
|
||||
* Reset the dehydration key.
|
||||
*
|
||||
* Creates a new key and stores it in secret storage.
|
||||
*
|
||||
* @returns The newly-generated key.
|
||||
*/
|
||||
resetKey(): Promise<RustSdkCryptoJs.DehydratedDeviceKey>;
|
||||
/**
|
||||
* Get and cache the encryption key from secret storage.
|
||||
*
|
||||
* If `create` is `true`, creates a new key if no existing key is present.
|
||||
*
|
||||
* @returns the key, if available, or `null` if no key is available
|
||||
*/
|
||||
private getKey;
|
||||
/**
|
||||
* Rehydrate the dehydrated device stored on the server.
|
||||
*
|
||||
* Checks if there is a dehydrated device on the server. If so, rehydrates
|
||||
* the device and processes the to-device events.
|
||||
*
|
||||
* Returns whether or not a dehydrated device was found.
|
||||
*/
|
||||
rehydrateDeviceIfAvailable(): Promise<boolean>;
|
||||
/**
|
||||
* Creates and uploads a new dehydrated device.
|
||||
*
|
||||
* Creates and stores a new key in secret storage if none is available.
|
||||
*/
|
||||
createAndUploadDehydratedDevice(): Promise<void>;
|
||||
/**
|
||||
* Schedule periodic creation of dehydrated devices.
|
||||
*/
|
||||
scheduleDeviceDehydration(): Promise<void>;
|
||||
/**
|
||||
* Stop the dehydrated device manager.
|
||||
*
|
||||
* Cancels any scheduled dehydration tasks.
|
||||
*/
|
||||
stop(): void;
|
||||
/**
|
||||
* Delete the current dehydrated device and stop the dehydrated device manager.
|
||||
*/
|
||||
delete(): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* The events fired by the DehydratedDeviceManager
|
||||
* @internal
|
||||
*/
|
||||
type DehydratedDevicesEvents = CryptoEvent.DehydratedDeviceCreated | CryptoEvent.DehydratedDeviceUploaded | CryptoEvent.RehydrationStarted | CryptoEvent.RehydrationProgress | CryptoEvent.RehydrationCompleted | CryptoEvent.RehydrationError | CryptoEvent.DehydrationKeyCached | CryptoEvent.DehydratedDeviceRotationError;
|
||||
/**
|
||||
* A map of the {@link DehydratedDeviceEvents} fired by the {@link DehydratedDeviceManager} and their payloads.
|
||||
* @internal
|
||||
*/
|
||||
type DehydratedDevicesEventMap = Pick<CryptoEventHandlerMap, DehydratedDevicesEvents>;
|
||||
export {};
|
||||
//# sourceMappingURL=DehydratedDeviceManager.d.ts.map
|
||||
1
node_modules/matrix-js-sdk/lib/rust-crypto/DehydratedDeviceManager.d.ts.map
generated
vendored
1
node_modules/matrix-js-sdk/lib/rust-crypto/DehydratedDeviceManager.d.ts.map
generated
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"DehydratedDeviceManager.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/DehydratedDeviceManager.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,eAAe,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAE9E,OAAO,EAAE,KAAK,SAAS,EAAoB,KAAK,aAAa,EAAU,MAAM,sBAAsB,CAAC;AAEpG,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAEpE,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,KAAK,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC5G,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAmBrE;;GAEG;AACH,eAAO,MAAM,cAAc,mDAAmD,CAAC;AAW/E;;;;;;;;;;;GAWG;AACH,qBAAa,uBAAwB,SAAQ,iBAAiB,CAAC,uBAAuB,EAAE,yBAAyB,CAAC;IAK1G,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,wBAAwB;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa;IARlC,8EAA8E;IAC9E,OAAO,CAAC,UAAU,CAAC,CAAiC;IAEpD,YACqB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,eAAe,CAAC,UAAU,EACtC,IAAI,EAAE,aAAa,CAAC,SAAS,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC,EACnD,wBAAwB,EAAE,wBAAwB,EAClD,aAAa,EAAE,uBAAuB,EAG1D;YAEa,QAAQ;IAKtB;;OAEG;IACU,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAyB3C;IAED;;;;;;;;;;;;;;;OAeG;IACU,KAAK,CAAC,IAAI,GAAE,oBAAoB,GAAG,OAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB3E;IAED;;OAEG;IACU,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAE3C;IAED;;;;;;OAMG;IACU,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAMpE;IAED;;;;;;OAMG;YACW,MAAM;IAuBpB;;;;;;;OAOG;IACU,0BAA0B,IAAI,OAAO,CAAC,OAAO,CAAC,CA2E1D;IAED;;;;OAIG;IACU,+BAA+B,IAAI,OAAO,CAAC,IAAI,CAAC,CAW5D;IAED;;OAEG;IACU,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CAWtD;IAED;;;;OAIG;IACI,IAAI,IAAI,IAAI,CAKlB;IAED;;OAEG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAwBnC;CACJ;AAED;;;GAGG;AACH,KAAK,uBAAuB,GACtB,WAAW,CAAC,uBAAuB,GACnC,WAAW,CAAC,wBAAwB,GACpC,WAAW,CAAC,kBAAkB,GAC9B,WAAW,CAAC,mBAAmB,GAC/B,WAAW,CAAC,oBAAoB,GAChC,WAAW,CAAC,gBAAgB,GAC5B,WAAW,CAAC,oBAAoB,GAChC,WAAW,CAAC,6BAA6B,CAAC;AAEhD;;;GAGG;AACH,KAAK,yBAAyB,GAAG,IAAI,CAAC,qBAAqB,EAAE,uBAAuB,CAAC,CAAC"}
|
||||
326
node_modules/matrix-js-sdk/lib/rust-crypto/DehydratedDeviceManager.js
generated
vendored
326
node_modules/matrix-js-sdk/lib/rust-crypto/DehydratedDeviceManager.js
generated
vendored
@@ -1,326 +0,0 @@
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
/*
|
||||
Copyright 2024 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-wasm";
|
||||
import { encodeUri } from "../utils.js";
|
||||
import { Method } from "../http-api/index.js";
|
||||
import { decodeBase64 } from "../base64.js";
|
||||
import { CryptoEvent } from "../crypto-api/index.js";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter.js";
|
||||
|
||||
/**
|
||||
* The response body of `GET /_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The response body of `GET /_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device/{device_id}/events`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The unstable URL prefix for dehydrated device endpoints
|
||||
*/
|
||||
export const UnstablePrefix = "/_matrix/client/unstable/org.matrix.msc3814.v1";
|
||||
/**
|
||||
* The name used for the dehydration key in Secret Storage
|
||||
*/
|
||||
const SECRET_STORAGE_NAME = "org.matrix.msc3814";
|
||||
|
||||
/**
|
||||
* The interval between creating dehydrated devices. (one week)
|
||||
*/
|
||||
const DEHYDRATION_INTERVAL = 7 * 24 * 60 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Manages dehydrated devices
|
||||
*
|
||||
* We have one of these per `RustCrypto`. It's responsible for
|
||||
*
|
||||
* * determining server support for dehydrated devices
|
||||
* * creating new dehydrated devices when requested, including periodically
|
||||
* replacing the dehydrated device with a new one
|
||||
* * rehydrating a device when requested, and when present
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class DehydratedDeviceManager extends TypedEventEmitter {
|
||||
constructor(logger, olmMachine, http, outgoingRequestProcessor, secretStorage) {
|
||||
super();
|
||||
/** the ID of the interval for periodically replacing the dehydrated device */
|
||||
_defineProperty(this, "intervalId", void 0);
|
||||
this.logger = logger;
|
||||
this.olmMachine = olmMachine;
|
||||
this.http = http;
|
||||
this.outgoingRequestProcessor = outgoingRequestProcessor;
|
||||
this.secretStorage = secretStorage;
|
||||
}
|
||||
async cacheKey(key) {
|
||||
await this.olmMachine.dehydratedDevices().saveDehydratedDeviceKey(key);
|
||||
this.emit(CryptoEvent.DehydrationKeyCached);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the server supports dehydrated devices.
|
||||
*/
|
||||
async isSupported() {
|
||||
// call the endpoint to get a dehydrated device. If it returns an
|
||||
// M_UNRECOGNIZED error, then dehydration is unsupported. If it returns
|
||||
// a successful response, or an M_NOT_FOUND, then dehydration is supported.
|
||||
// Any other exceptions are passed through.
|
||||
try {
|
||||
await this.http.authedRequest(Method.Get, "/dehydrated_device", undefined, undefined, {
|
||||
prefix: UnstablePrefix
|
||||
});
|
||||
} catch (error) {
|
||||
const err = error;
|
||||
if (err.errcode === "M_UNRECOGNIZED") {
|
||||
return false;
|
||||
} else if (err.errcode === "M_NOT_FOUND") {
|
||||
return true;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start using device dehydration.
|
||||
*
|
||||
* - Rehydrates a dehydrated device, if one is available and `opts.rehydrate`
|
||||
* is `true`.
|
||||
* - Creates a new dehydration key, if necessary, and stores it in Secret
|
||||
* Storage.
|
||||
* - If `opts.createNewKey` is set to true, always creates a new key.
|
||||
* - If a dehydration key is not available, creates a new one.
|
||||
* - Creates a new dehydrated device, and schedules periodically creating
|
||||
* new dehydrated devices.
|
||||
*
|
||||
* @param opts - options for device dehydration. For backwards compatibility
|
||||
* with old code, a boolean can be given here, which will be treated as
|
||||
* the `createNewKey` option. However, this is deprecated.
|
||||
*/
|
||||
async start(opts = {}) {
|
||||
if (typeof opts === "boolean") {
|
||||
opts = {
|
||||
createNewKey: opts
|
||||
};
|
||||
}
|
||||
if (opts.onlyIfKeyCached && !(await this.olmMachine.dehydratedDevices().getDehydratedDeviceKey())) {
|
||||
return;
|
||||
}
|
||||
this.stop();
|
||||
if (opts.rehydrate !== false) {
|
||||
try {
|
||||
await this.rehydrateDeviceIfAvailable();
|
||||
} catch (e) {
|
||||
// If rehydration fails, there isn't much we can do about it. Log
|
||||
// the error, and create a new device.
|
||||
this.logger.info("dehydration: Error rehydrating device:", e);
|
||||
this.emit(CryptoEvent.RehydrationError, e.message);
|
||||
}
|
||||
}
|
||||
if (opts.createNewKey) {
|
||||
await this.resetKey();
|
||||
}
|
||||
await this.scheduleDeviceDehydration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the dehydration key is stored in Secret Storage.
|
||||
*/
|
||||
async isKeyStored() {
|
||||
return Boolean(await this.secretStorage.isStored(SECRET_STORAGE_NAME));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the dehydration key.
|
||||
*
|
||||
* Creates a new key and stores it in secret storage.
|
||||
*
|
||||
* @returns The newly-generated key.
|
||||
*/
|
||||
async resetKey() {
|
||||
const key = RustSdkCryptoJs.DehydratedDeviceKey.createRandomKey();
|
||||
await this.secretStorage.store(SECRET_STORAGE_NAME, key.toBase64());
|
||||
// Also cache it in the rust SDK's crypto store.
|
||||
await this.cacheKey(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and cache the encryption key from secret storage.
|
||||
*
|
||||
* If `create` is `true`, creates a new key if no existing key is present.
|
||||
*
|
||||
* @returns the key, if available, or `null` if no key is available
|
||||
*/
|
||||
async getKey(create) {
|
||||
const cachedKey = await this.olmMachine.dehydratedDevices().getDehydratedDeviceKey();
|
||||
if (cachedKey) return cachedKey;
|
||||
const keyB64 = await this.secretStorage.get(SECRET_STORAGE_NAME);
|
||||
if (keyB64 === undefined) {
|
||||
if (!create) {
|
||||
return null;
|
||||
}
|
||||
return await this.resetKey();
|
||||
}
|
||||
|
||||
// We successfully found the key in secret storage: decode it, and cache it in
|
||||
// the rust SDK's crypto store.
|
||||
const bytes = decodeBase64(keyB64);
|
||||
try {
|
||||
const key = RustSdkCryptoJs.DehydratedDeviceKey.createKeyFromArray(bytes);
|
||||
await this.cacheKey(key);
|
||||
return key;
|
||||
} finally {
|
||||
bytes.fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rehydrate the dehydrated device stored on the server.
|
||||
*
|
||||
* Checks if there is a dehydrated device on the server. If so, rehydrates
|
||||
* the device and processes the to-device events.
|
||||
*
|
||||
* Returns whether or not a dehydrated device was found.
|
||||
*/
|
||||
async rehydrateDeviceIfAvailable() {
|
||||
const key = await this.getKey(false);
|
||||
if (!key) {
|
||||
return false;
|
||||
}
|
||||
let dehydratedDeviceResp;
|
||||
try {
|
||||
dehydratedDeviceResp = await this.http.authedRequest(Method.Get, "/dehydrated_device", undefined, undefined, {
|
||||
prefix: UnstablePrefix
|
||||
});
|
||||
} catch (error) {
|
||||
const err = error;
|
||||
// We ignore M_NOT_FOUND (there is no dehydrated device, so nothing
|
||||
// us to do) and M_UNRECOGNIZED (the server does not understand the
|
||||
// endpoint). We pass through any other errors.
|
||||
if (err.errcode === "M_NOT_FOUND" || err.errcode === "M_UNRECOGNIZED") {
|
||||
this.logger.info("dehydration: No dehydrated device");
|
||||
return false;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
this.logger.info("dehydration: dehydrated device found");
|
||||
this.emit(CryptoEvent.RehydrationStarted);
|
||||
const rehydratedDevice = await this.olmMachine.dehydratedDevices().rehydrate(key, new RustSdkCryptoJs.DeviceId(dehydratedDeviceResp.device_id), JSON.stringify(dehydratedDeviceResp.device_data));
|
||||
this.logger.info("dehydration: device rehydrated");
|
||||
let nextBatch = undefined;
|
||||
let toDeviceCount = 0;
|
||||
let roomKeyCount = 0;
|
||||
const path = encodeUri("/dehydrated_device/$device_id/events", {
|
||||
$device_id: dehydratedDeviceResp.device_id
|
||||
});
|
||||
do {
|
||||
const eventResp = await this.http.authedRequest(Method.Get, path, nextBatch ? {
|
||||
from: nextBatch
|
||||
} : undefined, undefined, {
|
||||
prefix: UnstablePrefix
|
||||
});
|
||||
toDeviceCount += eventResp.events.length;
|
||||
nextBatch = eventResp.next_batch;
|
||||
if (eventResp.events.length > 0) {
|
||||
const roomKeyInfos = await rehydratedDevice.receiveEvents(JSON.stringify(eventResp.events));
|
||||
roomKeyCount += roomKeyInfos.length;
|
||||
this.emit(CryptoEvent.RehydrationProgress, roomKeyCount, toDeviceCount);
|
||||
}
|
||||
} while (nextBatch !== undefined);
|
||||
this.logger.info(`dehydration: received ${roomKeyCount} room keys from ${toDeviceCount} to-device events`);
|
||||
this.emit(CryptoEvent.RehydrationCompleted);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and uploads a new dehydrated device.
|
||||
*
|
||||
* Creates and stores a new key in secret storage if none is available.
|
||||
*/
|
||||
async createAndUploadDehydratedDevice() {
|
||||
const key = await this.getKey(true);
|
||||
const dehydratedDevice = await this.olmMachine.dehydratedDevices().create();
|
||||
this.emit(CryptoEvent.DehydratedDeviceCreated);
|
||||
const request = await dehydratedDevice.keysForUpload("Dehydrated device", key);
|
||||
await this.outgoingRequestProcessor.makeOutgoingRequest(request);
|
||||
this.emit(CryptoEvent.DehydratedDeviceUploaded);
|
||||
this.logger.info("dehydration: uploaded device");
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule periodic creation of dehydrated devices.
|
||||
*/
|
||||
async scheduleDeviceDehydration() {
|
||||
// cancel any previously-scheduled tasks
|
||||
this.stop();
|
||||
await this.createAndUploadDehydratedDevice();
|
||||
this.intervalId = setInterval(() => {
|
||||
this.createAndUploadDehydratedDevice().catch(error => {
|
||||
this.emit(CryptoEvent.DehydratedDeviceRotationError, error.message);
|
||||
this.logger.error("Error creating dehydrated device:", error);
|
||||
});
|
||||
}, DEHYDRATION_INTERVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the dehydrated device manager.
|
||||
*
|
||||
* Cancels any scheduled dehydration tasks.
|
||||
*/
|
||||
stop() {
|
||||
if (this.intervalId) {
|
||||
clearInterval(this.intervalId);
|
||||
this.intervalId = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the current dehydrated device and stop the dehydrated device manager.
|
||||
*/
|
||||
async delete() {
|
||||
this.stop();
|
||||
try {
|
||||
await this.http.authedRequest(Method.Delete, "/dehydrated_device", undefined, {}, {
|
||||
prefix: UnstablePrefix
|
||||
});
|
||||
} catch (error) {
|
||||
const err = error;
|
||||
// If dehydrated devices aren't supported, or no dehydrated device
|
||||
// is found, we don't consider it an error, because we we'll end up
|
||||
// with no dehydrated device.
|
||||
if (err.errcode === "M_UNRECOGNIZED") {
|
||||
return;
|
||||
} else if (err.errcode === "M_NOT_FOUND") {
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The events fired by the DehydratedDeviceManager
|
||||
* @internal
|
||||
*/
|
||||
|
||||
/**
|
||||
* A map of the {@link DehydratedDeviceEvents} fired by the {@link DehydratedDeviceManager} and their payloads.
|
||||
* @internal
|
||||
*/
|
||||
//# sourceMappingURL=DehydratedDeviceManager.js.map
|
||||
1
node_modules/matrix-js-sdk/lib/rust-crypto/DehydratedDeviceManager.js.map
generated
vendored
1
node_modules/matrix-js-sdk/lib/rust-crypto/DehydratedDeviceManager.js.map
generated
vendored
File diff suppressed because one or more lines are too long
9
node_modules/matrix-js-sdk/lib/rust-crypto/KeyClaimManager.d.ts
generated
vendored
9
node_modules/matrix-js-sdk/lib/rust-crypto/KeyClaimManager.d.ts
generated
vendored
@@ -1,6 +1,5 @@
|
||||
import { type OlmMachine, type UserId } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { type OutgoingRequestProcessor } from "./OutgoingRequestProcessor.ts";
|
||||
import { type LogSpan } from "../logger.ts";
|
||||
import { OlmMachine, UserId } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
|
||||
/**
|
||||
* KeyClaimManager: linearises calls to OlmMachine.getMissingSessions to avoid races
|
||||
*
|
||||
@@ -24,10 +23,10 @@ export declare class KeyClaimManager {
|
||||
* Given a list of users, attempt to ensure that we have Olm Sessions active with each of their devices
|
||||
*
|
||||
* If we don't have an active olm session, we will claim a one-time key and start one.
|
||||
* @param logger - logger to use
|
||||
*
|
||||
* @param userList - list of userIDs to claim
|
||||
*/
|
||||
ensureSessionsForUsers(logger: LogSpan, userList: Array<UserId>): Promise<void>;
|
||||
ensureSessionsForUsers(userList: Array<UserId>): Promise<void>;
|
||||
private ensureSessionsForUsersInner;
|
||||
}
|
||||
//# sourceMappingURL=KeyClaimManager.d.ts.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/KeyClaimManager.d.ts.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/KeyClaimManager.d.ts.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"KeyClaimManager.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/KeyClaimManager.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,MAAM,EAAE,MAAM,oCAAoC,CAAC;AAElF,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;;;GAMG;AACH,qBAAa,eAAe;IAKpB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,wBAAwB;IAL7C,OAAO,CAAC,mBAAmB,CAAgB;IAC3C,OAAO,CAAC,OAAO,CAAS;IAExB,YACqB,UAAU,EAAE,UAAU,EACtB,wBAAwB,EAAE,wBAAwB,EAGtE;IAED;;;;OAIG;IACI,IAAI,IAAI,IAAI,CAElB;IAED;;;;;;OAMG;IACI,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAYrF;YAEa,2BAA2B;CAgB5C"}
|
||||
{"version":3,"file":"KeyClaimManager.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/KeyClaimManager.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAC;AAExE,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAEtE;;;;;;GAMG;AACH,qBAAa,eAAe;IAKpB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,wBAAwB;IAL7C,OAAO,CAAC,mBAAmB,CAAgB;IAC3C,OAAO,CAAC,OAAO,CAAS;gBAGH,UAAU,EAAE,UAAU,EACtB,wBAAwB,EAAE,wBAAwB;IAKvE;;;;OAIG;IACI,IAAI,IAAI,IAAI;IAInB;;;;;;OAMG;IACI,sBAAsB,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;YAcvD,2BAA2B;CAU5C"}
|
||||
32
node_modules/matrix-js-sdk/lib/rust-crypto/KeyClaimManager.js
generated
vendored
32
node_modules/matrix-js-sdk/lib/rust-crypto/KeyClaimManager.js
generated
vendored
@@ -1,4 +1,11 @@
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
"use strict";
|
||||
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.KeyClaimManager = void 0;
|
||||
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
||||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
@@ -22,12 +29,12 @@ limitations under the License.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class KeyClaimManager {
|
||||
class KeyClaimManager {
|
||||
constructor(olmMachine, outgoingRequestProcessor) {
|
||||
_defineProperty(this, "currentClaimPromise", void 0);
|
||||
_defineProperty(this, "stopped", false);
|
||||
this.olmMachine = olmMachine;
|
||||
this.outgoingRequestProcessor = outgoingRequestProcessor;
|
||||
(0, _defineProperty2.default)(this, "currentClaimPromise", void 0);
|
||||
(0, _defineProperty2.default)(this, "stopped", false);
|
||||
this.currentClaimPromise = Promise.resolve();
|
||||
}
|
||||
|
||||
@@ -44,35 +51,30 @@ export class KeyClaimManager {
|
||||
* Given a list of users, attempt to ensure that we have Olm Sessions active with each of their devices
|
||||
*
|
||||
* If we don't have an active olm session, we will claim a one-time key and start one.
|
||||
* @param logger - logger to use
|
||||
*
|
||||
* @param userList - list of userIDs to claim
|
||||
*/
|
||||
ensureSessionsForUsers(logger, userList) {
|
||||
ensureSessionsForUsers(userList) {
|
||||
// The Rust-SDK requires that we only have one getMissingSessions process in flight at once. This little dance
|
||||
// ensures that, by only having one call to ensureSessionsForUsersInner active at once (and making them
|
||||
// queue up in order).
|
||||
const prom = this.currentClaimPromise.catch(() => {
|
||||
// any errors in the previous claim will have been reported already, so there is nothing to do here.
|
||||
// we just throw away the error and start anew.
|
||||
}).then(() => this.ensureSessionsForUsersInner(logger, userList));
|
||||
}).then(() => this.ensureSessionsForUsersInner(userList));
|
||||
this.currentClaimPromise = prom;
|
||||
return prom;
|
||||
}
|
||||
async ensureSessionsForUsersInner(logger, userList) {
|
||||
async ensureSessionsForUsersInner(userList) {
|
||||
// bail out quickly if we've been stopped.
|
||||
if (this.stopped) {
|
||||
throw new Error(`Cannot ensure Olm sessions: shutting down`);
|
||||
}
|
||||
logger.info("Checking for missing Olm sessions");
|
||||
// By passing the userId array to rust we transfer ownership of the items to rust, causing
|
||||
// them to be invalidated on the JS side as soon as the method is called.
|
||||
// As we haven't created the `userList` let's clone the users, to not break the caller from re-using it.
|
||||
const claimRequest = await this.olmMachine.getMissingSessions(userList.map(u => u.clone()));
|
||||
const claimRequest = await this.olmMachine.getMissingSessions(userList);
|
||||
if (claimRequest) {
|
||||
logger.info("Making /keys/claim request");
|
||||
await this.outgoingRequestProcessor.makeOutgoingRequest(claimRequest);
|
||||
}
|
||||
logger.info("Olm sessions prepared");
|
||||
}
|
||||
}
|
||||
exports.KeyClaimManager = KeyClaimManager;
|
||||
//# sourceMappingURL=KeyClaimManager.js.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/KeyClaimManager.js.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/KeyClaimManager.js.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"KeyClaimManager.js","names":[],"sources":["../../src/rust-crypto/KeyClaimManager.ts"],"sourcesContent":["/*\nCopyright 2023 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { type OlmMachine, type UserId } from \"@matrix-org/matrix-sdk-crypto-wasm\";\n\nimport { type OutgoingRequestProcessor } from \"./OutgoingRequestProcessor.ts\";\nimport { type LogSpan } from \"../logger.ts\";\n\n/**\n * KeyClaimManager: linearises calls to OlmMachine.getMissingSessions to avoid races\n *\n * We have one of these per `RustCrypto` (and hence per `MatrixClient`).\n *\n * @internal\n */\nexport class KeyClaimManager {\n private currentClaimPromise: Promise<void>;\n private stopped = false;\n\n public constructor(\n private readonly olmMachine: OlmMachine,\n private readonly outgoingRequestProcessor: OutgoingRequestProcessor,\n ) {\n this.currentClaimPromise = Promise.resolve();\n }\n\n /**\n * Tell the KeyClaimManager to immediately stop processing requests.\n *\n * Any further calls, and any still in the queue, will fail with an error.\n */\n public stop(): void {\n this.stopped = true;\n }\n\n /**\n * Given a list of users, attempt to ensure that we have Olm Sessions active with each of their devices\n *\n * If we don't have an active olm session, we will claim a one-time key and start one.\n * @param logger - logger to use\n * @param userList - list of userIDs to claim\n */\n public ensureSessionsForUsers(logger: LogSpan, userList: Array<UserId>): Promise<void> {\n // The Rust-SDK requires that we only have one getMissingSessions process in flight at once. This little dance\n // ensures that, by only having one call to ensureSessionsForUsersInner active at once (and making them\n // queue up in order).\n const prom = this.currentClaimPromise\n .catch(() => {\n // any errors in the previous claim will have been reported already, so there is nothing to do here.\n // we just throw away the error and start anew.\n })\n .then(() => this.ensureSessionsForUsersInner(logger, userList));\n this.currentClaimPromise = prom;\n return prom;\n }\n\n private async ensureSessionsForUsersInner(logger: LogSpan, userList: Array<UserId>): Promise<void> {\n // bail out quickly if we've been stopped.\n if (this.stopped) {\n throw new Error(`Cannot ensure Olm sessions: shutting down`);\n }\n logger.info(\"Checking for missing Olm sessions\");\n // By passing the userId array to rust we transfer ownership of the items to rust, causing\n // them to be invalidated on the JS side as soon as the method is called.\n // As we haven't created the `userList` let's clone the users, to not break the caller from re-using it.\n const claimRequest = await this.olmMachine.getMissingSessions(userList.map((u) => u.clone()));\n if (claimRequest) {\n logger.info(\"Making /keys/claim request\");\n await this.outgoingRequestProcessor.makeOutgoingRequest(claimRequest);\n }\n logger.info(\"Olm sessions prepared\");\n }\n}\n"],"mappings":";AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAM,eAAe,CAAC;EAIlB,WAAW,CACG,UAAsB,EACtB,wBAAkD,EACrE;IAAA;IAAA,iCALgB,KAAK;IAAA,KAGF,UAAsB,GAAtB,UAAsB;IAAA,KACtB,wBAAkD,GAAlD,wBAAkD;IAEnE,IAAI,CAAC,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;EAChD;;EAEA;AACJ;AACA;AACA;AACA;EACW,IAAI,GAAS;IAChB,IAAI,CAAC,OAAO,GAAG,IAAI;EACvB;;EAEA;AACJ;AACA;AACA;AACA;AACA;AACA;EACW,sBAAsB,CAAC,MAAe,EAAE,QAAuB,EAAiB;IACnF;IACA;IACA;IACA,MAAM,IAAI,GAAG,IAAI,CAAC,mBAAmB,CAChC,KAAK,CAAC,MAAM;MACT;MACA;IAAA,CACH,CAAC,CACD,IAAI,CAAC,MAAM,IAAI,CAAC,2BAA2B,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACnE,IAAI,CAAC,mBAAmB,GAAG,IAAI;IAC/B,OAAO,IAAI;EACf;EAEA,MAAc,2BAA2B,CAAC,MAAe,EAAE,QAAuB,EAAiB;IAC/F;IACA,IAAI,IAAI,CAAC,OAAO,EAAE;MACd,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC;IAChE;IACA,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC;IAChD;IACA;IACA;IACA,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,QAAQ,CAAC,GAAG,CAAE,CAAC,IAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7F,IAAI,YAAY,EAAE;MACd,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAAC;MACzC,MAAM,IAAI,CAAC,wBAAwB,CAAC,mBAAmB,CAAC,YAAY,CAAC;IACzE;IACA,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC;EACxC;AACJ","ignoreList":[]}
|
||||
{"version":3,"file":"KeyClaimManager.js","names":["KeyClaimManager","constructor","olmMachine","outgoingRequestProcessor","_defineProperty2","default","currentClaimPromise","Promise","resolve","stop","stopped","ensureSessionsForUsers","userList","prom","catch","then","ensureSessionsForUsersInner","Error","claimRequest","getMissingSessions","makeOutgoingRequest","exports"],"sources":["../../src/rust-crypto/KeyClaimManager.ts"],"sourcesContent":["/*\nCopyright 2023 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { OlmMachine, UserId } from \"@matrix-org/matrix-sdk-crypto-wasm\";\n\nimport { OutgoingRequestProcessor } from \"./OutgoingRequestProcessor\";\n\n/**\n * KeyClaimManager: linearises calls to OlmMachine.getMissingSessions to avoid races\n *\n * We have one of these per `RustCrypto` (and hence per `MatrixClient`).\n *\n * @internal\n */\nexport class KeyClaimManager {\n private currentClaimPromise: Promise<void>;\n private stopped = false;\n\n public constructor(\n private readonly olmMachine: OlmMachine,\n private readonly outgoingRequestProcessor: OutgoingRequestProcessor,\n ) {\n this.currentClaimPromise = Promise.resolve();\n }\n\n /**\n * Tell the KeyClaimManager to immediately stop processing requests.\n *\n * Any further calls, and any still in the queue, will fail with an error.\n */\n public stop(): void {\n this.stopped = true;\n }\n\n /**\n * Given a list of users, attempt to ensure that we have Olm Sessions active with each of their devices\n *\n * If we don't have an active olm session, we will claim a one-time key and start one.\n *\n * @param userList - list of userIDs to claim\n */\n public ensureSessionsForUsers(userList: Array<UserId>): Promise<void> {\n // The Rust-SDK requires that we only have one getMissingSessions process in flight at once. This little dance\n // ensures that, by only having one call to ensureSessionsForUsersInner active at once (and making them\n // queue up in order).\n const prom = this.currentClaimPromise\n .catch(() => {\n // any errors in the previous claim will have been reported already, so there is nothing to do here.\n // we just throw away the error and start anew.\n })\n .then(() => this.ensureSessionsForUsersInner(userList));\n this.currentClaimPromise = prom;\n return prom;\n }\n\n private async ensureSessionsForUsersInner(userList: Array<UserId>): Promise<void> {\n // bail out quickly if we've been stopped.\n if (this.stopped) {\n throw new Error(`Cannot ensure Olm sessions: shutting down`);\n }\n const claimRequest = await this.olmMachine.getMissingSessions(userList);\n if (claimRequest) {\n await this.outgoingRequestProcessor.makeOutgoingRequest(claimRequest);\n }\n }\n}\n"],"mappings":";;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMA,eAAe,CAAC;EAIlBC,WAAWA,CACGC,UAAsB,EACtBC,wBAAkD,EACrE;IAAA,KAFmBD,UAAsB,GAAtBA,UAAsB;IAAA,KACtBC,wBAAkD,GAAlDA,wBAAkD;IAAA,IAAAC,gBAAA,CAAAC,OAAA;IAAA,IAAAD,gBAAA,CAAAC,OAAA,mBAJrD,KAAK;IAMnB,IAAI,CAACC,mBAAmB,GAAGC,OAAO,CAACC,OAAO,CAAC,CAAC;EAChD;;EAEA;AACJ;AACA;AACA;AACA;EACWC,IAAIA,CAAA,EAAS;IAChB,IAAI,CAACC,OAAO,GAAG,IAAI;EACvB;;EAEA;AACJ;AACA;AACA;AACA;AACA;AACA;EACWC,sBAAsBA,CAACC,QAAuB,EAAiB;IAClE;IACA;IACA;IACA,MAAMC,IAAI,GAAG,IAAI,CAACP,mBAAmB,CAChCQ,KAAK,CAAC,MAAM;MACT;MACA;IAAA,CACH,CAAC,CACDC,IAAI,CAAC,MAAM,IAAI,CAACC,2BAA2B,CAACJ,QAAQ,CAAC,CAAC;IAC3D,IAAI,CAACN,mBAAmB,GAAGO,IAAI;IAC/B,OAAOA,IAAI;EACf;EAEA,MAAcG,2BAA2BA,CAACJ,QAAuB,EAAiB;IAC9E;IACA,IAAI,IAAI,CAACF,OAAO,EAAE;MACd,MAAM,IAAIO,KAAK,CAAE,2CAA0C,CAAC;IAChE;IACA,MAAMC,YAAY,GAAG,MAAM,IAAI,CAAChB,UAAU,CAACiB,kBAAkB,CAACP,QAAQ,CAAC;IACvE,IAAIM,YAAY,EAAE;MACd,MAAM,IAAI,CAACf,wBAAwB,CAACiB,mBAAmB,CAACF,YAAY,CAAC;IACzE;EACJ;AACJ;AAACG,OAAA,CAAArB,eAAA,GAAAA,eAAA"}
|
||||
29
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestProcessor.d.ts
generated
vendored
29
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestProcessor.d.ts
generated
vendored
@@ -1,7 +1,15 @@
|
||||
import { type OlmMachine, type OutgoingRequest, PutDehydratedDeviceRequest, UploadSigningKeysRequest } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { type Logger } from "../logger.ts";
|
||||
import { type IHttpOpts, type MatrixHttpApi } from "../http-api/index.ts";
|
||||
import { type UIAuthCallback } from "../interactive-auth.ts";
|
||||
import { OlmMachine } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { IHttpOpts, MatrixHttpApi } from "../http-api";
|
||||
import { UIAuthCallback } from "../interactive-auth";
|
||||
/**
|
||||
* Common interface for all the request types returned by `OlmMachine.outgoingRequests`.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export interface OutgoingRequest {
|
||||
readonly id: string | undefined;
|
||||
readonly type: number;
|
||||
}
|
||||
/**
|
||||
* OutgoingRequestManager: turns `OutgoingRequest`s from the rust sdk into HTTP requests
|
||||
*
|
||||
@@ -15,22 +23,13 @@ import { type UIAuthCallback } from "../interactive-auth.ts";
|
||||
* @internal
|
||||
*/
|
||||
export declare class OutgoingRequestProcessor {
|
||||
private readonly logger;
|
||||
private readonly olmMachine;
|
||||
private readonly http;
|
||||
constructor(logger: Logger, olmMachine: OlmMachine, http: MatrixHttpApi<IHttpOpts & {
|
||||
constructor(olmMachine: OlmMachine, http: MatrixHttpApi<IHttpOpts & {
|
||||
onlyData: true;
|
||||
}>);
|
||||
makeOutgoingRequest<T>(msg: OutgoingRequest | UploadSigningKeysRequest | PutDehydratedDeviceRequest, uiaCallback?: UIAuthCallback<T>): Promise<void>;
|
||||
/**
|
||||
* Send the HTTP request for a `ToDeviceRequest`
|
||||
*
|
||||
* @param request - request to send
|
||||
* @returns JSON-serialized body of the response, if successful
|
||||
*/
|
||||
private sendToDeviceRequest;
|
||||
makeOutgoingRequest<T>(msg: OutgoingRequest, uiaCallback?: UIAuthCallback<T>): Promise<void>;
|
||||
private makeRequestWithUIA;
|
||||
private requestWithRetry;
|
||||
private rawJsonRequest;
|
||||
}
|
||||
//# sourceMappingURL=OutgoingRequestProcessor.d.ts.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestProcessor.d.ts.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestProcessor.d.ts.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"OutgoingRequestProcessor.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/OutgoingRequestProcessor.ts"],"names":[],"mappings":"AAgBA,OAAO,EAKH,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,0BAA0B,EAI1B,wBAAwB,EAC3B,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAyB,KAAK,SAAS,EAAE,KAAK,aAAa,EAAU,MAAM,sBAAsB,CAAC;AAEzG,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAI5E;;;;;;;;;;;GAWG;AACH,qBAAa,wBAAwB;IAE7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHzB,YACqB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,aAAa,CAAC,SAAS,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC,EACpE;IAES,mBAAmB,CAAC,CAAC,EAC9B,GAAG,EAAE,eAAe,GAAG,wBAAwB,GAAG,0BAA0B,EAC5E,WAAW,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC,CAoEf;IAED;;;;;OAKG;YACW,mBAAmB;YAsBnB,kBAAkB;YA2BlB,gBAAgB;YAwBhB,cAAc;CAuB/B"}
|
||||
{"version":3,"file":"OutgoingRequestProcessor.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/OutgoingRequestProcessor.ts"],"names":[],"mappings":"AAgBA,OAAO,EACH,UAAU,EASb,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EAAE,SAAS,EAAE,aAAa,EAAU,MAAM,aAAa,CAAC;AAE/D,OAAO,EAAa,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGhE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,wBAAwB;IAE7B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI;gBADJ,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,aAAa,CAAC,SAAS,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC;IAG3D,mBAAmB,CAAC,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,WAAW,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;YA4C3F,kBAAkB;YA2BlB,cAAc;CAwB/B"}
|
||||
177
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestProcessor.js
generated
vendored
177
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestProcessor.js
generated
vendored
@@ -1,25 +1,35 @@
|
||||
import _objectSpread from "@babel/runtime/helpers/objectSpread2";
|
||||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
"use strict";
|
||||
|
||||
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 { KeysBackupRequest, KeysClaimRequest, KeysQueryRequest, KeysUploadRequest, PutDehydratedDeviceRequest, RoomMessageRequest, SignatureUploadRequest, ToDeviceRequest, UploadSigningKeysRequest } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { calculateRetryBackoff, Method } from "../http-api/index.js";
|
||||
import { logDuration, sleep } from "../utils.js";
|
||||
import { ToDeviceMessageId } from "../@types/event.js";
|
||||
import { UnstablePrefix as DehydrationUnstablePrefix } from "./DehydratedDeviceManager.js";
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.OutgoingRequestProcessor = void 0;
|
||||
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
||||
var _matrixSdkCryptoWasm = require("@matrix-org/matrix-sdk-crypto-wasm");
|
||||
var _logger = require("../logger");
|
||||
var _httpApi = require("../http-api");
|
||||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } /*
|
||||
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.
|
||||
*/
|
||||
/**
|
||||
* Common interface for all the request types returned by `OlmMachine.outgoingRequests`.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
|
||||
/**
|
||||
* OutgoingRequestManager: turns `OutgoingRequest`s from the rust sdk into HTTP requests
|
||||
@@ -33,9 +43,8 @@ import { UnstablePrefix as DehydrationUnstablePrefix } from "./DehydratedDeviceM
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class OutgoingRequestProcessor {
|
||||
constructor(logger, olmMachine, http) {
|
||||
this.logger = logger;
|
||||
class OutgoingRequestProcessor {
|
||||
constructor(olmMachine, http) {
|
||||
this.olmMachine = olmMachine;
|
||||
this.http = http;
|
||||
}
|
||||
@@ -45,77 +54,35 @@ export class OutgoingRequestProcessor {
|
||||
/* refer https://docs.rs/matrix-sdk-crypto/0.6.0/matrix_sdk_crypto/requests/enum.OutgoingRequests.html
|
||||
* for the complete list of request types
|
||||
*/
|
||||
if (msg instanceof KeysUploadRequest) {
|
||||
resp = await this.requestWithRetry(Method.Post, "/_matrix/client/v3/keys/upload", {}, msg.body);
|
||||
} else if (msg instanceof KeysQueryRequest) {
|
||||
resp = await this.requestWithRetry(Method.Post, "/_matrix/client/v3/keys/query", {}, msg.body);
|
||||
} else if (msg instanceof KeysClaimRequest) {
|
||||
resp = await this.requestWithRetry(Method.Post, "/_matrix/client/v3/keys/claim", {}, msg.body);
|
||||
} else if (msg instanceof SignatureUploadRequest) {
|
||||
resp = await this.requestWithRetry(Method.Post, "/_matrix/client/v3/keys/signatures/upload", {}, msg.body);
|
||||
} else if (msg instanceof KeysBackupRequest) {
|
||||
resp = await this.requestWithRetry(Method.Put, "/_matrix/client/v3/room_keys/keys", {
|
||||
version: msg.version
|
||||
}, msg.body);
|
||||
} else if (msg instanceof ToDeviceRequest) {
|
||||
resp = await this.sendToDeviceRequest(msg);
|
||||
} else if (msg instanceof RoomMessageRequest) {
|
||||
const path = `/_matrix/client/v3/rooms/${encodeURIComponent(msg.room_id)}/send/` + `${encodeURIComponent(msg.event_type)}/${encodeURIComponent(msg.txn_id)}`;
|
||||
resp = await this.requestWithRetry(Method.Put, path, {}, msg.body);
|
||||
} else if (msg instanceof UploadSigningKeysRequest) {
|
||||
await this.makeRequestWithUIA(Method.Post, "/_matrix/client/v3/keys/device_signing/upload", {}, msg.body, uiaCallback);
|
||||
// SigningKeysUploadRequest does not implement OutgoingRequest and does not need to be marked as sent.
|
||||
return;
|
||||
} else if (msg instanceof PutDehydratedDeviceRequest) {
|
||||
const path = DehydrationUnstablePrefix + "/dehydrated_device";
|
||||
await this.rawJsonRequest(Method.Put, path, {}, msg.body);
|
||||
// PutDehydratedDeviceRequest does not implement OutgoingRequest and does not need to be marked as sent.
|
||||
return;
|
||||
if (msg instanceof _matrixSdkCryptoWasm.KeysUploadRequest) {
|
||||
resp = await this.rawJsonRequest(_httpApi.Method.Post, "/_matrix/client/v3/keys/upload", {}, msg.body);
|
||||
} else if (msg instanceof _matrixSdkCryptoWasm.KeysQueryRequest) {
|
||||
resp = await this.rawJsonRequest(_httpApi.Method.Post, "/_matrix/client/v3/keys/query", {}, msg.body);
|
||||
} else if (msg instanceof _matrixSdkCryptoWasm.KeysClaimRequest) {
|
||||
resp = await this.rawJsonRequest(_httpApi.Method.Post, "/_matrix/client/v3/keys/claim", {}, msg.body);
|
||||
} else if (msg instanceof _matrixSdkCryptoWasm.SignatureUploadRequest) {
|
||||
resp = await this.rawJsonRequest(_httpApi.Method.Post, "/_matrix/client/v3/keys/signatures/upload", {}, msg.body);
|
||||
} else if (msg instanceof _matrixSdkCryptoWasm.KeysBackupRequest) {
|
||||
resp = await this.rawJsonRequest(_httpApi.Method.Put, "/_matrix/client/v3/room_keys/keys", {}, msg.body);
|
||||
} else if (msg instanceof _matrixSdkCryptoWasm.ToDeviceRequest) {
|
||||
const path = `/_matrix/client/v3/sendToDevice/${encodeURIComponent(msg.event_type)}/` + encodeURIComponent(msg.txn_id);
|
||||
resp = await this.rawJsonRequest(_httpApi.Method.Put, path, {}, msg.body);
|
||||
} else if (msg instanceof _matrixSdkCryptoWasm.RoomMessageRequest) {
|
||||
const path = `/_matrix/client/v3/room/${encodeURIComponent(msg.room_id)}/send/` + `${encodeURIComponent(msg.event_type)}/${encodeURIComponent(msg.txn_id)}`;
|
||||
resp = await this.rawJsonRequest(_httpApi.Method.Put, path, {}, msg.body);
|
||||
} else if (msg instanceof _matrixSdkCryptoWasm.SigningKeysUploadRequest) {
|
||||
resp = await this.makeRequestWithUIA(_httpApi.Method.Post, "/_matrix/client/v3/keys/device_signing/upload", {}, msg.body, uiaCallback);
|
||||
} else {
|
||||
this.logger.warn("Unsupported outgoing message", Object.getPrototypeOf(msg));
|
||||
_logger.logger.warn("Unsupported outgoing message", Object.getPrototypeOf(msg));
|
||||
resp = "";
|
||||
}
|
||||
if (msg.id) {
|
||||
try {
|
||||
await logDuration(this.logger, `Mark Request as sent ${msg.type}`, async () => {
|
||||
await this.olmMachine.markRequestAsSent(msg.id, msg.type, resp);
|
||||
});
|
||||
} catch (e) {
|
||||
// Ignore errors which are caused by the olmMachine having been freed. The exact error message depends
|
||||
// on whether we are using a release or develop build of rust-sdk-crypto-wasm.
|
||||
if (e instanceof Error && (e.message === "Attempt to use a moved value" || e.message === "null pointer passed to rust")) {
|
||||
this.logger.debug(`Ignoring error '${e.message}': client is likely shutting down`);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.logger.trace(`Outgoing request type:${msg.type} does not have an ID`);
|
||||
await this.olmMachine.markRequestAsSent(msg.id, msg.type, resp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the HTTP request for a `ToDeviceRequest`
|
||||
*
|
||||
* @param request - request to send
|
||||
* @returns JSON-serialized body of the response, if successful
|
||||
*/
|
||||
async sendToDeviceRequest(request) {
|
||||
// a bit of extra logging, to help trace to-device messages through the system
|
||||
const parsedBody = JSON.parse(request.body);
|
||||
const messageList = [];
|
||||
for (const [userId, perUserMessages] of Object.entries(parsedBody.messages)) {
|
||||
for (const [deviceId, message] of Object.entries(perUserMessages)) {
|
||||
messageList.push(`${userId}/${deviceId} (msgid ${message[ToDeviceMessageId]})`);
|
||||
}
|
||||
}
|
||||
this.logger.info(`Sending batch of to-device messages. type=${request.event_type} txnid=${request.txn_id}`, messageList);
|
||||
const path = `/_matrix/client/v3/sendToDevice/${encodeURIComponent(request.event_type)}/` + encodeURIComponent(request.txn_id);
|
||||
return await this.requestWithRetry(Method.Put, path, {}, request.body);
|
||||
}
|
||||
async makeRequestWithUIA(method, path, queryParams, body, uiaCallback) {
|
||||
if (!uiaCallback) {
|
||||
return await this.requestWithRetry(method, path, queryParams, body);
|
||||
return await this.rawJsonRequest(method, path, queryParams, body);
|
||||
}
|
||||
const parsedBody = JSON.parse(body);
|
||||
const makeRequest = async auth => {
|
||||
@@ -123,29 +90,12 @@ export class OutgoingRequestProcessor {
|
||||
if (auth !== null) {
|
||||
newBody.auth = auth;
|
||||
}
|
||||
const resp = await this.requestWithRetry(method, path, queryParams, JSON.stringify(newBody));
|
||||
const resp = await this.rawJsonRequest(method, path, queryParams, JSON.stringify(newBody));
|
||||
return JSON.parse(resp);
|
||||
};
|
||||
const resp = await uiaCallback(makeRequest);
|
||||
return JSON.stringify(resp);
|
||||
}
|
||||
async requestWithRetry(method, path, queryParams, body) {
|
||||
let currentRetryCount = 0;
|
||||
while (true) {
|
||||
try {
|
||||
return await this.rawJsonRequest(method, path, queryParams, body);
|
||||
} catch (e) {
|
||||
currentRetryCount++;
|
||||
const backoff = calculateRetryBackoff(e, currentRetryCount, true);
|
||||
if (backoff < 0) {
|
||||
// Max number of retries reached, or error is not retryable. rethrow the error
|
||||
throw e;
|
||||
}
|
||||
// wait for the specified time and then retry the request
|
||||
await sleep(backoff);
|
||||
}
|
||||
}
|
||||
}
|
||||
async rawJsonRequest(method, path, queryParams, body) {
|
||||
const opts = {
|
||||
// inhibit the JSON stringification and parsing within HttpApi.
|
||||
@@ -156,14 +106,17 @@ export class OutgoingRequestProcessor {
|
||||
"Accept": "application/json"
|
||||
},
|
||||
// we use the full prefix
|
||||
prefix: "",
|
||||
// We set a timeout of 60 seconds to guard against requests getting stuck forever and wedging the
|
||||
// request loop (cf https://github.com/element-hq/element-web/issues/29534).
|
||||
//
|
||||
// (XXX: should we do this in the whole of the js-sdk?)
|
||||
localTimeoutMs: 60000
|
||||
prefix: ""
|
||||
};
|
||||
return await this.http.authedRequest(method, path, queryParams, body, opts);
|
||||
try {
|
||||
const response = await this.http.authedRequest(method, path, queryParams, body, opts);
|
||||
_logger.logger.info(`rust-crypto: successfully made HTTP request: ${method} ${path}`);
|
||||
return response;
|
||||
} catch (e) {
|
||||
_logger.logger.warn(`rust-crypto: error making HTTP request: ${method} ${path}: ${e}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.OutgoingRequestProcessor = OutgoingRequestProcessor;
|
||||
//# sourceMappingURL=OutgoingRequestProcessor.js.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestProcessor.js.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestProcessor.js.map
generated
vendored
File diff suppressed because one or more lines are too long
47
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestsManager.d.ts
generated
vendored
47
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestsManager.d.ts
generated
vendored
@@ -1,47 +0,0 @@
|
||||
import { type OlmMachine } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { type OutgoingRequestProcessor } from "./OutgoingRequestProcessor.ts";
|
||||
import { type Logger } from "../logger.ts";
|
||||
/**
|
||||
* OutgoingRequestsManager: responsible for processing outgoing requests from the OlmMachine.
|
||||
* Ensure that only one loop is going on at once, and that the requests are processed in order.
|
||||
*/
|
||||
export declare class OutgoingRequestsManager {
|
||||
private readonly logger;
|
||||
private readonly olmMachine;
|
||||
readonly outgoingRequestProcessor: OutgoingRequestProcessor;
|
||||
/** whether {@link stop} has been called */
|
||||
private stopped;
|
||||
/** whether {@link outgoingRequestLoop} is currently running */
|
||||
private outgoingRequestLoopRunning;
|
||||
/**
|
||||
* If there are additional calls to doProcessOutgoingRequests() while there is a current call running
|
||||
* we need to remember in order to call `doProcessOutgoingRequests` again (as there could be new requests).
|
||||
*
|
||||
* If this is defined, it is an indication that we need to do another iteration; in this case the deferred
|
||||
* will resolve once that next iteration completes. If it is undefined, there have been no new calls
|
||||
* to `doProcessOutgoingRequests` since the current iteration started.
|
||||
*/
|
||||
private nextLoopDeferred?;
|
||||
constructor(logger: Logger, olmMachine: OlmMachine, outgoingRequestProcessor: OutgoingRequestProcessor);
|
||||
/**
|
||||
* Shut down as soon as possible the current loop of outgoing requests processing.
|
||||
*/
|
||||
stop(): void;
|
||||
/**
|
||||
* Process the OutgoingRequests from the OlmMachine.
|
||||
*
|
||||
* This should be called at the end of each sync, to process any OlmMachine OutgoingRequests created by the rust sdk.
|
||||
* In some cases if OutgoingRequests need to be sent immediately, this can be called directly.
|
||||
*
|
||||
* Calls to doProcessOutgoingRequests() are processed synchronously, one after the other, in order.
|
||||
* If doProcessOutgoingRequests() is called while another call is still being processed, it will be queued.
|
||||
* Multiple calls to doProcessOutgoingRequests() when a call is already processing will be batched together.
|
||||
*/
|
||||
doProcessOutgoingRequests(): Promise<void>;
|
||||
private outgoingRequestLoop;
|
||||
/**
|
||||
* Make a single request to `olmMachine.outgoingRequests` and do the corresponding requests.
|
||||
*/
|
||||
private processOutgoingRequests;
|
||||
}
|
||||
//# sourceMappingURL=OutgoingRequestsManager.d.ts.map
|
||||
1
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestsManager.d.ts.map
generated
vendored
1
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestsManager.d.ts.map
generated
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"OutgoingRequestsManager.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/OutgoingRequestsManager.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,UAAU,EAAwB,MAAM,oCAAoC,CAAC;AAE3F,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C;;;GAGG;AACH,qBAAa,uBAAuB;IAkB5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;aACX,wBAAwB,EAAE,wBAAwB;IAnBtE,2CAA2C;IAC3C,OAAO,CAAC,OAAO,CAAS;IAExB,+DAA+D;IAC/D,OAAO,CAAC,0BAA0B,CAAS;IAE3C;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB,CAAC,CAA6B;IAEtD,YACqB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,EACvB,wBAAwB,EAAE,wBAAwB,EAClE;IAEJ;;OAEG;IACI,IAAI,IAAI,IAAI,CAElB;IAED;;;;;;;;;OASG;IACI,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CAyBhD;YAEa,mBAAmB;IA4BjC;;OAEG;YACW,uBAAuB;CA6CxC"}
|
||||
161
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestsManager.js
generated
vendored
161
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestsManager.js
generated
vendored
@@ -1,161 +0,0 @@
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
/*
|
||||
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 { logDuration } from "../utils.js";
|
||||
|
||||
/**
|
||||
* OutgoingRequestsManager: responsible for processing outgoing requests from the OlmMachine.
|
||||
* Ensure that only one loop is going on at once, and that the requests are processed in order.
|
||||
*/
|
||||
export class OutgoingRequestsManager {
|
||||
constructor(logger, olmMachine, outgoingRequestProcessor) {
|
||||
/** whether {@link stop} has been called */
|
||||
_defineProperty(this, "stopped", false);
|
||||
/** whether {@link outgoingRequestLoop} is currently running */
|
||||
_defineProperty(this, "outgoingRequestLoopRunning", false);
|
||||
/**
|
||||
* If there are additional calls to doProcessOutgoingRequests() while there is a current call running
|
||||
* we need to remember in order to call `doProcessOutgoingRequests` again (as there could be new requests).
|
||||
*
|
||||
* If this is defined, it is an indication that we need to do another iteration; in this case the deferred
|
||||
* will resolve once that next iteration completes. If it is undefined, there have been no new calls
|
||||
* to `doProcessOutgoingRequests` since the current iteration started.
|
||||
*/
|
||||
_defineProperty(this, "nextLoopDeferred", void 0);
|
||||
this.logger = logger;
|
||||
this.olmMachine = olmMachine;
|
||||
this.outgoingRequestProcessor = outgoingRequestProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shut down as soon as possible the current loop of outgoing requests processing.
|
||||
*/
|
||||
stop() {
|
||||
this.stopped = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the OutgoingRequests from the OlmMachine.
|
||||
*
|
||||
* This should be called at the end of each sync, to process any OlmMachine OutgoingRequests created by the rust sdk.
|
||||
* In some cases if OutgoingRequests need to be sent immediately, this can be called directly.
|
||||
*
|
||||
* Calls to doProcessOutgoingRequests() are processed synchronously, one after the other, in order.
|
||||
* If doProcessOutgoingRequests() is called while another call is still being processed, it will be queued.
|
||||
* Multiple calls to doProcessOutgoingRequests() when a call is already processing will be batched together.
|
||||
*/
|
||||
doProcessOutgoingRequests() {
|
||||
// Flag that we need at least one more iteration of the loop.
|
||||
//
|
||||
// It is important that we do this even if the loop is currently running. There is potential for a race whereby
|
||||
// a request is added to the queue *after* `OlmMachine.outgoingRequests` checks the queue, but *before* it
|
||||
// returns. In such a case, the item could sit there unnoticed for some time.
|
||||
//
|
||||
// In order to circumvent the race, we set a flag which tells the loop to go round once again even if the
|
||||
// queue appears to be empty.
|
||||
if (!this.nextLoopDeferred) {
|
||||
this.nextLoopDeferred = Promise.withResolvers();
|
||||
}
|
||||
|
||||
// ... and wait for it to complete.
|
||||
const result = this.nextLoopDeferred.promise;
|
||||
|
||||
// set the loop going if it is not already.
|
||||
if (!this.outgoingRequestLoopRunning) {
|
||||
this.outgoingRequestLoop().catch(e => {
|
||||
// this should not happen; outgoingRequestLoop should return any errors via `nextLoopDeferred`.
|
||||
/* istanbul ignore next */
|
||||
this.logger.error("Uncaught error in outgoing request loop", e);
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
async outgoingRequestLoop() {
|
||||
/* istanbul ignore if */
|
||||
if (this.outgoingRequestLoopRunning) {
|
||||
throw new Error("Cannot run two outgoing request loops");
|
||||
}
|
||||
this.outgoingRequestLoopRunning = true;
|
||||
try {
|
||||
while (!this.stopped && this.nextLoopDeferred) {
|
||||
const loopTickResolvers = this.nextLoopDeferred;
|
||||
|
||||
// reset `nextLoopDeferred` so that any future calls to `doProcessOutgoingRequests` are queued
|
||||
// for another additional iteration.
|
||||
this.nextLoopDeferred = undefined;
|
||||
|
||||
// make the requests and feed the results back to the `nextLoopDeferred`
|
||||
await this.processOutgoingRequests().then(loopTickResolvers.resolve, loopTickResolvers.reject);
|
||||
}
|
||||
} finally {
|
||||
this.outgoingRequestLoopRunning = false;
|
||||
}
|
||||
if (this.nextLoopDeferred) {
|
||||
// the loop was stopped, but there was a call to `doProcessOutgoingRequests`. Make sure that
|
||||
// we reject the promise in case anything is waiting for it.
|
||||
this.nextLoopDeferred.reject(new Error("OutgoingRequestsManager was stopped"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a single request to `olmMachine.outgoingRequests` and do the corresponding requests.
|
||||
*/
|
||||
async processOutgoingRequests() {
|
||||
if (this.stopped) return;
|
||||
const outgoingRequests = await this.olmMachine.outgoingRequests();
|
||||
let successes = 0;
|
||||
for (const request of outgoingRequests) {
|
||||
if (this.stopped) return;
|
||||
try {
|
||||
await logDuration(this.logger, `Make outgoing request ${request.type}`, async () => {
|
||||
await this.outgoingRequestProcessor.makeOutgoingRequest(request);
|
||||
successes++;
|
||||
});
|
||||
} catch (e) {
|
||||
// as part of the loop we silently ignore errors, but log them.
|
||||
// The rust sdk will retry the request later as it won't have been marked as sent.
|
||||
this.logger.error(`Failed to process outgoing request ${request.type}: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
// If we successfully handled any requests this time, more may have been queued as
|
||||
// part of that handling.
|
||||
//
|
||||
// For example, we may have processed a `/keys/claim` request, which
|
||||
// meant the rust side could establish an Olm session and is now ready to
|
||||
// send out an `m.secret.send` message.
|
||||
// (See https://github.com/element-hq/element-web/issues/30988.)
|
||||
//
|
||||
// So, if we have successfully processed any requests, flag that we need to make another
|
||||
// pass around the outgoing-requests loop, to make sure we handle any
|
||||
// pending requests immediately.
|
||||
//
|
||||
// If all requests failed (or there weren't any) we don't want to retry them in a tight
|
||||
// loop. They will be retried after the next sync.
|
||||
// (See https://github.com/element-hq/element-web/issues/31790.)
|
||||
if (successes > 0) {
|
||||
// We call doProcessOutgoingRequests but since we expect that we are
|
||||
// already processing outgoing requests, this call will not kick off
|
||||
// the processing loop, but just set `nextLoopDeferred` and return,
|
||||
// which will mean we loop one more time.
|
||||
this.doProcessOutgoingRequests().catch(e => {
|
||||
this.logger.warn("processOutgoingRequests: Error re-checking outgoing requests", e);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=OutgoingRequestsManager.js.map
|
||||
1
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestsManager.js.map
generated
vendored
1
node_modules/matrix-js-sdk/lib/rust-crypto/OutgoingRequestsManager.js.map
generated
vendored
File diff suppressed because one or more lines are too long
120
node_modules/matrix-js-sdk/lib/rust-crypto/PerSessionKeyBackupDownloader.d.ts
generated
vendored
120
node_modules/matrix-js-sdk/lib/rust-crypto/PerSessionKeyBackupDownloader.d.ts
generated
vendored
@@ -1,120 +0,0 @@
|
||||
import { type OlmMachine } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { type KeyBackupInfo } from "../crypto-api/keybackup.ts";
|
||||
import { type Logger } from "../logger.ts";
|
||||
import { type IHttpOpts, type MatrixHttpApi } from "../http-api/index.ts";
|
||||
import { type RustBackupManager } from "./backup.ts";
|
||||
/**
|
||||
* Used when an 'unable to decrypt' error occurs. It attempts to download the key from the backup.
|
||||
*
|
||||
* The current backup API lacks pagination, which can lead to lengthy key retrieval times for large histories (several 10s of minutes).
|
||||
* To mitigate this, keys are downloaded on demand as decryption errors occurs.
|
||||
* While this approach may result in numerous requests, it improves user experience by reducing wait times for message decryption.
|
||||
*
|
||||
* The PerSessionKeyBackupDownloader is resistant to backup configuration changes: it will automatically resume querying when
|
||||
* the backup is configured correctly.
|
||||
*/
|
||||
export declare class PerSessionKeyBackupDownloader {
|
||||
private readonly olmMachine;
|
||||
private readonly http;
|
||||
private readonly backupManager;
|
||||
private stopped;
|
||||
/**
|
||||
* The version and decryption key to use with current backup if all set up correctly.
|
||||
*
|
||||
* Will not be set unless `hasConfigurationProblem` is `false`.
|
||||
*/
|
||||
private configuration;
|
||||
/** We remember when a session was requested and not found in backup to avoid query again too soon.
|
||||
* Map of session_id to timestamp */
|
||||
private sessionLastCheckAttemptedTime;
|
||||
/** The logger to use */
|
||||
private readonly logger;
|
||||
/** Whether the download loop is running. */
|
||||
private downloadLoopRunning;
|
||||
/** The list of requests that are queued. */
|
||||
private queuedRequests;
|
||||
/** Remembers if we have a configuration problem. */
|
||||
private hasConfigurationProblem;
|
||||
/** The current server backup version check promise. To avoid doing a server call if one is in flight. */
|
||||
private currentBackupVersionCheck;
|
||||
/**
|
||||
* Creates a new instance of PerSessionKeyBackupDownloader.
|
||||
*
|
||||
* @param backupManager - The backup manager to use.
|
||||
* @param olmMachine - The olm machine to use.
|
||||
* @param http - The http instance to use.
|
||||
* @param logger - The logger to use.
|
||||
*/
|
||||
constructor(logger: Logger, olmMachine: OlmMachine, http: MatrixHttpApi<IHttpOpts & {
|
||||
onlyData: true;
|
||||
}>, backupManager: RustBackupManager);
|
||||
/**
|
||||
* Check if key download is successfully configured and active.
|
||||
*
|
||||
* @returns `true` if key download is correctly configured and active; otherwise `false`.
|
||||
*/
|
||||
isKeyBackupDownloadConfigured(): boolean;
|
||||
/**
|
||||
* Return the details of the latest backup on the server, when we last checked.
|
||||
*
|
||||
* This is just a convenience method to expose {@link RustBackupManager.getServerBackupInfo}.
|
||||
*/
|
||||
getServerBackupInfo(): Promise<KeyBackupInfo | null | undefined>;
|
||||
/**
|
||||
* Called when a MissingRoomKey or UnknownMessageIndex decryption error is encountered.
|
||||
*
|
||||
* This will try to download the key from the backup if there is a trusted active backup.
|
||||
* In case of success the key will be imported and the onRoomKeysUpdated callback will be called
|
||||
* internally by the rust-sdk and decryption will be retried.
|
||||
*
|
||||
* @param roomId - The room ID of the room where the error occurred.
|
||||
* @param megolmSessionId - The megolm session ID that is missing.
|
||||
*/
|
||||
onDecryptionKeyMissingError(roomId: string, megolmSessionId: string): void;
|
||||
stop(): void;
|
||||
/**
|
||||
* Called when the backup status changes (CryptoEvents)
|
||||
* This will trigger a check of the backup configuration.
|
||||
*/
|
||||
private onBackupStatusChanged;
|
||||
/** Returns true if the megolm session is already queued for download. */
|
||||
private isAlreadyInQueue;
|
||||
/**
|
||||
* Marks the session as not found in backup, to avoid retrying to soon for a key not in backup
|
||||
*
|
||||
* @param megolmSessionId - The megolm session ID that is missing.
|
||||
*/
|
||||
private markAsNotFoundInBackup;
|
||||
/** Returns true if the session was requested recently. */
|
||||
private wasRequestedRecently;
|
||||
private getBackupDecryptionKey;
|
||||
/**
|
||||
* Requests a key from the server side backup.
|
||||
*
|
||||
* @param version - The backup version to use.
|
||||
* @param roomId - The room ID of the room where the error occurred.
|
||||
* @param sessionId - The megolm session ID that is missing.
|
||||
*/
|
||||
private requestRoomKeyFromBackup;
|
||||
private downloadKeysLoop;
|
||||
/**
|
||||
* Query the backup for a key.
|
||||
*
|
||||
* @param targetRoomId - ID of the room that the session is used in.
|
||||
* @param targetSessionId - ID of the session for which to check backup.
|
||||
* @param configuration - The backup configuration to use.
|
||||
*/
|
||||
private queryKeyBackup;
|
||||
private decryptAndImport;
|
||||
/**
|
||||
* Gets the current backup configuration or create one if it doesn't exist.
|
||||
*
|
||||
* When a valid configuration is found it is cached and returned for subsequent calls.
|
||||
* Otherwise, if a check is forced or a check has not yet been done, a new check is done.
|
||||
*
|
||||
* @returns The backup configuration to use or null if there is a configuration problem.
|
||||
*/
|
||||
private getOrCreateBackupConfiguration;
|
||||
private internalCheckFromServer;
|
||||
}
|
||||
//# sourceMappingURL=PerSessionKeyBackupDownloader.d.ts.map
|
||||
1
node_modules/matrix-js-sdk/lib/rust-crypto/PerSessionKeyBackupDownloader.d.ts.map
generated
vendored
1
node_modules/matrix-js-sdk/lib/rust-crypto/PerSessionKeyBackupDownloader.d.ts.map
generated
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"PerSessionKeyBackupDownloader.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/PerSessionKeyBackupDownloader.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAGrE,OAAO,EAA2B,KAAK,aAAa,EAAyB,MAAM,4BAA4B,CAAC;AAEhH,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAgB,KAAK,SAAS,EAAe,KAAK,aAAa,EAAU,MAAM,sBAAsB,CAAC;AAC7G,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA8CrD;;;;;;;;;GASG;AACH,qBAAa,6BAA6B;IAuClC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAxClC,OAAO,CAAC,OAAO,CAAS;IAExB;;;;OAIG;IACH,OAAO,CAAC,aAAa,CAA8B;IAEnD;wCACoC;IACpC,OAAO,CAAC,6BAA6B,CAAkC;IAEvE,wBAAwB;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC,4CAA4C;IAC5C,OAAO,CAAC,mBAAmB,CAAS;IAEpC,4CAA4C;IAC5C,OAAO,CAAC,cAAc,CAAqB;IAE3C,oDAAoD;IACpD,OAAO,CAAC,uBAAuB,CAAS;IAExC,yGAAyG;IACzG,OAAO,CAAC,yBAAyB,CAA8C;IAE/E;;;;;;;OAOG;IACH,YACI,MAAM,EAAE,MAAM,EACG,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,aAAa,CAAC,SAAS,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC,EACnD,aAAa,EAAE,iBAAiB,EAOpD;IAED;;;;OAIG;IACI,6BAA6B,IAAI,OAAO,CAE9C;IAED;;;;OAIG;IACU,mBAAmB,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,GAAG,SAAS,CAAC,CAE5E;IAED;;;;;;;;;OASG;IACI,2BAA2B,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,CA0BhF;IAEM,IAAI,IAAI,IAAI,CAKlB;IAED;;;OAGG;IACH,OAAO,CAAC,qBAAqB,CAU3B;IAEF,yEAAyE;IACzE,OAAO,CAAC,gBAAgB;IAMxB;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAa9B,0DAA0D;IAC1D,OAAO,CAAC,oBAAoB;YAMd,sBAAsB;IAQpC;;;;;;OAMG;YACW,wBAAwB;YAexB,gBAAgB;IAoE9B;;;;;;OAMG;YACW,cAAc;YA4Cd,gBAAgB;IAc9B;;;;;;;OAOG;YACW,8BAA8B;YA0B9B,uBAAuB;CA+DxC"}
|
||||
441
node_modules/matrix-js-sdk/lib/rust-crypto/PerSessionKeyBackupDownloader.js
generated
vendored
441
node_modules/matrix-js-sdk/lib/rust-crypto/PerSessionKeyBackupDownloader.js
generated
vendored
@@ -1,441 +0,0 @@
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
/*
|
||||
Copyright 2023 - 2024 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 { CryptoEvent } from "../crypto-api/index.js";
|
||||
import { ClientPrefix, MatrixError, Method } from "../http-api/index.js";
|
||||
import { encodeUri, sleep } from "../utils.js";
|
||||
// The minimum time to wait between two retries in case of errors. To avoid hammering the server.
|
||||
const KEY_BACKUP_BACKOFF = 5000; // ms
|
||||
|
||||
/**
|
||||
* Enumerates the different kind of errors that can occurs when downloading and importing a key from backup.
|
||||
*/
|
||||
var KeyDownloadErrorCode = /*#__PURE__*/function (KeyDownloadErrorCode) {
|
||||
/** The requested key is not in the backup. */
|
||||
KeyDownloadErrorCode["MISSING_DECRYPTION_KEY"] = "MISSING_DECRYPTION_KEY";
|
||||
/** A network error occurred while trying to download the key from backup. */
|
||||
KeyDownloadErrorCode["NETWORK_ERROR"] = "NETWORK_ERROR";
|
||||
/** The loop has been stopped. */
|
||||
KeyDownloadErrorCode["STOPPED"] = "STOPPED";
|
||||
return KeyDownloadErrorCode;
|
||||
}(KeyDownloadErrorCode || {});
|
||||
class KeyDownloadError extends Error {
|
||||
constructor(code) {
|
||||
super(`Failed to get key from backup: ${code}`);
|
||||
this.code = code;
|
||||
this.name = "KeyDownloadError";
|
||||
}
|
||||
}
|
||||
class KeyDownloadRateLimitError extends Error {
|
||||
constructor(retryMillis) {
|
||||
super(`Failed to get key from backup: rate limited`);
|
||||
this.retryMillis = retryMillis;
|
||||
this.name = "KeyDownloadRateLimitError";
|
||||
}
|
||||
}
|
||||
|
||||
/** Details of a megolm session whose key we are trying to fetch. */
|
||||
|
||||
/** Holds the current backup decryptor and version that should be used.
|
||||
*
|
||||
* This is intended to be used as an immutable object (a new instance should be created if the configuration changes),
|
||||
* and some of the logic relies on that, so the properties are marked as `readonly`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Used when an 'unable to decrypt' error occurs. It attempts to download the key from the backup.
|
||||
*
|
||||
* The current backup API lacks pagination, which can lead to lengthy key retrieval times for large histories (several 10s of minutes).
|
||||
* To mitigate this, keys are downloaded on demand as decryption errors occurs.
|
||||
* While this approach may result in numerous requests, it improves user experience by reducing wait times for message decryption.
|
||||
*
|
||||
* The PerSessionKeyBackupDownloader is resistant to backup configuration changes: it will automatically resume querying when
|
||||
* the backup is configured correctly.
|
||||
*/
|
||||
export class PerSessionKeyBackupDownloader {
|
||||
/**
|
||||
* Creates a new instance of PerSessionKeyBackupDownloader.
|
||||
*
|
||||
* @param backupManager - The backup manager to use.
|
||||
* @param olmMachine - The olm machine to use.
|
||||
* @param http - The http instance to use.
|
||||
* @param logger - The logger to use.
|
||||
*/
|
||||
constructor(logger, olmMachine, http, backupManager) {
|
||||
_defineProperty(this, "stopped", false);
|
||||
/**
|
||||
* The version and decryption key to use with current backup if all set up correctly.
|
||||
*
|
||||
* Will not be set unless `hasConfigurationProblem` is `false`.
|
||||
*/
|
||||
_defineProperty(this, "configuration", null);
|
||||
/** We remember when a session was requested and not found in backup to avoid query again too soon.
|
||||
* Map of session_id to timestamp */
|
||||
_defineProperty(this, "sessionLastCheckAttemptedTime", new Map());
|
||||
/** The logger to use */
|
||||
_defineProperty(this, "logger", void 0);
|
||||
/** Whether the download loop is running. */
|
||||
_defineProperty(this, "downloadLoopRunning", false);
|
||||
/** The list of requests that are queued. */
|
||||
_defineProperty(this, "queuedRequests", []);
|
||||
/** Remembers if we have a configuration problem. */
|
||||
_defineProperty(this, "hasConfigurationProblem", false);
|
||||
/** The current server backup version check promise. To avoid doing a server call if one is in flight. */
|
||||
_defineProperty(this, "currentBackupVersionCheck", null);
|
||||
/**
|
||||
* Called when the backup status changes (CryptoEvents)
|
||||
* This will trigger a check of the backup configuration.
|
||||
*/
|
||||
_defineProperty(this, "onBackupStatusChanged", () => {
|
||||
// we want to force check configuration, so we clear the current one.
|
||||
this.hasConfigurationProblem = false;
|
||||
this.configuration = null;
|
||||
this.getOrCreateBackupConfiguration().then(configuration => {
|
||||
if (configuration) {
|
||||
// restart the download loop if it was stopped
|
||||
this.downloadKeysLoop();
|
||||
}
|
||||
});
|
||||
});
|
||||
this.olmMachine = olmMachine;
|
||||
this.http = http;
|
||||
this.backupManager = backupManager;
|
||||
this.logger = logger.getChild("[PerSessionKeyBackupDownloader]");
|
||||
backupManager.on(CryptoEvent.KeyBackupStatus, this.onBackupStatusChanged);
|
||||
backupManager.on(CryptoEvent.KeyBackupFailed, this.onBackupStatusChanged);
|
||||
backupManager.on(CryptoEvent.KeyBackupDecryptionKeyCached, this.onBackupStatusChanged);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if key download is successfully configured and active.
|
||||
*
|
||||
* @returns `true` if key download is correctly configured and active; otherwise `false`.
|
||||
*/
|
||||
isKeyBackupDownloadConfigured() {
|
||||
return this.configuration !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the details of the latest backup on the server, when we last checked.
|
||||
*
|
||||
* This is just a convenience method to expose {@link RustBackupManager.getServerBackupInfo}.
|
||||
*/
|
||||
async getServerBackupInfo() {
|
||||
return await this.backupManager.getServerBackupInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a MissingRoomKey or UnknownMessageIndex decryption error is encountered.
|
||||
*
|
||||
* This will try to download the key from the backup if there is a trusted active backup.
|
||||
* In case of success the key will be imported and the onRoomKeysUpdated callback will be called
|
||||
* internally by the rust-sdk and decryption will be retried.
|
||||
*
|
||||
* @param roomId - The room ID of the room where the error occurred.
|
||||
* @param megolmSessionId - The megolm session ID that is missing.
|
||||
*/
|
||||
onDecryptionKeyMissingError(roomId, megolmSessionId) {
|
||||
// Several messages encrypted with the same session may be decrypted at the same time,
|
||||
// so we need to be resistant and not query several time the same session.
|
||||
if (this.isAlreadyInQueue(roomId, megolmSessionId)) {
|
||||
// There is already a request queued for this session, no need to queue another one.
|
||||
this.logger.trace(`Not checking key backup for session ${megolmSessionId} as it is already queued`);
|
||||
return;
|
||||
}
|
||||
if (this.wasRequestedRecently(megolmSessionId)) {
|
||||
// We already tried to download this session recently and it was not in backup, no need to try again.
|
||||
this.logger.trace(`Not checking key backup for session ${megolmSessionId} as it was already requested recently`);
|
||||
return;
|
||||
}
|
||||
|
||||
// We always add the request to the queue, even if we have a configuration problem (can't access backup).
|
||||
// This is to make sure that if the configuration problem is resolved, we will try to download the key.
|
||||
// This will happen after an initial sync, at this point the backup will not yet be trusted and the decryption
|
||||
// key will not be available, but it will be just after the verification.
|
||||
// We don't need to persist it because currently on refresh the sdk will retry to decrypt the messages in error.
|
||||
this.queuedRequests.push({
|
||||
roomId,
|
||||
megolmSessionId
|
||||
});
|
||||
|
||||
// Start the download loop if it's not already running.
|
||||
this.downloadKeysLoop();
|
||||
}
|
||||
stop() {
|
||||
this.stopped = true;
|
||||
this.backupManager.off(CryptoEvent.KeyBackupStatus, this.onBackupStatusChanged);
|
||||
this.backupManager.off(CryptoEvent.KeyBackupFailed, this.onBackupStatusChanged);
|
||||
this.backupManager.off(CryptoEvent.KeyBackupDecryptionKeyCached, this.onBackupStatusChanged);
|
||||
}
|
||||
/** Returns true if the megolm session is already queued for download. */
|
||||
isAlreadyInQueue(roomId, megolmSessionId) {
|
||||
return this.queuedRequests.some(info => {
|
||||
return info.roomId == roomId && info.megolmSessionId == megolmSessionId;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the session as not found in backup, to avoid retrying to soon for a key not in backup
|
||||
*
|
||||
* @param megolmSessionId - The megolm session ID that is missing.
|
||||
*/
|
||||
markAsNotFoundInBackup(megolmSessionId) {
|
||||
const now = Date.now();
|
||||
this.sessionLastCheckAttemptedTime.set(megolmSessionId, now);
|
||||
// if too big make some cleaning to keep under control
|
||||
if (this.sessionLastCheckAttemptedTime.size > 100) {
|
||||
this.sessionLastCheckAttemptedTime = new Map(Array.from(this.sessionLastCheckAttemptedTime).filter((sid, ts) => {
|
||||
return Math.max(now - ts, 0) < KEY_BACKUP_BACKOFF;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if the session was requested recently. */
|
||||
wasRequestedRecently(megolmSessionId) {
|
||||
const lastCheck = this.sessionLastCheckAttemptedTime.get(megolmSessionId);
|
||||
if (!lastCheck) return false;
|
||||
return Math.max(Date.now() - lastCheck, 0) < KEY_BACKUP_BACKOFF;
|
||||
}
|
||||
async getBackupDecryptionKey() {
|
||||
try {
|
||||
return await this.olmMachine.getBackupKeys();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests a key from the server side backup.
|
||||
*
|
||||
* @param version - The backup version to use.
|
||||
* @param roomId - The room ID of the room where the error occurred.
|
||||
* @param sessionId - The megolm session ID that is missing.
|
||||
*/
|
||||
async requestRoomKeyFromBackup(version, roomId, sessionId) {
|
||||
const path = encodeUri("/room_keys/keys/$roomId/$sessionId", {
|
||||
$roomId: roomId,
|
||||
$sessionId: sessionId
|
||||
});
|
||||
return await this.http.authedRequest(Method.Get, path, {
|
||||
version
|
||||
}, undefined, {
|
||||
prefix: ClientPrefix.V3
|
||||
});
|
||||
}
|
||||
async downloadKeysLoop() {
|
||||
if (this.downloadLoopRunning) return;
|
||||
|
||||
// If we have a configuration problem, we don't want to try to download.
|
||||
// If any configuration change is detected, we will retry and restart the loop.
|
||||
if (this.hasConfigurationProblem) return;
|
||||
this.downloadLoopRunning = true;
|
||||
try {
|
||||
while (this.queuedRequests.length > 0) {
|
||||
// we just peek the first one without removing it, so if a new request for same key comes in while we're
|
||||
// processing this one, it won't queue another request.
|
||||
const request = this.queuedRequests[0];
|
||||
try {
|
||||
// The backup could have changed between the time we queued the request and now, so we need to check
|
||||
const configuration = await this.getOrCreateBackupConfiguration();
|
||||
if (!configuration) {
|
||||
// Backup is not configured correctly, so stop the loop.
|
||||
this.downloadLoopRunning = false;
|
||||
return;
|
||||
}
|
||||
const result = await this.queryKeyBackup(request.roomId, request.megolmSessionId, configuration);
|
||||
if (this.stopped) {
|
||||
return;
|
||||
}
|
||||
// We got the encrypted key from backup, let's try to decrypt and import it.
|
||||
try {
|
||||
await this.decryptAndImport(request, result, configuration);
|
||||
} catch (e) {
|
||||
this.logger.error(`Error while decrypting and importing key backup for session ${request.megolmSessionId}`, e);
|
||||
}
|
||||
// now remove the request from the queue as we've processed it.
|
||||
this.queuedRequests.shift();
|
||||
} catch (err) {
|
||||
if (err instanceof KeyDownloadError) {
|
||||
switch (err.code) {
|
||||
case KeyDownloadErrorCode.MISSING_DECRYPTION_KEY:
|
||||
this.markAsNotFoundInBackup(request.megolmSessionId);
|
||||
// continue for next one
|
||||
this.queuedRequests.shift();
|
||||
break;
|
||||
case KeyDownloadErrorCode.NETWORK_ERROR:
|
||||
// We don't want to hammer if there is a problem, so wait a bit.
|
||||
await sleep(KEY_BACKUP_BACKOFF);
|
||||
break;
|
||||
case KeyDownloadErrorCode.STOPPED:
|
||||
// If the downloader was stopped, we don't want to retry.
|
||||
this.downloadLoopRunning = false;
|
||||
return;
|
||||
}
|
||||
} else if (err instanceof KeyDownloadRateLimitError) {
|
||||
// we want to retry after the backoff time
|
||||
await sleep(err.retryMillis);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// all pending request have been processed, we can stop the loop.
|
||||
this.downloadLoopRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the backup for a key.
|
||||
*
|
||||
* @param targetRoomId - ID of the room that the session is used in.
|
||||
* @param targetSessionId - ID of the session for which to check backup.
|
||||
* @param configuration - The backup configuration to use.
|
||||
*/
|
||||
async queryKeyBackup(targetRoomId, targetSessionId, configuration) {
|
||||
this.logger.debug(`Checking key backup for session ${targetSessionId}`);
|
||||
if (this.stopped) throw new KeyDownloadError(KeyDownloadErrorCode.STOPPED);
|
||||
try {
|
||||
const res = await this.requestRoomKeyFromBackup(configuration.backupVersion, targetRoomId, targetSessionId);
|
||||
this.logger.debug(`Got key from backup for sessionId:${targetSessionId}`);
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (this.stopped) throw new KeyDownloadError(KeyDownloadErrorCode.STOPPED);
|
||||
this.logger.info(`No luck requesting key backup for session ${targetSessionId}: ${e}`);
|
||||
if (e instanceof MatrixError) {
|
||||
const errCode = e.data.errcode;
|
||||
if (errCode == "M_NOT_FOUND") {
|
||||
// Unfortunately the spec doesn't give us a way to differentiate between a missing key and a wrong version.
|
||||
// Synapse will return:
|
||||
// - "error": "Unknown backup version" if the version is wrong.
|
||||
// - "error": "No room_keys found" if the key is missing.
|
||||
// It's useful to know if the key is missing or if the version is wrong.
|
||||
// As it's not spec'ed, we fall back on considering the key is not in backup.
|
||||
// Notice that this request will be lost if instead the backup got out of sync (updated from other session).
|
||||
throw new KeyDownloadError(KeyDownloadErrorCode.MISSING_DECRYPTION_KEY);
|
||||
}
|
||||
if (e.isRateLimitError()) {
|
||||
let waitTime;
|
||||
try {
|
||||
waitTime = e.getRetryAfterMs() ?? undefined;
|
||||
} catch (error) {
|
||||
this.logger.warn("Error while retrieving a rate-limit retry delay", error);
|
||||
}
|
||||
if (waitTime && waitTime > 0) {
|
||||
this.logger.info(`Rate limited by server, waiting ${waitTime}ms`);
|
||||
}
|
||||
throw new KeyDownloadRateLimitError(waitTime ?? KEY_BACKUP_BACKOFF);
|
||||
}
|
||||
}
|
||||
throw new KeyDownloadError(KeyDownloadErrorCode.NETWORK_ERROR);
|
||||
}
|
||||
}
|
||||
async decryptAndImport(sessionInfo, data, configuration) {
|
||||
const sessionsToImport = {
|
||||
[sessionInfo.megolmSessionId]: data
|
||||
};
|
||||
const keys = await configuration.decryptor.decryptSessions(sessionsToImport);
|
||||
for (const k of keys) {
|
||||
k.room_id = sessionInfo.roomId;
|
||||
}
|
||||
await this.backupManager.importBackedUpRoomKeys(keys, configuration.backupVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current backup configuration or create one if it doesn't exist.
|
||||
*
|
||||
* When a valid configuration is found it is cached and returned for subsequent calls.
|
||||
* Otherwise, if a check is forced or a check has not yet been done, a new check is done.
|
||||
*
|
||||
* @returns The backup configuration to use or null if there is a configuration problem.
|
||||
*/
|
||||
async getOrCreateBackupConfiguration() {
|
||||
if (this.configuration) {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
// We already tried to check the configuration and it failed.
|
||||
// We don't want to try again immediately, we will retry if a configuration change is detected.
|
||||
if (this.hasConfigurationProblem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// This method can be called rapidly by several emitted CryptoEvent, so we need to make sure that we don't
|
||||
// query the server several times.
|
||||
if (this.currentBackupVersionCheck != null) {
|
||||
this.logger.debug(`Already checking server version, use current promise`);
|
||||
return await this.currentBackupVersionCheck;
|
||||
}
|
||||
this.currentBackupVersionCheck = this.internalCheckFromServer();
|
||||
try {
|
||||
return await this.currentBackupVersionCheck;
|
||||
} finally {
|
||||
this.currentBackupVersionCheck = null;
|
||||
}
|
||||
}
|
||||
async internalCheckFromServer() {
|
||||
let currentServerVersion = null;
|
||||
try {
|
||||
currentServerVersion = await this.backupManager.getServerBackupInfo();
|
||||
} catch (e) {
|
||||
this.logger.debug(`Backup: error while checking server version: ${e}`);
|
||||
this.hasConfigurationProblem = true;
|
||||
return null;
|
||||
}
|
||||
this.logger.debug(`Got current backup version from server: ${currentServerVersion?.version}`);
|
||||
if (currentServerVersion?.algorithm != "m.megolm_backup.v1.curve25519-aes-sha2") {
|
||||
this.logger.info(`Unsupported algorithm ${currentServerVersion?.algorithm}`);
|
||||
this.hasConfigurationProblem = true;
|
||||
return null;
|
||||
}
|
||||
if (!currentServerVersion?.version) {
|
||||
this.logger.info(`No current key backup`);
|
||||
this.hasConfigurationProblem = true;
|
||||
return null;
|
||||
}
|
||||
const activeVersion = await this.backupManager.getActiveBackupVersion();
|
||||
if (activeVersion == null || currentServerVersion.version != activeVersion) {
|
||||
// Either the current backup version on server side is not trusted, or it is out of sync with the active version on the client side.
|
||||
this.logger.info(`The current backup version on the server (${currentServerVersion.version}) is not trusted. Version we are currently backing up to: ${activeVersion}`);
|
||||
this.hasConfigurationProblem = true;
|
||||
return null;
|
||||
}
|
||||
const backupKeys = await this.getBackupDecryptionKey();
|
||||
if (!backupKeys?.decryptionKey) {
|
||||
this.logger.debug(`Not checking key backup for session (no decryption key)`);
|
||||
this.hasConfigurationProblem = true;
|
||||
return null;
|
||||
}
|
||||
if (activeVersion != backupKeys.backupVersion) {
|
||||
this.logger.debug(`Version for which we have a decryption key (${backupKeys.backupVersion}) doesn't match the version we are backing up to (${activeVersion})`);
|
||||
this.hasConfigurationProblem = true;
|
||||
return null;
|
||||
}
|
||||
const authData = currentServerVersion.auth_data;
|
||||
if (authData.public_key != backupKeys.decryptionKey.megolmV1PublicKey.publicKeyBase64) {
|
||||
this.logger.debug(`Key backup on server does not match our decryption key`);
|
||||
this.hasConfigurationProblem = true;
|
||||
return null;
|
||||
}
|
||||
const backupDecryptor = this.backupManager.createBackupDecryptor(backupKeys.decryptionKey);
|
||||
this.hasConfigurationProblem = false;
|
||||
this.configuration = {
|
||||
decryptor: backupDecryptor,
|
||||
backupVersion: activeVersion
|
||||
};
|
||||
return this.configuration;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=PerSessionKeyBackupDownloader.js.map
|
||||
1
node_modules/matrix-js-sdk/lib/rust-crypto/PerSessionKeyBackupDownloader.js.map
generated
vendored
1
node_modules/matrix-js-sdk/lib/rust-crypto/PerSessionKeyBackupDownloader.js.map
generated
vendored
File diff suppressed because one or more lines are too long
79
node_modules/matrix-js-sdk/lib/rust-crypto/RoomEncryptor.d.ts
generated
vendored
79
node_modules/matrix-js-sdk/lib/rust-crypto/RoomEncryptor.d.ts
generated
vendored
@@ -1,41 +1,28 @@
|
||||
import { HistoryVisibility as RustHistoryVisibility, type OlmMachine } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { type MatrixEvent, type IContent } from "../models/event.ts";
|
||||
import { type Room } from "../models/room.ts";
|
||||
import { type Logger } from "../logger.ts";
|
||||
import { type KeyClaimManager } from "./KeyClaimManager.ts";
|
||||
import { type RoomMember } from "../models/room-member.ts";
|
||||
import { HistoryVisibility } from "../@types/partials.ts";
|
||||
import { type OutgoingRequestsManager } from "./OutgoingRequestsManager.ts";
|
||||
import { type DeviceIsolationMode } from "../crypto-api/index.ts";
|
||||
import { OlmMachine } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { IContent, MatrixEvent } from "../models/event";
|
||||
import { Room } from "../models/room";
|
||||
import { KeyClaimManager } from "./KeyClaimManager";
|
||||
import { RoomMember } from "../models/room-member";
|
||||
import { OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
|
||||
/**
|
||||
* RoomEncryptor: responsible for encrypting messages to a given room
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export declare class RoomEncryptor {
|
||||
private readonly prefixedLogger;
|
||||
private readonly olmMachine;
|
||||
private readonly keyClaimManager;
|
||||
private readonly outgoingRequestManager;
|
||||
private readonly outgoingRequestProcessor;
|
||||
private readonly room;
|
||||
private encryptionSettings;
|
||||
/** whether the room members have been loaded and tracked for the first time */
|
||||
private lazyLoadedMembersResolved;
|
||||
private readonly prefixedLogger;
|
||||
/**
|
||||
* Ensures that there is only one encryption operation at a time for that room.
|
||||
*
|
||||
* An encryption operation is either a {@link prepareForEncryption} or an {@link encryptEvent} call.
|
||||
*/
|
||||
private currentEncryptionPromise;
|
||||
/**
|
||||
* @param prefixedLogger - A logger to use for log messages.
|
||||
* @param olmMachine - The rust-sdk's OlmMachine
|
||||
* @param keyClaimManager - Our KeyClaimManager, which manages the queue of one-time-key claim requests
|
||||
* @param outgoingRequestManager - The OutgoingRequestManager, which manages the queue of outgoing requests.
|
||||
* @param room - The room we want to encrypt for
|
||||
* @param encryptionSettings - body of the m.room.encryption event currently in force in this room
|
||||
*/
|
||||
constructor(prefixedLogger: Logger, olmMachine: OlmMachine, keyClaimManager: KeyClaimManager, outgoingRequestManager: OutgoingRequestsManager, room: Room, encryptionSettings: IContent);
|
||||
constructor(olmMachine: OlmMachine, keyClaimManager: KeyClaimManager, outgoingRequestProcessor: OutgoingRequestProcessor, room: Room, encryptionSettings: IContent);
|
||||
/**
|
||||
* Handle a new `m.room.encryption` event in this room
|
||||
*
|
||||
@@ -53,48 +40,20 @@ export declare class RoomEncryptor {
|
||||
*
|
||||
* This ensures that we have a megolm session ready to use and that we have shared its key with all the devices
|
||||
* in the room.
|
||||
* @param globalBlacklistUnverifiedDevices - When `true`, and `deviceIsolationMode` is `AllDevicesIsolationMode`,
|
||||
* will not send encrypted messages to unverified devices.
|
||||
* Ignored when `deviceIsolationMode` is `OnlySignedDevicesIsolationMode`.
|
||||
* @param deviceIsolationMode - The device isolation mode. See {@link DeviceIsolationMode}.
|
||||
*/
|
||||
prepareForEncryption(globalBlacklistUnverifiedDevices: boolean, deviceIsolationMode: DeviceIsolationMode): Promise<void>;
|
||||
/**
|
||||
* Encrypt an event for this room, or prepare for encryption.
|
||||
*
|
||||
* This will ensure that we have a megolm session for this room, share it with the devices in the room, and
|
||||
* then, if an event is provided, encrypt it using the session.
|
||||
*
|
||||
* @param event - Event to be encrypted, or null if only preparing for encryption (in which case we will pre-share the room key).
|
||||
* @param globalBlacklistUnverifiedDevices - When `true`, and `deviceIsolationMode` is `AllDevicesIsolationMode`,
|
||||
* will not send encrypted messages to unverified devices.
|
||||
* Ignored when `deviceIsolationMode` is `OnlySignedDevicesIsolationMode`.
|
||||
* @param deviceIsolationMode - The device isolation mode. See {@link DeviceIsolationMode}.
|
||||
*/
|
||||
encryptEvent(event: MatrixEvent | null, globalBlacklistUnverifiedDevices: boolean, deviceIsolationMode: DeviceIsolationMode): Promise<void>;
|
||||
/**
|
||||
* Prepare to encrypt events in this room.
|
||||
*
|
||||
* This ensures that we have a megolm session ready to use and that we have shared its key with all the devices
|
||||
* in the room.
|
||||
*
|
||||
* @param logger - a place to write diagnostics to
|
||||
* @param globalBlacklistUnverifiedDevices - When `true`, and `deviceIsolationMode` is `AllDevicesIsolationMode`,
|
||||
* will not send encrypted messages to unverified devices.
|
||||
* Ignored when `deviceIsolationMode` is `OnlySignedDevicesIsolationMode`.
|
||||
* @param deviceIsolationMode - The device isolation mode. See {@link DeviceIsolationMode}.
|
||||
*/
|
||||
private ensureEncryptionSession;
|
||||
ensureEncryptionSession(): Promise<void>;
|
||||
/**
|
||||
* Discard any existing group session for this room
|
||||
*/
|
||||
forceDiscardSession(): Promise<void>;
|
||||
private encryptEventInner;
|
||||
/**
|
||||
* Encrypt an event for this room
|
||||
*
|
||||
* This will ensure that we have a megolm session for this room, share it with the devices in the room, and
|
||||
* then encrypt the event using the session.
|
||||
*
|
||||
* @param event - Event to be encrypted.
|
||||
*/
|
||||
encryptEvent(event: MatrixEvent): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* Convert a HistoryVisibility to a RustHistoryVisibility
|
||||
* @param visibility - HistoryVisibility enum
|
||||
* @returns a RustHistoryVisibility enum
|
||||
*/
|
||||
export declare function toRustHistoryVisibility(visibility: HistoryVisibility): RustHistoryVisibility;
|
||||
//# sourceMappingURL=RoomEncryptor.d.ts.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/RoomEncryptor.d.ts.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/RoomEncryptor.d.ts.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"RoomEncryptor.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/RoomEncryptor.ts"],"names":[],"mappings":"AAiBA,OAAO,EAIH,iBAAiB,IAAI,qBAAqB,EAC1C,KAAK,UAAU,EAIlB,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,KAAK,MAAM,EAAW,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAG5E,OAAO,EAAE,KAAK,mBAAmB,EAA2B,MAAM,wBAAwB,CAAC;AAE3F;;;;GAIG;AACH,qBAAa,aAAa;IAoBlB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,kBAAkB;IAxB9B,+EAA+E;IAC/E,OAAO,CAAC,yBAAyB,CAAS;IAE1C;;;;OAIG;IACH,OAAO,CAAC,wBAAwB,CAAoC;IAEpE;;;;;;;OAOG;IACH,YACqB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,UAAU,EACtB,eAAe,EAAE,eAAe,EAChC,sBAAsB,EAAE,uBAAuB,EAC/C,IAAI,EAAE,IAAI,EACnB,kBAAkB,EAAE,QAAQ,EAYvC;IAED;;;;OAIG;IACI,aAAa,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAK3C;IAED;;;;OAIG;IACI,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAYhD;IAED;;;;;;;;;OASG;IACU,oBAAoB,CAC7B,gCAAgC,EAAE,OAAO,EACzC,mBAAmB,EAAE,mBAAmB,GACzC,OAAO,CAAC,IAAI,CAAC,CASf;IAED;;;;;;;;;;;OAWG;IACI,YAAY,CACf,KAAK,EAAE,WAAW,GAAG,IAAI,EACzB,gCAAgC,EAAE,OAAO,EACzC,mBAAmB,EAAE,mBAAmB,GACzC,OAAO,CAAC,IAAI,CAAC,CAsBf;IAED;;;;;;;;;;;OAWG;YACW,uBAAuB;IAiHrC;;OAEG;IACU,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKhD;YAEa,iBAAiB;CA6BlC;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,iBAAiB,GAAG,qBAAqB,CAW5F"}
|
||||
{"version":3,"file":"RoomEncryptor.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/RoomEncryptor.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAsB,UAAU,EAAkB,MAAM,oCAAoC,CAAC;AAGpG,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEtC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAEtE;;;;GAIG;AACH,qBAAa,aAAa;IAUlB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,wBAAwB;IACzC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,kBAAkB;IAb9B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAEhD;;;;;OAKG;gBAEkB,UAAU,EAAE,UAAU,EACtB,eAAe,EAAE,eAAe,EAChC,wBAAwB,EAAE,wBAAwB,EAClD,IAAI,EAAE,IAAI,EACnB,kBAAkB,EAAE,QAAQ;IAKxC;;;;OAIG;IACI,aAAa,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI;IAM5C;;;;OAIG;IACI,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAejD;;;;;OAKG;IACU,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC;IAiCrD;;OAEG;IACU,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAOjD;;;;;;;OAOG;IACU,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAgB/D"}
|
||||
258
node_modules/matrix-js-sdk/lib/rust-crypto/RoomEncryptor.js
generated
vendored
258
node_modules/matrix-js-sdk/lib/rust-crypto/RoomEncryptor.js
generated
vendored
@@ -1,4 +1,14 @@
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
"use strict";
|
||||
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.RoomEncryptor = void 0;
|
||||
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
||||
var _matrixSdkCryptoWasm = require("@matrix-org/matrix-sdk-crypto-wasm");
|
||||
var _event = require("../@types/event");
|
||||
var _logger = require("../logger");
|
||||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
@@ -15,52 +25,26 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { CollectStrategy, EncryptionAlgorithm, EncryptionSettings, HistoryVisibility as RustHistoryVisibility, RoomId, UserId } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { EventType } from "../@types/event.js";
|
||||
import { LogSpan } from "../logger.js";
|
||||
import { HistoryVisibility } from "../@types/partials.js";
|
||||
import { logDuration } from "../utils.js";
|
||||
import { KnownMembership } from "../@types/membership.js";
|
||||
import { DeviceIsolationModeKind } from "../crypto-api/index.js";
|
||||
|
||||
/**
|
||||
* RoomEncryptor: responsible for encrypting messages to a given room
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class RoomEncryptor {
|
||||
class RoomEncryptor {
|
||||
/**
|
||||
* @param prefixedLogger - A logger to use for log messages.
|
||||
* @param olmMachine - The rust-sdk's OlmMachine
|
||||
* @param keyClaimManager - Our KeyClaimManager, which manages the queue of one-time-key claim requests
|
||||
* @param outgoingRequestManager - The OutgoingRequestManager, which manages the queue of outgoing requests.
|
||||
* @param room - The room we want to encrypt for
|
||||
* @param encryptionSettings - body of the m.room.encryption event currently in force in this room
|
||||
*/
|
||||
constructor(prefixedLogger, olmMachine, keyClaimManager, outgoingRequestManager, room, encryptionSettings) {
|
||||
/** whether the room members have been loaded and tracked for the first time */
|
||||
_defineProperty(this, "lazyLoadedMembersResolved", false);
|
||||
/**
|
||||
* Ensures that there is only one encryption operation at a time for that room.
|
||||
*
|
||||
* An encryption operation is either a {@link prepareForEncryption} or an {@link encryptEvent} call.
|
||||
*/
|
||||
_defineProperty(this, "currentEncryptionPromise", Promise.resolve());
|
||||
this.prefixedLogger = prefixedLogger;
|
||||
constructor(olmMachine, keyClaimManager, outgoingRequestProcessor, room, encryptionSettings) {
|
||||
this.olmMachine = olmMachine;
|
||||
this.keyClaimManager = keyClaimManager;
|
||||
this.outgoingRequestManager = outgoingRequestManager;
|
||||
this.outgoingRequestProcessor = outgoingRequestProcessor;
|
||||
this.room = room;
|
||||
this.encryptionSettings = encryptionSettings;
|
||||
// start tracking devices for any users already known to be in this room.
|
||||
// Do not load members here, would defeat lazy loading.
|
||||
const members = room.getJoinedMembers();
|
||||
|
||||
// At this point just mark the known members as tracked, it might not be the full list of members
|
||||
// because of lazy loading. This is fine, because we will get a member list update when sending a message for
|
||||
// the first time, see `RoomEncryptor#ensureEncryptionSession`
|
||||
this.olmMachine.updateTrackedUsers(members.map(u => new RustSdkCryptoJs.UserId(u.userId))).catch(e => this.prefixedLogger.error("Error initializing tracked users", e));
|
||||
(0, _defineProperty2.default)(this, "prefixedLogger", void 0);
|
||||
this.prefixedLogger = _logger.logger.withPrefix(`[${room.roomId} encryption]`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,8 +54,7 @@ export class RoomEncryptor {
|
||||
*/
|
||||
onCryptoEvent(config) {
|
||||
if (JSON.stringify(this.encryptionSettings) != JSON.stringify(config)) {
|
||||
// This should currently be unreachable, since the Rust SDK will reject any attempts to change config.
|
||||
throw new Error("Cannot reconfigure an active RoomEncryptor");
|
||||
this.prefixedLogger.error(`Ignoring m.room.encryption event which requests a change of config`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,11 +64,11 @@ export class RoomEncryptor {
|
||||
* @param member - new membership state
|
||||
*/
|
||||
onRoomMembership(member) {
|
||||
if (member.membership == KnownMembership.Join || member.membership == KnownMembership.Invite && this.room.shouldEncryptForInvitedMembers()) {
|
||||
this.prefixedLogger.debug(`${member.membership} event for ${member.userId}`);
|
||||
if (member.membership == "join" || member.membership == "invite" && this.room.shouldEncryptForInvitedMembers()) {
|
||||
// make sure we are tracking the deviceList for this user
|
||||
this.olmMachine.updateTrackedUsers([new UserId(member.userId)]).catch(e => {
|
||||
this.prefixedLogger.error("Unable to update tracked users", e);
|
||||
});
|
||||
this.prefixedLogger.debug(`starting to track devices for: ${member.userId}`);
|
||||
this.olmMachine.updateTrackedUsers([new _matrixSdkCryptoWasm.UserId(member.userId)]);
|
||||
}
|
||||
|
||||
// TODO: handle leaves (including our own)
|
||||
@@ -96,197 +79,50 @@ export class RoomEncryptor {
|
||||
*
|
||||
* This ensures that we have a megolm session ready to use and that we have shared its key with all the devices
|
||||
* in the room.
|
||||
* @param globalBlacklistUnverifiedDevices - When `true`, and `deviceIsolationMode` is `AllDevicesIsolationMode`,
|
||||
* will not send encrypted messages to unverified devices.
|
||||
* Ignored when `deviceIsolationMode` is `OnlySignedDevicesIsolationMode`.
|
||||
* @param deviceIsolationMode - The device isolation mode. See {@link DeviceIsolationMode}.
|
||||
*/
|
||||
async prepareForEncryption(globalBlacklistUnverifiedDevices, deviceIsolationMode) {
|
||||
// We consider a prepareForEncryption as an encryption promise as it will potentially share keys
|
||||
// even if it doesn't send an event.
|
||||
// Usually this is called when the user starts typing, so we want to make sure we have keys ready when the
|
||||
// message is finally sent.
|
||||
// If `encryptEvent` is invoked before `prepareForEncryption` has completed, the `encryptEvent` call will wait for
|
||||
// `prepareForEncryption` to complete before executing.
|
||||
// The part where `encryptEvent` shares the room key will then usually be a no-op as it was already performed by `prepareForEncryption`.
|
||||
await this.encryptEvent(null, globalBlacklistUnverifiedDevices, deviceIsolationMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt an event for this room, or prepare for encryption.
|
||||
*
|
||||
* This will ensure that we have a megolm session for this room, share it with the devices in the room, and
|
||||
* then, if an event is provided, encrypt it using the session.
|
||||
*
|
||||
* @param event - Event to be encrypted, or null if only preparing for encryption (in which case we will pre-share the room key).
|
||||
* @param globalBlacklistUnverifiedDevices - When `true`, and `deviceIsolationMode` is `AllDevicesIsolationMode`,
|
||||
* will not send encrypted messages to unverified devices.
|
||||
* Ignored when `deviceIsolationMode` is `OnlySignedDevicesIsolationMode`.
|
||||
* @param deviceIsolationMode - The device isolation mode. See {@link DeviceIsolationMode}.
|
||||
*/
|
||||
encryptEvent(event, globalBlacklistUnverifiedDevices, deviceIsolationMode) {
|
||||
const logger = new LogSpan(this.prefixedLogger, event ? event.getTxnId() ?? "" : "prepareForEncryption");
|
||||
// Ensure order of encryption to avoid message ordering issues, as the scheduler only ensures
|
||||
// events order after they have been encrypted.
|
||||
const prom = this.currentEncryptionPromise.catch(() => {
|
||||
// Any errors in the previous call will have been reported already, so there is nothing to do here.
|
||||
// we just throw away the error and start anew.
|
||||
}).then(async () => {
|
||||
await logDuration(logger, "ensureEncryptionSession", async () => {
|
||||
await this.ensureEncryptionSession(logger, globalBlacklistUnverifiedDevices, deviceIsolationMode);
|
||||
});
|
||||
if (event) {
|
||||
await logDuration(logger, "encryptEventInner", async () => {
|
||||
await this.encryptEventInner(logger, event);
|
||||
});
|
||||
}
|
||||
});
|
||||
this.currentEncryptionPromise = prom;
|
||||
return prom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare to encrypt events in this room.
|
||||
*
|
||||
* This ensures that we have a megolm session ready to use and that we have shared its key with all the devices
|
||||
* in the room.
|
||||
*
|
||||
* @param logger - a place to write diagnostics to
|
||||
* @param globalBlacklistUnverifiedDevices - When `true`, and `deviceIsolationMode` is `AllDevicesIsolationMode`,
|
||||
* will not send encrypted messages to unverified devices.
|
||||
* Ignored when `deviceIsolationMode` is `OnlySignedDevicesIsolationMode`.
|
||||
* @param deviceIsolationMode - The device isolation mode. See {@link DeviceIsolationMode}.
|
||||
*/
|
||||
async ensureEncryptionSession(logger, globalBlacklistUnverifiedDevices, deviceIsolationMode) {
|
||||
async ensureEncryptionSession() {
|
||||
if (this.encryptionSettings.algorithm !== "m.megolm.v1.aes-sha2") {
|
||||
throw new Error(`Cannot encrypt in ${this.room.roomId} for unsupported algorithm '${this.encryptionSettings.algorithm}'`);
|
||||
}
|
||||
logger.debug("Starting encryption");
|
||||
const members = await this.room.getEncryptionTargetMembers();
|
||||
this.prefixedLogger.debug(`Encrypting for users (shouldEncryptForInvitedMembers: ${this.room.shouldEncryptForInvitedMembers()}):`, members.map(u => `${u.userId} (${u.membership})`));
|
||||
const userList = members.map(u => new _matrixSdkCryptoWasm.UserId(u.userId));
|
||||
await this.keyClaimManager.ensureSessionsForUsers(userList);
|
||||
this.prefixedLogger.debug("Sessions for users are ready; now sharing room key");
|
||||
const rustEncryptionSettings = new _matrixSdkCryptoWasm.EncryptionSettings();
|
||||
/* FIXME historyVisibility, rotation, etc */
|
||||
|
||||
// If this is the first time we are sending a message to the room, we may not yet have seen all the members
|
||||
// (so the Crypto SDK might not have a device list for them). So, if this is the first time we are encrypting
|
||||
// for this room, give the SDK the full list of members, to be on the safe side.
|
||||
//
|
||||
// This could end up being racy (if two calls to ensureEncryptionSession happen at the same time), but that's
|
||||
// not a particular problem, since `OlmMachine.updateTrackedUsers` just adds any users that weren't already tracked.
|
||||
if (!this.lazyLoadedMembersResolved) {
|
||||
await logDuration(logger, "loadMembersIfNeeded: updateTrackedUsers", async () => {
|
||||
await this.olmMachine.updateTrackedUsers(members.map(u => new RustSdkCryptoJs.UserId(u.userId)));
|
||||
});
|
||||
logger.debug(`Updated tracked users`);
|
||||
this.lazyLoadedMembersResolved = true;
|
||||
|
||||
// Query keys in case we don't have them for newly tracked members.
|
||||
// It's important after loading members for the first time, as likely most of them won't be
|
||||
// known yet and will be unable to decrypt messages despite being in the room for long.
|
||||
// This must be done before ensuring sessions. If not the devices of these users are not
|
||||
// known yet and will not get the room key.
|
||||
// We don't have API to only get the keys queries related to this member list, so we just
|
||||
// process the pending requests from the olmMachine. (usually these are processed
|
||||
// at the end of the sync, but we can't wait for that).
|
||||
// XXX future improvement process only KeysQueryRequests for the users that have never been queried.
|
||||
logger.debug(`Processing outgoing requests`);
|
||||
await logDuration(logger, "doProcessOutgoingRequests", async () => {
|
||||
await this.outgoingRequestManager.doProcessOutgoingRequests();
|
||||
});
|
||||
} else {
|
||||
// If members are already loaded it's less critical to await on key queries.
|
||||
// We might still want to trigger a processOutgoingRequests here.
|
||||
// The call to `ensureSessionsForUsers` below will wait a bit on in-flight key queries we are
|
||||
// interested in. If a sync handling happens in the meantime, and some new members are added to the room
|
||||
// or have new devices it would give us a chance to query them before sending.
|
||||
// It's less critical due to the racy nature of this process.
|
||||
logger.debug(`Processing outgoing requests in background`);
|
||||
this.outgoingRequestManager.doProcessOutgoingRequests();
|
||||
}
|
||||
logger.debug(`Encrypting for users (shouldEncryptForInvitedMembers: ${this.room.shouldEncryptForInvitedMembers()}):`, members.map(u => `${u.userId} (${u.membership})`));
|
||||
const userList = members.map(u => new UserId(u.userId));
|
||||
await logDuration(logger, "ensureSessionsForUsers", async () => {
|
||||
await this.keyClaimManager.ensureSessionsForUsers(logger, userList);
|
||||
});
|
||||
const rustEncryptionSettings = new EncryptionSettings();
|
||||
rustEncryptionSettings.historyVisibility = toRustHistoryVisibility(this.room.getHistoryVisibility());
|
||||
|
||||
// We only support megolm
|
||||
rustEncryptionSettings.algorithm = EncryptionAlgorithm.MegolmV1AesSha2;
|
||||
|
||||
// We need to convert the rotation period from milliseconds to microseconds
|
||||
// See https://spec.matrix.org/v1.8/client-server-api/#mroomencryption and
|
||||
// https://matrix-org.github.io/matrix-rust-sdk-crypto-wasm/classes/EncryptionSettings.html#rotationPeriod
|
||||
if (typeof this.encryptionSettings.rotation_period_ms === "number") {
|
||||
rustEncryptionSettings.rotationPeriod = BigInt(this.encryptionSettings.rotation_period_ms * 1000);
|
||||
}
|
||||
if (typeof this.encryptionSettings.rotation_period_msgs === "number") {
|
||||
rustEncryptionSettings.rotationPeriodMessages = BigInt(this.encryptionSettings.rotation_period_msgs);
|
||||
}
|
||||
switch (deviceIsolationMode.kind) {
|
||||
case DeviceIsolationModeKind.AllDevicesIsolationMode:
|
||||
{
|
||||
// When this.room.getBlacklistUnverifiedDevices() === null, the global settings should be used
|
||||
// See Room#getBlacklistUnverifiedDevices
|
||||
const onlyAllowTrustedDevices = this.room.getBlacklistUnverifiedDevices() ?? globalBlacklistUnverifiedDevices;
|
||||
rustEncryptionSettings.sharingStrategy = CollectStrategy.deviceBasedStrategy(onlyAllowTrustedDevices, deviceIsolationMode.errorOnVerifiedUserProblems);
|
||||
}
|
||||
break;
|
||||
case DeviceIsolationModeKind.OnlySignedDevicesIsolationMode:
|
||||
rustEncryptionSettings.sharingStrategy = CollectStrategy.identityBasedStrategy();
|
||||
break;
|
||||
}
|
||||
await logDuration(logger, "shareRoomKey", async () => {
|
||||
const shareMessages = await this.olmMachine.shareRoomKey(new RoomId(this.room.roomId),
|
||||
// safe to pass without cloning, as it's not reused here (before or after)
|
||||
userList, rustEncryptionSettings);
|
||||
if (shareMessages) {
|
||||
for (const m of shareMessages) {
|
||||
await this.outgoingRequestManager.outgoingRequestProcessor.makeOutgoingRequest(m);
|
||||
}
|
||||
const shareMessages = await this.olmMachine.shareRoomKey(new _matrixSdkCryptoWasm.RoomId(this.room.roomId), userList, rustEncryptionSettings);
|
||||
if (shareMessages) {
|
||||
for (const m of shareMessages) {
|
||||
await this.outgoingRequestProcessor.makeOutgoingRequest(m);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Discard any existing group session for this room
|
||||
*/
|
||||
async forceDiscardSession() {
|
||||
const r = await this.olmMachine.invalidateGroupSession(new RoomId(this.room.roomId));
|
||||
const r = await this.olmMachine.invalidateGroupSession(new _matrixSdkCryptoWasm.RoomId(this.room.roomId));
|
||||
if (r) {
|
||||
this.prefixedLogger.info("Discarded existing group session");
|
||||
}
|
||||
}
|
||||
async encryptEventInner(logger, event) {
|
||||
logger.debug("Encrypting actual message content");
|
||||
const room = new RoomId(this.room.roomId);
|
||||
const type = event.getType();
|
||||
const content = JSON.stringify(event.getContent());
|
||||
let encryptedContent;
|
||||
if (event.isState()) {
|
||||
encryptedContent = await this.olmMachine.encryptStateEvent(room, type,
|
||||
// Safety: we've already checked above that this is a state event, so the state key must exist.
|
||||
event.getStateKey(), content);
|
||||
} else {
|
||||
encryptedContent = await this.olmMachine.encryptRoomEvent(room, type, content);
|
||||
}
|
||||
event.makeEncrypted(EventType.RoomMessageEncrypted, JSON.parse(encryptedContent), this.olmMachine.identityKeys.curve25519.toBase64(), this.olmMachine.identityKeys.ed25519.toBase64());
|
||||
logger.debug("Encrypted event successfully");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a HistoryVisibility to a RustHistoryVisibility
|
||||
* @param visibility - HistoryVisibility enum
|
||||
* @returns a RustHistoryVisibility enum
|
||||
*/
|
||||
export function toRustHistoryVisibility(visibility) {
|
||||
switch (visibility) {
|
||||
case HistoryVisibility.Invited:
|
||||
return RustHistoryVisibility.Invited;
|
||||
case HistoryVisibility.Joined:
|
||||
return RustHistoryVisibility.Joined;
|
||||
case HistoryVisibility.Shared:
|
||||
return RustHistoryVisibility.Shared;
|
||||
case HistoryVisibility.WorldReadable:
|
||||
return RustHistoryVisibility.WorldReadable;
|
||||
/**
|
||||
* Encrypt an event for this room
|
||||
*
|
||||
* This will ensure that we have a megolm session for this room, share it with the devices in the room, and
|
||||
* then encrypt the event using the session.
|
||||
*
|
||||
* @param event - Event to be encrypted.
|
||||
*/
|
||||
async encryptEvent(event) {
|
||||
await this.ensureEncryptionSession();
|
||||
const encryptedContent = await this.olmMachine.encryptRoomEvent(new _matrixSdkCryptoWasm.RoomId(this.room.roomId), event.getType(), JSON.stringify(event.getContent()));
|
||||
event.makeEncrypted(_event.EventType.RoomMessageEncrypted, JSON.parse(encryptedContent), this.olmMachine.identityKeys.curve25519.toBase64(), this.olmMachine.identityKeys.ed25519.toBase64());
|
||||
}
|
||||
}
|
||||
exports.RoomEncryptor = RoomEncryptor;
|
||||
//# sourceMappingURL=RoomEncryptor.js.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/RoomEncryptor.js.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/RoomEncryptor.js.map
generated
vendored
File diff suppressed because one or more lines are too long
306
node_modules/matrix-js-sdk/lib/rust-crypto/backup.d.ts
generated
vendored
306
node_modules/matrix-js-sdk/lib/rust-crypto/backup.d.ts
generated
vendored
@@ -1,306 +0,0 @@
|
||||
import { type OlmMachine } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { type BackupTrustInfo, type KeyBackupCheck, type KeyBackupInfo, type KeyBackupSession, type KeyBackupRestoreOpts, type KeyBackupRestoreResult, type KeyBackupRoomSessions } from "../crypto-api/keybackup.ts";
|
||||
import { type Logger } from "../logger.ts";
|
||||
import { type IHttpOpts, type MatrixHttpApi } from "../http-api/index.ts";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
|
||||
import { type OutgoingRequestProcessor } from "./OutgoingRequestProcessor.ts";
|
||||
import { type BackupDecryptor } from "../common-crypto/CryptoBackend.ts";
|
||||
import { type ImportRoomKeysOpts, CryptoEvent } from "../crypto-api/index.ts";
|
||||
import { type IMegolmSessionData } from "../@types/crypto.ts";
|
||||
/** Authentification of the backup info, depends on algorithm */
|
||||
type AuthData = KeyBackupInfo["auth_data"];
|
||||
/**
|
||||
* Holds information of a created keybackup.
|
||||
* Useful to get the generated private key material and save it securely somewhere.
|
||||
*/
|
||||
interface KeyBackupCreationInfo {
|
||||
version: string;
|
||||
algorithm: string;
|
||||
authData: AuthData;
|
||||
decryptionKey: RustSdkCryptoJs.BackupDecryptionKey;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export declare class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents, RustBackupCryptoEventMap> {
|
||||
private readonly olmMachine;
|
||||
private readonly http;
|
||||
private readonly outgoingRequestProcessor;
|
||||
/** Have we checked if there is a backup on the server which we can use */
|
||||
private checkedForBackup;
|
||||
/**
|
||||
* The latest backup version on the server, when we last checked.
|
||||
*
|
||||
* If there was no backup on the server, `null`. If our attempt to check resulted in an error, `undefined`.
|
||||
*
|
||||
* Note that the backup was not necessarily verified.
|
||||
*/
|
||||
private serverBackupInfo;
|
||||
private activeBackupVersion;
|
||||
private stopped;
|
||||
/** whether {@link backupKeysLoop} is currently running */
|
||||
private backupKeysLoopRunning;
|
||||
/** The logger to use */
|
||||
private readonly logger;
|
||||
constructor(logger: Logger, olmMachine: OlmMachine, http: MatrixHttpApi<IHttpOpts & {
|
||||
onlyData: true;
|
||||
}>, outgoingRequestProcessor: OutgoingRequestProcessor);
|
||||
/**
|
||||
* Tells the RustBackupManager to stop.
|
||||
* The RustBackupManager is scheduling background uploads of keys to the backup, this
|
||||
* call allows to cancel the process when the client is stoppped.
|
||||
*/
|
||||
stop(): void;
|
||||
/**
|
||||
* Get the backup version we are currently backing up to, if any
|
||||
*/
|
||||
getActiveBackupVersion(): Promise<string | null>;
|
||||
/**
|
||||
* Return the details of the latest backup on the server, when we last checked.
|
||||
*
|
||||
* This normally returns a cached value, but if we haven't yet made a request to the server, it will fire one off.
|
||||
* It will always return the details of the active backup if key backup is enabled.
|
||||
*
|
||||
* If there was no backup on the server, `null`. If our attempt to check resulted in an error, `undefined`.
|
||||
*/
|
||||
getServerBackupInfo(): Promise<KeyBackupInfo | null | undefined>;
|
||||
/**
|
||||
* Determine if a key backup can be trusted.
|
||||
*
|
||||
* @param info - key backup info dict from {@link CryptoApi.getKeyBackupInfo}.
|
||||
*/
|
||||
isKeyBackupTrusted(info: KeyBackupInfo): Promise<BackupTrustInfo>;
|
||||
/**
|
||||
* Re-check the key backup and enable/disable it as appropriate.
|
||||
*
|
||||
* @param force - whether we should force a re-check even if one has already happened. If this is
|
||||
* `false`, and we have already done a check, `null` is returned rather than the actual info on the key backup.
|
||||
*/
|
||||
checkKeyBackupAndEnable(force: boolean): Promise<KeyBackupCheck | null>;
|
||||
/**
|
||||
* Handles a backup secret received event and store it if it matches the current backup version.
|
||||
*
|
||||
* Also enables key backup upload if it was not previously enabled, and the encryption key matches the received
|
||||
* decryption key.
|
||||
*
|
||||
* @param secret - The secret as received from a `m.secret.send` or `io.element.msc4385.secret.push` event for secret `m.megolm_backup.v1`.
|
||||
* @returns true if the secret is valid and has been stored, false otherwise.
|
||||
*/
|
||||
handleBackupSecretReceived(secret: string): Promise<boolean>;
|
||||
saveBackupDecryptionKey(backupDecryptionKey: RustSdkCryptoJs.BackupDecryptionKey, version: string): Promise<void>;
|
||||
/**
|
||||
* Import a list of room keys previously exported by exportRoomKeys
|
||||
*
|
||||
* @param keys - a list of session export objects
|
||||
* @param opts - options object
|
||||
* @returns a promise which resolves once the keys have been imported
|
||||
*/
|
||||
importRoomKeys(keys: IMegolmSessionData[], opts?: ImportRoomKeysOpts): Promise<void>;
|
||||
/**
|
||||
* Import a list of room keys previously exported by exportRoomKeysAsJson
|
||||
*
|
||||
* @param jsonKeys - a JSON string encoding a list of session export objects,
|
||||
* each of which is an IMegolmSessionData
|
||||
* @param opts - options object
|
||||
* @returns a promise which resolves once the keys have been imported
|
||||
*/
|
||||
importRoomKeysAsJson(jsonKeys: string, opts?: ImportRoomKeysOpts): Promise<void>;
|
||||
/**
|
||||
* Implementation of {@link CryptoBackend#importBackedUpRoomKeys}.
|
||||
*/
|
||||
importBackedUpRoomKeys(keys: IMegolmSessionData[], backupVersion: string, opts?: ImportRoomKeysOpts): Promise<void>;
|
||||
private keyBackupCheckInProgress;
|
||||
/** Helper to check the key backup status, and enable/disable it as appropriate
|
||||
*
|
||||
* A KeyBackupInfo can be passed if it was fetched recently, to avoid trying to
|
||||
* re-fetch it from the server.
|
||||
*/
|
||||
private doCheckKeyBackup;
|
||||
/**
|
||||
* Enable key backup upload for the given backup version, if it is not already.
|
||||
*
|
||||
* If backup is currently enabled for a different version, disables it first.
|
||||
*
|
||||
* Also emits one or more {@link CryptoEvent.KeyBackupStatus} events if the backup status changes.
|
||||
*
|
||||
* @param backupInfo - the desired backup version (and the encryption key).
|
||||
* @param activeVersion - the current active backup version (or `null`, if none).
|
||||
*/
|
||||
private enableOrSwitchKeyBackup;
|
||||
/**
|
||||
* Helper for {@link enableOrSwitchKeyBackup}.
|
||||
*
|
||||
* Enables key backup upload for the given backup version. Also emits
|
||||
* a {@link CryptoEvent.KeyBackupStatus} event.
|
||||
*/
|
||||
private enableKeyBackup;
|
||||
/**
|
||||
* Restart the backup key loop if there is an active trusted backup.
|
||||
* Doesn't try to check the backup server side. To be called when a new
|
||||
* megolm key is known locally.
|
||||
*/
|
||||
maybeUploadKey(): Promise<void>;
|
||||
private disableKeyBackup;
|
||||
private backupKeysLoop;
|
||||
/**
|
||||
* Utility method to count the number of keys in a backup request, in order to update the remaining keys count.
|
||||
* This should be the chunk size of the backup request for all requests but the last, but we don't have access to it
|
||||
* (it's static in the Rust SDK).
|
||||
* @param batch - The backup request to count the keys from.
|
||||
*
|
||||
* @returns The number of keys in the backup request.
|
||||
*/
|
||||
private keysCountInBatch;
|
||||
/**
|
||||
* Get information about a key backup from the server
|
||||
* - If version is provided, get information about that backup version.
|
||||
* - If no version is provided, get information about the latest backup.
|
||||
*
|
||||
* @param version - The version of the backup to get information about.
|
||||
* @returns Information object from API or null if there is no active backup.
|
||||
*/
|
||||
requestKeyBackupVersion(version?: string): Promise<KeyBackupInfo | null>;
|
||||
/**
|
||||
* Creates a new key backup by generating a new random private key, and then enable key backup upload and download
|
||||
* using the new backup version.
|
||||
*
|
||||
* If there is an existing backup server side it will be deleted and replaced
|
||||
* by the new one.
|
||||
*
|
||||
* Saves the decryption key in the Rust SDK's CryptoStore.
|
||||
*
|
||||
* @param signObject - Method that should sign the backup with existing device and
|
||||
* existing identity.
|
||||
* @returns a KeyBackupCreationInfo - All information related to the backup.
|
||||
*/
|
||||
setupKeyBackup(signObject: (authData: AuthData) => Promise<void>): Promise<KeyBackupCreationInfo>;
|
||||
/**
|
||||
* Deletes all key backups.
|
||||
*
|
||||
* Will call the API to delete active backup until there is no more present.
|
||||
*/
|
||||
deleteAllKeyBackupVersions(): Promise<void>;
|
||||
/**
|
||||
* Deletes the given key backup.
|
||||
*
|
||||
* @param version - The backup version to delete.
|
||||
*/
|
||||
deleteKeyBackupVersion(version: string): Promise<void>;
|
||||
/**
|
||||
* Creates a new backup decryptor for the given private key.
|
||||
* @param decryptionKey - The private key to use for decryption.
|
||||
*/
|
||||
createBackupDecryptor(decryptionKey: RustSdkCryptoJs.BackupDecryptionKey): BackupDecryptor;
|
||||
/**
|
||||
* Restore a key backup.
|
||||
*
|
||||
* @param backupVersion - The version of the backup to restore.
|
||||
* @param backupDecryptor - The backup decryptor to use to decrypt the keys.
|
||||
* @param opts - Options for the restore.
|
||||
* @returns The total number of keys and the total imported.
|
||||
*/
|
||||
restoreKeyBackup(backupVersion: string, backupDecryptor: BackupDecryptor, opts?: KeyBackupRestoreOpts): Promise<KeyBackupRestoreResult>;
|
||||
/**
|
||||
* Download and import the keys for a given room from the current backup version.
|
||||
*
|
||||
* @param roomId - The room in question.
|
||||
*/
|
||||
downloadLatestRoomKeyBackup(roomId: string): Promise<void>;
|
||||
/**
|
||||
* Call `/room_keys/keys` to download the key backup (room keys) for the given backup version.
|
||||
* https://spec.matrix.org/v1.12/client-server-api/#get_matrixclientv3room_keyskeys
|
||||
*
|
||||
* @param backupVersion
|
||||
* @returns The key backup response.
|
||||
*/
|
||||
private downloadKeyBackup;
|
||||
/**
|
||||
* Call `/room/keys/keys/{roomId}` to download the key backup (room keys) for a given backup version and room ID.
|
||||
* @param backupVersion - The version to download.
|
||||
* @param roomId - The ID of the room.
|
||||
* @returns The key backup response.
|
||||
*/
|
||||
private downloadRoomKeyBackup;
|
||||
/**
|
||||
* Import the room keys from a `/room_keys/keys` call.
|
||||
* Calls `opts.progressCallback` with the progress of the import.
|
||||
*
|
||||
* @param keyBackup - The response from the server containing the keys to import.
|
||||
* @param backupVersion - The version of the backup info.
|
||||
* @param backupDecryptor - The backup decryptor to use to decrypt the keys.
|
||||
* @param opts - Options for the import.
|
||||
*
|
||||
* @returns The total number of keys and the total imported.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private importKeyBackup;
|
||||
/**
|
||||
* Checks if the provided backup info matches the given private key.
|
||||
*
|
||||
* @param info - The backup info to check.
|
||||
* @param backupDecryptionKey - The `BackupDecryptionKey` private key to check against.
|
||||
* @returns `true` if the private key can decrypt the backup, `false` otherwise.
|
||||
*/
|
||||
private backupInfoMatchesBackupDecryptionKey;
|
||||
}
|
||||
/**
|
||||
* Implementation of {@link BackupDecryptor} for the rust crypto backend.
|
||||
*/
|
||||
export declare class RustBackupDecryptor implements BackupDecryptor {
|
||||
private readonly logger;
|
||||
private decryptionKey;
|
||||
sourceTrusted: boolean;
|
||||
constructor(logger: Logger, decryptionKey: RustSdkCryptoJs.BackupDecryptionKey);
|
||||
/**
|
||||
* Implements {@link BackupDecryptor#decryptSessions}
|
||||
*/
|
||||
decryptSessions(ciphertexts: Record<string, KeyBackupSession>): Promise<IMegolmSessionData[]>;
|
||||
/**
|
||||
* Implements {@link BackupDecryptor#free}
|
||||
*/
|
||||
free(): void;
|
||||
}
|
||||
/**
|
||||
* Fetch a key backup info from the server.
|
||||
*
|
||||
* If `version` is provided, calls `GET /room_keys/version/$version` and gets the backup info for that version.
|
||||
* See https://spec.matrix.org/v1.12/client-server-api/#get_matrixclientv3room_keysversionversion.
|
||||
*
|
||||
* If not, calls `GET /room_keys/version` and gets the latest backup info.
|
||||
* See https://spec.matrix.org/v1.12/client-server-api/#get_matrixclientv3room_keysversion
|
||||
*
|
||||
* @param http
|
||||
* @param version - the specific version of the backup info to fetch
|
||||
* @returns The key backup info or null if there is no backup.
|
||||
*/
|
||||
export declare function requestKeyBackupVersion(http: MatrixHttpApi<IHttpOpts & {
|
||||
onlyData: true;
|
||||
}>, version?: string): Promise<KeyBackupInfo | null>;
|
||||
/**
|
||||
* Checks if the provided decryption key matches the public key of the key backup info.
|
||||
*
|
||||
* @param decryptionKey - The decryption key to check.
|
||||
* @param keyBackupInfo - The key backup info to check against.
|
||||
* @returns `true` if the decryption key matches the key backup info, `false` otherwise.
|
||||
*/
|
||||
export declare function decryptionKeyMatchesKeyBackupInfo(decryptionKey: RustSdkCryptoJs.BackupDecryptionKey, keyBackupInfo: KeyBackupInfo): boolean;
|
||||
export type RustBackupCryptoEvents = CryptoEvent.KeyBackupStatus | CryptoEvent.KeyBackupSessionsRemaining | CryptoEvent.KeyBackupFailed | CryptoEvent.KeyBackupDecryptionKeyCached;
|
||||
export type RustBackupCryptoEventMap = {
|
||||
[CryptoEvent.KeyBackupStatus]: (enabled: boolean) => void;
|
||||
[CryptoEvent.KeyBackupSessionsRemaining]: (remaining: number) => void;
|
||||
[CryptoEvent.KeyBackupFailed]: (errCode: string) => void;
|
||||
[CryptoEvent.KeyBackupDecryptionKeyCached]: (version: string) => void;
|
||||
};
|
||||
/**
|
||||
* Response from GET `/room_keys/keys` endpoint.
|
||||
* See https://spec.matrix.org/latest/client-server-api/#get_matrixclientv3room_keyskeys
|
||||
*/
|
||||
export interface KeyBackup {
|
||||
rooms: Record<string, {
|
||||
sessions: KeyBackupRoomSessions;
|
||||
}>;
|
||||
}
|
||||
export {};
|
||||
//# sourceMappingURL=backup.d.ts.map
|
||||
1
node_modules/matrix-js-sdk/lib/rust-crypto/backup.d.ts.map
generated
vendored
1
node_modules/matrix-js-sdk/lib/rust-crypto/backup.d.ts.map
generated
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/backup.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,UAAU,EAA8B,MAAM,oCAAoC,CAAC;AACjG,OAAO,KAAK,eAAe,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EACH,KAAK,eAAe,EAEpB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAE7B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAgB,KAAK,SAAS,EAAe,KAAK,aAAa,EAAU,MAAM,sBAAsB,CAAC;AAC7G,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAErE,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAE9E,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAEH,KAAK,kBAAkB,EACvB,WAAW,EAEd,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,gEAAgE;AAChE,KAAK,QAAQ,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;AAE3C;;;GAGG;AACH,UAAU,qBAAqB;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,eAAe,CAAC,mBAAmB,CAAC;CACtD;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,iBAAiB,CAAC,sBAAsB,EAAE,wBAAwB,CAAC;IAwBlG,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,wBAAwB;IAzB7C,0EAA0E;IAC1E,OAAO,CAAC,gBAAgB,CAAS;IAEjC;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB,CAA+C;IAEvE,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,OAAO,CAAS;IAExB,0DAA0D;IAC1D,OAAO,CAAC,qBAAqB,CAAS;IAEtC,wBAAwB;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC,YACI,MAAM,EAAE,MAAM,EACG,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,aAAa,CAAC,SAAS,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC,EACnD,wBAAwB,EAAE,wBAAwB,EAItE;IAED;;;;OAIG;IACI,IAAI,IAAI,IAAI,CAElB;IAED;;OAEG;IACU,sBAAsB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAG5D;IAED;;;;;;;OAOG;IACU,mBAAmB,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,GAAG,SAAS,CAAC,CAK5E;IAED;;;;OAIG;IACU,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,CAAC,CAW7E;IAED;;;;;OAKG;IACI,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAY7E;IAED;;;;;;;;OAQG;IACU,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA4DxE;IAEY,uBAAuB,CAChC,mBAAmB,EAAE,eAAe,CAAC,mBAAmB,EACxD,OAAO,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAKf;IAED;;;;;;OAMG;IACU,cAAc,CAAC,IAAI,EAAE,kBAAkB,EAAE,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhG;IAED;;;;;;;OAOG;IACU,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAU5F;IAED;;OAEG;IACU,sBAAsB,CAC/B,IAAI,EAAE,kBAAkB,EAAE,EAC1B,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAsBf;IAED,OAAO,CAAC,wBAAwB,CAA+C;IAE/E;;;;OAIG;YACW,gBAAgB;IA2C9B;;;;;;;;;OASG;YACW,uBAAuB;IAgBrC;;;;;OAKG;YACW,eAAe;IAc7B;;;;OAIG;IACU,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAI3C;YAEa,gBAAgB;YAMhB,cAAc;IA+H5B;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IAKxB;;;;;;;OAOG;IACU,uBAAuB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAEpF;IAED;;;;;;;;;;;;OAYG;IACU,cAAc,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAsD7G;IAED;;;;OAIG;IACU,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC,CASvD;IAED;;;;OAIG;IACU,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWlE;IAED;;;OAGG;IACI,qBAAqB,CAAC,aAAa,EAAE,eAAe,CAAC,mBAAmB,GAAG,eAAe,CAEhG;IAED;;;;;;;OAOG;IACU,gBAAgB,CACzB,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,eAAe,EAChC,IAAI,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,sBAAsB,CAAC,CAIjC;IAED;;;;OAIG;IACU,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWtE;IAED;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAYzB;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IAS7B;;;;;;;;;;;;OAYG;YACW,eAAe;IAgG7B;;;;;;OAMG;IACH,OAAO,CAAC,oCAAoC;CAa/C;AACD;;GAEG;AACH,qBAAa,mBAAoB,YAAW,eAAe;IAKnD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJ3B,OAAO,CAAC,aAAa,CAAsC;IACpD,aAAa,EAAE,OAAO,CAAC;IAE9B,YACqB,MAAM,EAAE,MAAM,EAC/B,aAAa,EAAE,eAAe,CAAC,mBAAmB,EAIrD;IAED;;OAEG;IACU,eAAe,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAkBzG;IAED;;OAEG;IACI,IAAI,IAAI,IAAI,CAElB;CACJ;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,uBAAuB,CACzC,IAAI,EAAE,aAAa,CAAC,SAAS,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,CAAC,EACnD,OAAO,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAa/B;AAED;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAC7C,aAAa,EAAE,eAAe,CAAC,mBAAmB,EAClD,aAAa,EAAE,aAAa,GAC7B,OAAO,CAGT;AAeD,MAAM,MAAM,sBAAsB,GAC5B,WAAW,CAAC,eAAe,GAC3B,WAAW,CAAC,0BAA0B,GACtC,WAAW,CAAC,eAAe,GAC3B,WAAW,CAAC,4BAA4B,CAAC;AAE/C,MAAM,MAAM,wBAAwB,GAAG;IACnC,CAAC,WAAW,CAAC,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1D,CAAC,WAAW,CAAC,0BAA0B,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACtE,CAAC,WAAW,CAAC,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,CAAC,WAAW,CAAC,4BAA4B,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACzE,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,SAAS;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,qBAAqB,CAAA;KAAE,CAAC,CAAC;CAC9D"}
|
||||
880
node_modules/matrix-js-sdk/lib/rust-crypto/backup.js
generated
vendored
880
node_modules/matrix-js-sdk/lib/rust-crypto/backup.js
generated
vendored
@@ -1,880 +0,0 @@
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
/*
|
||||
Copyright 2023 - 2024 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-wasm";
|
||||
import { ClientPrefix, MatrixError, Method } from "../http-api/index.js";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter.js";
|
||||
import { encodeUri, logDuration } from "../utils.js";
|
||||
import { sleep } from "../utils.js";
|
||||
import { CryptoEvent, ImportRoomKeyStage } from "../crypto-api/index.js";
|
||||
|
||||
/** Authentification of the backup info, depends on algorithm */
|
||||
|
||||
/**
|
||||
* Holds information of a created keybackup.
|
||||
* Useful to get the generated private key material and save it securely somewhere.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class RustBackupManager extends TypedEventEmitter {
|
||||
constructor(logger, olmMachine, http, outgoingRequestProcessor) {
|
||||
super();
|
||||
/** Have we checked if there is a backup on the server which we can use */
|
||||
_defineProperty(this, "checkedForBackup", false);
|
||||
/**
|
||||
* The latest backup version on the server, when we last checked.
|
||||
*
|
||||
* If there was no backup on the server, `null`. If our attempt to check resulted in an error, `undefined`.
|
||||
*
|
||||
* Note that the backup was not necessarily verified.
|
||||
*/
|
||||
_defineProperty(this, "serverBackupInfo", undefined);
|
||||
_defineProperty(this, "activeBackupVersion", null);
|
||||
_defineProperty(this, "stopped", false);
|
||||
/** whether {@link backupKeysLoop} is currently running */
|
||||
_defineProperty(this, "backupKeysLoopRunning", false);
|
||||
/** The logger to use */
|
||||
_defineProperty(this, "logger", void 0);
|
||||
_defineProperty(this, "keyBackupCheckInProgress", null);
|
||||
this.olmMachine = olmMachine;
|
||||
this.http = http;
|
||||
this.outgoingRequestProcessor = outgoingRequestProcessor;
|
||||
this.logger = logger.getChild("[RustBackupManager]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the RustBackupManager to stop.
|
||||
* The RustBackupManager is scheduling background uploads of keys to the backup, this
|
||||
* call allows to cancel the process when the client is stoppped.
|
||||
*/
|
||||
stop() {
|
||||
this.stopped = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backup version we are currently backing up to, if any
|
||||
*/
|
||||
async getActiveBackupVersion() {
|
||||
if (!(await this.olmMachine.isBackupEnabled())) return null;
|
||||
return this.activeBackupVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the details of the latest backup on the server, when we last checked.
|
||||
*
|
||||
* This normally returns a cached value, but if we haven't yet made a request to the server, it will fire one off.
|
||||
* It will always return the details of the active backup if key backup is enabled.
|
||||
*
|
||||
* If there was no backup on the server, `null`. If our attempt to check resulted in an error, `undefined`.
|
||||
*/
|
||||
async getServerBackupInfo() {
|
||||
// Do a validity check if we haven't already done one. The check is likely to fail if we don't yet have the
|
||||
// backup keys -- but as a side-effect, it will populate `serverBackupInfo`.
|
||||
await this.checkKeyBackupAndEnable(false);
|
||||
return this.serverBackupInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a key backup can be trusted.
|
||||
*
|
||||
* @param info - key backup info dict from {@link CryptoApi.getKeyBackupInfo}.
|
||||
*/
|
||||
async isKeyBackupTrusted(info) {
|
||||
const signatureVerification = await this.olmMachine.verifyBackup(info);
|
||||
const backupKeys = await this.olmMachine.getBackupKeys();
|
||||
const decryptionKey = backupKeys?.decryptionKey;
|
||||
const backupMatchesSavedPrivateKey = !!decryptionKey && this.backupInfoMatchesBackupDecryptionKey(info, decryptionKey);
|
||||
return {
|
||||
matchesDecryptionKey: backupMatchesSavedPrivateKey,
|
||||
trusted: signatureVerification.trusted()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-check the key backup and enable/disable it as appropriate.
|
||||
*
|
||||
* @param force - whether we should force a re-check even if one has already happened. If this is
|
||||
* `false`, and we have already done a check, `null` is returned rather than the actual info on the key backup.
|
||||
*/
|
||||
checkKeyBackupAndEnable(force) {
|
||||
if (!force && this.checkedForBackup) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
// make sure there is only one check going on at a time
|
||||
if (!this.keyBackupCheckInProgress) {
|
||||
this.keyBackupCheckInProgress = this.doCheckKeyBackup().finally(() => {
|
||||
this.keyBackupCheckInProgress = null;
|
||||
});
|
||||
}
|
||||
return this.keyBackupCheckInProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a backup secret received event and store it if it matches the current backup version.
|
||||
*
|
||||
* Also enables key backup upload if it was not previously enabled, and the encryption key matches the received
|
||||
* decryption key.
|
||||
*
|
||||
* @param secret - The secret as received from a `m.secret.send` or `io.element.msc4385.secret.push` event for secret `m.megolm_backup.v1`.
|
||||
* @returns true if the secret is valid and has been stored, false otherwise.
|
||||
*/
|
||||
async handleBackupSecretReceived(secret) {
|
||||
// Currently we only receive the decryption key without any key backup version. It is important to
|
||||
// check that the secret is valid for the current version before storing it.
|
||||
// We force a check to ensure to have the latest version.
|
||||
let latestBackupInfo;
|
||||
try {
|
||||
latestBackupInfo = await this.requestKeyBackupVersion();
|
||||
} catch (e) {
|
||||
this.logger.warn("handleBackupSecretReceived: Error checking for latest key backup", e);
|
||||
return false;
|
||||
}
|
||||
if (!latestBackupInfo?.version) {
|
||||
// There is no server-side key backup.
|
||||
// This decryption key is useless to us.
|
||||
this.logger.warn("handleBackupSecretReceived: Received a backup decryption key, but there is no server-side key backup");
|
||||
return false;
|
||||
}
|
||||
let backupDecryptionKey;
|
||||
try {
|
||||
backupDecryptionKey = RustSdkCryptoJs.BackupDecryptionKey.fromBase64(secret);
|
||||
} catch (e) {
|
||||
this.logger.warn("handleBackupSecretReceived: Invalid backup decryption key", e);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const privateKeyMatches = this.backupInfoMatchesBackupDecryptionKey(latestBackupInfo, backupDecryptionKey);
|
||||
if (!privateKeyMatches) {
|
||||
this.logger.warn(`handleBackupSecretReceived: Private decryption key does not match the public key of the current server-side backup version (${latestBackupInfo.version})`);
|
||||
// just ignore the secret
|
||||
return false;
|
||||
}
|
||||
this.logger.info(`handleBackupSecretReceived: Valid decryption key for the current server-side backup version (${latestBackupInfo.version}) received`);
|
||||
await this.saveBackupDecryptionKey(backupDecryptionKey, latestBackupInfo.version);
|
||||
|
||||
// Check if backup upload should be enabled (e.g. the encryption key matches the decryption key),
|
||||
// and enable it if so.
|
||||
if (this.keyBackupCheckInProgress) {
|
||||
this.logger.debug("handleBackupSecretReceived: waiting for ongoing keybackup check to complete");
|
||||
await this.keyBackupCheckInProgress;
|
||||
}
|
||||
this.logger.debug("handleBackupSecretReceived: checking if we can enable keybackup upload");
|
||||
this.keyBackupCheckInProgress = this.doCheckKeyBackup(latestBackupInfo).finally(() => {
|
||||
this.keyBackupCheckInProgress = null;
|
||||
});
|
||||
await this.keyBackupCheckInProgress;
|
||||
return true;
|
||||
} catch (e) {
|
||||
this.logger.warn("handleBackupSecretReceived: Unable to validate backup decryption key", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
async saveBackupDecryptionKey(backupDecryptionKey, version) {
|
||||
await this.olmMachine.saveBackupDecryptionKey(backupDecryptionKey, version);
|
||||
// Emit an event that we have a new backup decryption key, so that the sdk can start
|
||||
// importing keys from backup if needed.
|
||||
this.emit(CryptoEvent.KeyBackupDecryptionKeyCached, version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a list of room keys previously exported by exportRoomKeys
|
||||
*
|
||||
* @param keys - a list of session export objects
|
||||
* @param opts - options object
|
||||
* @returns a promise which resolves once the keys have been imported
|
||||
*/
|
||||
async importRoomKeys(keys, opts) {
|
||||
await this.importRoomKeysAsJson(JSON.stringify(keys), opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a list of room keys previously exported by exportRoomKeysAsJson
|
||||
*
|
||||
* @param jsonKeys - a JSON string encoding a list of session export objects,
|
||||
* each of which is an IMegolmSessionData
|
||||
* @param opts - options object
|
||||
* @returns a promise which resolves once the keys have been imported
|
||||
*/
|
||||
async importRoomKeysAsJson(jsonKeys, opts) {
|
||||
await this.olmMachine.importExportedRoomKeys(jsonKeys, (progress, total) => {
|
||||
const importOpt = {
|
||||
total: Number(total),
|
||||
successes: Number(progress),
|
||||
stage: ImportRoomKeyStage.LoadKeys,
|
||||
failures: 0
|
||||
};
|
||||
opts?.progressCallback?.(importOpt);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link CryptoBackend#importBackedUpRoomKeys}.
|
||||
*/
|
||||
async importBackedUpRoomKeys(keys, backupVersion, opts) {
|
||||
const keysByRoom = new Map();
|
||||
for (const key of keys) {
|
||||
const roomId = new RustSdkCryptoJs.RoomId(key.room_id);
|
||||
if (!keysByRoom.has(roomId)) {
|
||||
keysByRoom.set(roomId, new Map());
|
||||
}
|
||||
keysByRoom.get(roomId).set(key.session_id, key);
|
||||
}
|
||||
await this.olmMachine.importBackedUpRoomKeys(keysByRoom, (progress, total, failures) => {
|
||||
const importOpt = {
|
||||
total: Number(total),
|
||||
successes: Number(progress),
|
||||
stage: ImportRoomKeyStage.LoadKeys,
|
||||
failures: Number(failures)
|
||||
};
|
||||
opts?.progressCallback?.(importOpt);
|
||||
}, backupVersion);
|
||||
}
|
||||
/** Helper to check the key backup status, and enable/disable it as appropriate
|
||||
*
|
||||
* A KeyBackupInfo can be passed if it was fetched recently, to avoid trying to
|
||||
* re-fetch it from the server.
|
||||
*/
|
||||
async doCheckKeyBackup(backupInfo) {
|
||||
this.logger.debug("Checking key backup status...");
|
||||
try {
|
||||
if (!backupInfo) {
|
||||
backupInfo = await this.requestKeyBackupVersion();
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.warn("Error checking for active key backup", e);
|
||||
this.serverBackupInfo = undefined;
|
||||
return null;
|
||||
}
|
||||
this.checkedForBackup = true;
|
||||
this.serverBackupInfo = backupInfo;
|
||||
const activeVersion = await this.getActiveBackupVersion();
|
||||
if (!backupInfo) {
|
||||
if (activeVersion !== null) {
|
||||
this.logger.debug("No key backup present on server: disabling key backup");
|
||||
await this.disableKeyBackup();
|
||||
} else {
|
||||
this.logger.debug("No key backup present on server: not enabling key backup");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const trustInfo = await this.isKeyBackupTrusted(backupInfo);
|
||||
|
||||
// Per the spec, we should enable key upload if either (a) the backup is signed by a trusted key, or
|
||||
// (b) the public key matches the private decryption key that we have received from 4S.
|
||||
if (!trustInfo.matchesDecryptionKey && !trustInfo.trusted) {
|
||||
if (activeVersion !== null) {
|
||||
this.logger.debug("Key backup present on server but not trusted: disabling key backup");
|
||||
await this.disableKeyBackup();
|
||||
} else {
|
||||
this.logger.debug("Key backup present on server but not trusted: not enabling key backup");
|
||||
}
|
||||
} else {
|
||||
await this.enableOrSwitchKeyBackup(backupInfo, activeVersion);
|
||||
}
|
||||
return {
|
||||
backupInfo,
|
||||
trustInfo
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable key backup upload for the given backup version, if it is not already.
|
||||
*
|
||||
* If backup is currently enabled for a different version, disables it first.
|
||||
*
|
||||
* Also emits one or more {@link CryptoEvent.KeyBackupStatus} events if the backup status changes.
|
||||
*
|
||||
* @param backupInfo - the desired backup version (and the encryption key).
|
||||
* @param activeVersion - the current active backup version (or `null`, if none).
|
||||
*/
|
||||
async enableOrSwitchKeyBackup(backupInfo, activeVersion) {
|
||||
if (activeVersion === null) {
|
||||
this.logger.debug(`Found usable key backup v${backupInfo.version}: enabling key backups`);
|
||||
await this.enableKeyBackup(backupInfo);
|
||||
} else if (activeVersion !== backupInfo.version) {
|
||||
this.logger.debug(`On backup version ${activeVersion} but found version ${backupInfo.version}: switching.`);
|
||||
// This will remove any pending backup request, remove the backup upload key from the OlmMachine and reset
|
||||
// the backup state of each room key we have.
|
||||
await this.disableKeyBackup();
|
||||
// Enabling will now trigger re-upload of all the keys
|
||||
await this.enableKeyBackup(backupInfo);
|
||||
} else {
|
||||
this.logger.debug(`Backup version ${backupInfo.version} still current`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for {@link enableOrSwitchKeyBackup}.
|
||||
*
|
||||
* Enables key backup upload for the given backup version. Also emits
|
||||
* a {@link CryptoEvent.KeyBackupStatus} event.
|
||||
*/
|
||||
async enableKeyBackup(backupInfo) {
|
||||
// we know for certain it must be a Curve25519 key, because we have verified it and only Curve25519
|
||||
// keys can be verified.
|
||||
await this.olmMachine.enableBackupV1(backupInfo.auth_data.public_key, backupInfo.version);
|
||||
this.activeBackupVersion = backupInfo.version;
|
||||
this.emit(CryptoEvent.KeyBackupStatus, true);
|
||||
this.backupKeysLoop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the backup key loop if there is an active trusted backup.
|
||||
* Doesn't try to check the backup server side. To be called when a new
|
||||
* megolm key is known locally.
|
||||
*/
|
||||
async maybeUploadKey() {
|
||||
if (this.activeBackupVersion != null) {
|
||||
this.backupKeysLoop();
|
||||
}
|
||||
}
|
||||
async disableKeyBackup() {
|
||||
await this.olmMachine.disableBackup();
|
||||
this.activeBackupVersion = null;
|
||||
this.emit(CryptoEvent.KeyBackupStatus, false);
|
||||
}
|
||||
async backupKeysLoop(maxDelay = 10000) {
|
||||
if (this.backupKeysLoopRunning) {
|
||||
this.logger.debug(`Backup loop already running`);
|
||||
return;
|
||||
}
|
||||
this.backupKeysLoopRunning = true;
|
||||
this.logger.debug(`Backup: Starting keys upload loop for backup version:${this.activeBackupVersion}.`);
|
||||
|
||||
// wait between 0 and `maxDelay` seconds, to avoid backup
|
||||
// requests from different clients hitting the server all at
|
||||
// the same time when a new key is sent
|
||||
const delay = Math.random() * maxDelay;
|
||||
await sleep(delay);
|
||||
try {
|
||||
// number of consecutive network failures for exponential backoff
|
||||
let numFailures = 0;
|
||||
// The number of keys left to back up. (Populated lazily: see more comments below.)
|
||||
let remainingToUploadCount = null;
|
||||
// To avoid computing the key when only a few keys were added (after a sync for example),
|
||||
// we compute the count only when at least two iterations are needed.
|
||||
let isFirstIteration = true;
|
||||
while (!this.stopped) {
|
||||
// Get a batch of room keys to upload
|
||||
let request = undefined;
|
||||
try {
|
||||
request = await logDuration(this.logger, "BackupRoomKeys: Get keys to backup from rust crypto-sdk", async () => {
|
||||
return await this.olmMachine.backupRoomKeys();
|
||||
});
|
||||
} catch (err) {
|
||||
this.logger.error("Backup: Failed to get keys to backup from rust crypto-sdk", err);
|
||||
}
|
||||
if (!request || this.stopped || !this.activeBackupVersion) {
|
||||
this.logger.debug(`Backup: Ending loop for version ${this.activeBackupVersion}.`);
|
||||
if (!request) {
|
||||
// nothing more to upload
|
||||
this.emit(CryptoEvent.KeyBackupSessionsRemaining, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await this.outgoingRequestProcessor.makeOutgoingRequest(request);
|
||||
numFailures = 0;
|
||||
if (this.stopped) break;
|
||||
|
||||
// Key count performance (`olmMachine.roomKeyCounts()`) can be pretty bad on some configurations.
|
||||
// In particular, we detected on some M1 macs that when the object store reaches a threshold, the count
|
||||
// performance stops growing in O(n) and suddenly becomes very slow (40s, 60s or more).
|
||||
// For reference, the performance drop occurs around 300-400k keys on the platforms where this issue is observed.
|
||||
// Even on other configurations, the count can take several seconds.
|
||||
// This will block other operations on the database, like sending messages.
|
||||
//
|
||||
// This is a workaround to avoid calling `olmMachine.roomKeyCounts()` too often, and only when necessary.
|
||||
// We don't call it on the first loop because there could be only a few keys to upload, and we don't want to wait for the count.
|
||||
if (!isFirstIteration && remainingToUploadCount === null) {
|
||||
try {
|
||||
const keyCount = await this.olmMachine.roomKeyCounts();
|
||||
remainingToUploadCount = keyCount.total - keyCount.backedUp;
|
||||
} catch (err) {
|
||||
this.logger.error("Backup: Failed to get key counts from rust crypto-sdk", err);
|
||||
}
|
||||
}
|
||||
if (remainingToUploadCount !== null) {
|
||||
this.emit(CryptoEvent.KeyBackupSessionsRemaining, remainingToUploadCount);
|
||||
const keysCountInBatch = this.keysCountInBatch(request);
|
||||
// `OlmMachine.roomKeyCounts` is called only once for the current backupKeysLoop. But new
|
||||
// keys could be added during the current loop (after a sync for example).
|
||||
// So the count can get out of sync with the real number of remaining keys to upload.
|
||||
// Depending on the number of new keys imported and the time to complete the loop,
|
||||
// this could result in multiple events being emitted with a remaining key count of 0.
|
||||
remainingToUploadCount = Math.max(remainingToUploadCount - keysCountInBatch, 0);
|
||||
}
|
||||
} catch (err) {
|
||||
numFailures++;
|
||||
this.logger.error("Backup: Error processing backup request for rust crypto-sdk", err);
|
||||
if (err instanceof MatrixError) {
|
||||
const errCode = err.data.errcode;
|
||||
if (errCode == "M_NOT_FOUND" || errCode == "M_WRONG_ROOM_KEYS_VERSION") {
|
||||
this.logger.debug(`Backup: Failed to upload keys to current vesion: ${errCode}.`);
|
||||
try {
|
||||
await this.disableKeyBackup();
|
||||
} catch (error) {
|
||||
this.logger.error("Backup: An error occurred while disabling key backup:", error);
|
||||
}
|
||||
this.emit(CryptoEvent.KeyBackupFailed, err.data.errcode);
|
||||
// There was an active backup and we are out of sync with the server
|
||||
// force a check server side
|
||||
this.backupKeysLoopRunning = false;
|
||||
this.checkKeyBackupAndEnable(true);
|
||||
return;
|
||||
} else if (err.isRateLimitError()) {
|
||||
// wait for that and then continue?
|
||||
try {
|
||||
const waitTime = err.getRetryAfterMs();
|
||||
if (waitTime && waitTime > 0) {
|
||||
await sleep(waitTime);
|
||||
continue;
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.warn("Backup: An error occurred while retrieving a rate-limit retry delay", error);
|
||||
} // else go to the normal backoff
|
||||
}
|
||||
}
|
||||
|
||||
// Some other errors (mx, network, or CORS or invalid urls?) anyhow backoff
|
||||
// exponential backoff if we have failures
|
||||
await sleep(1000 * Math.pow(2, Math.min(numFailures - 1, 4)));
|
||||
}
|
||||
isFirstIteration = false;
|
||||
}
|
||||
} finally {
|
||||
this.backupKeysLoopRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to count the number of keys in a backup request, in order to update the remaining keys count.
|
||||
* This should be the chunk size of the backup request for all requests but the last, but we don't have access to it
|
||||
* (it's static in the Rust SDK).
|
||||
* @param batch - The backup request to count the keys from.
|
||||
*
|
||||
* @returns The number of keys in the backup request.
|
||||
*/
|
||||
keysCountInBatch(batch) {
|
||||
const parsedBody = JSON.parse(batch.body);
|
||||
return countKeysInBackup(parsedBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about a key backup from the server
|
||||
* - If version is provided, get information about that backup version.
|
||||
* - If no version is provided, get information about the latest backup.
|
||||
*
|
||||
* @param version - The version of the backup to get information about.
|
||||
* @returns Information object from API or null if there is no active backup.
|
||||
*/
|
||||
async requestKeyBackupVersion(version) {
|
||||
return await requestKeyBackupVersion(this.http, version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new key backup by generating a new random private key, and then enable key backup upload and download
|
||||
* using the new backup version.
|
||||
*
|
||||
* If there is an existing backup server side it will be deleted and replaced
|
||||
* by the new one.
|
||||
*
|
||||
* Saves the decryption key in the Rust SDK's CryptoStore.
|
||||
*
|
||||
* @param signObject - Method that should sign the backup with existing device and
|
||||
* existing identity.
|
||||
* @returns a KeyBackupCreationInfo - All information related to the backup.
|
||||
*/
|
||||
async setupKeyBackup(signObject) {
|
||||
// Wait for any active call to `checkKeyBackupAndEnable` to complete, to avoid racing with it
|
||||
if (this.keyBackupCheckInProgress) {
|
||||
await this.keyBackupCheckInProgress;
|
||||
}
|
||||
|
||||
// Clean up any existing backup
|
||||
await this.deleteAllKeyBackupVersions();
|
||||
const randomKey = RustSdkCryptoJs.BackupDecryptionKey.createRandomKey();
|
||||
const pubKey = randomKey.megolmV1PublicKey;
|
||||
const authData = {
|
||||
public_key: pubKey.publicKeyBase64
|
||||
};
|
||||
await signObject(authData);
|
||||
const backupData = {
|
||||
algorithm: pubKey.algorithm,
|
||||
auth_data: authData
|
||||
};
|
||||
const res = await this.http.authedRequest(Method.Post, "/room_keys/version", undefined, backupData, {
|
||||
prefix: ClientPrefix.V3
|
||||
});
|
||||
|
||||
// This backup was just created and signed locally, so use the creation response to make up a full
|
||||
// `KeyBackupInfo` struct representing the new backup, instead of doing another discovery/trust check.
|
||||
const backupInfo = {
|
||||
algorithm: pubKey.algorithm,
|
||||
auth_data: authData,
|
||||
version: res.version,
|
||||
count: 0,
|
||||
etag: "" // we never actually use the etag, so we can just make up a value
|
||||
};
|
||||
|
||||
// saveBackupDecryptionKey emits KeyBackupDecryptionKeyCached. Cache and
|
||||
// enable the created backup first so listeners observe the new version.
|
||||
this.serverBackupInfo = backupInfo;
|
||||
this.checkedForBackup = true;
|
||||
await this.enableOrSwitchKeyBackup(backupInfo, await this.getActiveBackupVersion());
|
||||
await this.saveBackupDecryptionKey(randomKey, res.version);
|
||||
return {
|
||||
version: res.version,
|
||||
algorithm: pubKey.algorithm,
|
||||
authData: authData,
|
||||
decryptionKey: randomKey
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all key backups.
|
||||
*
|
||||
* Will call the API to delete active backup until there is no more present.
|
||||
*/
|
||||
async deleteAllKeyBackupVersions() {
|
||||
// there could be several backup versions. Delete all to be safe.
|
||||
let current = (await this.requestKeyBackupVersion())?.version ?? null;
|
||||
while (current != null) {
|
||||
await this.deleteKeyBackupVersion(current);
|
||||
current = (await this.requestKeyBackupVersion())?.version ?? null;
|
||||
}
|
||||
|
||||
// XXX: Should this also update Secret Storage and delete any existing keys?
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given key backup.
|
||||
*
|
||||
* @param version - The backup version to delete.
|
||||
*/
|
||||
async deleteKeyBackupVersion(version) {
|
||||
this.logger.debug(`deleteKeyBackupVersion v:${version}`);
|
||||
const path = encodeUri("/room_keys/version/$version", {
|
||||
$version: version
|
||||
});
|
||||
await this.http.authedRequest(Method.Delete, path, undefined, undefined, {
|
||||
prefix: ClientPrefix.V3
|
||||
});
|
||||
// If the backup we are deleting is the active one, we need to disable the key backup and to have the local properties reset
|
||||
if (this.activeBackupVersion === version) {
|
||||
this.serverBackupInfo = null;
|
||||
await this.disableKeyBackup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new backup decryptor for the given private key.
|
||||
* @param decryptionKey - The private key to use for decryption.
|
||||
*/
|
||||
createBackupDecryptor(decryptionKey) {
|
||||
return new RustBackupDecryptor(this.logger, decryptionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a key backup.
|
||||
*
|
||||
* @param backupVersion - The version of the backup to restore.
|
||||
* @param backupDecryptor - The backup decryptor to use to decrypt the keys.
|
||||
* @param opts - Options for the restore.
|
||||
* @returns The total number of keys and the total imported.
|
||||
*/
|
||||
async restoreKeyBackup(backupVersion, backupDecryptor, opts) {
|
||||
const keyBackup = await this.downloadKeyBackup(backupVersion);
|
||||
return this.importKeyBackup(keyBackup, backupVersion, backupDecryptor, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download and import the keys for a given room from the current backup version.
|
||||
*
|
||||
* @param roomId - The room in question.
|
||||
*/
|
||||
async downloadLatestRoomKeyBackup(roomId) {
|
||||
const {
|
||||
backupVersion,
|
||||
decryptionKey
|
||||
} = await this.olmMachine.getBackupKeys();
|
||||
if (!backupVersion || !decryptionKey) {
|
||||
this.logger.warn(`downloadLatestRoomKeyBackup: Could not download backup (backupVersion=${backupVersion}, hasDecryptionKey=${!!decryptionKey})`);
|
||||
return;
|
||||
}
|
||||
const sessions = await this.downloadRoomKeyBackup(backupVersion, roomId);
|
||||
const backupDecryptor = this.createBackupDecryptor(decryptionKey);
|
||||
this.importKeyBackup({
|
||||
rooms: {
|
||||
[roomId]: {
|
||||
sessions
|
||||
}
|
||||
}
|
||||
}, backupVersion, backupDecryptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call `/room_keys/keys` to download the key backup (room keys) for the given backup version.
|
||||
* https://spec.matrix.org/v1.12/client-server-api/#get_matrixclientv3room_keyskeys
|
||||
*
|
||||
* @param backupVersion
|
||||
* @returns The key backup response.
|
||||
*/
|
||||
downloadKeyBackup(backupVersion) {
|
||||
return this.http.authedRequest(Method.Get, "/room_keys/keys", {
|
||||
version: backupVersion
|
||||
}, undefined, {
|
||||
prefix: ClientPrefix.V3
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Call `/room/keys/keys/{roomId}` to download the key backup (room keys) for a given backup version and room ID.
|
||||
* @param backupVersion - The version to download.
|
||||
* @param roomId - The ID of the room.
|
||||
* @returns The key backup response.
|
||||
*/
|
||||
downloadRoomKeyBackup(backupVersion, roomId) {
|
||||
const path = encodeUri("/room_keys/keys/$roomId", {
|
||||
$roomId: roomId
|
||||
});
|
||||
return this.http.authedRequest(Method.Get, path, {
|
||||
version: backupVersion
|
||||
}, undefined, {
|
||||
prefix: ClientPrefix.V3
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Import the room keys from a `/room_keys/keys` call.
|
||||
* Calls `opts.progressCallback` with the progress of the import.
|
||||
*
|
||||
* @param keyBackup - The response from the server containing the keys to import.
|
||||
* @param backupVersion - The version of the backup info.
|
||||
* @param backupDecryptor - The backup decryptor to use to decrypt the keys.
|
||||
* @param opts - Options for the import.
|
||||
*
|
||||
* @returns The total number of keys and the total imported.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async importKeyBackup(keyBackup, backupVersion, backupDecryptor, opts) {
|
||||
// We have a full backup here, it can get quite big, so we need to decrypt and import it in chunks.
|
||||
|
||||
const CHUNK_SIZE = 200;
|
||||
// Get the total count as a first pass
|
||||
const totalKeyCount = countKeysInBackup(keyBackup);
|
||||
let totalImported = 0;
|
||||
let totalFailures = 0;
|
||||
opts?.progressCallback?.({
|
||||
total: totalKeyCount,
|
||||
successes: totalImported,
|
||||
stage: ImportRoomKeyStage.LoadKeys,
|
||||
failures: totalFailures
|
||||
});
|
||||
|
||||
/**
|
||||
* This method is called when we have enough chunks to decrypt.
|
||||
* It will decrypt the chunks and try to import the room keys.
|
||||
* @param roomChunks
|
||||
*/
|
||||
const handleChunkCallback = async roomChunks => {
|
||||
const currentChunk = [];
|
||||
for (const roomId of roomChunks.keys()) {
|
||||
// Decrypt the sessions for the given room
|
||||
const decryptedSessions = await backupDecryptor.decryptSessions(roomChunks.get(roomId));
|
||||
// Add the decrypted sessions to the current chunk
|
||||
decryptedSessions.forEach(session => {
|
||||
// We set the room_id for each session
|
||||
session.room_id = roomId;
|
||||
currentChunk.push(session);
|
||||
});
|
||||
}
|
||||
|
||||
// We have a chunk of decrypted keys: import them
|
||||
try {
|
||||
await this.importBackedUpRoomKeys(currentChunk, backupVersion);
|
||||
totalImported += currentChunk.length;
|
||||
} catch (e) {
|
||||
totalFailures += currentChunk.length;
|
||||
// We failed to import some keys, but we should still try to import the rest?
|
||||
// Log the error and continue
|
||||
this.logger.error("Error importing keys from backup", e);
|
||||
}
|
||||
opts?.progressCallback?.({
|
||||
total: totalKeyCount,
|
||||
successes: totalImported,
|
||||
stage: ImportRoomKeyStage.LoadKeys,
|
||||
failures: totalFailures
|
||||
});
|
||||
};
|
||||
let groupChunkCount = 0;
|
||||
let chunkGroupByRoom = new Map();
|
||||
|
||||
// Iterate over the rooms and sessions to group them in chunks
|
||||
// And we call the handleChunkCallback when we have enough chunks to decrypt
|
||||
for (const [roomId, roomData] of Object.entries(keyBackup.rooms)) {
|
||||
// If there are no sessions for the room, skip it
|
||||
if (!roomData.sessions) continue;
|
||||
|
||||
// Initialize a new chunk group for the current room
|
||||
chunkGroupByRoom.set(roomId, {});
|
||||
for (const [sessionId, session] of Object.entries(roomData.sessions)) {
|
||||
// We set previously the chunk group for the current room, so we can safely get it
|
||||
const sessionsForRoom = chunkGroupByRoom.get(roomId);
|
||||
sessionsForRoom[sessionId] = session;
|
||||
groupChunkCount += 1;
|
||||
// If we have enough chunks to decrypt, call the block callback
|
||||
if (groupChunkCount >= CHUNK_SIZE) {
|
||||
// We have enough chunks to decrypt
|
||||
await handleChunkCallback(chunkGroupByRoom);
|
||||
// Reset the chunk group
|
||||
chunkGroupByRoom = new Map();
|
||||
// There might be remaining keys for that room, so add back an entry for the current room.
|
||||
chunkGroupByRoom.set(roomId, {});
|
||||
groupChunkCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle remaining chunk if needed
|
||||
if (groupChunkCount > 0) {
|
||||
await handleChunkCallback(chunkGroupByRoom);
|
||||
}
|
||||
return {
|
||||
total: totalKeyCount,
|
||||
imported: totalImported
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the provided backup info matches the given private key.
|
||||
*
|
||||
* @param info - The backup info to check.
|
||||
* @param backupDecryptionKey - The `BackupDecryptionKey` private key to check against.
|
||||
* @returns `true` if the private key can decrypt the backup, `false` otherwise.
|
||||
*/
|
||||
backupInfoMatchesBackupDecryptionKey(info, backupDecryptionKey) {
|
||||
if (info.algorithm !== "m.megolm_backup.v1.curve25519-aes-sha2") {
|
||||
this.logger.warn("backupMatchesPrivateKey: Unsupported backup algorithm", info.algorithm);
|
||||
return false;
|
||||
}
|
||||
return info.auth_data?.public_key === backupDecryptionKey.megolmV1PublicKey.publicKeyBase64;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Implementation of {@link BackupDecryptor} for the rust crypto backend.
|
||||
*/
|
||||
export class RustBackupDecryptor {
|
||||
constructor(logger, decryptionKey) {
|
||||
_defineProperty(this, "decryptionKey", void 0);
|
||||
_defineProperty(this, "sourceTrusted", void 0);
|
||||
this.logger = logger;
|
||||
this.decryptionKey = decryptionKey;
|
||||
this.sourceTrusted = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@link BackupDecryptor#decryptSessions}
|
||||
*/
|
||||
async decryptSessions(ciphertexts) {
|
||||
const keys = [];
|
||||
for (const [sessionId, sessionData] of Object.entries(ciphertexts)) {
|
||||
try {
|
||||
const decrypted = JSON.parse(this.decryptionKey.decryptV1(sessionData.session_data.ephemeral, sessionData.session_data.mac, sessionData.session_data.ciphertext));
|
||||
decrypted.session_id = sessionId;
|
||||
keys.push(decrypted);
|
||||
} catch (e) {
|
||||
this.logger.debug("Failed to decrypt megolm session from backup", e, sessionData);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@link BackupDecryptor#free}
|
||||
*/
|
||||
free() {
|
||||
this.decryptionKey.free();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a key backup info from the server.
|
||||
*
|
||||
* If `version` is provided, calls `GET /room_keys/version/$version` and gets the backup info for that version.
|
||||
* See https://spec.matrix.org/v1.12/client-server-api/#get_matrixclientv3room_keysversionversion.
|
||||
*
|
||||
* If not, calls `GET /room_keys/version` and gets the latest backup info.
|
||||
* See https://spec.matrix.org/v1.12/client-server-api/#get_matrixclientv3room_keysversion
|
||||
*
|
||||
* @param http
|
||||
* @param version - the specific version of the backup info to fetch
|
||||
* @returns The key backup info or null if there is no backup.
|
||||
*/
|
||||
export async function requestKeyBackupVersion(http, version) {
|
||||
try {
|
||||
const path = version ? encodeUri("/room_keys/version/$version", {
|
||||
$version: version
|
||||
}) : "/room_keys/version";
|
||||
return await http.authedRequest(Method.Get, path, undefined, undefined, {
|
||||
prefix: ClientPrefix.V3
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.errcode === "M_NOT_FOUND") {
|
||||
return null;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the provided decryption key matches the public key of the key backup info.
|
||||
*
|
||||
* @param decryptionKey - The decryption key to check.
|
||||
* @param keyBackupInfo - The key backup info to check against.
|
||||
* @returns `true` if the decryption key matches the key backup info, `false` otherwise.
|
||||
*/
|
||||
export function decryptionKeyMatchesKeyBackupInfo(decryptionKey, keyBackupInfo) {
|
||||
const authData = keyBackupInfo.auth_data;
|
||||
return authData.public_key === decryptionKey.megolmV1PublicKey.publicKeyBase64;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the total number of keys present in a key backup.
|
||||
* @param keyBackup - The key backup to count the keys from.
|
||||
* @returns The total number of keys in the backup.
|
||||
*/
|
||||
function countKeysInBackup(keyBackup) {
|
||||
let count = 0;
|
||||
for (const {
|
||||
sessions
|
||||
} of Object.values(keyBackup.rooms)) {
|
||||
count += Object.keys(sessions).length;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response from GET `/room_keys/keys` endpoint.
|
||||
* See https://spec.matrix.org/latest/client-server-api/#get_matrixclientv3room_keyskeys
|
||||
*/
|
||||
//# sourceMappingURL=backup.js.map
|
||||
1
node_modules/matrix-js-sdk/lib/rust-crypto/backup.js.map
generated
vendored
1
node_modules/matrix-js-sdk/lib/rust-crypto/backup.js.map
generated
vendored
File diff suppressed because one or more lines are too long
9
node_modules/matrix-js-sdk/lib/rust-crypto/constants.js
generated
vendored
9
node_modules/matrix-js-sdk/lib/rust-crypto/constants.js
generated
vendored
@@ -1,3 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.RUST_SDK_STORE_PREFIX = void 0;
|
||||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
@@ -15,5 +21,6 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
/** The prefix used on indexeddbs created by rust-crypto */
|
||||
export const RUST_SDK_STORE_PREFIX = "matrix-js-sdk";
|
||||
const RUST_SDK_STORE_PREFIX = "matrix-js-sdk";
|
||||
exports.RUST_SDK_STORE_PREFIX = RUST_SDK_STORE_PREFIX;
|
||||
//# sourceMappingURL=constants.js.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/constants.js.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/constants.js.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"constants.js","names":[],"sources":["../../src/rust-crypto/constants.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/** The prefix used on indexeddbs created by rust-crypto */\nexport const RUST_SDK_STORE_PREFIX = \"matrix-js-sdk\";\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,OAAO,MAAM,qBAAqB,GAAG,eAAe","ignoreList":[]}
|
||||
{"version":3,"file":"constants.js","names":["RUST_SDK_STORE_PREFIX","exports"],"sources":["../../src/rust-crypto/constants.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/** The prefix used on indexeddbs created by rust-crypto */\nexport const RUST_SDK_STORE_PREFIX = \"matrix-js-sdk\";\n"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACO,MAAMA,qBAAqB,GAAG,eAAe;AAACC,OAAA,CAAAD,qBAAA,GAAAA,qBAAA"}
|
||||
4
node_modules/matrix-js-sdk/lib/rust-crypto/device-converter.d.ts
generated
vendored
4
node_modules/matrix-js-sdk/lib/rust-crypto/device-converter.d.ts
generated
vendored
@@ -1,6 +1,6 @@
|
||||
import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { Device } from "../models/device.ts";
|
||||
import { type DeviceKeys } from "../client.ts";
|
||||
import { Device } from "../models/device";
|
||||
import { DeviceKeys } from "../client";
|
||||
/**
|
||||
* Convert a {@link RustSdkCryptoJs.Device} to a {@link Device}
|
||||
* @param device - Rust Sdk device
|
||||
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/device-converter.d.ts.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/device-converter.d.ts.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"device-converter.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/device-converter.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,eAAe,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,MAAM,EAAsB,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,CAAC,MAAM,GAAG,MAAM,CAwD3G;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAIjF;AAGD,KAAK,WAAW,GAAG,UAAU,CAAC,MAAM,UAAU,CAAC,CAAC;AAEhD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAqBpE"}
|
||||
{"version":3,"file":"device-converter.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/device-converter.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,eAAe,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,MAAM,EAAsB,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,CAAC,MAAM,GAAG,MAAM,CAuD3G;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAIjF;AAGD,KAAK,WAAW,GAAG,UAAU,CAAC,MAAM,UAAU,CAAC,CAAC;AAEhD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAoBpE"}
|
||||
39
node_modules/matrix-js-sdk/lib/rust-crypto/device-converter.js
generated
vendored
39
node_modules/matrix-js-sdk/lib/rust-crypto/device-converter.js
generated
vendored
@@ -1,3 +1,15 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.deviceKeysToDeviceMap = deviceKeysToDeviceMap;
|
||||
exports.downloadDeviceToJsDevice = downloadDeviceToJsDevice;
|
||||
exports.rustDeviceToJsDevice = rustDeviceToJsDevice;
|
||||
var RustSdkCryptoJs = _interopRequireWildcard(require("@matrix-org/matrix-sdk-crypto-wasm"));
|
||||
var _device = require("../models/device");
|
||||
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; }
|
||||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
@@ -14,8 +26,6 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { Device, DeviceVerification } from "../models/device.js";
|
||||
/**
|
||||
* Convert a {@link RustSdkCryptoJs.Device} to a {@link Device}
|
||||
* @param device - Rust Sdk device
|
||||
@@ -23,7 +33,7 @@ import { Device, DeviceVerification } from "../models/device.js";
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function rustDeviceToJsDevice(device, userId) {
|
||||
function rustDeviceToJsDevice(device, userId) {
|
||||
// Copy rust device keys to Device.keys
|
||||
const keys = new Map();
|
||||
for (const [keyId, key] of device.keys.entries()) {
|
||||
@@ -31,11 +41,11 @@ export function rustDeviceToJsDevice(device, userId) {
|
||||
}
|
||||
|
||||
// Compute verified from device state
|
||||
let verified = DeviceVerification.Unverified;
|
||||
let verified = _device.DeviceVerification.Unverified;
|
||||
if (device.isBlacklisted()) {
|
||||
verified = DeviceVerification.Blocked;
|
||||
verified = _device.DeviceVerification.Blocked;
|
||||
} else if (device.isVerified()) {
|
||||
verified = DeviceVerification.Verified;
|
||||
verified = _device.DeviceVerification.Verified;
|
||||
}
|
||||
|
||||
// Convert rust signatures to Device.signatures
|
||||
@@ -67,15 +77,14 @@ export function rustDeviceToJsDevice(device, userId) {
|
||||
break;
|
||||
}
|
||||
});
|
||||
return new Device({
|
||||
return new _device.Device({
|
||||
deviceId: device.deviceId.toString(),
|
||||
userId: userId.toString(),
|
||||
keys,
|
||||
algorithms: Array.from(algorithms),
|
||||
verified,
|
||||
signatures,
|
||||
displayName: device.displayName,
|
||||
dehydrated: device.isDehydrated
|
||||
displayName: device.displayName
|
||||
});
|
||||
}
|
||||
|
||||
@@ -85,7 +94,7 @@ export function rustDeviceToJsDevice(device, userId) {
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function deviceKeysToDeviceMap(deviceKeys) {
|
||||
function deviceKeysToDeviceMap(deviceKeys) {
|
||||
return new Map(Object.entries(deviceKeys).map(([deviceId, device]) => [deviceId, downloadDeviceToJsDevice(device)]));
|
||||
}
|
||||
|
||||
@@ -97,22 +106,22 @@ export function deviceKeysToDeviceMap(deviceKeys) {
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function downloadDeviceToJsDevice(device) {
|
||||
function downloadDeviceToJsDevice(device) {
|
||||
var _device$unsigned;
|
||||
const keys = new Map(Object.entries(device.keys));
|
||||
const displayName = device.unsigned?.device_display_name;
|
||||
const displayName = (_device$unsigned = device.unsigned) === null || _device$unsigned === void 0 ? void 0 : _device$unsigned.device_display_name;
|
||||
const signatures = new Map();
|
||||
if (device.signatures) {
|
||||
// oxlint-disable-next-line guard-for-in
|
||||
for (const userId in device.signatures) {
|
||||
signatures.set(userId, new Map(Object.entries(device.signatures[userId])));
|
||||
}
|
||||
}
|
||||
return new Device({
|
||||
return new _device.Device({
|
||||
deviceId: device.device_id,
|
||||
userId: device.user_id,
|
||||
keys,
|
||||
algorithms: device.algorithms,
|
||||
verified: DeviceVerification.Unverified,
|
||||
verified: _device.DeviceVerification.Unverified,
|
||||
signatures,
|
||||
displayName
|
||||
});
|
||||
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/device-converter.js.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/device-converter.js.map
generated
vendored
File diff suppressed because one or more lines are too long
88
node_modules/matrix-js-sdk/lib/rust-crypto/index.d.ts
generated
vendored
88
node_modules/matrix-js-sdk/lib/rust-crypto/index.d.ts
generated
vendored
@@ -1,78 +1,22 @@
|
||||
import { RustCrypto } from "./rust-crypto.ts";
|
||||
import { type IHttpOpts, type MatrixHttpApi } from "../http-api/index.ts";
|
||||
import { type ServerSideSecretStorage } from "../secret-storage.ts";
|
||||
import { type Logger } from "../logger.ts";
|
||||
import { type CryptoStore } from "../crypto/store/base.ts";
|
||||
import { type CryptoCallbacks } from "../crypto-api/index.ts";
|
||||
/**
|
||||
* The arguments used to initialise RustCrypto, passed in to initRustCrypto.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export interface InitRustCryptoArgs {
|
||||
/** A `Logger` instance that will be used for debug output. */
|
||||
logger: Logger;
|
||||
/**
|
||||
* Low-level HTTP interface: used to make outgoing requests required by the rust SDK.
|
||||
* We expect it to set the access token, etc.
|
||||
*/
|
||||
http: MatrixHttpApi<IHttpOpts & {
|
||||
onlyData: true;
|
||||
}>;
|
||||
/** The local user's User ID. */
|
||||
userId: string;
|
||||
/** The local user's Device ID. */
|
||||
deviceId: string;
|
||||
/** Interface to server-side secret storage. */
|
||||
secretStorage: ServerSideSecretStorage;
|
||||
/** Crypto callbacks provided by the application. */
|
||||
cryptoCallbacks: CryptoCallbacks;
|
||||
/**
|
||||
* The prefix to use on the indexeddbs created by rust-crypto.
|
||||
* If `null`, a memory store will be used.
|
||||
*/
|
||||
storePrefix: string | null;
|
||||
/**
|
||||
* A passphrase to use to encrypt the indexeddb created by rust-crypto.
|
||||
*
|
||||
* Ignored if `storePrefix` is null, or `storeKey` is set. If neither this nor `storeKey` is set
|
||||
* (and `storePrefix` is not null), the indexeddb will be unencrypted.
|
||||
*/
|
||||
storePassphrase?: string;
|
||||
/**
|
||||
* A key to use to encrypt the indexeddb created by rust-crypto.
|
||||
*
|
||||
* Ignored if `storePrefix` is null. Otherwise, if it is set, it must be a 32-byte cryptographic key, which
|
||||
* will be used to encrypt the indexeddb. See also `storePassphrase`.
|
||||
*/
|
||||
storeKey?: Uint8Array;
|
||||
/** If defined, we will check if any data needs migrating from this store to the rust store. */
|
||||
legacyCryptoStore?: CryptoStore;
|
||||
/** The pickle key for `legacyCryptoStore` */
|
||||
legacyPickleKey?: string;
|
||||
/**
|
||||
* A callback which will receive progress updates on migration from `legacyCryptoStore`.
|
||||
*
|
||||
* Called with (-1, -1) to mark the end of migration.
|
||||
*/
|
||||
legacyMigrationProgressListener?: (progress: number, total: number) => void;
|
||||
/**
|
||||
* Whether to enable support for encrypting state events.
|
||||
*/
|
||||
enableEncryptedStateEvents?: boolean;
|
||||
/**
|
||||
* Optional PEM-formatted string that provides CA certificates. These will
|
||||
* be used to check X.509 signatures on user identities. Any user identity
|
||||
* that has a valid signature according to the supplied CAs will be
|
||||
* considered verified, without any manual verification taking place.
|
||||
*/
|
||||
caCertsPem?: string;
|
||||
}
|
||||
import { RustCrypto } from "./rust-crypto";
|
||||
import { IHttpOpts, MatrixHttpApi } from "../http-api";
|
||||
import { ServerSideSecretStorage } from "../secret-storage";
|
||||
import { ICryptoCallbacks } from "../crypto";
|
||||
/**
|
||||
* Create a new `RustCrypto` implementation
|
||||
*
|
||||
* @param args - InitRustCryptoArgs
|
||||
* @param http - Low-level HTTP interface: used to make outgoing requests required by the rust SDK.
|
||||
* We expect it to set the access token, etc.
|
||||
* @param userId - The local user's User ID.
|
||||
* @param deviceId - The local user's Device ID.
|
||||
* @param secretStorage - Interface to server-side secret storage.
|
||||
* @param cryptoCallbacks - Crypto callbacks provided by the application
|
||||
* @param storePrefix - the prefix to use on the indexeddbs created by rust-crypto.
|
||||
* If unset, a memory store will be used.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export declare function initRustCrypto(args: InitRustCryptoArgs): Promise<RustCrypto>;
|
||||
export declare function initRustCrypto(http: MatrixHttpApi<IHttpOpts & {
|
||||
onlyData: true;
|
||||
}>, userId: string, deviceId: string, secretStorage: ServerSideSecretStorage, cryptoCallbacks: ICryptoCallbacks, storePrefix: string | null): Promise<RustCrypto>;
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/index.d.ts.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/index.d.ts.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/index.ts"],"names":[],"mappings":"AAmBA,OAAO,EAA2C,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,KAAK,WAAW,EAAkB,MAAM,yBAAyB,CAAC;AAM3E,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IAC/B,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,IAAI,EAAE,aAAa,CAAC,SAAS,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IAEpD,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IAEf,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IAEjB,+CAA+C;IAC/C,aAAa,EAAE,uBAAuB,CAAC;IAEvC,oDAAoD;IACpD,eAAe,EAAE,eAAe,CAAC;IAEjC;;;OAGG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAE3B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,UAAU,CAAC;IAEtB,+FAA+F;IAC/F,iBAAiB,CAAC,EAAE,WAAW,CAAC;IAEhC,6CAA6C;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;;OAIG;IACH,+BAA+B,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAE5E;;OAEG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;IAErC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAkClF"}
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/index.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C;;;;;;;;;;;;;GAaG;AACH,wBAAsB,cAAc,CAChC,IAAI,EAAE,aAAa,CAAC,SAAS,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,CAAC,EACnD,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,uBAAuB,EACtC,eAAe,EAAE,gBAAgB,EACjC,WAAW,EAAE,MAAM,GAAG,IAAI,GAC3B,OAAO,CAAC,UAAU,CAAC,CAoCrB"}
|
||||
156
node_modules/matrix-js-sdk/lib/rust-crypto/index.js
generated
vendored
156
node_modules/matrix-js-sdk/lib/rust-crypto/index.js
generated
vendored
@@ -1,4 +1,14 @@
|
||||
import _objectSpread from "@babel/runtime/helpers/objectSpread2";
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.initRustCrypto = initRustCrypto;
|
||||
var RustSdkCryptoJs = _interopRequireWildcard(require("@matrix-org/matrix-sdk-crypto-wasm"));
|
||||
var _rustCrypto = require("./rust-crypto");
|
||||
var _logger = require("../logger");
|
||||
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; }
|
||||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
@@ -15,95 +25,35 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { StoreHandle } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE, RustCrypto } from "./rust-crypto.js";
|
||||
import { MigrationState } from "../crypto/store/base.js";
|
||||
import { migrateFromLegacyCrypto, migrateLegacyLocalTrustIfNeeded, migrateRoomSettingsFromLegacyCrypto } from "./libolm_migration.js";
|
||||
|
||||
/**
|
||||
* The arguments used to initialise RustCrypto, passed in to initRustCrypto.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a new `RustCrypto` implementation
|
||||
*
|
||||
* @param args - InitRustCryptoArgs
|
||||
* @param http - Low-level HTTP interface: used to make outgoing requests required by the rust SDK.
|
||||
* We expect it to set the access token, etc.
|
||||
* @param userId - The local user's User ID.
|
||||
* @param deviceId - The local user's Device ID.
|
||||
* @param secretStorage - Interface to server-side secret storage.
|
||||
* @param cryptoCallbacks - Crypto callbacks provided by the application
|
||||
* @param storePrefix - the prefix to use on the indexeddbs created by rust-crypto.
|
||||
* If unset, a memory store will be used.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export async function initRustCrypto(args) {
|
||||
const {
|
||||
logger
|
||||
} = args;
|
||||
|
||||
async function initRustCrypto(http, userId, deviceId, secretStorage, cryptoCallbacks, storePrefix) {
|
||||
var _ref;
|
||||
// initialise the rust matrix-sdk-crypto-wasm, if it hasn't already been done
|
||||
logger.debug("Initialising Rust crypto-sdk WASM artifact");
|
||||
await RustSdkCryptoJs.initAsync();
|
||||
logger.debug("Opening Rust CryptoStore");
|
||||
let storeHandle;
|
||||
if (args.storePrefix) {
|
||||
if (args.storeKey) {
|
||||
storeHandle = await StoreHandle.openWithKey(args.storePrefix, args.storeKey, logger);
|
||||
} else {
|
||||
storeHandle = await StoreHandle.open(args.storePrefix, args.storePassphrase, logger);
|
||||
}
|
||||
} else {
|
||||
storeHandle = await StoreHandle.open(null, null, logger);
|
||||
}
|
||||
if (args.legacyCryptoStore) {
|
||||
// We have a legacy crypto store, which we may need to migrate from.
|
||||
await migrateFromLegacyCrypto(_objectSpread({
|
||||
legacyStore: args.legacyCryptoStore,
|
||||
storeHandle
|
||||
}, args));
|
||||
}
|
||||
const rustCrypto = await initOlmMachine(args, storeHandle);
|
||||
storeHandle.free();
|
||||
logger.debug("Completed rust crypto-sdk setup");
|
||||
return rustCrypto;
|
||||
}
|
||||
async function initOlmMachine({
|
||||
logger,
|
||||
http,
|
||||
userId,
|
||||
deviceId,
|
||||
secretStorage,
|
||||
cryptoCallbacks,
|
||||
legacyCryptoStore,
|
||||
enableEncryptedStateEvents,
|
||||
caCertsPem
|
||||
}, storeHandle) {
|
||||
logger.debug("Init OlmMachine");
|
||||
const olmMachine = await RustSdkCryptoJs.OlmMachine.initFromStore(new RustSdkCryptoJs.UserId(userId), new RustSdkCryptoJs.DeviceId(deviceId), storeHandle, logger, caCertsPem);
|
||||
|
||||
// A final migration step, now that we have an OlmMachine.
|
||||
if (legacyCryptoStore) {
|
||||
await migrateRoomSettingsFromLegacyCrypto({
|
||||
logger,
|
||||
legacyStore: legacyCryptoStore,
|
||||
olmMachine
|
||||
});
|
||||
}
|
||||
// enable tracing in the rust-sdk
|
||||
new RustSdkCryptoJs.Tracing(RustSdkCryptoJs.LoggerLevel.Trace).turnOn();
|
||||
const u = new RustSdkCryptoJs.UserId(userId);
|
||||
const d = new RustSdkCryptoJs.DeviceId(deviceId);
|
||||
_logger.logger.info("Init OlmMachine");
|
||||
|
||||
// Disable room key requests, per https://github.com/vector-im/element-web/issues/26524.
|
||||
olmMachine.roomKeyRequestsEnabled = false;
|
||||
const rustCrypto = new RustCrypto(logger, olmMachine, http, userId, deviceId, secretStorage, cryptoCallbacks, enableEncryptedStateEvents);
|
||||
olmMachine.registerRoomKeyUpdatedCallback(sessions => rustCrypto.onRoomKeysUpdated(sessions));
|
||||
olmMachine.registerRoomKeysWithheldCallback(withheld => rustCrypto.onRoomKeysWithheld(withheld));
|
||||
olmMachine.registerUserIdentityUpdatedCallback(userId => rustCrypto.onUserIdentityUpdated(userId));
|
||||
olmMachine.registerDevicesUpdatedCallback(userIds => rustCrypto.onDevicesUpdated(userIds));
|
||||
|
||||
// Check if there are any key backup secrets pending processing. There may be multiple secrets to process if several devices have gossiped them.
|
||||
// The `registerReceiveSecretCallback` function will only be triggered for new secrets. If the client is restarted before processing them, the secrets will need to be manually handled.
|
||||
void rustCrypto.checkSecrets("m.megolm_backup.v1");
|
||||
|
||||
// Register a callback to be notified when a new secret is received, as for now only the key backup secret is supported (the cross signing secrets are handled automatically by the OlmMachine)
|
||||
olmMachine.registerReceiveSecretCallback((name, _value) =>
|
||||
// Instead of directly checking the secret value, we poll the inbox to get all values for that secret type.
|
||||
// Once we have all the values, we can safely clear the secret inbox.
|
||||
rustCrypto.checkSecrets(name));
|
||||
// TODO: use the pickle key for the passphrase
|
||||
const olmMachine = await RustSdkCryptoJs.OlmMachine.initialize(u, d, storePrefix !== null && storePrefix !== void 0 ? storePrefix : undefined, (_ref = storePrefix && "test pass") !== null && _ref !== void 0 ? _ref : undefined);
|
||||
const rustCrypto = new _rustCrypto.RustCrypto(olmMachine, http, userId, deviceId, secretStorage, cryptoCallbacks);
|
||||
await olmMachine.registerRoomKeyUpdatedCallback(sessions => rustCrypto.onRoomKeysUpdated(sessions));
|
||||
|
||||
// Tell the OlmMachine to think about its outgoing requests before we hand control back to the application.
|
||||
//
|
||||
@@ -115,49 +65,7 @@ async function initOlmMachine({
|
||||
//
|
||||
// XXX: find a less hacky way to do this.
|
||||
await olmMachine.outgoingRequests();
|
||||
if (legacyCryptoStore && (await legacyCryptoStore.containsData())) {
|
||||
const migrationState = await legacyCryptoStore.getMigrationState();
|
||||
if (migrationState < MigrationState.INITIAL_OWN_KEY_QUERY_DONE) {
|
||||
logger.debug(`Performing initial key query after migration`);
|
||||
// We need to do an initial keys query so that the rust stack can properly update trust of
|
||||
// the user device and identity from the migrated private keys.
|
||||
// If not done, there is a short period where the own device/identity trust will be undefined after migration.
|
||||
let initialKeyQueryDone = false;
|
||||
while (!initialKeyQueryDone) {
|
||||
try {
|
||||
await rustCrypto.userHasCrossSigningKeys(userId);
|
||||
initialKeyQueryDone = true;
|
||||
} catch (e) {
|
||||
// If the initial key query fails, we retry until it succeeds.
|
||||
logger.error("Failed to check for cross-signing keys after migration, retrying", e);
|
||||
}
|
||||
}
|
||||
|
||||
// If the private master cross-signing key was not cached in the legacy store, the rust session
|
||||
// will not be able to establish the trust of the user identity.
|
||||
// That means that after migration the session could revert to unverified.
|
||||
// In order to avoid asking the users to re-verify their sessions, we need to migrate the legacy local trust
|
||||
// (if the legacy session was already verified) to the new session.
|
||||
await migrateLegacyLocalTrustIfNeeded({
|
||||
legacyCryptoStore,
|
||||
rustCrypto,
|
||||
logger
|
||||
});
|
||||
await legacyCryptoStore.setMigrationState(MigrationState.INITIAL_OWN_KEY_QUERY_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have any recently-joined rooms, see if we have a pending key bundle for them.
|
||||
for (const pendingDetails of await olmMachine.getAllRoomsPendingKeyBundles()) {
|
||||
const roomId = pendingDetails.roomId.toString();
|
||||
if (Date.now() - pendingDetails.inviteAcceptedAtMillis <= MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE) {
|
||||
logger.info(`Checking for pending key bundle for recently-joined room ${roomId} (joined ${new Date(pendingDetails.inviteAcceptedAtMillis).toISOString()})`);
|
||||
await rustCrypto.maybeAcceptKeyBundle(roomId, pendingDetails.inviterId.toString());
|
||||
} else {
|
||||
logger.info(`Clearing pending-key-bundle flag for room ${roomId} (too old: joined ${new Date(pendingDetails.inviteAcceptedAtMillis).toISOString()})`);
|
||||
await olmMachine.clearRoomPendingKeyBundle(new RustSdkCryptoJs.RoomId(roomId));
|
||||
}
|
||||
}
|
||||
_logger.logger.info("Completed rust crypto-sdk setup");
|
||||
return rustCrypto;
|
||||
}
|
||||
//# sourceMappingURL=index.js.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/index.js.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
81
node_modules/matrix-js-sdk/lib/rust-crypto/libolm_migration.d.ts
generated
vendored
81
node_modules/matrix-js-sdk/lib/rust-crypto/libolm_migration.d.ts
generated
vendored
@@ -1,81 +0,0 @@
|
||||
import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { type Logger } from "../logger.ts";
|
||||
import { type CryptoStore } from "../crypto/store/base.ts";
|
||||
import { type IHttpOpts, type MatrixHttpApi } from "../http-api/index.ts";
|
||||
import { type RustCrypto } from "./rust-crypto.ts";
|
||||
/**
|
||||
* Determine if any data needs migrating from the legacy store, and do so.
|
||||
*
|
||||
* This migrates the base account data, and olm and megolm sessions. It does *not* migrate the room list, which should
|
||||
* happen after an `OlmMachine` is created, via {@link migrateRoomSettingsFromLegacyCrypto}.
|
||||
*
|
||||
* @param args - Arguments object.
|
||||
*/
|
||||
export declare function migrateFromLegacyCrypto(args: {
|
||||
/** A `Logger` instance that will be used for debug output. */
|
||||
logger: Logger;
|
||||
/**
|
||||
* Low-level HTTP interface: used to make outgoing requests required by the rust SDK.
|
||||
* We expect it to set the access token, etc.
|
||||
*/
|
||||
http: MatrixHttpApi<IHttpOpts & {
|
||||
onlyData: true;
|
||||
}>;
|
||||
/** Store to migrate data from. */
|
||||
legacyStore: CryptoStore;
|
||||
/** Pickle key for `legacyStore`. */
|
||||
legacyPickleKey?: string;
|
||||
/** Local user's User ID. */
|
||||
userId: string;
|
||||
/** Local user's Device ID. */
|
||||
deviceId: string;
|
||||
/** Rust crypto store to migrate data into. */
|
||||
storeHandle: RustSdkCryptoJs.StoreHandle;
|
||||
/**
|
||||
* A callback which will receive progress updates on migration from `legacyStore`.
|
||||
*
|
||||
* Called with (-1, -1) to mark the end of migration.
|
||||
*/
|
||||
legacyMigrationProgressListener?: (progress: number, total: number) => void;
|
||||
}): Promise<void>;
|
||||
/**
|
||||
* Determine if any room settings need migrating from the legacy store, and do so.
|
||||
*
|
||||
* @param args - Arguments object.
|
||||
*/
|
||||
export declare function migrateRoomSettingsFromLegacyCrypto({ logger, legacyStore, olmMachine, }: {
|
||||
/** A `Logger` instance that will be used for debug output. */
|
||||
logger: Logger;
|
||||
/** Store to migrate data from. */
|
||||
legacyStore: CryptoStore;
|
||||
/** OlmMachine to store the new data on. */
|
||||
olmMachine: RustSdkCryptoJs.OlmMachine;
|
||||
}): Promise<void>;
|
||||
/**
|
||||
* Check if the user's published identity (ie, public cross-signing keys) was trusted by the legacy session,
|
||||
* and if so mark it as trusted in the Rust session if needed.
|
||||
*
|
||||
* By default, if the legacy session didn't have the private MSK, the migrated session will revert to unverified,
|
||||
* even if the user has verified the session in the past.
|
||||
*
|
||||
* This only occurs if the private MSK was not cached in the crypto store (USK and SSK private keys won't help
|
||||
* to establish trust: the trust is rooted in the MSK).
|
||||
*
|
||||
* Rust crypto will only consider the current session as trusted if we import the private MSK itself.
|
||||
*
|
||||
* We could prompt the user to verify the session again, but it's probably better to just mark the user identity
|
||||
* as locally verified if it was before.
|
||||
*
|
||||
* See https://github.com/element-hq/element-web/issues/27079
|
||||
*
|
||||
* @param args - Argument object.
|
||||
*/
|
||||
export declare function migrateLegacyLocalTrustIfNeeded(args: {
|
||||
/** The legacy crypto store that is migrated. */
|
||||
legacyCryptoStore: CryptoStore;
|
||||
/** The migrated rust crypto stack. */
|
||||
rustCrypto: RustCrypto;
|
||||
/** The logger to use */
|
||||
logger: Logger;
|
||||
}): Promise<void>;
|
||||
//# sourceMappingURL=libolm_migration.d.ts.map
|
||||
1
node_modules/matrix-js-sdk/lib/rust-crypto/libolm_migration.d.ts.map
generated
vendored
1
node_modules/matrix-js-sdk/lib/rust-crypto/libolm_migration.d.ts.map
generated
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"libolm_migration.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/libolm_migration.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,eAAe,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,KAAK,WAAW,EAA+C,MAAM,yBAAyB,CAAC;AAExG,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1E,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAanD;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAChD,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,IAAI,EAAE,aAAa,CAAC,SAAS,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IAEpD,kCAAkC;IAClC,WAAW,EAAE,WAAW,CAAC;IAEzB,oCAAoC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;IAEf,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;IAEjB,8CAA8C;IAC9C,WAAW,EAAE,eAAe,CAAC,WAAW,CAAC;IAEzC;;;;OAIG;IACH,+BAA+B,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/E,GAAG,OAAO,CAAC,IAAI,CAAC,CAgFhB;AA8LD;;;;GAIG;AACH,wBAAsB,mCAAmC,CAAC,EACtD,MAAM,EACN,WAAW,EACX,UAAU,GACb,EAAE;IACC,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IAEf,kCAAkC;IAClC,WAAW,EAAE,WAAW,CAAC;IAEzB,2CAA2C;IAC3C,UAAU,EAAE,eAAe,CAAC,UAAU,CAAC;CAC1C,GAAG,OAAO,CAAC,IAAI,CAAC,CA8ChB;AAuBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,+BAA+B,CAAC,IAAI,EAAE;IACxD,gDAAgD;IAChD,iBAAiB,EAAE,WAAW,CAAC;IAC/B,sCAAsC;IACtC,UAAU,EAAE,UAAU,CAAC;IACvB,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+ChB"}
|
||||
390
node_modules/matrix-js-sdk/lib/rust-crypto/libolm_migration.js
generated
vendored
390
node_modules/matrix-js-sdk/lib/rust-crypto/libolm_migration.js
generated
vendored
@@ -1,390 +0,0 @@
|
||||
/*
|
||||
Copyright 2023-2024 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-wasm";
|
||||
import { MigrationState } from "../crypto/store/base.js";
|
||||
import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store.js";
|
||||
import { requestKeyBackupVersion } from "./backup.js";
|
||||
import { sleep } from "../utils.js";
|
||||
import { encodeBase64 } from "../base64.js";
|
||||
import decryptAESSecretStorageItem from "../utils/decryptAESSecretStorageItem.js";
|
||||
/**
|
||||
* Determine if any data needs migrating from the legacy store, and do so.
|
||||
*
|
||||
* This migrates the base account data, and olm and megolm sessions. It does *not* migrate the room list, which should
|
||||
* happen after an `OlmMachine` is created, via {@link migrateRoomSettingsFromLegacyCrypto}.
|
||||
*
|
||||
* @param args - Arguments object.
|
||||
*/
|
||||
export async function migrateFromLegacyCrypto(args) {
|
||||
const {
|
||||
logger,
|
||||
legacyStore
|
||||
} = args;
|
||||
|
||||
// initialise the rust matrix-sdk-crypto-wasm, if it hasn't already been done
|
||||
await RustSdkCryptoJs.initAsync();
|
||||
if (!(await legacyStore.containsData())) {
|
||||
// This store was never used. Nothing to migrate.
|
||||
return;
|
||||
}
|
||||
await legacyStore.startup();
|
||||
let accountPickle = null;
|
||||
await legacyStore.doTxn("readonly", [IndexedDBCryptoStore.STORE_ACCOUNT], txn => {
|
||||
legacyStore.getAccount(txn, acctPickle => {
|
||||
accountPickle = acctPickle;
|
||||
});
|
||||
});
|
||||
if (!accountPickle) {
|
||||
// This store is not properly set up. Nothing to migrate.
|
||||
logger.debug("Legacy crypto store is not set up (no account found). Not migrating.");
|
||||
return;
|
||||
}
|
||||
let migrationState = await legacyStore.getMigrationState();
|
||||
if (migrationState >= MigrationState.MEGOLM_SESSIONS_MIGRATED) {
|
||||
// All migration is done for now. The room list comes later, once we have an OlmMachine.
|
||||
return;
|
||||
}
|
||||
const nOlmSessions = await countOlmSessions(logger, legacyStore);
|
||||
const nMegolmSessions = await countMegolmSessions(logger, legacyStore);
|
||||
const totalSteps = 1 + nOlmSessions + nMegolmSessions;
|
||||
logger.info(`Migrating data from legacy crypto store. ${nOlmSessions} olm sessions and ${nMegolmSessions} megolm sessions to migrate.`);
|
||||
let stepsDone = 0;
|
||||
function onProgress(steps) {
|
||||
stepsDone += steps;
|
||||
args.legacyMigrationProgressListener?.(stepsDone, totalSteps);
|
||||
}
|
||||
onProgress(0);
|
||||
const pickleKey = new TextEncoder().encode(args.legacyPickleKey).slice();
|
||||
if (migrationState === MigrationState.NOT_STARTED) {
|
||||
logger.info("Migrating data from legacy crypto store. Step 1: base data");
|
||||
await migrateBaseData(args.http, args.userId, args.deviceId, legacyStore, pickleKey, args.storeHandle, logger);
|
||||
migrationState = MigrationState.INITIAL_DATA_MIGRATED;
|
||||
await legacyStore.setMigrationState(migrationState);
|
||||
}
|
||||
onProgress(1);
|
||||
if (migrationState === MigrationState.INITIAL_DATA_MIGRATED) {
|
||||
logger.info(`Migrating data from legacy crypto store. Step 2: olm sessions (${nOlmSessions} sessions to migrate).`);
|
||||
await migrateOlmSessions(logger, legacyStore, pickleKey, args.storeHandle, onProgress);
|
||||
migrationState = MigrationState.OLM_SESSIONS_MIGRATED;
|
||||
await legacyStore.setMigrationState(migrationState);
|
||||
}
|
||||
if (migrationState === MigrationState.OLM_SESSIONS_MIGRATED) {
|
||||
logger.info(`Migrating data from legacy crypto store. Step 3: megolm sessions (${nMegolmSessions} sessions to migrate).`);
|
||||
await migrateMegolmSessions(logger, legacyStore, pickleKey, args.storeHandle, onProgress);
|
||||
migrationState = MigrationState.MEGOLM_SESSIONS_MIGRATED;
|
||||
await legacyStore.setMigrationState(migrationState);
|
||||
}
|
||||
|
||||
// Migration is done.
|
||||
args.legacyMigrationProgressListener?.(-1, -1);
|
||||
logger.info("Migration from legacy crypto store complete");
|
||||
}
|
||||
async function migrateBaseData(http, userId, deviceId, legacyStore, pickleKey, storeHandle, logger) {
|
||||
const migrationData = new RustSdkCryptoJs.BaseMigrationData();
|
||||
migrationData.userId = new RustSdkCryptoJs.UserId(userId);
|
||||
migrationData.deviceId = new RustSdkCryptoJs.DeviceId(deviceId);
|
||||
await legacyStore.doTxn("readonly", [IndexedDBCryptoStore.STORE_ACCOUNT], txn => legacyStore.getAccount(txn, a => {
|
||||
migrationData.pickledAccount = a ?? "";
|
||||
}));
|
||||
const recoveryKey = await getAndDecryptCachedSecretKey(legacyStore, pickleKey, "m.megolm_backup.v1");
|
||||
|
||||
// If we have a backup recovery key, we need to try to figure out which backup version it is for.
|
||||
// All we can really do is ask the server for the most recent version and check if the cached key we have matches.
|
||||
// It is possible that the backup has changed since last time his session was opened.
|
||||
if (recoveryKey) {
|
||||
let backupCallDone = false;
|
||||
let backupInfo = null;
|
||||
while (!backupCallDone) {
|
||||
try {
|
||||
backupInfo = await requestKeyBackupVersion(http);
|
||||
backupCallDone = true;
|
||||
} catch (e) {
|
||||
logger.info("Failed to get backup version during migration, retrying in 2 seconds", e);
|
||||
// Retry until successful, use simple constant delay
|
||||
await sleep(2000);
|
||||
}
|
||||
}
|
||||
if (backupInfo && backupInfo.algorithm == "m.megolm_backup.v1.curve25519-aes-sha2") {
|
||||
// check if the recovery key matches, as the active backup version may have changed since the key was cached
|
||||
// and the migration started.
|
||||
try {
|
||||
const decryptionKey = RustSdkCryptoJs.BackupDecryptionKey.fromBase64(recoveryKey);
|
||||
const publicKey = backupInfo.auth_data?.public_key;
|
||||
const isValid = decryptionKey.megolmV1PublicKey.publicKeyBase64 == publicKey;
|
||||
if (isValid) {
|
||||
migrationData.backupVersion = backupInfo.version;
|
||||
migrationData.backupRecoveryKey = recoveryKey;
|
||||
} else {
|
||||
logger.debug("The backup key to migrate does not match the active backup version", `Cached pub key: ${decryptionKey.megolmV1PublicKey.publicKeyBase64}`, `Active pub key: ${publicKey}`);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn("Failed to check if the backup key to migrate matches the active backup version", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
migrationData.privateCrossSigningMasterKey = await getAndDecryptCachedSecretKey(legacyStore, pickleKey, "master");
|
||||
migrationData.privateCrossSigningSelfSigningKey = await getAndDecryptCachedSecretKey(legacyStore, pickleKey, "self_signing");
|
||||
migrationData.privateCrossSigningUserSigningKey = await getAndDecryptCachedSecretKey(legacyStore, pickleKey, "user_signing");
|
||||
await RustSdkCryptoJs.Migration.migrateBaseData(migrationData, pickleKey, storeHandle, logger);
|
||||
}
|
||||
async function countOlmSessions(logger, legacyStore) {
|
||||
logger.debug("Counting olm sessions to be migrated");
|
||||
let nSessions;
|
||||
await legacyStore.doTxn("readonly", [IndexedDBCryptoStore.STORE_SESSIONS], txn => legacyStore.countEndToEndSessions(txn, n => nSessions = n));
|
||||
return nSessions;
|
||||
}
|
||||
async function countMegolmSessions(logger, legacyStore) {
|
||||
logger.debug("Counting megolm sessions to be migrated");
|
||||
return await legacyStore.countEndToEndInboundGroupSessions();
|
||||
}
|
||||
async function migrateOlmSessions(logger, legacyStore, pickleKey, storeHandle, onBatchDone) {
|
||||
while (true) {
|
||||
const batch = await legacyStore.getEndToEndSessionsBatch();
|
||||
if (batch === null) return;
|
||||
logger.debug(`Migrating batch of ${batch.length} olm sessions`);
|
||||
const migrationData = [];
|
||||
for (const session of batch) {
|
||||
const pickledSession = new RustSdkCryptoJs.PickledSession();
|
||||
pickledSession.senderKey = session.deviceKey;
|
||||
pickledSession.pickle = session.session;
|
||||
pickledSession.lastUseTime = pickledSession.creationTime = new Date(session.lastReceivedMessageTs);
|
||||
migrationData.push(pickledSession);
|
||||
}
|
||||
await RustSdkCryptoJs.Migration.migrateOlmSessions(migrationData, pickleKey, storeHandle, logger);
|
||||
await legacyStore.deleteEndToEndSessionsBatch(batch);
|
||||
onBatchDone(batch.length);
|
||||
}
|
||||
}
|
||||
async function migrateMegolmSessions(logger, legacyStore, pickleKey, storeHandle, onBatchDone) {
|
||||
while (true) {
|
||||
const batch = await legacyStore.getEndToEndInboundGroupSessionsBatch();
|
||||
if (batch === null) return;
|
||||
logger.debug(`Migrating batch of ${batch.length} megolm sessions`);
|
||||
const migrationData = [];
|
||||
for (const session of batch) {
|
||||
const sessionData = session.sessionData;
|
||||
const pickledSession = new RustSdkCryptoJs.PickledInboundGroupSession();
|
||||
pickledSession.pickle = sessionData.session;
|
||||
pickledSession.roomId = new RustSdkCryptoJs.RoomId(sessionData.room_id);
|
||||
pickledSession.senderKey = session.senderKey;
|
||||
pickledSession.senderSigningKey = sessionData.keysClaimed?.["ed25519"];
|
||||
pickledSession.backedUp = !session.needsBackup;
|
||||
|
||||
// The Rust SDK `imported` flag is used to indicate the authenticity status of a Megolm
|
||||
// session, which tells us whether we can reliably tell which Olm device is the owner
|
||||
// (creator) of the session.
|
||||
//
|
||||
// If `imported` is true, then we have no cryptographic proof that the session is owned
|
||||
// by the device with the identity key `senderKey`.
|
||||
//
|
||||
// Only Megolm sessions received directly from the owning device via an encrypted
|
||||
// `m.room_key` to-device message should have `imported` flag set to false. Megolm
|
||||
// sessions received by any other currently available means (i.e. from a
|
||||
// `m.forwarded_room_key`, from v1 asymmetric server-side key backup, imported from a
|
||||
// file, etc) should have the `imported` flag set to true.
|
||||
//
|
||||
// Messages encrypted with such Megolm sessions will have a grey shield in the UI
|
||||
// ("Authenticity of this message cannot be guaranteed").
|
||||
//
|
||||
// However, we don't want to bluntly mark all sessions as `imported` during migration
|
||||
// because users will suddenly start seeing all their historic messages decorated with a
|
||||
// grey shield, which would be seen as a non-actionable regression.
|
||||
//
|
||||
// In the legacy crypto stack, the flag encoding similar information was called
|
||||
// `InboundGroupSessionData.untrusted`. The value of this flag was set as follows:
|
||||
//
|
||||
// - For outbound Megolm sessions created by our own device, `untrusted` is `undefined`.
|
||||
// - For Megolm sessions received via a `m.room_key` to-device message, `untrusted` is
|
||||
// `undefined`.
|
||||
// - For Megolm sessions received via a `m.forwarded_room_key` to-device message,
|
||||
// `untrusted` is `true`.
|
||||
// - For Megolm sessions imported from a (v1 asymmetric / "legacy") server-side key
|
||||
// backup, `untrusted` is `true`.
|
||||
// - For Megolm sessions imported from a file, untrusted is `undefined`.
|
||||
//
|
||||
// The main difference between the legacy crypto stack and the Rust crypto stack is that
|
||||
// the Rust stack considers sessions imported from a file as `imported` (not
|
||||
// authenticated). This is because the Megolm session export file format does not
|
||||
// encode this authenticity information.
|
||||
//
|
||||
// Given this migration is only a one-time thing, we make a concession to accept the
|
||||
// loss of information in this case, to avoid degrading UX in a non-actionable way.
|
||||
pickledSession.imported = sessionData.untrusted === true;
|
||||
migrationData.push(pickledSession);
|
||||
}
|
||||
await RustSdkCryptoJs.Migration.migrateMegolmSessions(migrationData, pickleKey, storeHandle, logger);
|
||||
await legacyStore.deleteEndToEndInboundGroupSessionsBatch(batch);
|
||||
onBatchDone(batch.length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if any room settings need migrating from the legacy store, and do so.
|
||||
*
|
||||
* @param args - Arguments object.
|
||||
*/
|
||||
export async function migrateRoomSettingsFromLegacyCrypto({
|
||||
logger,
|
||||
legacyStore,
|
||||
olmMachine
|
||||
}) {
|
||||
if (!(await legacyStore.containsData())) {
|
||||
// This store was never used. Nothing to migrate.
|
||||
return;
|
||||
}
|
||||
const migrationState = await legacyStore.getMigrationState();
|
||||
if (migrationState >= MigrationState.ROOM_SETTINGS_MIGRATED) {
|
||||
// We've already migrated the room settings.
|
||||
return;
|
||||
}
|
||||
let rooms = {};
|
||||
await legacyStore.doTxn("readwrite", [IndexedDBCryptoStore.STORE_ROOMS], txn => {
|
||||
legacyStore.getEndToEndRooms(txn, result => {
|
||||
rooms = result;
|
||||
});
|
||||
});
|
||||
logger.debug(`Migrating ${Object.keys(rooms).length} sets of room settings`);
|
||||
for (const [roomId, legacySettings] of Object.entries(rooms)) {
|
||||
try {
|
||||
const rustSettings = new RustSdkCryptoJs.RoomSettings();
|
||||
if (legacySettings.algorithm !== "m.megolm.v1.aes-sha2") {
|
||||
logger.warn(`Room ${roomId}: ignoring room with invalid algorithm ${legacySettings.algorithm}`);
|
||||
continue;
|
||||
}
|
||||
rustSettings.algorithm = RustSdkCryptoJs.EncryptionAlgorithm.MegolmV1AesSha2;
|
||||
rustSettings.sessionRotationPeriodMs = legacySettings.rotation_period_ms;
|
||||
rustSettings.sessionRotationPeriodMessages = legacySettings.rotation_period_msgs;
|
||||
await olmMachine.setRoomSettings(new RustSdkCryptoJs.RoomId(roomId), rustSettings);
|
||||
|
||||
// We don't attempt to clear out the settings from the old store, or record where we've gotten up to,
|
||||
// which means that if the app gets restarted while we're in the middle of this migration, we'll start
|
||||
// again from scratch. So be it. Given that legacy crypto loads the whole room list into memory on startup
|
||||
// anyway, we know it can't be that big.
|
||||
} catch (e) {
|
||||
logger.warn(`Room ${roomId}: ignoring settings ${JSON.stringify(legacySettings)} which caused error ${e}`);
|
||||
}
|
||||
}
|
||||
logger.debug(`Completed room settings migration`);
|
||||
await legacyStore.setMigrationState(MigrationState.ROOM_SETTINGS_MIGRATED);
|
||||
}
|
||||
async function getAndDecryptCachedSecretKey(legacyStore, legacyPickleKey, name) {
|
||||
const key = await new Promise(resolve => {
|
||||
legacyStore.doTxn("readonly", [IndexedDBCryptoStore.STORE_ACCOUNT], txn => {
|
||||
legacyStore.getSecretStorePrivateKey(txn, resolve, name);
|
||||
});
|
||||
});
|
||||
if (key && key.ciphertext && key.iv && key.mac) {
|
||||
return await decryptAESSecretStorageItem(key, legacyPickleKey, name);
|
||||
} else if (key instanceof Uint8Array) {
|
||||
// This is a legacy backward compatibility case where the key was stored in clear.
|
||||
return encodeBase64(key);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user's published identity (ie, public cross-signing keys) was trusted by the legacy session,
|
||||
* and if so mark it as trusted in the Rust session if needed.
|
||||
*
|
||||
* By default, if the legacy session didn't have the private MSK, the migrated session will revert to unverified,
|
||||
* even if the user has verified the session in the past.
|
||||
*
|
||||
* This only occurs if the private MSK was not cached in the crypto store (USK and SSK private keys won't help
|
||||
* to establish trust: the trust is rooted in the MSK).
|
||||
*
|
||||
* Rust crypto will only consider the current session as trusted if we import the private MSK itself.
|
||||
*
|
||||
* We could prompt the user to verify the session again, but it's probably better to just mark the user identity
|
||||
* as locally verified if it was before.
|
||||
*
|
||||
* See https://github.com/element-hq/element-web/issues/27079
|
||||
*
|
||||
* @param args - Argument object.
|
||||
*/
|
||||
export async function migrateLegacyLocalTrustIfNeeded(args) {
|
||||
const {
|
||||
legacyCryptoStore,
|
||||
rustCrypto,
|
||||
logger
|
||||
} = args;
|
||||
// Get the public cross-signing identity from rust.
|
||||
const rustOwnIdentity = await rustCrypto.getOwnIdentity();
|
||||
if (!rustOwnIdentity) {
|
||||
// There are no cross-signing keys published server side, so nothing to do here.
|
||||
return;
|
||||
}
|
||||
if (rustOwnIdentity.isVerified()) {
|
||||
// The rust session already trusts the keys, so again, nothing to do.
|
||||
return;
|
||||
}
|
||||
const legacyLocallyTrustedMSK = await getLegacyTrustedPublicMasterKeyBase64(legacyCryptoStore);
|
||||
if (!legacyLocallyTrustedMSK) {
|
||||
// The user never verified their identity in the legacy session, so nothing to do.
|
||||
return;
|
||||
}
|
||||
const mskInfo = JSON.parse(rustOwnIdentity.masterKey);
|
||||
if (!mskInfo.keys || Object.keys(mskInfo.keys).length === 0) {
|
||||
// This should not happen, but let's be safe
|
||||
logger.error("Post Migration | Unexpected error: no master key in the rust session.");
|
||||
return;
|
||||
}
|
||||
const rustSeenMSK = Object.values(mskInfo.keys)[0];
|
||||
if (rustSeenMSK && rustSeenMSK == legacyLocallyTrustedMSK) {
|
||||
logger.info(`Post Migration: Migrating legacy trusted MSK: ${legacyLocallyTrustedMSK} to locally verified.`);
|
||||
// Let's mark the user identity as locally verified as part of the migration.
|
||||
await rustOwnIdentity.verify();
|
||||
// As well as marking the MSK as trusted, `OlmMachine.verify` returns a
|
||||
// `SignatureUploadRequest` which will publish a signature of the MSK using
|
||||
// this device. In this case, we ignore the request: since the user hasn't
|
||||
// actually re-verified the MSK, we don't publish a new signature. (`.verify`
|
||||
// doesn't store the signature, and if we drop the request here it won't be
|
||||
// retried.)
|
||||
//
|
||||
// Not publishing the signature is consistent with the behaviour of
|
||||
// matrix-crypto-sdk when the private key is imported via
|
||||
// `importCrossSigningKeys`, and when the identity is verified via interactive
|
||||
// verification.
|
||||
//
|
||||
// [Aside: device signatures on the MSK are not considered by the rust-sdk to
|
||||
// establish the trust of the user identity so in any case, what we actually do
|
||||
// here is somewhat moot.]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the legacy store has a trusted public master key, and returns it if so.
|
||||
*
|
||||
* @param legacyStore - The legacy store to check.
|
||||
*
|
||||
* @returns `null` if there were no cross signing keys or if they were not trusted. The trusted public master key if it was.
|
||||
*/
|
||||
async function getLegacyTrustedPublicMasterKeyBase64(legacyStore) {
|
||||
let maybeTrustedKeys = null;
|
||||
await legacyStore.doTxn("readonly", "account", txn => {
|
||||
legacyStore.getCrossSigningKeys(txn, keys => {
|
||||
// can be an empty object after resetting cross-signing keys, see storeTrustedSelfKeys
|
||||
const msk = keys?.master;
|
||||
if (msk && Object.keys(msk.keys).length != 0) {
|
||||
// `msk.keys` is an object with { [`ed25519:${pubKey}`]: pubKey }
|
||||
maybeTrustedKeys = Object.values(msk.keys)[0];
|
||||
}
|
||||
});
|
||||
});
|
||||
return maybeTrustedKeys;
|
||||
}
|
||||
//# sourceMappingURL=libolm_migration.js.map
|
||||
1
node_modules/matrix-js-sdk/lib/rust-crypto/libolm_migration.js.map
generated
vendored
1
node_modules/matrix-js-sdk/lib/rust-crypto/libolm_migration.js.map
generated
vendored
File diff suppressed because one or more lines are too long
418
node_modules/matrix-js-sdk/lib/rust-crypto/rust-crypto.d.ts
generated
vendored
418
node_modules/matrix-js-sdk/lib/rust-crypto/rust-crypto.d.ts
generated
vendored
@@ -1,28 +1,24 @@
|
||||
import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import type { IMegolmSessionData } from "../@types/crypto.ts";
|
||||
import { type IDeviceLists, type IToDeviceEvent, type ReceivedToDeviceMessage } from "../sync-accumulator.ts";
|
||||
import type { ToDeviceBatch, ToDevicePayload } from "../models/ToDeviceMessage.ts";
|
||||
import { type MatrixEvent } from "../models/event.ts";
|
||||
import { type Room } from "../models/room.ts";
|
||||
import { type RoomMember } from "../models/room-member.ts";
|
||||
import { type BackupDecryptor, type CryptoBackend, type EventDecryptionResult, type OnSyncCompletedData } from "../common-crypto/CryptoBackend.ts";
|
||||
import { type Logger } from "../logger.ts";
|
||||
import { type IHttpOpts, type MatrixHttpApi } from "../http-api/index.ts";
|
||||
import { type BackupTrustInfo, type BootstrapCrossSigningOpts, type CreateSecretStorageOpts, type CrossSigningKeys, CrossSigningKey, type CrossSigningStatus, type CryptoApi, type CryptoCallbacks, CryptoEvent, type CryptoEventHandlerMap, type DeviceIsolationMode, DeviceVerificationStatus, type EventEncryptionInfo, type GeneratedSecretStorageKey, type ImportRoomKeysOpts, type KeyBackupCheck, type KeyBackupInfo, type KeyBackupRestoreOpts, type KeyBackupRestoreResult, type OwnDeviceKeys, type SecretStorageStatus, type StartDehydrationOpts, UserVerificationStatus, type VerificationRequest } from "../crypto-api/index.ts";
|
||||
import { type DeviceMap } from "../models/device.ts";
|
||||
import { type ServerSideSecretStorage } from "../secret-storage.ts";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
|
||||
import { type UIAuthCallback } from "../interactive-auth.ts";
|
||||
import { type RoomState } from "../matrix.ts";
|
||||
/** The maximum time, in milliseconds, since we accepted an invite, that we should accept a key bundle. */
|
||||
export declare const MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE: number;
|
||||
import type { IEventDecryptionResult, IMegolmSessionData } from "../@types/crypto";
|
||||
import type { IDeviceLists, IToDeviceEvent } from "../sync-accumulator";
|
||||
import type { IEncryptedEventInfo } from "../crypto/api";
|
||||
import { MatrixEvent } from "../models/event";
|
||||
import { Room } from "../models/room";
|
||||
import { RoomMember } from "../models/room-member";
|
||||
import { CryptoBackend, OnSyncCompletedData } from "../common-crypto/CryptoBackend";
|
||||
import { IHttpOpts, MatrixHttpApi } from "../http-api";
|
||||
import { UserTrustLevel } from "../crypto/CrossSigning";
|
||||
import { BootstrapCrossSigningOpts, CreateSecretStorageOpts, CrossSigningKey, CrossSigningStatus, CryptoCallbacks, DeviceVerificationStatus, GeneratedSecretStorageKey, ImportRoomKeysOpts, VerificationRequest } from "../crypto-api";
|
||||
import { DeviceMap } from "../models/device";
|
||||
import { ServerSideSecretStorage } from "../secret-storage";
|
||||
import { CryptoEvent } from "../crypto";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter";
|
||||
/**
|
||||
* An implementation of {@link CryptoBackend} using the Rust matrix-sdk-crypto.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export declare class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventHandlerMap> implements CryptoBackend {
|
||||
private readonly logger;
|
||||
export declare class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEventMap> implements CryptoBackend {
|
||||
/** The `OlmMachine` from the underlying rust crypto sdk. */
|
||||
private readonly olmMachine;
|
||||
/**
|
||||
@@ -37,28 +33,19 @@ export declare class RustCrypto extends TypedEventEmitter<RustCryptoEvents, Cryp
|
||||
private readonly secretStorage;
|
||||
/** Crypto callbacks provided by the application */
|
||||
private readonly cryptoCallbacks;
|
||||
/** Enable support for encrypted state events under MSC4362. */
|
||||
private readonly enableEncryptedStateEvents;
|
||||
/**
|
||||
* The number of iterations to use when deriving a recovery key from a passphrase.
|
||||
*/
|
||||
private readonly RECOVERY_KEY_DERIVATION_ITERATIONS;
|
||||
globalErrorOnUnknownDevices: boolean;
|
||||
private _trustCrossSignedDevices;
|
||||
private deviceIsolationMode;
|
||||
/** whether {@link stop} has been called */
|
||||
private stopped;
|
||||
/** whether {@link outgoingRequestLoop} is currently running */
|
||||
private outgoingRequestLoopRunning;
|
||||
/** mapping of roomId → encryptor class */
|
||||
private roomEncryptors;
|
||||
private eventDecryptor;
|
||||
private keyClaimManager;
|
||||
private outgoingRequestProcessor;
|
||||
private crossSigningIdentity;
|
||||
private readonly backupManager;
|
||||
private outgoingRequestsManager;
|
||||
private readonly perSessionBackupDownloader;
|
||||
private readonly dehydratedDeviceManager;
|
||||
private readonly reemitter;
|
||||
constructor(logger: Logger,
|
||||
constructor(
|
||||
/** The `OlmMachine` from the underlying rust crypto sdk. */
|
||||
olmMachine: RustSdkCryptoJs.OlmMachine,
|
||||
/**
|
||||
@@ -76,69 +63,42 @@ export declare class RustCrypto extends TypedEventEmitter<RustCryptoEvents, Cryp
|
||||
/** Interface to server-side secret storage */
|
||||
secretStorage: ServerSideSecretStorage,
|
||||
/** Crypto callbacks provided by the application */
|
||||
cryptoCallbacks: CryptoCallbacks,
|
||||
/** Enable support for encrypted state events under MSC4362. */
|
||||
enableEncryptedStateEvents?: boolean);
|
||||
/**
|
||||
* Return the OlmMachine only if {@link RustCrypto#stop} has not been called.
|
||||
*
|
||||
* This allows us to better handle race conditions where the client is stopped before or during a crypto API call.
|
||||
*
|
||||
* @throws ClientStoppedError if {@link RustCrypto#stop} has been called.
|
||||
*/
|
||||
private getOlmMachineOrThrow;
|
||||
set globalErrorOnUnknownDevices(_v: boolean);
|
||||
get globalErrorOnUnknownDevices(): boolean;
|
||||
cryptoCallbacks: CryptoCallbacks);
|
||||
stop(): void;
|
||||
encryptEvent(event: MatrixEvent, _room: Room): Promise<void>;
|
||||
decryptEvent(event: MatrixEvent): Promise<EventDecryptionResult>;
|
||||
decryptEvent(event: MatrixEvent): Promise<IEventDecryptionResult>;
|
||||
getEventEncryptionInfo(event: MatrixEvent): IEncryptedEventInfo;
|
||||
checkUserTrust(userId: string): UserTrustLevel;
|
||||
/**
|
||||
* Implementation of {@link CryptoBackend#getBackupDecryptor}.
|
||||
* Get the cross signing information for a given user.
|
||||
*
|
||||
* The cross-signing API is currently UNSTABLE and may change without notice.
|
||||
*
|
||||
* @param userId - the user ID to get the cross-signing info for.
|
||||
*
|
||||
* @returns the cross signing information for the user.
|
||||
*/
|
||||
getBackupDecryptor(backupInfo: KeyBackupInfo, privKey: Uint8Array): Promise<BackupDecryptor>;
|
||||
getStoredCrossSigningForUser(userId: string): null;
|
||||
/**
|
||||
* Implementation of {@link CryptoBackend#importBackedUpRoomKeys}.
|
||||
* This function is unneeded for the rust-crypto.
|
||||
* The cross signing key import and the device verification are done in {@link CryptoApi#bootstrapCrossSigning}
|
||||
*
|
||||
* The function is stub to keep the compatibility with the old crypto.
|
||||
* More information: https://github.com/vector-im/element-web/issues/25648
|
||||
*
|
||||
*
|
||||
* Implementation of {@link CryptoBackend#checkOwnCrossSigningTrust}
|
||||
*/
|
||||
importBackedUpRoomKeys(keys: IMegolmSessionData[], backupVersion: string, opts?: ImportRoomKeysOpts): Promise<void>;
|
||||
/**
|
||||
* Implementation of {@link CryptoBackend.maybeAcceptKeyBundle}.
|
||||
*/
|
||||
maybeAcceptKeyBundle(roomId: string, inviter: string): Promise<boolean>;
|
||||
/**
|
||||
* Implementation of {@link CryptoBackend.markRoomAsPendingKeyBundle}.
|
||||
*/
|
||||
markRoomAsPendingKeyBundle(roomId: string, inviter: string): Promise<void>;
|
||||
checkOwnCrossSigningTrust(): Promise<void>;
|
||||
globalBlacklistUnverifiedDevices: boolean;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#getVersion}.
|
||||
*/
|
||||
getVersion(): string;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#setDeviceIsolationMode}.
|
||||
*/
|
||||
setDeviceIsolationMode(isolationMode: DeviceIsolationMode): void;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#isEncryptionEnabledInRoom}.
|
||||
*/
|
||||
isEncryptionEnabledInRoom(roomId: string): Promise<boolean>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#isStateEncryptionEnabledInRoom}.
|
||||
*/
|
||||
isStateEncryptionEnabledInRoom(roomId: string): Promise<boolean>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#getOwnDeviceKeys}.
|
||||
*/
|
||||
getOwnDeviceKeys(): Promise<OwnDeviceKeys>;
|
||||
prepareToEncrypt(room: Room): void;
|
||||
forceDiscardSession(roomId: string): Promise<void>;
|
||||
exportRoomKeys(): Promise<IMegolmSessionData[]>;
|
||||
exportRoomKeysAsJson(): Promise<string>;
|
||||
importRoomKeys(keys: IMegolmSessionData[], opts?: ImportRoomKeysOpts): Promise<void>;
|
||||
importRoomKeysAsJson(keys: string, opts?: ImportRoomKeysOpts): Promise<void>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi.userHasCrossSigningKeys}.
|
||||
*/
|
||||
userHasCrossSigningKeys(userId?: string, downloadUncached?: boolean): Promise<boolean>;
|
||||
userHasCrossSigningKeys(): Promise<boolean>;
|
||||
prepareToEncrypt(room: Room): void;
|
||||
forceDiscardSession(roomId: string): Promise<void>;
|
||||
exportRoomKeys(): Promise<IMegolmSessionData[]>;
|
||||
importRoomKeys(keys: IMegolmSessionData[], opts?: ImportRoomKeysOpts): Promise<void>;
|
||||
/**
|
||||
* Get the device information for the given list of users.
|
||||
*
|
||||
@@ -167,38 +127,10 @@ export declare class RustCrypto extends TypedEventEmitter<RustCryptoEvents, Cryp
|
||||
* Implementation of {@link CryptoApi#setTrustCrossSignedDevices}.
|
||||
*/
|
||||
setTrustCrossSignedDevices(val: boolean): void;
|
||||
/**
|
||||
* Mark the given device as locally verified.
|
||||
*
|
||||
* Implementation of {@link CryptoApi#setDeviceVerified}.
|
||||
*/
|
||||
setDeviceVerified(userId: string, deviceId: string, verified?: boolean): Promise<void>;
|
||||
/**
|
||||
* Blindly cross-sign one of our other devices.
|
||||
*
|
||||
* Implementation of {@link CryptoApi#crossSignDevice}.
|
||||
*/
|
||||
crossSignDevice(deviceId: string): Promise<void>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#getDeviceVerificationStatus}.
|
||||
*/
|
||||
getDeviceVerificationStatus(userId: string, deviceId: string): Promise<DeviceVerificationStatus | null>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#getUserVerificationStatus}.
|
||||
*/
|
||||
getUserVerificationStatus(userId: string): Promise<UserVerificationStatus>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#pinCurrentUserIdentity}.
|
||||
*/
|
||||
pinCurrentUserIdentity(userId: string): Promise<void>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#withdrawVerificationRequirement}.
|
||||
*/
|
||||
withdrawVerificationRequirement(userId: string): Promise<void>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#getUserCrossSigningKeys}.
|
||||
*/
|
||||
getUserCrossSigningKeys(userId: string): Promise<Partial<CrossSigningKeys> | null>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#isCrossSigningReady}
|
||||
*/
|
||||
@@ -208,26 +140,17 @@ export declare class RustCrypto extends TypedEventEmitter<RustCryptoEvents, Cryp
|
||||
*/
|
||||
getCrossSigningKeyId(type?: CrossSigningKey): Promise<string | null>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#bootstrapCrossSigning}
|
||||
* Implementation of {@link CryptoApi#boostrapCrossSigning}
|
||||
*/
|
||||
bootstrapCrossSigning(opts: BootstrapCrossSigningOpts): Promise<void>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#isSecretStorageReady}
|
||||
*/
|
||||
isSecretStorageReady(): Promise<boolean>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#getSecretStorageStatus}
|
||||
*/
|
||||
getSecretStorageStatus(): Promise<SecretStorageStatus>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#bootstrapSecretStorage}
|
||||
*/
|
||||
bootstrapSecretStorage({ createSecretStorageKey, setupNewSecretStorage, setupNewKeyBackup, }?: CreateSecretStorageOpts): Promise<void>;
|
||||
/**
|
||||
* If we have a backup key for the current, trusted backup in cache,
|
||||
* save it to secret storage.
|
||||
*/
|
||||
private saveBackupKeyToStorage;
|
||||
bootstrapSecretStorage({ createSecretStorageKey, setupNewSecretStorage, }?: CreateSecretStorageOpts): Promise<void>;
|
||||
/**
|
||||
* Add the secretStorage key to the secret storage
|
||||
* - The secret storage key must have the `keyInfo` field filled
|
||||
@@ -251,10 +174,6 @@ export declare class RustCrypto extends TypedEventEmitter<RustCryptoEvents, Cryp
|
||||
* Implementation of {@link CryptoApi#createRecoveryKeyFromPassphrase}
|
||||
*/
|
||||
createRecoveryKeyFromPassphrase(password?: string): Promise<GeneratedSecretStorageKey>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#getEncryptionInfoForEvent}.
|
||||
*/
|
||||
getEncryptionInfoForEvent(event: MatrixEvent): Promise<EventEncryptionInfo | null>;
|
||||
/**
|
||||
* Returns to-device verification requests that are already in progress for the given user id.
|
||||
*
|
||||
@@ -271,28 +190,11 @@ export declare class RustCrypto extends TypedEventEmitter<RustCryptoEvents, Cryp
|
||||
* Implementation of {@link CryptoApi#findVerificationRequestDMInProgress}
|
||||
*
|
||||
* @param roomId - the room to use for verification
|
||||
* @param userId - search the verification request for the given user
|
||||
*
|
||||
* @returns the VerificationRequest that is in progress, if any
|
||||
*
|
||||
*/
|
||||
findVerificationRequestDMInProgress(roomId: string, userId?: string): VerificationRequest | undefined;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#requestVerificationDM}
|
||||
*/
|
||||
requestVerificationDM(userId: string, roomId: string): Promise<VerificationRequest>;
|
||||
/**
|
||||
* Send the verification content to a room
|
||||
* See https://spec.matrix.org/v1.7/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid
|
||||
*
|
||||
* Prefer to use {@link OutgoingRequestProcessor.makeOutgoingRequest} when dealing with {@link RustSdkCryptoJs.RoomMessageRequest}
|
||||
*
|
||||
* @param roomId - the targeted room
|
||||
* @param verificationEventContent - the request body.
|
||||
*
|
||||
* @returns the event id
|
||||
*/
|
||||
private sendVerificationRequestContent;
|
||||
findVerificationRequestDMInProgress(roomId: string): undefined;
|
||||
/**
|
||||
* The verification methods we offer to the other side during an interactive verification.
|
||||
*/
|
||||
@@ -340,107 +242,15 @@ export declare class RustCrypto extends TypedEventEmitter<RustCryptoEvents, Cryp
|
||||
* Implementation of {@link CryptoApi#storeSessionBackupPrivateKey}.
|
||||
*
|
||||
* @param key - the backup decryption key
|
||||
* @param version - the backup version for this key.
|
||||
*/
|
||||
storeSessionBackupPrivateKey(key: Uint8Array, version?: string): Promise<void>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#loadSessionBackupPrivateKeyFromSecretStorage}.
|
||||
*/
|
||||
loadSessionBackupPrivateKeyFromSecretStorage(): Promise<void>;
|
||||
/**
|
||||
* Get the current status of key backup.
|
||||
*
|
||||
* Implementation of {@link CryptoApi#getActiveSessionBackupVersion}.
|
||||
*/
|
||||
getActiveSessionBackupVersion(): Promise<string | null>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#getKeyBackupInfo}.
|
||||
*/
|
||||
getKeyBackupInfo(): Promise<KeyBackupInfo | null>;
|
||||
/**
|
||||
* Determine if a key backup can be trusted.
|
||||
*
|
||||
* Implementation of {@link CryptoApi#isKeyBackupTrusted}.
|
||||
*/
|
||||
isKeyBackupTrusted(info: KeyBackupInfo): Promise<BackupTrustInfo>;
|
||||
/**
|
||||
* Force a re-check of the key backup and enable/disable it as appropriate.
|
||||
*
|
||||
* Implementation of {@link CryptoApi#checkKeyBackupAndEnable}.
|
||||
*/
|
||||
checkKeyBackupAndEnable(): Promise<KeyBackupCheck | null>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#deleteKeyBackupVersion}.
|
||||
*/
|
||||
deleteKeyBackupVersion(version: string): Promise<void>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#resetKeyBackup}.
|
||||
*/
|
||||
resetKeyBackup(): Promise<void>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#disableKeyStorage}.
|
||||
*/
|
||||
disableKeyStorage(): Promise<void>;
|
||||
/**
|
||||
* Signs the given object with the current device and current identity (if available).
|
||||
* As defined in {@link https://spec.matrix.org/v1.8/appendices/#signing-json | Signing JSON}.
|
||||
*
|
||||
* Helper for {@link RustCrypto#resetKeyBackup}.
|
||||
*
|
||||
* @param obj - The object to sign
|
||||
*/
|
||||
private signObject;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#restoreKeyBackupWithPassphrase}.
|
||||
*/
|
||||
restoreKeyBackupWithPassphrase(passphrase: string, opts?: KeyBackupRestoreOpts): Promise<KeyBackupRestoreResult>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#restoreKeyBackup}.
|
||||
*/
|
||||
restoreKeyBackup(opts?: KeyBackupRestoreOpts): Promise<KeyBackupRestoreResult>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#isDehydrationSupported}.
|
||||
*/
|
||||
isDehydrationSupported(): Promise<boolean>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#startDehydration}.
|
||||
*/
|
||||
startDehydration(opts?: StartDehydrationOpts | boolean): Promise<void>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#importSecretsBundle}.
|
||||
*/
|
||||
importSecretsBundle(secrets: Parameters<NonNullable<CryptoApi["importSecretsBundle"]>>[0]): Promise<void>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#exportSecretsBundle}.
|
||||
*/
|
||||
exportSecretsBundle(): ReturnType<NonNullable<CryptoApi["exportSecretsBundle"]>>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#encryptToDeviceMessages}.
|
||||
*/
|
||||
encryptToDeviceMessages(eventType: string, devices: {
|
||||
userId: string;
|
||||
deviceId: string;
|
||||
}[], payload: ToDevicePayload): Promise<ToDeviceBatch>;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#resetEncryption}.
|
||||
*/
|
||||
resetEncryption(authUploadDeviceSigningKeys: UIAuthCallback<void>): Promise<void>;
|
||||
/**
|
||||
* Removes the secret storage key, default key pointer and all (known) secret storage data
|
||||
* from the user's account data
|
||||
*/
|
||||
private deleteSecretStorage;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#shareRoomHistoryWithUser}.
|
||||
*/
|
||||
shareRoomHistoryWithUser(roomId: string, userId: string): Promise<void>;
|
||||
storeSessionBackupPrivateKey(key: Uint8Array): Promise<void>;
|
||||
/**
|
||||
* Apply sync changes to the olm machine
|
||||
* @param events - the received to-device messages
|
||||
* @param oneTimeKeysCounts - the received one time key counts
|
||||
* @param unusedFallbackKeys - the received unused fallback keys
|
||||
* @param devices - the received device list updates
|
||||
* @returns A list of processed to-device messages.
|
||||
* @returns A list of preprocessed to-device messages.
|
||||
*/
|
||||
private receiveSyncChanges;
|
||||
/** called by the sync loop to preprocess incoming to-device messages
|
||||
@@ -448,7 +258,7 @@ export declare class RustCrypto extends TypedEventEmitter<RustCryptoEvents, Cryp
|
||||
* @param events - the received to-device messages
|
||||
* @returns A list of preprocessed to-device messages.
|
||||
*/
|
||||
preprocessToDeviceMessages(events: IToDeviceEvent[]): Promise<ReceivedToDeviceMessage[]>;
|
||||
preprocessToDeviceMessages(events: IToDeviceEvent[]): Promise<IToDeviceEvent[]>;
|
||||
/** called by the sync loop to process one time key counts and unused fallback keys
|
||||
*
|
||||
* @param oneTimeKeysCounts - the received one time key counts
|
||||
@@ -461,7 +271,7 @@ export declare class RustCrypto extends TypedEventEmitter<RustCryptoEvents, Cryp
|
||||
* @param deviceLists - device_lists field from /sync
|
||||
*/
|
||||
processDeviceLists(deviceLists: IDeviceLists): Promise<void>;
|
||||
/** called by the sync loop on m.room.encryption events
|
||||
/** called by the sync loop on m.room.encrypted events
|
||||
*
|
||||
* @param room - in which the event was received
|
||||
* @param event - encryption event to be processed
|
||||
@@ -469,24 +279,18 @@ export declare class RustCrypto extends TypedEventEmitter<RustCryptoEvents, Cryp
|
||||
onCryptoEvent(room: Room, event: MatrixEvent): Promise<void>;
|
||||
/** called by the sync loop after processing each sync.
|
||||
*
|
||||
* TODO: figure out something equivalent for sliding sync.
|
||||
*
|
||||
* @param syncState - information on the completed sync.
|
||||
*/
|
||||
onSyncCompleted(syncState: OnSyncCompletedData): void;
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#markAllTrackedUsersAsDirty}.
|
||||
*/
|
||||
markAllTrackedUsersAsDirty(): Promise<void>;
|
||||
/**
|
||||
* Handle an incoming m.key.verification.request event, received either in-room or in a to-device message.
|
||||
* Handle an incoming m.key.verification request event
|
||||
*
|
||||
* @param sender - the sender of the event
|
||||
* @param transactionId - the transaction ID for the verification. For to-device messages, this comes from the
|
||||
* content of the message; for in-room messages it is the event ID.
|
||||
* @param content - the content of the event
|
||||
*/
|
||||
private onIncomingKeyVerificationRequest;
|
||||
/** Utility function to wrap a rust `VerificationRequest` with our own {@link VerificationRequest}. */
|
||||
private makeVerificationRequest;
|
||||
/** called by the MatrixClient on a room membership event
|
||||
*
|
||||
* @param event - The matrix event which caused this event to fire.
|
||||
@@ -494,30 +298,6 @@ export declare class RustCrypto extends TypedEventEmitter<RustCryptoEvents, Cryp
|
||||
* @param oldMembership - The previous membership state. Null if it's a new member.
|
||||
*/
|
||||
onRoomMembership(event: MatrixEvent, member: RoomMember, oldMembership?: string): void;
|
||||
/**
|
||||
* Previously, it was sufficient to check if we need to rotate the room key
|
||||
* prior to sending a message. However, the history sharing feature
|
||||
* (MSC4268) breaks this logic:
|
||||
*
|
||||
* 1. Alice sends a message M1 in room X;
|
||||
* 2. Bob invites Charlie, who joins and immediately leaves the room;
|
||||
* 3. Alice sends another message M2 in room X.
|
||||
*
|
||||
* Under the old logic, Alice would not rotate her key after Charlie
|
||||
* leaves, resulting in M2 being encrypted with the same session as M1.
|
||||
* This would allow Charlie to decrypt M2 if he ever gains access to
|
||||
* the event.
|
||||
*
|
||||
* To counter this, we proactively discard any active outgoing Megolm
|
||||
* session when we see an event indicating the user left.
|
||||
*
|
||||
* Note that we have to do this in `onRoomStateEvent` rather than
|
||||
* `onRoomMembership`, because `onRoomMembership` is only called when we see
|
||||
* a *change* in membership. In the case of a gappy sync, we might miss
|
||||
* Charlie's invite and join, and only see the final `leave` event (so his
|
||||
* membership goes from `leave` to `leave`).
|
||||
*/
|
||||
onRoomStateEvent(event: MatrixEvent, _state: RoomState, _prevEvent: MatrixEvent | null): void;
|
||||
/** Callback for OlmMachine.registerRoomKeyUpdatedCallback
|
||||
*
|
||||
* Called by the rust-sdk whenever there is an update to (megolm) room keys. We
|
||||
@@ -528,82 +308,18 @@ export declare class RustCrypto extends TypedEventEmitter<RustCryptoEvents, Cryp
|
||||
*/
|
||||
onRoomKeysUpdated(keys: RustSdkCryptoJs.RoomKeyInfo[]): Promise<void>;
|
||||
private onRoomKeyUpdated;
|
||||
/**
|
||||
* Callback for `OlmMachine.registerRoomKeyWithheldCallback`.
|
||||
*
|
||||
* Called by the rust sdk whenever we are told that a key has been withheld. We see if we had any events that
|
||||
* failed to decrypt for the given session, and update their status if so.
|
||||
*
|
||||
* @param withheld - Details of the withheld sessions.
|
||||
*/
|
||||
onRoomKeysWithheld(withheld: RustSdkCryptoJs.RoomKeyWithheldInfo[]): Promise<void>;
|
||||
/**
|
||||
* Callback for `OlmMachine.registerUserIdentityUpdatedCallback`
|
||||
*
|
||||
* Called by the rust-sdk whenever there is an update to any user's cross-signing status. We re-check their trust
|
||||
* status and emit a `UserTrustStatusChanged` event, as well as a `KeysChanged` if it is our own identity that changed.
|
||||
*
|
||||
* @param userId - the user with the updated identity
|
||||
*/
|
||||
onUserIdentityUpdated(userId: RustSdkCryptoJs.UserId): Promise<void>;
|
||||
/**
|
||||
* Callback for `OlmMachine.registerDevicesUpdatedCallback`
|
||||
*
|
||||
* Called when users' devices have updated. Emits `WillUpdateDevices` and `DevicesUpdated`. In the JavaScript
|
||||
* crypto backend, these events are called at separate times, with `WillUpdateDevices` being emitted just before
|
||||
* the devices are saved, and `DevicesUpdated` being emitted just after. But the OlmMachine only gives us
|
||||
* one event, so we emit both events here.
|
||||
*
|
||||
* @param userIds - an array of user IDs of users whose devices have updated.
|
||||
*/
|
||||
onDevicesUpdated(userIds: string[]): Promise<void>;
|
||||
/**
|
||||
* Handles secret received from the rust secret inbox.
|
||||
*
|
||||
* The gossipped secrets are received using the `m.secret.send` or
|
||||
* `io.element.msc4385.secret.push` event types and are guaranteed to have
|
||||
* been received over a 1-to-1 Olm Session from a verified device.
|
||||
*
|
||||
* The only secret currently handled in this way is `m.megolm_backup.v1`.
|
||||
*
|
||||
* @param name - the secret name
|
||||
* @param value - the secret value
|
||||
*/
|
||||
private handleSecretReceived;
|
||||
/**
|
||||
* Called when a new secret is received in the rust secret inbox.
|
||||
*
|
||||
* Will poll the secret inbox and handle the secrets received.
|
||||
*
|
||||
* @param name - The name of the secret received.
|
||||
*/
|
||||
checkSecrets(name: string): Promise<void>;
|
||||
/**
|
||||
* Handle a live event received via /sync.
|
||||
* See {@link ClientEventHandlerMap#event}
|
||||
*
|
||||
* @param event - live event
|
||||
*/
|
||||
onLiveEventFromSync(event: MatrixEvent): Promise<void>;
|
||||
/**
|
||||
* Handle an in-room key verification event.
|
||||
*
|
||||
* @param event - a key validation request event.
|
||||
*/
|
||||
private onKeyVerificationEvent;
|
||||
/**
|
||||
* Returns the cross-signing user identity of the current user.
|
||||
*
|
||||
* Not part of the public crypto-api interface.
|
||||
* Used during migration from legacy js-crypto to update local trust if needed.
|
||||
*/
|
||||
getOwnIdentity(): Promise<RustSdkCryptoJs.OwnUserIdentity | undefined>;
|
||||
/**
|
||||
* Push a secret to all of the current user's verified devices.
|
||||
*/
|
||||
pushSecretToVerifiedDevices(name: string): Promise<void>;
|
||||
private outgoingRequestLoop;
|
||||
}
|
||||
type CryptoEvents = (typeof CryptoEvent)[keyof typeof CryptoEvent];
|
||||
type RustCryptoEvents = Exclude<CryptoEvents, CryptoEvent.LegacyCryptoStoreMigrationProgress>;
|
||||
type RustCryptoEvents = CryptoEvent.VerificationRequestReceived | CryptoEvent.UserTrustStatusChanged;
|
||||
type RustCryptoEventMap = {
|
||||
/**
|
||||
* Fires when a key verification request is received.
|
||||
*/
|
||||
[CryptoEvent.VerificationRequestReceived]: (request: VerificationRequest) => void;
|
||||
/**
|
||||
* Fires when the cross signing keys are imported during {@link CryptoApi#bootstrapCrossSigning}
|
||||
*/
|
||||
[CryptoEvent.UserTrustStatusChanged]: (userId: string, userTrustLevel: UserTrustLevel) => void;
|
||||
};
|
||||
export {};
|
||||
//# sourceMappingURL=rust-crypto.d.ts.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/rust-crypto.d.ts.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/rust-crypto.d.ts.map
generated
vendored
File diff suppressed because one or more lines are too long
1874
node_modules/matrix-js-sdk/lib/rust-crypto/rust-crypto.js
generated
vendored
1874
node_modules/matrix-js-sdk/lib/rust-crypto/rust-crypto.js
generated
vendored
File diff suppressed because it is too large
Load Diff
2
node_modules/matrix-js-sdk/lib/rust-crypto/rust-crypto.js.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/rust-crypto.js.map
generated
vendored
File diff suppressed because one or more lines are too long
15
node_modules/matrix-js-sdk/lib/rust-crypto/secret-storage.d.ts
generated
vendored
15
node_modules/matrix-js-sdk/lib/rust-crypto/secret-storage.d.ts
generated
vendored
@@ -1,6 +1,6 @@
|
||||
import { type SecretStorageKey, type ServerSideSecretStorage } from "../secret-storage.ts";
|
||||
import { ServerSideSecretStorage } from "../secret-storage";
|
||||
/**
|
||||
* Check that the private cross signing keys (master, self signing, user signing) are stored in the secret storage and encrypted with the default secret storage key.
|
||||
* Check that the private cross signing keys (master, self signing, user signing) are stored in the secret storage and encrypted with the same secret storage key.
|
||||
*
|
||||
* @param secretStorage - The secret store using account data
|
||||
* @returns True if the cross-signing keys are all stored and encrypted with the same secret storage key.
|
||||
@@ -8,15 +8,4 @@ import { type SecretStorageKey, type ServerSideSecretStorage } from "../secret-s
|
||||
* @internal
|
||||
*/
|
||||
export declare function secretStorageContainsCrossSigningKeys(secretStorage: ServerSideSecretStorage): Promise<boolean>;
|
||||
/**
|
||||
*
|
||||
* Check that the secret storage can access the given secrets using the default key.
|
||||
*
|
||||
* @param secretStorage - The secret store using account data
|
||||
* @param secretNames - The secret names to check
|
||||
* @returns True if all the given secrets are accessible and encrypted with the given key.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export declare function secretStorageCanAccessSecrets(secretStorage: ServerSideSecretStorage, secretNames: SecretStorageKey[]): Promise<boolean>;
|
||||
//# sourceMappingURL=secret-storage.d.ts.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/secret-storage.d.ts.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/secret-storage.d.ts.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"secret-storage.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/secret-storage.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE3F;;;;;;;GAOG;AACH,wBAAsB,qCAAqC,CAAC,aAAa,EAAE,uBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC,CAMpH;AAED;;;;;;;;;GASG;AACH,wBAAsB,6BAA6B,CAC/C,aAAa,EAAE,uBAAuB,EACtC,WAAW,EAAE,gBAAgB,EAAE,GAChC,OAAO,CAAC,OAAO,CAAC,CAYlB"}
|
||||
{"version":3,"file":"secret-storage.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/secret-storage.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAE5D;;;;;;;GAOG;AACH,wBAAsB,qCAAqC,CAAC,aAAa,EAAE,uBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC,CAiBpH"}
|
||||
44
node_modules/matrix-js-sdk/lib/rust-crypto/secret-storage.js
generated
vendored
44
node_modules/matrix-js-sdk/lib/rust-crypto/secret-storage.js
generated
vendored
@@ -1,3 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.secretStorageContainsCrossSigningKeys = secretStorageContainsCrossSigningKeys;
|
||||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
@@ -15,36 +21,26 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Check that the private cross signing keys (master, self signing, user signing) are stored in the secret storage and encrypted with the default secret storage key.
|
||||
* Check that the private cross signing keys (master, self signing, user signing) are stored in the secret storage and encrypted with the same secret storage key.
|
||||
*
|
||||
* @param secretStorage - The secret store using account data
|
||||
* @returns True if the cross-signing keys are all stored and encrypted with the same secret storage key.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export async function secretStorageContainsCrossSigningKeys(secretStorage) {
|
||||
return secretStorageCanAccessSecrets(secretStorage, ["m.cross_signing.master", "m.cross_signing.user_signing", "m.cross_signing.self_signing"]);
|
||||
}
|
||||
async function secretStorageContainsCrossSigningKeys(secretStorage) {
|
||||
// Check if the master cross-signing key is stored in secret storage
|
||||
const secretStorageMasterKeys = await secretStorage.isStored("m.cross_signing.master");
|
||||
|
||||
/**
|
||||
*
|
||||
* Check that the secret storage can access the given secrets using the default key.
|
||||
*
|
||||
* @param secretStorage - The secret store using account data
|
||||
* @param secretNames - The secret names to check
|
||||
* @returns True if all the given secrets are accessible and encrypted with the given key.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export async function secretStorageCanAccessSecrets(secretStorage, secretNames) {
|
||||
const defaultKeyId = await secretStorage.getDefaultKeyId();
|
||||
if (!defaultKeyId) return false;
|
||||
for (const secretName of secretNames) {
|
||||
// check which keys this particular secret is encrypted with
|
||||
const record = (await secretStorage.isStored(secretName)) || {};
|
||||
// if it's not encrypted with the right key, there is no point continuing
|
||||
if (!(defaultKeyId in record)) return false;
|
||||
}
|
||||
return true;
|
||||
// Master key not stored
|
||||
if (!secretStorageMasterKeys) return false;
|
||||
|
||||
// Get the user signing keys stored into the secret storage
|
||||
const secretStorageUserSigningKeys = (await secretStorage.isStored(`m.cross_signing.user_signing`)) || {};
|
||||
// Get the self signing keys stored into the secret storage
|
||||
const secretStorageSelfSigningKeys = (await secretStorage.isStored(`m.cross_signing.self_signing`)) || {};
|
||||
|
||||
// Check that one of the secret storage keys used to encrypt the master key was also used to encrypt the user-signing and self-signing keys
|
||||
return Object.keys(secretStorageMasterKeys).some(secretStorageKey => secretStorageUserSigningKeys[secretStorageKey] && secretStorageSelfSigningKeys[secretStorageKey]);
|
||||
}
|
||||
//# sourceMappingURL=secret-storage.js.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/secret-storage.js.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/secret-storage.js.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"secret-storage.js","names":[],"sources":["../../src/rust-crypto/secret-storage.ts"],"sourcesContent":["/*\nCopyright 2023 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { type SecretStorageKey, type ServerSideSecretStorage } from \"../secret-storage.ts\";\n\n/**\n * Check that the private cross signing keys (master, self signing, user signing) are stored in the secret storage and encrypted with the default secret storage key.\n *\n * @param secretStorage - The secret store using account data\n * @returns True if the cross-signing keys are all stored and encrypted with the same secret storage key.\n *\n * @internal\n */\nexport async function secretStorageContainsCrossSigningKeys(secretStorage: ServerSideSecretStorage): Promise<boolean> {\n return secretStorageCanAccessSecrets(secretStorage, [\n \"m.cross_signing.master\",\n \"m.cross_signing.user_signing\",\n \"m.cross_signing.self_signing\",\n ]);\n}\n\n/**\n *\n * Check that the secret storage can access the given secrets using the default key.\n *\n * @param secretStorage - The secret store using account data\n * @param secretNames - The secret names to check\n * @returns True if all the given secrets are accessible and encrypted with the given key.\n *\n * @internal\n */\nexport async function secretStorageCanAccessSecrets(\n secretStorage: ServerSideSecretStorage,\n secretNames: SecretStorageKey[],\n): Promise<boolean> {\n const defaultKeyId = await secretStorage.getDefaultKeyId();\n if (!defaultKeyId) return false;\n\n for (const secretName of secretNames) {\n // check which keys this particular secret is encrypted with\n const record = (await secretStorage.isStored(secretName)) || {};\n // if it's not encrypted with the right key, there is no point continuing\n if (!(defaultKeyId in record)) return false;\n }\n\n return true;\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAe,qCAAqC,CAAC,aAAsC,EAAoB;EAClH,OAAO,6BAA6B,CAAC,aAAa,EAAE,CAChD,wBAAwB,EACxB,8BAA8B,EAC9B,8BAA8B,CACjC,CAAC;AACN;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAe,6BAA6B,CAC/C,aAAsC,EACtC,WAA+B,EACf;EAChB,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,eAAe,CAAC,CAAC;EAC1D,IAAI,CAAC,YAAY,EAAE,OAAO,KAAK;EAE/B,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE;IAClC;IACA,MAAM,MAAM,GAAG,CAAC,MAAM,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/D;IACA,IAAI,EAAE,YAAY,IAAI,MAAM,CAAC,EAAE,OAAO,KAAK;EAC/C;EAEA,OAAO,IAAI;AACf","ignoreList":[]}
|
||||
{"version":3,"file":"secret-storage.js","names":["secretStorageContainsCrossSigningKeys","secretStorage","secretStorageMasterKeys","isStored","secretStorageUserSigningKeys","secretStorageSelfSigningKeys","Object","keys","some","secretStorageKey"],"sources":["../../src/rust-crypto/secret-storage.ts"],"sourcesContent":["/*\nCopyright 2023 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { ServerSideSecretStorage } from \"../secret-storage\";\n\n/**\n * Check that the private cross signing keys (master, self signing, user signing) are stored in the secret storage and encrypted with the same secret storage key.\n *\n * @param secretStorage - The secret store using account data\n * @returns True if the cross-signing keys are all stored and encrypted with the same secret storage key.\n *\n * @internal\n */\nexport async function secretStorageContainsCrossSigningKeys(secretStorage: ServerSideSecretStorage): Promise<boolean> {\n // Check if the master cross-signing key is stored in secret storage\n const secretStorageMasterKeys = await secretStorage.isStored(\"m.cross_signing.master\");\n\n // Master key not stored\n if (!secretStorageMasterKeys) return false;\n\n // Get the user signing keys stored into the secret storage\n const secretStorageUserSigningKeys = (await secretStorage.isStored(`m.cross_signing.user_signing`)) || {};\n // Get the self signing keys stored into the secret storage\n const secretStorageSelfSigningKeys = (await secretStorage.isStored(`m.cross_signing.self_signing`)) || {};\n\n // Check that one of the secret storage keys used to encrypt the master key was also used to encrypt the user-signing and self-signing keys\n return Object.keys(secretStorageMasterKeys).some(\n (secretStorageKey) =>\n secretStorageUserSigningKeys[secretStorageKey] && secretStorageSelfSigningKeys[secretStorageKey],\n );\n}\n"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAeA,qCAAqCA,CAACC,aAAsC,EAAoB;EAClH;EACA,MAAMC,uBAAuB,GAAG,MAAMD,aAAa,CAACE,QAAQ,CAAC,wBAAwB,CAAC;;EAEtF;EACA,IAAI,CAACD,uBAAuB,EAAE,OAAO,KAAK;;EAE1C;EACA,MAAME,4BAA4B,GAAG,CAAC,MAAMH,aAAa,CAACE,QAAQ,CAAE,8BAA6B,CAAC,KAAK,CAAC,CAAC;EACzG;EACA,MAAME,4BAA4B,GAAG,CAAC,MAAMJ,aAAa,CAACE,QAAQ,CAAE,8BAA6B,CAAC,KAAK,CAAC,CAAC;;EAEzG;EACA,OAAOG,MAAM,CAACC,IAAI,CAACL,uBAAuB,CAAC,CAACM,IAAI,CAC3CC,gBAAgB,IACbL,4BAA4B,CAACK,gBAAgB,CAAC,IAAIJ,4BAA4B,CAACI,gBAAgB,CACvG,CAAC;AACL"}
|
||||
62
node_modules/matrix-js-sdk/lib/rust-crypto/verification.d.ts
generated
vendored
62
node_modules/matrix-js-sdk/lib/rust-crypto/verification.d.ts
generated
vendored
@@ -1,17 +1,14 @@
|
||||
/// <reference types="node" />
|
||||
import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { type ShowQrCodeCallbacks, type ShowSasCallbacks, VerificationPhase, type VerificationRequest, VerificationRequestEvent, type VerificationRequestEventHandlerMap, type Verifier, VerifierEvent, type VerifierEventHandlerMap } from "../crypto-api/verification.ts";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
|
||||
import { type OutgoingRequestProcessor } from "./OutgoingRequestProcessor.ts";
|
||||
import { type MatrixEvent } from "../models/event.ts";
|
||||
import type { Logger } from "../logger.ts";
|
||||
import { ShowQrCodeCallbacks, ShowSasCallbacks, VerificationPhase, VerificationRequest, VerificationRequestEvent, VerificationRequestEventHandlerMap, Verifier, VerifierEvent, VerifierEventHandlerMap } from "../crypto-api/verification";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter";
|
||||
import { OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
|
||||
/**
|
||||
* An incoming, or outgoing, request to verify a user or a device via cross-signing.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export declare class RustVerificationRequest extends TypedEventEmitter<VerificationRequestEvent, VerificationRequestEventHandlerMap> implements VerificationRequest {
|
||||
private readonly logger;
|
||||
private readonly olmMachine;
|
||||
private readonly inner;
|
||||
private readonly outgoingRequestProcessor;
|
||||
private readonly supportedVerificationMethods;
|
||||
@@ -25,17 +22,11 @@ export declare class RustVerificationRequest extends TypedEventEmitter<Verificat
|
||||
/**
|
||||
* Construct a new RustVerificationRequest to wrap the rust-level `VerificationRequest`.
|
||||
*
|
||||
* @param logger - A logger instance which will be used to log events.
|
||||
* @param olmMachine - The `OlmMachine` from the underlying rust crypto sdk.
|
||||
* @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.
|
||||
* @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
|
||||
*/
|
||||
constructor(logger: Logger, olmMachine: RustSdkCryptoJs.OlmMachine, inner: RustSdkCryptoJs.VerificationRequest, outgoingRequestProcessor: OutgoingRequestProcessor, supportedVerificationMethods: string[]);
|
||||
/**
|
||||
* Hook which is called when the underlying rust class notifies us that there has been a change.
|
||||
*/
|
||||
private onChange;
|
||||
constructor(inner: RustSdkCryptoJs.VerificationRequest, outgoingRequestProcessor: OutgoingRequestProcessor, supportedVerificationMethods: string[]);
|
||||
private setVerifier;
|
||||
/**
|
||||
* Unique ID for this verification request.
|
||||
@@ -60,8 +51,6 @@ export declare class RustVerificationRequest extends TypedEventEmitter<Verificat
|
||||
get otherUserId(): string;
|
||||
/** For verifications via to-device messages: the ID of the other device. Otherwise, undefined. */
|
||||
get otherDeviceId(): string | undefined;
|
||||
/** Get the other device involved in the verification, if it is known */
|
||||
private getOtherDevice;
|
||||
/** True if the other party in this request is one of this user's own devices. */
|
||||
get isSelfVerification(): boolean;
|
||||
/** current phase of the request. */
|
||||
@@ -153,7 +142,7 @@ export declare class RustVerificationRequest extends TypedEventEmitter<Verificat
|
||||
* @param qrCodeData - the decoded QR code.
|
||||
* @returns A verifier; call `.verify()` on it to wait for the other side to complete the verification flow.
|
||||
*/
|
||||
scanQRCode(uint8Array: Uint8ClampedArray): Promise<Verifier>;
|
||||
scanQRCode(uint8Array: Uint8Array): Promise<Verifier>;
|
||||
/**
|
||||
* The verifier which is doing the actual verification, once the method has been established.
|
||||
* Only defined when the `phase` is Started.
|
||||
@@ -162,13 +151,13 @@ export declare class RustVerificationRequest extends TypedEventEmitter<Verificat
|
||||
/**
|
||||
* Stub implementation of {@link Crypto.VerificationRequest#getQRCodeBytes}.
|
||||
*/
|
||||
getQRCodeBytes(): Uint8ClampedArray | undefined;
|
||||
getQRCodeBytes(): Buffer | undefined;
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
generateQRCode(): Promise<Uint8ClampedArray | undefined>;
|
||||
generateQRCode(): Promise<Buffer | undefined>;
|
||||
/**
|
||||
* If this request has been cancelled, the cancellation code (e.g `m.user`) which is responsible for cancelling
|
||||
* this verification.
|
||||
@@ -188,16 +177,15 @@ export declare class RustVerificationRequest extends TypedEventEmitter<Verificat
|
||||
* @internal
|
||||
*/
|
||||
declare abstract class BaseRustVerifer<InnerType extends RustSdkCryptoJs.Qr | RustSdkCryptoJs.Sas> extends TypedEventEmitter<VerifierEvent | VerificationRequestEvent, VerifierEventHandlerMap & VerificationRequestEventHandlerMap> {
|
||||
protected inner: InnerType;
|
||||
protected readonly inner: InnerType;
|
||||
protected readonly outgoingRequestProcessor: OutgoingRequestProcessor;
|
||||
/** A deferred which completes when the verification completes (or rejects when it is cancelled/fails) */
|
||||
protected readonly completionDeferred: PromiseWithResolvers<void>;
|
||||
/** A promise which completes when the verification completes (or rejects when it is cancelled/fails) */
|
||||
protected readonly completionPromise: Promise<void>;
|
||||
constructor(inner: InnerType, outgoingRequestProcessor: OutgoingRequestProcessor);
|
||||
/**
|
||||
* Hook which is called when the underlying rust class notifies us that there has been a change.
|
||||
*
|
||||
* Can be overridden by subclasses to see if we can notify the application about an update. The overriding method
|
||||
* must call `super.onChange()`.
|
||||
* Can be overridden by subclasses to see if we can notify the application about an update.
|
||||
*/
|
||||
protected onChange(): void;
|
||||
/**
|
||||
@@ -273,10 +261,6 @@ export declare class RustSASVerifier extends BaseRustVerifer<RustSdkCryptoJs.Sas
|
||||
* or times out.
|
||||
*/
|
||||
verify(): Promise<void>;
|
||||
/**
|
||||
* Send the accept or start event, if it hasn't already been sent
|
||||
*/
|
||||
private sendAccept;
|
||||
/** if we can now show the callbacks, do so */
|
||||
protected onChange(): void;
|
||||
/**
|
||||
@@ -290,13 +274,6 @@ export declare class RustSASVerifier extends BaseRustVerifer<RustSdkCryptoJs.Sas
|
||||
* the SAS matches.
|
||||
*/
|
||||
getShowSasCallbacks(): ShowSasCallbacks | null;
|
||||
/**
|
||||
* Replace the inner Rust verifier with a different one.
|
||||
*
|
||||
* @param inner - the new Rust verifier
|
||||
* @internal
|
||||
*/
|
||||
replaceInner(inner: RustSdkCryptoJs.Sas): void;
|
||||
}
|
||||
/**
|
||||
* Convert a specced verification method identifier into a rust-side `VerificationMethod`.
|
||||
@@ -308,14 +285,5 @@ export declare class RustSASVerifier extends BaseRustVerifer<RustSdkCryptoJs.Sas
|
||||
* @internal
|
||||
*/
|
||||
export declare function verificationMethodIdentifierToMethod(method: string): RustSdkCryptoJs.VerificationMethod;
|
||||
/**
|
||||
* Return true if the event's type matches that of an in-room verification event
|
||||
*
|
||||
* @param event - MatrixEvent
|
||||
* @returns
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export declare function isVerificationEvent(event: MatrixEvent): boolean;
|
||||
export {};
|
||||
//# sourceMappingURL=verification.d.ts.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/verification.d.ts.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/verification.d.ts.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"verification.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/verification.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,eAAe,MAAM,oCAAoC,CAAC;AAGtE,OAAO,EAEH,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,wBAAwB,EACxB,KAAK,kCAAkC,EACvC,KAAK,QAAQ,EACb,aAAa,EACb,KAAK,uBAAuB,EAC/B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAE9E,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C;;;;GAIG;AACH,qBAAa,uBACT,SAAQ,iBAAiB,CAAC,wBAAwB,EAAE,kCAAkC,CACtF,YAAW,mBAAmB;IAuB1B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,wBAAwB;IACzC,OAAO,CAAC,QAAQ,CAAC,4BAA4B;IAzBjD,+FAA+F;IAC/F,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+E;IAEzG,4EAA4E;IAC5E,OAAO,CAAC,UAAU,CAAS;IAE3B,mFAAmF;IACnF,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,SAAS,CAAmD;IAEpE;;;;;;;;OAQG;IACH,YACqB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,eAAe,CAAC,UAAU,EACtC,KAAK,EAAE,eAAe,CAAC,mBAAmB,EAC1C,wBAAwB,EAAE,wBAAwB,EAClD,4BAA4B,EAAE,MAAM,EAAE,EAa1D;IAED;;OAEG;IACH,OAAO,CAAC,QAAQ;IAqBhB,OAAO,CAAC,WAAW;IASnB;;;;OAIG;IACH,IAAW,aAAa,IAAI,MAAM,GAAG,SAAS,CAE7C;IAED;;;;OAIG;IACH,IAAW,MAAM,IAAI,MAAM,GAAG,SAAS,CAEtC;IAED;;;;;OAKG;IACH,IAAW,aAAa,IAAI,OAAO,CAElC;IAED,qDAAqD;IACrD,IAAW,WAAW,IAAI,MAAM,CAE/B;IAED,kGAAkG;IAClG,IAAW,aAAa,IAAI,MAAM,GAAG,SAAS,CAE7C;IAED,wEAAwE;YAC1D,cAAc;IAQ5B,iFAAiF;IACjF,IAAW,kBAAkB,IAAI,OAAO,CAEvC;IAED,oCAAoC;IACpC,IAAW,KAAK,IAAI,iBAAiB,CAwBpC;IAED;;OAEG;IACH,IAAW,OAAO,IAAI,OAAO,CAI5B;IAED;;;OAGG;IACH,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED;;;OAGG;IACH,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED;;;;OAIG;IACH,IAAW,OAAO,IAAI,MAAM,GAAG,IAAI,CAElC;IAED,sGAAsG;IACtG,IAAW,OAAO,IAAI,MAAM,EAAE,CAE7B;IAED,4CAA4C;IAC5C,IAAW,YAAY,IAAI,MAAM,GAAG,IAAI,CAWvC;IAED;;;;;;;;OAQG;IACI,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CASvD;IAED;;;;OAIG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAmBnC;IAED;;;;;;;OAOG;IACU,MAAM,CAAC,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB9E;IAED;;;;;;;;;;;;;;OAcG;IACI,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,QAAQ,CAE3G;IAED;;;;;;OAMG;IACU,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyBhE;IAED;;;;;;;OAOG;IACU,UAAU,CAAC,UAAU,EAAE,iBAAiB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgBxE;IAED;;;OAGG;IACH,IAAW,QAAQ,IAAI,QAAQ,GAAG,SAAS,CAQ1C;IAED;;OAEG;IACI,cAAc,IAAI,iBAAiB,GAAG,SAAS,CAErD;IAED;;;;OAIG;IACU,cAAc,IAAI,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAWpE;IAED;;;OAGG;IACH,IAAW,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAE3C;IAED;;;;OAIG;IACH,IAAW,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAShD;CACJ;AAED;;;;;GAKG;AACH,uBAAe,eAAe,CAAC,SAAS,SAAS,eAAe,CAAC,EAAE,GAAG,eAAe,CAAC,GAAG,CAAE,SAAQ,iBAAiB,CAChH,aAAa,GAAG,wBAAwB,EACxC,uBAAuB,GAAG,kCAAkC,CAC/D;IAKO,SAAS,CAAC,KAAK,EAAE,SAAS;IAC1B,SAAS,CAAC,QAAQ,CAAC,wBAAwB,EAAE,wBAAwB;IALzE,yGAAyG;IACzG,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAE,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAElE,YACc,KAAK,EAAE,SAAS,EACP,wBAAwB,EAAE,wBAAwB,EAaxE;IAED;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,IAAI,IAAI,CAezB;IAED;;OAEG;IACH,IAAW,gBAAgB,IAAI,OAAO,CAErC;IAED;;OAEG;IACH,IAAW,MAAM,IAAI,MAAM,CAE1B;IAED;;;;;;;OAOG;IACI,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAM7B;IAED;;;;;OAKG;IACI,mBAAmB,IAAI,gBAAgB,GAAG,IAAI,CAEpD;IAED;;;;;OAKG;IACI,6BAA6B,IAAI,mBAAmB,GAAG,IAAI,CAEjE;CACJ;AAED,uEAAuE;AACvE,qBAAa,kBAAmB,SAAQ,eAAe,CAAC,eAAe,CAAC,EAAE,CAAE,YAAW,QAAQ;IAC3F,OAAO,CAAC,SAAS,CAAoC;IAErD,YAAmB,KAAK,EAAE,eAAe,CAAC,EAAE,EAAE,wBAAwB,EAAE,wBAAwB,EAE/F;IAED,SAAS,CAAC,QAAQ,IAAI,IAAI,CAazB;IAED;;;;;OAKG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAQnC;IAED;;;;OAIG;IACH,IAAW,iBAAiB,IAAI,iBAAiB,CA2BhD;IAED;;;;;OAKG;IACI,6BAA6B,IAAI,mBAAmB,GAAG,IAAI,CAEjE;YAEa,eAAe;CAMhC;AAED,oEAAoE;AACpE,qBAAa,eAAgB,SAAQ,eAAe,CAAC,eAAe,CAAC,GAAG,CAAE,YAAW,QAAQ;IACzF,OAAO,CAAC,SAAS,CAAiC;IAElD,YACI,KAAK,EAAE,eAAe,CAAC,GAAG,EAC1B,oBAAoB,EAAE,uBAAuB,EAC7C,wBAAwB,EAAE,wBAAwB,EAGrD;IAED;;;;;;;;OAQG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAGnC;IAED;;OAEG;YACW,UAAU;IAOxB,8CAA8C;IAC9C,SAAS,CAAC,QAAQ,IAAI,IAAI,CA0CzB;IAED;;OAEG;IACH,IAAW,iBAAiB,IAAI,iBAAiB,CAEhD;IAED;;;;;OAKG;IACI,mBAAmB,IAAI,gBAAgB,GAAG,IAAI,CAEpD;IAED;;;;;OAKG;IACI,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,GAAG,IAAI,CAcpD;CACJ;AAUD;;;;;;;;GAQG;AACH,wBAAgB,oCAAoC,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAAC,kBAAkB,CAMvG;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAe/D"}
|
||||
{"version":3,"file":"verification.d.ts","sourceRoot":"","sources":["../../src/rust-crypto/verification.ts"],"names":[],"mappings":";AAgBA,OAAO,KAAK,eAAe,MAAM,oCAAoC,CAAC;AAGtE,OAAO,EACH,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,kCAAkC,EAClC,QAAQ,EACR,aAAa,EACb,uBAAuB,EAC1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAmB,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAGvF;;;;GAIG;AACH,qBAAa,uBACT,SAAQ,iBAAiB,CAAC,wBAAwB,EAAE,kCAAkC,CACtF,YAAW,mBAAmB;IAqB1B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,wBAAwB;IACzC,OAAO,CAAC,QAAQ,CAAC,4BAA4B;IArBjD,+FAA+F;IAC/F,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+E;IAEzG,4EAA4E;IAC5E,OAAO,CAAC,UAAU,CAAS;IAE3B,mFAAmF;IACnF,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,SAAS,CAAmD;IAEpE;;;;;;OAMG;gBAEkB,KAAK,EAAE,eAAe,CAAC,mBAAmB,EAC1C,wBAAwB,EAAE,wBAAwB,EAClD,4BAA4B,EAAE,MAAM,EAAE;IAwB3D,OAAO,CAAC,WAAW;IASnB;;;;OAIG;IACH,IAAW,aAAa,IAAI,MAAM,GAAG,SAAS,CAE7C;IAED;;;;OAIG;IACH,IAAW,MAAM,IAAI,MAAM,GAAG,SAAS,CAEtC;IAED;;;;;OAKG;IACH,IAAW,aAAa,IAAI,OAAO,CAElC;IAED,qDAAqD;IACrD,IAAW,WAAW,IAAI,MAAM,CAE/B;IAED,kGAAkG;IAClG,IAAW,aAAa,IAAI,MAAM,GAAG,SAAS,CAE7C;IAED,iFAAiF;IACjF,IAAW,kBAAkB,IAAI,OAAO,CAEvC;IAED,oCAAoC;IACpC,IAAW,KAAK,IAAI,iBAAiB,CAwBpC;IAED;;OAEG;IACH,IAAW,OAAO,IAAI,OAAO,CAI5B;IAED;;;OAGG;IACH,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED;;;OAGG;IACH,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED;;;;OAIG;IACH,IAAW,OAAO,IAAI,MAAM,GAAG,IAAI,CAElC;IAED,sGAAsG;IACtG,IAAW,OAAO,IAAI,MAAM,EAAE,CAE7B;IAED,4CAA4C;IAC5C,IAAW,YAAY,IAAI,MAAM,GAAG,IAAI,CAWvC;IAED;;;;;;;;OAQG;IACI,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAWxD;;;;OAIG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAqBpC;;;;;;;OAOG;IACU,MAAM,CAAC,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB/E;;;;;;;;;;;;;;OAcG;IACI,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,QAAQ;IAI5G;;;;;;OAMG;IACU,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAsBjE;;;;;;;OAOG;IACU,UAAU,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;IAkBlE;;;OAGG;IACH,IAAW,QAAQ,IAAI,QAAQ,GAAG,SAAS,CAQ1C;IAED;;OAEG;IACI,cAAc,IAAI,MAAM,GAAG,SAAS;IAI3C;;;;OAIG;IACU,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAK1D;;;OAGG;IACH,IAAW,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAE3C;IAED;;;;OAIG;IACH,IAAW,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAEhD;CACJ;AAED;;;;;GAKG;AACH,uBAAe,eAAe,CAAC,SAAS,SAAS,eAAe,CAAC,EAAE,GAAG,eAAe,CAAC,GAAG,CAAE,SAAQ,iBAAiB,CAChH,aAAa,GAAG,wBAAwB,EACxC,uBAAuB,GAAG,kCAAkC,CAC/D;IAKO,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS;IACnC,SAAS,CAAC,QAAQ,CAAC,wBAAwB,EAAE,wBAAwB;IALzE,wGAAwG;IACxG,SAAS,CAAC,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;gBAG7B,KAAK,EAAE,SAAS,EAChB,wBAAwB,EAAE,wBAAwB;IA6BzE;;;;OAIG;IACH,SAAS,CAAC,QAAQ,IAAI,IAAI;IAE1B;;OAEG;IACH,IAAW,gBAAgB,IAAI,OAAO,CAErC;IAED;;OAEG;IACH,IAAW,MAAM,IAAI,MAAM,CAE1B;IAED;;;;;;;OAOG;IACI,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI;IAQ9B;;;;;OAKG;IACI,mBAAmB,IAAI,gBAAgB,GAAG,IAAI;IAIrD;;;;;OAKG;IACI,6BAA6B,IAAI,mBAAmB,GAAG,IAAI;CAGrE;AAED,uEAAuE;AACvE,qBAAa,kBAAmB,SAAQ,eAAe,CAAC,eAAe,CAAC,EAAE,CAAE,YAAW,QAAQ;IAC3F,OAAO,CAAC,SAAS,CAAoC;gBAElC,KAAK,EAAE,eAAe,CAAC,EAAE,EAAE,wBAAwB,EAAE,wBAAwB;IAIhG,SAAS,CAAC,QAAQ,IAAI,IAAI;IAW1B;;;;;OAKG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAUpC;;;;OAIG;IACH,IAAW,iBAAiB,IAAI,iBAAiB,CAuBhD;IAED;;;;;OAKG;IACI,6BAA6B,IAAI,mBAAmB,GAAG,IAAI;YAIpD,eAAe;CAMhC;AAED,oEAAoE;AACpE,qBAAa,eAAgB,SAAQ,eAAe,CAAC,eAAe,CAAC,GAAG,CAAE,YAAW,QAAQ;IACzF,OAAO,CAAC,SAAS,CAAiC;gBAG9C,KAAK,EAAE,eAAe,CAAC,GAAG,EAC1B,oBAAoB,EAAE,uBAAuB,EAC7C,wBAAwB,EAAE,wBAAwB;IAKtD;;;;;;;;OAQG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAQpC,8CAA8C;IAC9C,SAAS,CAAC,QAAQ,IAAI,IAAI;IA+B1B;;OAEG;IACH,IAAW,iBAAiB,IAAI,iBAAiB,CAEhD;IAED;;;;;OAKG;IACI,mBAAmB,IAAI,gBAAgB,GAAG,IAAI;CAGxD;AAUD;;;;;;;;GAQG;AACH,wBAAgB,oCAAoC,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAAC,kBAAkB,CAMvG"}
|
||||
346
node_modules/matrix-js-sdk/lib/rust-crypto/verification.js
generated
vendored
346
node_modules/matrix-js-sdk/lib/rust-crypto/verification.js
generated
vendored
@@ -1,4 +1,18 @@
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
"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; }
|
||||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
@@ -15,83 +29,55 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { QrState } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import { VerificationPhase, VerificationRequestEvent, VerifierEvent } from "../crypto-api/verification.js";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter.js";
|
||||
import { TypedReEmitter } from "../ReEmitter.js";
|
||||
import { EventType, MsgType } from "../@types/event.js";
|
||||
import { VerificationMethod } from "../types.js";
|
||||
/**
|
||||
* An incoming, or outgoing, request to verify a user or a device via cross-signing.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class RustVerificationRequest extends TypedEventEmitter {
|
||||
class RustVerificationRequest extends _typedEventEmitter.TypedEventEmitter {
|
||||
/**
|
||||
* Construct a new RustVerificationRequest to wrap the rust-level `VerificationRequest`.
|
||||
*
|
||||
* @param logger - A logger instance which will be used to log events.
|
||||
* @param olmMachine - The `OlmMachine` from the underlying rust crypto sdk.
|
||||
* @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.
|
||||
* @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
|
||||
*/
|
||||
constructor(logger, olmMachine, inner, outgoingRequestProcessor, supportedVerificationMethods) {
|
||||
constructor(inner, outgoingRequestProcessor, supportedVerificationMethods) {
|
||||
super();
|
||||
/** a reëmitter which relays VerificationRequestEvent.Changed events emitted by the verifier */
|
||||
_defineProperty(this, "reEmitter", void 0);
|
||||
/** Are we in the process of sending an `m.key.verification.ready` event? */
|
||||
_defineProperty(this, "_accepting", false);
|
||||
/** Are we in the process of sending an `m.key.verification.cancellation` event? */
|
||||
_defineProperty(this, "_cancelling", false);
|
||||
_defineProperty(this, "_verifier", void 0);
|
||||
this.logger = logger;
|
||||
this.olmMachine = olmMachine;
|
||||
this.inner = inner;
|
||||
this.outgoingRequestProcessor = outgoingRequestProcessor;
|
||||
this.supportedVerificationMethods = supportedVerificationMethods;
|
||||
this.reEmitter = new TypedReEmitter(this);
|
||||
/** 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();
|
||||
|
||||
// Obviously, the Rust object maintains a reference to the callback function. If the callback function maintains
|
||||
// a reference to the Rust object, then we have a reference cycle which means that `RustVerificationRequest`
|
||||
// will never be garbage-collected, and hence the underlying rust object will never be freed.
|
||||
//
|
||||
// To avoid this reference cycle, use a weak reference in the callback function. If the `RustVerificationRequest`
|
||||
// gets garbage-collected, then there is nothing to update!
|
||||
const weakThis = new WeakRef(this);
|
||||
inner.registerChangesCallback(async () => weakThis.deref()?.onChange());
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook which is called when the underlying rust class notifies us that there has been a change.
|
||||
*/
|
||||
onChange() {
|
||||
const verification = this.inner.getVerification();
|
||||
|
||||
// Set the _verifier object (wrapping the rust `Verification` as a js-sdk Verifier) if:
|
||||
// - we now have a `Verification` where we lacked one before
|
||||
// - we have transitioned from QR to SAS
|
||||
// - we are verifying with SAS, but we need to replace our verifier with a new one because both parties
|
||||
// tried to start verification at the same time, and we lost the tie breaking
|
||||
if (verification instanceof RustSdkCryptoJs.Sas) {
|
||||
if (this._verifier === undefined || this._verifier instanceof RustQrCodeVerifier) {
|
||||
this.setVerifier(new RustSASVerifier(verification, this, this.outgoingRequestProcessor));
|
||||
} else if (this._verifier instanceof RustSASVerifier) {
|
||||
this._verifier.replaceInner(verification);
|
||||
// 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));
|
||||
}
|
||||
} else if (verification instanceof RustSdkCryptoJs.Qr && this._verifier === undefined) {
|
||||
this.setVerifier(new RustQrCodeVerifier(verification, this.outgoingRequestProcessor));
|
||||
}
|
||||
this.emit(VerificationRequestEvent.Change);
|
||||
this.emit(_verification.VerificationRequestEvent.Change);
|
||||
};
|
||||
inner.registerChangesCallback(onChange);
|
||||
}
|
||||
setVerifier(verifier) {
|
||||
// if we already have a verifier, unsubscribe from its events
|
||||
if (this._verifier) {
|
||||
this.reEmitter.stopReEmitting(this._verifier, [VerificationRequestEvent.Change]);
|
||||
this.reEmitter.stopReEmitting(this._verifier, [_verification.VerificationRequestEvent.Change]);
|
||||
}
|
||||
this._verifier = verifier;
|
||||
this.reEmitter.reEmit(this._verifier, [VerificationRequestEvent.Change]);
|
||||
this.reEmitter.reEmit(this._verifier, [_verification.VerificationRequestEvent.Change]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +95,8 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
* For to-device verifications, `undefined`.
|
||||
*/
|
||||
get roomId() {
|
||||
return this.inner.roomId?.toString();
|
||||
var _this$inner$roomId;
|
||||
return (_this$inner$roomId = this.inner.roomId) === null || _this$inner$roomId === void 0 ? void 0 : _this$inner$roomId.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,16 +116,8 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
|
||||
/** For verifications via to-device messages: the ID of the other device. Otherwise, undefined. */
|
||||
get otherDeviceId() {
|
||||
return this.inner.otherDeviceId?.toString();
|
||||
}
|
||||
|
||||
/** Get the other device involved in the verification, if it is known */
|
||||
async getOtherDevice() {
|
||||
const otherDeviceId = this.inner.otherDeviceId;
|
||||
if (!otherDeviceId) {
|
||||
return undefined;
|
||||
}
|
||||
return await this.olmMachine.getDevice(this.inner.otherUserId, otherDeviceId, 5);
|
||||
var _this$inner$otherDevi;
|
||||
return (_this$inner$otherDevi = this.inner.otherDeviceId) === null || _this$inner$otherDevi === void 0 ? void 0 : _this$inner$otherDevi.toString();
|
||||
}
|
||||
|
||||
/** True if the other party in this request is one of this user's own devices. */
|
||||
@@ -152,11 +131,11 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
switch (phase) {
|
||||
case RustSdkCryptoJs.VerificationRequestPhase.Created:
|
||||
case RustSdkCryptoJs.VerificationRequestPhase.Requested:
|
||||
return VerificationPhase.Requested;
|
||||
return _verification.VerificationPhase.Requested;
|
||||
case RustSdkCryptoJs.VerificationRequestPhase.Ready:
|
||||
// if we're still sending the `m.key.verification.ready`, that counts as "Requested" in the js-sdk's
|
||||
// parlance.
|
||||
return this._accepting ? VerificationPhase.Requested : VerificationPhase.Ready;
|
||||
return this._accepting ? _verification.VerificationPhase.Requested : _verification.VerificationPhase.Ready;
|
||||
case RustSdkCryptoJs.VerificationRequestPhase.Transitioned:
|
||||
if (!this._verifier) {
|
||||
// this shouldn't happen, because the onChange handler should have created a _verifier.
|
||||
@@ -164,9 +143,9 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
}
|
||||
return this._verifier.verificationPhase;
|
||||
case RustSdkCryptoJs.VerificationRequestPhase.Done:
|
||||
return VerificationPhase.Done;
|
||||
return _verification.VerificationPhase.Done;
|
||||
case RustSdkCryptoJs.VerificationRequestPhase.Cancelled:
|
||||
return VerificationPhase.Cancelled;
|
||||
return _verification.VerificationPhase.Cancelled;
|
||||
}
|
||||
throw new Error(`Unknown verification phase ${phase}`);
|
||||
}
|
||||
@@ -177,7 +156,7 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
get pending() {
|
||||
if (this.inner.isPassive()) return false;
|
||||
const phase = this.phase;
|
||||
return phase !== VerificationPhase.Done && phase !== VerificationPhase.Cancelled;
|
||||
return phase !== _verification.VerificationPhase.Done && phase !== _verification.VerificationPhase.Cancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,12 +191,12 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
|
||||
/** the method picked in the .start event */
|
||||
get chosenMethod() {
|
||||
if (this.phase !== VerificationPhase.Started) return null;
|
||||
if (this.phase !== _verification.VerificationPhase.Started) return null;
|
||||
const verification = this.inner.getVerification();
|
||||
if (verification instanceof RustSdkCryptoJs.Sas) {
|
||||
return VerificationMethod.Sas;
|
||||
return "m.sas.v1";
|
||||
} else if (verification instanceof RustSdkCryptoJs.Qr) {
|
||||
return VerificationMethod.Reciprocate;
|
||||
return "m.reciprocate.v1";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@@ -262,7 +241,7 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
}
|
||||
|
||||
// phase may have changed, so emit a 'change' event
|
||||
this.emit(VerificationRequestEvent.Change);
|
||||
this.emit(_verification.VerificationRequestEvent.Change);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,7 +257,6 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
// already cancelling; do nothing
|
||||
return;
|
||||
}
|
||||
this.logger.info("Cancelling verification request with params:", params);
|
||||
this._cancelling = true;
|
||||
try {
|
||||
const req = this.inner.cancel();
|
||||
@@ -317,14 +295,9 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
* @param method - the name of the verification method to use.
|
||||
*/
|
||||
async startVerification(method) {
|
||||
if (method !== VerificationMethod.Sas) {
|
||||
if (method !== "m.sas.v1") {
|
||||
throw new Error(`Unsupported verification method ${method}`);
|
||||
}
|
||||
|
||||
// make sure that we have a list of the other user's devices (workaround https://github.com/matrix-org/matrix-rust-sdk/issues/2896)
|
||||
if (!(await this.getOtherDevice())) {
|
||||
throw new Error("startVerification(): other device is unknown");
|
||||
}
|
||||
const res = await this.inner.startSas();
|
||||
if (res) {
|
||||
const [, req] = res;
|
||||
@@ -347,7 +320,7 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
* @returns A verifier; call `.verify()` on it to wait for the other side to complete the verification flow.
|
||||
*/
|
||||
async scanQRCode(uint8Array) {
|
||||
const scan = RustSdkCryptoJs.QrCodeScan.fromBytes(uint8Array);
|
||||
const scan = RustSdkCryptoJs.QrCodeScan.fromBytes(new Uint8ClampedArray(uint8Array));
|
||||
const verifier = await this.inner.scanQrCode(scan);
|
||||
|
||||
// this should have triggered the onChange callback, and we should now have a verifier
|
||||
@@ -374,7 +347,7 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
//
|
||||
// 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.
|
||||
return this.phase === VerificationPhase.Started ? this._verifier : undefined;
|
||||
return this.phase === _verification.VerificationPhase.Started ? this._verifier : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -390,14 +363,8 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
* Implementation of {@link Crypto.VerificationRequest#generateQRCode}.
|
||||
*/
|
||||
async generateQRCode() {
|
||||
// make sure that we have a list of the other user's devices (workaround https://github.com/matrix-org/matrix-rust-sdk/issues/2896)
|
||||
if (!(await this.getOtherDevice())) {
|
||||
throw new Error("generateQRCode(): other device is unknown");
|
||||
}
|
||||
const innerVerifier = await this.inner.generateQrCode();
|
||||
// If we are unable to generate a QRCode, we return undefined
|
||||
if (!innerVerifier) return;
|
||||
return innerVerifier.toBytes();
|
||||
return Buffer.from(innerVerifier.toBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,7 +372,7 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
* this verification.
|
||||
*/
|
||||
get cancellationCode() {
|
||||
return this.inner.cancelInfo?.cancelCode() ?? null;
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -414,14 +381,7 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
* Only defined when phase is Cancelled
|
||||
*/
|
||||
get cancellingUserId() {
|
||||
const cancelInfo = this.inner.cancelInfo;
|
||||
if (!cancelInfo) {
|
||||
return undefined;
|
||||
} else if (cancelInfo.cancelledbyUs()) {
|
||||
return this.olmMachine.userId.toString();
|
||||
} else {
|
||||
return this.inner.otherUserId.toString();
|
||||
}
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,39 +391,37 @@ export class RustVerificationRequest extends TypedEventEmitter {
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class BaseRustVerifer extends TypedEventEmitter {
|
||||
exports.RustVerificationRequest = RustVerificationRequest;
|
||||
class BaseRustVerifer extends _typedEventEmitter.TypedEventEmitter {
|
||||
constructor(inner, outgoingRequestProcessor) {
|
||||
super();
|
||||
/** A deferred which completes when the verification completes (or rejects when it is cancelled/fails) */
|
||||
_defineProperty(this, "completionDeferred", void 0);
|
||||
this.inner = inner;
|
||||
this.outgoingRequestProcessor = outgoingRequestProcessor;
|
||||
this.completionDeferred = Promise.withResolvers();
|
||||
|
||||
// As with RustVerificationRequest, we need to avoid a reference cycle.
|
||||
// See the comments in RustVerificationRequest.
|
||||
const weakThis = new WeakRef(this);
|
||||
inner.registerChangesCallback(async () => weakThis.deref()?.onChange());
|
||||
|
||||
/** 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);
|
||||
});
|
||||
// stop the runtime complaining if nobody catches a failure
|
||||
this.completionDeferred.promise.catch(() => null);
|
||||
this.completionPromise.catch(() => null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook which is called when the underlying rust class notifies us that there has been a change.
|
||||
*
|
||||
* Can be overridden by subclasses to see if we can notify the application about an update. The overriding method
|
||||
* must call `super.onChange()`.
|
||||
* Can be overridden by subclasses to see if we can notify the application about an update.
|
||||
*/
|
||||
onChange() {
|
||||
if (this.inner.isDone()) {
|
||||
this.completionDeferred.resolve(undefined);
|
||||
} else if (this.inner.isCancelled()) {
|
||||
const cancelInfo = this.inner.cancelInfo();
|
||||
this.completionDeferred.reject(new Error(`Verification cancelled by ${cancelInfo.cancelledbyUs() ? "us" : "them"} with code ${cancelInfo.cancelCode()}: ${cancelInfo.reason()}`));
|
||||
}
|
||||
this.emit(VerificationRequestEvent.Change);
|
||||
}
|
||||
onChange() {}
|
||||
|
||||
/**
|
||||
* Returns true if the verification has been cancelled, either by us or the other side.
|
||||
@@ -517,23 +475,20 @@ class BaseRustVerifer extends TypedEventEmitter {
|
||||
}
|
||||
|
||||
/** A Verifier instance which is used to show and/or scan a QR code. */
|
||||
export class RustQrCodeVerifier extends BaseRustVerifer {
|
||||
class RustQrCodeVerifier extends BaseRustVerifer {
|
||||
constructor(inner, outgoingRequestProcessor) {
|
||||
super(inner, outgoingRequestProcessor);
|
||||
_defineProperty(this, "callbacks", null);
|
||||
(0, _defineProperty2.default)(this, "callbacks", null);
|
||||
}
|
||||
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 = {
|
||||
confirm: () => {
|
||||
this.confirmScanning();
|
||||
},
|
||||
confirm: () => this.confirmScanning(),
|
||||
cancel: () => this.cancel()
|
||||
};
|
||||
}
|
||||
super.onChange();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -546,10 +501,10 @@ export class RustQrCodeVerifier extends BaseRustVerifer {
|
||||
// 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) {
|
||||
this.emit(VerifierEvent.ShowReciprocateQr, this.callbacks);
|
||||
this.emit(_verification.VerifierEvent.ShowReciprocateQr, this.callbacks);
|
||||
}
|
||||
// Nothing to do here but wait.
|
||||
await this.completionDeferred.promise;
|
||||
await this.completionPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -559,28 +514,24 @@ export class RustQrCodeVerifier extends BaseRustVerifer {
|
||||
*/
|
||||
get verificationPhase() {
|
||||
switch (this.inner.state()) {
|
||||
case QrState.Created:
|
||||
case RustSdkCryptoJs.QrState.Created:
|
||||
// we have created a QR for display; neither side has yet sent an `m.key.verification.start`.
|
||||
return VerificationPhase.Ready;
|
||||
case QrState.Scanned:
|
||||
return _verification.VerificationPhase.Ready;
|
||||
case RustSdkCryptoJs.QrState.Scanned:
|
||||
// other side has scanned our QR and sent an `m.key.verification.start` with `m.reciprocate.v1`
|
||||
return VerificationPhase.Started;
|
||||
case QrState.Confirmed:
|
||||
return _verification.VerificationPhase.Started;
|
||||
case RustSdkCryptoJs.QrState.Confirmed:
|
||||
// we have confirmed the other side's scan and sent an `m.key.verification.done`.
|
||||
//
|
||||
// However, the verification is not yet "Done", because we have to wait until we have received the
|
||||
// `m.key.verification.done` from the other side (in particular, we don't mark the device/identity as
|
||||
// verified until that happens). If we return "Done" too soon, we risk the user cancelling the flow.
|
||||
return VerificationPhase.Started;
|
||||
case QrState.Reciprocated:
|
||||
return _verification.VerificationPhase.Done;
|
||||
case RustSdkCryptoJs.QrState.Reciprocated:
|
||||
// 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.
|
||||
return VerificationPhase.Started;
|
||||
case QrState.Done:
|
||||
return VerificationPhase.Done;
|
||||
case QrState.Cancelled:
|
||||
return VerificationPhase.Cancelled;
|
||||
return _verification.VerificationPhase.Started;
|
||||
case RustSdkCryptoJs.QrState.Done:
|
||||
return _verification.VerificationPhase.Done;
|
||||
case RustSdkCryptoJs.QrState.Cancelled:
|
||||
return _verification.VerificationPhase.Cancelled;
|
||||
default:
|
||||
throw new Error(`Unknown qr code state ${this.inner.state()}`);
|
||||
}
|
||||
@@ -604,10 +555,11 @@ export class RustQrCodeVerifier extends BaseRustVerifer {
|
||||
}
|
||||
|
||||
/** A Verifier instance which is used if we are exchanging emojis */
|
||||
export class RustSASVerifier extends BaseRustVerifer {
|
||||
exports.RustQrCodeVerifier = RustQrCodeVerifier;
|
||||
class RustSASVerifier extends BaseRustVerifer {
|
||||
constructor(inner, _verificationRequest, outgoingRequestProcessor) {
|
||||
super(inner, outgoingRequestProcessor);
|
||||
_defineProperty(this, "callbacks", null);
|
||||
(0, _defineProperty2.default)(this, "callbacks", null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -620,38 +572,26 @@ export class RustSASVerifier extends BaseRustVerifer {
|
||||
* or times out.
|
||||
*/
|
||||
async verify() {
|
||||
await this.sendAccept();
|
||||
await this.completionDeferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the accept or start event, if it hasn't already been sent
|
||||
*/
|
||||
async sendAccept() {
|
||||
const req = this.inner.accept();
|
||||
if (req) {
|
||||
await this.outgoingRequestProcessor.makeOutgoingRequest(req);
|
||||
}
|
||||
await this.completionPromise;
|
||||
}
|
||||
|
||||
/** if we can now show the callbacks, do so */
|
||||
onChange() {
|
||||
super.onChange();
|
||||
if (this.callbacks === null) {
|
||||
const emoji = this.inner.emoji();
|
||||
const decimal = this.inner.decimals();
|
||||
if (emoji === undefined && decimal === undefined) {
|
||||
return;
|
||||
}
|
||||
const sas = {};
|
||||
if (emoji) {
|
||||
sas.emoji = emoji.map(e => [e.symbol, e.description]);
|
||||
}
|
||||
if (decimal) {
|
||||
sas.decimal = [decimal[0], decimal[1], decimal[2]];
|
||||
}
|
||||
this.callbacks = {
|
||||
sas,
|
||||
sas: {
|
||||
decimal: decimal,
|
||||
emoji: emoji === null || emoji === void 0 ? void 0 : emoji.map(e => [e.symbol, e.description])
|
||||
},
|
||||
confirm: async () => {
|
||||
const requests = await this.inner.confirm();
|
||||
for (const m of requests) {
|
||||
@@ -659,19 +599,13 @@ export class RustSASVerifier extends BaseRustVerifer {
|
||||
}
|
||||
},
|
||||
mismatch: () => {
|
||||
const request = this.inner.cancelWithCode("m.mismatched_sas");
|
||||
if (request) {
|
||||
void this.outgoingRequestProcessor.makeOutgoingRequest(request);
|
||||
}
|
||||
throw new Error("impl");
|
||||
},
|
||||
cancel: () => {
|
||||
const request = this.inner.cancelWithCode("m.user");
|
||||
if (request) {
|
||||
this.outgoingRequestProcessor.makeOutgoingRequest(request);
|
||||
}
|
||||
throw new Error("impl");
|
||||
}
|
||||
};
|
||||
this.emit(VerifierEvent.ShowSas, this.callbacks);
|
||||
this.emit(_verification.VerifierEvent.ShowSas, this.callbacks);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -679,7 +613,7 @@ export class RustSASVerifier extends BaseRustVerifer {
|
||||
* Calculate an appropriate VerificationPhase for a VerificationRequest where this is the verifier.
|
||||
*/
|
||||
get verificationPhase() {
|
||||
return VerificationPhase.Started;
|
||||
return _verification.VerificationPhase.Started;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -691,36 +625,15 @@ export class RustSASVerifier extends BaseRustVerifer {
|
||||
getShowSasCallbacks() {
|
||||
return this.callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the inner Rust verifier with a different one.
|
||||
*
|
||||
* @param inner - the new Rust verifier
|
||||
* @internal
|
||||
*/
|
||||
replaceInner(inner) {
|
||||
if (this.inner != inner) {
|
||||
this.inner = inner;
|
||||
|
||||
// As with RustVerificationRequest, we need to avoid a reference cycle.
|
||||
// See the comments in RustVerificationRequest.
|
||||
const weakThis = new WeakRef(this);
|
||||
inner.registerChangesCallback(async () => weakThis.deref()?.onChange());
|
||||
|
||||
// replaceInner will only get called if we started the verification at the same time as the other side, and we lost
|
||||
// the tie breaker. So we need to re-accept their verification.
|
||||
this.sendAccept();
|
||||
this.onChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** For each specced verification method, the rust-side `VerificationMethod` corresponding to it */
|
||||
exports.RustSASVerifier = RustSASVerifier;
|
||||
const verificationMethodsByIdentifier = {
|
||||
[VerificationMethod.Sas]: RustSdkCryptoJs.VerificationMethod.SasV1,
|
||||
[VerificationMethod.ScanQrCode]: RustSdkCryptoJs.VerificationMethod.QrCodeScanV1,
|
||||
[VerificationMethod.ShowQrCode]: RustSdkCryptoJs.VerificationMethod.QrCodeShowV1,
|
||||
[VerificationMethod.Reciprocate]: RustSdkCryptoJs.VerificationMethod.ReciprocateV1
|
||||
"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
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -732,36 +645,11 @@ const verificationMethodsByIdentifier = {
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function verificationMethodIdentifierToMethod(method) {
|
||||
function verificationMethodIdentifierToMethod(method) {
|
||||
const meth = verificationMethodsByIdentifier[method];
|
||||
if (meth === undefined) {
|
||||
throw new Error(`Unknown verification method ${method}`);
|
||||
}
|
||||
return meth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the event's type matches that of an in-room verification event
|
||||
*
|
||||
* @param event - MatrixEvent
|
||||
* @returns
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function isVerificationEvent(event) {
|
||||
switch (event.getType()) {
|
||||
case EventType.KeyVerificationCancel:
|
||||
case EventType.KeyVerificationDone:
|
||||
case EventType.KeyVerificationMac:
|
||||
case EventType.KeyVerificationStart:
|
||||
case EventType.KeyVerificationKey:
|
||||
case EventType.KeyVerificationReady:
|
||||
case EventType.KeyVerificationAccept:
|
||||
return true;
|
||||
case EventType.RoomMessage:
|
||||
return event.getContent().msgtype === MsgType.KeyVerificationRequest;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=verification.js.map
|
||||
2
node_modules/matrix-js-sdk/lib/rust-crypto/verification.js.map
generated
vendored
2
node_modules/matrix-js-sdk/lib/rust-crypto/verification.js.map
generated
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user