Skip to content

Commit f5240cd

Browse files
authored
Merge pull request #1467 from uhoreg/fallback_keys
Add support for olm fallback keys
2 parents 03fc12e + e05a505 commit f5240cd

File tree

7 files changed

+96
-28
lines changed

7 files changed

+96
-28
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
"jest-localstorage-mock": "^2.4.3",
8585
"jsdoc": "^3.6.6",
8686
"matrix-mock-request": "^1.2.3",
87-
"olm": "https://packages.matrix.org/npm/olm/olm-3.1.4.tgz",
87+
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
8888
"rimraf": "^3.0.2",
8989
"terser": "^4.8.0",
9090
"tsify": "^4.0.2",

spec/unit/crypto/CrossSigningInfo.spec.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,16 @@ describe("CrossSigningInfo.getCrossSigningKey", function() {
8484
const info = new CrossSigningInfo(userId, {
8585
getCrossSigningKey: () => testKey,
8686
});
87-
const [pubKey, ab] = await info.getCrossSigningKey("master", masterKeyPub);
87+
const [pubKey, pkSigning] = await info.getCrossSigningKey("master", masterKeyPub);
8888
expect(pubKey).toEqual(masterKeyPub);
89-
expect(ab).toEqual({a: 106712, b: 106712});
89+
// check that the pkSigning object corresponds to the pubKey
90+
const signature = pkSigning.sign("message");
91+
const util = new global.Olm.Utility();
92+
try {
93+
util.ed25519_verify(pubKey, "message", signature);
94+
} finally {
95+
util.free();
96+
}
9097
});
9198

9299
it.each(types)("should request a key from the cache callback (if set)" +

src/@types/global.d.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,15 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import * as Olm from "olm";
17+
// this is needed to tell TS about global.Olm
18+
import * as Olm from "olm"; // eslint-disable-line @typescript-eslint/no-unused-vars
1819

1920
export {};
2021

2122
declare global {
2223
namespace NodeJS {
2324
interface Global {
2425
localStorage: Storage;
25-
Olm: Olm;
2626
}
2727
}
28-
interface Global {
29-
Olm: Olm;
30-
}
3128
}

src/crypto/OlmDevice.js

+30
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,36 @@ OlmDevice.prototype.generateOneTimeKeys = function(numKeys) {
477477
);
478478
};
479479

