Compare commits

...

221 Commits

Author SHA1 Message Date
RiotRobot 30720bfdd7 v6.2.0 2020-06-04 14:57:14 +01:00
RiotRobot 234a18f016 Prepare changelog for v6.2.0 2020-06-04 14:57:14 +01:00
RiotRobot 2c2d531e7f v6.2.0-rc.1 2020-06-02 13:48:12 +01:00
RiotRobot 91e0f7fbc4 Prepare changelog for v6.2.0-rc.1 2020-06-02 13:48:12 +01:00
J. Ryan Stinnett bebeec7d84 Merge pull request #1304 from MTRNord/fix-register-auth-with-new-spec
Make auth argument in the register request compliant with r0.6.0
2020-06-02 10:13:57 +01:00
Travis Ralston 86d448c285 Merge pull request #1393 from matrix-org/travis/uia-3pid
Send the wrong auth params with the right auth params
2020-06-01 09:14:39 -06:00
Travis Ralston 73f8867a6f Send the wrong auth params with the right auth params
Followon from https://github.com/matrix-org/matrix-react-sdk/pull/4667
2020-06-01 09:08:58 -06:00
Hubert Chathi 0d7cb2bf25 Merge pull request #1387 from matrix-org/uhoreg/picklekey
encrypt cached keys with pickle key
2020-05-28 13:51:25 -04:00
Hubert Chathi 991f590056 another unit fix test 2020-05-27 23:32:23 -04:00
Hubert Chathi 0e6758ccbc more unit test fixes 2020-05-27 23:21:15 -04:00
Hubert Chathi 1d9c6520e5 fix unit tests 2020-05-27 20:02:40 -04:00
Hubert Chathi c81f11df0a encrypt/decrypt cached private keys with pickle key 2020-05-27 19:34:28 -04:00
Travis Ralston f39a1e70de Fix the tests 2020-05-27 13:32:28 -06:00
Travis Ralston f0fa249d36 Request fresh flows on the initial registration request 2020-05-27 13:16:10 -06:00
Travis Ralston 8f72197817 Merge branch 'develop' into fix-register-auth-with-new-spec 2020-05-27 12:14:42 -06:00
Hubert Chathi 1556ac84da add a pickle key option to the client 2020-05-27 12:57:00 -04:00
David Baker b6da6bb835 Merge pull request #1385 from Sorunome/patch-1
Fix replying to key share requests
2020-05-26 12:10:44 +01:00
Sorunome b1a882ea79 Fix replying to key share requests
This check was meant to filter out key sharing requests from ourselves. Accidentally, it filtered out sharing requests from anyone *but* ourselves.
2020-05-26 13:04:31 +02:00
Michael Telatynski 3305f2cc72 Merge pull request #1384 from matrix-org/t3chguy/cdn
Add dist to package.json files so CDNs can serve it
2020-05-25 15:56:26 +01:00
Michael Telatynski c589a5211b Add dist to package.json files so CDNs can serve it
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-05-25 14:29:50 +01:00
Michael Telatynski ff0d91979b Merge pull request #1382 from matrix-org/t3chguy/fix_getVersion_warning
Fix getVersion warning saying undefined room
2020-05-22 14:44:11 +01:00
Michael Telatynski 9d6888cf74 Fix getVersion warning saying undefined room
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-05-22 14:21:51 +01:00
David Baker a4a7097c10 Merge pull request #1379 from matrix-org/dbkr/combine_default_rules_processing
Combine the two places we processed client-level default push rules
2020-05-21 15:11:46 +01:00
David Baker cf2b058e7c Update comment on default override rules 2020-05-21 10:06:12 +01:00
Hubert Chathi 048d7a9b63 Merge pull request #1378 from matrix-org/uhoreg/fix_mac_check
make MAC check robust against unpadded vs padded base64 differences
2020-05-20 12:05:35 -04:00
David Baker d6e37d0288 Combine the two places we processed client-level default push rules
We did this in two places: updated existing rules when the rules
were stored and then added new ones every time we computed push
action for an event. Just do both when the rules are saved.
2020-05-20 14:52:37 +01:00
Bruno Windels 3cd562fa96 Merge pull request #1375 from matrix-org/bwindels/remove-keybackup-format-migration
Remove key backup format migration
2020-05-20 08:05:28 +00:00
Michael Telatynski bd569cb041 Merge pull request #1241 from matrix-org/t3chguy/fix_unhomoglyph_import
Add simple browserify browser-matrix.js tests
2020-05-19 11:58:35 +01:00
RiotRobot e3c6a0e1a0 Merge branch 'master' into develop 2020-05-19 11:04:15 +01:00
RiotRobot b29176507c v6.1.0 2020-05-19 11:01:13 +01:00
RiotRobot db25e9719b Prepare changelog for v6.1.0 2020-05-19 11:01:13 +01:00
Michael Telatynski 661901b00d tidy up
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-05-18 20:11:32 +01:00
Michael Telatynski b9352cfcc1 Fix tests
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-05-18 20:03:04 +01:00
Michael Telatynski e381b1901e clean up test
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-05-18 19:39:30 +01:00
Hubert Chathi ebdff505eb make MAC check robust against unpadded vs padded base64 differences 2020-05-15 16:52:53 -04:00
Michael Telatynski f806e4342e Add simple browserify sync test
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-05-15 18:56:09 +01:00
Michael Telatynski 7f32d7d320 revert
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-05-15 16:55:00 +01:00
Michael Telatynski b1b4b21d45 Merge branches 'develop' and 't3chguy/fix_unhomoglyph_import' of github.com:matrix-org/matrix-js-sdk into t3chguy/fix_unhomoglyph_import
 Conflicts:
	src/utils.ts
2020-05-15 16:50:23 +01:00
Hubert Chathi 486e8b5445 Merge pull request #1376 from matrix-org/uhoreg/sas2
support new key agreement method for SAS
2020-05-15 10:59:11 -04:00
Bruno Windels 9fe0e1e85f lint 2020-05-15 13:44:10 +02:00
Bruno Windels 8d81240c58 remove key backup format migration 2020-05-15 12:37:41 +02:00
Hubert Chathi bdadcd4532 support new key agreement method for SAS 2020-05-14 16:14:45 -04:00
RiotRobot 1de9a24677 v6.1.0-rc.1 2020-05-14 16:06:17 +01:00
RiotRobot 6335f14a34 Prepare changelog for v6.1.0-rc.1 2020-05-14 16:06:16 +01:00
Bruno Windels da2ef381ac Merge pull request #1373 from matrix-org/bwindels/remove-asym-4s
Remove support for asymmetric 4S encryption
2020-05-14 08:10:01 +00:00
Bruno Windels 7b173f4c74 don't sign 4S keys anymore 2020-05-13 10:38:03 +02:00
Bruno Windels d49eb0bbc4 remove upgrade path from asym to sym 4S algorithm 2020-05-13 10:38:03 +02:00
Bruno Windels 91556d5bcd fix lint 2020-05-13 10:38:03 +02:00
Bruno Windels 039abe1f75 remove tests for upgrade path 2020-05-12 14:53:50 +02:00
Bruno Windels bc32faf467 don't sign/verify 4s key with cross signing master key anymore 2020-05-12 14:49:20 +02:00
Bruno Windels 74a4dfeb67 remove support for asym 4s enc 2020-05-12 14:29:54 +02:00
RiotRobot f120533fad Merge branch 'master' into develop 2020-05-05 11:01:32 +01:00
RiotRobot a1baf39299 v6.0.0 2020-05-05 10:54:38 +01:00
RiotRobot 9f0f1bcc68 Prepare changelog for v6.0.0 2020-05-05 10:54:37 +01:00
J. Ryan Stinnett 246963e181 Merge pull request #1368 from matrix-org/foldleft/13167-spinner-progress-rc
Add progress callback for key backups
2020-05-04 16:34:38 +01:00
Zoe e3134ab0de satisfy linter 2, now with more self-hatred 2020-05-04 16:25:43 +01:00
Zoe b38d52da72 satisfy the linter 2020-05-04 16:25:43 +01:00
Zoe 411c4f40d9 docs 2020-05-04 16:25:43 +01:00
Zoe 694a85b652 lint 2020-05-04 16:25:43 +01:00
Zoe c84e72f53a Added a progressCallback for backup key loading 2020-05-04 16:25:43 +01:00
David Baker cb7e1a9d82 Merge pull request #1367 from matrix-org/dbkr/increase_olm_second_phase_timeout
Increase timeout for 2nd phase of Olm session creation
2020-05-01 18:32:25 +01:00
David Baker 3c9dfc195e Increase timeout for 2nd phase of Olm session creation
The timeouts on the two phases of olm session creation are 2 and 10
seconds respectively, so sessions will fail if servers take more
than 10s to respond. Now that we have two phases, we can afford to
wait longer on the second one because the user's isn't waiting for
it to finish before the message will send, so increase it to 30s
so servers have more of a chance to respond.
2020-05-01 18:25:25 +01:00
David Baker a5c13041c6 Merge pull request #1366 from matrix-org/dbkr/retry_decryption_logging
Add logging on decryption retries
2020-05-01 17:49:13 +01:00
David Baker d699e98346 Add logging on decryption retries
For https://github.com/vector-im/riot-web/issues/13473
2020-05-01 17:36:49 +01:00
RiotRobot 135a76febd v6.0.0-rc.2 2020-05-01 16:18:21 +01:00
RiotRobot fae2856e7e Prepare changelog for v6.0.0-rc.2 2020-05-01 16:18:21 +01:00
David Baker 98426b6186 Merge pull request #1365 from matrix-org/dbkr/emit_on_store_trusted_master_key_rel
Emit event when a trusted self-key is stored
2020-05-01 15:37:09 +01:00
David Baker a63d9d2ccd lint 2020-05-01 15:28:15 +01:00
David Baker c567139e28 Emit event when a trusted self-key is stored
This will cause our own user trust status to change, so emit an
event to say so.
2020-05-01 15:28:09 +01:00
David Baker 3d495b0753 Merge pull request #1364 from matrix-org/dbkr/emit_on_store_trusted_master_key
Emit event when a trusted self-key is stored
2020-05-01 15:27:36 +01:00
David Baker 4946b55c21 lint 2020-05-01 15:13:02 +01:00
David Baker ad7d7154f4 Emit event when a trusted self-key is stored
This will cause our own user trust status to change, so emit an
event to say so.
2020-05-01 15:01:13 +01:00
Zoe e5b6a9f8cb Merge pull request #1352 from matrix-org/foldleft/12252-large-err
Customize error payload for oversized messages
2020-05-01 09:44:43 +01:00
Zoe 04f27d36ef Update src/crypto/OlmDevice.js
Co-authored-by: Travis Ralston <travpc@gmail.com>
2020-05-01 09:40:11 +01:00
J. Ryan Stinnett 683fc98fdc Merge pull request #1363 from matrix-org/jryans/key-backup-network-fail
Return null for key backup state when we haven't checked yet
2020-04-30 17:21:09 +01:00
J. Ryan Stinnett c303e83444 Return null for key backup state when we haven't checked yet
This allows distinguishing the "unknown" case from "not present".

Part of https://github.com/vector-im/riot-web/issues/13404
2020-04-30 15:40:27 +01:00
J. Ryan Stinnett ae5a40d686 Only consider key backup checked in error if 404
Other error codes are likely to mean we never actually reached the server at
all, so we should not consider that as checked.

Part of https://github.com/vector-im/riot-web/issues/13404
2020-04-30 15:22:50 +01:00
Zoe 409b7068bb Merge pull request #1351 from matrix-org/foldleft/13167-spinner-progress
Added a progressCallback for backup key loading
2020-04-30 14:09:16 +01:00
Zoe f2a12c7154 satisfy linter 2, now with more self-hatred 2020-04-30 14:06:20 +01:00
Zoe efac2eac07 satisfy the linter 2020-04-30 12:04:29 +01:00
RiotRobot f3f6f3e39a v6.0.0-rc.1 2020-04-30 11:14:04 +01:00
RiotRobot f905586dbd Prepare changelog for v6.0.0-rc.1 2020-04-30 11:14:03 +01:00
David Baker f393cea2c2 Merge pull request #1362 from matrix-org/dbkr/sessions_there_on_login_are_old_rel
Add initialFetch param to willUpdateDevices / devicesUpdated
2020-04-29 18:14:13 +01:00
David Baker e937998b40 Add initialFetch param to willUpdateDevices / devicesUpdated
This indicates whether the device database is being populated initially
2020-04-29 17:44:25 +01:00
David Baker f85ec08886 Merge pull request #1360 from matrix-org/dbkr/sessions_there_on_login_are_old
Add initialFetch param to willUpdateDevices / devicesUpdated
2020-04-29 17:42:12 +01:00
J. Ryan Stinnett 7ff68f3844 Merge pull request #1361 from matrix-org/bwindels/fixrequestreadyrace-rc
Fix race between sending .request and receiving .ready over to_device
2020-04-29 17:26:11 +01:00
Bruno Windels 04529bd524 add to_device verif request to txnId -> request map before sending
so if the .ready event arrives before the request finishes,
it still ends up in the correct VerificationRequest
2020-04-29 18:20:16 +02:00
Bruno Windels 2c681d93d9 Merge pull request #1359 from matrix-org/bwindels/fixrequestreadyrace
Fix race between sending .request and receiving .ready over to_device
2020-04-29 16:19:31 +00:00
David Baker 1451fcb040 Add initialFetch param to willUpdateDevices / devicesUpdated
This indicates whether the device database is being populated initially
2020-04-29 17:01:50 +01:00
Zoe 4986f3c2ca docs 2020-04-29 16:55:27 +01:00
Bruno Windels e9edb85a32 add to_device verif request to txnId -> request map before sending
so if the .ready event arrives before the request finishes,
it still ends up in the correct VerificationRequest
2020-04-29 17:51:32 +02:00
J. Ryan Stinnett dca60ae4ae Merge pull request #1358 from matrix-org/bwindels/fixwaitforeventrace-rc
Handle race between sending and await next event from other party
2020-04-29 15:21:35 +01:00
Bruno Windels 025f964b0b Merge pull request #1357 from matrix-org/bwindels/fixwaitforeventrace
Handle race between sending and await next event from other party
2020-04-29 13:53:35 +00:00
Bruno Windels ff46a8fa9e handle race between sending and await next event from other party 2020-04-29 15:52:20 +02:00
Bruno Windels 59412aba51 handle race between sending and await next event from other party 2020-04-29 15:45:51 +02:00
David Baker a351ee9f76 Merge pull request #1356 from matrix-org/dbkr/another_round_of_toast_rel
Add crypto.willUpdateDevices event and make getStoredDevices/getStoredDevicesForUser synchronous
2020-04-29 12:50:00 +01:00
David Baker a5b14092cd Make getStoredDevice calls sync 2020-04-29 12:23:03 +01:00
David Baker 90f1de9c3f Add crypto.willUpdateDevices event and make getStoredDevices/getStoredDevicesForUser synchronous
Add an event fired before devices are updated, so apps can monitor
changes in devices by observing the state before and after.

BREAKING CHANGE:
Make getStoredDevices/getStoredDevicesForUser synchronous so they
can safely be called from the event listener without racing with
the data being updated. They didn't do any async work so didn't need
to be async.

Add doc for crypto.devicesUpdated
2020-04-29 12:23:00 +01:00
David Baker a516f06946 Merge pull request #1354 from matrix-org/dbkr/another_round_of_toast
Add crypto.willUpdateDevices event and make getStoredDevices/getStoredDevicesForUser synchronous
2020-04-29 12:09:11 +01:00
David Baker 7c1f3e4519 Make getStoredDevice calls sync 2020-04-29 11:50:18 +01:00
Michael Telatynski 3bb1a6336b Merge pull request #1350 from matrix-org/t3chguy/redaction_redesign
Fix sender of local echo events in unsigned redactions
2020-04-29 11:17:28 +01:00
J. Ryan Stinnett 7dc926596f Merge pull request #1355 from matrix-org/jryans/rm-extra-key-backup-setup-rc
Remove redundant key backup setup path
2020-04-29 09:46:17 +01:00
J. Ryan Stinnett e775515c38 Remove the other copy instead 2020-04-28 21:34:17 +01:00
J. Ryan Stinnett c116f2b1bc Remove redundant key backup setup path
Bootstrap was create key backup twice due to some code duplication we missed in
previous refactorings.

Fixes https://github.com/vector-im/riot-web/issues/13423
2020-04-28 21:34:17 +01:00
J. Ryan Stinnett f24993d6aa Merge pull request #1353 from matrix-org/jryans/rm-extra-key-backup-setup
Remove redundant key backup setup path
2020-04-28 21:33:49 +01:00
David Baker f053cf1fdb Add crypto.willUpdateDevices event and make getStoredDevices/getStoredDevicesForUser synchronous
Add an event fired before devices are updated, so apps can monitor
changes in devices by observing the state before and after.

BREAKING CHANGE:
Make getStoredDevices/getStoredDevicesForUser synchronous so they
can safely be called from the event listener without racing with
the data being updated. They didn't do any async work so didn't need
to be async.

Add doc for crypto.devicesUpdated
2020-04-28 18:31:04 +01:00
J. Ryan Stinnett 251318eaf0 Remove the other copy instead 2020-04-28 17:20:49 +01:00
J. Ryan Stinnett a66e7d79ad Remove redundant key backup setup path
Bootstrap was create key backup twice due to some code duplication we missed in
previous refactorings.

Fixes https://github.com/vector-im/riot-web/issues/13423
2020-04-28 16:56:37 +01:00
Zoe 38884083a2 Customize error payload for oversized messages 2020-04-28 16:39:35 +01:00
Zoe 0b1088d9a8 lint 2020-04-28 11:31:41 +01:00
Zoe 875f9ca388 Added a progressCallback for backup key loading 2020-04-28 11:28:39 +01:00
David Baker 25cd1f25f1 Merge pull request #1349 from matrix-org/dbkr/retryDecryption_remove_dead_code
Remove some dead code from _retryDecryption
2020-04-28 11:23:53 +01:00
Michael Telatynski 7152ece70a Fix sender of local echo events in unsigned redactions
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-04-27 23:52:22 +01:00
David Baker 896c76ce9d Merge pull request #1348 from matrix-org/dbkr/keyrequest_wait_sync_process
Don't send key requests until after sync processing is finished
2020-04-24 14:38:54 +01:00
David Baker 21d3dd4506 Remove some dead code from _retryDecryption
I think this was attempting to remove the events from _pendingEvents
but a) it wasn't and b) it probably shouldn't be because the retry
itself will re-add them when the decryption attempt starts and remove
them if it succeeds.

Also fix what was presumably a c+p fail.
2020-04-24 14:36:42 +01:00
David Baker 300b32c8d7 Let's keep starting the timer for the retries straight away
rather than leaving them until next time the queue is sent
2020-04-24 13:16:07 +01:00
David Baker 19e3b35fc7 Fix immediate key request sending 2020-04-24 11:26:48 +01:00
David Baker 320811f9ed Don't send key requests until after sync processing is finished
Key requests wait for a short time (500ms) before being sent as
an attempt to wait for the key to arrive before sending a request
for it, but client startup takes way longer than this so this
would still result in key requests being sent for keys that we'd
fetched but were still waiting to be read out of the sync response
and put into the database.
2020-04-24 11:19:52 +01:00
David Baker 991457496f Merge pull request #1346 from matrix-org/dbkr/dont_talk_to_ourself
Prevent attempts to send olm messages to ourselves
2020-04-23 16:10:36 +01:00
RiotRobot 1cb6134758 v5.3.1-rc.4 2020-04-23 15:41:07 +01:00
RiotRobot a255b8e450 Prepare changelog for v5.3.1-rc.4 2020-04-23 15:41:07 +01:00
Bruno Windels 324f036b35 Merge pull request #1347 from matrix-org/bwindels/retry4s-rc
Retry account data upload requests
2020-04-23 14:33:36 +00:00
Bruno Windels 479a284e10 move fn to http-api and add jsdoc 2020-04-23 16:29:08 +02:00
Bruno Windels e0660ce01c log while retrying 2020-04-23 16:29:07 +02:00
Bruno Windels 3a3f6cb7ca cleanup, return straight away 2020-04-23 16:29:07 +02:00
Bruno Windels 9797e6021b exclude aborted requests and matrix errors when creating connectionerror
- throw an AbortError when aborting is detected
 - don't turn AbortError's into a ConnectionError
 - also consider the string "aborted" an AbortError for unit tests
 - unit tests like to reject the request with a MatrixError,
   so exclude those too
