1use crate::{
16 ContentDigest, Error, FieldSection, Message, Signer, VerifiedSignature, Verifier, fields,
17};
18use alloc::{boxed::Box, string::String};
19use bytes::Bytes;
20use core::{
21 pin::Pin,
22 task::{Context, Poll},
23};
24use http::{
25 HeaderMap, HeaderValue, Request, Response, Version,
26 header::{CONTENT_LENGTH, TRAILER, TRANSFER_ENCODING},
27};
28use http_body::{Body, Frame, SizeHint};
29use sha2::{Digest as _, Sha256};
30
31#[derive(Debug, thiserror::Error)]
33pub enum BodyError<E> {
34 #[error("HTTP body failed: {0}")]
36 Inner(E),
37 #[error(transparent)]
39 Integrity(#[from] Error),
40}
41
42pub fn sign_request<B: AsRef<[u8]>>(
45 request: &mut Request<B>,
46 effective_uri: &str,
47 signer: &Signer,
48) -> Result<(), Error> {
49 if !signer.input().covers_digest(FieldSection::Headers) {
50 return Err(Error::Policy("unsigned content digest"));
51 }
52 let mut message = Message::from_request(request, effective_uri)?;
53 message.headers.insert(
54 "content-digest",
55 fields::header(ContentDigest::new(request.body().as_ref()).to_field_value())?,
56 );
57 signer.sign(&message)?.append_to(&mut message.headers)?;
58 *request.headers_mut() = message.headers;
59 Ok(())
60}
61
62pub fn verify_request<B: AsRef<[u8]>>(
65 request: &Request<B>,
66 effective_uri: &str,
67 verifier: &Verifier,
68 now: i64,
69) -> Result<VerifiedSignature, Error> {
70 let message = Message::from_request(request, effective_uri)?;
71 let verified = verifier.verify(&message, now)?;
72 require_digest_coverage(&verified, &message)?;
73 ContentDigest::new(request.body().as_ref()).verify(&message.headers, &message.trailers)?;
74 Ok(verified)
75}
76
77pub fn sign_buffered_response<B: AsRef<[u8]>>(
80 response: &mut Response<B>,
81 request: Option<Message>,
82 signer: &Signer,
83) -> Result<(), Error> {
84 if !signer.input().covers_digest(FieldSection::Headers) {
85 return Err(Error::Policy("unsigned content digest"));
86 }
87 let mut message = Message::response(response.status(), response.headers().clone(), request)?;
88 let content = response.body().as_ref();
89 if !message.permits_response_trailers() && !content.is_empty() {
90 return Err(Error::InvalidField(
91 "content is forbidden for this response",
92 ));
93 }
94 message.headers.insert(
95 "content-digest",
96 fields::header(ContentDigest::new(content).to_field_value())?,
97 );
98 signer.sign(&message)?.append_to(&mut message.headers)?;
99 *response.headers_mut() = message.headers;
100 Ok(())
101}
102
103pub struct SigningBody<B> {
106 inner: Pin<Box<B>>,
107 hash: Sha256,
108 message: Message,
109 signer: Option<Signer>,
110 input_section: FieldSection,
111 trailers: Option<HeaderMap>,
112 done: bool,
113}
114
115impl<B> SigningBody<B> {
116 pub fn response(
125 mut response: Response<B>,
126 request: Option<Message>,
127 signer: Option<Signer>,
128 input_section: FieldSection,
129 ) -> Result<Response<Self>, Error> {
130 if !matches!(response.version(), Version::HTTP_11 | Version::HTTP_2) {
131 return Err(Error::TrailersUnavailable);
132 }
133 let mut message =
134 Message::response(response.status(), response.headers().clone(), request)?;
135 if !message.permits_response_trailers() {
136 return Err(Error::TrailersUnavailable);
137 }
138 if message.headers.contains_key("content-digest") {
139 return Err(Error::InvalidField("content digest already supplied"));
140 }
141 message.headers.remove(CONTENT_LENGTH);
144 message.headers.remove(TRANSFER_ENCODING);
145 let mut names = alloc::vec!["content-digest"];
146 if let Some(signer) = &signer {
147 if !signer.input().covers_digest(FieldSection::Trailers) {
148 return Err(Error::Policy("unsigned trailer digest"));
149 }
150 check_label(&message.headers, signer.label())?;
151 signer.input().preflight(&message)?;
152 names.push("signature");
153 match input_section {
154 FieldSection::Headers => {
155 message
156 .headers
157 .append("signature-input", signer.input_header()?);
158 },
159 FieldSection::Trailers => names.push("signature-input"),
160 }
161 }
162 declare_trailers(&mut message.headers, &names)?;
163 *response.headers_mut() = message.headers.clone();
164 Ok(response.map(|inner| Self {
165 inner: Box::pin(inner),
166 hash: Sha256::new(),
167 message,
168 signer,
169 input_section,
170 trailers: None,
171 done: false,
172 }))
173 }
174
175 fn finish(&mut self) -> Result<HeaderMap, Error> {
176 let mut trailers = self.trailers.take().unwrap_or_default();
177 if trailers.contains_key("content-digest") {
178 return Err(Error::InvalidField("body supplied its own digest"));
179 }
180 let digest = ContentDigest::from_bytes(self.hash.clone().finalize().into());
181 trailers.insert("content-digest", fields::header(digest.to_field_value())?);
182 if let Some(signer) = &self.signer {
183 check_label(&trailers, signer.label())?;
184 self.message.trailers = trailers.clone();
185 let signature = signer.sign(&self.message)?;
186 if self.input_section == FieldSection::Trailers {
187 signature.append_to(&mut trailers)?;
188 } else {
189 trailers.append("signature", signature.signature);
190 }
191 }
192 Ok(trailers)
193 }
194}
195
196impl<B: Body<Data = Bytes>> Body for SigningBody<B> {
197 type Data = Bytes;
198 type Error = BodyError<B::Error>;
199
200 fn poll_frame(
201 self: Pin<&mut Self>,
202 cx: &mut Context<'_>,
203 ) -> Poll<Option<Result<Frame<Bytes>, Self::Error>>> {
204 let this = self.get_mut();
205 if this.done {
206 return Poll::Ready(None);
207 }
208 loop {
209 match this.inner.as_mut().poll_frame(cx) {
210 Poll::Pending => return Poll::Pending,
211 Poll::Ready(Some(Err(error))) => {
212 this.done = true;
213 return Poll::Ready(Some(Err(BodyError::Inner(error))));
214 },
215 Poll::Ready(Some(Ok(frame))) => {
216 if this.trailers.is_some() {
217 this.done = true;
218 return Poll::Ready(Some(Err(Error::FrameOrder.into())));
219 }
220 match frame.into_data() {
221 Ok(data) => {
222 this.hash.update(&data);
223 return Poll::Ready(Some(Ok(Frame::data(data))));
224 },
225 Err(frame) => match frame.into_trailers() {
226 Ok(trailers) => this.trailers = Some(trailers),
227 Err(_) => {
228 this.done = true;
229 return Poll::Ready(Some(Err(Error::FrameOrder.into())));
230 },
231 },
232 }
233 },
234 Poll::Ready(None) => {
235 this.done = true;
236 return Poll::Ready(Some(
237 this.finish()
238 .map(Frame::trailers)
239 .map_err(BodyError::Integrity),
240 ));
241 },
242 }
243 }
244 }
245
246 fn is_end_stream(&self) -> bool {
247 self.done
248 }
249 fn size_hint(&self) -> SizeHint {
250 SizeHint::default()
251 }
252}
253
254pub struct VerifyingBody<B> {
306 inner: Pin<Box<B>>,
307 hash: Sha256,
308 message: Message,
309 verifier: Option<Verifier>,
310 now: i64,
311 trailers: Option<HeaderMap>,
312 done: bool,
313 verified: bool,
314 signature: Option<VerifiedSignature>,
315}
316
317impl<B> VerifyingBody<B> {
318 pub fn new(inner: B, mut message: Message, verifier: Option<Verifier>, now: i64) -> Self {
322 message.trailers.clear();
324 Self {
325 inner: Box::pin(inner),
326 hash: Sha256::new(),
327 message,
328 verifier,
329 now,
330 trailers: None,
331 done: false,
332 verified: false,
333 signature: None,
334 }
335 }
336
337 pub fn response(
339 response: Response<B>,
340 request: Option<Message>,
341 verifier: Option<Verifier>,
342 now: i64,
343 ) -> Result<Response<Self>, Error> {
344 let message = Message::response(response.status(), response.headers().clone(), request)?;
345 Ok(response.map(|inner| Self::new(inner, message, verifier, now)))
346 }
347
348 pub const fn is_verified(&self) -> bool {
350 self.verified
351 }
352
353 pub fn verified_signature(&self) -> Option<&VerifiedSignature> {
355 self.signature.as_ref()
356 }
357
358 fn finish(&mut self) -> Result<Option<HeaderMap>, Error> {
359 let trailers = self.trailers.take();
360 if let Some(trailers) = &trailers {
361 self.message.trailers = trailers.clone();
362 }
363 ContentDigest::from_bytes(self.hash.clone().finalize().into())
364 .verify(&self.message.headers, &self.message.trailers)?;
365 if let Some(verifier) = &self.verifier {
366 let signature = verifier.verify(&self.message, self.now)?;
367 require_digest_coverage(&signature, &self.message)?;
368 self.signature = Some(signature);
369 }
370 self.verified = true;
371 Ok(trailers)
372 }
373}
374
375impl<B: Body<Data = Bytes>> Body for VerifyingBody<B> {
376 type Data = Bytes;
377 type Error = BodyError<B::Error>;
378
379 fn poll_frame(
380 self: Pin<&mut Self>,
381 cx: &mut Context<'_>,
382 ) -> Poll<Option<Result<Frame<Bytes>, Self::Error>>> {
383 let this = self.get_mut();
384 if this.done {
385 return Poll::Ready(None);
386 }
387 loop {
388 match this.inner.as_mut().poll_frame(cx) {
389 Poll::Pending => return Poll::Pending,
390 Poll::Ready(Some(Err(error))) => {
391 this.done = true;
392 return Poll::Ready(Some(Err(BodyError::Inner(error))));
393 },
394 Poll::Ready(Some(Ok(frame))) => {
395 if this.trailers.is_some() {
396 this.done = true;
397 return Poll::Ready(Some(Err(Error::FrameOrder.into())));
398 }
399 match frame.into_data() {
400 Ok(data) => {
401 this.hash.update(&data);
402 return Poll::Ready(Some(Ok(Frame::data(data))));
403 },
404 Err(frame) => match frame.into_trailers() {
405 Ok(trailers) => this.trailers = Some(trailers),
406 Err(_) => {
407 this.done = true;
408 return Poll::Ready(Some(Err(Error::FrameOrder.into())));
409 },
410 },
411 }
412 },
413 Poll::Ready(None) => {
414 this.done = true;
415 return Poll::Ready(match this.finish() {
416 Ok(Some(trailers)) => Some(Ok(Frame::trailers(trailers))),
417 Ok(None) => None,
418 Err(error) => Some(Err(error.into())),
419 });
420 },
421 }
422 }
423 }
424
425 fn is_end_stream(&self) -> bool {
426 self.done
427 }
428 fn size_hint(&self) -> SizeHint {
429 SizeHint::default()
430 }
431}
432
433fn require_digest_coverage(signature: &VerifiedSignature, message: &Message) -> Result<(), Error> {
434 for section in [FieldSection::Headers, FieldSection::Trailers] {
435 if signature.input.covers_digest(section)
436 && let Some(value) = fields::combined(&[message.fields(section)], "content-digest")?
437 && ContentDigest::parse(&value).is_ok()
438 {
439 return Ok(());
440 }
441 }
442 Err(Error::Policy(
443 "signature does not cover the verified SHA-256 digest",
444 ))
445}
446
447fn check_label(headers: &HeaderMap, label: &str) -> Result<(), Error> {
448 for name in ["signature-input", "signature"] {
449 if let Some(value) = fields::combined(&[headers], name)?
450 && fields::dictionary(&value)?.contains_key(label)
451 {
452 return Err(Error::DuplicateLabel);
453 }
454 }
455 Ok(())
456}
457
458fn declare_trailers(headers: &mut HeaderMap, names: &[&str]) -> Result<(), Error> {
459 let previous = fields::combined(&[headers], "trailer")?.unwrap_or_default();
460 let mut declared: alloc::vec::Vec<String> = previous
461 .split(',')
462 .map(str::trim)
463 .filter(|name| !name.is_empty())
464 .map(str::to_ascii_lowercase)
465 .collect();
466 for name in names {
467 if !declared.iter().any(|entry| entry == name) {
468 declared.push((*name).into());
469 }
470 }
471 headers.insert(
472 TRAILER,
473 HeaderValue::try_from(declared.join(", ")).map_err(|_| Error::InvalidField("Trailer"))?,
474 );
475 Ok(())
476}