Files
synapse/rust/src/lib.rs
T
Erik JohnstonandClaude Opus 5.5 489c09175d Sign federation requests in Rust
Add `rust/src/federation`, starting with the `X-Matrix` request
signature and the transport seam the request layer will send through.

The signing code comes from matrix-hyper-federation-client (Apache-2.0),
changed to use Synapse's canonical JSON serializer, Python's header
layout and quoting, and `destination_is` for identity servers. It keeps
that crate's signature test vectors. The body is serialized to canonical
JSON once, then both signed and sent in that form.

The homeserver's signing key crosses to Rust as its algorithm, version
and seed, taken from `hs.signing_key`. Rust never reads the key file.

`FederationTransport` is what the request layer will send through.
`PyTransport` implements it with a Python callable, which is how Rust
level tests will inject responses in place of `FakeTransport`.

A trial test checks the Rust header is byte-identical to the one
`MatrixFederationHttpClient.build_auth_headers` builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5b7Ruy4cgt27Mjq6Ptxbb
2026-10-15 14:54:15 +00:00

133 lines
3.5 KiB
Rust

use std::{
convert::Infallible,
time::{SystemTime, UNIX_EPOCH},
};
use lazy_static::lazy_static;
use pyo3::{exceptions::PyRuntimeError, prelude::*};
use pyo3_log::ResetHandle;
pub mod acl;
pub mod caches;
pub mod canonical_json;
pub mod clock;
pub mod config;
pub mod database;
pub mod deferred;
pub mod duration;
pub mod errors;
pub mod event_auth;
pub mod events;
pub mod federation;
pub mod handlers;
pub mod homeserver;
pub mod http;
pub mod http_client;
pub mod identifier;
pub mod json;
pub mod libpq;
pub mod logging;
pub mod matrix_const;
pub mod msc4388_rendezvous;
pub mod push;
pub mod reactor;
pub mod rendezvous;
pub mod room_versions;
pub mod runtime;
pub mod segmenter;
pub mod state;
pub mod storage;
pub mod sync;
pub mod twisted_dispatch;
pub mod types;
pub mod visibility;
lazy_static! {
static ref LOGGING_HANDLE: ResetHandle = pyo3_log::init();
}
/// Returns the hash of all the rust source files at the time it was compiled.
///
/// Used by python to detect if the rust library is outdated.
#[pyfunction]
fn get_rust_file_digest() -> &'static str {
env!("SYNAPSE_RUST_DIGEST")
}
/// Returns the `rustc` version used when this native module was built.
///
/// This value is embedded at build time, so it can be exported as a prometheus metrics.
#[pyfunction]
pub fn get_rustc_version() -> &'static str {
env!("SYNAPSE_RUSTC_VERSION")
}
/// Formats the sum of two numbers as string.
#[pyfunction]
#[pyo3(text_signature = "(a, b, /)")]
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
Ok((a + b).to_string())
}
/// Reset the cached logging configuration of pyo3-log to pick up any changes
/// in the Python logging configuration.
///
#[pyfunction]
fn reset_logging_config() {
LOGGING_HANDLE.reset();
}
/// The entry point for defining the Python module.
#[pymodule]
fn synapse_rust(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
// We expect that system clock is set for after the Unix epoch, 1970. We'll
// panic later if it is not, so we check here and fail early.
if SystemTime::now().duration_since(UNIX_EPOCH).is_err() {
return Err(PyRuntimeError::new_err(
"system clock was set before the Unix epoch, 1970",
));
}
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
m.add_function(wrap_pyfunction!(get_rust_file_digest, m)?)?;
m.add_function(wrap_pyfunction!(get_rustc_version, m)?)?;
m.add_function(wrap_pyfunction!(reset_logging_config, m)?)?;
acl::register_module(py, m)?;
logging::context::register_module(py, m)?;
caches::python::register_module(py, m)?;
deferred::register_module(py, m)?;
database::register_module(py, m)?;
push::register_module(py, m)?;
events::register_module(py, m)?;
federation::register_module(py, m)?;
event_auth::register_module(py, m)?;
handlers::register_module(py, m)?;
http_client::register_module(py, m)?;
rendezvous::register_module(py, m)?;
runtime::register_module(py, m)?;
storage::python::register_module(py, m)?;
msc4388_rendezvous::register_module(py, m)?;
segmenter::register_module(py, m)?;
state::register_module(py, m)?;
sync::register_module(py, m)?;
room_versions::register_module(py, m)?;
types::register_module(py, m)?;
visibility::register_module(py, m)?;
Ok(())
}
pub trait UnwrapInfallible<T> {
fn unwrap_infallible(self) -> T;
}
impl<T> UnwrapInfallible<T> for Result<T, Infallible> {
fn unwrap_infallible(self) -> T {
match self {
Ok(val) => val,
Err(never) => match never {},
}
}
}