2020-04-23 16:29:07 +02:00
Bruno Windels a9212d33b1 retry PUT account data requests 2020-04-23 16:29:07 +02:00
Bruno Windels d32d3fd864 retry util function 2020-04-23 16:29:07 +02:00
Bruno Windels a19cdd06cf throw ConnectionError when request fails 2020-04-23 16:29:07 +02:00
Bruno Windels f4d0f89fda cleanup: use class for MatrixError
this way we get stacktraces as well
2020-04-23 16:29:07 +02:00
Bruno Windels ca34cb951e Merge pull request #1345 from matrix-org/bwindels/retry4s
Retry account data upload requests
2020-04-23 14:28:31 +00:00
David Baker 4c2d1b0385 typo
Co-Authored-By: J. Ryan Stinnett <jryans@gmail.com>
2020-04-23 14:40:17 +01:00
Bruno Windels 6b4fefc123 move fn to http-api and add jsdoc 2020-04-23 15:32:40 +02:00
Bruno Windels 86913dccb0 log while retrying 2020-04-23 15:25:10 +02:00
Bruno Windels 30101a4428 cleanup, return straight away 2020-04-23 15:22:46 +02:00
Bruno Windels 668e8f6f24 exclude aborted requests and matrix errors when creating connectionerror
- throw an AbortError when aborting is detected
 - don't turn AbortError's into a ConnectionError
 - also consider the string "aborted" an AbortError for unit tests
 - unit tests like to reject the request with a MatrixError,
   so exclude those too
2020-04-23 15:18:52 +02:00
David Baker 2f51e206c7 Prevent attempts to send olm messages to ourselves
The cause of this I've seen is us sending keyshare reqiests when
we didn't have keys but them arriving when we do, and us replying to
ourseles with keys, so exclude that explicity.

Also catch this lower down to catch any other code paths that
mistaklenly try this. Hopefully the comment explains enough as to why
it won't work.
2020-04-23 13:36:54 +01:00
Bruno Windels 78b8b36a87 retry PUT account data requests 2020-04-23 13:37:56 +02:00
Bruno Windels af3ef86d19 retry util function 2020-04-23 13:37:33 +02:00
Bruno Windels 50febaf477 throw ConnectionError when request fails 2020-04-23 13:37:10 +02:00
Bruno Windels 907f317b04 cleanup: use class for MatrixError
this way we get stacktraces as well
2020-04-23 13:36:40 +02:00
David Baker 6edb78d015 Merge pull request #1344 from matrix-org/dbkr/log_megolm_session_first_index
Log first known index with megolm session updates
2020-04-23 12:30:48 +01:00
David Baker b58846ab6e Log first known index with megolm session updates
Move the logging lower to a point where we know the first index.
2020-04-23 11:43:22 +01:00
David Baker 32233a2c7b Merge pull request #1343 from matrix-org/dbkr/prune_todevice_messages
Prune to_device messages to avoid sending empty messages
2020-04-22 13:54:29 +01:00
David Baker 8b2752441d Prune to_device messages to avoid sending empty messages
Fixes https://github.com/vector-im/riot-web/issues/13322
2020-04-22 13:48:57 +01:00
Michael Telatynski e6af29df94 Merge pull request #1335 from matrix-org/t3chguy/ts1
Convert bunch of things to TypeScript
2020-04-22 10:26:53 +01:00
David Baker 4ae5404fe4 Merge pull request #1342 from matrix-org/dbkr/log_new_olm_sessions
Add logging when making new Olm sessions
2020-04-22 09:47:44 +01:00
David Baker 7f0bdc8ddb Add logging when making new Olm sessions 2020-04-21 18:31:36 +01:00
Bruno Windels 39836f115b Merge pull request #1341 from matrix-org/bwindels/fixfilterlookup-rc
Fix: handle filter not found
2020-04-21 14:15:03 +00:00
Bruno Windels 4a699fe6a7 Merge pull request #1340 from matrix-org/bwindels/fixfilterlookup
Fix: handle filter not found
2020-04-21 14:13:33 +00:00
David Baker 7cc61a887c Merge pull request #1339 from matrix-org/dbkr/getAccountDataFromServer_404_rel
Make getAccountDataFromServer return null if not found
2020-04-21 15:10:20 +01:00
David Baker 8f73f1ed3c Merge pull request #1338 from matrix-org/dbkr/getAccountDataFromServer_404
Make getAccountDataFromServer return null if not found
2020-04-21 15:10:13 +01:00
Bruno Windels 67fb027d39 filter out invalid filter id values like 'undefined'
this way we don't attempt to fetch filters when validating them
with filterId==="undefined"
2020-04-21 16:00:45 +02:00
Bruno Windels c6b61ea0ea Fix unknown filters returning 400 rather than 404
This made getFilter in sync retrying continuously
I also refactored getOrCreateFilter to use await
2020-04-21 16:00:45 +02:00
Bruno Windels 32841234a7 filter out invalid filter id values like 'undefined'
this way we don't attempt to fetch filters when validating them
with filterId==="undefined"
2020-04-21 15:56:39 +02:00
Bruno Windels 6f6520ed0f Fix unknown filters returning 400 rather than 404
This made getFilter in sync retrying continuously
I also refactored getOrCreateFilter to use await
2020-04-21 15:54:50 +02:00
David Baker b3860f3754 Make getAccountDataFromServer return null if not found
Callers assume that if the account data isn't there, the method will
return null rather than throw an exception. This meant that this only
actually worked in these cases when the sync had completed and it was
using the locally stored account data, so this ceased to be a
problem in practice when we made it wait for the initial sync to finish
before entering the security setup flow.

Fixes https://github.com/vector-im/riot-web/issues/13169
2020-04-21 14:32:26 +01:00
David Baker 0958917317 Make getAccountDataFromServer return null if not found
Callers assume that if the account data isn't there, the method will
return null rather than throw an exception. This meant that this only
actually worked in these cases when the sync had completed and it was
using the locally stored account data, so this ceased to be a
problem in practice when we made it wait for the initial sync to finish
before entering the security setup flow.

Fixes https://github.com/vector-im/riot-web/issues/13169
2020-04-21 14:28:10 +01:00
David Baker f42fa7e791 Merge pull request #1337 from matrix-org/dbkr/setdefaultkey_fail_rel
Fix setDefaultKeyId to fail if the request fails
2020-04-21 14:21:42 +01:00
David Baker 7022d1f3bf Merge pull request #1336 from matrix-org/dbkr/setdefaultkey_fail
Fix setDefaultKeyId to fail if the request fails
2020-04-21 14:21:35 +01:00
David Baker b592c41f96 Fix setDefaultKeyId to fail if the request fails
It returned only when the echo came down the sync stream, but we
forgot to make it fail if the reuqest failed and it would just
never return in this case.

Fixes https://github.com/vector-im/riot-web/issues/13162
2020-04-21 13:52:48 +01:00
David Baker 62188571d7 Fix setDefaultKeyId to fail if the request fails
It returned only when the echo came down the sync stream, but we
forgot to make it fail if the reuqest failed and it would just
never return in this case.

Fixes https://github.com/vector-im/riot-web/issues/13162
2020-04-21 13:50:21 +01:00
Michael Telatynski 8cca3392f6 fix localStorage type
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-04-21 12:03:25 +01:00
Michael Telatynski 7082516664 Improve ts types and enable esModuleInterop
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-04-21 11:55:56 +01:00
Michael Telatynski 77a1fc9e60 Convert bunch of things to TypeScript
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-04-21 11:45:54 +01:00
Travis Ralston e2b1dd6532 Merge pull request #1328 from MichaelKohler/issue905-setRoomEncryption-doc
Document setRoomEncryption not modifying room state
2020-04-21 00:30:14 -06:00
Bruno Windels 7b7fdb0a65 Merge pull request #1333 from matrix-org/bwindels/onlyonefilterplease-rc
Fix: don't do extra /filter request when enabling lazy loading of members
2020-04-20 16:36:26 +00:00
Bruno Windels 414279f644 Merge pull request #1332 from matrix-org/bwindels/onlyonefilterplease
Fix: don't do extra /filter request when enabling lazy loading of members
2020-04-20 16:24:52 +00:00
Bruno Windels be91c8580d rename build function 2020-04-20 18:22:30 +02:00
Bruno Windels befaa782f6 when enabling LL, set the flag on Filter, rather than doing /filter 2020-04-20 18:22:30 +02:00
Bruno Windels 8e29dae36a rename build function 2020-04-20 18:06:10 +02:00
Bruno Windels d2438d10a6 when enabling LL, set the flag on Filter, rather than doing /filter 2020-04-20 17:26:19 +02:00
David Baker b3752bb2c6 Merge pull request #1331 from matrix-org/dbkr/fix_reject_no_auth_flow_found_rel
Reject attemptAuth promise if no auth flow found
2020-04-20 13:38:49 +01:00
Michael Kohler 02e145b0de Reject attemptAuth promise if no auth flow found 2020-04-20 13:34:18 +01:00
David Baker 0fcf8777a1 Merge pull request #1329 from MichaelKohler/fix-reject-no-auth-flow-found
Reject attemptAuth promise if no auth flow found
2020-04-20 13:06:44 +01:00
J. Ryan Stinnett 26cae5543b Merge pull request #1330 from matrix-org/dbkr/serialise_prekey_decryptions_rel
Serialise Olm prekey decryptions
2020-04-20 10:14:24 +01:00
David Baker b0573dec77 Add a catch so the error doesn't propagate to later decryptions 2020-04-20 10:08:13 +01:00
David Baker c5f4e762e5 Serialise Olm prekey decryptions
If they overlap, they can both try to create new sessions, but this
removes the OTK so it will only work once.

