1use std::{
4 fmt,
5 time::{SystemTime, UNIX_EPOCH},
6};
7
8use hkdf::Hkdf;
9use hpke::{
10 Deserializable, Kem as KemTrait, OpModeR, OpModeS, PskBundle, Serializable,
11 aead::{AeadCtxR, AeadCtxS, AeadTag, ChaCha20Poly1305 as HpkeChaCha20Poly1305},
12 inout::InOutBuf,
13 kdf::HkdfSha256,
14 kem::X25519HkdfSha256,
15 setup_receiver, setup_sender_with_rng,
16};
17use rand_chacha::ChaCha20Rng;
18use rand_core::SeedableRng;
19use sha2::{Digest, Sha256};
20use zeroize::Zeroizing;
21
22use crate::{
23 CLOCK_SKEW_SECS, EntropySource, Error, HARD_MAX_ID_LEN, Limits, Method, REQUEST_LIFETIME_SECS,
24 SystemEntropy,
25 codec::{
26 ENC_LEN, REQUEST_FIXED_LEN, RESPONSE_NONCE_LEN, TAG_LEN, encode_request_header,
27 parse_stream_start, stream_start_length,
28 },
29 compression,
30 message::{self, HeaderField, Request, RequestHead, Response},
31 response::{ResponseMode, ResponseOpener, ResponseRecord, ResponseSealer},
32};
33
34type Kem = X25519HkdfSha256;
35type Kdf = HkdfSha256;
36type HpkeAead = HpkeChaCha20Poly1305;
37type RecipientContext = AeadCtxR<HpkeAead, Kdf, Kem>;
38type SenderContext = AeadCtxS<HpkeAead, Kdf, Kem>;
39
40const STREAM_REQUEST_INFO_LABEL: &[u8] = b"message/hpke-http request\0v3\0";
41const STREAM_REQUEST_AAD_LABEL: &[u8] = b"hpke-http/3 request record\0";
42const STREAM_START: u8 = 1;
43const STREAM_DATA: u8 = 2;
44const STREAM_END: u8 = 3;
45pub const STREAM_DATA_LEN: usize = 64 * 1024;
47const STREAM_MAX_DATA_RECORDS: u64 = 1_048_576;
49const RESPONSE_EXPORT_LABEL: &[u8] = b"message/hpke-http response\0v3";
50const REPLAY_LABEL: &[u8] = b"hpke-http/replay\0v3\0";
51const RESPONSE_KEY_INFO: &[u8] = b"hpke-http/3 response key";
52const RESPONSE_NONCE_INFO: &[u8] = b"hpke-http/3 response nonce";
53const RESPONSE_SECRET_LEN: usize = 32;
54const RESPONSE_KEY_LEN: usize = 32;
55const AEAD_NONCE_LEN: usize = 12;
56const MIN_PSK_LEN: usize = 32;
57pub(crate) type ResponseKey = Zeroizing<[u8; RESPONSE_KEY_LEN]>;
58pub(crate) type ResponseNonce = Zeroizing<[u8; AEAD_NONCE_LEN]>;
59
60pub struct KeyPair {
62 private_key: Zeroizing<Vec<u8>>,
63 public_key: Vec<u8>,
64}
65
66impl fmt::Debug for KeyPair {
67 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
68 formatter
69 .debug_struct("KeyPair")
70 .field("private_key", &"[REDACTED]")
71 .field("public_key_len", &self.public_key.len())
72 .finish()
73 }
74}
75
76impl KeyPair {
77 #[must_use]
79 pub fn into_parts(self) -> (Zeroizing<Vec<u8>>, Vec<u8>) {
80 (self.private_key, self.public_key)
81 }
82
83 #[must_use]
85 pub fn public_key(&self) -> &[u8] {
86 &self.public_key
87 }
88}
89
90pub fn generate_key_pair() -> Result<KeyPair, Error> {
96 generate_key_pair_with_entropy(&mut SystemEntropy)
97}
98
99pub fn generate_key_pair_with_entropy(entropy: &mut impl EntropySource) -> Result<KeyPair, Error> {
105 let mut input_key_material = Zeroizing::new([0_u8; 32]);
106 entropy.fill(input_key_material.as_mut())?;
107 let (private_key, public_key) = Kem::derive_keypair(input_key_material.as_ref());
108 Ok(KeyPair {
109 private_key: Zeroizing::new(private_key.to_bytes().to_vec()),
110 public_key: public_key.to_bytes().to_vec(),
111 })
112}
113
114#[must_use]
116pub struct ProtectedRequest {
117 envelope: Vec<u8>,
118 response_token: ResponseToken,
119}
120
121impl fmt::Debug for ProtectedRequest {
122 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
123 formatter
124 .debug_struct("ProtectedRequest")
125 .field("envelope_len", &self.envelope.len())
126 .field("response_token", &self.response_token)
127 .finish()
128 }
129}
130
131impl ProtectedRequest {
132 #[must_use]
134 pub fn envelope(&self) -> &[u8] {
135 &self.envelope
136 }
137
138 pub fn into_parts(self) -> (Vec<u8>, ResponseToken) {
140 (self.envelope, self.response_token)
141 }
142}
143
144pub struct Client {
146 recipient_public_key: <Kem as KemTrait>::PublicKey,
147 recipient_key_id: Vec<u8>,
148 psk: Zeroizing<Vec<u8>>,
149 psk_id: Vec<u8>,
150 limits: Limits,
151}
152
153impl fmt::Debug for Client {
154 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
155 formatter
156 .debug_struct("Client")
157 .field("recipient_key_id_len", &self.recipient_key_id.len())
158 .field("psk", &"[REDACTED]")
159 .field("psk_id_len", &self.psk_id.len())
160 .field("limits", &self.limits)
161 .finish_non_exhaustive()
162 }
163}
164
165impl Client {
166 pub fn new(
173 recipient_public_key: &[u8],
174 recipient_key_id: Vec<u8>,
175 psk: Vec<u8>,
176 psk_id: Vec<u8>,
177 limits: Limits,
178 ) -> Result<Self, Error> {
179 let psk = Zeroizing::new(psk);
180 validate_id(&recipient_key_id)?;
181 validate_credential(psk.as_slice(), &psk_id)?;
182 let limits = limits.validate()?;
183 let recipient_public_key = <Kem as KemTrait>::PublicKey::from_bytes(recipient_public_key)
184 .map_err(|_| Error::InvalidConfiguration)?;
185 Ok(Self {
186 recipient_public_key,
187 recipient_key_id,
188 psk,
189 psk_id,
190 limits,
191 })
192 }
193
194 pub fn protect(&self, request: &Request) -> Result<ProtectedRequest, Error> {
201 self.protect_at_with_entropy(request, unix_time_seconds()?, &mut SystemEntropy)
202 }
203
204 pub fn protect_at(
213 &self,
214 request: &Request,
215 now_unix_s: u64,
216 ) -> Result<ProtectedRequest, Error> {
217 self.protect_at_with_entropy(request, now_unix_s, &mut SystemEntropy)
218 }
219
220 pub fn protect_with_entropy(
227 &self,
228 request: &Request,
229 entropy: &mut impl EntropySource,
230 ) -> Result<ProtectedRequest, Error> {
231 self.protect_at_with_entropy(request, unix_time_seconds()?, entropy)
232 }
233
234 pub fn protect_at_with_entropy(
241 &self,
242 request: &Request,
243 now_unix_s: u64,
244 entropy: &mut impl EntropySource,
245 ) -> Result<ProtectedRequest, Error> {
246 message::validate_request(request, self.limits)?;
247 let head = RequestHead {
248 method: request.method,
249 authority: request.authority.clone(),
250 path: request.path.clone(),
251 headers: request.headers.clone(),
252 };
253 let (mut writer, mut envelope) =
254 self.begin_stream_at_with_entropy(&head, now_unix_s, entropy)?;
255 let mut remaining = request.body.as_slice();
256 while !remaining.is_empty() {
257 let (used, record) = writer.push(remaining)?;
258 remaining = &remaining[used..];
259 if let Some(record) = record {
260 envelope.extend_from_slice(&record);
261 }
262 }
263 let (end, response_token) = writer.finish()?;
264 envelope.extend_from_slice(&end);
265 Ok(ProtectedRequest {
266 envelope,
267 response_token,
268 })
269 }
270}
271
272#[derive(Clone, Debug, Eq, PartialEq)]
274pub struct CredentialRequest {
275 pub psk_id: Vec<u8>,
277}
278
279#[must_use]
281pub struct PreparsedRequest {
282 pub credential: CredentialRequest,
284 pub token: StartToken,
286}
287
288#[must_use]
290pub struct StartToken {
291 envelope: Vec<u8>,
292}
293
294impl fmt::Debug for StartToken {
295 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
296 formatter
297 .debug_struct("StartToken")
298 .field("envelope_len", &self.envelope.len())
299 .finish()
300 }
301}
302
303#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
305pub struct ReplayRequest {
306 pub id: [u8; 32],
308 pub retain_until_exclusive: u64,
310}
311
312impl ReplayRequest {
313 #[must_use]
315 pub const fn decision(self, admitted: bool) -> ReplayDecision {
316 ReplayDecision {
317 id: self.id,
318 retain_until_exclusive: self.retain_until_exclusive,
319 admitted,
320 }
321 }
322}
323
324#[derive(Clone, Copy, Debug, Eq, PartialEq)]
326pub struct ReplayDecision {
327 id: [u8; 32],
328 retain_until_exclusive: u64,
329 admitted: bool,
330}
331
332fn check_replay_decision(
333 replay_id: [u8; 32],
334 retain_until_exclusive: u64,
335 decision: ReplayDecision,
336 now_unix_s: u64,
337) -> Result<(), Error> {
338 if replay_id != decision.id || retain_until_exclusive != decision.retain_until_exclusive {
339 return Err(Error::ReplayDecisionMismatch);
340 }
341 if !decision.admitted {
342 return Err(Error::ReplayRejected);
343 }
344 if now_unix_s >= retain_until_exclusive {
345 return Err(Error::InvalidRequestTime);
346 }
347 Ok(())
348}
349
350#[must_use]
352pub struct AuthenticatedRequest {
353 pub replay: ReplayRequest,
355 pub token: ReplayToken,
357}
358
359#[must_use]
361pub struct ReplayToken {
362 replay_id: [u8; 32],
363 retain_until_exclusive: u64,
364 request: Request,
365 response: ResponseCapability,
366}
367
368impl fmt::Debug for ReplayToken {
369 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
370 formatter
371 .debug_struct("ReplayToken")
372 .field("replay_id", &self.replay_id)
373 .field("retain_until_exclusive", &self.retain_until_exclusive)
374 .field("request", &"[REDACTED]")
375 .finish_non_exhaustive()
376 }
377}
378
379impl ReplayToken {
380 pub fn admit(self, decision: ReplayDecision) -> Result<OpenedRequest, Error> {
391 self.admit_at(decision, unix_time_seconds()?)
392 }
393
394 pub fn admit_at(
404 self,
405 decision: ReplayDecision,
406 now_unix_s: u64,
407 ) -> Result<OpenedRequest, Error> {
408 check_replay_decision(
409 self.replay_id,
410 self.retain_until_exclusive,
411 decision,
412 now_unix_s,
413 )?;
414 Ok(OpenedRequest {
415 request: self.request,
416 response: self.response,
417 })
418 }
419}
420
421#[must_use]
423pub struct OpenedRequest {
424 pub request: Request,
426 pub response: ResponseCapability,
428}
429
430pub struct Server {
432 recipient_private_key: <Kem as KemTrait>::PrivateKey,
433 recipient_key_id: Vec<u8>,
434 accepted_keys: Vec<(Vec<u8>, <Kem as KemTrait>::PrivateKey)>,
435 limits: Limits,
436}
437
438impl fmt::Debug for Server {
439 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
440 formatter
441 .debug_struct("Server")
442 .field("recipient_private_key", &"[REDACTED]")
443 .field("recipient_key_id_len", &self.recipient_key_id.len())
444 .field("accepted_key_count", &self.accepted_keys.len())
445 .field("limits", &self.limits)
446 .finish()
447 }
448}
449
450impl Server {
451 pub fn new(
458 recipient_private_key: &[u8],
459 recipient_key_id: Vec<u8>,
460 limits: Limits,
461 ) -> Result<Self, Error> {
462 Self::with_accepted_keys(recipient_private_key, recipient_key_id, Vec::new(), limits)
463 }
464
465 pub fn with_accepted_keys(
474 recipient_private_key: &[u8],
475 recipient_key_id: Vec<u8>,
476 accepted: Vec<(Vec<u8>, Vec<u8>)>,
477 limits: Limits,
478 ) -> Result<Self, Error> {
479 validate_id(&recipient_key_id)?;
480 let limits = limits.validate()?;
481 let recipient_private_key =
482 <Kem as KemTrait>::PrivateKey::from_bytes(recipient_private_key)
483 .map_err(|_| Error::InvalidConfiguration)?;
484 let accepted = accepted
485 .into_iter()
486 .map(|(private_key, key_id)| (Zeroizing::new(private_key), key_id))
487 .collect::<Vec<_>>();
488 let mut accepted_keys = Vec::with_capacity(accepted.len());
489 for (private_key, key_id) in accepted {
490 validate_id(&key_id)?;
491 if key_id == recipient_key_id || accepted_keys.iter().any(|(id, _)| *id == key_id) {
492 return Err(Error::InvalidConfiguration);
493 }
494 let private_key = <Kem as KemTrait>::PrivateKey::from_bytes(&private_key)
495 .map_err(|_| Error::InvalidConfiguration)?;
496 accepted_keys.push((key_id, private_key));
497 }
498 Ok(Self {
499 recipient_private_key,
500 recipient_key_id,
501 accepted_keys,
502 limits,
503 })
504 }
505
506 fn private_key_for(&self, key_id: &[u8]) -> Result<&<Kem as KemTrait>::PrivateKey, Error> {
507 if key_id == self.recipient_key_id {
508 return Ok(&self.recipient_private_key);
509 }
510 self.accepted_keys
511 .iter()
512 .find(|(id, _)| id == key_id)
513 .map(|(_, private_key)| private_key)
514 .ok_or(Error::UnknownRecipientKey)
515 }
516
517 #[must_use]
519 pub fn public_key(&self) -> Vec<u8> {
520 Kem::sk_to_pk(&self.recipient_private_key)
521 .to_bytes()
522 .to_vec()
523 }
524
525 pub fn max_complete_envelope_len(&self) -> Result<usize, Error> {
533 max_complete_envelope_len(self.limits)
534 }
535
536 pub fn preparse(&self, envelope: &[u8]) -> Result<PreparsedRequest, Error> {
542 let credential = self.preparse_credential(envelope)?;
543 Ok(PreparsedRequest {
544 credential,
545 token: StartToken {
546 envelope: envelope.to_vec(),
547 },
548 })
549 }
550
551 pub fn preparse_owned(&self, envelope: Vec<u8>) -> Result<PreparsedRequest, Error> {
561 let credential = self.preparse_credential(&envelope)?;
562 Ok(PreparsedRequest {
563 credential,
564 token: StartToken { envelope },
565 })
566 }
567
568 fn preparse_credential(&self, envelope: &[u8]) -> Result<CredentialRequest, Error> {
569 if envelope.len() > self.max_complete_envelope_len()? {
570 return Err(Error::LimitExceeded);
571 }
572 let first_len = self
573 .stream_start_length(envelope)?
574 .ok_or(Error::MalformedEnvelope)?;
575 if first_len > envelope.len() {
576 return Err(Error::MalformedEnvelope);
577 }
578 let parsed = parse_stream_start(
579 &envelope[..first_len],
580 max_stream_start_ciphertext(self.limits),
581 )?;
582 self.private_key_for(parsed.key_id)?;
583 Ok(CredentialRequest {
584 psk_id: parsed.psk_id.to_vec(),
585 })
586 }
587
588 pub fn authenticate(
597 &self,
598 token: StartToken,
599 psk: &[u8],
600 ) -> Result<AuthenticatedRequest, Error> {
601 self.authenticate_at(token, psk, unix_time_seconds()?)
602 }
603
604 pub fn authenticate_at(
610 &self,
611 token: StartToken,
612 psk: &[u8],
613 now_unix_s: u64,
614 ) -> Result<AuthenticatedRequest, Error> {
615 let StartToken { envelope } = token;
616 let first_len = self
617 .stream_start_length(&envelope)?
618 .ok_or(Error::MalformedEnvelope)?;
619 if first_len > envelope.len() {
620 return Err(Error::MalformedEnvelope);
621 }
622 let authenticated = self.authenticate_stream_at(
623 StreamStartToken {
624 first: envelope[..first_len].to_vec(),
625 },
626 psk,
627 now_unix_s,
628 )?;
629 let replay = authenticated.replay;
630 let StreamReplayToken {
631 head, mut reader, ..
632 } = authenticated.token;
633 let mut body = Vec::new();
634 let mut remaining = &envelope[first_len..];
635 while !remaining.is_empty() {
636 let (used, record) = reader.feed(remaining)?;
637 remaining = &remaining[used..];
638 if let Some(StreamRequestRecord::Data(part)) = record {
639 if part.len() > self.limits.max_body_len.saturating_sub(body.len()) {
640 return Err(Error::LimitExceeded);
641 }
642 body.extend_from_slice(&part);
643 }
644 }
645 let response = reader.finish_eof()?;
646 let request = Request {
647 method: head.method,
648 authority: head.authority,
649 path: head.path,
650 headers: head.headers,
651 body,
652 };
653 Ok(AuthenticatedRequest {
654 replay,
655 token: ReplayToken {
656 replay_id: replay.id,
657 retain_until_exclusive: replay.retain_until_exclusive,
658 request,
659 response,
660 },
661 })
662 }
663}
664
665#[cfg(test)]
666mod stream_tests {
667 use super::STREAM_MAX_DATA_RECORDS;
668 use crate::{Client, Error, Limits, Method, Request, RequestHead, Server, generate_key_pair};
669
670 #[test]
671 fn server_accepts_old_key_during_rotation() -> Result<(), Error> {
672 let old = generate_key_pair()?;
673 let new = generate_key_pair()?;
674 let old_public = old.public_key().to_vec();
675 let new_public = new.public_key().to_vec();
676 let (old_private, _) = old.into_parts();
677 let (new_private, _) = new.into_parts();
678 let psk = b"a 32-byte minimum rotation credential";
679 let server = Server::with_accepted_keys(
680 &new_private,
681 b"new".to_vec(),
682 vec![(old_private.to_vec(), b"old".to_vec())],
683 Limits::default(),
684 )?;
685 assert_eq!(server.public_key(), new_public);
686 let request = Request {
687 method: Method::Get,
688 authority: b"api.example.test".to_vec(),
689 path: b"/status".to_vec(),
690 headers: Vec::new(),
691 body: Vec::new(),
692 };
693 for (public_key, id) in [
694 (&old_public, b"old".as_slice()),
695 (&new_public, b"new".as_slice()),
696 ] {
697 let client = Client::new(
698 public_key,
699 id.to_vec(),
700 psk.to_vec(),
701 b"token-id".to_vec(),
702 Limits::default(),
703 )?;
704 let protected = client.protect(&request)?;
705 let preparsed = server.preparse(protected.envelope())?;
706 let _authenticated = server.authenticate(preparsed.token, psk)?;
707 }
708 let unknown = Client::new(
709 &old_public,
710 b"unknown".to_vec(),
711 psk.to_vec(),
712 b"token-id".to_vec(),
713 Limits::default(),
714 )?;
715 let protected = unknown.protect(&request)?;
716 assert_eq!(
717 server.preparse(protected.envelope()).err(),
718 Some(Error::UnknownRecipientKey)
719 );
720 assert_eq!(
721 Server::with_accepted_keys(
722 &new_private,
723 b"new".to_vec(),
724 vec![(new_private.to_vec(), b"new".to_vec())],
725 Limits::default(),
726 )
727 .err(),
728 Some(Error::InvalidConfiguration),
729 );
730 Ok(())
731 }
732
733 #[test]
734 fn data_record_count_guard_closes_both_sides() -> Result<(), Error> {
735 let keys = generate_key_pair()?;
736 let psk = b"a 32-byte minimum upload credential";
737 let client = Client::new(
738 keys.public_key(),
739 b"key".to_vec(),
740 psk.to_vec(),
741 b"psk".to_vec(),
742 Limits::default(),
743 )?;
744 let server = Server::new(&keys.into_parts().0, b"key".to_vec(), Limits::default())?;
745 let head = RequestHead {
746 method: Method::Post,
747 authority: b"api.example.test".to_vec(),
748 path: b"/upload".to_vec(),
749 headers: Vec::new(),
750 };
751 let (mut writer, _) = client.begin_stream(&head)?;
752 writer.data_records = STREAM_MAX_DATA_RECORDS;
753 assert_eq!(writer.push(b"x").err(), Some(Error::LimitExceeded));
754 assert!(writer.context.is_none());
755
756 let (mut writer, first) = client.begin_stream(&head)?;
757 let (used, record) = writer.push(b"x")?;
758 assert_eq!(used, 1);
759 assert!(record.is_none());
760 let (data, _) = writer.finish()?;
761 let preparsed = server.preparse_stream(&first)?;
762 let authenticated = server.authenticate_stream(preparsed.token, psk)?;
763 let mut reader = authenticated
764 .token
765 .admit(authenticated.replay.decision(true))?
766 .reader;
767 reader.data_records = STREAM_MAX_DATA_RECORDS;
768 assert_eq!(reader.feed(&data).err(), Some(Error::LimitExceeded));
769 assert!(reader.context.is_none());
770 assert!(reader.finish_eof().is_err());
771 Ok(())
772 }
773}
774
775pub(crate) struct ResponseMaterial {
776 pub(crate) secret: Zeroizing<[u8; RESPONSE_SECRET_LEN]>,
777 pub(crate) enc: [u8; ENC_LEN],
778 pub(crate) method: Method,
779 pub(crate) limits: Limits,
780}
781
782impl fmt::Debug for ResponseMaterial {
783 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
784 formatter
785 .debug_struct("ResponseMaterial")
786 .field("secret", &"[REDACTED]")
787 .field("enc_len", &self.enc.len())
788 .field("method", &self.method)
789 .field("limits", &self.limits)
790 .finish()
791 }
792}
793
794#[must_use]
796pub struct ResponseToken(ResponseMaterial);
797
798impl fmt::Debug for ResponseToken {
799 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
800 formatter
801 .debug_tuple("ResponseToken")
802 .field(&self.0)
803 .finish()
804 }
805}
806
807impl ResponseToken {
808 #[must_use]
810 pub fn into_opener(self) -> ResponseOpener {
811 ResponseOpener::new(self.0)
812 }
813
814 pub fn open_finite(self, envelope: &[u8]) -> Result<Response, Error> {
819 let mut opener = self.into_opener();
820 let mut offset = 0;
821 while offset < envelope.len() {
822 let (used, record) = opener.feed(&envelope[offset..])?;
823 offset += used;
824 if matches!(record, Some(ResponseRecord::Start(ref head)) if head.mode == ResponseMode::Sse)
825 {
826 return Err(Error::InvalidConfiguration);
827 }
828 if used == 0 {
829 return Err(Error::MalformedEnvelope);
830 }
831 }
832 opener.finish_eof()?.ok_or(Error::MalformedEnvelope)
833 }
834}
835
836#[must_use]
838pub struct ResponseCapability(ResponseMaterial);
839
840impl fmt::Debug for ResponseCapability {
841 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
842 formatter
843 .debug_tuple("ResponseCapability")
844 .field(&self.0)
845 .finish()
846 }
847}
848
849impl ResponseCapability {
850 pub fn into_sealer(
855 self,
856 status: u16,
857 headers: Vec<HeaderField>,
858 ) -> Result<(ResponseSealer, Vec<u8>), Error> {
859 self.into_sealer_with_entropy(status, headers, &mut SystemEntropy)
860 }
861
862 pub fn into_sealer_with_entropy(
867 self,
868 status: u16,
869 headers: Vec<HeaderField>,
870 entropy: &mut impl EntropySource,
871 ) -> Result<(ResponseSealer, Vec<u8>), Error> {
872 ResponseSealer::new(&self.0, status, headers, entropy)
873 }
874
875 pub fn protect_finite(self, response: &Response) -> Result<Vec<u8>, Error> {
880 self.protect_finite_with_entropy(response, &mut SystemEntropy)
881 }
882
883 pub fn protect_finite_with_entropy(
888 self,
889 response: &Response,
890 entropy: &mut impl EntropySource,
891 ) -> Result<Vec<u8>, Error> {
892 self.protect_finite_parts_with_entropy(
893 response.status,
894 response.headers.clone(),
895 &response.body,
896 entropy,
897 )
898 }
899
900 pub fn protect_finite_parts(
905 self,
906 status: u16,
907 headers: Vec<HeaderField>,
908 body: &[u8],
909 ) -> Result<Vec<u8>, Error> {
910 self.protect_finite_parts_with_entropy(status, headers, body, &mut SystemEntropy)
911 }
912
913 fn protect_finite_parts_with_entropy(
914 self,
915 status: u16,
916 headers: Vec<HeaderField>,
917 body: &[u8],
918 entropy: &mut impl EntropySource,
919 ) -> Result<Vec<u8>, Error> {
920 let (mut sealer, mut output) = self.into_sealer_with_entropy(status, headers, entropy)?;
921 if sealer.head_mode() != ResponseMode::Finite {
922 return Err(Error::InvalidConfiguration);
923 }
924 let data = sealer.seal_finite_body(body)?;
925 let end = sealer.finish()?;
926 output.reserve(data.as_ref().map_or(0, Vec::len) + end.len());
927 if let Some(data) = data {
928 output.extend_from_slice(&data);
929 }
930 output.extend_from_slice(&end);
931 Ok(output)
932 }
933}
934
935fn validate_id(identifier: &[u8]) -> Result<(), Error> {
936 if identifier.is_empty() || identifier.len() > HARD_MAX_ID_LEN {
937 return Err(Error::InvalidConfiguration);
938 }
939 Ok(())
940}
941
942fn validate_credential(psk: &[u8], psk_id: &[u8]) -> Result<(), Error> {
943 validate_id(psk_id)?;
944 if psk.len() < MIN_PSK_LEN || psk == psk_id {
945 return Err(Error::InvalidConfiguration);
946 }
947 Ok(())
948}
949
950fn unix_time_seconds() -> Result<u64, Error> {
951 SystemTime::now()
952 .duration_since(UNIX_EPOCH)
953 .map_err(|_| Error::ClockUnavailable)
954 .map(|duration| duration.as_secs())
955}
956
957fn replay_retention_deadline(issued_at_unix_s: u64, now_unix_s: u64) -> Result<u64, Error> {
958 let latest_issued_at = now_unix_s
959 .checked_add(CLOCK_SKEW_SECS)
960 .ok_or(Error::InvalidRequestTime)?;
961 let retain_until_exclusive = issued_at_unix_s
962 .checked_add(REQUEST_LIFETIME_SECS)
963 .and_then(|value| value.checked_add(CLOCK_SKEW_SECS))
964 .ok_or(Error::InvalidRequestTime)?;
965 if issued_at_unix_s > latest_issued_at || now_unix_s >= retain_until_exclusive {
966 return Err(Error::InvalidRequestTime);
967 }
968 Ok(retain_until_exclusive)
969}
970
971fn replay_id(header: &[u8], enc: &[u8]) -> [u8; 32] {
972 let mut digest = Sha256::new();
973 digest.update(REPLAY_LABEL);
974 digest.update(header);
975 digest.update(enc);
976 digest.finalize().into()
977}
978
979fn export_response_material<C>(
980 context: &C,
981 enc: &[u8],
982 method: Method,
983 limits: Limits,
984) -> Result<ResponseMaterial, Error>
985where
986 C: Exporter,
987{
988 let enc: [u8; ENC_LEN] = enc.try_into().map_err(|_| Error::CryptoFailure)?;
989 let mut secret = Zeroizing::new([0_u8; RESPONSE_SECRET_LEN]);
990 context.export_secret(RESPONSE_EXPORT_LABEL, secret.as_mut())?;
991 Ok(ResponseMaterial {
992 secret,
993 enc,
994 method,
995 limits,
996 })
997}
998
999pub(crate) fn derive_response_key_nonce(
1000 material: &ResponseMaterial,
1001 response_nonce: &[u8],
1002) -> Result<(ResponseKey, ResponseNonce), Error> {
1003 if response_nonce.len() != RESPONSE_NONCE_LEN {
1004 return Err(Error::MalformedEnvelope);
1005 }
1006 let mut salt = Zeroizing::new(Vec::with_capacity(ENC_LEN + RESPONSE_NONCE_LEN));
1007 salt.extend_from_slice(&material.enc);
1008 salt.extend_from_slice(response_nonce);
1009 let hkdf = Hkdf::<Sha256>::new(Some(&salt), material.secret.as_ref());
1010 let mut key = Zeroizing::new([0_u8; RESPONSE_KEY_LEN]);
1011 hkdf.expand(RESPONSE_KEY_INFO, key.as_mut())
1012 .map_err(|_| Error::CryptoFailure)?;
1013 let mut nonce = Zeroizing::new([0_u8; AEAD_NONCE_LEN]);
1014 hkdf.expand(RESPONSE_NONCE_INFO, nonce.as_mut())
1015 .map_err(|_| Error::CryptoFailure)?;
1016 Ok((key, nonce))
1017}
1018
1019trait Exporter {
1020 fn export_secret(&self, context: &[u8], output: &mut [u8]) -> Result<(), Error>;
1021}
1022
1023impl Exporter for hpke::aead::AeadCtxS<HpkeAead, Kdf, Kem> {
1024 fn export_secret(&self, context: &[u8], output: &mut [u8]) -> Result<(), Error> {
1025 self.export(context, output)
1026 .map_err(|_| Error::CryptoFailure)
1027 }
1028}
1029
1030impl Exporter for RecipientContext {
1031 fn export_secret(&self, context: &[u8], output: &mut [u8]) -> Result<(), Error> {
1032 self.export(context, output)
1033 .map_err(|_| Error::CryptoFailure)
1034 }
1035}
1036
1037fn max_stream_start_ciphertext(limits: Limits) -> usize {
1038 limits.max_header_bytes + limits.max_target_len + limits.max_header_count * 16 + 256 + TAG_LEN
1039}
1040
1041fn max_complete_envelope_len(limits: Limits) -> Result<usize, Error> {
1042 let records = limits
1043 .max_body_len
1044 .min(usize::try_from(STREAM_MAX_DATA_RECORDS).map_err(|_| Error::LimitExceeded)?);
1045 let data_overhead = records
1046 .checked_mul(4 + 2 + TAG_LEN)
1047 .ok_or(Error::LimitExceeded)?;
1048 REQUEST_FIXED_LEN
1049 .checked_add(2 * HARD_MAX_ID_LEN)
1050 .and_then(|length| length.checked_add(ENC_LEN + 4))
1051 .and_then(|length| length.checked_add(max_stream_start_ciphertext(limits)))
1052 .and_then(|length| length.checked_add(limits.max_body_len))
1053 .and_then(|length| length.checked_add(data_overhead))
1054 .and_then(|length| length.checked_add(4 + 1 + TAG_LEN))
1055 .ok_or(Error::LimitExceeded)
1056}
1057
1058fn stream_request_info(header: &[u8]) -> Vec<u8> {
1059 let mut info = Vec::with_capacity(STREAM_REQUEST_INFO_LABEL.len() + header.len());
1060 info.extend_from_slice(STREAM_REQUEST_INFO_LABEL);
1061 info.extend_from_slice(header);
1062 info
1063}
1064
1065fn stream_record_aad(header: &[u8], sequence: u64, ciphertext_len: u32) -> Vec<u8> {
1066 let mut aad = Vec::with_capacity(STREAM_REQUEST_AAD_LABEL.len() + header.len() + 12);
1067 aad.extend_from_slice(STREAM_REQUEST_AAD_LABEL);
1068 aad.extend_from_slice(header);
1069 aad.extend_from_slice(&sequence.to_be_bytes());
1070 aad.extend_from_slice(&ciphertext_len.to_be_bytes());
1071 aad
1072}
1073
1074fn seal_stream_record(
1075 context: &mut SenderContext,
1076 header: &[u8],
1077 sequence: u64,
1078 kind: u8,
1079 coding: Option<u8>,
1080 body: &[u8],
1081) -> Result<Vec<u8>, Error> {
1082 let ciphertext_len = body
1083 .len()
1084 .checked_add(1 + usize::from(coding.is_some()) + TAG_LEN)
1085 .ok_or(Error::LimitExceeded)?;
1086 let ciphertext_len = u32::try_from(ciphertext_len).map_err(|_| Error::LimitExceeded)?;
1087 let mut frame = Vec::with_capacity(4 + ciphertext_len as usize);
1088 frame.extend_from_slice(&ciphertext_len.to_be_bytes());
1089 frame.push(kind);
1090 if let Some(coding) = coding {
1091 frame.push(coding);
1092 }
1093 frame.extend_from_slice(body);
1094 let aad = stream_record_aad(header, sequence, ciphertext_len);
1095 let tag = context
1096 .seal_inout_detached(InOutBuf::from(&mut frame[4..]), &aad)
1097 .map_err(|_| Error::CryptoFailure)?;
1098 frame.extend_from_slice(tag.to_bytes().as_ref());
1099 Ok(frame)
1100}
1101
1102pub struct StreamRequestSealer {
1104 context: Option<SenderContext>,
1105 header: Vec<u8>,
1106 sequence: u64,
1107 data_records: u64,
1108 total_bytes: u64,
1109 content_length: Option<u64>,
1110 max_upload_bytes: u64,
1111 pending: Vec<u8>,
1112 response_token: Option<ResponseToken>,
1113}
1114
1115impl fmt::Debug for StreamRequestSealer {
1116 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1117 formatter
1118 .debug_struct("StreamRequestSealer")
1119 .field("sequence", &self.sequence)
1120 .field("total_bytes", &self.total_bytes)
1121 .finish_non_exhaustive()
1122 }
1123}
1124
1125impl StreamRequestSealer {
1126 pub fn push(&mut self, part: &[u8]) -> Result<(usize, Option<Vec<u8>>), Error> {
1133 let result = self.push_inner(part);
1134 if result.is_err() {
1135 self.context = None;
1136 self.response_token = None;
1137 }
1138 result
1139 }
1140
1141 fn push_inner(&mut self, part: &[u8]) -> Result<(usize, Option<Vec<u8>>), Error> {
1142 if self.context.is_none() {
1143 return Err(Error::InvalidConfiguration);
1144 }
1145 if part.is_empty() {
1146 return Ok((0, None));
1147 }
1148 if self.data_records >= STREAM_MAX_DATA_RECORDS {
1149 return Err(Error::LimitExceeded);
1150 }
1151 let consumed = part.len().min(STREAM_DATA_LEN - self.pending.len());
1152 let next_total = self
1153 .total_bytes
1154 .checked_add(consumed as u64)
1155 .ok_or(Error::LimitExceeded)?;
1156 if next_total > self.max_upload_bytes
1157 || self
1158 .content_length
1159 .is_some_and(|length| next_total > length)
1160 {
1161 return Err(Error::LimitExceeded);
1162 }
1163 self.pending.extend_from_slice(&part[..consumed]);
1164 self.total_bytes = next_total;
1165 if self.pending.len() == STREAM_DATA_LEN {
1166 let frame = self.seal_pending()?;
1167 Ok((consumed, Some(frame)))
1168 } else {
1169 Ok((consumed, None))
1170 }
1171 }
1172
1173 fn seal_pending(&mut self) -> Result<Vec<u8>, Error> {
1174 if self.pending.is_empty() || self.data_records >= STREAM_MAX_DATA_RECORDS {
1175 return Err(Error::LimitExceeded);
1176 }
1177 let (coding, coded) = compression::encode(&self.pending);
1178 let context = self.context.as_mut().ok_or(Error::InvalidConfiguration)?;
1179 let frame = seal_stream_record(
1180 context,
1181 &self.header,
1182 self.sequence,
1183 STREAM_DATA,
1184 Some(coding),
1185 &coded,
1186 )?;
1187 self.pending.clear();
1188 self.sequence += 1;
1189 self.data_records += 1;
1190 Ok(frame)
1191 }
1192
1193 pub fn finish(mut self) -> Result<(Vec<u8>, ResponseToken), Error> {
1198 if self
1199 .content_length
1200 .is_some_and(|length| self.total_bytes != length)
1201 {
1202 return Err(Error::InvalidConfiguration);
1203 }
1204 let mut frame = if self.pending.is_empty() {
1205 Vec::new()
1206 } else {
1207 self.seal_pending()?
1208 };
1209 let context = self.context.as_mut().ok_or(Error::InvalidConfiguration)?;
1210 frame.extend_from_slice(&seal_stream_record(
1211 context,
1212 &self.header,
1213 self.sequence,
1214 STREAM_END,
1215 None,
1216 &[],
1217 )?);
1218 let token = self
1219 .response_token
1220 .take()
1221 .ok_or(Error::InvalidConfiguration)?;
1222 Ok((frame, token))
1223 }
1224}
1225
1226impl Client {
1227 pub fn begin_stream(
1232 &self,
1233 head: &RequestHead,
1234 ) -> Result<(StreamRequestSealer, Vec<u8>), Error> {
1235 self.begin_stream_at_with_entropy(head, unix_time_seconds()?, &mut SystemEntropy)
1236 }
1237
1238 pub fn begin_stream_at_with_entropy(
1243 &self,
1244 head: &RequestHead,
1245 now_unix_s: u64,
1246 entropy: &mut impl EntropySource,
1247 ) -> Result<(StreamRequestSealer, Vec<u8>), Error> {
1248 let max_upload_bytes = self.limits.max_request_bytes;
1249 let content_length = message::stream_content_length(&head.headers)?;
1250 if content_length.is_some_and(|length| length > max_upload_bytes) {
1251 return Err(Error::LimitExceeded);
1252 }
1253 let clear_head = message::encode_stream_head(head, self.limits)?;
1254 let header = encode_request_header(&self.recipient_key_id, &self.psk_id, now_unix_s)?;
1255 let psk_bundle = PskBundle::new(self.psk.as_slice(), &self.psk_id)
1256 .map_err(|_| Error::InvalidConfiguration)?;
1257 let mut random_seed = Zeroizing::new([0_u8; 32]);
1258 entropy.fill(random_seed.as_mut())?;
1259 let mut random = ChaCha20Rng::from_seed(*random_seed);
1260 let (encapped_key, mut context) = setup_sender_with_rng::<HpkeAead, Kdf, Kem>(
1261 &OpModeS::Psk(psk_bundle),
1262 &self.recipient_public_key,
1263 &stream_request_info(&header),
1264 &mut random,
1265 )
1266 .map_err(|_| Error::CryptoFailure)?;
1267 let enc = encapped_key.to_bytes();
1268 let response_material =
1269 export_response_material(&context, enc.as_ref(), head.method, self.limits)?;
1270 let start = seal_stream_record(&mut context, &header, 0, STREAM_START, None, &clear_head)?;
1271 let mut first = Vec::with_capacity(header.len() + ENC_LEN + start.len());
1272 first.extend_from_slice(&header);
1273 first.extend_from_slice(enc.as_ref());
1274 first.extend_from_slice(&start);
1275 Ok((
1276 StreamRequestSealer {
1277 context: Some(context),
1278 header,
1279 sequence: 1,
1280 data_records: 0,
1281 total_bytes: 0,
1282 content_length,
1283 max_upload_bytes,
1284 pending: Vec::with_capacity(STREAM_DATA_LEN),
1285 response_token: Some(ResponseToken(response_material)),
1286 },
1287 first,
1288 ))
1289 }
1290}
1291
1292#[must_use]
1294pub struct PreparsedStreamRequest {
1295 pub credential: CredentialRequest,
1297 pub token: StreamStartToken,
1299}
1300
1301#[must_use]
1303pub struct StreamStartToken {
1304 first: Vec<u8>,
1305}
1306
1307#[must_use]
1309pub struct AuthenticatedStreamRequest {
1310 pub replay: ReplayRequest,
1312 pub token: StreamReplayToken,
1314}
1315
1316#[must_use]
1318pub struct StreamReplayToken {
1319 replay_id: [u8; 32],
1320 retain_until_exclusive: u64,
1321 head: RequestHead,
1322 reader: StreamRequestOpener,
1323}
1324
1325impl StreamReplayToken {
1326 pub fn admit(self, decision: ReplayDecision) -> Result<OpenedStreamRequest, Error> {
1331 self.admit_at(decision, unix_time_seconds()?)
1332 }
1333
1334 pub fn admit_at(
1339 self,
1340 decision: ReplayDecision,
1341 now_unix_s: u64,
1342 ) -> Result<OpenedStreamRequest, Error> {
1343 check_replay_decision(
1344 self.replay_id,
1345 self.retain_until_exclusive,
1346 decision,
1347 now_unix_s,
1348 )?;
1349 Ok(OpenedStreamRequest {
1350 head: self.head,
1351 reader: self.reader,
1352 })
1353 }
1354}
1355
1356#[must_use]
1358pub struct OpenedStreamRequest {
1359 pub head: RequestHead,
1361 pub reader: StreamRequestOpener,
1363}
1364
1365#[derive(Debug, Eq, PartialEq)]
1367pub enum StreamRequestRecord {
1368 Data(Vec<u8>),
1370 End,
1372}
1373
1374pub struct StreamRequestOpener {
1376 context: Option<RecipientContext>,
1377 header: Vec<u8>,
1378 sequence: u64,
1379 data_records: u64,
1380 total_bytes: u64,
1381 content_length: Option<u64>,
1382 max_upload_bytes: u64,
1383 frame: Vec<u8>,
1384 ended: bool,
1385 response_material: Option<ResponseMaterial>,
1386}
1387
1388impl fmt::Debug for StreamRequestOpener {
1389 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1390 formatter
1391 .debug_struct("StreamRequestOpener")
1392 .field("sequence", &self.sequence)
1393 .field("total_bytes", &self.total_bytes)
1394 .field("ended", &self.ended)
1395 .finish_non_exhaustive()
1396 }
1397}
1398
1399impl StreamRequestOpener {
1400 pub fn feed(&mut self, input: &[u8]) -> Result<(usize, Option<StreamRequestRecord>), Error> {
1407 let result = self.feed_inner(input);
1408 if result.is_err() {
1409 self.context = None;
1410 self.response_material = None;
1411 self.frame.clear();
1412 }
1413 result
1414 }
1415
1416 fn feed_inner(&mut self, input: &[u8]) -> Result<(usize, Option<StreamRequestRecord>), Error> {
1417 if self.context.is_none() || (self.ended && !input.is_empty()) {
1418 return Err(Error::MalformedEnvelope);
1419 }
1420 let mut used = 0;
1421 if self.frame.len() < 4 {
1422 let take = (4 - self.frame.len()).min(input.len());
1423 self.frame.extend_from_slice(&input[..take]);
1424 used += take;
1425 if self.frame.len() < 4 {
1426 return Ok((used, None));
1427 }
1428 }
1429 let ciphertext_len = u32::from_be_bytes(
1430 self.frame[..4]
1431 .try_into()
1432 .map_err(|_| Error::MalformedEnvelope)?,
1433 ) as usize;
1434 if ciphertext_len < 1 + TAG_LEN {
1435 return Err(Error::MalformedEnvelope);
1436 }
1437 if ciphertext_len > STREAM_DATA_LEN + 2 + TAG_LEN {
1438 return Err(Error::LimitExceeded);
1439 }
1440 let frame_len = 4 + ciphertext_len;
1441 let take = (frame_len - self.frame.len()).min(input.len() - used);
1442 self.frame.extend_from_slice(&input[used..used + take]);
1443 used += take;
1444 if self.frame.len() < frame_len {
1445 return Ok((used, None));
1446 }
1447 let plain_end = frame_len - TAG_LEN;
1448 let tag = AeadTag::<HpkeAead>::from_bytes(&self.frame[plain_end..])
1449 .map_err(|_| Error::AuthenticationFailed)?;
1450 let aad = stream_record_aad(
1451 &self.header,
1452 self.sequence,
1453 u32::try_from(ciphertext_len).map_err(|_| Error::LimitExceeded)?,
1454 );
1455 self.context
1456 .as_mut()
1457 .ok_or(Error::MalformedEnvelope)?
1458 .open_inout_detached(InOutBuf::from(&mut self.frame[4..plain_end]), &aad, &tag)
1459 .map_err(|_| Error::AuthenticationFailed)?;
1460 let kind = self.frame[4];
1461 let body = &self.frame[5..plain_end];
1462 let record = match kind {
1463 STREAM_DATA => {
1464 if body.len() < 2 || self.data_records >= STREAM_MAX_DATA_RECORDS {
1465 return Err(if body.len() < 2 {
1466 Error::MalformedEnvelope
1467 } else {
1468 Error::LimitExceeded
1469 });
1470 }
1471 let clear = compression::decode(body[0], &body[1..], STREAM_DATA_LEN)?;
1472 let next_total = self
1473 .total_bytes
1474 .checked_add(clear.len() as u64)
1475 .ok_or(Error::LimitExceeded)?;
1476 if next_total > self.max_upload_bytes
1477 || self
1478 .content_length
1479 .is_some_and(|length| next_total > length)
1480 {
1481 return Err(Error::LimitExceeded);
1482 }
1483 self.total_bytes = next_total;
1484 self.data_records += 1;
1485 self.sequence += 1;
1486 StreamRequestRecord::Data(clear)
1487 }
1488 STREAM_END => {
1489 if !body.is_empty()
1490 || self
1491 .content_length
1492 .is_some_and(|length| self.total_bytes != length)
1493 {
1494 return Err(Error::MalformedEnvelope);
1495 }
1496 self.ended = true;
1497 StreamRequestRecord::End
1498 }
1499 _ => return Err(Error::MalformedEnvelope),
1500 };
1501 self.frame.clear();
1502 Ok((used, Some(record)))
1503 }
1504
1505 pub fn finish_eof(mut self) -> Result<ResponseCapability, Error> {
1510 if !self.ended || !self.frame.is_empty() || self.context.is_none() {
1511 return Err(Error::MalformedEnvelope);
1512 }
1513 Ok(ResponseCapability(
1514 self.response_material
1515 .take()
1516 .ok_or(Error::MalformedEnvelope)?,
1517 ))
1518 }
1519}
1520
1521impl Server {
1522 pub fn stream_start_length(&self, input: &[u8]) -> Result<Option<usize>, Error> {
1527 stream_start_length(input, max_stream_start_ciphertext(self.limits))
1528 }
1529
1530 pub fn preparse_stream(&self, first: &[u8]) -> Result<PreparsedStreamRequest, Error> {
1535 let parsed = parse_stream_start(first, max_stream_start_ciphertext(self.limits))?;
1536 self.private_key_for(parsed.key_id)?;
1537 Ok(PreparsedStreamRequest {
1538 credential: CredentialRequest {
1539 psk_id: parsed.psk_id.to_vec(),
1540 },
1541 token: StreamStartToken {
1542 first: first.to_vec(),
1543 },
1544 })
1545 }
1546
1547 pub fn authenticate_stream(
1552 &self,
1553 token: StreamStartToken,
1554 psk: &[u8],
1555 ) -> Result<AuthenticatedStreamRequest, Error> {
1556 self.authenticate_stream_at(token, psk, unix_time_seconds()?)
1557 }
1558
1559 pub fn authenticate_stream_at(
1564 &self,
1565 token: StreamStartToken,
1566 psk: &[u8],
1567 now_unix_s: u64,
1568 ) -> Result<AuthenticatedStreamRequest, Error> {
1569 let StreamStartToken { first } = token;
1570 let max_upload_bytes = self.limits.max_request_bytes;
1571 let parsed = parse_stream_start(&first, max_stream_start_ciphertext(self.limits))?;
1572 let private_key = self.private_key_for(parsed.key_id)?;
1573 validate_credential(psk, parsed.psk_id).map_err(|_| Error::InvalidCredential)?;
1574 let encapped_key = <Kem as KemTrait>::EncappedKey::from_bytes(parsed.enc)
1575 .map_err(|_| Error::AuthenticationFailed)?;
1576 let psk_bundle =
1577 PskBundle::new(psk, parsed.psk_id).map_err(|_| Error::InvalidCredential)?;
1578 let mut context = setup_receiver::<HpkeAead, Kdf, Kem>(
1579 &OpModeR::Psk(psk_bundle),
1580 private_key,
1581 &encapped_key,
1582 &stream_request_info(parsed.header),
1583 )
1584 .map_err(|_| Error::AuthenticationFailed)?;
1585 let mut clear = parsed.ciphertext.to_vec();
1586 let plain_end = clear.len() - TAG_LEN;
1587 let tag = AeadTag::<HpkeAead>::from_bytes(&clear[plain_end..])
1588 .map_err(|_| Error::AuthenticationFailed)?;
1589 let aad = stream_record_aad(
1590 parsed.header,
1591 0,
1592 u32::try_from(clear.len()).map_err(|_| Error::LimitExceeded)?,
1593 );
1594 context
1595 .open_inout_detached(InOutBuf::from(&mut clear[..plain_end]), &aad, &tag)
1596 .map_err(|_| Error::AuthenticationFailed)?;
1597 if clear.first() != Some(&STREAM_START) {
1598 return Err(Error::MalformedEnvelope);
1599 }
1600 let head = message::decode_stream_head(&clear[1..plain_end], self.limits)?;
1601 let content_length = message::stream_content_length(&head.headers)?;
1602 if content_length.is_some_and(|length| length > max_upload_bytes) {
1603 return Err(Error::LimitExceeded);
1604 }
1605 let retain_until_exclusive =
1606 replay_retention_deadline(parsed.issued_at_unix_s, now_unix_s)?;
1607 let replay_id = replay_id(parsed.header, parsed.enc);
1608 let response_material =
1609 export_response_material(&context, parsed.enc, head.method, self.limits)?;
1610 Ok(AuthenticatedStreamRequest {
1611 replay: ReplayRequest {
1612 id: replay_id,
1613 retain_until_exclusive,
1614 },
1615 token: StreamReplayToken {
1616 replay_id,
1617 retain_until_exclusive,
1618 head,
1619 reader: StreamRequestOpener {
1620 context: Some(context),
1621 header: parsed.header.to_vec(),
1622 sequence: 1,
1623 data_records: 0,
1624 total_bytes: 0,
1625 content_length,
1626 max_upload_bytes,
1627 frame: Vec::new(),
1628 ended: false,
1629 response_material: Some(response_material),
1630 },
1631 },
1632 })
1633 }
1634}