tuwunel_service/fetcher/
validate.rs1use ruma::{CanonicalJsonObject, RoomVersionId};
10use serde::de::IgnoredAny;
11use tuwunel_core::{Err, Result, err, implement, matrix::event::gen_event_id};
12
13use super::{Op, Opts};
14
15#[implement(super::Service)]
21#[tracing::instrument(name = "validate", level = "trace", skip_all)]
22pub(super) async fn validate(&self, opts: &Opts, bytes: &[u8]) -> Result {
23 if opts.check_conforms {
24 match opts.op {
25 | Op::Backfill => serde_json::from_slice(bytes)
26 .map(|pdus: Vec<IgnoredAny>| !pdus.is_empty())
27 .map_err(|e| err!(BadServerResponse("malformed federation response: {e}")))
28 .and_then(|populated| {
29 populated
30 .then_some(())
31 .ok_or_else(|| err!(BadServerResponse("empty backfill response")))
32 }),
33 | _ => serde_json::from_slice(bytes)
34 .map(|_: IgnoredAny| ())
35 .map_err(|e| err!(BadServerResponse("malformed federation response: {e}"))),
36 }?;
37 }
38
39 let deep = opts.check_event_id || opts.check_hashes || opts.check_signature;
40 if matches!(opts.op, Op::Event | Op::AuthEvent) && deep {
41 self.verify_pdu(opts, bytes).await?;
42 }
43
44 Ok(())
45}
46
47#[implement(super::Service)]
52#[tracing::instrument(level = "trace", skip_all)]
53async fn verify_pdu(&self, opts: &Opts, bytes: &[u8]) -> Result {
54 let value: CanonicalJsonObject = serde_json::from_slice(bytes)
55 .map_err(|e| err!(BadServerResponse("PDU is not a canonical JSON object: {e}")))?;
56
57 let v11 = RoomVersionId::V11;
58 let room_version = opts.room_version.as_ref().unwrap_or(&v11);
59
60 if opts.check_event_id
61 && let Some(expected) = opts.event_id.as_ref()
62 {
63 let calculated = gen_event_id(&value, room_version)?;
64 if calculated != *expected {
65 return Err!(BadServerResponse("server returned the wrong event id"));
66 }
67 }
68
69 if opts.check_signature || opts.check_hashes {
70 self.services
71 .server_keys
72 .verify_event(&value, Some(room_version))
73 .await?;
74 }
75
76 Ok(())
77}