mirror of
https://git.asonix.dog/asonix/http-signature-normalization.git
synced 2024-11-25 10:51:01 +00:00
Add Http Signature verification middleware and...
Move base64 out of base lib
This commit is contained in:
parent
c468513d5a
commit
686e52213e
14 changed files with 276 additions and 59 deletions
|
@ -16,5 +16,4 @@ members = [
|
|||
]
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.10"
|
||||
chrono = "0.4"
|
||||
|
|
|
@ -10,7 +10,7 @@ edition = "2018"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[features]
|
||||
default = []
|
||||
digest = ["base64", "futures"]
|
||||
digest = ["base64"]
|
||||
sha-2 = ["digest", "sha2"]
|
||||
sha-3 = ["digest", "sha3"]
|
||||
|
||||
|
@ -26,7 +26,7 @@ required-features = ["sha-2"]
|
|||
actix-web = "1.0"
|
||||
base64 = { version = "0.10", optional = true }
|
||||
failure = "0.1"
|
||||
futures = { version = "0.1", optional = true }
|
||||
futures = "0.1"
|
||||
http-signature-normalization = { version = "0.1.0", path = ".." }
|
||||
sha2 = { version = "0.8", optional = true }
|
||||
sha3 = { version = "0.8", optional = true }
|
||||
|
|
|
@ -18,8 +18,8 @@ fn main() {
|
|||
&config,
|
||||
"my-key-id",
|
||||
&mut digest,
|
||||
"Hewwo owo",
|
||||
|s| Ok(s.as_bytes().to_vec()),
|
||||
"Hewwo-owo",
|
||||
|s| Ok(base64::encode(s)),
|
||||
)
|
||||
.unwrap()
|
||||
.send()
|
||||
|
|
|
@ -1,33 +1,47 @@
|
|||
use actix::System;
|
||||
use actix_web::{web, App, HttpRequest, HttpServer, ResponseError};
|
||||
use actix_web::{web, App, HttpResponse, HttpServer, ResponseError};
|
||||
use failure::Fail;
|
||||
use http_signature_normalization_actix::{prelude::*, verify::Algorithm};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
fn index((req, config): (HttpRequest, web::Data<Config>)) -> Result<&'static str, MyError> {
|
||||
let unverified = req.begin_verify(&config)?;
|
||||
#[derive(Clone, Debug)]
|
||||
struct MyVerify;
|
||||
|
||||
if let Some(a) = unverified.algorithm() {
|
||||
match *a {
|
||||
Algorithm::Hs2019 => (),
|
||||
impl SignatureVerify for MyVerify {
|
||||
type Error = MyError;
|
||||
type Future = Result<bool, Self::Error>;
|
||||
|
||||
fn signature_verify(
|
||||
&mut self,
|
||||
algorithm: Option<Algorithm>,
|
||||
_key_id: &str,
|
||||
signature: &str,
|
||||
signing_string: &str,
|
||||
) -> Self::Future {
|
||||
match algorithm {
|
||||
Some(Algorithm::Hs2019) => (),
|
||||
_ => return Err(MyError::Algorithm),
|
||||
};
|
||||
|
||||
let decoded = base64::decode(signature).map_err(|_| MyError::Decode)?;
|
||||
|
||||
Ok(decoded == signing_string.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
if unverified.verify(|bytes, string| bytes == string.as_bytes()) {
|
||||
Ok("Eyyyyup")
|
||||
} else {
|
||||
Ok("Nope")
|
||||
}
|
||||
fn index() -> &'static str {
|
||||
"Eyyyyup"
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let sys = System::new("server-example");
|
||||
|
||||
let config = Config::default();
|
||||
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.data(Config::default())
|
||||
.wrap(VerifyDigest::new(Sha256::new()))
|
||||
.wrap(VerifySignature::new(MyVerify, config.clone()).authorization())
|
||||
.route("/", web::post().to(index))
|
||||
})
|
||||
.bind("127.0.0.1:8010")?
|
||||
|
@ -44,10 +58,19 @@ enum MyError {
|
|||
|
||||
#[fail(display = "Unsupported algorithm")]
|
||||
Algorithm,
|
||||
|
||||
#[fail(display = "Couldn't decode signature")]
|
||||
Decode,
|
||||
}
|
||||
|
||||
impl ResponseError for MyError {
|
||||
// default 500
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
HttpResponse::BadRequest().finish()
|
||||
}
|
||||
|
||||
fn render_response(&self) -> HttpResponse {
|
||||
self.error_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VerifyError> for MyError {
|
||||
|
|
|
@ -12,19 +12,18 @@ pub struct Unsigned {
|
|||
|
||||
impl Signed {
|
||||
pub fn signature_header(self, hm: &mut HeaderMap) -> Result<(), InvalidHeaderValue> {
|
||||
let sig_header = self.signed.signature_header();
|
||||
hm.insert(
|
||||
HeaderName::from_static("Signature"),
|
||||
HeaderValue::from_str(&self.signed.signature_header())?,
|
||||
HeaderValue::from_str(&sig_header)?,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn authorization_header(self, hm: &mut HeaderMap) -> Result<(), InvalidHeaderValue> {
|
||||
hm.insert(
|
||||
AUTHORIZATION,
|
||||
HeaderValue::from_str(&self.signed.authorization_header())?,
|
||||
);
|
||||
let auth_header = self.signed.authorization_header();
|
||||
hm.insert(AUTHORIZATION, HeaderValue::from_str(&auth_header)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +31,7 @@ impl Signed {
|
|||
impl Unsigned {
|
||||
pub fn sign<F, E>(self, key_id: String, f: F) -> Result<Signed, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
F: FnOnce(&str) -> Result<String, E>,
|
||||
{
|
||||
let signed = self.unsigned.sign(key_id, f)?;
|
||||
Ok(Signed { signed })
|
||||
|
|
|
@ -81,7 +81,6 @@ where
|
|||
}
|
||||
|
||||
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,
|
||||
|
@ -89,6 +88,7 @@ where
|
|||
};
|
||||
let payload = req.take_payload();
|
||||
let service = self.0.clone();
|
||||
let mut verify_digest = self.2.clone();
|
||||
|
||||
Box::new(payload.concat2().from_err().and_then(move |bytes| {
|
||||
if verify_digest.verify(&vec, &bytes.as_ref()) {
|
||||
|
@ -103,15 +103,13 @@ where
|
|||
Either::B(err(VerifyError.into()))
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
if self.1 {
|
||||
} else if self.1 {
|
||||
Box::new(err(VerifyError.into()))
|
||||
} else {
|
||||
Box::new(self.0.borrow_mut().call(req))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_digest(h: &HeaderValue) -> Option<Vec<DigestPart>> {
|
||||
let h = h.to_str().ok()?.split(";").next()?;
|
||||
|
|
|
@ -37,7 +37,7 @@ pub trait SignExt: Sign {
|
|||
f: F,
|
||||
) -> Result<DigestClient<V>, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
F: FnOnce(&str) -> Result<String, E>,
|
||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||
K: Display,
|
||||
D: DigestCreate,
|
||||
|
@ -53,7 +53,7 @@ pub trait SignExt: Sign {
|
|||
f: F,
|
||||
) -> Result<DigestClient<V>, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
F: FnOnce(&str) -> Result<String, E>,
|
||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||
K: Display,
|
||||
D: DigestCreate,
|
||||
|
|
|
@ -19,7 +19,7 @@ impl SignExt for ClientRequest {
|
|||
f: F,
|
||||
) -> Result<DigestClient<V>, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
F: FnOnce(&str) -> Result<String, E>,
|
||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||
K: Display,
|
||||
D: DigestCreate,
|
||||
|
@ -42,7 +42,7 @@ impl SignExt for ClientRequest {
|
|||
f: F,
|
||||
) -> Result<DigestClient<V>, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
F: FnOnce(&str) -> Result<String, E>,
|
||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||
K: Display,
|
||||
D: DigestCreate,
|
||||
|
|
|
@ -4,6 +4,7 @@ use actix_web::http::{
|
|||
Method,
|
||||
};
|
||||
use failure::Fail;
|
||||
use futures::future::IntoFuture;
|
||||
use std::{collections::BTreeMap, fmt::Display};
|
||||
|
||||
mod sign;
|
||||
|
@ -11,9 +12,13 @@ mod sign;
|
|||
#[cfg(feature = "digest")]
|
||||
pub mod digest;
|
||||
|
||||
pub mod verify;
|
||||
pub mod create;
|
||||
pub mod middleware;
|
||||
pub mod prelude {
|
||||
pub use crate::{verify::Unverified, Config, Sign, Verify, VerifyError};
|
||||
pub use crate::{
|
||||
middleware::VerifySignature, verify::Unverified, Config, Sign, SignatureVerify, Verify,
|
||||
VerifyError,
|
||||
};
|
||||
|
||||
#[cfg(feature = "digest")]
|
||||
pub use crate::digest::{
|
||||
|
@ -22,32 +27,47 @@ pub mod prelude {
|
|||
|
||||
pub use actix_web::http::header::{InvalidHeaderValue, ToStrError};
|
||||
}
|
||||
pub mod verify;
|
||||
|
||||
pub mod create;
|
||||
|
||||
use self::{create::Unsigned, verify::Unverified};
|
||||
use self::{
|
||||
create::Unsigned,
|
||||
verify::{Algorithm, Unverified},
|
||||
};
|
||||
|
||||
pub trait Verify {
|
||||
fn begin_verify(&self, config: &Config) -> Result<Unverified, VerifyError>;
|
||||
}
|
||||
|
||||
pub trait SignatureVerify {
|
||||
type Error: actix_web::ResponseError;
|
||||
type Future: IntoFuture<Item = bool, Error = Self::Error>;
|
||||
|
||||
fn signature_verify(
|
||||
&mut self,
|
||||
algorithm: Option<Algorithm>,
|
||||
key_id: &str,
|
||||
signature: &str,
|
||||
signing_string: &str,
|
||||
) -> Self::Future;
|
||||
}
|
||||
|
||||
pub trait Sign {
|
||||
fn authorization_signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Self, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
F: FnOnce(&str) -> Result<String, E>,
|
||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||
K: Display,
|
||||
Self: Sized;
|
||||
|
||||
fn signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Self, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
F: FnOnce(&str) -> Result<String, E>,
|
||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||
K: Display,
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Config {
|
||||
pub config: http_signature_normalization::Config,
|
||||
}
|
||||
|
|
183
http-signature-normalization-actix/src/middleware.rs
Normal file
183
http-signature-normalization-actix/src/middleware.rs
Normal file
|
@ -0,0 +1,183 @@
|
|||
use actix_web::{
|
||||
body::Body,
|
||||
dev::{Service, ServiceRequest, ServiceResponse, Transform},
|
||||
HttpResponse, ResponseError,
|
||||
};
|
||||
use failure::Fail;
|
||||
use futures::{
|
||||
future::{err, ok, Either, FutureResult, IntoFuture},
|
||||
Future, Poll,
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{Config, SignatureVerify};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VerifySignature<T>(T, Config, HeaderKind, bool);
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VerifyMiddleware<T, S>(Rc<RefCell<S>>, Config, HeaderKind, bool, T);
|
||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub enum HeaderKind {
|
||||
Authorization,
|
||||
Signature,
|
||||
}
|
||||
#[derive(Clone, Debug, Fail)]
|
||||
#[fail(display = "Failed to verify http signature")]
|
||||
pub struct VerifyError;
|
||||
|
||||
impl<T> VerifySignature<T>
|
||||
where
|
||||
T: SignatureVerify,
|
||||
{
|
||||
pub fn new(verify_signature: T, config: Config) -> Self {
|
||||
VerifySignature(verify_signature, config, HeaderKind::Signature, true)
|
||||
}
|
||||
|
||||
pub fn authorization(self) -> Self {
|
||||
VerifySignature(self.0, self.1, HeaderKind::Authorization, self.3)
|
||||
}
|
||||
|
||||
pub fn optional(self) -> Self {
|
||||
VerifySignature(self.0, self.1, self.2, false)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> VerifyMiddleware<T, S>
|
||||
where
|
||||
T: SignatureVerify + 'static,
|
||||
T::Future: 'static,
|
||||
S: Service<
|
||||
Request = ServiceRequest,
|
||||
Response = ServiceResponse<Body>,
|
||||
Error = actix_web::Error,
|
||||
> + 'static,
|
||||
S::Error: 'static,
|
||||
{
|
||||
fn handle(
|
||||
&mut self,
|
||||
req: ServiceRequest,
|
||||
) -> Box<dyn Future<Item = ServiceResponse<Body>, Error = actix_web::Error>> {
|
||||
let res = self.1.begin_verify(
|
||||
req.method(),
|
||||
req.uri().path_and_query(),
|
||||
req.headers().clone(),
|
||||
);
|
||||
|
||||
let unverified = match res {
|
||||
Ok(unverified) => unverified,
|
||||
Err(_) => return Box::new(err(VerifyError.into())),
|
||||
};
|
||||
|
||||
let algorithm = unverified.algorithm().map(|a| a.clone());
|
||||
let key_id = unverified.key_id().to_owned();
|
||||
|
||||
let verified = unverified.verify(|signature, signing_string| {
|
||||
self.4
|
||||
.signature_verify(algorithm, &key_id, signature, signing_string)
|
||||
});
|
||||
|
||||
let service = self.0.clone();
|
||||
|
||||
Box::new(
|
||||
verified
|
||||
.into_future()
|
||||
.from_err::<actix_web::Error>()
|
||||
.and_then(move |verified| {
|
||||
if verified {
|
||||
Either::A(service.borrow_mut().call(req))
|
||||
} else {
|
||||
Either::B(err(VerifyError.into()))
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl HeaderKind {
|
||||
pub fn is_authorization(&self) -> bool {
|
||||
HeaderKind::Authorization == *self
|
||||
}
|
||||
|
||||
pub fn is_signature(&self) -> bool {
|
||||
HeaderKind::Signature == *self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> Transform<S> for VerifySignature<T>
|
||||
where
|
||||
T: SignatureVerify + Clone + 'static,
|
||||
S: Service<
|
||||
Request = ServiceRequest,
|
||||
Response = ServiceResponse<Body>,
|
||||
Error = actix_web::Error,
|
||||
> + '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.1.clone(),
|
||||
self.2,
|
||||
self.3,
|
||||
self.0.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> Service for VerifyMiddleware<T, S>
|
||||
where
|
||||
T: SignatureVerify + Clone + 'static,
|
||||
S: Service<
|
||||
Request = ServiceRequest,
|
||||
Response = ServiceResponse<Body>,
|
||||
Error = actix_web::Error,
|
||||
> + '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()
|
||||
}
|
||||
|
||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
||||
let authorization = req.headers().get("Authorization").is_some();
|
||||
let signature = req.headers().get("Signature").is_some();
|
||||
|
||||
if authorization || signature {
|
||||
if self.2.is_authorization() && authorization {
|
||||
return self.handle(req);
|
||||
}
|
||||
|
||||
if self.2.is_signature() && signature {
|
||||
return self.handle(req);
|
||||
}
|
||||
|
||||
Box::new(err(VerifyError.into()))
|
||||
} else if self.3 {
|
||||
Box::new(self.0.borrow_mut().call(req))
|
||||
} else {
|
||||
Box::new(err(VerifyError.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for VerifyError {
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
HttpResponse::BadRequest().finish()
|
||||
}
|
||||
|
||||
fn render_response(&self) -> HttpResponse {
|
||||
self.error_response()
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ impl Sign for ClientRequest {
|
|||
f: F,
|
||||
) -> Result<Self, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
F: FnOnce(&str) -> Result<String, E>,
|
||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||
K: Display,
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ impl Sign for ClientRequest {
|
|||
|
||||
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>,
|
||||
F: FnOnce(&str) -> Result<String, E>,
|
||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||
K: Display,
|
||||
{
|
||||
|
@ -37,7 +37,7 @@ impl Sign for ClientRequest {
|
|||
|
||||
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>,
|
||||
F: FnOnce(&str) -> Result<String, E>,
|
||||
E: From<ToStrError>,
|
||||
K: Display,
|
||||
{
|
||||
|
|
|
@ -52,10 +52,10 @@ impl Signed {
|
|||
impl Unsigned {
|
||||
pub fn sign<F, E>(self, key_id: String, f: F) -> Result<Signed, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
F: FnOnce(&str) -> Result<String, E>,
|
||||
{
|
||||
(f)(&self.signing_string).map(|v| Signed {
|
||||
signature: base64::encode(&v),
|
||||
(f)(&self.signing_string).map(|signature| Signed {
|
||||
signature,
|
||||
sig_headers: self.sig_headers,
|
||||
created: self.created,
|
||||
expires: self.expires,
|
||||
|
|
|
@ -196,7 +196,7 @@ mod tests {
|
|||
let authorization_header = config
|
||||
.begin_sign("GET", "/foo?bar=baz", headers)
|
||||
.sign("hi".to_owned(), |s| {
|
||||
Ok(s.as_bytes().to_vec()) as Result<_, std::io::Error>
|
||||
Ok(s.to_owned()) as Result<_, std::io::Error>
|
||||
})
|
||||
.unwrap()
|
||||
.authorization_header();
|
||||
|
@ -207,7 +207,7 @@ mod tests {
|
|||
let verified = config
|
||||
.begin_verify("GET", "/foo?bar=baz", headers)
|
||||
.unwrap()
|
||||
.verify(|bytes, string| string.as_bytes() == bytes);
|
||||
.verify(|sig, signing_string| sig == signing_string);
|
||||
|
||||
assert!(verified);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
#[derive(Debug)]
|
||||
pub struct Unverified {
|
||||
key_id: String,
|
||||
signature: Vec<u8>,
|
||||
signature: String,
|
||||
algorithm: Option<Algorithm>,
|
||||
signing_string: String,
|
||||
}
|
||||
|
@ -68,7 +68,6 @@ pub enum Algorithm {
|
|||
pub enum ValidateError {
|
||||
Missing,
|
||||
Expired,
|
||||
Decode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -85,7 +84,7 @@ impl Unverified {
|
|||
|
||||
pub fn verify<F, T>(&self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&[u8], &str) -> T,
|
||||
F: FnOnce(&str, &str) -> T,
|
||||
{
|
||||
(f)(&self.signature, &self.signing_string)
|
||||
}
|
||||
|
@ -104,13 +103,11 @@ impl Unvalidated {
|
|||
}
|
||||
}
|
||||
|
||||
let signature = base64::decode(&self.signature).map_err(|_| ValidateError::Decode)?;
|
||||
|
||||
Ok(Unverified {
|
||||
key_id: self.key_id,
|
||||
algorithm: self.algorithm,
|
||||
signing_string: self.signing_string,
|
||||
signature,
|
||||
signature: self.signature,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -247,7 +244,6 @@ impl fmt::Display for ValidateError {
|
|||
match *self {
|
||||
ValidateError::Missing => write!(f, "Http Signature is missing"),
|
||||
ValidateError::Expired => write!(f, "Http Signature is expired"),
|
||||
ValidateError::Decode => write!(f, "Http Signature could not be decoded"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -257,7 +253,6 @@ impl Error for ValidateError {
|
|||
match *self {
|
||||
ValidateError::Missing => "Http Signature is missing",
|
||||
ValidateError::Expired => "Http Signature is expired",
|
||||
ValidateError::Decode => "Http Signature could not be decoded",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue