refactor: Replace more uses of RoomVersionId with RoomVersionRules

This commit is contained in:
Ginger
2026-04-07 10:44:05 -04:00
parent 3e1f97487f
commit d62eeda130
14 changed files with 86 additions and 233 deletions
+3 -3
View File
@@ -3,7 +3,7 @@ use std::borrow::Borrow;
use conduwuit::{Err, Result, debug_error, implement, trace};
use ruma::{
CanonicalJsonObject, RoomVersionId, ServerName, ServerSigningKeyId,
api::federation::discovery::VerifyKey,
api::federation::discovery::VerifyKey, room_version_rules::RoomVersionRules,
};
use super::{PubKeyMap, PubKeys, extract_key};
@@ -13,9 +13,9 @@ use crate::server_keys::util::required_keys;
pub async fn get_event_keys(
&self,
object: &CanonicalJsonObject,
version: &RoomVersionId,
room_version_rules: &RoomVersionRules,
) -> Result<PubKeyMap> {
let required = match required_keys(object, version) {
let required = match required_keys(object, &room_version_rules.signatures) {
| Ok(required) => required,
| Err(e) => {
debug_error!("Failed to determine keys required to verify: {e}");
+3 -2
View File
@@ -18,6 +18,7 @@ use ruma::{
CanonicalJsonObject, MilliSecondsSinceUnixEpoch, OwnedServerSigningKeyId, RoomVersionId,
ServerName, ServerSigningKeyId,
api::federation::discovery::{ServerSigningKeys, VerifyKey},
room_version_rules::RoomVersionRules,
serde::Raw,
signatures::{Ed25519KeyPair, PublicKeyMap, PublicKeySet},
};
@@ -117,10 +118,10 @@ async fn add_signing_keys(&self, new_keys: ServerSigningKeys) {
pub async fn required_keys_exist(
&self,
object: &CanonicalJsonObject,
version: &RoomVersionId,
room_version_rules: &RoomVersionRules,
) -> bool {
trace!(?object, "Checking required keys exist");
let Ok(required_keys) = required_keys(object, version) else {
let Ok(required_keys) = required_keys(object, &room_version_rules.signatures) else {
debug_error!("Failed to determine required keys");
return false;
};
+4 -9
View File
@@ -1,5 +1,5 @@
use conduwuit::{Result, implement};
use ruma::{CanonicalJsonObject, RoomVersionId};
use ruma::{CanonicalJsonObject, RoomVersionId, room_version_rules::RoomVersionRules};
#[implement(super::Service)]
pub fn sign_json(&self, object: &mut CanonicalJsonObject) -> Result {
@@ -13,16 +13,11 @@ pub fn sign_json(&self, object: &mut CanonicalJsonObject) -> Result {
pub fn hash_and_sign_event(
&self,
object: &mut CanonicalJsonObject,
room_version: &RoomVersionId,
room_version_rules: &RoomVersionRules,
) -> Result {
use ruma::signatures::hash_and_sign_event;
let server_name = self.services.globals.server_name().as_str();
hash_and_sign_event(
server_name,
self.keypair(),
object,
&room_version.rules().unwrap().redaction,
)
.map_err(Into::into)
hash_and_sign_event(server_name, self.keypair(), object, &room_version_rules.redaction)
.map_err(Into::into)
}
+4 -147
View File
@@ -4,157 +4,14 @@ use ruma::{
CanonicalJsonObject, CanonicalJsonValue, IdParseError, OwnedEventId, OwnedServerName,
OwnedServerSigningKeyId, RoomVersionId, UserId,
canonical_json::JsonType,
signatures::{JsonError, VerificationError},
room_version_rules::SignaturesRules,
signatures::{JsonError, VerificationError, required_server_signatures_to_verify_event},
};
/// Whether the given event is an `m.room.member` invite that was created as the
/// result of a third-party invite.
///
/// Returns an error if the object has not the expected format of an
/// `m.room.member` event.
pub(super) fn is_invite_via_third_party_id(
object: &CanonicalJsonObject,
) -> Result<bool, JsonError> {
let Some(CanonicalJsonValue::String(raw_type)) = object.get("type") else {
return Err(JsonError::NotOfType {
target: "type".to_owned(),
of_type: JsonType::String,
}
.into());
};
if raw_type != "m.room.member" {
return Ok(false);
}
let Some(CanonicalJsonValue::Object(content)) = object.get("content") else {
return Err(JsonError::NotOfType {
target: "content".to_owned(),
of_type: JsonType::Object,
}
.into());
};
let Some(CanonicalJsonValue::String(membership)) = content.get("membership") else {
return Err(JsonError::NotOfType {
target: "membership".to_owned(),
of_type: JsonType::String,
}
.into());
};
if membership != "invite" {
return Ok(false);
}
match content.get("third_party_invite") {
| Some(CanonicalJsonValue::Object(_)) => Ok(true),
| None => Ok(false),
| _ => Err(JsonError::NotOfType {
target: "third_party_invite".to_owned(),
of_type: JsonType::Object,
}
.into()),
}
}
/// Extracts the server names to check signatures for given event.
///
/// Respects the rules for [validating signatures on received events] for
/// populating the result:
///
/// - Add the server of the sender, except if it's an invite event that results
/// from a third-party invite.
/// - For room versions 1 and 2, add the server of the `event_id`.
/// - For room versions that support restricted join rules, if it's a join event
/// with a `join_authorised_via_users_server`, add the server of that user.
///
/// [validating signatures on received events]: https://spec.matrix.org/latest/server-server-api/#validating-hashes-and-signatures-on-received-events
pub fn servers_to_check_signatures(
object: &CanonicalJsonObject,
version: &RoomVersionId,
) -> Result<BTreeSet<OwnedServerName>, VerificationError> {
let mut servers_to_check = BTreeSet::new();
if !is_invite_via_third_party_id(object)? {
match object.get("sender") {
| Some(CanonicalJsonValue::String(raw_sender)) => {
let user_id = <&UserId>::try_from(raw_sender.as_str()).map_err(|source| {
VerificationError::ParseIdentifier { identifier_type: "user ID", source }
})?;
servers_to_check.insert(user_id.server_name().to_owned());
},
| _ =>
return Err(JsonError::NotOfType {
target: "sender".to_owned(),
of_type: JsonType::String,
}
.into()),
};
}
match version {
| RoomVersionId::V1 | RoomVersionId::V2 => match object.get("event_id") {
| Some(CanonicalJsonValue::String(raw_event_id)) => {
let event_id: OwnedEventId = raw_event_id.parse().map_err(|source| {
VerificationError::ParseIdentifier { identifier_type: "event ID", source }
})?;
let server_name = event_id
.server_name()
.ok_or_else(|| VerificationError::ParseIdentifier {
identifier_type: "event ID",
source: IdParseError::InvalidServerName,
})?
.to_owned();
servers_to_check.insert(server_name);
},
| _ => {
return Err(JsonError::MissingField { path: "event_id".to_owned() }.into());
},
},
| RoomVersionId::V3
| RoomVersionId::V4
| RoomVersionId::V5
| RoomVersionId::V6
| RoomVersionId::V7 => {},
// TODO: And for all future versions that have join_authorised_via_users_server
| RoomVersionId::V8
| RoomVersionId::V9
| RoomVersionId::V10
| RoomVersionId::V11
| RoomVersionId::V12 => {
if let Some(authorized_user) = object
.get("content")
.and_then(|c| c.as_object())
.and_then(|c| c.get("join_authorised_via_users_server"))
{
let authorized_user = authorized_user.as_str().ok_or_else(|| -> JsonError {
JsonError::NotOfType {
target: "join_authorised_via_users_server".to_owned(),
of_type: JsonType::String,
}
.into()
})?;
let authorized_user = <&UserId>::try_from(authorized_user).map_err(|source| {
VerificationError::ParseIdentifier { identifier_type: "user ID", source }
})?;
servers_to_check.insert(authorized_user.server_name().to_owned());
}
},
| _ => unimplemented!(),
}
Ok(servers_to_check)
}
/// Extracts the server names and key ids to check signatures for given event.
pub fn required_keys(
object: &CanonicalJsonObject,
version: &RoomVersionId,
rules: &SignaturesRules,
) -> Result<BTreeMap<OwnedServerName, Vec<OwnedServerSigningKeyId>>, VerificationError> {
use CanonicalJsonValue::Object;
let mut map = BTreeMap::<OwnedServerName, Vec<OwnedServerSigningKeyId>>::new();
@@ -162,7 +19,7 @@ pub fn required_keys(
return Ok(map);
};
for server in servers_to_check_signatures(object, version)? {
for server in required_server_signatures_to_verify_event(object, rules)? {
let Some(Object(set)) = signatures.get(server.as_str()) else {
continue;
};
+14 -16
View File
@@ -2,7 +2,8 @@ use conduwuit::{
Err, Result, debug_warn, implement, matrix::event::gen_event_id_canonical_json, trace,
};
use ruma::{
CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, RoomVersionId, signatures::Verified,
CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, RoomVersionId,
room_version_rules::RoomVersionRules, signatures::Verified,
};
use serde_json::value::RawValue as RawJsonValue;
@@ -10,10 +11,10 @@ use serde_json::value::RawValue as RawJsonValue;
pub async fn validate_and_add_event_id(
&self,
pdu: &RawJsonValue,
room_version: &RoomVersionId,
room_version_rules: &RoomVersionRules,
) -> Result<(OwnedEventId, CanonicalJsonObject)> {
let (event_id, mut value) = gen_event_id_canonical_json(pdu, room_version)?;
if let Err(e) = self.verify_event(&value, Some(room_version)).await {
let (event_id, mut value) = gen_event_id_canonical_json(pdu, room_version_rules)?;
if let Err(e) = self.verify_event(&value, room_version_rules).await {
return Err!(BadServerResponse(debug_error!(
"Event {event_id} failed verification: {e:?}"
)));
@@ -28,12 +29,12 @@ pub async fn validate_and_add_event_id(
pub async fn validate_and_add_event_id_no_fetch(
&self,
pdu: &RawJsonValue,
room_version: &RoomVersionId,
room_version_rules: &RoomVersionRules,
) -> Result<(OwnedEventId, CanonicalJsonObject)> {
trace!(?pdu, "Validating PDU without fetching keys");
let (event_id, mut value) = gen_event_id_canonical_json(pdu, room_version)?;
let (event_id, mut value) = gen_event_id_canonical_json(pdu, room_version_rules)?;
trace!(event_id = event_id.as_str(), "Generated event ID, checking required keys");
if !self.required_keys_exist(&value, room_version).await {
if !self.required_keys_exist(&value, room_version_rules).await {
debug_warn!(
"Event {event_id} is missing required keys, cannot verify without fetching keys"
);
@@ -42,7 +43,7 @@ pub async fn validate_and_add_event_id_no_fetch(
)));
}
trace!("All required keys exist, verifying event");
if let Err(e) = self.verify_event(&value, Some(room_version)).await {
if let Err(e) = self.verify_event(&value, room_version_rules).await {
debug_warn!("Event verification failed");
return Err!(BadServerResponse(debug_error!(
"Event {event_id} failed verification: {e:?}"
@@ -59,21 +60,18 @@ pub async fn validate_and_add_event_id_no_fetch(
pub async fn verify_event(
&self,
event: &CanonicalJsonObject,
room_version: Option<&RoomVersionId>,
room_version_rules: &RoomVersionRules,
) -> Result<Verified> {
let room_version = room_version.unwrap_or(&RoomVersionId::V12);
let keys = self.get_event_keys(event, room_version).await?;
ruma::signatures::verify_event(&keys, event, &room_version.rules().unwrap())
.map_err(Into::into)
let keys = self.get_event_keys(event, room_version_rules).await?;
ruma::signatures::verify_event(&keys, event, room_version_rules).map_err(Into::into)
}
#[implement(super::Service)]
pub async fn verify_json(
&self,
event: &CanonicalJsonObject,
room_version: Option<&RoomVersionId>,
room_version_rules: &RoomVersionRules,
) -> Result {
let room_version = room_version.unwrap_or(&RoomVersionId::V12);
let keys = self.get_event_keys(event, room_version).await?;
let keys = self.get_event_keys(event, room_version_rules).await?;
ruma::signatures::verify_json(&keys, event).map_err(Into::into)
}