feat(crypto-js): Implement OlmMachine.decryptRoomEvent & siblings.
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
*/
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user