Compare commits

...

92 Commits

Author SHA1 Message Date
RiotRobot a12c250f2b v8.5.0 2020-10-12 13:15:07 +01:00
RiotRobot 57eef2d832 Prepare changelog for v8.5.0 2020-10-12 13:15:06 +01:00
RiotRobot 293860b6c5 v8.5.0-rc.1 2020-10-07 14:18:52 +01:00
RiotRobot 07667172cd Prepare changelog for v8.5.0-rc.1 2020-10-07 14:18:52 +01:00
J. Ryan Stinnett f5240cdac6 Merge pull request #1467 from uhoreg/fallback_keys
Add support for olm fallback keys
2020-10-07 10:24:12 +01:00
Michael Telatynski 03fc12e888 Merge pull request #1492 from matrix-org/t3chguy/fix/10545
Fix editing local echoes not updating them in real time
2020-10-07 00:15:16 +01:00
Hubert Chathi e05a50528e make test no longer dependent on emscripten internals 2020-10-06 19:01:01 -04:00
Hubert Chathi 356ee90417 lint 2020-10-06 18:33:45 -04:00
Hubert Chathi 5dced57724 bump olm release again and fix global.d.ts to work with the olm TS definitions 2020-10-06 17:55:40 -04:00
Michael Telatynski 8ec3b88c5d Update comments
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-10-06 22:55:34 +01:00
Michael Telatynski d7ef128510 Merge pull request #1491 from matrix-org/t3chguy/fix/9836
Fix re-emit of Event.replaced to be on client and not room
2020-10-06 22:50:14 +01:00
Hubert Chathi bb2502409b update yarn.lock 2020-10-06 15:34:02 -04:00
Hubert Chathi aa6aab4245 Merge remote-tracking branch 'origin/develop' into fallback_keys 2020-10-06 15:22:25 -04:00
Hubert Chathi 12aab0caeb depend on newer olm 2020-10-06 15:18:18 -04:00
David Baker 2f316c558d Merge pull request #1496 from matrix-org/dbkr/space_in_log_line
Add space to log line
2020-10-06 19:16:25 +01:00
David Baker f447273b75 Add space to log line 2020-10-06 18:59:34 +01:00
David Baker a0e66291df Merge pull request #1493 from matrix-org/revert-1487-dbkr/tsify_call
Revert "Convert call.js to Typescript & update WebRTC APIs"
2020-10-06 15:21:38 +01:00
David Baker 5733f46f4c Revert "Convert call.js to Typescript & update WebRTC APIs" 2020-10-06 15:16:32 +01:00
David Baker da2128feff Merge pull request #1487 from matrix-org/dbkr/tsify_call
Convert call.js to Typescript & update WebRTC APIs
2020-10-06 15:09:10 +01:00
Michael Telatynski d413faefdb double sync-browserify jest timeout
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-10-06 12:39:18 +01:00
Michael Telatynski 0d1d767d96 Fix editing local echoes not updating them in real time
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-10-06 12:32:50 +01:00
David Baker 5619554023 use MatrixEvent for event types 2020-10-06 11:03:05 +01:00
David Baker 53880a4bb2 This is how plurals work 2020-10-06 10:54:51 +01:00
David Baker 812ae227b6 Make type enums PascalCase 2020-10-06 10:53:42 +01:00
David Baker c6b44098ac remove space before type colon 2020-10-06 09:55:02 +01:00
David Baker 92b95a98d0 comment other snake case identifiers
and also sto mixing quotes
2020-10-06 09:52:59 +01:00
David Baker a3505ff42d Use right case for private enum field 2020-10-06 09:49:51 +01:00
Michael Telatynski 15b2e3ff1d Fix re-emit of Event.replaced to be on client and not room
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-10-06 08:23:19 +01:00
Hubert Chathi 1845d1ac55 Merge pull request #1436 from uhoreg/dehydration
Dehydrate and rehydrate devices
2020-10-05 17:19:28 -04:00
J. Ryan Stinnett 463819caa7 Merge pull request #1490 from matrix-org/jryans/key-setup-device-undefined
Keep local device after processing device list sync
2020-10-05 14:49:18 +01:00
J. Ryan Stinnett a0317d9587 Fix tests 2020-10-05 14:29:30 +01:00
J. Ryan Stinnett fa6ce0cb70 Keep local device after processing device list sync
This change ensure we always keep the local device in the device list, even if
the server claims it doesn't exist. This is important for initial startup, as it
is possible the device key upload takes a while to apply, and thus might not
appear in the first device list sync.

Fixes https://github.com/vector-im/element-web/issues/15319
2020-10-05 14:09:16 +01:00
David Baker 7471ff4b0d Make setSinkId reliable
As per comment, this seems to be the way to make setSinkId work reliably on Chrome.

