From 6477cc5072e3c0f43c847a9bba8cce103beaa6f0 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 13 Jun 2022 14:19:27 +0200 Subject: [PATCH 1/4] feat(crypto-nodejs): Add `store_path` and `store_passphrase` to the `OlmMachine` constructor. This patch adds the `store_path` and the `store_passphrase` arguments to the `OlmMachine` constructor to use a `CryptoStore` instead of having an in-memory Olm machine. --- crates/matrix-sdk-crypto-nodejs/Cargo.toml | 2 + .../matrix-sdk-crypto-nodejs/src/machine.rs | 60 +++++++++++++++---- .../tests/machine.test.js | 17 ++++++ 3 files changed, 68 insertions(+), 11 deletions(-) diff --git a/crates/matrix-sdk-crypto-nodejs/Cargo.toml b/crates/matrix-sdk-crypto-nodejs/Cargo.toml index d66723019..58242b295 100644 --- a/crates/matrix-sdk-crypto-nodejs/Cargo.toml +++ b/crates/matrix-sdk-crypto-nodejs/Cargo.toml @@ -27,12 +27,14 @@ tracing = ["tracing-subscriber"] [dependencies] matrix-sdk-crypto = { version = "0.5.0", path = "../matrix-sdk-crypto" } matrix-sdk-common = { version = "0.5.0", path = "../matrix-sdk-common" } +matrix-sdk-sled = { version = "0.1.0", path = "../matrix-sdk-sled", default-features = false, features = ["crypto-store"] } ruma = { version = "0.6.2", features = ["client-api-c", "rand", "unstable-msc2676", "unstable-msc2677"] } vodozemac = { git = "https://github.com/matrix-org/vodozemac/", rev = "d0e744287a14319c2a9148fef3747548c740fc36" } napi = { git = "https://github.com/Hywan/napi-rs", branch = "feat-either-n-up-to-26", default-features = false, features = ["napi6", "tokio_rt"] } napi-derive = { git = "https://github.com/Hywan/napi-rs", branch = "feat-either-n-up-to-26" } serde_json = "1.0.79" http = "0.2.6" +zeroize = "1.3.0" tracing-subscriber = { version = "0.3", default-features = false, features = ["tracing-log", "time", "smallvec", "fmt", "env-filter"], optional = true } [build-dependencies] diff --git a/crates/matrix-sdk-crypto-nodejs/src/machine.rs b/crates/matrix-sdk-crypto-nodejs/src/machine.rs index 5c29abeea..6d66809f8 100644 --- a/crates/matrix-sdk-crypto-nodejs/src/machine.rs +++ b/crates/matrix-sdk-crypto-nodejs/src/machine.rs @@ -1,6 +1,9 @@ //! The crypto specific Olm objects. -use std::collections::{BTreeMap, HashMap}; +use std::{ + collections::{BTreeMap, HashMap}, + sync::Arc, +}; use napi::bindgen_prelude::Either7; use napi_derive::*; @@ -9,6 +12,7 @@ use ruma::{ OwnedTransactionId, UInt, }; use serde_json::Value as JsonValue; +use zeroize::Zeroize; use crate::{ encryption, identifiers, into_err, requests, responses, responses::response_from_string, @@ -37,25 +41,59 @@ impl OlmMachine { /// Create a new memory-based `OlmMachine` asynchronously. /// - /// The created machine will keep the encryption keys only in - /// memory and once the object is dropped the keys will be lost. + /// 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] pub async fn initialize( user_id: &identifiers::UserId, device_id: &identifiers::DeviceId, - ) -> Self { - OlmMachine { - inner: matrix_sdk_crypto::OlmMachine::new( - user_id.inner.as_ref(), - device_id.inner.as_ref(), - ) - .await, - } + store_path: Option, + mut store_passphrase: Option, + ) -> napi::Result { + let store = store_path + .map(|store_path| { + matrix_sdk_sled::CryptoStore::open_with_passphrase( + store_path, + store_passphrase.as_deref(), + ) + .map(Arc::new) + .map_err(into_err) + }) + .transpose()?; + + store_passphrase.zeroize(); + + Ok(OlmMachine { + inner: match store { + Some(store) => matrix_sdk_crypto::OlmMachine::with_store( + user_id.inner.as_ref(), + device_id.inner.as_ref(), + store, + ) + .await + .map_err(into_err)?, + None => { + matrix_sdk_crypto::OlmMachine::new( + user_id.inner.as_ref(), + device_id.inner.as_ref(), + ) + .await + } + }, + }) } /// It's not possible to construct an `OlmMachine` with its diff --git a/crates/matrix-sdk-crypto-nodejs/tests/machine.test.js b/crates/matrix-sdk-crypto-nodejs/tests/machine.test.js index 03e95fbce..e120011e6 100644 --- a/crates/matrix-sdk-crypto-nodejs/tests/machine.test.js +++ b/crates/matrix-sdk-crypto-nodejs/tests/machine.test.js @@ -1,4 +1,7 @@ const { OlmMachine, UserId, DeviceId, RoomId, DeviceLists, RequestType, KeysUploadRequest, KeysQueryRequest, KeysClaimRequest, EncryptionSettings, DecryptedRoomEvent, VerificationState } = require('../'); +const path = require('node:path'); +const os = require('node:os'); +const fs = require('node:fs/promises'); describe(OlmMachine.name, () => { test('cannot be instantiated with the constructor', () => { @@ -9,6 +12,20 @@ describe(OlmMachine.name, () => { expect(await OlmMachine.initialize(new UserId('@foo:bar.org'), new DeviceId('baz'))).toBeInstanceOf(OlmMachine); }); + describe('can be instantiated with a store', () => { + test('with no passphrase', 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)).toBeInstanceOf(OlmMachine); + }); + + test('with a passphrase', 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')).toBeInstanceOf(OlmMachine); + }); + }); + const user = new UserId('@alice:example.org'); const device = new DeviceId('foobar'); const room = new RoomId('!baz:matrix.org'); From 83b730d1c826949d0ff28c895ded9d029cef1b3c Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 13 Jun 2022 14:39:47 +0200 Subject: [PATCH 2/4] chore(crypto-nodejs): Make the code compatible with Node.js < 18. --- crates/matrix-sdk-crypto-nodejs/tests/machine.test.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/matrix-sdk-crypto-nodejs/tests/machine.test.js b/crates/matrix-sdk-crypto-nodejs/tests/machine.test.js index e120011e6..61fea40b5 100644 --- a/crates/matrix-sdk-crypto-nodejs/tests/machine.test.js +++ b/crates/matrix-sdk-crypto-nodejs/tests/machine.test.js @@ -1,7 +1,7 @@ const { OlmMachine, UserId, DeviceId, RoomId, DeviceLists, RequestType, KeysUploadRequest, KeysQueryRequest, KeysClaimRequest, EncryptionSettings, DecryptedRoomEvent, VerificationState } = require('../'); -const path = require('node:path'); -const os = require('node:os'); -const fs = require('node:fs/promises'); +const path = require('path'); +const os = require('os'); +const fs = require('fs'); describe(OlmMachine.name, () => { test('cannot be instantiated with the constructor', () => { @@ -14,13 +14,13 @@ describe(OlmMachine.name, () => { describe('can be instantiated with a store', () => { test('with no passphrase', async () => { - const temp_directory = await fs.mkdtemp(path.join(os.tmpdir(), 'matrix-sdk-crypto--')); + const temp_directory = fs.mkdtempSync(path.join(os.tmpdir(), 'matrix-sdk-crypto--')); expect(await OlmMachine.initialize(new UserId('@foo:bar.org'), new DeviceId('baz'), temp_directory)).toBeInstanceOf(OlmMachine); }); test('with a passphrase', async () => { - const temp_directory = await fs.mkdtemp(path.join(os.tmpdir(), 'matrix-sdk-crypto--')); + const temp_directory = fs.mkdtempSync(path.join(os.tmpdir(), 'matrix-sdk-crypto--')); expect(await OlmMachine.initialize(new UserId('@foo:bar.org'), new DeviceId('baz'), temp_directory, 'hello')).toBeInstanceOf(OlmMachine); }); From 3833d35348b86ef6d006b4be37228dd482f3b0a0 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 13 Jun 2022 17:17:19 +0200 Subject: [PATCH 3/4] chore(crypto-nodejs): Drop Node.js v12.17. There is a segfault with `napi-rs` and Node.js in v12.17. It's an old version, it may be fair to drop its support for now. Let's see if people would need it in the future, we may work on `napi-rs` to fix this bug in case it's really necessary. --- .github/workflows/ci.yml | 2 +- crates/matrix-sdk-crypto-nodejs/README.md | 6 +++--- crates/matrix-sdk-crypto-nodejs/tests/machine.test.js | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10053774f..1a58e55f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -128,7 +128,7 @@ jobs: strategy: fail-fast: true matrix: - node-version: [12.17, 14.0, 15.0, 16.0] + node-version: [14.0, 15.0, 16.0] include: - node-version: 16.0 build-doc: true diff --git a/crates/matrix-sdk-crypto-nodejs/README.md b/crates/matrix-sdk-crypto-nodejs/README.md index 289945546..a7b8d8f9f 100644 --- a/crates/matrix-sdk-crypto-nodejs/README.md +++ b/crates/matrix-sdk-crypto-nodejs/README.md @@ -19,10 +19,10 @@ Node.js and npm Page](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). The binding is using NAPI 6 (Node API version 6), which means that is -compatible with Node.js versions 12.17.0, 14.0.0, 15.0.0 and 16.0.0 -(see [the full Node API version +compatible with Node.js versions 14.0.0, 15.0.0 and 16.0.0 (see [the +full Node API version matrix](https://nodejs.org/api/n-api.html#node-api-version-matrix)) -(we do not support the version 10.20). +(we do not support the versions 10.20 and 12.17.0). Once the Rust compiler, Node.js and npm are installed, you can run the following commands: diff --git a/crates/matrix-sdk-crypto-nodejs/tests/machine.test.js b/crates/matrix-sdk-crypto-nodejs/tests/machine.test.js index 61fea40b5..47cf62483 100644 --- a/crates/matrix-sdk-crypto-nodejs/tests/machine.test.js +++ b/crates/matrix-sdk-crypto-nodejs/tests/machine.test.js @@ -1,7 +1,7 @@ const { OlmMachine, UserId, DeviceId, RoomId, DeviceLists, RequestType, KeysUploadRequest, KeysQueryRequest, KeysClaimRequest, EncryptionSettings, DecryptedRoomEvent, VerificationState } = require('../'); const path = require('path'); const os = require('os'); -const fs = require('fs'); +const fs = require('fs/promises'); describe(OlmMachine.name, () => { test('cannot be instantiated with the constructor', () => { @@ -14,13 +14,13 @@ describe(OlmMachine.name, () => { describe('can be instantiated with a store', () => { test('with no passphrase', async () => { - const temp_directory = fs.mkdtempSync(path.join(os.tmpdir(), 'matrix-sdk-crypto--')); + 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)).toBeInstanceOf(OlmMachine); }); test('with a passphrase', async () => { - const temp_directory = fs.mkdtempSync(path.join(os.tmpdir(), 'matrix-sdk-crypto--')); + 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')).toBeInstanceOf(OlmMachine); }); From 073fb4558030c2d2dbe01b1bf4dc06b8698a5318 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 14 Jun 2022 09:41:03 +0200 Subject: [PATCH 4/4] feat(crypto-nodejs): Define Node.js versions policy. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now support only “current”, “active” or “maintenance” versions according to https://nodejs.org/en/about/releases/, which are compatible with NAPI v6. --- .github/workflows/ci.yml | 4 ++-- crates/matrix-sdk-crypto-nodejs/README.md | 13 ++++++++----- crates/matrix-sdk-crypto-nodejs/package.json | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a58e55f3..7d00586b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -128,9 +128,9 @@ jobs: strategy: fail-fast: true matrix: - node-version: [14.0, 15.0, 16.0] + node-version: [14.0, 16.0, 18.0] include: - - node-version: 16.0 + - node-version: 18.0 build-doc: true steps: diff --git a/crates/matrix-sdk-crypto-nodejs/README.md b/crates/matrix-sdk-crypto-nodejs/README.md index a7b8d8f9f..10a6d321a 100644 --- a/crates/matrix-sdk-crypto-nodejs/README.md +++ b/crates/matrix-sdk-crypto-nodejs/README.md @@ -18,11 +18,14 @@ 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 using NAPI 6 (Node API version 6), which means that is -compatible with Node.js versions 14.0.0, 15.0.0 and 16.0.0 (see [the -full Node API version -matrix](https://nodejs.org/api/n-api.html#node-api-version-matrix)) -(we do not support the versions 10.20 and 12.17.0). +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: 14.0.0, +16.0.0 and 18.0.0. Once the Rust compiler, Node.js and npm are installed, you can run the following commands: diff --git a/crates/matrix-sdk-crypto-nodejs/package.json b/crates/matrix-sdk-crypto-nodejs/package.json index f0ae76922..e83ba25e9 100644 --- a/crates/matrix-sdk-crypto-nodejs/package.json +++ b/crates/matrix-sdk-crypto-nodejs/package.json @@ -18,7 +18,7 @@ "typedoc": "^0.22.17" }, "engines": { - "node": ">= 10" + "node": ">= 14" }, "scripts": { "build": "napi build --platform --release --strip",