Crate http_signature_normalization_actix_extractor

Source
Expand description

§Http Signature Normalization Actix Extractor

Experimental Extractor for request signatures

This library takes a different approach from the other implementation, which prefers Middlewares.

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
    */
    Ok(())
}

async fn protected(signed_request: Signed<String, Cfg, Sha256, Key>) -> &'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<Self, Self::Error> {
        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<bool, Self::Error> {
        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()
    }
}

Structs§

Config
Configuration for signing and verifying signatures
DigestPart
A parsed part of a Digest header
RequestSignature
Wraps an extractor and calculates a request signature hash alongside.
Signature
All the required pieces for validating a request signature
SignedRequest
The SignedRequest Signature scheme

Enums§

Algorithm
Kinds of algorithms
DeprecatedAlgorithm
Algorithms that may be present in an HTTP Signature’s algorithm field, but are considered deprecated due to security issues
Error
Errors produced by the Extractor
PrepareVerifyError
Error preparing a header for validation

Traits§

ConfigGenerator
Injects a customized Config type for signature verification
VerifyDigest
Verifies the Digest header from the request
VerifyKey
Extracts a key from the request

Type Aliases§

Signed
An alias to simplify extracting a signed request