rama_net/tls/client/
parser.rs

1//! Perma-forked from
2//! tls-parser @ 65a2fe0b86f09235515337c501c8a512db1c6dba
3//!
4//! src and attribution: <https://github.com/rusticata/tls-parser>
5
6use super::hello::{ECHClientHello, ECHClientHelloOuter, HpkeSymmetricCipherSuite};
7use super::{ClientHello, ClientHelloExtension};
8use crate::address::Host;
9use crate::tls::{
10    ApplicationProtocol, CipherSuite, ExtensionId, ProtocolVersion, enums::CompressionAlgorithm,
11};
12use nom::{
13    IResult, Parser,
14    bytes::streaming::take,
15    combinator::{complete, cond, map, map_opt, map_parser, opt, verify},
16    error::{ErrorKind, make_error},
17    multi::{length_data, many0},
18    number::streaming::{be_u8, be_u16},
19};
20use rama_core::error::OpaqueError;
21use std::str;
22
23/// Parse a [`ClientHello`] from the raw "wire" bytes.
24///
25/// This function is not infallible, it can return an error if the input is not a valid
26/// TLS ClientHello message or if there is unexpected trailing data.
27pub fn parse_client_hello(i: &[u8]) -> Result<ClientHello, OpaqueError> {
28    match parse_client_hello_inner(i) {
29        Err(err) => Err(OpaqueError::from_display(format!(
30            "parse client hello handshake message: {err:?}"
31        ))),
32        Ok((i, hello)) => {
33            if i.is_empty() {
34                Ok(hello)
35            } else {
36                Err(OpaqueError::from_display(
37                    "parse client hello handshake message: unexpected trailer content",
38                ))
39            }
40        }
41    }
42}
43
44fn parse_client_hello_inner(i: &[u8]) -> IResult<&[u8], ClientHello> {
45    let (i, version) = be_u16(i)?;
46    let (i, _random) = take(32usize)(i)?;
47    let (i, sidlen) = verify(be_u8, |&n| n <= 32).parse(i)?;
48    let (i, _sid) = cond(sidlen > 0, take(sidlen as usize)).parse(i)?;
49    let (i, ciphers_len) = be_u16(i)?;
50    let (i, cipher_suites) = parse_cipher_suites(i, ciphers_len as usize)?;
51    let (i, comp_len) = be_u8(i)?;
52    let (i, compression_algorithms) = parse_compressions_algs(i, comp_len as usize)?;
53    let (i, opt_ext) = opt(complete(length_data(be_u16))).parse(i)?;
54
55    let mut extensions = vec![];
56    if let Some(mut i) = opt_ext {
57        while !i.is_empty() {
58            let (new_i, ch_ext) = parse_tls_client_hello_extension(i)?;
59            extensions.push(ch_ext);
60            i = new_i;
61        }
62    }
63
64    Ok((
65        i,
66        ClientHello {
67            protocol_version: version.into(),
68            cipher_suites,
69            compression_algorithms,
70            extensions,
71        },
72    ))
73}
74
75fn parse_cipher_suites(i: &[u8], len: usize) -> IResult<&[u8], Vec<CipherSuite>> {
76    if len == 0 {
77        return Ok((i, Vec::new()));
78    }
79    if len % 2 == 1 || len > i.len() {
80        return Err(nom::Err::Error(make_error(i, ErrorKind::LengthValue)));
81    }
82    let v = (i[..len])
83        .chunks(2)
84        .map(|chunk| CipherSuite::from(((chunk[0] as u16) << 8) | chunk[1] as u16))
85        .collect();
86    Ok((&i[len..], v))
87}
88
89fn parse_compressions_algs(i: &[u8], len: usize) -> IResult<&[u8], Vec<CompressionAlgorithm>> {
90    if len == 0 {
91        return Ok((i, Vec::new()));
92    }
93    if len > i.len() {
94        return Err(nom::Err::Error(make_error(i, ErrorKind::LengthValue)));
95    }
96    let v = (i[..len])
97        .iter()
98        .map(|&it| CompressionAlgorithm::from(it))
99        .collect();
100    Ok((&i[len..], v))
101}
102
103fn parse_tls_client_hello_extension(i: &[u8]) -> IResult<&[u8], ClientHelloExtension> {
104    let (i, ext_type) = be_u16(i)?;
105    let id = ExtensionId::from(ext_type);
106    let (i, ext_data) = length_data(be_u16).parse(i)?;
107
108    let ext_len = ext_data.len() as u16;
109
110    let (_, ext) = match id {
111        ExtensionId::SERVER_NAME => parse_tls_extension_sni_content(ext_data),
112        ExtensionId::SUPPORTED_GROUPS => parse_tls_extension_elliptic_curves_content(ext_data),
113        ExtensionId::EC_POINT_FORMATS => parse_tls_extension_ec_point_formats_content(ext_data),
114        ExtensionId::SIGNATURE_ALGORITHMS => {
115            parse_tls_extension_signature_algorithms_content(ext_data)
116        }
117        ExtensionId::APPLICATION_LAYER_PROTOCOL_NEGOTIATION => {
118            parse_tls_extension_alpn_content(ext_data)
119        }
120        ExtensionId::SUPPORTED_VERSIONS => {
121            parse_tls_extension_supported_versions_content(ext_data, ext_len)
122        }
123        ExtensionId::COMPRESS_CERTIFICATE => {
124            parse_tls_extension_certificate_compression_content(ext_data)
125        }
126        ExtensionId::DELEGATED_CREDENTIAL => parse_tls_extension_delegated_credentials(ext_data),
127        ExtensionId::RECORD_SIZE_LIMIT => {
128            let (i, v) = be_u16(ext_data)?;
129            Ok((i, ClientHelloExtension::RecordSizeLimit(v)))
130        }
131        ExtensionId::ENCRYPTED_CLIENT_HELLO => {
132            let (i, ech) = parse_ech_client_hello(ext_data)?;
133            Ok((i, ClientHelloExtension::EncryptedClientHello(ech)))
134        }
135        _ => Ok((
136            i,
137            ClientHelloExtension::Opaque {
138                id,
139                data: ext_data.to_vec(),
140            },
141        )),
142    }?;
143    Ok((i, ext))
144}
145
146// struct {
147//     ServerName server_name_list<1..2^16-1>
148// } ServerNameList;
149fn parse_tls_extension_sni_content(i: &[u8]) -> IResult<&[u8], ClientHelloExtension> {
150    if i.is_empty() {
151        // special case: SNI extension in server can be empty
152        return Ok((i, ClientHelloExtension::ServerName(None)));
153    }
154    let (i, list_len) = be_u16(i)?;
155    let (i, mut v) = map_parser(
156        take(list_len),
157        many0(complete(parse_tls_extension_sni_hostname)),
158    )
159    .parse(i)?;
160    if v.len() > 1 {
161        return Err(nom::Err::Error(nom::error::Error::new(
162            i,
163            ErrorKind::TooLarge,
164        )));
165    }
166    Ok((i, ClientHelloExtension::ServerName(v.pop())))
167}
168
169// struct {
170//     NameType name_type;
171//     select (name_type) {
172//         case host_name: HostName;
173//     } name;
174// } ServerName;
175//
176// enum {
177//     host_name(0), (255)
178// } NameType;
179//
180// opaque HostName<1..2^16-1>;
181fn parse_tls_extension_sni_hostname(i: &[u8]) -> IResult<&[u8], Host> {
182    let (i, nt) = be_u8(i)?;
183    if nt != 0 {
184        return Err(nom::Err::Error(nom::error::Error::new(i, ErrorKind::IsNot)));
185    }
186    let (i, v) = length_data(be_u16).parse(i)?;
187    let host = str::from_utf8(v)
188        .map_err(|_| nom::Err::Error(nom::error::Error::new(i, ErrorKind::Not)))?
189        .parse()
190        .map_err(|_| nom::Err::Error(nom::error::Error::new(i, ErrorKind::Not)))?;
191    Ok((i, host))
192}
193
194// defined in rfc8422
195fn parse_tls_extension_elliptic_curves_content(i: &[u8]) -> IResult<&[u8], ClientHelloExtension> {
196    map_parser(
197        length_data(be_u16),
198        map(parse_u16_type, ClientHelloExtension::SupportedGroups),
199    )
200    .parse(i)
201}
202
203fn parse_tls_extension_ec_point_formats_content(i: &[u8]) -> IResult<&[u8], ClientHelloExtension> {
204    map_parser(
205        length_data(be_u8),
206        map(parse_u8_type, ClientHelloExtension::ECPointFormats),
207    )
208    .parse(i)
209}
210
211// TLS 1.3 draft 23
212//       struct {
213//           select (Handshake.msg_type) {
214//               case client_hello:
215//                    ProtocolVersion versions<2..254>;
216//
217//               case server_hello: /* and HelloRetryRequest */
218//                    ProtocolVersion selected_version;
219//           };
220//       } SupportedVersions;
221// XXX the content depends on the current message type
222// XXX first case has length 1 + 2*n, while the second case has length 2
223fn parse_tls_extension_supported_versions_content(
224    i: &[u8],
225    ext_len: u16,
226) -> IResult<&[u8], ClientHelloExtension> {
227    if ext_len == 2 {
228        map(be_u16, |x| {
229            ClientHelloExtension::SupportedVersions(vec![ProtocolVersion::from(x)])
230        })
231        .parse(i)
232    } else {
233        let (i, _) = be_u8(i)?;
234        if ext_len == 0 {
235            return Err(nom::Err::Error(make_error(i, ErrorKind::Verify)));
236        }
237        let (i, l) = map_parser(take(ext_len - 1), parse_u16_type).parse(i)?;
238        Ok((i, ClientHelloExtension::SupportedVersions(l)))
239    }
240}
241
242/// Parse 'Signature Algorithms' extension (rfc8446, TLS 1.3 only)
243fn parse_tls_extension_signature_algorithms_content(
244    i: &[u8],
245) -> IResult<&[u8], ClientHelloExtension> {
246    map_parser(
247        length_data(be_u16),
248        map(parse_u16_type, ClientHelloExtension::SignatureAlgorithms),
249    )
250    .parse(i)
251}
252
253// Parse 'Delegated credentials' extensions (rfc9345)
254fn parse_tls_extension_delegated_credentials(i: &[u8]) -> IResult<&[u8], ClientHelloExtension> {
255    map_parser(
256        length_data(be_u16),
257        map(parse_u16_type, ClientHelloExtension::DelegatedCredentials),
258    )
259    .parse(i)
260}
261
262/// Defined in [RFC7301]
263fn parse_tls_extension_alpn_content(i: &[u8]) -> IResult<&[u8], ClientHelloExtension> {
264    map_parser(
265        length_data(be_u16),
266        map(
267            parse_protocol_name_list,
268            ClientHelloExtension::ApplicationLayerProtocolNegotiation,
269        ),
270    )
271    .parse(i)
272}
273
274fn parse_tls_extension_certificate_compression_content(
275    i: &[u8],
276) -> IResult<&[u8], ClientHelloExtension> {
277    map_parser(
278        length_data(be_u8),
279        map(parse_u16_type, ClientHelloExtension::CertificateCompression),
280    )
281    .parse(i)
282}
283
284fn parse_protocol_name_list(mut i: &[u8]) -> IResult<&[u8], Vec<ApplicationProtocol>> {
285    let mut v = vec![];
286    while !i.is_empty() {
287        let (n, alpn) = map_parser(length_data(be_u8), parse_protocol_name).parse(i)?;
288        v.push(alpn);
289        i = n;
290    }
291    Ok((&[], v))
292}
293
294fn parse_protocol_name(i: &[u8]) -> IResult<&[u8], ApplicationProtocol> {
295    let alpn = ApplicationProtocol::from(i);
296    Ok((&[], alpn))
297}
298
299fn parse_u8_type<T: From<u8>>(i: &[u8]) -> IResult<&[u8], Vec<T>> {
300    let v = i.iter().map(|i| T::from(*i)).collect();
301    Ok((&[], v))
302}
303
304fn parse_u16_type<T: From<u16>>(i: &[u8]) -> IResult<&[u8], Vec<T>> {
305    let len = i.len();
306    if len == 0 {
307        return Ok((i, Vec::new()));
308    }
309    if len % 2 == 1 || len > i.len() {
310        return Err(nom::Err::Error(make_error(i, ErrorKind::LengthValue)));
311    }
312    let v = (i[..len])
313        .chunks(2)
314        .map(|chunk| T::from(((chunk[0] as u16) << 8) | chunk[1] as u16))
315        .collect();
316    Ok((&i[len..], v))
317}
318
319fn parse_ech_client_hello(input: &[u8]) -> IResult<&[u8], ECHClientHello> {
320    let (input, is_outer) = map_opt(be_u8, |v| match v {
321        0 => Some(true),
322        1 => Some(false),
323        _ => None,
324    })
325    .parse(input)?;
326
327    match is_outer {
328        true => {
329            let (input, (kdf_id, aead_id, config_id)) = (be_u16, be_u16, be_u8).parse(input)?;
330            let (input, enc) = length_data(be_u16).parse(input)?;
331            let (input, payload) = length_data(be_u16).parse(input)?;
332
333            Ok((
334                input,
335                ECHClientHello::Outer(ECHClientHelloOuter {
336                    cipher_suite: HpkeSymmetricCipherSuite {
337                        aead_id: aead_id.into(),
338                        kdf_id: kdf_id.into(),
339                    },
340                    config_id,
341                    enc: enc.to_vec(),
342                    payload: payload.to_vec(),
343                }),
344            ))
345        }
346        false => Ok((input, ECHClientHello::Inner)),
347    }
348}
349
350#[cfg(test)]
351mod tests {
352    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
353
354    use super::*;
355    use crate::address::Domain;
356    use crate::tls::{
357        CertificateCompressionAlgorithm, ECPointFormat, ExtensionId, SignatureScheme,
358        SupportedGroup,
359    };
360
361    #[test]
362    fn test_parse_tls_extension_sni_hostname() {
363        let test_cases = [
364            ("", None),
365            ("\x00", None),
366            (
367                "\x00\x00\x05x.com",
368                Some(Host::Name(Domain::from_static("x.com"))),
369            ),
370            (
371                "\x00\x00\x10fp.ramaproxy.org",
372                Some(Host::Name(Domain::from_static("fp.ramaproxy.org"))),
373            ),
374            ("\x00\x00\x11fp.ramaproxy.org", None),
375            ("\x01\x00\x10fp.ramaproxy.org", None),
376            (
377                "\x00\x00\x09127.0.0.1",
378                Some(Host::Address(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)))),
379            ),
380            (
381                "\x00\x00\x276670:2e72:616d:6170:726f:7879:2e6f:7267",
382                Some(Host::Address(IpAddr::V6(Ipv6Addr::new(
383                    0x6670, 0x2e72, 0x616d, 0x6170, 0x726f, 0x7879, 0x2e6f, 0x7267,
384                )))),
385            ),
386        ];
387        for (input, expected_output) in test_cases {
388            let result = parse_tls_extension_sni_hostname(input.as_bytes());
389            match expected_output {
390                Some(host) => assert_eq!(host, result.unwrap().1),
391                None => assert!(result.is_err()),
392            }
393        }
394    }
395
396    #[test]
397    fn test_parse_client_hello_zero_bytes_failure() {
398        assert!(parse_client_hello(&[]).is_err());
399    }
400
401    #[test]
402    fn test_parse_client_hello_pcap_dump_apple_itunes_bytes_success() {
403        let client_hello = parse_client_hello(&[
404            0x03, 0x03, 0x74, 0xbd, 0x2a, 0x45, 0x51, 0x29, 0x95, 0x42, 0x61, 0x17, 0xab, 0x20,
405            0x8f, 0xf2, 0x30, 0xea, 0x72, 0x0f, 0x2e, 0xcd, 0x73, 0xff, 0xcb, 0xbc, 0x89, 0x10,
406            0x46, 0xc8, 0xb7, 0x3c, 0x31, 0xf0, 0x20, 0x25, 0xea, 0x68, 0xb2, 0x13, 0x62, 0xf7,
407            0x4b, 0x0f, 0x82, 0x57, 0xf6, 0xe9, 0x41, 0xc5, 0x28, 0x74, 0xa9, 0xf4, 0x80, 0x73,
408            0x90, 0x4f, 0x85, 0xe7, 0xa7, 0xaa, 0x84, 0x37, 0xe8, 0xdf, 0x97, 0x00, 0x2a, 0x7a,
409            0x7a, 0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0xc0, 0x2c, 0xc0, 0x2b, 0xcc, 0xa9, 0xc0,
410            0x30, 0xc0, 0x2f, 0xcc, 0xa8, 0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x14, 0xc0, 0x13, 0x00,
411            0x9d, 0x00, 0x9c, 0x00, 0x35, 0x00, 0x2f, 0xc0, 0x08, 0xc0, 0x12, 0x00, 0x0a, 0x01,
412            0x00, 0x01, 0x89, 0x8a, 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x18, 0x00,
413            0x00, 0x15, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x69, 0x74, 0x75, 0x6e, 0x65, 0x73, 0x2e,
414            0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x17, 0x00, 0x00, 0xff,
415            0x01, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x0a, 0x3a, 0x3a, 0x00, 0x1d,
416            0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x10,
417            0x00, 0x0e, 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31,
418            0x2e, 0x31, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
419            0x18, 0x00, 0x16, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x02, 0x03, 0x08,
420            0x05, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x12, 0x00,
421            0x00, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x29, 0x3a, 0x3a, 0x00, 0x01, 0x00, 0x00, 0x1d,
422            0x00, 0x20, 0x49, 0xee, 0x60, 0xa1, 0x29, 0xc0, 0x44, 0x44, 0xc3, 0x02, 0x8a, 0x25,
423            0x8c, 0x86, 0x64, 0xc3, 0x3a, 0xc0, 0xec, 0xbb, 0x6c, 0xe7, 0x93, 0xda, 0x51, 0xca,
424            0xef, 0x59, 0xc9, 0xee, 0x41, 0x31, 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x2b,
425            0x00, 0x0b, 0x0a, 0xda, 0xda, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00,
426            0x1b, 0x00, 0x03, 0x02, 0x00, 0x01, 0xda, 0xda, 0x00, 0x01, 0x00, 0x00, 0x15, 0x00,
427            0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
428            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
429            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
430            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
431            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
432            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
433            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
434            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
435            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
436            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
437            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
438            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
439            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
440            0x00, 0x00, 0x00, 0x00,
441        ])
442        .expect("to parse");
443        assert_eq!(
444            client_hello.cipher_suites(),
445            &[
446                CipherSuite::from(0x7a7a),
447                CipherSuite::TLS13_AES_128_GCM_SHA256,
448                CipherSuite::TLS13_AES_256_GCM_SHA384,
449                CipherSuite::TLS13_CHACHA20_POLY1305_SHA256,
450                CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
451                CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
452                CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
453                CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
454                CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
455                CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
456                CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
457                CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
458                CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
459                CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
460                CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384,
461                CipherSuite::TLS_RSA_WITH_AES_128_GCM_SHA256,
462                CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA,
463                CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA,
464                CipherSuite::TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
465                CipherSuite::TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
466                CipherSuite::TLS_RSA_WITH_3DES_EDE_CBC_SHA,
467            ]
468        );
469        assert_eq!(client_hello.extensions().len(), 16);
470
471        assert_eq_opaque_extension(
472            &client_hello.extensions()[0],
473            ExtensionId::from(0x8a8a),
474            &[],
475        );
476        assert_eq_server_name_extension(
477            &client_hello.extensions()[1],
478            Some(&Host::Name(Domain::from_static("init.itunes.apple.com"))),
479        );
480        assert_eq_opaque_extension(
481            &client_hello.extensions()[2],
482            ExtensionId::EXTENDED_MASTER_SECRET,
483            &[],
484        );
485        assert_eq_opaque_extension(
486            &client_hello.extensions()[3],
487            ExtensionId::RENEGOTIATION_INFO,
488            &[0x00],
489        );
490        assert_eq_supported_groups_extension(
491            &client_hello.extensions()[4],
492            &[
493                SupportedGroup::from(0x3a3a),
494                SupportedGroup::X25519,
495                SupportedGroup::SECP256R1,
496                SupportedGroup::SECP384R1,
497                SupportedGroup::SECP521R1,
498            ],
499        );
500        assert_eq_ec_point_formats_extension(
501            &client_hello.extensions()[5],
502            &[ECPointFormat::Uncompressed],
503        );
504        assert_eq_alpn_extension(
505            &client_hello.extensions()[6],
506            &[ApplicationProtocol::HTTP_2, ApplicationProtocol::HTTP_11],
507        );
508        assert_eq_opaque_extension(
509            &client_hello.extensions()[7],
510            ExtensionId::STATUS_REQUEST,
511            &[0x01, 0x00, 0x00, 0x00, 0x00],
512        );
513        assert_eq_signature_algorithms_extension(
514            &client_hello.extensions()[8],
515            &[
516                SignatureScheme::ECDSA_NISTP256_SHA256,
517                SignatureScheme::RSA_PSS_SHA256,
518                SignatureScheme::RSA_PKCS1_SHA256,
519                SignatureScheme::ECDSA_NISTP384_SHA384,
520                SignatureScheme::ECDSA_SHA1_Legacy,
521                SignatureScheme::RSA_PSS_SHA384,
522                SignatureScheme::RSA_PSS_SHA384,
523                SignatureScheme::RSA_PKCS1_SHA384,
524                SignatureScheme::RSA_PSS_SHA512,
525                SignatureScheme::RSA_PKCS1_SHA512,
526                SignatureScheme::RSA_PKCS1_SHA1,
527            ],
528        );
529        assert_eq_opaque_extension(
530            &client_hello.extensions()[9],
531            ExtensionId::SIGNED_CERTIFICATE_TIMESTAMP,
532            &[],
533        );
534        assert_eq_opaque_extension(
535            &client_hello.extensions()[10],
536            ExtensionId::KEY_SHARE,
537            &[
538                0x00, 0x29, 0x3a, 0x3a, 0x00, 0x01, 0x00, 0x00, 0x1d, 0x00, 0x20, 0x49, 0xee, 0x60,
539                0xa1, 0x29, 0xc0, 0x44, 0x44, 0xc3, 0x02, 0x8a, 0x25, 0x8c, 0x86, 0x64, 0xc3, 0x3a,
540                0xc0, 0xec, 0xbb, 0x6c, 0xe7, 0x93, 0xda, 0x51, 0xca, 0xef, 0x59, 0xc9, 0xee, 0x41,
541                0x31,
542            ],
543        );
544        assert_eq_opaque_extension(
545            &client_hello.extensions()[11],
546            ExtensionId::PSK_KEY_EXCHANGE_MODES,
547            &[0x01, 0x01],
548        );
549        assert_eq_supported_versions_extension(
550            &client_hello.extensions()[12],
551            &[
552                ProtocolVersion::from(0xdada),
553                ProtocolVersion::TLSv1_3,
554                ProtocolVersion::TLSv1_2,
555                ProtocolVersion::TLSv1_1,
556                ProtocolVersion::TLSv1_0,
557            ],
558        );
559        assert_eq_supported_certificate_compression_extension(
560            &client_hello.extensions()[13],
561            &[CertificateCompressionAlgorithm::Zlib],
562        );
563        assert_eq_opaque_extension(
564            &client_hello.extensions()[14],
565            ExtensionId::from(0xdada), // GREASE
566            &[0x00],
567        );
568        assert_eq_opaque_extension(
569            &client_hello.extensions()[15],
570            ExtensionId::from(0x0015), // padding
571            &[
572                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
573                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
574                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
575                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
576                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
577                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
578                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
579                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
580                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
581                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
582                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
583                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
584                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
585                0x00, 0x00, 0x00,
586            ],
587        );
588    }
589
590    fn assert_eq_opaque_extension(
591        ext: &ClientHelloExtension,
592        expected_id: ExtensionId,
593        expected_data: &[u8],
594    ) {
595        match ext {
596            ClientHelloExtension::Opaque { id, data } => {
597                assert_eq!(*id, expected_id);
598                assert_eq!(data, expected_data);
599            }
600            other => {
601                panic!("unexpected extension: {other:?}");
602            }
603        }
604    }
605
606    fn assert_eq_server_name_extension(ext: &ClientHelloExtension, expected_host: Option<&Host>) {
607        match ext {
608            ClientHelloExtension::ServerName(domain) => {
609                assert_eq!(domain.as_ref(), expected_host);
610            }
611            other => {
612                panic!("unexpected extension: {other:?}");
613            }
614        }
615    }
616
617    fn assert_eq_supported_groups_extension(
618        ext: &ClientHelloExtension,
619        expected_groups: &[SupportedGroup],
620    ) {
621        match ext {
622            ClientHelloExtension::SupportedGroups(groups) => {
623                assert_eq!(groups, expected_groups);
624            }
625            other => {
626                panic!("unexpected extension: {other:?}");
627            }
628        }
629    }
630
631    fn assert_eq_ec_point_formats_extension(
632        ext: &ClientHelloExtension,
633        expected_ec_point_formats: &[ECPointFormat],
634    ) {
635        match ext {
636            ClientHelloExtension::ECPointFormats(points) => {
637                assert_eq!(points, expected_ec_point_formats);
638            }
639            other => {
640                panic!("unexpected extension: {other:?}");
641            }
642        }
643    }
644
645    fn assert_eq_alpn_extension(
646        ext: &ClientHelloExtension,
647        expected_alpn_list: &[ApplicationProtocol],
648    ) {
649        match ext {
650            ClientHelloExtension::ApplicationLayerProtocolNegotiation(alpn_list) => {
651                assert_eq!(alpn_list, expected_alpn_list);
652            }
653            other => {
654                panic!("unexpected extension: {other:?}");
655            }
656        }
657    }
658
659    fn assert_eq_signature_algorithms_extension(
660        ext: &ClientHelloExtension,
661        expected_signature_algorithms: &[SignatureScheme],
662    ) {
663        match ext {
664            ClientHelloExtension::SignatureAlgorithms(algorithms) => {
665                assert_eq!(algorithms, expected_signature_algorithms);
666            }
667            other => {
668                panic!("unexpected extension: {other:?}");
669            }
670        }
671    }
672
673    fn assert_eq_supported_versions_extension(
674        ext: &ClientHelloExtension,
675        expected_version_list: &[ProtocolVersion],
676    ) {
677        match ext {
678            ClientHelloExtension::SupportedVersions(version_list) => {
679                assert_eq!(version_list, expected_version_list);
680            }
681            other => {
682                panic!("unexpected extension: {other:?}");
683            }
684        }
685    }
686
687    fn assert_eq_supported_certificate_compression_extension(
688        ext: &ClientHelloExtension,
689        expected_certificate_compression: &[CertificateCompressionAlgorithm],
690    ) {
691        match ext {
692            ClientHelloExtension::CertificateCompression(algorithms) => {
693                assert_eq!(algorithms, expected_certificate_compression);
694            }
695            other => {
696                panic!("unexpected extension: {other:?}");
697            }
698        }
699    }
700}