1use bytes::Bytes;
2use hex::FromHexError;
3use hmac::{digest::InvalidLength, Hmac, Mac};
4use http::{HeaderValue, Request};
5use sha2::Sha256;
6use thiserror::Error;
7use tower::filter::Predicate;
8
9#[derive(Debug, Clone)]
10pub struct HmacQueryParamValidator {
11 pub key: String,
12}
13
14#[derive(Error, Debug)]
16pub enum HmacQueryParamError {
17 #[error("Request did not have a query string")]
18 NoQueryString,
19 #[error("Request did not have an HMAC query param: {query_string}")]
20 QueryStringButNoHmac { query_string: String },
21 #[error("Hash key not appropriately set: {error:#?}")]
22 HashKeyNotSet { error: InvalidLength },
23 #[error("Failed decoding HMAC as hex string: {error:#?}")]
24 HexDecodingError { error: FromHexError },
25 #[error("Computed hash did not match provided hash")]
26 HashVerificationFailed,
27}
28
29#[derive(Debug, Clone)]
30pub struct HmacHeaderValidator {
31 pub key: String,
32 pub header_name: String,
33}
34
35#[derive(Error, Debug)]
37pub enum HmacHeaderError {
38 #[error("Request did not contain a '{header_name}' header.")]
39 NoHeader { header_name: String },
40 #[error("Request contained {num} headers with name '{header_name}'")]
41 MultipleHeaders { num: usize, header_name: String },
42 #[error("Hash key not appropriately set: {error:#?}")]
43 HashKeyNotSet { error: InvalidLength },
44 }
49
50impl<T> Predicate<Request<T>> for HmacQueryParamValidator {
51 type Request = http::Request<T>;
52 fn check(&mut self, request: Request<T>) -> Result<Self::Request, tower::BoxError> {
53 let query = request.uri().query().ok_or(HmacQueryParamError::NoQueryString)?;
55
56 let (hmac, params): (Vec<_>, Vec<_>) =
58 form_urlencoded::parse(query.as_bytes()).partition(|(key, _)| key == "hmac");
59 let hmac = &hmac
60 .first()
61 .ok_or(HmacQueryParamError::QueryStringButNoHmac {
62 query_string: query.to_string(),
63 })?
64 .1;
65
66 let query_string_without_hmac = form_urlencoded::Serializer::new(String::new())
68 .extend_pairs(params)
69 .finish();
70
71 let mut hasher = Hmac::<Sha256>::new_from_slice(self.key.as_bytes())
73 .map_err(|e| HmacQueryParamError::HashKeyNotSet { error: e })?;
74
75 hasher.update(&query_string_without_hmac.into_bytes());
77
78 let hmac_bytes =
83 hex::decode(hmac.as_bytes()).map_err(|e| HmacQueryParamError::HexDecodingError { error: e })?;
84
85 hasher
87 .verify(hmac_bytes.as_slice().into())
88 .map_err(|_| HmacQueryParamError::HashVerificationFailed)?;
89
90 Ok(request)
91 }
92}
93
94impl<B: Into<Bytes>> Predicate<Request<B>> for HmacHeaderValidator
95where
96 for<'a> &'a [u8]: From<&'a B>,
97{
98 type Request = http::Request<B>;
99 fn check(&mut self, request: Request<B>) -> Result<Self::Request, tower::BoxError> {
100 let header_values = request
102 .headers()
103 .get_all(&self.header_name)
104 .into_iter()
105 .collect::<Vec<&HeaderValue>>();
106
107 if header_values.is_empty() {
109 return Err(Box::new(HmacHeaderError::NoHeader {
110 header_name: self.header_name.clone(),
111 }));
112 } else if header_values.len() > 1 {
113 return Err(Box::new(HmacHeaderError::MultipleHeaders {
114 num: header_values.len(),
115 header_name: self.header_name.clone(),
116 }));
117 }
118
119 let hmac = header_values.first().ok_or(HmacHeaderError::NoHeader {
121 header_name: self.header_name.clone(),
122 })?;
123
124 let mut hasher = Hmac::<Sha256>::new_from_slice(self.key.as_bytes())
126 .map_err(|e| HmacHeaderError::HashKeyNotSet { error: e })?;
127
128 hasher.update(request.body().into());
130
131 let hmac_bytes =
136 hex::decode(hmac.as_bytes()).map_err(|e| HmacQueryParamError::HexDecodingError { error: e })?;
137
138 hasher
140 .verify(hmac_bytes.as_slice().into())
141 .map_err(|_| HmacQueryParamError::HashVerificationFailed)?;
142
143 Ok(request)
144 }
145}