You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-25 05:23:13 +03:00
Convert SecretStorage to TypeScript
This commit is contained in:
@@ -64,7 +64,8 @@ import {
|
|||||||
import { IIdentityServerProvider } from "./@types/IIdentityServerProvider";
|
import { IIdentityServerProvider } from "./@types/IIdentityServerProvider";
|
||||||
import type Request from "request";
|
import type Request from "request";
|
||||||
import { MatrixScheduler } from "./scheduler";
|
import { MatrixScheduler } from "./scheduler";
|
||||||
import { ICryptoCallbacks, ISecretStorageKeyInfo, NotificationCountType } from "./matrix";
|
import { ICryptoCallbacks, NotificationCountType } from "./matrix";
|
||||||
|
import { ISecretStorageKeyInfo } from "./crypto/SecretStorage";
|
||||||
import { MemoryCryptoStore } from "./crypto/store/memory-crypto-store";
|
import { MemoryCryptoStore } from "./crypto/store/memory-crypto-store";
|
||||||
import { LocalStorageCryptoStore } from "./crypto/store/localStorage-crypto-store";
|
import { LocalStorageCryptoStore } from "./crypto/store/localStorage-crypto-store";
|
||||||
import { IndexedDBCryptoStore } from "./crypto/store/indexeddb-crypto-store";
|
import { IndexedDBCryptoStore } from "./crypto/store/indexeddb-crypto-store";
|
||||||
@@ -83,7 +84,6 @@ import {
|
|||||||
IEncryptedEventInfo,
|
IEncryptedEventInfo,
|
||||||
IImportRoomKeysOpts,
|
IImportRoomKeysOpts,
|
||||||
IRecoveryKey,
|
IRecoveryKey,
|
||||||
ISecretStorageKey,
|
|
||||||
} from "./crypto/api";
|
} from "./crypto/api";
|
||||||
import { CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from "./crypto/CrossSigning";
|
import { CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from "./crypto/CrossSigning";
|
||||||
import { Room } from "./models/room";
|
import { Room } from "./models/room";
|
||||||
@@ -1841,7 +1841,9 @@ export class MatrixClient extends EventEmitter {
|
|||||||
* keyId: {string} the ID of the key
|
* keyId: {string} the ID of the key
|
||||||
* keyInfo: {object} details about the key (iv, mac, passphrase)
|
* keyInfo: {object} details about the key (iv, mac, passphrase)
|
||||||
*/
|
*/
|
||||||
public addSecretStorageKey(algorithm: string, opts: IAddSecretStorageKeyOpts, keyName?: string): ISecretStorageKey {
|
public addSecretStorageKey(
|
||||||
|
algorithm: string, opts: IAddSecretStorageKeyOpts, keyName?: string,
|
||||||
|
): Promise<{keyId: string, keyInfo: ISecretStorageKeyInfo}> {
|
||||||
if (!this.crypto) {
|
if (!this.crypto) {
|
||||||
throw new Error("End-to-end encryption disabled");
|
throw new Error("End-to-end encryption disabled");
|
||||||
}
|
}
|
||||||
@@ -1857,7 +1859,7 @@ export class MatrixClient extends EventEmitter {
|
|||||||
* for. Defaults to the default key ID if not provided.
|
* for. Defaults to the default key ID if not provided.
|
||||||
* @return {boolean} Whether we have the key.
|
* @return {boolean} Whether we have the key.
|
||||||
*/
|
*/
|
||||||
public hasSecretStorageKey(keyId?: string): boolean {
|
public hasSecretStorageKey(keyId?: string): Promise<boolean> {
|
||||||
if (!this.crypto) {
|
if (!this.crypto) {
|
||||||
throw new Error("End-to-end encryption disabled");
|
throw new Error("End-to-end encryption disabled");
|
||||||
}
|
}
|
||||||
@@ -1910,7 +1912,7 @@ export class MatrixClient extends EventEmitter {
|
|||||||
* with, or null if it is not present or not encrypted with a trusted
|
* with, or null if it is not present or not encrypted with a trusted
|
||||||
* key
|
* key
|
||||||
*/
|
*/
|
||||||
public isSecretStored(name: string, checkKey: boolean): Record<string, ISecretStorageKeyInfo> {
|
public isSecretStored(name: string, checkKey: boolean): Promise<Record<string, ISecretStorageKeyInfo>> {
|
||||||
if (!this.crypto) {
|
if (!this.crypto) {
|
||||||
throw new Error("End-to-end encryption disabled");
|
throw new Error("End-to-end encryption disabled");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import {
|
|||||||
CrossSigningKeys,
|
CrossSigningKeys,
|
||||||
ICrossSigningKey,
|
ICrossSigningKey,
|
||||||
ICryptoCallbacks,
|
ICryptoCallbacks,
|
||||||
ISecretStorageKeyInfo,
|
|
||||||
ISignedKey,
|
ISignedKey,
|
||||||
KeySignatures,
|
KeySignatures,
|
||||||
} from "../matrix";
|
} from "../matrix";
|
||||||
|
import { ISecretStorageKeyInfo } from "./SecretStorage";
|
||||||
import { IKeyBackupInfo } from "./keybackup";
|
import { IKeyBackupInfo } from "./keybackup";
|
||||||
|
|
||||||
interface ICrossSigningKeys {
|
interface ICrossSigningKeys {
|
||||||
@@ -337,7 +337,7 @@ class SSSSCryptoCallbacks {
|
|||||||
constructor(private readonly delegateCryptoCallbacks: ICryptoCallbacks) {}
|
constructor(private readonly delegateCryptoCallbacks: ICryptoCallbacks) {}
|
||||||
|
|
||||||
public async getSecretStorageKey(
|
public async getSecretStorageKey(
|
||||||
{ keys }: { keys: Record<string, object> },
|
{ keys }: { keys: Record<string, ISecretStorageKeyInfo> },
|
||||||
name: string,
|
name: string,
|
||||||
): Promise<[string, Uint8Array]> {
|
): Promise<[string, Uint8Array]> {
|
||||||
for (const keyId of Object.keys(keys)) {
|
for (const keyId of Object.keys(keys)) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
Copyright 2019 - 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -14,61 +14,107 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EventEmitter } from 'events';
|
|
||||||
import { logger } from '../logger';
|
import { logger } from '../logger';
|
||||||
import * as olmlib from './olmlib';
|
import * as olmlib from './olmlib';
|
||||||
import { randomString } from '../randomstring';
|
import { randomString } from '../randomstring';
|
||||||
import { encryptAES, decryptAES } from './aes';
|
import { encryptAES, decryptAES, IEncryptedPayload } from './aes';
|
||||||
import { encodeBase64 } from "./olmlib";
|
import { encodeBase64 } from "./olmlib";
|
||||||
|
import { ICryptoCallbacks, MatrixClient, MatrixEvent } from '../matrix';
|
||||||
|
import { IAddSecretStorageKeyOpts, IPassphraseInfo } from './api';
|
||||||
|
import { EventEmitter } from 'stream';
|
||||||
|
|
||||||
export const SECRET_STORAGE_ALGORITHM_V1_AES
|
export const SECRET_STORAGE_ALGORITHM_V1_AES = "m.secret_storage.v1.aes-hmac-sha2";
|
||||||
= "m.secret_storage.v1.aes-hmac-sha2";
|
|
||||||
|
|
||||||
const ZERO_STR = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
const ZERO_STR = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
||||||
|
|
||||||
|
export interface ISecretStorageKeyInfo {
|
||||||
|
name: string;
|
||||||
|
algorithm: string;
|
||||||
|
// technically the below are specific to AES keys. If we ever introduce another type,
|
||||||
|
// we can split into separate interfaces.
|
||||||
|
iv: string;
|
||||||
|
mac: string;
|
||||||
|
passphrase: IPassphraseInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SecretStorageKeyTuple = [keyId: string, keyInfo: ISecretStorageKeyInfo];
|
||||||
|
|
||||||
|
export interface ISecretRequest {
|
||||||
|
requestId: string;
|
||||||
|
promise: Promise<string>;
|
||||||
|
cancel: (reason: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IAccountDataClient extends EventEmitter {
|
||||||
|
getAccountDataFromServer: (string) => Promise<any>;
|
||||||
|
getAccountData: (string) => object;
|
||||||
|
setAccountData: (string, object) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISecretRequestInternal {
|
||||||
|
name: string;
|
||||||
|
devices: Array<string>;
|
||||||
|
resolve: (string) => void;
|
||||||
|
reject: (Error) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IDecryptors {
|
||||||
|
encrypt: (string) => Promise<IEncryptedPayload>;
|
||||||
|
decrypt: (IEncryptedPayload) => Promise<string>;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements Secure Secret Storage and Sharing (MSC1946)
|
* Implements Secure Secret Storage and Sharing (MSC1946)
|
||||||
* @module crypto/SecretStorage
|
* @module crypto/SecretStorage
|
||||||
*/
|
*/
|
||||||
export class SecretStorage extends EventEmitter {
|
export class SecretStorage {
|
||||||
constructor(baseApis, cryptoCallbacks) {
|
private accountDataAdapter: IAccountDataClient;
|
||||||
super();
|
private baseApis: MatrixClient;
|
||||||
this._baseApis = baseApis;
|
private cryptoCallbacks: ICryptoCallbacks;
|
||||||
this._cryptoCallbacks = cryptoCallbacks;
|
private requests = new Map<string, ISecretRequestInternal>();
|
||||||
this._requests = {};
|
|
||||||
this._incomingRequests = {};
|
// In it's pure javascript days, this was relying on some proper Javascript-style
|
||||||
|
// type-abuse where sometimes we'd pass in a fake client object with just the account
|
||||||
|
// data methods implemented, which is all this class needs unless you use the secret
|
||||||
|
// sharing code, so it was fine. As a low-touch TypeScript migration, this now has
|
||||||
|
// an extra, optional param for a real matrix client, so you can not pass it as long
|
||||||
|
// as you don't request any secrets.
|
||||||
|
// A better solution would probably be to split this class up into secret storage and
|
||||||
|
// secret sharing which are really two separate things, even though they share an MSC.
|
||||||
|
constructor(accountDataClient: IAccountDataClient, cryptoCallbacks: ICryptoCallbacks, matrixClient?: MatrixClient) {
|
||||||
|
this.accountDataAdapter = accountDataClient;
|
||||||
|
this.baseApis = matrixClient;
|
||||||
|
this.cryptoCallbacks = cryptoCallbacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDefaultKeyId() {
|
public async getDefaultKeyId(): Promise<string> {
|
||||||
const defaultKey = await this._baseApis.getAccountDataFromServer(
|
const defaultKey = await this.accountDataAdapter.getAccountDataFromServer(
|
||||||
'm.secret_storage.default_key',
|
'm.secret_storage.default_key',
|
||||||
);
|
);
|
||||||
if (!defaultKey) return null;
|
if (!defaultKey) return null;
|
||||||
return defaultKey.key;
|
return defaultKey.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDefaultKeyId(keyId) {
|
public setDefaultKeyId(keyId: string) {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const listener = (ev) => {
|
const listener = (ev) => {
|
||||||
if (
|
if (
|
||||||
ev.getType() === 'm.secret_storage.default_key' &&
|
ev.getType() === 'm.secret_storage.default_key' &&
|
||||||
ev.getContent().key === keyId
|
ev.getContent().key === keyId
|
||||||
) {
|
) {
|
||||||
this._baseApis.removeListener('accountData', listener);
|
this.accountDataAdapter.removeListener('accountData', listener);
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this._baseApis.on('accountData', listener);
|
this.accountDataAdapter.on('accountData', listener);
|
||||||
|
|
||||||
try {
|
this.accountDataAdapter.setAccountData(
|
||||||
await this._baseApis.setAccountData(
|
|
||||||
'm.secret_storage.default_key',
|
'm.secret_storage.default_key',
|
||||||
{ key: keyId },
|
{ key: keyId },
|
||||||
);
|
).catch(e => {
|
||||||
} catch (e) {
|
this.accountDataAdapter.removeListener('accountData', listener);
|
||||||
this._baseApis.removeListener('accountData', listener);
|
|
||||||
reject(e);
|
reject(e);
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,10 +131,12 @@ export class SecretStorage extends EventEmitter {
|
|||||||
* keyId: {string} the ID of the key
|
* keyId: {string} the ID of the key
|
||||||
* keyInfo: {object} details about the key (iv, mac, passphrase)
|
* keyInfo: {object} details about the key (iv, mac, passphrase)
|
||||||
*/
|
*/
|
||||||
async addKey(algorithm, opts, keyId) {
|
public async addKey(
|
||||||
const keyInfo = { algorithm };
|
algorithm: string, opts: IAddSecretStorageKeyOpts, keyId?: string,
|
||||||
|
): Promise<{keyId: string, keyInfo: ISecretStorageKeyInfo}> {
|
||||||
|
const keyInfo = { algorithm } as ISecretStorageKeyInfo;
|
||||||
|
|
||||||
if (!opts) opts = {};
|
if (!opts) opts = {} as IAddSecretStorageKeyOpts;
|
||||||
|
|
||||||
if (opts.name) {
|
if (opts.name) {
|
||||||
keyInfo.name = opts.name;
|
keyInfo.name = opts.name;
|
||||||
@@ -104,20 +152,20 @@ export class SecretStorage extends EventEmitter {
|
|||||||
keyInfo.mac = mac;
|
keyInfo.mac = mac;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Unknown key algorithm ${opts.algorithm}`);
|
throw new Error(`Unknown key algorithm ${algorithm}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!keyId) {
|
if (!keyId) {
|
||||||
do {
|
do {
|
||||||
keyId = randomString(32);
|
keyId = randomString(32);
|
||||||
} while (
|
} while (
|
||||||
await this._baseApis.getAccountDataFromServer(
|
await this.accountDataAdapter.getAccountDataFromServer(
|
||||||
`m.secret_storage.key.${keyId}`,
|
`m.secret_storage.key.${keyId}`,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this._baseApis.setAccountData(
|
await this.accountDataAdapter.setAccountData(
|
||||||
`m.secret_storage.key.${keyId}`, keyInfo,
|
`m.secret_storage.key.${keyId}`, keyInfo,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -134,8 +182,9 @@ export class SecretStorage extends EventEmitter {
|
|||||||
* for. Defaults to the default key ID if not provided.
|
* for. Defaults to the default key ID if not provided.
|
||||||
* @returns {Array?} If the key was found, the return value is an array of
|
* @returns {Array?} If the key was found, the return value is an array of
|
||||||
* the form [keyId, keyInfo]. Otherwise, null is returned.
|
* the form [keyId, keyInfo]. Otherwise, null is returned.
|
||||||
|
* XXX: why is this an array when addKey returns an object?
|
||||||
*/
|
*/
|
||||||
async getKey(keyId) {
|
public async getKey(keyId: string): Promise<SecretStorageKeyTuple> {
|
||||||
if (!keyId) {
|
if (!keyId) {
|
||||||
keyId = await this.getDefaultKeyId();
|
keyId = await this.getDefaultKeyId();
|
||||||
}
|
}
|
||||||
@@ -143,7 +192,7 @@ export class SecretStorage extends EventEmitter {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyInfo = await this._baseApis.getAccountDataFromServer(
|
const keyInfo = await this.accountDataAdapter.getAccountDataFromServer(
|
||||||
"m.secret_storage.key." + keyId,
|
"m.secret_storage.key." + keyId,
|
||||||
);
|
);
|
||||||
return keyInfo ? [keyId, keyInfo] : null;
|
return keyInfo ? [keyId, keyInfo] : null;
|
||||||
@@ -156,8 +205,8 @@ export class SecretStorage extends EventEmitter {
|
|||||||
* for. Defaults to the default key ID if not provided.
|
* for. Defaults to the default key ID if not provided.
|
||||||
* @return {boolean} Whether we have the key.
|
* @return {boolean} Whether we have the key.
|
||||||
*/
|
*/
|
||||||
async hasKey(keyId) {
|
public async hasKey(keyId?: string): Promise<boolean> {
|
||||||
return !!(await this.getKey(keyId));
|
return Boolean(await this.getKey(keyId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -168,7 +217,7 @@ export class SecretStorage extends EventEmitter {
|
|||||||
*
|
*
|
||||||
* @return {boolean} whether or not the key matches
|
* @return {boolean} whether or not the key matches
|
||||||
*/
|
*/
|
||||||
async checkKey(key, info) {
|
public async checkKey(key: Uint8Array, info: ISecretStorageKeyInfo): Promise<boolean> {
|
||||||
if (info.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
|
if (info.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
|
||||||
if (info.mac) {
|
if (info.mac) {
|
||||||
const { mac } = await SecretStorage._calculateKeyCheck(key, info.iv);
|
const { mac } = await SecretStorage._calculateKeyCheck(key, info.iv);
|
||||||
@@ -182,7 +231,7 @@ export class SecretStorage extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async _calculateKeyCheck(key, iv) {
|
public static async _calculateKeyCheck(key: Uint8Array, iv?: string): Promise<IEncryptedPayload> {
|
||||||
return await encryptAES(ZERO_STR, key, "", iv);
|
return await encryptAES(ZERO_STR, key, "", iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +243,7 @@ export class SecretStorage extends EventEmitter {
|
|||||||
* @param {Array} keys The IDs of the keys to use to encrypt the secret
|
* @param {Array} keys The IDs of the keys to use to encrypt the secret
|
||||||
* or null/undefined to use the default key.
|
* or null/undefined to use the default key.
|
||||||
*/
|
*/
|
||||||
async store(name, secret, keys) {
|
public async store(name: string, secret: string, keys?: Array<string>): Promise<void> {
|
||||||
const encrypted = {};
|
const encrypted = {};
|
||||||
|
|
||||||
if (!keys) {
|
if (!keys) {
|
||||||
@@ -211,7 +260,7 @@ export class SecretStorage extends EventEmitter {
|
|||||||
|
|
||||||
for (const keyId of keys) {
|
for (const keyId of keys) {
|
||||||
// get key information from key storage
|
// get key information from key storage
|
||||||
const keyInfo = await this._baseApis.getAccountDataFromServer(
|
const keyInfo = await this.accountDataAdapter.getAccountDataFromServer(
|
||||||
"m.secret_storage.key." + keyId,
|
"m.secret_storage.key." + keyId,
|
||||||
);
|
);
|
||||||
if (!keyInfo) {
|
if (!keyInfo) {
|
||||||
@@ -221,7 +270,7 @@ export class SecretStorage extends EventEmitter {
|
|||||||
// encrypt secret, based on the algorithm
|
// encrypt secret, based on the algorithm
|
||||||
if (keyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
|
if (keyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
|
||||||
const keys = { [keyId]: keyInfo };
|
const keys = { [keyId]: keyInfo };
|
||||||
const [, encryption] = await this._getSecretStorageKey(keys, name);
|
const [, encryption] = await this.getSecretStorageKey(keys, name);
|
||||||
encrypted[keyId] = await encryption.encrypt(secret);
|
encrypted[keyId] = await encryption.encrypt(secret);
|
||||||
} else {
|
} else {
|
||||||
logger.warn("unknown algorithm for secret storage key " + keyId
|
logger.warn("unknown algorithm for secret storage key " + keyId
|
||||||
@@ -231,34 +280,7 @@ export class SecretStorage extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// save encrypted secret
|
// save encrypted secret
|
||||||
await this._baseApis.setAccountData(name, { encrypted });
|
await this.accountDataAdapter.setAccountData(name, { encrypted });
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Temporary method to fix up existing accounts where secrets
|
|
||||||
* are incorrectly stored without the 'encrypted' level
|
|
||||||
*
|
|
||||||
* @param {string} name The name of the secret
|
|
||||||
* @param {object} secretInfo The account data object
|
|
||||||
* @returns {object} The fixed object or null if no fix was performed
|
|
||||||
*/
|
|
||||||
async _fixupStoredSecret(name, secretInfo) {
|
|
||||||
// We assume the secret was only stored passthrough for 1
|
|
||||||
// key - this was all the broken code supported.
|
|
||||||
const keys = Object.keys(secretInfo);
|
|
||||||
if (
|
|
||||||
keys.length === 1 && keys[0] !== 'encrypted' &&
|
|
||||||
secretInfo[keys[0]].passthrough
|
|
||||||
) {
|
|
||||||
const hasKey = await this.hasKey(keys[0]);
|
|
||||||
if (hasKey) {
|
|
||||||
logger.log("Fixing up passthrough secret: " + name);
|
|
||||||
await this.storePassthrough(name, keys[0]);
|
|
||||||
const newData = await this._baseApis.getAccountDataFromServer(name);
|
|
||||||
return newData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -268,24 +290,20 @@ export class SecretStorage extends EventEmitter {
|
|||||||
*
|
*
|
||||||
* @return {string} the contents of the secret
|
* @return {string} the contents of the secret
|
||||||
*/
|
*/
|
||||||
async get(name) {
|
async get(name: string): Promise<string> {
|
||||||
let secretInfo = await this._baseApis.getAccountDataFromServer(name);
|
const secretInfo = await this.accountDataAdapter.getAccountDataFromServer(name);
|
||||||
if (!secretInfo) {
|
if (!secretInfo) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!secretInfo.encrypted) {
|
if (!secretInfo.encrypted) {
|
||||||
// try to fix it up
|
|
||||||
secretInfo = await this._fixupStoredSecret(name, secretInfo);
|
|
||||||
if (!secretInfo || !secretInfo.encrypted) {
|
|
||||||
throw new Error("Content is not encrypted!");
|
throw new Error("Content is not encrypted!");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// get possible keys to decrypt
|
// get possible keys to decrypt
|
||||||
const keys = {};
|
const keys = {};
|
||||||
for (const keyId of Object.keys(secretInfo.encrypted)) {
|
for (const keyId of Object.keys(secretInfo.encrypted)) {
|
||||||
// get key information from key storage
|
// get key information from key storage
|
||||||
const keyInfo = await this._baseApis.getAccountDataFromServer(
|
const keyInfo = await this.accountDataAdapter.getAccountDataFromServer(
|
||||||
"m.secret_storage.key." + keyId,
|
"m.secret_storage.key." + keyId,
|
||||||
);
|
);
|
||||||
const encInfo = secretInfo.encrypted[keyId];
|
const encInfo = secretInfo.encrypted[keyId];
|
||||||
@@ -306,7 +324,7 @@ export class SecretStorage extends EventEmitter {
|
|||||||
let decryption;
|
let decryption;
|
||||||
try {
|
try {
|
||||||
// fetch private key from app
|
// fetch private key from app
|
||||||
[keyId, decryption] = await this._getSecretStorageKey(keys, name);
|
[keyId, decryption] = await this.getSecretStorageKey(keys, name);
|
||||||
|
|
||||||
const encInfo = secretInfo.encrypted[keyId];
|
const encInfo = secretInfo.encrypted[keyId];
|
||||||
|
|
||||||
@@ -331,17 +349,13 @@ export class SecretStorage extends EventEmitter {
|
|||||||
* with, or null if it is not present or not encrypted with a trusted
|
* with, or null if it is not present or not encrypted with a trusted
|
||||||
* key
|
* key
|
||||||
*/
|
*/
|
||||||
async isStored(name, checkKey) {
|
async isStored(name: string, checkKey: boolean): Promise<Record<string, ISecretStorageKeyInfo>> {
|
||||||
// check if secret exists
|
// check if secret exists
|
||||||
let secretInfo = await this._baseApis.getAccountDataFromServer(name);
|
const secretInfo = await this.accountDataAdapter.getAccountDataFromServer(name);
|
||||||
if (!secretInfo) return null;
|
if (!secretInfo) return null;
|
||||||
if (!secretInfo.encrypted) {
|
if (!secretInfo.encrypted) {
|
||||||
// try to fix it up
|
|
||||||
secretInfo = await this._fixupStoredSecret(name, secretInfo);
|
|
||||||
if (!secretInfo || !secretInfo.encrypted) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (checkKey === undefined) checkKey = true;
|
if (checkKey === undefined) checkKey = true;
|
||||||
|
|
||||||
@@ -350,7 +364,7 @@ export class SecretStorage extends EventEmitter {
|
|||||||
// filter secret encryption keys with supported algorithm
|
// filter secret encryption keys with supported algorithm
|
||||||
for (const keyId of Object.keys(secretInfo.encrypted)) {
|
for (const keyId of Object.keys(secretInfo.encrypted)) {
|
||||||
// get key information from key storage
|
// get key information from key storage
|
||||||
const keyInfo = await this._baseApis.getAccountDataFromServer(
|
const keyInfo = await this.accountDataAdapter.getAccountDataFromServer(
|
||||||
"m.secret_storage.key." + keyId,
|
"m.secret_storage.key." + keyId,
|
||||||
);
|
);
|
||||||
if (!keyInfo) continue;
|
if (!keyInfo) continue;
|
||||||
@@ -371,45 +385,48 @@ export class SecretStorage extends EventEmitter {
|
|||||||
*
|
*
|
||||||
* @param {string} name the name of the secret to request
|
* @param {string} name the name of the secret to request
|
||||||
* @param {string[]} devices the devices to request the secret from
|
* @param {string[]} devices the devices to request the secret from
|
||||||
*
|
|
||||||
* @return {string} the contents of the secret
|
|
||||||
*/
|
*/
|
||||||
request(name, devices) {
|
request(name: string, devices: Array<string>): ISecretRequest {
|
||||||
const requestId = this._baseApis.makeTxnId();
|
const requestId = this.baseApis.makeTxnId();
|
||||||
|
|
||||||
const requestControl = this._requests[requestId] = {
|
let resolve: (string) => void;
|
||||||
|
let reject: (Error) => void;
|
||||||
|
const promise = new Promise<string>((res, rej) => {
|
||||||
|
resolve = res;
|
||||||
|
reject = rej;
|
||||||
|
});
|
||||||
|
this.requests.set(requestId, {
|
||||||
name,
|
name,
|
||||||
devices,
|
devices,
|
||||||
};
|
resolve,
|
||||||
const promise = new Promise((resolve, reject) => {
|
reject,
|
||||||
requestControl.resolve = resolve;
|
|
||||||
requestControl.reject = reject;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const cancel = (reason) => {
|
const cancel = (reason) => {
|
||||||
// send cancellation event
|
// send cancellation event
|
||||||
const cancelData = {
|
const cancelData = {
|
||||||
action: "request_cancellation",
|
action: "request_cancellation",
|
||||||
requesting_device_id: this._baseApis.deviceId,
|
requesting_device_id: this.baseApis.deviceId,
|
||||||
request_id: requestId,
|
request_id: requestId,
|
||||||
};
|
};
|
||||||
const toDevice = {};
|
const toDevice = {};
|
||||||
for (const device of devices) {
|
for (const device of devices) {
|
||||||
toDevice[device] = cancelData;
|
toDevice[device] = cancelData;
|
||||||
}
|
}
|
||||||
this._baseApis.sendToDevice("m.secret.request", {
|
this.baseApis.sendToDevice("m.secret.request", {
|
||||||
[this._baseApis.getUserId()]: toDevice,
|
[this.baseApis.getUserId()]: toDevice,
|
||||||
});
|
});
|
||||||
|
|
||||||
// and reject the promise so that anyone waiting on it will be
|
// and reject the promise so that anyone waiting on it will be
|
||||||
// notified
|
// notified
|
||||||
requestControl.reject(new Error(reason || "Cancelled"));
|
reject(new Error(reason || "Cancelled"));
|
||||||
};
|
};
|
||||||
|
|
||||||
// send request to devices
|
// send request to devices
|
||||||
const requestData = {
|
const requestData = {
|
||||||
name,
|
name,
|
||||||
action: "request",
|
action: "request",
|
||||||
requesting_device_id: this._baseApis.deviceId,
|
requesting_device_id: this.baseApis.deviceId,
|
||||||
request_id: requestId,
|
request_id: requestId,
|
||||||
};
|
};
|
||||||
const toDevice = {};
|
const toDevice = {};
|
||||||
@@ -417,21 +434,21 @@ export class SecretStorage extends EventEmitter {
|
|||||||
toDevice[device] = requestData;
|
toDevice[device] = requestData;
|
||||||
}
|
}
|
||||||
logger.info(`Request secret ${name} from ${devices}, id ${requestId}`);
|
logger.info(`Request secret ${name} from ${devices}, id ${requestId}`);
|
||||||
this._baseApis.sendToDevice("m.secret.request", {
|
this.baseApis.sendToDevice("m.secret.request", {
|
||||||
[this._baseApis.getUserId()]: toDevice,
|
[this.baseApis.getUserId()]: toDevice,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
request_id: requestId,
|
requestId,
|
||||||
promise,
|
promise,
|
||||||
cancel,
|
cancel,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onRequestReceived(event) {
|
public async onRequestReceived(event: MatrixEvent): Promise<void> {
|
||||||
const sender = event.getSender();
|
const sender = event.getSender();
|
||||||
const content = event.getContent();
|
const content = event.getContent();
|
||||||
if (sender !== this._baseApis.getUserId()
|
if (sender !== this.baseApis.getUserId()
|
||||||
|| !(content.name && content.action
|
|| !(content.name && content.action
|
||||||
&& content.requesting_device_id && content.request_id)) {
|
&& content.requesting_device_id && content.request_id)) {
|
||||||
// ignore requests from anyone else, for now
|
// ignore requests from anyone else, for now
|
||||||
@@ -440,34 +457,45 @@ export class SecretStorage extends EventEmitter {
|
|||||||
const deviceId = content.requesting_device_id;
|
const deviceId = content.requesting_device_id;
|
||||||
// check if it's a cancel
|
// check if it's a cancel
|
||||||
if (content.action === "request_cancellation") {
|
if (content.action === "request_cancellation") {
|
||||||
|
/*
|
||||||
|
Looks like we intended to emit events when we got cancelations, but
|
||||||
|
we never put anything in the _incomingRequests object, and the request
|
||||||
|
itself doesn't use events anyway so if we were to wire up cancellations,
|
||||||
|
they probably ought to use the same callback interface. I'm leaving them
|
||||||
|
disabled for now while converting this file to typescript.
|
||||||
if (this._incomingRequests[deviceId]
|
if (this._incomingRequests[deviceId]
|
||||||
&& this._incomingRequests[deviceId][content.request_id]) {
|
&& this._incomingRequests[deviceId][content.request_id]) {
|
||||||
logger.info("received request cancellation for secret (" + sender
|
logger.info(
|
||||||
+ ", " + deviceId + ", " + content.request_id + ")");
|
"received request cancellation for secret (" + sender +
|
||||||
this._baseApis.emit("crypto.secrets.requestCancelled", {
|
", " + deviceId + ", " + content.request_id + ")",
|
||||||
|
);
|
||||||
|
this.baseApis.emit("crypto.secrets.requestCancelled", {
|
||||||
user_id: sender,
|
user_id: sender,
|
||||||
device_id: deviceId,
|
device_id: deviceId,
|
||||||
request_id: content.request_id,
|
request_id: content.request_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
} else if (content.action === "request") {
|
} else if (content.action === "request") {
|
||||||
if (deviceId === this._baseApis.deviceId) {
|
if (deviceId === this.baseApis.deviceId) {
|
||||||
// no point in trying to send ourself the secret
|
// no point in trying to send ourself the secret
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we have the secret
|
// check if we have the secret
|
||||||
logger.info("received request for secret (" + sender
|
logger.info(
|
||||||
+ ", " + deviceId + ", " + content.request_id + ")");
|
"received request for secret (" + sender +
|
||||||
if (!this._cryptoCallbacks.onSecretRequested) {
|
", " + deviceId + ", " + content.request_id + ")",
|
||||||
|
);
|
||||||
|
if (!this.cryptoCallbacks.onSecretRequested) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const secret = await this._cryptoCallbacks.onSecretRequested(
|
const secret = await this.cryptoCallbacks.onSecretRequested(
|
||||||
sender,
|
sender,
|
||||||
deviceId,
|
deviceId,
|
||||||
content.request_id,
|
content.request_id,
|
||||||
content.name,
|
content.name,
|
||||||
this._baseApis.checkDeviceTrust(sender, deviceId),
|
this.baseApis.checkDeviceTrust(sender, deviceId),
|
||||||
);
|
);
|
||||||
if (secret) {
|
if (secret) {
|
||||||
logger.info(`Preparing ${content.name} secret for ${deviceId}`);
|
logger.info(`Preparing ${content.name} secret for ${deviceId}`);
|
||||||
@@ -480,25 +508,25 @@ export class SecretStorage extends EventEmitter {
|
|||||||
};
|
};
|
||||||
const encryptedContent = {
|
const encryptedContent = {
|
||||||
algorithm: olmlib.OLM_ALGORITHM,
|
algorithm: olmlib.OLM_ALGORITHM,
|
||||||
sender_key: this._baseApis.crypto.olmDevice.deviceCurve25519Key,
|
sender_key: this.baseApis.crypto.olmDevice.deviceCurve25519Key,
|
||||||
ciphertext: {},
|
ciphertext: {},
|
||||||
};
|
};
|
||||||
await olmlib.ensureOlmSessionsForDevices(
|
await olmlib.ensureOlmSessionsForDevices(
|
||||||
this._baseApis.crypto.olmDevice,
|
this.baseApis.crypto.olmDevice,
|
||||||
this._baseApis,
|
this.baseApis,
|
||||||
{
|
{
|
||||||
[sender]: [
|
[sender]: [
|
||||||
this._baseApis.getStoredDevice(sender, deviceId),
|
this.baseApis.getStoredDevice(sender, deviceId),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
await olmlib.encryptMessageForDevice(
|
await olmlib.encryptMessageForDevice(
|
||||||
encryptedContent.ciphertext,
|
encryptedContent.ciphertext,
|
||||||
this._baseApis.getUserId(),
|
this.baseApis.getUserId(),
|
||||||
this._baseApis.deviceId,
|
this.baseApis.deviceId,
|
||||||
this._baseApis.crypto.olmDevice,
|
this.baseApis.crypto.olmDevice,
|
||||||
sender,
|
sender,
|
||||||
this._baseApis.getStoredDevice(sender, deviceId),
|
this.baseApis.getStoredDevice(sender, deviceId),
|
||||||
payload,
|
payload,
|
||||||
);
|
);
|
||||||
const contentMap = {
|
const contentMap = {
|
||||||
@@ -508,26 +536,26 @@ export class SecretStorage extends EventEmitter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
logger.info(`Sending ${content.name} secret for ${deviceId}`);
|
logger.info(`Sending ${content.name} secret for ${deviceId}`);
|
||||||
this._baseApis.sendToDevice("m.room.encrypted", contentMap);
|
this.baseApis.sendToDevice("m.room.encrypted", contentMap);
|
||||||
} else {
|
} else {
|
||||||
logger.info(`Request denied for ${content.name} secret for ${deviceId}`);
|
logger.info(`Request denied for ${content.name} secret for ${deviceId}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onSecretReceived(event) {
|
public onSecretReceived(event: MatrixEvent): void {
|
||||||
if (event.getSender() !== this._baseApis.getUserId()) {
|
if (event.getSender() !== this.baseApis.getUserId()) {
|
||||||
// we shouldn't be receiving secrets from anyone else, so ignore
|
// we shouldn't be receiving secrets from anyone else, so ignore
|
||||||
// because someone could be trying to send us bogus data
|
// because someone could be trying to send us bogus data
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const content = event.getContent();
|
const content = event.getContent();
|
||||||
logger.log("got secret share for request", content.request_id);
|
logger.log("got secret share for request", content.request_id);
|
||||||
const requestControl = this._requests[content.request_id];
|
const requestControl = this.requests.get(content.request_id);
|
||||||
if (requestControl) {
|
if (requestControl) {
|
||||||
// make sure that the device that sent it is one of the devices that
|
// make sure that the device that sent it is one of the devices that
|
||||||
// we requested from
|
// we requested from
|
||||||
const deviceInfo = this._baseApis.crypto.deviceList.getDeviceByIdentityKey(
|
const deviceInfo = this.baseApis.crypto.deviceList.getDeviceByIdentityKey(
|
||||||
olmlib.OLM_ALGORITHM,
|
olmlib.OLM_ALGORITHM,
|
||||||
event.getSenderKey(),
|
event.getSenderKey(),
|
||||||
);
|
);
|
||||||
@@ -550,12 +578,14 @@ export class SecretStorage extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getSecretStorageKey(keys, name) {
|
private async getSecretStorageKey(
|
||||||
if (!this._cryptoCallbacks.getSecretStorageKey) {
|
keys: Record<string, ISecretStorageKeyInfo>, name: string,
|
||||||
|
): Promise<[string, IDecryptors]> {
|
||||||
|
if (!this.cryptoCallbacks.getSecretStorageKey) {
|
||||||
throw new Error("No getSecretStorageKey callback supplied");
|
throw new Error("No getSecretStorageKey callback supplied");
|
||||||
}
|
}
|
||||||
|
|
||||||
const returned = await this._cryptoCallbacks.getSecretStorageKey({ keys }, name);
|
const returned = await this.cryptoCallbacks.getSecretStorageKey({ keys }, name);
|
||||||
|
|
||||||
if (!returned) {
|
if (!returned) {
|
||||||
throw new Error("getSecretStorageKey callback returned falsey");
|
throw new Error("getSecretStorageKey callback returned falsey");
|
||||||
@@ -571,10 +601,10 @@ export class SecretStorage extends EventEmitter {
|
|||||||
|
|
||||||
if (keys[keyId].algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
|
if (keys[keyId].algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
|
||||||
const decryption = {
|
const decryption = {
|
||||||
encrypt: async function(secret) {
|
encrypt: async function(secret: string): Promise<IEncryptedPayload> {
|
||||||
return await encryptAES(secret, privateKey, name);
|
return await encryptAES(secret, privateKey, name);
|
||||||
},
|
},
|
||||||
decrypt: async function(encInfo) {
|
decrypt: async function(encInfo: IEncryptedPayload): Promise<string> {
|
||||||
return await decryptAES(encInfo, privateKey, name);
|
return await decryptAES(encInfo, privateKey, name);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -16,7 +16,7 @@ limitations under the License.
|
|||||||
|
|
||||||
import { DeviceInfo } from "./deviceinfo";
|
import { DeviceInfo } from "./deviceinfo";
|
||||||
import { IKeyBackupInfo } from "./keybackup";
|
import { IKeyBackupInfo } from "./keybackup";
|
||||||
import { ISecretStorageKeyInfo } from "../matrix";
|
import { ISecretStorageKeyInfo } from "./SecretStorage";
|
||||||
|
|
||||||
// TODO: Merge this with crypto.js once converted
|
// TODO: Merge this with crypto.js once converted
|
||||||
|
|
||||||
@@ -112,9 +112,17 @@ export interface ISecretStorageKey {
|
|||||||
keyInfo: ISecretStorageKeyInfo;
|
keyInfo: ISecretStorageKeyInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IPassphraseInfo {
|
||||||
|
algorithm: "m.pbkdf2";
|
||||||
|
iterations: number;
|
||||||
|
salt: string;
|
||||||
|
bits: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IAddSecretStorageKeyOpts {
|
export interface IAddSecretStorageKeyOpts {
|
||||||
// depends on algorithm
|
name: string;
|
||||||
// TODO: Types
|
passphrase: IPassphraseInfo;
|
||||||
|
key: Uint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IImportOpts {
|
export interface IImportOpts {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { IndexedDBCryptoStore } from '../crypto/store/indexeddb-crypto-store';
|
|||||||
import { decryptAES, encryptAES } from './aes';
|
import { decryptAES, encryptAES } from './aes';
|
||||||
import anotherjson from "another-json";
|
import anotherjson from "another-json";
|
||||||
import { logger } from '../logger';
|
import { logger } from '../logger';
|
||||||
import { ISecretStorageKeyInfo } from "../matrix";
|
import { ISecretStorageKeyInfo } from "./SecretStorage";
|
||||||
|
|
||||||
// FIXME: these types should eventually go in a different file
|
// FIXME: these types should eventually go in a different file
|
||||||
type Signatures = Record<string, Record<string, string>>;
|
type Signatures = Record<string, Record<string, string>>;
|
||||||
|
|||||||
@@ -33,7 +33,14 @@ import { DeviceInfo, IDevice } from "./deviceinfo";
|
|||||||
import * as algorithms from "./algorithms";
|
import * as algorithms from "./algorithms";
|
||||||
import { createCryptoStoreCacheCallbacks, CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from './CrossSigning';
|
import { createCryptoStoreCacheCallbacks, CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from './CrossSigning';
|
||||||
import { EncryptionSetupBuilder } from "./EncryptionSetup";
|
import { EncryptionSetupBuilder } from "./EncryptionSetup";
|
||||||
import { SECRET_STORAGE_ALGORITHM_V1_AES, SecretStorage } from './SecretStorage';
|
import {
|
||||||
|
SECRET_STORAGE_ALGORITHM_V1_AES,
|
||||||
|
SecretStorage,
|
||||||
|
ISecretStorageKeyInfo,
|
||||||
|
SecretStorageKeyTuple,
|
||||||
|
ISecretRequest,
|
||||||
|
} from './SecretStorage';
|
||||||
|
import { IAddSecretStorageKeyOpts } from "./api";
|
||||||
import { OutgoingRoomKeyRequestManager } from './OutgoingRoomKeyRequestManager';
|
import { OutgoingRoomKeyRequestManager } from './OutgoingRoomKeyRequestManager';
|
||||||
import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store';
|
import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store';
|
||||||
import { ReciprocateQRCode, SCAN_QR_CODE_METHOD, SHOW_QR_CODE_METHOD } from './verification/QRCode';
|
import { ReciprocateQRCode, SCAN_QR_CODE_METHOD, SHOW_QR_CODE_METHOD } from './verification/QRCode';
|
||||||
@@ -359,7 +366,8 @@ export class Crypto extends EventEmitter {
|
|||||||
const cacheCallbacks = createCryptoStoreCacheCallbacks(cryptoStore, this.olmDevice);
|
const cacheCallbacks = createCryptoStoreCacheCallbacks(cryptoStore, this.olmDevice);
|
||||||
|
|
||||||
this.crossSigningInfo = new CrossSigningInfo(userId, cryptoCallbacks, cacheCallbacks);
|
this.crossSigningInfo = new CrossSigningInfo(userId, cryptoCallbacks, cacheCallbacks);
|
||||||
this.secretStorage = new SecretStorage(baseApis, cryptoCallbacks);
|
// Yes, we pass the client twice here: see SecretStorage
|
||||||
|
this.secretStorage = new SecretStorage(baseApis, cryptoCallbacks, baseApis);
|
||||||
this.dehydrationManager = new DehydrationManager(this);
|
this.dehydrationManager = new DehydrationManager(this);
|
||||||
|
|
||||||
// Assuming no app-supplied callback, default to getting from SSSS.
|
// Assuming no app-supplied callback, default to getting from SSSS.
|
||||||
@@ -970,15 +978,17 @@ export class Crypto extends EventEmitter {
|
|||||||
logger.log("Secure Secret Storage ready");
|
logger.log("Secure Secret Storage ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
public addSecretStorageKey(algorithm: string, opts: any, keyID: string): any { // TODO types
|
public addSecretStorageKey(
|
||||||
|
algorithm: string, opts: IAddSecretStorageKeyOpts, keyID: string,
|
||||||
|
): Promise<{keyId: string, keyInfo: ISecretStorageKeyInfo}> {
|
||||||
return this.secretStorage.addKey(algorithm, opts, keyID);
|
return this.secretStorage.addKey(algorithm, opts, keyID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public hasSecretStorageKey(keyID: string): boolean {
|
public hasSecretStorageKey(keyID: string): Promise<boolean> {
|
||||||
return this.secretStorage.hasKey(keyID);
|
return this.secretStorage.hasKey(keyID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSecretStorageKey(keyID?: string): any { // TODO types
|
public getSecretStorageKey(keyID?: string): Promise<SecretStorageKeyTuple> {
|
||||||
return this.secretStorage.getKey(keyID);
|
return this.secretStorage.getKey(keyID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -990,11 +1000,13 @@ export class Crypto extends EventEmitter {
|
|||||||
return this.secretStorage.get(name);
|
return this.secretStorage.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public isSecretStored(name: string, checkKey?: boolean): any { // TODO types
|
public isSecretStored(
|
||||||
|
name: string, checkKey?: boolean,
|
||||||
|
): Promise<Record<string, ISecretStorageKeyInfo>> {
|
||||||
return this.secretStorage.isStored(name, checkKey);
|
return this.secretStorage.isStored(name, checkKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public requestSecret(name: string, devices: string[]): Promise<any> { // TODO types
|
public requestSecret(name: string, devices: string[]): ISecretRequest { // TODO types
|
||||||
if (!devices) {
|
if (!devices) {
|
||||||
devices = Object.keys(this.deviceList.getRawStoredDevicesForUser(this.userId));
|
devices = Object.keys(this.deviceList.getRawStoredDevicesForUser(this.userId));
|
||||||
}
|
}
|
||||||
@@ -1009,7 +1021,7 @@ export class Crypto extends EventEmitter {
|
|||||||
return this.secretStorage.setDefaultKeyId(k);
|
return this.secretStorage.setDefaultKeyId(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
public checkSecretStorageKey(key: string, info: any): Promise<boolean> { // TODO types
|
public checkSecretStorageKey(key: Uint8Array, info: ISecretStorageKeyInfo): Promise<boolean> {
|
||||||
return this.secretStorage.checkKey(key, info);
|
return this.secretStorage.checkKey(key, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2996,9 +3008,9 @@ export class Crypto extends EventEmitter {
|
|||||||
} else if (event.getType() == "m.room_key_request") {
|
} else if (event.getType() == "m.room_key_request") {
|
||||||
this.onRoomKeyRequestEvent(event);
|
this.onRoomKeyRequestEvent(event);
|
||||||
} else if (event.getType() === "m.secret.request") {
|
} else if (event.getType() === "m.secret.request") {
|
||||||
this.secretStorage._onRequestReceived(event);
|
this.secretStorage.onRequestReceived(event);
|
||||||
} else if (event.getType() === "m.secret.send") {
|
} else if (event.getType() === "m.secret.send") {
|
||||||
this.secretStorage._onSecretReceived(event);
|
this.secretStorage.onSecretReceived(event);
|
||||||
} else if (event.getType() === "org.matrix.room_key.withheld") {
|
} else if (event.getType() === "org.matrix.room_key.withheld") {
|
||||||
this.onRoomKeyWithheldEvent(event);
|
this.onRoomKeyWithheldEvent(event);
|
||||||
} else if (event.getContent().transaction_id) {
|
} else if (event.getContent().transaction_id) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { MatrixScheduler } from "./scheduler";
|
|||||||
import { MatrixClient } from "./client";
|
import { MatrixClient } from "./client";
|
||||||
import { ICreateClientOpts } from "./client";
|
import { ICreateClientOpts } from "./client";
|
||||||
import { DeviceTrustLevel } from "./crypto/CrossSigning";
|
import { DeviceTrustLevel } from "./crypto/CrossSigning";
|
||||||
|
import { ISecretStorageKeyInfo } from "./crypto/SecretStorage";
|
||||||
|
|
||||||
export * from "./client";
|
export * from "./client";
|
||||||
export * from "./http-api";
|
export * from "./http-api";
|
||||||
@@ -122,17 +123,6 @@ export interface ICryptoCallbacks {
|
|||||||
getBackupKey?: () => Promise<Uint8Array>;
|
getBackupKey?: () => Promise<Uint8Array>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Move this to `SecretStorage` once converted
|
|
||||||
export interface ISecretStorageKeyInfo {
|
|
||||||
passphrase?: {
|
|
||||||
algorithm: "m.pbkdf2";
|
|
||||||
iterations: number;
|
|
||||||
salt: string;
|
|
||||||
};
|
|
||||||
iv?: string;
|
|
||||||
mac?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a Matrix Client. Similar to {@link module:client.MatrixClient}
|
* Construct a Matrix Client. Similar to {@link module:client.MatrixClient}
|
||||||
* except that the 'request', 'store' and 'scheduler' dependencies are satisfied.
|
* except that the 'request', 'store' and 'scheduler' dependencies are satisfied.
|
||||||
|
|||||||
Reference in New Issue
Block a user