mirror of
https://git.asonix.dog/asonix/http-signature-normalization.git
synced 2025-02-16 21:45:15 +00:00
Add digest header as feature
This commit is contained in:
parent
ef2a4e338f
commit
0fd087ced5
11 changed files with 816 additions and 206 deletions
|
@ -11,12 +11,25 @@ edition = "2018"
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
digest = ["base64", "futures"]
|
digest = ["base64", "futures"]
|
||||||
|
sha-2 = ["digest", "sha2"]
|
||||||
|
sha-3 = ["digest", "sha3"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "server"
|
||||||
|
required-features = ["sha-2"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "client"
|
||||||
|
required-features = ["sha-2"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = "1.0"
|
actix-web = "1.0"
|
||||||
base64 = { version = "0.10", optional = true }
|
base64 = { version = "0.10", optional = true }
|
||||||
http-signature-normalization = { version = "0.1.0", path = ".." }
|
failure = "0.1"
|
||||||
futures = { version = "0.1", optional = true }
|
futures = { version = "0.1", optional = true }
|
||||||
|
http-signature-normalization = { version = "0.1.0", path = ".." }
|
||||||
|
sha2 = { version = "0.8", optional = true }
|
||||||
|
sha3 = { version = "0.8", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix = "0.8"
|
actix = "0.8"
|
||||||
|
|
|
@ -2,18 +2,24 @@ use actix::System;
|
||||||
use actix_web::client::Client;
|
use actix_web::client::Client;
|
||||||
use futures::future::{lazy, Future};
|
use futures::future::{lazy, Future};
|
||||||
use http_signature_normalization_actix::prelude::*;
|
use http_signature_normalization_actix::prelude::*;
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
System::new("client-example")
|
System::new("client-example")
|
||||||
.block_on(lazy(|| {
|
.block_on(lazy(|| {
|
||||||
let config = Config::default();
|
let config = Config::default();
|
||||||
|
let mut digest = Sha256::new();
|
||||||
|
|
||||||
Client::default()
|
Client::default()
|
||||||
.get("http://127.0.0.1:8010/")
|
.post("http://127.0.0.1:8010/")
|
||||||
.header("User-Agent", "Actix Web")
|
.header("User-Agent", "Actix Web")
|
||||||
.authorization_signature::<_, MyError, _>(&config, "my-key-id", |s| {
|
.authorization_signature_with_digest::<_, MyError, _, _, _>(
|
||||||
Ok(s.as_bytes().to_vec())
|
&config,
|
||||||
})
|
"my-key-id",
|
||||||
|
&mut digest,
|
||||||
|
"Hewwo owo",
|
||||||
|
|s| Ok(s.as_bytes().to_vec()),
|
||||||
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.send()
|
.send()
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use actix::System;
|
use actix::System;
|
||||||
use actix_web::{web, App, HttpRequest, HttpServer, ResponseError};
|
use actix_web::{web, App, HttpRequest, HttpServer, ResponseError};
|
||||||
use http_signature_normalization_actix::{prelude::*, verify::Algorithm};
|
use http_signature_normalization_actix::{prelude::*, verify::Algorithm};
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
fn index((req, config): (HttpRequest, web::Data<Config>)) -> Result<&'static str, MyError> {
|
fn index((req, config): (HttpRequest, web::Data<Config>)) -> Result<&'static str, MyError> {
|
||||||
|
@ -26,7 +27,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
.data(Config::default())
|
.data(Config::default())
|
||||||
.route("/", web::get().to(index))
|
.wrap(VerifyDigest::new(Sha256::new()))
|
||||||
|
.route("/", web::post().to(index))
|
||||||
})
|
})
|
||||||
.bind("127.0.0.1:8010")?
|
.bind("127.0.0.1:8010")?
|
||||||
.start();
|
.start();
|
||||||
|
|
165
http-signature-normalization-actix/src/digest/middleware.rs
Normal file
165
http-signature-normalization-actix/src/digest/middleware.rs
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
use actix_web::{
|
||||||
|
dev::{Body, Service, ServiceRequest, ServiceResponse, Transform},
|
||||||
|
error::PayloadError,
|
||||||
|
http::header::HeaderValue,
|
||||||
|
web::Bytes,
|
||||||
|
HttpMessage, HttpResponse, ResponseError,
|
||||||
|
};
|
||||||
|
use failure::Fail;
|
||||||
|
use futures::{
|
||||||
|
future::{err, ok, Either, FutureResult},
|
||||||
|
stream::once,
|
||||||
|
Future, Poll, Stream,
|
||||||
|
};
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use super::{DigestPart, DigestVerify};
|
||||||
|
|
||||||
|
pub struct VerifyDigest<T>(bool, T);
|
||||||
|
pub struct VerifyMiddleware<T, S>(Rc<RefCell<S>>, bool, T);
|
||||||
|
|
||||||
|
#[derive(Debug, Fail)]
|
||||||
|
#[fail(display = "Error in upstream middleware")]
|
||||||
|
pub struct UpstreamError;
|
||||||
|
|
||||||
|
#[derive(Debug, Fail)]
|
||||||
|
#[fail(display = "Error verifying digest")]
|
||||||
|
pub struct VerifyError;
|
||||||
|
|
||||||
|
impl<T> VerifyDigest<T>
|
||||||
|
where
|
||||||
|
T: DigestVerify + Clone,
|
||||||
|
{
|
||||||
|
pub fn new(verify_digest: T) -> Self {
|
||||||
|
VerifyDigest(true, verify_digest)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optional(self) -> Self {
|
||||||
|
VerifyDigest(false, self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, S> Transform<S> for VerifyDigest<T>
|
||||||
|
where
|
||||||
|
T: DigestVerify + Clone + 'static,
|
||||||
|
S: Service<Request = ServiceRequest, Response = ServiceResponse<Body>> + 'static,
|
||||||
|
S::Error: 'static,
|
||||||
|
{
|
||||||
|
type Request = ServiceRequest;
|
||||||
|
type Response = ServiceResponse<Body>;
|
||||||
|
type Error = actix_web::Error;
|
||||||
|
type Transform = VerifyMiddleware<T, S>;
|
||||||
|
type InitError = ();
|
||||||
|
type Future = FutureResult<Self::Transform, Self::InitError>;
|
||||||
|
|
||||||
|
fn new_transform(&self, service: S) -> Self::Future {
|
||||||
|
ok(VerifyMiddleware(
|
||||||
|
Rc::new(RefCell::new(service)),
|
||||||
|
self.0,
|
||||||
|
self.1.clone(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, S> Service for VerifyMiddleware<T, S>
|
||||||
|
where
|
||||||
|
T: DigestVerify + Clone + 'static,
|
||||||
|
S: Service<Request = ServiceRequest, Response = ServiceResponse<Body>> + 'static,
|
||||||
|
S::Error: 'static,
|
||||||
|
{
|
||||||
|
type Request = ServiceRequest;
|
||||||
|
type Response = ServiceResponse<Body>;
|
||||||
|
type Error = actix_web::Error;
|
||||||
|
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error>>;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||||
|
self.0
|
||||||
|
.borrow_mut()
|
||||||
|
.poll_ready()
|
||||||
|
.map_err(|_| UpstreamError.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
||||||
|
let mut verify_digest = self.2.clone();
|
||||||
|
if let Some(digest) = req.headers().get("Digest") {
|
||||||
|
let vec = match parse_digest(digest) {
|
||||||
|
Some(vec) => vec,
|
||||||
|
None => return Box::new(err(VerifyError.into())),
|
||||||
|
};
|
||||||
|
let payload = req.take_payload();
|
||||||
|
let service = self.0.clone();
|
||||||
|
|
||||||
|
Box::new(payload.concat2().from_err().and_then(move |bytes| {
|
||||||
|
if verify_digest.verify(&vec, &bytes.as_ref()) {
|
||||||
|
req.set_payload(
|
||||||
|
(Box::new(once(Ok(bytes)))
|
||||||
|
as Box<dyn Stream<Item = Bytes, Error = PayloadError>>)
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Either::A(
|
||||||
|
service
|
||||||
|
.borrow_mut()
|
||||||
|
.call(req)
|
||||||
|
.map_err(|_| UpstreamError.into()),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Either::B(err(VerifyError.into()))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
if self.1 {
|
||||||
|
Box::new(err(VerifyError.into()))
|
||||||
|
} else {
|
||||||
|
Box::new(
|
||||||
|
self.0
|
||||||
|
.borrow_mut()
|
||||||
|
.call(req)
|
||||||
|
.map_err(|_| UpstreamError.into()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_digest(h: &HeaderValue) -> Option<Vec<DigestPart>> {
|
||||||
|
let h = h.to_str().ok()?.split(";").next()?;
|
||||||
|
let v: Vec<_> = h
|
||||||
|
.split(",")
|
||||||
|
.filter_map(|p| {
|
||||||
|
let mut iter = p.splitn(2, "=");
|
||||||
|
iter.next()
|
||||||
|
.and_then(|alg| iter.next().map(|value| (alg, value)))
|
||||||
|
})
|
||||||
|
.map(|(alg, value)| DigestPart {
|
||||||
|
algorithm: alg.to_owned(),
|
||||||
|
digest: value.to_owned(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if v.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResponseError for UpstreamError {
|
||||||
|
fn error_response(&self) -> HttpResponse {
|
||||||
|
HttpResponse::InternalServerError().finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_response(&self) -> HttpResponse {
|
||||||
|
Self::error_response(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResponseError for VerifyError {
|
||||||
|
fn error_response(&self) -> HttpResponse {
|
||||||
|
HttpResponse::BadRequest().finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_response(&self) -> HttpResponse {
|
||||||
|
Self::error_response(self)
|
||||||
|
}
|
||||||
|
}
|
87
http-signature-normalization-actix/src/digest/mod.rs
Normal file
87
http-signature-normalization-actix/src/digest/mod.rs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
use actix_web::{
|
||||||
|
client::{ClientRequest, ClientResponse},
|
||||||
|
error::PayloadError,
|
||||||
|
http::header::{InvalidHeaderValue, ToStrError},
|
||||||
|
web::Bytes,
|
||||||
|
};
|
||||||
|
use futures::{Future, Stream};
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::{Config, Sign};
|
||||||
|
|
||||||
|
pub mod middleware;
|
||||||
|
#[cfg(feature = "sha-2")]
|
||||||
|
pub mod sha2;
|
||||||
|
#[cfg(feature = "sha-3")]
|
||||||
|
pub mod sha3;
|
||||||
|
|
||||||
|
mod sign;
|
||||||
|
|
||||||
|
pub trait DigestCreate {
|
||||||
|
const NAME: &'static str;
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait DigestVerify {
|
||||||
|
fn verify(&mut self, digests: &[DigestPart], payload: &[u8]) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SignExt: Sign {
|
||||||
|
fn authorization_signature_with_digest<F, E, K, D, V>(
|
||||||
|
self,
|
||||||
|
config: &Config,
|
||||||
|
key_id: K,
|
||||||
|
digest: &mut D,
|
||||||
|
v: V,
|
||||||
|
f: F,
|
||||||
|
) -> Result<DigestClient<V>, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||||
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
|
K: Display,
|
||||||
|
D: DigestCreate,
|
||||||
|
V: AsRef<[u8]>,
|
||||||
|
Self: Sized;
|
||||||
|
|
||||||
|
fn signature_with_digest<F, E, K, D, V>(
|
||||||
|
self,
|
||||||
|
config: &Config,
|
||||||
|
key_id: K,
|
||||||
|
digest: &mut D,
|
||||||
|
v: V,
|
||||||
|
f: F,
|
||||||
|
) -> Result<DigestClient<V>, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||||
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
|
K: Display,
|
||||||
|
D: DigestCreate,
|
||||||
|
V: AsRef<[u8]>,
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DigestPart {
|
||||||
|
pub algorithm: String,
|
||||||
|
pub digest: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DigestClient<V> {
|
||||||
|
req: ClientRequest,
|
||||||
|
body: V,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V> DigestClient<V>
|
||||||
|
where
|
||||||
|
V: AsRef<[u8]>,
|
||||||
|
{
|
||||||
|
pub fn new(req: ClientRequest, body: V) -> Self {
|
||||||
|
DigestClient { req, body }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send(
|
||||||
|
self,
|
||||||
|
) -> impl Future<Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>> {
|
||||||
|
self.req.send_body(self.body.as_ref().to_vec())
|
||||||
|
}
|
||||||
|
}
|
153
http-signature-normalization-actix/src/digest/sha2.rs
Normal file
153
http-signature-normalization-actix/src/digest/sha2.rs
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
use sha2::{Digest as _, Sha224, Sha256, Sha384, Sha512, Sha512Trunc224, Sha512Trunc256};
|
||||||
|
|
||||||
|
use super::{DigestCreate, DigestPart, DigestVerify};
|
||||||
|
|
||||||
|
impl DigestCreate for Sha224 {
|
||||||
|
const NAME: &'static str = "sha-224";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Sha224 {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestCreate for Sha256 {
|
||||||
|
const NAME: &'static str = "sha-256";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Sha256 {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestCreate for Sha384 {
|
||||||
|
const NAME: &'static str = "sha-384";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Sha384 {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestCreate for Sha512 {
|
||||||
|
const NAME: &'static str = "sha-512";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Sha512 {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestCreate for Sha512Trunc224 {
|
||||||
|
const NAME: &'static str = "sha-512-224";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Sha512Trunc224 {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestCreate for Sha512Trunc256 {
|
||||||
|
const NAME: &'static str = "sha-512-256";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Sha512Trunc256 {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
231
http-signature-normalization-actix/src/digest/sha3.rs
Normal file
231
http-signature-normalization-actix/src/digest/sha3.rs
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
use sha3::{
|
||||||
|
Digest as _, Keccak224, Keccak256, Keccak256Full, Keccak384, Keccak512, Sha3_224, Sha3_256,
|
||||||
|
Sha3_384, Sha3_512,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{DigestCreate, DigestPart, DigestVerify};
|
||||||
|
|
||||||
|
impl DigestCreate for Sha3_224 {
|
||||||
|
const NAME: &'static str = "sha3-224";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Sha3_224 {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestCreate for Sha3_256 {
|
||||||
|
const NAME: &'static str = "sha3-256";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Sha3_256 {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestCreate for Sha3_384 {
|
||||||
|
const NAME: &'static str = "sha3-384";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Sha3_384 {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestCreate for Sha3_512 {
|
||||||
|
const NAME: &'static str = "sha3-512";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Sha3_512 {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestCreate for Keccak224 {
|
||||||
|
const NAME: &'static str = "keccak-224";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Keccak224 {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestCreate for Keccak256 {
|
||||||
|
const NAME: &'static str = "keccak-256";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Keccak256 {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestCreate for Keccak256Full {
|
||||||
|
const NAME: &'static str = "keccak-256-full";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Keccak256Full {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestCreate for Keccak384 {
|
||||||
|
const NAME: &'static str = "keccak-384";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Keccak384 {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestCreate for Keccak512 {
|
||||||
|
const NAME: &'static str = "keccak-512";
|
||||||
|
|
||||||
|
fn compute(&mut self, input: &[u8]) -> String {
|
||||||
|
self.input(input);
|
||||||
|
base64::encode(&self.result_reset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigestVerify for Keccak512 {
|
||||||
|
fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
|
||||||
|
if let Some(part) = parts
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.algorithm == <Self as DigestCreate>::NAME)
|
||||||
|
{
|
||||||
|
self.input(bytes);
|
||||||
|
let digest = base64::encode(&self.result_reset());
|
||||||
|
|
||||||
|
return part.digest == digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
58
http-signature-normalization-actix/src/digest/sign.rs
Normal file
58
http-signature-normalization-actix/src/digest/sign.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
use actix_web::{
|
||||||
|
client::ClientRequest,
|
||||||
|
http::header::{InvalidHeaderValue, ToStrError},
|
||||||
|
};
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
digest::{DigestClient, DigestCreate, SignExt},
|
||||||
|
Config, Sign,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl SignExt for ClientRequest {
|
||||||
|
fn authorization_signature_with_digest<F, E, K, D, V>(
|
||||||
|
self,
|
||||||
|
config: &Config,
|
||||||
|
key_id: K,
|
||||||
|
digest: &mut D,
|
||||||
|
v: V,
|
||||||
|
f: F,
|
||||||
|
) -> Result<DigestClient<V>, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||||
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
|
K: Display,
|
||||||
|
D: DigestCreate,
|
||||||
|
V: AsRef<[u8]>,
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let digest = digest.compute(v.as_ref());
|
||||||
|
|
||||||
|
self.set_header("Digest", format!("{}={}", D::NAME, digest))
|
||||||
|
.authorization_signature(config, key_id, f)
|
||||||
|
.map(|c| DigestClient::new(c, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature_with_digest<F, E, K, D, V>(
|
||||||
|
self,
|
||||||
|
config: &Config,
|
||||||
|
key_id: K,
|
||||||
|
digest: &mut D,
|
||||||
|
v: V,
|
||||||
|
f: F,
|
||||||
|
) -> Result<DigestClient<V>, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||||
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
|
K: Display,
|
||||||
|
D: DigestCreate,
|
||||||
|
V: AsRef<[u8]>,
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let digest = digest.compute(v.as_ref());
|
||||||
|
|
||||||
|
self.set_header("Digest", format!("{}={}", D::NAME, digest))
|
||||||
|
.signature(config, key_id, f)
|
||||||
|
.map(|c| DigestClient::new(c, v))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,7 @@
|
||||||
use actix_web::{
|
use actix_web::http::{
|
||||||
client::ClientRequest,
|
header::{HeaderMap, InvalidHeaderValue, ToStrError},
|
||||||
dev::ServiceRequest,
|
uri::PathAndQuery,
|
||||||
http::{
|
Method,
|
||||||
header::{HeaderMap, InvalidHeaderValue, ToStrError},
|
|
||||||
uri::PathAndQuery,
|
|
||||||
Method,
|
|
||||||
},
|
|
||||||
HttpRequest,
|
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
|
@ -14,40 +9,26 @@ use std::{
|
||||||
fmt::{self, Display},
|
fmt::{self, Display},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "digest")]
|
mod sign;
|
||||||
use actix_web::{client::ClientResponse, error::PayloadError, web::Bytes};
|
|
||||||
#[cfg(feature = "digest")]
|
|
||||||
use futures::{Future, Stream};
|
|
||||||
|
|
||||||
|
#[cfg(feature = "digest")]
|
||||||
|
pub mod digest;
|
||||||
|
|
||||||
|
pub mod verify;
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::{verify::Unverified, Config, Sign, Verify, VerifyError};
|
pub use crate::{verify::Unverified, Config, Sign, Verify, VerifyError};
|
||||||
|
|
||||||
#[cfg(feature = "digest")]
|
#[cfg(feature = "digest")]
|
||||||
pub use crate::Digest;
|
pub use crate::digest::{
|
||||||
|
middleware::VerifyDigest, DigestClient, DigestCreate, DigestPart, DigestVerify, SignExt,
|
||||||
|
};
|
||||||
|
|
||||||
pub use actix_web::http::header::{InvalidHeaderValue, ToStrError};
|
pub use actix_web::http::header::{InvalidHeaderValue, ToStrError};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod create;
|
pub mod create;
|
||||||
|
|
||||||
pub mod verify {
|
use self::{create::Unsigned, verify::Unverified};
|
||||||
pub use http_signature_normalization::verify::{
|
|
||||||
Algorithm, DeprecatedAlgorithm, ParseSignatureError, ParsedHeader, Unvalidated, Unverified,
|
|
||||||
ValidateError,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
use self::{
|
|
||||||
create::{Signed, Unsigned},
|
|
||||||
verify::Unverified,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "digest")]
|
|
||||||
pub trait Digest {
|
|
||||||
const NAME: &'static str;
|
|
||||||
|
|
||||||
fn compute(input: &[u8]) -> Vec<u8>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Verify {
|
pub trait Verify {
|
||||||
fn begin_verify(&self, config: &Config) -> Result<Unverified, VerifyError>;
|
fn begin_verify(&self, config: &Config) -> Result<Unverified, VerifyError>;
|
||||||
|
@ -67,38 +48,6 @@ pub trait Sign {
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
K: Display,
|
K: Display,
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
||||||
#[cfg(feature = "digest")]
|
|
||||||
fn authorization_signature_with_digest<F, E, K, D, V>(
|
|
||||||
self,
|
|
||||||
config: &Config,
|
|
||||||
key_id: K,
|
|
||||||
f: F,
|
|
||||||
v: V,
|
|
||||||
) -> Result<DigestClient<V>, E>
|
|
||||||
where
|
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
|
||||||
K: Display,
|
|
||||||
D: Digest,
|
|
||||||
V: AsRef<[u8]>,
|
|
||||||
Self: Sized;
|
|
||||||
|
|
||||||
#[cfg(feature = "digest")]
|
|
||||||
fn signature_with_digest<F, E, K, D, V>(
|
|
||||||
self,
|
|
||||||
config: &Config,
|
|
||||||
key_id: K,
|
|
||||||
f: F,
|
|
||||||
v: V,
|
|
||||||
) -> Result<DigestClient<V>, E>
|
|
||||||
where
|
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
|
||||||
K: Display,
|
|
||||||
D: Digest,
|
|
||||||
V: AsRef<[u8]>,
|
|
||||||
Self: Sized;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
|
@ -106,28 +55,6 @@ pub struct Config {
|
||||||
pub config: http_signature_normalization::Config,
|
pub config: http_signature_normalization::Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "digest")]
|
|
||||||
pub struct DigestClient<V> {
|
|
||||||
req: ClientRequest,
|
|
||||||
body: V,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "digest")]
|
|
||||||
impl<V> DigestClient<V>
|
|
||||||
where
|
|
||||||
V: AsRef<[u8]>,
|
|
||||||
{
|
|
||||||
pub fn new(req: ClientRequest, body: V) -> Self {
|
|
||||||
DigestClient { req, body }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send(
|
|
||||||
self,
|
|
||||||
) -> impl Future<Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>> {
|
|
||||||
self.req.send_body(self.body.as_ref().to_vec())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum VerifyError {
|
pub enum VerifyError {
|
||||||
Sig(http_signature_normalization::VerifyError),
|
Sig(http_signature_normalization::VerifyError),
|
||||||
|
@ -180,120 +107,6 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Verify for HttpRequest {
|
|
||||||
fn begin_verify(&self, config: &Config) -> Result<Unverified, VerifyError> {
|
|
||||||
config.begin_verify(
|
|
||||||
self.method(),
|
|
||||||
self.uri().path_and_query(),
|
|
||||||
self.headers().clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Verify for ServiceRequest {
|
|
||||||
fn begin_verify(&self, config: &Config) -> Result<Unverified, VerifyError> {
|
|
||||||
config.begin_verify(
|
|
||||||
self.method(),
|
|
||||||
self.uri().path_and_query(),
|
|
||||||
self.headers().clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sign for ClientRequest {
|
|
||||||
fn authorization_signature<F, E, K>(
|
|
||||||
mut self,
|
|
||||||
config: &Config,
|
|
||||||
key_id: K,
|
|
||||||
f: F,
|
|
||||||
) -> Result<Self, E>
|
|
||||||
where
|
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
|
||||||
K: Display,
|
|
||||||
{
|
|
||||||
let signed = prepare(&self, config, key_id, f)?;
|
|
||||||
signed.authorization_header(self.headers_mut())?;
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature<F, E, K>(mut self, config: &Config, key_id: K, f: F) -> Result<Self, E>
|
|
||||||
where
|
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
|
||||||
K: Display,
|
|
||||||
{
|
|
||||||
let signed = prepare(&self, config, key_id, f)?;
|
|
||||||
signed.signature_header(self.headers_mut())?;
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "digest")]
|
|
||||||
fn authorization_signature_with_digest<F, E, K, D, V>(
|
|
||||||
self,
|
|
||||||
config: &Config,
|
|
||||||
key_id: K,
|
|
||||||
f: F,
|
|
||||||
v: V,
|
|
||||||
) -> Result<DigestClient<V>, E>
|
|
||||||
where
|
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
|
||||||
K: Display,
|
|
||||||
D: Digest,
|
|
||||||
V: AsRef<[u8]>,
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
let digest = base64::encode(D::compute(v.as_ref()).as_slice());
|
|
||||||
|
|
||||||
self.set_header("Digest", format!("{}={}", D::NAME, digest))
|
|
||||||
.authorization_signature(config, key_id, f)
|
|
||||||
.map(|c| DigestClient::new(c, v))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "digest")]
|
|
||||||
fn signature_with_digest<F, E, K, D, V>(
|
|
||||||
self,
|
|
||||||
config: &Config,
|
|
||||||
key_id: K,
|
|
||||||
f: F,
|
|
||||||
v: V,
|
|
||||||
) -> Result<DigestClient<V>, E>
|
|
||||||
where
|
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
|
||||||
K: Display,
|
|
||||||
D: Digest,
|
|
||||||
V: AsRef<[u8]>,
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
let digest = base64::encode(D::compute(v.as_ref()).as_slice());
|
|
||||||
|
|
||||||
self.set_header("Digest", format!("{}={}", D::NAME, digest))
|
|
||||||
.signature(config, key_id, f)
|
|
||||||
.map(|c| DigestClient::new(c, v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prepare<F, E, K>(request: &ClientRequest, config: &Config, key_id: K, f: F) -> Result<Signed, E>
|
|
||||||
where
|
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
|
||||||
E: From<ToStrError>,
|
|
||||||
K: Display,
|
|
||||||
{
|
|
||||||
let unsigned = config.begin_sign(
|
|
||||||
request.get_method(),
|
|
||||||
request.get_uri().path_and_query(),
|
|
||||||
request.headers().clone(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let key_id = key_id.to_string();
|
|
||||||
|
|
||||||
let signed = unsigned.sign(key_id, f)?;
|
|
||||||
|
|
||||||
Ok(signed)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for VerifyError {
|
impl fmt::Display for VerifyError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
|
|
55
http-signature-normalization-actix/src/sign.rs
Normal file
55
http-signature-normalization-actix/src/sign.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use actix_web::{
|
||||||
|
client::ClientRequest,
|
||||||
|
http::header::{InvalidHeaderValue, ToStrError},
|
||||||
|
};
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::{create::Signed, Config, Sign};
|
||||||
|
|
||||||
|
impl Sign for ClientRequest {
|
||||||
|
fn authorization_signature<F, E, K>(
|
||||||
|
mut self,
|
||||||
|
config: &Config,
|
||||||
|
key_id: K,
|
||||||
|
f: F,
|
||||||
|
) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||||
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
|
K: Display,
|
||||||
|
{
|
||||||
|
let signed = prepare(&self, config, key_id, f)?;
|
||||||
|
signed.authorization_header(self.headers_mut())?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature<F, E, K>(mut self, config: &Config, key_id: K, f: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||||
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
|
K: Display,
|
||||||
|
{
|
||||||
|
let signed = prepare(&self, config, key_id, f)?;
|
||||||
|
signed.signature_header(self.headers_mut())?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare<F, E, K>(request: &ClientRequest, config: &Config, key_id: K, f: F) -> Result<Signed, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||||
|
E: From<ToStrError>,
|
||||||
|
K: Display,
|
||||||
|
{
|
||||||
|
let unsigned = config.begin_sign(
|
||||||
|
request.get_method(),
|
||||||
|
request.get_uri().path_and_query(),
|
||||||
|
request.headers().clone(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let key_id = key_id.to_string();
|
||||||
|
|
||||||
|
let signed = unsigned.sign(key_id, f)?;
|
||||||
|
|
||||||
|
Ok(signed)
|
||||||
|
}
|
27
http-signature-normalization-actix/src/verify.rs
Normal file
27
http-signature-normalization-actix/src/verify.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
use actix_web::{dev::ServiceRequest, HttpRequest};
|
||||||
|
pub use http_signature_normalization::verify::{
|
||||||
|
Algorithm, DeprecatedAlgorithm, ParseSignatureError, ParsedHeader, Unvalidated, Unverified,
|
||||||
|
ValidateError,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{Config, Verify, VerifyError};
|
||||||
|
|
||||||
|
impl Verify for HttpRequest {
|
||||||
|
fn begin_verify(&self, config: &Config) -> Result<Unverified, VerifyError> {
|
||||||
|
config.begin_verify(
|
||||||
|
self.method(),
|
||||||
|
self.uri().path_and_query(),
|
||||||
|
self.headers().clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Verify for ServiceRequest {
|
||||||
|
fn begin_verify(&self, config: &Config) -> Result<Unverified, VerifyError> {
|
||||||
|
config.begin_verify(
|
||||||
|
self.method(),
|
||||||
|
self.uri().path_and_query(),
|
||||||
|
self.headers().clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue