1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-25 05:23:13 +03:00

Adjust types and APIs to match React SDK

Various small tweaks and alignments to match React SDK as part of TypeScript
conversion.

Part of https://github.com/vector-im/element-web/issues/15350
This commit is contained in:
J. Ryan Stinnett
2020-10-09 17:21:11 +01:00
parent c6819e0450
commit e9590e9093
7 changed files with 92 additions and 48 deletions

View File

@@ -14,7 +14,7 @@
"build:minify-browser": "terser dist/browser-matrix.js --compress --mangle --source-map --output dist/browser-matrix.min.js", "build:minify-browser": "terser dist/browser-matrix.js --compress --mangle --source-map --output dist/browser-matrix.min.js",
"gendoc": "jsdoc -c jsdoc.json -P package.json", "gendoc": "jsdoc -c jsdoc.json -P package.json",
"lint": "yarn lint:types && yarn lint:js", "lint": "yarn lint:types && yarn lint:js",
"lint:js": "eslint --max-warnings 76 src spec", "lint:js": "eslint --max-warnings 73 src spec",
"lint:types": "tsc --noEmit", "lint:types": "tsc --noEmit",
"test": "jest spec/ --coverage --testEnvironment node", "test": "jest spec/ --coverage --testEnvironment node",
"test:watch": "jest spec/ --coverage --testEnvironment node --watch" "test:watch": "jest spec/ --coverage --testEnvironment node --watch"

View File

