Skip to content

Commit

Permalink
Add support for sending encrypted to_device
Browse files Browse the repository at this point in the history
  • Loading branch information
BillCarsonFr committed Feb 9, 2024
1 parent 595ad2b commit e09ecc3
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# UNRELEASED

- Adds a new API `Device#encryptToDeviceEvent` to encrypt an event using
olm.

# matrix-sdk-crypto-wasm v4.4.0

- Update dependencies, including matrix-rust-sdk to
Expand Down
37 changes: 36 additions & 1 deletion src/device.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
//! Types for a `Device`.

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

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

/// A device represents a E2EE capable client of an user.
Expand Down Expand Up @@ -51,6 +54,38 @@ impl Device {
}))
}

/// Encrypt an event to be sent to this device.
/// (olm encryption).
///
/// Prior to calling this method you must ensure that an olm session is
/// available for the target device. This can be done by calling the
/// [`get_missing_sessions()`](OlmMachine::get_missing_sessions)
///
/// The caller is responsible for sending the encrypted
/// event to the target device. If multiple messages are
/// encrypted using this method they should be sent in the same order as
/// they are encrypted.
///
/// # Returns
/// Returns a Promise for a `json` string of the encrypted event.
/// Can be used to create the payload for a `/sendToDevice` API.
#[wasm_bindgen(js_name = "encryptToDeviceEvent")]
pub fn encrypt_to_device_event(
&self,
event_type: String,
content: JsValue,
) -> Result<Promise, JsError> {
let me = self.inner.clone();
let content: Value = serde_wasm_bindgen::from_value(content)?;
let event_type = event_type.clone();

Ok(future_to_promise(async move {
let raw_encrypted = me.encrypt_event_raw(event_type.as_str(), &content).await?;

Ok(raw_encrypted.into_json().to_string())
}))
}

/// Is this device considered to be verified.
///
/// This method returns true if either the `is_locally_trusted`
Expand Down
134 changes: 134 additions & 0 deletions tests/device.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,140 @@ describe(OlmMachine.name, () => {
});
});