480+
/**
481+
* Generate a new fallback keys
482+
*
483+
* @return {Promise} Resolved once the account is saved back having generated the key
484+
*/
485+
OlmDevice.prototype.generateFallbackKey = async function() {
486+
await this._cryptoStore.doTxn(
487+
'readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT],
488+
(txn) => {
489+
this._getAccount(txn, (account) => {
490+
account.generate_fallback_key();
491+
this._storeAccount(txn, account);
492+
});
493+
},
494+
);
495+
};
496+
497+
OlmDevice.prototype.getFallbackKey = async function() {
498+
let result;
499+
await this._cryptoStore.doTxn(
500+
'readonly', [IndexedDBCryptoStore.STORE_ACCOUNT],
501+
(txn) => {
502+
this._getAccount(txn, (account) => {
503+
result = JSON.parse(account.fallback_key());
504+
});
505+
},
506+
);
507+
return result;
508+
};
509+
480510
/**
481511
* Generate a new outbound session
482512
*

src/crypto/index.js

+41-17
Original file line numberDiff line numberDiff line change
@@ -1859,6 +1859,14 @@ Crypto.prototype.updateOneTimeKeyCount = function(currentCount) {
18591859
}
18601860
};
18611861

1862+
Crypto.prototype.setNeedsNewFallback = function(needsNewFallback) {
1863+
this._needsNewFallback = !!needsNewFallback;
1864+
};
1865+
1866+
Crypto.prototype.getNeedsNewFallback = function() {
1867+
return this._needsNewFallback;
1868+
};
1869+
18621870
// check if it's time to upload one-time keys, and do so if so.
18631871
function _maybeUploadOneTimeKeys(crypto) {
18641872
// frequency with which to check & upload one-time keys
@@ -1906,27 +1914,31 @@ function _maybeUploadOneTimeKeys(crypto) {
19061914
// out stale private keys that won't receive a message.
19071915
const keyLimit = Math.floor(maxOneTimeKeys / 2);
19081916

1909-
function uploadLoop(keyCount) {
1910-
if (keyLimit <= keyCount) {
1911-
// If we don't need to generate any more keys then we are done.
1912-
return Promise.resolve();
1913-
}
1917+
async function uploadLoop(keyCount) {
1918+
while (keyLimit > keyCount || crypto.getNeedsNewFallback()) {
1919+
// Ask olm to generate new one time keys, then upload them to synapse.
1920+
if (keyLimit > keyCount) {
1921+
logger.info("generating oneTimeKeys");
1922+
const keysThisLoop = Math.min(keyLimit - keyCount, maxKeysPerCycle);
1923+
await crypto._olmDevice.generateOneTimeKeys(keysThisLoop);
1924+
}
19141925

1915-
const keysThisLoop = Math.min(keyLimit - keyCount, maxKeysPerCycle);
1926+
if (crypto.getNeedsNewFallback()) {
1927+
logger.info("generating fallback key");
1928+
await crypto._olmDevice.generateFallbackKey();
1929+
}
19161930

1917-
// Ask olm to generate new one time keys, then upload them to synapse.
1918-
return crypto._olmDevice.generateOneTimeKeys(keysThisLoop).then(() => {
1919-
return _uploadOneTimeKeys(crypto);
1920-
}).then((res) => {
1931+
logger.info("calling _uploadOneTimeKeys");
1932+
const res = await _uploadOneTimeKeys(crypto);
19211933
if (res.one_time_key_counts && res.one_time_key_counts.signed_curve25519) {
19221934
// if the response contains a more up to date value use this
19231935
// for the next loop
1924-
return uploadLoop(res.one_time_key_counts.signed_curve25519);
1936+
keyCount = res.one_time_key_counts.signed_curve25519;
19251937
} else {
1926-
throw new Error("response for uploading keys does not contain "
1927-
+ "one_time_key_counts.signed_curve25519");
1938+
throw new Error("response for uploading keys does not contain " +
1939+
"one_time_key_counts.signed_curve25519");
19281940
}
1929-
});
1941+
}
19301942
}
19311943

19321944
crypto._oneTimeKeyCheckInProgress = true;
@@ -1958,11 +1970,22 @@ function _maybeUploadOneTimeKeys(crypto) {
19581970

19591971
// returns a promise which resolves to the response
19601972
async function _uploadOneTimeKeys(crypto) {
1973+
const promises = [];
1974+
1975+
const fallbackJson = {};
1976+
if (crypto.getNeedsNewFallback()) {
1977+
const fallbackKeys = await crypto._olmDevice.getFallbackKey();
1978+
for (const [keyId, key] of Object.entries(fallbackKeys.curve25519)) {
1979+
const k = { key, fallback: true };
1980+
fallbackJson["signed_curve25519:" + keyId] = k;
1981+
promises.push(crypto._signObject(k));
1982+
}
1983+
crypto.setNeedsNewFallback(false);
1984+
}
1985+
19611986
const oneTimeKeys = await crypto._olmDevice.getOneTimeKeys();
19621987
const oneTimeJson = {};
19631988

1964-
const promises = [];
1965-
19661989
for (const keyId in oneTimeKeys.curve25519) {
19671990
if (oneTimeKeys.curve25519.hasOwnProperty(keyId)) {
19681991
const k = {
@@ -1976,7 +1999,8 @@ async function _uploadOneTimeKeys(crypto) {
19761999
await Promise.all(promises);
19772000

19782001
const res = await crypto._baseApis.uploadKeysRequest({
1979-
one_time_keys: oneTimeJson,
2002+
"one_time_keys": oneTimeJson,
2003+
"org.matrix.msc2732.fallback_keys": fallbackJson,
19802004
});
19812005

19822006
await crypto._olmDevice.markKeysAsPublished();

src/sync.js

+10
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,16 @@ SyncApi.prototype._processSyncResponse = async function(
13611361
const currentCount = data.device_one_time_keys_count.signed_curve25519 || 0;
13621362
this.opts.crypto.updateOneTimeKeyCount(currentCount);
13631363
}
1364+
if (this.opts.crypto && data["org.matrix.msc2732.device_unused_fallback_key_types"]) {
1365+
// The presence of device_unused_fallback_key_types indicates that the
1366+
// server supports fallback keys. If there's no unused
1367+
// signed_curve25519 fallback key we need a new one.
1368+
const unusedFallbackKeys = data["org.matrix.msc2732.device_unused_fallback_key_types"];
1369+
this.opts.crypto.setNeedsNewFallback(
1370+
unusedFallbackKeys instanceof Array &&
1371+
!unusedFallbackKeys.includes("signed_curve25519"),
1372+
);
1373+
}
13641374
};
13651375

13661376
/**

yarn.lock

+3-3
Original file line numberDiff line numberDiff line change
@@ -5411,9 +5411,9 @@ object.values@^1.1.1:
54115411
function-bind "^1.1.1"
54125412
has "^1.0.3"
54135413

5414-
"olm@https://packages.matrix.org/npm/olm/olm-3.1.4.tgz":
5415-
version "3.1.4"
5416-
resolved "https://packages.matrix.org/npm/olm/olm-3.1.4.tgz#0f03128b7d3b2f614d2216409a1dfccca765fdb3"
5414+
"olm@https://packages.matrix.org/npm/olm/olm-3.2.1.tgz":
5415+
version "3.2.1"
5416+
resolved "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz#d623d76f99c3518dde68be8c86618d68bc7b004a"
54175417

54185418
once@^1.3.0, once@^1.3.1, once@^1.4.0:
54195419
version "1.4.0"

0 commit comments

Comments
 (0)