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",
"gendoc": "jsdoc -c jsdoc.json -P package.json",
"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",
"test": "jest spec/ --coverage --testEnvironment node",
"test:watch": "jest spec/ --coverage --testEnvironment node --watch"

View File

@@ -194,7 +194,7 @@ describe("Secrets", function() {
};
resetCrossSigningKeys(alice);
const newKeyId = await alice.addSecretStorageKey(
const { keyId: newKeyId } = await alice.addSecretStorageKey(
SECRET_STORAGE_ALGORITHM_V1_AES,
);
// 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: {
onSecretRequested: e => {
expect(e.name).toBe("foo");
onSecretRequested: (userId, deviceId, requestId, secretName, deviceTrust) => {
expect(secretName).toBe("foo");
return "bar";
},
},

View File

@@ -235,7 +235,14 @@ function keyFromRecoverySession(session, decryptionKey) {
* {
* keys: {
* <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.
* Args:
* {string} keyId the ID of the new key
* {object} keyInfo Infomation about the key as above for `getSecretStorageKey`
* {Uint8Array} key the new private key
*
* @param {function} [opts.cryptoCallbacks.onSecretRequested]
@@ -1434,7 +1442,7 @@ wrapCryptoFuncs(MatrixClient, [
* containing the key, or rejects if the key cannot be obtained.
* Returns:
* {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
* 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 {object} backupInfo Backup metadata from `checkKeyBackup`
* @return {Promise<Buffer>} key backup key
* @return {Promise<Uint8Array>} key backup key
*/
MatrixClient.prototype.keyBackupKeyFromPassword = function(
password, backupInfo,
@@ -2024,7 +2034,7 @@ MatrixClient.prototype.keyBackupKeyFromPassword = function(
* The cross-signing API is currently UNSTABLE and may change without notice.
*
* @param {string} recoveryKey The recovery key
* @return {Buffer} key backup key
* @return {Uint8Array} key backup key
*/
MatrixClient.prototype.keyBackupKeyFromRecoveryKey = function(recoveryKey) {
return decodeRecoveryKey(recoveryKey);

View File

@@ -344,14 +344,14 @@ class SSSSCryptoCallbacks {
}
}
addPrivateKey(keyId, privKey) {
addPrivateKey(keyId, keyInfo, privKey) {
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, 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
* 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) {
const keyData = {algorithm};
const keyInfo = {algorithm};
if (!opts) opts = {};
if (opts.name) {
keyData.name = opts.name;
keyInfo.name = opts.name;
}
if (algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
if (opts.passphrase) {
keyData.passphrase = opts.passphrase;
keyInfo.passphrase = opts.passphrase;
}
if (opts.key) {
const {iv, mac} = await SecretStorage._calculateKeyCheck(opts.key);
keyData.iv = iv;
keyData.mac = mac;
keyInfo.iv = iv;
keyInfo.mac = mac;
}
} else {
throw new Error(`Unknown key algorithm ${opts.algorithm}`);
@@ -116,10 +118,13 @@ export class SecretStorage extends EventEmitter {
}
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) {
return;
}
const secret = await this._cryptoCallbacks.onSecretRequested({
user_id: sender,
device_id: deviceId,
request_id: content.request_id,
name: content.name,
device_trust: this._baseApis.checkDeviceTrust(sender, deviceId),
});
const secret = await this._cryptoCallbacks.onSecretRequested(
sender,
deviceId,
content.request_id,
content.name,
this._baseApis.checkDeviceTrust(sender, deviceId),
);
if (secret) {
logger.info(`Preparing ${content.name} secret for ${deviceId}`);
const payload = {

View File

@@ -633,7 +633,7 @@ Crypto.prototype.bootstrapCrossSigning = async function({
* containing the key, or rejects if the key cannot be obtained.
* Returns:
* {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({
createSecretStorageKey = async () => ({ }),
@@ -663,13 +663,13 @@ Crypto.prototype.bootstrapSecretStorage = async function({
opts.key = privateKey;
}
const keyId = await secretStorage.addKey(
const { keyId, keyInfo } = await secretStorage.addKey(
SECRET_STORAGE_ALGORITHM_V1_AES, opts,
);
if (privateKey) {
// make the private key available to encrypt 4S secrets
builder.ssssCryptoCallbacks.addPrivateKey(keyId, privateKey);
builder.ssssCryptoCallbacks.addPrivateKey(keyId, keyInfo, privateKey);
}
await secretStorage.setDefaultKeyId(keyId);
@@ -682,9 +682,9 @@ Crypto.prototype.bootstrapSecretStorage = async function({
{keys: {[keyId]: keyInfo}}, "",
);
if (key) {
const keyData = key[1];
builder.ssssCryptoCallbacks.addPrivateKey(keyId, keyData);
const {iv, mac} = await SecretStorage._calculateKeyCheck(keyData);
const privateKey = key[1];
builder.ssssCryptoCallbacks.addPrivateKey(keyId, keyInfo, privateKey);
const {iv, mac} = await SecretStorage._calculateKeyCheck(privateKey);
keyInfo.iv = iv;
keyInfo.mac = mac;

View File

@@ -1,7 +1,7 @@
/*
Copyright 2015, 2016 OpenMarket 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");
you may not use this file except in compliance with the License.
@@ -113,7 +113,7 @@ export function setCryptoStoreFactory(fac) {
cryptoStoreFactory = fac;
}
interface ICreateClientOpts {
export interface ICreateClientOpts {
baseUrl: string;
idBaseUrl?: string;
store?: Store;
@@ -126,6 +126,7 @@ interface ICreateClientOpts {
identityServer?: any;
localTimeoutMs?: number;
useAuthorizationHeader?: boolean;
timelineSupport?: boolean;
queryParams?: Record<string, unknown>;
deviceToImport?: {
olmDevice: {
@@ -136,26 +137,54 @@ interface ICreateClientOpts {
userId: string;
deviceId: string;
};
pickleKey?: string;
sessionStore?: any;
unstableClientRelationAggregation?: boolean;
verificationMethods?: Array<any>;
forceTURN?: boolean;
fallbackICEServerAllowed?: boolean;
cryptoCallbacks?: {
getCrossSigningKey?: (keyType: string, pubKey: Uint8Array) => Promise<Uint8Array>;
saveCrossSigningKeys?: (keys: Record<string, Uint8Array>) => unknown;
shouldUpgradeDeviceVerifications?: (
users: Record<string, any>
) => Promise<Array<string>>;
getSecretStorageKey?: (
keys: {keys: Record<string, {pubkey: Uint8Array}>}, name: string
) => Promise<[string, Uint8Array] | null>;
cacheSecretStorageKey?: (keyId: string, key: Uint8Array) => unknown;
onSecretRequested?: (
name: string, userId: string, deviceId: string,
requestId: string, deviceTrust: any
) => Promise<string>;
cryptoCallbacks?: ICryptoCallbacks;
}
export interface ICryptoCallbacks {
getCrossSigningKey?: (keyType: string, pubKey: Uint8Array) => Promise<Uint8Array>;
saveCrossSigningKeys?: (keys: Record<string, Uint8Array>) => unknown;
shouldUpgradeDeviceVerifications?: (
users: Record<string, any>
) => Promise<Array<string>>;
getSecretStorageKey?: (
keys: {keys: Record<string, ISecretStorageKeyInfo>}, name: string
) => Promise<[string, Uint8Array] | null>;
cacheSecretStorageKey?: (
keyId: string, keyInfo: ISecretStorageKeyInfo, key: Uint8Array
) => void;
onSecretRequested?: (
userId: string, deviceId: string,
requestId: string, secretName: string, deviceTrust: IDeviceTrustLevel
) => 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;
}
/**