Remove matrix-sdk-crypto-js
It now lives in its own repository at https://github.com/matrix-org/matrix-rust-sdk-crypto-nodejs
This commit is contained in:
committed by
Jonas Platte
parent
a554a92dec
commit
6e10eb9efb
@@ -18,7 +18,6 @@ concurrency:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
MATRIX_SDK_CRYPTO_NODEJS_PATH: bindings/matrix-sdk-crypto-nodejs
|
||||
MATRIX_SDK_CRYPTO_JS_PATH: bindings/matrix-sdk-crypto-js
|
||||
|
||||
jobs:
|
||||
@@ -59,16 +58,7 @@ jobs:
|
||||
run: target/debug/xtask ci bindings
|
||||
|
||||
lint-js-bindings:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
include:
|
||||
- name: "[m]-crypto-nodejs"
|
||||
path: "bindings/matrix-sdk-crypto-nodejs"
|
||||
- name: "[m]-crypto-js"
|
||||
path: "bindings/matrix-sdk-crypto-js"
|
||||
|
||||
name: lint ${{ matrix.name }}
|
||||
name: lint [m]-crypto-js
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -79,85 +69,13 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
|
||||
- name: Install NPM dependencies
|
||||
working-directory: ${{ matrix.path }}
|
||||
working-directory: bindings/matrix-sdk-crypto-js
|
||||
run: npm install
|
||||
|
||||
- name: run lint
|
||||
working-directory: ${{ matrix.path }}
|
||||
working-directory: bindings/matrix-sdk-crypto-js
|
||||
run: npm run lint
|
||||
|
||||
test-matrix-sdk-crypto-nodejs:
|
||||
name: ${{ matrix.os-name }} [m]-crypto-nodejs, v${{ matrix.node-version }}
|
||||
if: github.event_name == 'push' || !github.event.pull_request.draft
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
# Use Ubuntu LTS-1 for broader glibc compatibility.
|
||||
os: [ubuntu-20.04]
|
||||
node-version: [16.0, 18.0, 19.0, 20.0]
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
os-name: 🐧
|
||||
|
||||
- os: macos-latest
|
||||
os-name: 🍏
|
||||
node-version: 20.0
|
||||
|
||||
- node-version: 20.0
|
||||
build-doc: true
|
||||
|
||||
steps:
|
||||
- name: Checkout the repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Load cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/head/main' }}
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install NPM dependencies
|
||||
working-directory: ${{ env.MATRIX_SDK_CRYPTO_NODEJS_PATH }}
|
||||
run: npm install
|
||||
|
||||
- name: Build the Node.js binding
|
||||
working-directory: ${{ env.MATRIX_SDK_CRYPTO_NODEJS_PATH }}
|
||||
run: npm run release-build
|
||||
|
||||
- name: Test the Node.js binding
|
||||
working-directory: ${{ env.MATRIX_SDK_CRYPTO_NODEJS_PATH }}
|
||||
run: npm run test
|
||||
|
||||
# Building in dev-mode and copy lib in failure case
|
||||
- name: Build the Node.js binding in non-release
|
||||
if: failure()
|
||||
working-directory: ${{ env.MATRIX_SDK_CRYPTO_NODEJS_PATH }}
|
||||
run: |
|
||||
cp *.node release-mode-lib.node
|
||||
npm run build
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
name: Failure Files
|
||||
path: |
|
||||
bindings/matrix-sdk-crypto-nodejs/*.node
|
||||
/var/crash/*.crash
|
||||
|
||||
- if: ${{ matrix.build-doc }}
|
||||
name: Build the documentation
|
||||
working-directory: ${{ env.MATRIX_SDK_CRYPTO_NODEJS_PATH }}
|
||||
run: npm run doc
|
||||
|
||||
test-matrix-sdk-crypto-js:
|
||||
name: 🕸 [m]-crypto-js
|
||||
if: github.event_name == 'push' || !github.event.pull_request.draft
|
||||
|
||||
@@ -48,12 +48,6 @@ jobs:
|
||||
run:
|
||||
cargo doc --no-deps --workspace --features docsrs
|
||||
|
||||
- name: Build `matrix-sdk-crypto-nodejs` doc
|
||||
run: |
|
||||
cd bindings/matrix-sdk-crypto-nodejs
|
||||
npm install
|
||||
npm run build && npm run doc
|
||||
|
||||
- name: Build `matrix-sdk-crypto-js` doc
|
||||
run: |
|
||||
cd bindings/matrix-sdk-crypto-js
|
||||
@@ -63,10 +57,8 @@ jobs:
|
||||
- name: Prepare the doc hierarchy
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p doc/bindings/matrix-sdk-crypto-nodejs/
|
||||
mkdir -p doc/bindings/matrix-sdk-crypto-js/
|
||||
mv target/doc/* doc/
|
||||
mv bindings/matrix-sdk-crypto-nodejs/docs/* doc/bindings/matrix-sdk-crypto-nodejs/
|
||||
mv bindings/matrix-sdk-crypto-js/docs/* doc/bindings/matrix-sdk-crypto-js/
|
||||
|
||||
- name: Deploy documentation
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
name: Prepare Crypto-Node.js Release
|
||||
#
|
||||
# This is a helper workflow to craft a new Node.js release, trigger this via
|
||||
# the Github Workflow UI by dispatching it manually. Provide the version, the
|
||||
# matrix-sdk-crypto-nodejs npm package should be set to, and a optionally the
|
||||
# old version (as used in the git tag) this release should be compared to.
|
||||
#
|
||||
# This will then:
|
||||
# 1. bump the npm version to the one you specified
|
||||
# 2. commit that change together with the changelog (if it changed, see below)
|
||||
# 3. create the appropriate tag on that commit
|
||||
# 4. create the Github draft release, including the changes (if given, see below)
|
||||
# 5. push these to a new branch, including tag, triggering the `release-crypto-nodejs` workflow
|
||||
# 6. create a PR to merge these back into `main`
|
||||
#
|
||||
# Additionally, if you provide a tag to comapare this tag to, this will:
|
||||
# 1. create a changelog between the two releases, used for the github release
|
||||
# 2. update the Changelog.md and include it in the commit
|
||||
#
|
||||
# The remaining tasks are done by the release-crypto-nodejs workflow.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'New Node.js SemVer version to create'
|
||||
required: true
|
||||
type: string
|
||||
previous_version:
|
||||
description: 'Create the changelog by comparing to this old SemVer Version (as used in the tag) '
|
||||
type: string
|
||||
|
||||
env:
|
||||
PKG_PATH: "bindings/matrix-sdk-crypto-nodejs"
|
||||
TAG_PREFIX: "matrix-sdk-crypto-nodejs-v"
|
||||
|
||||
jobs:
|
||||
prepare-release:
|
||||
name: "Preparing crypto-nodejs release tag"
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
tag: "${{ env.TAG_PREFIX }}${{ inputs.version }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
|
||||
# Generate changelog since last tag, if given
|
||||
- name: Generate a changelog for upload
|
||||
if: inputs.previous_version
|
||||
uses: orhun/git-cliff-action@v1
|
||||
with:
|
||||
config: "${{ env.PKG_PATH }}/cliff.toml"
|
||||
args: --strip header "${{env.TAG_PREFIX}}${{ inputs.previous_version }}..HEAD"
|
||||
env:
|
||||
GIT_CLIFF_TAG: "Changes ${{ inputs.previous_version }} -> ${{ inputs.version }}"
|
||||
GIT_CLIFF_OUTPUT: "${{ env.PKG_PATH }}/CHANGES-${{ inputs.version }}.md"
|
||||
|
||||
# Update changelog since last tag, if given
|
||||
- name: Update existing Changelog
|
||||
if: inputs.previous_version
|
||||
uses: orhun/git-cliff-action@v1
|
||||
with:
|
||||
config: "${{ env.PKG_PATH }}/cliff.toml"
|
||||
args: "${{env.TAG_PREFIX}}${{ inputs.previous_version }}..HEAD"
|
||||
env:
|
||||
GIT_CLIFF_TAG: "${{ inputs.version }}"
|
||||
GIT_CLIFF_PREPEND: "${{ env.PKG_PATH }}/CHANGELOG.md"
|
||||
|
||||
- name: Set version
|
||||
id: package_version
|
||||
working-directory: ${{ env.PKG_PATH }}
|
||||
run: npm version ${{ inputs.version }}
|
||||
|
||||
- uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
default_author: github_actions
|
||||
message: "Tagging Crypto-Node.js for release"
|
||||
tag: "${{env.TAG_PREFIX}}${{ inputs.version }}"
|
||||
new_branch: "gh-action/release-${{ env.TAG_PREFIX }}${{ inputs.version }}"
|
||||
push: true
|
||||
add: |
|
||||
${{ env.PKG_PATH }}/package.json
|
||||
${{ env.PKG_PATH }}/CHANGELOG.md
|
||||
|
||||
# if we have generated changes
|
||||
- name: Update Github Release notes
|
||||
if: inputs.previous_version
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
tag_name: ${{ env.TAG_PREFIX }}${{ inputs.version }}
|
||||
body_path: "${{ env.PKG_PATH }}/CHANGES-${{ inputs.version }}.md"
|
||||
|
||||
# no changes, use the default changelog for the body
|
||||
- name: Update Github Release notes
|
||||
if: ${{!inputs.previous_version}}
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
tag_name: ${{ env.TAG_PREFIX }}${{ inputs.version }}
|
||||
body_path: "${{ env.PKG_PATH }}/CHANGELOG.md"
|
||||
|
||||
# let's create a PR for all this, too
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
title: "Preparing Release ${{ env.TAG_PREFIX }}${{ inputs.version }}"
|
||||
body: |
|
||||
Automatic Pull-Request to merge release ${{ env.TAG_PREFIX }}${{ inputs.version }}
|
||||
|
||||
trigger-release:
|
||||
# and trigger the tagging release workflow
|
||||
uses: ./.github/workflows/release-crypto-nodejs.yml
|
||||
needs: ['prepare-release']
|
||||
name: "Trigger release Workflow"
|
||||
with:
|
||||
tag: ${{needs.prepare-release.outputs.tag}}
|
||||
secrets:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
@@ -1,139 +0,0 @@
|
||||
name: Release Crypto-Node.js
|
||||
#
|
||||
# This workflow releases the crypto-bindings for nodejs
|
||||
#
|
||||
# It is triggered when seeing a tag prefixed matching `matrix-sdk-crypto-nodejs-v[0-9]+.*`,
|
||||
# which then build the native bindings for linux, mac and windows via the CI and uploads
|
||||
# them to the corresponding Github Release tag. Once they are finished, this workflow will
|
||||
# package the npm tar.gz and uploads that to the Github Release tag as well, before publishing
|
||||
# it to npmjs.com automatically.
|
||||
#
|
||||
# The usual way to trigger this is by manually triggering the `prep-crypto-nodejs-release`
|
||||
# workflow. See its documentation for instructions how to use it.
|
||||
|
||||
env:
|
||||
PKG_PATH: "bindings/matrix-sdk-crypto-nodejs"
|
||||
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: 'aarch64-linux-gnu-gcc'
|
||||
CARGO_TARGET_I686_UNKNOWN_LINUX_GNU_LINKER: 'i686-linux-gnu-gcc'
|
||||
CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER: 'arm-linux-gnueabihf-gcc'
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- matrix-sdk-crypto-nodejs-v[0-9]+.*
|
||||
workflow_call:
|
||||
inputs:
|
||||
tag:
|
||||
description: "The tag to build with"
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
NPM_TOKEN:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
upload-assets:
|
||||
name: "Upload prebuilt libraries"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# ----------------------------------- Linux
|
||||
# Use Ubuntu LTS-1 for broader glibc compatibility.
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
os: ubuntu-20.04
|
||||
- target: i686-unknown-linux-gnu
|
||||
apt_install: gcc-i686-linux-gnu g++-i686-linux-gnu
|
||||
os: ubuntu-20.04
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
os: ubuntu-20.04
|
||||
apt_install: gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
|
||||
- target: arm-unknown-linux-gnueabihf
|
||||
os: ubuntu-20.04
|
||||
apt_install: gcc-arm-linux-gnueabihf
|
||||
- target: x86_64-unknown-linux-musl
|
||||
os: ubuntu-20.04
|
||||
apt_install: musl-tools
|
||||
# ----------------------------------- macOS
|
||||
- target: aarch64-apple-darwin
|
||||
os: macos-latest
|
||||
- target: x86_64-apple-darwin
|
||||
os: macos-latest
|
||||
# ----------------------------------- Windows
|
||||
- target: x86_64-pc-windows-msvc
|
||||
os: windows-latest
|
||||
- target: i686-pc-windows-msvc
|
||||
os: windows-latest
|
||||
- target: aarch64-pc-windows-msvc
|
||||
os: windows-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
# use the given tag
|
||||
- uses: actions/checkout@v3
|
||||
name: "Checking out ${{ inputs.tag }}"
|
||||
if: "${{ inputs.tag }}"
|
||||
with:
|
||||
ref: ${{ inputs.tag }}
|
||||
# use the default
|
||||
- uses: actions/checkout@v3
|
||||
if: "${{ !inputs.tag }}"
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: nightly-2023-07-03
|
||||
targets: ${{ matrix.target }}
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
- if: ${{ matrix.apt_install }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ${{ matrix.apt_install }}
|
||||
- name: Build lib
|
||||
working-directory: ${{env.PKG_PATH}}
|
||||
run: |
|
||||
npm install --ignore-scripts
|
||||
npx napi build --platform --release --strip --target ${{ matrix.target }}
|
||||
- name: Upload artifacts to release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
files: ${{env.PKG_PATH}}/*.node
|
||||
|
||||
publish-nodejs-package:
|
||||
name: "Package nodejs package"
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- upload-assets
|
||||
steps:
|
||||
# use the given tag
|
||||
- uses: actions/checkout@v3
|
||||
name: "Checking out ${{ inputs.tag }}"
|
||||
if: "${{ inputs.tag }}"
|
||||
with:
|
||||
ref: ${{ inputs.tag }}
|
||||
# use the default
|
||||
- uses: actions/checkout@v3
|
||||
if: "${{ !inputs.tag }}"
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: nightly-2023-07-03
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
- name: Build lib
|
||||
working-directory: ${{env.PKG_PATH}}
|
||||
run: |
|
||||
npm install --ignore-scripts
|
||||
npm run build
|
||||
npm pack
|
||||
- name: Upload npm package to release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
files: ${{env.PKG_PATH}}/*tgz
|
||||
- name: Publish to npmjs.com
|
||||
uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
package: ${{env.PKG_PATH}}/package.json
|
||||
access: public
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
Generated
-102
@@ -956,15 +956,6 @@ version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.3"
|
||||
@@ -2402,16 +2393,6 @@ version = "0.2.147"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.7"
|
||||
@@ -2756,24 +2737,6 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matrix-sdk-crypto-nodejs"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"http",
|
||||
"matrix-sdk-common",
|
||||
"matrix-sdk-crypto",
|
||||
"matrix-sdk-sqlite",
|
||||
"napi",
|
||||
"napi-build",
|
||||
"napi-derive",
|
||||
"ruma",
|
||||
"serde_json",
|
||||
"tracing-subscriber",
|
||||
"vodozemac",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matrix-sdk-ffi"
|
||||
version = "0.2.0"
|
||||
@@ -3082,64 +3045,6 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "napi"
|
||||
version = "2.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ede2d12cd6fce44da537a4be1f5510c73be2506c2e32dfaaafd1f36968f3a0e"
|
||||
dependencies = [
|
||||
"bitflags 2.3.3",
|
||||
"ctor",
|
||||
"napi-derive",
|
||||
"napi-sys",
|
||||
"once_cell",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "napi-build"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "882a73d9ef23e8dc2ebbffb6a6ae2ef467c0f18ac10711e4cc59c5485d41df0e"
|
||||
|
||||
[[package]]
|
||||
name = "napi-derive"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da1c6a8fa84d549aa8708fcd062372bf8ec6e849de39016ab921067d21bde367"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"convert_case",
|
||||
"napi-derive-backend",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "napi-derive-backend"
|
||||
version = "1.0.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20bbc7c69168d06a848f925ec5f0e0997f98e8c8d4f2cc30157f0da51c009e17"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"semver",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "napi-sys"
|
||||
version = "2.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "166b5ef52a3ab5575047a9fe8d4a030cdd0f63c96f071cd6907674453b07bae3"
|
||||
dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.11"
|
||||
@@ -5268,7 +5173,6 @@ dependencies = [
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"time 0.3.22",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
@@ -5337,12 +5241,6 @@ dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
|
||||
@@ -3,7 +3,6 @@ members = [
|
||||
"benchmarks",
|
||||
"bindings/matrix-sdk-crypto-ffi",
|
||||
"bindings/matrix-sdk-crypto-js",
|
||||
"bindings/matrix-sdk-crypto-nodejs",
|
||||
"bindings/matrix-sdk-ffi",
|
||||
"crates/*",
|
||||
"testing/*",
|
||||
|
||||
+10
-5
@@ -9,17 +9,22 @@ maintained by the owners of the Matrix Rust SDK project.
|
||||
crate,
|
||||
* [`matrix-sdk-crypto-js`], JavaScript bindings of the
|
||||
[`matrix-sdk-crypto`] crate,
|
||||
* [`matrix-sdk-crypto-nodejs`], Node.js bindings of the
|
||||
[`matrix-sdk-crypto`] crate,
|
||||
* [`matrix-sdk-ffi`], UniFFI bindings of the [`matrix-sdk`] crate.
|
||||
|
||||
There are also external bindings in other repositories:
|
||||
|
||||
* [`matrix-sdk-crypto-nodejs`], Node.js bindings of the
|
||||
[`matrix-sdk-crypto`] crate
|
||||
|
||||
[`apple`]: ./apple
|
||||
[`matrix-sdk-crypto-ffi`]: ./matrix-sdk-crypto-ffi
|
||||
[`matrix-sdk-crypto-js`]: ./matrix-sdk-crypto-js
|
||||
[`matrix-sdk-crypto-nodejs`]: ./matrix-sdk-crypto-nodejs
|
||||
[`matrix-sdk-crypto`]: ../crates/matrix-sdk-crypto
|
||||
[`matrix-sdk-ffi`]: ./matrix-sdk-ffi
|
||||
[`matrix-sdk`]: ../crates/matrix-sdk
|
||||
|
||||
# Contributing
|
||||
To contribute read this [guide](./CONTRIBUTING.md).
|
||||
[`matrix-sdk-crypto-nodejs`]: https://github.com/matrix-org/matrix-rust-sdk-crypto-nodejs
|
||||
|
||||
## Contributing
|
||||
|
||||
To contribute read this [guide](./CONTRIBUTING.md).
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/node_modules
|
||||
/package-lock.json
|
||||
/index.js
|
||||
/index.d.ts
|
||||
/matrix-sdk-crypto.*.node
|
||||
/docs/*
|
||||
*.tgz
|
||||
@@ -1,7 +0,0 @@
|
||||
src/
|
||||
tests/
|
||||
Cargo.toml
|
||||
build.rs
|
||||
*.node
|
||||
*.tgz
|
||||
tsconfig.json
|
||||
@@ -1 +0,0 @@
|
||||
/pkg
|
||||
@@ -1,9 +0,0 @@
|
||||
// prettier configuration: the same as the conventions used throughout Matrix.org
|
||||
// see: https://github.com/matrix-org/eslint-plugin-matrix-org/blob/main/.prettierrc.js
|
||||
|
||||
module.exports = {
|
||||
printWidth: 120,
|
||||
tabWidth: 4,
|
||||
quoteProps: "consistent",
|
||||
trailingComma: "all",
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
# Matrix-Rust-SDK Node.js Bindings
|
||||
|
||||
## 0.1.0-beta.1 - 2022-07-14
|
||||
|
||||
- Fixing broken download link, [#842](https://github.com/matrix-org/matrix-rust-sdk/issues/842)
|
||||
|
||||
## 0.1.0-beta.0 - 2022-07-12
|
||||
|
||||
Welcome to the first release of `matrix-sdk-crypto-nodejs`. This is a
|
||||
Node.js binding for the Rust `matrix-sdk-crypto` library. This is a
|
||||
no-network-IO implementation of a state machine, named `OlmMachine`,
|
||||
that handles E2EE (End-to-End Encryption) for Matrix clients.
|
||||
|
||||
The goal of this binding is _not_ to cover the entirety of the
|
||||
`matrix-sdk-crypto` API, but only what's required to build Matrix bots
|
||||
or Matrix bridges (i.e. to connect different networks together via the
|
||||
Matrix protocol).
|
||||
|
||||
This project replaces and deprecates a previous project, with the same
|
||||
name and same goals, inside [the `matrix-rust-sdk-bindings`
|
||||
repository](https://github.com/matrix-org/matrix-rust-sdk-bindings),
|
||||
with the NPM package name `@turt2live/matrix-sdk-crypto-nodejs`. The
|
||||
The new official package name is
|
||||
`@matrix-org/matrix-sdk-crypto-nodejs`.
|
||||
|
||||
Note: All bindings are now part of [the `matrix-rust-sdk`
|
||||
repository](https://github.com/matrix-org/matrix-rust-sdk) (see the
|
||||
`bindings/` root directory).
|
||||
|
||||
[A documentation is available inside the new
|
||||
`matrix-sdk-crypto-nodejs`
|
||||
project](https://github.com/matrix-org/matrix-rust-sdk/tree/0bde5ccf38f8cda3865297a2d12ddcdaf4b80ca7/bindings/matrix-sdk-crypto-nodejs).
|
||||
@@ -1,45 +0,0 @@
|
||||
[package]
|
||||
authors = ["Ivan Enderlin <ivane@element.io>"]
|
||||
description = "Matrix encryption library, for NodeJS"
|
||||
edition = "2021"
|
||||
homepage = "https://github.com/matrix-org/matrix-rust-sdk"
|
||||
keywords = ["matrix", "chat", "messaging", "ruma", "nio"]
|
||||
license = "Apache-2.0"
|
||||
name = "matrix-sdk-crypto-nodejs"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/matrix-org/matrix-rust-sdk"
|
||||
rust-version = { workspace = true }
|
||||
version = "0.6.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[features]
|
||||
default = ["bundled-sqlite"]
|
||||
bundled-sqlite = ["matrix-sdk-sqlite/bundled"]
|
||||
qrcode = ["matrix-sdk-crypto/qrcode"]
|
||||
tracing = ["dep:tracing-subscriber"]
|
||||
|
||||
[dependencies]
|
||||
matrix-sdk-common = { version = "0.6.0", path = "../../crates/matrix-sdk-common", features = ["js"] }
|
||||
matrix-sdk-sqlite = { version = "0.1.0", path = "../../crates/matrix-sdk-sqlite", features = ["crypto-store"] }
|
||||
ruma = { workspace = true, features = ["rand"] }
|
||||
napi = { version = "2.9.1", default-features = false, features = ["napi6", "tokio_rt"] }
|
||||
napi-derive = "2.9.1"
|
||||
serde_json = { workspace = true }
|
||||
http = { workspace = true }
|
||||
tracing-subscriber = { version = "0.3", default-features = false, features = ["tracing-log", "time", "smallvec", "fmt", "env-filter"], optional = true }
|
||||
vodozemac = { workspace = true, features = ["js"] }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
[dependencies.matrix-sdk-crypto]
|
||||
path = "../../crates/matrix-sdk-crypto"
|
||||
version = "0.6.0"
|
||||
default_features = false
|
||||
features = ["js", "automatic-room-key-forwarding"]
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = "2.0.0"
|
||||
@@ -1,220 +0,0 @@
|
||||
# `matrix-sdk-crypto-nodejs`
|
||||
|
||||
Welcome to the [Node.js] binding for the Rust [`matrix-sdk-crypto`]
|
||||
library! This binding is part of the [`matrix-rust-sdk`] project,
|
||||
which is a library implementation of a [Matrix] client-server.
|
||||
|
||||
`matrix-sdk-crypto-nodejs` is a no-network-IO implementation of a
|
||||
state machine, named `OlmMachine`, that handles E2EE ([End-to-End
|
||||
Encryption](https://en.wikipedia.org/wiki/End-to-end_encryption)) for
|
||||
[Matrix] clients.
|
||||
|
||||
## Usage
|
||||
|
||||
Just add the latest release to your `package.json`:
|
||||
|
||||
```sh
|
||||
$ npm install --save @matrix-org/matrix-sdk-crypto-nodejs
|
||||
```
|
||||
|
||||
When installing, NPM will download the corresponding prebuilt Rust library for your current host system. The following are supported:
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Platform</th>
|
||||
<th>Architecture</th>
|
||||
<th>Triple</th>
|
||||
<th>Prebuilt available</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="5">Linux</td>
|
||||
<td rowspan="2"><code>aarch</code></td>
|
||||
<td><code>aarch64-unknown-linux-gnu</code></td>
|
||||
<td>✅</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>arm-unknown-linux-gnueabihf</code></td>
|
||||
<td>✅</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="3"><code>amd</code></td>
|
||||
<td><code>x86_64-unknown-linux-gnu</code></td>
|
||||
<td>✅</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>x86_64-unknown-linux-musl</code></td>
|
||||
<td>✅</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>i686-unknown-linux-gnu</code></td>
|
||||
<td>✅</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">macOS</td>
|
||||
<td><code>aarch</code></td>
|
||||
<td><code>arch64-apple-darwin</code></td>
|
||||
<td>✅</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>amd</code></td>
|
||||
<td><code>x86_64-apple-darwin</code></td>
|
||||
<td>✅</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="3">Windows</td>
|
||||
<td><code>aarch</code></td>
|
||||
<td><code>aarch64-pc-windows-msvc</code></td>
|
||||
<td>✅</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2"><code>amd</code></td>
|
||||
<td><code>x86_64-pc-windows-msvc</code></td>
|
||||
<td>✅</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>i686-pc-windows-msvc</code></td>
|
||||
<td>✅</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Development
|
||||
|
||||
This Node.js binding is written in [Rust]. To build this binding, you
|
||||
need to install the Rust compiler, see [the Install Rust
|
||||
Page](https://www.rust-lang.org/tools/install). Then, the workflow is
|
||||
pretty classical by using [npm], see [the Downloading and installing
|
||||
Node.js and npm
|
||||
Page](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).
|
||||
|
||||
The binding is compatible with, and tested against, the Node.js
|
||||
versions that are in “current”, “active” or “maintenance” states,
|
||||
according to [the Node.js Releases
|
||||
Page](https://nodejs.org/en/about/releases/), _and_ which are
|
||||
compatible with [NAPI v6 (Node.js
|
||||
API)](https://nodejs.org/api/n-api.html#node-api-version-matrix). It
|
||||
means that this binding will work with the following versions: 16.0.0,
|
||||
18.0.0, 19.0.0 and 20.0.0.
|
||||
|
||||
Once the Rust compiler, Node.js and npm are installed, you can run the
|
||||
following commands:
|
||||
|
||||
```sh
|
||||
$ npm install --ignore-scripts
|
||||
$ npm run build
|
||||
$ npm run test
|
||||
```
|
||||
|
||||
An `index.js`, `index.d.ts` and a `*.node` files should be
|
||||
generated. At the same level of those files, you can edit a file and
|
||||
try this:
|
||||
|
||||
```javascript
|
||||
const { OlmMachine } = require("./index.js");
|
||||
|
||||
// Let's see what we can do.
|
||||
```
|
||||
|
||||
The `OlmMachine` state machine works in a push/pull manner:
|
||||
|
||||
- You push state changes and events retrieved from a Matrix homeserver
|
||||
`/sync` response, into the state machine,
|
||||
- You pull requests that you will need to send back to the homeserver
|
||||
out of the state machine.
|
||||
|
||||
```javascript
|
||||
const { OlmMachine, UserId, DeviceId, RoomId, DeviceLists } = require("./index.js");
|
||||
|
||||
async function main() {
|
||||
// Define a user ID.
|
||||
const alice = new UserId("@alice:example.org");
|
||||
|
||||
// Define a device ID.
|
||||
const device = new DeviceId("DEVICEID");
|
||||
|
||||
// Let's create the `OlmMachine` state machine.
|
||||
const machine = await OlmMachine.initialize(alice, device);
|
||||
|
||||
// Let's pretend we have received changes and events from a
|
||||
// `/sync` endpoint of a Matrix homeserver, …
|
||||
const toDeviceEvents = "[]"; // JSON-encoded list of events
|
||||
const changedDevices = new DeviceLists();
|
||||
const oneTimeKeyCounts = {};
|
||||
const unusedFallbackKeys = [];
|
||||
|
||||
// … and push them into the state machine.
|
||||
const decryptedToDevice = await machine.receiveSyncChanges(
|
||||
toDeviceEvents,
|
||||
changedDevices,
|
||||
oneTimeKeyCounts,
|
||||
unusedFallbackKeys,
|
||||
);
|
||||
|
||||
// Now, let's pull requests that we need to send out to the Matrix
|
||||
// homeserver.
|
||||
const outgoingRequests = await machine.outgoingRequests();
|
||||
|
||||
// To complete the workflow, send the requests here out and call
|
||||
// `machine.markRequestAsSent`.
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
### With tracing (experimental)
|
||||
|
||||
If you want to enable [tracing](https://tracing.rs), i.e. to get the
|
||||
logs, you should re-compile the extension with the `tracing` feature
|
||||
turned on:
|
||||
|
||||
```sh
|
||||
$ npm run build -- --features tracing
|
||||
```
|
||||
|
||||
Now, you can use the `MATRIX_LOG` environment variable to tweak the log filtering, such as:
|
||||
|
||||
```sh
|
||||
$ MATRIX_LOG=debug npm run test
|
||||
```
|
||||
|
||||
See
|
||||
[`tracing-subscriber`](https://tracing.rs/tracing_subscriber/index.html)
|
||||
to learn more about the `RUST_LOG`/`MATRIX_LOG` environment variable.
|
||||
|
||||
#### Using tracing in dependent projects
|
||||
|
||||
To enable tracing in client applications that import these bindings, here's how to do it in
|
||||
a local development environment:
|
||||
|
||||
- In this directory, run `npm link` to make your local build of the bindings available to
|
||||
other Node projects on your system
|
||||
- In your client app's source directory, run `npm link @matrix-org/matrix-sdk-crypto-nodejs`
|
||||
to make it use your trace-enabled local build of the bindings
|
||||
- In your client app's source code, add a call to `initTracing` near startup time
|
||||
- Run your app with the `MATRIX_LOG` environment variable set to the desired log level
|
||||
|
||||
Either `npm link` command may be substituted with `yarn link`.
|
||||
|
||||
## Documentation
|
||||
|
||||
[The documentation can be found
|
||||
online](https://matrix-org.github.io/matrix-rust-sdk/bindings/matrix-sdk-crypto-nodejs/).
|
||||
|
||||
To generate the documentation locally, please run the following
|
||||
command:
|
||||
|
||||
```sh
|
||||
$ npm run doc
|
||||
```
|
||||
|
||||
The documentation is generated in the `./docs` directory.
|
||||
|
||||
[Node.js]: https://nodejs.org/
|
||||
[`matrix-sdk-crypto`]: https://github.com/matrix-org/matrix-rust-sdk/tree/main/crates/matrix-sdk-crypto
|
||||
[`matrix-rust-sdk`]: https://github.com/matrix-org/matrix-rust-sdk
|
||||
[Matrix]: https://matrix.org/
|
||||
[Rust]: https://www.rust-lang.org/
|
||||
[npm]: https://www.npmjs.com/
|
||||
@@ -1,3 +0,0 @@
|
||||
fn main() {
|
||||
napi_build::setup();
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
const { HttpsProxyAgent } = require("https-proxy-agent");
|
||||
const { DownloaderHelper } = require("node-downloader-helper");
|
||||
const { version } = require("./package.json");
|
||||
const { platform, arch } = process;
|
||||
|
||||
const DOWNLOADS_BASE_URL = "https://github.com/matrix-org/matrix-rust-sdk/releases/download";
|
||||
const CURRENT_VERSION = `matrix-sdk-crypto-nodejs-v${version}`;
|
||||
|
||||
const byteHelper = function (value) {
|
||||
if (value === 0) {
|
||||
return "0 b";
|
||||
}
|
||||
const units = ["b", "kB", "MB", "GB", "TB"];
|
||||
const number = Math.floor(Math.log(value) / Math.log(1024));
|
||||
return (value / Math.pow(1024, Math.floor(number))).toFixed(1) + " " + units[number];
|
||||
};
|
||||
|
||||
function download_lib(libname) {
|
||||
let startTime = new Date();
|
||||
|
||||
const url = `${DOWNLOADS_BASE_URL}/${CURRENT_VERSION}/${libname}`;
|
||||
console.info(`Downloading lib ${libname} from ${url}`);
|
||||
const dl = new DownloaderHelper(url, __dirname, {
|
||||
override: true,
|
||||
});
|
||||
|
||||
const proxy = process.env.https_proxy ?? process.env.HTTPS_PROXY;
|
||||
if (proxy) {
|
||||
const proxyAgent = new HttpsProxyAgent(proxy);
|
||||
dl.updateOptions({
|
||||
httpsRequestOptions: { agent: proxyAgent },
|
||||
});
|
||||
}
|
||||
|
||||
dl.on("end", () => console.info("Download Completed"));
|
||||
dl.on("error", (err) => console.info("Download Failed", err));
|
||||
dl.on("progress", (stats) => {
|
||||
const progress = stats.progress.toFixed(1);
|
||||
const speed = byteHelper(stats.speed);
|
||||
const downloaded = byteHelper(stats.downloaded);
|
||||
const total = byteHelper(stats.total);
|
||||
|
||||
// print every one second (`progress.throttled` can be used instead)
|
||||
const currentTime = new Date();
|
||||
const elaspsedTime = currentTime - startTime;
|
||||
if (elaspsedTime > 1000) {
|
||||
startTime = currentTime;
|
||||
console.info(`${speed}/s - ${progress}% [${downloaded}/${total}]`);
|
||||
}
|
||||
});
|
||||
dl.start().catch((err) => console.error(err));
|
||||
}
|
||||
|
||||
function isMusl() {
|
||||
// For Node 10
|
||||
if (!process.report || typeof process.report.getReport !== "function") {
|
||||
try {
|
||||
return readFileSync("/usr/bin/ldd", "utf8").includes("musl");
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
const { glibcVersionRuntime } = process.report.getReport().header;
|
||||
return !glibcVersionRuntime;
|
||||
}
|
||||
}
|
||||
|
||||
switch (platform) {
|
||||
case "win32":
|
||||
switch (arch) {
|
||||
case "x64":
|
||||
download_lib("matrix-sdk-crypto.win32-x64-msvc.node");
|
||||
break;
|
||||
case "ia32":
|
||||
download_lib("matrix-sdk-crypto.win32-ia32-msvc.node");
|
||||
break;
|
||||
case "arm64":
|
||||
download_lib("matrix-sdk-crypto.win32-arm64-msvc.node");
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on Windows: ${arch}`);
|
||||
}
|
||||
break;
|
||||
case "darwin":
|
||||
switch (arch) {
|
||||
case "x64":
|
||||
download_lib("matrix-sdk-crypto.darwin-x64.node");
|
||||
break;
|
||||
case "arm64":
|
||||
download_lib("matrix-sdk-crypto.darwin-arm64.node");
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on macOS: ${arch}`);
|
||||
}
|
||||
break;
|
||||
case "linux":
|
||||
switch (arch) {
|
||||
case "x64":
|
||||
if (isMusl()) {
|
||||
download_lib("matrix-sdk-crypto.linux-x64-musl.node");
|
||||
} else {
|
||||
download_lib("matrix-sdk-crypto.linux-x64-gnu.node");
|
||||
}
|
||||
break;
|
||||
case "arm64":
|
||||
if (isMusl()) {
|
||||
throw new Error("Linux for arm64 musl isn't support at the moment");
|
||||
} else {
|
||||
download_lib("matrix-sdk-crypto.linux-arm64-gnu.node");
|
||||
}
|
||||
break;
|
||||
case "arm":
|
||||
download_lib("matrix-sdk-crypto.linux-arm-gnueabihf.node");
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on Linux: ${arch}`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"name": "@matrix-org/matrix-sdk-crypto-nodejs",
|
||||
"version": "0.1.0-beta.1",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"napi": {
|
||||
"name": "matrix-sdk-crypto",
|
||||
"triples": {
|
||||
"additional": [
|
||||
"aarch64-apple-darwin"
|
||||
]
|
||||
}
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@napi-rs/cli": "^2.9.0",
|
||||
"jest": "^28.1.0",
|
||||
"prettier": "^2.8.3",
|
||||
"typedoc": "^0.22.17",
|
||||
"yargs-parser": "~21.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "prettier --check .",
|
||||
"release-build": "napi build --platform --release --strip",
|
||||
"build": "napi build --platform",
|
||||
"postinstall": "node download-lib.js",
|
||||
"test": "jest --verbose --testTimeout 10000",
|
||||
"doc": "typedoc --tsconfig ."
|
||||
},
|
||||
"dependencies": {
|
||||
"https-proxy-agent": "^5.0.1",
|
||||
"node-downloader-helper": "^2.1.5"
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
use std::{
|
||||
io::{Cursor, Read},
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
use napi::bindgen_prelude::Uint8Array;
|
||||
use napi_derive::*;
|
||||
|
||||
use crate::into_err;
|
||||
|
||||
/// A type to encrypt and to decrypt anything that can fit in an
|
||||
/// `Uint8Array`, usually big buffer.
|
||||
#[napi]
|
||||
pub struct Attachment;
|
||||
|
||||
#[napi]
|
||||
impl Attachment {
|
||||
/// Encrypt the content of the `Uint8Array`.
|
||||
///
|
||||
/// It produces an `EncryptedAttachment`, which can be used to
|
||||
/// retrieve the media encryption information, or the encrypted
|
||||
/// data.
|
||||
#[napi]
|
||||
pub fn encrypt(array: Uint8Array) -> napi::Result<EncryptedAttachment> {
|
||||
let buffer: &[u8] = array.deref();
|
||||
|
||||
let mut cursor = Cursor::new(buffer);
|
||||
let mut encryptor = matrix_sdk_crypto::AttachmentEncryptor::new(&mut cursor);
|
||||
|
||||
let mut encrypted_data = Vec::new();
|
||||
encryptor.read_to_end(&mut encrypted_data).map_err(into_err)?;
|
||||
|
||||
let media_encryption_info = Some(encryptor.finish());
|
||||
|
||||
Ok(EncryptedAttachment {
|
||||
encrypted_data: Uint8Array::new(encrypted_data),
|
||||
media_encryption_info,
|
||||
})
|
||||
}
|
||||
|
||||
/// Decrypt an `EncryptedAttachment`.
|
||||
///
|
||||
/// The encrypted attachment can be created manually, or from the
|
||||
/// `encrypt` method.
|
||||
///
|
||||
/// **Warning**: The encrypted attachment can be used only
|
||||
/// **once**! The encrypted data will still be present, but the
|
||||
/// media encryption info (which contain secrets) will be
|
||||
/// destroyed. It is still possible to get a JSON-encoded backup
|
||||
/// by calling `EncryptedAttachment.mediaEncryptionInfo`.
|
||||
#[napi]
|
||||
pub fn decrypt(attachment: &mut EncryptedAttachment) -> napi::Result<Uint8Array> {
|
||||
let Some(media_encryption_info) = attachment.media_encryption_info.take() else {
|
||||
return Err(napi::Error::from_reason(
|
||||
"The media encryption info are absent from the given encrypted attachment"
|
||||
.to_owned(),
|
||||
));
|
||||
};
|
||||
|
||||
let encrypted_data: &[u8] = attachment.encrypted_data.deref();
|
||||
|
||||
let mut cursor = Cursor::new(encrypted_data);
|
||||
let mut decryptor =
|
||||
matrix_sdk_crypto::AttachmentDecryptor::new(&mut cursor, media_encryption_info)
|
||||
.map_err(into_err)?;
|
||||
|
||||
let mut decrypted_data = Vec::new();
|
||||
decryptor.read_to_end(&mut decrypted_data).map_err(into_err)?;
|
||||
|
||||
Ok(Uint8Array::new(decrypted_data))
|
||||
}
|
||||
}
|
||||
|
||||
/// An encrypted attachment, usually created from `Attachment.encrypt`.
|
||||
#[napi]
|
||||
pub struct EncryptedAttachment {
|
||||
media_encryption_info: Option<matrix_sdk_crypto::MediaEncryptionInfo>,
|
||||
|
||||
/// The actual encrypted data.
|
||||
pub encrypted_data: Uint8Array,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl EncryptedAttachment {
|
||||
/// Create a new encrypted attachment manually.
|
||||
///
|
||||
/// It needs encrypted data, stored in an `Uint8Array`, and a
|
||||
/// [media encryption
|
||||
/// information](https://docs.rs/matrix-sdk-crypto/latest/matrix_sdk_crypto/struct.MediaEncryptionInfo.html),
|
||||
/// as a JSON-encoded string.
|
||||
///
|
||||
/// The media encryption information aren't stored as a string:
|
||||
/// they are parsed, validated and fully deserialized.
|
||||
///
|
||||
/// See [the specification to learn
|
||||
/// more](https://spec.matrix.org/unstable/client-server-api/#extensions-to-mroommessage-msgtypes).
|
||||
#[napi(constructor)]
|
||||
pub fn new(encrypted_data: Uint8Array, media_encryption_info: String) -> napi::Result<Self> {
|
||||
Ok(Self {
|
||||
encrypted_data,
|
||||
media_encryption_info: Some(
|
||||
serde_json::from_str(media_encryption_info.as_str()).map_err(into_err)?,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the media encryption info as a JSON-encoded string. The
|
||||
/// structure is fully valid.
|
||||
///
|
||||
/// If the media encryption info have been consumed already, it
|
||||
/// will return `null`.
|
||||
#[napi(getter)]
|
||||
pub fn media_encryption_info(&self) -> Option<String> {
|
||||
serde_json::to_string(self.media_encryption_info.as_ref()?).ok()
|
||||
}
|
||||
|
||||
/// Check whether the media encryption info has been consumed by
|
||||
/// `Attachment.decrypt` already.
|
||||
#[napi(getter)]
|
||||
pub fn has_media_encryption_info_been_consumed(&self) -> bool {
|
||||
self.media_encryption_info.is_none()
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use matrix_sdk_common::deserialized_responses::ShieldState as RustShieldState;
|
||||
use napi::bindgen_prelude::{BigInt, FromNapiValue, ToNapiValue};
|
||||
use napi_derive::*;
|
||||
|
||||
use crate::events;
|
||||
|
||||
/// An encryption algorithm to be used to encrypt messages sent to a
|
||||
/// room.
|
||||
#[napi]
|
||||
pub enum EncryptionAlgorithm {
|
||||
/// Olm version 1 using Curve25519, AES-256, and SHA-256.
|
||||
OlmV1Curve25519AesSha2,
|
||||
|
||||
/// Megolm version 1 using AES-256 and SHA-256.
|
||||
MegolmV1AesSha2,
|
||||
}
|
||||
|
||||
impl From<EncryptionAlgorithm> for matrix_sdk_crypto::types::EventEncryptionAlgorithm {
|
||||
fn from(value: EncryptionAlgorithm) -> Self {
|
||||
use EncryptionAlgorithm::*;
|
||||
|
||||
match value {
|
||||
OlmV1Curve25519AesSha2 => Self::OlmV1Curve25519AesSha2,
|
||||
MegolmV1AesSha2 => Self::MegolmV1AesSha2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<matrix_sdk_crypto::types::EventEncryptionAlgorithm> for EncryptionAlgorithm {
|
||||
fn from(value: matrix_sdk_crypto::types::EventEncryptionAlgorithm) -> Self {
|
||||
use matrix_sdk_crypto::types::EventEncryptionAlgorithm::*;
|
||||
|
||||
match value {
|
||||
OlmV1Curve25519AesSha2 => Self::OlmV1Curve25519AesSha2,
|
||||
MegolmV1AesSha2 => Self::MegolmV1AesSha2,
|
||||
_ => unreachable!("Unknown variant"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Settings for an encrypted room.
|
||||
///
|
||||
/// This determines the algorithm and rotation periods of a group
|
||||
/// session.
|
||||
#[napi]
|
||||
pub struct EncryptionSettings {
|
||||
/// The encryption algorithm that should be used in the room.
|
||||
pub algorithm: EncryptionAlgorithm,
|
||||
|
||||
/// How long the session should be used before changing it,
|
||||
/// expressed in microseconds.
|
||||
pub rotation_period: BigInt,
|
||||
|
||||
/// How many messages should be sent before changing the session.
|
||||
pub rotation_period_messages: BigInt,
|
||||
|
||||
/// The history visibility of the room when the session was
|
||||
/// created.
|
||||
pub history_visibility: events::HistoryVisibility,
|
||||
|
||||
/// Should untrusted devices receive the room key, or should they be
|
||||
/// excluded from the conversation.
|
||||
pub only_allow_trusted_devices: bool,
|
||||
}
|
||||
|
||||
impl Default for EncryptionSettings {
|
||||
fn default() -> Self {
|
||||
let default = matrix_sdk_crypto::olm::EncryptionSettings::default();
|
||||
|
||||
Self {
|
||||
algorithm: default.algorithm.into(),
|
||||
rotation_period: {
|
||||
let n: u64 = default.rotation_period.as_micros().try_into().unwrap();
|
||||
|
||||
n.into()
|
||||
},
|
||||
rotation_period_messages: {
|
||||
let n = default.rotation_period_msgs;
|
||||
|
||||
n.into()
|
||||
},
|
||||
history_visibility: default.history_visibility.into(),
|
||||
only_allow_trusted_devices: default.only_allow_trusted_devices,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl EncryptionSettings {
|
||||
/// Create a new `EncryptionSettings` with default values.
|
||||
#[napi(constructor)]
|
||||
pub fn new() -> EncryptionSettings {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&EncryptionSettings> for matrix_sdk_crypto::olm::EncryptionSettings {
|
||||
fn from(value: &EncryptionSettings) -> Self {
|
||||
Self {
|
||||
algorithm: value.algorithm.into(),
|
||||
rotation_period: Duration::from_micros(value.rotation_period.get_u64().1),
|
||||
rotation_period_msgs: value.rotation_period_messages.get_u64().1,
|
||||
history_visibility: value.history_visibility.into(),
|
||||
only_allow_trusted_devices: value.only_allow_trusted_devices,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Take a look at [`matrix_sdk_common::deserialized_responses::ShieldState`]
|
||||
/// for more info.
|
||||
#[napi]
|
||||
pub enum ShieldColor {
|
||||
Red,
|
||||
Grey,
|
||||
None,
|
||||
}
|
||||
|
||||
/// Take a look at [`matrix_sdk_common::deserialized_responses::ShieldState`]
|
||||
/// for more info.
|
||||
#[napi]
|
||||
pub struct ShieldState {
|
||||
pub color: ShieldColor,
|
||||
pub message: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl From<RustShieldState> for ShieldState {
|
||||
fn from(value: RustShieldState) -> Self {
|
||||
match value {
|
||||
RustShieldState::Red { message } => {
|
||||
ShieldState { color: ShieldColor::Red, message: Some(message) }
|
||||
}
|
||||
RustShieldState::Grey { message } => {
|
||||
ShieldState { color: ShieldColor::Grey, message: Some(message) }
|
||||
}
|
||||
RustShieldState::None => ShieldState { color: ShieldColor::None, message: None },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/// Generic error wrapping `napi::Error`.
|
||||
#[derive(Debug)]
|
||||
pub struct Error(napi::Error);
|
||||
|
||||
impl<E> From<E> for Error
|
||||
where
|
||||
E: std::error::Error,
|
||||
{
|
||||
fn from(error: E) -> Self {
|
||||
Self(napi::Error::from_reason(error.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for napi::Error {
|
||||
#[inline]
|
||||
fn from(value: Error) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to replace the `E` to `Error` to `napi::Error` conversion.
|
||||
#[inline]
|
||||
pub fn into_err<E>(error: E) -> napi::Error
|
||||
where
|
||||
E: std::error::Error,
|
||||
{
|
||||
Error::from(error).into()
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
//! Types related to events.
|
||||
|
||||
use napi::bindgen_prelude::{FromNapiValue, ToNapiValue};
|
||||
use napi_derive::*;
|
||||
use ruma::events::room::history_visibility::HistoryVisibility as RumaHistoryVisibility;
|
||||
|
||||
/// Who can see a room's history.
|
||||
#[napi]
|
||||
#[derive(Debug)]
|
||||
pub enum HistoryVisibility {
|
||||
/// Previous events are accessible to newly joined members from
|
||||
/// the point they were invited onwards.
|
||||
///
|
||||
/// Events stop being accessible when the member's state changes
|
||||
/// to something other than *invite* or *join*.
|
||||
Invited,
|
||||
|
||||
/// Previous events are accessible to newly joined members from
|
||||
/// the point they joined the room onwards.
|
||||
///
|
||||
/// Events stop being accessible when the member's state changes
|
||||
/// to something other than *join*.
|
||||
Joined,
|
||||
|
||||
/// Previous events are always accessible to newly joined members.
|
||||
///
|
||||
/// All events in the room are accessible, even those sent when
|
||||
/// the member was not a part of the room.
|
||||
Shared,
|
||||
|
||||
/// All events while this is the `HistoryVisibility` value may be
|
||||
/// shared by any participating homeserver with anyone, regardless
|
||||
/// of whether they have ever joined the room.
|
||||
WorldReadable,
|
||||
}
|
||||
|
||||
impl From<HistoryVisibility> for RumaHistoryVisibility {
|
||||
fn from(value: HistoryVisibility) -> Self {
|
||||
use HistoryVisibility::*;
|
||||
|
||||
match value {
|
||||
Invited => Self::Invited,
|
||||
Joined => Self::Joined,
|
||||
Shared => Self::Shared,
|
||||
WorldReadable => Self::WorldReadable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RumaHistoryVisibility> for HistoryVisibility {
|
||||
fn from(value: RumaHistoryVisibility) -> Self {
|
||||
use RumaHistoryVisibility::*;
|
||||
|
||||
match value {
|
||||
Invited => Self::Invited,
|
||||
Joined => Self::Joined,
|
||||
Shared => Self::Shared,
|
||||
WorldReadable => Self::WorldReadable,
|
||||
_ => unreachable!("Unknown variant"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,281 +0,0 @@
|
||||
//! Types for [Matrix](https://matrix.org/) identifiers for devices,
|
||||
//! events, keys, rooms, servers, users and URIs.
|
||||
|
||||
use napi::bindgen_prelude::{FromNapiValue, ToNapiValue};
|
||||
use napi_derive::*;
|
||||
|
||||
use crate::into_err;
|
||||
|
||||
/// A Matrix [user ID].
|
||||
///
|
||||
/// [user ID]: https://spec.matrix.org/v1.2/appendices/#user-identifiers
|
||||
#[napi]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UserId {
|
||||
pub(crate) inner: ruma::OwnedUserId,
|
||||
}
|
||||
|
||||
impl From<ruma::OwnedUserId> for UserId {
|
||||
fn from(inner: ruma::OwnedUserId) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl UserId {
|
||||
/// Parse/validate and create a new `UserId`.
|
||||
#[napi(constructor, strict)]
|
||||
pub fn new(id: String) -> napi::Result<Self> {
|
||||
Ok(Self::from(ruma::UserId::parse(id.as_str()).map_err(into_err)?))
|
||||
}
|
||||
|
||||
/// Returns the user's localpart.
|
||||
#[napi(getter)]
|
||||
pub fn localpart(&self) -> String {
|
||||
self.inner.localpart().to_owned()
|
||||
}
|
||||
|
||||
/// Returns the server name of the user ID.
|
||||
#[napi(getter)]
|
||||
pub fn server_name(&self) -> ServerName {
|
||||
ServerName { inner: self.inner.server_name().to_owned() }
|
||||
}
|
||||
|
||||
/// Whether this user ID is a historical one.
|
||||
///
|
||||
/// A historical user ID is one that doesn't conform to the latest
|
||||
/// specification of the user ID grammar but is still accepted
|
||||
/// because it was previously allowed.
|
||||
#[napi]
|
||||
pub fn is_historical(&self) -> bool {
|
||||
self.inner.is_historical()
|
||||
}
|
||||
|
||||
/// Return the user ID as a string.
|
||||
#[napi]
|
||||
#[allow(clippy::inherent_to_string)]
|
||||
pub fn to_string(&self) -> String {
|
||||
self.inner.as_str().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
/// A Matrix device ID.
|
||||
///
|
||||
/// Device identifiers in Matrix are completely opaque character
|
||||
/// sequences. This type is provided simply for its semantic value.
|
||||
#[napi]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DeviceId {
|
||||
pub(crate) inner: ruma::OwnedDeviceId,
|
||||
}
|
||||
|
||||
impl From<ruma::OwnedDeviceId> for DeviceId {
|
||||
fn from(inner: ruma::OwnedDeviceId) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl DeviceId {
|
||||
/// Create a new `DeviceId`.
|
||||
#[napi(constructor, strict)]
|
||||
pub fn new(id: String) -> Self {
|
||||
Self::from(Into::<ruma::OwnedDeviceId>::into(id))
|
||||
}
|
||||
|
||||
/// Return the device ID as a string.
|
||||
#[napi]
|
||||
#[allow(clippy::inherent_to_string)]
|
||||
pub fn to_string(&self) -> String {
|
||||
self.inner.as_str().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
/// A Matrix device key ID.
|
||||
///
|
||||
/// A key algorithm and a device ID, combined with a ‘:’.
|
||||
#[napi]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DeviceKeyId {
|
||||
pub(crate) inner: ruma::OwnedDeviceKeyId,
|
||||
}
|
||||
|
||||
impl From<ruma::OwnedDeviceKeyId> for DeviceKeyId {
|
||||
fn from(inner: ruma::OwnedDeviceKeyId) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl DeviceKeyId {
|
||||
/// Parse/validate and create a new `DeviceKeyId`.
|
||||
#[napi(constructor, strict)]
|
||||
pub fn new(id: String) -> napi::Result<Self> {
|
||||
Ok(Self::from(ruma::DeviceKeyId::parse(id.as_str()).map_err(into_err)?))
|
||||
}
|
||||
|
||||
/// Returns key algorithm of the device key ID.
|
||||
#[napi(getter)]
|
||||
pub fn algorithm(&self) -> DeviceKeyAlgorithm {
|
||||
self.inner.algorithm().into()
|
||||
}
|
||||
|
||||
/// Returns device ID of the device key ID.
|
||||
#[napi(getter)]
|
||||
pub fn device_id(&self) -> DeviceId {
|
||||
self.inner.device_id().to_owned().into()
|
||||
}
|
||||
|
||||
/// Return the device key ID as a string.
|
||||
#[napi]
|
||||
#[allow(clippy::inherent_to_string)]
|
||||
pub fn to_string(&self) -> String {
|
||||
self.inner.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// The basic key algorithms in the specification.
|
||||
#[napi]
|
||||
pub struct DeviceKeyAlgorithm {
|
||||
inner: ruma::DeviceKeyAlgorithm,
|
||||
}
|
||||
|
||||
impl From<ruma::DeviceKeyAlgorithm> for DeviceKeyAlgorithm {
|
||||
fn from(inner: ruma::DeviceKeyAlgorithm) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl DeviceKeyAlgorithm {
|
||||
/// Read the device key algorithm's name. If the name is
|
||||
/// `Unknown`, one may be interested by the `to_string` method to
|
||||
/// read the original name.
|
||||
#[napi(getter)]
|
||||
pub fn name(&self) -> DeviceKeyAlgorithmName {
|
||||
self.inner.clone().into()
|
||||
}
|
||||
|
||||
/// Return the device key algorithm as a string.
|
||||
#[napi]
|
||||
#[allow(clippy::inherent_to_string)]
|
||||
pub fn to_string(&self) -> String {
|
||||
self.inner.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// The basic key algorithm names in the specification.
|
||||
#[napi]
|
||||
pub enum DeviceKeyAlgorithmName {
|
||||
/// The Ed25519 signature algorithm.
|
||||
Ed25519,
|
||||
|
||||
/// The Curve25519 ECDH algorithm.
|
||||
Curve25519,
|
||||
|
||||
/// The Curve25519 ECDH algorithm, but the key also contains
|
||||
/// signatures.
|
||||
SignedCurve25519,
|
||||
|
||||
/// An unknown device key algorithm.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl From<ruma::DeviceKeyAlgorithm> for DeviceKeyAlgorithmName {
|
||||
fn from(value: ruma::DeviceKeyAlgorithm) -> Self {
|
||||
use ruma::DeviceKeyAlgorithm::*;
|
||||
|
||||
match value {
|
||||
Ed25519 => Self::Ed25519,
|
||||
Curve25519 => Self::Curve25519,
|
||||
SignedCurve25519 => Self::SignedCurve25519,
|
||||
_ => Self::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A Matrix [room ID].
|
||||
///
|
||||
/// [room ID]: https://spec.matrix.org/v1.2/appendices/#room-ids-and-event-ids
|
||||
#[napi]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RoomId {
|
||||
pub(crate) inner: ruma::OwnedRoomId,
|
||||
}
|
||||
|
||||
impl From<ruma::OwnedRoomId> for RoomId {
|
||||
fn from(inner: ruma::OwnedRoomId) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl RoomId {
|
||||
/// Parse/validate and create a new `RoomId`.
|
||||
#[napi(constructor, strict)]
|
||||
pub fn new(id: String) -> napi::Result<Self> {
|
||||
Ok(Self::from(ruma::RoomId::parse(id).map_err(into_err)?))
|
||||
}
|
||||
|
||||
/// Returns the user's localpart.
|
||||
#[napi(getter)]
|
||||
pub fn localpart(&self) -> String {
|
||||
self.inner.localpart().to_owned()
|
||||
}
|
||||
|
||||
/// Returns the server name of the room ID.
|
||||
#[napi(getter)]
|
||||
pub fn server_name(&self) -> ServerName {
|
||||
ServerName { inner: self.inner.server_name().to_owned() }
|
||||
}
|
||||
|
||||
/// Return the room ID as a string.
|
||||
#[napi]
|
||||
#[allow(clippy::inherent_to_string)]
|
||||
pub fn to_string(&self) -> String {
|
||||
self.inner.as_str().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
/// A Matrix-spec compliant [server name].
|
||||
///
|
||||
/// It consists of a host and an optional port (separated by a colon if
|
||||
/// present).
|
||||
///
|
||||
/// [server name]: https://spec.matrix.org/v1.2/appendices/#server-name
|
||||
#[napi]
|
||||
#[derive(Debug)]
|
||||
pub struct ServerName {
|
||||
inner: ruma::OwnedServerName,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl ServerName {
|
||||
/// Parse/validate and create a new `ServerName`.
|
||||
#[napi(constructor, strict)]
|
||||
pub fn new(name: String) -> napi::Result<Self> {
|
||||
Ok(Self { inner: ruma::ServerName::parse(name).map_err(into_err)? })
|
||||
}
|
||||
|
||||
/// Returns the host of the server name.
|
||||
///
|
||||
/// That is: Return the part of the server before `:<port>` or the
|
||||
/// full server name if there is no port.
|
||||
#[napi(getter)]
|
||||
pub fn host(&self) -> String {
|
||||
self.inner.host().to_owned()
|
||||
}
|
||||
|
||||
/// Returns the port of the server name if any.
|
||||
#[napi(getter)]
|
||||
pub fn port(&self) -> Option<u16> {
|
||||
self.inner.port()
|
||||
}
|
||||
|
||||
/// Returns true if and only if the server name is an IPv4 or IPv6
|
||||
/// address.
|
||||
#[napi]
|
||||
pub fn is_ip_literal(&self) -> bool {
|
||||
self.inner.is_ip_literal()
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
//#![warn(missing_docs, missing_debug_implementations)]
|
||||
|
||||
use napi_derive::napi;
|
||||
|
||||
pub mod attachment;
|
||||
pub mod encryption;
|
||||
mod errors;
|
||||
pub mod events;
|
||||
pub mod identifiers;
|
||||
pub mod machine;
|
||||
pub mod olm;
|
||||
pub mod requests;
|
||||
pub mod responses;
|
||||
pub mod sync_events;
|
||||
#[cfg(feature = "tracing")]
|
||||
pub mod tracing;
|
||||
pub mod types;
|
||||
pub mod vodozemac;
|
||||
|
||||
/// Object containing the versions of the Rust libraries we are using.
|
||||
#[napi]
|
||||
pub struct Versions {
|
||||
/// The version of the vodozemac crate.
|
||||
#[napi(getter)]
|
||||
pub vodozemac: String,
|
||||
|
||||
/// The version of the matrix-sdk-crypto crate.
|
||||
#[napi(getter)]
|
||||
pub matrix_sdk_crypto: String,
|
||||
}
|
||||
|
||||
/// Get the versions of the Rust libraries we are using.
|
||||
#[napi(js_name = "getVersions")]
|
||||
pub fn get_versions() -> Versions {
|
||||
Versions {
|
||||
vodozemac: matrix_sdk_crypto::vodozemac::VERSION.to_owned(),
|
||||
matrix_sdk_crypto: matrix_sdk_crypto::VERSION.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
use crate::errors::into_err;
|
||||
@@ -1,479 +0,0 @@
|
||||
//! The crypto specific Olm objects.
|
||||
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
mem::ManuallyDrop,
|
||||
ops::Deref,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use napi::bindgen_prelude::{within_runtime_if_available, Either7, FromNapiValue, ToNapiValue};
|
||||
use napi_derive::*;
|
||||
use ruma::{serde::Raw, DeviceKeyAlgorithm, OwnedTransactionId, UInt};
|
||||
use serde_json::{value::RawValue, Value as JsonValue};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::{
|
||||
encryption, identifiers, into_err, olm, requests, responses, responses::response_from_string,
|
||||
sync_events, types, vodozemac,
|
||||
};
|
||||
|
||||
/// The value used by the `OlmMachine` JS class.
|
||||
///
|
||||
/// It has 2 states: `Opened` and `Closed`. Why maintaining the state here?
|
||||
/// Because NodeJS has no way to drop an object explicitly, and we want to be
|
||||
/// able to “close” the `OlmMachine` to free all associated data. More over,
|
||||
/// `napi-rs` doesn't allow a function to take the ownership of the type itself
|
||||
/// (`fn close(self) { … }`). So we manage the state ourselves.
|
||||
///
|
||||
/// Using the `OlmMachine` when its state is `Closed` will panic.
|
||||
enum OlmMachineInner {
|
||||
Opened(ManuallyDrop<matrix_sdk_crypto::OlmMachine>),
|
||||
Closed,
|
||||
}
|
||||
|
||||
impl Drop for OlmMachineInner {
|
||||
fn drop(&mut self) {
|
||||
if let Self::Opened(machine) = self {
|
||||
// SAFETY: `self` won't be used anymore after this `take`, so it's safe to do it
|
||||
// here.
|
||||
let machine = unsafe { ManuallyDrop::take(machine) };
|
||||
within_runtime_if_available(move || drop(machine));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for OlmMachineInner {
|
||||
type Target = matrix_sdk_crypto::OlmMachine;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Opened(machine) => machine,
|
||||
Self::Closed => panic!("The `OlmMachine` has been closed, cannot use it anymore"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the type of store an `OlmMachine` can use.
|
||||
#[derive(Default)]
|
||||
#[napi]
|
||||
pub enum StoreType {
|
||||
/// Use `matrix-sdk-sqlite`.
|
||||
#[default]
|
||||
Sqlite,
|
||||
}
|
||||
|
||||
/// State machine implementation of the Olm/Megolm encryption protocol
|
||||
/// used for Matrix end to end encryption.
|
||||
// #[napi(custom_finalize)]
|
||||
#[napi]
|
||||
pub struct OlmMachine {
|
||||
inner: OlmMachineInner,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl OlmMachine {
|
||||
// JavaScript doesn't support asynchronous constructor. So let's
|
||||
// use a factory pattern, where the constructor cannot be used (it
|
||||
// returns an error), and a new method is provided to construct
|
||||
// the object. napi provides `#[napi(factory)]` to address those
|
||||
// needs automatically. Unfortunately, it doesn't support
|
||||
// asynchronous factory methods.
|
||||
//
|
||||
// So let's do this manually. The `initialize` async method _is_
|
||||
// the factory function. We also manually implement the
|
||||
// constructor to raise an error when called.
|
||||
|
||||
/// Create a new `OlmMachine` asynchronously.
|
||||
///
|
||||
/// The persistence of the encryption keys and all the inner
|
||||
/// objects are controlled by the `store_path` argument.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `user_id`, the unique ID of the user that owns this machine.
|
||||
/// * `device_id`, the unique id of the device that owns this machine.
|
||||
/// * `store_path`, the path to a directory where the state of the machine
|
||||
/// should be persisted; if not set, the created machine will keep the
|
||||
/// encryption keys only in memory, and once the object is dropped, the
|
||||
/// keys will be lost.
|
||||
/// * `store_passphrase`, the passphrase that should be used to encrypt the
|
||||
/// data at rest in the store. **Warning**, if no passphrase is given, the
|
||||
/// store and all its data will remain unencrypted. This argument is
|
||||
/// ignored if `store_path` is not set.
|
||||
#[napi(strict)]
|
||||
pub async fn initialize(
|
||||
user_id: &identifiers::UserId,
|
||||
device_id: &identifiers::DeviceId,
|
||||
store_path: Option<String>,
|
||||
mut store_passphrase: Option<String>,
|
||||
store_type: Option<StoreType>,
|
||||
) -> napi::Result<OlmMachine> {
|
||||
let user_id = user_id.clone().inner;
|
||||
let device_id = device_id.clone().inner;
|
||||
|
||||
let user_id = user_id.as_ref();
|
||||
let device_id = device_id.as_ref();
|
||||
|
||||
Ok(OlmMachine {
|
||||
inner: OlmMachineInner::Opened(ManuallyDrop::new(match store_path {
|
||||
Some(store_path) => {
|
||||
let machine = match store_type.unwrap_or_default() {
|
||||
StoreType::Sqlite => {
|
||||
matrix_sdk_crypto::OlmMachine::with_store(
|
||||
user_id,
|
||||
device_id,
|
||||
matrix_sdk_sqlite::SqliteCryptoStore::open(
|
||||
store_path,
|
||||
store_passphrase.as_deref(),
|
||||
)
|
||||
.await
|
||||
.map(Arc::new)
|
||||
.map_err(into_err)?,
|
||||
)
|
||||
.await
|
||||
}
|
||||
};
|
||||
|
||||
store_passphrase.zeroize();
|
||||
|
||||
machine.map_err(into_err)?
|
||||
}
|
||||
|
||||
None => matrix_sdk_crypto::OlmMachine::new(user_id, device_id).await,
|
||||
})),
|
||||
})
|
||||
}
|
||||
|
||||
/// It's not possible to construct an `OlmMachine` with its
|
||||
/// constructor because building an `OlmMachine` is
|
||||
/// asynchronous. Please use the `finalize` method.
|
||||
#[napi(constructor)]
|
||||
pub fn new() -> napi::Result<Self> {
|
||||
Err(napi::Error::from_reason(
|
||||
"To build an `OlmMachine`, please use the `initialize` method",
|
||||
))
|
||||
}
|
||||
|
||||
/// The unique user ID that owns this `OlmMachine` instance.
|
||||
#[napi(getter)]
|
||||
pub fn user_id(&self) -> identifiers::UserId {
|
||||
identifiers::UserId::from(self.inner.user_id().to_owned())
|
||||
}
|
||||
|
||||
/// The unique device ID that identifies this `OlmMachine`.
|
||||
#[napi(getter)]
|
||||
pub fn device_id(&self) -> identifiers::DeviceId {
|
||||
identifiers::DeviceId::from(self.inner.device_id().to_owned())
|
||||
}
|
||||
|
||||
/// Get the public parts of our Olm identity keys.
|
||||
#[napi(getter)]
|
||||
pub fn identity_keys(&self) -> vodozemac::IdentityKeys {
|
||||
self.inner.identity_keys().into()
|
||||
}
|
||||
|
||||
/// Handle a to-device and one-time key counts from a sync response.
|
||||
///
|
||||
/// This will decrypt and handle to-device events returning the
|
||||
/// decrypted versions of them, as a JSON-encoded string.
|
||||
///
|
||||
/// To decrypt an event from the room timeline, please use
|
||||
/// `decrypt_room_event`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `to_device_events`, the to-device events of the current sync response.
|
||||
/// * `changed_devices`, the list of devices that changed in this sync
|
||||
/// response.
|
||||
/// * `one_time_keys_count`, the current one-time keys counts that the sync
|
||||
/// response returned.
|
||||
#[napi(strict)]
|
||||
pub async fn receive_sync_changes(
|
||||
&self,
|
||||
to_device_events: String,
|
||||
changed_devices: &sync_events::DeviceLists,
|
||||
one_time_key_counts: HashMap<String, u32>,
|
||||
unused_fallback_keys: Vec<String>,
|
||||
) -> napi::Result<String> {
|
||||
let to_device_events = serde_json::from_str(to_device_events.as_ref()).map_err(into_err)?;
|
||||
let changed_devices = changed_devices.inner.clone();
|
||||
let one_time_key_counts = one_time_key_counts
|
||||
.iter()
|
||||
.map(|(key, value)| (DeviceKeyAlgorithm::from(key.as_str()), UInt::from(*value)))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
let unused_fallback_keys = Some(
|
||||
unused_fallback_keys
|
||||
.into_iter()
|
||||
.map(|key| DeviceKeyAlgorithm::from(key.as_str()))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
serde_json::to_string(
|
||||
&self
|
||||
.inner
|
||||
.receive_sync_changes(
|
||||
to_device_events,
|
||||
&changed_devices,
|
||||
&one_time_key_counts,
|
||||
unused_fallback_keys.as_deref(),
|
||||
)
|
||||
.await
|
||||
.map_err(into_err)?,
|
||||
)
|
||||
.map_err(into_err)
|
||||
}
|
||||
|
||||
/// Get the outgoing requests that need to be sent out.
|
||||
///
|
||||
/// This returns a list of `KeysUploadRequest`, or
|
||||
/// `KeysQueryRequest`, or `KeysClaimRequest`, or
|
||||
/// `ToDeviceRequest`, or `SignatureUploadRequest`, or
|
||||
/// `RoomMessageRequest`, or `KeysBackupRequest`. Those requests
|
||||
/// need to be sent out to the server and the responses need to be
|
||||
/// passed back to the state machine using `mark_request_as_sent`.
|
||||
#[napi]
|
||||
pub async fn outgoing_requests(
|
||||
&self,
|
||||
) -> napi::Result<
|
||||
Vec<
|
||||
// We could be tempted to use `requests::OutgoingRequests` as its
|
||||
// a type alias for this giant `Either7`. But `napi` won't unfold
|
||||
// it properly into a valid TypeScript definition, so… let's
|
||||
// copy-paste :-(.
|
||||
Either7<
|
||||
requests::KeysUploadRequest,
|
||||
requests::KeysQueryRequest,
|
||||
requests::KeysClaimRequest,
|
||||
requests::ToDeviceRequest,
|
||||
requests::SignatureUploadRequest,
|
||||
requests::RoomMessageRequest,
|
||||
requests::KeysBackupRequest,
|
||||
>,
|
||||
>,
|
||||
> {
|
||||
self.inner
|
||||
.outgoing_requests()
|
||||
.await
|
||||
.map_err(into_err)?
|
||||
.into_iter()
|
||||
.map(requests::OutgoingRequest)
|
||||
.map(TryFrom::try_from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Mark the request with the given request ID as sent.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `request_id`, the unique ID of the request that was sent out. This is
|
||||
/// needed to couple the response with the now sent out request.
|
||||
/// * `request_type`, the request type associated to the request ID.
|
||||
/// * `response`, the response that was received from the server after the
|
||||
/// outgoing request was sent out.
|
||||
#[napi(strict)]
|
||||
pub async fn mark_request_as_sent(
|
||||
&self,
|
||||
request_id: String,
|
||||
request_type: requests::RequestType,
|
||||
response: String,
|
||||
) -> napi::Result<bool> {
|
||||
let transaction_id = OwnedTransactionId::from(request_id);
|
||||
let response = response_from_string(response.as_str()).map_err(into_err)?;
|
||||
let incoming_response = responses::OwnedResponse::try_from((request_type, response))?;
|
||||
|
||||
self.inner
|
||||
.mark_request_as_sent(&transaction_id, &incoming_response)
|
||||
.await
|
||||
.map(|_| true)
|
||||
.map_err(into_err)
|
||||
}
|
||||
|
||||
/// Get the a key claiming request for the user/device pairs that
|
||||
/// we are missing Olm sessions for.
|
||||
///
|
||||
/// Returns `null` if no key claiming request needs to be sent
|
||||
/// out.
|
||||
///
|
||||
/// Sessions need to be established between devices so group
|
||||
/// sessions for a room can be shared with them.
|
||||
///
|
||||
/// This should be called every time a group session needs to be
|
||||
/// shared as well as between sync calls. After a sync some
|
||||
/// devices may request room keys without us having a valid Olm
|
||||
/// session with them, making it impossible to server the room key
|
||||
/// request, thus it’s necessary to check for missing sessions
|
||||
/// between sync as well.
|
||||
///
|
||||
/// Note: Care should be taken that only one such request at a
|
||||
/// time is in flight, e.g. using a lock.
|
||||
///
|
||||
/// The response of a successful key claiming requests needs to be
|
||||
/// passed to the `OlmMachine` with the `mark_request_as_sent`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `users`, the list of users that we should check if we lack a session
|
||||
/// with one of their devices. This can be an empty array or `null` when
|
||||
/// calling this method between sync requests.
|
||||
#[napi(strict)]
|
||||
pub async fn get_missing_sessions(
|
||||
&self,
|
||||
users: Option<Vec<&identifiers::UserId>>,
|
||||
) -> napi::Result<Option<requests::KeysClaimRequest>> {
|
||||
let users = users
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|user| user.inner.clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
match self
|
||||
.inner
|
||||
.get_missing_sessions(users.iter().map(AsRef::as_ref))
|
||||
.await
|
||||
.map_err(into_err)?
|
||||
{
|
||||
Some((transaction_id, keys_claim_request)) => Ok(Some(
|
||||
requests::KeysClaimRequest::try_from((
|
||||
transaction_id.to_string(),
|
||||
&keys_claim_request,
|
||||
))
|
||||
.map_err(into_err)?,
|
||||
)),
|
||||
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the tracked users.
|
||||
///
|
||||
/// This will mark users that weren’t seen before for a key query
|
||||
/// and tracking.
|
||||
///
|
||||
/// If the user is already known to the Olm machine it will not be
|
||||
/// considered for a key query.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `users`, an array over user IDs that should be marked for tracking.
|
||||
#[napi(strict)]
|
||||
pub async fn update_tracked_users(&self, users: Vec<&identifiers::UserId>) -> napi::Result<()> {
|
||||
let users = users.into_iter().map(|user| user.inner.clone()).collect::<Vec<_>>();
|
||||
|
||||
self.inner.update_tracked_users(users.iter().map(AsRef::as_ref)).await.map_err(into_err)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get to-device requests to share a room key with users in a room.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `room_id`, the room ID of the room where the room key will be used.
|
||||
/// * `users`, the list of users that should receive the room key.
|
||||
/// * `encryption_settings`, the encryption settings.
|
||||
#[napi(strict)]
|
||||
pub async fn share_room_key(
|
||||
&self,
|
||||
room_id: &identifiers::RoomId,
|
||||
users: Vec<&identifiers::UserId>,
|
||||
encryption_settings: &encryption::EncryptionSettings,
|
||||
) -> napi::Result<String> {
|
||||
let room_id = room_id.inner.clone();
|
||||
let users = users.into_iter().map(|user| user.inner.clone()).collect::<Vec<_>>();
|
||||
let encryption_settings =
|
||||
matrix_sdk_crypto::olm::EncryptionSettings::from(encryption_settings);
|
||||
|
||||
serde_json::to_string(
|
||||
&self
|
||||
.inner
|
||||
.share_room_key(&room_id, users.iter().map(AsRef::as_ref), encryption_settings)
|
||||
.await
|
||||
.map_err(into_err)?,
|
||||
)
|
||||
.map_err(into_err)
|
||||
}
|
||||
|
||||
/// Encrypt a JSON-encoded content for the given room.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `room_id`, the ID of the room for which the message should be
|
||||
/// encrypted.
|
||||
/// * `event_type`, the plaintext type of the event.
|
||||
/// * `content`, the JSON-encoded content of the message that should be
|
||||
/// encrypted.
|
||||
#[napi(strict)]
|
||||
pub async fn encrypt_room_event(
|
||||
&self,
|
||||
room_id: &identifiers::RoomId,
|
||||
event_type: String,
|
||||
content: String,
|
||||
) -> napi::Result<String> {
|
||||
let room_id = room_id.inner.clone();
|
||||
let content: JsonValue = serde_json::from_str(content.as_str()).map_err(into_err)?;
|
||||
|
||||
serde_json::to_string(
|
||||
&self
|
||||
.inner
|
||||
.encrypt_room_event_raw(&room_id, content, event_type.as_ref())
|
||||
.await
|
||||
.map_err(into_err)?,
|
||||
)
|
||||
.map_err(into_err)
|
||||
}
|
||||
|
||||
/// Decrypt an event from a room timeline.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `event`, the event that should be decrypted.
|
||||
/// * `room_id`, the ID of the room where the event was sent to.
|
||||
#[napi(strict)]
|
||||
pub async fn decrypt_room_event(
|
||||
&self,
|
||||
event: String,
|
||||
room_id: &identifiers::RoomId,
|
||||
) -> napi::Result<responses::DecryptedRoomEvent> {
|
||||
let event = Raw::from_json(RawValue::from_string(event).map_err(into_err)?);
|
||||
let room_id = room_id.inner.clone();
|
||||
|
||||
let room_event = self.inner.decrypt_room_event(&event, &room_id).await.map_err(into_err)?;
|
||||
|
||||
Ok(room_event.into())
|
||||
}
|
||||
|
||||
/// Get the status of the private cross signing keys.
|
||||
///
|
||||
/// This can be used to check which private cross signing keys we
|
||||
/// have stored locally.
|
||||
#[napi]
|
||||
pub async fn cross_signing_status(&self) -> olm::CrossSigningStatus {
|
||||
self.inner.cross_signing_status().await.into()
|
||||
}
|
||||
|
||||
/// Sign the given message using our device key and if available
|
||||
/// cross-signing master key.
|
||||
#[napi(strict)]
|
||||
pub async fn sign(&self, message: String) -> types::Signatures {
|
||||
self.inner.sign(message.as_str()).await.into()
|
||||
}
|
||||
|
||||
/// Shut down the `OlmMachine`.
|
||||
///
|
||||
/// The `OlmMachine` cannot be used after this method has been called,
|
||||
/// otherwise it will panic.
|
||||
///
|
||||
/// All associated resources will be closed too, like the crypto storage
|
||||
/// connections.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller is responsible to **not** use any objects that came from this
|
||||
/// `OlmMachine` after this `close` method has been called.
|
||||
#[napi(strict)]
|
||||
pub fn close(&mut self) {
|
||||
self.inner = OlmMachineInner::Closed;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
//! Olm types.
|
||||
|
||||
use napi_derive::*;
|
||||
|
||||
/// Struct representing the state of our private cross signing keys,
|
||||
/// it shows which private cross signing keys we have locally stored.
|
||||
#[napi]
|
||||
#[derive(Debug)]
|
||||
pub struct CrossSigningStatus {
|
||||
inner: matrix_sdk_crypto::olm::CrossSigningStatus,
|
||||
}
|
||||
|
||||
impl From<matrix_sdk_crypto::olm::CrossSigningStatus> for CrossSigningStatus {
|
||||
fn from(inner: matrix_sdk_crypto::olm::CrossSigningStatus) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl CrossSigningStatus {
|
||||
/// Do we have the master key.
|
||||
#[napi(getter)]
|
||||
pub fn has_master(&self) -> bool {
|
||||
self.inner.has_master
|
||||
}
|
||||
|
||||
/// Do we have the self signing key, this one is necessary to sign
|
||||
/// our own devices.
|
||||
#[napi(getter)]
|
||||
pub fn has_self_signing(&self) -> bool {
|
||||
self.inner.has_self_signing
|
||||
}
|
||||
|
||||
/// Do we have the user signing key, this one is necessary to sign
|
||||
/// other users.
|
||||
#[napi(getter)]
|
||||
pub fn has_user_signing(&self) -> bool {
|
||||
self.inner.has_user_signing
|
||||
}
|
||||
}
|
||||
@@ -1,401 +0,0 @@
|
||||
//! Types to handle requests.
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use matrix_sdk_crypto::requests::{
|
||||
KeysBackupRequest as RumaKeysBackupRequest, KeysQueryRequest as RumaKeysQueryRequest,
|
||||
RoomMessageRequest as RumaRoomMessageRequest, ToDeviceRequest as RumaToDeviceRequest,
|
||||
};
|
||||
use napi::bindgen_prelude::{Either7, FromNapiValue, ToNapiValue};
|
||||
use napi_derive::*;
|
||||
use ruma::{
|
||||
api::client::keys::{
|
||||
claim_keys::v3::Request as RumaKeysClaimRequest,
|
||||
upload_keys::v3::Request as RumaKeysUploadRequest,
|
||||
upload_signatures::v3::Request as RumaSignatureUploadRequest,
|
||||
},
|
||||
events::EventContent,
|
||||
};
|
||||
|
||||
use crate::into_err;
|
||||
|
||||
/// Data for a request to the `/keys/upload` API endpoint
|
||||
/// ([specification]).
|
||||
///
|
||||
/// Publishes end-to-end encryption keys for the device.
|
||||
///
|
||||
/// [specification]: https://spec.matrix.org/unstable/client-server-api/#post_matrixclientv3keysupload
|
||||
#[napi]
|
||||
pub struct KeysUploadRequest {
|
||||
/// The request ID.
|
||||
#[napi(readonly)]
|
||||
pub id: String,
|
||||
|
||||
/// A JSON-encoded string containing the rest of the payload: `device_keys`,
|
||||
/// `one_time_keys`, `fallback_keys`.
|
||||
///
|
||||
/// It represents the body of the HTTP request.
|
||||
#[napi(readonly)]
|
||||
pub body: String,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl KeysUploadRequest {
|
||||
/// Get its request type.
|
||||
#[napi(getter, js_name = "type")]
|
||||
pub fn request_type(&self) -> RequestType {
|
||||
RequestType::KeysUpload
|
||||
}
|
||||
}
|
||||
|
||||
/// Data for a request to the `/keys/query` API endpoint
|
||||
/// ([specification]).
|
||||
///
|
||||
/// Returns the current devices and identity keys for the given users.
|
||||
///
|
||||
/// [specification]: https://spec.matrix.org/unstable/client-server-api/#post_matrixclientv3keysquery
|
||||
#[napi]
|
||||
pub struct KeysQueryRequest {
|
||||
/// The request ID.
|
||||
#[napi(readonly)]
|
||||
pub id: String,
|
||||
|
||||
/// A JSON-encoded object of form:
|
||||
///
|
||||
/// ```json
|
||||
/// {"timeout": …, "one_time_keys": …}
|
||||
/// ```
|
||||
#[napi(readonly)]
|
||||
pub body: String,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl KeysQueryRequest {
|
||||
/// Get its request type.
|
||||
#[napi(getter, js_name = "type")]
|
||||
pub fn request_type(&self) -> RequestType {
|
||||
RequestType::KeysQuery
|
||||
}
|
||||
}
|
||||
|
||||
/// Data for a request to the `/keys/claim` API endpoint
|
||||
/// ([specification]).
|
||||
///
|
||||
/// Claims one-time keys that can be used to establish 1-to-1 E2EE
|
||||
/// sessions.
|
||||
///
|
||||
/// [specification]: https://spec.matrix.org/unstable/client-server-api/#post_matrixclientv3keysclaim
|
||||
#[napi]
|
||||
pub struct KeysClaimRequest {
|
||||
/// The request ID.
|
||||
#[napi(readonly)]
|
||||
pub id: String,
|
||||
|
||||
/// A JSON-encoded object of form:
|
||||
///
|
||||
/// ```json
|
||||
/// {"event_type": …, "txn_id": …, "messages": …}
|
||||
/// ```
|
||||
#[napi(readonly)]
|
||||
pub body: String,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl KeysClaimRequest {
|
||||
/// Get its request type.
|
||||
#[napi(getter, js_name = "type")]
|
||||
pub fn request_type(&self) -> RequestType {
|
||||
RequestType::KeysClaim
|
||||
}
|
||||
}
|
||||
|
||||
/// Data for a request to the `/sendToDevice` API endpoint
|
||||
/// ([specification]).
|
||||
///
|
||||
/// Send an event to a single device or to a group of devices.
|
||||
///
|
||||
/// [specification]: https://spec.matrix.org/unstable/client-server-api/#put_matrixclientv3sendtodeviceeventtypetxnid
|
||||
#[napi]
|
||||
pub struct ToDeviceRequest {
|
||||
/// The request ID.
|
||||
#[napi(readonly)]
|
||||
pub id: String,
|
||||
|
||||
/// A string representing the type of event being sent to each devices.
|
||||
#[napi(readonly)]
|
||||
pub event_type: String,
|
||||
|
||||
/// A string representing a request identifier unique to the access token
|
||||
/// used to send the request.
|
||||
#[napi(readonly)]
|
||||
pub txn_id: String,
|
||||
|
||||
/// A JSON-encoded string containing the rest of the payload: `messages`.
|
||||
///
|
||||
/// It represents the body of the HTTP request.
|
||||
#[napi(readonly)]
|
||||
pub body: String,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl ToDeviceRequest {
|
||||
/// Get its request type.
|
||||
#[napi(getter, js_name = "type")]
|
||||
pub fn request_type(&self) -> RequestType {
|
||||
RequestType::ToDevice
|
||||
}
|
||||
}
|
||||
|
||||
/// Data for a request to the `/keys/signatures/upload` API endpoint
|
||||
/// ([specification]).
|
||||
///
|
||||
/// Publishes cross-signing signatures for the user.
|
||||
///
|
||||
/// [specification]: https://spec.matrix.org/unstable/client-server-api/#post_matrixclientv3keyssignaturesupload
|
||||
#[napi]
|
||||
pub struct SignatureUploadRequest {
|
||||
/// The request ID.
|
||||
#[napi(readonly)]
|
||||
pub id: String,
|
||||
|
||||
/// A JSON-encoded string containing the rest of the payload: `signed_keys`.
|
||||
///
|
||||
/// It represents the body of the HTTP request.
|
||||
#[napi(readonly)]
|
||||
pub body: String,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl SignatureUploadRequest {
|
||||
/// Get its request type.
|
||||
#[napi(getter, js_name = "type")]
|
||||
pub fn request_type(&self) -> RequestType {
|
||||
RequestType::SignatureUpload
|
||||
}
|
||||
}
|
||||
|
||||
/// A customized owned request type for sending out room messages
|
||||
/// ([specification]).
|
||||
///
|
||||
/// [specification]: https://spec.matrix.org/unstable/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid
|
||||
#[napi]
|
||||
pub struct RoomMessageRequest {
|
||||
/// The request ID.
|
||||
#[napi(readonly)]
|
||||
pub id: String,
|
||||
|
||||
/// A string representing the room to send the event to.
|
||||
#[napi(readonly)]
|
||||
pub room_id: String,
|
||||
|
||||
/// A string representing the transaction ID for this event.
|
||||
///
|
||||
/// Clients should generate an ID unique across requests with the same
|
||||
/// access token; it will be used by the server to ensure idempotency of
|
||||
/// requests.
|
||||
#[napi(readonly)]
|
||||
pub txn_id: String,
|
||||
|
||||
/// A string representing the type of event to be sent.
|
||||
#[napi(readonly)]
|
||||
pub event_type: String,
|
||||
|
||||
/// A JSON-encoded string containing the message's content.
|
||||
#[napi(readonly, js_name = "body")]
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl RoomMessageRequest {
|
||||
/// Get its request type.
|
||||
#[napi(getter, js_name = "type")]
|
||||
pub fn request_type(&self) -> RequestType {
|
||||
RequestType::RoomMessage
|
||||
}
|
||||
}
|
||||
|
||||
/// A request that will back up a batch of room keys to the server
|
||||
/// ([specification]).
|
||||
///
|
||||
/// [specification]: https://spec.matrix.org/unstable/client-server-api/#put_matrixclientv3room_keyskeys
|
||||
#[napi]
|
||||
pub struct KeysBackupRequest {
|
||||
/// The request ID.
|
||||
#[napi(readonly)]
|
||||
pub id: String,
|
||||
|
||||
/// A JSON-encoded string containing the rest of the payload: `rooms`.
|
||||
///
|
||||
/// It represents the body of the HTTP request.
|
||||
#[napi(readonly)]
|
||||
pub body: String,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl KeysBackupRequest {
|
||||
/// Get its request type.
|
||||
#[napi(getter, js_name = "type")]
|
||||
pub fn request_type(&self) -> RequestType {
|
||||
RequestType::KeysBackup
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! request {
|
||||
(
|
||||
$destination_request:ident from $source_request:ident
|
||||
$( extracts $( $field_name:ident : $field_type:tt ),+ $(,)? )?
|
||||
$( $( and )? groups $( $grouped_field_name:ident $( { $grouped_field_transformation:expr } )? ),+ $(,)? )?
|
||||
) => {
|
||||
impl TryFrom<(String, &$source_request)> for $destination_request {
|
||||
type Error = napi::Error;
|
||||
|
||||
fn try_from(
|
||||
(request_id, request): (String, &$source_request),
|
||||
) -> Result<Self, Self::Error> {
|
||||
request!(
|
||||
@__try_from $destination_request from $source_request
|
||||
(request_id = request_id.into(), request = request)
|
||||
$( extracts [ $( $field_name : $field_type, )+ ] )?
|
||||
$( groups [ $( $grouped_field_name $( { $grouped_field_transformation } )? , )+ ] )?
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
@__try_from $destination_request:ident from $source_request:ident
|
||||
(request_id = $request_id:expr, request = $request:expr)
|
||||
$( extracts [ $( $field_name:ident : $field_type:tt ),* $(,)? ] )?
|
||||
$( groups [ $( $grouped_field_name:ident $( { $grouped_field_transformation:expr } )? ),* $(,)? ] )?
|
||||
) => {
|
||||
{
|
||||
Ok($destination_request {
|
||||
id: $request_id,
|
||||
$(
|
||||
$(
|
||||
$field_name: request!(@__field $field_name : $field_type ; request = $request),
|
||||
)*
|
||||
)?
|
||||
$(
|
||||
body: {
|
||||
let mut map = serde_json::Map::new();
|
||||
$(
|
||||
|
||||
let field = &$request.$grouped_field_name;
|
||||
$(
|
||||
let field = {
|
||||
let $grouped_field_name = field;
|
||||
|
||||
$grouped_field_transformation
|
||||
};
|
||||
)?
|
||||
map.insert(stringify!($grouped_field_name).to_owned(), serde_json::to_value(field).map_err(into_err)?);
|
||||
)*
|
||||
let object = serde_json::Value::Object(map);
|
||||
|
||||
serde_json::to_string(&object).map_err(into_err)?.into()
|
||||
}
|
||||
)?
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
( @__field $field_name:ident : $field_type:ident ; request = $request:expr ) => {
|
||||
request!(@__field_type as $field_type ; request = $request, field_name = $field_name)
|
||||
};
|
||||
|
||||
( @__field_type as string ; request = $request:expr, field_name = $field_name:ident ) => {
|
||||
$request.$field_name.to_string().into()
|
||||
};
|
||||
|
||||
( @__field_type as json ; request = $request:expr, field_name = $field_name:ident ) => {
|
||||
serde_json::to_string(&$request.$field_name).map_err(into_err)?.into()
|
||||
};
|
||||
|
||||
( @__field_type as event_type ; request = $request:expr, field_name = $field_name:ident ) => {
|
||||
$request.content.event_type().to_string().into()
|
||||
};
|
||||
}
|
||||
|
||||
request!(KeysUploadRequest from RumaKeysUploadRequest groups device_keys, one_time_keys, fallback_keys);
|
||||
request!(KeysQueryRequest from RumaKeysQueryRequest groups timeout { timeout.as_ref().map(Duration::as_millis).map(u64::try_from).transpose().map_err(into_err)? }, device_keys, token);
|
||||
request!(KeysClaimRequest from RumaKeysClaimRequest groups timeout { timeout.as_ref().map(Duration::as_millis).map(u64::try_from).transpose().map_err(into_err)? }, one_time_keys);
|
||||
request!(ToDeviceRequest from RumaToDeviceRequest extracts event_type: string, txn_id: string and groups messages);
|
||||
request!(SignatureUploadRequest from RumaSignatureUploadRequest groups signed_keys);
|
||||
request!(RoomMessageRequest from RumaRoomMessageRequest extracts room_id: string, txn_id: string, event_type: event_type, content: json);
|
||||
request!(KeysBackupRequest from RumaKeysBackupRequest groups rooms);
|
||||
|
||||
pub type OutgoingRequests = Either7<
|
||||
KeysUploadRequest,
|
||||
KeysQueryRequest,
|
||||
KeysClaimRequest,
|
||||
ToDeviceRequest,
|
||||
SignatureUploadRequest,
|
||||
RoomMessageRequest,
|
||||
KeysBackupRequest,
|
||||
>;
|
||||
|
||||
pub(crate) struct OutgoingRequest(pub(crate) matrix_sdk_crypto::OutgoingRequest);
|
||||
|
||||
impl TryFrom<OutgoingRequest> for OutgoingRequests {
|
||||
type Error = napi::Error;
|
||||
|
||||
fn try_from(outgoing_request: OutgoingRequest) -> Result<Self, Self::Error> {
|
||||
let request_id = outgoing_request.0.request_id().to_string();
|
||||
|
||||
Ok(match outgoing_request.0.request() {
|
||||
matrix_sdk_crypto::OutgoingRequests::KeysUpload(request) => {
|
||||
Either7::A(KeysUploadRequest::try_from((request_id, request))?)
|
||||
}
|
||||
|
||||
matrix_sdk_crypto::OutgoingRequests::KeysQuery(request) => {
|
||||
Either7::B(KeysQueryRequest::try_from((request_id, request))?)
|
||||
}
|
||||
|
||||
matrix_sdk_crypto::OutgoingRequests::KeysClaim(request) => {
|
||||
Either7::C(KeysClaimRequest::try_from((request_id, request))?)
|
||||
}
|
||||
|
||||
matrix_sdk_crypto::OutgoingRequests::ToDeviceRequest(request) => {
|
||||
Either7::D(ToDeviceRequest::try_from((request_id, request))?)
|
||||
}
|
||||
|
||||
matrix_sdk_crypto::OutgoingRequests::SignatureUpload(request) => {
|
||||
Either7::E(SignatureUploadRequest::try_from((request_id, request))?)
|
||||
}
|
||||
|
||||
matrix_sdk_crypto::OutgoingRequests::RoomMessage(request) => {
|
||||
Either7::F(RoomMessageRequest::try_from((request_id, request))?)
|
||||
}
|
||||
|
||||
matrix_sdk_crypto::OutgoingRequests::KeysBackup(request) => {
|
||||
Either7::G(KeysBackupRequest::try_from((request_id, request))?)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent the type of a request.
|
||||
#[napi]
|
||||
pub enum RequestType {
|
||||
/// Represents a `KeysUploadRequest`.
|
||||
KeysUpload,
|
||||
|
||||
/// Represents a `KeysQueryRequest`.
|
||||
KeysQuery,
|
||||
|
||||
/// Represents a `KeysClaimRequest`.
|
||||
KeysClaim,
|
||||
|
||||
/// Represents a `ToDeviceRequest`.
|
||||
ToDevice,
|
||||
|
||||
/// Represents a `SignatureUploadRequest`.
|
||||
SignatureUpload,
|
||||
|
||||
/// Represents a `RoomMessageRequest`.
|
||||
RoomMessage,
|
||||
|
||||
/// Represents a `KeysBackupRequest`.
|
||||
KeysBackup,
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
use matrix_sdk_common::deserialized_responses::{AlgorithmInfo, EncryptionInfo};
|
||||
use matrix_sdk_crypto::IncomingResponse;
|
||||
use napi_derive::*;
|
||||
pub(crate) use ruma::api::client::{
|
||||
backup::add_backup_keys::v3::Response as KeysBackupResponse,
|
||||
keys::{
|
||||
claim_keys::v3::Response as KeysClaimResponse, get_keys::v3::Response as KeysQueryResponse,
|
||||
upload_keys::v3::Response as KeysUploadResponse,
|
||||
upload_signatures::v3::Response as SignatureUploadResponse,
|
||||
},
|
||||
message::send_message_event::v3::Response as RoomMessageResponse,
|
||||
to_device::send_event_to_device::v3::Response as ToDeviceResponse,
|
||||
};
|
||||
use ruma::api::IncomingResponse as RumaIncomingResponse;
|
||||
|
||||
use crate::{encryption, identifiers, into_err, requests::RequestType};
|
||||
|
||||
pub(crate) fn response_from_string(body: &str) -> http::Result<http::Response<Vec<u8>>> {
|
||||
http::Response::builder().status(200).body(body.as_bytes().to_vec())
|
||||
}
|
||||
|
||||
/// Intermediate private type to store an incoming owned response,
|
||||
/// without the need to manage lifetime.
|
||||
pub(crate) enum OwnedResponse {
|
||||
KeysUpload(KeysUploadResponse),
|
||||
KeysQuery(KeysQueryResponse),
|
||||
KeysClaim(KeysClaimResponse),
|
||||
ToDevice(ToDeviceResponse),
|
||||
SignatureUpload(SignatureUploadResponse),
|
||||
RoomMessage(RoomMessageResponse),
|
||||
KeysBackup(KeysBackupResponse),
|
||||
}
|
||||
|
||||
impl From<KeysUploadResponse> for OwnedResponse {
|
||||
fn from(response: KeysUploadResponse) -> Self {
|
||||
OwnedResponse::KeysUpload(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KeysQueryResponse> for OwnedResponse {
|
||||
fn from(response: KeysQueryResponse) -> Self {
|
||||
OwnedResponse::KeysQuery(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KeysClaimResponse> for OwnedResponse {
|
||||
fn from(response: KeysClaimResponse) -> Self {
|
||||
OwnedResponse::KeysClaim(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ToDeviceResponse> for OwnedResponse {
|
||||
fn from(response: ToDeviceResponse) -> Self {
|
||||
OwnedResponse::ToDevice(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SignatureUploadResponse> for OwnedResponse {
|
||||
fn from(response: SignatureUploadResponse) -> Self {
|
||||
Self::SignatureUpload(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RoomMessageResponse> for OwnedResponse {
|
||||
fn from(response: RoomMessageResponse) -> Self {
|
||||
OwnedResponse::RoomMessage(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KeysBackupResponse> for OwnedResponse {
|
||||
fn from(r: KeysBackupResponse) -> Self {
|
||||
Self::KeysBackup(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<(RequestType, http::Response<Vec<u8>>)> for OwnedResponse {
|
||||
type Error = napi::Error;
|
||||
|
||||
fn try_from(
|
||||
(request_type, response): (RequestType, http::Response<Vec<u8>>),
|
||||
) -> Result<Self, Self::Error> {
|
||||
match request_type {
|
||||
RequestType::KeysUpload => {
|
||||
KeysUploadResponse::try_from_http_response(response).map(Into::into)
|
||||
}
|
||||
|
||||
RequestType::KeysQuery => {
|
||||
KeysQueryResponse::try_from_http_response(response).map(Into::into)
|
||||
}
|
||||
|
||||
RequestType::KeysClaim => {
|
||||
KeysClaimResponse::try_from_http_response(response).map(Into::into)
|
||||
}
|
||||
|
||||
RequestType::ToDevice => {
|
||||
ToDeviceResponse::try_from_http_response(response).map(Into::into)
|
||||
}
|
||||
|
||||
RequestType::SignatureUpload => {
|
||||
SignatureUploadResponse::try_from_http_response(response).map(Into::into)
|
||||
}
|
||||
|
||||
RequestType::RoomMessage => {
|
||||
RoomMessageResponse::try_from_http_response(response).map(Into::into)
|
||||
}
|
||||
|
||||
RequestType::KeysBackup => {
|
||||
KeysBackupResponse::try_from_http_response(response).map(Into::into)
|
||||
}
|
||||
}
|
||||
.map_err(into_err)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a OwnedResponse> for IncomingResponse<'a> {
|
||||
fn from(response: &'a OwnedResponse) -> Self {
|
||||
match response {
|
||||
OwnedResponse::KeysUpload(response) => IncomingResponse::KeysUpload(response),
|
||||
OwnedResponse::KeysQuery(response) => IncomingResponse::KeysQuery(response),
|
||||
OwnedResponse::KeysClaim(response) => IncomingResponse::KeysClaim(response),
|
||||
OwnedResponse::ToDevice(response) => IncomingResponse::ToDevice(response),
|
||||
OwnedResponse::SignatureUpload(response) => IncomingResponse::SignatureUpload(response),
|
||||
OwnedResponse::RoomMessage(response) => IncomingResponse::RoomMessage(response),
|
||||
OwnedResponse::KeysBackup(response) => IncomingResponse::KeysBackup(response),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A decrypted room event.
|
||||
#[napi]
|
||||
pub struct DecryptedRoomEvent {
|
||||
/// The JSON-encoded decrypted event.
|
||||
#[napi(readonly)]
|
||||
pub event: String,
|
||||
|
||||
encryption_info: Option<EncryptionInfo>,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl DecryptedRoomEvent {
|
||||
/// The user ID of the event sender, note this is untrusted data
|
||||
/// unless the `verification_state` is as well trusted.
|
||||
#[napi(getter)]
|
||||
pub fn sender(&self) -> Option<identifiers::UserId> {
|
||||
Some(identifiers::UserId::from(self.encryption_info.as_ref()?.sender.clone()))
|
||||
}
|
||||
|
||||
/// The device ID of the device that sent us the event, note this
|
||||
/// is untrusted data unless `verification_state` is as well
|
||||
/// trusted.
|
||||
#[napi(getter)]
|
||||
pub fn sender_device(&self) -> Option<identifiers::DeviceId> {
|
||||
Some(self.encryption_info.as_ref()?.sender_device.as_ref()?.clone().into())
|
||||
}
|
||||
|
||||
/// The Curve25519 key of the device that created the megolm
|
||||
/// decryption key originally.
|
||||
#[napi(getter)]
|
||||
pub fn sender_curve25519_key(&self) -> Option<String> {
|
||||
Some(match &self.encryption_info.as_ref()?.algorithm_info {
|
||||
AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, .. } => curve25519_key.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// The signing Ed25519 key that have created the megolm key that
|
||||
/// was used to decrypt this session.
|
||||
#[napi(getter)]
|
||||
pub fn sender_claimed_ed25519_key(&self) -> Option<String> {
|
||||
match &self.encryption_info.as_ref()?.algorithm_info {
|
||||
AlgorithmInfo::MegolmV1AesSha2 { sender_claimed_keys, .. } => {
|
||||
sender_claimed_keys.get(&ruma::DeviceKeyAlgorithm::Ed25519).cloned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Chain of Curve25519 keys through which this session was
|
||||
/// forwarded, via `m.forwarded_room_key` events.
|
||||
#[napi(getter)]
|
||||
pub fn forwarding_curve25519_key_chain(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
/// The verification state of the device that sent us the event,
|
||||
/// note this is the state of the device at the time of
|
||||
/// decryption. It may change in the future if a device gets
|
||||
/// verified or deleted.
|
||||
#[napi]
|
||||
pub fn shield_state(&self, strict: bool) -> Option<encryption::ShieldState> {
|
||||
let state = &self.encryption_info.as_ref()?.verification_state;
|
||||
if strict {
|
||||
Some(state.to_shield_state_strict().into())
|
||||
} else {
|
||||
Some(state.to_shield_state_lax().into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<matrix_sdk_common::deserialized_responses::TimelineEvent> for DecryptedRoomEvent {
|
||||
fn from(value: matrix_sdk_common::deserialized_responses::TimelineEvent) -> Self {
|
||||
Self { event: value.event.json().get().to_owned(), encryption_info: value.encryption_info }
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
//! `GET /_matrix/client/*/sync`.
|
||||
|
||||
use napi_derive::*;
|
||||
|
||||
use crate::identifiers;
|
||||
|
||||
/// Information on E2E device updates.
|
||||
#[napi]
|
||||
pub struct DeviceLists {
|
||||
pub(crate) inner: ruma::api::client::sync::sync_events::DeviceLists,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl DeviceLists {
|
||||
/// Create an empty `DeviceLists`.
|
||||
#[napi(constructor, strict)]
|
||||
pub fn new(
|
||||
changed: Option<Vec<&identifiers::UserId>>,
|
||||
left: Option<Vec<&identifiers::UserId>>,
|
||||
) -> Self {
|
||||
let mut inner = ruma::api::client::sync::sync_events::DeviceLists::default();
|
||||
|
||||
inner.changed = changed.into_iter().flatten().map(|user| user.inner.clone()).collect();
|
||||
inner.left = left.into_iter().flatten().map(|user| user.inner.clone()).collect();
|
||||
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
/// Returns true if there are no device list updates.
|
||||
#[napi]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner.is_empty()
|
||||
}
|
||||
|
||||
/// List of users who have updated their device identity keys or
|
||||
/// who now share an encrypted room with the client since the
|
||||
/// previous sync.
|
||||
#[napi(getter)]
|
||||
pub fn changed(&self) -> Vec<identifiers::UserId> {
|
||||
self.inner.changed.iter().map(|user| identifiers::UserId::from(user.to_owned())).collect()
|
||||
}
|
||||
|
||||
/// List of users who no longer share encrypted rooms since the
|
||||
/// previous sync response.
|
||||
#[napi(getter)]
|
||||
pub fn left(&self) -> Vec<identifiers::UserId> {
|
||||
self.inner.left.iter().map(|user| identifiers::UserId::from(user.to_owned())).collect()
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
use napi_derive::*;
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||
|
||||
/// Subscribe to tracing events, i.e. turn on logs.
|
||||
#[napi]
|
||||
pub fn init_tracing() {
|
||||
tracing_subscriber::registry()
|
||||
.with(fmt::layer())
|
||||
.with(EnvFilter::from_env("MATRIX_LOG"))
|
||||
.init();
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use napi_derive::*;
|
||||
|
||||
use crate::{
|
||||
identifiers::{DeviceKeyId, UserId},
|
||||
vodozemac::Ed25519Signature,
|
||||
};
|
||||
|
||||
#[napi]
|
||||
#[derive(Default)]
|
||||
pub struct Signatures {
|
||||
inner: matrix_sdk_crypto::types::Signatures,
|
||||
}
|
||||
|
||||
impl From<matrix_sdk_crypto::types::Signatures> for Signatures {
|
||||
fn from(inner: matrix_sdk_crypto::types::Signatures) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl Signatures {
|
||||
/// Creates a new, empty, signatures collection.
|
||||
#[napi(constructor)]
|
||||
pub fn new() -> Self {
|
||||
matrix_sdk_crypto::types::Signatures::new().into()
|
||||
}
|
||||
|
||||
/// Add the given signature from the given signer and the given key ID to
|
||||
/// the collection.
|
||||
#[napi(strict)]
|
||||
pub fn add_signature(
|
||||
&mut self,
|
||||
signer: &UserId,
|
||||
key_id: &DeviceKeyId,
|
||||
signature: &Ed25519Signature,
|
||||
) -> Option<MaybeSignature> {
|
||||
self.inner
|
||||
.add_signature(signer.inner.clone(), key_id.inner.clone(), signature.inner)
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
/// Try to find an Ed25519 signature from the given signer with
|
||||
/// the given key ID.
|
||||
#[napi(strict)]
|
||||
pub fn get_signature(&self, signer: &UserId, key_id: &DeviceKeyId) -> Option<Ed25519Signature> {
|
||||
self.inner.get_signature(signer.inner.as_ref(), key_id.inner.as_ref()).map(Into::into)
|
||||
}
|
||||
|
||||
/// Get the map of signatures that belong to the given user.
|
||||
#[napi(strict)]
|
||||
pub fn get(&self, signer: &UserId) -> Option<HashMap<String, MaybeSignature>> {
|
||||
self.inner.get(signer.inner.as_ref()).map(|map| {
|
||||
map.iter()
|
||||
.map(|(device_key_id, maybe_signature)| {
|
||||
(device_key_id.as_str().to_owned(), maybe_signature.clone().into())
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
/// Remove all the signatures we currently hold.
|
||||
#[napi]
|
||||
pub fn clear(&mut self) {
|
||||
self.inner.clear();
|
||||
}
|
||||
|
||||
/// Do we hold any signatures or is our collection completely
|
||||
/// empty.
|
||||
#[napi(getter)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner.is_empty()
|
||||
}
|
||||
|
||||
/// How many signatures do we currently hold.
|
||||
#[napi(getter)]
|
||||
pub fn count(&self) -> usize {
|
||||
self.inner.signature_count()
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a potentially decoded signature (but not a validated
|
||||
/// one).
|
||||
#[napi]
|
||||
pub struct Signature {
|
||||
inner: matrix_sdk_crypto::types::Signature,
|
||||
}
|
||||
|
||||
impl From<matrix_sdk_crypto::types::Signature> for Signature {
|
||||
fn from(inner: matrix_sdk_crypto::types::Signature) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl Signature {
|
||||
/// Get the Ed25519 signature, if this is one.
|
||||
#[napi(getter)]
|
||||
pub fn ed25519(&self) -> Option<Ed25519Signature> {
|
||||
self.inner.ed25519().map(Into::into)
|
||||
}
|
||||
|
||||
/// Convert the signature to a base64 encoded string.
|
||||
#[napi]
|
||||
pub fn to_base64(&self) -> String {
|
||||
self.inner.to_base64()
|
||||
}
|
||||
}
|
||||
|
||||
type MaybeSignatureInner =
|
||||
Result<matrix_sdk_crypto::types::Signature, matrix_sdk_crypto::types::InvalidSignature>;
|
||||
|
||||
/// Represents a signature that is either valid _or_ that could not be
|
||||
/// decoded.
|
||||
#[napi]
|
||||
pub struct MaybeSignature {
|
||||
inner: MaybeSignatureInner,
|
||||
}
|
||||
|
||||
impl From<MaybeSignatureInner> for MaybeSignature {
|
||||
fn from(inner: MaybeSignatureInner) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl MaybeSignature {
|
||||
/// Check whether the signature has been successfully decoded.
|
||||
#[napi(getter)]
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.inner.is_ok()
|
||||
}
|
||||
|
||||
/// Check whether the signature could not be successfully decoded.
|
||||
#[napi(getter)]
|
||||
pub fn is_invalid(&self) -> bool {
|
||||
self.inner.is_err()
|
||||
}
|
||||
|
||||
/// The signature, if successfully decoded.
|
||||
#[napi(getter)]
|
||||
pub fn signature(&self) -> Option<Signature> {
|
||||
self.inner.as_ref().cloned().map(Into::into).ok()
|
||||
}
|
||||
|
||||
/// The base64 encoded string that is claimed to contain a
|
||||
/// signature but could not be decoded, if any.
|
||||
#[napi(getter)]
|
||||
pub fn invalid_signature_source(&self) -> Option<String> {
|
||||
match &self.inner {
|
||||
Ok(_) => None,
|
||||
Err(signature) => Some(signature.source.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
use napi_derive::*;
|
||||
|
||||
use crate::into_err;
|
||||
|
||||
/// An Ed25519 public key, used to verify digital signatures.
|
||||
#[napi]
|
||||
#[derive(Clone)]
|
||||
pub struct Ed25519PublicKey {
|
||||
inner: vodozemac::Ed25519PublicKey,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl Ed25519PublicKey {
|
||||
/// The number of bytes an Ed25519 public key has.
|
||||
#[napi(getter)]
|
||||
pub fn length(&self) -> u32 {
|
||||
vodozemac::Ed25519PublicKey::LENGTH as u32
|
||||
}
|
||||
|
||||
/// Serialize an Ed25519 public key to an unpadded base64
|
||||
/// representation.
|
||||
#[napi]
|
||||
pub fn to_base64(&self) -> String {
|
||||
self.inner.to_base64()
|
||||
}
|
||||
}
|
||||
|
||||
/// An Ed25519 digital signature, can be used to verify the
|
||||
/// authenticity of a message.
|
||||
#[napi]
|
||||
pub struct Ed25519Signature {
|
||||
pub(crate) inner: vodozemac::Ed25519Signature,
|
||||
}
|
||||
|
||||
impl From<vodozemac::Ed25519Signature> for Ed25519Signature {
|
||||
fn from(inner: vodozemac::Ed25519Signature) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl Ed25519Signature {
|
||||
/// Try to create an Ed25519 signature from an unpadded base64
|
||||
/// representation.
|
||||
#[napi(constructor, strict)]
|
||||
pub fn new(signature: String) -> napi::Result<Self> {
|
||||
Ok(Self {
|
||||
inner: vodozemac::Ed25519Signature::from_base64(signature.as_str())
|
||||
.map_err(into_err)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Serialize a Ed25519 signature to an unpadded base64
|
||||
/// representation.
|
||||
#[napi]
|
||||
pub fn to_base64(&self) -> String {
|
||||
self.inner.to_base64()
|
||||
}
|
||||
}
|
||||
|
||||
/// A Curve25519 public key.
|
||||
#[napi]
|
||||
#[derive(Clone)]
|
||||
pub struct Curve25519PublicKey {
|
||||
inner: vodozemac::Curve25519PublicKey,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl Curve25519PublicKey {
|
||||
/// The number of bytes a Curve25519 public key has.
|
||||
#[napi(getter)]
|
||||
pub fn length(&self) -> u32 {
|
||||
vodozemac::Curve25519PublicKey::LENGTH as u32
|
||||
}
|
||||
|
||||
/// Serialize an Curve25519 public key to an unpadded base64
|
||||
/// representation.
|
||||
#[napi]
|
||||
pub fn to_base64(&self) -> String {
|
||||
self.inner.to_base64()
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct holding the two public identity keys of an account.
|
||||
#[napi]
|
||||
pub struct IdentityKeys {
|
||||
ed25519: Ed25519PublicKey,
|
||||
curve25519: Curve25519PublicKey,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl IdentityKeys {
|
||||
/// The Ed25519 public key, used for signing.
|
||||
#[napi(getter)]
|
||||
pub fn ed25519(&self) -> Ed25519PublicKey {
|
||||
self.ed25519.clone()
|
||||
}
|
||||
|
||||
/// The Curve25519 public key, used for establish shared secrets.
|
||||
#[napi(getter)]
|
||||
pub fn curve25519(&self) -> Curve25519PublicKey {
|
||||
self.curve25519.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<matrix_sdk_crypto::olm::IdentityKeys> for IdentityKeys {
|
||||
fn from(value: matrix_sdk_crypto::olm::IdentityKeys) -> Self {
|
||||
Self {
|
||||
ed25519: Ed25519PublicKey { inner: value.ed25519 },
|
||||
curve25519: Curve25519PublicKey { inner: value.curve25519 },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
const { Attachment, EncryptedAttachment } = require("../");
|
||||
|
||||
describe(Attachment.name, () => {
|
||||
const originalData = "hello";
|
||||
const textEncoder = new TextEncoder();
|
||||
const textDecoder = new TextDecoder();
|
||||
|
||||
let encryptedAttachment;
|
||||
|
||||
test("can encrypt data", () => {
|
||||
encryptedAttachment = Attachment.encrypt(textEncoder.encode(originalData));
|
||||
|
||||
const mediaEncryptionInfo = JSON.parse(encryptedAttachment.mediaEncryptionInfo);
|
||||
|
||||
expect(mediaEncryptionInfo).toMatchObject({
|
||||
v: "v2",
|
||||
key: {
|
||||
kty: expect.any(String),
|
||||
key_ops: expect.arrayContaining(["encrypt", "decrypt"]),
|
||||
alg: expect.any(String),
|
||||
k: expect.any(String),
|
||||
ext: expect.any(Boolean),
|
||||
},
|
||||
iv: expect.stringMatching(/^[A-Za-z0-9\+/]+$/),
|
||||
hashes: {
|
||||
sha256: expect.stringMatching(/^[A-Za-z0-9\+/]+$/),
|
||||
},
|
||||
});
|
||||
|
||||
const encryptedData = encryptedAttachment.encryptedData;
|
||||
expect(
|
||||
encryptedData.every((i) => {
|
||||
i != 0;
|
||||
}),
|
||||
).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test("can decrypt data", () => {
|
||||
expect(encryptedAttachment.hasMediaEncryptionInfoBeenConsumed).toStrictEqual(false);
|
||||
|
||||
const decryptedAttachment = Attachment.decrypt(encryptedAttachment);
|
||||
|
||||
expect(textDecoder.decode(decryptedAttachment)).toStrictEqual(originalData);
|
||||
expect(encryptedAttachment.hasMediaEncryptionInfoBeenConsumed).toStrictEqual(true);
|
||||
});
|
||||
|
||||
test("can only decrypt once", () => {
|
||||
expect(encryptedAttachment.hasMediaEncryptionInfoBeenConsumed).toStrictEqual(true);
|
||||
|
||||
expect(() => {
|
||||
textDecoder.decode(decryptedAttachment);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe(EncryptedAttachment.name, () => {
|
||||
const originalData = "hello";
|
||||
const textDecoder = new TextDecoder();
|
||||
|
||||
test("can be created manually", () => {
|
||||
const encryptedAttachment = new EncryptedAttachment(
|
||||
new Uint8Array([24, 150, 67, 37, 144]),
|
||||
JSON.stringify({
|
||||
v: "v2",
|
||||
key: {
|
||||
kty: "oct",
|
||||
key_ops: ["encrypt", "decrypt"],
|
||||
alg: "A256CTR",
|
||||
k: "QbNXUjuukFyEJ8cQZjJuzN6mMokg0HJIjx0wVMLf5BM",
|
||||
ext: true,
|
||||
},
|
||||
iv: "xk2AcWkomiYAAAAAAAAAAA",
|
||||
hashes: {
|
||||
sha256: "JsRbDXgOja4xvDiF3DwBuLHdxUzIrVYIuj7W/t3aEok",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(encryptedAttachment.hasMediaEncryptionInfoBeenConsumed).toStrictEqual(false);
|
||||
expect(textDecoder.decode(Attachment.decrypt(encryptedAttachment))).toStrictEqual(originalData);
|
||||
expect(encryptedAttachment.hasMediaEncryptionInfoBeenConsumed).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
@@ -1,30 +0,0 @@
|
||||
const { EncryptionAlgorithm, EncryptionSettings, HistoryVisibility, VerificationState } = require("../");
|
||||
|
||||
describe("EncryptionAlgorithm", () => {
|
||||
test("has the correct variant values", () => {
|
||||
expect(EncryptionAlgorithm.OlmV1Curve25519AesSha2).toStrictEqual(0);
|
||||
expect(EncryptionAlgorithm.MegolmV1AesSha2).toStrictEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe(EncryptionSettings.name, () => {
|
||||
test("can be instantiated with default values", () => {
|
||||
const es = new EncryptionSettings();
|
||||
|
||||
expect(es.algorithm).toStrictEqual(EncryptionAlgorithm.MegolmV1AesSha2);
|
||||
expect(es.rotationPeriod).toStrictEqual(604800000000n);
|
||||
expect(es.rotationPeriodMessages).toStrictEqual(100n);
|
||||
expect(es.historyVisibility).toStrictEqual(HistoryVisibility.Shared);
|
||||
});
|
||||
|
||||
test("checks the history visibility values", () => {
|
||||
const es = new EncryptionSettings();
|
||||
|
||||
es.historyVisibility = HistoryVisibility.Invited;
|
||||
|
||||
expect(es.historyVisibility).toStrictEqual(HistoryVisibility.Invited);
|
||||
expect(() => {
|
||||
es.historyVisibility = 42;
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
const { HistoryVisibility } = require("../");
|
||||
|
||||
describe("HistoryVisibility", () => {
|
||||
test("has the correct variant values", () => {
|
||||
expect(HistoryVisibility.Invited).toStrictEqual(0);
|
||||
expect(HistoryVisibility.Joined).toStrictEqual(1);
|
||||
expect(HistoryVisibility.Shared).toStrictEqual(2);
|
||||
expect(HistoryVisibility.WorldReadable).toStrictEqual(3);
|
||||
});
|
||||
});
|
||||
@@ -1,140 +0,0 @@
|
||||
const {
|
||||
UserId,
|
||||
DeviceId,
|
||||
DeviceKeyId,
|
||||
DeviceKeyAlgorithm,
|
||||
DeviceKeyAlgorithmName,
|
||||
RoomId,
|
||||
ServerName,
|
||||
} = require("../");
|
||||
|
||||
describe(UserId.name, () => {
|
||||
test("cannot be invalid", () => {
|
||||
expect(() => {
|
||||
new UserId("@foobar");
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
const user = new UserId("@foo:bar.org");
|
||||
|
||||
test("localpart is present", () => {
|
||||
expect(user.localpart).toStrictEqual("foo");
|
||||
});
|
||||
|
||||
test("server name is present", () => {
|
||||
expect(user.serverName).toBeInstanceOf(ServerName);
|
||||
});
|
||||
|
||||
test("user ID is not historical", () => {
|
||||
expect(user.isHistorical()).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test("can read the user ID as a string", () => {
|
||||
expect(user.toString()).toStrictEqual("@foo:bar.org");
|
||||
});
|
||||
});
|
||||
|
||||
describe(DeviceId.name, () => {
|
||||
const device = new DeviceId("foo");
|
||||
|
||||
test("can read the device ID as a string", () => {
|
||||
expect(device.toString()).toStrictEqual("foo");
|
||||
});
|
||||
});
|
||||
|
||||
describe(DeviceKeyId.name, () => {
|
||||
for (const deviceKey of [
|
||||
{
|
||||
name: "ed25519",
|
||||
id: "ed25519:foobar",
|
||||
algorithmName: DeviceKeyAlgorithmName.Ed25519,
|
||||
algorithm: "ed25519",
|
||||
deviceId: "foobar",
|
||||
},
|
||||
|
||||
{
|
||||
name: "curve25519",
|
||||
id: "curve25519:foobar",
|
||||
algorithmName: DeviceKeyAlgorithmName.Curve25519,
|
||||
algorithm: "curve25519",
|
||||
deviceId: "foobar",
|
||||
},
|
||||
|
||||
{
|
||||
name: "signed curve25519",
|
||||
id: "signed_curve25519:foobar",
|
||||
algorithmName: DeviceKeyAlgorithmName.SignedCurve25519,
|
||||
algorithm: "signed_curve25519",
|
||||
deviceId: "foobar",
|
||||
},
|
||||
|
||||
{
|
||||
name: "unknown",
|
||||
id: "hello:foobar",
|
||||
algorithmName: DeviceKeyAlgorithmName.Unknown,
|
||||
algorithm: "hello",
|
||||
deviceId: "foobar",
|
||||
},
|
||||
]) {
|
||||
test(`${deviceKey.name} algorithm`, () => {
|
||||
const dk = new DeviceKeyId(deviceKey.id);
|
||||
|
||||
expect(dk.algorithm.name).toStrictEqual(deviceKey.algorithmName);
|
||||
expect(dk.algorithm.toString()).toStrictEqual(deviceKey.algorithm);
|
||||
expect(dk.deviceId.toString()).toStrictEqual(deviceKey.deviceId);
|
||||
expect(dk.toString()).toStrictEqual(deviceKey.id);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe("DeviceKeyAlgorithmName", () => {
|
||||
test("has the correct variants", () => {
|
||||
expect(DeviceKeyAlgorithmName.Ed25519).toStrictEqual(0);
|
||||
expect(DeviceKeyAlgorithmName.Curve25519).toStrictEqual(1);
|
||||
expect(DeviceKeyAlgorithmName.SignedCurve25519).toStrictEqual(2);
|
||||
expect(DeviceKeyAlgorithmName.Unknown).toStrictEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe(RoomId.name, () => {
|
||||
test("cannot be invalid", () => {
|
||||
expect(() => {
|
||||
new RoomId("!foo");
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
const room = new RoomId("!foo:bar.org");
|
||||
|
||||
test("localpart is present", () => {
|
||||
expect(room.localpart).toStrictEqual("foo");
|
||||
});
|
||||
|
||||
test("server name is present", () => {
|
||||
expect(room.serverName).toBeInstanceOf(ServerName);
|
||||
});
|
||||
|
||||
test("can read the room ID as string", () => {
|
||||
expect(room.toString()).toStrictEqual("!foo:bar.org");
|
||||
});
|
||||
});
|
||||
|
||||
describe(ServerName.name, () => {
|
||||
test("cannot be invalid", () => {
|
||||
expect(() => {
|
||||
new ServerName("@foobar");
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test("host is present", () => {
|
||||
expect(new ServerName("foo.org").host).toStrictEqual("foo.org");
|
||||
});
|
||||
|
||||
test("port can be optional", () => {
|
||||
expect(new ServerName("foo.org").port).toStrictEqual(null);
|
||||
expect(new ServerName("foo.org:1234").port).toStrictEqual(1234);
|
||||
});
|
||||
|
||||
test("server is not an IP literal", () => {
|
||||
expect(new ServerName("foo.org").isIpLiteral()).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
@@ -1,483 +0,0 @@
|
||||
const {
|
||||
OlmMachine,
|
||||
UserId,
|
||||
DeviceId,
|
||||
DeviceKeyId,
|
||||
RoomId,
|
||||
DeviceLists,
|
||||
RequestType,
|
||||
KeysUploadRequest,
|
||||
KeysQueryRequest,
|
||||
KeysClaimRequest,
|
||||
EncryptionSettings,
|
||||
DecryptedRoomEvent,
|
||||
VerificationState,
|
||||
CrossSigningStatus,
|
||||
MaybeSignature,
|
||||
ShieldColor,
|
||||
StoreType,
|
||||
Versions,
|
||||
getVersions,
|
||||
} = require("../");
|
||||
const path = require("path");
|
||||
const os = require("os");
|
||||
const fs = require("fs/promises");
|
||||
|
||||
describe("StoreType", () => {
|
||||
test("has the correct variant values", () => {
|
||||
expect(StoreType.Sqlite).toStrictEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Versions", () => {
|
||||
test("can find out the crate versions", async () => {
|
||||
const versions = getVersions();
|
||||
|
||||
expect(versions).toBeInstanceOf(Versions);
|
||||
expect(versions.vodozemac).toBeDefined();
|
||||
expect(versions.matrixSdkCrypto).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe(OlmMachine.name, () => {
|
||||
test("cannot be instantiated with the constructor", () => {
|
||||
expect(() => {
|
||||
new OlmMachine();
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test("can be instantiated with the async initializer", async () => {
|
||||
expect(await OlmMachine.initialize(new UserId("@foo:bar.org"), new DeviceId("baz"))).toBeInstanceOf(OlmMachine);
|
||||
});
|
||||
|
||||
describe("can be instantiated with a store", () => {
|
||||
for (const [store_type, store_name] of [
|
||||
[StoreType.Sqlite, "sqlite"],
|
||||
[null, "default"],
|
||||
]) {
|
||||
test(`with no passphrase (store: ${store_name})`, async () => {
|
||||
const temp_directory = await fs.mkdtemp(path.join(os.tmpdir(), "matrix-sdk-crypto--"));
|
||||
|
||||
expect(
|
||||
await OlmMachine.initialize(
|
||||
new UserId("@foo:bar.org"),
|
||||
new DeviceId("baz"),
|
||||
temp_directory,
|
||||
null,
|
||||
store_type,
|
||||
),
|
||||
).toBeInstanceOf(OlmMachine);
|
||||
});
|
||||
|
||||
test(`with a passphrase (store: ${store_name})`, async () => {
|
||||
const temp_directory = await fs.mkdtemp(path.join(os.tmpdir(), "matrix-sdk-crypto--"));
|
||||
|
||||
expect(
|
||||
await OlmMachine.initialize(
|
||||
new UserId("@foo:bar.org"),
|
||||
new DeviceId("baz"),
|
||||
temp_directory,
|
||||
"hello",
|
||||
store_type,
|
||||
),
|
||||
).toBeInstanceOf(OlmMachine);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const user = new UserId("@alice:example.org");
|
||||
const device = new DeviceId("foobar");
|
||||
const room = new RoomId("!baz:matrix.org");
|
||||
|
||||
function machine(new_user, new_device) {
|
||||
return OlmMachine.initialize(new_user || user, new_device || device);
|
||||
}
|
||||
|
||||
test("can drop/close, and then re-open", async () => {
|
||||
const temp_directory = await fs.mkdtemp(path.join(os.tmpdir(), "matrix-sdk-crypto--"));
|
||||
|
||||
let m1 = await OlmMachine.initialize(
|
||||
new UserId("@test:bar.org"),
|
||||
new DeviceId("device"),
|
||||
temp_directory,
|
||||
"hello",
|
||||
);
|
||||
m1.close();
|
||||
|
||||
let m2 = await OlmMachine.initialize(
|
||||
new UserId("@test:bar.org"),
|
||||
new DeviceId("device"),
|
||||
temp_directory,
|
||||
"hello",
|
||||
);
|
||||
m2.close();
|
||||
});
|
||||
|
||||
test("can read user ID", async () => {
|
||||
expect((await machine()).userId.toString()).toStrictEqual(user.toString());
|
||||
});
|
||||
|
||||
test("can read device ID", async () => {
|
||||
expect((await machine()).deviceId.toString()).toStrictEqual(device.toString());
|
||||
});
|
||||
|
||||
test("can read identity keys", async () => {
|
||||
const identityKeys = (await machine()).identityKeys;
|
||||
|
||||
expect(identityKeys.ed25519.toBase64()).toMatch(/^[A-Za-z0-9+/]+$/);
|
||||
expect(identityKeys.curve25519.toBase64()).toMatch(/^[A-Za-z0-9+/]+$/);
|
||||
});
|
||||
|
||||
test("can receive sync changes", async () => {
|
||||
const m = await machine();
|
||||
const toDeviceEvents = JSON.stringify([]);
|
||||
const changedDevices = new DeviceLists();
|
||||
const oneTimeKeyCounts = {};
|
||||
const unusedFallbackKeys = [];
|
||||
|
||||
const receiveSyncChanges = JSON.parse(
|
||||
await m.receiveSyncChanges(toDeviceEvents, changedDevices, oneTimeKeyCounts, unusedFallbackKeys),
|
||||
);
|
||||
|
||||
expect(receiveSyncChanges).toEqual([[], []]);
|
||||
});
|
||||
|
||||
test("can get the outgoing requests that need to be send out", async () => {
|
||||
const m = await machine();
|
||||
const toDeviceEvents = JSON.stringify([]);
|
||||
const changedDevices = new DeviceLists();
|
||||
const oneTimeKeyCounts = {};
|
||||
const unusedFallbackKeys = [];
|
||||
|
||||
const receiveSyncChanges = JSON.parse(
|
||||
await m.receiveSyncChanges(toDeviceEvents, changedDevices, oneTimeKeyCounts, unusedFallbackKeys),
|
||||
);
|
||||
|
||||
expect(receiveSyncChanges).toEqual([[], []]);
|
||||
|
||||
const outgoingRequests = await m.outgoingRequests();
|
||||
|
||||
expect(outgoingRequests).toHaveLength(2);
|
||||
|
||||
{
|
||||
expect(outgoingRequests[0]).toBeInstanceOf(KeysUploadRequest);
|
||||
expect(outgoingRequests[0].id).toBeDefined();
|
||||
expect(outgoingRequests[0].type).toStrictEqual(RequestType.KeysUpload);
|
||||
|
||||
const body = JSON.parse(outgoingRequests[0].body);
|
||||
expect(body.device_keys).toBeDefined();
|
||||
expect(body.one_time_keys).toBeDefined();
|
||||
}
|
||||
|
||||
{
|
||||
expect(outgoingRequests[1]).toBeInstanceOf(KeysQueryRequest);
|
||||
expect(outgoingRequests[1].id).toBeDefined();
|
||||
expect(outgoingRequests[1].type).toStrictEqual(RequestType.KeysQuery);
|
||||
|
||||
const body = JSON.parse(outgoingRequests[1].body);
|
||||
expect(body.timeout).toBeDefined();
|
||||
expect(body.device_keys).toBeDefined();
|
||||
expect(body.token).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
describe("setup workflow to mark requests as sent", () => {
|
||||
let m;
|
||||
let ougoingRequests;
|
||||
|
||||
beforeAll(async () => {
|
||||
m = await machine(new UserId("@alice:example.org"), new DeviceId("DEVICEID"));
|
||||
|
||||
const toDeviceEvents = JSON.stringify([]);
|
||||
const changedDevices = new DeviceLists();
|
||||
const oneTimeKeyCounts = {};
|
||||
const unusedFallbackKeys = [];
|
||||
|
||||
await m.receiveSyncChanges(toDeviceEvents, changedDevices, oneTimeKeyCounts, unusedFallbackKeys);
|
||||
outgoingRequests = await m.outgoingRequests();
|
||||
|
||||
expect(outgoingRequests).toHaveLength(2);
|
||||
});
|
||||
|
||||
test("can mark requests as sent", async () => {
|
||||
{
|
||||
const request = outgoingRequests[0];
|
||||
expect(request).toBeInstanceOf(KeysUploadRequest);
|
||||
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keysupload
|
||||
const hypothetical_response = JSON.stringify({
|
||||
one_time_key_counts: {
|
||||
curve25519: 10,
|
||||
signed_curve25519: 20,
|
||||
},
|
||||
});
|
||||
const marked = await m.markRequestAsSent(request.id, request.type, hypothetical_response);
|
||||
expect(marked).toStrictEqual(true);
|
||||
}
|
||||
|
||||
{
|
||||
const request = outgoingRequests[1];
|
||||
expect(request).toBeInstanceOf(KeysQueryRequest);
|
||||
|
||||
// https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keysquery
|
||||
const hypothetical_response = JSON.stringify({
|
||||
device_keys: {
|
||||
"@alice:example.org": {
|
||||
JLAFKJWSCS: {
|
||||
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
|
||||
device_id: "JLAFKJWSCS",
|
||||
keys: {
|
||||
"curve25519:JLAFKJWSCS": "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4",
|
||||
"ed25519:JLAFKJWSCS": "nE6W2fCblxDcOFmeEtCHNl8/l8bXcu7GKyAswA4r3mM",
|
||||
},
|
||||
signatures: {
|
||||
"@alice:example.org": {
|
||||
"ed25519:JLAFKJWSCS":
|
||||
"m53Wkbh2HXkc3vFApZvCrfXcX3AI51GsDHustMhKwlv3TuOJMj4wistcOTM8q2+e/Ro7rWFUb9ZfnNbwptSUBA",
|
||||
},
|
||||
},
|
||||
unsigned: {
|
||||
device_display_name: "Alice's mobile phone",
|
||||
},
|
||||
user_id: "@alice:example.org",
|
||||
},
|
||||
},
|
||||
},
|
||||
failures: {},
|
||||
});
|
||||
const marked = await m.markRequestAsSent(request.id, request.type, hypothetical_response);
|
||||
expect(marked).toStrictEqual(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("setup workflow to encrypt/decrypt events", () => {
|
||||
let m;
|
||||
const user = new UserId("@alice:example.org");
|
||||
const device = new DeviceId("JLAFKJWSCS");
|
||||
const room = new RoomId("!test:localhost");
|
||||
|
||||
beforeAll(async () => {
|
||||
m = await machine(user, device);
|
||||
});
|
||||
|
||||
test("can pass keysquery and keysclaim requests directly", async () => {
|
||||
{
|
||||
// derived from https://github.com/matrix-org/matrix-rust-sdk/blob/7f49618d350fab66b7e1dc4eaf64ec25ceafd658/benchmarks/benches/crypto_bench/keys_query.json
|
||||
const hypothetical_response = JSON.stringify({
|
||||
device_keys: {
|
||||
"@example:localhost": {
|
||||
AFGUOBTZWM: {
|
||||
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
|
||||
device_id: "AFGUOBTZWM",
|
||||
keys: {
|
||||
"curve25519:AFGUOBTZWM": "boYjDpaC+7NkECQEeMh5dC+I1+AfriX0VXG2UV7EUQo",
|
||||
"ed25519:AFGUOBTZWM": "NayrMQ33ObqMRqz6R9GosmHdT6HQ6b/RX/3QlZ2yiec",
|
||||
},
|
||||
signatures: {
|
||||
"@example:localhost": {
|
||||
"ed25519:AFGUOBTZWM":
|
||||
"RoSWvru1jj6fs2arnTedWsyIyBmKHMdOu7r9gDi0BZ61h9SbCK2zLXzuJ9ZFLao2VvA0yEd7CASCmDHDLYpXCA",
|
||||
},
|
||||
},
|
||||
user_id: "@example:localhost",
|
||||
unsigned: {
|
||||
device_display_name: "rust-sdk",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
failures: {},
|
||||
master_keys: {
|
||||
"@example:localhost": {
|
||||
user_id: "@example:localhost",
|
||||
usage: ["master"],
|
||||
keys: {
|
||||
"ed25519:n2lpJGx0LiKnuNE1IucZP3QExrD4SeRP0veBHPe3XUU":
|
||||
"n2lpJGx0LiKnuNE1IucZP3QExrD4SeRP0veBHPe3XUU",
|
||||
},
|
||||
signatures: {
|
||||
"@example:localhost": {
|
||||
"ed25519:TCSJXPWGVS":
|
||||
"+j9G3L41I1fe0++wwusTTQvbboYW0yDtRWUEujhwZz4MAltjLSfJvY0hxhnz+wHHmuEXvQDen39XOpr1p29sAg",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
self_signing_keys: {
|
||||
"@example:localhost": {
|
||||
user_id: "@example:localhost",
|
||||
usage: ["self_signing"],
|
||||
keys: {
|
||||
"ed25519:kQXOuy639Yt47mvNTdrIluoC6DMvfbZLYbxAmwiDyhI":
|
||||
"kQXOuy639Yt47mvNTdrIluoC6DMvfbZLYbxAmwiDyhI",
|
||||
},
|
||||
signatures: {
|
||||
"@example:localhost": {
|
||||
"ed25519:n2lpJGx0LiKnuNE1IucZP3QExrD4SeRP0veBHPe3XUU":
|
||||
"q32ifix/qyRpvmegw2BEJklwoBCAJldDNkcX+fp+lBA4Rpyqtycxge6BA4hcJdxYsy3oV0IHRuugS8rJMMFyAA",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
user_signing_keys: {
|
||||
"@example:localhost": {
|
||||
user_id: "@example:localhost",
|
||||
usage: ["user_signing"],
|
||||
keys: {
|
||||
"ed25519:g4ED07Fnqf3GzVWNN1pZ0IFrPQVdqQf+PYoJNH4eE0s":
|
||||
"g4ED07Fnqf3GzVWNN1pZ0IFrPQVdqQf+PYoJNH4eE0s",
|
||||
},
|
||||
signatures: {
|
||||
"@example:localhost": {
|
||||
"ed25519:n2lpJGx0LiKnuNE1IucZP3QExrD4SeRP0veBHPe3XUU":
|
||||
"nKQu8alQKDefNbZz9luYPcNj+Z+ouQSot4fU/A23ELl1xrI06QVBku/SmDx0sIW1ytso0Cqwy1a+3PzCa1XABg",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const marked = await m.markRequestAsSent("foo", RequestType.KeysQuery, hypothetical_response);
|
||||
}
|
||||
|
||||
{
|
||||
// derived from https://github.com/matrix-org/matrix-rust-sdk/blob/7f49618d350fab66b7e1dc4eaf64ec25ceafd658/benchmarks/benches/crypto_bench/keys_claim.json
|
||||
const hypothetical_response = JSON.stringify({
|
||||
one_time_keys: {
|
||||
"@example:localhost": {
|
||||
AFGUOBTZWM: {
|
||||
"signed_curve25519:AAAABQ": {
|
||||
key: "9IGouMnkB6c6HOd4xUsNv4i3Dulb4IS96TzDordzOws",
|
||||
signatures: {
|
||||
"@example:localhost": {
|
||||
"ed25519:AFGUOBTZWM":
|
||||
"2bvUbbmJegrV0eVP/vcJKuIWC3kud+V8+C0dZtg4dVovOSJdTP/iF36tQn2bh5+rb9xLlSeztXBdhy4c+LiOAg",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
failures: {},
|
||||
});
|
||||
const marked = await m.markRequestAsSent("bar", RequestType.KeysClaim, hypothetical_response);
|
||||
}
|
||||
});
|
||||
|
||||
test("can share a room key", async () => {
|
||||
const other_users = [new UserId("@example:localhost")];
|
||||
|
||||
const requests = JSON.parse(await m.shareRoomKey(room, other_users, new EncryptionSettings()));
|
||||
|
||||
expect(requests).toHaveLength(1);
|
||||
expect(requests[0].event_type).toBeDefined();
|
||||
expect(requests[0].txn_id).toBeDefined();
|
||||
expect(requests[0].messages).toBeDefined();
|
||||
expect(requests[0].messages["@example:localhost"]).toBeDefined();
|
||||
});
|
||||
|
||||
let encrypted;
|
||||
|
||||
test("can encrypt an event", async () => {
|
||||
encrypted = JSON.parse(
|
||||
await m.encryptRoomEvent(
|
||||
room,
|
||||
"m.room.message",
|
||||
JSON.stringify({
|
||||
hello: "world",
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
expect(encrypted.algorithm).toBeDefined();
|
||||
expect(encrypted.ciphertext).toBeDefined();
|
||||
expect(encrypted.sender_key).toBeDefined();
|
||||
expect(encrypted.device_id).toStrictEqual(device.toString());
|
||||
expect(encrypted.session_id).toBeDefined();
|
||||
});
|
||||
|
||||
test("can decrypt an event", async () => {
|
||||
const decrypted = await m.decryptRoomEvent(
|
||||
JSON.stringify({
|
||||
type: "m.room.encrypted",
|
||||
event_id: "$xxxxx:example.org",
|
||||
origin_server_ts: Date.now(),
|
||||
sender: user.toString(),
|
||||
content: encrypted,
|
||||
unsigned: {
|
||||
age: 1234,
|
||||
},
|
||||
}),
|
||||
room,
|
||||
);
|
||||
|
||||
expect(decrypted).toBeInstanceOf(DecryptedRoomEvent);
|
||||
|
||||
const event = JSON.parse(decrypted.event);
|
||||
expect(event.content.hello).toStrictEqual("world");
|
||||
|
||||
expect(decrypted.sender.toString()).toStrictEqual(user.toString());
|
||||
expect(decrypted.senderDevice.toString()).toStrictEqual(device.toString());
|
||||
expect(decrypted.senderCurve25519Key).toBeDefined();
|
||||
expect(decrypted.senderClaimedEd25519Key).toBeDefined();
|
||||
expect(decrypted.forwardingCurve25519KeyChain).toHaveLength(0);
|
||||
expect(decrypted.shieldState(true).color).toStrictEqual(ShieldColor.Red);
|
||||
expect(decrypted.shieldState(false).color).toStrictEqual(ShieldColor.Red);
|
||||
});
|
||||
});
|
||||
|
||||
test("can update tracked users", async () => {
|
||||
const m = await machine();
|
||||
|
||||
expect(await m.updateTrackedUsers([user])).toStrictEqual(undefined);
|
||||
});
|
||||
|
||||
test("can read cross-signing status", async () => {
|
||||
const m = await machine();
|
||||
const crossSigningStatus = await m.crossSigningStatus();
|
||||
|
||||
expect(crossSigningStatus).toBeInstanceOf(CrossSigningStatus);
|
||||
expect(crossSigningStatus.hasMaster).toStrictEqual(false);
|
||||
expect(crossSigningStatus.hasSelfSigning).toStrictEqual(false);
|
||||
expect(crossSigningStatus.hasUserSigning).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test("can sign a message", async () => {
|
||||
const m = await machine();
|
||||
const signatures = await m.sign("foo");
|
||||
|
||||
expect(signatures.isEmpty).toStrictEqual(false);
|
||||
expect(signatures.count).toStrictEqual(1n);
|
||||
|
||||
let base64;
|
||||
|
||||
// `get`
|
||||
{
|
||||
const signature = signatures.get(user);
|
||||
|
||||
expect(signature).toMatchObject({
|
||||
"ed25519:foobar": expect.any(MaybeSignature),
|
||||
});
|
||||
expect(signature["ed25519:foobar"].isValid).toStrictEqual(true);
|
||||
expect(signature["ed25519:foobar"].isInvalid).toStrictEqual(false);
|
||||
expect(signature["ed25519:foobar"].invalidSignatureSource).toBeNull();
|
||||
|
||||
base64 = signature["ed25519:foobar"].signature.toBase64();
|
||||
|
||||
expect(base64).toMatch(/^[A-Za-z0-9\+/]+$/);
|
||||
expect(signature["ed25519:foobar"].signature.ed25519.toBase64()).toStrictEqual(base64);
|
||||
}
|
||||
|
||||
// `getSignature`
|
||||
{
|
||||
const signature = signatures.getSignature(user, new DeviceKeyId("ed25519:foobar"));
|
||||
expect(signature.toBase64()).toStrictEqual(base64);
|
||||
}
|
||||
|
||||
// Unknown signatures.
|
||||
{
|
||||
expect(signatures.get(new UserId("@hello:example.org"))).toBeNull();
|
||||
expect(signatures.getSignature(user, new DeviceKeyId("world:foobar"))).toBeNull();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,40 +0,0 @@
|
||||
const {
|
||||
RequestType,
|
||||
KeysUploadRequest,
|
||||
KeysQueryRequest,
|
||||
KeysClaimRequest,
|
||||
ToDeviceRequest,
|
||||
SignatureUploadRequest,
|
||||
RoomMessageRequest,
|
||||
KeysBackupRequest,
|
||||
} = require("../");
|
||||
|
||||
describe("RequestType", () => {
|
||||
test("has the correct variant values", () => {
|
||||
expect(RequestType.KeysUpload).toStrictEqual(0);
|
||||
expect(RequestType.KeysQuery).toStrictEqual(1);
|
||||
expect(RequestType.KeysClaim).toStrictEqual(2);
|
||||
expect(RequestType.ToDevice).toStrictEqual(3);
|
||||
expect(RequestType.SignatureUpload).toStrictEqual(4);
|
||||
expect(RequestType.RoomMessage).toStrictEqual(5);
|
||||
expect(RequestType.KeysBackup).toStrictEqual(6);
|
||||
});
|
||||
});
|
||||
|
||||
for (const request of [
|
||||
KeysUploadRequest,
|
||||
KeysQueryRequest,
|
||||
KeysClaimRequest,
|
||||
ToDeviceRequest,
|
||||
SignatureUploadRequest,
|
||||
RoomMessageRequest,
|
||||
KeysBackupRequest,
|
||||
]) {
|
||||
describe(request.name, () => {
|
||||
test("cannot be instantiated", () => {
|
||||
expect(() => {
|
||||
new request();
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
const { DecryptedRoomEvent } = require("../");
|
||||
|
||||
describe(DecryptedRoomEvent.name, () => {
|
||||
test("cannot be instantiated", () => {
|
||||
expect(() => {
|
||||
new DecryptedRoomEvent();
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
@@ -1,31 +0,0 @@
|
||||
const { DeviceLists, UserId } = require("../");
|
||||
|
||||
describe(DeviceLists.name, () => {
|
||||
test("can be empty", () => {
|
||||
const empty = new DeviceLists();
|
||||
|
||||
expect(empty.isEmpty()).toStrictEqual(true);
|
||||
expect(empty.changed).toHaveLength(0);
|
||||
expect(empty.left).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("can be coerced empty", () => {
|
||||
const empty = new DeviceLists([], []);
|
||||
|
||||
expect(empty.isEmpty()).toStrictEqual(true);
|
||||
expect(empty.changed).toHaveLength(0);
|
||||
expect(empty.left).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("returns the correct `changed` and `left`", () => {
|
||||
const list = new DeviceLists([new UserId("@foo:bar.org")], [new UserId("@baz:qux.org")]);
|
||||
|
||||
expect(list.isEmpty()).toStrictEqual(false);
|
||||
|
||||
expect(list.changed).toHaveLength(1);
|
||||
expect(list.changed[0].toString()).toStrictEqual("@foo:bar.org");
|
||||
|
||||
expect(list.left).toHaveLength(1);
|
||||
expect(list.left[0].toString()).toStrictEqual("@baz:qux.org");
|
||||
});
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true
|
||||
},
|
||||
"typedocOptions": {
|
||||
"entryPoints": ["index.d.ts"],
|
||||
"out": "docs",
|
||||
"readme": "README.md"
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,6 @@ coverage:
|
||||
ignore:
|
||||
- "bindings/matrix-sdk-crypto-ffi"
|
||||
- "bindings/matrix-sdk-crypto-js"
|
||||
- "bindings/matrix-sdk-crypto-nodejs"
|
||||
- "bindings/matrix-sdk-ffi"
|
||||
- "crates/matrix-sdk-indexeddb"
|
||||
- "testing"
|
||||
|
||||
@@ -14,7 +14,6 @@ exclude = [
|
||||
# bindings
|
||||
"matrix-sdk-crypto-ffi",
|
||||
"matrix-sdk-crypto-js",
|
||||
"matrix-sdk-crypto-nodejs",
|
||||
"matrix-sdk-ffi",
|
||||
# wasm only, can't get coverage
|
||||
"matrix-sdk-indexeddb",
|
||||
|
||||
Reference in New Issue
Block a user