From c71fc68d8e82a9ec55014e971f7d2dcaa136717d Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Fri, 8 Sep 2023 23:32:17 +0200 Subject: [PATCH] record --- src/lib.rs | 50 +++++++++++--- src/proto.rs | 186 ++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 195 insertions(+), 41 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5b4d9b3..ea6b30b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,12 +2,10 @@ pub mod proto; use std::{ fmt::Debug, - io::{self}, + io::{self, BufWriter, Read, Write}, net::{TcpStream, ToSocketAddrs}, }; -use crate::proto::CipherSuite; - type Result = std::result::Result; pub struct ClientConnection {} @@ -24,19 +22,26 @@ struct ClientSetupConnection {} impl ClientSetupConnection { fn establish(host: impl ToSocketAddrs) -> Result { - let mut stream = TcpStream::connect(host)?; + let mut stream = BufWriter::new(LoggingWriter(TcpStream::connect(host)?)); let handshake = proto::Handshake::ClientHello { legacy_version: proto::LEGACY_VERSION, random: rand::random(), legacy_session_id: 0, - cipher_suites: vec![CipherSuite::TlsAes128GcmSha256].into(), + cipher_suites: vec![proto::CipherSuite::TlsAes128GcmSha256].into(), legacy_compressions_methods: 0, - extensions: vec![].into(), + extensions: vec![proto::ExtensionCH::SupportedVersions { + versions: vec![proto::TLSV3].into(), + }] + .into(), }; - proto::write_handshake(&mut stream, handshake)?; + let plaintext = proto::TLSPlaintext::Handshake { + handshake, + }; + plaintext.write(&mut stream)?; + stream.flush()?; - let res = proto::read_handshake(&mut stream)?; - dbg!(res); + // let res: proto::TLSPlaintext = proto::Value::read(&mut stream.get_mut())?; + // dbg!(res); todo!() } @@ -66,3 +71,30 @@ impl From for Error { Self { kind: value } } } + +#[derive(Debug)] +struct LoggingWriter(W); + +impl io::Write for LoggingWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + let len = self.0.write(buf); + if let Ok(len) = len { + eprintln!("wrote bytes: {:x?}", &buf[..len]); + } + len + } + + fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } +} + +impl io::Read for LoggingWriter { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let len = self.0.read(buf); + if let Ok(len) = len { + eprintln!("read bytes: {:x?}", &buf[..len]); + } + len + } +} diff --git a/src/proto.rs b/src/proto.rs index 09b9a4e..733669c 100644 --- a/src/proto.rs +++ b/src/proto.rs @@ -8,26 +8,65 @@ use byteorder::{BigEndian as B, ReadBytesExt, WriteBytesExt}; use crate::ErrorKind; +#[derive(Debug, Clone)] +pub enum TLSPlaintext { + Invalid { + legacy_version: ProtocolVersion, + fragment: List, + }, + ChangeCipherSpec, + Alert, + Handshake { + handshake: Handshake, + }, + ApplicationData, +} + +impl TLSPlaintext { + pub fn write(&self, w: &mut impl Write) -> io::Result<()> { + match self { + TLSPlaintext::Invalid { + legacy_version, + fragment, + } => todo!(), + TLSPlaintext::ChangeCipherSpec => todo!(), + TLSPlaintext::Alert => todo!(), + TLSPlaintext::Handshake { handshake } => { + 22u8.write(w)?; // handshake + LEGACY_VERSION.write(w)?; + let len: u16 = handshake.byte_size().try_into().unwrap(); + len.write(w)?; + handshake.write(w)?; + Ok(()) + } + TLSPlaintext::ApplicationData => todo!(), + } + } +} + +pub type ProtocolVersion = u16; +pub type Random = [u8; 32]; + // 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_version: ProtocolVersion, + random: Random, legacy_session_id: u8, cipher_suites: List, legacy_compressions_methods: u8, - extensions: List, + extensions: List, } = 1, ServerHello { - legacy_version: u16, - random: [u8; 32], + legacy_version: ProtocolVersion, + random: Random, legacy_session_id_echo: u8, cipher_suite: CipherSuite, legacy_compression_method: u8, - extensions: List, + extensions: List, } = 2, NewSessionTicket {} = 4, EndOfEarlyData {} = 5, @@ -41,7 +80,8 @@ proto_enum! { } } -pub const LEGACY_VERSION: u16 = 0x0303; // TLS v1.2 +pub const LEGACY_VERSION: ProtocolVersion = 0x0303; // TLS v1.2 +pub const TLSV3: ProtocolVersion = 0x0304; proto_enum! { #[derive(Debug, Clone, Copy)] @@ -54,11 +94,9 @@ proto_enum! { } } -pub type Extension = (ExtensionType, List); - proto_enum! { - #[derive(Debug, Clone, Copy)] - pub enum ExtensionType: u16 { + #[derive(Debug, Clone)] + pub enum ExtensionCH: u16 { ServerName = 0, MaxFragmentLength = 1, StatusRequest = 5, @@ -73,19 +111,67 @@ proto_enum! { Padding = 21, PreSharedKey = 41, EarlyData = 42, - SupportedVersions = 43, + SupportedVersions { + versions: List, + } = 43, Cookie = 44, PskKeyExchangeModes = 45, CertificateAuthorities = 47, - OidFilters = 48, PostHandshakeAuth = 49, SignatureAlgorithmsCert = 50, KeyShare = 51, } } +proto_enum! { + #[derive(Debug, Clone, Copy)] + pub enum ExtensionSH: u16 { + PreSharedKey = 41, + SupportedVersions { + selected_version: ProtocolVersion, + } = 43, + KeyShare = 51, + } +} + +macro_rules! proto_struct { + {$(#[$meta:meta])* pub struct $name:ident { + $( + $field_name:ident : $field_ty:ty, + )* + }} => { + $(#[$meta])* + pub struct $name { + $( + $field_name: $field_ty, + )* + } + + + impl Value for $name { + fn write(&self, mut w: &mut W) -> io::Result<()> { + $( + Value::write(&self.$field_name, &mut w)?; + )* + Ok(()) + } + + fn read(r: &mut R) -> crate::Result { + let ( $( $field_name ),* ) = ($( { discard!($field_name); Value::read(r)? } ),*); + + Ok(Self { + $( + $field_name, + )* + }) + } + } + }; +} +use proto_struct; + macro_rules! proto_enum { - ($(#[$meta:meta])* pub enum $name:ident: $discr_ty:ty { + {$(#[$meta:meta])* pub enum $name:ident: $discr_ty:ty { $( $KindName:ident $({ $( @@ -93,7 +179,7 @@ macro_rules! proto_enum { )* })? = $discriminant:expr, )* - }) => { + }} => { $(#[$meta])* pub enum $name { $( @@ -141,6 +227,7 @@ macro_rules! proto_enum { match kind { $( discr_consts::$KindName => { + #[allow(unused_parens)] $(let ( $( $field_name ),* ) = ($( { discard!($field_name); Value::read(r)? } ),*);)? Ok(Self::$KindName $({ @@ -154,6 +241,26 @@ macro_rules! proto_enum { _ => Err(ErrorKind::InvalidHandshake(Box::new(kind)).into()), } } + + fn byte_size(&self) -> usize { + mod discr_consts { + $( + #[allow(non_upper_case_globals)] + pub(super) const $KindName: $discr_ty = $discriminant; + )* + } + + match self { + $( + Self::$KindName $( { + $( $field_name, )* + } )? => { + $( $( $field_name.byte_size() + )* )? discr_consts::$KindName.byte_size() + } + )* + } + + } } }; } @@ -174,38 +281,40 @@ impl Debug for List { } } -impl + TryFrom> Value for List { +impl + TryFrom + Default> 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)?); + let mut remaining_byte_size = Len::read(r)?.into(); + let mut v = Vec::new(); + + while remaining_byte_size > 0 { + let value = T::read(r)?; + remaining_byte_size -= value.byte_size(); + v.push(value); } Ok(Self(v, PhantomData)) } fn write(&self, w: &mut W) -> io::Result<()> { + let byte_size = self.0.iter().map(Value::byte_size).sum::(); Len::write( - &self - .0 - .len() + &byte_size .try_into() .unwrap_or_else(|_| panic!("list is too large for domain: {}", self.0.len())), w, - ) + )?; + for elem in &self.0 { + elem.write(w)?; + } + Ok(()) + } + fn byte_size(&self) -> usize { + Len::byte_size(&Default::default()) + self.0.iter().map(Value::byte_size).sum::() } -} - -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; + fn byte_size(&self) -> usize; } impl Value for [V; N] { @@ -221,6 +330,9 @@ impl Value for [V; N] { } Ok(values.try_into().unwrap()) } + fn byte_size(&self) -> usize { + self.iter().map(Value::byte_size).sum() + } } impl Value for u8 { @@ -230,6 +342,9 @@ impl Value for u8 { fn read(r: &mut R) -> crate::Result { r.read_u8().map_err(Into::into) } + fn byte_size(&self) -> usize { + 1 + } } impl Value for u16 { @@ -239,6 +354,9 @@ impl Value for u16 { fn read(r: &mut R) -> crate::Result { r.read_u16::().map_err(Into::into) } + fn byte_size(&self) -> usize { + 2 + } } impl Value for (T, U) { @@ -251,6 +369,10 @@ impl Value for (T, U) { fn read(r: &mut R) -> crate::Result { Ok((T::read(r)?, U::read(r)?)) } + + fn byte_size(&self) -> usize { + self.0.byte_size() + self.1.byte_size() + } } macro_rules! discard {