Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(crypto): Port to Wasm (in a JS host) and to NodeJS #675

Merged
merged 61 commits into from
May 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
7569c08
feat(crypto) Add `wasm-bindgen` as a dep and simplify `Cargo.toml`.
Hywan May 9, 2022
7817af9
feat(crypto) Add the `js` feature.
Hywan May 9, 2022
d3c20b2
feat(crypto) Generate a `cdylib` for the crate.
Hywan May 9, 2022
5c6a646
chore(crypto) Fix a typo in the code.
Hywan May 9, 2022
0fe0910
feat(crypto) Reduce Wasm binary size by enabling LTO.
Hywan May 9, 2022
3318789
feat(crypto) Implement `UserId`, `DeviceId` and `OlmMachine` in Wasm …
Hywan May 9, 2022
3aa46fa
feat(crypto) Implement `OlmMachine.receive_sync_changes` & friends.
Hywan May 10, 2022
901715b
chore(crypto) Generate a smaller Wasm module.
Hywan May 11, 2022
7e5eec8
feat(crypto) Implement `OlmMachine.outgoing_requests`.
Hywan May 12, 2022
fdb9fe2
feat(crypto) Remove existing `unwrap` code.
Hywan May 12, 2022
fd4dff7
feat(crypto) Implement `OlmMachine.user_id`, `.device_id` and `.displ…
Hywan May 12, 2022
569adb7
feat(crypto) Implement `OlmMachine.identity_keys`.
Hywan May 12, 2022
08665dc
feat(crypto): Implement our own `future_to_promise` helper to simplif…
Hywan May 16, 2022
1d38e54
feat(crypto): Return a `JsError` rather than a `String`.
Hywan May 16, 2022
f0bb35a
feat(crypto): Add `changed` and `left` to the constructor of `DeviceL…
Hywan May 16, 2022
acd5de3
feat(crypto): Implement `ServerName`, and add `UserId.serverName`.
Hywan May 16, 2022
2d15f75
feat(crypto): Use JavaScript naming style for methods.
Hywan May 16, 2022
056f348
feat(crypto): Implement `OlmMachine.trackedUsers`.
Hywan May 16, 2022
9f159ff
feat(crypto): Implement `OlmMachine.mark_request_as_sent`.
Hywan May 16, 2022
86c6e60
chore(crypto): Refactor the code with `TryFrom` on a tuple.
Hywan May 16, 2022
2451815
feat(crypto): Implement `*PublicKey.to_base64` and `.length`.
Hywan May 17, 2022
444ff99
feat(crypto): Implement `RoomId`.
Hywan May 17, 2022
ee713f9
feat(crypto): Implement `OlmMachine.encrypt` and `.invalidate_group_s…
Hywan May 17, 2022
b6a5d49
Merge branch 'main' into feat-crypto-wasm
Hywan May 31, 2022
30e9189
fix(crypto): Use JavaScript naming convention for `OlmMachine.invalid…
Hywan May 17, 2022
dc5b15e
fix(crypto): `OutgoingRequest::ToDeviceRequest` was not correctly map…
Hywan May 17, 2022
8b94aed
fix(crypto): Use `txn_id` for `transaction_id`.
Hywan May 17, 2022
e9d37d7
docs(crypto): Add missing docs.
Hywan May 17, 2022
c20349f
feat(crypto): Implement `OlmMachine.share_group_session`.
Hywan May 17, 2022
a024c9b
docs(crypto): Add missing documentation.
Hywan May 17, 2022
f01cfe4
feat(crypto): Implement `EncryptionSettings.new` with default values.
Hywan May 17, 2022
8161360
feat(crypto): Add `toString` methods on identifier objects.
Hywan May 17, 2022
16b5eeb
feat(crypto): Implement a hacky `downcast` function.
Hywan May 17, 2022
da8699f
feat(crypto): `DeviceLists.new` expects `Array<UserId>`s now.
Hywan May 17, 2022
db30ef6
test(crypto): Add tests for the Wasm API.
Hywan May 17, 2022
cdb252b
chore(crypto): Move everything inside `crates/matrix-sdk-crypto/`.
Hywan May 19, 2022
6b2df7a
feat(crypto): Implement `OlmMachine.get_missing_sessions.
Hywan May 19, 2022
f3ab1ae
chore(crypto): Fix a typo.
Hywan May 19, 2022
250b85d
feat(crypto): Extract `js` module to its own crate: `matrix-sdk-crypt…
Hywan May 31, 2022
6c472f8
feat(crypto-js): Start adding support for NodeJS.
Hywan May 23, 2022
bce11b2
feat(crypto-js): Implement `OlmMachine.update_tracked_users`.
Hywan May 23, 2022
84fe78b
Merge branch 'main' into feat-crypto-wasm
Hywan May 31, 2022
1a731ec
feat(crypto-js): Rename `OlmMachine.encrypt` to `.encrypt_room_event`.
Hywan May 23, 2022
4df5ee6
chore(crypto-js): Improve the `Makefile` for NodeJS support.
Hywan May 23, 2022
afef910
feat(crypto-js): Continue to port the API to NodeJS.
Hywan May 24, 2022
ee64814
feat(crypto-nodejs): Split into a new crate: `matrix-sdk-crypto-nodejs`.
Hywan May 24, 2022
3194ad1
feat(crypto-js): Implement `OlmMachine.get_verification`.
Hywan May 24, 2022
2a4dce3
Merge branch 'main' into feat-crypto-wasm
Hywan May 31, 2022
b6a6378
chore(crypto-js): Fix `cargo fmt`.
Hywan May 30, 2022
51488b4
doc(crypto-js) Add missing documentation.
Hywan May 30, 2022
bef1dfb
chore(crypto-js): Fix typos.
Hywan May 30, 2022
6afeeea
test(ci): Do not compile `matrix-sdk-crypto` to Wasm, but `-crypto-js…
Hywan May 30, 2022
efe5ea6
test(ci): Exclude `matrix-sdk-crypto-(js|nodejs)` from code coverage.
Hywan May 30, 2022
3f8e3b6
test(xtask) Replace `WasmFeatureSet::MatrixSdkCrypto` by `*Js`.
Hywan May 30, 2022
0335fdd
doc(crypto): Add missing module documentation.
Hywan May 30, 2022
7fa89f7
chore(crypto-js): Update `vodozemac`'s version to match `matrix-sdk-c…
Hywan May 30, 2022
6ff5c8e
chore(crypto-js): Thanks Clippy.
Hywan May 30, 2022
bb8217d
doc(crypto-nodejs) Disable missing docs for now.
Hywan May 30, 2022
0debbf2
chore(crypto-js): Remove an unknown Clippy lint for now.
Hywan May 30, 2022
4db1ad3
feat(crypto-nodejs): Derive `Debug` for `UserId`.
Hywan May 30, 2022
7931c4a
chore(crypto-js): Clean up code and make CI happy.
Hywan May 30, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: tarpaulin
args: --ignore-config --exclude-files "crates/matrix-sdk/examples/*,crates/matrix-sdk-common,crates/matrix-sdk-test" --out Xml
args: --workspace --ignore-config --exclude-files "crates/matrix-sdk/examples/*,crates/matrix-sdk-common,crates/matrix-sdk-test" --exclude matrix-sdk-crypto-js --exclude matrix-sdk-crypto-nodejs --out Xml

