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

Split up, rename, and move ISecretStorageKeyInfo (#3242)

* Move SecretStorageKeyInfo interfaces out to a new module

* Replace usages of ISecretStorageKeyInfo with SecretStorageKeyDescription
This commit is contained in:
Richard van der Hoff
2023-04-03 11:11:03 +01:00
committed by GitHub
parent 78aa6cb62b
commit 41d3ffdab9
10 changed files with 149 additions and 64 deletions

View File

@@ -25,10 +25,10 @@ import { encryptAES } from "../../../src/crypto/aes";
import { createSecretStorageKey, resetCrossSigningKeys } from "./crypto-utils"; import { createSecretStorageKey, resetCrossSigningKeys } from "./crypto-utils";
import { logger } from "../../../src/logger"; import { logger } from "../../../src/logger";
import { ClientEvent, ICreateClientOpts, ICrossSigningKey, MatrixClient } from "../../../src/client"; import { ClientEvent, ICreateClientOpts, ICrossSigningKey, MatrixClient } from "../../../src/client";
import { ISecretStorageKeyInfo } from "../../../src/crypto/api";
import { DeviceInfo } from "../../../src/crypto/deviceinfo"; import { DeviceInfo } from "../../../src/crypto/deviceinfo";
import { ISignatures } from "../../../src/@types/signed"; import { ISignatures } from "../../../src/@types/signed";
import { ICurve25519AuthData } from "../../../src/crypto/keybackup"; import { ICurve25519AuthData } from "../../../src/crypto/keybackup";
import { SecretStorageKeyDescription } from "../../../src/secret-storage";
async function makeTestClient( async function makeTestClient(
userInfo: { userId: string; deviceId: string }, userInfo: { userId: string; deviceId: string },
@@ -541,7 +541,9 @@ describe("Secrets", function () {
await alice.bootstrapSecretStorage({}); await alice.bootstrapSecretStorage({});
expect(alice.getAccountData("m.secret_storage.default_key")!.getContent()).toEqual({ key: "key_id" }); expect(alice.getAccountData("m.secret_storage.default_key")!.getContent()).toEqual({ key: "key_id" });
const keyInfo = alice.getAccountData("m.secret_storage.key.key_id")!.getContent<ISecretStorageKeyInfo>(); const keyInfo = alice
.getAccountData("m.secret_storage.key.key_id")!
.getContent<SecretStorageKeyDescription>();
expect(keyInfo.algorithm).toEqual("m.secret_storage.v1.aes-hmac-sha2"); expect(keyInfo.algorithm).toEqual("m.secret_storage.v1.aes-hmac-sha2");
expect(keyInfo.passphrase).toEqual({ expect(keyInfo.passphrase).toEqual({
algorithm: "m.pbkdf2", algorithm: "m.pbkdf2",

View File

@@ -106,7 +106,6 @@ import {
IEncryptedEventInfo, IEncryptedEventInfo,
IImportRoomKeysOpts, IImportRoomKeysOpts,
IRecoveryKey, IRecoveryKey,
ISecretStorageKeyInfo,
} from "./crypto/api"; } from "./crypto/api";
import { EventTimelineSet } from "./models/event-timeline-set"; import { EventTimelineSet } from "./models/event-timeline-set";
import { VerificationRequest } from "./crypto/verification/request/VerificationRequest"; import { VerificationRequest } from "./crypto/verification/request/VerificationRequest";
@@ -208,6 +207,7 @@ import { CryptoBackend } from "./common-crypto/CryptoBackend";
import { RUST_SDK_STORE_PREFIX } from "./rust-crypto/constants"; import { RUST_SDK_STORE_PREFIX } from "./rust-crypto/constants";
import { CryptoApi } from "./crypto-api"; import { CryptoApi } from "./crypto-api";
import { DeviceInfoMap } from "./crypto/DeviceList"; import { DeviceInfoMap } from "./crypto/DeviceList";
import { SecretStorageKeyDescription } from "./secret-storage";
export type Store = IStore; export type Store = IStore;
@@ -2463,7 +2463,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
return this.crypto.beginKeyVerification(method, userId, deviceId); return this.crypto.beginKeyVerification(method, userId, deviceId);
} }
public checkSecretStorageKey(key: Uint8Array, info: ISecretStorageKeyInfo): Promise<boolean> { public checkSecretStorageKey(key: Uint8Array, info: SecretStorageKeyDescription): Promise<boolean> {
if (!this.crypto) { if (!this.crypto) {
throw new Error("End-to-end encryption disabled"); throw new Error("End-to-end encryption disabled");
} }
@@ -2863,7 +2863,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
algorithm: string, algorithm: string,
opts: IAddSecretStorageKeyOpts, opts: IAddSecretStorageKeyOpts,
keyName?: string, keyName?: string,
): Promise<{ keyId: string; keyInfo: ISecretStorageKeyInfo }> { ): Promise<{ keyId: string; keyInfo: SecretStorageKeyDescription }> {
if (!this.crypto) { if (!this.crypto) {
throw new Error("End-to-end encryption disabled"); throw new Error("End-to-end encryption disabled");
} }
@@ -2929,7 +2929,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* 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): Promise<Record<string, ISecretStorageKeyInfo> | null> { public isSecretStored(name: string): Promise<Record<string, SecretStorageKeyDescription> | null> {
if (!this.crypto) { if (!this.crypto) {
throw new Error("End-to-end encryption disabled"); throw new Error("End-to-end encryption disabled");
} }
@@ -3306,7 +3306,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* encrypted with, or null if it is not present or not encrypted with a * encrypted with, or null if it is not present or not encrypted with a
* trusted key * trusted key
*/ */
public isKeyBackupKeyStored(): Promise<Record<string, ISecretStorageKeyInfo> | null> { public isKeyBackupKeyStored(): Promise<Record<string, SecretStorageKeyDescription> | null> {
return Promise.resolve(this.isSecretStored("m.megolm_backup.v1")); return Promise.resolve(this.isSecretStored("m.megolm_backup.v1"));
} }

View File

@@ -31,7 +31,7 @@ import { OlmDevice } from "./OlmDevice";
import { ICryptoCallbacks } from "."; import { ICryptoCallbacks } from ".";
import { ISignatures } from "../@types/signed"; import { ISignatures } from "../@types/signed";
import { CryptoStore, SecretStorePrivateKeys } from "./store/base"; import { CryptoStore, SecretStorePrivateKeys } from "./store/base";
import { ISecretStorageKeyInfo } from "./api"; import { SecretStorageKeyDescription } from "../secret-storage";
const KEY_REQUEST_TIMEOUT_MS = 1000 * 60; const KEY_REQUEST_TIMEOUT_MS = 1000 * 60;
@@ -169,7 +169,7 @@ export class CrossSigningInfo {
// check what SSSS keys have encrypted the master key (if any) // check what SSSS keys have encrypted the master key (if any)
const stored = (await secretStorage.isStored("m.cross_signing.master")) || {}; const stored = (await secretStorage.isStored("m.cross_signing.master")) || {};
// then check which of those SSSS keys have also encrypted the SSK and USK // then check which of those SSSS keys have also encrypted the SSK and USK
function intersect(s: Record<string, ISecretStorageKeyInfo>): void { function intersect(s: Record<string, SecretStorageKeyDescription>): void {
for (const k of Object.keys(stored)) { for (const k of Object.keys(stored)) {
if (!s[k]) { if (!s[k]) {
delete stored[k]; delete stored[k];

View File

@@ -28,10 +28,10 @@ import {
ISignedKey, ISignedKey,
KeySignatures, KeySignatures,
} from "../client"; } from "../client";
import { ISecretStorageKeyInfo } from "./api";
import { IKeyBackupInfo } from "./keybackup"; import { IKeyBackupInfo } from "./keybackup";
import { TypedEventEmitter } from "../models/typed-event-emitter"; import { TypedEventEmitter } from "../models/typed-event-emitter";
import { IAccountDataClient } from "./SecretStorage"; import { IAccountDataClient } from "./SecretStorage";
import { SecretStorageKeyDescription } from "../secret-storage";
interface ICrossSigningKeys { interface ICrossSigningKeys {
authUpload: IBootstrapCrossSigningOpts["authUploadDeviceSigningKeys"]; authUpload: IBootstrapCrossSigningOpts["authUploadDeviceSigningKeys"];
@@ -326,7 +326,7 @@ class SSSSCryptoCallbacks {
public constructor(private readonly delegateCryptoCallbacks?: ICryptoCallbacks) {} public constructor(private readonly delegateCryptoCallbacks?: ICryptoCallbacks) {}
public async getSecretStorageKey( public async getSecretStorageKey(
{ keys }: { keys: Record<string, ISecretStorageKeyInfo> }, { keys }: { keys: Record<string, SecretStorageKeyDescription> },
name: string, name: string,
): Promise<[string, Uint8Array] | null> { ): Promise<[string, Uint8Array] | null> {
for (const keyId of Object.keys(keys)) { for (const keyId of Object.keys(keys)) {
@@ -348,7 +348,7 @@ class SSSSCryptoCallbacks {
return null; return null;
} }
public addPrivateKey(keyId: string, keyInfo: ISecretStorageKeyInfo, privKey: Uint8Array): void { public addPrivateKey(keyId: string, keyInfo: SecretStorageKeyDescription, privKey: Uint8Array): void {
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
this.delegateCryptoCallbacks?.cacheSecretStorageKey?.(keyId, keyInfo, privKey); this.delegateCryptoCallbacks?.cacheSecretStorageKey?.(keyId, keyInfo, privKey);

View File

@@ -23,16 +23,17 @@ import { calculateKeyCheck, decryptAES, encryptAES, IEncryptedPayload } from "./
import { ICryptoCallbacks, IEncryptedContent } from "."; import { ICryptoCallbacks, IEncryptedContent } from ".";
import { IContent, MatrixEvent } from "../models/event"; import { IContent, MatrixEvent } from "../models/event";
import { ClientEvent, ClientEventHandlerMap, MatrixClient } from "../client"; import { ClientEvent, ClientEventHandlerMap, MatrixClient } from "../client";
import { IAddSecretStorageKeyOpts, ISecretStorageKeyInfo } from "./api"; import { IAddSecretStorageKeyOpts } from "./api";
import { TypedEventEmitter } from "../models/typed-event-emitter"; import { TypedEventEmitter } from "../models/typed-event-emitter";
import { defer, IDeferred } from "../utils"; import { defer, IDeferred } from "../utils";
import { ToDeviceMessageId } from "../@types/event"; import { ToDeviceMessageId } from "../@types/event";
import { SecretStorageKeyDescription, SecretStorageKeyDescriptionAesV1 } from "../secret-storage";
export const SECRET_STORAGE_ALGORITHM_V1_AES = "m.secret_storage.v1.aes-hmac-sha2"; export const SECRET_STORAGE_ALGORITHM_V1_AES = "m.secret_storage.v1.aes-hmac-sha2";
// Some of the key functions use a tuple and some use an object... // Some of the key functions use a tuple and some use an object...
export type SecretStorageKeyTuple = [keyId: string, keyInfo: ISecretStorageKeyInfo]; export type SecretStorageKeyTuple = [keyId: string, keyInfo: SecretStorageKeyDescription];
export type SecretStorageKeyObject = { keyId: string; keyInfo: ISecretStorageKeyInfo }; export type SecretStorageKeyObject = { keyId: string; keyInfo: SecretStorageKeyDescription };
export interface ISecretRequest { export interface ISecretRequest {
requestId: string; requestId: string;
@@ -127,13 +128,16 @@ export class SecretStorage<B extends MatrixClient | undefined = MatrixClient> {
opts: IAddSecretStorageKeyOpts = {}, opts: IAddSecretStorageKeyOpts = {},
keyId?: string, keyId?: string,
): Promise<SecretStorageKeyObject> { ): Promise<SecretStorageKeyObject> {
const keyInfo = { algorithm } as ISecretStorageKeyInfo; if (algorithm !== SECRET_STORAGE_ALGORITHM_V1_AES) {
throw new Error(`Unknown key algorithm ${algorithm}`);
}
const keyInfo = { algorithm } as SecretStorageKeyDescriptionAesV1;
if (opts.name) { if (opts.name) {
keyInfo.name = opts.name; keyInfo.name = opts.name;
} }
if (algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
if (opts.passphrase) { if (opts.passphrase) {
keyInfo.passphrase = opts.passphrase; keyInfo.passphrase = opts.passphrase;
} }
@@ -142,15 +146,12 @@ export class SecretStorage<B extends MatrixClient | undefined = MatrixClient> {
keyInfo.iv = iv; keyInfo.iv = iv;
keyInfo.mac = mac; keyInfo.mac = mac;
} }
} else {
throw new Error(`Unknown key algorithm ${algorithm}`);
}
if (!keyId) { if (!keyId) {
do { do {
keyId = randomString(32); keyId = randomString(32);
} while ( } while (
await this.accountDataAdapter.getAccountDataFromServer<ISecretStorageKeyInfo>( await this.accountDataAdapter.getAccountDataFromServer<SecretStorageKeyDescription>(
`m.secret_storage.key.${keyId}`, `m.secret_storage.key.${keyId}`,
) )
); );
@@ -181,7 +182,7 @@ export class SecretStorage<B extends MatrixClient | undefined = MatrixClient> {
return null; return null;
} }
const keyInfo = await this.accountDataAdapter.getAccountDataFromServer<ISecretStorageKeyInfo>( const keyInfo = await this.accountDataAdapter.getAccountDataFromServer<SecretStorageKeyDescription>(
"m.secret_storage.key." + keyId, "m.secret_storage.key." + keyId,
); );
return keyInfo ? [keyId, keyInfo] : null; return keyInfo ? [keyId, keyInfo] : null;
@@ -206,7 +207,7 @@ export class SecretStorage<B extends MatrixClient | undefined = MatrixClient> {
* *
* @returns whether or not the key matches * @returns whether or not the key matches
*/ */
public async checkKey(key: Uint8Array, info: ISecretStorageKeyInfo): Promise<boolean> { public async checkKey(key: Uint8Array, info: SecretStorageKeyDescription): 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 calculateKeyCheck(key, info.iv); const { mac } = await calculateKeyCheck(key, info.iv);
@@ -245,7 +246,7 @@ export class SecretStorage<B extends MatrixClient | undefined = MatrixClient> {
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.accountDataAdapter.getAccountDataFromServer<ISecretStorageKeyInfo>( const keyInfo = await this.accountDataAdapter.getAccountDataFromServer<SecretStorageKeyDescription>(
"m.secret_storage.key." + keyId, "m.secret_storage.key." + keyId,
); );
if (!keyInfo) { if (!keyInfo) {
@@ -284,10 +285,10 @@ export class SecretStorage<B extends MatrixClient | undefined = MatrixClient> {
} }
// get possible keys to decrypt // get possible keys to decrypt
const keys: Record<string, ISecretStorageKeyInfo> = {}; const keys: Record<string, SecretStorageKeyDescription> = {};
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.accountDataAdapter.getAccountDataFromServer<ISecretStorageKeyInfo>( const keyInfo = await this.accountDataAdapter.getAccountDataFromServer<SecretStorageKeyDescription>(
"m.secret_storage.key." + keyId, "m.secret_storage.key." + keyId,
); );
const encInfo = secretInfo.encrypted[keyId]; const encInfo = secretInfo.encrypted[keyId];
@@ -322,17 +323,17 @@ export class SecretStorage<B extends MatrixClient | undefined = MatrixClient> {
* 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 async isStored(name: string): Promise<Record<string, ISecretStorageKeyInfo> | null> { public async isStored(name: string): Promise<Record<string, SecretStorageKeyDescription> | null> {
// check if secret exists // check if secret exists
const secretInfo = await this.accountDataAdapter.getAccountDataFromServer<ISecretInfo>(name); const secretInfo = await this.accountDataAdapter.getAccountDataFromServer<ISecretInfo>(name);
if (!secretInfo?.encrypted) return null; if (!secretInfo?.encrypted) return null;
const ret: Record<string, ISecretStorageKeyInfo> = {}; const ret: Record<string, SecretStorageKeyDescription> = {};
// 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.accountDataAdapter.getAccountDataFromServer<ISecretStorageKeyInfo>( const keyInfo = await this.accountDataAdapter.getAccountDataFromServer<SecretStorageKeyDescription>(
"m.secret_storage.key." + keyId, "m.secret_storage.key." + keyId,
); );
if (!keyInfo) continue; if (!keyInfo) continue;
@@ -544,7 +545,7 @@ export class SecretStorage<B extends MatrixClient | undefined = MatrixClient> {
} }
private async getSecretStorageKey( private async getSecretStorageKey(
keys: Record<string, ISecretStorageKeyInfo>, keys: Record<string, SecretStorageKeyDescription>,
name: string, name: string,
): Promise<[string, IDecryptors]> { ): Promise<[string, IDecryptors]> {
if (!this.cryptoCallbacks.getSecretStorageKey) { if (!this.cryptoCallbacks.getSecretStorageKey) {

View File

@@ -16,6 +16,13 @@ limitations under the License.
import { DeviceInfo } from "./deviceinfo"; import { DeviceInfo } from "./deviceinfo";
import { IKeyBackupInfo } from "./keybackup"; import { IKeyBackupInfo } from "./keybackup";
import { PassphraseInfo } from "../secret-storage";
/* re-exports for backwards compatibility. */
export {
PassphraseInfo as IPassphraseInfo,
SecretStorageKeyDescription as ISecretStorageKeyInfo,
} from "../secret-storage";
// TODO: Merge this with crypto.js once converted // TODO: Merge this with crypto.js once converted
@@ -98,26 +105,9 @@ export interface ICreateSecretStorageOpts {
getKeyBackupPassphrase?: () => Promise<Uint8Array>; getKeyBackupPassphrase?: () => Promise<Uint8Array>;
} }
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 interface IPassphraseInfo {
algorithm: "m.pbkdf2";
iterations: number;
salt: string;
bits?: number;
}
export interface IAddSecretStorageKeyOpts { export interface IAddSecretStorageKeyOpts {
pubkey?: string; pubkey?: string;
passphrase?: IPassphraseInfo; passphrase?: PassphraseInfo;
name?: string; name?: string;
key?: Uint8Array; key?: Uint8Array;
} }

View File

@@ -21,13 +21,13 @@ import { decodeBase64, encodeBase64 } from "./olmlib";
import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store"; import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store";
import { decryptAES, encryptAES } from "./aes"; import { decryptAES, encryptAES } from "./aes";
import { logger } from "../logger"; import { logger } from "../logger";
import { ISecretStorageKeyInfo } from "./api";
import { Crypto } from "./index"; import { Crypto } from "./index";
import { Method } from "../http-api"; import { Method } from "../http-api";
import { SecretStorageKeyDescription } from "../secret-storage";
export interface IDehydratedDevice { export interface IDehydratedDevice {
device_id: string; // eslint-disable-line camelcase device_id: string; // eslint-disable-line camelcase
device_data: ISecretStorageKeyInfo & { device_data: SecretStorageKeyDescription & {
// eslint-disable-line camelcase // eslint-disable-line camelcase
algorithm: string; algorithm: string;
account: string; // pickle account: string; // pickle

View File

@@ -48,7 +48,6 @@ import {
IEncryptedEventInfo, IEncryptedEventInfo,
IImportRoomKeysOpts, IImportRoomKeysOpts,
IRecoveryKey, IRecoveryKey,
ISecretStorageKeyInfo,
} from "./api"; } from "./api";
import { OutgoingRoomKeyRequestManager } from "./OutgoingRoomKeyRequestManager"; import { OutgoingRoomKeyRequestManager } from "./OutgoingRoomKeyRequestManager";
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store"; import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
@@ -91,6 +90,7 @@ import { IMessage } from "./algorithms/olm";
import { CryptoBackend, OnSyncCompletedData } from "../common-crypto/CryptoBackend"; import { CryptoBackend, OnSyncCompletedData } from "../common-crypto/CryptoBackend";
import { RoomState, RoomStateEvent } from "../models/room-state"; import { RoomState, RoomStateEvent } from "../models/room-state";
import { MapWithDefault, recursiveMapToObject } from "../utils"; import { MapWithDefault, recursiveMapToObject } from "../utils";
import { SecretStorageKeyDescription } from "../secret-storage";
const DeviceVerification = DeviceInfo.DeviceVerification; const DeviceVerification = DeviceInfo.DeviceVerification;
@@ -142,10 +142,10 @@ export interface ICryptoCallbacks {
saveCrossSigningKeys?: (keys: Record<string, Uint8Array>) => void; saveCrossSigningKeys?: (keys: Record<string, Uint8Array>) => void;
shouldUpgradeDeviceVerifications?: (users: Record<string, any>) => Promise<string[]>; shouldUpgradeDeviceVerifications?: (users: Record<string, any>) => Promise<string[]>;
getSecretStorageKey?: ( getSecretStorageKey?: (
keys: { keys: Record<string, ISecretStorageKeyInfo> }, keys: { keys: Record<string, SecretStorageKeyDescription> },
name: string, name: string,
) => Promise<[string, Uint8Array] | null>; ) => Promise<[string, Uint8Array] | null>;
cacheSecretStorageKey?: (keyId: string, keyInfo: ISecretStorageKeyInfo, key: Uint8Array) => void; cacheSecretStorageKey?: (keyId: string, keyInfo: SecretStorageKeyDescription, key: Uint8Array) => void;
onSecretRequested?: ( onSecretRequested?: (
userId: string, userId: string,
deviceId: string, deviceId: string,
@@ -153,7 +153,10 @@ export interface ICryptoCallbacks {
secretName: string, secretName: string,
deviceTrust: DeviceTrustLevel, deviceTrust: DeviceTrustLevel,
) => Promise<string | undefined>; ) => Promise<string | undefined>;
getDehydrationKey?: (keyInfo: ISecretStorageKeyInfo, checkFunc: (key: Uint8Array) => void) => Promise<Uint8Array>; getDehydrationKey?: (
keyInfo: SecretStorageKeyDescription,
checkFunc: (key: Uint8Array) => void,
) => Promise<Uint8Array>;
getBackupKey?: () => Promise<Uint8Array>; getBackupKey?: () => Promise<Uint8Array>;
} }
@@ -923,7 +926,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
return keyId; return keyId;
}; };
const ensureCanCheckPassphrase = async (keyId: string, keyInfo: ISecretStorageKeyInfo): Promise<void> => { const ensureCanCheckPassphrase = async (keyId: string, keyInfo: SecretStorageKeyDescription): Promise<void> => {
if (!keyInfo.mac) { if (!keyInfo.mac) {
const key = await this.baseApis.cryptoCallbacks.getSecretStorageKey?.( const key = await this.baseApis.cryptoCallbacks.getSecretStorageKey?.(
{ keys: { [keyId]: keyInfo } }, { keys: { [keyId]: keyInfo } },
@@ -1130,7 +1133,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
return this.secretStorage.get(name); return this.secretStorage.get(name);
} }
public isSecretStored(name: string): Promise<Record<string, ISecretStorageKeyInfo> | null> { public isSecretStored(name: string): Promise<Record<string, SecretStorageKeyDescription> | null> {
return this.secretStorage.isStored(name); return this.secretStorage.isStored(name);
} }
@@ -1149,7 +1152,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
return this.secretStorage.setDefaultKeyId(k); return this.secretStorage.setDefaultKeyId(k);
} }
public checkSecretStorageKey(key: Uint8Array, info: ISecretStorageKeyInfo): Promise<boolean> { public checkSecretStorageKey(key: Uint8Array, info: SecretStorageKeyDescription): Promise<boolean> {
return this.secretStorage.checkKey(key, info); return this.secretStorage.checkKey(key, info);
} }

View File

@@ -55,6 +55,7 @@ export * from "./@types/requests";
export * from "./@types/search"; export * from "./@types/search";
export * from "./models/room-summary"; export * from "./models/room-summary";
export * as ContentHelpers from "./content-helpers"; export * as ContentHelpers from "./content-helpers";
export * as SecretStorage from "./secret-storage";
export type { ICryptoCallbacks } from "./crypto"; // used to be located here export type { ICryptoCallbacks } from "./crypto"; // used to be located here
export { createNewMatrixCall } from "./webrtc/call"; export { createNewMatrixCall } from "./webrtc/call";
export type { MatrixCall } from "./webrtc/call"; export type { MatrixCall } from "./webrtc/call";

88
src/secret-storage.ts Normal file
View File

@@ -0,0 +1,88 @@
/*
Copyright 2021-2023 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* Implementation of server-side secret storage
*
* @see https://spec.matrix.org/v1.6/client-server-api/#storage
*/
/**
* Common base interface for Secret Storage Keys.
*
* The common properties for all encryption keys used in server-side secret storage.
*
* @see https://spec.matrix.org/v1.6/client-server-api/#key-storage
*/
export interface SecretStorageKeyDescriptionCommon {
/** A human-readable name for this key. */
// XXX: according to the spec, this is optional
name: string;
/** The encryption algorithm used with this key. */
algorithm: string;
/** Information for deriving this key from a passphrase. */
// XXX: according to the spec, this is optional
passphrase: PassphraseInfo;
}
/**
* Properties for a SSSS key using the `m.secret_storage.v1.aes-hmac-sha2` algorithm.
*
* Corresponds to `AesHmacSha2KeyDescription` in the specification.
*
* @see https://spec.matrix.org/v1.6/client-server-api/#msecret_storagev1aes-hmac-sha2
*/
export interface SecretStorageKeyDescriptionAesV1 extends SecretStorageKeyDescriptionCommon {
// XXX: strictly speaking, we should be able to enforce the algorithm here. But
// this interface ends up being incorrectly used where other algorithms are in use (notably
// in device-dehydration support), and unpicking that is too much like hard work
// at the moment.
// algorithm: "m.secret_storage.v1.aes-hmac-sha2";
/** The 16-byte AES initialization vector, encoded as base64. */
iv: string;
/** The MAC of the result of encrypting 32 bytes of 0, encoded as base64. */
mac: string;
}
/**
* Union type for secret storage keys.
*
* For now, this is only {@link SecretStorageKeyDescriptionAesV1}, but other interfaces may be added in future.
*/
export type SecretStorageKeyDescription = SecretStorageKeyDescriptionAesV1;
/**
* Information on how to generate the key from a passphrase.
*
* @see https://spec.matrix.org/v1.6/client-server-api/#deriving-keys-from-passphrases
*/
export interface PassphraseInfo {
/** The algorithm to be used to derive the key. */
algorithm: "m.pbkdf2";
/** The number of PBKDF2 iterations to use. */
iterations: number;
/** The salt to be used for PBKDF2. */
salt: string;
/** The number of bits to generate. Defaults to 256. */
bits?: number;
}