1use std::{collections::BTreeMap, fmt};
2
3use axum::extract::State;
4use ruma::{
5 OwnedUserId, UserId,
6 api::client::keys::upload_signatures::v3::{Failure, FailureErrorCode, Request, Response},
7};
8use serde::{
9 Deserialize, Deserializer,
10 de::{DeserializeSeed, IgnoredAny, MapAccess, Visitor},
11};
12use serde_json::{error::Category, json, value::RawValue};
13use tuwunel_core::{
14 Error, Result, debug, err,
15 smallvec::SmallVec,
16 utils::{IterStream, ReadyExt, stream::BroadbandExt},
17 warn,
18};
19use tuwunel_service::Services;
20
21use crate::Ruma;
22
23type Failures = BTreeMap<OwnedUserId, BTreeMap<String, Failure>>;
24type Rejected = (FailureErrorCode, String);
25type Signatures = SmallVec<[(String, String); 1]>;
26
27struct ObjectField<'a>(&'a str);
28
29struct ObjectFieldVisitor<'a>(&'a str);
30
31struct SignaturePairs(Signatures);
32
33struct SignaturePairsVisitor;
34
35pub(crate) async fn upload_signatures_route(
39 State(services): State<crate::State>,
40 body: Ruma<Request>,
41) -> Result<Response> {
42 let sender_user = body.sender_user();
43
44 if body.signed_keys.is_empty() {
45 debug!("Empty signed_keys sent in key signature upload");
46 return Ok(Response::new());
47 }
48
49 let failures = body
50 .signed_keys
51 .iter()
52 .flat_map(|(user_id, keys)| {
53 keys.iter()
54 .map(move |(key_id, key)| (user_id.as_ref(), key_id, key))
55 })
56 .stream()
57 .broad_filter_map(async |(user_id, key_id, key)| {
58 sign_key(&services, sender_user, user_id, key_id, key)
59 .await
60 .err()
61 .map(|rejected| (user_id, key_id, rejected))
62 })
63 .ready_fold(
64 Ok(Failures::new()),
65 |failures: Result<Failures>, (user_id, key_id, rejected)| {
66 let mut failures = failures?;
67 let failure = failure(rejected)?;
68
69 failures
70 .entry(user_id.to_owned())
71 .or_default()
72 .insert(key_id.to_owned(), failure);
73
74 Ok(failures)
75 },
76 )
77 .await?;
78
79 Ok(Response { failures })
80}
81
82async fn sign_key(
83 services: &Services,
84 sender_user: &UserId,
85 user_id: &UserId,
86 key_id: &str,
87 key: &RawValue,
88) -> Result<(), Rejected> {
89 let signatures = signatures_from_key(sender_user, key)
90 .map_err(|error| (FailureErrorCode::InvalidSignature, error))?;
91
92 services
93 .users
94 .sign_key(user_id, key_id, signatures, sender_user)
95 .await
96 .map_err(|error| match error {
97 | Error::Signatures(_) => (
98 FailureErrorCode::InvalidSignature,
99 "Signature does not verify against the stored key.".to_owned(),
100 ),
101 | error => {
102 if !matches!(&error, Error::Request(..)) {
103 warn!(?error, "Failed to upload key signature");
104 }
105
106 (FailureErrorCode::from(error.kind().to_string()), error.sanitized_message())
107 },
108 })
109}
110
111fn signatures_from_key(sender_user: &UserId, key: &RawValue) -> Result<Signatures, String> {
112 let signatures = object_field(key.get(), "signatures")
113 .map_err(|error| {
114 if matches!(error.classify(), Category::Data) {
115 String::from("The signed key must be an object.")
116 } else {
117 format!("Invalid signed key JSON: {error}")
118 }
119 })?
120 .ok_or_else(|| String::from("No signature from the uploading user."))?;
121
122 let signatures = object_field(signatures.get(), sender_user.as_str())
123 .map_err(|_| String::from("The signatures field must be an object."))?
124 .ok_or_else(|| String::from("No signature from the uploading user."))?;
125
126 if !signatures.get().trim_start().starts_with('{') {
127 return Err(String::from("Signatures from the uploading user must be an object."));
128 }
129
130 let SignaturePairs(signatures) = serde_json::from_str(signatures.get())
131 .map_err(|_| String::from("Signature values must be strings."))?;
132
133 (!signatures.is_empty())
134 .then_some(signatures)
135 .ok_or_else(|| String::from("No signature from the uploading user."))
136}
137
138fn failure((errcode, error): Rejected) -> Result<Failure> {
139 serde_json::from_value(json!({ "errcode": errcode, "error": error }))
140 .map_err(|error| err!(SerdeDe("Failed to encode signature upload failure: {error}")))
141}
142
143fn object_field<'de>(json: &'de str, field: &str) -> serde_json::Result<Option<&'de RawValue>> {
144 let mut deserializer = serde_json::Deserializer::from_str(json);
145 let value = ObjectField(field).deserialize(&mut deserializer)?;
146
147 deserializer.end()?;
148 Ok(value)
149}
150
151impl<'de> DeserializeSeed<'de> for ObjectField<'_> {
152 type Value = Option<&'de RawValue>;
153
154 fn deserialize<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>
155 where
156 D: Deserializer<'de>,
157 {
158 deserializer.deserialize_map(ObjectFieldVisitor(self.0))
159 }
160}
161
162impl<'de> Visitor<'de> for ObjectFieldVisitor<'_> {
163 type Value = Option<&'de RawValue>;
164
165 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
166 formatter.write_str("a JSON object")
167 }
168
169 fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
170 where
171 A: MapAccess<'de>,
172 {
173 let mut value = None;
174
175 while let Some(name) = map.next_key::<String>()? {
176 if name == self.0 {
177 value = Some(map.next_value()?);
178 } else {
179 map.next_value::<IgnoredAny>()?;
180 }
181 }
182
183 Ok(value)
184 }
185}
186
187impl<'de> Deserialize<'de> for SignaturePairs {
188 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
189 where
190 D: Deserializer<'de>,
191 {
192 deserializer.deserialize_map(SignaturePairsVisitor)
193 }
194}
195
196impl<'de> Visitor<'de> for SignaturePairsVisitor {
197 type Value = SignaturePairs;
198
199 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
200 formatter.write_str("an object containing string signatures")
201 }
202
203 fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
204 where
205 A: MapAccess<'de>,
206 {
207 let mut signatures = Signatures::new();
208
209 while let Some((key_id, signature)) = map.next_entry::<String, String>()? {
210 if let Some(index) = signatures
211 .iter()
212 .position(|(existing, _)| existing == &key_id)
213 {
214 signatures[index].1 = signature;
215 } else {
216 signatures.push((key_id, signature));
217 }
218 }
219
220 Ok(SignaturePairs(signatures))
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use ruma::user_id;
227 use serde_json::value::to_raw_value;
228
229 use super::*;
230
231 #[test]
232 fn extracts_only_uploading_users_signatures() {
233 let sender_user = user_id!("@alice:example.com");
234 let key = to_raw_value(&json!({
235 "signatures": {
236 "@alice:example.com": { "ed25519:ALICE": "alice-signature" },
237 "@bob:example.com": { "ed25519:BOB": "bob-signature" },
238 },
239 }))
240 .expect("signed key should serialize");
241
242 let signatures = signatures_from_key(sender_user, &key)
243 .expect("the uploading user's signature should be extracted");
244
245 assert_eq!(signatures.as_slice(), &[(
246 "ed25519:ALICE".to_owned(),
247 "alice-signature".to_owned()
248 )]);
249 }
250
251 #[test]
252 fn rejects_empty_uploading_user_signatures() {
253 let sender_user = user_id!("@alice:example.com");
254 let key = to_raw_value(&json!({ "signatures": { "@alice:example.com": {} } }))
255 .expect("signed key should serialize");
256
257 let error = signatures_from_key(sender_user, &key)
258 .expect_err("an empty signature map should be rejected");
259
260 assert_eq!(error, "No signature from the uploading user.");
261 }
262
263 #[test]
264 fn rejects_missing_uploading_user_signatures() {
265 let sender_user = user_id!("@alice:example.com");
266 let key = to_raw_value(&json!({ "user_id": "@alice:example.com" }))
267 .expect("signed key should serialize");
268
269 let error = signatures_from_key(sender_user, &key)
270 .expect_err("a missing signature map should be rejected");
271
272 assert_eq!(error, "No signature from the uploading user.");
273 }
274
275 #[test]
276 fn rejects_non_object_signed_key() {
277 let sender_user = user_id!("@alice:example.com");
278 let key = to_raw_value(&json!([])).expect("signed key should serialize");
279
280 let error = signatures_from_key(sender_user, &key)
281 .expect_err("a non-object signed key should be rejected");
282
283 assert_eq!(error, "The signed key must be an object.");
284 }
285
286 #[test]
287 fn rejects_non_string_signature() {
288 let sender_user = user_id!("@alice:example.com");
289 let key = to_raw_value(&json!({
290 "signatures": { "@alice:example.com": { "ed25519:ALICE": 7 } },
291 }))
292 .expect("signed key should serialize");
293
294 let error = signatures_from_key(sender_user, &key)
295 .expect_err("a non-string signature should be rejected");
296
297 assert_eq!(error, "Signature values must be strings.");
298 }
299
300 #[test]
301 fn serializes_typed_failure() {
302 let failure = failure((
303 FailureErrorCode::InvalidSignature,
304 "Signature does not verify.".to_owned(),
305 ))
306 .expect("failure should deserialize");
307
308 let failure = serde_json::to_value(failure).expect("failure should serialize");
309
310 assert_eq!(failure["errcode"], "M_INVALID_SIGNATURE");
311 assert_eq!(failure["error"], "Signature does not verify.");
312 }
313}