- name: Upload to codecov.io
uses: codecov/codecov-action@v3
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
RUSTDOCFLAGS: "--enable-index-page -Zunstable-options --cfg docsrs -Dwarnings"
with:
command: doc
args: --no-deps --workspace --features docsrs -Zrustdoc-map
args: --no-deps --workspace --exclude matrix-sdk-crypto-js --exclude matrix-sdk-crypto-nodejs --features docsrs -Zrustdoc-map

- name: Deploy docs
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/wasm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- matrix-sdk-qrcode
- matrix-sdk-base
- matrix-sdk-common
- matrix-sdk-crypto
- matrix-sdk-crypto-js
- indexeddb-no-crypto
- indexeddb-with-crypto

Expand All @@ -54,7 +54,7 @@ jobs:
profile: minimal
override: true

- name: Install WasmPack
- name: Install wasm-pack
uses: jetli/[email protected]
with:
version: 'latest'
Expand Down
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
members = ["benchmarks", "crates/*", "labs/*", "xtask"]
# xtask and labs should only be compiled when invoked explicitly
default-members = ["benchmarks", "crates/*"]

resolver = "2"

[profile.release]
lto = true
Hywan marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions crates/matrix-sdk-crypto-js/.cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
target = "wasm32-unknown-unknown"
38 changes: 38 additions & 0 deletions crates/matrix-sdk-crypto-js/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[package]
authors = ["Ivan Enderlin <[email protected]>"]
description = "Matrix encryption library, for JavaScript"
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-js"
readme = "README.md"
repository = "https://github.com/matrix-org/matrix-rust-sdk"
rust-version = "1.60"
version = "0.5.0"

