diff --git a/src/crypto/CrossSigning.js b/src/crypto/CrossSigning.js index 69c31ae7d..0394ed9ce 100644 --- a/src/crypto/CrossSigning.js +++ b/src/crypto/CrossSigning.js @@ -105,6 +105,20 @@ export class CrossSigningInfo extends EventEmitter { }; } + /** + * Check whether the private keys exist in secret storage. + * + * @param {SecretStorage} secretStorage The secret store using account data + * @returns {boolean} Whether all private keys were found in storage + */ + isStoredInSecretStorage(secretStorage) { + let stored = true; + for (const name of ["master", "self_signing", "user_signing"]) { + stored &= secretStorage.isStored(`m.cross_signing.${name}`, false); + } + return stored; + } + /** * Store private keys in secret storage for use by other devices. This is * typically called in conjunction with the creation of new cross-signing @@ -119,6 +133,58 @@ export class CrossSigningInfo extends EventEmitter { } } + /** + * Get private keys from secret storage created by some other device. This + * also passes the private keys to the app-specific callback. + * + * @param {SecretStorage} secretStorage The secret store using account data + */ + getFromSecretStorage(secretStorage) { + if (!this._callbacks.saveCrossSigningKeys) { + throw new Error("No saveCrossSigningKeys callback supplied"); + } + + // Retrieve private keys from secret storage + const privateKeys = {}; + for (const name of ["master", "self_signing", "user_signing"]) { + privateKeys[name] = secretStorage.get(`m.cross_signing.${name}`); + } + + // Regenerate public keys from private keys + // XXX: Do we want to _also_ download public keys from the homeserver to + // verify they agree...? + const signings = {}; + const publicKeys = {}; + const keys = {}; + try { + for (const name of ["master", "self_signing", "user_signing"]) { + signings[name] = new global.Olm.PkSigning(); + publicKeys[name] = signings[name].init_with_seed(privateKeys[name]); + keys[name] = { + user_id: this.userId, + usage: [name], + keys: { + ['ed25519:' + publicKeys[name]]: publicKeys[name], + }, + }; + if (name !== "master") { + pkSign( + keys[name], signings["master"], + this.userId, publicKeys["master"], + ); + } + } + } finally { + for (const signing of signings) { + signing.free(); + } + } + + // Save public keys locally and private keys via app callback + Object.assign(this.keys, keys); + this._callbacks.saveCrossSigningKeys(privateKeys); + } + /** * Get the ID used to identify the user. This can also be used to test for * the existence of a given key type. diff --git a/src/crypto/index.js b/src/crypto/index.js index e2bf15a1b..db0368755 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -268,6 +268,7 @@ Crypto.prototype.init = async function() { (txn) => { this._cryptoStore.getCrossSigningKeys(txn, (keys) => { if (keys) { + logger.log("Loaded cross-signing public keys from crypto store"); this._crossSigningInfo.setKeys(keys); } }); @@ -304,12 +305,24 @@ Crypto.prototype.bootstrapSecretStorage = async function({ // effectively need it for both reading and writing secrets. let crossSigningKeysChanged = false; if (!this._crossSigningInfo.getId()) { - logger.log("Cross-signing keys not found, creating new keys"); - await this.resetCrossSigningKeys( - CrossSigningLevel.MASTER, - { doInteractiveAuthFlow }, + logger.log( + "Cross-signing public keys not found on device, " + + "checking secret storage for private keys", ); - crossSigningKeysChanged = true; + if (this._crossSigningInfo.isStoredInSecretStorage(this._secretStorage)) { + logger.log("Cross-signing private keys found in secret storage"); + this._crossSigningInfo.getFromSecretStorage(this._secretStorage); + } else { + logger.log( + "Cross-signing private keys not found in secret storage, " + + "creating new keys", + ); + await this.resetCrossSigningKeys( + CrossSigningLevel.MASTER, + { doInteractiveAuthFlow }, + ); + crossSigningKeysChanged = true; + } } // Check if Secure Secret Storage has a default key. If we don't have one, create the @@ -324,7 +337,7 @@ Crypto.prototype.bootstrapSecretStorage = async function({ // If cross-signing keys changed, store them in Secure Secret Storage. if (crossSigningKeysChanged) { - logger.log("Storing cross-signing keys in secret storage"); + logger.log("Storing cross-signing private keys in secret storage"); // XXX: We need to think about how to re-do this step if it fails. await this._crossSigningInfo.storeInSecretStorage(this._secretStorage); }