/// A Response represents a response from an HTTP server. /// /// The structure is created from a request and then used to either create (server) or validate /// (client) a `Server-Authentication` header. /// /// Like `Request`, Responses are built with `ResponseBuilders`. /// /// # Examples /// /// See the documentation in the crate root for examples. #[derive(Debug, Clone)] pubstruct Response<'a> {
method: &'a str,
host: &'a str,
port: u16,
path: &'a str,
req_header: &'a Header,
hash: Option<&'a [u8]>,
ext: Option<&'a str>,
}
impl<'a> Response<'a> { /// Create a new Header for this response, based on the given request and request header pubfn make_header(&self, key: &Key) -> Result<Header> { let ts = self.req_header.ts.ok_or(Error::MissingTs)?; let nonce = self.req_header.nonce.as_ref().ok_or(Error::MissingNonce)?; let mac = Mac::new(
MacType::Response,
key,
ts,
nonce, self.method, self.host, self.port, self.path, self.hash, self.ext,
)?;
// Per JS implementation, the Server-Authorization header includes only mac, hash, and ext
Header::new(
None,
None,
None,
Some(mac), self.ext.map(|v| v.to_string()), self.hash.map(|v| v.to_vec()),
None,
None,
)
}
/// Validate a Server-Authorization header. /// /// This checks that the MAC matches and, if a hash has been supplied locally, /// checks that one was provided from the server and that it, too, matches. pubfn validate_header(&self, response_header: &Header, key: &Key) -> bool { // extract required fields, returning early if they are not present let ts = matchself.req_header.ts {
Some(ts) => ts,
None => { returnfalse;
}
}; let nonce = matchself.req_header.nonce {
Some(ref nonce) => nonce,
None => { returnfalse;
}
}; let header_mac = match response_header.mac {
Some(ref mac) => mac,
None => { returnfalse;
}
}; let header_ext = response_header.ext.as_ref().map(|ext| &ext[..]); let header_hash = response_header.hash.as_ref().map(|hash| &hash[..]);
// first verify the MAC match Mac::new(
MacType::Response,
key,
ts,
nonce, self.method, self.host, self.port, self.path,
header_hash,
header_ext,
) {
Ok(calculated_mac) => { if &calculated_mac != header_mac { returnfalse;
}
}
Err(_) => { returnfalse;
}
};
impl<'a> ResponseBuilder<'a> { /// Generate a new Response from a request header. /// /// This is more commonly accessed through `Request::make_response`. pubfn from_request_header(
req_header: &'a Header,
method: &'a str,
host: &'a str,
port: u16,
path: &'a str,
) -> Self {
ResponseBuilder(Response {
method,
host,
port,
path,
req_header,
hash: None,
ext: None,
})
}
/// Set the content hash for the response. /// /// This should always be calculated from the response payload, not copied from a header. pubfn hash<H: Into<Option<&'a [u8]>>>(mut self, hash: H) -> Self { self.0.hash = hash.into(); self
}
/// Set the `ext` Hawk property for the response. /// /// This need only be set on the server; it is ignored in validating responses on the client. pubfn ext<S: Into<Option<&'a str>>>(mut self, ext: S) -> Self { self.0.ext = ext.into(); self
}
/// Get the response from this builder pubfn response(self) -> Response<'a> { self.0
}
}
#[cfg(all(test, any(feature = "use_ring", feature = "use_openssl")))] mod test { usesuper::ResponseBuilder; usecrate::credentials::Key; usecrate::header::Header; usecrate::mac::Mac; use std::time::{Duration, UNIX_EPOCH};
#[test] fn test_validation_hash_in_header() { // When a hash is provided in the response header, but no hash is added to the Response, // it is ignored (so validation succeeds) let req_header = make_req_header(); let resp =
ResponseBuilder::from_request_header(&req_header, "POST", "localhost", 9988, "/a/b")
.response(); let mac: Mac = Mac::from(vec![ 33, 147, 159, 211, 184, 194, 189, 74, 53, 229, 241, 161, 215, 145, 22, 34, 206, 207, 242, 100, 33, 193, 36, 96, 149, 133, 180, 4, 132, 87, 207, 238,
]); let server_header = Header::new(
None,
None,
None,
Some(mac),
Some("server-ext"),
Some(vec![1, 2, 3, 4]),
None,
None,
)
.unwrap();
assert!(resp.validate_header(&server_header, &Key::new("tok", crate::SHA256).unwrap()));
}
#[test] fn test_validation_hash_required_but_not_given() { // When Response.hash is called, but no hash is in the hader, validation fails. let req_header = make_req_header(); let hash = vec![1, 2, 3, 4]; let resp =
ResponseBuilder::from_request_header(&req_header, "POST", "localhost", 9988, "/a/b")
.hash(&hash[..])
.response(); let mac: Mac = Mac::from(vec![ 48, 133, 228, 163, 224, 197, 222, 77, 117, 81, 143, 73, 71, 120, 68, 238, 228, 40, 55, 64, 190, 73, 102, 123, 79, 185, 199, 26, 62, 1, 137, 170,
]); let server_header = Header::new(
None,
None,
None,
Some(mac),
Some("server-ext"),
None,
None,
None,
)
.unwrap();
assert!(!resp.validate_header(&server_header, &Key::new("tok", crate::SHA256).unwrap()));
}
#[test] fn test_validation_hash_validated() { // When a hash is provided in the response header and the Response.hash method is called, // the two must match let req_header = make_req_header(); let hash = vec![1, 2, 3, 4]; let resp =
ResponseBuilder::from_request_header(&req_header, "POST", "localhost", 9988, "/a/b")
.hash(&hash[..])
.response(); let mac: Mac = Mac::from(vec![ 33, 147, 159, 211, 184, 194, 189, 74, 53, 229, 241, 161, 215, 145, 22, 34, 206, 207, 242, 100, 33, 193, 36, 96, 149, 133, 180, 4, 132, 87, 207, 238,
]); let server_header = Header::new(
None,
None,
None,
Some(mac),
Some("server-ext"),
Some(vec![1, 2, 3, 4]),
None,
None,
)
.unwrap();
assert!(resp.validate_header(&server_header, &Key::new("tok", crate::SHA256).unwrap()));
// a different supplied hash won't match.. let hash = vec![99, 99, 99, 99]; let resp =
ResponseBuilder::from_request_header(&req_header, "POST", "localhost", 9988, "/a/b")
.hash(&hash[..])
.response();
assert!(!resp.validate_header(&server_header, &Key::new("tok", crate::SHA256).unwrap()));
}
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.22Bemerkung:
(vorverarbeitet am 2026-08-25)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.