[package.metadata.docs.rs]
features = ["docsrs"]
rustdoc-args = ["--cfg", "docsrs"]

[package.metadata.wasm-pack.profile.release]
wasm-opt = ['-Oz']

[lib]
crate-type = ["cdylib"]

[features]
default = []
qrcode = ["matrix-sdk-crypto/qrcode"]
docsrs = []

[dependencies]
matrix-sdk-crypto = { version = "0.5.0", path = "../matrix-sdk-crypto" }
ruma = { version = "0.6.2", features = ["client-api-c", "js", "rand", "unstable-msc2676", "unstable-msc2677"] }
vodozemac = { git = "https://github.com/matrix-org/vodozemac/", rev = "d0e744287a14319c2a9148fef3747548c740fc36", features = ["js"] }
wasm-bindgen = "0.2.80"
wasm-bindgen-futures = "0.4.30"
js-sys = "0.3.49"
serde_json = "1.0.79"
http = "0.2.6"
anyhow = "1.0"
Empty file.
5 changes: 5 additions & 0 deletions crates/matrix-sdk-crypto-js/js/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
build:
RUSTFLAGS='-C opt-level=z' wasm-pack build --release --target nodejs --out-name matrix_sdk_crypto --out-dir ./js/pkg ../
poljar marked this conversation as resolved.
Show resolved Hide resolved

