Skip to main content

internet/ietf/tls1_3/encoding/
handshake.rs

1//! TLS 1.3 Handshake encoding following [RFC 8446].
2//!
3//! Encoding is supported for the following structures:
4//!
5//!  - [`HandshakeType`]
6//!  - [`Handshake`]
7//!  - [`CipherSuite`]
8//!  - [`Random`]
9//!  - [`ClientHello`]
10//!  - [`ServerHello`]
11//!  - [`NewSessionTicket`]
12//!  - [`EndOfEarlyData`]
13//!  - [`EncryptedExtensions`]
14//!  - [`SignatureScheme`]
15//!  - [`CertificateEntry`]
16//!  - [`Certificate`]
17//!  - [`CertificateRequest`]
18//!  - [`CertificateVerify`]
19//!  - [`Finished`]
20//!  - [`KeyUpdateRequest`]
21//!  - [`KeyUpdate`]
22//!
23//! [RFC 8446]: https://datatracker.ietf.org/doc/html/rfc8446
24
25use crate::{
26    Buf,
27    BufError::{self},
28    BufMut, BufResult, Codec, Cursor,
29    ietf::tls::{LengthPrefix, ProtocolVersion, TlsVec, u24},
30    ietf::tls1_3::Extension,
31};
32use ring::rand::{SecureRandom, SystemRandom};
33
34/// A Handshake Type following [Section 4].
35///
36/// According to RFC 8446, unknown handshake message types MUST be treated as fatal errors.
37/// Therefore, this is implemented as an enum.
38///
39/// [Section 4]: https://datatracker.ietf.org/doc/html/rfc8446#section-4
40#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
41#[repr(u8)]
42pub enum HandshakeType {
43    /// client_hello(1)
44    ClientHello = 1,
45    /// server_hello(2)
46    ServerHello = 2,
47    /// new_session_ticket(4)
48    NewSessionTicket = 4,
49    /// end_of_early_data(5)
50    EndOfEarlyData = 5,
51    /// encrypted_extensions(8)
52    EncryptedExtensions = 8,
53    /// certificate(11)
54    Certificate = 11,
55    /// certificate_request(13)
56    CertificateRequest = 13,
57    /// certificate_verify(15)
58    CertificateVerify = 15,
59    /// finished(20)
60    Finished = 20,
61    /// key_update(24)
62    KeyUpdate = 24,
63    /// message_hash(254)
64    MessageHash = 254,
65}
66
67impl Codec for HandshakeType {
68    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
69        (*self as u8).encode(writer, ())
70    }
71
72    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
73        match u8::decode(reader, ())? {
74            x if x == (Self::ClientHello as u8) => Ok(Self::ClientHello),
75            x if x == (Self::ServerHello as u8) => Ok(Self::ServerHello),
76            x if x == (Self::NewSessionTicket as u8) => Ok(Self::NewSessionTicket),
77            x if x == (Self::EndOfEarlyData as u8) => Ok(Self::EndOfEarlyData),
78            x if x == (Self::EncryptedExtensions as u8) => Ok(Self::EncryptedExtensions),
79            x if x == (Self::Certificate as u8) => Ok(Self::Certificate),
80            x if x == (Self::CertificateRequest as u8) => Ok(Self::CertificateRequest),
81            x if x == (Self::CertificateVerify as u8) => Ok(Self::CertificateVerify),
82            x if x == (Self::Finished as u8) => Ok(Self::Finished),
83            x if x == (Self::KeyUpdate as u8) => Ok(Self::KeyUpdate),
84            x if x == (Self::MessageHash as u8) => Ok(Self::MessageHash),
85            _ => Err(BufError::UnexpectedValue),
86        }
87    }
88}
89
90/// A Handshake message following [Section 4].
91///
92/// [Section 4]: https://datatracker.ietf.org/doc/html/rfc8446#section-4
93#[derive(Debug, Clone, PartialEq, Eq, Hash)]
94pub struct Handshake {
95    /// The type of the handshake message.
96    pub msg_type: HandshakeType,
97    /// The body of the handshake message.
98    pub msg: Vec<u8>,
99}
100
101impl Codec for Handshake {
102    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
103        self.msg_type.encode(writer, ())?;
104        let len = u24::from_usize(self.msg.len())?;
105        len.encode(writer, ())?;
106        writer.write_slice(&self.msg)
107    }
108
109    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
110        let msg_type = HandshakeType::decode(reader, ())?;
111        let length_u24 = u24::decode(reader, ())?;
112        let length = length_u24.as_usize();
113
114        if reader.remaining() < length {
115            return Err(BufError::UnexpectedEof);
116        }
117
118        let mut msg = vec![0u8; length];
119        reader.read_into(&mut msg)?;
120
121        Ok(Self { msg_type, msg })
122    }
123}
124
125/// A Cipher Suite following [Appendix B.4].
126///
127/// Unknown cipher suites in ServerHello MUST be treated as errors.
128/// Therefore, this is implemented as an enum.
129///
130/// [Appendix B.4]: https://datatracker.ietf.org/doc/html/rfc8446#appendix-B.4
131#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
132#[repr(u16)]
133pub enum CipherSuite {
134    /// TLS_AES_128_GCM_SHA256
135    TlsAes128GcmSha256 = 0x1301,
136    /// TLS_AES_256_GCM_SHA384
137    TlsAes256GcmSha384 = 0x1302,
138    /// TLS_CHACHA20_POLY1305_SHA256
139    TlsChacha20Poly1305Sha256 = 0x1303,
140    /// TLS_AES_128_CCM_SHA256
141    TlsAes128CcmSha256 = 0x1304,
142    /// TLS_AES_128_CCM_8_SHA256
143    TlsAes128Ccm8Sha256 = 0x1305,
144    /// TLS_EMPTY_RENEGOTIATION_INFO_SCSV
145    TlsEmptyRenegotiationInfoScsv = 0x00FF,
146}
147
148impl Codec for CipherSuite {
149    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
150        (*self as u16).encode(writer, ())
151    }
152
153    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
154        match u16::decode(reader, ())? {
155            x if x == (Self::TlsAes128GcmSha256 as u16) => Ok(Self::TlsAes128GcmSha256),
156            x if x == (Self::TlsAes256GcmSha384 as u16) => Ok(Self::TlsAes256GcmSha384),
157            x if x == (Self::TlsChacha20Poly1305Sha256 as u16) => {
158                Ok(Self::TlsChacha20Poly1305Sha256)
159            }
160            x if x == (Self::TlsAes128CcmSha256 as u16) => Ok(Self::TlsAes128CcmSha256),
161            x if x == (Self::TlsAes128Ccm8Sha256 as u16) => Ok(Self::TlsAes128Ccm8Sha256),
162            x if x == (Self::TlsEmptyRenegotiationInfoScsv as u16) => {
163                Ok(Self::TlsEmptyRenegotiationInfoScsv)
164            }
165            _ => Err(BufError::UnexpectedValue),
166        }
167    }
168}
169
170/// A Random structure following [Section 4.1.2].
171///
172/// [Section 4.1.2]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.1.2
173#[derive(Debug, Clone, PartialEq, Eq, Hash)]
174pub struct Random(pub [u8; 32]);
175
176impl Random {
177    /// Generates a new Random structure using a secure random number generator.
178    pub fn random() -> Self {
179        let mut random = [0u8; 32];
180        let rng = SystemRandom::new();
181        rng.fill(&mut random).unwrap();
182        Self(random)
183    }
184}
185
186impl Codec for Random {
187    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
188        writer.write_array(&self.0)
189    }
190
191    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
192        Ok(Self(reader.read_array::<32>()?))
193    }
194}
195
196/// A Client Hello message following [Section 4.1.2].
197///
198/// [Section 4.1.2]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.1.2
199#[derive(Debug, Clone, PartialEq, Eq, Hash)]
200pub struct ClientHello {
201    /// The legacy version (Fixed TLS v1.2 since TLS 1.3).
202    pub legacy_version: ProtocolVersion,
203    /// The random structure.
204    pub random: Random,
205    /// The legacy session ID.
206    pub legacy_session_id: TlsVec<u8, u8>,
207    /// The cipher suites.
208    pub cipher_suites: TlsVec<CipherSuite, u16>,
209    /// The legacy compression methods.
210    pub legacy_compression_methods: TlsVec<u8, u8>,
211    /// The extensions.
212    pub extensions: TlsVec<Extension, u16>,
213}
214
215impl ClientHello {
216    /// Creates a default ClientHello with common cipher suites and the provided extensions.
217    pub fn default(extensions: TlsVec<Extension, u16>) -> Self {
218        Self {
219            legacy_version: ProtocolVersion::TLS12,
220            random: Random::random(),
221            legacy_session_id: TlsVec::new(vec![]),
222            cipher_suites: TlsVec::new(vec![
223                CipherSuite::TlsAes128GcmSha256,
224                CipherSuite::TlsChacha20Poly1305Sha256,
225            ]),
226            legacy_compression_methods: TlsVec::new(vec![0x00]),
227            extensions,
228        }
229    }
230}
231
232impl Codec for ClientHello {
233    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
234        self.legacy_version.encode(writer, ())?;
235        self.random.encode(writer, ())?;
236        self.legacy_session_id.encode(writer, ())?;
237        self.cipher_suites.encode(writer, ())?;
238        self.legacy_compression_methods.encode(writer, ())?;
239        self.extensions.encode(writer, ())
240    }
241
242    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
243        Ok(Self {
244            legacy_version: ProtocolVersion::decode(reader, ())?,
245            random: Random::decode(reader, ())?,
246            legacy_session_id: TlsVec::<u8, u8>::decode(reader, ())?,
247            cipher_suites: TlsVec::<CipherSuite, u16>::decode(reader, ())?,
248            legacy_compression_methods: TlsVec::<u8, u8>::decode(reader, ())?,
249            extensions: TlsVec::<Extension, u16>::decode(reader, ())?,
250        })
251    }
252}
253
254/// A Server Hello message following [Section 4.1.3].
255///
256/// [Section 4.1.3]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.1.3
257#[derive(Debug, Clone, PartialEq, Eq, Hash)]
258pub struct ServerHello {
259    /// The legacy version.
260    pub legacy_version: ProtocolVersion,
261    /// The random structure.
262    pub random: Random,
263    /// The legacy session ID echo.
264    pub legacy_session_id_echo: TlsVec<u8, u8>,
265    /// The cipher suite.
266    pub cipher_suite: CipherSuite,
267    /// The legacy compression method.
268    pub legacy_compression_method: u8,
269    /// The extensions.
270    pub extensions: TlsVec<Extension, u16>,
271}
272
273impl Codec for ServerHello {
274    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
275        self.legacy_version.encode(writer, ())?;
276        self.random.encode(writer, ())?;
277        self.legacy_session_id_echo.encode(writer, ())?;
278        self.cipher_suite.encode(writer, ())?;
279        self.legacy_compression_method.encode(writer, ())?;
280        self.extensions.encode(writer, ())
281    }
282
283    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
284        Ok(Self {
285            legacy_version: ProtocolVersion::decode(reader, ())?,
286            random: Random::decode(reader, ())?,
287            legacy_session_id_echo: TlsVec::<u8, u8>::decode(reader, ())?,
288            cipher_suite: CipherSuite::decode(reader, ())?,
289            legacy_compression_method: u8::decode(reader, ())?,
290            extensions: TlsVec::<Extension, u16>::decode(reader, ())?,
291        })
292    }
293}
294
295/// A New Session Ticket message following [Section 4.6.1].
296///
297/// [Section 4.6.1]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.6.1
298#[derive(Debug, Clone, PartialEq, Eq, Hash)]
299pub struct NewSessionTicket {
300    /// The ticket lifetime in seconds.
301    pub ticket_lifetime: u32,
302    /// The ticket age add.
303    pub ticket_age_add: u32,
304    /// The ticket nonce.
305    pub ticket_nonce: TlsVec<u8, u8>,
306    /// The ticket.
307    pub ticket: TlsVec<u8, u16>,
308    /// The extensions.
309    pub extensions: TlsVec<Extension, u16>,
310}
311
312impl Codec for NewSessionTicket {
313    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
314        self.ticket_lifetime.encode(writer, ())?;
315        self.ticket_age_add.encode(writer, ())?;
316        self.ticket_nonce.encode(writer, ())?;
317        self.ticket.encode(writer, ())?;
318        self.extensions.encode(writer, ())
319    }
320
321    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
322        Ok(Self {
323            ticket_lifetime: u32::decode(reader, ())?,
324            ticket_age_add: u32::decode(reader, ())?,
325            ticket_nonce: TlsVec::<u8, u8>::decode(reader, ())?,
326            ticket: TlsVec::<u8, u16>::decode(reader, ())?,
327            extensions: TlsVec::<Extension, u16>::decode(reader, ())?,
328        })
329    }
330}
331
332/// An End Of Early Data message following [Section 4.5].
333///
334/// [Section 4.5]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.5
335#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
336pub struct EndOfEarlyData;
337
338impl Codec for EndOfEarlyData {
339    fn encode<W: BufMut>(&self, _writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
340        Ok(())
341    }
342
343    fn decode<R: Buf>(_reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
344        Ok(Self)
345    }
346}
347
348/// An Encrypted Extensions message following [Section 4.3.1].
349///
350/// [Section 4.3.1]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.3.1
351#[derive(Debug, Clone, PartialEq, Eq, Hash)]
352pub struct EncryptedExtensions {
353    /// The extensions.
354    pub extensions: TlsVec<Extension, u16>,
355}
356
357impl Codec for EncryptedExtensions {
358    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
359        self.extensions.encode(writer, ())
360    }
361
362    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
363        Ok(Self {
364            extensions: TlsVec::<Extension, u16>::decode(reader, ())?,
365        })
366    }
367}
368
369/// A Signature Scheme following [Section 4.2.3].
370///
371/// Unknown schemes are ignored per RFC, so this is a struct with constants.
372///
373/// [Section 4.2.3]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.3
374#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
375pub struct SignatureScheme(pub u16);
376
377impl SignatureScheme {
378    /// rsa_pkcs1_sha256(0x0401)
379    pub const RSA_PKCS1_SHA256: Self = Self(0x0401);
380    /// ecdsa_secp256r1_sha256(0x0403)
381    pub const ECDSA_SECP256R1_SHA256: Self = Self(0x0403);
382    /// ecdsa_secp384r1_sha384(0x0503)
383    pub const ECDSA_SECP384R1_SHA384: Self = Self(0x0503);
384    /// rsa_pss_rsae_sha256(0x0804)
385    pub const RSA_PSS_RSAE_SHA256: Self = Self(0x0804);
386    /// rsa_pss_rsae_sha384(0x0805)
387    pub const RSA_PSS_RSAE_SHA384: Self = Self(0x0805);
388    /// ed25519(0x0807)
389    pub const ED25519: Self = Self(0x0807);
390}
391
392impl Codec for SignatureScheme {
393    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
394        self.0.encode(writer, ())
395    }
396
397    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
398        Ok(Self(u16::decode(reader, ())?))
399    }
400}
401
402/// A Certificate Entry following [Section 4.4.2].
403///
404/// [Section 4.4.2]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.4.2
405#[derive(Debug, Clone, PartialEq, Eq, Hash)]
406pub struct CertificateEntry {
407    /// The certificate data.
408    pub cert_data: TlsVec<u8, u24>,
409    /// The extensions.
410    pub extensions: TlsVec<Extension, u16>,
411}
412
413impl Codec for CertificateEntry {
414    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
415        self.cert_data.encode(writer, ())?;
416        self.extensions.encode(writer, ())
417    }
418
419    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
420        Ok(Self {
421            cert_data: TlsVec::<u8, u24>::decode(reader, ())?,
422            extensions: TlsVec::<Extension, u16>::decode(reader, ())?,
423        })
424    }
425}
426
427/// A Certificate message following [Section 4.4.2].
428///
429/// [Section 4.4.2]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.4.2
430#[derive(Debug, Clone, PartialEq, Eq, Hash)]
431pub struct Certificate {
432    /// The certificate request context.
433    pub certificate_request_context: TlsVec<u8, u8>,
434    /// The certificate list.
435    pub certificate_list: TlsVec<CertificateEntry, u24>,
436}
437
438impl Codec for Certificate {
439    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
440        self.certificate_request_context.encode(writer, ())?;
441        self.certificate_list.encode(writer, ())
442    }
443
444    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
445        Ok(Self {
446            certificate_request_context: TlsVec::<u8, u8>::decode(reader, ())?,
447            certificate_list: TlsVec::<CertificateEntry, u24>::decode(reader, ())?,
448        })
449    }
450}
451
452/// A Certificate Request message following [Section 4.3.2].
453///
454/// [Section 4.3.2]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.3.2
455#[derive(Debug, Clone, PartialEq, Eq, Hash)]
456pub struct CertificateRequest {
457    /// The certificate request context.
458    pub certificate_request_context: TlsVec<u8, u8>,
459    /// The extensions.
460    pub extensions: TlsVec<Extension, u16>,
461}
462
463impl Codec for CertificateRequest {
464    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
465        self.certificate_request_context.encode(writer, ())?;
466        self.extensions.encode(writer, ())
467    }
468
469    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
470        Ok(Self {
471            certificate_request_context: TlsVec::<u8, u8>::decode(reader, ())?,
472            extensions: TlsVec::<Extension, u16>::decode(reader, ())?,
473        })
474    }
475}
476
477/// A Certificate Verify message following [Section 4.4.3].
478///
479/// [Section 4.4.3]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.4.3
480#[derive(Debug, Clone, PartialEq, Eq, Hash)]
481pub struct CertificateVerify {
482    /// The signature algorithm.
483    pub algorithm: SignatureScheme,
484    /// The signature.
485    pub signature: TlsVec<u8, u16>,
486}
487
488impl Codec for CertificateVerify {
489    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
490        self.algorithm.encode(writer, ())?;
491        self.signature.encode(writer, ())
492    }
493
494    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
495        Ok(Self {
496            algorithm: SignatureScheme::decode(reader, ())?,
497            signature: TlsVec::<u8, u16>::decode(reader, ())?,
498        })
499    }
500}
501
502/// A Finished message following [Section 4.4.4].
503///
504/// [Section 4.4.4]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.4.4
505#[derive(Debug, Clone, PartialEq, Eq, Hash)]
506pub struct Finished {
507    /// The verify data.
508    pub verify_data: Vec<u8>,
509}
510
511impl Codec for Finished {
512    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
513        self.verify_data.encode(writer, ())
514    }
515
516    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
517        Ok(Self {
518            verify_data: Vec::<u8>::decode(reader, ())?,
519        })
520    }
521}
522
523/// A Key Update Request following [Section 4.6.3].
524///
525/// Unknown values MUST be treated as illegal_parameter errors.
526/// Therefore, this is implemented as an enum.
527///
528/// [Section 4.6.3]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.6.3
529#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
530#[repr(u8)]
531pub enum KeyUpdateRequest {
532    /// update_not_requested(0)
533    UpdateNotRequested = 0,
534    /// update_requested(1)
535    UpdateRequested = 1,
536}
537
538impl Codec for KeyUpdateRequest {
539    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
540        (*self as u8).encode(writer, ())
541    }
542
543    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
544        match u8::decode(reader, ())? {
545            x if x == (Self::UpdateNotRequested as u8) => Ok(Self::UpdateNotRequested),
546            x if x == (Self::UpdateRequested as u8) => Ok(Self::UpdateRequested),
547            _ => Err(BufError::UnexpectedValue),
548        }
549    }
550}
551
552/// A Key Update message following [Section 4.6.3].
553///
554/// [Section 4.6.3]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.6.3
555#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
556pub struct KeyUpdate {
557    /// The key update request.
558    pub request_update: KeyUpdateRequest,
559}
560
561impl Codec for KeyUpdate {
562    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
563        self.request_update.encode(writer, ())
564    }
565
566    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
567        Ok(Self {
568            request_update: KeyUpdateRequest::decode(reader, ())?,
569        })
570    }
571}
572
573#[cfg(test)]
574mod tests {
575    use core::fmt::Debug;
576
577    use super::{CipherSuite, HandshakeType, KeyUpdateRequest};
578    use crate::{Codec, Cursor};
579
580    fn codec_roundtrip<T: Codec<C> + Debug + Eq, C: Copy>(
581        etalon_struct: T,
582        etalon_bytes: &[u8],
583        context: C,
584    ) {
585        let mut encoded_bytes = vec![];
586        {
587            let writer = &mut Cursor::new(&mut encoded_bytes);
588            etalon_struct.encode(writer, context).unwrap();
589        }
590        assert_eq!(etalon_bytes, &encoded_bytes);
591
592        let decoded_struct = {
593            let reader = &mut Cursor::new(&mut encoded_bytes);
594            T::decode(reader, context).unwrap()
595        };
596        assert_eq!(etalon_struct, decoded_struct);
597
598        encoded_bytes.fill(0x00);
599        {
600            let writer = &mut Cursor::new(&mut encoded_bytes);
601            decoded_struct.encode(writer, context).unwrap();
602        }
603        assert_eq!(etalon_bytes, &encoded_bytes);
604    }
605
606    #[test]
607    fn handshake_type() {
608        let etalon_bytes = &[0x01];
609        let etalon_struct = HandshakeType::ClientHello;
610
611        codec_roundtrip(etalon_struct, etalon_bytes, ());
612    }
613
614    #[test]
615    fn cipher_suite() {
616        let etalon_bytes = &[0x13, 0x01];
617        let etalon_struct = CipherSuite::TlsAes128GcmSha256;
618
619        codec_roundtrip(etalon_struct, etalon_bytes, ());
620    }
621
622    #[test]
623    fn key_update_request() {
624        let etalon_bytes = &[0x01];
625        let etalon_struct = KeyUpdateRequest::UpdateRequested;
626
627        codec_roundtrip(etalon_struct, etalon_bytes, ());
628    }
629}