@@ 465,8 465,27 @@ impl<'a> HttpbisSignature<'a> {
.collect::<Result<_, _>>()?;
let mut params = SignatureParams::default();
+ let mut params_src = String::new();
for (key, value) in list.params {
+ params_src.push(';');
+ <String as AsRef<str>>::as_ref(&key)
+ .serialize_as_bare_item(&mut params_src)?;
+ match value {
+ sfv::BareItem::Integer(value) => {
+ value.serialize_as_bare_item(&mut params_src)?
+ }
+ sfv::BareItem::String(ref value) => {
+ <String as AsRef<str>>::as_ref(&value)
+ .serialize_as_bare_item(&mut params_src)?
+ }
+ _ => {
+ // all supported parameters use those types so others are
+ // invalid
+ return Err(crate::ParseError::InvalidStructure);
+ }
+ }
+
if key == "created" {
params.created = match value {
sfv::BareItem::Integer(value) => Some(
@@ 510,7 529,7 @@ impl<'a> HttpbisSignature<'a> {
}
}
- inputs.insert(name, (covered_components, params));
+ inputs.insert(name, (covered_components, params, params_src));
}
}
}
@@ 518,9 537,48 @@ impl<'a> HttpbisSignature<'a> {
let mut result = Vec::new();
+ for value in headers.get_all(crate::SIGNATURE_HEADER) {
+ let dict = sfv::Parser::parse_dictionary(value.as_bytes())
+ .map_err(crate::ParseError::InvalidSyntax)?;
+
+ for (name, value) in dict {
+ let input = match inputs.remove(&name) {
+ Some(input) => input,
+ None => continue, // maybe should throw an error?
+ };
+
+ match value {
+ sfv::ListEntry::Item(value) => {
+ if !value.params.is_empty() {
+ return Err(crate::ParseError::UnknownParam);
+ }
+
+ if let sfv::BareItem::ByteSeq(content) = value.bare_item {
+ result.push(Self {
+ name: name.into(),
+ params: input.1,
+ params_src: input.2.into(),
+ covered_components: input.0.into(),
+ signature: content,
+ });
+ } else {
+ return Err(crate::ParseError::InvalidStructure);
+ }
+ }
+ sfv::ListEntry::InnerList(_) => {
+ return Err(crate::ParseError::InvalidStructure)
+ }
+ }
+ }
+ }
+
Ok(result)
}
+ pub fn parse_from_request<B>(req: http::Request<B>) -> Result<Vec<Self>, crate::ParseError> {
+ Self::parse_inner(RequestOrResponse::Request(req))
+ }
+
fn verify_inner<E: std::fmt::Debug, B>(
&self,
src: RequestOrResponse<B>,
@@ 555,6 613,18 @@ impl<'a> HttpbisSignature<'a> {
) -> Result<bool, crate::VerifyError<E>> {
self.verify_inner(RequestOrResponse::Request(request), None, verify)
}
+
+ pub fn params(&self) -> &SignatureParams {
+ &self.params
+ }
+
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn covered_components(&self) -> &[ComponentId<'_>] {
+ &self.covered_components
+ }
}
enum RequestOrResponse<B> {
@@ 649,6 719,14 @@ impl<'a> AsBareItem for &Cow<'a, str> {
}
}
+impl<'a> AsBareItem for i64 {
+ fn serialize_as_bare_item(&self, result: &mut String) -> Result<(), crate::CommonError> {
+ use std::fmt::Write;
+ write!(result, "{}", self).unwrap();
+ Ok(())
+ }
+}
+
fn create_signature_base<B>(
params_src: &str,
covered_components: &[ComponentId<'_>],
@@ 750,4 828,32 @@ mod test {
"@signature-params": ("@status" "content-type" "content-digest" "content-length");created=1618884473;keyid="test-key-ecc-p256""#,
);
}
+
+ #[test]
+ fn test_parse_minimal() {
+ let req = http::Request::builder()
+ .method(http::Method::POST)
+ .uri("/foo?param=Value&Pet=dog")
+ .header(http::header::HOST, "example.com")
+ .header(http::header::DATE, "Tue, 20 Apr 2021 02:07:55 GMT")
+ .header(http::header::CONTENT_TYPE, "application/json")
+ .header("Content-Digest", "sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\
+ aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:")
+ .header(http::header::CONTENT_LENGTH, "18")
+ .header(SIGNATURE_INPUT_HEADER, r#"sig-b21=();created=1618884473;keyid="test-key-rsa-pss";nonce="b3k2pp5k7z-50gnwp.yemd""#)
+ .header(crate::SIGNATURE_HEADER, "sig-b21=:d2pmTvmbncD3xQm8E9ZV2828BjQWGgiwAaw5bAkgibUopemLJcWDy/lkbbHAve4cRAtx31Iq786U7it++wgGxbtRxf8Udx7zFZsckzXaJMkA7ChG52eSkFxykJeNqsrWH5S+oxNFlD4dzVuwe8DhTSja8xxbR/Z2cOGdCbzR72rgFWhzx2VjBqJzsPLMIQKhO4DGezXehhWwE56YCE+O6c0mKZsfxVrogUvA4HELjVKWmAvtl6UnCh8jYzuVG5WSb/QEVPnP5TmcAnLH1g+s++v6d4s8m0gCw1fV5/SITLq9mhho8K3+7EPYTU8IU1bLhdxO5Nyt8C8ssinQ98Xw9Q==:")
+ .body(r#"{"hello": "world"}"#)
+ .unwrap();
+
+ let result = HttpbisSignature::parse_from_request(req).unwrap();
+ assert_eq!(result.len(), 1);
+
+ let result = result.into_iter().next().unwrap();
+ assert_eq!(result.name(), "sig-b21");
+
+ let params = result.params();
+ assert_eq!(params.created, Some(1618884473));
+
+ assert!(result.covered_components().is_empty());
+ }
}
@@ 44,6 44,18 @@ pub enum ParseError {
#[error("A parameter value was outside of supported range")]
ValueOutOfRange,
+
+ #[error("An invalid character was found")]
+ InvalidCharacter,
+
+ #[error("Specified parameters cannot be used together")]
+ ConflictingParams,
+
+ #[error("A requested component was not found")]
+ MissingComponent,
+
+ #[error("Attempted to use an unimplemented feature")]
+ Unsupported,
}
/// Errors that may be produced when creating a signature
@@ 100,6 112,17 @@ impl<T: std::fmt::Debug> From<CommonError> for VerifyError<T> {
}
}
+impl From<CommonError> for ParseError {
+ fn from(src: CommonError) -> Self {
+ match src {
+ CommonError::InvalidCharacter => Self::InvalidCharacter,
+ CommonError::ConflictingParams => Self::ConflictingParams,
+ CommonError::MissingComponent => Self::MissingComponent,
+ CommonError::Unsupported => Self::Unsupported,
+ }
+ }
+}
+
/// Errors that may be produced when verifying a signature
#[derive(Debug, thiserror::Error)]
pub enum VerifyError<T: std::fmt::Debug> {