1#![deny(missing_docs)]
7
8use actix_web::{
9 error::BlockingError,
10 http::Uri,
11 web::{self, ServiceConfig},
12 FromRequest, HttpRequest, HttpResponse, ResponseError,
13};
14use apub_core::{
15 digest::{Digest, DigestBuilder, DigestFactory},
16 ingest::{is_local, Authority, Ingest, IngestFactory},
17 repo::{Dereference, Repo, RepoFactory},
18 session::SessionFactory,
19 signature::{PrivateKeyBuilder, Verify, VerifyBuilder, VerifyFactory},
20};
21use apub_privatekey::{PrivateKeyRepo, PrivateKeyRepoFactory};
22use apub_publickey::{PublicKeyClient, PublicKeyError, PublicKeyRepo, PublicKeyRepoFactory};
23use http_signature_normalization_actix::{
24 digest::DigestName,
25 prelude::{
26 Algorithm, DeprecatedAlgorithm, DigestPart, DigestVerify, SignatureVerified,
27 SignatureVerify, VerifyDigest, VerifySignature,
28 },
29};
30use std::{future::Future, marker::PhantomData, pin::Pin};
31use url::{Host, Url};
32
33#[derive(Clone, Debug)]
35pub struct Config {
36 pub local_host: Host<String>,
40
41 pub local_port: Option<u16>,
45
46 pub scheme: String,
50
51 pub server_actor_id: Url,
53}
54
55impl Config {
56 fn build_uri(&self, uri: &Uri) -> String {
57 if let Some(port) = self.local_port {
58 format!("{}{}:{}{}", self.scheme, self.local_host, port, uri)
59 } else {
60 format!("{}{}{}", self.scheme, self.local_host, uri)
61 }
62 }
63
64 pub fn is_local(&self, url: &Url) -> bool {
66 is_local(&self.local_host, self.local_port, url)
67 }
68}
69
70pub use http_signature_normalization_actix::Config as SignatureConfig;
72
73#[derive(Debug, thiserror::Error)]
75pub enum VerifyError {
76 #[error("Unsupported algorithm: {0}")]
78 Algorithm(String),
79
80 #[error("Invalid Key ID: {0}")]
82 KeyId(String),
83
84 #[error("Actor {0} is not public key's owner")]
86 InvalidOwner(Url),
87
88 #[error("Public Key {0} is not the expected key")]
90 InvalidKey(Url),
91
92 #[error("No key associated with Key ID")]
94 KeyNotFound,
95
96 #[error("Key verification panicked")]
98 Canceled,
99}
100
101type RFactErr<R> = <<R as RepoFactory>::Repo as Repo>::Error;
102type PubKeyRepoErr<P> = <<P as PublicKeyRepoFactory>::PublicKeyRepo as PublicKeyRepo>::Error;
103type VFactErr<V> = <<V as VerifyFactory>::Verify as Verify>::Error;
104type PrivKeyRepoErr<P> = <<P as PrivateKeyRepoFactory>::PrivateKeyRepo as PrivateKeyRepo>::Error;
105type PrivKeyError<R> = <<R as RepoFactory>::Crypto as PrivateKeyBuilder>::Error;
106
107pub fn inbox<A, I, V, E>(
139 config: Config,
140 ingest_factory: I,
141 verifier: V,
142 signature_config: SignatureConfig,
143 require_signature: bool,
144) -> impl FnOnce(&mut ServiceConfig)
145where
146 A: for<'de> serde::de::Deserialize<'de> + 'static,
147 I: IngestFactory<A> + PrivateKeyRepoFactory + RepoFactory + SessionFactory + 'static,
148 I::PrivateKeyRepo: PrivateKeyRepo<PrivateKey = I::Crypto>,
149 <I::Ingest as Ingest<A>>::ActorId: FromRequest + AsRef<Url>,
150 <I::Ingest as Ingest<A>>::Error: From<<I::Repo as Repo>::Error>,
151 V: RepoFactory
152 + SessionFactory
153 + PublicKeyRepoFactory
154 + PrivateKeyRepoFactory
155 + VerifyFactory
156 + DigestFactory
157 + Clone
158 + 'static,
159 V::PrivateKeyRepo: PrivateKeyRepo<PrivateKey = V::Crypto>,
160 V::Crypto: PrivateKeyBuilder,
161 <V as DigestFactory>::Digest: Clone,
162 E: ResponseError
163 + From<VerifyError>
164 + From<VFactErr<V>>
165 + From<PublicKeyError<PubKeyRepoErr<V>, RFactErr<V>>>
166 + From<PrivKeyRepoErr<V>>
167 + From<PrivKeyError<V>>
168 + 'static,
169{
170 move |service_config: &mut ServiceConfig| {
171 let signature = VerifySignature::new(
172 VerifyMiddleware::<V, E>::new(verifier, config),
173 signature_config,
174 );
175
176 let digest = VerifyDigest::new(DigestWrapper(V::Digest::build()));
177
178 let (signature, digest) = if require_signature {
179 (signature, digest)
180 } else {
181 (signature.optional(), digest.optional())
182 };
183
184 service_config.service(
185 web::scope("")
186 .app_data(web::Data::new(ingest_factory))
187 .wrap(digest)
188 .wrap(signature)
189 .route("", web::post().to(inbox_handler::<A, I>)),
190 );
191 }
192}
193
194pub fn serve_objects<D, R, V, E>(
226 config: Config,
227 repo: R,
228 verifier: V,
229 signature_config: SignatureConfig,
230 require_signature: bool,
231) -> impl FnOnce(&mut ServiceConfig)
232where
233 D: Dereference + From<Url> + 'static,
234 <D as Dereference>::Output: serde::ser::Serialize,
235 R: Repo + 'static,
236 V: RepoFactory
237 + SessionFactory
238 + PublicKeyRepoFactory
239 + PrivateKeyRepoFactory
240 + VerifyFactory
241 + Clone
242 + 'static,
243 V::PrivateKeyRepo: PrivateKeyRepo<PrivateKey = V::Crypto>,
244 V::Crypto: PrivateKeyBuilder,
245 E: ResponseError
246 + From<VerifyError>
247 + From<VFactErr<V>>
248 + From<PublicKeyError<PubKeyRepoErr<V>, RFactErr<V>>>
249 + From<PrivKeyRepoErr<V>>
250 + From<PrivKeyError<V>>
251 + 'static,
252{
253 move |service_config: &mut ServiceConfig| {
254 let verifier = VerifySignature::new(
255 VerifyMiddleware::<V, E>::new(verifier, config.clone()),
256 signature_config,
257 );
258
259 let verifier = if require_signature {
260 verifier
261 } else {
262 verifier.optional()
263 };
264
265 service_config.service(
266 web::scope("/{object}")
267 .app_data(web::Data::new(repo))
268 .app_data(web::Data::new(config))
269 .wrap(verifier)
270 .route("", web::get().to(serve_object_handler::<D, R>)),
271 );
272 }
273}
274
275async fn inbox_handler<A, I>(
276 ingest_factory: web::Data<I>,
277 authority: Option<SignatureVerified>,
278 activity: web::Json<A>,
279 actor_id: <I::Ingest as Ingest<A>>::ActorId,
280) -> HttpResponse
281where
282 A: for<'de> serde::de::Deserialize<'de> + 'static,
283 I: IngestFactory<A> + PrivateKeyRepoFactory + RepoFactory + SessionFactory,
284 I::PrivateKeyRepo: PrivateKeyRepo<PrivateKey = I::Crypto>,
285 <I::Ingest as Ingest<A>>::ActorId: FromRequest + AsRef<Url>,
286 <I::Ingest as Ingest<A>>::Error: From<<I::Repo as Repo>::Error>,
287{
288 let url = actor_id.as_ref();
289
290 let private_key_repo = ingest_factory.build_private_key_repo();
291 let private_key = match private_key_repo.fetch(url).await {
292 Ok(private_key) => private_key,
293 Err(_) => return HttpResponse::BadRequest().finish(),
294 };
295
296 let activity = activity.into_inner();
297 let remote_repo = ingest_factory.build_repo(private_key);
298 let mut session = ingest_factory.build_session();
299 let ingest = ingest_factory.build_ingest();
300
301 if let Some(auth) = authority {
302 if let Ok(url) = auth.key_id().parse() {
303 if ingest
304 .ingest(
305 Authority::Actor(url),
306 actor_id,
307 &activity,
308 &remote_repo,
309 &mut session,
310 )
311 .await
312 .is_ok()
313 {
314 return HttpResponse::Accepted().finish();
315 }
316 }
317 } else if ingest
318 .ingest(
319 Authority::None,
320 actor_id,
321 &activity,
322 &remote_repo,
323 &mut session,
324 )
325 .await
326 .is_ok()
327 {
328 return HttpResponse::Accepted().finish();
329 }
330
331 HttpResponse::BadRequest().finish()
332}
333
334async fn serve_object_handler<D, R>(
335 req: HttpRequest,
336 repo: web::Data<R>,
337 config: web::Data<Config>,
338) -> HttpResponse
339where
340 D: Dereference + From<Url> + 'static,
341 <D as Dereference>::Output: serde::ser::Serialize,
342 R: Repo + 'static,
343{
344 let url: Url = match config.build_uri(req.uri()).parse() {
345 Ok(url) => url,
346 Err(_) => return HttpResponse::BadRequest().finish(),
347 };
348
349 let res = repo.fetch(D::from(url), ()).await;
350
351 match res {
352 Ok(Some(object)) => HttpResponse::Ok()
353 .content_type("application/activity+json")
354 .json(object),
355 Ok(None) => HttpResponse::NotFound().finish(),
356 Err(_) => HttpResponse::InternalServerError().finish(),
357 }
358}
359
360#[derive(Clone)]
361struct DigestWrapper<D>(D);
362
363impl<D> DigestName for DigestWrapper<D>
364where
365 D: Digest,
366{
367 const NAME: &'static str = D::NAME;
368}
369
370impl<D> DigestVerify for DigestWrapper<D>
371where
372 D: Digest + Clone,
373{
374 fn update(&mut self, part: &[u8]) {
375 self.0.update(part);
376 }
377
378 fn verify(&mut self, digests: &[DigestPart]) -> bool {
379 if let Some(part) = digests.iter().find(|part| part.algorithm == D::NAME) {
380 return self.0.clone().verify(&part.digest);
381 }
382
383 false
384 }
385}
386
387struct VerifyMiddleware<V, E> {
388 verifier: V,
389 config: Config,
390 _error: PhantomData<fn() -> E>,
391}
392
393impl<V, E> Clone for VerifyMiddleware<V, E>
394where
395 V: Clone,
396{
397 fn clone(&self) -> Self {
398 Self {
399 verifier: self.verifier.clone(),
400 config: self.config.clone(),
401 _error: PhantomData,
402 }
403 }
404}
405
406impl<V, E> VerifyMiddleware<V, E>
407where
408 V: VerifyFactory,
409{
410 fn new(verifier: V, config: Config) -> Self {
411 Self {
412 verifier,
413 config,
414 _error: PhantomData,
415 }
416 }
417}
418
419async fn verify<V, E>(
420 verifier: &V,
421 config: Config,
422 algorithm: Option<Algorithm>,
423 key_id: String,
424 signature: String,
425 signing_string: String,
426) -> Result<bool, E>
427where
428 V: RepoFactory + SessionFactory + VerifyFactory + PublicKeyRepoFactory + PrivateKeyRepoFactory,
429 V::PrivateKeyRepo: PrivateKeyRepo<PrivateKey = V::Crypto>,
430 V::Crypto: PrivateKeyBuilder,
431 <<V as VerifyFactory>::Verify as Verify>::Error: 'static,
432 E: ResponseError
433 + From<VerifyError>
434 + From<VFactErr<V>>
435 + From<PublicKeyError<PubKeyRepoErr<V>, RFactErr<V>>>
436 + From<PrivKeyRepoErr<V>>
437 + From<PrivKeyError<V>>
438 + 'static,
439{
440 match algorithm {
441 None | Some(Algorithm::Hs2019 | Algorithm::Deprecated(DeprecatedAlgorithm::RsaSha256)) => {}
442 Some(other) => return Err(VerifyError::Algorithm(other.to_string()).into()),
443 };
444
445 let key_id = key_id.parse().map_err(|_| VerifyError::KeyId(key_id))?;
446
447 let private_key_repo = verifier.build_private_key_repo();
448
449 let crypto = private_key_repo.fetch(&config.server_actor_id).await?;
450
451 let session = verifier.build_session();
452 let http_repo = verifier.build_repo(crypto);
453 let public_key_repo = verifier.public_key_repo();
454
455 let public_key_client = PublicKeyClient::new(
456 public_key_repo,
457 http_repo,
458 config.local_host,
459 config.local_port,
460 );
461
462 let public_key = public_key_client
463 .find(&key_id, session)
464 .await
465 .map_err(E::from)?
466 .ok_or(VerifyError::KeyNotFound)?;
467
468 let verified = web::block(move || {
469 let verified =
470 <<V as VerifyFactory>::Verify as VerifyBuilder>::build(&public_key.public_key_pem)?
471 .verify(&signing_string, &signature)?;
472
473 Ok(verified) as Result<bool, VFactErr<V>>
474 })
475 .await
476 .map_err(VerifyError::from)??;
477
478 Ok(verified)
479}
480
481impl<V, E> SignatureVerify for VerifyMiddleware<V, E>
482where
483 V: RepoFactory
484 + SessionFactory
485 + PublicKeyRepoFactory
486 + PrivateKeyRepoFactory
487 + VerifyFactory
488 + Clone
489 + 'static,
490 V::PrivateKeyRepo: PrivateKeyRepo<PrivateKey = V::Crypto>,
491 V::Crypto: PrivateKeyBuilder,
492 E: ResponseError
493 + From<VerifyError>
494 + From<VFactErr<V>>
495 + From<PublicKeyError<PubKeyRepoErr<V>, RFactErr<V>>>
496 + From<PrivKeyRepoErr<V>>
497 + From<PrivKeyError<V>>
498 + 'static,
499{
500 type Error = E;
501 type Future = Pin<Box<dyn Future<Output = Result<bool, Self::Error>>>>;
502
503 fn signature_verify(
504 &mut self,
505 algorithm: Option<Algorithm>,
506 key_id: String,
507 signature: String,
508 signing_string: String,
509 ) -> Self::Future {
510 let verifier = self.verifier.clone();
511 let config = self.config.clone();
512
513 Box::pin(async move {
514 verify::<V, E>(
515 &verifier,
516 config,
517 algorithm,
518 key_id,
519 signature,
520 signing_string,
521 )
522 .await
523 })
524 }
525}
526
527impl From<BlockingError> for VerifyError {
528 fn from(_: BlockingError) -> Self {
529 VerifyError::Canceled
530 }
531}