Skip to content

feat(crypto-js): Implement key verification #788

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

Merged
merged 37 commits into from
Sep 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
9155989
feat(crypto): Simplify code and add documentation.
Hywan Aug 29, 2022
53e21e0
feat(crypto-js): Start implementation key verification API.
Hywan Aug 29, 2022
b6f01b3
feat(crypto-js): Implement the `Device` and `UserDevice` API.
Hywan Aug 31, 2022
e00b922
feat(crypto-js): Implement `Device.request_verification`.
Hywan Aug 31, 2022
e659c72
chore(crypto-js): Some methods have been renamed.
Hywan Sep 1, 2022
155b187
test(crypto-js): Write first tests for key verification.
Hywan Sep 1, 2022
e6141d8
test(crypto-js): Continue to test `m.key.verification.request` and `.…
Hywan Sep 5, 2022
14f2297
feat(crypto-js): Implement `VerificationRequest.start_sas`.
Hywan Sep 5, 2022
95709bb
test(crypto-js): Test the `Sas` implementation.
Hywan Sep 5, 2022
b5a8103
feat(crypto-js): Implement `Sas.accept`.
Hywan Sep 7, 2022
6239d31
test(crypto-js): Test the `Emoji` and decimals implementations.
Hywan Sep 7, 2022
c471a6f
test(crypto-js): Split the Key Verification test case into a test suite.
Hywan Sep 7, 2022
8948333
test(crypto-js): Test until `m.key.verification.done` \o/.
Hywan Sep 7, 2022
71a2fac
test(crypto-js): Reorganize the tests a little bit.
Hywan Sep 7, 2022
e8331cc
feat(crypto-js): Enable the `qrcode` feature by default.
Hywan Sep 7, 2022
1be17d3
feat(crypto-js): Implement `OlmMachine.(export|import)_cross_signing_…
Hywan Sep 7, 2022
e367d85
feat(crypto-js): Implement `OlmMachine.bootstrap_cross_signing`.
Hywan Sep 8, 2022
bbfc076
test(crypto-js): Inject bootstrap cross signing keys when setting up …
Hywan Sep 8, 2022
0d57983
feat(crypto-js): Implement `VerificationRequest.generate_qr_code`.
Hywan Sep 8, 2022
c7e0b3e
test(crypto-js): Testing key verification workflow until QR code gene…
Hywan Sep 8, 2022
6a05f83
test(crypto-js): Test `Qr.toBytes`.
Hywan Sep 8, 2022
cbb5080
test(crypto-js): Properly test `Qr.toBytes`.
Hywan Sep 8, 2022
6f89d02
test(crypto-js): Test `QrCode.render_into_buffer`.
Hywan Sep 8, 2022
9834b67
feat(crypto-js): `QrCode.renderIntoBuffer` returns an `Uint8ClampedAr…
Hywan Sep 8, 2022
581c537
test(crypto-js): Properly test `Qr.toQrCode.
Hywan Sep 12, 2022
58ea598
feat(crypto-js): Implement `VerificationRequest.scan_qr_code`.
Hywan Sep 12, 2022
792b458
feat(crypto-js): Implement `Qr.reciprocate` and `.confirm_scanning`.
Hywan Sep 12, 2022
cb95c59
test(crypto-js): Test `m.key.verification.start` and `.done` for QR c…
Hywan Sep 12, 2022
1c50cee
feat(crypto-js): Implement `Sas` and `Qr` `cancel*` methods.
Hywan Sep 12, 2022
3b526ea
doc(crypto-js): Add missing documentation.
Hywan Sep 12, 2022
7edd6a1
doc(crypto-js): Fix module documentation.
Hywan Sep 12, 2022
2f35b2c
feat(crypto-js): `QrCodeScan` implements `Debug`.
Hywan Sep 12, 2022
1d9ac6e
chore(crypto-js): Fix typos.
Hywan Sep 12, 2022
8027a30
test(crypto-js): Remove dependency to `canvas`.
Hywan Sep 12, 2022
6842fb9
chore(crypto-js): Make Clippy happy.
Hywan Sep 14, 2022
4c4fcf9
chore(crypto-js): Inline `vodozemac` dependency.
Hywan Sep 14, 2022
9d2e0fe
doc(crypto-js): Fix typos.
Hywan Sep 15, 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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 5 additions & 7 deletions bindings/matrix-sdk-crypto-js/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,17 @@ wasm-opt = ['-Oz']
crate-type = ["cdylib"]

[features]
default = ["tracing"]
qrcode = ["matrix-sdk-crypto/qrcode"]
default = ["tracing", "qrcode"]
qrcode = ["matrix-sdk-crypto/qrcode", "dep:matrix-sdk-qrcode"]
tracing = []

[dependencies]
matrix-sdk-common = { version = "0.5.0", path = "../../crates/matrix-sdk-common" }
matrix-sdk-crypto = { version = "0.5.0", path = "../../crates/matrix-sdk-crypto" }
matrix-sdk-indexeddb = { version = "0.1.0", path = "../../crates/matrix-sdk-indexeddb" }
matrix-sdk-qrcode = { version = "0.3.0", path = "../../crates/matrix-sdk-qrcode", optional = true }
ruma = { version = "0.7.0", features = ["client-api-c", "js", "rand", "unstable-msc2676", "unstable-msc2677"] }
vodozemac = { version = "0.3.0", features = ["js"] }
wasm-bindgen = "0.2.80"
wasm-bindgen-futures = "0.4.30"
js-sys = "0.3.49"
Expand All @@ -39,8 +41,4 @@ http = "0.2.6"
anyhow = "1.0.58"
tracing = { version = "0.1.35", default-features = false, features = ["attributes"] }
tracing-subscriber = { version = "0.3.14", default-features = false, features = ["registry", "std"] }
zeroize = "1.3.0"

