1mod tests;
7
8use std::time::SystemTime;
9
10use arminius_lib_db::{Database, DatabaseError, ResultExt as _, TransactedDb};
11use ruma::{
12 KeyId, OwnedServerName, OwnedServerSigningKeyId, ServerName, ServerSigningKeyId,
13 ServerSigningKeyVersion, SigningKeyAlgorithm,
14 api::federation::discovery::{OldVerifyKey, ServerSigningKeys},
15 serde::{Base64, Raw},
16 signatures::Ed25519KeyPair,
17};
18
19#[derive(thiserror::Error, Debug)]
21pub enum ServerKeyError {
22 #[error("Database error: {0}")]
24 DatabaseError(#[from] DatabaseError),
25 #[error("Key error: {0}")]
27 KeyError(#[from] ruma::signatures::Ed25519KeyPairParseError),
28 #[error("Key not found")]
30 NotFound,
31 #[error("Configuration error: {0}")]
33 ConfigurationError(String),
34}
35
36impl From<sqlx::Error> for ServerKeyError {
37 fn from(err: sqlx::Error) -> Self {
38 ServerKeyError::DatabaseError(DatabaseError::from(err))
39 }
40}
41
42#[derive(Debug, Eq, PartialEq)]
44pub struct ServerKey {
45 pub key: Vec<u8>,
47 pub expires_at: chrono::DateTime<chrono::Utc>,
54}
55
56#[rust_analyzer::prefer_underscore_import]
59pub trait ServerKeyQueries: Database {
60 #[tracing::instrument(skip(self))]
63 async fn fetch_active_server_key(&mut self) -> Result<Ed25519KeyPair, ServerKeyError> {
64 let res = sqlx::query!(
65 "
66 SELECT
67 key_pair, key_version
68 FROM
69 server_private_keys
70 WHERE
71 key_pair IS NOT NULL
72 "
73 )
74 .fetch_optional(self.connection().await?)
75 .await?
76 .ok_or(ServerKeyError::NotFound)?;
77
78 Ed25519KeyPair::from_der(
79 &res.key_pair.ok_or(ServerKeyError::ConfigurationError(
80 "Signing key in database invalid".to_owned(),
81 ))?[..],
82 res.key_version,
83 )
84 .map_err(ServerKeyError::from)
85 }
86
87 #[tracing::instrument(skip(self))]
89 async fn fetch_expired_verify_keys(
90 &mut self,
91 ) -> Result<Vec<(OwnedServerSigningKeyId, OldVerifyKey)>, ServerKeyError> {
92 Ok(sqlx::query!(
93 "SELECT expired_at, verify_key, key_version FROM server_private_keys WHERE key_pair IS NULL;"
94 )
95 .fetch_all(self.connection().await?)
96 .await?
97 .iter()
98 .map(|row| {
99 let expired_at = row.expired_at.ok_or(ServerKeyError::ConfigurationError(
100 "Expired key does not have expiry timestamp set".to_owned(),
101 ))?;
102
103 let date =
104 ruma::MilliSecondsSinceUnixEpoch::from_system_time(SystemTime::from(expired_at))
105 .ok_or(ServerKeyError::ConfigurationError(
106 "Invalid timestamp for expired key".to_owned(),
107 ))?;
108 let b64 = Base64::new(row.verify_key.clone());
109 let name = ServerSigningKeyId::from_parts(
110 SigningKeyAlgorithm::Ed25519,
111 &ServerSigningKeyVersion::parse(row.key_version.clone()).map_err(|_| {
112 ServerKeyError::ConfigurationError("Invalid server key version".to_owned())
113 })?,
114 );
115 let verify_key = OldVerifyKey::new(date, b64);
116 Ok(Some((name, verify_key)))
117 })
118 .collect::<Result<Vec<_>, ServerKeyError>>()?
119 .into_iter()
120 .flatten()
121 .collect::<Vec<_>>())
122 }
123
124 #[tracing::instrument(skip(self))]
126 async fn fetch_foreign_server_keys(
127 &mut self,
128 server_names: &ServerName,
129 key_id: &KeyId<SigningKeyAlgorithm, ServerSigningKeyVersion>,
130 ) -> Result<Option<ServerKey>, DatabaseError> {
131 let algorithm = key_id.algorithm();
132 Ok(sqlx::query!(
133 "
134 SELECT
135 public_key,
136 expires_at
137 FROM
138 server_public_keys
139 WHERE
140 server_name = $1
141 AND key_algorithm = $2
142 AND key_name = $3
143 ",
144 server_names.as_str(),
145 algorithm.as_str(),
146 key_id.key_name().as_str(),
147 )
148 .fetch_optional(self.connection().await?)
149 .await?
150 .map(|row| ServerKey { key: row.public_key, expires_at: row.expires_at }))
151 }
152
153 #[tracing::instrument(skip(self))]
161 async fn upsert_server_key(
162 &mut self,
163 server_name: &ServerName,
164 key_id: &KeyId<SigningKeyAlgorithm, ServerSigningKeyVersion>,
165 server_key: &ServerKey,
166 ) -> Result<(), DatabaseError> {
167 let algorithm = key_id.algorithm();
168 let result = sqlx::query!(
169 "
170 INSERT INTO server_public_keys (server_name, key_algorithm, key_name, public_key, expires_at)
171 VALUES ($1, $2, $3, $4, $5)
172 ON CONFLICT (server_name, key_name, key_algorithm)
173 DO UPDATE SET
174 expires_at = excluded.expires_at
175 WHERE
176 server_public_keys.public_key = excluded.public_key
177 ",
178 server_name.as_str(),
179 algorithm.as_str(),
180 key_id.key_name().as_str(),
181 server_key.key,
182 server_key.expires_at,
183 )
184 .execute(self.connection().await?)
185 .await?;
186 if result.rows_affected() == 0 {
187 Err(DatabaseError::InvalidData(format!(
188 "A key {key_id} for the server {server_name} already exists in the database, but now another key with the same ID is being written",
189 )))
190 } else {
191 Ok(())
192 }
193 }
194
195 #[tracing::instrument(skip(self))]
197 async fn fetch_key_response(
198 &mut self,
199 server_names: &[OwnedServerName],
200 ) -> Result<Vec<Raw<ServerSigningKeys>>, DatabaseError> {
201 let names: Vec<String> = server_names.iter().map(ToString::to_string).collect();
202 sqlx::query!(
203 "
204 SELECT
205 response
206 FROM
207 server_key_responses
208 WHERE
209 server_name IN (
210 SELECT
211 *
212 FROM
213 UNNEST($1::text[]))
214 ",
215 &names[..]
216 )
217 .fetch_all(self.connection().await?)
218 .await?
219 .into_iter()
220 .map(|row| {
221 Raw::<ServerSigningKeys>::from_json_string(row.response)
222 .invalid_data("Could not create raw value")
223 })
224 .collect::<Result<Vec<_>, _>>()
225 }
226
227 #[tracing::instrument(skip(self, key_pair_der, verify_key))]
242 async fn import_private_key(
243 &mut self,
244 key_id: &KeyId<SigningKeyAlgorithm, ServerSigningKeyVersion>,
245 key_pair_der: &[u8],
246 verify_key: &[u8],
247 ) -> Result<bool, DatabaseError> {
248 let result = sqlx::query!(
249 r#"
250 INSERT INTO server_private_keys (key_version, key_pair, verify_key)
251 VALUES ($1, $2, $3)
252 ON CONFLICT (key_version)
253 DO NOTHING
254 "#,
255 key_id.as_str(),
256 key_pair_der,
257 verify_key,
258 )
259 .execute(self.connection().await?)
260 .await?;
261 Ok(result.rows_affected() > 0)
262 }
263
264 #[tracing::instrument(skip(self))]
268 async fn upsert_key_response(
269 &mut self,
270 server_name: &ServerName,
271 key_response: Raw<ServerSigningKeys>,
272 ) -> Result<(), DatabaseError> {
273 sqlx::query!(
279 "
280 INSERT INTO server_key_responses (server_name, response)
281 VALUES ($1, $2)
282 ON CONFLICT (server_name)
283 DO UPDATE SET
284 response = excluded.response
285 ",
286 server_name.as_str(),
287 key_response.json().get()
288 )
289 .execute(self.connection().await?)
290 .await?;
291 Ok(())
292 }
293}
294
295pub trait TransactedServerKeyQueries: ServerKeyQueries {
297 async fn rotate_server_keys(&mut self) -> Result<(), ServerKeyError> {
299 let der = Ed25519KeyPair::generate();
305
306 let tmp_key_pair = Ed25519KeyPair::from_der(der.as_ref(), String::new()).map_err(|_| {
309 ServerKeyError::ConfigurationError("Server keys have invalid format".to_owned())
310 })?;
311 let name: String = bs58::encode(tmp_key_pair.public_key())
312 .with_alphabet(bs58::Alphabet::BITCOIN)
313 .into_string()
314 .chars()
315 .take(8)
316 .collect();
317
318 sqlx::query!(
319 "UPDATE server_private_keys SET key_pair = NULL, expired_at = NOW() WHERE key_pair IS NOT NULL;"
320 )
321 .execute(&mut *self.connection().await?)
322 .await?;
323
324 let der_ref: &[u8] = der.as_ref();
325
326 sqlx::query!(
327 "INSERT INTO server_private_keys (key_version, key_pair, verify_key) VALUES ($1, $2, $3)",
328 name,
329 der_ref,
330 &tmp_key_pair.public_key(),
331 )
332 .execute(&mut *self.connection().await?)
333 .await?;
334 Ok(())
335 }
336
337 async fn generate_key_if_not_exists(&mut self) -> Result<(), ServerKeyError> {
340 if self.fetch_active_server_key().await.is_ok() {
341 Ok(())
342 } else {
343 self.rotate_server_keys().await
344 }
345 }
346}
347
348impl<T: Database> ServerKeyQueries for T {}
349
350impl<'c> TransactedServerKeyQueries for TransactedDb<'c> {}