diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10053774f..7d00586b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -128,9 +128,9 @@ jobs: strategy: fail-fast: true matrix: - node-version: [12.17, 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/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/README.md b/crates/matrix-sdk-crypto-nodejs/README.md index 289945546..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 12.17.0, 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). +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", 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..47cf62483 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('path'); +const os = require('os'); +const fs = require('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');