diff --git a/spec/unit/crypto/secrets.spec.js b/spec/unit/crypto/secrets.spec.js index b58bb8888..ca1f04eba 100644 --- a/spec/unit/crypto/secrets.spec.js +++ b/spec/unit/crypto/secrets.spec.js @@ -281,4 +281,58 @@ describe("Secrets", function() { expect(crossSigning.isStoredInSecretStorage(secretStorage)).toBeTruthy(); expect(secretStorage.hasKey()).toBeTruthy(); }); + + it("bootstraps when cross-signing keys in secret storage", async function() { + const decryption = new global.Olm.PkDecryption(); + decryption.generate_key(); + const storagePrivateKey = decryption.get_private_key(); + + let crossSigningKeys = {}; + const bob = await makeTestClient( + { + userId: "@bob:example.com", + deviceId: "bob1", + }, + { + cryptoCallbacks: { + getCrossSigningKey: t => crossSigningKeys[t], + saveCrossSigningKeys: k => crossSigningKeys = k, + getSecretStorageKey: request => { + const defaultKeyId = bob.getDefaultSecretStorageKeyId(); + expect(Object.keys(request.keys)).toEqual([defaultKeyId]); + return [defaultKeyId, storagePrivateKey]; + }, + }, + }, + ); + + bob.uploadDeviceSigningKeys = async () => {}; + bob.uploadKeySignatures = async () => {}; + bob.setAccountData = async function(eventType, contents, callback) { + const event = new MatrixEvent({ + type: eventType, + content: contents, + }); + this.store.storeAccountDataEvents([ + event, + ]); + this.emit("accountData", event); + }; + + const crossSigning = bob._crypto._crossSigningInfo; + const secretStorage = bob._crypto._secretStorage; + + // Set up cross-signing keys from scratch with specific storage key + await bob.bootstrapSecretStorage({ + createSecretStorageKey: async () => ({ privkey: storagePrivateKey }), + }); + + // Clear local cross-signing keys and read from secret storage + crossSigning.keys = {}; + await bob.bootstrapSecretStorage(); + + expect(crossSigning.getId()).toBeTruthy(); + expect(crossSigning.isStoredInSecretStorage(secretStorage)).toBeTruthy(); + expect(secretStorage.hasKey()).toBeTruthy(); + }); }); diff --git a/src/crypto/CrossSigning.js b/src/crypto/CrossSigning.js index 5a32709df..9671cf2dd 100644 --- a/src/crypto/CrossSigning.js +++ b/src/crypto/CrossSigning.js @@ -178,7 +178,7 @@ export class CrossSigningInfo extends EventEmitter { } } } finally { - for (const signing of signings) { + for (const signing of Object.values(signings)) { signing.free(); } } diff --git a/src/crypto/index.js b/src/crypto/index.js index 762bc6c85..a3074de2c 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -328,9 +328,15 @@ Crypto.prototype.init = async function() { * Args: * {function} A function that makes the request requiring auth. Receives the * auth data as an object. + * @param {function} [opts.createSecretStorageKey] Optional. Function + * called to await a secret storage key creation flow. + * Returns: + * {Promise} A promise which resolves to key creation data for + * `addSecretKey`: an object with either `passphrase` or `privkey` fields. */ Crypto.prototype.bootstrapSecretStorage = async function({ authUploadDeviceSigningKeys, + createSecretStorageKey = async () => { }, } = {}) { logger.log("Bootstrapping Secure Secret Storage"); @@ -364,8 +370,10 @@ Crypto.prototype.bootstrapSecretStorage = async function({ // default key (which will also be signed by the cross-signing master key). if (!this._secretStorage.hasKey()) { logger.log("Secret storage default key not found, creating new key"); + const keyOptions = await createSecretStorageKey(); const newKeyId = await this.addSecretKey( SECRET_STORAGE_ALGORITHM_V1, + keyOptions, ); await this.setDefaultSecretStorageKeyId(newKeyId); }