Compare commits

...

75 Commits

Author SHA1 Message Date
RiotRobot 82e9eefce6 v34.6.0 2024-09-24 12:37:19 +00:00
RiotRobot a13e0389db v34.6.0-rc.0 2024-09-18 13:31:13 +00:00
Hubert Chathi dbb4828eda Add crypto mode setting for invisible crypto, and apply it to decrypting events (#4407)
Adds a global "crypto mode" setting to the crypto API (only works with Rust crypto), and changes the decryption settings based on that.
2024-09-18 12:53:07 +01:00
Hugh Nimmo-Smith 414ac9d8cc Don't share full key history for RTC per-participant encryption (#4406)
* Don't share full key history for RTC per-participant encryption

Also record stats for how many keys have been sent/received and age of those received

* Update src/matrixrtc/MatrixRTCSession.ts

Co-authored-by: Robin <robin@robin.town>

* Add comment about why we track total age of events

---------

Co-authored-by: Robin <robin@robin.town>
2024-09-18 08:57:26 +00:00
Johannes Marbach 30058a4bdc Add ESLint rule to ensure .ts extensions on imports (#4409)
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2024-09-18 07:40:01 +00:00
Florian Duros fab9cab3df Move crypto/key_passphrase.ts to crypto-api/key-passphrase.ts (#4401)
* Move `crypto/key_passphrase.ts` to `crypto-api/key-passphrase.ts`

* Re-export `crypto-api/key-passphrase` into `crypto/key_passphrase.ts`

* Add doc

* Deprecate `MatrixClient.keyBackupKeyFromPassword`

* Move `keyFromAuthData` to `common-crypto/key-passphrase.ts`

* Fix faulty import

* Keep `keyFromPassphrase` in old crypto

* - Rename `deriveKey` into `deriveRecoveryKeyFromPassphrase`
- Call `deriveRecoveryKeyFromPassphrase` into `RustCrypto.createRecoveryKeyFromPassphrase` instead of using `keyFromPassphrase`

* Remove alternative in `keyBackupKeyFromPassword` deprecation.

* Add tests for `keyFromAuthData`

* Deprecate `keyFromAuthData`

* Review changes
2024-09-17 13:05:47 +00:00
Johannes Marbach 53b599f8fe Export membership types (#4405)
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2024-09-14 10:44:51 +00:00
Timo 17f6cc733e Fix redact sending over in embedded (widget) mode (#4398) 2024-09-13 16:07:44 +00:00
Johannes Marbach c8403f39aa Fix build failure in node.js example (#4394)
* Fix node.js example

Relates to: element-hq/element-web#26922
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>

* Update examples/node/app.js

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* Update examples/node/package.json

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* Move imports to the top of the file

---------

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2024-09-13 15:35:45 +00:00
Florian Duros 8cf5df73ee Move crypto/recoverykey.ts to crypto-api/recovery-key.ts (#4399)
* Move `crypto/recoverykey.ts` to `crypto-api/recovery-key.ts`

* Re-export `crypto-api/recovery-key` into `crypto/recoverykey`

* Add a bit of doc

* Deprecate `MatrixClient.isValidRecoveryKey` and `MatrixClient.keyBackupKeyFromRecoveryKey`

* Import `index.ts` directly

* Update `recovery-key.ts` doc

* Add tests for `decodeRecoveryKey`

* Move `recovery-key.spec.ts` file
2024-09-13 13:26:30 +00:00
Florian Duros febe27ddcc Replace matrix.org/matrix-react-sdk by element-hq/matrix-react-sdk (#4404) 2024-09-13 13:03:31 +00:00
Hubert Chathi 7987ce76ec Bump dependency on rust-sdk-crypto-wasm to v8.0.0 (#4396) 2024-09-13 10:22:33 +01:00
Richard van der Hoff 60cedf2fdb Improve documentation on {encrypt,decrypt}AES (#4397) 2024-09-11 14:13:12 +00:00
Robin ed44514974 Expose the event ID of a call membership (#4395)
This is in line with the other information we're already exposing, such as the event's sender and timestamp. We want this in order to play around with adding reactions to the membership event.
2024-09-10 20:15:07 +00:00
RiotRobot 9f8c1ee953 Merge branch 'master' into develop 2024-09-10 12:31:32 +00:00
RiotRobot 593a57fc2b v34.5.0 2024-09-10 12:30:54 +00:00
Will Hunt e8128d34a1 MSC4133 - Extended profiles (#4391)
* Add MSC4133 functionality.

* Add MSC4133 capability.

* Tidy

* Add tests for extended profiles.

* improve docs

* undefined

* Add a prefix function to reduce reptitiveness

* Add a docstring
2024-09-09 12:06:38 +00:00
Richard van der Hoff ba7bd06295 Element-R: Mark unsupported MatrixClient methods as deprecated (#4389)
Slap a `@deprecated` tag on all the MatrixClient methods which no longer work
with rust crypto.
2024-09-05 13:37:08 +00:00
Richard van der Hoff e4db6008b8 Minor improvements to logging in device verification (#4390)
A grab-bag of small logging improvements in the Rust crypto wrapper.
2024-09-05 10:22:54 +00:00
Johannes Marbach 52f35409ec Stabilise MSC4156 (#4381)
* Stabilise MSC4156

Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>

* Add deprecation comments

* Add minimum Matrix version

---------

Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2024-09-04 12:01:33 +00:00
renovate[bot] f50aab37c3 Update babel monorepo (#4387)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 21:29:21 +00:00
renovate[bot] df0f817f83 Update dependency typedoc-plugin-mdn-links to v3.2.10 (#4386)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 21:20:01 +00:00
renovate[bot] 7efa6352f8 Update shogo82148/actions-upload-release-asset digest to aac270e (#4385)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 20:32:50 +00:00
renovate[bot] f74614705e Update mheap/github-action-required-labels digest to d25134c (#4384)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 20:32:29 +00:00
renovate[bot] 169e8f8613 Update dependency @types/node to v18.19.47 (#4383)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 15:22:51 +00:00
renovate[bot] f2f77bd1f7 Update all non-major dependencies (#4382)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 13:51:09 +00:00
RiotRobot 5a1c70ad19 v34.5.0-rc.0 2024-09-03 12:31:16 +00:00
David Baker 27cb16ffe4 Merge pull request #4379 from matrix-org/renovate/matrix-org
Update dependency matrix-widget-api to v1.9.0
2024-08-30 17:05:06 +01:00
renovate[bot] 9be0b3e701 Update dependency matrix-widget-api to v1.9.0 2024-08-30 15:54:06 +00:00
Johannes Marbach 05ba27f36b Change imports for Node.js compatibility (#4377)
* Change imports for Node.js compatibility

Fixes: #4287
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>

* Run prettier

* Run prettier (again)

* Add comment

* Update babel.config.cjs

---------

Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2024-08-29 15:56:32 +00:00
David Baker e6acfdf275 Rename merge release notes script to cjs (#4361)
and update the corresponding workflow file
2024-08-28 10:40:16 +00:00
RiotRobot 2a6612c73a Merge branch 'master' into develop 2024-08-27 12:38:13 +00:00
RiotRobot 2f8b05b0da v34.4.0 2024-08-27 12:37:46 +00:00
Hugh Nimmo-Smith fe984ede6e Use prefixed loggers for MatrixRTC (#4378) 2024-08-23 19:04:22 +00:00
Richard van der Hoff 3f74b9a0cc Deprecate unused CryptoCallbacks (#4376) 2024-08-23 14:00:27 +00:00
David Baker 802b996b10 Set web team as codeowner for the scripts dir (#4354)
The scripts in here are used in the release, and from the develop
branch too (because it's the main branch and github actions does this)
so it's critical for the release process.
2024-08-23 12:46:27 +00:00
renovate[bot] 8d44f9d665 Update dependency eslint-plugin-unicorn to v55 (#4371)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-22 12:32:26 +00:00
renovate[bot] a72a1b294a Update dependency eslint-plugin-jsdoc to v50 (#4370)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-22 11:22:35 +00:00
renovate[bot] ab5f32f984 Update typedoc (#4367)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-22 10:46:01 +00:00
renovate[bot] 6a21d812ab Update typescript-eslint monorepo to v7.18.0 (#4368)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-22 10:41:47 +00:00
Richard van der Hoff ee94e93354 set entrypoints to use ./lib rather than ./src (#4357)
Currently, we replace the entrypoints in package.json during the release
cycle. I think. historically, this was done to make matrix-react-sdk and
element-web development easier, but neither of those projects actually use
these entrypoints (instead they import from `src`).

Accordingly, I think the switcheroo is unnecessary; furthermore it causes a
whole bunch of confusion by making the development environment different from
the release environment, and it complicates our CI and release process.

In short, the switcheroo has to die.
2024-08-21 18:31:05 +00:00
renovate[bot] 31c4786a96 Update dependency fetch-mock to v11 (#4369)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-21 16:14:39 +00:00
renovate[bot] 17b6e59819 Update shogo82148/actions-upload-release-asset digest to 6d4fd50 (#4366)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-21 15:47:42 +00:00
renovate[bot] 42510022a1 Update dependency typescript to v5.5.4 (#4365)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-21 15:08:24 +00:00
renovate[bot] 5f0978ac3f Update babel monorepo (#4363)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-21 14:41:16 +00:00
renovate[bot] cd6787e0ac Update dependency @types/node to v18.19.44 (#4364)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-21 14:22:08 +00:00
renovate[bot] 03baa3e358 Update all non-major dependencies (#4362)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-21 13:51:57 +00:00
RiotRobot 658563e2a7 v34.4.0-rc.1 2024-08-21 12:56:47 +00:00
David Baker 26d3033b17 Merge pull request #4358 from matrix-org/backport-4356-to-staging
[Backport staging] Rename slowReporter to reflect that it is CommonJS
2024-08-21 13:01:49 +01:00
David Baker a4bd7dc7d7 Merge branch 'staging' into backport-4356-to-staging 2024-08-21 12:53:16 +01:00
David Baker 1f48544b38 Merge pull request #4359 from matrix-org/dbkr/backport_4355
Move `type:module` declaration into package.json. (Backport 4355)
2024-08-21 12:46:40 +01:00
Richard van der Hoff 7ef4062f59 Move type:module declaration into package.json. (#4355)
* Rename `switch_package_to_release.js` to `.cjs`

Slightly surprisingly, the symlink is enough to make `node
switch_package_to_release.js` work.

* Rename .eslintrc.js to .cjs

Again, declare this as commonjs

* Move `type:module` declaration into package.json.

matrix-js-sdk is built into ECMAScript modules, and we should declare it as
such. See https://nodejs.org/api/packages.html#type. Failure to do so causes
problems for javascript projects attempting to build against matrix-js-sdk: see https://github.com/matrix-org/matrix-js-sdk/issues/4347.

Previously, we did this as part of the package.json switcheroo, but that is
unnecessarily fragile.

matrix-react-sdk, element-web, etc are unaffected by this, because they use the
typescript files directly, by importing `matrix-js-sdk/src/...`.
2024-08-21 12:04:59 +01:00
Richard van der Hoff 968bc51a35 Rename slowReporter to reflect that it is CommonJS (#4356)
Fix tests failing as a result of
https://github.com/matrix-org/matrix-js-sdk/pull/4355. This wasn't detected in
CI because the slowReporter is only enabled when building against develop.

(cherry picked from commit d413f5042e)
2024-08-21 09:39:52 +00:00
Richard van der Hoff d413f5042e Rename slowReporter to reflect that it is CommonJS (#4356)
Fix tests failing as a result of
https://github.com/matrix-org/matrix-js-sdk/pull/4355. This wasn't detected in
CI because the slowReporter is only enabled when building against develop.
2024-08-20 20:48:27 +00:00
Richard van der Hoff b8e8b14375 Move type:module declaration into package.json. (#4355)
* Rename `switch_package_to_release.js` to `.cjs`

Slightly surprisingly, the symlink is enough to make `node
switch_package_to_release.js` work.

* Rename .eslintrc.js to .cjs

Again, declare this as commonjs

* Move `type:module` declaration into package.json.

matrix-js-sdk is built into ECMAScript modules, and we should declare it as
such. See https://nodejs.org/api/packages.html#type. Failure to do so causes
problems for javascript projects attempting to build against matrix-js-sdk: see https://github.com/matrix-org/matrix-js-sdk/issues/4347.

Previously, we did this as part of the package.json switcheroo, but that is
unnecessarily fragile.

matrix-react-sdk, element-web, etc are unaffected by this, because they use the
typescript files directly, by importing `matrix-js-sdk/src/...`.
2024-08-20 16:23:03 +00:00
RiotRobot 43e58871de v34.4.0-rc.0 2024-08-20 13:40:49 +00:00
Richard van der Hoff 2544c14032 Unrevert prerelease fix, and fix release error (#4353)
* Reapply "Add "type" = "module" to ensure it is present (#4350)" (#4352)

This reverts commit 8214fd7156.

* Mark prettier config file as CommonJS

I *think* this will fix a problem with the release process in which we saw an
error:

```
Error:  Invalid configuration for file "/home/runner/work/matrix-js-sdk/matrix-js-sdk/package.json":
Error:  module is not defined in ES module scope
Error:  This file is being treated as an ES module because it has a '.js' file extension and '/home/runner/work/matrix-js-sdk/matrix-js-sdk/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
```
2024-08-20 13:16:44 +00:00
RiotRobot 8d19782c57 Merge branch 'master' into develop 2024-08-20 11:30:12 +00:00
RiotRobot 340bbe1a8f v34.3.1 2024-08-20 11:29:48 +00:00
David Baker 8214fd7156 Revert "Add "type" = "module" to ensure it is present (#4350)" (#4352)
This reverts commit d6080398db.
2024-08-20 11:10:15 +00:00
David Baker a0efed8b88 Merge commit from fork
Detect cycles when looking for predecessor rooms
2024-08-20 11:27:28 +01:00
Hugh Nimmo-Smith c408c0d1d5 Retry event decryption failures on first failure (#4346)
* Retry event decryption failures on first failure

* Suggestion from code review

Co-authored-by: Andrew Ferrazzutti <andrewf@element.io>

---------

Co-authored-by: Andrew Ferrazzutti <andrewf@element.io>
2024-08-19 13:35:45 +00:00
BLCK d6080398db Add "type" = "module" to ensure it is present (#4350) 2024-08-15 14:50:05 +00:00
Hugh Nimmo-Smith 467908703b Don't attempt to adjust for clock skews when calculating group call membership expiry (#4340)
* Use origin server timestamp for calculating group call membership expiry

* Fix tests

* Docs

* Refactor comments to reflect that the logic hasn't changed

* Make comment maintainable

* Fix up merge

* Fix test
2024-08-15 09:11:06 +00:00
Hugh Nimmo-Smith 87eddaf51a Handle MatrixRTC encryption keys arriving out of order (#4345)
* Handle MatrixRTC encryption keys arriving out of order

* Apply suggestions from code review

Co-authored-by: Andrew Ferrazzutti <andrewf@element.io>

* Suggestion from code review

---------

Co-authored-by: Andrew Ferrazzutti <andrewf@element.io>
2024-08-15 07:58:36 +00:00
Hugh Nimmo-Smith c65ef03567 Resend MatrixRTC encryption keys if a membership has changed (#4343)
* Resend MatrixRTC encryption keys if a membership has changed

* JSDoc

* Update src/matrixrtc/MatrixRTCSession.ts

Co-authored-by: Andrew Ferrazzutti <andrewf@element.io>

* Add note about using Set. symmetricDifference() when available

* Always store latest fingerprints

Should reduce unnecessary retransmits

* Refactor

---------

Co-authored-by: Andrew Ferrazzutti <andrewf@element.io>
2024-08-14 14:08:52 +00:00
RiotRobot 78cbf7cd28 Resetting package fields for development 2024-08-13 12:06:18 +00:00
RiotRobot dc7c1a4fef Merge branch 'master' into develop 2024-08-13 12:06:17 +00:00
RiotRobot 1ae0c2f3ee v34.3.0 2024-08-13 12:05:45 +00:00
Hugh Nimmo-Smith affaa95fb4 delayBeforeuse => delayBeforeUse for consistency (#4344) 2024-08-09 14:34:13 +00:00
Andrew Ferrazzutti 9176d3a671 Use non-legacy calls if any are found (#4337)
Akin to how legacy call events should be sent in rooms where there is
any ongoing legacy call, send non-legacy events in rooms where there are
only non-legacy calls; else fall back to the config preference.
2024-08-07 15:44:44 +00:00
RiotRobot de50129a53 v34.3.0-rc.1 2024-08-06 12:26:55 +00:00
Michael Telatynski 5568dfdd41 Move olm to dependencies as its types are needed downstream
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-08-06 13:25:55 +01:00
David Baker e0ef467d7d break instead of return 2024-07-29 13:55:08 +01:00
David Baker 79299891fd Detect cycles when looking for predecessor rooms 2024-07-29 11:14:58 +01:00
226 changed files with 4011 additions and 2280 deletions
+9 -1
View File
@@ -1,5 +1,5 @@
module.exports = {
plugins: ["matrix-org", "import", "jsdoc"],
plugins: ["matrix-org", "import", "jsdoc", "node"],
extends: ["plugin:matrix-org/babel", "plugin:matrix-org/jest", "plugin:import/typescript"],
parserOptions: {
project: ["./tsconfig.json"],
@@ -128,6 +128,14 @@ module.exports = {
// These need a bit more work before we can enable
// "jsdoc/check-param-names": "error",
// "jsdoc/check-indentation": "error",
// Ensure .ts extension on imports outside of tests
"node/file-extension-in-import": [
"error",
"always",
{
tryExtensions: [".ts"],
},
],
},
},
{
+1
View File
@@ -2,6 +2,7 @@
/.github/workflows/** @matrix-org/element-web-team
/package.json @matrix-org/element-web-team
/yarn.lock @matrix-org/element-web-team
/scripts/** @matrix-org/element-web-team
/src/webrtc @matrix-org/element-call-reviewers
/src/matrixrtc @matrix-org/element-call-reviewers
/spec/*/webrtc @matrix-org/element-call-reviewers
@@ -22,7 +22,7 @@ runs:
- name: Upload tarball signature
if: ${{ inputs.upload-url }}
uses: shogo82148/actions-upload-release-asset@8f032eff0255912cc9c8455797fd6d72f25c7ab7 # v1
uses: shogo82148/actions-upload-release-asset@aac270e08f6b4547ada0b3800f88e1eb3ce9d400 # v1
with:
upload_url: ${{ inputs.upload-url }}
asset_path: ${{ env.VERSION }}.tar.gz.asc
@@ -29,13 +29,13 @@ runs:
- name: Upload asset signatures
if: inputs.gpg-fingerprint
uses: shogo82148/actions-upload-release-asset@8f032eff0255912cc9c8455797fd6d72f25c7ab7 # v1
uses: shogo82148/actions-upload-release-asset@aac270e08f6b4547ada0b3800f88e1eb3ce9d400 # v1
with:
upload_url: ${{ inputs.upload-url }}
asset_path: ${{ inputs.asset-path }}.asc
- name: Upload assets
uses: shogo82148/actions-upload-release-asset@8f032eff0255912cc9c8455797fd6d72f25c7ab7 # v1
uses: shogo82148/actions-upload-release-asset@aac270e08f6b4547ada0b3800f88e1eb3ce9d400 # v1
with:
upload_url: ${{ inputs.upload-url }}
asset_path: ${{ inputs.asset-path }}
@@ -21,14 +21,14 @@ concurrency:
jobs:
playwright:
name: Playwright
uses: matrix-org/matrix-react-sdk/.github/workflows/end-to-end-tests.yaml@develop
uses: element-hq/matrix-react-sdk/.github/workflows/end-to-end-tests.yaml@develop
permissions:
actions: read
issues: read
pull-requests: read
with:
matrix-js-sdk-sha: ${{ github.sha }}
react-sdk-repository: matrix-org/matrix-react-sdk
react-sdk-repository: element-hq/matrix-react-sdk
# We only want to run the playwright tests on merge queue to prevent regressions
# from creeping in. They take a long time to run and consume multiple concurrent runners.
skip: ${{ github.event_name != 'merge_group' }}
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
include:
- repo: element-hq/element-web
event: element-web-notify
- repo: matrix-org/matrix-react-sdk
- repo: element-hq/matrix-react-sdk
event: upstream-sdk-notify
runs-on: ubuntu-latest
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
name: Preview Changelog
runs-on: ubuntu-latest
steps:
- uses: mheap/github-action-required-labels@5847eef68201219cf0a4643ea7be61e77837bbce # v5
- uses: mheap/github-action-required-labels@d25134c992b943fb6ad00c25ea00eb5988c0a9dd # v5
if: github.event_name != 'merge_group'
with:
labels: |
@@ -56,7 +56,7 @@ jobs:
script: |
const { RELEASE_ID: releaseId, DEPENDENCY, VERSION } = process.env;
const { owner, repo } = context.repo;
const script = require("./.action-repo/scripts/release/merge-release-notes.js");
const script = require("./.action-repo/scripts/release/merge-release-notes.cjs");
let deps = [];
if (DEPENDENCY.includes("/")) {
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
with:
final: ${{ inputs.mode == 'final' }}
npm: ${{ inputs.npm }}
downstreams: '["matrix-org/matrix-react-sdk", "element-hq/element-web"]'
downstreams: '["element-hq/matrix-react-sdk", "element-hq/element-web"]'
docs:
name: Publish Documentation
+1 -1
View File
@@ -76,7 +76,7 @@ jobs:
matrix-react-sdk:
name: Downstream test matrix-react-sdk
if: github.event_name == 'merge_group'
uses: matrix-org/matrix-react-sdk/.github/workflows/tests.yml@develop
uses: element-hq/matrix-react-sdk/.github/workflows/tests.yml@develop
with:
disable_coverage: true
matrix-js-sdk-sha: ${{ github.sha }}
View File
+59
View File
@@ -1,3 +1,62 @@
Changes in [34.6.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.6.0) (2024-09-24)
==================================================================================================
## 🦖 Deprecations
* Element-R: Mark unsupported MatrixClient methods as deprecated ([#4389](https://github.com/matrix-org/matrix-js-sdk/pull/4389)). Contributed by @richvdh.
## ✨ Features
* Add crypto mode setting for invisible crypto, and apply it to decrypting events ([#4407](https://github.com/matrix-org/matrix-js-sdk/pull/4407)). Contributed by @uhoreg.
* Don't share full key history for RTC per-participant encryption ([#4406](https://github.com/matrix-org/matrix-js-sdk/pull/4406)). Contributed by @hughns.
* Export membership types ([#4405](https://github.com/matrix-org/matrix-js-sdk/pull/4405)). Contributed by @Johennes.
* Fix sending redacts in embedded (widget) mode ([#4398](https://github.com/matrix-org/matrix-js-sdk/pull/4398)). Contributed by @toger5.
* Expose the event ID of a call membership ([#4395](https://github.com/matrix-org/matrix-js-sdk/pull/4395)). Contributed by @robintown.
* MSC4133 - Extended profiles ([#4391](https://github.com/matrix-org/matrix-js-sdk/pull/4391)). Contributed by @Half-Shot.
Changes in [34.5.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.5.0) (2024-09-10)
==================================================================================================
## 🦖 Deprecations
* Deprecate unused callback hooks `CryptoCallbacks.onSecretRequested` and `CryptoCallbacks.getDehydrationKey` ([#4376](https://github.com/matrix-org/matrix-js-sdk/pull/4376)). Contributed by @richvdh.
Changes in [34.4.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.4.0) (2024-08-27)
==================================================================================================
## ✨ Features
* Use non-legacy calls if any are found ([#4337](https://github.com/matrix-org/matrix-js-sdk/pull/4337)). Contributed by @AndrewFerr.
## 🐛 Bug Fixes
* Retry event decryption failures on first failure ([#4346](https://github.com/matrix-org/matrix-js-sdk/pull/4346)). Contributed by @hughns.
* Ensure "type" = "module" ES declaration in pre-release.sh ([#4350](https://github.com/matrix-org/matrix-js-sdk/pull/4350)). Contributed by @BLCK-B.
* Handle MatrixRTC encryption keys arriving out of order ([#4345](https://github.com/matrix-org/matrix-js-sdk/pull/4345)). Contributed by @hughns.
* Resend MatrixRTC encryption keys if a membership has changed ([#4343](https://github.com/matrix-org/matrix-js-sdk/pull/4343)). Contributed by @hughns.
Changes in [34.3.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.3.1) (2024-08-20)
==================================================================================================
# Security
- Fixes for [CVE-2024-42369](https://nvd.nist.gov/vuln/detail/CVE-2024-42369) / [GHSA-vhr5-g3pm-49fm](https://github.com/matrix-org/matrix-js-sdk/security/advisories/GHSA-vhr5-g3pm-49fm).
Changes in [34.3.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.3.0) (2024-08-13)
==================================================================================================
## ✨ Features
* Bump matrix-widget-api ([#4336](https://github.com/matrix-org/matrix-js-sdk/pull/4336)). Contributed by @AndrewFerr.
* Also check for MSC3757 for session state keys ([#4334](https://github.com/matrix-org/matrix-js-sdk/pull/4334)). Contributed by @AndrewFerr.
* Support Futures via widgets ([#4311](https://github.com/matrix-org/matrix-js-sdk/pull/4311)). Contributed by @AndrewFerr.
* Support MSC4140: Delayed events (Futures) ([#4294](https://github.com/matrix-org/matrix-js-sdk/pull/4294)). Contributed by @AndrewFerr.
* Handle late-arriving `m.room_key.withheld` messages ([#4310](https://github.com/matrix-org/matrix-js-sdk/pull/4310)). Contributed by @richvdh.
* Be specific about what is considered a MSC4143 call member event. ([#4328](https://github.com/matrix-org/matrix-js-sdk/pull/4328)). Contributed by @toger5.
* Add index.ts for matrixrtc module ([#4314](https://github.com/matrix-org/matrix-js-sdk/pull/4314)). Contributed by @toger5.
## 🐛 Bug Fixes
* Fix hashed ID server lookups with no Olm ([#4333](https://github.com/matrix-org/matrix-js-sdk/pull/4333)). Contributed by @dbkr.
Changes in [34.2.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.2.0) (2024-07-30)
==================================================================================================
## 🐛 Bug Fixes
+10 -1
View File
@@ -14,7 +14,16 @@ module.exports = {
modules: process.env.NODE_ENV === "test" ? "commonjs" : false,
},
],
"@babel/preset-typescript",
[
"@babel/preset-typescript",
{
// When using the transpiled javascript in `lib`, Node.js requires `.js` extensions on any `import`
// specifiers. However, Jest uses the TS source (via babel) and fails to resolve the `.js` names.
// To resolve this,we use the `.ts` names in the source, and rewrite the `import` specifiers to use
// `.js` during transpilation, *except* when we are targetting Jest.
rewriteImportExtensions: process.env.NODE_ENV !== "test",
},
],
],
plugins: [
"@babel/plugin-transform-numeric-separator",
+16 -11
View File
@@ -1,9 +1,15 @@
import clc from "cli-color";
import fs from "fs";
import readline from "readline";
import sdk, { ClientEvent, EventType, MsgType, RoomEvent } from "matrix-js-sdk";
import { KnownMembership } from "matrix-js-sdk/lib/@types/membership.js";
var myHomeServer = "http://localhost:8008";
var myUserId = "@example:localhost";
var myAccessToken = "QGV4YW1wbGU6bG9jYWxob3N0.qPEvLuYfNBjxikiCjP";
var sdk = require("matrix-js-sdk");
var clc = require("cli-color");
var matrixClient = sdk.createClient({
baseUrl: "http://localhost:8008",
baseUrl: myHomeServer,
accessToken: myAccessToken,
userId: myUserId,
});
@@ -15,7 +21,6 @@ var numMessagesToShow = 20;
// Reading from stdin
var CLEAR_CONSOLE = "\x1B[2J";
var readline = require("readline");
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
@@ -97,7 +102,7 @@ rl.on("line", function (line) {
})
.then(function (url) {
var content = {
msgtype: "m.file",
msgtype: MsgType.File,
body: filename,
url: JSON.parse(url).content_uri,
};
@@ -138,7 +143,7 @@ rl.on("line", function (line) {
// ==== END User input
// show the room list after syncing.
matrixClient.on("sync", function (state, prevState, data) {
matrixClient.on(ClientEvent.Sync, function (state, prevState, data) {
switch (state) {
case "PREPARED":
setRoomList();
@@ -149,7 +154,7 @@ matrixClient.on("sync", function (state, prevState, data) {
}
});
matrixClient.on("Room", function () {
matrixClient.on(ClientEvent.Room, function () {
setRoomList();
if (!viewingRoom) {
printRoomList();
@@ -158,7 +163,7 @@ matrixClient.on("Room", function () {
});
// print incoming messages.
matrixClient.on("Room.timeline", function (event, room, toStartOfTimeline) {
matrixClient.on(RoomEvent.Timeline, function (event, room, toStartOfTimeline) {
if (toStartOfTimeline) {
return; // don't print paginated results
}
@@ -305,7 +310,7 @@ function printRoomInfo(room) {
print(eTypeHeader + sendHeader + contentHeader);
print(new Array(100).join("-"));
eventMap.keys().forEach(function (eventType) {
if (eventType === "m.room.member") {
if (eventType === EventType.RoomMember) {
return;
} // use /members instead.
var eventEventMap = eventMap.get(eventType);
@@ -343,7 +348,7 @@ function printLine(event) {
name = name.slice(0, maxNameWidth - 1) + "\u2026";
}
if (event.getType() === "m.room.message") {
if (event.getType() === EventType.RoomMessage) {
body = event.getContent().body;
} else if (event.isState()) {
var stateName = event.getType();
@@ -394,4 +399,4 @@ function fixWidth(str, len) {
return str;
}
matrixClient.startClient(numMessagesToShow); // messages for each room.
matrixClient.startClient({ initialSyncLimit: numMessagesToShow });
+2 -1
View File
@@ -5,8 +5,9 @@
"main": "app.js",
"author": "",
"license": "Apache 2.0",
"type": "module",
"dependencies": {
"cli-color": "^1.0.0",
"matrix-js-sdk": "^32.0.0"
"matrix-js-sdk": "^34.5.0"
}
}
+1 -1
View File
@@ -39,7 +39,7 @@ if (env["GITHUB_ACTIONS"] !== undefined) {
// if we're running against the develop branch, also enable the slow test reporter
if (env["GITHUB_REF"] == "refs/heads/develop") {
reporters.push("<rootDir>/spec/slowReporter.js");
reporters.push("<rootDir>/spec/slowReporter.cjs");
}
config.reporters = reporters;
}
+10 -13
View File
@@ -1,6 +1,6 @@
{
"name": "matrix-js-sdk",
"version": "34.3.0-rc.0",
"version": "34.6.0",
"description": "Matrix Client-Server SDK for Javascript",
"engines": {
"node": ">=20.0.0"
@@ -31,13 +31,10 @@
"keywords": [
"matrix-org"
],
"type": "module",
"main": "./lib/index.js",
"browser": "./lib/browser-index.js",
"matrix_src_main": "./src/index.ts",
"matrix_src_browser": "./src/browser-index.ts",
"matrix_lib_main": "./lib/index.js",
"matrix_lib_browser": "./lib/browser-index.js",
"matrix_lib_typings": "./lib/index.d.ts",
"typings": "./lib/index.d.ts",
"author": "matrix.org",
"license": "Apache-2.0",
"files": [
@@ -53,7 +50,8 @@
],
"dependencies": {
"@babel/runtime": "^7.12.5",
"@matrix-org/matrix-sdk-crypto-wasm": "^7.0.0",
"@matrix-org/matrix-sdk-crypto-wasm": "^9.0.0",
"@matrix-org/olm": "3.2.15",
"another-json": "^0.2.0",
"bs58": "^6.0.0",
"content-type": "^1.0.4",
@@ -82,7 +80,6 @@
"@babel/preset-env": "^7.12.11",
"@babel/preset-typescript": "^7.12.7",
"@casualbot/jest-sonar-reporter": "2.2.7",
"@matrix-org/olm": "3.2.15",
"@peculiar/webcrypto": "^1.4.5",
"@types/bs58": "^4.0.1",
"@types/content-type": "^1.1.5",
@@ -101,12 +98,13 @@
"eslint-import-resolver-typescript": "^3.5.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^28.0.0",
"eslint-plugin-jsdoc": "^48.0.0",
"eslint-plugin-jsdoc": "^50.0.0",
"eslint-plugin-matrix-org": "^1.0.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-tsdoc": "^0.3.0",
"eslint-plugin-unicorn": "^54.0.0",
"eslint-plugin-unicorn": "^55.0.0",
"fake-indexeddb": "^5.0.2",
"fetch-mock": "10.1.0",
"fetch-mock": "11.1.3",
"fetch-mock-jest": "^1.5.1",
"husky": "^9.0.0",
"jest": "^29.0.0",
@@ -130,6 +128,5 @@
"outputDirectory": "coverage",
"outputName": "jest-sonar-report.xml",
"relativePaths": true
},
"typings": "./lib/index.d.ts"
}
}
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env node
const fsProm = require("fs/promises");
const PKGJSON = "package.json";
async function main() {
const pkgJson = JSON.parse(await fsProm.readFile(PKGJSON, "utf8"));
for (const field of ["main", "typings"]) {
if (pkgJson["matrix_lib_" + field] !== undefined) {
pkgJson[field] = pkgJson["matrix_lib_" + field];
}
}
await fsProm.writeFile(PKGJSON, JSON.stringify(pkgJson, null, 2));
}
main();
-22
View File
@@ -1,22 +0,0 @@
#!/usr/bin/env node
const fsProm = require("fs/promises");
const PKGJSON = "package.json";
async function main() {
const pkgJson = JSON.parse(await fsProm.readFile(PKGJSON, "utf8"));
for (const field of ["main", "typings"]) {
if (pkgJson["matrix_lib_" + field] !== undefined) {
pkgJson[field] = pkgJson["matrix_lib_" + field];
}
}
// matrix-js-sdk is built into ECMAScript modules. Make sure we declare it as such.
// See https://nodejs.org/api/packages.html#type.
pkgJson["type"] = "module";
await fsProm.writeFile(PKGJSON, JSON.stringify(pkgJson, null, 2));
}
main();
+1
View File
@@ -0,0 +1 @@
switch_package_to_release.cjs
+82
View File
@@ -82,6 +82,7 @@ import { SecretStorageKeyDescription } from "../../../src/secret-storage";
import {
CrossSigningKey,
CryptoCallbacks,
CryptoMode,
DecryptionFailureCode,
EventShieldColour,
EventShieldReason,
@@ -746,6 +747,87 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
);
});
newBackendOnly(
"fails with an error when cross-signed sender is required but sender is not cross-signed",
async () => {
// This tests that a message will not be decrypted if the sender
// is not sufficiently trusted according to the selected crypto
// mode.
//
// This test is almost the same as the "Alice receives a megolm
// message" test, with the main difference that we set the
// crypto mode.
expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} });
// Start by using Invisible crypto mode
aliceClient.getCrypto()!.setCryptoMode(CryptoMode.Invisible);
await startClientAndAwaitFirstSync();
const p2pSession = await createOlmSession(testOlmAccount, keyReceiver);
const groupSession = new Olm.OutboundGroupSession();
groupSession.create();
// make the room_key event
const roomKeyEncrypted = encryptGroupSessionKey({
recipient: aliceClient.getUserId()!,
recipientCurve25519Key: keyReceiver.getDeviceKey(),
recipientEd25519Key: keyReceiver.getSigningKey(),
olmAccount: testOlmAccount,
p2pSession: p2pSession,
groupSession: groupSession,
room_id: ROOM_ID,
});
// encrypt a message with the group session
const messageEncrypted = encryptMegolmEvent({
senderKey: testSenderKey,
groupSession: groupSession,
room_id: ROOM_ID,
});
// Alice gets both the events in a single sync
const syncResponse = {
next_batch: 1,
to_device: {
events: [roomKeyEncrypted],
},
rooms: {
join: {
[ROOM_ID]: { timeline: { events: [messageEncrypted] } },
},
},
};
syncResponder.sendOrQueueSyncResponse(syncResponse);
await syncPromise(aliceClient);
const room = aliceClient.getRoom(ROOM_ID)!;
const event = room.getLiveTimeline().getEvents()[0];
expect(event.isEncrypted()).toBe(true);
// it probably won't be decrypted yet, because it takes a while to process the olm keys
const decryptedEvent = await testUtils.awaitDecryption(event);
// It will error as an unknown device because we haven't fetched
// the sender's device keys.
expect(decryptedEvent.decryptionFailureReason).toEqual(DecryptionFailureCode.UNKNOWN_SENDER_DEVICE);
// Next, try decrypting in transition mode, which should also
// fail for the same reason
aliceClient.getCrypto()!.setCryptoMode(CryptoMode.Transition);
await event.attemptDecryption(aliceClient["cryptoBackend"]!);
expect(decryptedEvent.decryptionFailureReason).toEqual(DecryptionFailureCode.UNKNOWN_SENDER_DEVICE);
// Decrypting in legacy mode should succeed since it doesn't
// care about device trust.
aliceClient.getCrypto()!.setCryptoMode(CryptoMode.Legacy);
await event.attemptDecryption(aliceClient["cryptoBackend"]!);
expect(decryptedEvent.decryptionFailureReason).toEqual(null);
},
);
it("Decryption fails with Unable to decrypt for other errors", async () => {
expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} });
await startClientAndAwaitFirstSync();
@@ -0,0 +1,38 @@
/*
* Copyright 2024 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { keyFromAuthData } from "../../../src/common-crypto/key-passphrase.ts";
describe("key-passphrase", () => {
describe("keyFromAuthData", () => {
it("should throw an error if salt or iterations are missing", async () => {
// missing salt
expect(() => keyFromAuthData({ private_key_iterations: 5 }, "passphrase")).toThrow(
"Salt and/or iterations not found: this backup cannot be restored with a passphrase",
);
// missing iterations
expect(() => keyFromAuthData({ private_key_salt: "salt" }, "passphrase")).toThrow(
"Salt and/or iterations not found: this backup cannot be restored with a passphrase",
);
});
it("should derive key from auth data", async () => {
const key = await keyFromAuthData({ private_key_salt: "salt", private_key_iterations: 5 }, "passphrase");
expect(key).toBeDefined();
});
});
});
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright 2024 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { decodeRecoveryKey, encodeRecoveryKey } from "../../../src/crypto-api";
describe("recovery key", () => {
describe("decodeRecoveryKey", () => {
it("should thrown an incorrect length error", () => {
const key = [0, 1];
const encodedKey = encodeRecoveryKey(key)!;
expect(() => decodeRecoveryKey(encodedKey)).toThrow("Incorrect length");
});
it("should thrown an incorrect parity", () => {
const key = Array.from({ length: 32 }, (_, i) => i);
let encodedKey = encodeRecoveryKey(key)!;
// Mutate the encoded key to have incorrect parity
encodedKey = encodedKey.replace("EsSz", "EsSZ");
expect(() => decodeRecoveryKey(encodedKey!)).toThrow("Incorrect parity");
});
it("should decode a valid encoded key", () => {
const key = Array.from({ length: 32 }, (_, i) => i);
const encodedKey = encodeRecoveryKey(key)!;
expect(decodeRecoveryKey(encodedKey)).toEqual(new Uint8Array(key));
});
});
});
+118
View File
@@ -1029,6 +1029,124 @@ describe("MatrixClient", function () {
});
});
describe("extended profiles", () => {
const unstableMSC4133Prefix = `${ClientPrefix.Unstable}/uk.tcpip.msc4133`;
const userId = "@profile_user:example.org";
beforeEach(() => {
unstableFeatures["uk.tcpip.msc4133"] = true;
});
it("throws when unsupported by server", async () => {
unstableFeatures["uk.tcpip.msc4133"] = false;
const errorMessage = "Server does not support extended profiles";
await expect(client.doesServerSupportExtendedProfiles()).resolves.toEqual(false);
await expect(client.getExtendedProfile(userId)).rejects.toThrow(errorMessage);
await expect(client.getExtendedProfileProperty(userId, "test_key")).rejects.toThrow(errorMessage);
await expect(client.setExtendedProfileProperty("test_key", "foo")).rejects.toThrow(errorMessage);
await expect(client.deleteExtendedProfileProperty("test_key")).rejects.toThrow(errorMessage);
await expect(client.patchExtendedProfile({ test_key: "foo" })).rejects.toThrow(errorMessage);
await expect(client.setExtendedProfile({ test_key: "foo" })).rejects.toThrow(errorMessage);
});
it("can fetch a extended user profile", async () => {
const testProfile = {
test_key: "foo",
};
httpLookups = [
{
method: "GET",
prefix: unstableMSC4133Prefix,
path: "/profile/" + encodeURIComponent(userId),
data: testProfile,
},
];
await expect(client.getExtendedProfile(userId)).resolves.toEqual(testProfile);
expect(httpLookups).toHaveLength(0);
});
it("can fetch a property from a extended user profile", async () => {
const testProfile = {
test_key: "foo",
};
httpLookups = [
{
method: "GET",
prefix: unstableMSC4133Prefix,
path: "/profile/" + encodeURIComponent(userId) + "/test_key",
data: testProfile,
},
];
await expect(client.getExtendedProfileProperty(userId, "test_key")).resolves.toEqual("foo");
expect(httpLookups).toHaveLength(0);
});
it("can set a property in our extended profile", async () => {
httpLookups = [
{
method: "PUT",
prefix: unstableMSC4133Prefix,
path: "/profile/" + encodeURIComponent(client.credentials.userId!) + "/test_key",
expectBody: {
test_key: "foo",
},
},
];
await expect(client.setExtendedProfileProperty("test_key", "foo")).resolves.toEqual(undefined);
expect(httpLookups).toHaveLength(0);
});
it("can delete a property in our extended profile", async () => {
httpLookups = [
{
method: "DELETE",
prefix: unstableMSC4133Prefix,
path: "/profile/" + encodeURIComponent(client.credentials.userId!) + "/test_key",
},
];
await expect(client.deleteExtendedProfileProperty("test_key")).resolves.toEqual(undefined);
expect(httpLookups).toHaveLength(0);
});
it("can patch our extended profile", async () => {
const testProfile = {
test_key: "foo",
};
const patchedProfile = {
existing: "key",
test_key: "foo",
};
httpLookups = [
{
method: "PATCH",
prefix: unstableMSC4133Prefix,
path: "/profile/" + encodeURIComponent(client.credentials.userId!),
data: patchedProfile,
expectBody: testProfile,
},
];
await expect(client.patchExtendedProfile(testProfile)).resolves.toEqual(patchedProfile);
});
it("can replace our extended profile", async () => {
const testProfile = {
test_key: "foo",
};
httpLookups = [
{
method: "PUT",
prefix: unstableMSC4133Prefix,
path: "/profile/" + encodeURIComponent(client.credentials.userId!),
data: testProfile,
expectBody: testProfile,
},
];
await expect(client.setExtendedProfile(testProfile)).resolves.toEqual(undefined);
});
});
it("should create (unstable) file trees", async () => {
const userId = "@test:example.org";
const roomId = "!room:example.org";
+9 -22
View File
@@ -98,20 +98,6 @@ describe("CallMembership", () => {
expect(membership.getAbsoluteExpiry()).toEqual(5000 + 1000);
});
it("considers memberships unexpired if local age low enough", () => {
const fakeEvent = makeMockEvent(1000);
fakeEvent.getLocalAge = jest.fn().mockReturnValue(3000);
const membership = new CallMembership(fakeEvent, membershipTemplate);
expect(membership.isExpired()).toEqual(false);
});
it("considers memberships expired when local age large", () => {
const fakeEvent = makeMockEvent(1000);
fakeEvent.localTimestamp = Date.now() - 6000;
const membership = new CallMembership(fakeEvent, membershipTemplate);
expect(membership.isExpired()).toEqual(true);
});
it("returns preferred foci", () => {
const fakeEvent = makeMockEvent();
const mockFocus = { type: "this_is_a_mock_focus" };
@@ -198,13 +184,6 @@ describe("CallMembership", () => {
beforeEach(() => {
// server origin timestamp for this event is 1000
fakeEvent = makeMockEvent(1000);
// our clock would have been at 2000 at the creation time (our clock at event receive time - age)
// (ie. the local clock is 1 second ahead of the servers' clocks)
fakeEvent.localTimestamp = 2000;
// for simplicity's sake, we say that the event's age is zero
fakeEvent.getLocalAge = jest.fn().mockReturnValue(0);
membership = new CallMembership(fakeEvent!, membershipTemplate);
jest.useFakeTimers();
@@ -215,6 +194,13 @@ describe("CallMembership", () => {
});
it("converts expiry time into local clock", () => {
// our clock would have been at 2000 at the creation time (our clock at event receive time - age)
// (ie. the local clock is 1 second ahead of the servers' clocks)
fakeEvent.localTimestamp = 2000;
// for simplicity's sake, we say that the event's age is zero
fakeEvent.getLocalAge = jest.fn().mockReturnValue(0);
// for sanity's sake, make sure the server-relative expiry time is what we expect
expect(membership.getAbsoluteExpiry()).toEqual(6000);
// therefore the expiry time converted to our clock should be 1 second later
@@ -223,7 +209,8 @@ describe("CallMembership", () => {
it("calculates time until expiry", () => {
jest.setSystemTime(2000);
expect(membership.getMsUntilExpiry()).toEqual(5000);
// should be using absolute expiry time
expect(membership.getMsUntilExpiry()).toEqual(4000);
});
});
});
+495 -28
View File
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { EventTimeline, EventType, MatrixClient, MatrixError, MatrixEvent, Room } from "../../../src";
import { encodeBase64, EventTimeline, EventType, MatrixClient, MatrixError, MatrixEvent, Room } from "../../../src";
import { KnownMembership } from "../../../src/@types/membership";
import {
CallMembershipData,
@@ -73,14 +73,17 @@ describe("MatrixRTCSession", () => {
});
it("ignores expired memberships events", () => {
jest.useFakeTimers();
const expiredMembership = Object.assign({}, membershipTemplate);
expiredMembership.expires = 1000;
expiredMembership.device_id = "EXPIRED";
const mockRoom = makeMockRoom([membershipTemplate, expiredMembership], 10000);
const mockRoom = makeMockRoom([membershipTemplate, expiredMembership]);
jest.advanceTimersByTime(2000);
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
expect(sess?.memberships.length).toEqual(1);
expect(sess?.memberships[0].deviceId).toEqual("AAAAAAA");
jest.useRealTimers();
});
it("ignores memberships events of members not in the room", () => {
@@ -91,12 +94,15 @@ describe("MatrixRTCSession", () => {
});
it("honours created_ts", () => {
jest.useFakeTimers();
jest.setSystemTime(500);
const expiredMembership = Object.assign({}, membershipTemplate);
expiredMembership.created_ts = 500;
expiredMembership.expires = 1000;
const mockRoom = makeMockRoom([expiredMembership]);
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
expect(sess?.memberships[0].getAbsoluteExpiry()).toEqual(1500);
jest.useRealTimers();
});
it("returns empty session if no membership events are present", () => {
@@ -304,6 +310,8 @@ describe("MatrixRTCSession", () => {
describe("getOldestMembership", () => {
it("returns the oldest membership event", () => {
jest.useFakeTimers();
jest.setSystemTime(4000);
const mockRoom = makeMockRoom([
Object.assign({}, membershipTemplate, { device_id: "foo", created_ts: 3000 }),
Object.assign({}, membershipTemplate, { device_id: "old", created_ts: 1000 }),
@@ -312,12 +320,15 @@ describe("MatrixRTCSession", () => {
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
expect(sess.getOldestMembership()!.deviceId).toEqual("old");
jest.useRealTimers();
});
});
describe("getsActiveFocus", () => {
const activeFociConfig = { type: "livekit", livekit_service_url: "https://active.url" };
it("gets the correct active focus with oldest_membership", () => {
jest.useFakeTimers();
jest.setSystemTime(3000);
const mockRoom = makeMockRoom([
Object.assign({}, membershipTemplate, {
device_id: "foo",
@@ -335,6 +346,7 @@ describe("MatrixRTCSession", () => {
focus_selection: "oldest_membership",
});
expect(sess.getActiveFocus()).toBe(activeFociConfig);
jest.useRealTimers();
});
it("does not provide focus if the selction method is unknown", () => {
const mockRoom = makeMockRoom([
@@ -356,6 +368,8 @@ describe("MatrixRTCSession", () => {
expect(sess.getActiveFocus()).toBe(undefined);
});
it("gets the correct active focus legacy", () => {
jest.useFakeTimers();
jest.setSystemTime(3000);
const mockRoom = makeMockRoom([
Object.assign({}, membershipTemplate, {
device_id: "foo",
@@ -370,6 +384,7 @@ describe("MatrixRTCSession", () => {
sess.joinRoomSession([{ type: "livekit", livekit_service_url: "htts://test.org" }]);
expect(sess.getActiveFocus()).toBe(activeFociConfig);
jest.useRealTimers();
});
});
@@ -513,9 +528,8 @@ describe("MatrixRTCSession", () => {
const eventContent = await eventSentPromise;
// definitely should have renewed by 1 second before the expiry!
const timeElapsed = 60 * 60 * 1000 - 1000;
const event = mockRTCEvent(eventContent.memberships, mockRoom.roomId, timeElapsed);
jest.setSystemTime(1000);
const event = mockRTCEvent(eventContent.memberships, mockRoom.roomId);
const getState = mockRoom.getLiveTimeline().getState(EventTimeline.FORWARDS)!;
getState.getStateEvents = jest.fn().mockReturnValue(event);
getState.events = new Map([
@@ -538,6 +552,8 @@ describe("MatrixRTCSession", () => {
sendStateEventMock.mockReset().mockImplementation(resolveFn);
// definitely should have renewed by 1 second before the expiry!
const timeElapsed = 60 * 60 * 1000 - 1000;
jest.setSystemTime(Date.now() + timeElapsed);
jest.advanceTimersByTime(timeElapsed);
await eventReSentPromise;
@@ -578,24 +594,48 @@ describe("MatrixRTCSession", () => {
});
it("sends keys when joining", async () => {
const eventSentPromise = new Promise((resolve) => {
sendEventMock.mockImplementation(resolve);
});
jest.useFakeTimers();
try {
const eventSentPromise = new Promise((resolve) => {
sendEventMock.mockImplementation(resolve);
});
sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
await eventSentPromise;
expect(sendEventMock).toHaveBeenCalledWith(
expect.stringMatching(".*"),
"io.element.call.encryption_keys",
{
call_id: "",
device_id: "AAAAAAA",
keys: [
{
index: 0,
key: expect.stringMatching(".*"),
},
],
sent_ts: Date.now(),
},
);
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1);
} finally {
jest.useRealTimers();
}
});
it("does not send key if join called when already joined", () => {
sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
await eventSentPromise;
expect(client.sendStateEvent).toHaveBeenCalledTimes(1);
expect(client.sendEvent).toHaveBeenCalledTimes(1);
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1);
expect(sendEventMock).toHaveBeenCalledWith(expect.stringMatching(".*"), "io.element.call.encryption_keys", {
call_id: "",
device_id: "AAAAAAA",
keys: [
{
index: 0,
key: expect.stringMatching(".*"),
},
],
});
sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
expect(client.sendStateEvent).toHaveBeenCalledTimes(1);
expect(client.sendEvent).toHaveBeenCalledTimes(1);
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1);
});
it("retries key sends", async () => {
@@ -624,6 +664,7 @@ describe("MatrixRTCSession", () => {
await eventSentPromise;
expect(sendEventMock).toHaveBeenCalledTimes(2);
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(2);
} finally {
jest.useRealTimers();
}
@@ -657,6 +698,7 @@ describe("MatrixRTCSession", () => {
sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
await keysSentPromise1;
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1);
sendEventMock.mockClear();
jest.advanceTimersByTime(10000);
@@ -674,12 +716,232 @@ describe("MatrixRTCSession", () => {
mockRoom.getLiveTimeline().getState = jest
.fn()
.mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId, undefined));
.mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId));
sess.onMembershipUpdate();
await keysSentPromise2;
expect(sendEventMock).toHaveBeenCalled();
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(2);
} finally {
jest.useRealTimers();
}
});
it("Does not re-send key if memberships stays same", async () => {
jest.useFakeTimers();
try {
const keysSentPromise1 = new Promise((resolve) => {
sendEventMock.mockImplementation(resolve);
});
const member1 = membershipTemplate;
const member2 = Object.assign({}, membershipTemplate, {
device_id: "BBBBBBB",
});
const mockRoom = makeMockRoom([member1, member2]);
mockRoom.getLiveTimeline().getState = jest
.fn()
.mockReturnValue(makeMockRoomState([member1, member2], mockRoom.roomId));
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
await keysSentPromise1;
// make sure an encryption key was sent
expect(sendEventMock).toHaveBeenCalledWith(
expect.stringMatching(".*"),
"io.element.call.encryption_keys",
{
call_id: "",
device_id: "AAAAAAA",
keys: [
{
index: 0,
key: expect.stringMatching(".*"),
},
],
sent_ts: Date.now(),
},
);
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1);
sendEventMock.mockClear();
// these should be a no-op:
sess.onMembershipUpdate();
expect(sendEventMock).toHaveBeenCalledTimes(0);
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1);
} finally {
jest.useRealTimers();
}
});
it("Re-sends key if a member changes membership ID", async () => {
jest.useFakeTimers();
try {
const keysSentPromise1 = new Promise((resolve) => {
sendEventMock.mockImplementation(resolve);
});
const member1 = membershipTemplate;
const member2 = {
...membershipTemplate,
device_id: "BBBBBBB",
};
const mockRoom = makeMockRoom([member1, member2]);
mockRoom.getLiveTimeline().getState = jest
.fn()
.mockReturnValue(makeMockRoomState([member1, member2], mockRoom.roomId));
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
await keysSentPromise1;
// make sure an encryption key was sent
expect(sendEventMock).toHaveBeenCalledWith(
expect.stringMatching(".*"),
"io.element.call.encryption_keys",
{
call_id: "",
device_id: "AAAAAAA",
keys: [
{
index: 0,
key: expect.stringMatching(".*"),
},
],
sent_ts: Date.now(),
},
);
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1);
sendEventMock.mockClear();
// this should be a no-op:
sess.onMembershipUpdate();
expect(sendEventMock).toHaveBeenCalledTimes(0);
// advance time to avoid key throttling
jest.advanceTimersByTime(10000);
// update membership ID
member2.membershipID = "newID";
const keysSentPromise2 = new Promise((resolve) => {
sendEventMock.mockImplementation(resolve);
});
// this should re-send the key
sess.onMembershipUpdate();
await keysSentPromise2;
expect(sendEventMock).toHaveBeenCalledWith(
expect.stringMatching(".*"),
"io.element.call.encryption_keys",
{
call_id: "",
device_id: "AAAAAAA",
keys: [
{
index: 0,
key: expect.stringMatching(".*"),
},
],
sent_ts: Date.now(),
},
);
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(2);
} finally {
jest.useRealTimers();
}
});
it("Re-sends key if a member changes created_ts", async () => {
jest.useFakeTimers();
jest.setSystemTime(1000);
try {
const keysSentPromise1 = new Promise((resolve) => {
sendEventMock.mockImplementation(resolve);
});
const member1 = { ...membershipTemplate, created_ts: 1000 };
const member2 = {
...membershipTemplate,
created_ts: 1000,
device_id: "BBBBBBB",
};
const mockRoom = makeMockRoom([member1, member2]);
mockRoom.getLiveTimeline().getState = jest
.fn()
.mockReturnValue(makeMockRoomState([member1, member2], mockRoom.roomId));
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
await keysSentPromise1;
// make sure an encryption key was sent
expect(sendEventMock).toHaveBeenCalledWith(
expect.stringMatching(".*"),
"io.element.call.encryption_keys",
{
call_id: "",
device_id: "AAAAAAA",
keys: [
{
index: 0,
key: expect.stringMatching(".*"),
},
],
sent_ts: Date.now(),
},
);
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1);
sendEventMock.mockClear();
// this should be a no-op:
sess.onMembershipUpdate();
expect(sendEventMock).toHaveBeenCalledTimes(0);
// advance time to avoid key throttling
jest.advanceTimersByTime(10000);
// update created_ts
member2.created_ts = 5000;
const keysSentPromise2 = new Promise((resolve) => {
sendEventMock.mockImplementation(resolve);
});
// this should re-send the key
sess.onMembershipUpdate();
await keysSentPromise2;
expect(sendEventMock).toHaveBeenCalledWith(
expect.stringMatching(".*"),
"io.element.call.encryption_keys",
{
call_id: "",
device_id: "AAAAAAA",
keys: [
{
index: 0,
key: expect.stringMatching(".*"),
},
],
sent_ts: Date.now(),
},
);
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(2);
} finally {
jest.useRealTimers();
}
@@ -711,6 +973,8 @@ describe("MatrixRTCSession", () => {
sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
const firstKeysPayload = await keysSentPromise1;
expect(firstKeysPayload.keys).toHaveLength(1);
expect(firstKeysPayload.keys[0].index).toEqual(0);
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1);
sendEventMock.mockClear();
@@ -720,15 +984,17 @@ describe("MatrixRTCSession", () => {
mockRoom.getLiveTimeline().getState = jest
.fn()
.mockReturnValue(makeMockRoomState([membershipTemplate], mockRoom.roomId, undefined));
.mockReturnValue(makeMockRoomState([membershipTemplate], mockRoom.roomId));
sess.onMembershipUpdate();
jest.advanceTimersByTime(10000);
const secondKeysPayload = await keysSentPromise2;
expect(secondKeysPayload.keys).toHaveLength(2);
expect(secondKeysPayload.keys).toHaveLength(1);
expect(secondKeysPayload.keys[0].index).toEqual(1);
expect(onMyEncryptionKeyChanged).toHaveBeenCalledTimes(2);
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(2);
} finally {
jest.useRealTimers();
}
@@ -749,6 +1015,7 @@ describe("MatrixRTCSession", () => {
await keysSentPromise1;
sendEventMock.mockClear();
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1);
const onMembershipsChanged = jest.fn();
sess.on(MatrixRTCSessionEvent.MembershipsChanged, onMembershipsChanged);
@@ -759,7 +1026,7 @@ describe("MatrixRTCSession", () => {
mockRoom.getLiveTimeline().getState = jest
.fn()
.mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId, undefined));
.mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId));
sess.onMembershipUpdate();
await new Promise((resolve) => {
@@ -767,6 +1034,7 @@ describe("MatrixRTCSession", () => {
});
expect(sendEventMock).not.toHaveBeenCalled();
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1);
} finally {
jest.useRealTimers();
}
@@ -791,9 +1059,7 @@ describe("MatrixRTCSession", () => {
const onMembershipsChanged = jest.fn();
sess.on(MatrixRTCSessionEvent.MembershipsChanged, onMembershipsChanged);
mockRoom.getLiveTimeline().getState = jest
.fn()
.mockReturnValue(makeMockRoomState([], mockRoom.roomId, undefined));
mockRoom.getLiveTimeline().getState = jest.fn().mockReturnValue(makeMockRoomState([], mockRoom.roomId));
sess.onMembershipUpdate();
expect(onMembershipsChanged).toHaveBeenCalled();
@@ -803,7 +1069,7 @@ describe("MatrixRTCSession", () => {
jest.useFakeTimers();
try {
const membership = Object.assign({}, membershipTemplate);
const mockRoom = makeMockRoom([membership], 0);
const mockRoom = makeMockRoom([membership]);
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
const membershipObject = sess.memberships[0];
@@ -831,7 +1097,7 @@ describe("MatrixRTCSession", () => {
expires: 1000,
}),
];
const mockRoomNoExpired = makeMockRoom(mockMemberships, 0);
const mockRoomNoExpired = makeMockRoom(mockMemberships);
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoomNoExpired);
@@ -870,6 +1136,7 @@ describe("MatrixRTCSession", () => {
it("fills in created_ts for other memberships on update", () => {
client.sendStateEvent = jest.fn();
jest.useFakeTimers();
jest.setSystemTime(1000);
const mockRoom = makeMockRoom([
Object.assign({}, membershipTemplate, {
device_id: "OTHERDEVICE",
@@ -927,11 +1194,13 @@ describe("MatrixRTCSession", () => {
],
}),
getSender: jest.fn().mockReturnValue("@bob:example.org"),
getTs: jest.fn().mockReturnValue(Date.now()),
} as unknown as MatrixEvent);
const bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!;
expect(bobKeys).toHaveLength(1);
expect(bobKeys[0]).toEqual(Buffer.from("this is the key", "utf-8"));
expect(sess!.statistics.counters.roomEventEncryptionKeysReceived).toEqual(1);
});
it("collects keys at non-zero indices", () => {
@@ -950,6 +1219,7 @@ describe("MatrixRTCSession", () => {
],
}),
getSender: jest.fn().mockReturnValue("@bob:example.org"),
getTs: jest.fn().mockReturnValue(Date.now()),
} as unknown as MatrixEvent);
const bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!;
@@ -959,6 +1229,134 @@ describe("MatrixRTCSession", () => {
expect(bobKeys[2]).toBeFalsy();
expect(bobKeys[3]).toBeFalsy();
expect(bobKeys[4]).toEqual(Buffer.from("this is the key", "utf-8"));
expect(sess!.statistics.counters.roomEventEncryptionKeysReceived).toEqual(1);
});
it("collects keys by merging", () => {
const mockRoom = makeMockRoom([membershipTemplate]);
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
sess.onCallEncryption({
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
getContent: jest.fn().mockReturnValue({
device_id: "bobsphone",
call_id: "",
keys: [
{
index: 0,
key: "dGhpcyBpcyB0aGUga2V5",
},
],
}),
getSender: jest.fn().mockReturnValue("@bob:example.org"),
getTs: jest.fn().mockReturnValue(Date.now()),
} as unknown as MatrixEvent);
let bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!;
expect(bobKeys).toHaveLength(1);
expect(bobKeys[0]).toEqual(Buffer.from("this is the key", "utf-8"));
expect(sess!.statistics.counters.roomEventEncryptionKeysReceived).toEqual(1);
sess.onCallEncryption({
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
getContent: jest.fn().mockReturnValue({
device_id: "bobsphone",
call_id: "",
keys: [
{
index: 4,
key: "dGhpcyBpcyB0aGUga2V5",
},
],
}),
getSender: jest.fn().mockReturnValue("@bob:example.org"),
getTs: jest.fn().mockReturnValue(Date.now()),
} as unknown as MatrixEvent);
bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!;
expect(bobKeys).toHaveLength(5);
expect(bobKeys[4]).toEqual(Buffer.from("this is the key", "utf-8"));
expect(sess!.statistics.counters.roomEventEncryptionKeysReceived).toEqual(2);
});
it("ignores older keys at same index", () => {
const mockRoom = makeMockRoom([membershipTemplate]);
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
sess.onCallEncryption({
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
getContent: jest.fn().mockReturnValue({
device_id: "bobsphone",
call_id: "",
keys: [
{
index: 0,
key: encodeBase64(Buffer.from("newer key", "utf-8")),
},
],
}),
getSender: jest.fn().mockReturnValue("@bob:example.org"),
getTs: jest.fn().mockReturnValue(2000),
} as unknown as MatrixEvent);
sess.onCallEncryption({
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
getContent: jest.fn().mockReturnValue({
device_id: "bobsphone",
call_id: "",
keys: [
{
index: 0,
key: encodeBase64(Buffer.from("older key", "utf-8")),
},
],
}),
getSender: jest.fn().mockReturnValue("@bob:example.org"),
getTs: jest.fn().mockReturnValue(1000), // earlier timestamp than the newer key
} as unknown as MatrixEvent);
const bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!;
expect(bobKeys).toHaveLength(1);
expect(bobKeys[0]).toEqual(Buffer.from("newer key", "utf-8"));
expect(sess!.statistics.counters.roomEventEncryptionKeysReceived).toEqual(2);
});
it("key timestamps are treated as monotonic", () => {
const mockRoom = makeMockRoom([membershipTemplate]);
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
sess.onCallEncryption({
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
getContent: jest.fn().mockReturnValue({
device_id: "bobsphone",
call_id: "",
keys: [
{
index: 0,
key: encodeBase64(Buffer.from("first key", "utf-8")),
},
],
}),
getSender: jest.fn().mockReturnValue("@bob:example.org"),
getTs: jest.fn().mockReturnValue(1000),
} as unknown as MatrixEvent);
sess.onCallEncryption({
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
getContent: jest.fn().mockReturnValue({
device_id: "bobsphone",
call_id: "",
keys: [
{
index: 0,
key: encodeBase64(Buffer.from("second key", "utf-8")),
},
],
}),
getSender: jest.fn().mockReturnValue("@bob:example.org"),
getTs: jest.fn().mockReturnValue(1000), // same timestamp as the first key
} as unknown as MatrixEvent);
const bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!;
expect(bobKeys).toHaveLength(1);
expect(bobKeys[0]).toEqual(Buffer.from("second key", "utf-8"));
});
it("ignores keys event for the local participant", () => {
@@ -977,9 +1375,78 @@ describe("MatrixRTCSession", () => {
],
}),
getSender: jest.fn().mockReturnValue(client.getUserId()),
getTs: jest.fn().mockReturnValue(Date.now()),
} as unknown as MatrixEvent);
const myKeys = sess.getKeysForParticipant(client.getUserId()!, client.getDeviceId()!)!;
expect(myKeys).toBeFalsy();
expect(sess!.statistics.counters.roomEventEncryptionKeysReceived).toEqual(0);
});
it("tracks total age statistics for collected keys", () => {
jest.useFakeTimers();
try {
const mockRoom = makeMockRoom([membershipTemplate]);
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
// defaults to getTs()
jest.setSystemTime(1000);
sess.onCallEncryption({
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
getContent: jest.fn().mockReturnValue({
device_id: "bobsphone",
call_id: "",
keys: [
{
index: 0,
key: "dGhpcyBpcyB0aGUga2V5",
},
],
}),
getSender: jest.fn().mockReturnValue("@bob:example.org"),
getTs: jest.fn().mockReturnValue(0),
} as unknown as MatrixEvent);
expect(sess!.statistics.totals.roomEventEncryptionKeysReceivedTotalAge).toEqual(1000);
jest.setSystemTime(2000);
sess.onCallEncryption({
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
getContent: jest.fn().mockReturnValue({
device_id: "bobsphone",
call_id: "",
keys: [
{
index: 0,
key: "dGhpcyBpcyB0aGUga2V5",
},
],
sent_ts: 0,
}),
getSender: jest.fn().mockReturnValue("@bob:example.org"),
getTs: jest.fn().mockReturnValue(Date.now()),
} as unknown as MatrixEvent);
expect(sess!.statistics.totals.roomEventEncryptionKeysReceivedTotalAge).toEqual(3000);
jest.setSystemTime(3000);
sess.onCallEncryption({
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
getContent: jest.fn().mockReturnValue({
device_id: "bobsphone",
call_id: "",
keys: [
{
index: 0,
key: "dGhpcyBpcyB0aGUga2V5",
},
],
sent_ts: 1000,
}),
getSender: jest.fn().mockReturnValue("@bob:example.org"),
getTs: jest.fn().mockReturnValue(Date.now()),
} as unknown as MatrixEvent);
expect(sess!.statistics.totals.roomEventEncryptionKeysReceivedTotalAge).toEqual(5000);
} finally {
jest.useRealTimers();
}
});
});
@@ -102,6 +102,7 @@ describe("MatrixRTCSessionManager", () => {
getContent: jest.fn().mockReturnValue({}),
getSender: jest.fn().mockReturnValue("@mock:user.example"),
getRoomId: jest.fn().mockReturnValue("!room:id"),
isDecryptionFailure: jest.fn().mockReturnValue(false),
sender: {
userId: "@mock:user.example",
},
@@ -110,4 +111,93 @@ describe("MatrixRTCSessionManager", () => {
await new Promise(process.nextTick);
expect(onCallEncryptionMock).toHaveBeenCalled();
});
describe("event decryption", () => {
it("Retries decryption and processes success", async () => {
try {
jest.useFakeTimers();
const room1 = makeMockRoom([membershipTemplate]);
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
jest.spyOn(client, "getRoom").mockReturnValue(room1);
client.emit(ClientEvent.Room, room1);
const onCallEncryptionMock = jest.fn();
client.matrixRTC.getRoomSession(room1).onCallEncryption = onCallEncryptionMock;
let isDecryptionFailure = true;
client.decryptEventIfNeeded = jest
.fn()
.mockReturnValueOnce(Promise.resolve())
.mockImplementation(() => {
isDecryptionFailure = false;
return Promise.resolve();
});
const timelineEvent = {
getType: jest.fn().mockReturnValue(EventType.CallEncryptionKeysPrefix),
getContent: jest.fn().mockReturnValue({}),
getSender: jest.fn().mockReturnValue("@mock:user.example"),
getRoomId: jest.fn().mockReturnValue("!room:id"),
isDecryptionFailure: jest.fn().mockImplementation(() => isDecryptionFailure),
getId: jest.fn().mockReturnValue("event_id"),
sender: {
userId: "@mock:user.example",
},
} as unknown as MatrixEvent;
client.emit(RoomEvent.Timeline, timelineEvent, undefined, undefined, false, {} as IRoomTimelineData);
expect(client.decryptEventIfNeeded).toHaveBeenCalledTimes(1);
expect(onCallEncryptionMock).toHaveBeenCalledTimes(0);
// should retry after one second:
await jest.advanceTimersByTimeAsync(1500);
expect(client.decryptEventIfNeeded).toHaveBeenCalledTimes(2);
expect(onCallEncryptionMock).toHaveBeenCalledTimes(1);
} finally {
jest.useRealTimers();
}
});
it("Retries decryption and processes failure", async () => {
try {
jest.useFakeTimers();
const room1 = makeMockRoom([membershipTemplate]);
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
jest.spyOn(client, "getRoom").mockReturnValue(room1);
client.emit(ClientEvent.Room, room1);
const onCallEncryptionMock = jest.fn();
client.matrixRTC.getRoomSession(room1).onCallEncryption = onCallEncryptionMock;
client.decryptEventIfNeeded = jest.fn().mockReturnValue(Promise.resolve());
const timelineEvent = {
getType: jest.fn().mockReturnValue(EventType.CallEncryptionKeysPrefix),
getContent: jest.fn().mockReturnValue({}),
getSender: jest.fn().mockReturnValue("@mock:user.example"),
getRoomId: jest.fn().mockReturnValue("!room:id"),
isDecryptionFailure: jest.fn().mockReturnValue(true), // always fail
getId: jest.fn().mockReturnValue("event_id"),
sender: {
userId: "@mock:user.example",
},
} as unknown as MatrixEvent;
client.emit(RoomEvent.Timeline, timelineEvent, undefined, undefined, false, {} as IRoomTimelineData);
expect(client.decryptEventIfNeeded).toHaveBeenCalledTimes(1);
expect(onCallEncryptionMock).toHaveBeenCalledTimes(0);
// should retry after one second:
await jest.advanceTimersByTimeAsync(1500);
expect(client.decryptEventIfNeeded).toHaveBeenCalledTimes(2);
expect(onCallEncryptionMock).toHaveBeenCalledTimes(0);
// doesn't retry again:
await jest.advanceTimersByTimeAsync(1500);
expect(client.decryptEventIfNeeded).toHaveBeenCalledTimes(2);
expect(onCallEncryptionMock).toHaveBeenCalledTimes(0);
} finally {
jest.useRealTimers();
}
});
});
});
+7 -7
View File
@@ -20,10 +20,10 @@ import { randomString } from "../../../src/randomstring";
type MembershipData = CallMembershipData[] | SessionMembershipData;
export function makeMockRoom(membershipData: MembershipData, localAge: number | null = null): Room {
export function makeMockRoom(membershipData: MembershipData): Room {
const roomId = randomString(8);
// Caching roomState here so it does not get recreated when calling `getLiveTimeline.getState()`
const roomState = makeMockRoomState(membershipData, roomId, localAge);
const roomState = makeMockRoomState(membershipData, roomId);
return {
roomId: roomId,
hasMembershipState: jest.fn().mockReturnValue(true),
@@ -34,8 +34,8 @@ export function makeMockRoom(membershipData: MembershipData, localAge: number |
} as unknown as Room;
}
export function makeMockRoomState(membershipData: MembershipData, roomId: string, localAge: number | null = null) {
const event = mockRTCEvent(membershipData, roomId, localAge);
export function makeMockRoomState(membershipData: MembershipData, roomId: string) {
const event = mockRTCEvent(membershipData, roomId);
return {
on: jest.fn(),
off: jest.fn(),
@@ -57,7 +57,7 @@ export function makeMockRoomState(membershipData: MembershipData, roomId: string
};
}
export function mockRTCEvent(membershipData: MembershipData, roomId: string, localAge: number | null): MatrixEvent {
export function mockRTCEvent(membershipData: MembershipData, roomId: string): MatrixEvent {
return {
getType: jest.fn().mockReturnValue(EventType.GroupCallMemberPrefix),
getContent: jest.fn().mockReturnValue(
@@ -68,11 +68,11 @@ export function mockRTCEvent(membershipData: MembershipData, roomId: string, loc
},
),
getSender: jest.fn().mockReturnValue("@mock:user.example"),
getTs: jest.fn().mockReturnValue(1000),
localTimestamp: Date.now() - (localAge ?? 10),
getTs: jest.fn().mockReturnValue(Date.now()),
getRoomId: jest.fn().mockReturnValue(roomId),
sender: {
userId: "@mock:user.example",
},
isDecryptionFailure: jest.fn().mockReturnValue(false),
} as unknown as MatrixEvent;
}
+2 -2
View File
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { UnstableValue } from "../NamespacedValue";
import { IClientWellKnown } from "../client";
import { UnstableValue } from "../NamespacedValue.ts";
import { IClientWellKnown } from "../client.ts";
// disable lint because these are wire responses
/* eslint-disable camelcase */
+3 -3
View File
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { RelatesToRelationship, REFERENCE_RELATION } from "./extensible_events";
import { UnstableValue } from "../NamespacedValue";
import { MAssetEvent, MLocationEvent, MTimestampEvent } from "./location";
import { RelatesToRelationship, REFERENCE_RELATION } from "./extensible_events.ts";
import { UnstableValue } from "../NamespacedValue.ts";
import { MAssetEvent, MLocationEvent, MTimestampEvent } from "./location.ts";
/**
* Beacon info and beacon event types as described in MSC3672
+2 -2
View File
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import type { ISignatures } from "./signed";
import type { ISignatures } from "./signed.ts";
export type OlmGroupSessionExtraData = {
untrusted?: boolean;
@@ -22,7 +22,7 @@ export type OlmGroupSessionExtraData = {
};
// Backwards compatible re-export
export type { EventDecryptionResult as IEventDecryptionResult } from "../common-crypto/CryptoBackend";
export type { EventDecryptionResult as IEventDecryptionResult } from "../common-crypto/CryptoBackend.ts";
interface Extensible {
[key: string]: any;
+11 -11
View File
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { NamespacedValue, UnstableValue } from "../NamespacedValue";
import { NamespacedValue, UnstableValue } from "../NamespacedValue.ts";
import {
PolicyRuleEventContent,
RoomAvatarEventContent,
@@ -34,16 +34,16 @@ import {
RoomTopicEventContent,
SpaceChildEventContent,
SpaceParentEventContent,
} from "./state_events";
} from "./state_events.ts";
import {
ExperimentalGroupCallRoomMemberState,
IGroupCallRoomMemberState,
IGroupCallRoomState,
} from "../webrtc/groupCall";
import { MSC3089EventContent } from "../models/MSC3089Branch";
import { M_BEACON, M_BEACON_INFO, MBeaconEventContent, MBeaconInfoEventContent } from "./beacon";
import { XOR } from "./common";
import { ReactionEventContent, RoomMessageEventContent, StickerEventContent } from "./events";
} from "../webrtc/groupCall.ts";
import { MSC3089EventContent } from "../models/MSC3089Branch.ts";
import { M_BEACON, M_BEACON_INFO, MBeaconEventContent, MBeaconInfoEventContent } from "./beacon.ts";
import { XOR } from "./common.ts";
import { ReactionEventContent, RoomMessageEventContent, StickerEventContent } from "./events.ts";
import {
MCallAnswer,
MCallBase,
@@ -54,10 +54,10 @@ import {
MCallSelectAnswer,
SDPStreamMetadata,
SDPStreamMetadataKey,
} from "../webrtc/callEventTypes";
import { EncryptionKeysEventContent, ICallNotifyContent } from "../matrixrtc/types";
import { M_POLL_END, M_POLL_START, PollEndEventContent, PollStartEventContent } from "./polls";
import { SessionMembershipData } from "../matrixrtc/CallMembership";
} from "../webrtc/callEventTypes.ts";
import { EncryptionKeysEventContent, ICallNotifyContent } from "../matrixrtc/types.ts";
import { M_POLL_END, M_POLL_START, PollEndEventContent, PollStartEventContent } from "./polls.ts";
import { SessionMembershipData } from "../matrixrtc/CallMembership.ts";
export enum EventType {
// Room state events
+3 -3
View File
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { MsgType, RelationType } from "./event";
import { FileInfo, ImageInfo, MediaEventContent } from "./media";
import { XOR } from "./common";
import { MsgType, RelationType } from "./event.ts";
import { FileInfo, ImageInfo, MediaEventContent } from "./media.ts";
import { XOR } from "./common.ts";
interface BaseTimelineEvent {
"body": string;
+1 -1
View File
@@ -16,7 +16,7 @@ limitations under the License.
import { EitherAnd, NamespacedValue, Optional, UnstableValue } from "matrix-events-sdk";
import { isProvided } from "../extensible_events_v1/utilities";
import { isProvided } from "../extensible_events_v1/utilities.ts";
// Types and utilities for MSC1767: Extensible events (version 1) in Matrix
+2 -2
View File
@@ -17,8 +17,8 @@ limitations under the License.
// Types for MSC3488 - m.location: Extending events with location data
import { EitherAnd } from "matrix-events-sdk";
import { UnstableValue } from "../NamespacedValue";
import { M_TEXT } from "./extensible_events";
import { UnstableValue } from "../NamespacedValue.ts";
import { M_TEXT } from "./extensible_events.ts";
export enum LocationAssetType {
Self = "m.self",
+1 -1
View File
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { MsgType } from "../@types/event";
import { MsgType } from "../@types/event.ts";
/**
* Information on encrypted media attachments.
+1 -1
View File
@@ -21,7 +21,7 @@ import {
REFERENCE_RELATION,
RelatesToRelationship,
TSNamespace,
} from "./extensible_events";
} from "./extensible_events.ts";
/**
* Identifier for a disclosed poll.
+1 -1
View File
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { AuthDict } from "../interactive-auth";
import { AuthDict } from "../interactive-auth.ts";
/**
* The request body of a call to `POST /_matrix/client/v3/register`.
+8 -8
View File
@@ -14,14 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { IContent, IEvent } from "../models/event";
import { Preset, Visibility } from "./partials";
import { IEventWithRoomId, SearchKey } from "./search";
import { IRoomEventFilter } from "../filter";
import { Direction } from "../models/event-timeline";
import { PushRuleAction } from "./PushRules";
import { IRoomEvent } from "../sync-accumulator";
import { EventType, RelationType, RoomType } from "./event";
import { IContent, IEvent } from "../models/event.ts";
import { Preset, Visibility } from "./partials.ts";
import { IEventWithRoomId, SearchKey } from "./search.ts";
import { IRoomEventFilter } from "../filter.ts";
import { Direction } from "../models/event-timeline.ts";
import { PushRuleAction } from "./PushRules.ts";
import { IRoomEvent } from "../sync-accumulator.ts";
import { EventType, RelationType, RoomType } from "./event.ts";
// allow camelcase as these are things that go onto the wire
/* eslint-disable camelcase */
+3 -3
View File
@@ -16,9 +16,9 @@ limitations under the License.
// Types relating to the /search API
import { IRoomEvent, IStateEvent } from "../sync-accumulator";
import { IRoomEventFilter } from "../filter";
import { SearchResult } from "../models/search-result";
import { IRoomEvent, IStateEvent } from "../sync-accumulator.ts";
import { IRoomEventFilter } from "../filter.ts";
import { SearchResult } from "../models/search-result.ts";
/* eslint-disable camelcase */
export interface IEventWithRoomId extends IRoomEvent {
+3 -3
View File
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { IPublicRoomsChunkRoom } from "../client";
import { RoomType } from "./event";
import { IStrippedState } from "../sync-accumulator";
import { IPublicRoomsChunkRoom } from "../client.ts";
import { RoomType } from "./event.ts";
import { IStrippedState } from "../sync-accumulator.ts";
// Types relating to Rooms of type `m.space` and related APIs
+4 -4
View File
@@ -14,10 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { RoomType } from "./event";
import { GuestAccess, HistoryVisibility, JoinRule, RestrictedAllowType } from "./partials";
import { ImageInfo } from "./media";
import { PolicyRecommendation } from "../models/invites-ignorer";
import { RoomType } from "./event.ts";
import { GuestAccess, HistoryVisibility, JoinRule, RestrictedAllowType } from "./partials.ts";
import { ImageInfo } from "./media.ts";
import { PolicyRecommendation } from "../models/invites-ignorer.ts";
export interface RoomCanonicalAliasEventContent {
alias?: string;
+1 -1
View File
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { IdServerUnbindResult } from "./partials";
import { IdServerUnbindResult } from "./partials.ts";
// Types relating to Synapse Admin APIs
+1 -1
View File
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { ServerControlledNamespacedValue } from "../NamespacedValue";
import { ServerControlledNamespacedValue } from "../NamespacedValue.ts";
/**
* https://github.com/matrix-org/matrix-doc/pull/3773
+2 -2
View File
@@ -16,8 +16,8 @@ limitations under the License.
import { EitherAnd } from "matrix-events-sdk";
import { UnstableValue } from "../NamespacedValue";
import { IMessageRendering } from "./extensible_events";
import { UnstableValue } from "../NamespacedValue.ts";
import { IMessageRendering } from "./extensible_events.ts";
/**
* Extensible topic event type based on MSC3765
+1 -1
View File
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { AuthDict, IAuthData } from "../interactive-auth";
import { AuthDict, IAuthData } from "../interactive-auth.ts";
/**
* Helper type to represent HTTP request body for a UIA enabled endpoint
+1 -1
View File
@@ -19,7 +19,7 @@ limitations under the License.
// eslint-disable-next-line no-restricted-imports
import { EventEmitter } from "events";
import { ListenerMap, TypedEventEmitter } from "./models/typed-event-emitter";
import { ListenerMap, TypedEventEmitter } from "./models/typed-event-emitter.ts";
export class ReEmitter {
public constructor(private readonly target: EventEmitter) {}
+13 -8
View File
@@ -14,14 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { ToDeviceMessageId } from "./@types/event";
import { logger } from "./logger";
import { MatrixClient, ClientEvent } from "./client";
import { MatrixError } from "./http-api";
import { IndexedToDeviceBatch, ToDeviceBatch, ToDeviceBatchWithTxnId, ToDevicePayload } from "./models/ToDeviceMessage";
import { MatrixScheduler } from "./scheduler";
import { SyncState } from "./sync";
import { MapWithDefault } from "./utils";
import { ToDeviceMessageId } from "./@types/event.ts";
import { logger } from "./logger.ts";
import { MatrixClient, ClientEvent } from "./client.ts";
import { MatrixError } from "./http-api/index.ts";
import {
IndexedToDeviceBatch,
ToDeviceBatch,
ToDeviceBatchWithTxnId,
ToDevicePayload,
} from "./models/ToDeviceMessage.ts";
import { MatrixScheduler } from "./scheduler.ts";
import { SyncState } from "./sync.ts";
import { MapWithDefault } from "./utils.ts";
const MAX_BATCH_SIZE = 20;
+4 -4
View File
@@ -15,10 +15,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { IClientWellKnown, IWellKnownConfig, IServerVersions } from "./client";
import { logger } from "./logger";
import { MatrixError, Method, timeoutSignal } from "./http-api";
import { SUPPORTED_MATRIX_VERSIONS } from "./version-support";
import { IClientWellKnown, IWellKnownConfig, IServerVersions } from "./client.ts";
import { logger } from "./logger.ts";
import { MatrixError, Method, timeoutSignal } from "./http-api/index.ts";
import { SUPPORTED_MATRIX_VERSIONS } from "./version-support.ts";
// Dev note: Auto discovery is part of the spec.
// See: https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery
+2 -2
View File
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import * as matrixcs from "./matrix";
import * as matrixcs from "./matrix.ts";
type BrowserMatrix = typeof matrixcs;
declare global {
@@ -40,5 +40,5 @@ if (indexedDB) {
matrixcs.setCryptoStoreFactory(() => new matrixcs.IndexedDBCryptoStore(indexedDB!, "matrix-js-sdk:crypto"));
}
export * from "./matrix";
export * from "./matrix.ts";
globalThis.matrixcs = matrixcs;
+357 -123
View File
@@ -20,8 +20,8 @@ limitations under the License.
import { Optional } from "matrix-events-sdk";
import type { IDeviceKeys, IMegolmSessionData, IOneTimeKey } from "./@types/crypto";
import { ISyncStateData, SetPresence, SyncApi, SyncApiOptions, SyncState } from "./sync";
import type { IDeviceKeys, IMegolmSessionData, IOneTimeKey } from "./@types/crypto.ts";
import { ISyncStateData, SetPresence, SyncApi, SyncApiOptions, SyncState } from "./sync.ts";
import {
EventStatus,
IContent,
@@ -31,29 +31,29 @@ import {
MatrixEventEvent,
MatrixEventHandlerMap,
PushDetails,
} from "./models/event";
import { StubStore } from "./store/stub";
import { CallEvent, CallEventHandlerMap, createNewMatrixCall, MatrixCall, supportsMatrixCall } from "./webrtc/call";
import { Filter, IFilterDefinition, IRoomEventFilter } from "./filter";
import { CallEventHandler, CallEventHandlerEvent, CallEventHandlerEventHandlerMap } from "./webrtc/callEventHandler";
} from "./models/event.ts";
import { StubStore } from "./store/stub.ts";
import { CallEvent, CallEventHandlerMap, createNewMatrixCall, MatrixCall, supportsMatrixCall } from "./webrtc/call.ts";
import { Filter, IFilterDefinition, IRoomEventFilter } from "./filter.ts";
import { CallEventHandler, CallEventHandlerEvent, CallEventHandlerEventHandlerMap } from "./webrtc/callEventHandler.ts";
import {
GroupCallEventHandler,
GroupCallEventHandlerEvent,
GroupCallEventHandlerEventHandlerMap,
} from "./webrtc/groupCallEventHandler";
import * as utils from "./utils";
import { noUnsafeEventProps, QueryDict, replaceParam, safeSet, sleep } from "./utils";
import { Direction, EventTimeline } from "./models/event-timeline";
import { IActionsObject, PushProcessor } from "./pushprocessor";
import { AutoDiscovery, AutoDiscoveryAction } from "./autodiscovery";
import * as olmlib from "./crypto/olmlib";
import { decodeBase64, encodeBase64, encodeUnpaddedBase64Url } from "./base64";
import { IExportedDevice as IExportedOlmDevice } from "./crypto/OlmDevice";
import { IOlmDevice } from "./crypto/algorithms/megolm";
import { TypedReEmitter } from "./ReEmitter";
import { IRoomEncryption } from "./crypto/RoomList";
import { logger, Logger } from "./logger";
import { SERVICE_TYPES } from "./service-types";
} from "./webrtc/groupCallEventHandler.ts";
import * as utils from "./utils.ts";
import { noUnsafeEventProps, QueryDict, replaceParam, safeSet, sleep } from "./utils.ts";
import { Direction, EventTimeline } from "./models/event-timeline.ts";
import { IActionsObject, PushProcessor } from "./pushprocessor.ts";
import { AutoDiscovery, AutoDiscoveryAction } from "./autodiscovery.ts";
import * as olmlib from "./crypto/olmlib.ts";
import { decodeBase64, encodeBase64, encodeUnpaddedBase64Url } from "./base64.ts";
import { IExportedDevice as IExportedOlmDevice } from "./crypto/OlmDevice.ts";
import { IOlmDevice } from "./crypto/algorithms/megolm.ts";
import { TypedReEmitter } from "./ReEmitter.ts";
import { IRoomEncryption } from "./crypto/RoomList.ts";
import { logger, Logger } from "./logger.ts";
import { SERVICE_TYPES } from "./service-types.ts";
import {
Body,
ClientPrefix,
@@ -73,7 +73,7 @@ import {
Upload,
UploadOpts,
UploadResponse,
} from "./http-api";
} from "./http-api/index.ts";
import {
Crypto,
CryptoEvent,
@@ -83,14 +83,12 @@ import {
ICryptoCallbacks,
IRoomKeyRequestBody,
isCryptoAvailable,
} from "./crypto";
import { DeviceInfo } from "./crypto/deviceinfo";
import { decodeRecoveryKey } from "./crypto/recoverykey";
import { keyFromAuthData } from "./crypto/key_passphrase";
import { User, UserEvent, UserEventHandlerMap } from "./models/user";
import { getHttpUriForMxc } from "./content-repo";
import { SearchResult } from "./models/search-result";
import { DEHYDRATION_ALGORITHM, IDehydratedDevice, IDehydratedDeviceKeyInfo } from "./crypto/dehydration";
} from "./crypto/index.ts";
import { DeviceInfo } from "./crypto/deviceinfo.ts";
import { User, UserEvent, UserEventHandlerMap } from "./models/user.ts";
import { getHttpUriForMxc } from "./content-repo.ts";
import { SearchResult } from "./models/search-result.ts";
import { DEHYDRATION_ALGORITHM, IDehydratedDevice, IDehydratedDeviceKeyInfo } from "./crypto/dehydration.ts";
import {
IKeyBackupInfo,
IKeyBackupPrepareOpts,
@@ -98,21 +96,21 @@ import {
IKeyBackupRestoreResult,
IKeyBackupRoomSessions,
IKeyBackupSession,
} from "./crypto/keybackup";
import { IIdentityServerProvider } from "./@types/IIdentityServerProvider";
import { MatrixScheduler } from "./scheduler";
import { BeaconEvent, BeaconEventHandlerMap } from "./models/beacon";
import { AuthDict } from "./interactive-auth";
import { IMinimalEvent, IRoomEvent, IStateEvent } from "./sync-accumulator";
import { CrossSigningKey, ICreateSecretStorageOpts, IEncryptedEventInfo, IRecoveryKey } from "./crypto/api";
import { EventTimelineSet } from "./models/event-timeline-set";
import { VerificationRequest } from "./crypto/verification/request/VerificationRequest";
import { VerificationBase as Verification } from "./crypto/verification/Base";
import * as ContentHelpers from "./content-helpers";
import { CrossSigningInfo, DeviceTrustLevel, ICacheCallbacks, UserTrustLevel } from "./crypto/CrossSigning";
import { NotificationCountType, Room, RoomEvent, RoomEventHandlerMap, RoomNameState } from "./models/room";
import { RoomMemberEvent, RoomMemberEventHandlerMap } from "./models/room-member";
import { IPowerLevelsContent, RoomStateEvent, RoomStateEventHandlerMap } from "./models/room-state";
} from "./crypto/keybackup.ts";
import { IIdentityServerProvider } from "./@types/IIdentityServerProvider.ts";
import { MatrixScheduler } from "./scheduler.ts";
import { BeaconEvent, BeaconEventHandlerMap } from "./models/beacon.ts";
import { AuthDict } from "./interactive-auth.ts";
import { IMinimalEvent, IRoomEvent, IStateEvent } from "./sync-accumulator.ts";
import { CrossSigningKey, ICreateSecretStorageOpts, IEncryptedEventInfo, IRecoveryKey } from "./crypto/api.ts";
import { EventTimelineSet } from "./models/event-timeline-set.ts";
import { VerificationRequest } from "./crypto/verification/request/VerificationRequest.ts";
import { VerificationBase as Verification } from "./crypto/verification/Base.ts";
import * as ContentHelpers from "./content-helpers.ts";
import { CrossSigningInfo, DeviceTrustLevel, ICacheCallbacks, UserTrustLevel } from "./crypto/CrossSigning.ts";
import { NotificationCountType, Room, RoomEvent, RoomEventHandlerMap, RoomNameState } from "./models/room.ts";
import { RoomMemberEvent, RoomMemberEventHandlerMap } from "./models/room-member.ts";
import { IPowerLevelsContent, RoomStateEvent, RoomStateEventHandlerMap } from "./models/room-state.ts";
import {
DelayedEventInfo,
IAddThreePidOnlyBody,
@@ -138,7 +136,7 @@ import {
SendDelayedEventRequestOpts,
SendDelayedEventResponse,
UpdateDelayedEventAction,
} from "./@types/requests";
} from "./@types/requests.ts";
import {
EventType,
LOCAL_NOTIFICATION_SETTINGS_PREFIX,
@@ -153,15 +151,22 @@ import {
UNSTABLE_MSC3088_ENABLED,
UNSTABLE_MSC3088_PURPOSE,
UNSTABLE_MSC3089_TREE_SUBTYPE,
} from "./@types/event";
import { GuestAccess, HistoryVisibility, IdServerUnbindResult, JoinRule, Preset, Visibility } from "./@types/partials";
import { EventMapper, eventMapperFor, MapperOpts } from "./event-mapper";
import { randomString } from "./randomstring";
import { BackupManager, IKeyBackup, IKeyBackupCheck, IPreparedKeyBackupVersion, TrustInfo } from "./crypto/backup";
import { DEFAULT_TREE_POWER_LEVELS_TEMPLATE, MSC3089TreeSpace } from "./models/MSC3089TreeSpace";
import { ISignatures } from "./@types/signed";
import { IStore } from "./store";
import { ISecretRequest } from "./crypto/SecretStorage";
} from "./@types/event.ts";
import {
GuestAccess,
HistoryVisibility,
IdServerUnbindResult,
JoinRule,
Preset,
Visibility,
} from "./@types/partials.ts";
import { EventMapper, eventMapperFor, MapperOpts } from "./event-mapper.ts";
import { randomString } from "./randomstring.ts";
import { BackupManager, IKeyBackup, IKeyBackupCheck, IPreparedKeyBackupVersion, TrustInfo } from "./crypto/backup.ts";
import { DEFAULT_TREE_POWER_LEVELS_TEMPLATE, MSC3089TreeSpace } from "./models/MSC3089TreeSpace.ts";
import { ISignatures } from "./@types/signed.ts";
import { IStore } from "./store/index.ts";
import { ISecretRequest } from "./crypto/SecretStorage.ts";
import {
IEventWithRoomId,
ISearchRequestBody,
@@ -169,9 +174,9 @@ import {
ISearchResults,
IStateEventWithRoomId,
SearchOrderBy,
} from "./@types/search";
import { ISynapseAdminDeactivateResponse, ISynapseAdminWhoisResponse } from "./@types/synapse";
import { IHierarchyRoom } from "./@types/spaces";
} from "./@types/search.ts";
import { ISynapseAdminDeactivateResponse, ISynapseAdminWhoisResponse } from "./@types/synapse.ts";
import { IHierarchyRoom } from "./@types/spaces.ts";
import {
IPusher,
IPusherRequest,
@@ -181,11 +186,11 @@ import {
PushRuleActionName,
PushRuleKind,
RuleId,
} from "./@types/PushRules";
import { IThreepid } from "./@types/threepids";
import { CryptoStore, OutgoingRoomKeyRequest } from "./crypto/store/base";
import { GroupCall, GroupCallIntent, GroupCallType, IGroupCallDataChannelOptions } from "./webrtc/groupCall";
import { MediaHandler } from "./webrtc/mediaHandler";
} from "./@types/PushRules.ts";
import { IThreepid } from "./@types/threepids.ts";
import { CryptoStore, OutgoingRoomKeyRequest } from "./crypto/store/base.ts";
import { GroupCall, GroupCallIntent, GroupCallType, IGroupCallDataChannelOptions } from "./webrtc/groupCall.ts";
import { MediaHandler } from "./webrtc/mediaHandler.ts";
import {
ILoginFlowsResponse,
IRefreshTokenResponse,
@@ -193,11 +198,11 @@ import {
LoginResponse,
LoginTokenPostResponse,
SSOAction,
} from "./@types/auth";
import { TypedEventEmitter } from "./models/typed-event-emitter";
import { MAIN_ROOM_TIMELINE, ReceiptType } from "./@types/read_receipts";
import { MSC3575SlidingSyncRequest, MSC3575SlidingSyncResponse, SlidingSync } from "./sliding-sync";
import { SlidingSyncSdk } from "./sliding-sync-sdk";
} from "./@types/auth.ts";
import { TypedEventEmitter } from "./models/typed-event-emitter.ts";
import { MAIN_ROOM_TIMELINE, ReceiptType } from "./@types/read_receipts.ts";
import { MSC3575SlidingSyncRequest, MSC3575SlidingSyncResponse, SlidingSync } from "./sliding-sync.ts";
import { SlidingSyncSdk } from "./sliding-sync-sdk.ts";
import {
determineFeatureSupport,
FeatureSupport,
@@ -205,33 +210,40 @@ import {
THREAD_RELATION_TYPE,
ThreadFilterType,
threadFilterTypeToFilter,
} from "./models/thread";
import { M_BEACON_INFO, MBeaconInfoEventContent } from "./@types/beacon";
import { NamespacedValue, UnstableValue } from "./NamespacedValue";
import { ToDeviceMessageQueue } from "./ToDeviceMessageQueue";
import { ToDeviceBatch } from "./models/ToDeviceMessage";
import { IgnoredInvites } from "./models/invites-ignorer";
import { UIARequest, UIAResponse } from "./@types/uia";
import { LocalNotificationSettings } from "./@types/local_notifications";
import { buildFeatureSupportMap, Feature, ServerSupport } from "./feature";
import { BackupDecryptor, CryptoBackend } from "./common-crypto/CryptoBackend";
import { RUST_SDK_STORE_PREFIX } from "./rust-crypto/constants";
import { BootstrapCrossSigningOpts, CrossSigningKeyInfo, CryptoApi, ImportRoomKeysOpts } from "./crypto-api";
import { DeviceInfoMap } from "./crypto/DeviceList";
} from "./models/thread.ts";
import { M_BEACON_INFO, MBeaconInfoEventContent } from "./@types/beacon.ts";
import { NamespacedValue, UnstableValue } from "./NamespacedValue.ts";
import { ToDeviceMessageQueue } from "./ToDeviceMessageQueue.ts";
import { ToDeviceBatch } from "./models/ToDeviceMessage.ts";
import { IgnoredInvites } from "./models/invites-ignorer.ts";
import { UIARequest, UIAResponse } from "./@types/uia.ts";
import { LocalNotificationSettings } from "./@types/local_notifications.ts";
import { buildFeatureSupportMap, Feature, ServerSupport } from "./feature.ts";
import { BackupDecryptor, CryptoBackend } from "./common-crypto/CryptoBackend.ts";
import { RUST_SDK_STORE_PREFIX } from "./rust-crypto/constants.ts";
import {
BootstrapCrossSigningOpts,
CrossSigningKeyInfo,
CryptoApi,
decodeRecoveryKey,
ImportRoomKeysOpts,
} from "./crypto-api/index.ts";
import { DeviceInfoMap } from "./crypto/DeviceList.ts";
import {
AddSecretStorageKeyOpts,
SecretStorageKeyDescription,
ServerSideSecretStorage,
ServerSideSecretStorageImpl,
} from "./secret-storage";
import { RegisterRequest, RegisterResponse } from "./@types/registration";
import { MatrixRTCSessionManager } from "./matrixrtc/MatrixRTCSessionManager";
import { getRelationsThreadFilter } from "./thread-utils";
import { KnownMembership, Membership } from "./@types/membership";
import { RoomMessageEventContent, StickerEventContent } from "./@types/events";
import { ImageInfo } from "./@types/media";
import { Capabilities, ServerCapabilities } from "./serverCapabilities";
import { sha256 } from "./digest";
} from "./secret-storage.ts";
import { RegisterRequest, RegisterResponse } from "./@types/registration.ts";
import { MatrixRTCSessionManager } from "./matrixrtc/MatrixRTCSessionManager.ts";
import { getRelationsThreadFilter } from "./thread-utils.ts";
import { KnownMembership, Membership } from "./@types/membership.ts";
import { RoomMessageEventContent, StickerEventContent } from "./@types/events.ts";
import { ImageInfo } from "./@types/media.ts";
import { Capabilities, ServerCapabilities } from "./serverCapabilities.ts";
import { sha256 } from "./digest.ts";
import { keyFromAuthData } from "./common-crypto/key-passphrase.ts";
export type Store = IStore;
@@ -537,6 +549,8 @@ export const UNSTABLE_MSC2666_QUERY_MUTUAL_ROOMS = "uk.half-shot.msc2666.query_m
export const UNSTABLE_MSC4140_DELAYED_EVENTS = "org.matrix.msc4140";
export const UNSTABLE_MSC4133_EXTENDED_PROFILES = "uk.tcpip.msc4133";
enum CrossSigningKeyType {
MasterKey = "master_key",
SelfSigningKey = "self_signing_key",
@@ -1583,6 +1597,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @returns Promise which resolves to undefined if a device could not be dehydrated, or
* to the new device ID if the dehydration was successful.
* @returns Rejects: with an error response.
*
* @deprecated MSC2697 device dehydration is not supported for rust cryptography.
*/
public async rehydrateDevice(): Promise<string | undefined> {
if (this.crypto) {
@@ -1683,6 +1699,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param deviceDisplayName - The device display name for the
* dehydrated device.
* @returns A promise that resolves when the dehydrated device is stored.
*
* @deprecated Not supported for Rust Cryptography.
*/
public async setDehydrationKey(
key: Uint8Array,
@@ -1697,13 +1715,15 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
}
/**
* Creates a new dehydrated device (without queuing periodic dehydration)
* Creates a new MSC2967 dehydrated device (without queuing periodic dehydration)
* @param key - the dehydration key
* @param keyInfo - Information about the key. Primarily for
* information about how to generate the key from a passphrase.
* @param deviceDisplayName - The device display name for the
* dehydrated device.
* @returns the device id of the newly created dehydrated device
*
* @deprecated Not supported for Rust Cryptography. Prefer {@link CryptoApi.startDehydration}.
*/
public async createDehydratedDevice(
key: Uint8Array,
@@ -1718,6 +1738,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
return this.crypto.dehydrationManager.dehydrateDevice();
}
/** @deprecated Not supported for Rust Cryptography. */
public async exportDevice(): Promise<IExportedDevice | undefined> {
if (!this.crypto) {
this.logger.warn("not exporting device if crypto is not enabled");
@@ -2114,6 +2135,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
*
* It will return a Promise which will resolve when the crypto layer has been
* successfully initialised.
*
* @deprecated libolm is deprecated. Prefer {@link initRustCrypto}.
*/
public async initCrypto(): Promise<void> {
if (!isCryptoAvailable()) {
@@ -2231,6 +2254,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
// importing rust-crypto will download the webassembly, so we delay it until we know it will be
// needed.
this.logger.debug("Downloading Rust crypto library");
// blocked on https://github.com/matrix-org/matrix-js-sdk/issues/4392 / https://github.com/babel/babel/issues/16750
// eslint-disable-next-line node/file-extension-in-import
const RustCrypto = await import("./rust-crypto");
const rustCrypto = await RustCrypto.initRustCrypto({
@@ -2307,7 +2332,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @returns base64-encoded ed25519 key. Null if crypto is
* disabled.
*
* @deprecated Prefer {@link CryptoApi.getOwnDeviceKeys}
* @deprecated Not supported for Rust Cryptography.Prefer {@link CryptoApi.getOwnDeviceKeys}
*/
public getDeviceEd25519Key(): string | null {
return this.crypto?.getDeviceEd25519Key() ?? null;
@@ -2319,7 +2344,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @returns base64-encoded curve25519 key. Null if crypto is
* disabled.
*
* @deprecated Use {@link CryptoApi.getOwnDeviceKeys}
* @deprecated Not supported for Rust Cryptography. Use {@link CryptoApi.getOwnDeviceKeys}
*/
public getDeviceCurve25519Key(): string | null {
return this.crypto?.getDeviceCurve25519Key() ?? null;
@@ -2340,7 +2365,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
*
* @returns A promise which resolves to a map userId-\>deviceId-\>`DeviceInfo`
*
* @deprecated Prefer {@link CryptoApi.getUserDeviceInfo}
* @deprecated Not supported for Rust Cryptography. Prefer {@link CryptoApi.getUserDeviceInfo}
*/
public downloadKeys(userIds: string[], forceDownload?: boolean): Promise<DeviceInfoMap> {
if (!this.crypto) {
@@ -2355,7 +2380,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param userId - the user to list keys for.
*
* @returns list of devices
* @deprecated Prefer {@link CryptoApi.getUserDeviceInfo}
* @deprecated Not supported for Rust Cryptography. Prefer {@link CryptoApi.getUserDeviceInfo}
*/
public getStoredDevicesForUser(userId: string): DeviceInfo[] {
if (!this.crypto) {
@@ -2371,7 +2396,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param deviceId - unique identifier for the device
*
* @returns device or null
* @deprecated Prefer {@link CryptoApi.getUserDeviceInfo}
* @deprecated Not supported for Rust Cryptography. Prefer {@link CryptoApi.getUserDeviceInfo}
*/
public getStoredDevice(userId: string, deviceId: string): DeviceInfo | null {
if (!this.crypto) {
@@ -2421,6 +2446,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
*
* @remarks
* Fires {@link CryptoEvent.DeviceVerificationChanged}
*
* @deprecated Not supported for Rust Cryptography.
*/
public setDeviceBlocked(userId: string, deviceId: string, blocked = true): Promise<void> {
return this.setDeviceVerification(userId, deviceId, null, blocked, null);
@@ -2440,6 +2467,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
*
* @remarks
* Fires {@link CryptoEvent#DeviceVerificationChanged}
*
* @deprecated Not supported for Rust Cryptography.
*/
public setDeviceKnown(userId: string, deviceId: string, known = true): Promise<void> {
return this.setDeviceVerification(userId, deviceId, null, null, known);
@@ -2467,7 +2496,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @returns resolves to a VerificationRequest
* when the request has been sent to the other party.
*
* @deprecated Prefer {@link CryptoApi.requestVerificationDM}.
* @deprecated Not supported for Rust Cryptography. Prefer {@link CryptoApi.requestVerificationDM}.
*/
public requestVerificationDM(userId: string, roomId: string): Promise<VerificationRequest> {
if (!this.crypto) {
@@ -2482,7 +2511,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param roomId - the room to use for verification
*
* @returns the VerificationRequest that is in progress, if any
* @deprecated Prefer {@link CryptoApi.findVerificationRequestDMInProgress}.
* @deprecated Not supported for Rust Cryptography. Prefer {@link CryptoApi.findVerificationRequestDMInProgress}.
*/
public findVerificationRequestDMInProgress(roomId: string): VerificationRequest | undefined {
if (!this.cryptoBackend) {
@@ -2501,7 +2530,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param userId - the ID of the user to query
*
* @returns the VerificationRequests that are in progress
* @deprecated Prefer {@link CryptoApi.getVerificationRequestsToDeviceInProgress}.
* @deprecated Not supported for Rust Cryptography. Prefer {@link CryptoApi.getVerificationRequestsToDeviceInProgress}.
*/
public getVerificationRequestsToDeviceInProgress(userId: string): VerificationRequest[] {
if (!this.crypto) {
@@ -2520,7 +2549,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @returns resolves to a VerificationRequest
* when the request has been sent to the other party.
*
* @deprecated Prefer {@link CryptoApi#requestOwnUserVerification} or {@link CryptoApi#requestDeviceVerification}.
* @deprecated Not supported for Rust Cryptography. Prefer {@link CryptoApi#requestOwnUserVerification} or {@link CryptoApi#requestDeviceVerification}.
*/
public requestVerification(userId: string, devices?: string[]): Promise<VerificationRequest> {
if (!this.crypto) {
@@ -2537,7 +2566,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param deviceId - the device to verify
*
* @returns a verification object
* @deprecated Use `requestVerification` instead.
* @deprecated Prefer {@link CryptoApi#requestOwnUserVerification} or {@link CryptoApi#requestDeviceVerification}.
*/
public beginKeyVerification(method: string, userId: string, deviceId: string): Verification<any, any> {
if (!this.crypto) {
@@ -2632,7 +2661,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* "master", "self_signing", or "user_signing". Defaults to "master".
*
* @returns the key ID
* @deprecated prefer {@link Crypto.CryptoApi#getCrossSigningKeyId}
* @deprecated Not supported for Rust Cryptography. prefer {@link Crypto.CryptoApi#getCrossSigningKeyId}
*/
public getCrossSigningId(type: CrossSigningKey | string = CrossSigningKey.Master): string | null {
if (!this.crypto) {
@@ -2649,7 +2678,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param userId - the user ID to get the cross-signing info for.
*
* @returns the cross signing information for the user.
* @deprecated Prefer {@link CryptoApi#userHasCrossSigningKeys}
* @deprecated Not supported for Rust Cryptography. Prefer {@link CryptoApi#userHasCrossSigningKeys}
*/
public getStoredCrossSigningForUser(userId: string): CrossSigningInfo | null {
if (!this.cryptoBackend) {
@@ -2698,6 +2727,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param deviceId - The ID of the device to check
*
* @returns true if the device is cross-signed
*
* @deprecated Not supported for Rust Cryptography.
*/
public checkIfOwnDeviceCrossSigned(deviceId: string): boolean {
if (!this.crypto) {
@@ -2727,6 +2758,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param privateKey - The private key
* @param expectedPublicKey - The public key
* @returns true if the key matches, otherwise false
*
* @deprecated Not supported for Rust Cryptography.
*/
public checkCrossSigningPrivateKey(privateKey: Uint8Array, expectedPublicKey: string): boolean {
if (!this.crypto) {
@@ -2735,7 +2768,9 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
return this.crypto.checkCrossSigningPrivateKey(privateKey, expectedPublicKey);
}
// deprecated: use requestVerification instead
/**
* @deprecated Not supported for Rust Cryptography. Prefer {@link CryptoApi#requestDeviceVerification}.
*/
public legacyDeviceVerification(userId: string, deviceId: string, method: string): Promise<VerificationRequest> {
if (!this.crypto) {
throw new Error("End-to-end encryption disabled");
@@ -2852,6 +2887,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
/**
* Counts the number of end to end session keys that are waiting to be backed up
* @returns Promise which resolves to the number of sessions requiring backup
*
* @deprecated Not supported for Rust Cryptography.
*/
public countSessionsNeedingBackup(): Promise<number> {
if (!this.crypto) {
@@ -3033,6 +3070,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param devices - the devices to request the secret from
*
* @returns the secret request object
* @deprecated Not supported for Rust Cryptography.
*/
public requestSecret(name: string, devices: string[]): ISecretRequest {
if (!this.crypto) {
@@ -3092,6 +3130,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* Get e2e information on the device that sent an event
*
* @param event - event to be checked
* @deprecated Not supported for Rust Cryptography.
*/
public async getEventSenderDeviceInfo(event: MatrixEvent): Promise<DeviceInfo | null> {
if (!this.crypto) {
@@ -3121,6 +3160,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param event - The event to check for
*
* @returns A room key request, or null if there is none
*
* @deprecated Not supported for Rust Cryptography.
*/
public getOutgoingRoomKeyRequest(event: MatrixEvent): Promise<OutgoingRoomKeyRequest | null> {
if (!this.crypto) {
@@ -3145,6 +3186,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param event - event of which to cancel and resend the room
* key request.
* @returns A promise that will resolve when the key request is queued
*
* @deprecated Not supported for Rust Cryptography.
*/
public cancelAndResendEventRoomKeyRequest(event: MatrixEvent): Promise<void> {
if (!this.crypto) {
@@ -3210,6 +3253,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* resolves once the message has been encrypted and sent to the given
* userDeviceMap, and returns the `{ contentMap, deviceInfoByDeviceId }`
* of the successfully sent messages.
*
* @deprecated Not supported for Rust Cryptography.
*/
public encryptAndSendToDevices(userDeviceInfoArr: IOlmDevice<DeviceInfo>[], payload: object): Promise<void> {
if (!this.crypto) {
@@ -3225,7 +3270,6 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param roomId - The ID of the room to discard the session for
*
* @deprecated Prefer {@link CryptoApi.forceDiscardSession | `CryptoApi.forceDiscardSession`}:
*
*/
public forceDiscardSession(roomId: string): void {
if (!this.cryptoBackend) {
@@ -3326,7 +3370,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
/**
* @param info - key backup info dict from getKeyBackupVersion()
*
* @deprecated Prefer {@link CryptoApi.isKeyBackupTrusted | `CryptoApi.isKeyBackupTrusted`}.
* @deprecated Not supported for Rust Cryptography. Prefer {@link CryptoApi.isKeyBackupTrusted | `CryptoApi.isKeyBackupTrusted`}.
*/
public isKeyBackupTrusted(info: IKeyBackupInfo): Promise<TrustInfo> {
if (!this.crypto) {
@@ -3340,7 +3384,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* the server, otherwise false. If we haven't completed a successful check
* of key backup status yet, returns null.
*
* @deprecated Prefer direct access to {@link Crypto.CryptoApi.getActiveSessionBackupVersion}:
* @deprecated Not supported for Rust Cryptography. Prefer direct access to {@link Crypto.CryptoApi.getActiveSessionBackupVersion}:
*
* ```javascript
* let enabled = (await client.getCrypto().getActiveSessionBackupVersion()) !== null;
@@ -3373,7 +3417,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
/**
* Disable backing up of keys.
*
* @deprecated It should be unnecessary to disable key backup.
* @deprecated Not supported for Rust Cryptography. It should be unnecessary to disable key backup.
*/
public disableKeyBackup(): void {
if (!this.crypto) {
@@ -3394,7 +3438,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @returns Object that can be passed to createKeyBackupVersion and
* additionally has a 'recovery_key' member with the user-facing recovery key string.
*
* @deprecated Use {@link Crypto.CryptoApi.resetKeyBackup | `CryptoApi.resetKeyBackup`}.
* @deprecated Not supported for Rust cryptography. Use {@link Crypto.CryptoApi.resetKeyBackup | `CryptoApi.resetKeyBackup`}.
*/
public async prepareKeyBackupVersion(
password?: string | Uint8Array | null,
@@ -3522,6 +3566,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param data - Object keys to send
* @returns a promise that will resolve when the keys
* are uploaded
*
* @deprecated Not supported for Rust Cryptography.
*/
public sendKeyBackup(
roomId: undefined,
@@ -3559,8 +3605,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* Marks all group sessions as needing to be backed up and schedules them to
* upload in the background as soon as possible.
*
* (This is done automatically as part of {@link CryptoApi.resetKeyBackup},
* so there is probably no need to call this manually.)
* @deprecated Not supported for Rust Cryptography. This is done automatically as part of
* {@link CryptoApi.resetKeyBackup}, so there is probably no need to call this manually.
*/
public async scheduleAllGroupSessionsForBackup(): Promise<void> {
if (!this.crypto) {
@@ -3578,6 +3624,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* so there is probably no need to call this manually.)
*
* @returns Promise which resolves to the number of sessions requiring a backup.
* @deprecated Not supported for Rust Cryptography.
*/
public flagAllGroupSessionsForBackup(): Promise<number> {
if (!this.crypto) {
@@ -3587,6 +3634,12 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
return this.crypto.backupManager.flagAllGroupSessionsForBackup();
}
/**
* Return true if recovery key is valid.
* Try to decode the recovery key and check if it's successful.
* @param recoveryKey
* @deprecated Use {@link decodeRecoveryKey} directly
*/
public isValidRecoveryKey(recoveryKey: string): boolean {
try {
decodeRecoveryKey(recoveryKey);
@@ -3605,6 +3658,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param password - Passphrase
* @param backupInfo - Backup metadata from `checkKeyBackup`
* @returns key backup key
* @deprecated Deriving a backup key from a passphrase is not part of the matrix spec. Instead, a random key is generated and stored/shared via 4S.
*/
public keyBackupKeyFromPassword(password: string, backupInfo: IKeyBackupInfo): Promise<Uint8Array> {
return keyFromAuthData(backupInfo.auth_data, password);
@@ -3618,6 +3672,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
*
* @param recoveryKey - The recovery key
* @returns key backup key
* @deprecated Use {@link decodeRecoveryKey} directly
*/
public keyBackupKeyFromRecoveryKey(recoveryKey: string): Uint8Array {
return decodeRecoveryKey(recoveryKey);
@@ -4028,10 +4083,6 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
public deleteKeysFromBackup(roomId: string, sessionId: undefined, version?: string): Promise<void>;
public deleteKeysFromBackup(roomId: string, sessionId: string, version?: string): Promise<void>;
public async deleteKeysFromBackup(roomId?: string, sessionId?: string, version?: string): Promise<void> {
if (!this.crypto) {
throw new Error("End-to-end encryption disabled");
}
const path = this.makeKeyBackupPath(roomId!, sessionId!, version);
await this.http.authedRequest(Method.Delete, path.path, path.queryData, undefined, { prefix: ClientPrefix.V3 });
}
@@ -4042,6 +4093,9 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param roomId - the room for which keys should be shared.
* @param userIds - a list of users to share with. The keys will be sent to
* all of the user's current devices.
*
* @deprecated Do not use this method. It does not work with the Rust crypto stack, and even with the legacy
* stack it introduces a security vulnerability.
*/
public async sendSharedHistoryKeys(roomId: string, userIds: string[]): Promise<void> {
if (!this.crypto) {
@@ -4285,13 +4339,11 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
signPromise = this.http.requestOtherUrl<IThirdPartySigned>(Method.Post, url);
}
let queryParams: QueryDict = {};
const queryParams: QueryDict = {};
if (opts.viaServers) {
// server_name has been deprecated in favour of via with Matrix >1.11 (MSC4156)
queryParams.server_name = opts.viaServers;
queryParams.via = opts.viaServers;
if (this.canSupport.get(Feature.MigrateServerNameToVia) === ServerSupport.Unstable) {
queryParams = replaceParam("via", "org.matrix.msc4156.via", queryParams);
}
}
const data: IJoinRequestBody = {};
@@ -4334,13 +4386,11 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
const path = utils.encodeUri("/knock/$roomIdOrAlias", { $roomIdOrAlias: roomIdOrAlias });
let queryParams: QueryDict = {};
const queryParams: QueryDict = {};
if (opts.viaServers) {
// server_name has been deprecated in favour of via with Matrix >1.11 (MSC4156)
queryParams.server_name = opts.viaServers;
queryParams.via = opts.viaServers;
if (this.canSupport.get(Feature.MigrateServerNameToVia) === ServerSupport.Unstable) {
queryParams = replaceParam("via", "org.matrix.msc4156.via", queryParams);
}
}
const body: Record<string, string> = {};
@@ -5587,10 +5637,15 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
private findPredecessorRooms(room: Room, verifyLinks: boolean, msc3946ProcessDynamicPredecessor: boolean): Room[] {
const ret: Room[] = [];
const seenRoomIDs = new Set<string>([room.roomId]);
// Work backwards from newer to older rooms
let predecessorRoomId = room.findPredecessor(msc3946ProcessDynamicPredecessor)?.roomId;
while (predecessorRoomId !== null) {
if (predecessorRoomId) {
if (seenRoomIDs.has(predecessorRoomId)) break;
seenRoomIDs.add(predecessorRoomId);
}
const predecessorRoom = this.getRoom(predecessorRoomId);
if (predecessorRoom === null) {
break;
@@ -7899,6 +7954,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
/**
* The app may wish to see if we have a key cached without
* triggering a user interaction.
*
* @deprecated Not supported for Rust Cryptography.
*/
public getCrossSigningCacheCallbacks(): ICacheCallbacks | undefined {
// XXX: Private member access
@@ -8766,6 +8823,183 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
return this.http.authedRequest(Method.Get, path);
}
/**
* Determine if the server supports extended profiles, as described by MSC4133.
*
* @returns `true` if supported, otherwise `false`
*/
public async doesServerSupportExtendedProfiles(): Promise<boolean> {
return this.doesServerSupportUnstableFeature(UNSTABLE_MSC4133_EXTENDED_PROFILES);
}
/**
* Get the prefix used for extended profile requests.
*
* @returns The prefix for use with `authedRequest`
*/
private async getExtendedProfileRequestPrefix(): Promise<string> {
if (await this.doesServerSupportUnstableFeature("uk.tcpip.msc4133.stable")) {
return ClientPrefix.V3;
}
return "/_matrix/client/unstable/uk.tcpip.msc4133";
}
/**
* Fetch a user's *extended* profile, which may include additonal keys.
*
* @see https://github.com/tcpipuk/matrix-spec-proposals/blob/main/proposals/4133-extended-profiles.md
* @param userId The user ID to fetch the profile of.
* @returns A set of keys to property values.
*
* @throws An error if the server does not support MSC4133.
* @throws A M_NOT_FOUND error if the profile could not be found.
*/
public async getExtendedProfile(userId: string): Promise<Record<string, unknown>> {
if (!(await this.doesServerSupportExtendedProfiles())) {
throw new Error("Server does not support extended profiles");
}
return this.http.authedRequest(
Method.Get,
utils.encodeUri("/profile/$userId", { $userId: userId }),
undefined,
undefined,
{
prefix: await this.getExtendedProfileRequestPrefix(),
},
);
}
/**
* Fetch a specific key from the user's *extended* profile.
*
* @see https://github.com/tcpipuk/matrix-spec-proposals/blob/main/proposals/4133-extended-profiles.md
* @param userId The user ID to fetch the profile of.
* @param key The key of the property to fetch.
* @returns The property value.
*
* @throws An error if the server does not support MSC4133.
* @throws A M_NOT_FOUND error if the key was not set OR the profile could not be found.
*/
public async getExtendedProfileProperty(userId: string, key: string): Promise<unknown> {
if (!(await this.doesServerSupportExtendedProfiles())) {
throw new Error("Server does not support extended profiles");
}
const profile = (await this.http.authedRequest(
Method.Get,
utils.encodeUri("/profile/$userId/$key", { $userId: userId, $key: key }),
undefined,
undefined,
{
prefix: await this.getExtendedProfileRequestPrefix(),
},
)) as Record<string, unknown>;
return profile[key];
}
/**
* Set a property on your *extended* profile.
*
* @see https://github.com/tcpipuk/matrix-spec-proposals/blob/main/proposals/4133-extended-profiles.md
* @param key The key of the property to set.
* @param value The value to set on the propety.
*
* @throws An error if the server does not support MSC4133 OR the server disallows editing the user profile.
*/
public async setExtendedProfileProperty(key: string, value: unknown): Promise<void> {
if (!(await this.doesServerSupportExtendedProfiles())) {
throw new Error("Server does not support extended profiles");
}
const userId = this.getUserId();
await this.http.authedRequest(
Method.Put,
utils.encodeUri("/profile/$userId/$key", { $userId: userId, $key: key }),
undefined,
{ [key]: value },
{
prefix: await this.getExtendedProfileRequestPrefix(),
},
);
}
/**
* Delete a property on your *extended* profile.
*
* @see https://github.com/tcpipuk/matrix-spec-proposals/blob/main/proposals/4133-extended-profiles.md
* @param key The key of the property to delete.
*
* @throws An error if the server does not support MSC4133 OR the server disallows editing the user profile.
*/
public async deleteExtendedProfileProperty(key: string): Promise<void> {
if (!(await this.doesServerSupportExtendedProfiles())) {
throw new Error("Server does not support extended profiles");
}
const userId = this.getUserId();
await this.http.authedRequest(
Method.Delete,
utils.encodeUri("/profile/$userId/$key", { $userId: userId, $key: key }),
undefined,
undefined,
{
prefix: await this.getExtendedProfileRequestPrefix(),
},
);
}
/**
* Update multiple properties on your *extended* profile. This will
* merge with any existing keys.
*
* @see https://github.com/tcpipuk/matrix-spec-proposals/blob/main/proposals/4133-extended-profiles.md
* @param profile The profile object to merge with the existing profile.
* @returns The newly merged profile.
*
* @throws An error if the server does not support MSC4133 OR the server disallows editing the user profile.
*/
public async patchExtendedProfile(profile: Record<string, unknown>): Promise<Record<string, unknown>> {
if (!(await this.doesServerSupportExtendedProfiles())) {
throw new Error("Server does not support extended profiles");
}
const userId = this.getUserId();
return this.http.authedRequest(
Method.Patch,
utils.encodeUri("/profile/$userId", { $userId: userId }),
{},
profile,
{
prefix: await this.getExtendedProfileRequestPrefix(),
},
);
}
/**
* Set multiple properties on your *extended* profile. This will completely
* replace the existing profile, removing any unspecified keys.
*
* @see https://github.com/tcpipuk/matrix-spec-proposals/blob/main/proposals/4133-extended-profiles.md
* @param profile The profile object to set.
*
* @throws An error if the server does not support MSC4133 OR the server disallows editing the user profile.
*/
public async setExtendedProfile(profile: Record<string, unknown>): Promise<void> {
if (!(await this.doesServerSupportExtendedProfiles())) {
throw new Error("Server does not support extended profiles");
}
const userId = this.getUserId();
await this.http.authedRequest(
Method.Put,
utils.encodeUri("/profile/$userId", { $userId: userId }),
{},
profile,
{
prefix: await this.getExtendedProfileRequestPrefix(),
},
);
}
/**
* @returns Promise which resolves to a list of the user's threepids.
* @returns Rejects: with an error response.
+8 -8
View File
@@ -14,14 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import type { IDeviceLists, IToDeviceEvent } from "../sync-accumulator";
import { IClearEvent, MatrixEvent } from "../models/event";
import { Room } from "../models/room";
import { CryptoApi, DecryptionFailureCode, ImportRoomKeysOpts } from "../crypto-api";
import { CrossSigningInfo, UserTrustLevel } from "../crypto/CrossSigning";
import { IEncryptedEventInfo } from "../crypto/api";
import { KeyBackupInfo, KeyBackupSession } from "../crypto-api/keybackup";
import { IMegolmSessionData } from "../@types/crypto";
import type { IDeviceLists, IToDeviceEvent } from "../sync-accumulator.ts";
import { IClearEvent, MatrixEvent } from "../models/event.ts";
import { Room } from "../models/room.ts";
import { CryptoApi, DecryptionFailureCode, ImportRoomKeysOpts } from "../crypto-api/index.ts";
import { CrossSigningInfo, UserTrustLevel } from "../crypto/CrossSigning.ts";
import { IEncryptedEventInfo } from "../crypto/api.ts";
import { KeyBackupInfo, KeyBackupSession } from "../crypto-api/keybackup.ts";
import { IMegolmSessionData } from "../@types/crypto.ts";
/**
* Common interface for the crypto implementations
+43
View File
@@ -0,0 +1,43 @@
/*
* Copyright 2024 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { deriveRecoveryKeyFromPassphrase } from "../crypto-api/index.ts";
/* eslint-disable camelcase */
interface IAuthData {
private_key_salt?: string;
private_key_iterations?: number;
private_key_bits?: number;
}
/**
* Derive a backup key from a passphrase using the salt and iterations from the auth data.
* @param authData - The auth data containing the salt and iterations
* @param passphrase - The passphrase to derive the key from
* @deprecated Deriving a backup key from a passphrase is not part of the matrix spec. Instead, a random key is generated and stored/shared via 4S.
*/
export function keyFromAuthData(authData: IAuthData, passphrase: string): Promise<Uint8Array> {
if (!authData.private_key_salt || !authData.private_key_iterations) {
throw new Error("Salt and/or iterations not found: " + "this backup cannot be restored with a passphrase");
}
return deriveRecoveryKeyFromPassphrase(
passphrase,
authData.private_key_salt,
authData.private_key_iterations,
authData.private_key_bits,
);
}
+7 -7
View File
@@ -14,10 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { MBeaconEventContent, MBeaconInfoContent, MBeaconInfoEventContent } from "./@types/beacon";
import { MsgType } from "./@types/event";
import { M_TEXT, REFERENCE_RELATION } from "./@types/extensible_events";
import { isProvided } from "./extensible_events_v1/utilities";
import { MBeaconEventContent, MBeaconInfoContent, MBeaconInfoEventContent } from "./@types/beacon.ts";
import { MsgType } from "./@types/event.ts";
import { M_TEXT, REFERENCE_RELATION } from "./@types/extensible_events.ts";
import { isProvided } from "./extensible_events_v1/utilities.ts";
import {
M_ASSET,
LocationAssetType,
@@ -28,9 +28,9 @@ import {
MLocationContent,
MAssetContent,
LegacyLocationEventContent,
} from "./@types/location";
import { MRoomTopicEventContent, MTopicContent, M_TOPIC } from "./@types/topic";
import { RoomMessageEventContent } from "./@types/events";
} from "./@types/location.ts";
import { MRoomTopicEventContent, MTopicContent, M_TOPIC } from "./@types/topic.ts";
import { RoomMessageEventContent } from "./@types/events.ts";
/**
* Generates the content for a HTML Message event
+1 -1
View File
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { encodeParams } from "./utils";
import { encodeParams } from "./utils.ts";
/**
* Get the HTTP URL for an MXC URI.
+75 -11
View File
@@ -15,15 +15,15 @@ limitations under the License.
*/
import type { SecretsBundle } from "@matrix-org/matrix-sdk-crypto-wasm";
import type { IMegolmSessionData } from "../@types/crypto";
import { Room } from "../models/room";
import { DeviceMap } from "../models/device";
import { UIAuthCallback } from "../interactive-auth";
import { PassphraseInfo, SecretStorageCallbacks, SecretStorageKeyDescription } from "../secret-storage";
import { VerificationRequest } from "./verification";
import { BackupTrustInfo, KeyBackupCheck, KeyBackupInfo } from "./keybackup";
import { ISignatures } from "../@types/signed";
import { MatrixEvent } from "../models/event";
import type { IMegolmSessionData } from "../@types/crypto.ts";
import { Room } from "../models/room.ts";
import { DeviceMap } from "../models/device.ts";
import { UIAuthCallback } from "../interactive-auth.ts";
import { PassphraseInfo, SecretStorageCallbacks, SecretStorageKeyDescription } from "../secret-storage.ts";
import { VerificationRequest } from "./verification.ts";
import { BackupTrustInfo, KeyBackupCheck, KeyBackupInfo } from "./keybackup.ts";
import { ISignatures } from "../@types/signed.ts";
import { MatrixEvent } from "../models/event.ts";
/**
* Public interface to the cryptography parts of the js-sdk
@@ -40,6 +40,13 @@ export interface CryptoApi {
*/
globalBlacklistUnverifiedDevices: boolean;
/**
* The cryptography mode to use.
*
* @see CryptoMode
*/
setCryptoMode(cryptoMode: CryptoMode): void;
/**
* Return the current version of the crypto module.
* For example: `Rust SDK ${versions.matrix_sdk_crypto} (${versions.git_sha}), Vodozemac ${versions.vodozemac}`.
@@ -589,6 +596,24 @@ export enum DecryptionFailureCode {
*/
HISTORICAL_MESSAGE_USER_NOT_JOINED = "HISTORICAL_MESSAGE_USER_NOT_JOINED",
/**
* The sender's identity is not verified, but was previously verified.
*/
SENDER_IDENTITY_PREVIOUSLY_VERIFIED = "SENDER_IDENTITY_PREVIOUSLY_VERIFIED",
/**
* The sender device is not cross-signed. This will only be used if the
* crypto mode is set to `CryptoMode.Invisible` or `CryptoMode.Transition`.
*/
UNSIGNED_SENDER_DEVICE = "UNSIGNED_SENDER_DEVICE",
/**
* We weren't able to link the message back to any known device. This will
* only be used if the crypto mode is set to `CryptoMode.Invisible` or
* `CryptoMode.Transition`.
*/
UNKNOWN_SENDER_DEVICE = "UNKNOWN_SENDER_DEVICE",
/** Unknown or unclassified error. */
UNKNOWN_ERROR = "UNKNOWN_ERROR",
@@ -632,6 +657,38 @@ export enum DecryptionFailureCode {
UNKNOWN_ENCRYPTION_ALGORITHM = "UNKNOWN_ENCRYPTION_ALGORITHM",
}
/**
* The cryptography mode. Affects how messages are encrypted and decrypted.
* Only supported by Rust crypto.
*/
export enum CryptoMode {
/**
* Message encryption keys are shared with all devices in the room, except for
* blacklisted devices, or unverified devices if
* `globalBlacklistUnverifiedDevices` is set. Events from all senders are
* decrypted.
*/
Legacy,
/**
* Events are encrypted as with `Legacy` mode, but encryption will throw an error if a
* verified user has an unsigned device, or if a verified user replaces
* their identity. Events are decrypted only if they come from cross-signed
* devices, or devices that existed before the Rust crypto SDK started
* tracking device trust: other events will result in a decryption failure. (To access the failure
* reason, see {@link MatrixEvent.decryptionFailureReason}.)
*/
Transition,
/**
* Message encryption keys are only shared with devices that have been cross-signed by their owner.
* Encryption will throw an error if a verified user replaces their identity. Events are
* decrypted only if they come from a cross-signed device other events will result in a decryption
* failure. (To access the failure reason, see {@link MatrixEvent.decryptionFailureReason}.)
*/
Invisible,
}
/**
* Options object for `CryptoApi.bootstrapCrossSigning`.
*/
@@ -809,6 +866,8 @@ export interface CryptoCallbacks extends SecretStorageCallbacks {
* @param key - private key to store
*/
cacheSecretStorageKey?: (keyId: string, keyInfo: SecretStorageKeyDescription, key: Uint8Array) => void;
/** @deprecated: unused with the Rust crypto stack. */
onSecretRequested?: (
userId: string,
deviceId: string,
@@ -816,10 +875,13 @@ export interface CryptoCallbacks extends SecretStorageCallbacks {
secretName: string,
deviceTrust: DeviceVerificationStatus,
) => Promise<string | undefined>;
/** @deprecated: unused with the Rust crypto stack. */
getDehydrationKey?: (
keyInfo: SecretStorageKeyDescription,
checkFunc: (key: Uint8Array) => void,
) => Promise<Uint8Array>;
getBackupKey?: () => Promise<Uint8Array>;
}
@@ -960,5 +1022,7 @@ export interface OwnDeviceKeys {
curve25519: string;
}
export * from "./verification";
export * from "./keybackup";
export * from "./verification.ts";
export * from "./keybackup.ts";
export * from "./recovery-key.ts";
export * from "./key-passphrase.ts";
+58
View File
@@ -0,0 +1,58 @@
/*
* Copyright 2024 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.
*/
const DEFAULT_BIT_SIZE = 256;
/**
* Derive a recovery key from a passphrase and salt using PBKDF2.
* @see https://spec.matrix.org/v1.11/client-server-api/#deriving-keys-from-passphrases
*
* @param passphrase - The passphrase to derive the key from
* @param salt - The salt to use in the derivation
* @param iterations - The number of iterations to use in the derivation
* @param numBits - The number of bits to derive
*/
export async function deriveRecoveryKeyFromPassphrase(
passphrase: string,
salt: string,
iterations: number,
numBits = DEFAULT_BIT_SIZE,
): Promise<Uint8Array> {
if (!globalThis.crypto.subtle || !TextEncoder) {
throw new Error("Password-based backup is not available on this platform");
}
const key = await globalThis.crypto.subtle.importKey(
"raw",
new TextEncoder().encode(passphrase),
{ name: "PBKDF2" },
false,
["deriveBits"],
);
const keybits = await globalThis.crypto.subtle.deriveBits(
{
name: "PBKDF2",
salt: new TextEncoder().encode(salt),
iterations: iterations,
hash: "SHA-512",
},
key,
numBits,
);
return new Uint8Array(keybits);
}
+2 -2
View File
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { ISigned } from "../@types/signed";
import { IEncryptedPayload } from "../crypto/aes";
import { ISigned } from "../@types/signed.ts";
import { IEncryptedPayload } from "../crypto/aes.ts";
export interface Curve25519AuthData {
public_key: string;
+69
View File
@@ -0,0 +1,69 @@
/*
* Copyright 2024 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import bs58 from "bs58";
// picked arbitrarily but to try & avoid clashing with any bitcoin ones
// (which are also base58 encoded, but bitcoin's involve a lot more hashing)
const OLM_RECOVERY_KEY_PREFIX = [0x8b, 0x01];
const KEY_SIZE = 32;
/**
* Encode a recovery key using the Matrix {@link https://spec.matrix.org/v1.11/appendices/#cryptographic-key-representation | Cryptographic key representation}
* @param key
*/
export function encodeRecoveryKey(key: ArrayLike<number>): string | undefined {
const buf = Buffer.alloc(OLM_RECOVERY_KEY_PREFIX.length + key.length + 1);
buf.set(OLM_RECOVERY_KEY_PREFIX, 0);
buf.set(key, OLM_RECOVERY_KEY_PREFIX.length);
let parity = 0;
for (let i = 0; i < buf.length - 1; ++i) {
parity ^= buf[i];
}
buf[buf.length - 1] = parity;
const base58key = bs58.encode(buf);
return base58key.match(/.{1,4}/g)?.join(" ");
}
/**
* Decode a recovery key encoded with the Matrix {@link https://spec.matrix.org/v1.11/appendices/#cryptographic-key-representation | Cryptographic key representation} encoding.
* @param recoveryKey
*/
export function decodeRecoveryKey(recoveryKey: string): Uint8Array {
const result = bs58.decode(recoveryKey.replace(/ /g, ""));
let parity = 0;
for (const b of result) {
parity ^= b;
}
if (parity !== 0) {
throw new Error("Incorrect parity");
}
for (let i = 0; i < OLM_RECOVERY_KEY_PREFIX.length; ++i) {
if (result[i] !== OLM_RECOVERY_KEY_PREFIX[i]) {
throw new Error("Incorrect prefix");
}
}
if (result.length !== OLM_RECOVERY_KEY_PREFIX.length + KEY_SIZE + 1) {
throw new Error("Incorrect length");
}
return Uint8Array.from(result.slice(OLM_RECOVERY_KEY_PREFIX.length, OLM_RECOVERY_KEY_PREFIX.length + KEY_SIZE));
}
+2 -2
View File
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { MatrixEvent } from "../models/event";
import { TypedEventEmitter } from "../models/typed-event-emitter";
import { MatrixEvent } from "../models/event.ts";
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
/**
* An incoming, or outgoing, request to verify a user or a device via cross-signing.
+17 -13
View File
@@ -19,19 +19,23 @@ limitations under the License.
*/
import type { PkSigning } from "@matrix-org/olm";
import { IObject, pkSign, pkVerify } from "./olmlib";
import { logger } from "../logger";
import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store";
import { decryptAES, encryptAES } from "./aes";
import { DeviceInfo } from "./deviceinfo";
import { ISignedKey, MatrixClient } from "../client";
import { OlmDevice } from "./OlmDevice";
import { ICryptoCallbacks } from ".";
import { ISignatures } from "../@types/signed";
import { CryptoStore, SecretStorePrivateKeys } from "./store/base";
import { ServerSideSecretStorage, SecretStorageKeyDescription } from "../secret-storage";
import { CrossSigningKeyInfo, DeviceVerificationStatus, UserVerificationStatus as UserTrustLevel } from "../crypto-api";
import { decodeBase64, encodeBase64 } from "../base64";
import { IObject, pkSign, pkVerify } from "./olmlib.ts";
import { logger } from "../logger.ts";
import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store.ts";
import { decryptAES, encryptAES } from "./aes.ts";
import { DeviceInfo } from "./deviceinfo.ts";
import { ISignedKey, MatrixClient } from "../client.ts";
import { OlmDevice } from "./OlmDevice.ts";
import { ICryptoCallbacks } from "./index.ts";
import { ISignatures } from "../@types/signed.ts";
import { CryptoStore, SecretStorePrivateKeys } from "./store/base.ts";
import { ServerSideSecretStorage, SecretStorageKeyDescription } from "../secret-storage.ts";
import {
CrossSigningKeyInfo,
DeviceVerificationStatus,
UserVerificationStatus as UserTrustLevel,
} from "../crypto-api/index.ts";
import { decodeBase64, encodeBase64 } from "../base64.ts";
// backwards-compatibility re-exports
export { UserTrustLevel };
+11 -11
View File
@@ -18,17 +18,17 @@ limitations under the License.
* Manages the list of other users' devices
*/
import { logger } from "../logger";
import { DeviceInfo, IDevice } from "./deviceinfo";
import { CrossSigningInfo, ICrossSigningInfo } from "./CrossSigning";
import * as olmlib from "./olmlib";
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
import { chunkPromises, defer, IDeferred, sleep } from "../utils";
import { DeviceKeys, IDownloadKeyResult, Keys, MatrixClient, SigningKeys } from "../client";
import { OlmDevice } from "./OlmDevice";
import { CryptoStore } from "./store/base";
import { TypedEventEmitter } from "../models/typed-event-emitter";
import { CryptoEvent, CryptoEventHandlerMap } from "./index";
import { logger } from "../logger.ts";
import { DeviceInfo, IDevice } from "./deviceinfo.ts";
import { CrossSigningInfo, ICrossSigningInfo } from "./CrossSigning.ts";
import * as olmlib from "./olmlib.ts";
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store.ts";
import { chunkPromises, defer, IDeferred, sleep } from "../utils.ts";
import { DeviceKeys, IDownloadKeyResult, Keys, MatrixClient, SigningKeys } from "../client.ts";
import { OlmDevice } from "./OlmDevice.ts";
import { CryptoStore } from "./store/base.ts";
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
import { CryptoEvent, CryptoEventHandlerMap } from "./index.ts";
/* State transition diagram for DeviceList.deviceTrackingStatus
*
+11 -11
View File
@@ -14,17 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { logger } from "../logger";
import { MatrixEvent } from "../models/event";
import { createCryptoStoreCacheCallbacks, ICacheCallbacks } from "./CrossSigning";
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
import { Method, ClientPrefix } from "../http-api";
import { Crypto, ICryptoCallbacks } from "./index";
import { ClientEvent, ClientEventHandlerMap, CrossSigningKeys, ISignedKey, KeySignatures } from "../client";
import { IKeyBackupInfo } from "./keybackup";
import { TypedEventEmitter } from "../models/typed-event-emitter";
import { AccountDataClient, SecretStorageKeyDescription } from "../secret-storage";
import { BootstrapCrossSigningOpts, CrossSigningKeyInfo } from "../crypto-api";
import { logger } from "../logger.ts";
import { MatrixEvent } from "../models/event.ts";
import { createCryptoStoreCacheCallbacks, ICacheCallbacks } from "./CrossSigning.ts";
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store.ts";
import { Method, ClientPrefix } from "../http-api/index.ts";
import { Crypto, ICryptoCallbacks } from "./index.ts";
import { ClientEvent, ClientEventHandlerMap, CrossSigningKeys, ISignedKey, KeySignatures } from "../client.ts";
import { IKeyBackupInfo } from "./keybackup.ts";
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
import { AccountDataClient, SecretStorageKeyDescription } from "../secret-storage.ts";
import { BootstrapCrossSigningOpts, CrossSigningKeyInfo } from "../crypto-api/index.ts";
interface ICrossSigningKeys {
authUpload: BootstrapCrossSigningOpts["authUploadDeviceSigningKeys"];
+8 -8
View File
@@ -16,14 +16,14 @@ limitations under the License.
import { Account, InboundGroupSession, OutboundGroupSession, Session, Utility } from "@matrix-org/olm";
import { logger, Logger } from "../logger";
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
import { CryptoStore, IProblem, ISessionInfo, IWithheld } from "./store/base";
import { IOlmDevice, IOutboundGroupSessionKey } from "./algorithms/megolm";
import { IMegolmSessionData, OlmGroupSessionExtraData } from "../@types/crypto";
import { IMessage } from "./algorithms/olm";
import { DecryptionFailureCode } from "../crypto-api";
import { DecryptionError } from "../common-crypto/CryptoBackend";
import { logger, Logger } from "../logger.ts";
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store.ts";
import { CryptoStore, IProblem, ISessionInfo, IWithheld } from "./store/base.ts";
import { IOlmDevice, IOutboundGroupSessionKey } from "./algorithms/megolm.ts";
import { IMegolmSessionData, OlmGroupSessionExtraData } from "../@types/crypto.ts";
import { IMessage } from "./algorithms/olm.ts";
import { DecryptionFailureCode } from "../crypto-api/index.ts";
import { DecryptionError } from "../common-crypto/CryptoBackend.ts";
// The maximum size of an event is 65K, and we base64 the content, so this is a
// reasonable approximation to the biggest plaintext we can encrypt.
+6 -6
View File
@@ -16,12 +16,12 @@ limitations under the License.
import { v4 as uuidv4 } from "uuid";
import { logger } from "../logger";
import { MatrixClient } from "../client";
import { IRoomKeyRequestBody, IRoomKeyRequestRecipient } from "./index";
import { CryptoStore, OutgoingRoomKeyRequest } from "./store/base";
import { EventType, ToDeviceMessageId } from "../@types/event";
import { MapWithDefault } from "../utils";
import { logger } from "../logger.ts";
import { MatrixClient } from "../client.ts";
import { IRoomKeyRequestBody, IRoomKeyRequestRecipient } from "./index.ts";
import { CryptoStore, OutgoingRoomKeyRequest } from "./store/base.ts";
import { EventType, ToDeviceMessageId } from "../@types/event.ts";
import { MapWithDefault } from "../utils.ts";
/**
* Internal module. Management of outgoing room key requests.
+2 -2
View File
@@ -18,8 +18,8 @@ limitations under the License.
* Manages the list of encrypted rooms
*/
import { CryptoStore } from "./store/base";
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
import { CryptoStore } from "./store/base.ts";
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store.ts";
/* eslint-disable camelcase */
export interface IRoomEncryption {
+7 -7
View File
@@ -15,13 +15,13 @@ limitations under the License.
*/
import { v4 as uuidv4 } from "uuid";
import { MatrixClient } from "../client";
import { ICryptoCallbacks, IEncryptedContent } from "./index";
import { defer, IDeferred } from "../utils";
import { ToDeviceMessageId } from "../@types/event";
import { logger } from "../logger";
import { MatrixEvent } from "../models/event";
import * as olmlib from "./olmlib";
import { MatrixClient } from "../client.ts";
import { ICryptoCallbacks, IEncryptedContent } from "./index.ts";
import { defer, IDeferred } from "../utils.ts";
import { ToDeviceMessageId } from "../@types/event.ts";
import { logger } from "../logger.ts";
import { MatrixEvent } from "../models/event.ts";
import * as olmlib from "./olmlib.ts";
export interface ISecretRequest {
requestId: string;
+7 -7
View File
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { ICryptoCallbacks } from ".";
import { MatrixEvent } from "../models/event";
import { MatrixClient } from "../client";
import { ICryptoCallbacks } from "./index.ts";
import { MatrixEvent } from "../models/event.ts";
import { MatrixClient } from "../client.ts";
import {
SecretStorageKeyDescription,
SecretStorageKeyTuple,
@@ -25,8 +25,8 @@ import {
AccountDataClient,
ServerSideSecretStorage,
ServerSideSecretStorageImpl,
} from "../secret-storage";
import { ISecretRequest, SecretSharing } from "./SecretSharing";
} from "../secret-storage.ts";
import { ISecretRequest, SecretSharing } from "./SecretSharing.ts";
/* re-exports for backwards compatibility */
export type {
@@ -34,9 +34,9 @@ export type {
SecretStorageKeyTuple,
SecretStorageKeyObject,
SECRET_STORAGE_ALGORITHM_V1_AES,
} from "../secret-storage";
} from "../secret-storage.ts";
export type { ISecretRequest } from "./SecretSharing";
export type { ISecretRequest } from "./SecretSharing.ts";
/**
* Implements Secure Secret Storage and Sharing (MSC1946)
+16 -9
View File
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { decodeBase64, encodeBase64 } from "../base64";
import { decodeBase64, encodeBase64 } from "../base64.ts";
// salt for HKDF, with 8 bytes of zeros
const zeroSalt = new Uint8Array(8);
@@ -30,12 +30,17 @@ export interface IEncryptedPayload {
}
/**
* encrypt a string
* Encrypt a string using AES-CTR.
*
* @param data - the plaintext to encrypt
* @param key - the encryption key to use
* @param name - the name of the secret
* @param ivStr - the initialization vector to use
* @param key - the encryption key to use as an input to the HKDF function which is used to derive the AES key for
* encryption. Obviously, the same key must be provided when decrypting.
* @param name - the name of the secret. Used as an input to the HKDF operation which is used to derive the AES key,
* so again the same value must be provided when decrypting.
* @param ivStr - the base64-encoded initialization vector to use. If not supplied, a random one will be generated.
*
* @returns The encrypted result, including the ciphertext itself, the initialization vector (as supplied in `ivStr`,
* or generated), and an HMAC on the ciphertext all base64-encoded.
*/
export async function encryptAES(
data: string,
@@ -79,11 +84,13 @@ export async function encryptAES(
}
/**
* decrypt a string
* Decrypt an AES-encrypted string.
*
* @param data - the encrypted data
* @param key - the encryption key to use
* @param name - the name of the secret
* @param data - the encrypted data, returned by {@link encryptAES}.
* @param key - the encryption key to use as an input to the HKDF function which is used to derive the AES key. Must
* be the same as provided to {@link encryptAES}.
* @param name - the name of the secret. Also used as an input to the HKDF operation which is used to derive the AES
* key, so again must be the same as provided to {@link encryptAES}.
*/
export async function decryptAES(data: IEncryptedPayload, key: Uint8Array, name: string): Promise<string> {
const [aesKey, hmacKey] = await deriveKeys(key, name);
+10 -10
View File
@@ -18,15 +18,15 @@ limitations under the License.
* Internal module. Defines the base classes of the encryption implementations
*/
import type { IMegolmSessionData } from "../../@types/crypto";
import { MatrixClient } from "../../client";
import { Room } from "../../models/room";
import { OlmDevice } from "../OlmDevice";
import { IContent, MatrixEvent, RoomMember } from "../../matrix";
import { Crypto, IEncryptedContent, IEventDecryptionResult, IncomingRoomKeyRequest } from "..";
import { DeviceInfo } from "../deviceinfo";
import { IRoomEncryption } from "../RoomList";
import { DeviceInfoMap } from "../DeviceList";
import type { IMegolmSessionData } from "../../@types/crypto.ts";
import { MatrixClient } from "../../client.ts";
import { Room } from "../../models/room.ts";
import { OlmDevice } from "../OlmDevice.ts";
import { IContent, MatrixEvent, RoomMember } from "../../matrix.ts";
import { Crypto, IEncryptedContent, IEventDecryptionResult, IncomingRoomKeyRequest } from "../index.ts";
import { DeviceInfo } from "../deviceinfo.ts";
import { IRoomEncryption } from "../RoomList.ts";
import { DeviceInfoMap } from "../DeviceList.ts";
/**
* Map of registered encryption algorithm classes. A map from string to {@link EncryptionAlgorithm} class
@@ -233,4 +233,4 @@ export function registerAlgorithm<P extends IParams = IParams>(
}
/* Re-export for backwards compatibility. Deprecated: this is an internal class. */
export { DecryptionError } from "../../common-crypto/CryptoBackend";
export { DecryptionError } from "../../common-crypto/CryptoBackend.ts";
+3 -3
View File
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import "./olm";
import "./megolm";
import "./olm.ts";
import "./megolm.ts";
export * from "./base";
export * from "./base.ts";
+19 -19
View File
@@ -20,9 +20,9 @@ limitations under the License.
import { v4 as uuidv4 } from "uuid";
import type { IEventDecryptionResult, IMegolmSessionData } from "../../@types/crypto";
import { logger, Logger } from "../../logger";
import * as olmlib from "../olmlib";
import type { IEventDecryptionResult, IMegolmSessionData } from "../../@types/crypto.ts";
import { logger, Logger } from "../../logger.ts";
import * as olmlib from "../olmlib.ts";
import {
DecryptionAlgorithm,
DecryptionClassParams,
@@ -30,22 +30,22 @@ import {
IParams,
registerAlgorithm,
UnknownDeviceError,
} from "./base";
import { IDecryptedGroupMessage, WITHHELD_MESSAGES } from "../OlmDevice";
import { Room } from "../../models/room";
import { DeviceInfo } from "../deviceinfo";
import { IOlmSessionResult } from "../olmlib";
import { DeviceInfoMap } from "../DeviceList";
import { IContent, MatrixEvent } from "../../models/event";
import { EventType, MsgType, ToDeviceMessageId } from "../../@types/event";
import { IMegolmEncryptedContent, IncomingRoomKeyRequest, IEncryptedContent } from "../index";
import { RoomKeyRequestState } from "../OutgoingRoomKeyRequestManager";
import { OlmGroupSessionExtraData } from "../../@types/crypto";
import { MatrixError } from "../../http-api";
import { immediate, MapWithDefault } from "../../utils";
import { KnownMembership } from "../../@types/membership";
import { DecryptionFailureCode } from "../../crypto-api";
import { DecryptionError } from "../../common-crypto/CryptoBackend";
} from "./base.ts";
import { IDecryptedGroupMessage, WITHHELD_MESSAGES } from "../OlmDevice.ts";
import { Room } from "../../models/room.ts";
import { DeviceInfo } from "../deviceinfo.ts";
import { IOlmSessionResult } from "../olmlib.ts";
import { DeviceInfoMap } from "../DeviceList.ts";
import { IContent, MatrixEvent } from "../../models/event.ts";
import { EventType, MsgType, ToDeviceMessageId } from "../../@types/event.ts";
import { IMegolmEncryptedContent, IncomingRoomKeyRequest, IEncryptedContent } from "../index.ts";
import { RoomKeyRequestState } from "../OutgoingRoomKeyRequestManager.ts";
import { OlmGroupSessionExtraData } from "../../@types/crypto.ts";
import { MatrixError } from "../../http-api/index.ts";
import { immediate, MapWithDefault } from "../../utils.ts";
import { KnownMembership } from "../../@types/membership.ts";
import { DecryptionFailureCode } from "../../crypto-api/index.ts";
import { DecryptionError } from "../../common-crypto/CryptoBackend.ts";
// determine whether the key can be shared with invitees
export function isRoomSharedHistory(room: Room): boolean {
+11 -11
View File
@@ -18,17 +18,17 @@ limitations under the License.
* Defines m.olm encryption/decryption
*/
import type { IEventDecryptionResult } from "../../@types/crypto";
import { logger } from "../../logger";
import * as olmlib from "../olmlib";
import { DeviceInfo } from "../deviceinfo";
import { DecryptionAlgorithm, EncryptionAlgorithm, registerAlgorithm } from "./base";
import { Room } from "../../models/room";
import { IContent, MatrixEvent } from "../../models/event";
import { IEncryptedContent, IOlmEncryptedContent } from "../index";
import { IInboundSession } from "../OlmDevice";
import { DecryptionFailureCode } from "../../crypto-api";
import { DecryptionError } from "../../common-crypto/CryptoBackend";
import type { IEventDecryptionResult } from "../../@types/crypto.ts";
import { logger } from "../../logger.ts";
import * as olmlib from "../olmlib.ts";
import { DeviceInfo } from "../deviceinfo.ts";
import { DecryptionAlgorithm, EncryptionAlgorithm, registerAlgorithm } from "./base.ts";
import { Room } from "../../models/room.ts";
import { IContent, MatrixEvent } from "../../models/event.ts";
import { IEncryptedContent, IOlmEncryptedContent } from "../index.ts";
import { IInboundSession } from "../OlmDevice.ts";
import { DecryptionFailureCode } from "../../crypto-api/index.ts";
import { DecryptionError } from "../../common-crypto/CryptoBackend.ts";
const DeviceVerification = DeviceInfo.DeviceVerification;
+5 -5
View File
@@ -14,26 +14,26 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { DeviceInfo } from "./deviceinfo";
import { DeviceInfo } from "./deviceinfo.ts";
/* re-exports for backwards compatibility. */
// CrossSigningKey is used as a value in `client.ts`, we can't export it as a type
export { CrossSigningKey } from "../crypto-api";
export { CrossSigningKey } from "../crypto-api/index.ts";
export type {
GeneratedSecretStorageKey as IRecoveryKey,
CreateSecretStorageOpts as ICreateSecretStorageOpts,
} from "../crypto-api";
} from "../crypto-api/index.ts";
export type {
ImportRoomKeyProgressData as IImportOpts,
ImportRoomKeysOpts as IImportRoomKeysOpts,
} from "../crypto-api";
} from "../crypto-api/index.ts";
export type {
AddSecretStorageKeyOpts as IAddSecretStorageKeyOpts,
PassphraseInfo as IPassphraseInfo,
SecretStorageKeyDescription as ISecretStorageKeyInfo,
} from "../secret-storage";
} from "../secret-storage.ts";
// TODO: Merge this with crypto.js once converted
+17 -17
View File
@@ -18,29 +18,29 @@ limitations under the License.
* Classes for dealing with key backup.
*/
import type { IMegolmSessionData } from "../@types/crypto";
import { MatrixClient } from "../client";
import { logger } from "../logger";
import { MEGOLM_ALGORITHM, verifySignature } from "./olmlib";
import { DeviceInfo } from "./deviceinfo";
import { DeviceTrustLevel } from "./CrossSigning";
import { keyFromPassphrase } from "./key_passphrase";
import { encodeUri, safeSet, sleep } from "../utils";
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
import { encodeRecoveryKey } from "./recoverykey";
import { calculateKeyCheck, decryptAES, encryptAES, IEncryptedPayload } from "./aes";
import type { IMegolmSessionData } from "../@types/crypto.ts";
import { MatrixClient } from "../client.ts";
import { logger } from "../logger.ts";
import { MEGOLM_ALGORITHM, verifySignature } from "./olmlib.ts";
import { DeviceInfo } from "./deviceinfo.ts";
import { DeviceTrustLevel } from "./CrossSigning.ts";
import { keyFromPassphrase } from "./key_passphrase.ts";
import { encodeUri, safeSet, sleep } from "../utils.ts";
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store.ts";
import { calculateKeyCheck, decryptAES, encryptAES, IEncryptedPayload } from "./aes.ts";
import {
Curve25519SessionData,
IAes256AuthData,
ICurve25519AuthData,
IKeyBackupInfo,
IKeyBackupSession,
} from "./keybackup";
import { UnstableValue } from "../NamespacedValue";
import { CryptoEvent } from "./index";
import { ClientPrefix, HTTPError, MatrixError, Method } from "../http-api";
import { BackupTrustInfo } from "../crypto-api/keybackup";
import { BackupDecryptor } from "../common-crypto/CryptoBackend";
} from "./keybackup.ts";
import { UnstableValue } from "../NamespacedValue.ts";
import { CryptoEvent } from "./index.ts";
import { ClientPrefix, HTTPError, MatrixError, Method } from "../http-api/index.ts";
import { BackupTrustInfo } from "../crypto-api/keybackup.ts";
import { BackupDecryptor } from "../common-crypto/CryptoBackend.ts";
import { encodeRecoveryKey } from "../crypto-api/index.ts";
const KEY_BACKUP_KEYS_PER_REQUEST = 200;
const KEY_BACKUP_CHECK_RATE_LIMIT = 5000; // ms
+8 -8
View File
@@ -16,14 +16,14 @@ limitations under the License.
import anotherjson from "another-json";
import type { IDeviceKeys, IOneTimeKey } from "../@types/crypto";
import { decodeBase64, encodeBase64 } from "../base64";
import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store";
import { decryptAES, encryptAES } from "./aes";
import { logger } from "../logger";
import { Crypto } from "./index";
import { Method } from "../http-api";
import { SecretStorageKeyDescription } from "../secret-storage";
import type { IDeviceKeys, IOneTimeKey } from "../@types/crypto.ts";
import { decodeBase64, encodeBase64 } from "../base64.ts";
import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store.ts";
import { decryptAES, encryptAES } from "./aes.ts";
import { logger } from "../logger.ts";
import { Crypto } from "./index.ts";
import { Method } from "../http-api/index.ts";
import { SecretStorageKeyDescription } from "../secret-storage.ts";
export interface IDehydratedDevice {
device_id: string; // eslint-disable-line camelcase
+2 -2
View File
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { Device } from "../models/device";
import { DeviceInfo } from "./deviceinfo";
import { Device } from "../models/device.ts";
import { DeviceInfo } from "./deviceinfo.ts";
/**
* Convert a {@link DeviceInfo} to a {@link Device}.
+2 -2
View File
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { ISignatures } from "../@types/signed";
import { DeviceVerification } from "../models/device";
import { ISignatures } from "../@types/signed.ts";
import { DeviceVerification } from "../models/device.ts";
export interface IDevice {
keys: Record<string, string>;
+72 -58
View File
@@ -20,55 +20,59 @@ limitations under the License.
import anotherjson from "another-json";
import { v4 as uuidv4 } from "uuid";
import type { IDeviceKeys, IEventDecryptionResult, IMegolmSessionData, IOneTimeKey } from "../@types/crypto";
import type { IDeviceKeys, IEventDecryptionResult, IMegolmSessionData, IOneTimeKey } from "../@types/crypto.ts";
import type { PkDecryption, PkSigning } from "@matrix-org/olm";
import { EventType, ToDeviceMessageId } from "../@types/event";
import { TypedReEmitter } from "../ReEmitter";
import { logger } from "../logger";
import { IExportedDevice, OlmDevice } from "./OlmDevice";
import { IOlmDevice } from "./algorithms/megolm";
import * as olmlib from "./olmlib";
import { DeviceInfoMap, DeviceList } from "./DeviceList";
import { DeviceInfo, IDevice } from "./deviceinfo";
import type { DecryptionAlgorithm, EncryptionAlgorithm } from "./algorithms";
import * as algorithms from "./algorithms";
import { createCryptoStoreCacheCallbacks, CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from "./CrossSigning";
import { EncryptionSetupBuilder } from "./EncryptionSetup";
import { SecretStorage as LegacySecretStorage } from "./SecretStorage";
import { CrossSigningKey, ICreateSecretStorageOpts, IEncryptedEventInfo, IRecoveryKey } from "./api";
import { OutgoingRoomKeyRequestManager } from "./OutgoingRoomKeyRequestManager";
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
import { VerificationBase } from "./verification/Base";
import { ReciprocateQRCode, SCAN_QR_CODE_METHOD, SHOW_QR_CODE_METHOD } from "./verification/QRCode";
import { SAS as SASVerification } from "./verification/SAS";
import { keyFromPassphrase } from "./key_passphrase";
import { decodeRecoveryKey, encodeRecoveryKey } from "./recoverykey";
import { VerificationRequest } from "./verification/request/VerificationRequest";
import { InRoomChannel, InRoomRequests } from "./verification/request/InRoomChannel";
import { Request, ToDeviceChannel, ToDeviceRequests } from "./verification/request/ToDeviceChannel";
import { IllegalMethod } from "./verification/IllegalMethod";
import { KeySignatureUploadError } from "../errors";
import { calculateKeyCheck, decryptAES, encryptAES, IEncryptedPayload } from "./aes";
import { DehydrationManager } from "./dehydration";
import { BackupManager, LibOlmBackupDecryptor, backupTrustInfoFromLegacyTrustInfo } from "./backup";
import { IStore } from "../store";
import { Room, RoomEvent } from "../models/room";
import { RoomMember, RoomMemberEvent } from "../models/room-member";
import { EventStatus, IContent, IEvent, MatrixEvent, MatrixEventEvent } from "../models/event";
import { ToDeviceBatch } from "../models/ToDeviceMessage";
import { ClientEvent, IKeysUploadResponse, ISignedKey, IUploadKeySignaturesResponse, MatrixClient } from "../client";
import { IRoomEncryption, RoomList } from "./RoomList";
import { IKeyBackupInfo } from "./keybackup";
import { ISyncStateData } from "../sync";
import { CryptoStore } from "./store/base";
import { IVerificationChannel } from "./verification/request/Channel";
import { TypedEventEmitter } from "../models/typed-event-emitter";
import { IDeviceLists, ISyncResponse, IToDeviceEvent } from "../sync-accumulator";
import { ISignatures } from "../@types/signed";
import { IMessage } from "./algorithms/olm";
import { BackupDecryptor, CryptoBackend, DecryptionError, OnSyncCompletedData } from "../common-crypto/CryptoBackend";
import { RoomState, RoomStateEvent } from "../models/room-state";
import { MapWithDefault, recursiveMapToObject } from "../utils";
import { EventType, ToDeviceMessageId } from "../@types/event.ts";
import { TypedReEmitter } from "../ReEmitter.ts";
import { logger } from "../logger.ts";
import { IExportedDevice, OlmDevice } from "./OlmDevice.ts";
import { IOlmDevice } from "./algorithms/megolm.ts";
import * as olmlib from "./olmlib.ts";
import { DeviceInfoMap, DeviceList } from "./DeviceList.ts";
import { DeviceInfo, IDevice } from "./deviceinfo.ts";
import type { DecryptionAlgorithm, EncryptionAlgorithm } from "./algorithms/index.ts";
import * as algorithms from "./algorithms/index.ts";
import { createCryptoStoreCacheCallbacks, CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from "./CrossSigning.ts";
import { EncryptionSetupBuilder } from "./EncryptionSetup.ts";
import { SecretStorage as LegacySecretStorage } from "./SecretStorage.ts";
import { CrossSigningKey, ICreateSecretStorageOpts, IEncryptedEventInfo, IRecoveryKey } from "./api.ts";
import { OutgoingRoomKeyRequestManager } from "./OutgoingRoomKeyRequestManager.ts";
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store.ts";
import { VerificationBase } from "./verification/Base.ts";
import { ReciprocateQRCode, SCAN_QR_CODE_METHOD, SHOW_QR_CODE_METHOD } from "./verification/QRCode.ts";
import { SAS as SASVerification } from "./verification/SAS.ts";
import { keyFromPassphrase } from "./key_passphrase.ts";
import { VerificationRequest } from "./verification/request/VerificationRequest.ts";
import { InRoomChannel, InRoomRequests } from "./verification/request/InRoomChannel.ts";
import { Request, ToDeviceChannel, ToDeviceRequests } from "./verification/request/ToDeviceChannel.ts";
import { IllegalMethod } from "./verification/IllegalMethod.ts";
import { KeySignatureUploadError } from "../errors.ts";
import { calculateKeyCheck, decryptAES, encryptAES, IEncryptedPayload } from "./aes.ts";
import { DehydrationManager } from "./dehydration.ts";
import { BackupManager, LibOlmBackupDecryptor, backupTrustInfoFromLegacyTrustInfo } from "./backup.ts";
import { IStore } from "../store/index.ts";
import { Room, RoomEvent } from "../models/room.ts";
import { RoomMember, RoomMemberEvent } from "../models/room-member.ts";
import { EventStatus, IContent, IEvent, MatrixEvent, MatrixEventEvent } from "../models/event.ts";
import { ToDeviceBatch } from "../models/ToDeviceMessage.ts";
import { ClientEvent, IKeysUploadResponse, ISignedKey, IUploadKeySignaturesResponse, MatrixClient } from "../client.ts";
import { IRoomEncryption, RoomList } from "./RoomList.ts";
import { IKeyBackupInfo } from "./keybackup.ts";
import { ISyncStateData } from "../sync.ts";
import { CryptoStore } from "./store/base.ts";
import { IVerificationChannel } from "./verification/request/Channel.ts";
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
import { IDeviceLists, ISyncResponse, IToDeviceEvent } from "../sync-accumulator.ts";
import { ISignatures } from "../@types/signed.ts";
import { IMessage } from "./algorithms/olm.ts";
import {
BackupDecryptor,
CryptoBackend,
DecryptionError,
OnSyncCompletedData,
} from "../common-crypto/CryptoBackend.ts";
import { RoomState, RoomStateEvent } from "../models/room-state.ts";
import { MapWithDefault, recursiveMapToObject } from "../utils.ts";
import {
AccountDataClient,
AddSecretStorageKeyOpts,
@@ -77,15 +81,18 @@ import {
SecretStorageKeyObject,
SecretStorageKeyTuple,
ServerSideSecretStorageImpl,
} from "../secret-storage";
import { ISecretRequest } from "./SecretSharing";
} from "../secret-storage.ts";
import { ISecretRequest } from "./SecretSharing.ts";
import {
BackupTrustInfo,
BootstrapCrossSigningOpts,
CrossSigningKeyInfo,
CrossSigningStatus,
CryptoMode,
decodeRecoveryKey,
DecryptionFailureCode,
DeviceVerificationStatus,
encodeRecoveryKey,
EventEncryptionInfo,
EventShieldColour,
EventShieldReason,
@@ -94,18 +101,18 @@ import {
KeyBackupInfo,
OwnDeviceKeys,
VerificationRequest as CryptoApiVerificationRequest,
} from "../crypto-api";
import { Device, DeviceMap } from "../models/device";
import { deviceInfoToDevice } from "./device-converter";
import { ClientPrefix, MatrixError, Method } from "../http-api";
import { decodeBase64, encodeBase64 } from "../base64";
import { KnownMembership } from "../@types/membership";
} from "../crypto-api/index.ts";
import { Device, DeviceMap } from "../models/device.ts";
import { deviceInfoToDevice } from "./device-converter.ts";
import { ClientPrefix, MatrixError, Method } from "../http-api/index.ts";
import { decodeBase64, encodeBase64 } from "../base64.ts";
import { KnownMembership } from "../@types/membership.ts";
/* re-exports for backwards compatibility */
export type {
BootstrapCrossSigningOpts as IBootstrapCrossSigningOpts,
CryptoCallbacks as ICryptoCallbacks,
} from "../crypto-api";
} from "../crypto-api/index.ts";
const DeviceVerification = DeviceInfo.DeviceVerification;
@@ -642,6 +649,13 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
this.backupManager.checkAndStart();
}
/**
* Implementation of {@link Crypto.CryptoApi#setCryptoMode}.
*/
public setCryptoMode(cryptoMode: CryptoMode): void {
throw new Error("Not supported");
}
/**
* Implementation of {@link Crypto.CryptoApi#getVersion}.
*/
@@ -4374,4 +4388,4 @@ class IncomingRoomKeyRequestCancellation {
}
// a number of types are re-exported for backwards compatibility, in case any applications are referencing it.
export type { IEventDecryptionResult, IMegolmSessionData } from "../@types/crypto";
export type { IEventDecryptionResult, IMegolmSessionData } from "../@types/crypto.ts";
+11 -57
View File
@@ -14,75 +14,29 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { randomString } from "../randomstring";
import { randomString } from "../randomstring.ts";
import { deriveRecoveryKeyFromPassphrase } from "../crypto-api/index.ts";
const DEFAULT_ITERATIONS = 500000;
const DEFAULT_BITSIZE = 256;
/* eslint-disable camelcase */
interface IAuthData {
private_key_salt?: string;
private_key_iterations?: number;
private_key_bits?: number;
}
/* eslint-enable camelcase */
interface IKey {
key: Uint8Array;
salt: string;
iterations: number;
}
export function keyFromAuthData(authData: IAuthData, password: string): Promise<Uint8Array> {
if (!authData.private_key_salt || !authData.private_key_iterations) {
throw new Error("Salt and/or iterations not found: " + "this backup cannot be restored with a passphrase");
}
return deriveKey(
password,
authData.private_key_salt,
authData.private_key_iterations,
authData.private_key_bits || DEFAULT_BITSIZE,
);
}
export async function keyFromPassphrase(password: string): Promise<IKey> {
/**
* Generate a new recovery key, based on a passphrase.
* @param passphrase - The passphrase to generate the key from
*/
export async function keyFromPassphrase(passphrase: string): Promise<IKey> {
const salt = randomString(32);
const key = await deriveKey(password, salt, DEFAULT_ITERATIONS, DEFAULT_BITSIZE);
const key = await deriveRecoveryKeyFromPassphrase(passphrase, salt, DEFAULT_ITERATIONS);
return { key, salt, iterations: DEFAULT_ITERATIONS };
}
export async function deriveKey(
password: string,
salt: string,
iterations: number,
numBits = DEFAULT_BITSIZE,
): Promise<Uint8Array> {
if (!globalThis.crypto.subtle || !TextEncoder) {
throw new Error("Password-based backup is not available on this platform");
}
const key = await globalThis.crypto.subtle.importKey(
"raw",
new TextEncoder().encode(password),
{ name: "PBKDF2" },
false,
["deriveBits"],
);
const keybits = await globalThis.crypto.subtle.deriveBits(
{
name: "PBKDF2",
salt: new TextEncoder().encode(salt),
iterations: iterations,
hash: "SHA-512",
},
key,
numBits,
);
return new Uint8Array(keybits);
}
// Re-export the key passphrase functions to avoid breaking changes
export { deriveRecoveryKeyFromPassphrase as deriveKey };
export { keyFromAuthData } from "../common-crypto/key-passphrase.ts";
+2 -2
View File
@@ -15,7 +15,7 @@ limitations under the License.
*/
// Export for backward compatibility
import { ImportRoomKeyProgressData } from "../crypto-api";
import { ImportRoomKeyProgressData } from "../crypto-api/index.ts";
export type {
Curve25519AuthData as ICurve25519AuthData,
@@ -24,7 +24,7 @@ export type {
Curve25519SessionData,
KeyBackupSession as IKeyBackupSession,
KeyBackupRoomSessions as IKeyBackupRoomSessions,
} from "../crypto-api/keybackup";
} from "../crypto-api/keybackup.ts";
/* eslint-enable camelcase */
+10 -10
View File
@@ -21,16 +21,16 @@ limitations under the License.
import anotherjson from "another-json";
import type { PkSigning } from "@matrix-org/olm";
import type { IOneTimeKey } from "../@types/crypto";
import { OlmDevice } from "./OlmDevice";
import { DeviceInfo } from "./deviceinfo";
import { Logger, logger } from "../logger";
import { IClaimOTKsResult, MatrixClient } from "../client";
import { ISignatures } from "../@types/signed";
import { MatrixEvent } from "../models/event";
import { EventType } from "../@types/event";
import { IMessage } from "./algorithms/olm";
import { MapWithDefault } from "../utils";
import type { IOneTimeKey } from "../@types/crypto.ts";
import { OlmDevice } from "./OlmDevice.ts";
import { DeviceInfo } from "./deviceinfo.ts";
import { Logger, logger } from "../logger.ts";
import { IClaimOTKsResult, MatrixClient } from "../client.ts";
import { ISignatures } from "../@types/signed.ts";
import { MatrixEvent } from "../models/event.ts";
import { EventType } from "../@types/event.ts";
import { IMessage } from "./algorithms/olm.ts";
import { MapWithDefault } from "../utils.ts";
enum Algorithm {
Olm = "m.olm.v1.curve25519-aes-sha2",
+2 -45
View File
@@ -14,48 +14,5 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import bs58 from "bs58";
// picked arbitrarily but to try & avoid clashing with any bitcoin ones
// (which are also base58 encoded, but bitcoin's involve a lot more hashing)
const OLM_RECOVERY_KEY_PREFIX = [0x8b, 0x01];
const KEY_SIZE = 32;
export function encodeRecoveryKey(key: ArrayLike<number>): string | undefined {
const buf = Buffer.alloc(OLM_RECOVERY_KEY_PREFIX.length + key.length + 1);
buf.set(OLM_RECOVERY_KEY_PREFIX, 0);
buf.set(key, OLM_RECOVERY_KEY_PREFIX.length);
let parity = 0;
for (let i = 0; i < buf.length - 1; ++i) {
parity ^= buf[i];
}
buf[buf.length - 1] = parity;
const base58key = bs58.encode(buf);
return base58key.match(/.{1,4}/g)?.join(" ");
}
export function decodeRecoveryKey(recoveryKey: string): Uint8Array {
const result = bs58.decode(recoveryKey.replace(/ /g, ""));
let parity = 0;
for (const b of result) {
parity ^= b;
}
if (parity !== 0) {
throw new Error("Incorrect parity");
}
for (let i = 0; i < OLM_RECOVERY_KEY_PREFIX.length; ++i) {
if (result[i] !== OLM_RECOVERY_KEY_PREFIX[i]) {
throw new Error("Incorrect prefix");
}
}
if (result.length !== OLM_RECOVERY_KEY_PREFIX.length + KEY_SIZE + 1) {
throw new Error("Incorrect length");
}
return Uint8Array.from(result.slice(OLM_RECOVERY_KEY_PREFIX.length, OLM_RECOVERY_KEY_PREFIX.length + KEY_SIZE));
}
// Re-export to avoid breaking changes
export * from "../crypto-api/recovery-key.ts";
+13 -13
View File
@@ -14,19 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { IRoomKeyRequestBody, IRoomKeyRequestRecipient } from "../index";
import { RoomKeyRequestState } from "../OutgoingRoomKeyRequestManager";
import { IOlmDevice } from "../algorithms/megolm";
import { TrackingStatus } from "../DeviceList";
import { IRoomEncryption } from "../RoomList";
import { IDevice } from "../deviceinfo";
import { ICrossSigningInfo } from "../CrossSigning";
import { Logger } from "../../logger";
import { InboundGroupSessionData } from "../OlmDevice";
import { MatrixEvent } from "../../models/event";
import { DehydrationManager } from "../dehydration";
import { IEncryptedPayload } from "../aes";
import { CrossSigningKeyInfo } from "../../crypto-api";
import { IRoomKeyRequestBody, IRoomKeyRequestRecipient } from "../index.ts";
import { RoomKeyRequestState } from "../OutgoingRoomKeyRequestManager.ts";
import { IOlmDevice } from "../algorithms/megolm.ts";
import { TrackingStatus } from "../DeviceList.ts";
import { IRoomEncryption } from "../RoomList.ts";
import { IDevice } from "../deviceinfo.ts";
import { ICrossSigningInfo } from "../CrossSigning.ts";
import { Logger } from "../../logger.ts";
import { InboundGroupSessionData } from "../OlmDevice.ts";
import { MatrixEvent } from "../../models/event.ts";
import { DehydrationManager } from "../dehydration.ts";
import { IEncryptedPayload } from "../aes.ts";
import { CrossSigningKeyInfo } from "../../crypto-api/index.ts";
/**
* Internal module. Definitions for storage for the crypto module
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { Logger, logger } from "../../logger";
import { deepCompare } from "../../utils";
import { Logger, logger } from "../../logger.ts";
import { deepCompare } from "../../utils.ts";
import {
CryptoStore,
IDeviceData,
@@ -31,13 +31,13 @@ import {
SecretStorePrivateKeys,
SESSION_BATCH_SIZE,
ACCOUNT_OBJECT_KEY_MIGRATION_STATE,
} from "./base";
import { IRoomKeyRequestBody, IRoomKeyRequestRecipient } from "../index";
import { IOlmDevice } from "../algorithms/megolm";
import { IRoomEncryption } from "../RoomList";
import { InboundGroupSessionData } from "../OlmDevice";
import { IndexedDBCryptoStore } from "./indexeddb-crypto-store";
import { CrossSigningKeyInfo } from "../../crypto-api";
} from "./base.ts";
import { IRoomKeyRequestBody, IRoomKeyRequestRecipient } from "../index.ts";
import { IOlmDevice } from "../algorithms/megolm.ts";
import { IRoomEncryption } from "../RoomList.ts";
import { InboundGroupSessionData } from "../OlmDevice.ts";
import { IndexedDBCryptoStore } from "./indexeddb-crypto-store.ts";
import { CrossSigningKeyInfo } from "../../crypto-api/index.ts";
const PROFILE_TRANSACTIONS = false;
+12 -12
View File
@@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { logger, Logger } from "../../logger";
import { LocalStorageCryptoStore } from "./localStorage-crypto-store";
import { MemoryCryptoStore } from "./memory-crypto-store";
import * as IndexedDBCryptoStoreBackend from "./indexeddb-crypto-store-backend";
import { InvalidCryptoStoreError, InvalidCryptoStoreState } from "../../errors";
import * as IndexedDBHelpers from "../../indexeddb-helpers";
import { logger, Logger } from "../../logger.ts";
import { LocalStorageCryptoStore } from "./localStorage-crypto-store.ts";
import { MemoryCryptoStore } from "./memory-crypto-store.ts";
import * as IndexedDBCryptoStoreBackend from "./indexeddb-crypto-store-backend.ts";
import { InvalidCryptoStoreError, InvalidCryptoStoreState } from "../../errors.ts";
import * as IndexedDBHelpers from "../../indexeddb-helpers.ts";
import {
CryptoStore,
IDeviceData,
@@ -34,12 +34,12 @@ import {
ParkedSharedHistory,
SecretStorePrivateKeys,
ACCOUNT_OBJECT_KEY_MIGRATION_STATE,
} from "./base";
import { IRoomKeyRequestBody } from "../index";
import { IOlmDevice } from "../algorithms/megolm";
import { IRoomEncryption } from "../RoomList";
import { InboundGroupSessionData } from "../OlmDevice";
import { CrossSigningKeyInfo } from "../../crypto-api";
} from "./base.ts";
import { IRoomKeyRequestBody } from "../index.ts";
import { IOlmDevice } from "../algorithms/megolm.ts";
import { IRoomEncryption } from "../RoomList.ts";
import { InboundGroupSessionData } from "../OlmDevice.ts";
import { CrossSigningKeyInfo } from "../../crypto-api/index.ts";
/*
* Internal module. indexeddb storage for e2e.
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { logger } from "../../logger";
import { MemoryCryptoStore } from "./memory-crypto-store";
import { logger } from "../../logger.ts";
import { MemoryCryptoStore } from "./memory-crypto-store.ts";
import {
CryptoStore,
IDeviceData,
@@ -28,12 +28,12 @@ import {
Mode,
SecretStorePrivateKeys,
SESSION_BATCH_SIZE,
} from "./base";
import { IOlmDevice } from "../algorithms/megolm";
import { IRoomEncryption } from "../RoomList";
import { InboundGroupSessionData } from "../OlmDevice";
import { safeSet } from "../../utils";
import { CrossSigningKeyInfo } from "../../crypto-api";
} from "./base.ts";
import { IOlmDevice } from "../algorithms/megolm.ts";
import { IRoomEncryption } from "../RoomList.ts";
import { InboundGroupSessionData } from "../OlmDevice.ts";
import { safeSet } from "../../utils.ts";
import { CrossSigningKeyInfo } from "../../crypto-api/index.ts";
/**
* Internal module. Partial localStorage backed storage for e2e.
+8 -8
View File
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { logger } from "../../logger";
import { deepCompare, promiseTry, safeSet } from "../../utils";
import { logger } from "../../logger.ts";
import { deepCompare, promiseTry, safeSet } from "../../utils.ts";
import {
CryptoStore,
IDeviceData,
@@ -30,12 +30,12 @@ import {
ParkedSharedHistory,
SecretStorePrivateKeys,
SESSION_BATCH_SIZE,
} from "./base";
import { IRoomKeyRequestBody } from "../index";
import { IOlmDevice } from "../algorithms/megolm";
import { IRoomEncryption } from "../RoomList";
import { InboundGroupSessionData } from "../OlmDevice";
import { CrossSigningKeyInfo } from "../../crypto-api";
} from "./base.ts";
import { IRoomKeyRequestBody } from "../index.ts";
import { IOlmDevice } from "../algorithms/megolm.ts";
import { IRoomEncryption } from "../RoomList.ts";
import { InboundGroupSessionData } from "../OlmDevice.ts";
import { CrossSigningKeyInfo } from "../../crypto-api/index.ts";
function encodeSessionKey(senderCurve25519Key: string, sessionId: string): string {
return encodeURIComponent(senderCurve25519Key) + "/" + encodeURIComponent(sessionId);
+11 -11
View File
@@ -19,23 +19,23 @@ limitations under the License.
* Base class for verification methods.
*/
import { MatrixEvent } from "../../models/event";
import { EventType } from "../../@types/event";
import { logger } from "../../logger";
import { DeviceInfo } from "../deviceinfo";
import { newTimeoutError } from "./Error";
import { KeysDuringVerification, requestKeysDuringVerification } from "../CrossSigning";
import { IVerificationChannel } from "./request/Channel";
import { MatrixClient } from "../../client";
import { VerificationRequest } from "./request/VerificationRequest";
import { TypedEventEmitter } from "../../models/typed-event-emitter";
import { MatrixEvent } from "../../models/event.ts";
import { EventType } from "../../@types/event.ts";
import { logger } from "../../logger.ts";
import { DeviceInfo } from "../deviceinfo.ts";
import { newTimeoutError } from "./Error.ts";
import { KeysDuringVerification, requestKeysDuringVerification } from "../CrossSigning.ts";
import { IVerificationChannel } from "./request/Channel.ts";
import { MatrixClient } from "../../client.ts";
import { VerificationRequest } from "./request/VerificationRequest.ts";
import { TypedEventEmitter } from "../../models/typed-event-emitter.ts";
import {
ShowQrCodeCallbacks,
ShowSasCallbacks,
Verifier,
VerifierEvent,
VerifierEventHandlerMap,
} from "../../crypto-api/verification";
} from "../../crypto-api/verification.ts";
const timeoutException = new Error("Verification timed out");
+2 -2
View File
@@ -18,8 +18,8 @@ limitations under the License.
* Error messages.
*/
import { MatrixEvent } from "../../models/event";
import { EventType } from "../../@types/event";
import { MatrixEvent } from "../../models/event.ts";
import { EventType } from "../../@types/event.ts";
export function newVerificationError(code: string, reason: string, extraData?: Record<string, any>): MatrixEvent {
const content = Object.assign({}, { code, reason }, extraData);
+5 -5
View File
@@ -19,11 +19,11 @@ limitations under the License.
* do verification with this method).
*/
import { VerificationBase as Base, VerificationEvent, VerificationEventHandlerMap } from "./Base";
import { IVerificationChannel } from "./request/Channel";
import { MatrixClient } from "../../client";
import { MatrixEvent } from "../../models/event";
import { VerificationRequest } from "./request/VerificationRequest";
import { VerificationBase as Base, VerificationEvent, VerificationEventHandlerMap } from "./Base.ts";
import { IVerificationChannel } from "./request/Channel.ts";
import { MatrixClient } from "../../client.ts";
import { MatrixEvent } from "../../models/event.ts";
import { VerificationRequest } from "./request/VerificationRequest.ts";
export class IllegalMethod extends Base<VerificationEvent, VerificationEventHandlerMap> {
public static factory(
+10 -10
View File
@@ -18,16 +18,16 @@ limitations under the License.
* QR code key verification.
*/
import { VerificationBase as Base } from "./Base";
import { newKeyMismatchError, newUserCancelledError } from "./Error";
import { decodeBase64, encodeUnpaddedBase64 } from "../../base64";
import { logger } from "../../logger";
import { VerificationRequest } from "./request/VerificationRequest";
import { MatrixClient } from "../../client";
import { IVerificationChannel } from "./request/Channel";
import { MatrixEvent } from "../../models/event";
import { ShowQrCodeCallbacks, VerifierEvent } from "../../crypto-api/verification";
import { VerificationMethod } from "../../types";
import { VerificationBase as Base } from "./Base.ts";
import { newKeyMismatchError, newUserCancelledError } from "./Error.ts";
import { decodeBase64, encodeUnpaddedBase64 } from "../../base64.ts";
import { logger } from "../../logger.ts";
import { VerificationRequest } from "./request/VerificationRequest.ts";
import { MatrixClient } from "../../client.ts";
import { IVerificationChannel } from "./request/Channel.ts";
import { MatrixEvent } from "../../models/event.ts";
import { ShowQrCodeCallbacks, VerifierEvent } from "../../crypto-api/verification.ts";
import { VerificationMethod } from "../../types.ts";
export const SHOW_QR_CODE_METHOD = VerificationMethod.ShowQrCode;
export const SCAN_QR_CODE_METHOD = VerificationMethod.ScanQrCode;
+9 -9
View File
@@ -21,27 +21,27 @@ limitations under the License.
import anotherjson from "another-json";
import { Utility, SAS as OlmSAS } from "@matrix-org/olm";
import { VerificationBase as Base, SwitchStartEventError } from "./Base";
import { VerificationBase as Base, SwitchStartEventError } from "./Base.ts";
import {
errorFactory,
newInvalidMessageError,
newKeyMismatchError,
newUnknownMethodError,
newUserCancelledError,
} from "./Error";
import { logger } from "../../logger";
import { IContent, MatrixEvent } from "../../models/event";
import { generateDecimalSas } from "./SASDecimal";
import { EventType } from "../../@types/event";
import { EmojiMapping, GeneratedSas, ShowSasCallbacks, VerifierEvent } from "../../crypto-api/verification";
import { VerificationMethod } from "../../types";
} from "./Error.ts";
import { logger } from "../../logger.ts";
import { IContent, MatrixEvent } from "../../models/event.ts";
import { generateDecimalSas } from "./SASDecimal.ts";
import { EventType } from "../../@types/event.ts";
import { EmojiMapping, GeneratedSas, ShowSasCallbacks, VerifierEvent } from "../../crypto-api/verification.ts";
import { VerificationMethod } from "../../types.ts";
// backwards-compatibility exports
export type {
ShowSasCallbacks as ISasEvent,
GeneratedSas as IGeneratedSas,
EmojiMapping,
} from "../../crypto-api/verification";
} from "../../crypto-api/verification.ts";
const START_TYPE = EventType.KeyVerificationStart;
+2 -2
View File
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { MatrixEvent } from "../../../models/event";
import { VerificationRequest } from "./VerificationRequest";
import { MatrixEvent } from "../../../models/event.ts";
import { VerificationRequest } from "./VerificationRequest.ts";
export interface IVerificationChannel {
request?: VerificationRequest;
@@ -15,13 +15,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { VerificationRequest, REQUEST_TYPE, READY_TYPE, START_TYPE } from "./VerificationRequest";
import { logger } from "../../../logger";
import { IVerificationChannel } from "./Channel";
import { EventType, TimelineEvents } from "../../../@types/event";
import { MatrixClient } from "../../../client";
import { MatrixEvent } from "../../../models/event";
import { IRequestsMap } from "../..";
import { VerificationRequest, REQUEST_TYPE, READY_TYPE, START_TYPE } from "./VerificationRequest.ts";
import { logger } from "../../../logger.ts";
import { IVerificationChannel } from "./Channel.ts";
import { EventType, TimelineEvents } from "../../../@types/event.ts";
import { MatrixClient } from "../../../client.ts";
import { MatrixEvent } from "../../../models/event.ts";
import { IRequestsMap } from "../../index.ts";
const MESSAGE_TYPE = EventType.RoomMessage;
const M_REFERENCE = "m.reference";
@@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { randomString } from "../../../randomstring";
import { logger } from "../../../logger";
import { randomString } from "../../../randomstring.ts";
import { logger } from "../../../logger.ts";
import {
CANCEL_TYPE,
PHASE_STARTED,
@@ -25,12 +25,12 @@ import {
READY_TYPE,
START_TYPE,
VerificationRequest,
} from "./VerificationRequest";
import { errorFromEvent, newUnexpectedMessageError } from "../Error";
import { MatrixEvent } from "../../../models/event";
import { IVerificationChannel } from "./Channel";
import { MatrixClient } from "../../../client";
import { IRequestsMap } from "../..";
} from "./VerificationRequest.ts";
import { errorFromEvent, newUnexpectedMessageError } from "../Error.ts";
import { MatrixEvent } from "../../../models/event.ts";
import { IVerificationChannel } from "./Channel.ts";
import { MatrixClient } from "../../../client.ts";
import { IRequestsMap } from "../../index.ts";
export type Request = VerificationRequest<ToDeviceChannel>;
@@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { logger } from "../../../logger";
import { errorFactory, errorFromEvent, newUnexpectedMessageError, newUnknownMethodError } from "../Error";
import { QRCodeData, SCAN_QR_CODE_METHOD } from "../QRCode";
import { IVerificationChannel } from "./Channel";
import { MatrixClient } from "../../../client";
import { MatrixEvent } from "../../../models/event";
import { EventType } from "../../../@types/event";
import { VerificationBase } from "../Base";
import { VerificationMethod } from "../../index";
import { TypedEventEmitter } from "../../../models/typed-event-emitter";
import { logger } from "../../../logger.ts";
import { errorFactory, errorFromEvent, newUnexpectedMessageError, newUnknownMethodError } from "../Error.ts";
import { QRCodeData, SCAN_QR_CODE_METHOD } from "../QRCode.ts";
import { IVerificationChannel } from "./Channel.ts";
import { MatrixClient } from "../../../client.ts";
import { MatrixEvent } from "../../../models/event.ts";
import { EventType } from "../../../@types/event.ts";
import { VerificationBase } from "../Base.ts";
import { VerificationMethod } from "../../index.ts";
import { TypedEventEmitter } from "../../../models/typed-event-emitter.ts";
import {
canAcceptVerificationRequest,
VerificationPhase as Phase,
@@ -31,10 +31,10 @@ import {
VerificationRequestEvent,
VerificationRequestEventHandlerMap,
Verifier,
} from "../../../crypto-api/verification";
} from "../../../crypto-api/verification.ts";
// backwards-compatibility exports
export { VerificationPhase as Phase, VerificationRequestEvent } from "../../../crypto-api/verification";
export { VerificationPhase as Phase, VerificationRequestEvent } from "../../../crypto-api/verification.ts";
// How long after the event's timestamp that the request times out
const TIMEOUT_FROM_EVENT_TS = 10 * 60 * 1000; // 10 minutes
+21 -15
View File
@@ -25,15 +25,15 @@ import {
ISendEventFromWidgetResponseData,
} from "matrix-widget-api";
import { MatrixEvent, IEvent, IContent, EventStatus } from "./models/event";
import { MatrixEvent, IEvent, IContent, EventStatus } from "./models/event.ts";
import {
ISendEventResponse,
SendDelayedEventRequestOpts,
SendDelayedEventResponse,
UpdateDelayedEventAction,
} from "./@types/requests";
import { EventType, StateEvents } from "./@types/event";
import { logger } from "./logger";
} from "./@types/requests.ts";
import { EventType, StateEvents } from "./@types/event.ts";
import { logger } from "./logger.ts";
import {
MatrixClient,
ClientEvent,
@@ -42,15 +42,15 @@ import {
SendToDeviceContentMap,
IOpenIDToken,
UNSTABLE_MSC4140_DELAYED_EVENTS,
} from "./client";
import { SyncApi, SyncState } from "./sync";
import { SlidingSyncSdk } from "./sliding-sync-sdk";
import { User } from "./models/user";
import { Room } from "./models/room";
import { ToDeviceBatch, ToDevicePayload } from "./models/ToDeviceMessage";
import { DeviceInfo } from "./crypto/deviceinfo";
import { IOlmDevice } from "./crypto/algorithms/megolm";
import { MapWithDefault, recursiveMapToObject } from "./utils";
} from "./client.ts";
import { SyncApi, SyncState } from "./sync.ts";
import { SlidingSyncSdk } from "./sliding-sync-sdk.ts";
import { User } from "./models/user.ts";
import { Room } from "./models/room.ts";
import { ToDeviceBatch, ToDevicePayload } from "./models/ToDeviceMessage.ts";
import { DeviceInfo } from "./crypto/deviceinfo.ts";
import { IOlmDevice } from "./crypto/algorithms/megolm.ts";
import { MapWithDefault, recursiveMapToObject } from "./utils.ts";
interface IStateEventRequest {
eventType: string;
@@ -291,11 +291,17 @@ export class RoomWidgetClient extends MatrixClient {
event: MatrixEvent,
delayOpts?: SendDelayedEventRequestOpts,
): Promise<ISendEventResponse | SendDelayedEventResponse> {
// We need to extend the content with the redacts parameter
// The js sdk uses event.redacts but the widget api uses event.content.redacts
// This will be converted back to event.redacts in the widget driver.
const content = event.event.redacts
? { ...event.getContent(), redacts: event.event.redacts }
: event.getContent();
if (delayOpts) {
// TODO: updatePendingEvent for delayed events?
const response = await this.widgetApi.sendRoomEvent(
event.getType(),
event.getContent(),
content,
room.roomId,
"delay" in delayOpts ? delayOpts.delay : undefined,
"parent_delay_id" in delayOpts ? delayOpts.parent_delay_id : undefined,
@@ -305,7 +311,7 @@ export class RoomWidgetClient extends MatrixClient {
let response: ISendEventFromWidgetResponseData;
try {
response = await this.widgetApi.sendRoomEvent(event.getType(), event.getContent(), room.roomId);
response = await this.widgetApi.sendRoomEvent(event.getType(), content, room.roomId);
} catch (e) {
this.updatePendingEventStatus(room, event, EventStatus.NOT_SENT);
throw e;

Some files were not shown because too many files have changed in this diff Show More