# Http Signature Normalization Actix-Web Extractor _Experimental Extractor for request signatures_ - [crates.io](https://crates.io/crates/http-signature-normalization-actix-extractor) - [docs.rs](https://docs.rs/http-signature-normalization-actix-extractor) - [Hit me up on Mastodon](https://masto.asonix.dog/@asonix) Http Signature Normalization is a minimal-dependency crate for producing HTTP Signatures with user-provided signing and verification. The API is simple; there's a series of steps for creation and verification with types that ensure reasonable usage. This library takes a different approach from the other implementation, which prefers Middlewares. ## Usage This crate provides extensions the ClientRequest type from Actix Web, and provides middlewares for verifying HTTP Signatures, and optionally, Digest headers #### First, add this crate to your dependencies ```toml actix-rt = "2.7.0" actix-web = "4.0.0" thiserror = "1" http-signature-normalization-actix-extractor = "0.1.0" sha2 = "0.10" ``` #### Then, use it in your client ```rust use actix_web::{http::StatusCode, web, App, HttpRequest, HttpResponse, HttpServer, ResponseError}; use http_signature_normalization_actix_extractor::{ Algorithm, Config, ConfigGenerator, DeprecatedAlgorithm, Signed, VerifyKey, }; use sha2::Sha256; #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| App::new().route("/", web::post().to(protected))) .bind("127.0.0.1:8010")? .run() .await } async fn protected(signed_request: Signed) -> &'static str { let (value, signature) = signed_request.into_parts(); println!("{}", value); println!("{:#?}", signature); "hewwo, mr obama" } pub struct Cfg; #[derive(Debug)] pub struct Key; #[derive(Debug, thiserror::Error)] pub enum VerifyError { #[error("Unsupported algorithm")] Algorithm, #[error("Couldn't decode signature")] Decode, #[error("Invalid key")] Key, } impl ConfigGenerator for Cfg { fn config() -> Config { Config::new().require_header("accept") } } #[async_trait::async_trait(?Send)] impl VerifyKey for Key { type Error = VerifyError; async fn init( _: &HttpRequest, key_id: &str, algorithm: Option<&Algorithm>, ) -> Result { match algorithm { Some(Algorithm::Hs2019 | Algorithm::Deprecated(DeprecatedAlgorithm::RsaSha256)) => (), _ => return Err(VerifyError::Algorithm), }; if key_id != "my-key-id" { return Err(VerifyError::Key); } Ok(Key) } fn verify(&mut self, signature: &str, signing_string: &str) -> Result { use subtle::ConstantTimeEq; let decoded = base64::decode(&signature).map_err(|_| VerifyError::Decode)?; Ok(decoded.ct_eq(signing_string.as_bytes()).into()) } } impl ResponseError for VerifyError { fn status_code(&self) -> StatusCode { StatusCode::BAD_REQUEST } fn error_response(&self) -> HttpResponse { HttpResponse::BadRequest().finish() } } ``` ### Contributing Feel free to open issues for anything you find an issue with. Please note that any contributed code will be licensed under the AGPLv3. ### License Copyright © 2022 Riley Trautman HTTP Signature Normalization Actix Extractor is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. HTTP Signature Normalization Actix Extractor is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. This file is part of HTTP Signature Normalization Actix Extractor. You should have received a copy of the GNU General Public License along with HTTP Signature Normalization Actix Extractor. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/).