diff --git a/src/lib.rs b/src/lib.rs index 5195b99..3462565 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,16 +49,16 @@ impl ClientSetupConnection { groups: vec![proto::NamedGroup::X25519].into(), }, // passing this doesnt work and shows up as TLSv1.2 in wireshark and gives a handshake error - /*proto::ExtensionCH::KeyShare { + proto::ExtensionCH::KeyShare { entries: vec![proto::KeyShareEntry::X25519 { len: public.as_bytes().len().try_into().unwrap(), key_exchange: *public.as_bytes(), }] .into(), - },*/ - /*proto::ExtensionCH::SignatureAlgorithms { + }, + proto::ExtensionCH::SignatureAlgorithms { supported_signature_algorithms: vec![proto::SignatureScheme::ED25519].into(), - },*/ + }, proto::ExtensionCH::SupportedVersions { versions: vec![proto::TLSV13].into(), }, @@ -97,6 +97,7 @@ pub enum ErrorKind { impl From for Error { fn from(value: io::Error) -> Self { + panic!("error: {value}"); Self { kind: ErrorKind::Io(value), } @@ -105,6 +106,7 @@ impl From for Error { impl From for Error { fn from(value: ErrorKind) -> Self { + panic!("error:{value:?}"); Self { kind: value } } } diff --git a/src/main.rs b/src/main.rs index 48c139d..951fc8f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ // An example program that makes a shitty HTTP/1.1 request. fn main() { - tls::ClientConnection::establish("nilstrieb.dev", 443).unwrap(); + tls::ClientConnection::establish("google.com", 443).unwrap(); } diff --git a/src/proto.rs b/src/proto.rs index 89beec8..d1ff5da 100644 --- a/src/proto.rs +++ b/src/proto.rs @@ -7,7 +7,7 @@ use std::{ use crate::ErrorKind; -use self::ser_de::{proto_enum, proto_struct, u24, List, Todo, Value}; +use self::ser_de::{proto_enum, proto_struct, u24, FrameReader, List, Todo, Value}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum TLSPlaintext { @@ -58,6 +58,7 @@ impl TLSPlaintext { } pub fn read(r: &mut impl Read) -> crate::Result { + let r = &mut FrameReader::new(r); let discr = u8::read(r)?; let _legacy_version = ProtocolVersion::read(r)?; let _len = u16::read(r)?; @@ -101,7 +102,7 @@ proto_enum! { } = 1, ServerHello { legacy_version: ProtocolVersion, - random: Random, + random: ServerHelloRandom, legacy_session_id_echo: LegacySessionId, cipher_suite: CipherSuite, legacy_compression_method: u8, @@ -190,7 +191,7 @@ proto_enum! { } = 43, Cookie { todo: Todo, } = 44, KeyShare { - group: NamedGroup, + key_share: ServerHelloKeyshare, } = 51, } } @@ -329,9 +330,60 @@ proto_enum! { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ServerHelloRandom(Random); + +impl Value for ServerHelloRandom { + fn write(&self, w: &mut W) -> io::Result<()> { + self.0.write(w) + } + + fn read(r: &mut FrameReader) -> crate::Result { + let random = Random::read(r)?; + if random == HELLO_RETRY_REQUEST { + r.is_hello_retry_request = true; + } + Ok(Self(random)) + } + + fn byte_size(&self) -> usize { + self.0.byte_size() + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ServerHelloKeyshare { + HelloRetryRequest(NamedGroup), + ServerHello(KeyShareEntry), +} + +impl Value for ServerHelloKeyshare { + fn write(&self, w: &mut W) -> io::Result<()> { + match self { + Self::HelloRetryRequest(group) => group.write(w), + Self::ServerHello(entry) => entry.write(w), + } + } + + fn read(r: &mut FrameReader) -> crate::Result { + if r.is_hello_retry_request { + NamedGroup::read(r).map(Self::HelloRetryRequest) + } else { + KeyShareEntry::read(r).map(Self::ServerHello) + } + } + + fn byte_size(&self) -> usize { + match self { + Self::HelloRetryRequest(group) => group.byte_size(), + Self::ServerHello(entry) => entry.byte_size(), + } + } +} + impl Handshake { pub fn is_hello_retry_request(&self) -> bool { - matches!(self, Handshake::ServerHello { random, .. } if random == &HELLO_RETRY_REQUEST) + matches!(self, Handshake::ServerHello { random, .. } if random.0 == HELLO_RETRY_REQUEST) } } diff --git a/src/proto/ser_de.rs b/src/proto/ser_de.rs index d3513b9..f271804 100644 --- a/src/proto/ser_de.rs +++ b/src/proto/ser_de.rs @@ -4,8 +4,34 @@ use std::{ io::{self, Read, Write}, marker::PhantomData, num::TryFromIntError, + ops::{Deref, DerefMut}, }; +pub struct FrameReader { + read: R, + pub is_hello_retry_request: bool, +} + +impl FrameReader { + pub fn new(read: R) -> Self { + FrameReader { read, is_hello_retry_request: false } + } +} + +impl Deref for FrameReader { + type Target = R; + + fn deref(&self) -> &Self::Target { + &self.read + } +} + +impl DerefMut for FrameReader { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.read + } +} + /// ```ignore /// proto_struct! {} /// ``` @@ -31,7 +57,7 @@ macro_rules! proto_struct { Ok(()) } - fn read(r: &mut R) -> crate::Result { + fn read(r: &mut $crate::proto::ser_de::FrameReader) -> crate::Result { let ( $( $field_name ),* ) = ($( { crate::proto::ser_de::discard!($field_name); crate::proto::ser_de::Value::read(r)? } ),*); Ok(Self { @@ -120,7 +146,7 @@ macro_rules! proto_enum { } } - fn read(r: &mut R) -> crate::Result { + fn read(r: &mut $crate::proto::ser_de::FrameReader) -> crate::Result { mod discr_consts { #[allow(unused_imports)] use super::*; @@ -140,8 +166,9 @@ macro_rules! proto_enum { match kind { $( discr_consts::$KindName => { - #[allow(unused_parens)] - $(let ( $( $field_name ),* ) = ($( { crate::proto::ser_de::discard!($field_name); crate::proto::ser_de::Value::read(r)? } ),*);)? + $($( + let $field_name = crate::proto::ser_de::Value::read(r)?; + )*)? Ok(Self::$KindName $({ $( @@ -186,7 +213,7 @@ impl Value for Todo { todo!() } - fn read(_: &mut R) -> crate::Result { + fn read(_: &mut FrameReader) -> crate::Result { todo!() } @@ -211,11 +238,13 @@ impl Debug for List { } impl + TryFrom + Default> Value for List { - fn read(r: &mut R) -> crate::Result { + fn read(r: &mut FrameReader) -> crate::Result { + eprintln!("reading a list"); let mut remaining_byte_size = Len::read(r)?.into(); let mut v = Vec::new(); - + eprintln!("list.. remaining bytes {remaining_byte_size}"); while remaining_byte_size > 0 { + eprintln!("things {remaining_byte_size} {v:?}"); let value = T::read(r)?; remaining_byte_size -= value.byte_size(); v.push(value); @@ -242,7 +271,7 @@ impl + TryFrom + Default> Value for Li pub trait Value: Sized + std::fmt::Debug { fn write(&self, w: &mut W) -> io::Result<()>; - fn read(r: &mut R) -> crate::Result; + fn read(r: &mut FrameReader) -> crate::Result; fn byte_size(&self) -> usize; } @@ -250,7 +279,7 @@ 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 { + fn read(r: &mut FrameReader) -> crate::Result { // ugly :( let mut values = Vec::with_capacity(N); for _ in 0..N { @@ -268,7 +297,7 @@ impl Value for u8 { fn write(&self, w: &mut W) -> io::Result<()> { w.write_u8(*self) } - fn read(r: &mut R) -> crate::Result { + fn read(r: &mut FrameReader) -> crate::Result { r.read_u8().map_err(Into::into) } fn byte_size(&self) -> usize { @@ -280,7 +309,7 @@ impl Value for u16 { fn write(&self, w: &mut W) -> io::Result<()> { w.write_u16::(*self) } - fn read(r: &mut R) -> crate::Result { + fn read(r: &mut FrameReader) -> crate::Result { r.read_u16::().map_err(Into::into) } fn byte_size(&self) -> usize { @@ -293,7 +322,7 @@ impl Value for u32 { w.write_u32::(*self) } - fn read(r: &mut R) -> crate::Result { + fn read(r: &mut FrameReader) -> crate::Result { r.read_u32::().map_err(Into::into) } @@ -309,7 +338,7 @@ impl Value for (T, U) { Ok(()) } - fn read(r: &mut R) -> crate::Result { + fn read(r: &mut FrameReader) -> crate::Result { Ok((T::read(r)?, U::read(r)?)) } @@ -327,7 +356,7 @@ impl Value for u24 { w.write_u24::(self.0) } - fn read(r: &mut R) -> crate::Result { + fn read(r: &mut FrameReader) -> crate::Result { r.read_u24::().map_err(Into::into).map(u24) } diff --git a/src/proto/tests.rs b/src/proto/tests.rs index 8783359..61bd3c6 100644 --- a/src/proto/tests.rs +++ b/src/proto/tests.rs @@ -18,7 +18,7 @@ fn parse_hello_retry_request() { TLSPlaintext::Handshake { handshake: Handshake::ServerHello { legacy_version: LEGACY_TLSV12, - random: HELLO_RETRY_REQUEST, + random: ServerHelloRandom(HELLO_RETRY_REQUEST), legacy_session_id_echo: b"\xdd\x0f\x25\x0a\xf0\xa6\xd9\xb0\x1c\x28\x2f\x55\xcb\xab\x07\x94\ \x2e\xb3\x98\x96\x32\x81\xad\x8d\x24\x72\x52\x2a\x45\x26\x10\xa2" @@ -31,7 +31,7 @@ fn parse_hello_retry_request() { selected_version: TLSV13 }, ExtensionSH::KeyShare { - group: NamedGroup::X25519 + key_share: ServerHelloKeyshare::HelloRetryRequest(NamedGroup::X25519) } ] .into() @@ -39,3 +39,51 @@ fn parse_hello_retry_request() { } ); } + +#[test] +fn parse_server_hello() { + let mut bytes: &[u8] = b"\x16\x03\x03\x00\x7a\x02\x00\x00\x76\x03\x03\x15\x2a\x7b\x01\xaa\ + \x65\xde\x1f\xe0\x87\x52\x73\xd6\x7d\xd4\x8c\xc8\xf1\x9d\x55\x09\ + \x4c\xbd\xa2\xb0\xc9\x77\xa2\x4b\x81\xed\x63\x20\x05\xc1\x7d\x07\ + \x34\x68\xaf\xd5\xfc\x7f\x1c\x0c\x07\xd7\x14\x9e\x2b\x66\x87\x44\ + \x02\xbb\xf7\xb7\x1d\x6a\x29\xaf\x93\xaf\xe2\x02\x13\x01\x00\x00\ + \x2e\x00\x33\x00\x24\x00\x1d\x00\x20\x8e\xbc\x32\x53\xd7\x4d\xf9\ + \x4a\xb8\x04\x03\xda\xfe\xbf\xf5\xab\x6f\x8f\x65\x2a\x1d\x70\xde\ + \xe7\xaf\x93\x82\x59\x70\xac\x75\x4d\x00\x2b\x00\x02\x03\x04"; + + let handshake = TLSPlaintext::read(&mut bytes).unwrap(); + + assert_eq!( + handshake, + TLSPlaintext::Handshake { + handshake: Handshake::ServerHello { + legacy_version: LEGACY_TLSV12, + random: ServerHelloRandom( + *b"\x15\x2a\x7b\x01\xaa\x65\xde\x1f\xe0\x87\x52\x73\xd6\x7d\xd4\x8c\ + \xc8\xf1\x9d\x55\x09\x4c\xbd\xa2\xb0\xc9\x77\xa2\x4b\x81\xed\x63" + ), + legacy_session_id_echo: + b"\x05\xc1\x7d\x07\x34\x68\xaf\xd5\xfc\x7f\x1c\x0c\x07\xd7\x14\x9e\ + \x2b\x66\x87\x44\x02\xbb\xf7\xb7\x1d\x6a\x29\xaf\x93\xaf\xe2\x02" + .to_vec() + .into(), + cipher_suite: CipherSuite::TlsAes128GcmSha256, + legacy_compression_method: 0, + extensions: vec![ + ExtensionSH::KeyShare { + key_share: ServerHelloKeyshare::ServerHello(KeyShareEntry::X25519 { + len: 32, + key_exchange: + *b"\x8e\xbc\x32\x53\xd7\x4d\xf9\x4a\xb8\x04\x03\xda\xfe\xbf\xf5\xab\ + \x6f\x8f\x65\x2a\x1d\x70\xde\xe7\xaf\x93\x82\x59\x70\xac\x75\x4d" + }) + }, + ExtensionSH::SupportedVersions { + selected_version: TLSV13 + }, + ] + .into() + } + } + ); +}