describe("Send to device", () => {
const userId1 = new UserId("@alice:example.org");
const deviceId1 = new DeviceId("alice_device");

const userId2 = new UserId("@bob:example.org");
const deviceId2 = new DeviceId("bob_device");

function machine(newUser, newDevice) {
return OlmMachine.initialize(newUser || userId1, newDevice || deviceId1);
}

it("can send a to-device message", async () => {
// Olm machine.
const m = await machine(userId1, deviceId1);

// Make m aware of another device, and get some OTK to be able to establish a session.
{
// derived from https://github.com/matrix-org/matrix-rust-sdk/blob/7f49618d350fab66b7e1dc4eaf64ec25ceafd658/benchmarks/benches/crypto_bench/keys_query.json
const hypotheticalResponse = JSON.stringify({
device_keys: {
"@example:localhost": {
AFGUOBTZWM: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "AFGUOBTZWM",
keys: {
"curve25519:AFGUOBTZWM": "boYjDpaC+7NkECQEeMh5dC+I1+AfriX0VXG2UV7EUQo",
"ed25519:AFGUOBTZWM": "NayrMQ33ObqMRqz6R9GosmHdT6HQ6b/RX/3QlZ2yiec",
},
signatures: {
"@example:localhost": {
"ed25519:AFGUOBTZWM":
"RoSWvru1jj6fs2arnTedWsyIyBmKHMdOu7r9gDi0BZ61h9SbCK2zLXzuJ9ZFLao2VvA0yEd7CASCmDHDLYpXCA",
},
},
user_id: "@example:localhost",
unsigned: {
device_display_name: "rust-sdk",
},
},
},
},
failures: {},
master_keys: {
"@example:localhost": {
user_id: "@example:localhost",
usage: ["master"],
keys: {
"ed25519:n2lpJGx0LiKnuNE1IucZP3QExrD4SeRP0veBHPe3XUU":
"n2lpJGx0LiKnuNE1IucZP3QExrD4SeRP0veBHPe3XUU",
},
signatures: {
"@example:localhost": {
"ed25519:TCSJXPWGVS":
"+j9G3L41I1fe0++wwusTTQvbboYW0yDtRWUEujhwZz4MAltjLSfJvY0hxhnz+wHHmuEXvQDen39XOpr1p29sAg",
},
},
},
},
self_signing_keys: {
"@example:localhost": {
user_id: "@example:localhost",
usage: ["self_signing"],
keys: {
"ed25519:kQXOuy639Yt47mvNTdrIluoC6DMvfbZLYbxAmwiDyhI":
"kQXOuy639Yt47mvNTdrIluoC6DMvfbZLYbxAmwiDyhI",
},
signatures: {
"@example:localhost": {
"ed25519:n2lpJGx0LiKnuNE1IucZP3QExrD4SeRP0veBHPe3XUU":
"q32ifix/qyRpvmegw2BEJklwoBCAJldDNkcX+fp+lBA4Rpyqtycxge6BA4hcJdxYsy3oV0IHRuugS8rJMMFyAA",
},
},
},
},
user_signing_keys: {
"@example:localhost": {
user_id: "@example:localhost",
usage: ["user_signing"],
keys: {
"ed25519:g4ED07Fnqf3GzVWNN1pZ0IFrPQVdqQf+PYoJNH4eE0s":
"g4ED07Fnqf3GzVWNN1pZ0IFrPQVdqQf+PYoJNH4eE0s",
},
signatures: {
"@example:localhost": {
"ed25519:n2lpJGx0LiKnuNE1IucZP3QExrD4SeRP0veBHPe3XUU":
"nKQu8alQKDefNbZz9luYPcNj+Z+ouQSot4fU/A23ELl1xrI06QVBku/SmDx0sIW1ytso0Cqwy1a+3PzCa1XABg",
},
},
},
},
});
const marked = await m.markRequestAsSent("foo", RequestType.KeysQuery, hypotheticalResponse);
}

{
// derived from https://github.com/matrix-org/matrix-rust-sdk/blob/7f49618d350fab66b7e1dc4eaf64ec25ceafd658/benchmarks/benches/crypto_bench/keys_claim.json
const hypotheticalResponse = JSON.stringify({
one_time_keys: {
"@example:localhost": {
AFGUOBTZWM: {
"signed_curve25519:AAAABQ": {
key: "9IGouMnkB6c6HOd4xUsNv4i3Dulb4IS96TzDordzOws",
signatures: {
"@example:localhost": {
"ed25519:AFGUOBTZWM":
"2bvUbbmJegrV0eVP/vcJKuIWC3kud+V8+C0dZtg4dVovOSJdTP/iF36tQn2bh5+rb9xLlSeztXBdhy4c+LiOAg",
},
},
},
},
},
},
failures: {},
});
const marked = await m.markRequestAsSent("bar", RequestType.KeysClaim, hypotheticalResponse);
}

// Pick the device we want to encrypt to.
const device2 = await m.getDevice(new UserId("@example:localhost"), new DeviceId("AFGUOBTZWM"));

const content = {
body: "Hello, World!",
};
const type = "some.custon.event.type";

const toDevice = await device2.encryptToDeviceEvent(type, content);

const olmContent = JSON.parse(toDevice);
expect(olmContent.algorithm).toStrictEqual("m.olm.v1.curve25519-aes-sha2");
expect(olmContent.ciphertext).toBeDefined();
expect(olmContent.ciphertext["boYjDpaC+7NkECQEeMh5dC+I1+AfriX0VXG2UV7EUQo"]).toBeDefined();
});
});

describe("Key Verification", () => {
const userId1 = new UserId("@alice:example.org");
const deviceId1 = new DeviceId("alice_device");
Expand Down

0 comments on commit e09ecc3

Please sign in to comment.