use std::{ fmt::Debug, io::{self, Read, Write}, marker::PhantomData, }; use byteorder::{BigEndian as B, ReadBytesExt, WriteBytesExt}; use crate::ErrorKind; // https://datatracker.ietf.org/doc/html/rfc8446#section-4 proto_enum! { #[derive(Debug, Clone)] pub enum Handshake: u8 { // https://datatracker.ietf.org/doc/html/rfc8446#section-4.1.2 ClientHello { legacy_version: u16, random: [u8; 32], legacy_session_id: u8, cipher_suites: List, legacy_compressions_methods: u8, extensions: List, } = 1, ServerHello { legacy_version: u16, random: [u8; 32], legacy_session_id_echo: u8, cipher_suite: CipherSuite, legacy_compression_method: u8, extensions: List, } = 2, NewSessionTicket {} = 4, EndOfEarlyData {} = 5, EncryptedExtensions {} = 8, Certificate {} = 11, CertificateRequest {} = 13, CertificateVerify {} = 15, Finished {} = 20, KeyUpdate {} = 24, MessageHash {} = 254, } } pub const LEGACY_VERSION: u16 = 0x0303; // TLS v1.2 proto_enum! { #[derive(Debug, Clone, Copy)] pub enum CipherSuite: [u8; 2] { TlsAes128GcmSha256 = [0x13, 0x01], TlsAes256GcmSha384 = [0x13, 0x02], TlsChacha20Poly1305Sha256 = [0x13, 0x03], TlsAes128CcmSha256 = [0x13, 0x04], TlsAes128Ccm8Sha256 = [0x13, 0x05], } } pub type Extension = (ExtensionType, List); proto_enum! { #[derive(Debug, Clone, Copy)] pub enum ExtensionType: u16 { ServerName = 0, MaxFragmentLength = 1, StatusRequest = 5, SupportedGroups = 10, SignatureAlgorithms = 13, UseSrtp = 14, Heartbeat = 15, ApplicationLayerProtocolNegotiation = 16, SignedCertificateTimestamp = 18, ClientCertificateType = 19, ServerCertificateType = 20, Padding = 21, PreSharedKey = 41, EarlyData = 42, SupportedVersions = 43, Cookie = 44, PskKeyExchangeModes = 45, CertificateAuthorities = 47, OidFilters = 48, PostHandshakeAuth = 49, SignatureAlgorithmsCert = 50, KeyShare = 51, } } macro_rules! proto_enum { ($(#[$meta:meta])* pub enum $name:ident: $discr_ty:ty { $( $KindName:ident $({ $( $field_name:ident : $field_ty:ty, )* })? = $discriminant:expr, )* }) => { $(#[$meta])* pub enum $name { $( $KindName $({ $( $field_name: $field_ty, )* })?, )* } impl Value for $name { fn write(&self, mut w: &mut W) -> io::Result<()> { mod discr_consts { $( #[allow(non_upper_case_globals)] pub(super) const $KindName: $discr_ty = $discriminant; )* } match self { $( Self::$KindName $( { $( $field_name, )* } )? => { Value::write(&discr_consts::$KindName, &mut w)?; $($( Value::write($field_name, &mut w)?; )*)? Ok(()) } )* } } fn read(r: &mut R) -> crate::Result { mod discr_consts { $( #[allow(non_upper_case_globals)] pub(super) const $KindName: $discr_ty = $discriminant; )* } let kind: $discr_ty = Value::read(r)?; match kind { $( discr_consts::$KindName => { $(let ( $( $field_name ),* ) = ($( { discard!($field_name); Value::read(r)? } ),*);)? Ok(Self::$KindName $({ $( $field_name, )* })*) }, )* _ => Err(ErrorKind::InvalidHandshake(Box::new(kind)).into()), } } } }; } use proto_enum; #[derive(Clone)] pub struct List(Vec, PhantomData); impl From> for List { fn from(value: Vec) -> Self { Self(value, PhantomData) } } impl Debug for List { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_list().entries(self.0.iter()).finish() } } impl + TryFrom> Value for List { fn read(r: &mut R) -> crate::Result { let len: usize = Len::read(r)?.into(); let mut v = Vec::with_capacity(len.max(1000)); for _ in 0..len { v.push(T::read(r)?); } Ok(Self(v, PhantomData)) } fn write(&self, w: &mut W) -> io::Result<()> { Len::write( &self .0 .len() .try_into() .unwrap_or_else(|_| panic!("list is too large for domain: {}", self.0.len())), w, ) } } pub fn write_handshake(w: &mut W, handshake: Handshake) -> io::Result<()> { handshake.write(w) } pub fn read_handshake(r: &mut R) -> crate::Result { Handshake::read(r) } pub trait Value: Sized + std::fmt::Debug { fn write(&self, w: &mut W) -> io::Result<()>; fn read(r: &mut R) -> crate::Result; } impl Value for [V; N] { fn write(&self, w: &mut W) -> io::Result<()> { self.iter().try_for_each(|v| Value::write(v, w)) } fn read(r: &mut R) -> crate::Result { // ugly :( let mut values = Vec::with_capacity(N); for _ in 0..N { let value = V::read(r)?; values.push(value); } Ok(values.try_into().unwrap()) } } impl Value for u8 { fn write(&self, w: &mut W) -> io::Result<()> { w.write_u8(*self) } fn read(r: &mut R) -> crate::Result { r.read_u8().map_err(Into::into) } } impl Value for u16 { fn write(&self, w: &mut W) -> io::Result<()> { w.write_u16::(*self) } fn read(r: &mut R) -> crate::Result { r.read_u16::().map_err(Into::into) } } impl Value for (T, U) { fn write(&self, w: &mut W) -> io::Result<()> { T::write(&self.0, w)?; T::write(&self.0, w)?; Ok(()) } fn read(r: &mut R) -> crate::Result { Ok((T::read(r)?, U::read(r)?)) } } macro_rules! discard { ($($tt:tt)*) => {}; } use discard;