1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-26 17:03:12 +03:00

Even moar typescriptification

This commit is contained in:
Michael Telatynski
2021-06-24 19:19:41 +01:00
parent b4dc1e1555
commit 40aa6ba96a
13 changed files with 389 additions and 328 deletions

View File

@@ -21,25 +21,23 @@ import { MemoryCryptoStore } from '../../../src/crypto/store/memory-crypto-store
import 'fake-indexeddb/auto';
import 'jest-localstorage-mock';
import {
ROOM_KEY_REQUEST_STATES,
} from '../../../src/crypto/OutgoingRoomKeyRequestManager';
import { RoomKeyRequestState } from '../../../src/crypto/OutgoingRoomKeyRequestManager';
const requests = [
{
requestId: "A",
requestBody: { session_id: "A", room_id: "A" },
state: ROOM_KEY_REQUEST_STATES.SENT,
state: RoomKeyRequestState.Sent,
},
{
requestId: "B",
requestBody: { session_id: "B", room_id: "B" },
state: ROOM_KEY_REQUEST_STATES.SENT,
state: RoomKeyRequestState.Sent,
},
{
requestId: "C",
requestBody: { session_id: "C", room_id: "C" },
state: ROOM_KEY_REQUEST_STATES.UNSENT,
state: RoomKeyRequestState.Unsent,
},
];
@@ -68,9 +66,9 @@ describe.each([
it("getAllOutgoingRoomKeyRequestsByState retrieves all entries in a given state",
async () => {
const r = await
store.getAllOutgoingRoomKeyRequestsByState(ROOM_KEY_REQUEST_STATES.SENT);
store.getAllOutgoingRoomKeyRequestsByState(RoomKeyRequestState.Sent);
expect(r).toHaveLength(2);
requests.filter((e) => e.state == ROOM_KEY_REQUEST_STATES.SENT).forEach((e) => {
requests.filter((e) => e.state === RoomKeyRequestState.Sent).forEach((e) => {
expect(r).toContainEqual(e);
});
});
@@ -78,10 +76,10 @@ describe.each([
test("getOutgoingRoomKeyRequestByState retrieves any entry in a given state",
async () => {
const r =
await store.getOutgoingRoomKeyRequestByState([ROOM_KEY_REQUEST_STATES.SENT]);
await store.getOutgoingRoomKeyRequestByState([RoomKeyRequestState.Sent]);
expect(r).not.toBeNull();
expect(r).not.toBeUndefined();
expect(r.state).toEqual(ROOM_KEY_REQUEST_STATES.SENT);
expect(r.state).toEqual(RoomKeyRequestState.Sent);
expect(requests).toContainEqual(r);
});
});

View File

@@ -59,7 +59,7 @@ import {
IKeyBackupPrepareOpts,
IKeyBackupRestoreOpts,
IKeyBackupRestoreResult,
IKeyBackupVersion,
IKeyBackupInfo,
} from "./crypto/keybackup";
import { IIdentityServerProvider } from "./@types/IIdentityServerProvider";
import type Request from "request";
@@ -116,6 +116,7 @@ import { ReadStream } from "fs";
import { WebStorageSessionStore } from "./store/session/webstorage";
import { BackupManager, IKeyBackupCheck, IPreparedKeyBackupVersion, TrustInfo } from "./crypto/backup";
import { DEFAULT_TREE_POWER_LEVELS_TEMPLATE, MSC3089TreeSpace } from "./models/MSC3089TreeSpace";
import { ISignatures } from "./@types/signed";
export type Store = StubStore | MemoryStore | LocalIndexedDBStoreBackend | RemoteIndexedDBStoreBackend;
export type SessionStore = WebStorageSessionStore;
@@ -375,6 +376,39 @@ interface ICapabilities {
"m.room_versions"?: IRoomVersionsCapability;
}
/* eslint-disable camelcase */
export interface ICrossSigningKey {
keys: { [algorithm: string]: string };
signatures?: ISignatures;
usage: string[];
user_id: string;
}
enum CrossSigningKeyType {
MasterKey = "master_key",
SelfSigningKey = "self_signing_key",
UserSigningKey = "user_signing_key",
}
export type CrossSigningKeys = Record<CrossSigningKeyType, ICrossSigningKey>;
export interface ISignedKey {
keys: Record<string, string>;
signatures: ISignatures;
user_id: string;
algorithms: string[];
device_id: string;
}
/* eslint-enable camelcase */
export type KeySignatures = Record<string, Record<string, ICrossSigningKey | ISignedKey>>;
interface IUploadKeySignaturesResponse {
failures: Record<string, Record<string, {
errcode: string;
error: string;
}>>;
}
/**
* Represents a Matrix Client. Only directly construct this if you want to use
* custom modules. Normally, {@link createClient} should be used
@@ -2086,7 +2120,7 @@ export class MatrixClient extends EventEmitter {
* Get information about the current key backup.
* @returns {Promise} Information object from API or null
*/
public getKeyBackupVersion(): Promise<IKeyBackupVersion> {
public getKeyBackupVersion(): Promise<IKeyBackupInfo> {
return this.http.authedRequest(
undefined, "GET", "/room_keys/version", undefined, undefined,
{ prefix: PREFIX_UNSTABLE },
@@ -2120,7 +2154,7 @@ export class MatrixClient extends EventEmitter {
* ]
* }
*/
public isKeyBackupTrusted(info: IKeyBackupVersion): Promise<TrustInfo> {
public isKeyBackupTrusted(info: IKeyBackupInfo): Promise<TrustInfo> {
return this.crypto.backupManager.isKeyBackupTrusted(info);
}
@@ -2143,7 +2177,7 @@ export class MatrixClient extends EventEmitter {
* @param {object} info Backup information object as returned by getKeyBackupVersion
* @returns {Promise<void>} Resolves when complete.
*/
public enableKeyBackup(info: IKeyBackupVersion): Promise<void> {
public enableKeyBackup(info: IKeyBackupInfo): Promise<void> {
if (!this.crypto) {
throw new Error("End-to-end encryption disabled");
}
@@ -2219,7 +2253,7 @@ export class MatrixClient extends EventEmitter {
* @returns {Promise<object>} Object with 'version' param indicating the version created
*/
// TODO: Fix types
public async createKeyBackupVersion(info: IKeyBackupVersion): Promise<IKeyBackupVersion> {
public async createKeyBackupVersion(info: IKeyBackupInfo): Promise<IKeyBackupInfo> {
if (!this.crypto) {
throw new Error("End-to-end encryption disabled");
}
@@ -2375,7 +2409,7 @@ export class MatrixClient extends EventEmitter {
* @param {object} backupInfo Backup metadata from `checkKeyBackup`
* @return {Promise<Uint8Array>} key backup key
*/
public keyBackupKeyFromPassword(password: string, backupInfo: IKeyBackupVersion): Promise<Uint8Array> {
public keyBackupKeyFromPassword(password: string, backupInfo: IKeyBackupInfo): Promise<Uint8Array> {
return keyFromAuthData(backupInfo.auth_data, password);
}
@@ -2388,7 +2422,7 @@ export class MatrixClient extends EventEmitter {
* @param {string} recoveryKey The recovery key
* @return {Uint8Array} key backup key
*/
public keyBackupKeyFromRecoveryKey(recoveryKey: string): ArrayLike<number> {
public keyBackupKeyFromRecoveryKey(recoveryKey: string): Uint8Array {
return decodeRecoveryKey(recoveryKey);
}
@@ -2410,7 +2444,7 @@ export class MatrixClient extends EventEmitter {
password: string,
targetRoomId: string,
targetSessionId: string,
backupInfo: IKeyBackupVersion,
backupInfo: IKeyBackupInfo,
opts: IKeyBackupRestoreOpts,
): Promise<IKeyBackupRestoreResult> {
const privKey = await keyFromAuthData(backupInfo.auth_data, password);
@@ -2434,7 +2468,7 @@ export class MatrixClient extends EventEmitter {
*/
// TODO: Types
public async restoreKeyBackupWithSecretStorage(
backupInfo: IKeyBackupVersion,
backupInfo: IKeyBackupInfo,
targetRoomId?: string,
targetSessionId?: string,
opts?: IKeyBackupRestoreOpts,
@@ -2474,7 +2508,7 @@ export class MatrixClient extends EventEmitter {
recoveryKey: string,
targetRoomId: string,
targetSessionId: string,
backupInfo: IKeyBackupVersion,
backupInfo: IKeyBackupInfo,
opts: IKeyBackupRestoreOpts,
): Promise<IKeyBackupRestoreResult> {
const privKey = decodeRecoveryKey(recoveryKey);
@@ -2485,7 +2519,7 @@ export class MatrixClient extends EventEmitter {
public async restoreKeyBackupWithCache(
targetRoomId: string,
targetSessionId: string,
backupInfo: IKeyBackupVersion,
backupInfo: IKeyBackupInfo,
opts?: IKeyBackupRestoreOpts,
): Promise<IKeyBackupRestoreResult> {
const privKey = await this.crypto.getSessionBackupPrivateKey();
@@ -2499,7 +2533,7 @@ export class MatrixClient extends EventEmitter {
privKey: ArrayLike<number>,
targetRoomId: string,
targetSessionId: string,
backupInfo: IKeyBackupVersion,
backupInfo: IKeyBackupInfo,
opts?: IKeyBackupRestoreOpts,
): Promise<IKeyBackupRestoreResult> {
const cacheCompleteCallback = opts?.cacheCompleteCallback;
@@ -7090,7 +7124,7 @@ export class MatrixClient extends EventEmitter {
return this.http.authedRequest(callback, "POST", "/keys/upload", undefined, content);
}
public uploadKeySignatures(content: any): Promise<any> { // TODO: Types
public uploadKeySignatures(content: KeySignatures): Promise<IUploadKeySignaturesResponse> {
return this.http.authedRequest(
undefined, "POST", '/keys/signatures/upload', undefined,
content, {
@@ -7189,7 +7223,7 @@ export class MatrixClient extends EventEmitter {
return this.http.authedRequest(undefined, "GET", path, qps, undefined);
}
public uploadDeviceSigningKeys(auth: any, keys: any): Promise<any> { // TODO: Lots of types
public uploadDeviceSigningKeys(auth: any, keys: CrossSigningKeys): Promise<{}> { // TODO: types
const data = Object.assign({}, keys);
if (auth) Object.assign(data, { auth });
return this.http.authedRequest(
@@ -7601,7 +7635,11 @@ export class MatrixClient extends EventEmitter {
* supplied.
* @return {Promise} Resolves to the result object
*/
public sendToDevice(eventType: string, contentMap: any, txnId?: string): Promise<any> { // TODO: Types
public sendToDevice(
eventType: string,
contentMap: { [userId: string]: { [deviceId: string]: Record<string, any>; } },
txnId?: string,
): Promise<{}> {
const path = utils.encodeUri("/sendToDevice/$eventType/$txnId", {
$eventType: eventType,
$txnId: txnId ? txnId : this.makeTxnId(),

View File

@@ -28,26 +28,27 @@ import { decryptAES, encryptAES } from './aes';
import { PkSigning } from "@matrix-org/olm";
import { DeviceInfo } from "./deviceinfo";
import { SecretStorage } from "./SecretStorage";
import { CryptoStore, MatrixClient } from "../client";
import { CryptoStore, ICrossSigningKey, ISignedKey, MatrixClient } from "../client";
import { OlmDevice } from "./OlmDevice";
import { ICryptoCallbacks } from "../matrix";
import { ISignatures } from "../@types/signed";
const KEY_REQUEST_TIMEOUT_MS = 1000 * 60;
function publicKeyFromKeyInfo(keyInfo: any): any { // TODO types
function publicKeyFromKeyInfo(keyInfo: ICrossSigningKey): string {
// `keys` is an object with { [`ed25519:${pubKey}`]: pubKey }
// We assume only a single key, and we want the bare form without type
// prefix, so we select the values.
return Object.values(keyInfo.keys)[0];
}
interface ICacheCallbacks {
export interface ICacheCallbacks {
getCrossSigningKeyCache?(type: string, expectedPublicKey?: string): Promise<Uint8Array>;
storeCrossSigningKeyCache?(type: string, key: Uint8Array): Promise<void>;
}
export class CrossSigningInfo extends EventEmitter {
public keys: Record<string, any> = {}; // TODO types
public keys: Record<string, ICrossSigningKey> = {};
public firstUse = true;
// This tracks whether we've ever verified this user with any identity.
// When you verify a user, any devices online at the time that receive
@@ -368,8 +369,8 @@ export class CrossSigningInfo extends EventEmitter {
this.keys = {};
}
public setKeys(keys: Record<string, any>): void {
const signingKeys: Record<string, object> = {};
public setKeys(keys: Record<string, ICrossSigningKey>): void {
const signingKeys: Record<string, ICrossSigningKey> = {};
if (keys.master) {
if (keys.master.user_id !== this.userId) {
const error = "Mismatched user ID " + keys.master.user_id +
@@ -448,7 +449,7 @@ export class CrossSigningInfo extends EventEmitter {
}
}
public async signObject<T extends object>(data: T, type: string): Promise<T> {
public async signObject<T extends object>(data: T, type: string): Promise<T & { signatures: ISignatures }> {
if (!this.keys[type]) {
throw new Error(
"Attempted to sign with " + type + " key but no such key present",
@@ -457,13 +458,13 @@ export class CrossSigningInfo extends EventEmitter {
const [pubkey, signing] = await this.getCrossSigningKey(type);
try {
pkSign(data, signing, this.userId, pubkey);
return data;
return data as T & { signatures: ISignatures };
} finally {
signing.free();
}
}
public async signUser(key: CrossSigningInfo): Promise<object> {
public async signUser(key: CrossSigningInfo): Promise<ICrossSigningKey> {
if (!this.keys.user_signing) {
logger.info("No user signing key: not signing user");
return;
@@ -471,7 +472,7 @@ export class CrossSigningInfo extends EventEmitter {
return this.signObject(key.keys.master, "user_signing");
}
public async signDevice(userId: string, device: DeviceInfo): Promise<object> {
public async signDevice(userId: string, device: DeviceInfo): Promise<ISignedKey> {
if (userId !== this.userId) {
throw new Error(
`Trying to sign ${userId}'s device; can only sign our own device`,
@@ -481,7 +482,7 @@ export class CrossSigningInfo extends EventEmitter {
logger.info("No self signing key: not signing device");
return;
}
return this.signObject(
return this.signObject<Omit<ISignedKey, "signatures">>(
{
algorithms: device.algorithms,
keys: device.keys,

View File

@@ -1,11 +1,24 @@
import { logger } from "../logger";
import { MatrixEvent } from "../models/event";
import { EventEmitter } from "events";
import { createCryptoStoreCacheCallbacks } from "./CrossSigning";
import { createCryptoStoreCacheCallbacks, ICacheCallbacks } from "./CrossSigning";
import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store';
import { PREFIX_UNSTABLE } from "../http-api";
import { Crypto } from "./index";
import {
PREFIX_UNSTABLE,
} from "../http-api";
CrossSigningKeys,
ICrossSigningKey,
ICryptoCallbacks,
ISecretStorageKeyInfo,
ISignedKey,
KeySignatures,
} from "../matrix";
import { IKeyBackupInfo } from "./keybackup";
interface ICrossSigningKeys {
authUpload(authData: any): Promise<{}>;
keys: Record<string, ICrossSigningKey>;
}
/**
* Builds an EncryptionSetupOperation by calling any of the add.. methods.
@@ -17,18 +30,23 @@ import {
* more than once.
*/
export class EncryptionSetupBuilder {
public readonly accountDataClientAdapter: AccountDataClientAdapter;
public readonly crossSigningCallbacks: CrossSigningCallbacks;
public readonly ssssCryptoCallbacks: SSSSCryptoCallbacks;
private crossSigningKeys: ICrossSigningKeys = null;
private keySignatures: KeySignatures = null;
private keyBackupInfo: IKeyBackupInfo = null;
private sessionBackupPrivateKey: Uint8Array;
/**
* @param {Object.<String, MatrixEvent>} accountData pre-existing account data, will only be read, not written.
* @param {CryptoCallbacks} delegateCryptoCallbacks crypto callbacks to delegate to if the key isn't in cache yet
*/
constructor(accountData, delegateCryptoCallbacks) {
constructor(accountData: Record<string, MatrixEvent>, delegateCryptoCallbacks: ICryptoCallbacks) {
this.accountDataClientAdapter = new AccountDataClientAdapter(accountData);
this.crossSigningCallbacks = new CrossSigningCallbacks();
this.ssssCryptoCallbacks = new SSSSCryptoCallbacks(delegateCryptoCallbacks);
this._crossSigningKeys = null;
this._keySignatures = null;
this._keyBackupInfo = null;
}
/**
@@ -42,8 +60,8 @@ export class EncryptionSetupBuilder {
* an empty authDict, to obtain the flows.
* @param {Object} keys the new keys
*/
addCrossSigningKeys(authUpload, keys) {
this._crossSigningKeys = { authUpload, keys };
public addCrossSigningKeys(authUpload: ICrossSigningKeys["authUpload"], keys: ICrossSigningKeys["keys"]): void {
this.crossSigningKeys = { authUpload, keys };
}
/**
@@ -54,8 +72,8 @@ export class EncryptionSetupBuilder {
*
* @param {Object} keyBackupInfo as received from/sent to the server
*/
addSessionBackup(keyBackupInfo) {
this._keyBackupInfo = keyBackupInfo;
public addSessionBackup(keyBackupInfo: IKeyBackupInfo): void {
this.keyBackupInfo = keyBackupInfo;
}
/**
@@ -65,8 +83,8 @@ export class EncryptionSetupBuilder {
*
* @param {Uint8Array} privateKey
*/
addSessionBackupPrivateKeyToCache(privateKey) {
this._sessionBackupPrivateKey = privateKey;
public addSessionBackupPrivateKeyToCache(privateKey: Uint8Array): void {
this.sessionBackupPrivateKey = privateKey;
}
/**
@@ -75,14 +93,14 @@ export class EncryptionSetupBuilder {
*
* @param {String} userId
* @param {String} deviceId
* @param {String} signature
* @param {Object} signature
*/
addKeySignature(userId, deviceId, signature) {
if (!this._keySignatures) {
this._keySignatures = {};
public addKeySignature(userId: string, deviceId: string, signature: ISignedKey): void {
if (!this.keySignatures) {
this.keySignatures = {};
}
const userSignatures = this._keySignatures[userId] || {};
this._keySignatures[userId] = userSignatures;
const userSignatures = this.keySignatures[userId] || {};
this.keySignatures[userId] = userSignatures;
userSignatures[deviceId] = signature;
}
@@ -91,7 +109,7 @@ export class EncryptionSetupBuilder {
* @param {Object} content
* @return {Promise}
*/
setAccountData(type, content) {
public setAccountData(type: string, content: object): Promise<void> {
return this.accountDataClientAdapter.setAccountData(type, content);
}
@@ -99,13 +117,13 @@ export class EncryptionSetupBuilder {
* builds the operation containing all the parts that have been added to the builder
* @return {EncryptionSetupOperation}
*/
buildOperation() {
const accountData = this.accountDataClientAdapter._values;
public buildOperation(): EncryptionSetupOperation {
const accountData = this.accountDataClientAdapter.values;
return new EncryptionSetupOperation(
accountData,
this._crossSigningKeys,
this._keyBackupInfo,
this._keySignatures,
this.crossSigningKeys,
this.keyBackupInfo,
this.keySignatures,
);
}
@@ -118,9 +136,9 @@ export class EncryptionSetupBuilder {
* @param {Crypto} crypto
* @return {Promise}
*/
async persist(crypto) {
public async persist(crypto: Crypto): Promise<void> {
// store private keys in cache
if (this._crossSigningKeys) {
if (this.crossSigningKeys) {
const cacheCallbacks = createCryptoStoreCacheCallbacks(crypto.cryptoStore, crypto.olmDevice);
for (const type of ["master", "self_signing", "user_signing"]) {
logger.log(`Cache ${type} cross-signing private key locally`);
@@ -132,13 +150,13 @@ export class EncryptionSetupBuilder {
'readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT],
(txn) => {
crypto.cryptoStore.storeCrossSigningKeys(
txn, this._crossSigningKeys.keys);
txn, this.crossSigningKeys.keys);
},
);
}
// store session backup key in cache
if (this._sessionBackupPrivateKey) {
await crypto.storeSessionBackupPrivateKey(this._sessionBackupPrivateKey);
if (this.sessionBackupPrivateKey) {
await crypto.storeSessionBackupPrivateKey(this.sessionBackupPrivateKey);
}
}
}
@@ -156,58 +174,58 @@ export class EncryptionSetupOperation {
* @param {Object} keyBackupInfo
* @param {Object} keySignatures
*/
constructor(accountData, crossSigningKeys, keyBackupInfo, keySignatures) {
this._accountData = accountData;
this._crossSigningKeys = crossSigningKeys;
this._keyBackupInfo = keyBackupInfo;
this._keySignatures = keySignatures;
}
constructor(
private readonly accountData: Map<string, object>,
private readonly crossSigningKeys: ICrossSigningKeys,
private readonly keyBackupInfo: IKeyBackupInfo,
private readonly keySignatures: KeySignatures,
) {}
/**
* Runs the (remaining part of, in the future) operation by sending requests to the server.
* @param {Crypto} crypto
*/
async apply(crypto) {
public async apply(crypto: Crypto): Promise<void> {
const baseApis = crypto.baseApis;
// upload cross-signing keys
if (this._crossSigningKeys) {
const keys = {};
for (const [name, key] of Object.entries(this._crossSigningKeys.keys)) {
if (this.crossSigningKeys) {
const keys: Partial<CrossSigningKeys> = {};
for (const [name, key] of Object.entries(this.crossSigningKeys.keys)) {
keys[name + "_key"] = key;
}
// We must only call `uploadDeviceSigningKeys` from inside this auth
// helper to ensure we properly handle auth errors.
await this._crossSigningKeys.authUpload(authDict => {
return baseApis.uploadDeviceSigningKeys(authDict, keys);
await this.crossSigningKeys.authUpload(authDict => {
return baseApis.uploadDeviceSigningKeys(authDict, keys as CrossSigningKeys);
});
// pass the new keys to the main instance of our own CrossSigningInfo.
crypto.crossSigningInfo.setKeys(this._crossSigningKeys.keys);
crypto.crossSigningInfo.setKeys(this.crossSigningKeys.keys);
}
// set account data
if (this._accountData) {
for (const [type, content] of this._accountData) {
if (this.accountData) {
for (const [type, content] of this.accountData) {
await baseApis.setAccountData(type, content);
}
}
// upload first cross-signing signatures with the new key
// (e.g. signing our own device)
if (this._keySignatures) {
await baseApis.uploadKeySignatures(this._keySignatures);
if (this.keySignatures) {
await baseApis.uploadKeySignatures(this.keySignatures);
}
// need to create/update key backup info
if (this._keyBackupInfo) {
if (this._keyBackupInfo.version) {
if (this.keyBackupInfo) {
if (this.keyBackupInfo.version) {
// session backup signature
// The backup is trusted because the user provided the private key.
// Sign the backup with the cross signing key so the key backup can
// be trusted via cross-signing.
await baseApis.http.authedRequest(
undefined, "PUT", "/room_keys/version/" + this._keyBackupInfo.version,
undefined, "PUT", "/room_keys/version/" + this.keyBackupInfo.version,
undefined, {
algorithm: this._keyBackupInfo.algorithm,
auth_data: this._keyBackupInfo.auth_data,
algorithm: this.keyBackupInfo.algorithm,
auth_data: this.keyBackupInfo.auth_data,
},
{ prefix: PREFIX_UNSTABLE },
);
@@ -215,7 +233,7 @@ export class EncryptionSetupOperation {
// add new key backup
await baseApis.http.authedRequest(
undefined, "POST", "/room_keys/version",
undefined, this._keyBackupInfo,
undefined, this.keyBackupInfo,
{ prefix: PREFIX_UNSTABLE },
);
}
@@ -228,20 +246,20 @@ export class EncryptionSetupOperation {
* implementing the methods related to account data in MatrixClient
*/
class AccountDataClientAdapter extends EventEmitter {
public readonly values = new Map<string, object>();
/**
* @param {Object.<String, MatrixEvent>} accountData existing account data
* @param {Object.<String, MatrixEvent>} existingValues existing account data
*/
constructor(accountData) {
constructor(private readonly existingValues: Record<string, MatrixEvent>) {
super();
this._existingValues = accountData;
this._values = new Map();
}
/**
* @param {String} type
* @return {Promise<Object>} the content of the account data
*/
getAccountDataFromServer(type) {
public getAccountDataFromServer(type: string): Promise<object> {
return Promise.resolve(this.getAccountData(type));
}
@@ -249,12 +267,12 @@ class AccountDataClientAdapter extends EventEmitter {
* @param {String} type
* @return {Object} the content of the account data
*/
getAccountData(type) {
const modifiedValue = this._values.get(type);
public getAccountData(type: string): object {
const modifiedValue = this.values.get(type);
if (modifiedValue) {
return modifiedValue;
}
const existingValue = this._existingValues[type];
const existingValue = this.existingValues[type];
if (existingValue) {
return existingValue.getContent();
}
@@ -266,9 +284,9 @@ class AccountDataClientAdapter extends EventEmitter {
* @param {Object} content
* @return {Promise}
*/
setAccountData(type, content) {
const lastEvent = this._values.get(type);
this._values.set(type, content);
public setAccountData(type: string, content: object): Promise<void> {
const lastEvent = this.values.get(type);
this.values.set(type, content);
// ensure accountData is emitted on the next tick,
// as SecretStorage listens for it while calling this method
// and it seems to rely on this.
@@ -284,27 +302,25 @@ class AccountDataClientAdapter extends EventEmitter {
* by both cache callbacks (see createCryptoStoreCacheCallbacks) as non-cache callbacks.
* See CrossSigningInfo constructor
*/
class CrossSigningCallbacks {
constructor() {
this.privateKeys = new Map();
}
class CrossSigningCallbacks implements ICryptoCallbacks, ICacheCallbacks {
public readonly privateKeys = new Map<string, Uint8Array>();
// cache callbacks
getCrossSigningKeyCache(type, expectedPublicKey) {
public getCrossSigningKeyCache(type: string, expectedPublicKey: string): Promise<Uint8Array> {
return this.getCrossSigningKey(type, expectedPublicKey);
}
storeCrossSigningKeyCache(type, key) {
public storeCrossSigningKeyCache(type: string, key: Uint8Array): Promise<void> {
this.privateKeys.set(type, key);
return Promise.resolve();
}
// non-cache callbacks
getCrossSigningKey(type, _expectedPubkey) {
public getCrossSigningKey(type: string, expectedPubkey: string): Promise<Uint8Array> {
return Promise.resolve(this.privateKeys.get(type));
}
saveCrossSigningKeys(privateKeys) {
public saveCrossSigningKeys(privateKeys: Record<string, Uint8Array>) {
for (const [type, privateKey] of Object.entries(privateKeys)) {
this.privateKeys.set(type, privateKey);
}
@@ -316,39 +332,36 @@ class CrossSigningCallbacks {
* the SecretStorage crypto callbacks
*/
class SSSSCryptoCallbacks {
constructor(delegateCryptoCallbacks) {
this._privateKeys = new Map();
this._delegateCryptoCallbacks = delegateCryptoCallbacks;
}
private readonly privateKeys = new Map<string, Uint8Array>();
async getSecretStorageKey({ keys }, name) {
constructor(private readonly delegateCryptoCallbacks: ICryptoCallbacks) {}
public async getSecretStorageKey(
{ keys }: { keys: Record<string, object> },
name: string,
): Promise<[string, Uint8Array]> {
for (const keyId of Object.keys(keys)) {
const privateKey = this._privateKeys.get(keyId);
const privateKey = this.privateKeys.get(keyId);
if (privateKey) {
return [keyId, privateKey];
}
}
// if we don't have the key cached yet, ask
// for it to the general crypto callbacks and cache it
if (this._delegateCryptoCallbacks) {
const result = await this._delegateCryptoCallbacks.
if (this.delegateCryptoCallbacks) {
const result = await this.delegateCryptoCallbacks.
getSecretStorageKey({ keys }, name);
if (result) {
const [keyId, privateKey] = result;
this._privateKeys.set(keyId, privateKey);
this.privateKeys.set(keyId, privateKey);
}
return result;
}
}
addPrivateKey(keyId, keyInfo, privKey) {
this._privateKeys.set(keyId, privKey);
public addPrivateKey(keyId: string, keyInfo: ISecretStorageKeyInfo, privKey: Uint8Array): void {
this.privateKeys.set(keyId, privKey);
// Also pass along to application to cache if it wishes
if (
this._delegateCryptoCallbacks &&
this._delegateCryptoCallbacks.cacheSecretStorageKey
) {
this._delegateCryptoCallbacks.cacheSecretStorageKey(keyId, keyInfo, privKey);
}
this.delegateCryptoCallbacks?.cacheSecretStorageKey?.(keyId, keyInfo, privKey);
}
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2017 Vector Creations Ltd
Copyright 2017 - 2021 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.
@@ -15,6 +15,10 @@ limitations under the License.
*/
import { logger } from '../logger';
import {CryptoStore, MatrixClient} from "../client";
import {IRoomKeyRequestBody, IRoomKeyRequestRecipient} from "./index";
import { OutgoingRoomKeyRequest } from './store/base';
import {EventType} from "../@types/event";
/**
* Internal module. Management of outgoing room key requests.
@@ -57,61 +61,58 @@ const SEND_KEY_REQUESTS_DELAY_MS = 500;
*
* @enum {number}
*/
export const ROOM_KEY_REQUEST_STATES = {
export enum RoomKeyRequestState {
/** request not yet sent */
UNSENT: 0,
Unsent,
/** request sent, awaiting reply */
SENT: 1,
Sent,
/** reply received, cancellation not yet sent */
CANCELLATION_PENDING: 2,
CancellationPending,
/**
* Cancellation not yet sent and will transition to UNSENT instead of
* being deleted once the cancellation has been sent.
*/
CANCELLATION_PENDING_AND_WILL_RESEND: 3,
};
CancellationPendingAndWillResend,
}
export class OutgoingRoomKeyRequestManager {
constructor(baseApis, deviceId, cryptoStore) {
this._baseApis = baseApis;
this._deviceId = deviceId;
this._cryptoStore = cryptoStore;
// handle for the delayed call to _sendOutgoingRoomKeyRequests. Non-null
// handle for the delayed call to sendOutgoingRoomKeyRequests. Non-null
// if the callback has been set, or if it is still running.
this._sendOutgoingRoomKeyRequestsTimer = null;
private sendOutgoingRoomKeyRequestsTimer: NodeJS.Timeout = null;
// sanity check to ensure that we don't end up with two concurrent runs
// of _sendOutgoingRoomKeyRequests
this._sendOutgoingRoomKeyRequestsRunning = false;
// of sendOutgoingRoomKeyRequests
private sendOutgoingRoomKeyRequestsRunning = false;
this._clientRunning = false;
}
private clientRunning = false;
constructor(
private readonly baseApis: MatrixClient,
private readonly deviceId: string,
private readonly cryptoStore: CryptoStore,
) {}
/**
* Called when the client is started. Sets background processes running.
*/
start() {
this._clientRunning = true;
public start(): void {
this.clientRunning = true;
}
/**
* Called when the client is stopped. Stops any running background processes.
*/
stop() {
public stop(): void {
logger.log('stopping OutgoingRoomKeyRequestManager');
// stop the timer on the next run
this._clientRunning = false;
this.clientRunning = false;
}
/**
* Send any requests that have been queued
*/
sendQueuedRequests() {
this._startTimer();
public sendQueuedRequests(): void {
this.startTimer();
}
/**
@@ -131,54 +132,58 @@ export class OutgoingRoomKeyRequestManager {
* pending list (or we have established that a similar request already
* exists)
*/
async queueRoomKeyRequest(requestBody, recipients, resend=false) {
const req = await this._cryptoStore.getOutgoingRoomKeyRequest(
public async queueRoomKeyRequest(
requestBody: IRoomKeyRequestBody,
recipients: IRoomKeyRequestRecipient[],
resend = false,
): Promise<void> {
const req = await this.cryptoStore.getOutgoingRoomKeyRequest(
requestBody,
);
if (!req) {
await this._cryptoStore.getOrAddOutgoingRoomKeyRequest({
await this.cryptoStore.getOrAddOutgoingRoomKeyRequest({
requestBody: requestBody,
recipients: recipients,
requestId: this._baseApis.makeTxnId(),
state: ROOM_KEY_REQUEST_STATES.UNSENT,
requestId: this.baseApis.makeTxnId(),
state: RoomKeyRequestState.Unsent,
});
} else {
switch (req.state) {
case ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING_AND_WILL_RESEND:
case ROOM_KEY_REQUEST_STATES.UNSENT:
case RoomKeyRequestState.CancellationPendingAndWillResend:
case RoomKeyRequestState.Unsent:
// nothing to do here, since we're going to send a request anyways
return;
case ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING: {
case RoomKeyRequestState.CancellationPending: {
// existing request is about to be cancelled. If we want to
// resend, then change the state so that it resends after
// cancelling. Otherwise, just cancel the cancellation.
const state = resend ?
ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING_AND_WILL_RESEND :
ROOM_KEY_REQUEST_STATES.SENT;
await this._cryptoStore.updateOutgoingRoomKeyRequest(
req.requestId, ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING, {
RoomKeyRequestState.CancellationPendingAndWillResend :
RoomKeyRequestState.Sent;
await this.cryptoStore.updateOutgoingRoomKeyRequest(
req.requestId, RoomKeyRequestState.CancellationPending, {
state,
cancellationTxnId: this._baseApis.makeTxnId(),
cancellationTxnId: this.baseApis.makeTxnId(),
},
);
break;
}
case ROOM_KEY_REQUEST_STATES.SENT: {
case RoomKeyRequestState.Sent: {
// a request has already been sent. If we don't want to
// resend, then do nothing. If we do want to, then cancel the
// existing request and send a new one.
if (resend) {
const state =
ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING_AND_WILL_RESEND;
RoomKeyRequestState.CancellationPendingAndWillResend;
const updatedReq =
await this._cryptoStore.updateOutgoingRoomKeyRequest(
req.requestId, ROOM_KEY_REQUEST_STATES.SENT, {
await this.cryptoStore.updateOutgoingRoomKeyRequest(
req.requestId, RoomKeyRequestState.Sent, {
state,
cancellationTxnId: this._baseApis.makeTxnId(),
cancellationTxnId: this.baseApis.makeTxnId(),
// need to use a new transaction ID so that
// the request gets sent
requestTxnId: this._baseApis.makeTxnId(),
requestTxnId: this.baseApis.makeTxnId(),
},
);
if (!updatedReq) {
@@ -201,7 +206,7 @@ export class OutgoingRoomKeyRequestManager {
// here, as it will slow down processing of received keys if we
// do.)
try {
await this._sendOutgoingRoomKeyRequestCancellation(
await this.sendOutgoingRoomKeyRequestCancellation(
updatedReq,
true,
);
@@ -232,8 +237,8 @@ export class OutgoingRoomKeyRequestManager {
* @returns {Promise} resolves when the request has been updated in our
* pending list.
*/
cancelRoomKeyRequest(requestBody) {
return this._cryptoStore.getOutgoingRoomKeyRequest(
public cancelRoomKeyRequest(requestBody: IRoomKeyRequestBody): Promise<void> {
return this.cryptoStore.getOutgoingRoomKeyRequest(
requestBody,
).then((req) => {
if (!req) {
@@ -241,12 +246,12 @@ export class OutgoingRoomKeyRequestManager {
return;
}
switch (req.state) {
case ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING:
case ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING_AND_WILL_RESEND:
case RoomKeyRequestState.CancellationPending:
case RoomKeyRequestState.CancellationPendingAndWillResend:
// nothing to do here
return;
case ROOM_KEY_REQUEST_STATES.UNSENT:
case RoomKeyRequestState.Unsent:
// just delete it
// FIXME: ghahah we may have attempted to send it, and
@@ -258,16 +263,16 @@ export class OutgoingRoomKeyRequestManager {
'deleting unnecessary room key request for ' +
stringifyRequestBody(requestBody),
);
return this._cryptoStore.deleteOutgoingRoomKeyRequest(
req.requestId, ROOM_KEY_REQUEST_STATES.UNSENT,
return this.cryptoStore.deleteOutgoingRoomKeyRequest(
req.requestId, RoomKeyRequestState.Unsent,
);
case ROOM_KEY_REQUEST_STATES.SENT: {
case RoomKeyRequestState.Sent: {
// send a cancellation.
return this._cryptoStore.updateOutgoingRoomKeyRequest(
req.requestId, ROOM_KEY_REQUEST_STATES.SENT, {
state: ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING,
cancellationTxnId: this._baseApis.makeTxnId(),
return this.cryptoStore.updateOutgoingRoomKeyRequest(
req.requestId, RoomKeyRequestState.Sent, {
state: RoomKeyRequestState.CancellationPending,
cancellationTxnId: this.baseApis.makeTxnId(),
},
).then((updatedReq) => {
if (!updatedReq) {
@@ -294,14 +299,14 @@ export class OutgoingRoomKeyRequestManager {
// (We also don't want to wait for the response from the server
// here, as it will slow down processing of received keys if we
// do.)
this._sendOutgoingRoomKeyRequestCancellation(
this.sendOutgoingRoomKeyRequestCancellation(
updatedReq,
).catch((e) => {
logger.error(
"Error sending room key request cancellation;"
+ " will retry later.", e,
);
this._startTimer();
this.startTimer();
});
});
}
@@ -320,10 +325,8 @@ export class OutgoingRoomKeyRequestManager {
* @return {Promise} resolves to a list of all the
* {@link module:crypto/store/base~OutgoingRoomKeyRequest}
*/
getOutgoingSentRoomKeyRequest(userId, deviceId) {
return this._cryptoStore.getOutgoingRoomKeyRequestsByTarget(
userId, deviceId, [ROOM_KEY_REQUEST_STATES.SENT],
);
public getOutgoingSentRoomKeyRequest(userId: string, deviceId: string): OutgoingRoomKeyRequest[] {
return this.cryptoStore.getOutgoingRoomKeyRequestsByTarget(userId, deviceId, [RoomKeyRequestState.Sent]);
}
/**
@@ -333,29 +336,27 @@ export class OutgoingRoomKeyRequestManager {
* For example, after initialization or self-verification.
* @return {Promise} An array of `queueRoomKeyRequest` outputs.
*/
async cancelAndResendAllOutgoingRequests() {
const outgoings = await this._cryptoStore.getAllOutgoingRoomKeyRequestsByState(
ROOM_KEY_REQUEST_STATES.SENT,
);
public async cancelAndResendAllOutgoingRequests(): Promise<void[]> {
const outgoings = await this.cryptoStore.getAllOutgoingRoomKeyRequestsByState(RoomKeyRequestState.Sent);
return Promise.all(outgoings.map(({ requestBody, recipients }) =>
this.queueRoomKeyRequest(requestBody, recipients, true)));
}
// start the background timer to send queued requests, if the timer isn't
// already running
_startTimer() {
if (this._sendOutgoingRoomKeyRequestsTimer) {
private startTimer(): void {
if (this.sendOutgoingRoomKeyRequestsTimer) {
return;
}
const startSendingOutgoingRoomKeyRequests = () => {
if (this._sendOutgoingRoomKeyRequestsRunning) {
if (this.sendOutgoingRoomKeyRequestsRunning) {
throw new Error("RoomKeyRequestSend already in progress!");
}
this._sendOutgoingRoomKeyRequestsRunning = true;
this.sendOutgoingRoomKeyRequestsRunning = true;
this._sendOutgoingRoomKeyRequests().finally(() => {
this._sendOutgoingRoomKeyRequestsRunning = false;
this.sendOutgoingRoomKeyRequests().finally(() => {
this.sendOutgoingRoomKeyRequestsRunning = false;
}).catch((e) => {
// this should only happen if there is an indexeddb error,
// in which case we're a bit stuffed anyway.
@@ -365,7 +366,7 @@ export class OutgoingRoomKeyRequestManager {
});
};
this._sendOutgoingRoomKeyRequestsTimer = global.setTimeout(
this.sendOutgoingRoomKeyRequestsTimer = global.setTimeout(
startSendingOutgoingRoomKeyRequests,
SEND_KEY_REQUESTS_DELAY_MS,
);
@@ -374,47 +375,47 @@ export class OutgoingRoomKeyRequestManager {
// look for and send any queued requests. Runs itself recursively until
// there are no more requests, or there is an error (in which case, the
// timer will be restarted before the promise resolves).
_sendOutgoingRoomKeyRequests() {
if (!this._clientRunning) {
this._sendOutgoingRoomKeyRequestsTimer = null;
private sendOutgoingRoomKeyRequests(): Promise<void> {
if (!this.clientRunning) {
this.sendOutgoingRoomKeyRequestsTimer = null;
return Promise.resolve();
}
return this._cryptoStore.getOutgoingRoomKeyRequestByState([
ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING,
ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING_AND_WILL_RESEND,
ROOM_KEY_REQUEST_STATES.UNSENT,
]).then((req) => {
return this.cryptoStore.getOutgoingRoomKeyRequestByState([
RoomKeyRequestState.CancellationPending,
RoomKeyRequestState.CancellationPendingAndWillResend,
RoomKeyRequestState.Unsent,
]).then((req: OutgoingRoomKeyRequest) => {
if (!req) {
this._sendOutgoingRoomKeyRequestsTimer = null;
this.sendOutgoingRoomKeyRequestsTimer = null;
return;
}
let prom;
switch (req.state) {
case ROOM_KEY_REQUEST_STATES.UNSENT:
prom = this._sendOutgoingRoomKeyRequest(req);
case RoomKeyRequestState.Unsent:
prom = this.sendOutgoingRoomKeyRequest(req);
break;
case ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING:
prom = this._sendOutgoingRoomKeyRequestCancellation(req);
case RoomKeyRequestState.CancellationPending:
prom = this.sendOutgoingRoomKeyRequestCancellation(req);
break;
case ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING_AND_WILL_RESEND:
prom = this._sendOutgoingRoomKeyRequestCancellation(req, true);
case RoomKeyRequestState.CancellationPendingAndWillResend:
prom = this.sendOutgoingRoomKeyRequestCancellation(req, true);
break;
}
return prom.then(() => {
// go around the loop again
return this._sendOutgoingRoomKeyRequests();
return this.sendOutgoingRoomKeyRequests();
}).catch((e) => {
logger.error("Error sending room key request; will retry later.", e);
this._sendOutgoingRoomKeyRequestsTimer = null;
this.sendOutgoingRoomKeyRequestsTimer = null;
});
});
}
// given a RoomKeyRequest, send it and update the request record
_sendOutgoingRoomKeyRequest(req) {
private sendOutgoingRoomKeyRequest(req: OutgoingRoomKeyRequest): Promise<void> {
logger.log(
`Requesting keys for ${stringifyRequestBody(req.requestBody)}` +
` from ${stringifyRecipientList(req.recipients)}` +
@@ -423,24 +424,24 @@ export class OutgoingRoomKeyRequestManager {
const requestMessage = {
action: "request",
requesting_device_id: this._deviceId,
requesting_device_id: this.deviceId,
request_id: req.requestId,
body: req.requestBody,
};
return this._sendMessageToDevices(
return this.sendMessageToDevices(
requestMessage, req.recipients, req.requestTxnId || req.requestId,
).then(() => {
return this._cryptoStore.updateOutgoingRoomKeyRequest(
req.requestId, ROOM_KEY_REQUEST_STATES.UNSENT,
{ state: ROOM_KEY_REQUEST_STATES.SENT },
return this.cryptoStore.updateOutgoingRoomKeyRequest(
req.requestId, RoomKeyRequestState.Unsent,
{ state: RoomKeyRequestState.Sent },
);
});
}
// Given a RoomKeyRequest, cancel it and delete the request record unless
// andResend is set, in which case transition to UNSENT.
_sendOutgoingRoomKeyRequestCancellation(req, andResend) {
private sendOutgoingRoomKeyRequestCancellation(req: OutgoingRoomKeyRequest, andResend = false): Promise<void> {
logger.log(
`Sending cancellation for key request for ` +
`${stringifyRequestBody(req.requestBody)} to ` +
@@ -450,30 +451,30 @@ export class OutgoingRoomKeyRequestManager {
const requestMessage = {
action: "request_cancellation",
requesting_device_id: this._deviceId,
requesting_device_id: this.deviceId,
request_id: req.requestId,
};
return this._sendMessageToDevices(
return this.sendMessageToDevices(
requestMessage, req.recipients, req.cancellationTxnId,
).then(() => {
if (andResend) {
// We want to resend, so transition to UNSENT
return this._cryptoStore.updateOutgoingRoomKeyRequest(
return this.cryptoStore.updateOutgoingRoomKeyRequest(
req.requestId,
ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING_AND_WILL_RESEND,
{ state: ROOM_KEY_REQUEST_STATES.UNSENT },
RoomKeyRequestState.CancellationPendingAndWillResend,
{ state: RoomKeyRequestState.Unsent },
);
}
return this._cryptoStore.deleteOutgoingRoomKeyRequest(
req.requestId, ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING,
return this.cryptoStore.deleteOutgoingRoomKeyRequest(
req.requestId, RoomKeyRequestState.CancellationPending,
);
});
}
// send a RoomKeyRequest to a list of recipients
_sendMessageToDevices(message, recipients, txnId) {
const contentMap = {};
private sendMessageToDevices(message, recipients, txnId: string): Promise<{}> {
const contentMap: Record<string, Record<string, Record<string, any>>> = {};
for (const recip of recipients) {
if (!contentMap[recip.userId]) {
contentMap[recip.userId] = {};
@@ -481,9 +482,7 @@ export class OutgoingRoomKeyRequestManager {
contentMap[recip.userId][recip.deviceId] = message;
}
return this._baseApis.sendToDevice(
'm.room_key_request', contentMap, txnId,
);
return this.baseApis.sendToDevice(EventType.RoomKeyRequest, contentMap, txnId);
}
}

View File

@@ -25,7 +25,7 @@ const subtleCrypto = (typeof window !== "undefined" && window.crypto) ?
// salt for HKDF, with 8 bytes of zeros
const zeroSalt = new Uint8Array(8);
interface IEncryptedPayload {
export interface IEncryptedPayload {
iv: string;
ciphertext: string;
mac: string;

View File

@@ -15,7 +15,7 @@ limitations under the License.
*/
import { DeviceInfo } from "./deviceinfo";
import { IKeyBackupVersion } from "./keybackup";
import { IKeyBackupInfo } from "./keybackup";
import { ISecretStorageKeyInfo } from "../matrix";
// TODO: Merge this with crypto.js once converted
@@ -85,7 +85,7 @@ export interface ICreateSecretStorageOpts {
* The current key backup object. If passed,
* the passphrase and recovery key from this backup will be used.
*/
keyBackupInfo?: IKeyBackupVersion;
keyBackupInfo?: IKeyBackupInfo;
/**
* If true, a new key backup version will be

View File

@@ -29,11 +29,11 @@ import { keyFromPassphrase } from './key_passphrase';
import { sleep } from "../utils";
import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store';
import { encodeRecoveryKey } from './recoverykey';
import { IKeyBackupVersion } from "./keybackup";
import { IKeyBackupInfo } from "./keybackup";
const KEY_BACKUP_KEYS_PER_REQUEST = 200;
type AuthData = IKeyBackupVersion["auth_data"];
type AuthData = IKeyBackupInfo["auth_data"];
type SigInfo = {
deviceId: string,
@@ -49,7 +49,7 @@ export type TrustInfo = {
};
export interface IKeyBackupCheck {
backupInfo: IKeyBackupVersion;
backupInfo: IKeyBackupInfo;
trustInfo: TrustInfo;
}
@@ -90,7 +90,7 @@ interface BackupAlgorithm {
*/
export class BackupManager {
private algorithm: BackupAlgorithm | undefined;
public backupInfo: IKeyBackupVersion | undefined; // The info dict from /room_keys/version
public backupInfo: IKeyBackupInfo | undefined; // The info dict from /room_keys/version
public checkedForBackup: boolean; // Have we checked the server for a backup we can use?
private sendingBackups: boolean; // Are we currently sending backups?
constructor(private readonly baseApis: MatrixClient, public readonly getKey: GetKey) {
@@ -102,7 +102,7 @@ export class BackupManager {
return this.backupInfo && this.backupInfo.version;
}
public static async makeAlgorithm(info: IKeyBackupVersion, getKey: GetKey): Promise<BackupAlgorithm> {
public static async makeAlgorithm(info: IKeyBackupInfo, getKey: GetKey): Promise<BackupAlgorithm> {
const Algorithm = algorithmsByName[info.algorithm];
if (!Algorithm) {
throw new Error("Unknown backup algorithm");
@@ -110,7 +110,7 @@ export class BackupManager {
return await Algorithm.init(info.auth_data, getKey);
}
public async enableKeyBackup(info: IKeyBackupVersion): Promise<void> {
public async enableKeyBackup(info: IKeyBackupInfo): Promise<void> {
this.backupInfo = info;
if (this.algorithm) {
this.algorithm.free();
@@ -166,7 +166,7 @@ export class BackupManager {
};
}
public async createKeyBackupVersion(info: IKeyBackupVersion): Promise<void> {
public async createKeyBackupVersion(info: IKeyBackupInfo): Promise<void> {
this.algorithm = await BackupManager.makeAlgorithm(info, this.getKey);
}
@@ -183,7 +183,7 @@ export class BackupManager {
this.checkedForBackup = true;
return null;
}
let backupInfo: IKeyBackupVersion;
let backupInfo: IKeyBackupInfo;
try {
backupInfo = await this.baseApis.getKeyBackupVersion();
} catch (e) {
@@ -260,7 +260,7 @@ export class BackupManager {
* ]
* }
*/
public async isKeyBackupTrusted(backupInfo: IKeyBackupVersion): Promise<TrustInfo> {
public async isKeyBackupTrusted(backupInfo: IKeyBackupInfo): Promise<TrustInfo> {
const ret = {
usable: false,
trusted_locally: false,

View File

@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { ISignatures } from "../@types/signed";
/**
* @module crypto/deviceinfo
*/

View File

@@ -52,10 +52,11 @@ import { IStore } from "../store";
import { Room } from "../models/room";
import { RoomMember } from "../models/room-member";
import { MatrixEvent } from "../models/event";
import { MatrixClient, IKeysUploadResponse, SessionStore, CryptoStore } from "../client";
import { MatrixClient, IKeysUploadResponse, SessionStore, CryptoStore, ISignedKey } from "../client";
import type { EncryptionAlgorithm, DecryptionAlgorithm } from "./algorithms/base";
import type { RoomList } from "./RoomList";
import { IRecoveryKey, IEncryptedEventInfo } from "./api";
import { IKeyBackupInfo } from "./keybackup";
const DeviceVerification = DeviceInfo.DeviceVerification;
@@ -91,7 +92,7 @@ interface IInitOpts {
export interface IBootstrapCrossSigningOpts {
setupNewCrossSigning?: boolean;
authUploadDeviceSigningKeys?(makeRequest: (authData: any) => void): Promise<void>;
authUploadDeviceSigningKeys?(makeRequest: (authData: any) => void): Promise<{}>;
}
interface IBootstrapSecretStorageOpts {
@@ -111,7 +112,7 @@ interface IRoomKey {
algorithm: string;
}
interface IRoomKeyRequestBody extends IRoomKey {
export interface IRoomKeyRequestBody extends IRoomKey {
session_id: string;
sender_key: string
}
@@ -157,7 +158,7 @@ interface ISyncDeviceLists {
left: string[];
}
interface IRoomKeyRequestRecipient {
export interface IRoomKeyRequestRecipient {
userId: string;
deviceId: string;
}
@@ -276,7 +277,7 @@ export class Crypto extends EventEmitter {
* or a class that implements a verification method.
*/
constructor(
private readonly baseApis: MatrixClient,
public readonly baseApis: MatrixClient,
public readonly sessionStore: SessionStore,
private readonly userId: string,
private readonly deviceId: string,
@@ -622,7 +623,7 @@ export class Crypto extends EventEmitter {
// Cross-sign own device
const device = this.deviceList.getStoredDevice(this.userId, this.deviceId);
const deviceSignature = await crossSigningInfo.signDevice(this.userId, device);
const deviceSignature = await crossSigningInfo.signDevice(this.userId, device) as ISignedKey;
builder.addKeySignature(this.userId, this.deviceId, deviceSignature);
// Sign message key backup with cross-signing master key
@@ -932,7 +933,7 @@ export class Crypto extends EventEmitter {
await secretStorage.store("m.megolm_backup.v1", olmlib.encodeBase64(privateKey));
// create keyBackupInfo object to add to builder
const data = {
const data: IKeyBackupInfo = {
algorithm: info.algorithm,
auth_data: info.auth_data,
};
@@ -2843,8 +2844,8 @@ export class Crypto extends EventEmitter {
* Re-send any outgoing key requests, eg after verification
* @returns {Promise}
*/
public cancelAndResendAllOutgoingKeyRequests(): Promise<void> {
return this.outgoingRoomKeyRequestManager.cancelAndResendAllOutgoingRequests();
public async cancelAndResendAllOutgoingKeyRequests(): Promise<void> {
await this.outgoingRoomKeyRequestManager.cancelAndResendAllOutgoingRequests();
}
/**
@@ -3291,9 +3292,7 @@ export class Crypto extends EventEmitter {
// it. This won't always be the case though so we need to re-send any that have already been sent
// to avoid races.
const requestsToResend =
await this.outgoingRoomKeyRequestManager.getOutgoingSentRoomKeyRequest(
sender, device.deviceId,
);
await this.outgoingRoomKeyRequestManager.getOutgoingSentRoomKeyRequest(sender, device.deviceId);
for (const keyReq of requestsToResend) {
this.requestRoomKey(keyReq.requestBody, keyReq.recipients, true);
}

View File

@@ -32,7 +32,7 @@ export interface IKeyBackupRoomSessions {
}
/* eslint-disable camelcase */
export interface IKeyBackupVersion {
export interface IKeyBackupInfo {
algorithm: string;
auth_data: {
public_key: string;
@@ -41,9 +41,9 @@ export interface IKeyBackupVersion {
private_key_iterations: number;
private_key_bits?: number;
};
count: number;
etag: string;
version: string; // number contained within
count?: number;
etag?: string;
version?: string; // number contained within
}
/* eslint-enable camelcase */

View File

@@ -566,7 +566,7 @@ export function encodeBase64(uint8Array: ArrayBuffer | Uint8Array): string {
* @param {Uint8Array} uint8Array The data to encode.
* @return {string} The unpadded base64.
*/
export function encodeUnpaddedBase64(uint8Array: Uint8Array): string {
export function encodeUnpaddedBase64(uint8Array: ArrayBuffer | Uint8Array): string {
return encodeBase64(uint8Array).replace(/=+$/g, '');
}

View File

@@ -10,6 +10,9 @@
* @interface CryptoStore
*/
import { IRoomKeyRequestBody, IRoomKeyRequestRecipient } from "../index";
import { RoomKeyRequestState } from "../OutgoingRoomKeyRequestManager";
/**
* Represents an outgoing room key request
*
@@ -32,3 +35,11 @@
* @property {Number} state current state of this request (states are defined
* in {@link module:crypto/OutgoingRoomKeyRequestManager~ROOM_KEY_REQUEST_STATES})
*/
export interface OutgoingRoomKeyRequest {
requestId: string;
requestTxnId?: string;
cancellationTxnId?: string;
recipients: IRoomKeyRequestRecipient[];
requestBody: IRoomKeyRequestBody;
state: RoomKeyRequestState;
}