Fixes https://github.com/vector-im/riot-web/issues/13229
2020-04-20 10:08:07 +01:00
Michael Kohler f4c08477d0 Reject attemptAuth promise if no auth flow found 2020-04-19 14:04:10 +02:00
Michael Kohler 4342ee6d4a Document setRoomEncrption not modifying room state (fixes #905) 2020-04-18 19:49:05 +02:00
Michael Telatynski 7b73561923 Merge pull request #1327 from nchamo/fix-filter-component-check
Fix FilterComponent allowed_values check
2020-04-18 14:45:31 +01:00
Nicolas Chamo 87c0cf233c Update src/filter-component.js
Co-Authored-By: Michael Telatynski <7t3chguy@googlemail.com>
2020-04-17 23:59:38 -03:00
Nicolas Chamo 145cd7894b More lint fixes 2020-04-17 19:56:32 -03:00
Nicolas Chamo 6cf8a76c29 linting 2020-04-17 19:50:15 -03:00
nchamo 7ea09ebe4a Add fix and test
Signed-off-by: Nicolas Chamo <nicolas@chamo.com.ar>
2020-04-17 19:37:33 -03:00
David Baker 1dcb16365b Merge pull request #1326 from matrix-org/dbkr/serialise_prekey_decryptions
Serialise Olm prekey decryptions
2020-04-17 18:07:24 +01:00
David Baker 83b33d9d7a Add a catch so the error doesn't propagate to later decryptions 2020-04-17 18:03:15 +01:00
David Baker 83879fa945 Serialise Olm prekey decryptions
If they overlap, they can both try to create new sessions, but this
removes the OTK so it will only work once.

Fixes https://github.com/vector-im/riot-web/issues/13229
2020-04-17 17:47:46 +01:00
RiotRobot c3903f2796 v5.3.1-rc.3 2020-04-17 17:35:49 +01:00
RiotRobot 18564553ad Prepare changelog for v5.3.1-rc.3 2020-04-17 17:35:48 +01:00
J. Ryan Stinnett fe3e87a9d6 Merge pull request #1323 from matrix-org/dbkr/fix_ssss_reset_rel
Fix cross-signing/SSSS reset
2020-04-17 13:42:47 +01:00
Bruno Windels 78c734d2e9 Merge pull request #1325 from matrix-org/bwindels/fixkeybackupcrash-rc
Fix: crash when backup key needs fixing from corruption issue
2020-04-17 12:23:08 +00:00
Bruno Windels 5afa16d454 method is on crypto 2020-04-17 14:19:55 +02:00
Bruno Windels 57233416d9 Merge pull request #1324 from matrix-org/bwindels/fixkeybackupcrash
Fix: crash when backup key needs fixing from corruption issue
2020-04-17 12:11:07 +00:00
Bruno Windels f56ce29210 method is on crypto 2020-04-17 13:51:12 +02:00
David Baker 149f59e9be lint 2020-04-16 20:42:00 +01:00
David Baker 761892d6b1 Fix cross-signing/SSSS reset
We re-used the old SSSS key even when resetting, meaning we prompted
the user to create a new passphrase but then ignored it and kept using
the old one.

Fixes https://github.com/vector-im/riot-web/issues/13212
2020-04-16 20:41:55 +01:00
David Baker 4b639d5f9a Merge pull request #1322 from matrix-org/dbkr/fix_ssss_reset
Fix cross-signing/SSSS reset
2020-04-16 20:38:47 +01:00
David Baker da8ed77aae lint 2020-04-16 20:18:27 +01:00
David Baker 12f46909f7 Fix cross-signing/SSSS reset
We re-used the old SSSS key even when resetting, meaning we prompted
the user to create a new passphrase but then ignored it and kept using
the old one.

Fixes https://github.com/vector-im/riot-web/issues/13212
2020-04-16 20:13:18 +01:00
RiotRobot faf18b1996 v5.3.1-rc.2 2020-04-16 18:39:06 +01:00
RiotRobot 218deddd00 Prepare changelog for v5.3.1-rc.2 2020-04-16 18:39:06 +01:00
Bruno Windels d6fe650c4c Merge pull request #1321 from matrix-org/bwindels/untrustedqrcodereciprocate-rc
Implement QR code reciprocate for self-verification with untrusted MSK
2020-04-16 12:38:04 +00:00
Bruno Windels 919189db1a Merge pull request #1320 from matrix-org/bwindels/untrustedqrcodereciprocate
Implement QR code reciprocate for self-verification with untrusted MSK
2020-04-16 12:34:46 +00:00
Bruno Windels 951191d99a update comments 2020-04-16 14:31:44 +02:00
Bruno Windels f814a96eac update comments 2020-04-16 14:30:51 +02:00
Bruno Windels 2654f4bf0f implement qr code reciprocate for self-verif with untrusted MSK 2020-04-16 13:26:56 +02:00
Bruno Windels 218aa423c3 implement qr code reciprocate for self-verif with untrusted MSK 2020-04-16 13:01:46 +02:00
Marcel ea8362ed63 Keep the null in authData if this is the first stage. 2020-04-10 19:32:50 +02:00
Marcel 3a1508c2ab Fix "should make a request if no authdata is provided" test 2020-04-10 19:15:21 +02:00
Marcel f914391aaf Revert hack to set auth = null 2020-04-10 18:31:35 +02:00
Marcel 379322db0d Revert setting authDict = null by default
Remove console.log
Revert null checks on this._data
2020-04-08 20:50:59 +02:00
Marcel bf6d1f6555 Fix interactive-auth.js lint 2020-04-07 13:26:27 +02:00
Marcel 9faeac4c83 Merge branch 'develop' into fix-register-auth-with-new-spec 2020-04-07 13:15:51 +02:00
Marcel a863cf3d72 Fix auth linter error and tests 2020-04-07 13:13:49 +02:00
Marcel 45a92bcf9c Fix auth check as auth == {} not works 2020-04-06 17:30:32 +02:00
Marcel 0a0441756b Fix auth check 2020-04-06 17:23:24 +02:00
Marcel c1de2ebbf9 Fix authdata to only be null if nothing had been added. 2020-04-06 17:14:43 +02:00
Marcel 557c2db955 Handle Error during user-interactive auth: TypeError: "this._data is null" 2020-04-06 15:37:52 +02:00
Marcel ed8d064a13 Replace {} with null to be compliant with newer specs. While older also accepted null for the auth part of the registration request object 2020-04-06 15:22:04 +02:00
Michael Telatynski 9da1f7b8d5 Fix unhomoglyph import to make browser-matrix.js happy once more 2020-03-02 10:54:14 +00:00
44 changed files with 1150 additions and 1258 deletions
+182
View File
@@ -1,3 +1,185 @@
Changes in [6.2.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.2.0) (2020-06-04)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.2.0-rc.1...v6.2.0)
* No changes since rc.1
Changes in [6.2.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.2.0-rc.1) (2020-06-02)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.1.0...v6.2.0-rc.1)
* Make auth argument in the register request compliant with r0.6.0
[\#1304](https://github.com/matrix-org/matrix-js-sdk/pull/1304)
* Send the wrong auth params with the right auth params
[\#1393](https://github.com/matrix-org/matrix-js-sdk/pull/1393)
* encrypt cached keys with pickle key
[\#1387](https://github.com/matrix-org/matrix-js-sdk/pull/1387)
* Fix replying to key share requests
[\#1385](https://github.com/matrix-org/matrix-js-sdk/pull/1385)
* Add dist to package.json files so CDNs can serve it
[\#1384](https://github.com/matrix-org/matrix-js-sdk/pull/1384)
* Fix getVersion warning saying undefined room
[\#1382](https://github.com/matrix-org/matrix-js-sdk/pull/1382)
* Combine the two places we processed client-level default push rules
[\#1379](https://github.com/matrix-org/matrix-js-sdk/pull/1379)
* make MAC check robust against unpadded vs padded base64 differences
[\#1378](https://github.com/matrix-org/matrix-js-sdk/pull/1378)
* Remove key backup format migration
[\#1375](https://github.com/matrix-org/matrix-js-sdk/pull/1375)
* Add simple browserify browser-matrix.js tests
[\#1241](https://github.com/matrix-org/matrix-js-sdk/pull/1241)
* support new key agreement method for SAS
[\#1376](https://github.com/matrix-org/matrix-js-sdk/pull/1376)
Changes in [6.1.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.1.0) (2020-05-19)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.1.0-rc.1...v6.1.0)
* No changes since rc.1
Changes in [6.1.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.1.0-rc.1) (2020-05-14)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.0.0...v6.1.0-rc.1)
* Remove support for asymmetric 4S encryption
[\#1373](https://github.com/matrix-org/matrix-js-sdk/pull/1373)
* Increase timeout for 2nd phase of Olm session creation
[\#1367](https://github.com/matrix-org/matrix-js-sdk/pull/1367)
* Add logging on decryption retries
[\#1366](https://github.com/matrix-org/matrix-js-sdk/pull/1366)
* Emit event when a trusted self-key is stored
[\#1364](https://github.com/matrix-org/matrix-js-sdk/pull/1364)
* Customize error payload for oversized messages
[\#1352](https://github.com/matrix-org/matrix-js-sdk/pull/1352)
* Return null for key backup state when we haven't checked yet
[\#1363](https://github.com/matrix-org/matrix-js-sdk/pull/1363)
* Added a progressCallback for backup key loading
[\#1351](https://github.com/matrix-org/matrix-js-sdk/pull/1351)
* Add initialFetch param to willUpdateDevices / devicesUpdated
[\#1360](https://github.com/matrix-org/matrix-js-sdk/pull/1360)
* Fix race between sending .request and receiving .ready over to_device
[\#1359](https://github.com/matrix-org/matrix-js-sdk/pull/1359)
* Handle race between sending and await next event from other party
[\#1357](https://github.com/matrix-org/matrix-js-sdk/pull/1357)
* Add crypto.willUpdateDevices event and make
getStoredDevices/getStoredDevicesForUser synchronous
[\#1354](https://github.com/matrix-org/matrix-js-sdk/pull/1354)
* Fix sender of local echo events in unsigned redactions
[\#1350](https://github.com/matrix-org/matrix-js-sdk/pull/1350)
* Remove redundant key backup setup path
[\#1353](https://github.com/matrix-org/matrix-js-sdk/pull/1353)
* Remove some dead code from _retryDecryption
[\#1349](https://github.com/matrix-org/matrix-js-sdk/pull/1349)
* Don't send key requests until after sync processing is finished
[\#1348](https://github.com/matrix-org/matrix-js-sdk/pull/1348)
* Prevent attempts to send olm messages to ourselves
[\#1346](https://github.com/matrix-org/matrix-js-sdk/pull/1346)
* Retry account data upload requests
[\#1345](https://github.com/matrix-org/matrix-js-sdk/pull/1345)
* Log first known index with megolm session updates
[\#1344](https://github.com/matrix-org/matrix-js-sdk/pull/1344)
* Prune to_device messages to avoid sending empty messages
[\#1343](https://github.com/matrix-org/matrix-js-sdk/pull/1343)
* Convert bunch of things to TypeScript
[\#1335](https://github.com/matrix-org/matrix-js-sdk/pull/1335)
* Add logging when making new Olm sessions
[\#1342](https://github.com/matrix-org/matrix-js-sdk/pull/1342)
* Fix: handle filter not found
[\#1340](https://github.com/matrix-org/matrix-js-sdk/pull/1340)
* Make getAccountDataFromServer return null if not found
[\#1338](https://github.com/matrix-org/matrix-js-sdk/pull/1338)
* Fix setDefaultKeyId to fail if the request fails
[\#1336](https://github.com/matrix-org/matrix-js-sdk/pull/1336)
* Document setRoomEncryption not modifying room state
[\#1328](https://github.com/matrix-org/matrix-js-sdk/pull/1328)
* Fix: don't do extra /filter request when enabling lazy loading of members
[\#1332](https://github.com/matrix-org/matrix-js-sdk/pull/1332)
* Reject attemptAuth promise if no auth flow found
[\#1329](https://github.com/matrix-org/matrix-js-sdk/pull/1329)
* Fix FilterComponent allowed_values check
[\#1327](https://github.com/matrix-org/matrix-js-sdk/pull/1327)
* Serialise Olm prekey decryptions
[\#1326](https://github.com/matrix-org/matrix-js-sdk/pull/1326)
* Fix: crash when backup key needs fixing from corruption issue
[\#1324](https://github.com/matrix-org/matrix-js-sdk/pull/1324)
* Fix cross-signing/SSSS reset
[\#1322](https://github.com/matrix-org/matrix-js-sdk/pull/1322)
* Implement QR code reciprocate for self-verification with untrusted MSK
[\#1320](https://github.com/matrix-org/matrix-js-sdk/pull/1320)
Changes in [6.0.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.0.0) (2020-05-05)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.0.0-rc.2...v6.0.0)
* Add progress callback for key backups
[\#1368](https://github.com/matrix-org/matrix-js-sdk/pull/1368)
Changes in [6.0.0-rc.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.0.0-rc.2) (2020-05-01)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.0.0-rc.1...v6.0.0-rc.2)
* Emit event when a trusted self-key is stored
[\#1365](https://github.com/matrix-org/matrix-js-sdk/pull/1365)
Changes in [6.0.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.0.0-rc.1) (2020-04-30)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v5.3.1-rc.4...v6.0.0-rc.1)
BREAKING CHANGES
---
* client.getStoredDevicesForUser and client.getStoredDevices are no longer async
All Changes
---
* Add initialFetch param to willUpdateDevices / devicesUpdated
[\#1362](https://github.com/matrix-org/matrix-js-sdk/pull/1362)
* Fix race between sending .request and receiving .ready over to_device
[\#1361](https://github.com/matrix-org/matrix-js-sdk/pull/1361)
* Handle race between sending and await next event from other party
[\#1358](https://github.com/matrix-org/matrix-js-sdk/pull/1358)
* Add crypto.willUpdateDevices event and make
getStoredDevices/getStoredDevicesForUser synchronous
[\#1356](https://github.com/matrix-org/matrix-js-sdk/pull/1356)
* Remove redundant key backup setup path
[\#1355](https://github.com/matrix-org/matrix-js-sdk/pull/1355)
Changes in [5.3.1-rc.4](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v5.3.1-rc.4) (2020-04-23)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v5.3.1-rc.3...v5.3.1-rc.4)
* Retry account data upload requests
[\#1347](https://github.com/matrix-org/matrix-js-sdk/pull/1347)
* Fix: handle filter not found
[\#1341](https://github.com/matrix-org/matrix-js-sdk/pull/1341)
* Make getAccountDataFromServer return null if not found
[\#1339](https://github.com/matrix-org/matrix-js-sdk/pull/1339)
* Fix setDefaultKeyId to fail if the request fails
[\#1337](https://github.com/matrix-org/matrix-js-sdk/pull/1337)
* Fix: don't do extra /filter request when enabling lazy loading of members
[\#1333](https://github.com/matrix-org/matrix-js-sdk/pull/1333)
* Reject attemptAuth promise if no auth flow found
[\#1331](https://github.com/matrix-org/matrix-js-sdk/pull/1331)
* Serialise Olm prekey decryptions
[\#1330](https://github.com/matrix-org/matrix-js-sdk/pull/1330)
Changes in [5.3.1-rc.3](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v5.3.1-rc.3) (2020-04-17)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v5.3.1-rc.2...v5.3.1-rc.3)
* Fix cross-signing/SSSS reset
[\#1323](https://github.com/matrix-org/matrix-js-sdk/pull/1323)
* Fix: crash when backup key needs fixing from corruption issue
[\#1325](https://github.com/matrix-org/matrix-js-sdk/pull/1325)
Changes in [5.3.1-rc.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v5.3.1-rc.2) (2020-04-16)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v5.3.1-rc.1...v5.3.1-rc.2)
* Implement QR code reciprocate for self-verification with untrusted MSK
[\#1321](https://github.com/matrix-org/matrix-js-sdk/pull/1321)
Changes in [5.3.1-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v5.3.1-rc.1) (2020-04-15)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v5.3.0-rc.1...v5.3.1-rc.1)
+3 -1
View File
@@ -1,6 +1,6 @@
{
"name": "matrix-js-sdk",
"version": "5.3.1-rc.1",
"version": "6.2.0",
"description": "Matrix Client-Server SDK for Javascript",
"scripts": {
"prepare": "yarn build",
@@ -35,6 +35,7 @@
"author": "matrix.org",
"license": "Apache-2.0",
"files": [
"dist",
"lib",
"src",
"git-revision.txt",
@@ -68,6 +69,7 @@
"@babel/preset-typescript": "^7.7.4",
"@babel/register": "^7.7.4",
"@types/node": "12",
"@types/request": "^2.48.4",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0",
"babelify": "^10.0.0",
+23
View File
@@ -0,0 +1,23 @@
/*
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.
*/
// stub for browser-matrix browserify tests
global.XMLHttpRequest = jest.fn();
afterAll(() => {
// clean up XMLHttpRequest mock
global.XMLHttpRequest = undefined;
});
+103
View File
@@ -0,0 +1,103 @@
/*
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.
*/
// load XmlHttpRequest mock
import "./setupTests";
import "../../dist/browser-matrix"; // uses browser-matrix instead of the src
import {MockStorageApi} from "../MockStorageApi";
import {WebStorageSessionStore} from "../../src/store/session/webstorage";
import MockHttpBackend from "matrix-mock-request";
import {LocalStorageCryptoStore} from "../../src/crypto/store/localStorage-crypto-store";
import * as utils from "../test-utils";
const USER_ID = "@user:test.server";
const DEVICE_ID = "device_id";
const ACCESS_TOKEN = "access_token";
const ROOM_ID = "!room_id:server.test";
/* global matrixcs */
describe("Browserify Test", function() {
let client;
let httpBackend;
async function createTestClient() {
const sessionStoreBackend = new MockStorageApi();
const sessionStore = new WebStorageSessionStore(sessionStoreBackend);
const httpBackend = new MockHttpBackend();
const options = {
baseUrl: "http://" + USER_ID + ".test.server",
userId: USER_ID,
accessToken: ACCESS_TOKEN,
deviceId: DEVICE_ID,
sessionStore: sessionStore,
request: httpBackend.requestFn,
cryptoStore: new LocalStorageCryptoStore(sessionStoreBackend),
};
const client = matrixcs.createClient(options);
httpBackend.when("GET", "/pushrules").respond(200, {});
httpBackend.when("POST", "/filter").respond(200, { filter_id: "fid" });
return { client, httpBackend };
}
beforeEach(async () => {
({client, httpBackend} = await createTestClient());
await client.startClient();
});
afterEach(async () => {
client.stopClient();
await httpBackend.stop();
});
it("Sync", async function() {
const event = utils.mkMembership({
room: ROOM_ID,
mship: "join",
user: "@other_user:server.test",
name: "Displayname",
});
const syncData = {
next_batch: "batch1",
rooms: {
join: {},
},
};
syncData.rooms.join[ROOM_ID] = {
timeline: {
events: [
event,
],
limited: false,
},
};
httpBackend.when("GET", "/sync").respond(200, syncData);
await Promise.race([
Promise.all([
httpBackend.flushAllExpected(),
]),
new Promise((_, reject) => {
client.once("sync.unexpectedError", reject);
}),
]);
}, 10000);
});
+3 -2
View File
@@ -3,6 +3,7 @@ import HttpBackend from "matrix-mock-request";
import {MatrixClient} from "../../src/matrix";
import {MatrixScheduler} from "../../src/scheduler";
import {MemoryStore} from "../../src/store/memory";
import {MatrixError} from "../../src/http-api";
describe("MatrixClient opts", function() {
const baseUrl = "http://localhost.or.something";
@@ -132,10 +133,10 @@ describe("MatrixClient opts", function() {
});
it("shouldn't retry sending events", function(done) {
httpBackend.when("PUT", "/txn1").fail(500, {
httpBackend.when("PUT", "/txn1").fail(500, new MatrixError({
errcode: "M_SOMETHING",
error: "Ruh roh",
});
}));
client.sendTextMessage("!foo:bar", "a body", "txn1").then(function(res) {
expect(false).toBe(true, "sendTextMessage resolved but shouldn't");
}, function(err) {
+9
View File
@@ -16,6 +16,7 @@ limitations under the License.
*/
import {logger} from '../src/logger';
import * as utils from "../src/utils";
// try to load the olm library.
try {
@@ -24,3 +25,11 @@ try {
} catch (e) {
logger.warn("unable to run crypto tests: libolm not available");
}
// also try to set node crypto
try {
const crypto = require('crypto');
utils.setCrypto(crypto);
} catch (err) {
console.log('nodejs was compiled without crypto support: some tests will fail');
}
+4
View File
@@ -313,6 +313,10 @@ describe("Crypto", function() {
// make a room key request, and record the transaction ID for the
// sendToDevice call
await aliceClient.cancelAndResendEventRoomKeyRequest(event);
// key requests get queued until the sync has finished, but we don't
// let the client set up enough for that to happen, so gut-wrench a bit
// to force it to send now.
aliceClient._crypto._outgoingRoomKeyRequestManager.sendQueuedRequests();
jest.runAllTimers();
await Promise.resolve();
expect(aliceClient.sendToDevice).toBeCalledTimes(1);
+4 -2
View File
@@ -25,6 +25,7 @@ import {
import {MemoryCryptoStore} from '../../../src/crypto/store/memory-crypto-store';
import 'fake-indexeddb/auto';
import 'jest-localstorage-mock';
import {OlmDevice} from "../../../src/crypto/OlmDevice";
const userId = "@alice:example.com";
@@ -233,8 +234,9 @@ describe.each([
it("should cache data to the store and retrieve it", async () => {
await store.startup();
const olmDevice = new OlmDevice(store);
const { getCrossSigningKeyCache, storeCrossSigningKeyCache } =
createCryptoStoreCacheCallbacks(store);
createCryptoStoreCacheCallbacks(store, olmDevice);
await storeCrossSigningKeyCache("self_signing", testKey);
// If we've not saved anything, don't expect anything
@@ -243,6 +245,6 @@ describe.each([
expect(nokey).toBeNull();
const key = await getCrossSigningKeyCache("self_signing", "");
expect(key).toEqual(testKey);
expect(new Uint8Array(key)).toEqual(testKey);
});
});
+1 -1
View File
@@ -546,7 +546,7 @@ describe("MegolmBackup", function() {
const key = Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 8]);
await client._crypto.storeSessionBackupPrivateKey(key);
const result = await client._crypto.getSessionBackupPrivateKey();
expect(result).toEqual(key);
expect(new Uint8Array(result)).toEqual(key);
});
it('caches session backup keys as it encounters them', async function() {
-450
View File
@@ -20,7 +20,6 @@ import {SECRET_STORAGE_ALGORITHM_V1_AES} from "../../../src/crypto/SecretStorage
import {MatrixEvent} from "../../../src/models/event";
import {TestClient} from '../../TestClient';
import {makeTestClients} from './verification/util';
import {encryptAES} from "../../../src/crypto/aes";
import * as utils from "../../../src/utils";
@@ -288,17 +287,11 @@ describe("Secrets", function() {
olmlib.decodeBase64("1R6JVlXX99UcfUZzKuCDGQgJTw8ur1/ofgPD8pp+96M="),
);
const SSPubKey = "0DfNsRDzEvkCLA0gD3m7VAGJ5VClhjEsewI35xq873Q";
const backupKey = new Uint8Array(
olmlib.decodeBase64(
"OTmOKrPsWP1qxmt0s2qat4vYxULG+U9cmFAWFco3A5U=",
),
);
const SSSSKey = new Uint8Array(
olmlib.decodeBase64(
"XrmITOOdBhw6yY5Bh7trb/bgp1FRdIGyCUxxMP873R0=",
),
);
const SSSSPubKey = "v3A8HTypbccUm6jaRCRw5l+7lSdzQUACeY9xpQ5BVmE";
it("bootstraps when no storage or cross-signing keys locally", async function() {
const key = new Uint8Array(16);
@@ -403,319 +396,6 @@ describe("Secrets", function() {
expect(await secretStorage.hasKey()).toBeTruthy();
});
it("converts asymmetric SSSS to symmetric SSSS", async function() {
let crossSigningKeys = {};
const secretStorageKeys = {
"old_key_id": SSSSKey,
"new_key_id": SSSSKey,
};
const alice = await makeTestClient(
{userId: "@alice:example.com", deviceId: "Osborne2"},
{
cryptoCallbacks: {
getCrossSigningKey: t => crossSigningKeys[t],
saveCrossSigningKeys: k => crossSigningKeys = k,
getSecretStorageKey: ({keys}, name) => {
for (const keyId of Object.keys(keys)) {
if (secretStorageKeys[keyId]) {
return [keyId, secretStorageKeys[keyId]];
}
}
},
},
},
);
const encryption = new global.Olm.PkEncryption();
encryption.set_recipient_key(SSSSPubKey);
alice.store.storeAccountDataEvents([
new MatrixEvent({
type: "m.secret_storage.default_key",
content: {
key: "old_key_id",
},
}),
new MatrixEvent({
type: "m.secret_storage.key.old_key_id",
content: sign({
algorithm: "m.secret_storage.v1.curve25519-aes-sha2",
passphrase: {
algorithm: "m.pbkdf2",
iterations: 500000,
salt: "GbkvwKHVMveo1zGVSb2GMMdCinG2npJK",
},
pubkey: SSSSPubKey,
}, XSK, "@alice:example.com"),
}),
new MatrixEvent({
type: "m.cross_signing.master",
content: {
encrypted: {
old_key_id: encryption.encrypt(olmlib.encodeBase64(XSK)),
},
},
}),
new MatrixEvent({
type: "m.cross_signing.self_signing",
content: {
encrypted: {
old_key_id: encryption.encrypt(olmlib.encodeBase64(SSK)),
},
},
}),
new MatrixEvent({
type: "m.cross_signing.user_signing",
content: {
encrypted: {
old_key_id: encryption.encrypt(olmlib.encodeBase64(USK)),
},
},
}),
new MatrixEvent({
type: "m.megolm_backup.v1",
content: {
encrypted: {
old_key_id: encryption.encrypt(
olmlib.encodeBase64(backupKey),
),
},
},
}),
]);
encryption.free();
alice._crypto._deviceList.storeCrossSigningForUser("@alice:example.com", {
keys: {
master: {
user_id: "@alice:example.com",
usage: ["master"],
keys: {
[`ed25519:${XSPubKey}`]: XSPubKey,
},
},
self_signing: sign({
user_id: "@alice:example.com",
usage: ["self_signing"],
keys: {
[`ed25519:${SSPubKey}`]: SSPubKey,
},
}, XSK, "@alice:example.com"),
user_signing: sign({
user_id: "@alice:example.com",
usage: ["user_signing"],
keys: {
[`ed25519:${USPubKey}`]: USPubKey,
},
}, XSK, "@alice:example.com"),
},
});
alice.getKeyBackupVersion = async () => {
return {
version: "1",
algorithm: "m.megolm_backup.v1.curve25519-aes-sha2",
auth_data: sign({
public_key: "pxEXhg+4vdMf/kFwP4bVawFWdb0EmytL3eFJx++zQ0A",
}, XSK, "@alice:example.com"),
};
};
const origAddSecretStorageKey = alice._crypto.addSecretStorageKey;
alice._crypto.addSecretStorageKey = async function(algorithm, opts, keyId) {
return await origAddSecretStorageKey.call(
alice._crypto, algorithm, opts, keyId || "new_key_id",
);
};
alice.setAccountData = async function(name, data) {
const event = new MatrixEvent({
type: name,
content: data,
});
alice.store.storeAccountDataEvents([event]);
this.emit("accountData", event);
};
await alice.bootstrapSecretStorage();
// it should create a new key with the same parameters
expect(alice.getAccountData("m.secret_storage.default_key").getContent())
.toEqual({key: "new_key_id"});
const newKeyInfo = alice.getAccountData("m.secret_storage.key.new_key_id")
.getContent();
expect(newKeyInfo.algorithm)
.toEqual("m.secret_storage.v1.aes-hmac-sha2");
expect(newKeyInfo.passphrase).toEqual({
algorithm: "m.pbkdf2",
iterations: 500000,
salt: "GbkvwKHVMveo1zGVSb2GMMdCinG2npJK",
});
expect(newKeyInfo).toHaveProperty("iv");
expect(newKeyInfo).toHaveProperty("mac");
expect(alice.checkSecretStorageKey(secretStorageKeys.new_key_id, newKeyInfo))
.toBeTruthy();
const crossSigningMaster = alice.getAccountData("m.cross_signing.master")
.getContent();
expect(crossSigningMaster.encrypted).toHaveProperty("new_key_id");
expect(crossSigningMaster.encrypted).not.toHaveProperty("old_key_id");
expect(await alice.getSecret("m.cross_signing.master"))
.toEqual(olmlib.encodeBase64(XSK));
const USKInfo = alice.getAccountData("m.cross_signing.self_signing")
.getContent();
expect(USKInfo.encrypted).toHaveProperty("new_key_id");
expect(USKInfo.encrypted).not.toHaveProperty("old_key_id");
expect(await alice.getSecret("m.cross_signing.user_signing"))
.toEqual(olmlib.encodeBase64(USK));
const SSKInfo = alice.getAccountData("m.cross_signing.self_signing")
.getContent();
expect(SSKInfo.encrypted).toHaveProperty("new_key_id");
expect(SSKInfo.encrypted).not.toHaveProperty("old_key_id");
expect(await alice.getSecret("m.cross_signing.self_signing"))
.toEqual(olmlib.encodeBase64(SSK));
const backupKeyInfo = alice.getAccountData("m.megolm_backup.v1")
.getContent();
expect(backupKeyInfo.encrypted).toHaveProperty("new_key_id");
expect(backupKeyInfo.encrypted).not.toHaveProperty("old_key_id");
expect(await alice.getSecret("m.megolm_backup.v1"))
.toEqual(olmlib.encodeBase64(backupKey));
});
it("converts asymmetric SSSS with passthrough", async function() {
let crossSigningKeys = {};
const secretStorageKeys = {
"old_key_id": SSSSKey,
"new_key_id": SSSSKey,
};
const alice = await makeTestClient(
{userId: "@alice:example.com", deviceId: "Osborne2"},
{
cryptoCallbacks: {
getCrossSigningKey: t => crossSigningKeys[t],
saveCrossSigningKeys: k => crossSigningKeys = k,
getSecretStorageKey: ({keys}, name) => {
for (const keyId of Object.keys(keys)) {
if (secretStorageKeys[keyId]) {
return [keyId, secretStorageKeys[keyId]];
}
}
},
},
},
);
const encryption = new global.Olm.PkEncryption();
encryption.set_recipient_key(SSSSPubKey);
alice.store.storeAccountDataEvents([
new MatrixEvent({
type: "m.secret_storage.default_key",
content: {
key: "old_key_id",
},
}),
new MatrixEvent({
type: "m.secret_storage.key.old_key_id",
content: sign({
algorithm: "m.secret_storage.v1.curve25519-aes-sha2",
passphrase: {
algorithm: "m.pbkdf2",
iterations: 500000,
salt: "GbkvwKHVMveo1zGVSb2GMMdCinG2npJK",
},
pubkey: "v3A8HTypbccUm6jaRCRw5l+7lSdzQUACeY9xpQ5BVmE",
}, XSK, "@alice:example.com"),
}),
new MatrixEvent({
type: "m.cross_signing.master",
content: {
encrypted: {
old_key_id: encryption.encrypt(olmlib.encodeBase64(XSK)),
},
},
}),
new MatrixEvent({
type: "m.cross_signing.self_signing",
content: {
encrypted: {
old_key_id: encryption.encrypt(olmlib.encodeBase64(SSK)),
},
},
}),
new MatrixEvent({
type: "m.cross_signing.user_signing",
content: {
encrypted: {
old_key_id: encryption.encrypt(olmlib.encodeBase64(USK)),
},
},
}),
new MatrixEvent({
type: "m.megolm_backup.v1",
content: {
encrypted: {
old_key_id: {
passthrough: true,
},
},
},
}),
]);
encryption.free();
alice._crypto._deviceList.storeCrossSigningForUser("@alice:example.com", {
keys: {
master: {
user_id: "@alice:example.com",
usage: ["master"],
keys: {
[`ed25519:${XSPubKey}`]: XSPubKey,
},
},
self_signing: sign({
user_id: "@alice:example.com",
usage: ["self_signing"],
keys: {
[`ed25519:${SSPubKey}`]: SSPubKey,
},
}, XSK, "@alice:example.com"),
user_signing: sign({
user_id: "@alice:example.com",
usage: ["user_signing"],
keys: {
[`ed25519:${USPubKey}`]: USPubKey,
},
}, XSK, "@alice:example.com"),
},
});
alice.getKeyBackupVersion = async () => {
return {
version: "1",
algorithm: "m.megolm_backup.v1.curve25519-aes-sha2",
auth_data: sign({
public_key: "v3A8HTypbccUm6jaRCRw5l+7lSdzQUACeY9xpQ5BVmE",
}, XSK, "@alice:example.com"),
};
};
const origAddSecretStorageKey = alice._crypto.addSecretStorageKey;
alice._crypto.addSecretStorageKey = async function(algorithm, opts, keyId) {
return await origAddSecretStorageKey.call(
alice._crypto, algorithm, opts, keyId || "new_key_id",
);
};
alice.setAccountData = async function(name, data) {
const event = new MatrixEvent({
type: name,
content: data,
});
alice.store.storeAccountDataEvents([event]);
this.emit("accountData", event);
};
await alice.bootstrapSecretStorage();
// the new backup key should be the encoded SSSS key
const backupKeyInfo = alice.getAccountData("m.megolm_backup.v1")
.getContent();
expect(backupKeyInfo.encrypted).toHaveProperty("new_key_id");
expect(backupKeyInfo.encrypted).not.toHaveProperty("old_key_id");
expect(await alice.getSecret("m.megolm_backup.v1"))
.toEqual(olmlib.encodeBase64(SSSSKey));
});
it("adds passphrase checking if it's lacking", async function() {
let crossSigningKeys = {
master: XSK,
@@ -847,135 +527,5 @@ describe("Secrets", function() {
expect(alice.checkSecretStorageKey(secretStorageKeys.key_id, keyInfo))
.toBeTruthy();
});
it("fixes backup keys in the wrong format", async function() {
let crossSigningKeys = {
master: XSK,
user_signing: USK,
self_signing: SSK,
};
const secretStorageKeys = {
key_id: SSSSKey,
};
const alice = await makeTestClient(
{userId: "@alice:example.com", deviceId: "Osborne2"},
{
cryptoCallbacks: {
getCrossSigningKey: t => crossSigningKeys[t],
saveCrossSigningKeys: k => crossSigningKeys = k,
getSecretStorageKey: ({keys}, name) => {
for (const keyId of Object.keys(keys)) {
if (secretStorageKeys[keyId]) {
return [keyId, secretStorageKeys[keyId]];
}
}
},
},
},
);
alice.store.storeAccountDataEvents([
new MatrixEvent({
type: "m.secret_storage.default_key",
content: {
key: "key_id",
},
}),
new MatrixEvent({
type: "m.secret_storage.key.key_id",
content: {
algorithm: "m.secret_storage.v1.aes-hmac-sha2",
passphrase: {
algorithm: "m.pbkdf2",
iterations: 500000,
salt: "GbkvwKHVMveo1zGVSb2GMMdCinG2npJK",
},
},
}),
new MatrixEvent({
type: "m.cross_signing.master",
content: {
encrypted: {
key_id: {ciphertext: "bla", mac: "bla", iv: "bla"},
},
},
}),
new MatrixEvent({
type: "m.cross_signing.self_signing",
content: {
encrypted: {
key_id: {ciphertext: "bla", mac: "bla", iv: "bla"},
},
},
}),
new MatrixEvent({
type: "m.cross_signing.user_signing",
content: {
encrypted: {
key_id: {ciphertext: "bla", mac: "bla", iv: "bla"},
},
},
}),
new MatrixEvent({
type: "m.megolm_backup.v1",
content: {
encrypted: {
key_id: await encryptAES(
"123,45,6,7,89,1,234,56,78,90,12,34,5,67,8,90",
secretStorageKeys.key_id, "m.megolm_backup.v1",
),
},
},
}),
]);
alice._crypto._deviceList.storeCrossSigningForUser("@alice:example.com", {
keys: {
master: {
user_id: "@alice:example.com",
usage: ["master"],
keys: {
[`ed25519:${XSPubKey}`]: XSPubKey,
},
},
self_signing: sign({
user_id: "@alice:example.com",
usage: ["self_signing"],
keys: {
[`ed25519:${SSPubKey}`]: SSPubKey,
},
}, XSK, "@alice:example.com"),
user_signing: sign({
user_id: "@alice:example.com",
usage: ["user_signing"],
keys: {
[`ed25519:${USPubKey}`]: USPubKey,
},
}, XSK, "@alice:example.com"),
},
});
alice.getKeyBackupVersion = async () => {
return {
version: "1",
algorithm: "m.megolm_backup.v1.curve25519-aes-sha2",
auth_data: sign({
public_key: "pxEXhg+4vdMf/kFwP4bVawFWdb0EmytL3eFJx++zQ0A",
}, XSK, "@alice:example.com"),
};
};
alice.setAccountData = async function(name, data) {
const event = new MatrixEvent({
type: name,
content: data,
});
alice.store.storeAccountDataEvents([event]);
this.emit("accountData", event);
};
await alice.bootstrapSecretStorage();
const backupKey = alice.getAccountData("m.megolm_backup.v1")
.getContent();
expect(backupKey.encrypted).toHaveProperty("key_id");
expect(await alice.getSecret("m.megolm_backup.v1"))
.toEqual("ey0GB1kB6jhOWgwiBUMIWg==");
});
});
});
@@ -181,11 +181,14 @@ describe("SAS verification", function() {
it("should verify a key", async () => {
let macMethod;
let keyAgreement;
const origSendToDevice = bob.client.sendToDevice.bind(bob.client);
bob.client.sendToDevice = function(type, map) {
if (type === "m.key.verification.accept") {
macMethod = map[alice.client.getUserId()][alice.client.deviceId]
.message_authentication_code;
keyAgreement = map[alice.client.getUserId()][alice.client.deviceId]
.key_agreement_protocol;
}
return origSendToDevice(type, map);
};
@@ -212,6 +215,7 @@ describe("SAS verification", function() {
// make sure that it uses the preferred method
expect(macMethod).toBe("hkdf-hmac-sha256");
expect(keyAgreement).toBe("curve25519-hkdf-sha256");
// make sure Alice and Bob verified each other
const bobDevice
+34
View File
@@ -0,0 +1,34 @@
import {FilterComponent} from "../../src/filter-component";
import {mkEvent} from '../test-utils';
describe("Filter Component", function() {
describe("types", function() {
it("should filter out events with other types", function() {
const filter = new FilterComponent({ types: ['m.room.message'] });
const event = mkEvent({
type: 'm.room.member',
content: { },
room: 'roomId',
event: true,
});
const checkResult = filter.check(event);
expect(checkResult).toBe(false);
});
it("should validate events with the same type", function() {
const filter = new FilterComponent({ types: ['m.room.message'] });
const event = mkEvent({
type: 'm.room.message',
content: { },
room: 'roomId',
event: true,
});
const checkResult = filter.check(event);
expect(checkResult).toBe(true);
});
});
});
+30 -1
View File
@@ -97,7 +97,7 @@ describe("InteractiveAuth", function() {
// first we expect a call to doRequest
doRequest.mockImplementation(function(authData) {
logger.log("request1", authData);
expect(authData).toEqual({});
expect(authData).toEqual(null); // first request should be null
const err = new MatrixError({
session: "sessionId",
flows: [
@@ -143,4 +143,33 @@ describe("InteractiveAuth", function() {
expect(stateUpdated).toBeCalledTimes(1);
});
});
it("should start an auth stage and reject if no auth flow", function() {
const doRequest = jest.fn();
const stateUpdated = jest.fn();
const ia = new InteractiveAuth({
matrixClient: new FakeClient(),
doRequest: doRequest,
stateUpdated: stateUpdated,
});
doRequest.mockImplementation(function(authData) {
logger.log("request1", authData);
expect(authData).toEqual(null); // first request should be null
const err = new MatrixError({
session: "sessionId",
flows: [],
params: {
"logintype": { param: "aa" },
},
});
err.httpStatus = 401;
throw err;
});
return ia.attemptAuth().catch(function(error) {
expect(error.message).toBe('No appropriate authentication flow found');
});
});
});
+25
View File
@@ -0,0 +1,25 @@
/*
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.
*/
export {};
declare global {
namespace NodeJS {
interface Global {
localStorage: Storage;
}
}
}
-3
View File
@@ -206,9 +206,6 @@ MatrixBaseApis.prototype.register = function(
inhibitLogin = undefined;
}
if (auth === undefined || auth === null) {
auth = {};
}
if (sessionId) {
auth.session = sessionId;
}
+108 -71
View File
@@ -35,14 +35,19 @@ import {StubStore} from "./store/stub";
import {createNewMatrixCall} from "./webrtc/call";
import * as utils from './utils';
import {sleep} from './utils';
import {MatrixError, PREFIX_MEDIA_R0, PREFIX_UNSTABLE} from "./http-api";
import {
MatrixError,
PREFIX_MEDIA_R0,
PREFIX_UNSTABLE,
retryNetworkOperation,
} from "./http-api";
import {getHttpUriForMxc} from "./content-repo";
import * as ContentHelpers from "./content-helpers";
import * as olmlib from "./crypto/olmlib";
import {ReEmitter} from './ReEmitter';
import {RoomList} from './crypto/RoomList';
import {logger} from './logger';
import {Crypto, isCryptoAvailable, fixBackupKey} from './crypto';
import {Crypto, isCryptoAvailable} from './crypto';
import {decodeRecoveryKey} from './crypto/recoverykey';
import {keyFromAuthData} from './crypto/key_passphrase';
import {randomString} from './randomstring';
@@ -105,6 +110,9 @@ function keyFromRecoverySession(session, decryptionKey) {
* If provided, opts.deviceId and opts.userId should **NOT** be provided
* (they are present in the exported data).
*
* @param {string} opts.pickleKey Key used to pickle olm objects or other
* sensitive data.
*
* @param {IdentityServerProvider} [opts.identityServer]
* Optional. A provider object with one function `getAccessToken`, which is a
* callback that returns a Promise<String> of an identity access token to supply
@@ -279,6 +287,8 @@ export function MatrixClient(opts) {
// will be used during async initialization of the crypto
this._exportedOlmDeviceToImport = opts.deviceToImport.olmDevice;
}
} else if (opts.pickleKey) {
this.pickleKey = opts.pickleKey;
}
this.scheduler = opts.scheduler;
@@ -732,6 +742,7 @@ MatrixClient.prototype.initCrypto = async function() {
"crypto.roomKeyRequestCancellation",
"crypto.warning",
"crypto.devicesUpdated",
"crypto.willUpdateDevices",
"deviceVerificationChanged",
"userTrustStatusChanged",
"crossSigning.keysChanged",
@@ -740,6 +751,7 @@ MatrixClient.prototype.initCrypto = async function() {
logger.log("Crypto: initialising crypto object...");
await crypto.init({
exportedOlmDevice: this._exportedOlmDeviceToImport,
pickleKey: this.pickleKey,
});
delete this._exportedOlmDeviceToImport;
@@ -820,9 +832,9 @@ MatrixClient.prototype.downloadKeys = function(userIds, forceDownload) {
*
* @param {string} userId the user to list keys for.
*
* @return {Promise<module:crypto/deviceinfo[]>} list of devices
* @return {module:crypto/deviceinfo[]} list of devices
*/
MatrixClient.prototype.getStoredDevicesForUser = async function(userId) {
MatrixClient.prototype.getStoredDevicesForUser = function(userId) {
if (this._crypto === null) {
throw new Error("End-to-end encryption disabled");
}
@@ -835,9 +847,9 @@ MatrixClient.prototype.getStoredDevicesForUser = async function(userId) {
* @param {string} userId the user to list keys for.
* @param {string} deviceId unique identifier for the device
*
* @return {Promise<?module:crypto/deviceinfo>} device or null
* @return {module:crypto/deviceinfo} device or null
*/
MatrixClient.prototype.getStoredDevice = async function(userId, deviceId) {
MatrixClient.prototype.getStoredDevice = function(userId, deviceId) {
if (this._crypto === null) {
throw new Error("End-to-end encryption disabled");
}
@@ -1304,7 +1316,6 @@ wrapCryptoFuncs(MatrixClient, [
"bootstrapSecretStorage",
"addSecretStorageKey",
"hasSecretStorageKey",
"secretStorageKeyNeedsUpgrade",
"storeSecret",
"getSecret",
"isSecretStored",
@@ -1358,7 +1369,8 @@ MatrixClient.prototype.cancelAndResendEventRoomKeyRequest = function(event) {
};
/**
* Enable end-to-end encryption for a room.
* Enable end-to-end encryption for a room. This does not modify room state.
* Any messages sent before the returned promise resolves will be sent unencrypted.
* @param {string} roomId The room ID to enable encryption in.
* @param {object} config The encryption config for the room.
* @return {Promise} A promise that will resolve when encryption is set up.
@@ -1430,15 +1442,17 @@ MatrixClient.prototype.exportRoomKeys = function() {
* Import a list of room keys previously exported by exportRoomKeys
*
* @param {Object[]} keys a list of session export objects
* @param {Object} opts
* @param {Function} opts.progressCallback called with an object that has a "stage" param
*
* @return {Promise} a promise which resolves when the keys
* have been imported
*/
MatrixClient.prototype.importRoomKeys = function(keys) {
MatrixClient.prototype.importRoomKeys = function(keys, opts) {
if (!this._crypto) {
throw new Error("End-to-end encryption disabled");
}
return this._crypto.importRoomKeys(keys);
return this._crypto.importRoomKeys(keys, opts);
};
/**
@@ -1498,12 +1512,16 @@ MatrixClient.prototype.isKeyBackupTrusted = function(info) {
/**
* @returns {bool} true if the client is configured to back up keys to
* the server, otherwise false.
* the server, otherwise false. If we haven't completed a successful check
* of key backup status yet, returns null.
*/
MatrixClient.prototype.getKeyBackupEnabled = function() {
if (this._crypto === null) {
throw new Error("End-to-end encryption disabled");
}
if (!this._crypto._checkedForBackup) {
return null;
}
return Boolean(this._crypto.backupKey);
};
@@ -1816,17 +1834,7 @@ MatrixClient.prototype.restoreKeyBackupWithPassword = async function(
MatrixClient.prototype.restoreKeyBackupWithSecretStorage = async function(
backupInfo, targetRoomId, targetSessionId, opts,
) {
const storedKey = await this.getSecret("m.megolm_backup.v1");
// ensure that the key is in the right format. If not, fix the key and
// store the fixed version
const fixedKey = fixBackupKey(storedKey);
if (fixedKey) {
const [keyId] = await this.getSecretStorageKey();
await this.storeSecret("m.megolm_backup.v1", fixedKey, [keyId]);
}
const privKey = decodeBase64(fixedKey || storedKey);
const privKey = decodeBase64(await this.getSecret("m.megolm_backup.v1"));
return this._restoreKeyBackup(
privKey, targetRoomId, targetSessionId, backupInfo, opts,
);
@@ -1881,7 +1889,10 @@ MatrixClient.prototype.restoreKeyBackupWithCache = async function(
MatrixClient.prototype._restoreKeyBackup = function(
privKey, targetRoomId, targetSessionId, backupInfo,
{ cacheCompleteCallback }={}, // For sequencing during tests
{
cacheCompleteCallback, // For sequencing during tests
progressCallback,
}={},
) {
if (this._crypto === null) {
throw new Error("End-to-end encryption disabled");
@@ -1916,6 +1927,12 @@ MatrixClient.prototype._restoreKeyBackup = function(
console.warn("Error caching session backup key:", e);
}).then(cacheCompleteCallback);
if (progressCallback) {
progressCallback({
stage: "fetch",
});
}
return this._http.authedRequest(
undefined, "GET", path.path, path.queryData, undefined,
{prefix: PREFIX_UNSTABLE},
@@ -1950,7 +1967,7 @@ MatrixClient.prototype._restoreKeyBackup = function(
}
}
return this.importRoomKeys(keys);
return this.importRoomKeys(keys, { progressCallback });
}).then(() => {
return this._crypto.setTrustedBackupPubKey(backupPubKey);
}).then(() => {
@@ -2086,6 +2103,7 @@ MatrixClient.prototype.getUsers = function() {
/**
* Set account data event for the current user.
* It will retry the request up to 5 times.
* @param {string} eventType The event type
* @param {Object} contents the contents object for the event
* @param {module:client.callback} callback Optional.
@@ -2097,9 +2115,13 @@ MatrixClient.prototype.setAccountData = function(eventType, contents, callback)
$userId: this.credentials.userId,
$type: eventType,
});
return this._http.authedRequest(
callback, "PUT", path, undefined, contents,
);
const promise = retryNetworkOperation(5, () => {
return this._http.authedRequest(undefined, "PUT", path, undefined, contents);
});
if (callback) {
promise.then(result => callback(null, result), callback);
}
return promise;
};
/**
@@ -2134,9 +2156,17 @@ MatrixClient.prototype.getAccountDataFromServer = async function(eventType) {
$userId: this.credentials.userId,
$type: eventType,
});
return this._http.authedRequest(
undefined, "GET", path, undefined,
);
try {
const result = await this._http.authedRequest(
undefined, "GET", path, undefined,
);
return result;
} catch (e) {
if (e.data && e.data.errcode === 'M_NOT_FOUND') {
return null;
}
throw e;
}
};
/**
@@ -2450,6 +2480,7 @@ MatrixClient.prototype._sendCompleteEvent = function(roomId, eventObject, txnId,
const localEvent = new MatrixEvent(Object.assign(eventObject, {
event_id: "~" + roomId + ":" + txnId,
user_id: this.credentials.userId,
sender: this.credentials.userId,
room_id: roomId,
origin_server_ts: new Date().getTime(),
}));
@@ -4510,64 +4541,54 @@ MatrixClient.prototype.getFilter = function(userId, filterId, allowCached) {
* @param {Filter} filter
* @return {Promise<String>} Filter ID
*/
MatrixClient.prototype.getOrCreateFilter = function(filterName, filter) {
MatrixClient.prototype.getOrCreateFilter = async function(filterName, filter) {
const filterId = this.store.getFilterIdByName(filterName);
let promise = Promise.resolve();
const self = this;
let existingId = undefined;
if (filterId) {
// check that the existing filter matches our expectations
promise = self.getFilter(self.credentials.userId,
filterId, true,
).then(function(existingFilter) {
const oldDef = existingFilter.getDefinition();
const newDef = filter.getDefinition();
try {
const existingFilter =
await this.getFilter(this.credentials.userId, filterId, true);
if (existingFilter) {
const oldDef = existingFilter.getDefinition();
const newDef = filter.getDefinition();
if (utils.deepCompare(oldDef, newDef)) {
// super, just use that.
// debuglog("Using existing filter ID %s: %s", filterId,
// JSON.stringify(oldDef));
return Promise.resolve(filterId);
if (utils.deepCompare(oldDef, newDef)) {
// super, just use that.
// debuglog("Using existing filter ID %s: %s", filterId,
// JSON.stringify(oldDef));
existingId = filterId;
}
}
// debuglog("Existing filter ID %s: %s; new filter: %s",
// filterId, JSON.stringify(oldDef), JSON.stringify(newDef));
self.store.setFilterIdByName(filterName, undefined);
return undefined;
}, function(error) {
} catch (error) {
// Synapse currently returns the following when the filter cannot be found:
// {
// errcode: "M_UNKNOWN",
// name: "M_UNKNOWN",
// message: "No row found",
// data: Object, httpStatus: 404
// }
if (error.httpStatus === 404 &&
(error.errcode === "M_UNKNOWN" || error.errcode === "M_NOT_FOUND")) {
// Clear existing filterId from localStorage
// if it no longer exists on the server
self.store.setFilterIdByName(filterName, undefined);
// Return a undefined value for existingId further down the promise chain
return undefined;
} else {
if (error.errcode !== "M_UNKNOWN" && error.errcode !== "M_NOT_FOUND") {
throw error;
}
});
}
// if the filter doesn't exist anymore on the server, remove from store
if (!existingId) {
this.store.setFilterIdByName(filterName, undefined);
}
}
return promise.then(function(existingId) {
if (existingId) {
return existingId;
}
if (existingId) {
return existingId;
}
// create a new filter
return self.createFilter(filter.getDefinition(),
).then(function(createdFilter) {
// debuglog("Created new filter ID %s: %s", createdFilter.filterId,
// JSON.stringify(createdFilter.getDefinition()));
self.store.setFilterIdByName(filterName, createdFilter.filterId);
return createdFilter.filterId;
});
});
// create a new filter
const createdFilter = await this.createFilter(filter.getDefinition());
// debuglog("Created new filter ID %s: %s", createdFilter.filterId,
// JSON.stringify(createdFilter.getDefinition()));
this.store.setFilterIdByName(filterName, createdFilter.filterId);
return createdFilter.filterId;
};
@@ -5578,6 +5599,22 @@ MatrixClient.prototype.generateClientSecret = function() {
* });
*/
/**
* Fires whenever the stored devices for a user have changed
* @event module:client~MatrixClient#"crypto.devicesUpdated"
* @param {String[]} users A list of user IDs that were updated
* @param {bool} initialFetch If true, the store was empty (apart
* from our own device) and has been seeded.
*/
/**
* Fires whenever the stored devices for a user will be updated
* @event module:client~MatrixClient#"crypto.willUpdateDevices"
* @param {String[]} users A list of user IDs that will be updated
* @param {bool} initialFetch If true, the store is empty (apart
* from our own device) and is being seeded.
*/
/**
* Fires whenever the status of e2e key backup changes, as returned by getKeyBackupEnabled()
* @event module:client~MatrixClient#"crypto.keyBackupStatus"
+15 -4
View File
@@ -24,6 +24,7 @@ import {decodeBase64, encodeBase64, pkSign, pkVerify} from './olmlib';
import {EventEmitter} from 'events';
import {logger} from '../logger';
import {IndexedDBCryptoStore} from '../crypto/store/indexeddb-crypto-store';
import {decryptAES, encryptAES} from './aes';
function publicKeyFromKeyInfo(keyInfo) {
// `keys` is an object with { [`ed25519:${pubKey}`]: pubKey }
@@ -637,10 +638,10 @@ export class DeviceTrustLevel {
}
}
export function createCryptoStoreCacheCallbacks(store) {
export function createCryptoStoreCacheCallbacks(store, olmdevice) {
return {
getCrossSigningKeyCache: function(type, _expectedPublicKey) {
return new Promise((resolve) => {
getCrossSigningKeyCache: async function(type, _expectedPublicKey) {
const key = await new Promise((resolve) => {
return store.doTxn(
'readonly',
[IndexedDBCryptoStore.STORE_ACCOUNT],
@@ -649,13 +650,23 @@ export function createCryptoStoreCacheCallbacks(store) {
},
);
});
if (key && key.ciphertext) {
const pickleKey = Buffer.from(olmdevice._pickleKey);
const decrypted = await decryptAES(key, pickleKey, type);
return decodeBase64(decrypted);
} else {
return key;
}
},
storeCrossSigningKeyCache: function(type, key) {
storeCrossSigningKeyCache: async function(type, key) {
if (!(key instanceof Uint8Array)) {
throw new Error(
`storeCrossSigningKeyCache expects Uint8Array, got ${key}`,
);
}
const pickleKey = Buffer.from(olmdevice._pickleKey);
key = await encryptAES(encodeBase64(key), pickleKey, type);
return store.doTxn(
'readwrite',
[IndexedDBCryptoStore.STORE_ACCOUNT],
+7 -1
View File
@@ -109,6 +109,9 @@ export class DeviceList extends EventEmitter {
this._savePromiseTime = null;
// The timer used to delay the save
this._saveTimer = null;
// True if we have fetched data from the server or loaded a non-empty
// set of device data from the store
this._hasFetched = null;
}
/**
@@ -118,6 +121,7 @@ export class DeviceList extends EventEmitter {
await this._cryptoStore.doTxn(
'readonly', [IndexedDBCryptoStore.STORE_DEVICE_DATA], (txn) => {
this._cryptoStore.getEndToEndDeviceData(txn, (deviceData) => {
this._hasFetched = Boolean(deviceData && deviceData.devices);
this._devices = deviceData ? deviceData.devices : {},
this._crossSigningInfo = deviceData ?
deviceData.crossSigningInfo || {} : {};
@@ -652,6 +656,7 @@ export class DeviceList extends EventEmitter {
});
const finished = (success) => {
this.emit("crypto.willUpdateDevices", users, !this._hasFetched);
users.forEach((u) => {
this._dirty = true;
@@ -677,7 +682,8 @@ export class DeviceList extends EventEmitter {
}
});
this.saveIfDirty();
this.emit("crypto.devicesUpdated", users);
this.emit("crypto.devicesUpdated", users, !this._hasFetched);
this._hasFetched = true;
};
return prom;
+15 -1
View File
@@ -36,9 +36,15 @@ function checkPayloadLength(payloadString) {
// Note that even if we manage to do the encryption, the message send may fail,
// because by the time we've wrapped the ciphertext in the event object, it may
// exceed 65K. But at least we won't just fail with "abort()" in that case.
throw new Error("Message too long (" + payloadString.length + " bytes). " +
const err = new Error("Message too long (" + payloadString.length + " bytes). " +
"The maximum for an encrypted message is " +
MAX_PLAINTEXT_LENGTH + " bytes.");
// TODO: [TypeScript] We should have our own error types
err.data = {
errcode: "M_TOO_LARGE",
error: "Payload too large for encrypted message",
};
throw err;
}
}
@@ -105,6 +111,9 @@ export function OlmDevice(cryptoStore) {
// Keep track of sessions that we're starting, so that we don't start
// multiple sessions for the same device at the same time.
this._sessionsInProgress = {};
// Used by olm to serialise prekey message decryptions
this._olmPrekeyPromise = Promise.resolve();
}
/**
@@ -1029,6 +1038,11 @@ OlmDevice.prototype.addInboundGroupSession = async function(
}
}
logger.info(
"Storing megolm session " + senderKey + "/" + sessionId +
" with first index " + session.first_known_index(),
);
const sessionData = {
room_id: roomId,
session: session.pickle(this._pickleKey),
+12 -16
View File
@@ -97,10 +97,6 @@ export class OutgoingRoomKeyRequestManager {
*/
start() {
this._clientRunning = true;
// set the timer going, to handle any requests which didn't get sent
// on the previous run of the client.
this._startTimer();
}
/**
@@ -113,7 +109,14 @@ export class OutgoingRoomKeyRequestManager {
}
/**
* Send off a room key request, if we haven't already done so.
* Send any requests that have been queued
*/
sendQueuedRequests() {
this._startTimer();
}
/**
* Queue up a room key request, if we haven't already queued or sent one.
*
* The `requestBody` is compared (with a deep-equality check) against
* previous queued or sent requests and if it matches, no change is made.
@@ -129,7 +132,7 @@ export class OutgoingRoomKeyRequestManager {
* pending list (or we have established that a similar request already
* exists)
*/
async sendRoomKeyRequest(requestBody, recipients, resend=false) {
async queueRoomKeyRequest(requestBody, recipients, resend=false) {
const req = await this._cryptoStore.getOutgoingRoomKeyRequest(
requestBody,
);
@@ -184,7 +187,7 @@ export class OutgoingRoomKeyRequestManager {
// in state ROOM_KEY_REQUEST_STATES.SENT, so we must have
// raced with another tab to mark the request cancelled.
// Try again, to make sure the request is resent.
return await this.sendRoomKeyRequest(
return await this.queueRoomKeyRequest(
requestBody, recipients, resend,
);
}
@@ -220,9 +223,6 @@ export class OutgoingRoomKeyRequestManager {
throw new Error('unhandled state: ' + req.state);
}
}
// some of the branches require the timer to be started. Just start it
// all the time, because it doesn't hurt to start it.
this._startTimer();
}
/**
@@ -332,14 +332,14 @@ export class OutgoingRoomKeyRequestManager {
* This is intended for situations where something substantial has changed, and we
* don't really expect the other end to even care about the cancellation.
* For example, after initialization or self-verification.
* @return {Promise} An array of `sendRoomKeyRequest` outputs.
* @return {Promise} An array of `queueRoomKeyRequest` outputs.
*/
async cancelAndResendAllOutgoingRequests() {
const outgoings = await this._cryptoStore.getAllOutgoingRoomKeyRequestsByState(
ROOM_KEY_REQUEST_STATES.SENT,
);
return Promise.all(outgoings.map(({ requestBody, recipients }) =>
this.sendRoomKeyRequest(requestBody, recipients, true)));
this.queueRoomKeyRequest(requestBody, recipients, true)));
}
// start the background timer to send queued requests, if the timer isn't
@@ -381,15 +381,12 @@ export class OutgoingRoomKeyRequestManager {
return Promise.resolve();
}
logger.log("Looking for queued outgoing room key requests");
return this._cryptoStore.getOutgoingRoomKeyRequestByState([
ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING,
ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING_AND_WILL_RESEND,
ROOM_KEY_REQUEST_STATES.UNSENT,
]).then((req) => {
if (!req) {
logger.log("No more outgoing room key requests");
this._sendOutgoingRoomKeyRequestsTimer = null;
return;
}
@@ -413,7 +410,6 @@ export class OutgoingRoomKeyRequestManager {
}).catch((e) => {
logger.error("Error sending room key request; will retry later.", e);
this._sendOutgoingRoomKeyRequestsTimer = null;
this._startTimer();
});
});
}
+27 -173
View File
@@ -17,16 +17,12 @@ limitations under the License.
import {EventEmitter} from 'events';
import {logger} from '../logger';
import * as olmlib from './olmlib';
import {pkVerify} from './olmlib';
import {randomString} from '../randomstring';
import {encryptAES, decryptAES} from './aes';
import {encodeBase64} from "./olmlib";
export const SECRET_STORAGE_ALGORITHM_V1_AES
= "m.secret_storage.v1.aes-hmac-sha2";
// don't use curve25519 for writing data.
export const SECRET_STORAGE_ALGORITHM_V1_CURVE25519
= "m.secret_storage.v1.curve25519-aes-sha2";
const ZERO_STR = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
@@ -35,11 +31,10 @@ const ZERO_STR = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
* @module crypto/SecretStorage
*/
export class SecretStorage extends EventEmitter {
constructor(baseApis, cryptoCallbacks, crossSigningInfo) {
constructor(baseApis, cryptoCallbacks) {
super();
this._baseApis = baseApis;
this._cryptoCallbacks = cryptoCallbacks;
this._crossSigningInfo = crossSigningInfo;
this._requests = {};
this._incomingRequests = {};
}
@@ -53,7 +48,7 @@ export class SecretStorage extends EventEmitter {
}
setDefaultKeyId(keyId) {
return new Promise((resolve) => {
return new Promise(async (resolve, reject) => {
const listener = (ev) => {
if (
ev.getType() === 'm.secret_storage.default_key' &&
@@ -65,10 +60,15 @@ export class SecretStorage extends EventEmitter {
};
this._baseApis.on('accountData', listener);
this._baseApis.setAccountData(
'm.secret_storage.default_key',
{ key: keyId },
);
try {
await this._baseApis.setAccountData(
'm.secret_storage.default_key',
{ key: keyId },
);
} catch (e) {
this._baseApis.removeListener('accountData', listener);
reject(e);
}
});
}
@@ -92,9 +92,7 @@ export class SecretStorage extends EventEmitter {
keyData.name = opts.name;
}
switch (algorithm) {
case SECRET_STORAGE_ALGORITHM_V1_AES:
{
if (algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
if (opts.passphrase) {
keyData.passphrase = opts.passphrase;
}
@@ -103,9 +101,7 @@ export class SecretStorage extends EventEmitter {
keyData.iv = iv;
keyData.mac = mac;
}
break;
}
default:
} else {
throw new Error(`Unknown key algorithm ${opts.algorithm}`);
}
@@ -119,8 +115,6 @@ export class SecretStorage extends EventEmitter {
);
}
await this._crossSigningInfo.signObject(keyData, 'master');
await this._baseApis.setAccountData(
`m.secret_storage.key.${keyId}`, keyData,
);
@@ -128,33 +122,6 @@ export class SecretStorage extends EventEmitter {
return keyId;
}
/**
* Signs a given secret storage key with the cross-signing master key.
*
* @param {string} [keyId = default key's ID] The ID of the key to sign.
* Defaults to the default key ID if not provided.
*/
async signKey(keyId) {
if (!keyId) {
keyId = await this.getDefaultKeyId();
}
if (!keyId) {
throw new Error("signKey requires a key ID");
}
const keyInfo = await this._baseApis.getAccountDataFromServer(
`m.secret_storage.key.${keyId}`,
);
if (!keyInfo) {
throw new Error(`Key ${keyId} does not exist in account data`);
}
await this._crossSigningInfo.signObject(keyInfo, 'master');
await this._baseApis.setAccountData(
`m.secret_storage.key.${keyId}`, keyInfo,
);
}
/**
* Get the key information for a given ID.
*
@@ -188,15 +155,6 @@ export class SecretStorage extends EventEmitter {
return !!(await this.getKey(keyId));
}
async keyNeedsUpgrade(keyId) {
const keyInfo = await this.getKey(keyId);
if (keyInfo && keyInfo[1].algorithm === SECRET_STORAGE_ALGORITHM_V1_CURVE25519) {
return true;
} else {
return false;
}
}
/**
* Check whether a key matches what we expect based on the key info
*
@@ -206,32 +164,15 @@ export class SecretStorage extends EventEmitter {
* @return {boolean} whether or not the key matches
*/
async checkKey(key, info) {
switch (info.algorithm) {
case SECRET_STORAGE_ALGORITHM_V1_AES:
{
if (info.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
if (info.mac) {
const {mac} = await SecretStorage._calculateKeyCheck(key, info.iv);
return info.mac === mac;
return info.mac.replace(/=+$/g, '') === mac.replace(/=+$/g, '');
} else {
// if we have no information, we have to assume the key is right
return true;
}
}
case SECRET_STORAGE_ALGORITHM_V1_CURVE25519:
{
let decryption = null;
try {
decryption = new global.Olm.PkDecryption();
const gotPubkey = decryption.init_with_private_key(key);
// make sure it agrees with the given pubkey
return gotPubkey === info.pubkey;
} catch (e) {
return false;
} finally {
if (decryption) decryption.free();
}
}
default:
} else {
throw new Error("Unknown algorithm");
}
}
@@ -273,15 +214,11 @@ export class SecretStorage extends EventEmitter {
}
// encrypt secret, based on the algorithm
switch (keyInfo.algorithm) {
case SECRET_STORAGE_ALGORITHM_V1_AES:
{
if (keyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
const keys = {[keyId]: keyInfo};
const [, encryption] = await this._getSecretStorageKey(keys, name);
encrypted[keyId] = await encryption.encrypt(secret);
break;
}
default:
} else {
logger.warn("unknown algorithm for secret storage key " + keyId
+ ": " + keyInfo.algorithm);
// do nothing if we don't understand the encryption algorithm
@@ -347,24 +284,11 @@ export class SecretStorage extends EventEmitter {
"m.secret_storage.key." + keyId,
);
const encInfo = secretInfo.encrypted[keyId];
switch (keyInfo.algorithm) {
case SECRET_STORAGE_ALGORITHM_V1_AES:
// only use keys we understand the encryption algorithm of
if (keyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
if (encInfo.iv && encInfo.ciphertext && encInfo.mac) {
keys[keyId] = keyInfo;
}
break;
case SECRET_STORAGE_ALGORITHM_V1_CURVE25519:
if (
keyInfo.pubkey && (
(encInfo.ciphertext && encInfo.mac && encInfo.ephemeral) ||
encInfo.passthrough
)
) {
keys[keyId] = keyInfo;
}
break;
default:
// do nothing if we don't understand the encryption algorithm
}
}
@@ -413,8 +337,7 @@ export class SecretStorage extends EventEmitter {
const ret = {};
// check if secret is encrypted by a known/trusted secret and
// encryption looks sane
// filter secret encryption keys with supported algorithm
for (const keyId of Object.keys(secretInfo.encrypted)) {
// get key information from key storage
const keyInfo = await this._baseApis.getAccountDataFromServer(
@@ -423,49 +346,11 @@ export class SecretStorage extends EventEmitter {
if (!keyInfo) continue;
const encInfo = secretInfo.encrypted[keyId];
// We don't actually need the decryption object if it's a passthrough
// since we just want to return the key itself.
if (encInfo.passthrough) {
try {
pkVerify(
keyInfo,
this._crossSigningInfo.getId('master'),
this._crossSigningInfo.userId,
);
} catch (e) {
// not trusted, so move on to the next key
continue;
}
ret[keyId] = keyInfo;
continue;
}
switch (keyInfo.algorithm) {
case SECRET_STORAGE_ALGORITHM_V1_AES:
// only use keys we understand the encryption algorithm of
if (keyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
if (encInfo.iv && encInfo.ciphertext && encInfo.mac) {
ret[keyId] = keyInfo;
}
break;
case SECRET_STORAGE_ALGORITHM_V1_CURVE25519:
if (keyInfo.pubkey && encInfo.ciphertext && encInfo.mac
&& encInfo.ephemeral) {
if (checkKey) {
try {
pkVerify(
keyInfo,
this._crossSigningInfo.getId('master'),
this._crossSigningInfo.userId,
);
} catch (e) {
// not trusted, so move on to the next key
continue;
}
}
ret[keyId] = keyInfo;
}
break;
default:
// do nothing if we don't understand the encryption algorithm
}
}
return Object.keys(ret).length ? ret : null;
@@ -592,7 +477,7 @@ export class SecretStorage extends EventEmitter {
this._baseApis,
{
[sender]: [
await this._baseApis.getStoredDevice(sender, deviceId),
this._baseApis.getStoredDevice(sender, deviceId),
],
},
);
@@ -602,7 +487,7 @@ export class SecretStorage extends EventEmitter {
this._baseApis.deviceId,
this._baseApis._crypto._olmDevice,
sender,
this._baseApis._crypto.getStoredDevice(sender, deviceId),
this._baseApis.getStoredDevice(sender, deviceId),
payload,
);
const contentMap = {
@@ -669,9 +554,7 @@ export class SecretStorage extends EventEmitter {
throw new Error("App returned unknown key from getSecretStorageKey!");
}
switch (keys[keyId].algorithm) {
case SECRET_STORAGE_ALGORITHM_V1_AES:
{
if (keys[keyId].algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
const decryption = {
encrypt: async function(secret) {
return await encryptAES(secret, privateKey, name);
@@ -681,36 +564,7 @@ export class SecretStorage extends EventEmitter {
},
};
return [keyId, decryption];
}
case SECRET_STORAGE_ALGORITHM_V1_CURVE25519:
{
const pkDecryption = new global.Olm.PkDecryption();
let pubkey;
try {
pubkey = pkDecryption.init_with_private_key(privateKey);
} catch (e) {
pkDecryption.free();
throw new Error("getSecretStorageKey callback returned invalid key");
}
if (pubkey !== keys[keyId].pubkey) {
pkDecryption.free();
throw new Error(
"getSecretStorageKey callback returned incorrect key",
);
}
const decryption = {
free: pkDecryption.free.bind(pkDecryption),
decrypt: async function(encInfo) {
return pkDecryption.decrypt(
encInfo.ephemeral, encInfo.mac, encInfo.ciphertext,
);
},
// needed for passthrough
get_private_key: pkDecryption.get_private_key.bind(pkDecryption),
};
return [keyId, decryption];
}
default:
} else {
throw new Error("Unknown key type: " + keys[keyId].algorithm);
}
}
+2 -2
View File
@@ -84,9 +84,9 @@ async function decryptNode(data, key, name) {
const [aesKey, hmacKey] = deriveKeysNode(key, name);
const hmac = crypto.createHmac("sha256", hmacKey)
.update(data.ciphertext, "base64").digest("base64");
.update(data.ciphertext, "base64").digest("base64").replace(/=+$/g, '');
if (hmac !== data.mac) {
if (hmac !== data.mac.replace(/=+$/g, '')) {
throw new Error(`Error decrypting secret ${name}: bad MAC`);
}
+33 -10
View File
@@ -311,7 +311,7 @@ MegolmEncryption.prototype._ensureOutboundSession = async function(
}
await this._shareKeyWithDevices(
session, key, payload, retryDevices, failedDevices,
session, key, payload, retryDevices, failedDevices, 30000,
);
await this._notifyFailedOlmDevices(session, key, failedDevices);
@@ -521,6 +521,33 @@ MegolmEncryption.prototype._encryptAndSendKeysToDevices = function(
}
return Promise.all(promises).then(() => {
// prune out any devices that encryptMessageForDevice could not encrypt for,
// in which case it will have just not added anything to the ciphertext object.
// There's no point sending messages to devices if we couldn't encrypt to them,
// since that's effectively a blank message.
for (const userId of Object.keys(contentMap)) {
for (const deviceId of Object.keys(contentMap[userId])) {
if (Object.keys(contentMap[userId][deviceId].ciphertext).length === 0) {
logger.log(
"No ciphertext for device " +
userId + ":" + deviceId + ": pruning",
);
delete contentMap[userId][deviceId];
}
}
// No devices left for that user? Strip that too.
if (Object.keys(contentMap[userId]).length === 0) {
logger.log("Pruned all devices for user " + userId);
delete contentMap[userId];
}
}
// Is there anything left?
if (Object.keys(contentMap).length === 0) {
logger.log("No users left to send to: aborting");
return;
}
return this._baseApis.sendToDevice("m.room.encrypted", contentMap).then(() => {
// store that we successfully uploaded the keys of the current slice
for (const userId of Object.keys(contentMap)) {
@@ -1296,7 +1323,6 @@ MegolmDecryption.prototype.onRoomKeyEvent = function(event) {
keysClaimed = event.getKeysClaimed();
}
logger.log(`Received and adding key for megolm session ${senderKey}|${sessionId}`);
return this._olmDevice.addInboundGroupSession(
content.room_id, senderKey, forwardingKeyChain, sessionId,
content.session_key, keysClaimed,
@@ -1555,7 +1581,8 @@ MegolmDecryption.prototype.importRoomKey = function(session) {
};
/**
* Have another go at decrypting events after we receive a key
* Have another go at decrypting events after we receive a key. Resolves once
* decryption has been re-attempted on all events.
*
* @private
* @param {String} senderKey
@@ -1574,21 +1601,17 @@ MegolmDecryption.prototype._retryDecryption = async function(senderKey, sessionI
return true;
}
pending.delete(sessionId);
if (pending.size === 0) {
this._pendingEvents[senderKey];
}
logger.debug("Retrying decryption on events", [...pending]);
await Promise.all([...pending].map(async (ev) => {
try {
await ev.attemptDecryption(this._crypto);
await ev.attemptDecryption(this._crypto, true);
} catch (e) {
// don't die if something goes wrong
}
}));
// ev.attemptDecryption will re-add to this._pendingEvents if an event
// couldn't be decrypted
// If decrypted successfully, they'll have been removed from _pendingEvents
return !((this._pendingEvents[senderKey] || {})[sessionId]);
};
+19
View File
@@ -264,6 +264,25 @@ OlmDecryption.prototype.decryptEvent = async function(event) {
*/
OlmDecryption.prototype._decryptMessage = async function(
theirDeviceIdentityKey, message,
) {
// This is a wrapper that serialises decryptions of prekey messages, because
// otherwise we race between deciding we have no active sessions for the message
// and creating a new one, which we can only do once because it removes the OTK.
if (message.type !== 0) {
// not a prekey message: we can safely just try & decrypt it
return this._reallyDecryptMessage(theirDeviceIdentityKey, message);
} else {
const myPromise = this._olmDevice._olmPrekeyPromise.then(() => {
return this._reallyDecryptMessage(theirDeviceIdentityKey, message);
});
// we want the error, but don't propagate it to the next decryption
this._olmDevice._olmPrekeyPromise = myPromise.catch(() => {});
return await myPromise;
}
};
OlmDecryption.prototype._reallyDecryptMessage = async function(
theirDeviceIdentityKey, message,
) {
const sessionIds = await this._olmDevice.getSessionIdsForDevice(
theirDeviceIdentityKey,
+97 -127
View File
@@ -56,6 +56,7 @@ import {ToDeviceChannel, ToDeviceRequests} from "./verification/request/ToDevice
import * as httpApi from "../http-api";
import {IllegalMethod} from "./verification/IllegalMethod";
import {KeySignatureUploadError} from "../errors";
import {decryptAES, encryptAES} from './aes';
const DeviceVerification = DeviceInfo.DeviceVerification;
@@ -169,7 +170,9 @@ export function Crypto(baseApis, sessionStore, userId, deviceId,
this._deviceList.on(
'userCrossSigningUpdated', this._onDeviceListUserCrossSigningUpdated,
);
this._reEmitter.reEmit(this._deviceList, ["crypto.devicesUpdated"]);
this._reEmitter.reEmit(this._deviceList, [
"crypto.devicesUpdated", "crypto.willUpdateDevices",
]);
// the last time we did a check for the number of one-time-keys on the
// server.
@@ -223,8 +226,13 @@ export function Crypto(baseApis, sessionStore, userId, deviceId,
this._toDeviceVerificationRequests = new ToDeviceRequests();
this._inRoomVerificationRequests = new InRoomRequests();
// This flag will be unset whilst the client processes a sync response
// so that we don't start requesting keys until we've actually finished
// processing the response.
this._sendKeyRequestsImmediately = false;
const cryptoCallbacks = this._baseApis._cryptoCallbacks || {};
const cacheCallbacks = createCryptoStoreCacheCallbacks(cryptoStore);
const cacheCallbacks = createCryptoStoreCacheCallbacks(cryptoStore, this._olmDevice);
this._crossSigningInfo = new CrossSigningInfo(
userId,
@@ -233,7 +241,7 @@ export function Crypto(baseApis, sessionStore, userId, deviceId,
);
this._secretStorage = new SecretStorage(
baseApis, cryptoCallbacks, this._crossSigningInfo,
baseApis, cryptoCallbacks,
);
// Assuming no app-supplied callback, default to getting from SSSS.
@@ -257,6 +265,7 @@ utils.inherits(Crypto, EventEmitter);
Crypto.prototype.init = async function(opts) {
const {
exportedOlmDevice,
pickleKey,
} = opts || {};
logger.log("Crypto: initialising Olm...");
@@ -266,7 +275,7 @@ Crypto.prototype.init = async function(opts) {
? "Crypto: initialising Olm device from exported device..."
: "Crypto: initialising Olm device...",
);
await this._olmDevice.init({ fromExportedDevice: exportedOlmDevice });
await this._olmDevice.init({ fromExportedDevice: exportedOlmDevice, pickleKey });
logger.log("Crypto: loading device list...");
await this._deviceList.load();
@@ -553,8 +562,6 @@ Crypto.prototype.bootstrapSecretStorage = async function({
keyInfo.iv = iv;
keyInfo.mac = mac;
await this._crossSigningInfo.signObject(keyInfo, 'master');
await this._baseApis.setAccountData(
`m.secret_storage.key.${keyId}`, keyInfo,
);
@@ -579,9 +586,16 @@ Crypto.prototype.bootstrapSecretStorage = async function({
await resetCrossSigning();
if (!oldKeyInfo || oldKeyInfo.algorithm !== SECRET_STORAGE_ALGORITHM_V1_AES) {
// if we already have a usable default SSSS key, just use it.
if (
setupNewSecretStorage ||
!oldKeyInfo ||
oldKeyInfo.algorithm !== SECRET_STORAGE_ALGORITHM_V1_AES
) {
// if we already have a usable default SSSS key and aren't resetting SSSS just use it.
// otherwise, create a new one
// Note: we leave the old SSSS key in place: there could be other secrets using it, in theory.
// We could move them to the new key but a) that would mean we'd need to prompt for the old
// passphrase, and b) it's not clear that would be the right thing to do anyway.
const { keyInfo, privateKey } = await createSecretStorageKey();
newKeyId = await createSSSS(keyInfo, privateKey);
}
@@ -589,14 +603,6 @@ Crypto.prototype.bootstrapSecretStorage = async function({
if (oldKeyInfo && oldKeyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
await ensureCanCheckPassphrase(oldKeyId, oldKeyInfo);
}
if (setupNewKeyBackup) {
const info = await this._baseApis.prepareKeyBackupVersion(
null /* random key */,
{ secureSecretStorage: true },
);
await this._baseApis.createKeyBackupVersion(info);
}
} else if (!inStorage && keyBackupInfo) {
// we have an existing backup, but no SSSS
@@ -644,64 +650,6 @@ Crypto.prototype.bootstrapSecretStorage = async function({
undefined, keyBackupInfo,
{prefix: httpApi.PREFIX_UNSTABLE},
);
} else if (!(Object.values(decryptionKeys).some(
info => info.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES,
))) {
// we have an asymmetric SSSS
logger.log("Asymmetric SSSS found. Switching SSSS to symmetric");
const keys = {};
// fetch the cross-signing private keys (needed to sign the new
// SSSS key, and so that we can re-encrypt them with the new key).
// We store the cross-signing keys, and temporarily set a callback
// so that when the private keys are needed while setting things
// up, we can provide it.
this._baseApis._cryptoCallbacks.getCrossSigningKey =
name => crossSigningPrivateKeys[name];
for (const type of ["master", "self_signing", "user_signing"]) {
const secretName = `m.cross_signing.${type}`;
const secret = await this.getSecret(secretName);
keys[type] = secret;
crossSigningPrivateKeys[type]
= new Uint8Array(olmlib.decodeBase64(secret));
}
await this.checkOwnCrossSigningTrust();
const opts = {};
let oldKey = null;
for (const [keyId, keyInfo] of Object.entries(decryptionKeys)) {
// See if the old key was generated from a passphrase. If
// yes, use the same settings.
if (keyId in ssssKeys) {
oldKey = ssssKeys[keyId];
if (keyInfo.passphrase) {
opts.passphrase = keyInfo.passphrase;
}
break;
}
}
// create new symmetric SSSS key
newKeyId = await createSSSS(opts, oldKey);
logger.log("re-encrypting cross-signing keys");
// re-encrypt all the cross-signing keys with the new key
for (const type of ["master", "self_signing", "user_signing"]) {
const secretName = `m.cross_signing.${type}`;
await this.storeSecret(secretName, keys[type], [newKeyId]);
}
if (await this.isSecretStored("m.megolm_backup.v1", false)) {
// re-encrypt the backup key if we had one too
logger.log("re-encrypting backup key");
const backupKey = await this.getSecret("m.megolm_backup.v1");
await this.storeSecret(
"m.megolm_backup.v1", fixBackupKey(backupKey) || backupKey,
[newKeyId],
);
}
} else if (!this._crossSigningInfo.getId()) {
// we have SSSS, but we don't know if the server's cross-signing
// keys should be trusted
@@ -734,11 +682,6 @@ Crypto.prototype.bootstrapSecretStorage = async function({
// See also https://github.com/vector-im/riot-web/issues/11635
if (Object.keys(crossSigningPrivateKeys).length) {
logger.log("Storing cross-signing private keys in secret storage");
// SSSS expects its keys to be signed by cross-signing master key.
// Since we have just reset cross-signing keys, we need to re-sign the
// SSSS default key with the new cross-signing master key so that the
// following storage step can proceed.
await this._secretStorage.signKey();
// Assuming no app-supplied callback, default to storing in SSSS.
if (!appCallbacks.saveCrossSigningKeys) {
await CrossSigningInfo.storeInSecretStorage(
@@ -769,17 +712,8 @@ Crypto.prototype.bootstrapSecretStorage = async function({
const sessionBackupKey = await this.getSecret('m.megolm_backup.v1');
if (sessionBackupKey) {
logger.info("Got session backup key from secret storage: caching");
// fix up the backup key if it's in the wrong format, and replace
// in secret storage
const fixedBackupKey = fixBackupKey(sessionBackupKey);
if (fixedBackupKey) {
await this.storeSecret(
"m.megolm_backup.v1", fixedBackupKey, [newKeyId || oldKeyId],
);
}
const decodedBackupKey = new Uint8Array(olmlib.decodeBase64(
fixedBackupKey || sessionBackupKey,
));
const decodedBackupKey =
new Uint8Array(olmlib.decodeBase64(sessionBackupKey));
await this.storeSessionBackupPrivateKey(decodedBackupKey);
}
} finally {
@@ -797,26 +731,6 @@ Crypto.prototype.bootstrapSecretStorage = async function({
logger.log("Secure Secret Storage ready");
};
/**
* Fix up the backup key, that may be in the wrong format due to a bug in a
* migration step. Some backup keys were stored as a comma-separated list of
* integers, rather than a base64-encoded byte array. If this function is
* passed a string that looks like a list of integers rather than a base64
* string, it will attempt to convert it to the right format.
*
* @param {string} key the key to check
* @returns {null | string} If the key is in the wrong format, then the fixed
* key will be returned. Otherwise null will be returned.
*
*/
export function fixBackupKey(key) {
if (key.indexOf(",") < 0) {
return null;
}
const fixedKey = Uint8Array.from(key.split(","), x => parseInt(x));
return olmlib.encodeBase64(fixedKey);
}
Crypto.prototype.addSecretStorageKey = function(algorithm, opts, keyID) {
return this._secretStorage.addKey(algorithm, opts, keyID);
};
@@ -825,10 +739,6 @@ Crypto.prototype.hasSecretStorageKey = function(keyID) {
return this._secretStorage.hasKey(keyID);
};
Crypto.prototype.secretStorageKeyNeedsUpgrade = function(keyID) {
return this._secretStorage.keyNeedsUpgrade(keyID);
};
Crypto.prototype.getSecretStorageKey = function(keyID) {
return this._secretStorage.getKey(keyID);
};
@@ -890,7 +800,7 @@ Crypto.prototype.checkSecretStoragePrivateKey = function(privateKey, expectedPub
* @returns {Promise} the key, if any, or null
*/
Crypto.prototype.getSessionBackupPrivateKey = async function() {
let key = await new Promise((resolve) => {
const key = await new Promise((resolve) => {
this._cryptoStore.doTxn(
'readonly',
[IndexedDBCryptoStore.STORE_ACCOUNT],
@@ -904,12 +814,13 @@ Crypto.prototype.getSessionBackupPrivateKey = async function() {
);
});
// make sure we have a Uint8Array, rather than a string
if (key && typeof(key === "string")) {
key = new Uint8Array(olmlib.decodeBase64(fixBackupKey(key) || key));
await this.storeSessionBackupPrivateKey(key);
if (key && key.ciphertext) {
const pickleKey = Buffer.from(this._olmDevice._pickleKey);
const decrypted = await decryptAES(key, pickleKey, "m.megolm_backup.v1");
return olmlib.decodeBase64(decrypted);
} else {
return key;
}
return key;
};
/**
@@ -921,6 +832,8 @@ Crypto.prototype.storeSessionBackupPrivateKey = async function(key) {
if (!(key instanceof Uint8Array)) {
throw new Error(`storeSessionBackupPrivateKey expects Uint8Array, got ${key}`);
}
const pickleKey = Buffer.from(this._olmDevice._pickleKey);
key = await encryptAES(olmlib.encodeBase64(key), pickleKey, "m.megolm_backup.v1");
return this._cryptoStore.doTxn(
'readwrite',
[IndexedDBCryptoStore.STORE_ACCOUNT],
@@ -1476,8 +1389,9 @@ Crypto.prototype._checkAndStartKeyBackup = async function() {
backupInfo = await this._baseApis.getKeyBackupVersion();
} catch (e) {
logger.log("Error checking for active key backup", e);
if (e.httpStatus / 100 === 4) {
// well that's told us. we won't try again.
if (e.httpStatus === 404) {
// 404 is returned when the key backup does not exist, so that
// counts as successfully checking.
this._checkedForBackup = true;
}
return null;
@@ -2020,6 +1934,10 @@ Crypto.prototype.setDeviceVerification = async function(
if (!this._crossSigningInfo.getId() && userId === this._crossSigningInfo.userId) {
this._storeTrustedSelfKeys(xsk.keys);
// This will cause our own user trust to change, so emit the event
this.emit(
"userTrustStatusChanged", this._userId, this.checkUserTrust(userId),
);
}
// Now sign the master key with our user signing key (unless it's ourself)
@@ -2162,7 +2080,8 @@ Crypto.prototype.requestVerification = function(userId, devices) {
if (existingRequest) {
return Promise.resolve(existingRequest);
}
const channel = new ToDeviceChannel(this._baseApis, userId, devices);
const channel = new ToDeviceChannel(this._baseApis, userId, devices,
ToDeviceChannel.makeTransactionId());
return this._requestVerificationWithChannel(
userId,
channel,
@@ -2175,6 +2094,10 @@ Crypto.prototype._requestVerificationWithChannel = async function(
) {
let request = new VerificationRequest(
channel, this._verificationMethods, this._baseApis);
// if transaction id is already known, add request
if (channel.transactionId) {
requestsMap.setRequestByChannel(channel, request);
}
await request.sendRequest();
// don't replace the request created by a racing remote echo
const racingRequest = requestsMap.getRequestByChannel(channel);
@@ -2542,17 +2465,37 @@ Crypto.prototype.exportRoomKeys = async function() {
* Import a list of room keys previously exported by exportRoomKeys
*
* @param {Object[]} keys a list of session export objects
* @param {Object} opts
* @param {Function} opts.progressCallback called with an object which has a stage param
* @return {Promise} a promise which resolves once the keys have been imported
*/
Crypto.prototype.importRoomKeys = function(keys) {
Crypto.prototype.importRoomKeys = function(keys, opts = {}) {
let successes = 0;
let failures = 0;
const total = keys.length;
function updateProgress() {
opts.progressCallback({
stage: "load_keys",
successes,
failures,
total,
});
}
return Promise.all(keys.map((key) => {
if (!key.room_id || !key.algorithm) {
logger.warn("ignoring room key entry with missing fields", key);
failures++;
if (opts.progressCallback) { updateProgress(); }
return null;
}
const alg = this._getRoomDecryptor(key.room_id, key.algorithm);
return alg.importRoomKey(key);
return alg.importRoomKey(key).finally((r) => {
successes++;
if (opts.progressCallback) { updateProgress(); }
});
}));
};
@@ -2868,9 +2811,13 @@ Crypto.prototype.handleDeviceListChanges = async function(syncData, syncDeviceLi
* @return {Promise} a promise that resolves when the key request is queued
*/
Crypto.prototype.requestRoomKey = function(requestBody, recipients, resend=false) {
return this._outgoingRoomKeyRequestManager.sendRoomKeyRequest(
return this._outgoingRoomKeyRequestManager.queueRoomKeyRequest(
requestBody, recipients, resend,
).catch((e) => {
).then(() => {
if (this._sendKeyRequestsImmediately) {
this._outgoingRoomKeyRequestManager.sendQueuedRequests();
}
}).catch((e) => {
// this normally means we couldn't talk to the store
logger.error(
'Error requesting key for event', e,
@@ -2935,6 +2882,8 @@ Crypto.prototype.onSyncWillProcess = async function(syncData) {
this._deviceList.startTrackingDeviceList(this._userId);
this._roomDeviceTrackingState = {};
}
this._sendKeyRequestsImmediately = false;
};
/**
@@ -2966,6 +2915,14 @@ Crypto.prototype.onSyncCompleted = async function(syncData) {
if (!syncData.catchingUp) {
_maybeUploadOneTimeKeys(this);
this._processReceivedRoomKeyRequests();
// likewise don't start requesting keys until we've caught up
// on to_device messages, otherwise we'll request keys that we're
// just about to get.
this._outgoingRoomKeyRequestManager.sendQueuedRequests();
// Sync has finished so send key requests straight away.
this._sendKeyRequestsImmediately = true;
}
};
@@ -3485,6 +3442,19 @@ Crypto.prototype._processReceivedRoomKeyRequest = async function(req) {
return;
}
if (deviceId === this._deviceId) {
// We'll always get these because we send room key requests to
// '*' (ie. 'all devices') which includes the sending device,
// so ignore requests from ourself because apart from it being
// very silly, it won't work because an Olm session cannot send
// messages to itself.
// The log here is probably superfluous since we know this will
// always happen, but let's log anyway for now just in case it
// causes issues.
logger.log("Ignoring room key request from ourselves");
return;
}
// todo: should we queue up requests we don't yet have keys for,
// in case they turn up later?
+32
View File
@@ -207,6 +207,25 @@ export async function ensureOlmSessionsForDevices(
for (const deviceInfo of devices) {
const deviceId = deviceInfo.deviceId;
const key = deviceInfo.getIdentityKey();
if (key === olmDevice.deviceCurve25519Key) {
// We should never be trying to start a session with ourself.
// Apart from talking to yourself being the first sign of madness,
// olm sessions can't do this because they get confused when
// they get a message and see that the 'other side' has started a
// new chain when this side has an active sender chain.
// If you see this message being logged in the wild, we should find
// the thing that is trying to send Olm messages to itself and fix it.
logger.info("Attempted to start session with ourself! Ignoring");
// We must fill in the section in the return value though, as callers
// expect it to be there.
result[userId][deviceId] = {
device: deviceInfo,
sessionId: null,
};
continue;
}
if (!olmDevice._sessionsInProgress[key]) {
// pre-emptively mark the session as in-progress to avoid race
// conditions. If we find that we already have a session, then
@@ -238,6 +257,11 @@ export async function ensureOlmSessionsForDevices(
delete resolveSession[key];
}
if (sessionId === null || force) {
if (force) {
logger.info("Forcing new Olm session for " + userId + ":" + deviceId);
} else {
logger.info("Making new Olm session for " + userId + ":" + deviceId);
}
devicesWithoutSession.push([userId, deviceId]);
}
result[userId][deviceId] = {
@@ -277,6 +301,14 @@ export async function ensureOlmSessionsForDevices(
const deviceInfo = devices[j];
const deviceId = deviceInfo.deviceId;
const key = deviceInfo.getIdentityKey();
if (key === olmDevice.deviceCurve25519Key) {
// We've already logged about this above. Skip here too
// otherwise we'll log saying there are no one-time keys
// which will be confusing.
continue;
}
if (result[userId][deviceId].sessionId && !force) {
// we already have a result for this device
continue;
@@ -369,7 +369,7 @@ export class LocalStorageCryptoStore extends MemoryCryptoStore {
getSecretStorePrivateKey(txn, func, type) {
const key = getJsonItem(this.store, E2E_PREFIX + `ssss_cache.${type}`);
func(key ? Uint8Array.from(key) : key);
func(key);
}
storeCrossSigningKeys(txn, keys) {
@@ -380,7 +380,7 @@ export class LocalStorageCryptoStore extends MemoryCryptoStore {
storeSecretStorePrivateKey(txn, type, key) {
setJsonItem(
this.store, E2E_PREFIX + `ssss_cache.${type}`, Array.from(key),
this.store, E2E_PREFIX + `ssss_cache.${type}`, key,
);
}
+6 -1
View File
@@ -122,6 +122,11 @@ export class VerificationBase extends EventEmitter {
if (this._done) {
return Promise.reject(new Error("Verification is already done"));
}
const existingEvent = this.request.getEventFromOtherParty(type);
if (existingEvent) {
return Promise.resolve(existingEvent);
}
this._expectedEvent = type;
return new Promise((resolve, reject) => {
this._resolveEvent = resolve;
@@ -370,7 +375,7 @@ export class VerificationBase extends EventEmitter {
for (const [keyId, keyInfo] of Object.entries(keys)) {
const deviceId = keyId.split(':', 2)[1];
const device = await this._baseApis.getStoredDevice(userId, deviceId);
const device = this._baseApis.getStoredDevice(userId, deviceId);
if (device) {
await verifier(keyId, device, keyInfo);
verifiedDevices.push(deviceId);
+53 -18
View File
@@ -65,20 +65,29 @@ export class ReciprocateQRCode extends Base {
this.emit("show_reciprocate_qr", this.reciprocateQREvent);
});
// 3. determine key to sign
// 3. determine key to sign / mark as trusted
const keys = {};
if (qrCodeData.mode === MODE_VERIFY_OTHER_USER) {
// add master key to keys to be signed, only if we're not doing self-verification
const masterKey = qrCodeData.otherUserMasterKey;
keys[`ed25519:${masterKey}`] = masterKey;
} else if (qrCodeData.mode === MODE_VERIFY_SELF_TRUSTED) {
const deviceId = this.request.targetDevice.deviceId;
keys[`ed25519:${deviceId}`] = qrCodeData.otherDeviceKey;
} else {
// TODO: not sure if MODE_VERIFY_SELF_UNTRUSTED makes sense to sign anything here?
switch (qrCodeData.mode) {
case MODE_VERIFY_OTHER_USER: {
// add master key to keys to be signed, only if we're not doing self-verification
const masterKey = qrCodeData.otherUserMasterKey;
keys[`ed25519:${masterKey}`] = masterKey;
break;
}
case MODE_VERIFY_SELF_TRUSTED: {
const deviceId = this.request.targetDevice.deviceId;
keys[`ed25519:${deviceId}`] = qrCodeData.otherDeviceKey;
break;
}
case MODE_VERIFY_SELF_UNTRUSTED: {
const masterKey = qrCodeData.myMasterKey;
keys[`ed25519:${masterKey}`] = masterKey;
break;
}
}
// 4. sign the key
// 4. sign the key (or mark own MSK as verified in case of MODE_VERIFY_SELF_TRUSTED)
await this._verifyKeys(this.userId, keys, (keyId, device, keyInfo) => {
// make sure the device has the expected keys
const targetKey = keys[keyId];
@@ -108,11 +117,15 @@ const MODE_VERIFY_SELF_TRUSTED = 0x01; // We trust the master key
const MODE_VERIFY_SELF_UNTRUSTED = 0x02; // We do not trust the master key
export class QRCodeData {
constructor(mode, sharedSecret, otherUserMasterKey, otherDeviceKey, buffer) {
constructor(
mode, sharedSecret, otherUserMasterKey,
otherDeviceKey, myMasterKey, buffer,
) {
this._sharedSecret = sharedSecret;
this._mode = mode;
this._otherUserMasterKey = otherUserMasterKey;
this._otherDeviceKey = otherDeviceKey;
this._myMasterKey = myMasterKey;
this._buffer = buffer;
}
@@ -121,22 +134,28 @@ export class QRCodeData {
const mode = QRCodeData._determineMode(request, client);
let otherUserMasterKey = null;
let otherDeviceKey = null;
let myMasterKey = null;
if (mode === MODE_VERIFY_OTHER_USER) {
const otherUserCrossSigningInfo =
client.getStoredCrossSigningForUser(request.otherUserId);
otherUserMasterKey = otherUserCrossSigningInfo.getId("master");
} else if (mode === MODE_VERIFY_SELF_TRUSTED) {
otherDeviceKey = await QRCodeData._getOtherDeviceKey(request, client);
} else if (mode === MODE_VERIFY_SELF_UNTRUSTED) {
const myUserId = client.getUserId();
const myCrossSigningInfo = client.getStoredCrossSigningForUser(myUserId);
myMasterKey = myCrossSigningInfo.getId("master");
}
const qrData = QRCodeData._generateQrData(
request, client, mode,
sharedSecret,
otherUserMasterKey,
otherDeviceKey,
myMasterKey,
);
const buffer = QRCodeData._generateBuffer(qrData);
return new QRCodeData(mode, sharedSecret,
otherUserMasterKey, otherDeviceKey, buffer);
otherUserMasterKey, otherDeviceKey, myMasterKey, buffer);
}
get buffer() {
@@ -147,14 +166,30 @@ export class QRCodeData {
return this._mode;
}
/**
* only set when mode is MODE_VERIFY_SELF_TRUSTED
* @return {string} device key of other party at time of generating QR code
*/
get otherDeviceKey() {
return this._otherDeviceKey;
}
/**
* only set when mode is MODE_VERIFY_OTHER_USER
* @return {string} master key of other party at time of generating QR code
*/
get otherUserMasterKey() {
return this._otherUserMasterKey;
}
/**
* only set when mode is MODE_VERIFY_SELF_UNTRUSTED
* @return {string} own master key at time of generating QR code
*/
get myMasterKey() {
return this._myMasterKey;
}
/**
* The unpadded base64 encoded shared secret.
*/
@@ -172,7 +207,7 @@ export class QRCodeData {
const myUserId = client.getUserId();
const otherDevice = request.targetDevice;
const otherDeviceId = otherDevice ? otherDevice.deviceId : null;
const device = await client.getStoredDevice(myUserId, otherDeviceId);
const device = client.getStoredDevice(myUserId, otherDeviceId);
if (!device) {
throw new Error("could not find device " + otherDeviceId);
}
@@ -198,7 +233,8 @@ export class QRCodeData {
}
static _generateQrData(request, client, mode,
encodedSharedSecret, otherUserMasterKey, otherDeviceKey,
encodedSharedSecret, otherUserMasterKey,
otherDeviceKey, myMasterKey,
) {
const myUserId = client.getUserId();
const transactionId = request.channel.transactionId;
@@ -213,16 +249,15 @@ export class QRCodeData {
};
const myCrossSigningInfo = client.getStoredCrossSigningForUser(myUserId);
const myMasterKey = myCrossSigningInfo.getId("master");
if (mode === MODE_VERIFY_OTHER_USER) {
// First key is our master cross signing key
qrData.firstKeyB64 = myMasterKey;
qrData.firstKeyB64 = myCrossSigningInfo.getId("master");
// Second key is the other user's master cross signing key
qrData.secondKeyB64 = otherUserMasterKey;
} else if (mode === MODE_VERIFY_SELF_TRUSTED) {
// First key is our master cross signing key
qrData.firstKeyB64 = myMasterKey;
qrData.firstKeyB64 = myCrossSigningInfo.getId("master");
qrData.secondKeyB64 = otherDeviceKey;
} else if (mode === MODE_VERIFY_SELF_UNTRUSTED) {
// First key is our device's key
+32 -13
View File
@@ -175,11 +175,33 @@ function calculateMAC(olmSAS, method) {
};
}
const calculateKeyAgreement = {
"curve25519-hkdf-sha256": function(sas, olmSAS, bytes) {
const ourInfo = `${sas._baseApis.getUserId()}|${sas._baseApis.deviceId}|`
+ `${sas.ourSASPubKey}|`;
const theirInfo = `${sas.userId}|${sas.deviceId}|${sas.theirSASPubKey}|`;
const sasInfo =
"MATRIX_KEY_VERIFICATION_SAS|"
+ (sas.initiatedByMe ? ourInfo + theirInfo : theirInfo + ourInfo)
+ sas._channel.transactionId;
return olmSAS.generate_bytes(sasInfo, bytes);
},
"curve25519": function(sas, olmSAS, bytes) {
const ourInfo = `${sas._baseApis.getUserId()}${sas._baseApis.deviceId}`;
const theirInfo = `${sas.userId}${sas.deviceId}`;
const sasInfo =
"MATRIX_KEY_VERIFICATION_SAS"
+ (sas.initiatedByMe ? ourInfo + theirInfo : theirInfo + ourInfo)
+ sas._channel.transactionId;
return olmSAS.generate_bytes(sasInfo, bytes);
},
};
/* lists of algorithms/methods that are supported. The key agreement, hashes,
* and MAC lists should be sorted in order of preference (most preferred
* first).
*/
const KEY_AGREEMENT_LIST = ["curve25519"];
const KEY_AGREEMENT_LIST = ["curve25519-hkdf-sha256", "curve25519"];
const HASHES_LIST = ["sha256"];
const MAC_LIST = ["hkdf-hmac-sha256", "hmac-sha256"];
const SAS_LIST = Object.keys(sasGenerators);
@@ -291,12 +313,14 @@ export class SAS extends Base {
if (typeof content.commitment !== "string") {
throw newInvalidMessageError();
}
const keyAgreement = content.key_agreement_protocol;
const macMethod = content.message_authentication_code;
const hashCommitment = content.commitment;
const olmSAS = new global.Olm.SAS();
try {
this.ourSASPubKey = olmSAS.get_pubkey();
await this._send("m.key.verification.key", {
key: olmSAS.get_pubkey(),
key: this.ourSASPubKey,
});
@@ -308,13 +332,10 @@ export class SAS extends Base {
if (olmutil.sha256(commitmentStr) !== hashCommitment) {
throw newMismatchedCommitmentError();
}
this.theirSASPubKey = content.key;
olmSAS.set_their_key(content.key);
const sasInfo = "MATRIX_KEY_VERIFICATION_SAS"
+ this._baseApis.getUserId() + this._baseApis.deviceId
+ this.userId + this.deviceId
+ this._channel.transactionId;
const sasBytes = olmSAS.generate_bytes(sasInfo, 6);
const sasBytes = calculateKeyAgreement[keyAgreement](this, olmSAS, 6);
const verifySAS = new Promise((resolve, reject) => {
this.sasEvent = {
sas: generateSas(sasBytes, sasMethods),
@@ -394,16 +415,14 @@ export class SAS extends Base {
let e = await this._waitForEvent("m.key.verification.key");
// FIXME: make sure event is properly formed
content = e.getContent();
this.theirSASPubKey = content.key;
olmSAS.set_their_key(content.key);
this.ourSASPubKey = olmSAS.get_pubkey();
await this._send("m.key.verification.key", {
key: olmSAS.get_pubkey(),
key: this.ourSASPubKey,
});
const sasInfo = "MATRIX_KEY_VERIFICATION_SAS"
+ this.userId + this.deviceId
+ this._baseApis.getUserId() + this._baseApis.deviceId
+ this._channel.transactionId;
const sasBytes = olmSAS.generate_bytes(sasInfo, 6);
const sasBytes = calculateKeyAgreement[keyAgreement](this, olmSAS, 6);
const verifySAS = new Promise((resolve, reject) => {
this.sasEvent = {
sas: generateSas(sasBytes, sasMethods),
@@ -877,4 +877,8 @@ export class VerificationRequest extends EventEmitter {
this._setPhase(newTransitions[newTransitions.length - 1].phase);
}
}
getEventFromOtherParty(type) {
return this._eventsByThem.get(type);
}
}
+3 -2
View File
@@ -108,8 +108,9 @@ FilterComponent.prototype._checkFields =
}
const allowed_values = self[name];
if (allowed_values) {
if (!allowed_values.map(match_func)) {
if (allowed_values && allowed_values.length > 0) {
const anyMatch = allowed_values.some(match_func);
if (!anyMatch) {
return false;
}
}
+5 -7
View File
@@ -56,13 +56,6 @@ Filter.LAZY_LOADING_MESSAGES_FILTER = {
lazy_load_members: true,
};
Filter.LAZY_LOADING_SYNC_FILTER = {
room: {
state: Filter.LAZY_LOADING_MESSAGES_FILTER,
},
};
/**
* Get the ID of this filter on your homeserver (if known)
* @return {?Number} The filter ID
@@ -96,6 +89,7 @@ Filter.prototype.setDefinition = function(definition) {
// "state": {
// "types": ["m.room.*"],
// "not_rooms": ["!726s6s6q:example.com"],
// "lazy_load_members": true,
// },
// "timeline": {
// "limit": 10,
@@ -177,6 +171,10 @@ Filter.prototype.setTimelineLimit = function(limit) {
setProp(this.definition, "room.timeline.limit", limit);
};
Filter.prototype.setLazyLoadMembers = function(enabled) {
setProp(this.definition, "room.state.lazy_load_members", !!enabled);
};
/**
* Control whether left rooms should be included in responses.
* @param {boolean} includeLeave True to make rooms the user has left appear
+86 -8
View File
@@ -276,6 +276,9 @@ MatrixHttpApi.prototype = {
callbacks.clearTimeout(xhr.timeout_timer);
var resp;
try {
if (xhr.status === 0) {
throw new AbortError();
}
if (!xhr.responseText) {
throw new Error('No response body.');
}
@@ -789,6 +792,17 @@ const requestCallback = function(
userDefinedCallback = userDefinedCallback || function() {};
return function(err, response, body) {
if (err) {
// the unit tests use matrix-mock-request, which throw the string "aborted" when aborting a request.
// See https://github.com/matrix-org/matrix-mock-request/blob/3276d0263a561b5b8326b47bae720578a2c7473a/src/index.js#L48
const aborted = err.name === "AbortError" || err === "aborted";
if (!aborted && !(err instanceof MatrixError)) {
// browser-request just throws normal Error objects,
// not `TypeError`s like fetch does. So just assume any
// error is due to the connection.
err = new ConnectionError("request failed", err);
}
}
if (!err) {
try {
if (response.statusCode >= 400) {
@@ -892,12 +906,76 @@ function getResponseContentType(response) {
* @prop {Object} data The raw Matrix error JSON used to construct this object.
* @prop {integer} httpStatus The numeric HTTP status code given
*/
export function MatrixError(errorJson) {
errorJson = errorJson || {};
this.errcode = errorJson.errcode;
this.name = errorJson.errcode || "Unknown error code";
this.message = errorJson.error || "Unknown message";
this.data = errorJson;
export class MatrixError extends Error {
constructor(errorJson) {
errorJson = errorJson || {};
super(`MatrixError: ${errorJson.errcode}`);
this.errcode = errorJson.errcode;
this.name = errorJson.errcode || "Unknown error code";
this.message = errorJson.error || "Unknown message";
this.data = errorJson;
}
}
/**
* Construct a ConnectionError. This is a JavaScript Error indicating
* that a request failed because of some error with the connection, either
* CORS was not correctly configured on the server, the server didn't response,
* the request timed out, or the internet connection on the client side went down.
* @constructor
*/
export class ConnectionError extends Error {
constructor(message, cause = undefined) {
super(message + (cause ? `: ${cause.message}` : ""));
this._cause = cause;
}
get name() {
return "ConnectionError";
}
get cause() {
return this._cause;
}
}
export class AbortError extends Error {
constructor() {
super("Operation aborted");
}
get name() {
return "AbortError";
}
}
/**
* Retries a network operation run in a callback.
* @param {number} maxAttempts maximum attempts to try
* @param {Function} callback callback that returns a promise of the network operation. If rejected with ConnectionError, it will be retried by calling the callback again.
* @return {any} the result of the network operation
* @throws {ConnectionError} If after maxAttempts the callback still throws ConnectionError
*/
export async function retryNetworkOperation(maxAttempts, callback) {
let attempts = 0;
let lastConnectionError = null;
while (attempts < maxAttempts) {
try {
if (attempts > 0) {
const timeout = 1000 * Math.pow(2, attempts);
console.log(`network operation failed ${attempts} times,` +
` retrying in ${timeout}ms...`);
await new Promise(r => setTimeout(r, timeout));
}
return await callback();
} catch (err) {
if (err instanceof ConnectionError) {
attempts += 1;
lastConnectionError = err;
} else {
throw err;
}
}
}
throw lastConnectionError;
}
MatrixError.prototype = Object.create(Error.prototype);
MatrixError.prototype.constructor = MatrixError;
+29 -12
View File
@@ -143,11 +143,13 @@ InteractiveAuth.prototype = {
this._resolveFunc = resolve;
this._rejectFunc = reject;
// if we have no flows, try a request (we'll have
// just a session ID in _data if resuming)
if (!this._data.flows) {
const hasFlows = this._data && this._data.flows;
// if we have no flows, try a request to acquire the flows
if (!hasFlows) {
if (this._busyChangedCallback) this._busyChangedCallback(true);
this._doRequest(this._data).finally(() => {
// Do a fresh request as we're just acquiring flows.
this._doRequest(null).finally(() => {
if (this._busyChangedCallback) this._busyChangedCallback(false);
});
} else {
@@ -186,7 +188,11 @@ InteractiveAuth.prototype = {
}
authDict = {
type: EMAIL_STAGE_TYPE,
// TODO: Remove `threepid_creds` once servers support proper UIA
// See https://github.com/matrix-org/synapse/issues/5665
// See https://github.com/matrix-org/matrix-doc/issues/2220
threepid_creds: creds,
threepidCreds: creds,
};
}
}
@@ -264,11 +270,16 @@ InteractiveAuth.prototype = {
}
}
// use the sessionid from the last request.
const auth = {
session: this._data.session,
};
utils.extend(auth, authData);
// use the sessionid from the last request, if one is present.
let auth;
if (this._data.session) {
auth = {
session: this._data.session,
};
utils.extend(auth, authData);
} else {
auth = authData;
}
try {
// NB. the 'background' flag is deprecated by the busyChanged
@@ -325,7 +336,7 @@ InteractiveAuth.prototype = {
} catch (error) {
// sometimes UI auth errors don't come with flows
const errorFlows = error.data ? error.data.flows : null;
const haveFlows = Boolean(this._data.flows) || Boolean(errorFlows);
const haveFlows = this._data.flows || Boolean(errorFlows);
if (error.httpStatus !== 401 || !error.data || !haveFlows) {
// doesn't look like an interactive-auth failure.
if (!background) {
@@ -351,7 +362,13 @@ InteractiveAuth.prototype = {
error.data.session = this._data.session;
}
this._data = error.data;
this._startNextAuthStage();
try {
this._startNextAuthStage();
} catch (e) {
this._rejectFunc(e);
this._resolveFunc = null;
this._rejectFunc = null;
}
if (
!this._emailSid &&
@@ -414,7 +431,7 @@ InteractiveAuth.prototype = {
return;
}
if (this._data.errcode || this._data.error) {
if (this._data && this._data.errcode || this._data.error) {
this._stateUpdatedCallback(nextStage, {
errcode: this._data.errcode || "",
error: this._data.error || "",
+22 -2
View File
@@ -16,8 +16,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import type Request from "request";
import {MemoryCryptoStore} from "./crypto/store/memory-crypto-store";
import {LocalStorageCryptoStore} from "./crypto/store/localStorage-crypto-store";
import {IndexedDBCryptoStore} from "./crypto/store/indexeddb-crypto-store";
import {MemoryStore} from "./store/memory";
import {StubStore} from "./store/stub";
import {LocalIndexedDBStoreBackend} from "./store/indexeddb-local-backend";
import {RemoteIndexedDBStoreBackend} from "./store/indexeddb-remote-backend";
import {MatrixScheduler} from "./scheduler";
import {MatrixClient} from "./client";
@@ -89,6 +96,10 @@ export function wrapRequest(wrapper) {
};
}
type Store =
StubStore | MemoryStore | LocalIndexedDBStoreBackend | RemoteIndexedDBStoreBackend;
type CryptoStore = MemoryCryptoStore | LocalStorageCryptoStore | IndexedDBCryptoStore;
let cryptoStoreFactory = () => new MemoryCryptoStore;
@@ -102,6 +113,15 @@ export function setCryptoStoreFactory(fac) {
cryptoStoreFactory = fac;
}
interface ICreateClientOpts {
baseUrl: string;
store?: Store;
cryptoStore?: CryptoStore;
scheduler?: MatrixScheduler;
request?: Request;
}
/**
* Construct a Matrix Client. Similar to {@link module:client.MatrixClient}
* except that the 'request', 'store' and 'scheduler' dependencies are satisfied.
@@ -125,10 +145,10 @@ export function setCryptoStoreFactory(fac) {
* @see {@link module:client.MatrixClient} for the full list of options for
* <code>opts</code>.
*/
export function createClient(opts) {
export function createClient(opts: ICreateClientOpts | string) {
if (typeof opts === "string") {
opts = {
"baseUrl": opts,
"baseUrl": opts as string,
};
}
opts.request = opts.request || requestInstance;
+11 -5
View File
@@ -390,11 +390,12 @@ utils.extend(MatrixEvent.prototype, {
* @internal
*
* @param {module:crypto} crypto crypto module
* @param {bool} isRetry True if this is a retry (enables more logging)
*
* @returns {Promise} promise which resolves (to undefined) when the decryption
* attempt is completed.
*/
attemptDecryption: async function(crypto) {
attemptDecryption: async function(crypto, isRetry) {
// start with a couple of sanity checks.
if (!this.isEncrypted()) {
throw new Error("Attempt to decrypt event which isn't encrypted");
@@ -406,7 +407,7 @@ utils.extend(MatrixEvent.prototype, {
) {
// we may want to just ignore this? let's start with rejecting it.
throw new Error(
"Attempt to decrypt event which has already been encrypted",
"Attempt to decrypt event which has already been decrypted",
);
}
@@ -424,7 +425,7 @@ utils.extend(MatrixEvent.prototype, {
return this._decryptionPromise;
}
this._decryptionPromise = this._decryptionLoop(crypto);
this._decryptionPromise = this._decryptionLoop(crypto, isRetry);
return this._decryptionPromise;
},
@@ -469,7 +470,7 @@ utils.extend(MatrixEvent.prototype, {
return recipients;
},
_decryptionLoop: async function(crypto) {
_decryptionLoop: async function(crypto, isRetry) {
// make sure that this method never runs completely synchronously.
// (doing so would mean that we would clear _decryptionPromise *before*
// it is set in attemptDecryption - and hence end up with a stuck
@@ -486,13 +487,18 @@ utils.extend(MatrixEvent.prototype, {
res = this._badEncryptedMessage("Encryption not enabled");
} else {
res = await crypto.decryptEvent(this);
if (isRetry) {
logger.info(`Decrypted event on retry (id=${this.getId()})`);
}
}
} catch (e) {
if (e.name !== "DecryptionError") {
// not a decryption error: log the whole exception as an error
// (and don't bother with a retry)
const re = isRetry ? 're' : '';
logger.error(
`Error decrypting event (id=${this.getId()}): ${e.stack || e}`,
`Error ${re}decrypting event ` +
`(id=${this.getId()}): ${e.stack || e}`,
);
this._decryptionPromise = null;
this._retryDecryption = false;
+2 -2
View File
@@ -209,7 +209,7 @@ utils.inherits(Room, EventEmitter);
Room.prototype.getVersion = function() {
const createEvent = this.currentState.getStateEvents("m.room.create", "");
if (!createEvent) {
logger.warn("Room " + this.room_id + " does not have an m.room.create event");
logger.warn("Room " + this.roomId + " does not have an m.room.create event");
return '1';
}
const ver = createEvent.getContent()['room_version'];
@@ -675,7 +675,7 @@ Room.prototype.hasUnverifiedDevices = async function() {
}
const e2eMembers = await this.getEncryptionTargetMembers();
for (const member of e2eMembers) {
const devices = await this._client.getStoredDevicesForUser(member.userId);
const devices = this._client.getStoredDevicesForUser(member.userId);
if (devices.some((device) => device.isUnverified())) {
return true;
}
+21 -43
View File
@@ -23,9 +23,8 @@ import {escapeRegExp, globToRegexp, isNullOrUndefined} from "./utils";
const RULEKINDS_IN_ORDER = ['override', 'content', 'room', 'sender', 'underride'];
// The default override rules to apply when calculating actions for an event. These
// defaults apply under no other circumstances to avoid confusing the client with server
// state. We do this for two reasons:
// The default override rules to apply to the push rules that arrive from the server.
// We do this for two reasons:
// 1. Synapse is unlikely to send us the push rule in an incremental sync - see
// https://github.com/matrix-org/synapse/pull/4867#issuecomment-481446072 for
// more details.
@@ -364,33 +363,6 @@ export function PushProcessor(client) {
return actionObj;
};
const applyRuleDefaults = function(clientRuleset) {
// Deep clone the object before we mutate it
const ruleset = JSON.parse(JSON.stringify(clientRuleset));
if (!clientRuleset['global']) {
clientRuleset['global'] = {};
}
if (!clientRuleset['global']['override']) {
clientRuleset['global']['override'] = [];
}
// Apply default overrides
const globalOverrides = clientRuleset['global']['override'];
for (const override of DEFAULT_OVERRIDE_RULES) {
const existingRule = globalOverrides
.find((r) => r.rule_id === override.rule_id);
if (!existingRule) {
const ruleId = override.rule_id;
console.warn(`Adding default global override for ${ruleId}`);
globalOverrides.push(override);
}
}
return ruleset;
};
this.ruleMatchesEvent = function(rule, ev) {
let ret = true;
for (let i = 0; i < rule.conditions.length; ++i) {
@@ -410,8 +382,7 @@ export function PushProcessor(client) {
* @return {PushAction}
*/
this.actionsForEvent = function(ev) {
const rules = applyRuleDefaults(client.pushRules);
return pushActionsForEventAndRulesets(ev, rules);
return pushActionsForEventAndRulesets(ev, client.pushRules);
};
/**
@@ -476,18 +447,25 @@ PushProcessor.rewriteDefaultRules = function(incomingRules) {
if (!newRules.global) newRules.global = {};
if (!newRules.global.override) newRules.global.override = [];
// Fix default override rules
newRules.global.override = newRules.global.override.map(r => {
const defaultRule = DEFAULT_OVERRIDE_RULES.find(d => d.rule_id === r.rule_id);
if (!defaultRule) return r;
// Merge the client-level defaults with the ones from the server
const globalOverrides = newRules.global.override;
for (const override of DEFAULT_OVERRIDE_RULES) {
const existingRule = globalOverrides
.find((r) => r.rule_id === override.rule_id);
// Copy over the actions, default, and conditions. Don't touch the user's
// preference.
r.default = defaultRule.default;
r.conditions = defaultRule.conditions;
r.actions = defaultRule.actions;
return r;
});
if (existingRule) {
// Copy over the actions, default, and conditions. Don't touch the user's
// preference.
existingRule.default = override.default;
existingRule.conditions = override.conditions;
existingRule.actions = override.actions;
} else {
// Add the rule
const ruleId = override.rule_id;
console.warn(`Adding default global override for ${ruleId}`);
globalOverrides.push(override);
}
}
return newRules;
};
+25 -2
View File
@@ -25,6 +25,15 @@ limitations under the License.
import {User} from "../models/user";
import * as utils from "../utils";
function isValidFilterId(filterId) {
const isValidStr = typeof filterId === "string" &&
!!filterId &&
filterId !== "undefined" && // exclude these as we've serialized undefined in localStorage before
filterId !== "null";
return isValidStr || typeof filterId === "number";
}
/**
* Construct a new in-memory data store for the Matrix Client.
* @constructor
@@ -273,8 +282,17 @@ MemoryStore.prototype = {
if (!this.localStorage) {
return null;
}
const key = "mxjssdk_memory_filter_" + filterName;
// XXX Storage.getItem doesn't throw ...
// or are we using something different
// than window.localStorage in some cases
// that does throw?
// that would be very naughty
try {
return this.localStorage.getItem("mxjssdk_memory_filter_" + filterName);
const value = this.localStorage.getItem(key);
if (isValidFilterId(value)) {
return value;
}
} catch (e) {}
return null;
},
@@ -288,8 +306,13 @@ MemoryStore.prototype = {
if (!this.localStorage) {
return;
}
const key = "mxjssdk_memory_filter_" + filterName;
try {
this.localStorage.setItem("mxjssdk_memory_filter_" + filterName, filterId);
if (isValidFilterId(filterId)) {
this.localStorage.setItem(key, filterId);
} else {
this.localStorage.removeItem(key);
}
} catch (e) {}
},
+11 -14
View File
@@ -511,6 +511,12 @@ SyncApi.prototype.sync = function() {
checkLazyLoadStatus(); // advance to the next stage
}
function buildDefaultFilter() {
const filter = new Filter(client.credentials.userId);
filter.setTimelineLimit(self.opts.initialSyncLimit);
return filter;
}
const checkLazyLoadStatus = async () => {
debuglog("Checking lazy load status...");
if (this.opts.lazyLoadMembers && client.isGuest()) {
@@ -520,19 +526,11 @@ SyncApi.prototype.sync = function() {
debuglog("Checking server lazy load support...");
const supported = await client.doesServerSupportLazyLoading();
if (supported) {
try {
debuglog("Creating and storing lazy load sync filter...");
this.opts.filter = await client.createFilter(
Filter.LAZY_LOADING_SYNC_FILTER,
);
debuglog("Created and stored lazy load sync filter");
} catch (err) {
logger.error(
"Creating and storing lazy load sync filter failed",
err,
);
throw err;
debuglog("Enabling lazy load on sync filter...");
if (!this.opts.filter) {
this.opts.filter = buildDefaultFilter();
}
this.opts.filter.setLazyLoadMembers(true);
} else {
debuglog("LL: lazy loading requested but not supported " +
"by server, so disabling");
@@ -575,8 +573,7 @@ SyncApi.prototype.sync = function() {
if (self.opts.filter) {
filter = self.opts.filter;
} else {
filter = new Filter(client.credentials.userId);
filter.setTimelineLimit(self.opts.initialSyncLimit);
filter = buildDefaultFilter();
}
let filterId;
+3 -1
View File
@@ -21,6 +21,7 @@ limitations under the License.
*/
import unhomoglyph from 'unhomoglyph';
import {ConnectionError} from "./http-api";
/**
* Encode a dictionary of query parameters.
@@ -265,7 +266,7 @@ export function checkObjectHasNoAdditionalKeys(obj: object, allowedKeys: string[
* @param {Object} obj The object to deep copy.
* @return {Object} A copy of the object without any references to the original.
*/
export function deepCopy(obj: object): object {
export function deepCopy<T>(obj: T): T {
return JSON.parse(JSON.stringify(obj));
}
@@ -372,6 +373,7 @@ export function extend() {
const target = arguments[0] || {};
for (let i = 1; i < arguments.length; i++) {
const source = arguments[i];
if (!source) continue;
for (const propName in source) { // eslint-disable-line guard-for-in
target[propName] = source[propName];
}
+1
View File
@@ -2,6 +2,7 @@
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true,
"module": "commonjs",
"moduleResolution": "node",
"target": "es2016",
+42 -261
View File
@@ -1033,6 +1033,11 @@
dependencies:
"@types/babel-types" "*"
"@types/caseless@*":
version "0.12.2"
resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8"
integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
@@ -1058,16 +1063,36 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==
"@types/node@*":
version "13.13.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.1.tgz#1ba94c5a177a1692518bfc7b41aec0aa1a14354e"
integrity sha512-uysqysLJ+As9jqI5yqjwP3QJrhOcUwBjHUlUxPxjbplwKoILvXVsmYWEhfmAQlrPfbRZmhJB007o4L9sKqtHqQ==
"@types/node@12":
version "12.12.34"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.34.tgz#0a5d6ae5d22612f0cf5f10320e1fc5d2a745dcb8"
integrity sha512-BneGN0J9ke24lBRn44hVHNeDlrXRYF+VRp0HbSUNnEZahXGAysHZIqnf/hER6aabdBgzM4YOV4jrR8gj4Zfi0g==
"@types/request@^2.48.4":
version "2.48.4"
resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.4.tgz#df3d43d7b9ed3550feaa1286c6eabf0738e6cf7e"
integrity sha512-W1t1MTKYR8PxICH+A4HgEIPuAC3sbljoEVfyZbeFJJDbr30guDspJri2XOaM2E+Un7ZjrihaDi7cf6fPa2tbgw==
dependencies:
"@types/caseless" "*"
"@types/node" "*"
"@types/tough-cookie" "*"
form-data "^2.5.0"
"@types/stack-utils@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
"@types/tough-cookie@*":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d"
integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==
"@types/yargs-parser@*":
version "15.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
@@ -1116,11 +1141,6 @@ abab@^2.0.0:
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a"
integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
acorn-globals@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf"
@@ -1214,11 +1234,6 @@ ansi-escapes@^3.0.0, ansi-escapes@^3.2.0:
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
ansi-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
@@ -1249,19 +1264,6 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
aproba@^1.0.3:
version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
are-we-there-yet@~1.1.2:
version "1.1.5"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
dependencies:
delegates "^1.0.0"
readable-stream "^2.0.6"
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@@ -1877,11 +1879,6 @@ chokidar@^2.1.8:
optionalDependencies:
fsevents "^1.2.7"
chownr@^1.1.1:
version "1.1.4"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
ci-info@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
@@ -1947,11 +1944,6 @@ co@^4.6.0:
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
collection-visit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
@@ -2029,11 +2021,6 @@ console-browserify@^1.1.0:
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
constantinople@^3.0.1, constantinople@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/constantinople/-/constantinople-3.1.2.tgz#d45ed724f57d3d10500017a7d3a889c1381ae647"
@@ -2193,13 +2180,6 @@ debug@^2.2.0, debug@^2.3.3:
dependencies:
ms "2.0.0"
debug@^3.2.6:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
dependencies:
ms "^2.1.1"
debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
@@ -2217,11 +2197,6 @@ decode-uri-component@^0.2.0:
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
deep-extend@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
@@ -2266,11 +2241,6 @@ delayed-stream@~1.0.0:
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
deps-sort@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.1.tgz#9dfdc876d2bcec3386b6829ac52162cda9fa208d"
@@ -2289,11 +2259,6 @@ des.js@^1.0.0:
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
detect-libc@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
detect-newline@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2"
@@ -2866,6 +2831,15 @@ forever-agent@~0.6.1:
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
form-data@^2.5.0:
version "2.5.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.6"
mime-types "^2.1.12"
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
@@ -2882,13 +2856,6 @@ fragment-cache@^0.2.1:
dependencies:
map-cache "^0.2.2"
fs-minipass@^1.2.5:
version "1.2.7"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
dependencies:
minipass "^2.6.0"
fs-readdir-recursive@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27"
@@ -2922,20 +2889,6 @@ functions-have-names@^1.2.0:
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.1.tgz#a981ac397fa0c9964551402cdc5533d7a4d52f91"
integrity sha512-j48B/ZI7VKs3sgeI2cZp7WXWmZXu7Iq5pl5/vptV5N2mq+DGFuS/ulaDjtaoLpYzuD6u8UgrUKHfgo7fDTSiBA==
gauge@~2.7.3:
version "2.7.4"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
dependencies:
aproba "^1.0.3"
console-control-strings "^1.0.0"
has-unicode "^2.0.0"
object-assign "^4.1.0"
signal-exit "^3.0.0"
string-width "^1.0.1"
strip-ansi "^3.0.1"
wide-align "^1.1.0"
gensync@^1.0.0-beta.1:
version "1.0.0-beta.1"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
@@ -3028,11 +2981,6 @@ has-symbols@^1.0.0, has-symbols@^1.0.1:
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
has-value@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
@@ -3142,7 +3090,7 @@ https-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
iconv-lite@0.4.24, iconv-lite@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@@ -3154,13 +3102,6 @@ ieee754@^1.1.4:
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
ignore-walk@^3.0.1:
version "3.0.3"
resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37"
integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==
dependencies:
minimatch "^3.0.4"
ignore@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
@@ -3210,11 +3151,6 @@ inherits@2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
inline-source-map@~0.6.0:
version "0.6.2"
resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5"
@@ -3415,13 +3351,6 @@ is-extglob@^2.1.0, is-extglob@^2.1.1:
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
is-fullwidth-code-point@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
dependencies:
number-is-nan "^1.0.0"
is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
@@ -4437,21 +4366,6 @@ minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
dependencies:
safe-buffer "^5.1.2"
yallist "^3.0.0"
minizlib@^1.2.1:
version "1.3.3"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
dependencies:
minipass "^2.9.0"
mixin-deep@^1.2.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
@@ -4465,7 +4379,7 @@ mkdirp-classic@^0.5.2:
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz#54c441ce4c96cd7790e10b41a87aa51068ecab2b"
integrity sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g==
mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1:
mkdirp@^0.5.1, mkdirp@~0.5.1:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
@@ -4548,15 +4462,6 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
needle@^2.2.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.1.tgz#14af48732463d7475696f937626b1b993247a56a"
integrity sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g==
dependencies:
debug "^3.2.6"
iconv-lite "^0.4.4"
sax "^1.2.4"
nice-try@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
@@ -4590,35 +4495,11 @@ node-notifier@^5.4.2:
shellwords "^0.1.1"
which "^1.3.0"
node-pre-gyp@*:
version "0.14.0"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83"
integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==
dependencies:
detect-libc "^1.0.2"
mkdirp "^0.5.1"
needle "^2.2.1"
nopt "^4.0.1"
npm-packlist "^1.1.6"
npmlog "^4.0.2"
rc "^1.2.7"
rimraf "^2.6.1"
semver "^5.3.0"
tar "^4.4.2"
node-releases@^1.1.53:
version "1.1.53"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.53.tgz#2d821bfa499ed7c5dffc5e2f28c88e78a08ee3f4"
integrity sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==
nopt@^4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==
dependencies:
abbrev "1"
osenv "^0.1.4"
normalize-package-data@^2.3.2:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
@@ -4641,27 +4522,6 @@ normalize-path@^3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
npm-bundled@^1.0.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b"
integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==
dependencies:
npm-normalize-package-bin "^1.0.1"
npm-normalize-package-bin@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2"
integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==
npm-packlist@^1.1.6:
version "1.4.8"
resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e"
integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==
dependencies:
ignore-walk "^3.0.1"
npm-bundled "^1.0.1"
npm-normalize-package-bin "^1.0.1"
npm-run-path@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
@@ -4669,21 +4529,6 @@ npm-run-path@^2.0.0:
dependencies:
path-key "^2.0.0"
npmlog@^4.0.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
dependencies:
are-we-there-yet "~1.1.2"
console-control-strings "~1.1.0"
gauge "~2.7.3"
set-blocking "~2.0.0"
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
nwsapi@^2.0.7:
version "2.2.0"
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
@@ -4795,24 +4640,11 @@ os-browserify@~0.3.0:
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
os-homedir@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
os-tmpdir@^1.0.0, os-tmpdir@~1.0.2:
os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
osenv@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
dependencies:
os-homedir "^1.0.0"
os-tmpdir "^1.0.0"
p-each-series@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71"
@@ -5272,16 +5104,6 @@ randomfill@^1.0.3:
randombytes "^2.0.5"
safe-buffer "^5.1.0"
rc@^1.2.7:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
dependencies:
deep-extend "^0.6.0"
ini "~1.3.0"
minimist "^1.2.0"
strip-json-comments "~2.0.1"
react-ace@^6.5.0:
version "6.6.0"
resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-6.6.0.tgz#a79457ef03c3b1f8d4fc598a003b1d6ad464f1a0"
@@ -5341,7 +5163,7 @@ read-pkg@^3.0.0:
normalize-package-data "^2.3.2"
path-type "^3.0.0"
readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6:
readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@@ -5601,7 +5423,7 @@ rimraf@2.6.3:
dependencies:
glob "^7.1.3"
rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3:
rimraf@^2.5.4, rimraf@^2.6.3:
version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
@@ -5699,7 +5521,7 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
set-blocking@^2.0.0, set-blocking@~2.0.0:
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
@@ -5970,16 +5792,7 @@ string-length@^2.0.0:
astral-regex "^1.0.0"
strip-ansi "^4.0.0"
string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
dependencies:
code-point-at "^1.0.0"
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
"string-width@^1.0.2 || 2", string-width@^2.1.0:
string-width@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
@@ -6044,13 +5857,6 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
strip-ansi@^3.0.0, strip-ansi@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
dependencies:
ansi-regex "^2.0.0"
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
@@ -6082,7 +5888,7 @@ strip-eof@^1.0.0:
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
strip-json-comments@^2.0.0, strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
strip-json-comments@^2.0.0, strip-json-comments@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
@@ -6140,19 +5946,6 @@ taffydb@2.6.2:
resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.6.2.tgz#7cbcb64b5a141b6a2efc2c5d2c67b4e150b2a268"
integrity sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=
tar@^4.4.2:
version "4.4.13"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
dependencies:
chownr "^1.1.1"
fs-minipass "^1.2.5"
minipass "^2.8.6"
minizlib "^1.2.1"
mkdirp "^0.5.0"
safe-buffer "^5.1.2"
yallist "^3.0.3"
terser@^4.4.3:
version "4.6.10"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.10.tgz#90f5bd069ff456ddbc9503b18e52f9c493d3b7c2"
@@ -6683,13 +6476,6 @@ which@^1.2.9, which@^1.3.0:
dependencies:
isexe "^2.0.0"
wide-align@^1.1.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
dependencies:
string-width "^1.0.2 || 2"
window-size@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
@@ -6775,11 +6561,6 @@ yallist@^2.1.2:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
yallist@^3.0.0, yallist@^3.0.3:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
yargs-parser@^13.1.2:
version "13.1.2"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"