use actix_web::{http::StatusCode, web, App, HttpRequest, HttpResponse, HttpServer, ResponseError}; use base64::{engine::general_purpose::STANDARD, Engine}; use http_signature_normalization_actix::{digest::ring::Sha256, prelude::*}; use std::future::{ready, Ready}; use tracing::info; use tracing_actix_web::TracingLogger; use tracing_error::ErrorLayer; use tracing_subscriber::{layer::SubscriberExt, EnvFilter}; #[derive(Clone, Debug)] struct MyVerify; impl SignatureVerify for MyVerify { type Error = MyError; type Future = Ready>; fn signature_verify( &mut self, algorithm: Option, key_id: String, signature: String, signing_string: String, ) -> Self::Future { match algorithm { Some(Algorithm::Hs2019) => (), _ => return ready(Err(MyError::Algorithm)), }; if key_id != "my-key-id" { return ready(Err(MyError::Key)); } let decoded = match STANDARD.decode(&signature) { Ok(decoded) => decoded, Err(_) => return ready(Err(MyError::Decode)), }; info!("Signing String\n{}", signing_string); ready(Ok(decoded == signing_string.as_bytes())) } } async fn index( _: DigestVerified, sig_verified: SignatureVerified, req: HttpRequest, _body: web::Bytes, ) -> &'static str { info!("Verified request for {}", sig_verified.key_id()); info!("{:?}", req); "Eyyyyup" } #[actix_rt::main] async fn main() -> Result<(), Box> { let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); let subscriber = tracing_subscriber::Registry::default() .with(env_filter) .with(ErrorLayer::default()) .with(tracing_subscriber::fmt::layer()); tracing::subscriber::set_global_default(subscriber)?; let config = Config::new().require_header("accept").require_digest(); HttpServer::new(move || { App::new() .wrap(VerifyDigest::new(Sha256::new()).optional()) .wrap(VerifySignature::new(MyVerify, config.clone())) .wrap(TracingLogger::default()) .route("/", web::post().to(index)) }) .bind("127.0.0.1:8010")? .run() .await?; Ok(()) } #[derive(Debug, thiserror::Error)] enum MyError { #[error("Failed to verify, {0}")] Verify(#[from] PrepareVerifyError), #[error("Unsupported algorithm")] Algorithm, #[error("Couldn't decode signature")] Decode, #[error("Invalid key")] Key, } impl ResponseError for MyError { fn status_code(&self) -> StatusCode { StatusCode::BAD_REQUEST } fn error_response(&self) -> HttpResponse { HttpResponse::BadRequest().finish() } }