test:
node --test ../tests/js/**.js
61 changes: 61 additions & 0 deletions crates/matrix-sdk-crypto-js/src/events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//! Types related to events.

use ruma::events::room::history_visibility::HistoryVisibility as RumaHistoryVisibility;
use wasm_bindgen::prelude::*;

/// Who can see a room's history.
#[wasm_bindgen]
#[derive(Debug, Clone)]
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"),
Hywan marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
26 changes: 26 additions & 0 deletions crates/matrix-sdk-crypto-js/src/future.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::future::Future;

use js_sys::Promise;
use wasm_bindgen::{JsValue, UnwrapThrowExt};
use wasm_bindgen_futures::spawn_local;

pub(crate) fn future_to_promise<F, T>(future: F) -> Promise
where
F: Future<Output = Result<T, anyhow::Error>> + 'static,
T: Into<JsValue>,
{
let mut future = Some(future);

Promise::new(&mut |resolve, reject| {
let future = future.take().unwrap_throw();

spawn_local(async move {
match future.await {
Ok(value) => resolve.call1(&JsValue::UNDEFINED, &value.into()).unwrap_throw(),
Err(value) => {
reject.call1(&JsValue::UNDEFINED, &value.to_string().into()).unwrap_throw()
}
};
});
})
}
171 changes: 171 additions & 0 deletions crates/matrix-sdk-crypto-js/src/identifiers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
//! Types for [Matrix](https://matrix.org/) identifiers for devices,
//! events, keys, rooms, servers, users and URIs.

use wasm_bindgen::prelude::*;

/// A Matrix [user ID].
///
/// [user ID]: https://spec.matrix.org/v1.2/appendices/#user-identifiers
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub struct UserId {
pub(crate) inner: ruma::OwnedUserId,
}

impl UserId {
pub(crate) fn new_with(inner: ruma::OwnedUserId) -> Self {
Self { inner }
}
}

#[wasm_bindgen]
impl UserId {
/// Parse/validate and create a new `UserId`.
#[wasm_bindgen(constructor)]
pub fn new(id: &str) -> Result<UserId, JsError> {
Ok(Self::new_with(ruma::UserId::parse(id)?))
}

/// Returns the user's localpart.
pub fn localpart(&self) -> String {
self.inner.localpart().to_owned()
}

/// Returns the server name of the user ID.
#[wasm_bindgen(js_name = "serverName")]
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.
#[wasm_bindgen(getter, js_name = "isHistorical")]
pub fn is_historical(&self) -> bool {
self.inner.is_historical()
}

/// Return the user ID as a string.
#[wasm_bindgen(js_name = "toString")]
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
self.inner.as_str().to_owned()
}
}

/// A Matrix key ID.
///
/// Device identifiers in Matrix are completely opaque character
/// sequences. This type is provided simply for its semantic value.
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub struct DeviceId {
pub(crate) inner: ruma::OwnedDeviceId,
}

impl DeviceId {
pub(crate) fn new_with(inner: ruma::OwnedDeviceId) -> Self {
Self { inner }
}
}

#[wasm_bindgen]
impl DeviceId {
/// Create a new `DeviceId`.
#[wasm_bindgen(constructor)]
pub fn new(id: &str) -> DeviceId {
Self::new_with(id.into())
}

/// Return the device ID as a string.
#[wasm_bindgen(js_name = "toString")]
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
self.inner.as_str().to_owned()
}
}

/// A Matrix [room ID].
///
/// [room ID]: https://spec.matrix.org/v1.2/appendices/#room-ids-and-event-ids
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub struct RoomId {
pub(crate) inner: ruma::OwnedRoomId,
}

impl RoomId {
pub(crate) fn new_with(inner: ruma::OwnedRoomId) -> Self {
Self { inner }
}
}

#[wasm_bindgen]
impl RoomId {
/// Parse/validate and create a new `RoomId`.
#[wasm_bindgen(constructor)]
pub fn new(id: &str) -> Result<RoomId, JsError> {
Ok(Self::new_with(ruma::RoomId::parse(id)?))
}

/// Returns the user's localpart.
pub fn localpart(&self) -> String {
self.inner.localpart().to_owned()
}

/// Returns the server name of the room ID.
#[wasm_bindgen(js_name = "serverName")]
pub fn server_name(&self) -> ServerName {
ServerName { inner: self.inner.server_name().to_owned() }
}

/// Return the room ID as a string.
#[wasm_bindgen(js_name = "toString")]
#[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
#[wasm_bindgen]
#[derive(Debug)]
pub struct ServerName {
Hywan marked this conversation as resolved.
Show resolved Hide resolved
inner: ruma::OwnedServerName,
}

#[wasm_bindgen]
impl ServerName {
/// Parse/validate and create a new `ServerName`.
#[wasm_bindgen(constructor)]
pub fn new(name: &str) -> Result<ServerName, JsError> {
Ok(Self { inner: ruma::ServerName::parse(name)? })
}

/// 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.
pub fn host(&self) -> String {
self.inner.host().to_owned()
}

/// Returns the port of the server name if any.
pub fn port(&self) -> Option<u16> {
self.inner.port()
}

/// Returns true if and only if the server name is an IPv4 or IPv6
/// address.
#[wasm_bindgen(js_name = "isIpLiteral")]
pub fn is_ip_literal(&self) -> bool {
self.inner.is_ip_literal()
}
}
Loading