From 13c9385ef7c0fa05fe2cc363e7364a3bab77b9c6 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Sun, 31 Aug 2025 17:17:53 +0000 Subject: [PATCH] Add v1/v2 support to gen_event_id() scheme. (#12) Add v1/v2 and improve reference and content hashing suite. (#12) Signed-off-by: Jason Volk --- src/core/matrix/event/id.rs | 29 +++++++++++-- src/service/server_keys/sign.rs | 76 +++++++++++++++++++++++++++------ 2 files changed, 90 insertions(+), 15 deletions(-) diff --git a/src/core/matrix/event/id.rs b/src/core/matrix/event/id.rs index 587e17a3..15f365c6 100644 --- a/src/core/matrix/event/id.rs +++ b/src/core/matrix/event/id.rs @@ -1,4 +1,4 @@ -use ruma::{CanonicalJsonObject, OwnedEventId, RoomVersionId}; +use ruma::{CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, RoomVersionId}; use serde_json::value::RawValue as RawJsonValue; use crate::{Result, debug_error, err, matrix::room_version}; @@ -20,13 +20,36 @@ pub fn gen_event_id_canonical_json( Ok((event_id, value)) } -/// Generates a correct eventId for the incoming pdu. +/// Generates a correct eventId for the PDU. For v1/v2 incoming PDU's the +/// value's event_id is passed through. For all outgoing PDU's and for v3+ +/// incoming PDU's it is generated. pub fn gen_event_id( value: &CanonicalJsonObject, room_version_id: &RoomVersionId, ) -> Result { let room_version_rules = room_version::rules(room_version_id)?; + let require_event_id = room_version_rules.event_format.require_event_id; + + // We don't actually generate any event_id for incoming events in v1/v2 rooms, + // just pass them through. + if let Some(event_id) = require_event_id + .then(|| value.get("event_id")) + .flatten() + .and_then(CanonicalJsonValue::as_str) + .map(OwnedEventId::try_from) + .transpose()? + { + return Ok(event_id); + } + + // For outgoing v1/v2 add the server part. This has to be our origin but we + // can't assert that here. + let server_name = require_event_id + .then(|| value.get("origin")) + .flatten() + .and_then(CanonicalJsonValue::as_str); + let reference_hash = ruma::signatures::reference_hash(value, &room_version_rules)?; - OwnedEventId::from_parts('$', &reference_hash, None).map_err(Into::into) + OwnedEventId::from_parts('$', &reference_hash, server_name).map_err(Into::into) } diff --git a/src/service/server_keys/sign.rs b/src/service/server_keys/sign.rs index 58d08d86..ecedccd2 100644 --- a/src/service/server_keys/sign.rs +++ b/src/service/server_keys/sign.rs @@ -1,28 +1,71 @@ -use ruma::{CanonicalJsonObject, RoomVersionId}; -use tuwunel_core::{Result, err, implement}; +use ruma::{CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, RoomVersionId}; +use tuwunel_core::{ + Result, implement, + matrix::{event::gen_event_id, room_version}, +}; #[implement(super::Service)] -pub fn sign_json(&self, object: &mut CanonicalJsonObject) -> Result { - use ruma::signatures::sign_json; +pub fn gen_id_hash_and_sign_event( + &self, + object: &mut CanonicalJsonObject, + room_version_id: &RoomVersionId, +) -> Result { + object.remove("event_id"); - let server_name = self.services.globals.server_name().as_str(); - sign_json(server_name, self.keypair(), object).map_err(Into::into) + if room_version::rules(room_version_id)? + .event_format + .require_event_id + { + self.gen_id_hash_and_sign_event_v1(object, room_version_id) + } else { + self.gen_id_hash_and_sign_event_v3(object, room_version_id) + } +} + +#[implement(super::Service)] +fn gen_id_hash_and_sign_event_v1( + &self, + object: &mut CanonicalJsonObject, + room_version_id: &RoomVersionId, +) -> Result { + let event_id = gen_event_id(object, room_version_id)?; + + object.insert("event_id".into(), CanonicalJsonValue::String(event_id.clone().into())); + + self.services + .server_keys + .hash_and_sign_event(object, room_version_id)?; + + Ok(event_id) +} + +#[implement(super::Service)] +fn gen_id_hash_and_sign_event_v3( + &self, + object: &mut CanonicalJsonObject, + room_version_id: &RoomVersionId, +) -> Result { + self.services + .server_keys + .hash_and_sign_event(object, room_version_id)?; + + let event_id = gen_event_id(object, room_version_id)?; + + object.insert("event_id".into(), CanonicalJsonValue::String(event_id.clone().into())); + + Ok(event_id) } #[implement(super::Service)] pub fn hash_and_sign_event( &self, object: &mut CanonicalJsonObject, - room_version: &RoomVersionId, + room_version_id: &RoomVersionId, ) -> Result { use ruma::signatures::hash_and_sign_event; let server_name = &self.services.server.name; - let room_version_rules = room_version.rules().ok_or_else(|| { - err!(Request(UnsupportedRoomVersion( - "Cannot hash and sign event for unknown room version {room_version:?}." - ))) - })?; + let room_version_rules = room_version::rules(room_version_id)?; hash_and_sign_event( server_name.as_str(), @@ -32,3 +75,12 @@ pub fn hash_and_sign_event( ) .map_err(Into::into) } + +#[implement(super::Service)] +pub fn sign_json(&self, object: &mut CanonicalJsonObject) -> Result { + use ruma::signatures::sign_json; + + let server_name = self.services.globals.server_name().as_str(); + + sign_json(server_name, self.keypair(), object).map_err(Into::into) +}