Add http signature verification
This commit is contained in:
		
							parent
							
								
									eea0577686
								
							
						
					
					
						commit
						9fdd3bec18
					
				
					 5 changed files with 92 additions and 13 deletions
				
			
		|  | @ -81,6 +81,9 @@ pub struct AcceptedActors { | |||
|     pub inbox: XsdAnyUri, | ||||
| 
 | ||||
|     pub endpoints: Endpoints, | ||||
| 
 | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     pub public_key: Option<PublicKey>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] | ||||
|  |  | |||
|  | @ -27,6 +27,12 @@ pub enum MyError { | |||
|     #[error("Couldn't parse the signature header")] | ||||
|     HeaderValidation(#[from] actix_web::http::header::InvalidHeaderValue), | ||||
| 
 | ||||
|     #[error("Couldn't decode base64")] | ||||
|     Base64(#[from] base64::DecodeError), | ||||
| 
 | ||||
|     #[error("Invalid algorithm provided to verifier")] | ||||
|     Algorithm, | ||||
| 
 | ||||
|     #[error("Wrong ActivityPub kind")] | ||||
|     Kind, | ||||
| 
 | ||||
|  | @ -50,6 +56,9 @@ pub enum MyError { | |||
| 
 | ||||
|     #[error("URI is missing domain field")] | ||||
|     Domain, | ||||
| 
 | ||||
|     #[error("Public key is missing")] | ||||
|     MissingKey, | ||||
| } | ||||
| 
 | ||||
| impl ResponseError for MyError { | ||||
|  |  | |||
							
								
								
									
										28
									
								
								src/inbox.rs
									
										
									
									
									
								
							
							
						
						
									
										28
									
								
								src/inbox.rs
									
										
									
									
									
								
							|  | @ -1,3 +1,9 @@ | |||
| use crate::{ | ||||
|     apub::{AcceptedActors, AcceptedObjects, ValidTypes}, | ||||
|     db_actor::{DbActor, DbQuery, Pool}, | ||||
|     error::MyError, | ||||
|     state::{State, UrlKind}, | ||||
| }; | ||||
| use activitystreams::{ | ||||
|     activity::apub::{Accept, Announce, Follow, Undo}, | ||||
|     context, | ||||
|  | @ -8,13 +14,6 @@ use actix_web::{client::Client, web, HttpResponse}; | |||
| use futures::join; | ||||
| use log::error; | ||||
| 
 | ||||
| use crate::{ | ||||
|     apub::{AcceptedActors, AcceptedObjects, ValidTypes}, | ||||
|     db_actor::{DbActor, DbQuery, Pool}, | ||||
|     error::MyError, | ||||
|     state::{State, UrlKind}, | ||||
| }; | ||||
| 
 | ||||
| pub async fn inbox( | ||||
|     db_actor: web::Data<Addr<DbActor>>, | ||||
|     state: web::Data<State>, | ||||
|  | @ -23,7 +22,12 @@ pub async fn inbox( | |||
| ) -> Result<HttpResponse, MyError> { | ||||
|     let input = input.into_inner(); | ||||
| 
 | ||||
|     let actor = fetch_actor(state.clone(), &client, &input.actor).await?; | ||||
|     let actor = fetch_actor( | ||||
|         state.clone().into_inner(), | ||||
|         client.clone().into_inner(), | ||||
|         &input.actor, | ||||
|     ) | ||||
|     .await?; | ||||
| 
 | ||||
|     match input.kind { | ||||
|         ValidTypes::Announce | ValidTypes::Create => { | ||||
|  | @ -217,15 +221,15 @@ async fn handle_follow( | |||
|     let client = client.into_inner(); | ||||
|     let accept2 = accept.clone(); | ||||
|     actix::Arbiter::spawn(async move { | ||||
|         let _ = deliver(&state.into_inner(), &client, actor_inbox, &accept2).await; | ||||
|         let _ = deliver(&state.into_inner(), &client.clone(), actor_inbox, &accept2).await; | ||||
|     }); | ||||
| 
 | ||||
|     Ok(response(accept)) | ||||
| } | ||||
| 
 | ||||
| async fn fetch_actor( | ||||
|     state: web::Data<State>, | ||||
|     client: &web::Data<Client>, | ||||
| pub async fn fetch_actor( | ||||
|     state: std::sync::Arc<State>, | ||||
|     client: std::sync::Arc<Client>, | ||||
|     actor_id: &XsdAnyUri, | ||||
| ) -> Result<AcceptedActors, MyError> { | ||||
|     if let Some(actor) = state.get_actor(actor_id).await { | ||||
|  |  | |||
							
								
								
									
										12
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								src/main.rs
									
										
									
									
									
								
							|  | @ -2,7 +2,9 @@ | |||
| use activitystreams::{actor::apub::Application, context, endpoint::EndpointProperties}; | ||||
| use actix_web::{client::Client, middleware::Logger, web, App, HttpServer, Responder}; | ||||
| use bb8_postgres::tokio_postgres; | ||||
| use http_signature_normalization_actix::prelude::{VerifyDigest, VerifySignature}; | ||||
| use rsa_pem::KeyExt; | ||||
| use sha2::{Digest, Sha256}; | ||||
| 
 | ||||
| mod apub; | ||||
| mod db_actor; | ||||
|  | @ -10,6 +12,7 @@ mod error; | |||
| mod inbox; | ||||
| mod label; | ||||
| mod state; | ||||
| mod verifier; | ||||
| mod webfinger; | ||||
| 
 | ||||
| use self::{ | ||||
|  | @ -18,6 +21,8 @@ use self::{ | |||
|     error::MyError, | ||||
|     label::ArbiterLabelFactory, | ||||
|     state::{State, UrlKind}, | ||||
|     verifier::MyVerify, | ||||
|     webfinger::RelayResolver, | ||||
| }; | ||||
| 
 | ||||
| async fn index() -> impl Responder { | ||||
|  | @ -84,6 +89,11 @@ async fn main() -> Result<(), anyhow::Error> { | |||
|         let client = Client::default(); | ||||
| 
 | ||||
|         App::new() | ||||
|             .wrap(VerifyDigest::new(Sha256::new())) | ||||
|             .wrap(VerifySignature::new( | ||||
|                 MyVerify(state.clone(), client.clone()), | ||||
|                 Default::default(), | ||||
|             )) | ||||
|             .wrap(Logger::default()) | ||||
|             .data(actor) | ||||
|             .data(state.clone()) | ||||
|  | @ -91,7 +101,7 @@ async fn main() -> Result<(), anyhow::Error> { | |||
|             .service(web::resource("/").route(web::get().to(index))) | ||||
|             .service(web::resource("/inbox").route(web::post().to(inbox::inbox))) | ||||
|             .service(web::resource("/actor").route(web::get().to(actor_route))) | ||||
|             .service(actix_webfinger::resource::<_, webfinger::RelayResolver>()) | ||||
|             .service(actix_webfinger::resource::<_, RelayResolver>()) | ||||
|     }) | ||||
|     .bind("127.0.0.1:8080")? | ||||
|     .run() | ||||
|  |  | |||
							
								
								
									
										53
									
								
								src/verifier.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/verifier.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,53 @@ | |||
| use crate::{error::MyError, state::State}; | ||||
| use actix_web::client::Client; | ||||
| use http_signature_normalization_actix::prelude::*; | ||||
| use rsa::{hash::Hashes, padding::PaddingScheme, PublicKey, RSAPublicKey}; | ||||
| use rsa_pem::KeyExt; | ||||
| use std::{future::Future, pin::Pin, sync::Arc}; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct MyVerify(pub State, pub Client); | ||||
| 
 | ||||
| impl SignatureVerify for MyVerify { | ||||
|     type Error = MyError; | ||||
|     type Future = Pin<Box<dyn Future<Output = Result<bool, Self::Error>>>>; | ||||
| 
 | ||||
|     fn signature_verify( | ||||
|         &mut self, | ||||
|         algorithm: Option<Algorithm>, | ||||
|         key_id: &str, | ||||
|         signature: &str, | ||||
|         signing_string: &str, | ||||
|     ) -> Self::Future { | ||||
|         let key_id = key_id.to_owned(); | ||||
|         let signature = signature.to_owned(); | ||||
|         let signing_string = signing_string.to_owned(); | ||||
| 
 | ||||
|         let state = Arc::new(self.0.clone()); | ||||
|         let client = Arc::new(self.1.clone()); | ||||
| 
 | ||||
|         Box::pin(async move { | ||||
|             let actor = crate::inbox::fetch_actor(state, client, &key_id.parse()?).await?; | ||||
| 
 | ||||
|             let public_key = actor.public_key.ok_or(MyError::MissingKey)?; | ||||
| 
 | ||||
|             let public_key = RSAPublicKey::from_pem_pkcs8(&public_key.public_key_pem)?; | ||||
| 
 | ||||
|             match algorithm { | ||||
|                 Some(Algorithm::Hs2019) => (), | ||||
|                 _ => return Err(MyError::Algorithm), | ||||
|             }; | ||||
| 
 | ||||
|             let decoded = base64::decode(signature)?; | ||||
| 
 | ||||
|             public_key.verify( | ||||
|                 PaddingScheme::PKCS1v15, | ||||
|                 Some(&Hashes::SHA2_256), | ||||
|                 &decoded, | ||||
|                 signing_string.as_bytes(), | ||||
|             )?; | ||||
| 
 | ||||
|             Ok(true) | ||||
|         }) | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		
		Reference in a new issue