Also don't make our own mediastreams if we don't need to (ie. in a normal call
our tracks should come with their owen MediaStreams).
2020-10-03 16:57:33 +01:00
Hubert Chathi 65a5bfac88 update property name 2020-10-02 15:52:09 -04:00
David Baker d2321410f8 Pass stream to addTrack too
Otherwise we end up with separate, unbundled 'streamless tracks'
which is not what we want.
2020-10-02 15:12:12 +01:00
Hubert Chathi f90b5a99cd Merge remote-tracking branch 'origin/develop' into dehydration 2020-10-01 21:55:05 -04:00
David Baker cc4656b36a Update call.js to use new WebRTC APIs
and some other related & semi-related refactoring
2020-10-01 19:15:49 +01:00
J. Ryan Stinnett 34bc63a146 Merge pull request #1489 from matrix-org/jryans/no-console
Enforce logger module via lint rules
2020-10-01 14:56:15 +01:00
J. Ryan Stinnett 09bd91a588 Enforce logger module via lint rules
This adds lint rules (and fixes various errors) to ensure we use the `logger`
intermediary module, rather than accessing the console directly.
2020-10-01 14:28:24 +01:00
David Baker bb3e082e5b Make direction public, remove didConnect
Various flags that may or may not have been part of the poorly defined
external API before. Neither were used internally. Make direction a
public export but remove didConnect.
2020-10-01 10:15:27 +01:00
Hubert Chathi 60c863f829 lint 2020-09-30 18:33:27 -04:00
Hubert Chathi 2d2a73bf52 make dehydrated device name configurable 2020-09-30 18:05:52 -04:00
Hubert Chathi 7b9f73709d use "dehydration" as the cache name 2020-09-30 17:50:37 -04:00
Hubert Chathi 1dc89f642d use logger instead of console 2020-09-30 17:46:18 -04:00
David Baker a78b59010a Convert call.js to Typescript
This does a fairly basic rewriting of the whole file to bring it up
to a vaguely modern standard of code. Nothing should have changed
fundamentally, but it probably should do: as per the global type
overrides, we use a lot of deprecated WebRTC APIs and should probably
update.
2020-09-30 18:54:48 +01:00
Hubert Chathi a2e1d94fcf more lint 2020-09-30 01:22:46 -04:00
Hubert Chathi b181c83b93 lint 2020-09-30 01:16:57 -04:00
Hubert Chathi cf7c84c4ba more linting, because GitHub is showing me outdated CI info 2020-09-30 01:10:33 -04:00
Hubert Chathi 6e5230f9f9 fix lint again 2020-09-30 01:07:35 -04:00
Hubert Chathi f03f7c0acb fix lint 2020-09-30 00:58:37 -04:00
Hubert Chathi aa9b807b82 update to latest dehydration endpoints, and simplify API 2020-09-30 00:48:07 -04:00
Travis Ralston fa9921e091 Merge pull request #1462 from orangecms/develop
extend method redactEvent with reason
2020-09-28 15:05:00 -06:00
RiotRobot a9a6b2de48 Merge branch 'master' into develop 2020-09-28 16:09:28 +01:00
RiotRobot 1ef746658f v8.4.1 2020-09-28 16:06:30 +01:00
RiotRobot fe0099b497 Prepare changelog for v8.4.1 2020-09-28 16:06:30 +01:00
J. Ryan Stinnett 7e9c4146a5 Merge pull request #1486 from matrix-org/dbkr/catch_exceptions_from_call_event_handler_rel
Catch exception from call event handler
2020-09-28 16:01:07 +01:00
David Baker 3e978c64e4 Merge pull request #1484 from matrix-org/dbkr/catch_exceptions_from_call_event_handler
Catch exception from call event handler
2020-09-28 15:56:29 +01:00
David Baker 13c5920a46 Merge pull request #1485 from matrix-org/dbkr/ignore_invalid_candidates_rel
Ignore invalid candidates
2020-09-28 15:55:00 +01:00
David Baker 3b19203fd2 Catch exception from call event handler
Otherwise they leak out ot the sync loop. It's unfortunate that
exceptions from event handlers leak out to the emitter, and this
seems a bit clumsy, but having to wrap eveery event emit in a try
block seems worse.
2020-09-28 15:48:06 +01:00
David Baker 84cd05b218 Ignore invalid candidates 2020-09-28 15:47:02 +01:00
David Baker 9b9a9642ee Merge pull request #1483 from matrix-org/dbkr/ignore_invalid_candidates
Ignore invalid candidates
2020-09-28 15:41:32 +01:00
David Baker 639d2317ed Catch exception from call event handler
Otherwise they leak out ot the sync loop. It's unfortunate that
exceptions from event handlers leak out to the emitter, and this
seems a bit clumsy, but having to wrap eveery event emit in a try
block seems worse.
2020-09-28 15:39:21 +01:00
David Baker 52379d7655 Ignore invalid candidates 2020-09-28 15:22:01 +01:00
Michael Telatynski 29827362d6 Merge pull request #1478 from matrix-org/t3chguy-patch-1
Always push docs if they are generated
2020-09-28 14:32:07 +01:00
RiotRobot f33315f610 Merge branch 'master' into develop 2020-09-28 14:23:19 +01:00
RiotRobot aec92a41da v8.4.0 2020-09-28 14:20:06 +01:00
RiotRobot d570811ddc Prepare changelog for v8.4.0 2020-09-28 14:20:06 +01:00
J. Ryan Stinnett 9cd015a218 Merge pull request #1482 from matrix-org/jryans/guard-backup-sig-release
Only sign key backup with cross-signing keys when available
2020-09-28 12:13:23 +01:00
J. Ryan Stinnett 8d2cc5096e Fix default key cache check 2020-09-28 10:38:13 +01:00
J. Ryan Stinnett 7ec0bf69f3 Only sign key backup with cross-signing keys when available
This changes the key backup setup step to only sign with cross-signing keys when
both the public and private keys are already available without prompting. In
many cases down these paths, the cross-signing keys either may not exist or may
not be accessible. We always sign the key backup with your device key as well,
so there is always a route to trust the key backup even if this is skipped.

Fixes https://github.com/vector-im/element-web/issues/15230
2020-09-28 10:38:13 +01:00
J. Ryan Stinnett f3a8306107 Merge pull request #1481 from matrix-org/jryans/guard-backup-sig
Only sign key backup with cross-signing keys when available
2020-09-28 10:21:34 +01:00
J. Ryan Stinnett 2b29e9934c Fix default key cache check 2020-09-28 10:16:20 +01:00
J. Ryan Stinnett 4b1104e463 Only sign key backup with cross-signing keys when available
This changes the key backup setup step to only sign with cross-signing keys when
both the public and private keys are already available without prompting. In
many cases down these paths, the cross-signing keys either may not exist or may
not be accessible. We always sign the key backup with your device key as well,
so there is always a route to trust the key backup even if this is skipped.