@@ -194,7 +194,7 @@ describe("Secrets", function() {
}; };
resetCrossSigningKeys(alice); resetCrossSigningKeys(alice);
const newKeyId = await alice.addSecretStorageKey( const { keyId: newKeyId } = await alice.addSecretStorageKey(
SECRET_STORAGE_ALGORITHM_V1_AES, SECRET_STORAGE_ALGORITHM_V1_AES,
); );
// we don't await on this because it waits for the event to come down the sync // we don't await on this because it waits for the event to come down the sync
@@ -226,8 +226,8 @@ describe("Secrets", function() {
], ],
{ {
cryptoCallbacks: { cryptoCallbacks: {
onSecretRequested: e => { onSecretRequested: (userId, deviceId, requestId, secretName, deviceTrust) => {
expect(e.name).toBe("foo"); expect(secretName).toBe("foo");
return "bar"; return "bar";
}, },
}, },

View File

@@ -235,7 +235,14 @@ function keyFromRecoverySession(session, decryptionKey) {
* { * {
* keys: { * keys: {
* <key name>: { * <key name>: {
* pubkey: {UInt8Array} * "algorithm": "m.secret_storage.v1.aes-hmac-sha2",
* "passphrase": {
* "algorithm": "m.pbkdf2",
* "iterations": 500000,
* "salt": "..."
* },
* "iv": "...",
* "mac": "..."
* }, ... * }, ...
* } * }
* } * }
@@ -247,6 +254,7 @@ function keyFromRecoverySession(session, decryptionKey) {
* desired to avoid user prompts. * desired to avoid user prompts.
* Args: * Args:
* {string} keyId the ID of the new key * {string} keyId the ID of the new key
* {object} keyInfo Infomation about the key as above for `getSecretStorageKey`
* {Uint8Array} key the new private key * {Uint8Array} key the new private key
* *
* @param {function} [opts.cryptoCallbacks.onSecretRequested] * @param {function} [opts.cryptoCallbacks.onSecretRequested]
@@ -1434,7 +1442,7 @@ wrapCryptoFuncs(MatrixClient, [
* containing the key, or rejects if the key cannot be obtained. * containing the key, or rejects if the key cannot be obtained.
* Returns: * Returns:
* {Promise} A promise which resolves to key creation data for * {Promise} A promise which resolves to key creation data for
* SecretStorage#addKey: an object with `passphrase` and/or `pubkey` fields. * SecretStorage#addKey: an object with `passphrase` etc fields.
*/ */
/** /**
@@ -1449,7 +1457,9 @@ wrapCryptoFuncs(MatrixClient, [
* @param {string} [keyName] the name of the key. If not given, a random * @param {string} [keyName] the name of the key. If not given, a random
* name will be generated. * name will be generated.
* *
* @return {string} the name of the key * @return {object} An object with:
* keyId: {string} the ID of the key
* keyInfo: {object} details about the key (iv, mac, passphrase)
*/ */
/** /**
@@ -2009,7 +2019,7 @@ MatrixClient.prototype.isValidRecoveryKey = function(recoveryKey) {
* *
* @param {string} password Passphrase * @param {string} password Passphrase
* @param {object} backupInfo Backup metadata from `checkKeyBackup` * @param {object} backupInfo Backup metadata from `checkKeyBackup`
* @return {Promise<Buffer>} key backup key * @return {Promise<Uint8Array>} key backup key
*/ */
MatrixClient.prototype.keyBackupKeyFromPassword = function( MatrixClient.prototype.keyBackupKeyFromPassword = function(
password, backupInfo, password, backupInfo,
@@ -2024,7 +2034,7 @@ MatrixClient.prototype.keyBackupKeyFromPassword = function(
* The cross-signing API is currently UNSTABLE and may change without notice. * The cross-signing API is currently UNSTABLE and may change without notice.
* *
* @param {string} recoveryKey The recovery key * @param {string} recoveryKey The recovery key
* @return {Buffer} key backup key * @return {Uint8Array} key backup key
*/ */
MatrixClient.prototype.keyBackupKeyFromRecoveryKey = function(recoveryKey) { MatrixClient.prototype.keyBackupKeyFromRecoveryKey = function(recoveryKey) {
return decodeRecoveryKey(recoveryKey); return decodeRecoveryKey(recoveryKey);

View File

@@ -344,14 +344,14 @@ class SSSSCryptoCallbacks {
} }
} }
addPrivateKey(keyId, privKey) { addPrivateKey(keyId, keyInfo, privKey) {
this._privateKeys.set(keyId, privKey); this._privateKeys.set(keyId, privKey);
// Also pass along to application to cache if it wishes // Also pass along to application to cache if it wishes
if ( if (
this._delegateCryptoCallbacks && this._delegateCryptoCallbacks &&
this._delegateCryptoCallbacks.cacheSecretStorageKey this._delegateCryptoCallbacks.cacheSecretStorageKey
) { ) {
this._delegateCryptoCallbacks.cacheSecretStorageKey(keyId, privKey); this._delegateCryptoCallbacks.cacheSecretStorageKey(keyId, keyInfo, privKey);
} }
} }
} }

View File

@@ -81,25 +81,27 @@ export class SecretStorage extends EventEmitter {
* @param {string} [keyId] the ID of the key. If not given, a random * @param {string} [keyId] the ID of the key. If not given, a random
* ID will be generated. * ID will be generated.
* *
* @return {string} the ID of the key * @return {object} An object with:
* keyId: {string} the ID of the key
* keyInfo: {object} details about the key (iv, mac, passphrase)
*/ */
async addKey(algorithm, opts, keyId) { async addKey(algorithm, opts, keyId) {
const keyData = {algorithm}; const keyInfo = {algorithm};
if (!opts) opts = {}; if (!opts) opts = {};
if (opts.name) { if (opts.name) {
keyData.name = opts.name; keyInfo.name = opts.name;
} }
if (algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) { if (algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
if (opts.passphrase) { if (opts.passphrase) {
keyData.passphrase = opts.passphrase; keyInfo.passphrase = opts.passphrase;
} }
if (opts.key) { if (opts.key) {
const {iv, mac} = await SecretStorage._calculateKeyCheck(opts.key); const {iv, mac} = await SecretStorage._calculateKeyCheck(opts.key);
keyData.iv = iv; keyInfo.iv = iv;
keyData.mac = mac; keyInfo.mac = mac;
} }
} else { } else {
throw new Error(`Unknown key algorithm ${opts.algorithm}`); throw new Error(`Unknown key algorithm ${opts.algorithm}`);
@@ -116,10 +118,13 @@ export class SecretStorage extends EventEmitter {
} }
await this._baseApis.setAccountData( await this._baseApis.setAccountData(
`m.secret_storage.key.${keyId}`, keyData, `m.secret_storage.key.${keyId}`, keyInfo,
); );
return keyId; return {
keyId,
keyInfo,
};
} }
/** /**
@@ -457,13 +462,13 @@ export class SecretStorage extends EventEmitter {
if (!this._cryptoCallbacks.onSecretRequested) { if (!this._cryptoCallbacks.onSecretRequested) {
return; return;
} }
const secret = await this._cryptoCallbacks.onSecretRequested({ const secret = await this._cryptoCallbacks.onSecretRequested(
user_id: sender, sender,
device_id: deviceId, deviceId,
request_id: content.request_id, content.request_id,
name: content.name, content.name,
device_trust: 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}`);
const payload = { const payload = {

View File

@@ -633,7 +633,7 @@ Crypto.prototype.bootstrapCrossSigning = async function({
* containing the key, or rejects if the key cannot be obtained. * containing the key, or rejects if the key cannot be obtained.
* Returns: * Returns:
* {Promise} A promise which resolves to key creation data for * {Promise} A promise which resolves to key creation data for
* SecretStorage#addKey: an object with `passphrase` and/or `pubkey` fields. * SecretStorage#addKey: an object with `passphrase` etc fields.
*/ */
Crypto.prototype.bootstrapSecretStorage = async function({ Crypto.prototype.bootstrapSecretStorage = async function({
createSecretStorageKey = async () => ({ }), createSecretStorageKey = async () => ({ }),
@@ -663,13 +663,13 @@ Crypto.prototype.bootstrapSecretStorage = async function({
opts.key = privateKey; opts.key = privateKey;
} }
const keyId = await secretStorage.addKey( const { keyId, keyInfo } = await secretStorage.addKey(
SECRET_STORAGE_ALGORITHM_V1_AES, opts, SECRET_STORAGE_ALGORITHM_V1_AES, opts,
); );
if (privateKey) { if (privateKey) {
// make the private key available to encrypt 4S secrets // make the private key available to encrypt 4S secrets
builder.ssssCryptoCallbacks.addPrivateKey(keyId, privateKey); builder.ssssCryptoCallbacks.addPrivateKey(keyId, keyInfo, privateKey);
} }
await secretStorage.setDefaultKeyId(keyId); await secretStorage.setDefaultKeyId(keyId);
@@ -682,9 +682,9 @@ Crypto.prototype.bootstrapSecretStorage = async function({
{keys: {[keyId]: keyInfo}}, "", {keys: {[keyId]: keyInfo}}, "",
); );
if (key) { if (key) {
const keyData = key[1]; const privateKey = key[1];
builder.ssssCryptoCallbacks.addPrivateKey(keyId, keyData); builder.ssssCryptoCallbacks.addPrivateKey(keyId, keyInfo, privateKey);
const {iv, mac} = await SecretStorage._calculateKeyCheck(keyData); const {iv, mac} = await SecretStorage._calculateKeyCheck(privateKey);
keyInfo.iv = iv; keyInfo.iv = iv;
keyInfo.mac = mac; keyInfo.mac = mac;

View File

@@ -1,7 +1,7 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd Copyright 2017 Vector Creations Ltd
Copyright 2019 The Matrix.org Foundation C.I.C. Copyright 2019, 2020 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.
@@ -113,7 +113,7 @@ export function setCryptoStoreFactory(fac) {
cryptoStoreFactory = fac; cryptoStoreFactory = fac;
} }
interface ICreateClientOpts { export interface ICreateClientOpts {
baseUrl: string; baseUrl: string;
idBaseUrl?: string; idBaseUrl?: string;
store?: Store; store?: Store;
@@ -126,6 +126,7 @@ interface ICreateClientOpts {
identityServer?: any; identityServer?: any;
localTimeoutMs?: number; localTimeoutMs?: number;
useAuthorizationHeader?: boolean; useAuthorizationHeader?: boolean;
timelineSupport?: boolean;
queryParams?: Record<string, unknown>; queryParams?: Record<string, unknown>;
deviceToImport?: { deviceToImport?: {
olmDevice: { olmDevice: {
@@ -136,26 +137,54 @@ interface ICreateClientOpts {
userId: string; userId: string;
deviceId: string; deviceId: string;
}; };
pickleKey?: string;
sessionStore?: any; sessionStore?: any;
unstableClientRelationAggregation?: boolean; unstableClientRelationAggregation?: boolean;
verificationMethods?: Array<any>; verificationMethods?: Array<any>;
forceTURN?: boolean; forceTURN?: boolean;
fallbackICEServerAllowed?: boolean; fallbackICEServerAllowed?: boolean;
cryptoCallbacks?: { cryptoCallbacks?: ICryptoCallbacks;
}
export interface ICryptoCallbacks {
getCrossSigningKey?: (keyType: string, pubKey: Uint8Array) => Promise<Uint8Array>; getCrossSigningKey?: (keyType: string, pubKey: Uint8Array) => Promise<Uint8Array>;
saveCrossSigningKeys?: (keys: Record<string, Uint8Array>) => unknown; saveCrossSigningKeys?: (keys: Record<string, Uint8Array>) => unknown;
shouldUpgradeDeviceVerifications?: ( shouldUpgradeDeviceVerifications?: (
users: Record<string, any> users: Record<string, any>
) => Promise<Array<string>>; ) => Promise<Array<string>>;
getSecretStorageKey?: ( getSecretStorageKey?: (
keys: {keys: Record<string, {pubkey: Uint8Array}>}, name: string keys: {keys: Record<string, ISecretStorageKeyInfo>}, name: string
) => Promise<[string, Uint8Array] | null>; ) => Promise<[string, Uint8Array] | null>;
cacheSecretStorageKey?: (keyId: string, key: Uint8Array) => unknown; cacheSecretStorageKey?: (
keyId: string, keyInfo: ISecretStorageKeyInfo, key: Uint8Array
) => void;
onSecretRequested?: ( onSecretRequested?: (
name: string, userId: string, deviceId: string, userId: string, deviceId: string,
requestId: string, deviceTrust: any requestId: string, secretName: string, deviceTrust: IDeviceTrustLevel
) => Promise<string>; ) => Promise<string>;
getDehydrationKey?: (
keyInfo: ISecretStorageKeyInfo,
checkFunc: (Uint8Array) => void,
) => Promise<Uint8Array>;
}
// TODO: Move this to `SecretStorage` once converted
export interface ISecretStorageKeyInfo {
passphrase?: {
algorithm: "m.pbkdf2";
iterations: number;
salt: string;
}; };
iv?: string;
mac?: string;
}
// TODO: Move this to `CrossSigning` once converted
export interface IDeviceTrustLevel {
isVerified(): boolean;
isCrossSigningVerified(): boolean;
isLocallyVerified(): boolean;
isTofu(): boolean;
} }
/** /**