From dd8c157bb95a6604ebe0b25a143755c42b57f87f Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 9 Dec 2021 19:00:23 -0500 Subject: [PATCH] Improve fallback key behaviour (#2037) --- package.json | 2 +- src/crypto/OlmDevice.ts | 14 +++++++++++++- src/crypto/index.ts | 42 +++++++++++++++++++++++++++++++++++------ src/sync.ts | 7 +++++-- 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index d73a33e03..280ef9da7 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "@babel/preset-env": "^7.12.11", "@babel/preset-typescript": "^7.12.7", "@babel/register": "^7.12.10", - "@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz", + "@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.7.tgz", "@types/bs58": "^4.0.1", "@types/jest": "^26.0.20", "@types/node": "12", diff --git a/src/crypto/OlmDevice.ts b/src/crypto/OlmDevice.ts index 2fd1858ab..be071493c 100644 --- a/src/crypto/OlmDevice.ts +++ b/src/crypto/OlmDevice.ts @@ -543,13 +543,25 @@ export class OlmDevice { 'readonly', [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { this.getAccount(txn, (account: Account) => { - result = JSON.parse(account.fallback_key()); + result = JSON.parse(account.unpublished_fallback_key()); }); }, ); return result; } + public async forgetOldFallbackKey(): Promise { + await this.cryptoStore.doTxn( + 'readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT], + (txn) => { + this.getAccount(txn, (account: Account) => { + account.forget_old_fallback_key(); + this.storeAccount(txn, account); + }); + }, + ); + } + /** * Generate a new outbound session * diff --git a/src/crypto/index.ts b/src/crypto/index.ts index 860c2acfe..6b71ed8ec 100644 --- a/src/crypto/index.ts +++ b/src/crypto/index.ts @@ -248,6 +248,7 @@ export class Crypto extends EventEmitter { private oneTimeKeyCount: number; private needsNewFallback: boolean; + private fallbackCleanup?: number; // setTimeout ID /** * Cryptography bits @@ -1850,8 +1851,23 @@ export class Crypto extends EventEmitter { } if (this.getNeedsNewFallback()) { - logger.info("generating fallback key"); - await this.olmDevice.generateFallbackKey(); + const fallbackKeys = await this.olmDevice.getFallbackKey(); + // if fallbackKeys is non-empty, we've already generated a + // fallback key, but it hasn't been published yet, so we + // can use that instead of generating a new one + if (!fallbackKeys.curve25519 || + Object.keys(fallbackKeys.curve25519).length == 0) { + logger.info("generating fallback key"); + if (this.fallbackCleanup) { + // cancel any pending fallback cleanup because generating + // a new fallback key will already drop the old fallback + // that would have been dropped, and we don't want to kill + // the current key + clearTimeout(this.fallbackCleanup); + delete this.fallbackCleanup; + } + await this.olmDevice.generateFallbackKey(); + } } logger.info("calling uploadOneTimeKeys"); @@ -1898,8 +1914,9 @@ export class Crypto extends EventEmitter { private async uploadOneTimeKeys() { const promises = []; - const fallbackJson: Record = {}; + let fallbackJson: Record; if (this.getNeedsNewFallback()) { + fallbackJson = {}; const fallbackKeys = await this.olmDevice.getFallbackKey(); for (const [keyId, key] of Object.entries(fallbackKeys.curve25519)) { const k = { key, fallback: true }; @@ -1924,10 +1941,23 @@ export class Crypto extends EventEmitter { await Promise.all(promises); - const res = await this.baseApis.uploadKeysRequest({ + const requestBody: Record = { "one_time_keys": oneTimeJson, - "org.matrix.msc2732.fallback_keys": fallbackJson, - }); + }; + + if (fallbackJson) { + requestBody["org.matrix.msc2732.fallback_keys"] = fallbackJson; + requestBody["fallback_keys"] = fallbackJson; + } + + const res = await this.baseApis.uploadKeysRequest(requestBody); + + if (fallbackJson) { + this.fallbackCleanup = setTimeout(() => { + delete this.fallbackCleanup; + this.olmDevice.forgetOldFallbackKey(); + }, 60*60*1000); + } await this.olmDevice.markKeysAsPublished(); return res; diff --git a/src/sync.ts b/src/sync.ts index 2a0030bff..2b583eeb7 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -1416,11 +1416,14 @@ export class SyncApi { const currentCount = data.device_one_time_keys_count.signed_curve25519 || 0; this.opts.crypto.updateOneTimeKeyCount(currentCount); } - if (this.opts.crypto && data["org.matrix.msc2732.device_unused_fallback_key_types"]) { + if (this.opts.crypto && + (data["device_unused_fallback_key_types"] || + data["org.matrix.msc2732.device_unused_fallback_key_types"])) { // The presence of device_unused_fallback_key_types indicates that the // server supports fallback keys. If there's no unused // signed_curve25519 fallback key we need a new one. - const unusedFallbackKeys = data["org.matrix.msc2732.device_unused_fallback_key_types"]; + const unusedFallbackKeys = data["device_unused_fallback_key_types"] || + data["org.matrix.msc2732.device_unused_fallback_key_types"]; this.opts.crypto.setNeedsNewFallback( unusedFallbackKeys instanceof Array && !unusedFallbackKeys.includes("signed_curve25519"),