Skip to main content

tuwunel_service/fetcher/
validate.rs

1//! Two-tier response validation: a cheap conformance check, then an opt-in deep
2//! PDU pass (event id, content hashes, signatures) that runs only for event ops
3//! with those checks enabled. The per-check toggles live on [`Opts`].
4
5use ruma::{CanonicalJsonObject, RoomVersionId};
6use serde::de::IgnoredAny;
7use tuwunel_core::{Err, Result, err, implement, matrix::event::gen_event_id};
8
9use super::{Op, Opts};
10
11/// Poison detection applied before a response is accepted, so a hostile server
12/// answering with garbage transparently rolls onto the next candidate. Full
13/// auth resolution stays outside; this only rejects responses we can prove bad
14/// from the bytes alone.
15#[implement(super::Service)]
16#[tracing::instrument(name = "validate", level = "trace", skip_all)]
17pub(super) async fn validate(&self, opts: &Opts, bytes: &[u8]) -> Result {
18	if opts.check_conforms {
19		serde_json::from_slice::<IgnoredAny>(bytes)
20			.map_err(|e| err!(BadServerResponse("malformed federation response: {e}")))?;
21	}
22
23	let deep = opts.check_event_id || opts.check_hashes || opts.check_signature;
24	if matches!(opts.op, Op::Event | Op::AuthEvent) && deep {
25		self.verify_pdu(opts, bytes).await?;
26	}
27
28	Ok(())
29}
30
31#[implement(super::Service)]
32#[tracing::instrument(level = "trace", skip_all)]
33async fn verify_pdu(&self, opts: &Opts, bytes: &[u8]) -> Result {
34	let value: CanonicalJsonObject = serde_json::from_slice(bytes)
35		.map_err(|e| err!(BadServerResponse("PDU is not a canonical JSON object: {e}")))?;
36
37	let v11 = RoomVersionId::V11;
38	let room_version = opts.room_version.as_ref().unwrap_or(&v11);
39
40	if opts.check_event_id
41		&& let Some(expected) = opts.event_id.as_ref()
42	{
43		let calculated = gen_event_id(&value, room_version)?;
44		if calculated != *expected {
45			return Err!(BadServerResponse("server returned the wrong event id"));
46		}
47	}
48
49	if opts.check_signature || opts.check_hashes {
50		self.services
51			.server_keys
52			.verify_event(&value, Some(room_version))
53			.await?;
54	}
55
56	Ok(())
57}