1mod fingerprints;
2pub use fingerprints::*;
3use rand::{thread_rng, Rng};
4
5use crate::client::ClientConnectionData;
6use crate::common_state::Context;
7use crate::crypto::{ActiveKeyExchange, SupportedKxGroup};
8use crate::msgs::base::{Payload, PayloadU16};
9use crate::msgs::codec::{Codec, LengthPrefixedBuffer};
10use crate::msgs::enums::{ECPointFormat, ExtensionType, PSKKeyExchangeMode};
11use crate::msgs::handshake::{
12 CertificateStatusRequest, ClientExtension, KeyShareEntry, OcspCertificateStatusRequest,
13};
14use crate::msgs::handshake::{HelloRetryRequest, UnknownExtension};
15use crate::version::{TLS12, TLS13};
16use crate::versions::EnabledVersions;
17use crate::{
18 CipherSuite, ClientConfig, Error, NamedGroup, ProtocolVersion, SignatureScheme, ALL_VERSIONS,
19};
20use alloc::sync::Arc;
21use core::fmt::Debug;
22use std::boxed::Box;
23use std::vec;
24use std::{collections::HashMap, vec::Vec};
25
26use static_init::dynamic;
27
28#[derive(Clone, Debug, Default)]
29pub(crate) struct CraftOptions(Option<FingerprintBuilder>);
30
31impl CraftOptions {
32 fn get(&self) -> &FingerprintBuilder {
33 assert!(self.0.is_some(), "The tls client config doesn't contain a fingerprint, please consider calling ClientConfig::with_fingerprint(...)");
34 self.0.as_ref().unwrap()
35 }
36
37 pub(crate) fn patch_extension(
38 &self,
39 cx: &mut Context<'_, ClientConnectionData>,
40 config: &ClientConfig,
41 hrr: Option<&HelloRetryRequest>,
42 extension: &mut Vec<ClientExtension>,
43 ) {
44 self.get()
45 .fingerprint
46 .patch_extension(cx, config, hrr, extension)
47 }
48
49 pub(crate) fn patch_cipher(
50 &self,
51 cx: &mut Context<'_, ClientConnectionData>,
52 config: &ClientConfig,
53 extension: &mut Vec<CipherSuite>,
54 ) {
55 if !config.craft.get().override_suite {
56 return;
57 }
58 self.get()
59 .fingerprint
60 .patch_cipher(cx, extension)
61 }
62}
63
64#[allow(dead_code)]
65#[repr(usize)]
66enum BoringSslGreaseIndex {
67 Cipher,
68 Group,
69 Extension1,
70 Extension2,
71 Version,
72 TicketExtension,
73 EchConfigId,
74 NumOfGrease,
75}
76
77#[derive(Debug)]
78struct GreaseSeed([u16; BoringSslGreaseIndex::NumOfGrease as usize]);
79
80impl GreaseSeed {
81 fn get(&self, idx: BoringSslGreaseIndex) -> u16 {
82 self.0[idx as usize]
83 }
84}
85
86pub(crate) struct CraftConnectionData {
87 grease_seed: GreaseSeed,
88 pub(crate) our_key_share_alt: Vec<Box<dyn ActiveKeyExchange>>,
89 pub(crate) extension_order: Vec<usize>,
90}
91
92impl Debug for CraftConnectionData {
93 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
94 f.debug_struct("CraftConnectionData")
95 .field("grease_seed", &self.grease_seed)
96 .field("our_key_share_alt", &"hidden")
97 .finish()
98 }
99}
100
101impl CraftConnectionData {
102 pub(crate) fn new() -> Self {
103 use BoringSslGreaseIndex::*;
104 let mut grease_seed = [0u16; NumOfGrease as usize];
105 thread_rng().fill(&mut grease_seed);
106 for seed in grease_seed.iter_mut() {
107 let unit = (*seed & 0xf0u16) | 0x0au16;
108 *seed = unit << 8 | unit;
109 }
110 if grease_seed[Extension1 as usize] == grease_seed[Extension2 as usize] {
111 grease_seed[Extension2 as usize] ^= 0x1010;
112 }
113 Self {
114 grease_seed: GreaseSeed(grease_seed),
115 our_key_share_alt: Vec::new(),
116 extension_order: Vec::new(),
117 }
118 }
119
120 pub(crate) fn find_key_share(
121 &mut self,
122 target_group: NamedGroup,
123 ) -> Option<Box<dyn ActiveKeyExchange>> {
124 for i in 0..self.our_key_share_alt.len() {
125 if self.our_key_share_alt[i].group() == target_group {
126 return Some(self.our_key_share_alt.swap_remove(i));
127 }
128 }
129
130 None
131 }
132}
133
134#[derive(Debug)]
136pub enum GreaseOr<T> {
137 Grease,
139 T(T),
141}
142
143impl<T: Clone> GreaseOr<T> {
144 pub(crate) fn is_grease(&self) -> bool {
145 matches!(self, Grease)
146 }
147
148 pub(crate) fn val(&self) -> T {
149 match self {
150 Grease => unimplemented!(),
151 Self::T(t) => t.clone(),
152 }
153 }
154}
155
156use GreaseOr::Grease;
157
158pub(crate) trait CreateUnknown: Clone + Debug {
159 fn create_unknown(grease: u16) -> Self;
160}
161
162impl<T> GreaseOr<T> {
163 fn val_or(&self, grease: u16) -> T
164 where
165 T: CreateUnknown,
166 {
167 match self {
168 Grease => T::create_unknown(grease),
169 Self::T(t) => t.clone(),
170 }
171 }
172}
173
174impl<T> From<T> for GreaseOr<T> {
175 fn from(value: T) -> Self {
176 Self::T(value)
177 }
178}
179
180pub type GreaseOrCurve = GreaseOr<NamedGroup>;
182
183pub type GreaseOrVersion = GreaseOr<ProtocolVersion>;
185
186pub type GreaseOrCipher = GreaseOr<CipherSuite>;
188
189impl CreateUnknown for NamedGroup {
190 fn create_unknown(grease: u16) -> Self {
191 Self::Unknown(grease)
192 }
193}
194
195impl CreateUnknown for ProtocolVersion {
196 fn create_unknown(grease: u16) -> Self {
197 Self::Unknown(grease)
198 }
199}
200
201impl CreateUnknown for CipherSuite {
202 fn create_unknown(grease: u16) -> Self {
203 Self::Unknown(grease)
204 }
205}
206
207#[derive(Debug)]
208struct CraftFakeKxGroup {
209 name: NamedGroup,
210}
211
212impl SupportedKxGroup for CraftFakeKxGroup {
213 fn name(&self) -> NamedGroup {
214 self.name
215 }
216
217 fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> {
218 todo!()
219 }
220}
221
222pub(crate) static FAKE_SECP256R1: &'static dyn SupportedKxGroup = &CraftFakeKxGroup {
223 name: NamedGroup::secp256r1,
224};
225
226pub(crate) static FAKE_SECP384R1: &'static dyn SupportedKxGroup = &CraftFakeKxGroup {
227 name: NamedGroup::secp384r1,
228};
229
230pub(crate) static FAKE_SECP521R1: &'static dyn SupportedKxGroup = &CraftFakeKxGroup {
231 name: NamedGroup::secp521r1,
232};
233
234pub(crate) static FAKE_FFDHE2048: &'static dyn SupportedKxGroup = &CraftFakeKxGroup {
235 name: NamedGroup::FFDHE2048,
236};
237
238pub(crate) static FAKE_FFDHE3072: &'static dyn SupportedKxGroup = &CraftFakeKxGroup {
239 name: NamedGroup::FFDHE3072,
240};
241
242fn to_fake_curves(group: &NamedGroup) -> &'static dyn SupportedKxGroup {
243 match group {
244 NamedGroup::secp256r1 => FAKE_SECP256R1,
245 NamedGroup::secp384r1 => FAKE_SECP384R1,
246 NamedGroup::secp521r1 => FAKE_SECP521R1,
247 NamedGroup::FFDHE2048 => FAKE_FFDHE2048,
248 NamedGroup::FFDHE3072 => FAKE_FFDHE3072,
249 _ => unimplemented!(),
250 }
251}
252
253#[derive(Debug, Clone)]
255pub enum CraftExtension {
256 Grease1,
258
259 Grease2,
261
262 RenegotiationInfo,
264
265 SupportedCurves(&'static [GreaseOrCurve]),
267
268 SupportedVersions(&'static [GreaseOrVersion]),
270
271 SignedCertificateTimestamp,
273
274 KeyShare(&'static [GreaseOrCurve]),
276
277 FakeApplicationSettings,
279
280 FakeCompressCert,
282
283 CompressCert(&'static [crate::CertificateCompressionAlgorithm]),
285
286 Padding,
288
289 Protocols(&'static [&'static [u8]]),
291
292 FakeDelegatedCredentials(&'static [SignatureScheme]),
294
295 FakeRecordSizeLimit(u16),
297}
298
299macro_rules! get_origin_ext {
300 ($extract:expr, $ext:path, $strict_mode:expr) => {
301 match $extract {
302 Some($ext(v)) => v,
303 _ => {
304 assert!(!$strict_mode);
305 return Err(());
306 }
307 }
308 };
309}
310
311impl CraftExtension {
312 fn make_ext(typ: ExtensionType, payload: Vec<u8>) -> ClientExtension {
313 ClientExtension::Unknown(UnknownExtension {
314 typ,
315 payload: Payload(payload),
316 })
317 }
318
319 fn to_rustls_extension(
320 &self,
321 cx: &mut Context<'_, ClientConnectionData>,
322 config: &ClientConfig,
323 ext_store: &mut HashMap<u16, ClientExtension>,
324 hrr: Option<&HelloRetryRequest>,
325 ) -> Result<ClientExtension, ()> {
326 let craft_config = config.craft.get();
327 Ok(match self {
328 Self::Grease1 => Self::make_ext(
329 cx.data
330 .craft_connection_data
331 .grease_seed
332 .get(BoringSslGreaseIndex::Extension1)
333 .into(),
334 Vec::new(),
335 ),
336 Self::Grease2 => Self::make_ext(
337 cx.data
338 .craft_connection_data
339 .grease_seed
340 .get(BoringSslGreaseIndex::Extension2)
341 .into(),
342 vec![0],
343 ),
344 Self::RenegotiationInfo => Self::make_ext(ExtensionType::RenegotiationInfo, vec![0]),
345 Self::SupportedCurves(curves) => {
346 let mut origin_curves = get_origin_ext!(
347 ext_store.remove(&ExtensionType::EllipticCurves.get_u16()),
348 ClientExtension::NamedGroups,
349 craft_config.strict_mode
350 );
351 if config
352 .craft
353 .get()
354 .override_supported_curves
355 {
356 for (i, v) in curves.iter().enumerate() {
357 if v.is_grease() {
358 origin_curves.insert(
359 i,
360 cx.data
361 .craft_connection_data
362 .grease_seed
363 .get(BoringSslGreaseIndex::Group)
364 .into(),
365 );
366 break;
367 }
368 }
369 }
370 ClientExtension::NamedGroups(origin_curves)
371 }
372 Self::SupportedVersions(versions) => {
373 let origin_versions = get_origin_ext!(
374 ext_store.remove(&ExtensionType::SupportedVersions.get_u16()),
375 ClientExtension::SupportedVersions,
376 craft_config.strict_mode
377 );
378 ClientExtension::SupportedVersions(
379 versions
380 .iter()
381 .map(|v| {
382 v.val_or(
383 cx.data
384 .craft_connection_data
385 .grease_seed
386 .get(BoringSslGreaseIndex::Version),
387 )
388 })
389 .filter(|v| {
390 if !v.craft_is_unknown() && !origin_versions.contains(v) {
391 assert!(
392 !craft_config.strict_mode,
393 "unknown version {:?}, all: {:?}",
394 v, origin_versions
395 );
396 false
397 } else {
398 true
399 }
400 })
401 .collect(),
402 )
403 }
404 Self::SignedCertificateTimestamp => Self::make_ext(ExtensionType::SCT, vec![]),
405 Self::KeyShare(key_share_spec) => {
406 if hrr.is_some()
407 && hrr
408 .unwrap()
409 .get_requested_key_share_group()
410 .is_some()
411 || !craft_config.override_keyshare
412 {
413 return ext_store
414 .remove(&ExtensionType::KeyShare.get_u16())
415 .ok_or(());
416 }
417 let mut origin_ks = get_origin_ext!(
418 ext_store.remove(&ExtensionType::KeyShare.get_u16()),
419 ClientExtension::KeyShare,
420 craft_config.strict_mode
421 )
422 .into_iter()
423 .next();
424
425 let origin_ks_group = origin_ks.as_ref().unwrap().group;
426
427 let mut key_shares = vec![];
428
429 for group_spec in key_share_spec.iter() {
430 key_shares.push(match group_spec {
431 Grease => KeyShareEntry {
432 group: group_spec.val_or(
433 cx.data
434 .craft_connection_data
435 .grease_seed
436 .get(BoringSslGreaseIndex::Group),
437 ),
438 payload: PayloadU16(vec![0]),
439 },
440 GreaseOr::T(group) => {
441 if origin_ks.is_some()
442 && (origin_ks_group != config.provider.kx_groups[0].name()
443 || *group == origin_ks_group)
444 {
445 origin_ks.take().unwrap()
446 } else if !key_shares
447 .iter()
448 .any(|ks: &KeyShareEntry| ks.group.get_u16() == group.get_u16())
449 {
450 let ks_data = match config
451 .find_kx_group(*group)
452 .and_then(|v| v.start().ok())
453 {
454 Some(ks_data) => ks_data,
455 None => {
456 assert!(
457 !craft_config.strict_mode,
458 "unsupported group specified for psk"
459 );
460 continue;
461 }
462 };
463 let ks_ext = KeyShareEntry::new(*group, ks_data.pub_key());
464
465 cx.data
466 .craft_connection_data
467 .our_key_share_alt
468 .push(ks_data);
469 ks_ext
470 } else {
471 continue;
472 }
473 }
474 });
475 }
476
477 if let Some(origin_ks) = origin_ks {
478 key_shares.push(origin_ks);
479 }
480
481 ClientExtension::KeyShare(key_shares)
482 }
483 Self::FakeApplicationSettings => {
484 Self::make_ext(17513.into(), vec![0, 3, 2, b'h', b'2'])
485 }
486 Self::Padding => {
487 ClientExtension::CraftPadding(CraftPadding {
488 psk_len: if let Some(ClientExtension::PresharedKey(psk)) =
489 ext_store.get(&ExtensionType::PreSharedKey.get_u16())
490 {
491 2 + 2 + 2 + psk.identities.iter().map(|v| 2 + v.identity.0.len() + 4 ).sum::<usize>() + 2 + psk.binders.iter().map(|b| 1 + b.0.0.len()).sum::<usize>()
492 } else {
493 0
494 },
495 })
496 }
497 Self::FakeCompressCert => Self::make_ext(0x001b.into(), vec![2, 0, 0]),
498 Self::CompressCert(algorithms) => {
499 if craft_config.strict_mode {
500 config
501 .certificate_compression_algorithms
502 .iter()
503 .zip(algorithms.iter())
504 .for_each(|(a, b)| {
505 assert_eq!(a.alg, *b);
506 });
507 }
508 ext_store
509 .remove(&ExtensionType::CompressCertificate.get_u16())
510 .ok_or(())?
511 }
512 Self::Protocols(protocols) => {
513 if craft_config.strict_mode
514 && (protocols.len() != config.alpn_protocols.len()
515 || protocols
516 .iter()
517 .zip(config.alpn_protocols.iter())
518 .any(|(p1, p2)| p1 != &p2.as_slice()))
519 {
520 panic!()
521 }
522 ext_store
523 .remove(&ExtensionType::ALProtocolNegotiation.get_u16())
524 .ok_or(())?
525 }
526 Self::FakeDelegatedCredentials(delegated) => {
527 let mut buf = vec![];
528 {
529 let length_padded =
530 LengthPrefixedBuffer::new(crate::msgs::codec::ListLength::U16, &mut buf);
531 for sig in delegated.iter() {
532 sig.encode(length_padded.buf);
533 }
534 };
535 Self::make_ext(34.into(), buf)
536 }
537 Self::FakeRecordSizeLimit(limit) => {
538 Self::make_ext(28.into(), limit.to_be_bytes().to_vec())
539 }
540 })
541 }
542}
543
544#[derive(Clone, Debug)]
546pub struct CraftPadding {
547 psk_len: usize,
548}
549
550impl Codec for CraftPadding {
551 fn encode(&self, bytes: &mut Vec<u8>) {
552 let unpadded = self.psk_len + bytes.len() - 4;
553 if unpadded > 0xff && unpadded < 0x200 {
554 let mut padding_len = 0x200 - unpadded;
555 if padding_len > 4 {
556 padding_len -= 4;
557 } else {
558 padding_len = 1
559 }
560 bytes.resize(bytes.len() + padding_len, 0);
561 } else {
562 bytes.resize(bytes.len() - 4, 0);
564 }
565 }
566
567 fn read(_: &mut crate::msgs::codec::Reader) -> Result<Self, crate::InvalidMessage> {
568 todo!()
569 }
570}
571
572#[derive(Debug, Clone)]
575pub enum KeepExtension {
576 Must(ExtensionType),
578 Optional(ExtensionType),
581 OrDefault(ExtensionType, ClientExtension),
584}
585
586#[derive(Debug, Clone)]
588pub enum ExtensionSpec {
589 Craft(CraftExtension),
592
593 Rustls(ClientExtension),
595
596 Keep(KeepExtension),
600}
601
602fn shuffle_extensions(extensions: &[ExtensionSpec], config: &ClientConfig) -> Vec<usize> {
603 use ExtensionSpec::*;
604
605 let mut to_shuffle = Vec::with_capacity(extensions.len());
606 let mut do_not_shuffle = Vec::with_capacity(extensions.len());
607 for (i, ext) in extensions.iter().enumerate() {
608 match ext {
609 Craft(CraftExtension::Grease1 | CraftExtension::Grease2)
610 | Craft(CraftExtension::Padding)
611 | Keep(KeepExtension::Optional(ExtensionType::PreSharedKey)) => {
612 do_not_shuffle.push(i);
613 }
614 _ => to_shuffle.push(i),
615 }
616 }
617
618 let mut random_buf = [0u8, 0, 0, 0];
619 let rand_gen = config.provider.secure_random;
620 for i in (1..to_shuffle.len()).rev() {
621 rand_gen.fill(&mut random_buf).unwrap();
622 let swap_idx = (u32::from_be_bytes(random_buf) as usize) % (i + 1);
623 to_shuffle.swap(i, swap_idx);
624 }
625
626 for i in do_not_shuffle {
627 to_shuffle.insert(i, i);
628 }
629
630 to_shuffle
631}
632
633#[derive(Debug, Clone, Default)]
641pub struct Fingerprint {
642 pub extensions: &'static [ExtensionSpec],
646 pub shuffle_extensions: bool,
649 pub cipher: &'static [GreaseOrCipher],
651}
652
653impl Fingerprint {
654 pub fn builder(&self) -> FingerprintBuilder {
656 FingerprintBuilder {
657 fingerprint: self.clone(),
658 override_alpn: true,
659 strict_mode: true,
660 override_supported_curves: true,
661 override_version: true,
662 override_keyshare: true,
663 override_cert_compress: true,
664 override_suite: true,
665 }
666 }
667
668 fn patch_extension(
669 &self,
670 cx: &mut Context<'_, ClientConnectionData>,
671 config: &ClientConfig,
672 hrr: Option<&HelloRetryRequest>,
673 extension: &mut Vec<ClientExtension>,
674 ) {
675 let craft_config = config.craft.get();
676 let mut ext_store = HashMap::new();
685 for ext in extension.drain(..) {
686 match ext {
687 ClientExtension::ServerName(_)
688 | ClientExtension::SessionTicket(_)
689 | ClientExtension::KeyShare(_)
690 | ClientExtension::PresharedKey(_)
691 | ClientExtension::Protocols(_)
692 | ClientExtension::NamedGroups(_)
693 | ClientExtension::SupportedVersions(_)
694 | ClientExtension::Cookie(_)
695 | ClientExtension::EarlyData
696 | ClientExtension::CompressCertificate(_) => {
697 ext_store.insert(ext.get_type().get_u16(), ext);
698 }
699 _ => (),
700 }
701 }
702
703 use ExtensionSpec::*;
704 use KeepExtension::*;
705
706 if hrr.is_none()
707 && craft_config
708 .fingerprint
709 .shuffle_extensions
710 {
711 cx.data
712 .craft_connection_data
713 .extension_order = shuffle_extensions(self.extensions, config);
714 }
715
716 let order = {
717 let mut iter_a = None;
718 let mut iter_b = None;
719
720 let order = &cx
721 .data
722 .craft_connection_data
723 .extension_order;
724 if order.is_empty() {
725 iter_a = Some(0..self.extensions.len())
726 } else {
727 iter_b = Some(order.clone().into_iter())
728 }
729 iter_a
730 .into_iter()
731 .flatten()
732 .chain(iter_b.into_iter().flatten())
733 };
734
735 for idx in order {
736 let spec = &self.extensions[idx];
737 extension.push(match spec {
738 Craft(ext) => match ext.to_rustls_extension(cx, config, &mut ext_store, hrr) {
739 Ok(ext) => ext,
740 Err(_) => continue,
741 },
742 Rustls(ext) => ext.clone(),
743 Keep(Must(ext_type)) => match ext_store.remove(&ext_type.get_u16()) {
744 Some(ext) => ext,
745 None => {
746 if matches!(ext_type, ExtensionType::ServerName) && config.enable_sni {
747 continue;
748 }
749 assert!(
750 !craft_config.strict_mode,
751 "expecting {:?}, but got nothing",
752 ext_type
753 );
754 continue;
755 }
756 },
757 Keep(Optional(ext)) => match ext_store.remove(&ext.get_u16()) {
758 Some(ext) => ext,
759 None => {
760 continue;
761 }
762 },
763 Keep(OrDefault(ext, default_ext)) => ext_store
764 .remove(&ext.get_u16())
765 .unwrap_or_else(|| default_ext.clone()),
766 })
767 }
768 }
769
770 pub(crate) fn patch_cipher(
771 &self,
772 cx: &mut Context<'_, ClientConnectionData>,
773 extension: &mut Vec<CipherSuite>,
774 ) {
775 *extension = self
776 .cipher
777 .iter()
778 .map(|c| {
779 c.val_or(
780 cx.data
781 .craft_connection_data
782 .grease_seed
783 .get(BoringSslGreaseIndex::Cipher),
784 )
785 })
786 .collect();
787 }
788}
789
790#[derive(Debug, Clone)]
791pub struct FingerprintBuilder {
796 fingerprint: Fingerprint,
797 override_alpn: bool,
798 override_version: bool,
799 override_supported_curves: bool,
800 strict_mode: bool,
801 override_keyshare: bool,
802 override_cert_compress: bool,
803 override_suite: bool,
804}
805
806impl FingerprintBuilder {
807 pub fn do_not_override_alpn(mut self) -> Self {
813 self.override_alpn = false;
814 self
815 }
816
817 pub fn dangerous_disable_override_keyshare(mut self) -> Self {
822 self.override_keyshare = false;
823 self
824 }
825
826 pub fn dangerous_disable_override_suite(mut self) -> Self {
828 self.override_suite = false;
829 self
830 }
831
832 pub fn dangerous_craft_test_mode(mut self) -> Self {
838 self.strict_mode = false;
839 self.override_alpn = false;
840 self.override_version = false;
841 self.override_cert_compress = false;
842 self
843 }
844
845 fn build(self) -> CraftOptions {
846 CraftOptions(Some(self))
847 }
848
849 pub(crate) fn patch_config(self, mut config: ClientConfig) -> ClientConfig {
850 if self.override_version {
851 assert_eq!(ALL_VERSIONS, &[&TLS13, &TLS12]);
852 config.versions = EnabledVersions::new(ALL_VERSIONS); }
854 for ext in self.fingerprint.extensions.iter() {
855 match ext {
856 ExtensionSpec::Craft(CraftExtension::SupportedCurves(curves)) => {
857 if !self.override_supported_curves {
858 continue;
859 }
860
861 let curves_need_modification = curves
862 .iter()
863 .filter(|c| !c.is_grease())
864 .zip(config.provider.kx_groups.iter())
865 .all(|(c1, c2)| c1.val() == c2.name());
866
867 if !curves_need_modification {
868 continue;
869 }
870
871 let mut provider = config.provider.as_ref().clone();
872
873 let mut grease_offset = 0;
874 for (idx, curve) in curves.iter().enumerate() {
875 match curve {
876 Grease => {
877 grease_offset += 1;
878 continue;
879 }
880 GreaseOr::T(curve) => {
881 if let Some(old_idx) = provider
882 .kx_groups
883 .iter()
884 .position(|v| v.name() == *curve)
885 {
886 if idx - grease_offset == old_idx {
887 continue;
888 }
889 assert!(
890 idx - grease_offset < old_idx,
891 "idx {idx}, old_idx {old_idx}"
892 );
893 provider.kx_groups.swap(idx, old_idx);
894 } else {
895 provider
896 .kx_groups
897 .insert(idx - grease_offset, to_fake_curves(curve))
898 }
899 }
900 }
901 }
902 config.provider = Arc::new(provider);
903 }
904 ExtensionSpec::Craft(CraftExtension::Protocols(protocols)) => {
905 if !self.override_alpn {
906 continue;
907 }
908 config.alpn_protocols = protocols
909 .iter()
910 .map(|p| p.to_vec())
911 .collect();
912 }
913 ExtensionSpec::Craft(CraftExtension::CompressCert(algos)) => {
914 if !self.override_cert_compress {
915 continue;
916 }
917 config.certificate_compression_algorithms = algos
918 .iter()
919 .map(|algo| match algo {
920 crate::CertificateCompressionAlgorithm::Zlib => crate::ZLIB_DEFAULT,
921 crate::CertificateCompressionAlgorithm::Brotli => crate::BROTLI_DEFAULT,
922 crate::CertificateCompressionAlgorithm::Zstd => crate::ZSTD_DEFAULT,
923 crate::CertificateCompressionAlgorithm::Unknown(_) => unimplemented!(),
924 })
925 .collect();
926 }
927 _ => (),
928 }
929 }
930 config.craft = self.build();
931 config
932 }
933}
934
935pub struct FingerprintSet {
937 pub main: Fingerprint,
940 pub test_alpn_http1: Fingerprint,
943 pub test_no_alpn: Fingerprint,
946}
947
948impl core::ops::Deref for FingerprintSet {
949 type Target = Fingerprint;
950
951 fn deref(&self) -> &Self::Target {
954 &self.main
955 }
956}