1use alloc::boxed::Box;
2use alloc::vec;
3use alloc::vec::Vec;
4use core::iter;
5
6use pki_types::{DnsName, EchConfigListBytes, ServerName};
7use subtle::ConstantTimeEq;
8
9use crate::CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV;
10use crate::client::tls13;
11use crate::crypto::SecureRandom;
12use crate::crypto::hash::Hash;
13use crate::crypto::hpke::{EncapsulatedSecret, Hpke, HpkePublicKey, HpkeSealer, HpkeSuite};
14use crate::hash_hs::{HandshakeHash, HandshakeHashBuffer};
15use crate::log::{debug, trace, warn};
16use crate::msgs::base::{Payload, PayloadU16};
17use crate::msgs::codec::{Codec, Reader};
18use crate::msgs::enums::{ExtensionType, HpkeKem};
19use crate::msgs::handshake::{
20 ClientExtensions, ClientHelloPayload, EchConfigContents, EchConfigPayload, Encoding,
21 EncryptedClientHello, EncryptedClientHelloOuter, HandshakeMessagePayload, HandshakePayload,
22 HelloRetryRequest, HpkeKeyConfig, HpkeSymmetricCipherSuite, PresharedKeyBinder,
23 PresharedKeyOffer, Random, ServerHelloPayload, ServerNamePayload,
24};
25use crate::msgs::message::{Message, MessagePayload};
26use crate::msgs::persist;
27use crate::msgs::persist::Retrieved;
28use crate::tls13::key_schedule::{
29 KeyScheduleEarly, KeyScheduleHandshakeStart, server_ech_hrr_confirmation_secret,
30};
31use crate::{
32 AlertDescription, ClientConfig, CommonState, EncryptedClientHelloError, Error,
33 PeerIncompatible, PeerMisbehaved, ProtocolVersion, Tls13CipherSuite,
34};
35
36#[derive(Clone, Debug)]
38pub enum EchMode {
39 Enable(EchConfig),
42
43 Grease(EchGreaseConfig),
48}
49
50impl EchMode {
51 pub fn fips(&self) -> bool {
53 match self {
54 Self::Enable(ech_config) => ech_config.suite.fips(),
55 Self::Grease(grease_config) => grease_config.suite.fips(),
56 }
57 }
58}
59
60impl From<EchConfig> for EchMode {
61 fn from(config: EchConfig) -> Self {
62 Self::Enable(config)
63 }
64}
65
66impl From<EchGreaseConfig> for EchMode {
67 fn from(config: EchGreaseConfig) -> Self {
68 Self::Grease(config)
69 }
70}
71
72#[derive(Clone, Debug)]
76pub struct EchConfig {
77 pub(crate) config: EchConfigPayload,
79
80 pub(crate) suite: &'static dyn Hpke,
83}
84
85impl EchConfig {
86 pub fn new(
101 ech_config_list: EchConfigListBytes<'_>,
102 hpke_suites: &[&'static dyn Hpke],
103 ) -> Result<Self, Error> {
104 let ech_configs = Vec::<EchConfigPayload>::read(&mut Reader::init(&ech_config_list))
105 .map_err(|_| {
106 Error::InvalidEncryptedClientHello(EncryptedClientHelloError::InvalidConfigList)
107 })?;
108
109 #[cfg_attr(not(feature = "logging"), allow(clippy::unused_enumerate_index))]
112 for (_i, config) in ech_configs.iter().enumerate() {
113 let contents = match config {
114 EchConfigPayload::V18(contents) => contents,
115 EchConfigPayload::Unknown {
116 version: _version, ..
117 } => {
118 warn!(
119 "ECH config {} has unsupported version {:?}",
120 _i + 1,
121 _version
122 );
123 continue; }
125 };
126
127 if contents.has_unknown_mandatory_extension() || contents.has_duplicate_extension() {
128 warn!("ECH config has duplicate, or unknown mandatory extensions: {contents:?}",);
129 continue; }
131
132 let key_config = &contents.key_config;
133 for cipher_suite in &key_config.symmetric_cipher_suites {
134 if cipher_suite.aead_id.tag_len().is_none() {
135 continue; }
137
138 let suite = HpkeSuite {
139 kem: key_config.kem_id,
140 sym: *cipher_suite,
141 };
142 if let Some(hpke) = hpke_suites
143 .iter()
144 .find(|hpke| hpke.suite() == suite)
145 {
146 debug!(
147 "selected ECH config ID {:?} suite {:?} public_name {:?}",
148 key_config.config_id, suite, contents.public_name
149 );
150 return Ok(Self {
151 config: config.clone(),
152 suite: *hpke,
153 });
154 }
155 }
156 }
157
158 Err(EncryptedClientHelloError::NoCompatibleConfig.into())
159 }
160
161 pub(super) fn state(
162 &self,
163 server_name: ServerName<'static>,
164 config: &ClientConfig,
165 ) -> Result<EchState, Error> {
166 EchState::new(
167 self,
168 server_name.clone(),
169 config
170 .client_auth_cert_resolver
171 .has_certs(),
172 config.provider.secure_random,
173 config.enable_sni,
174 )
175 }
176
177 pub(crate) fn hpke_info(&self) -> Vec<u8> {
181 let mut info = Vec::with_capacity(128);
182 info.extend_from_slice(b"tls ech\0");
184 self.config.encode(&mut info);
185 info
186 }
187}
188
189#[derive(Clone, Debug)]
191pub struct EchGreaseConfig {
192 pub(crate) suite: &'static dyn Hpke,
193 pub(crate) placeholder_key: HpkePublicKey,
194}
195
196impl EchGreaseConfig {
197 pub fn new(suite: &'static dyn Hpke, placeholder_key: HpkePublicKey) -> Self {
207 Self {
208 suite,
209 placeholder_key,
210 }
211 }
212
213 pub(crate) fn grease_ext(
218 &self,
219 secure_random: &'static dyn SecureRandom,
220 inner_name: ServerName<'static>,
221 outer_hello: &ClientHelloPayload,
222 ) -> Result<EncryptedClientHello, Error> {
223 trace!("Preparing GREASE ECH extension");
224
225 let mut config_id: [u8; 1] = [0; 1];
227 secure_random.fill(&mut config_id[..])?;
228
229 let suite = self.suite.suite();
230
231 let mut grease_state = EchState::new(
234 &EchConfig {
235 config: EchConfigPayload::V18(EchConfigContents {
236 key_config: HpkeKeyConfig {
237 config_id: config_id[0],
238 kem_id: HpkeKem::DHKEM_P256_HKDF_SHA256,
239 public_key: PayloadU16::new(self.placeholder_key.0.clone()),
240 symmetric_cipher_suites: vec![suite.sym],
241 },
242 maximum_name_length: 0,
243 public_name: DnsName::try_from("filler").unwrap(),
244 extensions: Vec::default(),
245 }),
246 suite: self.suite,
247 },
248 inner_name,
249 false,
250 secure_random,
251 false, )?;
253
254 let encoded_inner_hello = grease_state.encode_inner_hello(outer_hello, None, &None);
257
258 let payload_len = encoded_inner_hello.len()
260 + suite
261 .sym
262 .aead_id
263 .tag_len()
264 .unwrap();
267 let mut payload = vec![0; payload_len];
268 secure_random.fill(&mut payload)?;
269
270 Ok(EncryptedClientHello::Outer(EncryptedClientHelloOuter {
272 cipher_suite: suite.sym,
273 config_id: config_id[0],
274 enc: PayloadU16::new(grease_state.enc.0),
275 payload: PayloadU16::new(payload),
276 }))
277 }
278}
279
280#[derive(Debug, Clone, Copy, Eq, PartialEq)]
282pub enum EchStatus {
283 NotOffered,
285 Grease,
287 Offered,
289 Accepted,
291 Rejected,
293}
294
295pub(crate) struct EchState {
297 pub(crate) outer_name: DnsName<'static>,
300 pub(crate) early_data_key_schedule: Option<KeyScheduleEarly>,
303 pub(crate) inner_hello_random: Random,
305 pub(crate) inner_hello_transcript: HandshakeHashBuffer,
308 secure_random: &'static dyn SecureRandom,
310 sender: Box<dyn HpkeSealer>,
312 config_id: u8,
314 inner_name: ServerName<'static>,
316 maximum_name_length: u8,
319 cipher_suite: HpkeSymmetricCipherSuite,
322 enc: EncapsulatedSecret,
325 enable_sni: bool,
327 sent_extensions: Vec<ExtensionType>,
329 accepted_by_hello_retry_request: Option<bool>,
332}
333
334impl EchState {
335 pub(crate) fn new(
336 config: &EchConfig,
337 inner_name: ServerName<'static>,
338 client_auth_enabled: bool,
339 secure_random: &'static dyn SecureRandom,
340 enable_sni: bool,
341 ) -> Result<Self, Error> {
342 let EchConfigPayload::V18(config_contents) = &config.config else {
343 unreachable!("ECH config version mismatch");
346 };
347 let key_config = &config_contents.key_config;
348
349 let (enc, sender) = config.suite.setup_sealer(
352 &config.hpke_info(),
353 &HpkePublicKey(key_config.public_key.0.clone()),
354 )?;
355
356 let mut inner_hello_transcript = HandshakeHashBuffer::new();
358 if client_auth_enabled {
359 inner_hello_transcript.set_client_auth_enabled();
360 }
361
362 Ok(Self {
363 secure_random,
364 sender,
365 config_id: key_config.config_id,
366 inner_name,
367 outer_name: config_contents.public_name.clone(),
368 maximum_name_length: config_contents.maximum_name_length,
369 cipher_suite: config.suite.suite().sym,
370 enc,
371 inner_hello_random: Random::new(secure_random)?,
372 inner_hello_transcript,
373 early_data_key_schedule: None,
374 enable_sni,
375 sent_extensions: Vec::new(),
376 accepted_by_hello_retry_request: None,
377 })
378 }
379
380 pub(crate) fn ech_hello(
389 &mut self,
390 mut outer_hello: ClientHelloPayload,
391 retry_req: Option<&HelloRetryRequest>,
392 resuming: &Option<Retrieved<&persist::Tls13ClientSessionValue>>,
393 ) -> Result<ClientHelloPayload, Error> {
394 trace!(
395 "Preparing ECH offer {}",
396 if retry_req.is_some() { "for retry" } else { "" }
397 );
398
399 let encoded_inner_hello = self.encode_inner_hello(&outer_hello, retry_req, resuming);
401
402 let payload_len = encoded_inner_hello.len()
406 + self
407 .cipher_suite
408 .aead_id
409 .tag_len()
410 .unwrap();
413
414 let enc = match retry_req.is_some() {
416 true => Vec::default(),
417 false => self.enc.0.clone(),
418 };
419
420 fn outer_hello_ext(ctx: &EchState, enc: Vec<u8>, payload: Vec<u8>) -> EncryptedClientHello {
421 EncryptedClientHello::Outer(EncryptedClientHelloOuter {
422 cipher_suite: ctx.cipher_suite,
423 config_id: ctx.config_id,
424 enc: PayloadU16::new(enc),
425 payload: PayloadU16::new(payload),
426 })
427 }
428
429 if let Some(psk_offer) = outer_hello.preshared_key_offer.as_mut() {
434 self.grease_psk(psk_offer)?;
435 }
436
437 outer_hello.encrypted_client_hello =
439 Some(outer_hello_ext(self, enc.clone(), vec![0; payload_len]));
440
441 let payload = self
443 .sender
444 .seal(&outer_hello.get_encoding(), &encoded_inner_hello)?;
445
446 outer_hello.encrypted_client_hello = Some(outer_hello_ext(self, enc, payload));
448
449 Ok(outer_hello)
450 }
451
452 pub(crate) fn confirm_acceptance(
454 self,
455 ks: &mut KeyScheduleHandshakeStart,
456 server_hello: &ServerHelloPayload,
457 server_hello_encoded: &Payload<'_>,
458 hash: &'static dyn Hash,
459 server_name: &mut ServerName<'static>,
460 common: &mut CommonState,
461 ) -> Result<Option<EchAccepted>, Error> {
462 if self.accepted_by_hello_retry_request == Some(false) {
465 trace!("ECH rejected by server in hello retry request");
466 *server_name = ServerName::DnsName(self.outer_name);
467 return Ok(None);
468 }
469
470 let inner_transcript = self
472 .inner_hello_transcript
473 .start_hash(hash);
474
475 let mut confirmation_transcript = inner_transcript.clone();
478
479 confirmation_transcript
482 .add_message(&Self::server_hello_conf(server_hello, server_hello_encoded));
483
484 let derived = ks.server_ech_confirmation_secret(
486 self.inner_hello_random.0.as_ref(),
487 confirmation_transcript.current_hash(),
488 );
489
490 match ConstantTimeEq::ct_eq(derived.as_ref(), server_hello.random.0[24..].as_ref()).into() {
495 true => {
496 trace!("ECH accepted by server");
497 Ok(Some(EchAccepted {
498 transcript: inner_transcript,
499 random: self.inner_hello_random,
500 sent_extensions: self.sent_extensions,
501 }))
502 }
503 false if self.accepted_by_hello_retry_request == Some(true) => Err(common
506 .send_fatal_alert(
507 AlertDescription::IllegalParameter,
508 PeerMisbehaved::EchAcceptanceVariedAfterRetry,
509 )),
510 false => {
511 trace!("ECH rejected by server");
512
513 *server_name = ServerName::DnsName(self.outer_name);
517
518 Ok(None)
519 }
520 }
521 }
522
523 pub(crate) fn confirm_hrr_acceptance(
524 &mut self,
525 hrr: &HelloRetryRequest,
526 cs: &Tls13CipherSuite,
527 common: &mut CommonState,
528 ) -> Result<bool, Error> {
529 let ech_conf = match &hrr.encrypted_client_hello {
531 None => {
533 self.accepted_by_hello_retry_request = Some(false);
534 return Ok(false);
535 }
536 Some(ech_conf) if ech_conf.bytes().len() != 8 => {
539 return Err({
540 common.send_fatal_alert(
541 AlertDescription::DecodeError,
542 PeerMisbehaved::IllegalHelloRetryRequestWithInvalidEch,
543 )
544 });
545 }
546 Some(ech_conf) => ech_conf,
547 };
548
549 let confirmation_transcript = self.inner_hello_transcript.clone();
552 let mut confirmation_transcript =
553 confirmation_transcript.start_hash(cs.common.hash_provider);
554 confirmation_transcript.rollup_for_hrr();
555 confirmation_transcript.add_message(&Self::hello_retry_request_conf(hrr));
556
557 let derived = server_ech_hrr_confirmation_secret(
558 cs.hkdf_provider,
559 &self.inner_hello_random.0,
560 confirmation_transcript.current_hash(),
561 );
562
563 let accepted = match ConstantTimeEq::ct_eq(derived.as_ref(), ech_conf.bytes()).into() {
564 true => {
565 trace!("ECH accepted by server in hello retry request");
566 true
567 }
568 false => {
569 trace!("ECH rejected by server in hello retry request");
570 false
571 }
572 };
573 self.accepted_by_hello_retry_request = Some(accepted);
574 Ok(accepted)
575 }
576
577 pub(crate) fn transcript_hrr_update(&mut self, hash: &'static dyn Hash, m: &Message<'_>) {
582 trace!("Updating ECH inner transcript for HRR");
583
584 let inner_transcript = self
585 .inner_hello_transcript
586 .clone()
587 .start_hash(hash);
588
589 let mut inner_transcript_buffer = inner_transcript.into_hrr_buffer();
590 inner_transcript_buffer.add_message(m);
591 self.inner_hello_transcript = inner_transcript_buffer;
592 }
593
594 fn encode_inner_hello(
596 &mut self,
597 outer_hello: &ClientHelloPayload,
598 retryreq: Option<&HelloRetryRequest>,
599 resuming: &Option<Retrieved<&persist::Tls13ClientSessionValue>>,
600 ) -> Vec<u8> {
601 let mut inner_hello = ClientHelloPayload {
603 client_version: outer_hello.client_version,
605 session_id: outer_hello.session_id,
606 compression_methods: outer_hello.compression_methods.clone(),
607
608 extensions: Box::new(ClientExtensions::default()),
610
611 random: self.inner_hello_random,
615
616 cipher_suites: outer_hello
620 .cipher_suites
621 .iter()
622 .filter(|cs| **cs != TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
623 .cloned()
624 .collect(),
625 };
626
627 inner_hello.order_seed = outer_hello.order_seed;
628
629 inner_hello.encrypted_client_hello = Some(EncryptedClientHello::Inner);
632
633 let inner_sni = match &self.inner_name {
634 ServerName::DnsName(dns_name) if self.enable_sni => Some(dns_name),
637 _ => None,
638 };
639
640 let outer_extensions = outer_hello.used_extensions_in_encoding_order();
646 let mut compressed_exts = Vec::with_capacity(outer_extensions.len());
647 for ext in outer_extensions {
648 if matches!(
652 ext,
653 ExtensionType::ExtendedMasterSecret
654 | ExtensionType::SessionTicket
655 | ExtensionType::ECPointFormats
656 ) {
657 continue;
658 }
659
660 if ext == ExtensionType::ServerName {
661 if let Some(sni_value) = inner_sni {
663 inner_hello.server_name = Some(ServerNamePayload::from(sni_value));
664 }
665 continue;
667 }
668
669 if ext.ech_compress() {
672 compressed_exts.push(ext);
673 }
674
675 inner_hello.clone_one(outer_hello, ext);
676 }
677
678 inner_hello.contiguous_extensions = compressed_exts.clone();
681
682 self.sent_extensions = inner_hello.collect_used();
686
687 if let Some(resuming) = resuming.as_ref() {
689 let mut chp = HandshakeMessagePayload(HandshakePayload::ClientHello(inner_hello));
690
691 self.early_data_key_schedule = Some(tls13::fill_in_psk_binder(
693 resuming,
694 &self.inner_hello_transcript,
695 &mut chp,
696 ));
697
698 inner_hello = match chp.0 {
701 HandshakePayload::ClientHello(chp) => chp,
702 _ => unreachable!(),
704 };
705 }
706
707 trace!("ECH Inner Hello: {inner_hello:#?}");
708
709 let mut encoded_hello = inner_hello.ech_inner_encoding(compressed_exts);
713
714 let max_name_len = usize::from(self.maximum_name_length);
717 let max_name_len = if max_name_len > 0 { max_name_len } else { 255 };
718
719 let name_padding_len = match &inner_hello.server_name {
720 Some(ServerNamePayload::SingleDnsName(name)) => {
721 Ord::max(0, max_name_len.saturating_sub(name.as_ref().len()))
724 }
725 _ => max_name_len + 9,
728 };
729 encoded_hello.extend(iter::repeat(0).take(name_padding_len));
730
731 let padding_len = 31 - ((encoded_hello.len() - 1) % 32);
734 encoded_hello.extend(iter::repeat(0).take(padding_len));
735
736 let inner_hello_msg = Message {
738 version: match retryreq {
739 Some(_) => ProtocolVersion::TLSv1_2,
743 None => ProtocolVersion::TLSv1_0,
749 },
750 payload: MessagePayload::handshake(HandshakeMessagePayload(
751 HandshakePayload::ClientHello(inner_hello),
752 )),
753 };
754
755 self.inner_hello_transcript
757 .add_message(&inner_hello_msg);
758
759 encoded_hello
760 }
761
762 fn grease_psk(&self, psk_offer: &mut PresharedKeyOffer) -> Result<(), Error> {
764 for ident in psk_offer.identities.iter_mut() {
765 self.secure_random
768 .fill(&mut ident.identity.0)?;
769 let mut ticket_age = [0_u8; 4];
772 self.secure_random
773 .fill(&mut ticket_age)?;
774 ident.obfuscated_ticket_age = u32::from_be_bytes(ticket_age);
775 }
776
777 psk_offer.binders = psk_offer
780 .binders
781 .iter()
782 .map(|old_binder| {
783 let mut new_binder = vec![0; old_binder.as_ref().len()];
786 self.secure_random
787 .fill(&mut new_binder)?;
788 Ok::<PresharedKeyBinder, Error>(PresharedKeyBinder::from(new_binder))
789 })
790 .collect::<Result<_, _>>()?;
791 Ok(())
792 }
793
794 fn server_hello_conf(
795 server_hello: &ServerHelloPayload,
796 server_hello_encoded: &Payload<'_>,
797 ) -> Message<'static> {
798 let mut encoded = server_hello_encoded.clone().into_vec();
806 encoded[SERVER_HELLO_ECH_CONFIRMATION_SPAN].fill(0x00);
807
808 Message {
809 version: ProtocolVersion::TLSv1_3,
810 payload: MessagePayload::Handshake {
811 encoded: Payload::Owned(encoded),
812 parsed: HandshakeMessagePayload(HandshakePayload::ServerHello(
813 server_hello.clone(),
814 )),
815 },
816 }
817 }
818
819 fn hello_retry_request_conf(retry_req: &HelloRetryRequest) -> Message<'_> {
820 Self::ech_conf_message(HandshakeMessagePayload(
821 HandshakePayload::HelloRetryRequest(retry_req.clone()),
822 ))
823 }
824
825 fn ech_conf_message(hmp: HandshakeMessagePayload<'_>) -> Message<'_> {
826 let mut hmp_encoded = Vec::new();
827 hmp.payload_encode(&mut hmp_encoded, Encoding::EchConfirmation);
828 Message {
829 version: ProtocolVersion::TLSv1_3,
830 payload: MessagePayload::Handshake {
831 encoded: Payload::new(hmp_encoded),
832 parsed: hmp,
833 },
834 }
835 }
836}
837
838const SERVER_HELLO_ECH_CONFIRMATION_SPAN: core::ops::Range<usize> =
846 (1 + 3 + 2 + 24)..(1 + 3 + 2 + 32);
847
848pub(crate) struct EchAccepted {
852 pub(crate) transcript: HandshakeHash,
853 pub(crate) random: Random,
854 pub(crate) sent_extensions: Vec<ExtensionType>,
855}
856
857pub(crate) fn fatal_alert_required(
858 retry_configs: Option<Vec<EchConfigPayload>>,
859 common: &mut CommonState,
860) -> Error {
861 common.send_fatal_alert(
862 AlertDescription::EncryptedClientHelloRequired,
863 PeerIncompatible::ServerRejectedEncryptedClientHello(retry_configs),
864 )
865}
866
867#[cfg(test)]
868mod tests {
869 use std::string::String;
870
871 use super::*;
872 use crate::enums::CipherSuite;
873 use crate::msgs::enums::{Compression, HpkeAead, HpkeKdf};
874 use crate::msgs::handshake::{Random, ServerExtensions, SessionId};
875
876 #[test]
877 fn server_hello_conf_alters_server_hello_random() {
878 let server_hello = ServerHelloPayload {
879 legacy_version: ProtocolVersion::TLSv1_2,
880 random: Random([0xffu8; 32]),
881 session_id: SessionId::empty(),
882 cipher_suite: CipherSuite::TLS13_AES_256_GCM_SHA384,
883 compression_method: Compression::Null,
884 extensions: Box::new(ServerExtensions::default()),
885 };
886 let message = Message {
887 version: ProtocolVersion::TLSv1_3,
888 payload: MessagePayload::handshake(HandshakeMessagePayload(
889 HandshakePayload::ServerHello(server_hello.clone()),
890 )),
891 };
892 let Message {
893 payload:
894 MessagePayload::Handshake {
895 encoded: server_hello_encoded_before,
896 ..
897 },
898 ..
899 } = &message
900 else {
901 unreachable!("ServerHello is a handshake message");
902 };
903
904 let message = EchState::server_hello_conf(&server_hello, server_hello_encoded_before);
905
906 let Message {
907 payload:
908 MessagePayload::Handshake {
909 encoded: server_hello_encoded_after,
910 ..
911 },
912 ..
913 } = &message
914 else {
915 unreachable!("ServerHello is a handshake message");
916 };
917
918 assert_eq!(
919 std::format!("{server_hello_encoded_before:x?}"),
920 "020000280303ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001302000000",
921 "beforehand eight bytes at end of Random should be 0xff here ^^^^^^^^^^^^^^^^ "
922 );
923 assert_eq!(
924 std::format!("{server_hello_encoded_after:x?}"),
925 "020000280303ffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000001302000000",
926 " afterwards those bytes are zeroed ^^^^^^^^^^^^^^^^ "
927 );
928 }
929
930 #[test]
931 fn inner_client_hello_length_conceals_inner_name_length() {
932 let base_inner_len = inner_hello_encoding_for_name(dns_name_of_len(1), true).len();
933 assert!(
934 base_inner_len % 32 == 0,
935 "inner hello length must be 32-byte padded"
936 );
937 assert!(
938 base_inner_len >= 256,
939 "inner hello must include inner name and its padding"
940 );
941
942 for inner_name_len in 1..251 {
943 assert_eq!(
944 inner_hello_encoding_for_name(dns_name_of_len(inner_name_len), true).len(),
945 base_inner_len,
946 "all inner hello lengths must be invariant wrt inner name length"
947 );
948 }
949 }
950
951 #[test]
952 fn inner_client_hello_length_does_not_leak_length_of_omitted_inner_name() {
953 let base_inner_len = inner_hello_encoding_for_name(dns_name_of_len(1), false).len();
954 assert!(
955 base_inner_len % 32 == 0,
956 "inner hello length must be 32-byte padded"
957 );
958 assert!(
959 base_inner_len >= 256,
960 "inner hello must include maximum_name_length bytes of padding"
961 );
962
963 for inner_name_len in 1..251 {
964 assert_eq!(
965 inner_hello_encoding_for_name(dns_name_of_len(inner_name_len), false).len(),
966 base_inner_len,
967 "all inner hello lengths must be invariant wrt inner name length"
968 );
969 }
970 }
971
972 #[macro_rules_attribute::apply(test_for_each_provider)]
973 mod hello_retry_request {
974 use super::*;
975 use crate::common_state::Side;
976 use crate::crypto::SharedSecret;
977 use crate::msgs::base::PayloadU16;
978 use crate::msgs::handshake::{HelloRetryRequestExtensions, SupportedProtocolVersions};
979 use crate::tls13::key_schedule::KeySchedulePreHandshake;
980
981 #[test]
982 fn ech_rejection_in_server_hello_after_acceptance_in_hello_retry_request() {
983 let suite = tls13_suite();
984 let hash = suite.common.hash_provider;
985 let mut state = ech_state();
986
987 state
989 .ech_hello(outer_hello(), None, &None)
990 .unwrap();
991 let hrr = accepting_hello_retry_request(&state, suite);
992 assert!(
993 state
994 .confirm_hrr_acceptance(&hrr, suite, &mut CommonState::new(Side::Client))
995 .unwrap()
996 );
997 state.transcript_hrr_update(hash, &hrr_message(&hrr));
998 state
999 .ech_hello(outer_hello(), Some(&hrr), &None)
1000 .unwrap();
1001
1002 let server_hello = server_hello(suite);
1004 let mut server_name = inner_name();
1005 assert_eq!(
1006 state
1007 .confirm_acceptance(
1008 &mut handshake_key_schedule(suite),
1009 &server_hello,
1010 &encode(&server_hello),
1011 hash,
1012 &mut server_name,
1013 &mut CommonState::new(Side::Client),
1014 )
1015 .err(),
1016 Some(PeerMisbehaved::EchAcceptanceVariedAfterRetry.into())
1017 );
1018 }
1019
1020 #[test]
1021 fn ech_acceptance_in_server_hello_after_rejection_in_hello_retry_request() {
1022 let suite = tls13_suite();
1023 let hash = suite.common.hash_provider;
1024 let mut key_schedule = handshake_key_schedule(suite);
1025
1026 let mut state = ech_state();
1028 state
1029 .ech_hello(outer_hello(), None, &None)
1030 .unwrap();
1031 let server_hello = confirming_server_hello(&state, &mut key_schedule, suite);
1032 let mut server_name = inner_name();
1033 assert!(
1034 state
1035 .confirm_acceptance(
1036 &mut key_schedule,
1037 &server_hello,
1038 &encode(&server_hello),
1039 hash,
1040 &mut server_name,
1041 &mut CommonState::new(Side::Client),
1042 )
1043 .unwrap()
1044 .is_some()
1045 );
1046 assert_eq!(server_name, inner_name());
1047
1048 let mut state = ech_state();
1050 state
1051 .ech_hello(outer_hello(), None, &None)
1052 .unwrap();
1053 let hrr = hello_retry_request(suite, None);
1054 assert!(
1055 !state
1056 .confirm_hrr_acceptance(&hrr, suite, &mut CommonState::new(Side::Client))
1057 .unwrap()
1058 );
1059 state.transcript_hrr_update(hash, &hrr_message(&hrr));
1060 let server_hello = confirming_server_hello(&state, &mut key_schedule, suite);
1061 let mut server_name = inner_name();
1062 assert!(
1063 state
1064 .confirm_acceptance(
1065 &mut key_schedule,
1066 &server_hello,
1067 &encode(&server_hello),
1068 hash,
1069 &mut server_name,
1070 &mut CommonState::new(Side::Client),
1071 )
1072 .unwrap()
1073 .is_none()
1074 );
1075 assert_eq!(server_name, ServerName::from(public_name()));
1076 }
1077
1078 fn tls13_suite() -> &'static Tls13CipherSuite {
1079 provider::cipher_suite::TLS13_AES_128_GCM_SHA256
1080 .tls13()
1081 .unwrap()
1082 }
1083
1084 fn ech_state() -> EchState {
1085 let config = EchConfig {
1086 config: EchConfigPayload::V18(EchConfigContents {
1087 key_config: HpkeKeyConfig {
1088 config_id: 0,
1089 kem_id: MockHpke::SUITE.kem,
1090 public_key: PayloadU16::new(vec![0; 32]),
1091 symmetric_cipher_suites: vec![MockHpke::SUITE.sym],
1092 },
1093 maximum_name_length: 255,
1094 public_name: public_name(),
1095 extensions: vec![],
1096 }),
1097 suite: &MockHpke,
1098 };
1099
1100 EchState::new(&config, inner_name(), false, &FixedRandom, true).unwrap()
1101 }
1102
1103 fn inner_name() -> ServerName<'static> {
1104 ServerName::try_from("inner.example.com").unwrap()
1105 }
1106
1107 fn public_name() -> DnsName<'static> {
1108 DnsName::try_from("public.example.com").unwrap()
1109 }
1110
1111 fn outer_hello() -> ClientHelloPayload {
1112 ClientHelloPayload {
1113 client_version: ProtocolVersion::TLSv1_2,
1114 random: Random([0u8; 32]),
1115 session_id: SessionId::empty(),
1116 cipher_suites: vec![tls13_suite().common.suite],
1117 compression_methods: vec![Compression::Null],
1118 extensions: Box::new(ClientExtensions {
1119 server_name: Some(ServerNamePayload::from(&public_name())),
1120 supported_versions: Some(SupportedProtocolVersions {
1121 tls13: true,
1122 tls12: false,
1123 }),
1124 ..Default::default()
1125 }),
1126 }
1127 }
1128
1129 fn hello_retry_request(
1130 suite: &'static Tls13CipherSuite,
1131 encrypted_client_hello: Option<Payload<'static>>,
1132 ) -> HelloRetryRequest {
1133 HelloRetryRequest {
1134 legacy_version: ProtocolVersion::TLSv1_2,
1135 session_id: SessionId::empty(),
1136 cipher_suite: suite.common.suite,
1137 extensions: HelloRetryRequestExtensions {
1138 cookie: Some(PayloadU16::new(vec![1, 2, 3, 4])),
1139 supported_versions: Some(ProtocolVersion::TLSv1_3),
1140 encrypted_client_hello,
1141 ..HelloRetryRequestExtensions::default()
1142 },
1143 }
1144 }
1145
1146 fn accepting_hello_retry_request(
1148 state: &EchState,
1149 suite: &'static Tls13CipherSuite,
1150 ) -> HelloRetryRequest {
1151 let mut hrr = hello_retry_request(suite, Some(Payload::new(vec![0u8; 8])));
1152 let mut transcript = state
1153 .inner_hello_transcript
1154 .clone()
1155 .start_hash(suite.common.hash_provider);
1156 transcript.rollup_for_hrr();
1157 transcript.add_message(&EchState::hello_retry_request_conf(&hrr));
1158 let confirmation = server_ech_hrr_confirmation_secret(
1159 suite.hkdf_provider,
1160 &state.inner_hello_random.0,
1161 transcript.current_hash(),
1162 );
1163 hrr.encrypted_client_hello = Some(Payload::new(confirmation.to_vec()));
1164 hrr
1165 }
1166
1167 fn hrr_message(hrr: &HelloRetryRequest) -> Message<'static> {
1168 Message {
1169 version: ProtocolVersion::TLSv1_2,
1170 payload: MessagePayload::handshake(HandshakeMessagePayload(
1171 HandshakePayload::HelloRetryRequest(hrr.clone()),
1172 )),
1173 }
1174 }
1175
1176 fn server_hello(suite: &'static Tls13CipherSuite) -> ServerHelloPayload {
1177 ServerHelloPayload {
1178 legacy_version: ProtocolVersion::TLSv1_2,
1179 random: Random([0x55u8; 32]),
1180 session_id: SessionId::empty(),
1181 cipher_suite: suite.common.suite,
1182 compression_method: Compression::Null,
1183 extensions: Box::new(ServerExtensions::default()),
1184 }
1185 }
1186
1187 fn confirming_server_hello(
1189 state: &EchState,
1190 key_schedule: &mut KeyScheduleHandshakeStart,
1191 suite: &'static Tls13CipherSuite,
1192 ) -> ServerHelloPayload {
1193 let mut server_hello = server_hello(suite);
1194 let mut transcript = state
1195 .inner_hello_transcript
1196 .clone()
1197 .start_hash(suite.common.hash_provider);
1198 transcript.add_message(&EchState::server_hello_conf(
1199 &server_hello,
1200 &encode(&server_hello),
1201 ));
1202 let confirmation = key_schedule.server_ech_confirmation_secret(
1203 &state.inner_hello_random.0,
1204 transcript.current_hash(),
1205 );
1206 server_hello.random.0[24..].copy_from_slice(&confirmation);
1207 server_hello
1208 }
1209
1210 fn encode(server_hello: &ServerHelloPayload) -> Payload<'static> {
1211 Payload::new(
1212 HandshakeMessagePayload(HandshakePayload::ServerHello(server_hello.clone()))
1213 .get_encoding(),
1214 )
1215 }
1216
1217 fn handshake_key_schedule(suite: &'static Tls13CipherSuite) -> KeyScheduleHandshakeStart {
1218 KeySchedulePreHandshake::new(suite).into_handshake(SharedSecret::from(&[0u8; 32][..]))
1219 }
1220 }
1221
1222 fn inner_hello_encoding_for_name(name: DnsName<'static>, enable_sni: bool) -> Vec<u8> {
1223 let config = EchConfig {
1224 config: EchConfigPayload::V18(EchConfigContents {
1225 key_config: HpkeKeyConfig {
1226 config_id: 0,
1227 kem_id: MockHpke::SUITE.kem,
1228 public_key: PayloadU16::new(vec![0; 32]),
1229 symmetric_cipher_suites: vec![],
1230 },
1231 maximum_name_length: 255,
1232 public_name: DnsName::try_from("public").unwrap(),
1233 extensions: vec![],
1234 }),
1235 suite: &MockHpke,
1236 };
1237
1238 EchState::new(
1239 &config,
1240 ServerName::from(name.clone()),
1241 false,
1242 &FixedRandom,
1243 enable_sni,
1244 )
1245 .unwrap()
1246 .encode_inner_hello(
1247 &ClientHelloPayload {
1248 client_version: ProtocolVersion::TLSv1_3,
1249 random: Random([0u8; 32]),
1250 session_id: SessionId::empty(),
1251 cipher_suites: vec![],
1252 compression_methods: vec![Compression::Null],
1253 extensions: Box::new(ClientExtensions {
1254 server_name: Some(ServerNamePayload::from(&name)),
1255 ..Default::default()
1256 }),
1257 },
1258 None,
1259 &None,
1260 )
1261 }
1262
1263 fn dns_name_of_len(mut len: usize) -> DnsName<'static> {
1264 let mut s = String::new();
1265 let labels = len.div_ceil(63);
1266 for _ in 0..labels {
1267 let chars = Ord::min(len, 63);
1268 len -= chars;
1269 for _ in 0..chars {
1270 s.push('a');
1271 }
1272 if len != 0 {
1273 s.push('.');
1274 }
1275 }
1276 DnsName::try_from(s).unwrap()
1277 }
1278
1279 #[derive(Debug)]
1280 struct MockHpke;
1281
1282 impl MockHpke {
1283 const SUITE: HpkeSuite = HpkeSuite {
1284 kem: HpkeKem::DHKEM_P256_HKDF_SHA256,
1285 sym: HpkeSymmetricCipherSuite {
1286 kdf_id: HpkeKdf::HKDF_SHA256,
1287 aead_id: HpkeAead::AES_128_GCM,
1288 },
1289 };
1290 }
1291
1292 impl Hpke for MockHpke {
1293 #[cfg_attr(coverage_nightly, coverage(off))]
1294 fn seal(
1295 &self,
1296 _info: &[u8],
1297 _aad: &[u8],
1298 _plaintext: &[u8],
1299 _pub_key: &HpkePublicKey,
1300 ) -> Result<(EncapsulatedSecret, Vec<u8>), Error> {
1301 todo!()
1302 }
1303
1304 fn setup_sealer(
1305 &self,
1306 _info: &[u8],
1307 _pub_key: &HpkePublicKey,
1308 ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error> {
1309 Ok((EncapsulatedSecret(vec![]), Box::new(MockHpkeSealer)))
1310 }
1311
1312 #[cfg_attr(coverage_nightly, coverage(off))]
1313 fn open(
1314 &self,
1315 _enc: &EncapsulatedSecret,
1316 _info: &[u8],
1317 _aad: &[u8],
1318 _ciphertext: &[u8],
1319 _secret_key: &crate::crypto::hpke::HpkePrivateKey,
1320 ) -> Result<Vec<u8>, Error> {
1321 todo!()
1322 }
1323
1324 #[cfg_attr(coverage_nightly, coverage(off))]
1325 fn setup_opener(
1326 &self,
1327 _enc: &EncapsulatedSecret,
1328 _info: &[u8],
1329 _secret_key: &crate::crypto::hpke::HpkePrivateKey,
1330 ) -> Result<Box<dyn crate::crypto::hpke::HpkeOpener + 'static>, Error> {
1331 todo!()
1332 }
1333
1334 #[cfg_attr(coverage_nightly, coverage(off))]
1335 fn generate_key_pair(
1336 &self,
1337 ) -> Result<(HpkePublicKey, crate::crypto::hpke::HpkePrivateKey), Error> {
1338 todo!()
1339 }
1340
1341 fn suite(&self) -> HpkeSuite {
1342 Self::SUITE
1343 }
1344 }
1345
1346 #[derive(Debug)]
1347 struct MockHpkeSealer;
1348
1349 impl HpkeSealer for MockHpkeSealer {
1350 fn seal(&mut self, _aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
1351 Ok(vec![0xff; plaintext.len() + 16])
1353 }
1354 }
1355
1356 #[derive(Debug)]
1357 struct FixedRandom;
1358
1359 impl SecureRandom for FixedRandom {
1360 fn fill(&self, buf: &mut [u8]) -> Result<(), crate::rand::GetRandomFailed> {
1361 buf.fill(0x55);
1362 Ok(())
1363 }
1364 }
1365}