Skip to main content

tuwunel_service/server_keys/
mod.rs

1//! Federation signing-key storage, acquisition, signing, and verification.
2//!
3//! The service loads the local Ed25519 identity, caches remote current and old
4//! verify keys, fetches missing keys from origins or configured notaries, and
5//! supplies the cryptographic operations used by federation event handling.
6
7mod acquire;
8mod get;
9mod keypair;
10mod request;
11mod sign;
12mod verify;
13
14use std::{collections::BTreeMap, sync::Arc, time::Duration};
15
16use futures::StreamExt;
17use ruma::{
18	CanonicalJsonObject, MilliSecondsSinceUnixEpoch, OwnedServerSigningKeyId, ServerName,
19	ServerSigningKeyId,
20	api::federation::discovery::{ServerSigningKeys, VerifyKey},
21	room_version_rules::RoomVersionRules,
22	serde::Raw,
23	signatures::{Ed25519KeyPair, PublicKeyMap, PublicKeySet},
24};
25use serde_json::value::RawValue as RawJsonValue;
26use tuwunel_core::{
27	Result, implement,
28	utils::{IterStream, timepoint_from_now},
29};
30use tuwunel_database::{Deserialized, Json, Map};
31
32/// Manages the local signing identity and cached remote verification keys.
33///
34/// Cached keys are retained by key ID without enforcing `valid_until_ts` on
35/// reads. Missing keys can be acquired from remote origins or trusted notaries.
36pub struct Service {
37	keypair: Box<Ed25519KeyPair>,
38	verify_keys: VerifyKeys,
39	minimum_valid: Duration,
40	services: Arc<crate::services::OnceServices>,
41	db: Data,
42}
43
44struct Data {
45	server_signingkeys: Arc<Map>,
46}
47
48/// Verify keys indexed by Matrix server signing-key ID.
49///
50/// Current and retired keys can be merged into this representation for event
51/// verification.
52pub type VerifyKeys = BTreeMap<OwnedServerSigningKeyId, VerifyKey>;
53
54/// Public keys grouped first by server name and then by key ID.
55///
56/// This is the map shape accepted by ruma's signature verification helpers.
57pub type PubKeyMap = PublicKeyMap;
58
59/// Public keys for one server, indexed by textual key ID.
60///
61/// Values contain the decoded public-key material expected by ruma.
62pub type PubKeys = PublicKeySet;
63
64impl crate::Service for Service {
65	fn build(args: &crate::Args<'_>) -> Result<Arc<Self>> {
66		let minimum_valid = Duration::from_hours(1);
67
68		let (keypair, verify_keys) = keypair::init(args.db)?;
69		debug_assert!(verify_keys.len() == 1, "only one active verify_key supported");
70
71		Ok(Arc::new(Self {
72			keypair,
73			verify_keys,
74			minimum_valid,
75			services: args.services.clone(),
76			db: Data {
77				server_signingkeys: args.db["server_signingkeys"].clone(),
78			},
79		}))
80	}
81
82	fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
83}
84
85/// Returns the local Ed25519 signing keypair.
86///
87/// The keypair is loaded or generated when the service is built and remains
88/// fixed for the service lifetime.
89#[implement(Service)]
90#[inline]
91#[must_use]
92pub fn keypair(&self) -> &Ed25519KeyPair { &self.keypair }
93
94/// Returns the signing-key ID for the active local verify key.
95///
96/// This delegates to [`Self::active_verify_key`] and therefore panics if the
97/// service was initialized without an active key.
98#[implement(Service)]
99#[inline]
100#[must_use]
101pub fn active_key_id(&self) -> &ServerSigningKeyId { self.active_verify_key().0 }
102
103/// Returns the active local signing-key ID and verify key.
104///
105/// Initialization normally supplies exactly one entry. A missing entry panics,
106/// and debug builds also assert that no second active key exists.
107#[implement(Service)]
108#[inline]
109#[must_use]
110pub fn active_verify_key(&self) -> (&ServerSigningKeyId, &VerifyKey) {
111	debug_assert!(self.verify_keys.len() <= 1, "more than one active verify_key");
112	self.verify_keys
113		.iter()
114		.next()
115		.map(|(id, key)| (id.as_ref(), key))
116		.expect("missing active verify_key")
117}
118
119/// Merges a fetched signing-key document into the local cache.
120///
121/// Only current and old verify-key maps are retained from the incoming document;
122/// its signatures and validity timestamp are not preserved. The read, merge,
123/// and write sequence is not atomic.
124#[implement(Service)]
125async fn add_signing_keys(&self, new_keys: ServerSigningKeys) {
126	let origin = &new_keys.server_name;
127
128	// (timo) Not atomic, but this is not critical
129	let mut keys: ServerSigningKeys = self
130		.db
131		.server_signingkeys
132		.get(origin)
133		.await
134		.deserialized()
135		.unwrap_or_else(|_| {
136			// Just insert "now", it doesn't matter
137			ServerSigningKeys::new(origin.to_owned(), MilliSecondsSinceUnixEpoch::now())
138		});
139
140	keys.verify_keys.extend(new_keys.verify_keys);
141	keys.old_verify_keys
142		.extend(new_keys.old_verify_keys);
143
144	self.db
145		.server_signingkeys
146		.raw_put(origin, Json(&keys));
147}
148
149/// Checks whether every signature key required by an event is cached.
150///
151/// Invalid signature metadata, database errors, and malformed stored key data
152/// all produce `false`; this method never fetches missing keys.
153#[implement(Service)]
154pub async fn required_keys_exist(
155	&self,
156	object: &CanonicalJsonObject,
157	rules: &RoomVersionRules,
158) -> bool {
159	use ruma::signatures::required_keys;
160
161	let Ok(required_keys) = required_keys(object, &rules.signatures) else {
162		return false;
163	};
164
165	required_keys
166		.iter()
167		.flat_map(|(server, key_ids)| key_ids.iter().map(move |key_id| (server, key_id)))
168		.stream()
169		.all(|(server, key_id)| self.verify_key_exists(server, key_id))
170		.await
171}
172
173/// Checks whether one current or retired verify key is cached for a server.
174///
175/// The check is based on key-ID presence only and does not evaluate the stored
176/// key document's validity interval. Read or decoding errors produce `false`.
177#[implement(Service)]
178pub async fn verify_key_exists(&self, origin: &ServerName, key_id: &ServerSigningKeyId) -> bool {
179	type KeysMap<'a> = BTreeMap<&'a ServerSigningKeyId, &'a RawJsonValue>;
180
181	let Ok(keys) = self
182		.db
183		.server_signingkeys
184		.get(origin)
185		.await
186		.deserialized::<Raw<ServerSigningKeys>>()
187	else {
188		return false;
189	};
190
191	if let Ok(Some(verify_keys)) = keys.get_field::<KeysMap<'_>>("verify_keys")
192		&& verify_keys.contains_key(key_id)
193	{
194		return true;
195	}
196
197	if let Ok(Some(old_verify_keys)) = keys.get_field::<KeysMap<'_>>("old_verify_keys")
198		&& old_verify_keys.contains_key(key_id)
199	{
200		return true;
201	}
202
203	false
204}
205
206/// Returns all cached verify keys usable for a server.
207///
208/// Retired keys are converted and merged with current keys. Storage errors are
209/// suppressed to an empty map, and the local active key is added for our names.
210#[implement(Service)]
211pub async fn verify_keys_for(&self, origin: &ServerName) -> VerifyKeys {
212	let mut keys = self
213		.signing_keys_for(origin)
214		.await
215		.map(|keys| merge_old_keys(keys).verify_keys)
216		.unwrap_or(BTreeMap::new());
217
218	if self.services.globals.server_is_ours(origin) {
219		keys.extend(self.verify_keys.clone());
220	}
221
222	keys
223}
224
225/// Loads the cached signing-key document for a server.
226///
227/// The returned document reflects the service's merged cache representation;
228/// reads do not enforce `valid_until_ts`. Acquisition currently preserves key
229/// maps but not incoming document signatures or validity metadata.
230#[implement(Service)]
231pub async fn signing_keys_for(&self, origin: &ServerName) -> Result<ServerSigningKeys> {
232	self.db
233		.server_signingkeys
234		.get(origin)
235		.await
236		.deserialized()
237}
238
239#[implement(Service)]
240fn minimum_valid_ts(&self) -> MilliSecondsSinceUnixEpoch {
241	let timepoint =
242		timepoint_from_now(self.minimum_valid).expect("SystemTime should not overflow");
243
244	MilliSecondsSinceUnixEpoch::from_system_time(timepoint).expect("UInt should not overflow")
245}
246
247fn merge_old_keys(mut keys: ServerSigningKeys) -> ServerSigningKeys {
248	keys.verify_keys.extend(
249		keys.old_verify_keys
250			.clone()
251			.into_iter()
252			.map(|(key_id, old)| (key_id, VerifyKey::new(old.key))),
253	);
254
255	keys
256}
257
258fn extract_key(mut keys: ServerSigningKeys, key_id: &ServerSigningKeyId) -> Option<VerifyKey> {
259	keys.verify_keys.remove(key_id).or_else(|| {
260		keys.old_verify_keys
261			.remove(key_id)
262			.map(|old| VerifyKey::new(old.key))
263	})
264}
265
266fn key_exists(keys: &ServerSigningKeys, key_id: &ServerSigningKeyId) -> bool {
267	keys.verify_keys.contains_key(key_id) || keys.old_verify_keys.contains_key(key_id)
268}