Fixes https://github.com/vector-im/element-web/issues/15230
2020-09-25 17:36:47 +01:00
Hubert Chathi 60b9ef959d don't include an empty signatures field when calculating the signature 2020-09-24 18:46:17 -04:00
Hubert Chathi 0074c2cf57 mark fallback keys as fallback 2020-09-24 18:09:07 -04:00
Hubert Chathi b0204ab54e use unstable prefix 2020-09-24 17:41:08 -04:00
J. Ryan Stinnett bc5b07aa75 Merge pull request #1479 from matrix-org/jryans/upgrade-deps-2020-09-23
Upgrade dependencies
2020-09-24 16:40:36 +01:00
Michael Telatynski cfe90dbed5 Always push docs if they are generated 2020-09-24 12:47:23 +01:00
J. Ryan Stinnett fa913655bc Upgrade dependencies 2020-09-23 17:35:24 +01:00
Daniel Maslowski aad7484c8d Add optional reason to be passed to redactEvent
Signed-off-by: Daniel Maslowski <info@orangecms.org>
2020-09-21 21:50:38 +02:00
Hubert Chathi 16dccd75c1 fix string 2020-09-18 19:20:09 -04:00
Hubert Chathi 5151b52688 Merge remote-tracking branch 'origin/develop' into dehydration 2020-09-18 19:13:22 -04:00
Hubert Chathi 492c1d77d8 style fixes 2020-09-16 17:09:33 -04:00
Hubert Chathi 3952768667 Merge remote-tracking branch 'origin/develop' into fallback_keys 2020-09-15 12:04:30 -04:00
Hubert Chathi 6331b34cf7 lint 2020-09-14 23:00:07 -04:00
Hubert Chathi 2ebe1dfa16 support olm fallback keys 2020-09-14 22:10:16 -04:00
Hubert Chathi 19f3996e09 only cross-sign dehydrated device if cross-signing is available 2020-09-03 16:06:06 -04:00
Hubert Chathi 25c2cc1768 various fixes 2020-09-03 11:45:37 -04:00
Hubert Chathi 438861ae5e separate dehydration functions to its own file and cache the key 2020-08-31 15:44:29 -04:00
Hubert Chathi ad85740ae2 allow other login functions to be used 2020-08-19 18:01:31 -04:00
Hubert Chathi 6e8e3e4150 use newer version of dehydration proposal and add some comments 2020-08-17 18:09:23 -04:00
Hubert Chathi 8a4440c314 initial work on dehydration 2020-08-04 16:06:19 -04:00
30 changed files with 1455 additions and 572 deletions
+2
View File
@@ -28,6 +28,8 @@ module.exports = {
"indent": "off",
"no-constant-condition": "off",
"no-async-promise-executor": "off",
// We use a `logger` intermediary module
"no-console": "error",
},
overrides: [{
"files": ["src/**/*.ts"],
+57
View File
@@ -1,3 +1,60 @@
Changes in [8.5.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.5.0) (2020-10-12)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.5.0-rc.1...v8.5.0)
* No changes since rc.1
Changes in [8.5.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.5.0-rc.1) (2020-10-07)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.4.1...v8.5.0-rc.1)
* Add support for olm fallback keys
[\#1467](https://github.com/matrix-org/matrix-js-sdk/pull/1467)
* Fix editing local echoes not updating them in real time
[\#1492](https://github.com/matrix-org/matrix-js-sdk/pull/1492)
* Fix re-emit of Event.replaced to be on client and not room
[\#1491](https://github.com/matrix-org/matrix-js-sdk/pull/1491)
* Add space to log line
[\#1496](https://github.com/matrix-org/matrix-js-sdk/pull/1496)
* Revert "Convert call.js to Typescript & update WebRTC APIs"
[\#1493](https://github.com/matrix-org/matrix-js-sdk/pull/1493)
* Convert call.js to Typescript & update WebRTC APIs
[\#1487](https://github.com/matrix-org/matrix-js-sdk/pull/1487)
* Dehydrate and rehydrate devices
[\#1436](https://github.com/matrix-org/matrix-js-sdk/pull/1436)
* Keep local device after processing device list sync
[\#1490](https://github.com/matrix-org/matrix-js-sdk/pull/1490)
* Enforce logger module via lint rules
[\#1489](https://github.com/matrix-org/matrix-js-sdk/pull/1489)
* Extend method redactEvent with reason
[\#1462](https://github.com/matrix-org/matrix-js-sdk/pull/1462)
* Catch exception from call event handler
[\#1484](https://github.com/matrix-org/matrix-js-sdk/pull/1484)
* Ignore invalid candidates
[\#1483](https://github.com/matrix-org/matrix-js-sdk/pull/1483)
* Always push docs if they are generated
[\#1478](https://github.com/matrix-org/matrix-js-sdk/pull/1478)
* Only sign key backup with cross-signing keys when available
[\#1481](https://github.com/matrix-org/matrix-js-sdk/pull/1481)
* Upgrade dependencies
[\#1479](https://github.com/matrix-org/matrix-js-sdk/pull/1479)
Changes in [8.4.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.4.1) (2020-09-28)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.4.0...v8.4.1)
* Catch exception from call event handler
[\#1486](https://github.com/matrix-org/matrix-js-sdk/pull/1486)
* Ignore invalid candidates
[\#1485](https://github.com/matrix-org/matrix-js-sdk/pull/1485)
Changes in [8.4.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.4.0) (2020-09-28)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.4.0-rc.1...v8.4.0)
* Only sign key backup with cross-signing keys when available
[\#1482](https://github.com/matrix-org/matrix-js-sdk/pull/1482)
Changes in [8.4.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.4.0-rc.1) (2020-09-23)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.3.0...v8.4.0-rc.1)
+30 -30
View File
@@ -1,6 +1,6 @@
{
"name": "matrix-js-sdk",
"version": "8.4.0-rc.1",
"version": "8.5.0",
"description": "Matrix Client-Server SDK for Javascript",
"scripts": {
"prepare": "yarn build",
@@ -46,49 +46,49 @@
"release.sh"
],
"dependencies": {
"@babel/runtime": "^7.8.3",
"@babel/runtime": "^7.11.2",
"another-json": "^0.2.0",
"browser-request": "^0.3.3",
"bs58": "^4.0.1",
"content-type": "^1.0.2",
"loglevel": "^1.6.4",
"qs": "^6.5.2",
"request": "^2.88.0",
"unhomoglyph": "^1.0.2"
"content-type": "^1.0.4",
"loglevel": "^1.7.0",
"qs": "^6.9.4",
"request": "^2.88.2",
"unhomoglyph": "^1.0.6"
},
"devDependencies": {
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-proposal-numeric-separator": "^7.7.4",
"@babel/plugin-proposal-object-rest-spread": "^7.7.4",
"@babel/plugin-syntax-dynamic-import": "^7.7.4",
"@babel/plugin-transform-runtime": "^7.8.3",
"@babel/preset-env": "^7.7.6",
"@babel/preset-typescript": "^7.7.4",
"@babel/register": "^7.7.4",
"@babel/cli": "^7.11.6",
"@babel/core": "^7.11.6",
"@babel/plugin-proposal-class-properties": "^7.10.4",
"@babel/plugin-proposal-numeric-separator": "^7.10.4",
"@babel/plugin-proposal-object-rest-spread": "^7.11.0",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.11.5",
"@babel/preset-env": "^7.11.5",
"@babel/preset-typescript": "^7.10.4",
"@babel/register": "^7.11.5",
"@types/node": "12",
"@types/request": "^2.48.4",
"babel-eslint": "^10.0.3",
"@types/request": "^2.48.5",
"babel-eslint": "^10.1.0",
"babel-jest": "^24.9.0",
"babelify": "^10.0.0",
"better-docs": "^2.3.2",
"browserify": "^16.5.0",
"browserify": "^16.5.2",
"docdash": "^1.2.0",
"eslint": "7.3.1",
"eslint": "7.9.0",
"eslint-config-matrix-org": "^0.1.2",
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-babel": "^5.3.1",
"exorcist": "^1.0.1",
"fake-indexeddb": "^3.0.0",
"fake-indexeddb": "^3.1.2",
"jest": "^24.9.0",
"jest-localstorage-mock": "^2.4.0",
"jsdoc": "^3.6.5",
"jest-localstorage-mock": "^2.4.3",
"jsdoc": "^3.6.6",
"matrix-mock-request": "^1.2.3",
"olm": "https://packages.matrix.org/npm/olm/olm-3.1.4.tgz",
"rimraf": "^3.0.0",
"terser": "^4.4.3",
"tsify": "^4.0.1",
"typescript": "^3.7.3"
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
"rimraf": "^3.0.2",
"terser": "^4.8.0",
"tsify": "^4.0.2",
"typescript": "^3.9.7"
},
"jest": {
"testEnvironment": "node"
+2 -4
View File
@@ -327,6 +327,7 @@ if [ -z "$skip_jsdoc" ]; then
$release index.html
git add "$release"
git commit --no-verify -m "Add jsdoc for $release" index.html "$release"
git push origin gh-pages
fi
# if it is a pre-release, leave it on the release branch for now.
@@ -341,11 +342,8 @@ git checkout master
git pull
git merge "$rel_branch"
# push master and docs (if generated) to github
# push master to github
git push origin master
if [ -z "$skip_jsdoc" ]; then
git push origin gh-pages
fi
# finally, merge master back onto develop (if it exists)
if [ $(git branch -lr | grep origin/develop -c) -ge 1 ]; then
+1 -1
View File
@@ -99,5 +99,5 @@ describe("Browserify Test", function() {
client.once("sync.unexpectedError", reject);
}),
]);
}, 10000);
}, 20000); // additional timeout as this test can take quite a while
});
+1 -1
View File
@@ -31,5 +31,5 @@ try {
const crypto = require('crypto');
utils.setCrypto(crypto);
} catch (err) {
console.log('nodejs was compiled without crypto support: some tests will fail');
logger.log('nodejs was compiled without crypto support: some tests will fail');
}
+11 -3
View File
@@ -26,6 +26,7 @@ import {MemoryCryptoStore} from '../../../src/crypto/store/memory-crypto-store';
import 'fake-indexeddb/auto';
import 'jest-localstorage-mock';
import {OlmDevice} from "../../../src/crypto/OlmDevice";
import {logger} from '../../../src/logger';
const userId = "@alice:example.com";
@@ -51,7 +52,7 @@ const masterKeyPub = "nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk";
describe("CrossSigningInfo.getCrossSigningKey", function() {
if (!global.Olm) {
console.warn('Not running megolm backup unit tests: libolm not present');
logger.warn('Not running megolm backup unit tests: libolm not present');
return;
}
@@ -83,9 +84,16 @@ describe("CrossSigningInfo.getCrossSigningKey", function() {
const info = new CrossSigningInfo(userId, {
getCrossSigningKey: () => testKey,
});
const [pubKey, ab] = await info.getCrossSigningKey("master", masterKeyPub);
const [pubKey, pkSigning] = await info.getCrossSigningKey("master", masterKeyPub);
expect(pubKey).toEqual(masterKeyPub);
expect(ab).toEqual({a: 106712, b: 106712});
// check that the pkSigning object corresponds to the pubKey
const signature = pkSigning.sign("message");
const util = new global.Olm.Utility();
try {
util.ed25519_verify(pubKey, "message", signature);
} finally {
util.free();
}
});
it.each(types)("should request a key from the cache callback (if set)" +
+2
View File
@@ -72,6 +72,8 @@ describe('DeviceList', function() {
function createTestDeviceList() {
const baseApis = {
downloadKeysForUsers: downloadSpy,
getUserId: () => '@test1:sw1v.org',
deviceId: 'HGKAWHRVJQ',
};
const mockOlm = {
verifySignature: function(key, message, signature) {},
+2 -1
View File
@@ -22,6 +22,7 @@ import {TestClient} from '../../TestClient';
import {HttpResponse, setHttpResponses} from '../../test-utils';
import { resetCrossSigningKeys } from "./crypto-utils";
import { MatrixError } from '../../../src/http-api';
import {logger} from '../../../src/logger';
async function makeTestClient(userInfo, options, keys) {
if (!keys) keys = {};
@@ -49,7 +50,7 @@ async function makeTestClient(userInfo, options, keys) {
describe("Cross Signing", function() {
if (!global.Olm) {
console.warn('Not running megolm backup unit tests: libolm not present');
logger.warn('Not running megolm backup unit tests: libolm not present');
return;
}
+3 -2
View File
@@ -22,6 +22,7 @@ import {TestClient} from '../../TestClient';
import {makeTestClients} from './verification/util';
import {encryptAES} from "../../../src/crypto/aes";
import {resetCrossSigningKeys, createSecretStorageKey} from "./crypto-utils";
import {logger} from '../../../src/logger';
import * as utils from "../../../src/utils";
@@ -29,7 +30,7 @@ try {
const crypto = require('crypto');
utils.setCrypto(crypto);
} catch (err) {
console.log('nodejs was compiled without crypto support');
logger.log('nodejs was compiled without crypto support');
}
async function makeTestClient(userInfo, options) {
@@ -60,7 +61,7 @@ function sign(obj, key, userId) {
describe("Secrets", function() {
if (!global.Olm) {
console.warn('Not running megolm backup unit tests: libolm not present');
logger.warn('Not running megolm backup unit tests: libolm not present');
return;
}
+3 -2
View File
@@ -18,12 +18,13 @@ limitations under the License.
import {TestClient} from '../../../TestClient';
import {MatrixEvent} from "../../../../src/models/event";
import nodeCrypto from "crypto";
import {logger} from '../../../../src/logger';
export async function makeTestClients(userInfos, options) {
const clients = [];
const clientMap = {};
const sendToDevice = function(type, map) {
// console.log(this.getUserId(), "sends", type, map);
// logger.log(this.getUserId(), "sends", type, map);
for (const [userId, devMap] of Object.entries(map)) {
if (userId in clientMap) {
for (const [deviceId, msg] of Object.entries(devMap)) {
@@ -67,7 +68,7 @@ export async function makeTestClients(userInfos, options) {
setImmediate(() => {
for (const tc of clients) {
if (tc.client === this) { // eslint-disable-line babel/no-invalid-this
console.log("sending remote echo!!");
logger.log("sending remote echo!!");
tc.client.emit("Room.timeline", remoteEcho);
} else {
tc.client.emit("Room.timeline", event);
+2 -5
View File
@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import * as Olm from "olm";
// this is needed to tell TS about global.Olm
import * as Olm from "olm"; // eslint-disable-line @typescript-eslint/no-unused-vars
export {};
@@ -22,10 +23,6 @@ declare global {
namespace NodeJS {
interface Global {
localStorage: Storage;
Olm: Olm;
}
}
interface Global {
Olm: Olm;
}
}
+137 -9
View File
@@ -55,6 +55,7 @@ import {PushProcessor} from "./pushprocessor";
import {encodeBase64, decodeBase64} from "./crypto/olmlib";
import { User } from "./models/user";
import {AutoDiscovery} from "./autodiscovery";
import {DEHYDRATION_ALGORITHM} from "./crypto/dehydration";
const SCROLLBACK_DELAY_MS = 3000;
export const CRYPTO_ENABLED = isCryptoAvailable();
@@ -459,6 +460,121 @@ export function MatrixClient(opts) {
utils.inherits(MatrixClient, EventEmitter);
utils.extend(MatrixClient.prototype, MatrixBaseApis.prototype);
/**
* Try to rehydrate a device if available. The client must have been
* initialized with a `cryptoCallback.getDehydrationKey` option, and this
* function must be called before initCrypto and startClient are called.
*
* @return {Promise} Resolves to undefined if a device could not be dehydrated, or
* to the new device ID if the dehydration was successful.
* @return {module:http-api.MatrixError} Rejects: with an error response.
*/
MatrixClient.prototype.rehydrateDevice = async function() {
if (this._crypto) {
throw new Error("Cannot rehydrate device after crypto is initialized");
}
if (!this._cryptoCallbacks.getDehydrationKey) {
return;
}
let getDeviceResult;
try {
getDeviceResult = await this._http.authedRequest(
undefined,
"GET",
"/dehydrated_device",
undefined, undefined,
{
prefix: "/_matrix/client/unstable/org.matrix.msc2697.v2",
},
);
} catch (e) {
logger.info("could not get dehydrated device", e);
return;
}
if (!getDeviceResult.device_data || !getDeviceResult.device_id) {
logger.info("no dehydrated device found");
return;
}
const account = new global.Olm.Account();
try {
const deviceData = getDeviceResult.device_data;
if (deviceData.algorithm !== DEHYDRATION_ALGORITHM) {
logger.warn("Wrong algorithm for dehydrated device");
return;
}
logger.log("unpickling dehydrated device");
const key = await this._cryptoCallbacks.getDehydrationKey(
deviceData,
(k) => {
// copy the key so that it doesn't get clobbered
account.unpickle(new Uint8Array(k), deviceData.account);
},
);
account.unpickle(key, deviceData.account);
logger.log("unpickled device");
const rehydrateResult = await this._http.authedRequest(
undefined,
"POST",
"/dehydrated_device/claim",
undefined,
{
device_id: getDeviceResult.device_id,
},
{
prefix: "/_matrix/client/unstable/org.matrix.msc2697.v2",
},
);
if (rehydrateResult.success === true) {
this.deviceId = getDeviceResult.device_id;
logger.info("using dehydrated device");
const pickleKey = this.pickleKey || "DEFAULT_KEY";
this._exportedOlmDeviceToImport = {
pickledAccount: account.pickle(pickleKey),
sessions: [],
pickleKey: pickleKey,
};
account.free();
return this.deviceId;
} else {
account.free();
logger.info("not using dehydrated device");
return;
}
} catch (e) {
account.free();
logger.warn("could not unpickle", e);
}
};
/**
* Set the dehydration key. This will also periodically dehydrate devices to
* the server.
*
* @param {Uint8Array} key the dehydration key
* @param {object} [keyInfo] Information about the key. Primarily for
* information about how to generate the key from a passphrase.
* @param {string} [deviceDisplayName] The device display name for the
* dehydrated device.
* @return {Promise} A promise that resolves when the dehydrated device is stored.
*/
MatrixClient.prototype.setDehydrationKey = async function(
key, keyInfo = {}, deviceDisplayName = undefined,
) {
if (!(this._crypto)) {
logger.warn('not dehydrating device if crypto is not enabled');
return;
}
return await this._crypto._dehydrationManager.setDehydrationKey(
key, keyInfo, deviceDisplayName,
);
};
MatrixClient.prototype.exportDevice = async function() {
if (!(this._crypto)) {
logger.warn('not exporting device if crypto is not enabled');
@@ -2054,7 +2170,7 @@ MatrixClient.prototype._restoreKeyBackup = function(
// This is async.
this._crypto.storeSessionBackupPrivateKey(privKey)
.catch((e) => {
console.warn("Error caching session backup key:", e);
logger.warn("Error caching session backup key:", e);
}).then(cacheCompleteCallback);
if (progressCallback) {
@@ -2858,14 +2974,19 @@ function _sendEventHttpRequest(client, event) {
* @param {string} eventId
* @param {string} [txnId] transaction id. One will be made up if not
* supplied.
* @param {module:client.callback} callback Optional.
* @param {object|module:client.callback} callbackOrOpts
* Options to pass on, may contain `reason`.
* Can be callback for backwards compatibility.
* @return {Promise} Resolves: TODO
* @return {module:http-api.MatrixError} Rejects: with an error response.
*/
MatrixClient.prototype.redactEvent = function(roomId, eventId, txnId, callback) {
MatrixClient.prototype.redactEvent = function(roomId, eventId, txnId, callbackOrOpts) {
const opts = typeof(callbackOrOpts) === 'object' ? callbackOrOpts : {};
const reason = opts.reason;
const callback = typeof(callbackOrOpts) === 'function' ? callbackOrOpts : undefined;
return this._sendCompleteEvent(roomId, {
type: "m.room.redaction",
content: {},
content: { reason: reason },
redacts: eventId,
}, txnId, callback);
};
@@ -5231,7 +5352,11 @@ function setupCallEventHandler(client) {
// This call has previously been answered or hung up: ignore it
return;
}
callEventHandler(e);
try {
callEventHandler(e);
} catch (e) {
logger.error("Caught exception handling call event", e);
}
});
callEventBuffer = [];
}
@@ -5257,7 +5382,11 @@ function setupCallEventHandler(client) {
} else {
// This one wasn't buffered so just run the event handler for it
// straight away
callEventHandler(event);
try {
callEventHandler(event);
} catch (e) {
logger.error("Caught exception handling call event", e);
}
}
});
}
@@ -5462,9 +5591,8 @@ function _PojoToMatrixEventMapper(client, options) {
}
event.attemptDecryption(client._crypto);
}
const room = client.getRoom(event.getRoomId());
if (room && !preventReEmit) {
room.reEmitter.reEmit(event, ["Event.replaced"]);
if (!preventReEmit) {
client.reEmitter.reEmit(event, ["Event.replaced"]);
}
return event;
}
+9 -6
View File
@@ -211,13 +211,16 @@ export class CrossSigningInfo extends EventEmitter {
/**
* Check whether the private keys exist in the local key cache.
*
* @param {string} [type] The type of key to get. One of "master",
* "self_signing", or "user_signing". Optional, will check all by default.
* @returns {boolean} True if all keys are stored in the local cache.
*/
async isStoredInKeyCache() {
async isStoredInKeyCache(type) {
const cacheCallbacks = this._cacheCallbacks;
if (!cacheCallbacks) return false;
for (const type of ["master", "self_signing", "user_signing"]) {
if (!await cacheCallbacks.getCrossSigningKeyCache(type)) {
const types = type ? [type] : ["master", "self_signing", "user_signing"];
for (const t of types) {
if (!await cacheCallbacks.getCrossSigningKeyCache(t)) {
return false;
}
}
@@ -731,7 +734,7 @@ export async function requestKeysDuringVerification(baseApis, userId, deviceId)
if (baseApis.getUserId() !== userId) {
return;
}
console.log("Cross-signing: Self-verification done; requesting keys");
logger.log("Cross-signing: Self-verification done; requesting keys");
// This happens asynchronously, and we're not concerned about waiting for
// it. We return here in order to test.
return new Promise((resolve, reject) => {
@@ -745,7 +748,7 @@ export async function requestKeysDuringVerification(baseApis, userId, deviceId)
const crossSigning = new CrossSigningInfo(
original.userId,
{ getCrossSigningKey: async (type) => {
console.debug("Cross-signing: requesting secret",
logger.debug("Cross-signing: requesting secret",
type, deviceId);
const { promise } = client.requestSecret(
`m.cross_signing.${type}`, [deviceId],
@@ -808,6 +811,6 @@ export async function requestKeysDuringVerification(baseApis, userId, deviceId)
timeout,
]).then(resolve, reject);
}).catch((e) => {
console.warn("Cross-signing: failure while requesting keys:", e);
logger.warn("Cross-signing: failure while requesting keys:", e);
});
}
+11 -2
View File
@@ -848,6 +848,7 @@ class DeviceListUpdateSerialiser {
await _updateStoredDeviceKeysForUser(
this._olmDevice, userId, userStore, dkResponse || {},
this._baseApis.getUserId(), this._baseApis.deviceId,
);
// put the updates into the object that will be returned as our results
@@ -885,8 +886,9 @@ class DeviceListUpdateSerialiser {
}
async function _updateStoredDeviceKeysForUser(_olmDevice, userId, userStore,
userResult) {
async function _updateStoredDeviceKeysForUser(
_olmDevice, userId, userStore, userResult, localUserId, localDeviceId,
) {
let updated = false;
// remove any devices in the store which aren't in the response
@@ -896,6 +898,13 @@ async function _updateStoredDeviceKeysForUser(_olmDevice, userId, userStore,
}
if (!(deviceId in userResult)) {
if (userId === localUserId && deviceId === localDeviceId) {
logger.warn(
`Local device ${deviceId} missing from sync, skipping removal`,
);
continue;
}
logger.log("Device " + userId + ":" + deviceId +
" has been removed");
delete userStore[deviceId];
+31 -1
View File
@@ -144,7 +144,7 @@ OlmDevice.prototype.init = async function(opts = {}) {
try {
if (fromExportedDevice) {
if (pickleKey) {
console.warn(
logger.warn(
'ignoring opts.pickleKey'
+ ' because opts.fromExportedDevice is present.',
);
@@ -477,6 +477,36 @@ OlmDevice.prototype.generateOneTimeKeys = function(numKeys) {
);
};
/**
* Generate a new fallback keys
*
* @return {Promise} Resolved once the account is saved back having generated the key
*/
OlmDevice.prototype.generateFallbackKey = async function() {
await this._cryptoStore.doTxn(
'readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT],
(txn) => {
this._getAccount(txn, (account) => {
account.generate_fallback_key();
this._storeAccount(txn, account);
});
},
);
};
OlmDevice.prototype.getFallbackKey = async function() {
let result;
await this._cryptoStore.doTxn(
'readonly', [IndexedDBCryptoStore.STORE_ACCOUNT],
(txn) => {
this._getAccount(txn, (account) => {
result = JSON.parse(account.fallback_key());
});
},
);
return result;
};
/**
* Generate a new outbound session
*
+1 -1
View File
@@ -247,7 +247,7 @@ export class SecretStorage extends EventEmitter {
) {
const hasKey = await this.hasKey(keys[0]);
if (hasKey) {
console.log("Fixing up passthrough secret: " + name);
logger.log("Fixing up passthrough secret: " + name);
await this.storePassthrough(name, keys[0]);
const newData = await this._baseApis.getAccountDataFromServer(name);
return newData;
+265
View File
@@ -0,0 +1,265 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {decodeBase64, encodeBase64} from './olmlib';
import {IndexedDBCryptoStore} from '../crypto/store/indexeddb-crypto-store';
import {decryptAES, encryptAES} from './aes';
import anotherjson from "another-json";
import {logger} from '../logger';
// FIXME: these types should eventually go in a different file
type Signatures = Record<string, Record<string, string>>;
interface DeviceKeys {
algorithms: Array<string>;
device_id: string; // eslint-disable-line camelcase
user_id: string; // eslint-disable-line camelcase
keys: Record<string, string>;
signatures?: Signatures;
}
interface OneTimeKey {
key: string;
fallback?: boolean;
signatures?: Signatures;
}
export const DEHYDRATION_ALGORITHM = "org.matrix.msc2697.v1.olm.libolm_pickle";
const oneweek = 7 * 24 * 60 * 60 * 1000;
export class DehydrationManager {
private inProgress = false;
private timeoutId: any;
private key: Uint8Array;
private keyInfo: {[props: string]: any};
private deviceDisplayName: string;
constructor(private crypto) {
this.getDehydrationKeyFromCache();
}
async getDehydrationKeyFromCache(): Promise<void> {
return this.crypto._cryptoStore.doTxn(
'readonly',
[IndexedDBCryptoStore.STORE_ACCOUNT],
(txn) => {
this.crypto._cryptoStore.getSecretStorePrivateKey(
txn,
async (result) => {
if (result) {
const {key, keyInfo, deviceDisplayName, time} = result;
const pickleKey = Buffer.from(this.crypto._olmDevice._pickleKey);
const decrypted = await decryptAES(key, pickleKey, DEHYDRATION_ALGORITHM);
this.key = decodeBase64(decrypted);
this.keyInfo = keyInfo;
this.deviceDisplayName = deviceDisplayName;
const now = Date.now();
const delay = Math.max(1, time + oneweek - now);
this.timeoutId = global.setTimeout(
this.dehydrateDevice.bind(this), delay,
);
}
},
"dehydration",
);
},
);
}
async setDehydrationKey(
key: Uint8Array, keyInfo: {[props: string]: any} = {},
deviceDisplayName: string = undefined,
): Promise<void> {
if (!key) {
// unsetting the key -- cancel any pending dehydration task
if (this.timeoutId) {
global.clearTimeout(this.timeoutId);
this.timeoutId = undefined;
}
// clear storage
this.crypto._cryptoStore.doTxn(
'readwrite',
[IndexedDBCryptoStore.STORE_ACCOUNT],
(txn) => {
this.crypto._cryptoStore.storeSecretStorePrivateKey(
txn, "dehydration", null,
);
},
);
this.key = undefined;
this.keyInfo = undefined;
return;
}
// Check to see if it's the same key as before. If it's different,
// dehydrate a new device. If it's the same, we can keep the same
// device. (Assume that keyInfo and deviceDisplayNamme will be the
// same if the key is the same.)
let matches: boolean = this.key && key.length == this.key.length;
for (let i = 0; matches && i < key.length; i++) {
if (key[i] != this.key[i]) {
matches = false;
}
}
if (!matches) {
this.key = key;
this.keyInfo = keyInfo;
this.deviceDisplayName = deviceDisplayName;
// start dehydration in the background
this.dehydrateDevice();
}
}
private async dehydrateDevice(): Promise<void> {
if (this.inProgress) {
logger.log("Dehydration already in progress -- not starting new dehydration");
return;
}
this.inProgress = true;
if (this.timeoutId) {
global.clearTimeout(this.timeoutId);
this.timeoutId = undefined;
}
try {
const pickleKey = Buffer.from(this.crypto._olmDevice._pickleKey);
// update the crypto store with the timestamp
const key = await encryptAES(encodeBase64(this.key), pickleKey, DEHYDRATION_ALGORITHM);
this.crypto._cryptoStore.doTxn(
'readwrite',
[IndexedDBCryptoStore.STORE_ACCOUNT],
(txn) => {
this.crypto._cryptoStore.storeSecretStorePrivateKey(
txn, "dehydration",
{
keyInfo: this.keyInfo,
key,
deviceDisplayName: this.deviceDisplayName,
time: Date.now(),
},
);
},
);
logger.log("Attempting to dehydrate device");
logger.log("Creating account");
// create the account and all the necessary keys
const account = new global.Olm.Account();
account.create();
const e2eKeys = JSON.parse(account.identity_keys());
const maxKeys = account.max_number_of_one_time_keys();
// FIXME: generate in small batches?
account.generate_one_time_keys(maxKeys / 2);
account.generate_fallback_key();
const otks: Record<string, string> = JSON.parse(account.one_time_keys());
const fallbacks: Record<string, string> = JSON.parse(account.fallback_key());
account.mark_keys_as_published();
// dehydrate the account and store it on the server
const pickledAccount = account.pickle(new Uint8Array(this.key));
const deviceData: {[props: string]: any} = {
algorithm: DEHYDRATION_ALGORITHM,
account: pickledAccount,
};
if (this.keyInfo.passphrase) {
deviceData.passphrase = this.keyInfo.passphrase;
}
logger.log("Uploading account to server");
const dehydrateResult = await this.crypto._baseApis._http.authedRequest(
undefined,
"PUT",
"/dehydrated_device",
undefined,
{
device_data: deviceData,
initial_device_display_name: this.deviceDisplayName,
},
{
prefix: "/_matrix/client/unstable/org.matrix.msc2697.v2",
},
);
// send the keys to the server
const deviceId = dehydrateResult.device_id;
logger.log("Preparing device keys", deviceId);
const deviceKeys: DeviceKeys = {
algorithms: this.crypto._supportedAlgorithms,
device_id: deviceId,
user_id: this.crypto._userId,
keys: {
[`ed25519:${deviceId}`]: e2eKeys.ed25519,
[`curve25519:${deviceId}`]: e2eKeys.curve25519,
},
};
const deviceSignature = account.sign(anotherjson.stringify(deviceKeys));
deviceKeys.signatures = {
[this.crypto._userId]: {
[`ed25519:${deviceId}`]: deviceSignature,
},
};
if (this.crypto._crossSigningInfo.getId("self_signing")) {
await this.crypto._crossSigningInfo.signObject(deviceKeys, "self_signing");
}
logger.log("Preparing one-time keys");
const oneTimeKeys = {};
for (const [keyId, key] of Object.entries(otks.curve25519)) {
const k: OneTimeKey = {key};
const signature = account.sign(anotherjson.stringify(k));
k.signatures = {
[this.crypto._userId]: {
[`ed25519:${deviceId}`]: signature,
},
};
oneTimeKeys[`signed_curve25519:${keyId}`] = k;
}
logger.log("Preparing fallback keys");
const fallbackKeys = {};
for (const [keyId, key] of Object.entries(fallbacks.curve25519)) {
const k: OneTimeKey = {key, fallback: true};
const signature = account.sign(anotherjson.stringify(k));
k.signatures = {
[this.crypto._userId]: {
[`ed25519:${deviceId}`]: signature,
},
};
fallbackKeys[`signed_curve25519:${keyId}`] = k;
}
logger.log("Uploading keys to server");
await this.crypto._baseApis._http.authedRequest(
undefined,
"POST",
"/keys/upload/" + encodeURI(deviceId),
undefined,
{
"device_keys": deviceKeys,
"one_time_keys": oneTimeKeys,
"org.matrix.msc2732.fallback_keys": fallbackKeys,
},
);
logger.log("Done dehydrating");
// dehydrate again in a week
this.timeoutId = global.setTimeout(
this.dehydrateDevice.bind(this), oneweek,
);
} finally {
this.inProgress = false;
}
}
}
+74 -25
View File
@@ -56,6 +56,7 @@ import {ToDeviceChannel, ToDeviceRequests} from "./verification/request/ToDevice
import {IllegalMethod} from "./verification/IllegalMethod";
import {KeySignatureUploadError} from "../errors";
import {decryptAES, encryptAES} from './aes';
import {DehydrationManager} from './dehydration';
const DeviceVerification = DeviceInfo.DeviceVerification;
@@ -146,7 +147,7 @@ export function Crypto(baseApis, sessionStore, userId, deviceId,
method,
);
} else {
console.warn(`Excluding unknown verification method ${method}`);
logger.warn(`Excluding unknown verification method ${method}`);
}
}
} else {
@@ -243,6 +244,8 @@ export function Crypto(baseApis, sessionStore, userId, deviceId,
baseApis, cryptoCallbacks,
);
this._dehydrationManager = new DehydrationManager(this);
// Assuming no app-supplied callback, default to getting from SSSS.
if (!cryptoCallbacks.getCrossSigningKey && cryptoCallbacks.getSecretStorageKey) {
cryptoCallbacks.getCrossSigningKey = async (type) => {
@@ -758,10 +761,20 @@ Crypto.prototype.bootstrapSecretStorage = async function({
// The backup is trusted because the user provided the private key.
// Sign the backup with the cross-signing key so the key backup can
// be trusted via cross-signing.
logger.log("Adding cross signing signature to key backup");
await this._crossSigningInfo.signObject(
keyBackupInfo.auth_data, "master",
);
if (
this._crossSigningInfo.getId() &&
this._crossSigningInfo.isStoredInKeyCache("master")
) {
logger.log("Adding cross-signing signature to key backup");
await this._crossSigningInfo.signObject(
keyBackupInfo.auth_data, "master",
);
} else {
logger.warn(
"Cross-signing keys not available, skipping signature on key backup",
);
}
builder.addSessionBackup(keyBackupInfo);
} else {
// 4S is already set up
@@ -810,8 +823,20 @@ Crypto.prototype.bootstrapSecretStorage = async function({
algorithm: info.algorithm,
auth_data: info.auth_data,
};
// sign with cross-sign master key
await this._crossSigningInfo.signObject(data.auth_data, "master");
if (
this._crossSigningInfo.getId() &&
this._crossSigningInfo.isStoredInKeyCache("master")
) {
// sign with cross-sign master key
logger.log("Adding cross-signing signature to key backup");
await this._crossSigningInfo.signObject(data.auth_data, "master");
} else {
logger.warn(
"Cross-signing keys not available, skipping signature on key backup",
);
}
// sign with the device fingerprint
await this._signObject(data.auth_data);
@@ -1834,6 +1859,14 @@ Crypto.prototype.updateOneTimeKeyCount = function(currentCount) {
}
};
Crypto.prototype.setNeedsNewFallback = function(needsNewFallback) {
this._needsNewFallback = !!needsNewFallback;
};
Crypto.prototype.getNeedsNewFallback = function() {
return this._needsNewFallback;
};
// check if it's time to upload one-time keys, and do so if so.
function _maybeUploadOneTimeKeys(crypto) {
// frequency with which to check & upload one-time keys
@@ -1881,27 +1914,31 @@ function _maybeUploadOneTimeKeys(crypto) {
// out stale private keys that won't receive a message.
const keyLimit = Math.floor(maxOneTimeKeys / 2);
function uploadLoop(keyCount) {
if (keyLimit <= keyCount) {
// If we don't need to generate any more keys then we are done.
return Promise.resolve();
}
async function uploadLoop(keyCount) {
while (keyLimit > keyCount || crypto.getNeedsNewFallback()) {
// Ask olm to generate new one time keys, then upload them to synapse.
if (keyLimit > keyCount) {
logger.info("generating oneTimeKeys");
const keysThisLoop = Math.min(keyLimit - keyCount, maxKeysPerCycle);
await crypto._olmDevice.generateOneTimeKeys(keysThisLoop);
}
const keysThisLoop = Math.min(keyLimit - keyCount, maxKeysPerCycle);
if (crypto.getNeedsNewFallback()) {
logger.info("generating fallback key");
await crypto._olmDevice.generateFallbackKey();
}
// Ask olm to generate new one time keys, then upload them to synapse.
return crypto._olmDevice.generateOneTimeKeys(keysThisLoop).then(() => {
return _uploadOneTimeKeys(crypto);
}).then((res) => {
logger.info("calling _uploadOneTimeKeys");
const res = await _uploadOneTimeKeys(crypto);
if (res.one_time_key_counts && res.one_time_key_counts.signed_curve25519) {
// if the response contains a more up to date value use this
// for the next loop
return uploadLoop(res.one_time_key_counts.signed_curve25519);
keyCount = res.one_time_key_counts.signed_curve25519;
} else {
throw new Error("response for uploading keys does not contain "
+ "one_time_key_counts.signed_curve25519");
throw new Error("response for uploading keys does not contain " +
"one_time_key_counts.signed_curve25519");
}
});
}
}
crypto._oneTimeKeyCheckInProgress = true;
@@ -1933,11 +1970,22 @@ function _maybeUploadOneTimeKeys(crypto) {
// returns a promise which resolves to the response
async function _uploadOneTimeKeys(crypto) {
const promises = [];
const fallbackJson = {};
if (crypto.getNeedsNewFallback()) {
const fallbackKeys = await crypto._olmDevice.getFallbackKey();
for (const [keyId, key] of Object.entries(fallbackKeys.curve25519)) {
const k = { key, fallback: true };
fallbackJson["signed_curve25519:" + keyId] = k;
promises.push(crypto._signObject(k));
}
crypto.setNeedsNewFallback(false);
}
const oneTimeKeys = await crypto._olmDevice.getOneTimeKeys();
const oneTimeJson = {};
const promises = [];
for (const keyId in oneTimeKeys.curve25519) {
if (oneTimeKeys.curve25519.hasOwnProperty(keyId)) {
const k = {
@@ -1951,7 +1999,8 @@ async function _uploadOneTimeKeys(crypto) {
await Promise.all(promises);
const res = await crypto._baseApis.uploadKeysRequest({
one_time_keys: oneTimeJson,
"one_time_keys": oneTimeJson,
"org.matrix.msc2732.fallback_keys": fallbackJson,
});
await crypto._olmDevice.markKeysAsPublished();
@@ -2378,7 +2427,7 @@ Crypto.prototype.getEventSenderDeviceInfo = function(event) {
if (claimedKey !== device.getFingerprint()) {
logger.warn(
"Event " + event.getId() + " claims ed25519 key " + claimedKey +
"but sender device has key " + device.getFingerprint());
" but sender device has key " + device.getFingerprint());
return null;
}
+3 -2
View File
@@ -26,6 +26,7 @@ import {
newUserCancelledError,
} from './Error';
import {encodeUnpaddedBase64, decodeBase64} from "../olmlib";
import {logger} from '../../logger';
export const SHOW_QR_CODE_METHOD = "m.qr_code.show.v1";
export const SCAN_QR_CODE_METHOD = "m.qr_code.scan.v1";
@@ -94,7 +95,7 @@ export class ReciprocateQRCode extends Base {
if (!targetKey) throw newKeyMismatchError();
if (keyInfo !== targetKey) {
console.error("key ID from key info does not match");
logger.error("key ID from key info does not match");
throw newKeyMismatchError();
}
for (const deviceKeyId in device.keys) {
@@ -102,7 +103,7 @@ export class ReciprocateQRCode extends Base {
const deviceTargetKey = keys[deviceKeyId];
if (!deviceTargetKey) throw newKeyMismatchError();
if (device.keys[deviceKeyId] !== deviceTargetKey) {
console.error("master key does not match");
logger.error("master key does not match");
throw newKeyMismatchError();
}
}
+1 -1
View File
@@ -961,7 +961,7 @@ export async function retryNetworkOperation(maxAttempts, callback) {
try {
if (attempts > 0) {
const timeout = 1000 * Math.pow(2, attempts);
console.log(`network operation failed ${attempts} times,` +
logger.log(`network operation failed ${attempts} times,` +
` retrying in ${timeout}ms...`);
await new Promise(r => setTimeout(r, timeout));
}
+2
View File
@@ -40,11 +40,13 @@ log.methodFactory = function(methodName, logLevel, loggerName) {
methodName === "warn" ||
methodName === "trace" ||
methodName === "info";
/* eslint-disable no-console */
if (supportedByConsole) {
return console[methodName](...args);
} else {
return console.log(...args);
}
/* eslint-enable no-console */
};
};
+1 -1
View File
@@ -806,7 +806,7 @@ EventTimelineSet.prototype.aggregateRelations = function(event) {
this.room,
);
isNewRelations = true;
relatesToEvent = this.findEventById(relatesToEventId);
relatesToEvent = this.findEventById(relatesToEventId) || this.room.getPendingEvent(relatesToEventId);
if (relatesToEvent) {
relationsWithEventType.setTargetEvent(relatesToEvent);
}
+2
View File
@@ -904,6 +904,8 @@ utils.extend(MatrixEvent.prototype, {
/**
* Set an event that replaces the content of this event, through an m.replace relation.
*
* @fires module:models/event.MatrixEvent#"Event.replaced"
*
* @param {MatrixEvent?} newEvent the event with the replacing content, if any.
*/
makeReplaced(newEvent) {
+5 -4
View File
@@ -16,6 +16,7 @@ limitations under the License.
import {EventEmitter} from 'events';
import {EventStatus} from '../models/event';
import {logger} from '../logger';
/**
* A container for relation events that supports easy access to common ways of
@@ -60,7 +61,7 @@ export class Relations extends EventEmitter {
const relation = event.getRelation();
if (!relation) {
console.error("Event must have relation info");
logger.error("Event must have relation info");
return;
}
@@ -68,7 +69,7 @@ export class Relations extends EventEmitter {
const eventType = event.getType();
if (this.relationType !== relationType || this.eventType !== eventType) {
console.error("Event relation info doesn't match this container");
logger.error("Event relation info doesn't match this container");
return;
}
@@ -104,7 +105,7 @@ export class Relations extends EventEmitter {
const relation = event.getRelation();
if (!relation) {
console.error("Event must have relation info");
logger.error("Event must have relation info");
return;
}
@@ -112,7 +113,7 @@ export class Relations extends EventEmitter {
const eventType = event.getType();
if (this.relationType !== relationType || this.eventType !== eventType) {
console.error("Event relation info doesn't match this container");
logger.error("Event relation info doesn't match this container");
return;
}
+14
View File
@@ -373,6 +373,20 @@ Room.prototype.hasPendingEvent = function(eventId) {
return this._pendingEventList.some(event => event.getId() === eventId);
};
/**
* Get a specific event from the pending event list, if configured, null otherwise.
*
* @param {string} eventId The event ID to check for.
* @return {MatrixEvent}
*/
Room.prototype.getPendingEvent = function(eventId) {
if (this._opts.pendingEventOrdering !== "detached") {
return null;
}
return this._pendingEventList.find(event => event.getId() === eventId);
};
/**
* Get the live unfiltered timeline for this room.
*
+2 -1
View File
@@ -16,6 +16,7 @@ limitations under the License.
*/
import {escapeRegExp, globToRegexp, isNullOrUndefined} from "./utils";
import {logger} from './logger';
/**
* @module pushprocessor
@@ -444,7 +445,7 @@ PushProcessor.rewriteDefaultRules = function(incomingRules) {
} else {
// Add the rule
const ruleId = override.rule_id;
console.warn(`Adding default global override for ${ruleId}`);
logger.warn(`Adding default global override for ${ruleId}`);
globalOverrides.push(override);
}
}
+10
View File
@@ -1361,6 +1361,16 @@ SyncApi.prototype._processSyncResponse = async function(
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"]) {
// 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"];
this.opts.crypto.setNeedsNewFallback(
unusedFallbackKeys instanceof Array &&
!unusedFallbackKeys.includes("signed_curve25519"),
);
}
};
/**
+7
View File
@@ -741,6 +741,13 @@ MatrixCall.prototype._gotRemoteIceCandidate = function(cand) {
//debuglog("Ignoring remote ICE candidate because call has ended");
return;
}
if (
(cand.sdpMid === null || cand.sdpMid === undefined) &&
(cand.sdpMLineIndex === null || cand.sdpMLineIndex === undefined)
) {
debuglog("Ignoring remote ICE candidate with no sdpMid or sdpMLineIndex");
return;
}
debuglog("Got remote ICE " + cand.sdpMid + " candidate: " + cand.candidate);
this.peerConn.addIceCandidate(
new this.webRtc.RtcIceCandidate(cand),
+764 -470
View File
File diff suppressed because it is too large Load Diff