rustls/craft/
mod.rs

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/// An enum representing either a valid value of type `T` or a GREASE (Generate Random Extensions And Sustain Extensibility) placeholder.
135#[derive(Debug)]
136pub enum GreaseOr<T> {
137    /// A GREASE placeholder value, which will be generated randomly per session.
138    Grease,
139    /// A valid value of the generic type `T`.
140    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
180/// A type that can either hold a valid `NamedGroup` or serve as a GREASE placeholder.
181pub type GreaseOrCurve = GreaseOr<NamedGroup>;
182
183/// A type that can either hold a valid `ProtocolVersion` or serve as a GREASE placeholder.
184pub type GreaseOrVersion = GreaseOr<ProtocolVersion>;
185
186/// A type that can either hold a valid `CipherSuite` or serve as a GREASE placeholder.
187pub 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/// Craft client extension provides customization to rustls client extensions, or offers some unavailable extensions in rustls.
254#[derive(Debug, Clone)]
255pub enum CraftExtension {
256    /// The first grease extension in the list
257    Grease1,
258
259    /// The second grease extension in the list
260    Grease2,
261
262    /// RenegotiationInfo extension that hard coded with `RenegotiationNever`
263    RenegotiationInfo,
264
265    /// SupportedCurves that supports grease or NamedCurve
266    SupportedCurves(&'static [GreaseOrCurve]),
267
268    /// SupportedVersions that supports grease or tls versions
269    SupportedVersions(&'static [GreaseOrVersion]),
270
271    /// Hardcoded SignedCertificateTimestamp
272    SignedCertificateTimestamp,
273
274    /// KeyShare that supports grease or NamedCurve
275    KeyShare(&'static [GreaseOrCurve]),
276
277    /// Hardcoded fake BoringSSL ApplicationSettings.
278    FakeApplicationSettings,
279
280    /// Hardcoded fake CompressCert extension that provides no compression algorithm
281    FakeCompressCert,
282
283    /// CompressCert extension
284    CompressCert(&'static [crate::CertificateCompressionAlgorithm]),
285
286    /// Client Hello Padding extension that mimics the BoringSSL padding style
287    Padding,
288
289    /// ALPN extension
290    Protocols(&'static [&'static [u8]]),
291
292    /// Fake DelegatedCredentials extension
293    FakeDelegatedCredentials(&'static [SignatureScheme]),
294
295    /// Fake RecordSizeLimit extension
296    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 /* ext_type */ + 2 /* len */ + 2 /* ident_len */ + psk.identities.iter().map(|v| 2 /* len */ + v.identity.0.len() + 4 /* obfs ticket age */).sum::<usize>() + 2 /* binders len */ + 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/// A boringssl-style client padding presented by Craftls.
545#[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            // A dirty trick to delete the already written ext type and size.
563            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/// `KeepExtension` gives fine-grained control over the inclusion of extensions originally generated by Rustls.
573/// It dictates whether to keep certain Rustls extensions, use them optionally, or provide a default if unavailable.
574#[derive(Debug, Clone)]
575pub enum KeepExtension {
576    /// Specifies that the `ExtensionType` must be provided by Rustls. If the extension is not present, there might be a configuration or implementation error.
577    Must(ExtensionType),
578    /// Specifies that the `ExtensionType` may be provided by Rustls. Its absence will not be considered
579    /// an error.
580    Optional(ExtensionType),
581    /// Specifies that the `ExtensionType` should be provided by Rustls; if it is not available,
582    /// the specified `ClientExtension` will be used as a fallback.
583    OrDefault(ExtensionType, ClientExtension),
584}
585
586/// `ExtensionSpec` outlines the types of client extensions that can be used in a fingerprint specification
587#[derive(Debug, Clone)]
588pub enum ExtensionSpec {
589    /// A `CraftExtension` represents an extension not inherently supported by Rustls or a customization not available in Rustls.
590    /// These extensions will be eventually be converted into Rustls `ClientExtension`s.
591    Craft(CraftExtension),
592
593    /// A `ClientExtension` native to Rustls. Extensions specified here are directly included in the client hello message.
594    Rustls(ClientExtension),
595
596    /// A `KeepExtension` dictates the retention policy for extensions that are generated by Rustls by default.
597    /// It allows for specifying whether certain Rustls-generated extensions should be kept as-is, used conditionally,
598    /// or replaced with a default if not present.
599    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/// Represents a TLS fingerprint
634///
635/// # Available Fingerprints:
636/// * [`CHROME_108`]
637/// * [`CHROME_112`]
638/// * [`SAFARI_17_1`]
639/// * [`FIREFOX_105`]
640#[derive(Debug, Clone, Default)]
641pub struct Fingerprint {
642    /// The TLS ClientHello extensions included in the fingerprint. Each `ExtensionSpec` determines the nature and handling
643    /// of an extension, whether it's a Craft extension, a native Rustls extension, or subject
644    /// to conditional inclusion based on Rustls' defaults.
645    pub extensions: &'static [ExtensionSpec],
646    /// Indicates whether the list of extensions should be randomly reordered
647    /// before being sent in the ClientHello message. This shuffling process is introduced to mimic BoringSSL.
648    pub shuffle_extensions: bool,
649    /// Specifies the list of ciphers included in the ClientHello.
650    pub cipher: &'static [GreaseOrCipher],
651}
652
653impl Fingerprint {
654    /// Creates a fingerprint builder that allow you to tweak the fingerprint and patch the client config. See also [`crate::ClientConfig::with_fingerprint`]
655    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        // if extension
677        //     .iter()
678        //     .any(|v| matches!(v, ClientExtension::EarlyData))
679        // {
680        //     assert!(!craft_config.strict_mode);
681        //     return;
682        // }
683
684        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)]
791/// A builder for constructing a [`Fingerprint`] with customizable configurations.
792/// The builder allows specific aspects of the TLS fingerprint to be overridden,
793/// ensuring that the final [`crate::ClientConfig`] and ClientHello align with desired specifications or
794/// testing conditions.
795pub 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    /// Disables the overriding of the ALPN settings.
808    /// Use this option when working with HTTP clients, such as `hyper`, that internally manage
809    /// ALPN settings; they may raise issues if ALPN is set externally. While this option skips
810    /// setting ALPN, it still validates the ALPN during TLS handshakes against expected values.
811    /// Non-compliance will lead to a panic, ensuring adherence to the specified ALPN requirements.
812    pub fn do_not_override_alpn(mut self) -> Self {
813        self.override_alpn = false;
814        self
815    }
816
817    /// Disables the override of key share configurations. Intended only for testing purposes,
818    /// this option allows the default key share behavior to be used, bypassing the key share settings
819    /// specified in the `Fingerprint`. This can help in testing scenarios where non-standard key
820    /// share configurations are to be evaluated or default behavior is required.
821    pub fn dangerous_disable_override_keyshare(mut self) -> Self {
822        self.override_keyshare = false;
823        self
824    }
825
826    /// Disables the override of cipher suites. Intended only for testing purposes
827    pub fn dangerous_disable_override_suite(mut self) -> Self {
828        self.override_suite = false;
829        self
830    }
831
832    /// Enters a craftls test mode that disables various overrides and strict checking against the [`Fingerprint`].
833    /// This mode is intended for testing and should be used with caution as it relaxes the
834    /// constraints normally enforced by the builder, potentially allowing configurations that
835    /// deviate from the specified `Fingerprint`. This can be useful for testing how the system
836    /// behaves under non-standard or unexpected configurations.
837    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); // enable both tls 1.2 and 1.3
853        }
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
935/// Represents a collection of [`Fingerprint`] instances, each configured with different ALPN extensions.
936pub struct FingerprintSet {
937    /// The default `Fingerprint` variant configured for HTTP/2 (h2) clients.
938    /// This is the primary variant used for most scenarios and is designed to be consistent with browsers.
939    pub main: Fingerprint,
940    /// A `Fingerprint` variant specifically tailored for clients that use HTTP/1.1 (http1).
941    /// This variant is useful for testing or scenarios where HTTP/2 support is unavailable.
942    pub test_alpn_http1: Fingerprint,
943    /// A `Fingerprint` variant without any specific ALPN settings, suitable for use with both HTTP/1.1 and non-HTTP clients.
944    /// This variant provides a craftible option for testing or supporting clients where ALPN may not be applicable.
945    pub test_no_alpn: Fingerprint,
946}
947
948impl core::ops::Deref for FingerprintSet {
949    type Target = Fingerprint;
950
951    /// Provides implicit access to the [`FingerprintSet::main`] variant when a [`FingerprintSet`] is dereferenced.
952    /// This allows the `main` variant to be used as the default when no explicit selection is made from the set.
953    fn deref(&self) -> &Self::Target {
954        &self.main
955    }
956}