feat(crypto-js): Implement OlmMachine.decryptRoomEvent & siblings.

This commit is contained in:
Ivan Enderlin
2022-06-14 16:16:54 +02:00
parent 56d74e25b8
commit f8cd2310be
8 changed files with 161 additions and 24 deletions
+1
View File
@@ -28,6 +28,7 @@ docsrs = []
[dependencies]
matrix-sdk-crypto = { version = "0.5.0", path = "../matrix-sdk-crypto" }
matrix-sdk-common = { version = "0.5.0", path = "../matrix-sdk-common" }
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"
@@ -96,3 +96,29 @@ impl From<ruma::EventEncryptionAlgorithm> for EncryptionAlgorithm {
}
}
}
/// The verification state of the device that sent an event to us.
#[wasm_bindgen]
#[derive(Debug)]
pub enum VerificationState {
/// The device is trusted.
Trusted,
/// The device is not trusted.
Untrusted,
/// The device is not known to us.
UnknownDevice,
}
impl From<&matrix_sdk_common::deserialized_responses::VerificationState> for VerificationState {
fn from(value: &matrix_sdk_common::deserialized_responses::VerificationState) -> Self {
use matrix_sdk_common::deserialized_responses::VerificationState::*;
match value {
Trusted => Self::Trusted,
Untrusted => Self::Untrusted,
UnknownDevice => Self::UnknownDevice,
}
}
}
@@ -12,8 +12,8 @@ pub struct UserId {
pub(crate) inner: ruma::OwnedUserId,
}
impl UserId {
pub(crate) fn new_with(inner: ruma::OwnedUserId) -> Self {
impl From<ruma::OwnedUserId> for UserId {
fn from(inner: ruma::OwnedUserId) -> Self {
Self { inner }
}
}
@@ -23,7 +23,7 @@ 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)?))
Ok(Self::from(ruma::UserId::parse(id)?))
}
/// Returns the user's localpart.
@@ -66,8 +66,8 @@ pub struct DeviceId {
pub(crate) inner: ruma::OwnedDeviceId,
}
impl DeviceId {
pub(crate) fn new_with(inner: ruma::OwnedDeviceId) -> Self {
impl From<ruma::OwnedDeviceId> for DeviceId {
fn from(inner: ruma::OwnedDeviceId) -> Self {
Self { inner }
}
}
@@ -77,7 +77,7 @@ impl DeviceId {
/// Create a new `DeviceId`.
#[wasm_bindgen(constructor)]
pub fn new(id: &str) -> DeviceId {
Self::new_with(id.into())
Self::from(ruma::OwnedDeviceId::from(id))
}
/// Return the device ID as a string.
@@ -97,8 +97,8 @@ pub struct RoomId {
pub(crate) inner: ruma::OwnedRoomId,
}
impl RoomId {
pub(crate) fn new_with(inner: ruma::OwnedRoomId) -> Self {
impl From<ruma::OwnedRoomId> for RoomId {
fn from(inner: ruma::OwnedRoomId) -> Self {
Self { inner }
}
}
@@ -108,7 +108,7 @@ 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)?))
Ok(Self::from(ruma::RoomId::parse(id)?))
}
/// Returns the user's localpart.
+32 -8
View File
@@ -3,7 +3,10 @@
use std::collections::BTreeMap;
use js_sys::{Array, Map, Promise, Set};
use ruma::{DeviceKeyAlgorithm, OwnedTransactionId, UInt};
use ruma::{
events::room::encrypted::OriginalSyncRoomEncryptedEvent, DeviceKeyAlgorithm,
OwnedTransactionId, UInt,
};
use serde_json::Value as JsonValue;
use wasm_bindgen::prelude::*;
@@ -51,13 +54,13 @@ impl OlmMachine {
/// The unique user ID that owns this `OlmMachine` instance.
#[wasm_bindgen(getter, js_name = "userId")]
pub fn user_id(&self) -> identifiers::UserId {
identifiers::UserId::new_with(self.inner.user_id().to_owned())
identifiers::UserId::from(self.inner.user_id().to_owned())
}
/// The unique device ID that identifies this `OlmMachine`.
#[wasm_bindgen(getter, js_name = "deviceId")]
pub fn device_id(&self) -> identifiers::DeviceId {
identifiers::DeviceId::new_with(self.inner.device_id().to_owned())
identifiers::DeviceId::from(self.inner.device_id().to_owned())
}
/// Get the public parts of our Olm identity keys.
@@ -81,11 +84,9 @@ impl OlmMachine {
pub fn tracked_users(&self) -> Set {
let set = Set::new(&JsValue::UNDEFINED);
self.inner.tracked_users().into_iter().map(identifiers::UserId::new_with).for_each(
|user| {
set.add(&user.into());
},
);
self.inner.tracked_users().into_iter().map(identifiers::UserId::from).for_each(|user| {
set.add(&user.into());
});
set
}
@@ -261,6 +262,29 @@ impl OlmMachine {
}))
}
/// 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.
#[wasm_bindgen(js_name = "decryptRoomEvent")]
pub fn decrypt_room_event(
&self,
event: &str,
room_id: &identifiers::RoomId,
) -> Result<Promise, JsError> {
let event: OriginalSyncRoomEncryptedEvent = serde_json::from_str(event)?;
let room_id = room_id.inner.clone();
let me = self.inner.clone();
Ok(future_to_promise(async move {
let room_event = me.decrypt_room_event(&event, room_id.as_ref()).await?;
Ok(responses::DecryptedRoomEvent::from(room_event))
}))
}
/// Invalidate the currently active outbound group session for the
/// given room.
///
+81 -1
View File
@@ -1,5 +1,7 @@
//! Types related to responses.
use js_sys::{Array, JsString};
use matrix_sdk_common::deserialized_responses::{AlgorithmInfo, EncryptionInfo};
use matrix_sdk_crypto::IncomingResponse;
pub(crate) use ruma::api::client::{
backup::add_backup_keys::v3::Response as KeysBackupResponse,
@@ -14,7 +16,7 @@ pub(crate) use ruma::api::client::{
use ruma::api::IncomingResponse as RumaIncomingResponse;
use wasm_bindgen::prelude::*;
use crate::requests::RequestType;
use crate::{encryption, identifiers, 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())
@@ -126,3 +128,81 @@ impl<'a> From<&'a OwnedResponse> for IncomingResponse<'a> {
}
}
}
/// A decrypted room event.
#[wasm_bindgen(getter_with_clone)]
#[derive(Debug)]
pub struct DecryptedRoomEvent {
/// The JSON-encoded decrypted event.
#[wasm_bindgen(readonly)]
pub event: JsString,
encryption_info: Option<EncryptionInfo>,
}
#[wasm_bindgen]
impl DecryptedRoomEvent {
/// The user ID of the event sender, note this is untrusted data
/// unless the `verification_state` is as well trusted.
#[wasm_bindgen(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.
#[wasm_bindgen(getter, js_name = "senderDevice")]
pub fn sender_device(&self) -> Option<identifiers::DeviceId> {
Some(identifiers::DeviceId::from(self.encryption_info.as_ref()?.sender_device.clone()))
}
/// The Curve25519 key of the device that created the megolm
/// decryption key originally.
#[wasm_bindgen(getter, js_name = "senderCurve25519Key")]
pub fn sender_curve25519_key(&self) -> Option<JsString> {
Some(match &self.encryption_info.as_ref()?.algorithm_info {
AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, .. } => curve25519_key.clone().into(),
})
}
/// The signing Ed25519 key that have created the megolm key that
/// was used to decrypt this session.
#[wasm_bindgen(getter, js_name = "senderClaimedEd25519Key")]
pub fn sender_claimed_ed25519_key(&self) -> Option<JsString> {
match &self.encryption_info.as_ref()?.algorithm_info {
AlgorithmInfo::MegolmV1AesSha2 { sender_claimed_keys, .. } => {
sender_claimed_keys.get(&ruma::DeviceKeyAlgorithm::Ed25519).cloned().map(Into::into)
}
}
}
/// Chain of Curve25519 keys through which this session was
/// forwarded, via `m.forwarded_room_key` events.
#[wasm_bindgen(getter, js_name = "forwardingCurve25519KeyChain")]
pub fn forwarding_curve25519_key_chain(&self) -> Option<Array> {
Some(match &self.encryption_info.as_ref()?.algorithm_info {
AlgorithmInfo::MegolmV1AesSha2 { forwarding_curve25519_key_chain, .. } => {
forwarding_curve25519_key_chain.iter().map(JsValue::from).collect()
}
})
}
/// 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.
#[wasm_bindgen(getter, js_name = "verificationState")]
pub fn verification_state(&self) -> Option<encryption::VerificationState> {
Some((&self.encryption_info.as_ref()?.verification_state).into())
}
}
impl From<matrix_sdk_common::deserialized_responses::RoomEvent> for DecryptedRoomEvent {
fn from(value: matrix_sdk_common::deserialized_responses::RoomEvent) -> Self {
Self {
event: value.event.json().get().to_owned().into(),
encryption_info: value.encryption_info,
}
}
}
@@ -50,7 +50,7 @@ impl DeviceLists {
self.inner
.changed
.iter()
.map(|user| identifiers::UserId::new_with(user.clone()))
.map(|user| identifiers::UserId::from(user.clone()))
.map(JsValue::from)
.collect()
}
@@ -62,7 +62,7 @@ impl DeviceLists {
self.inner
.left
.iter()
.map(|user| identifiers::UserId::new_with(user.clone()))
.map(|user| identifiers::UserId::from(user.clone()))
.map(JsValue::from)
.collect()
}
@@ -1,4 +1,4 @@
const { EncryptionAlgorithm, EncryptionSettings, HistoryVisibility } = require('../pkg/matrix_sdk_crypto');
const { EncryptionAlgorithm, EncryptionSettings, HistoryVisibility, VerificationState } = require('../pkg/matrix_sdk_crypto');
describe('EncryptionAlgorithm', () => {
test('has the correct variant values', () => {
@@ -26,3 +26,11 @@ describe(EncryptionSettings.name, () => {
expect(() => { es.historyVisibility = 42 }).toThrow();
});
});
describe('VerificationState', () => {
test('has the correct variant values', () => {
expect(VerificationState.Trusted).toStrictEqual(0);
expect(VerificationState.Untrusted).toStrictEqual(1);
expect(VerificationState.UnknownDevice).toStrictEqual(2);
});
});
@@ -1,4 +1,4 @@
const { OlmMachine, UserId, DeviceId, RoomId, DeviceLists, RequestType, KeysUploadRequest, KeysQueryRequest, KeysClaimRequest, EncryptionSettings } = require('../pkg/matrix_sdk_crypto');
const { OlmMachine, UserId, DeviceId, RoomId, DeviceLists, RequestType, KeysUploadRequest, KeysQueryRequest, KeysClaimRequest, EncryptionSettings, DecryptedRoomEvent, VerificationState } = require('../pkg/matrix_sdk_crypto');
describe(OlmMachine.name, () => {
test('can be instantiated with the async initializer', async () => {
@@ -310,7 +310,6 @@ describe(OlmMachine.name, () => {
expect(encrypted.session_id).toBeDefined();
});
/*
test('can decrypt an event', async () => {
const decrypted = await m.decryptRoomEvent(
JSON.stringify({
@@ -338,6 +337,5 @@ describe(OlmMachine.name, () => {
expect(decrypted.forwardingCurve25519KeyChain).toHaveLength(0);
expect(decrypted.verificationState).toStrictEqual(VerificationState.Trusted);
});
*/
});
});