w5500_tls/handshake/
client_hello.rs

1// This module contains ugly code because I did a lot in `const` functions,
2// which limits what functions I can use.
3
4use hmac::digest::generic_array::{typenum::U32, GenericArray};
5use w5500_hl::Hostname;
6
7use crate::{
8    cipher_suites::CipherSuite, extension::ExtensionType, key_schedule::KeySchedule, ContentType,
9    TlsVersion,
10};
11use core::mem::size_of;
12use sha2::Sha256;
13
14use super::HandshakeType;
15
16macro_rules! const_concat_bytes {
17    ($a:expr, $b:expr $(,)*) => {{
18        const __LEN: usize = $a.len() + $b.len();
19        const __CONCATENATED: [u8; __LEN] = {
20            let mut out: [u8; __LEN] = [0u8; __LEN];
21            let mut i = 0;
22            while i < $a.len() {
23                out[i] = $a[i];
24                i += 1;
25            }
26            i = 0;
27            while i < $b.len() {
28                out[i + $a.len()] = $b[i];
29                i += 1;
30            }
31            out
32        };
33
34        __CONCATENATED
35    }};
36}
37
38/// # References
39///
40/// * [RFC 8846 Section 4.2.3](https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.3)
41/// * [RFC 8446 Section 9.1](https://datatracker.ietf.org/doc/html/rfc8446#section-9.1)
42#[repr(u16)]
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44#[non_exhaustive]
45#[cfg_attr(feature = "defmt", derive(defmt::Format))]
46pub enum SignatureScheme {
47    // RSASSA-PKCS1-v1_5 algorithms
48    RsaPkcs1Sha256 = 0x0401, // required
49    RsaPkcs1Sha384 = 0x0501,
50    RsaPkcs1Sha512 = 0x0601,
51    // ECDSA algorithms
52    EcdsaSecp256r1Sha256 = 0x0403, // required
53    EcdsaSecp384r1Sha384 = 0x0503,
54    EcdsaSecp521r1Sha512 = 0x0603,
55    // RSASSA-PSS algorithms with public key OID rsaEncryption
56    RsaPssRsaeSha256 = 0x0804, // required
57    RsaPssRsaeSha384 = 0x0805,
58    RsaPssRsaeSha512 = 0x0806,
59    // EdDSA algorithms
60    Ed25519 = 0x0807,
61    Ed448 = 0x0808,
62    // RSASSA-PSS algorithms with public key OID RSASSA-PSS
63    RsaPssPssSha256 = 0x0809,
64    RsaPssPssSha384 = 0x080a,
65    RsaPssPssSha512 = 0x080b,
66    // Legacy algorithms
67    RsaPkcs1Sha1 = 0x0201,
68    EcdsaSha1 = 0x0203,
69    // private_use(0xFE00..0xFFFF),
70    // (0xFFFF)
71}
72
73impl From<SignatureScheme> for u16 {
74    #[inline]
75    fn from(signature_scheme: SignatureScheme) -> Self {
76        signature_scheme as u16
77    }
78}
79
80impl TryFrom<u16> for SignatureScheme {
81    type Error = u16;
82
83    fn try_from(value: u16) -> Result<Self, Self::Error> {
84        match value {
85            x if x == (Self::RsaPkcs1Sha256 as u16) => Ok(Self::RsaPkcs1Sha256),
86            x if x == (Self::RsaPkcs1Sha384 as u16) => Ok(Self::RsaPkcs1Sha384),
87            x if x == (Self::RsaPkcs1Sha512 as u16) => Ok(Self::RsaPkcs1Sha512),
88            x if x == (Self::EcdsaSecp256r1Sha256 as u16) => Ok(Self::EcdsaSecp256r1Sha256),
89            x if x == (Self::EcdsaSecp384r1Sha384 as u16) => Ok(Self::EcdsaSecp384r1Sha384),
90            x if x == (Self::EcdsaSecp521r1Sha512 as u16) => Ok(Self::EcdsaSecp521r1Sha512),
91            x if x == (Self::RsaPssRsaeSha256 as u16) => Ok(Self::RsaPssRsaeSha256),
92            x if x == (Self::RsaPssRsaeSha384 as u16) => Ok(Self::RsaPssRsaeSha384),
93            x if x == (Self::RsaPssRsaeSha512 as u16) => Ok(Self::RsaPssRsaeSha512),
94            x if x == (Self::Ed25519 as u16) => Ok(Self::Ed25519),
95            x if x == (Self::Ed448 as u16) => Ok(Self::Ed448),
96            x if x == (Self::RsaPssPssSha256 as u16) => Ok(Self::RsaPssPssSha256),
97            x if x == (Self::RsaPssPssSha384 as u16) => Ok(Self::RsaPssPssSha384),
98            x if x == (Self::RsaPssPssSha512 as u16) => Ok(Self::RsaPssPssSha512),
99            x if x == (Self::RsaPkcs1Sha1 as u16) => Ok(Self::RsaPkcs1Sha1),
100            x if x == (Self::EcdsaSha1 as u16) => Ok(Self::EcdsaSha1),
101            x => Err(x),
102        }
103    }
104}
105
106/// # References
107///
108/// * [RFC 6066 Section 3](https://datatracker.ietf.org/doc/html/rfc6066#section-3)
109#[repr(u8)]
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub(crate) enum NameType {
112    Hostname = 0,
113}
114
115/// # References
116///
117/// * [RFC 8446 Section 4.2.7](https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.7)
118/// * [RFC 8446 Section 9.1](https://datatracker.ietf.org/doc/html/rfc8446#section-9.1)
119#[repr(u16)]
120#[non_exhaustive]
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122#[allow(non_camel_case_types, dead_code)]
123#[cfg_attr(feature = "defmt", derive(defmt::Format))]
124pub(crate) enum NamedGroup {
125    // Elliptic Curve Groups (ECDHE)
126    secp256r1 = 0x0017, // required
127    secp384r1 = 0x0018,
128    secp521r1 = 0x0019,
129    x25519 = 0x001D,
130    x448 = 0x001E,
131    // Finite Field Groups (DHE)
132    ffdhe2048 = 0x0100,
133    ffdhe3072 = 0x0101,
134    ffdhe4096 = 0x0102,
135    ffdhe6144 = 0x0103,
136    ffdhe8192 = 0x0104,
137    // Reserved Code Points
138    // ffdhe_private_use(0x01FC..0x01FF),
139    // ecdhe_private_use(0xFE00..0xFEFF),
140}
141
142impl NamedGroup {
143    pub const fn msb(self) -> u8 {
144        ((self as u16) >> 8) as u8
145    }
146
147    pub const fn lsb(self) -> u8 {
148        self as u8
149    }
150}
151
152impl TryFrom<u16> for NamedGroup {
153    type Error = u16;
154
155    fn try_from(value: u16) -> Result<Self, Self::Error> {
156        match value {
157            x if x == (Self::secp256r1 as u16) => Ok(Self::secp256r1),
158            x if x == (Self::secp384r1 as u16) => Ok(Self::secp384r1),
159            x if x == (Self::secp521r1 as u16) => Ok(Self::secp521r1),
160            x if x == (Self::x25519 as u16) => Ok(Self::x25519),
161            x if x == (Self::x448 as u16) => Ok(Self::x448),
162            x if x == (Self::ffdhe2048 as u16) => Ok(Self::ffdhe2048),
163            x if x == (Self::ffdhe3072 as u16) => Ok(Self::ffdhe3072),
164            x if x == (Self::ffdhe4096 as u16) => Ok(Self::ffdhe4096),
165            x if x == (Self::ffdhe6144 as u16) => Ok(Self::ffdhe6144),
166            x if x == (Self::ffdhe8192 as u16) => Ok(Self::ffdhe8192),
167            x => Err(x),
168        }
169    }
170}
171
172/// # References
173///
174/// * [RFC 8446 Section 4.2.9](https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.9)
175#[repr(u8)]
176#[non_exhaustive]
177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
178#[allow(dead_code)]
179#[cfg_attr(feature = "defmt", derive(defmt::Format))]
180pub(crate) enum PskKeyExchangeMode {
181    /// PSK-only key establishment.
182    ///
183    /// In this mode, the server MUST NOT supply a `key_share` value.
184    Ke = 0,
185    /// PSK with (EC)DHE key establishment.
186    ///
187    /// In this mode, the  client and server MUST supply `key_share` values as
188    /// described in [RFC 8446 Section 4.2.8].
189    ///
190    /// [RFC 8446 Section 4.2.8]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.8
191    DheKe = 1,
192}
193
194/// Create a vector with up-to 2**8-1 bytes at compile time.
195// N = NUM_ELEMENTS * ELEMENT_SIZE + size_of::<u8>();
196const fn vector_u8<const NUM_ELEMENTS: usize, const ELEMENT_SIZE: usize, const N: usize>(
197    values: [[u8; ELEMENT_SIZE]; NUM_ELEMENTS],
198) -> [u8; N] {
199    let mut ret: [u8; N] = [0; N];
200
201    let length: usize = ELEMENT_SIZE * NUM_ELEMENTS;
202
203    // fill in vector length
204    ret[0] = length as u8;
205
206    // for loops not allowed in const
207    let mut value_idx: usize = 0;
208    while value_idx < NUM_ELEMENTS {
209        let mut value_byte_idx: usize = 0;
210        while value_byte_idx < ELEMENT_SIZE {
211            ret[value_idx * ELEMENT_SIZE + value_byte_idx + size_of::<u8>()] =
212                values[value_idx][value_byte_idx];
213            value_byte_idx += 1;
214        }
215        value_idx += 1;
216    }
217
218    ret
219}
220
221/// Create a vector with up-to 2**16-1 bytes at compile time.
222// N = NUM_ELEMENTS * ELEMENT_SIZE + size_of::<u16>();
223const fn vector_u16<const NUM_ELEMENTS: usize, const ELEMENT_SIZE: usize, const N: usize>(
224    values: [[u8; ELEMENT_SIZE]; NUM_ELEMENTS],
225) -> [u8; N] {
226    let mut ret: [u8; N] = [0; N];
227
228    let length: usize = ELEMENT_SIZE * NUM_ELEMENTS;
229    let length: u16 = length as u16;
230
231    let mut length_idx: usize = 0;
232    while length_idx < size_of::<u16>() {
233        ret[length_idx] = length.to_be_bytes()[length_idx];
234        length_idx += 1;
235    }
236
237    // for loops not allowed in const
238    let mut value_idx: usize = 0;
239    while value_idx < NUM_ELEMENTS {
240        let mut value_byte_idx: usize = 0;
241        while value_byte_idx < ELEMENT_SIZE {
242            ret[value_idx * ELEMENT_SIZE + value_byte_idx + size_of::<u16>()] =
243                values[value_idx][value_byte_idx];
244            value_byte_idx += 1;
245        }
246        value_idx += 1;
247    }
248
249    ret
250}
251
252/// Create a `SupportedVersions`.
253///
254/// # References
255///
256/// * [RFC 8446 Appendix B.3.1.1](https://datatracker.ietf.org/doc/html/rfc8446#appendix-B.3.1.1)
257///
258/// ```text
259/// struct {
260///     select (Handshake.msg_type) {
261///         case client_hello:
262///              ProtocolVersion versions<2..254>;
263///
264///         case server_hello: /* and HelloRetryRequest */
265///              ProtocolVersion selected_version;
266///     };
267/// } SupportedVersions;
268/// ```
269// N = NUM_VERSIONS * size_of::<u16>() + size_of::<u8>();
270const fn supported_versions<const NUM_VERSIONS: usize, const N: usize>(
271    versions: [u16; NUM_VERSIONS],
272) -> [u8; N] {
273    let mut versions_bytes: [[u8; 2]; NUM_VERSIONS] = [[0; 2]; NUM_VERSIONS];
274    let mut version_idx: usize = 0;
275    while version_idx < NUM_VERSIONS {
276        versions_bytes[version_idx] = versions[version_idx].to_be_bytes();
277        version_idx += 1;
278    }
279    vector_u8(versions_bytes)
280}
281
282/// Create a `PskKeyExchangeModes`.
283///
284/// # References
285///
286/// * [RFC 8446 Appendix 4.2.9](https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.9)
287///
288/// ```text
289/// struct {
290///     PskKeyExchangeMode ke_modes<1..255>;
291/// } PskKeyExchangeModes;
292/// ```
293// N = NUM_MODES + size_of::<u8>();
294const fn psk_key_exchange_modes<const NUM_MODES: usize, const N: usize>(
295    modes: [PskKeyExchangeMode; NUM_MODES],
296) -> [u8; N] {
297    let mut mode_bytes: [[u8; 1]; NUM_MODES] = [[0; 1]; NUM_MODES];
298    let mut mode_idx: usize = 0;
299    while mode_idx < NUM_MODES {
300        mode_bytes[mode_idx][0] = modes[mode_idx] as u8;
301        mode_idx += 1;
302    }
303    vector_u8(mode_bytes)
304}
305
306/// Create a `SignatureSchemeList`.
307///
308/// # References
309///
310/// * [RFC 8446 Section 4.2.3](https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.3)
311///
312/// ```text
313/// struct {
314///     SignatureScheme supported_signature_algorithms<2..2^16-2>;
315/// } SignatureSchemeList;
316/// ```
317// N = NUM_SCHEMES * size_of::<u16>() + size_of::<u16>();
318const fn signature_scheme_list<const NUM_SCHEMES: usize, const N: usize>(
319    schemes: [SignatureScheme; NUM_SCHEMES],
320) -> [u8; N] {
321    let mut schemes_bytes: [[u8; 2]; NUM_SCHEMES] = [[0; 2]; NUM_SCHEMES];
322    let mut scheme_idx: usize = 0;
323    while scheme_idx < NUM_SCHEMES {
324        schemes_bytes[scheme_idx] = (schemes[scheme_idx] as u16).to_be_bytes();
325        scheme_idx += 1;
326    }
327    vector_u16(schemes_bytes)
328}
329
330/// Create a `NamedGroupList`.
331///
332/// # References
333///
334/// * [RFC 8446 Section 4.2.7](https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.7)
335///
336/// ```text
337/// struct {
338///     NamedGroup named_group_list<2..2^16-1>;
339/// } NamedGroupList;
340/// ```
341// N = N_VALUES * size_of::<u16>() + size_of::<u16>();
342const fn named_group_list<const N_VALUES: usize, const N: usize>(
343    values: [NamedGroup; N_VALUES],
344) -> [u8; N] {
345    let mut value_bytes: [[u8; 2]; N_VALUES] = [[0; 2]; N_VALUES];
346    let mut value_idx: usize = 0;
347    while value_idx < N_VALUES {
348        value_bytes[value_idx] = (values[value_idx] as u16).to_be_bytes();
349        value_idx += 1;
350    }
351    vector_u16(value_bytes)
352}
353
354/// Create an `Extension`.
355///
356/// # References
357///
358/// * [RFC 8446 Section 4.2](https://datatracker.ietf.org/doc/html/rfc8446#section-4.2)
359///
360/// ```text
361/// struct {
362///     ExtensionType extension_type;
363///     opaque extension_data<0..2^16-1>;
364/// } Extension;
365/// ```
366// N = DATA_LEN + size_of::<u16>() + size_of::<u16>()
367const fn extension<const DATA_LEN: usize, const N: usize>(
368    extension: ExtensionType,
369    data: [u8; DATA_LEN],
370) -> [u8; N] {
371    let mut ret: [u8; N] = [0; N];
372    ret[0] = (extension as u16).to_be_bytes()[0];
373    ret[1] = (extension as u16).to_be_bytes()[1];
374    ret[2] = (data.len() as u16).to_be_bytes()[0];
375    ret[3] = (data.len() as u16).to_be_bytes()[1];
376
377    let mut data_idx: usize = 0;
378    while data_idx < DATA_LEN {
379        ret[data_idx + 4] = data[data_idx];
380        data_idx += 1;
381    }
382
383    ret
384}
385
386/// Create a list of cipher suites.
387///
388/// # References
389///
390/// * [RFC 8446 Section 4.1.2](https://datatracker.ietf.org/doc/html/rfc8446#section-4.1.2)
391///
392/// ```text
393/// uint8 CipherSuite[2];    /* Cryptographic suite selector */
394///
395/// struct {
396///     ProtocolVersion legacy_version = 0x0303;    /* TLS v1.2 */
397///     Random random;
398///     opaque legacy_session_id<0..32>;
399///     CipherSuite cipher_suites<2..2^16-2>;
400///     opaque legacy_compression_methods<1..2^8-1>;
401///     Extension extensions<8..2^16-1>;
402/// } ClientHello;
403/// ```
404// N = N_VALUES * size_of::<u16>() + size_of::<u16>();
405const fn cipher_suites<const N_VALUES: usize, const N: usize>(
406    values: [CipherSuite; N_VALUES],
407) -> [u8; N] {
408    let mut value_bytes: [[u8; 2]; N_VALUES] = [[0; 2]; N_VALUES];
409    let mut value_idx: usize = 0;
410    while value_idx < N_VALUES {
411        value_bytes[value_idx] = values[value_idx].value();
412        value_idx += 1;
413    }
414    vector_u16(value_bytes)
415}
416
417const CONTENT_TYPE: [u8; 1] = [ContentType::Handshake as u8];
418const TLS_VERSION: [u8; 2] = (TlsVersion::V1_2 as u16).to_be_bytes();
419
420pub const RECORD_HEADER_NO_LENGTH: [u8; CONTENT_TYPE.len() + TLS_VERSION.len()] =
421    const_concat_bytes!(CONTENT_TYPE, TLS_VERSION);
422
423const LEGACY_SESION_ID_LENGTH: [u8; 1] = [0];
424
425const CIPHER_SUITES: [CipherSuite; 1] = [CipherSuite::TLS_AES_128_GCM_SHA256];
426const CIPHER_SUITES_LIST: [u8; CIPHER_SUITES.len() * size_of::<u16>() + size_of::<u16>()] =
427    cipher_suites(CIPHER_SUITES);
428
429// length 1, value null
430const LEGACY_COMPRESSION_METHODS: [u8; 2] = [1, 0];
431
432pub const LEGACY_THINGS_AND_CIPHER_SUITES: [u8; LEGACY_SESION_ID_LENGTH.len()
433    + CIPHER_SUITES_LIST.len()
434    + LEGACY_COMPRESSION_METHODS.len()] = const_concat_bytes!(
435    const_concat_bytes!(LEGACY_SESION_ID_LENGTH, CIPHER_SUITES_LIST),
436    LEGACY_COMPRESSION_METHODS
437);
438
439const SUPPORTED_VERSIONS: [u16; 1] = [TlsVersion::V1_3 as u16];
440const CLIENT_HELLO_SUPPORTED_VERSIONS: [u8; SUPPORTED_VERSIONS.len() * size_of::<u16>()
441    + size_of::<u8>()] = supported_versions(SUPPORTED_VERSIONS);
442const CLIENT_HELLO_SUPPORTED_VERSIONS_EXTENSION: [u8; CLIENT_HELLO_SUPPORTED_VERSIONS.len()
443    + size_of::<u16>()
444    + size_of::<u16>()] = extension(
445    ExtensionType::SupportedVersions,
446    CLIENT_HELLO_SUPPORTED_VERSIONS,
447);
448
449const SIGNATURE_SCHEMES: [SignatureScheme; 9] = [
450    SignatureScheme::RsaPkcs1Sha256,
451    SignatureScheme::RsaPkcs1Sha384,
452    SignatureScheme::RsaPkcs1Sha512,
453    SignatureScheme::EcdsaSecp256r1Sha256,
454    SignatureScheme::EcdsaSecp384r1Sha384,
455    SignatureScheme::RsaPssRsaeSha256,
456    SignatureScheme::RsaPssRsaeSha384,
457    SignatureScheme::RsaPssRsaeSha512,
458    SignatureScheme::Ed25519,
459];
460const SIGNATURE_SCHEME_LIST: [u8; SIGNATURE_SCHEMES.len() * size_of::<u16>() + size_of::<u16>()] =
461    signature_scheme_list(SIGNATURE_SCHEMES);
462const SIGNATURE_ALGORITHMS_EXTENSION: [u8; SIGNATURE_SCHEME_LIST.len()
463    + size_of::<u16>()
464    + size_of::<u16>()] = extension(ExtensionType::SignatureAlgorithms, SIGNATURE_SCHEME_LIST);
465
466const SUPPORTED_GROUPS: [NamedGroup; 1] = [NamedGroup::secp256r1];
467const NAMED_GROUP_LIST: [u8; SUPPORTED_GROUPS.len() * size_of::<u16>() + size_of::<u16>()] =
468    named_group_list(SUPPORTED_GROUPS);
469const SUPPORTED_GROUPS_EXTENSION: [u8; NAMED_GROUP_LIST.len()
470    + size_of::<u16>()
471    + size_of::<u16>()] = extension(ExtensionType::SupportedGroups, NAMED_GROUP_LIST);
472
473const KEY_EXCHANGE_MODES: [PskKeyExchangeMode; 1] = [PskKeyExchangeMode::DheKe];
474const KEY_EXCHANGE_MODES_LIST: [u8; KEY_EXCHANGE_MODES.len() + size_of::<u8>()] =
475    psk_key_exchange_modes(KEY_EXCHANGE_MODES);
476const KEY_EXCHANGE_MODES_EXTENSION: [u8; KEY_EXCHANGE_MODES_LIST.len()
477    + size_of::<u16>()
478    + size_of::<u16>()] = extension(ExtensionType::PskKeyExchangeModes, KEY_EXCHANGE_MODES_LIST);
479
480pub const CONST_EXTENSIONS: [u8; SUPPORTED_GROUPS_EXTENSION.len()
481    + KEY_EXCHANGE_MODES_EXTENSION.len()
482    + CLIENT_HELLO_SUPPORTED_VERSIONS_EXTENSION.len()
483    + SIGNATURE_ALGORITHMS_EXTENSION.len()] = const_concat_bytes!(
484    const_concat_bytes!(SUPPORTED_GROUPS_EXTENSION, KEY_EXCHANGE_MODES_EXTENSION),
485    const_concat_bytes!(
486        CLIENT_HELLO_SUPPORTED_VERSIONS_EXTENSION,
487        SIGNATURE_ALGORITHMS_EXTENSION
488    ),
489);
490
491struct ClientHelloWriter<'a> {
492    buf: &'a mut [u8],
493    len: usize,
494    key_schedule: &'a mut KeySchedule,
495}
496
497impl<'a> ClientHelloWriter<'a> {
498    pub fn copy_from_slice(&mut self, src: &[u8]) {
499        self.copy_from_slice_no_hash(src);
500        self.key_schedule.update_transcript_hash(src);
501    }
502
503    pub fn copy_from_slice_no_hash(&mut self, src: &[u8]) {
504        self.buf[self.len..(self.len + src.len())].copy_from_slice(src);
505        self.len += src.len();
506    }
507
508    pub fn push(&mut self, byte: u8) {
509        self.buf[self.len] = byte;
510        self.key_schedule.update_transcript_hash(&[byte]);
511        self.len += 1;
512    }
513
514    pub fn write_binder(&mut self, psk: &[u8], truncated_transcript_hash: Sha256) {
515        let binder: GenericArray<u8, U32> =
516            self.key_schedule.binder(psk, truncated_transcript_hash);
517        self.copy_from_slice(&binder);
518    }
519}
520
521#[allow(clippy::too_many_arguments)]
522pub fn ser(
523    buf: &mut [u8],
524    random: &[u8; 32],
525    hostname: &Hostname,
526    client_public_key: &[u8; 65],
527    key_schedule: &mut KeySchedule,
528    psk: &[u8],
529    identity: &[u8],
530    record_size_limit: u16,
531) -> usize {
532    let mut writer: ClientHelloWriter = ClientHelloWriter {
533        buf,
534        len: 0,
535        key_schedule,
536    };
537
538    let extensions_length: u16 =
539        137 + (CONST_EXTENSIONS.len() as u16) + u16::from(hostname.len()) + (identity.len() as u16);
540    let handshake_length: u16 = 43 + extensions_length;
541    let tls_plaintext_length: u16 = 4 + handshake_length;
542
543    // the record header is not included in the transcript hash
544    writer.copy_from_slice_no_hash(&RECORD_HEADER_NO_LENGTH);
545    writer.copy_from_slice_no_hash(&tls_plaintext_length.to_be_bytes());
546    let start_of_record: usize = writer.len;
547
548    writer.push(HandshakeType::ClientHello as u8);
549    writer.push(0);
550    writer.copy_from_slice(&handshake_length.to_be_bytes());
551    let start_of_handshake: usize = writer.len;
552
553    writer.copy_from_slice(&u16::from(TlsVersion::V1_2).to_be_bytes());
554    writer.copy_from_slice(random);
555    writer.copy_from_slice(&LEGACY_THINGS_AND_CIPHER_SUITES);
556    writer.copy_from_slice(&extensions_length.to_be_bytes());
557    let start_of_extensions: usize = writer.len;
558
559    writer.copy_from_slice(&CONST_EXTENSIONS);
560
561    // server name indication
562    // https://datatracker.ietf.org/doc/html/rfc6066#section-3
563    {
564        let hostname_len: u16 = hostname.len().into();
565        let server_name_list_len: u16 = hostname_len + 3;
566        let extension_len: u16 = server_name_list_len + 2;
567
568        writer.copy_from_slice(&u16::from(ExtensionType::ServerName).to_be_bytes());
569        writer.copy_from_slice(&extension_len.to_be_bytes());
570        writer.copy_from_slice(&server_name_list_len.to_be_bytes());
571        writer.push(NameType::Hostname as u8);
572        writer.copy_from_slice(&hostname_len.to_be_bytes());
573        writer.copy_from_slice(hostname.as_bytes());
574    }
575
576    // key share
577    // https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.8
578    {
579        const P256_UNCOMPRESSED_POINT_SIZE: u16 = 65;
580        const CLIENT_SHARES_LEN: u16 = P256_UNCOMPRESSED_POINT_SIZE
581            + (size_of::<u16>() as u16)
582            + (size_of::<NamedGroup>() as u16);
583        const EXTENSION_LEN: u16 = CLIENT_SHARES_LEN + (size_of::<u16>() as u16);
584
585        const KEY_SHARE_EXTENSION_HEADER: [u8; 10] = [
586            ExtensionType::KeyShare.msb(),
587            ExtensionType::KeyShare.lsb(),
588            (EXTENSION_LEN >> 8) as u8,
589            EXTENSION_LEN as u8,
590            (CLIENT_SHARES_LEN >> 8) as u8,
591            CLIENT_SHARES_LEN as u8,
592            NamedGroup::secp256r1.msb(),
593            NamedGroup::secp256r1.lsb(),
594            (P256_UNCOMPRESSED_POINT_SIZE >> 8) as u8,
595            P256_UNCOMPRESSED_POINT_SIZE as u8,
596        ];
597        writer.copy_from_slice(&KEY_SHARE_EXTENSION_HEADER);
598        writer.copy_from_slice(client_public_key);
599    }
600
601    // record size limit
602    // https://www.rfc-editor.org/rfc/rfc8449
603    {
604        writer.copy_from_slice(&u16::from(ExtensionType::RecordSizeLimit).to_be_bytes());
605        writer.copy_from_slice(&2_u16.to_be_bytes());
606        writer.copy_from_slice(&record_size_limit.to_be_bytes());
607    }
608
609    // pre-shared key
610    // https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.11
611    let len: usize = {
612        let identity_len: u16 = identity.len() as u16;
613        let identities_len: u16 =
614            (identity_len + (size_of::<u32>() as u16)) + (size_of::<u16>() as u16);
615        const BINDER_LEN: u8 = 32;
616        const BINDERS_LEN: u16 = (BINDER_LEN as u16) + (size_of::<u8>() as u16);
617        let extension_len: u16 =
618            identities_len + BINDERS_LEN + (size_of::<u16>() as u16) + (size_of::<u16>() as u16);
619
620        // For identities established externally, an obfuscated_ticket_age of 0
621        // SHOULD be used.
622        const OBFUSCATED_TICKET_AGE: u32 = 0;
623
624        writer.copy_from_slice(&u16::from(ExtensionType::PreSharedKey).to_be_bytes());
625        writer.copy_from_slice(&extension_len.to_be_bytes());
626        writer.copy_from_slice(&identities_len.to_be_bytes());
627        writer.copy_from_slice(&identity_len.to_be_bytes());
628        writer.copy_from_slice(identity);
629        writer.copy_from_slice(&OBFUSCATED_TICKET_AGE.to_be_bytes());
630        let truncated_transcript_hash: Sha256 = writer.key_schedule.transcript_hash();
631        writer.copy_from_slice(&BINDERS_LEN.to_be_bytes());
632        writer.copy_from_slice(&[BINDER_LEN]);
633        writer.write_binder(psk, truncated_transcript_hash);
634        writer.len
635    };
636
637    let actual_extensions_length: u16 = (len - start_of_extensions) as u16;
638    assert_eq!(actual_extensions_length, extensions_length);
639
640    let actual_handshake_length: u16 = (len - start_of_handshake) as u16;
641    assert_eq!(actual_handshake_length, handshake_length);
642
643    let actual_tls_plaintext_length: u16 = (len - start_of_record) as u16;
644    assert_eq!(actual_tls_plaintext_length, tls_plaintext_length);
645
646    len
647}