1use 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
23pub 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
146fn parse_tls_extension_sni_content(i: &[u8]) -> IResult<&[u8], ClientHelloExtension> {
150 if i.is_empty() {
151 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
169fn 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
194fn 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
211fn 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
242fn 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
253fn 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
262fn 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), &[0x00],
567 );
568 assert_eq_opaque_extension(
569 &client_hello.extensions()[15],
570 ExtensionId::from(0x0015), &[
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}