feat(crypto-js): Request types have fields extracted from their “body” directly

feat(crypto-js): Request types have fields extracted from their “body” directly
This commit is contained in:
Ivan Enderlin
2023-01-18 13:41:14 +01:00
committed by GitHub
8 changed files with 390 additions and 171 deletions

View File

@@ -130,7 +130,7 @@ impl UserIdentity {
}
/// Create a `VerificationRequest` object after the verification
/// request content has been sent out. }
/// request content has been sent out.
#[wasm_bindgen(js_name = "requestVerification")]
pub fn request_verification(
&self,

View File

@@ -638,8 +638,15 @@ impl OlmMachine {
/// This method can be used to pass verification events that are happening
/// in rooms to the `OlmMachine`. The event should be in the decrypted form.
#[wasm_bindgen(js_name = "receiveVerificationEvent")]
pub fn receive_verification_event(&self, event: &str) -> Result<Promise, JsError> {
let event: ruma::events::AnyMessageLikeEvent = serde_json::from_str(event)?;
pub fn receive_verification_event(
&self,
event: &str,
room_id: &identifiers::RoomId,
) -> Result<Promise, JsError> {
let room_id = room_id.inner.clone();
let event: ruma::events::AnySyncMessageLikeEvent = serde_json::from_str(event)?;
let event = event.into_full_event(room_id);
let me = self.inner.clone();
Ok(future_to_promise(async move {

View File

@@ -11,10 +11,13 @@ use matrix_sdk_crypto::{
},
OutgoingRequests,
};
use ruma::api::client::keys::{
claim_keys::v3::Request as OriginalKeysClaimRequest,
upload_keys::v3::Request as OriginalKeysUploadRequest,
upload_signatures::v3::Request as OriginalSignatureUploadRequest,
use ruma::{
api::client::keys::{
claim_keys::v3::Request as OriginalKeysClaimRequest,
upload_keys::v3::Request as OriginalKeysUploadRequest,
upload_signatures::v3::Request as OriginalSignatureUploadRequest,
},
events::EventContent,
};
use wasm_bindgen::prelude::*;
@@ -33,11 +36,10 @@ pub struct KeysUploadRequest {
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded object of form:
/// A JSON-encoded string containing the rest of the payload: `device_keys`,
/// `one_time_keys`, `fallback_keys`.
///
/// ```json
/// {"device_keys": …, "one_time_keys": …, "fallback_keys": …}
/// ```
/// It represents the body of the HTTP request.
#[wasm_bindgen(readonly)]
pub body: JsString,
}
@@ -70,11 +72,10 @@ pub struct KeysQueryRequest {
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded object of form:
/// A JSON-encoded string containing the rest of the payload: `timeout`,
/// `device_keys`, `token`.
///
/// ```json
/// {"timeout": …, "device_keys": …, "token": …}
/// ```
/// It represents the body of the HTTP request.
#[wasm_bindgen(readonly)]
pub body: JsString,
}
@@ -108,11 +109,10 @@ pub struct KeysClaimRequest {
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded object of form:
/// A JSON-encoded string containing the rest of the payload: `timeout`,
/// `one_time_keys`.
///
/// ```json
/// {"timeout": …, "one_time_keys": …}
/// ```
/// It represents the body of the HTTP request.
#[wasm_bindgen(readonly)]
pub body: JsString,
}
@@ -145,11 +145,18 @@ pub struct ToDeviceRequest {
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded object of form:
/// A string representing the type of event being sent to each devices.
#[wasm_bindgen(readonly)]
pub event_type: JsString,
/// A string representing a request identifier unique to the access token
/// used to send the request.
#[wasm_bindgen(readonly)]
pub txn_id: JsString,
/// A JSON-encoded string containing the rest of the payload: `messages`.
///
/// ```json
/// {"event_type": …, "txn_id": …, "messages": …}
/// ```
/// It represents the body of the HTTP request.
#[wasm_bindgen(readonly)]
pub body: JsString,
}
@@ -158,8 +165,13 @@ pub struct ToDeviceRequest {
impl ToDeviceRequest {
/// Create a new `ToDeviceRequest`.
#[wasm_bindgen(constructor)]
pub fn new(id: JsString, body: JsString) -> ToDeviceRequest {
Self { id: Some(id), body }
pub fn new(
id: JsString,
event_type: JsString,
txn_id: JsString,
body: JsString,
) -> ToDeviceRequest {
Self { id: Some(id), event_type, txn_id, body }
}
/// Get its request type.
@@ -182,11 +194,9 @@ pub struct SignatureUploadRequest {
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded object of form:
/// A JSON-encoded string containing the rest of the payload: `signed_keys`.
///
/// ```json
/// {"signed_keys": …, "txn_id": …, "messages": …}
/// ```
/// It represents the body of the HTTP request.
#[wasm_bindgen(readonly)]
pub body: JsString,
}
@@ -217,21 +227,39 @@ pub struct RoomMessageRequest {
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded object of form:
///
/// ```json
/// {"room_id": …, "txn_id": …, "content": …}
/// ```
/// A string representing the room to send the event to.
#[wasm_bindgen(readonly)]
pub body: JsString,
pub room_id: JsString,
/// A string representing the transaction ID for this event.
///
/// Clients should generate an ID unique across requests with the same
/// access token; it will be used by the server to ensure idempotency of
/// requests.
#[wasm_bindgen(readonly)]
pub txn_id: JsString,
/// A string representing the type of even from the message's content.
#[wasm_bindgen(readonly)]
pub event_type: JsString,
/// A JSON-encoded string containing the message's body.
#[wasm_bindgen(readonly, js_name = "body")]
pub content: JsString,
}
#[wasm_bindgen]
impl RoomMessageRequest {
/// Create a new `RoomMessageRequest`.
#[wasm_bindgen(constructor)]
pub fn new(id: JsString, body: JsString) -> RoomMessageRequest {
Self { id: Some(id), body }
pub fn new(
id: JsString,
room_id: JsString,
txn_id: JsString,
event_type: JsString,
content: JsString,
) -> RoomMessageRequest {
Self { id: Some(id), room_id, txn_id, event_type, content }
}
/// Get its request type.
@@ -252,32 +280,9 @@ pub struct KeysBackupRequest {
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded object of form:
/// A JSON-encoded string containing the rest of the payload: `rooms`.
///
/// ```json
/// {"rooms": …}
/// ```
#[wasm_bindgen(readonly)]
pub body: JsString,
}
/** Other Requests * */
/// Request that will publish a cross signing identity.
///
/// This uploads the public cross signing key triplet.
#[wasm_bindgen(getter_with_clone)]
#[derive(Debug)]
pub struct SigningKeysUploadRequest {
/// The request ID.
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded object of form:
///
/// ```json
/// {"master_key": …, "self_signing_key": …, "user_signing_key": …}
/// ```
/// It represents the body of the HTTP request.
#[wasm_bindgen(readonly)]
pub body: JsString,
}
@@ -297,28 +302,42 @@ impl KeysBackupRequest {
}
}
/** Other Requests * */
/// Request that will publish a cross signing identity.
///
/// This uploads the public cross signing key triplet.
#[wasm_bindgen(getter_with_clone)]
#[derive(Debug)]
pub struct SigningKeysUploadRequest {
/// The request ID.
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded string containing the rest of the payload: `master_key`,
/// `self_signing_key`, `user_signing_key`.
///
/// It represents the body of the HTTP request.
#[wasm_bindgen(readonly)]
pub body: JsString,
}
macro_rules! request {
($destination_request:ident from $source_request:ident maps fields $( $field:ident ),+ $(,)? ) => {
impl $destination_request {
pub(crate) fn to_json(request: &$source_request) -> Result<String, serde_json::Error> {
let mut map = serde_json::Map::new();
$(
map.insert(stringify!($field).to_owned(), serde_json::to_value(&request.$field).unwrap());
)+
let object = serde_json::Value::Object(map);
serde_json::to_string(&object)
}
}
(
$destination_request:ident from $source_request:ident
$( extracts $( $field_name:ident : $field_type:tt ),+ $(,)? )?
$( $( and )? groups $( $grouped_field_name:ident ),+ $(,)? )?
) => {
impl TryFrom<&$source_request> for $destination_request {
type Error = serde_json::Error;
fn try_from(request: &$source_request) -> Result<Self, Self::Error> {
Ok($destination_request {
id: None,
body: Self::to_json(request)?.into(),
})
request!(
@__try_from $destination_request from $source_request
(request_id = None, request = request)
$( extracts [ $( $field_name : $field_type, )+ ] )?
$( groups [ $( $grouped_field_name, )+ ] )?
)
}
}
@@ -328,26 +347,73 @@ macro_rules! request {
fn try_from(
(request_id, request): (String, &$source_request),
) -> Result<Self, Self::Error> {
Ok($destination_request {
id: Some(request_id.into()),
body: Self::to_json(request)?.into(),
})
request!(
@__try_from $destination_request from $source_request
(request_id = Some(request_id.into()), request = request)
$( extracts [ $( $field_name : $field_type, )+ ] )?
$( groups [ $( $grouped_field_name, )+ ] )?
)
}
}
};
(
@__try_from $destination_request:ident from $source_request:ident
(request_id = $request_id:expr, request = $request:expr)
$( extracts [ $( $field_name:ident : $field_type:tt ),* $(,)? ] )?
$( groups [ $( $grouped_field_name:ident ),* $(,)? ] )?
) => {
{
Ok($destination_request {
id: $request_id,
$(
$(
$field_name: request!(@__field $field_name : $field_type ; request = $request),
)*
)?
$(
body: {
let mut map = serde_json::Map::new();
$(
map.insert(stringify!($grouped_field_name).to_owned(), serde_json::to_value(&$request.$grouped_field_name).unwrap());
)*
let object = serde_json::Value::Object(map);
serde_json::to_string(&object)?.into()
}
)?
})
}
};
( @__field $field_name:ident : $field_type:ident ; request = $request:expr ) => {
request!(@__field_type as $field_type ; request = $request, field_name = $field_name)
};
( @__field_type as string ; request = $request:expr, field_name = $field_name:ident ) => {
$request.$field_name.to_string().into()
};
( @__field_type as json ; request = $request:expr, field_name = $field_name:ident ) => {
serde_json::to_string(&$request.$field_name)?.into()
};
( @__field_type as event_type ; request = $request:expr, field_name = $field_name:ident ) => {
$request.content.event_type().to_string().into()
};
}
// Outgoing Requests
request!(KeysUploadRequest from OriginalKeysUploadRequest maps fields device_keys, one_time_keys, fallback_keys);
request!(KeysQueryRequest from OriginalKeysQueryRequest maps fields timeout, device_keys, token);
request!(KeysClaimRequest from OriginalKeysClaimRequest maps fields timeout, one_time_keys);
request!(ToDeviceRequest from OriginalToDeviceRequest maps fields event_type, txn_id, messages);
request!(SignatureUploadRequest from OriginalSignatureUploadRequest maps fields signed_keys);
request!(RoomMessageRequest from OriginalRoomMessageRequest maps fields room_id, txn_id, content);
request!(KeysBackupRequest from OriginalKeysBackupRequest maps fields rooms);
request!(KeysUploadRequest from OriginalKeysUploadRequest groups device_keys, one_time_keys, fallback_keys);
request!(KeysQueryRequest from OriginalKeysQueryRequest groups timeout, device_keys, token);
request!(KeysClaimRequest from OriginalKeysClaimRequest groups timeout, one_time_keys);
request!(ToDeviceRequest from OriginalToDeviceRequest extracts event_type: string, txn_id: string and groups messages);
request!(SignatureUploadRequest from OriginalSignatureUploadRequest groups signed_keys);
request!(RoomMessageRequest from OriginalRoomMessageRequest extracts room_id: string, txn_id: string, event_type: event_type, content: json);
request!(KeysBackupRequest from OriginalKeysBackupRequest groups rooms);
// Other Requests
request!(SigningKeysUploadRequest from OriginalUploadSigningKeysRequest maps fields master_key, self_signing_key, user_signing_key);
request!(SigningKeysUploadRequest from OriginalUploadSigningKeysRequest groups master_key, self_signing_key, user_signing_key);
// JavaScript has no complex enums like Rust. To return structs of
// different types, we have no choice that hiding everything behind a

View File

@@ -6,7 +6,7 @@ describe(Attachment.name, () => {
const textDecoder = new TextDecoder();
let encryptedAttachment;
test('can encrypt data', () => {
encryptedAttachment = Attachment.encrypt(textEncoder.encode(originalData));

View File

@@ -172,14 +172,12 @@ describe('Key Verification', () => {
expect(verificationRequest1.isCancelled()).toStrictEqual(false);
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
outgoingVerificationRequest = JSON.parse(outgoingVerificationRequest.body);
expect(outgoingVerificationRequest.event_type).toStrictEqual('m.key.verification.request');
const toDeviceEvents = [{
sender: userId1.toString(),
type: outgoingVerificationRequest.event_type,
content: outgoingVerificationRequest.messages[userId2.toString()][deviceId2.toString()],
content: JSON.parse(outgoingVerificationRequest.body).messages[userId2.toString()][deviceId2.toString()],
}];
// Let's send the verification request to `m2`.
@@ -225,13 +223,12 @@ describe('Key Verification', () => {
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
// The request verification is ready.
outgoingVerificationRequest = JSON.parse(outgoingVerificationRequest.body);
expect(outgoingVerificationRequest.event_type).toStrictEqual('m.key.verification.ready');
const toDeviceEvents = [{
sender: userId2.toString(),
type: outgoingVerificationRequest.event_type,
content: outgoingVerificationRequest.messages[userId1.toString()][deviceId1.toString()],
content: JSON.parse(outgoingVerificationRequest.body).messages[userId1.toString()][deviceId1.toString()],
}];
// Let's send the verification ready to `m1`.
@@ -279,14 +276,12 @@ describe('Key Verification', () => {
expect(sas2.decimals()).toBeUndefined();
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
outgoingVerificationRequest = JSON.parse(outgoingVerificationRequest.body);
expect(outgoingVerificationRequest.event_type).toStrictEqual('m.key.verification.start');
const toDeviceEvents = [{
sender: userId2.toString(),
type: outgoingVerificationRequest.event_type,
content: outgoingVerificationRequest.messages[userId1.toString()][deviceId1.toString()],
content: JSON.parse(outgoingVerificationRequest.body).messages[userId1.toString()][deviceId1.toString()],
}];
// Let's send the SAS start to `m1`.
@@ -324,15 +319,14 @@ describe('Key Verification', () => {
// Let's accept thet SAS start request.
let outgoingVerificationRequest = sas1.accept();
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
outgoingVerificationRequest = JSON.parse(outgoingVerificationRequest.body);
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
expect(outgoingVerificationRequest.event_type).toStrictEqual('m.key.verification.accept');
const toDeviceEvents = [{
sender: userId1.toString(),
type: outgoingVerificationRequest.event_type,
content: outgoingVerificationRequest.messages[userId2.toString()][deviceId2.toString()],
content: JSON.parse(outgoingVerificationRequest.body).messages[userId2.toString()][deviceId2.toString()],
}];
// Let's send the SAS accept to `m2`.
@@ -350,22 +344,18 @@ describe('Key Verification', () => {
let toDeviceRequest = outgoingRequests.find((request) => request.type == RequestType.ToDevice);
expect(toDeviceRequest).toBeInstanceOf(ToDeviceRequest);
const toDeviceRequestId = toDeviceRequest.id;
const toDeviceRequestType = toDeviceRequest.type;
toDeviceRequest = JSON.parse(toDeviceRequest.body);
expect(toDeviceRequest.event_type).toStrictEqual('m.key.verification.key');
const toDeviceEvents = [{
sender: userId2.toString(),
type: toDeviceRequest.event_type,
content: toDeviceRequest.messages[userId1.toString()][deviceId1.toString()],
content: JSON.parse(toDeviceRequest.body).messages[userId1.toString()][deviceId1.toString()],
}];
// Let's send te SAS key to `m1`.
await m1.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
m2.markRequestAsSent(toDeviceRequestId, toDeviceRequestType, '{}');
m2.markRequestAsSent(toDeviceRequest.id, toDeviceRequest.type, '{}');
});
test('other side sends back verification key (`m.key.verification.key`)', async () => {
@@ -374,22 +364,18 @@ describe('Key Verification', () => {
let toDeviceRequest = outgoingRequests.find((request) => request.type == RequestType.ToDevice);
expect(toDeviceRequest).toBeInstanceOf(ToDeviceRequest);
const toDeviceRequestId = toDeviceRequest.id;
const toDeviceRequestType = toDeviceRequest.type;
toDeviceRequest = JSON.parse(toDeviceRequest.body);
expect(toDeviceRequest.event_type).toStrictEqual('m.key.verification.key');
const toDeviceEvents = [{
sender: userId1.toString(),
type: toDeviceRequest.event_type,
content: toDeviceRequest.messages[userId2.toString()][deviceId2.toString()],
content: JSON.parse(toDeviceRequest.body).messages[userId2.toString()][deviceId2.toString()],
}];
// Let's send te SAS key to `m2`.
await m2.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
m1.markRequestAsSent(toDeviceRequestId, toDeviceRequestType, '{}');
m1.markRequestAsSent(toDeviceRequest.id, toDeviceRequest.type, '{}');
});
test('emojis match from both sides', () => {
@@ -447,14 +433,12 @@ describe('Key Verification', () => {
let outgoingVerificationRequest = outgoingVerificationRequests[0];
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
outgoingVerificationRequest = JSON.parse(outgoingVerificationRequest.body);
expect(outgoingVerificationRequest.event_type).toStrictEqual('m.key.verification.mac');
const toDeviceEvents = [{
sender: userId1.toString(),
type: outgoingVerificationRequest.event_type,
content: outgoingVerificationRequest.messages[userId2.toString()][deviceId2.toString()],
content: JSON.parse(outgoingVerificationRequest.body).messages[userId2.toString()][deviceId2.toString()],
}];
// Let's send te SAS confirmation to `m2`.
@@ -473,14 +457,12 @@ describe('Key Verification', () => {
let outgoingVerificationRequest = outgoingVerificationRequests[0];
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
outgoingVerificationRequest = JSON.parse(outgoingVerificationRequest.body);
expect(outgoingVerificationRequest.event_type).toStrictEqual('m.key.verification.mac');
const toDeviceEvents = [{
sender: userId2.toString(),
type: outgoingVerificationRequest.event_type,
content: outgoingVerificationRequest.messages[userId1.toString()][deviceId1.toString()],
content: JSON.parse(outgoingVerificationRequest.body).messages[userId1.toString()][deviceId1.toString()],
}];
// Let's send te SAS confirmation to `m1`.
@@ -492,14 +474,12 @@ describe('Key Verification', () => {
let outgoingVerificationRequest = outgoingVerificationRequests[1];
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
outgoingVerificationRequest = JSON.parse(outgoingVerificationRequest.body);
expect(outgoingVerificationRequest.event_type).toStrictEqual('m.key.verification.done');
const toDeviceEvents = [{
sender: userId2.toString(),
type: outgoingVerificationRequest.event_type,
content: outgoingVerificationRequest.messages[userId1.toString()][deviceId1.toString()],
content: JSON.parse(outgoingVerificationRequest.body).messages[userId1.toString()][deviceId1.toString()],
}];
// Let's send te SAS done to `m1`.
@@ -514,22 +494,18 @@ describe('Key Verification', () => {
let toDeviceRequest = outgoingRequests.find((request) => request.type == RequestType.ToDevice);
expect(toDeviceRequest).toBeInstanceOf(ToDeviceRequest);
const toDeviceRequestId = toDeviceRequest.id;
const toDeviceRequestType = toDeviceRequest.type;
toDeviceRequest = JSON.parse(toDeviceRequest.body);
expect(toDeviceRequest.event_type).toStrictEqual('m.key.verification.done');
const toDeviceEvents = [{
sender: userId1.toString(),
type: toDeviceRequest.event_type,
content: toDeviceRequest.messages[userId2.toString()][deviceId2.toString()],
content: JSON.parse(toDeviceRequest.body).messages[userId2.toString()][deviceId2.toString()],
}];
// Let's send te SAS key to `m2`.
await m2.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
m1.markRequestAsSent(toDeviceRequestId, toDeviceRequestType, '{}');
m1.markRequestAsSent(toDeviceRequest.id, toDeviceRequest.type, '{}');
});
test('can see if verification is done', () => {
@@ -604,14 +580,12 @@ describe('Key Verification', () => {
expect(verificationRequest1.isCancelled()).toStrictEqual(false);
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
outgoingVerificationRequest = JSON.parse(outgoingVerificationRequest.body);
expect(outgoingVerificationRequest.event_type).toStrictEqual('m.key.verification.request');
const toDeviceEvents = [{
sender: userId1.toString(),
type: outgoingVerificationRequest.event_type,
content: outgoingVerificationRequest.messages[userId2.toString()][deviceId2.toString()],
content: JSON.parse(outgoingVerificationRequest.body).messages[userId2.toString()][deviceId2.toString()],
}];
// Let's send the verification request to `m2`.
@@ -660,13 +634,12 @@ describe('Key Verification', () => {
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
// The request verification is ready.
outgoingVerificationRequest = JSON.parse(outgoingVerificationRequest.body);
expect(outgoingVerificationRequest.event_type).toStrictEqual('m.key.verification.ready');
const toDeviceEvents = [{
sender: userId2.toString(),
type: outgoingVerificationRequest.event_type,
content: outgoingVerificationRequest.messages[userId1.toString()][deviceId1.toString()],
content: JSON.parse(outgoingVerificationRequest.body).messages[userId1.toString()][deviceId1.toString()],
}];
// Let's send the verification ready to `m1`.
@@ -818,14 +791,12 @@ describe('Key Verification', () => {
let outgoingVerificationRequest = qr1.reciprocate();
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
outgoingVerificationRequest = JSON.parse(outgoingVerificationRequest.body);
expect(outgoingVerificationRequest.event_type).toStrictEqual('m.key.verification.start');
const toDeviceEvents = [{
sender: userId1.toString(),
type: outgoingVerificationRequest.event_type,
content: outgoingVerificationRequest.messages[userId2.toString()][deviceId2.toString()],
content: JSON.parse(outgoingVerificationRequest.body).messages[userId2.toString()][deviceId2.toString()],
}];
// Let's send the verification request to `m2`.
@@ -840,14 +811,12 @@ describe('Key Verification', () => {
let outgoingVerificationRequest = qr2.confirmScanning();
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
outgoingVerificationRequest = JSON.parse(outgoingVerificationRequest.body);
expect(outgoingVerificationRequest.event_type).toStrictEqual('m.key.verification.done');
const toDeviceEvents = [{
sender: userId2.toString(),
type: outgoingVerificationRequest.event_type,
content: outgoingVerificationRequest.messages[userId1.toString()][deviceId1.toString()],
content: JSON.parse(outgoingVerificationRequest.body).messages[userId1.toString()][deviceId1.toString()],
}];
// Let's send the verification request to `m2`.

View File

@@ -30,6 +30,7 @@ async function addMachineToMachine(machineToAdd, machine) {
expect(outgoingRequests[0]).toBeInstanceOf(KeysUploadRequest);
expect(outgoingRequests[0].id).toBeDefined();
expect(outgoingRequests[0].type).toStrictEqual(RequestType.KeysUpload);
expect(outgoingRequests[0].body).toBeDefined();
const body = JSON.parse(outgoingRequests[0].body);
expect(body.device_keys).toBeDefined();
@@ -45,14 +46,13 @@ async function addMachineToMachine(machineToAdd, machine) {
const marked = await machineToAdd.markRequestAsSent(outgoingRequests[0].id, outgoingRequests[0].type, hypothetical_response);
expect(marked).toStrictEqual(true);
keysUploadRequest = body;
keysUploadRequest = outgoingRequests[0];
}
{
expect(outgoingRequests[1]).toBeInstanceOf(KeysQueryRequest);
let [signingKeysUploadRequest, _] = await machineToAdd.bootstrapCrossSigning(true);
signingKeysUploadRequest = JSON.parse(signingKeysUploadRequest.body);
// Let's forge a `KeysQuery`'s response.
let keyQueryResponse = {
@@ -64,10 +64,12 @@ async function addMachineToMachine(machineToAdd, machine) {
const userId = machineToAdd.userId.toString();
const deviceId = machineToAdd.deviceId.toString();
keyQueryResponse.device_keys[userId] = {};
keyQueryResponse.device_keys[userId][deviceId] = keysUploadRequest.device_keys;
keyQueryResponse.master_keys[userId] = signingKeysUploadRequest.master_key;
keyQueryResponse.self_signing_keys[userId] = signingKeysUploadRequest.self_signing_key;
keyQueryResponse.user_signing_keys[userId] = signingKeysUploadRequest.user_signing_key;
keyQueryResponse.device_keys[userId][deviceId] = JSON.parse(keysUploadRequest.body).device_keys;
const keys = JSON.parse(signingKeysUploadRequest.body);
keyQueryResponse.master_keys[userId] = keys.master_key;
keyQueryResponse.self_signing_keys[userId] = keys.self_signing_key;
keyQueryResponse.user_signing_keys[userId] = keys.user_signing_key;
const marked = await machine.markRequestAsSent(outgoingRequests[1].id, outgoingRequests[1].type, JSON.stringify(keyQueryResponse));
expect(marked).toStrictEqual(true);

View File

@@ -5,6 +5,7 @@ const {
DeviceKeyId,
DeviceLists,
EncryptionSettings,
EventId,
InboundGroupSession,
KeysClaimRequest,
KeysQueryRequest,
@@ -14,9 +15,11 @@ const {
OwnUserIdentity,
RequestType,
RoomId,
RoomMessageRequest,
SignatureUploadRequest,
ToDeviceRequest,
UserId,
UserIdentity,
VerificationRequest,
VerificationState,
} = require('../pkg/matrix_sdk_crypto_js');
@@ -99,7 +102,7 @@ describe(OlmMachine.name, () => {
test('can drop/close', async () => {
m = await machine();
m.close();
})
});
test('can drop/close with a store', async () => {
let store_name = 'temporary';
@@ -133,7 +136,7 @@ describe(OlmMachine.name, () => {
deleting.onerror = () => { throw new Error('failed to remove the database (error)') };
deleting.onblocked = () => { throw new Error('failed to remove the database (blocked)') };
}
})
});
test('can read user ID', async () => {
expect((await machine()).userId.toString()).toStrictEqual(user.toString());
@@ -198,6 +201,7 @@ describe(OlmMachine.name, () => {
expect(outgoingRequests[0]).toBeInstanceOf(KeysUploadRequest);
expect(outgoingRequests[0].id).toBeDefined();
expect(outgoingRequests[0].type).toStrictEqual(RequestType.KeysUpload);
expect(outgoingRequests[0].body).toBeDefined();
const body = JSON.parse(outgoingRequests[0].body);
expect(body.device_keys).toBeDefined();
@@ -208,6 +212,7 @@ describe(OlmMachine.name, () => {
expect(outgoingRequests[1]).toBeInstanceOf(KeysQueryRequest);
expect(outgoingRequests[1].id).toBeDefined();
expect(outgoingRequests[1].type).toStrictEqual(RequestType.KeysQuery);
expect(outgoingRequests[1].body).toBeDefined();
const body = JSON.parse(outgoingRequests[1].body);
expect(body.timeout).toBeDefined();
@@ -622,4 +627,196 @@ describe(OlmMachine.name, () => {
});
});
});
describe('can do in-room verification', () => {
let m;
const user = new UserId('@alice:example.org');
const device = new DeviceId('JLAFKJWSCS');
const room = new RoomId('!test:localhost');
beforeAll(async () => {
m = await machine(user, device);
});
test('can inject devices from someone else', async () => {
{
const hypothetical_response = JSON.stringify({
"device_keys": {
"@example:morpheus.localhost": {
"ATRLDCRXAC": {
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "ATRLDCRXAC",
"keys": {
"curve25519:ATRLDCRXAC": "cAVT5Es3Z3F5pFD+2w3HT7O9+R3PstzYVkzD51X/FWQ",
"ed25519:ATRLDCRXAC": "V2w/T/x7i7AXiCCtS6JldrpbvRliRoef3CqTUNqMRHA"
},
"signatures": {
"@example:morpheus.localhost": {
"ed25519:ATRLDCRXAC": "ro2BjO5J6089B/JOANHnFmGrogrC2TIdMlgJbJO00DjOOcGxXfvOezCFIORTwZNHvkHU617YIGl/4keTDIWvBQ"
}
},
"user_id": "@example:morpheus.localhost",
"unsigned": {
"device_display_name": "Element Desktop: Linux"
}
},
"EYYGYTCTNC": {
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "EYYGYTCTNC",
"keys": {
"curve25519:EYYGYTCTNC": "Pqu50fo472wgb6NjKkaUxjuqoAIEAmhln2gw/zSQ7Ek",
"ed25519:EYYGYTCTNC": "Pf/2QPvui8lDty6TCTglVPRVM+irNHYavNNkyv5yFpU"
},
"signatures": {
"@example:morpheus.localhost": {
"ed25519:EYYGYTCTNC": "pnP5BYLEUUaxDgrvdzCznkjNDbvY1/MFBr1JejdnLiXlcmxRULQpIWZUCO7QTbULsCwMsYQNGn50nfmjBQX3CQ"
}
},
"user_id": "@example:morpheus.localhost",
"unsigned": {
"device_display_name": "WeeChat-Matrix-rs"
}
},
"SUMODVLSIU": {
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "SUMODVLSIU",
"keys": {
"curve25519:SUMODVLSIU": "geQXWGWc++gcUHk0JcFmEVSjyzDOnk2mjVsUQwbNqQU",
"ed25519:SUMODVLSIU": "ccktaQ3g+B18E6FwVhTBYie26OlHbvDUzDEtxOQ4Qcs"
},
"signatures": {
"@example:morpheus.localhost": {
"ed25519:SUMODVLSIU": "Yn+AOxHRt1GQpY2xT2Jcqqn8jh5+Vw23ctA7NXyDiWPsLPLNTpjGWHMjZdpUqflQvpiKfhODPICoIa7Pu0iSAg",
"ed25519:rUiMNDjIu6gqsrhJPbj3phyIzuEtuQGrLOEa9mCbtTM": "Cio6k/sq289XNTOvTCWre7Q6zg+A3euzMUe7Uy1T3gPqYFzX+kt7EAxrhbPqx1HyXAEz9zD0D/uw9VEXFCvWBQ"
}
},
"user_id": "@example:morpheus.localhost",
"unsigned": {
"device_display_name": "Element Desktop (Linux)"
}
}
}
},
"failures": {},
"master_keys": {
"@example:morpheus.localhost": {
"user_id": "@example:morpheus.localhost",
"usage": [
"master"
],
"keys": {
"ed25519:ZzU4WCyBfOFitdGmfKCq6F39iQCDk/zhNNTsi+tWH7A": "ZzU4WCyBfOFitdGmfKCq6F39iQCDk/zhNNTsi+tWH7A"
},
"signatures": {
"@example:morpheus.localhost": {
"ed25519:SUMODVLSIU": "RL6WOuuzB/mZ+edfUFG/KeEcmKh+NaWpM6m2bUYmDnJrtTCYyoU+pgHJuL2/6nynemmONo18JEHBuqtNcMq2AQ"
}
}
}
},
"self_signing_keys": {
"@example:morpheus.localhost": {
"user_id": "@example:morpheus.localhost",
"usage": [
"self_signing"
],
"keys": {
"ed25519:rUiMNDjIu6gqsrhJPbj3phyIzuEtuQGrLOEa9mCbtTM": "rUiMNDjIu6gqsrhJPbj3phyIzuEtuQGrLOEa9mCbtTM"
},
"signatures": {
"@example:morpheus.localhost": {
"ed25519:ZzU4WCyBfOFitdGmfKCq6F39iQCDk/zhNNTsi+tWH7A": "uCBn9rpeg6umY8H97ejN26UMp6QDwNL98869t1DoVGL50J8adLN05OZd8lYk9QzwTr2d56ZTGYSYX8kv28SDDA"
}
}
}
},
"user_signing_keys": {
"@example:morpheus.localhost": {
"user_id": "@example:morpheus.localhost",
"usage": [
"user_signing"
],
"keys": {
"ed25519:GLhEKLQ50jnF6IMEPsO2ucpHUNIUEnbBXs5gYbHg4Aw": "GLhEKLQ50jnF6IMEPsO2ucpHUNIUEnbBXs5gYbHg4Aw"
},
"signatures": {
"@example:morpheus.localhost": {
"ed25519:ZzU4WCyBfOFitdGmfKCq6F39iQCDk/zhNNTsi+tWH7A": "4fIyWlVzuz1pgoegNLZASycORXqKycVS0dNq5vmmwsVEudp1yrPhndnaIJ3fjF8LDHvwzXTvohOid7DiU1j0AA"
}
}
}
}
});
const marked = await m.markRequestAsSent('foo', RequestType.KeysQuery, hypothetical_response);
}
});
test('can start an in-room SAS verification', async () => {
let _ = m.bootstrapCrossSigning(true);
const identity = await m.getIdentity(new UserId('@example:morpheus.localhost'));
expect(identity).toBeInstanceOf(UserIdentity);
expect(identity.isVerified()).toStrictEqual(false);
const eventId = new EventId('$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg');
const verificationRequest = await identity.requestVerification(room, eventId);
expect(verificationRequest).toBeInstanceOf(VerificationRequest);
await m.receiveVerificationEvent(
JSON.stringify({
"sender": "@example:morpheus.localhost",
"type": "m.key.verification.ready",
"event_id": "$QguWmaeMt6Hao7Ea6XHDInvr8ndknev79t9a2eBxlz0",
"origin_server_ts": 1674037263075,
"content": {
"methods": [
"m.sas.v1",
"m.qr_code.show.v1",
"m.reciprocate.v1"
],
"from_device": "SUMODVLSIU",
"m.relates_to": {
"rel_type": "m.reference",
"event_id": eventId.toString(),
}
}
}),
room
);
expect(verificationRequest.roomId.toString()).toStrictEqual(room.toString());
const [_sas, outgoingVerificationRequest] = await verificationRequest.startSas();
expect(outgoingVerificationRequest).toBeInstanceOf(RoomMessageRequest);
expect(outgoingVerificationRequest.id).toBeDefined();
expect(outgoingVerificationRequest.room_id).toStrictEqual(room.toString());
expect(outgoingVerificationRequest.txn_id).toBeDefined();
expect(outgoingVerificationRequest.event_type).toStrictEqual('m.key.verification.start');
expect(outgoingVerificationRequest.body).toBeDefined();
const body = JSON.parse(outgoingVerificationRequest.body);
expect(body).toMatchObject({
from_device: expect.any(String),
method: 'm.sas.v1',
key_agreement_protocols: [expect.any(String)],
hashes: [expect.any(String)],
message_authentication_codes: [expect.any(String), expect.any(String)],
short_authentication_string: ['decimal', 'emoji'],
'm.relates_to': {
rel_type: 'm.reference',
event_id: eventId.toString(),
}
});
})
});
});

View File

@@ -11,25 +11,3 @@ describe('RequestType', () => {
expect(RequestType.KeysBackup).toStrictEqual(6);
});
});
for (const [request, requestType] of [
[KeysUploadRequest, RequestType.KeysUpload],
[KeysQueryRequest, RequestType.KeysQuery],
[KeysClaimRequest, RequestType.KeysClaim],
[ToDeviceRequest, RequestType.ToDevice],
[SignatureUploadRequest, RequestType.SignatureUpload],
[RoomMessageRequest, RequestType.RoomMessage],
[KeysBackupRequest, RequestType.KeysBackup],
]) {
describe(request.name, () => {
test('can be instantiated', () => {
const r = new (request)('foo', '{"bar": "baz"}');
expect(r).toBeInstanceOf(request);
expect(r.id).toStrictEqual('foo');
expect(r.body).toStrictEqual('{"bar": "baz"}');
expect(r.type).toStrictEqual(requestType);
});
})
}