From 26aa3d3ce732634e8a7c69ebf8f9df99efed64fb Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 13 Nov 2019 14:09:40 +0000 Subject: [PATCH] Support default keys --- spec/unit/crypto/secrets.spec.js | 47 ++++++++++++++++++++++++++++++++ src/client.js | 2 ++ src/crypto/Secrets.js | 30 +++++++++++++++++++- src/crypto/index.js | 8 ++++++ 4 files changed, 86 insertions(+), 1 deletion(-) diff --git a/spec/unit/crypto/secrets.spec.js b/spec/unit/crypto/secrets.spec.js index f650d38fb..1ac143085 100644 --- a/spec/unit/crypto/secrets.spec.js +++ b/spec/unit/crypto/secrets.spec.js @@ -110,6 +110,53 @@ describe("Secrets", function() { } }); + it("should refuse to encrypt with zero keys", async function() { + const alice = await makeTestClient( + {userId: "@alice:example.com", deviceId: "Osborne2"}, + ); + + try { + await alice.storeSecret("foo", "bar", []); + expect(true).toBeFalsy(); + } catch (e) { + } + }); + + it("should encrypt with default key if keys is null", async function() { + const alice = await makeTestClient( + {userId: "@alice:example.com", deviceId: "Osborne2"}, + ); + alice.setAccountData = async function(eventType, contents, callback) { + alice.store.storeAccountDataEvents([ + new MatrixEvent({ + type: eventType, + content: contents, + }), + ]); + }; + + const newKeyId = await alice.addSecretKey( + 'm.secret_storage.v1.curve25519-aes-sha2', + ); + await alice.setDefaultKeyId(newKeyId); + await alice.storeSecret("foo", "bar"); + + const accountData = alice.getAccountData('foo'); + expect(accountData.getContent().encrypted).toBeTruthy(); + }); + + it("should refuse to encrypt if no keys given and no default key", async function() { + const alice = await makeTestClient( + {userId: "@alice:example.com", deviceId: "Osborne2"}, + ); + + try { + await alice.storeSecret("foo", "bar"); + expect(true).toBeFalsy(); + } catch (e) { + } + }); + it("should request secrets from other clients", async function() { const [osborne2, vax] = await makeTestClients( [ diff --git a/src/client.js b/src/client.js index 8b23a9a75..60d66cede 100644 --- a/src/client.js +++ b/src/client.js @@ -1113,6 +1113,8 @@ wrapCryptoFuncs(MatrixClient, [ "getSecret", "isSecretStored", "requestSecret", + "getDefaultKeyId", + "setDefaultKeyId", ]); /** diff --git a/src/crypto/Secrets.js b/src/crypto/Secrets.js index abbfe02a5..8d45caaa3 100644 --- a/src/crypto/Secrets.js +++ b/src/crypto/Secrets.js @@ -34,6 +34,19 @@ export default class SecretStorage extends EventEmitter { this._cryptoCallbacks = cryptoCallbacks; } + getDefaultKeyId() { + const defaultKeyEvent = this._baseApis.getAccountData('m.secret_storage.default_key'); + if (!defaultKeyEvent) return null; + return defaultKeyEvent.getContent().key; + } + + setDefaultKeyId(keyId) { + return this._baseApis.setAccountData( + 'm.secret_storage.default_key', + { key: keyId }, + ); + } + /** * Add a key for encrypting secrets. * @@ -49,6 +62,8 @@ export default class SecretStorage extends EventEmitter { async addKey(algorithm, opts, keyID) { const keyData = {algorithm}; + if (!opts) opts = {}; + if (opts.name) { keyData.name = opts.name; } @@ -86,7 +101,7 @@ export default class SecretStorage extends EventEmitter { if (!keyID) { do { keyID = randomString(32); - } while (!this._baseApis.getAccountData(`m.secret_storage.key.${keyID}`)); + } while (this._baseApis.getAccountData(`m.secret_storage.key.${keyID}`)); } // FIXME: sign keyData? @@ -106,10 +121,23 @@ export default class SecretStorage extends EventEmitter { * @param {string} name The name of the secret * @param {string} secret The secret contents. * @param {Array} keys The IDs of the keys to use to encrypt the secret + * or null/undefined to use the default key. */ async store(name, secret, keys) { const encrypted = {}; + if (!keys) { + const defaultKeyId = this.getDefaultKeyId(); + if (!defaultKeyId) { + throw new Error("No keys specified and no default key present"); + } + keys = [defaultKeyId]; + } + + if (keys.length === 0) { + throw new Error("Zero keys given to encrypt with!"); + } + for (const keyName of keys) { // get key information from key storage const keyInfo = this._baseApis.getAccountData( diff --git a/src/crypto/index.js b/src/crypto/index.js index 9ba5d9439..d90a5b9cb 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -236,6 +236,14 @@ Crypto.prototype.requestSecret = function(name, devices) { return this._secretStorage.request(name, devices); }; +Crypto.prototype.getDefaultKeyId = function() { + return this._secretStorage.getDefaultKeyId(); +}; + +Crypto.prototype.setDefaultKeyId = function(k) { + return this._secretStorage.setDefaultKeyId(k); +}; + /** * Checks that a given private key matches a given public key * This can be used by the getCrossSigningKey callback to verify that the