[dependencies.vodozemac]
version = "0.3.0"
features = ["js"]
zeroize = "1.3.0"
8 changes: 4 additions & 4 deletions bindings/matrix-sdk-crypto-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@
"pkg/matrix_sdk_crypto.d.ts"
],
"devDependencies": {
"wasm-pack": "^0.10.2",
"cross-env": "^7.0.3",
"fake-indexeddb": "^4.0",
"jest": "^28.1.0",
"typedoc": "^0.22.17",
"cross-env": "^7.0.3",
"yargs-parser": "~21.0.1",
"fake-indexeddb": "^4.0"
"wasm-pack": "^0.10.2",
"yargs-parser": "~21.0.1"
},
"engines": {
"node": ">= 10"
Expand Down
263 changes: 263 additions & 0 deletions bindings/matrix-sdk-crypto-js/src/device.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
//! Types for a `Device`.

use js_sys::{Array, Map, Promise};
use wasm_bindgen::prelude::*;

use crate::{
future::future_to_promise,
identifiers::{self, DeviceId, UserId},
types, verification, vodozemac,
};

/// A device represents a E2EE capable client of an user.
#[wasm_bindgen]
#[derive(Debug)]
pub struct Device {
pub(crate) inner: matrix_sdk_crypto::Device,
}

impl From<matrix_sdk_crypto::Device> for Device {
fn from(inner: matrix_sdk_crypto::Device) -> Self {
Self { inner }
}
}

#[wasm_bindgen]
impl Device {
/// Request an interactive verification with this device.
#[wasm_bindgen(js_name = "requestVerification")]
pub fn request_verification(&self, methods: Option<Array>) -> Result<Promise, JsError> {
let methods = methods
.map(|array| {
array
.iter()
.map(|method| {
verification::VerificationMethod::try_from(method).map(Into::into)
})
.collect::<Result<_, _>>()
})
.transpose()?;
let me = self.inner.clone();

Ok(future_to_promise(async move {
let tuple = Array::new();
let (verification_request, outgoing_verification_request) = match methods {
Some(methods) => me.request_verification_with_methods(methods).await,
None => me.request_verification().await,
};

tuple.set(0, verification::VerificationRequest::from(verification_request).into());
tuple.set(
1,
verification::OutgoingVerificationRequest::from(outgoing_verification_request)
.try_into()?,
);

Ok(tuple)
}))
}

/// Is this device considered to be verified.
///
/// This method returns true if either the `is_locally_trusted`
/// method returns `true` or if the `is_cross_signing_trusted`
/// method returns `true`.
#[wasm_bindgen(js_name = "isVerified")]
pub fn is_verified(&self) -> bool {
self.inner.is_verified()
}

/// Is this device considered to be verified using cross signing.
#[wasm_bindgen(js_name = "isCrossSigningTrusted")]
pub fn is_cross_signing_trusted(&self) -> bool {
self.inner.is_cross_signing_trusted()
}

/// Set the local trust state of the device to the given state.
///
/// This won’t affect any cross signing trust state, this only
/// sets a flag marking to have the given trust state.
///
/// `trust_state` represents the new trust state that should be
/// set for the device.
#[wasm_bindgen(js_name = "setLocalTrust")]
pub fn set_local_trust(&self, local_state: LocalTrust) -> Promise {
let me = self.inner.clone();

future_to_promise(async move {
me.set_local_trust(local_state.into()).await?;

Ok(JsValue::NULL)
})
}

/// The user ID of the device owner.
#[wasm_bindgen(getter, js_name = "userId")]
pub fn user_id(&self) -> UserId {
self.inner.user_id().to_owned().into()
}

/// The unique ID of the device.
#[wasm_bindgen(getter, js_name = "deviceId")]
pub fn device_id(&self) -> DeviceId {
self.inner.device_id().to_owned().into()
}

/// Get the human readable name of the device.
#[wasm_bindgen(getter, js_name = "displayName")]
pub fn display_name(&self) -> Option<String> {
self.inner.display_name().map(ToOwned::to_owned)
}

/// Get the key of the given key algorithm belonging to this device.
#[wasm_bindgen(js_name = "getKey")]
pub fn get_key(
&self,
algorithm: identifiers::DeviceKeyAlgorithmName,
) -> Result<Option<vodozemac::DeviceKey>, JsError> {
Ok(self.inner.get_key(algorithm.try_into()?).cloned().map(Into::into))
}

/// Get the Curve25519 key of the given device.
#[wasm_bindgen(getter, js_name = "curve25519Key")]
pub fn curve25519_key(&self) -> Option<vodozemac::Curve25519PublicKey> {
self.inner.curve25519_key().map(Into::into)
}

/// Get the Ed25519 key of the given device.
#[wasm_bindgen(getter, js_name = "ed25519Key")]
pub fn ed25519_key(&self) -> Option<vodozemac::Ed25519PublicKey> {
self.inner.ed25519_key().map(Into::into)
}

/// Get a map containing all the device keys.
#[wasm_bindgen(getter)]
pub fn keys(&self) -> Map {
let map = Map::new();

for (device_key_id, device_key) in self.inner.keys() {
map.set(
&identifiers::DeviceKeyId::from(device_key_id.clone()).into(),
&vodozemac::DeviceKey::from(device_key.clone()).into(),
);
}

map
}

/// Get a map containing all the device signatures.
#[wasm_bindgen(getter)]
pub fn signatures(&self) -> types::Signatures {
self.inner.signatures().clone().into()
}

/// Get the trust state of the device.
#[wasm_bindgen(getter, js_name = "localTrustState")]
pub fn local_trust_state(&self) -> LocalTrust {
self.inner.local_trust_state().into()
}

/// Is the device locally marked as trusted?
#[wasm_bindgen(js_name = "isLocallyTrusted")]
pub fn is_locally_trusted(&self) -> bool {
self.inner.is_locally_trusted()
}

/// Is the device locally marked as blacklisted?
///
/// Blacklisted devices won’t receive any group sessions.
#[wasm_bindgen(js_name = "isBlacklisted")]
pub fn is_blacklisted(&self) -> bool {
self.inner.is_blacklisted()
}

/// Is the device deleted?
#[wasm_bindgen(js_name = "isDeleted")]
pub fn is_deleted(&self) -> bool {
self.inner.is_deleted()
}
}

/// The local trust state of a device.
#[wasm_bindgen]
#[derive(Debug)]
pub enum LocalTrust {
/// The device has been verified and is trusted.
Verified,

/// The device been blacklisted from communicating.
BlackListed,

/// The trust state of the device is being ignored.
Ignored,

/// The trust state is unset.
Unset,
}

impl From<matrix_sdk_crypto::LocalTrust> for LocalTrust {
fn from(value: matrix_sdk_crypto::LocalTrust) -> Self {
use matrix_sdk_crypto::LocalTrust::*;

match value {
Verified => Self::Verified,
BlackListed => Self::BlackListed,
Ignored => Self::Ignored,
Unset => Self::Unset,
}
}
}

impl From<LocalTrust> for matrix_sdk_crypto::LocalTrust {
fn from(value: LocalTrust) -> Self {
use LocalTrust::*;

match value {
Verified => Self::Verified,
BlackListed => Self::BlackListed,
Ignored => Self::Ignored,
Unset => Self::Unset,
}
}
}

/// A read only view over all devices belonging to a user.
#[wasm_bindgen]
#[derive(Debug)]
pub struct UserDevices {
pub(crate) inner: matrix_sdk_crypto::UserDevices,
}

impl From<matrix_sdk_crypto::UserDevices> for UserDevices {
fn from(inner: matrix_sdk_crypto::UserDevices) -> Self {
Self { inner }
}
}

#[wasm_bindgen]
impl UserDevices {
/// Get the specific device with the given device ID.
pub fn get(&self, device_id: &DeviceId) -> Option<Device> {
self.inner.get(&device_id.inner).map(Into::into)
}

/// Returns true if there is at least one devices of this user
/// that is considered to be verified, false otherwise.
///
/// This won't consider your own device as verified, as your own
/// device is always implicitly verified.
#[wasm_bindgen(js_name = "isAnyVerified")]
pub fn is_any_verified(&self) -> bool {
self.inner.is_any_verified()
}

/// Array over all the device IDs of the user devices.
pub fn keys(&self) -> Array {
self.inner.keys().map(ToOwned::to_owned).map(DeviceId::from).map(JsValue::from).collect()
}

/// Iterator over all the devices of the user devices.
pub fn devices(&self) -> Array {
self.inner.devices().map(Device::from).map(JsValue::from).collect()
}
}
21 changes: 20 additions & 1 deletion bindings/matrix-sdk-crypto-js/src/identifiers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ impl DeviceKeyId {
#[wasm_bindgen]
#[derive(Debug)]
pub struct DeviceKeyAlgorithm {
inner: ruma::DeviceKeyAlgorithm,
pub(crate) inner: ruma::DeviceKeyAlgorithm,
}

impl From<ruma::DeviceKeyAlgorithm> for DeviceKeyAlgorithm {
Expand Down Expand Up @@ -180,6 +180,25 @@ pub enum DeviceKeyAlgorithmName {
Unknown,
}

impl TryFrom<DeviceKeyAlgorithmName> for ruma::DeviceKeyAlgorithm {
type Error = JsError;

fn try_from(value: DeviceKeyAlgorithmName) -> Result<Self, Self::Error> {
use DeviceKeyAlgorithmName::*;

Ok(match value {
Ed25519 => Self::Ed25519,
Curve25519 => Self::Curve25519,
SignedCurve25519 => Self::SignedCurve25519,
Unknown => {
return Err(JsError::new(
"The `DeviceKeyAlgorithmName.Unknown` variant cannot be converted",
))
}
})
}
}

impl From<ruma::DeviceKeyAlgorithm> for DeviceKeyAlgorithmName {
fn from(value: ruma::DeviceKeyAlgorithm) -> Self {
use ruma::DeviceKeyAlgorithm::*;
Expand Down
3 changes: 3 additions & 0 deletions bindings/matrix-sdk-crypto-js/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#![allow(clippy::drop_non_drop)] // triggered by wasm_bindgen code

pub mod attachment;
pub mod device;
pub mod encryption;
pub mod events;
mod future;
Expand All @@ -26,9 +27,11 @@ pub mod machine;
pub mod olm;
pub mod requests;
pub mod responses;
pub mod store;
pub mod sync_events;
mod tracing;
pub mod types;
pub mod verification;
pub mod vodozemac;

use js_sys::{Object, Reflect};
Expand Down
Loading