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<Result<bool, Self::Error>>;
fn signature_verify(
&mut self,
algorithm: Option<Algorithm>,
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<dyn std::error::Error>> {
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)]
enum MyError {
Verify(PrepareVerifyError),
Algorithm,
Decode,
Key,
}
impl std::fmt::Display for MyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Verify(_) => f.write_str("Failed to verify signature"),
Self::Algorithm => f.write_str("Unsupported algorithm"),
Self::Decode => f.write_str("Couldn't decode signature"),
Self::Key => f.write_str("Invalid key"),
}
}
}
impl std::error::Error for MyError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Verify(v) => Some(v),
Self::Algorithm | Self::Decode | Self::Key => None,
}
}
}
impl From<PrepareVerifyError> for MyError {
fn from(value: PrepareVerifyError) -> Self {
Self::Verify(value)
}
}
impl ResponseError for MyError {
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}
fn error_response(&self) -> HttpResponse {
HttpResponse::BadRequest().finish()
}
}