getting ServerHello from google

This commit is contained in:
nora 2023-09-24 21:37:08 +02:00
parent 22893824c8
commit df9e3ea17d
5 changed files with 156 additions and 25 deletions

View file

@ -49,16 +49,16 @@ impl ClientSetupConnection {
groups: vec![proto::NamedGroup::X25519].into(), groups: vec![proto::NamedGroup::X25519].into(),
}, },
// passing this doesnt work and shows up as TLSv1.2 in wireshark and gives a handshake error // 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 { entries: vec![proto::KeyShareEntry::X25519 {
len: public.as_bytes().len().try_into().unwrap(), len: public.as_bytes().len().try_into().unwrap(),
key_exchange: *public.as_bytes(), key_exchange: *public.as_bytes(),
}] }]
.into(), .into(),
},*/ },
/*proto::ExtensionCH::SignatureAlgorithms { proto::ExtensionCH::SignatureAlgorithms {
supported_signature_algorithms: vec![proto::SignatureScheme::ED25519].into(), supported_signature_algorithms: vec![proto::SignatureScheme::ED25519].into(),
},*/ },
proto::ExtensionCH::SupportedVersions { proto::ExtensionCH::SupportedVersions {
versions: vec![proto::TLSV13].into(), versions: vec![proto::TLSV13].into(),
}, },
@ -97,6 +97,7 @@ pub enum ErrorKind {
impl From<io::Error> for Error { impl From<io::Error> for Error {
fn from(value: io::Error) -> Self { fn from(value: io::Error) -> Self {
panic!("error: {value}");
Self { Self {
kind: ErrorKind::Io(value), kind: ErrorKind::Io(value),
} }
@ -105,6 +106,7 @@ impl From<io::Error> for Error {
impl From<ErrorKind> for Error { impl From<ErrorKind> for Error {
fn from(value: ErrorKind) -> Self { fn from(value: ErrorKind) -> Self {
panic!("error:{value:?}");
Self { kind: value } Self { kind: value }
} }
} }

View file

@ -1,4 +1,4 @@
// An example program that makes a shitty HTTP/1.1 request. // An example program that makes a shitty HTTP/1.1 request.
fn main() { fn main() {
tls::ClientConnection::establish("nilstrieb.dev", 443).unwrap(); tls::ClientConnection::establish("google.com", 443).unwrap();
} }

View file

@ -7,7 +7,7 @@ use std::{
use crate::ErrorKind; 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)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum TLSPlaintext { pub enum TLSPlaintext {
@ -58,6 +58,7 @@ impl TLSPlaintext {
} }
pub fn read(r: &mut impl Read) -> crate::Result<Self> { pub fn read(r: &mut impl Read) -> crate::Result<Self> {
let r = &mut FrameReader::new(r);
let discr = u8::read(r)?; let discr = u8::read(r)?;
let _legacy_version = ProtocolVersion::read(r)?; let _legacy_version = ProtocolVersion::read(r)?;
let _len = u16::read(r)?; let _len = u16::read(r)?;
@ -101,7 +102,7 @@ proto_enum! {
} = 1, } = 1,
ServerHello { ServerHello {
legacy_version: ProtocolVersion, legacy_version: ProtocolVersion,
random: Random, random: ServerHelloRandom,
legacy_session_id_echo: LegacySessionId, legacy_session_id_echo: LegacySessionId,
cipher_suite: CipherSuite, cipher_suite: CipherSuite,
legacy_compression_method: u8, legacy_compression_method: u8,
@ -190,7 +191,7 @@ proto_enum! {
} = 43, } = 43,
Cookie { todo: Todo, } = 44, Cookie { todo: Todo, } = 44,
KeyShare { KeyShare {
group: NamedGroup, key_share: ServerHelloKeyshare,
} = 51, } = 51,
} }
} }
@ -329,9 +330,60 @@ proto_enum! {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ServerHelloRandom(Random);
impl Value for ServerHelloRandom {
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
self.0.write(w)
}
fn read<R: Read>(r: &mut FrameReader<R>) -> crate::Result<Self> {
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<W: Write>(&self, w: &mut W) -> io::Result<()> {
match self {
Self::HelloRetryRequest(group) => group.write(w),
Self::ServerHello(entry) => entry.write(w),
}
}
fn read<R: Read>(r: &mut FrameReader<R>) -> crate::Result<Self> {
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 { impl Handshake {
pub fn is_hello_retry_request(&self) -> bool { 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)
} }
} }

View file

@ -4,8 +4,34 @@ use std::{
io::{self, Read, Write}, io::{self, Read, Write},
marker::PhantomData, marker::PhantomData,
num::TryFromIntError, num::TryFromIntError,
ops::{Deref, DerefMut},
}; };
pub struct FrameReader<R> {
read: R,
pub is_hello_retry_request: bool,
}
impl<R> FrameReader<R> {
pub fn new(read: R) -> Self {
FrameReader { read, is_hello_retry_request: false }
}
}
impl<R> Deref for FrameReader<R> {
type Target = R;
fn deref(&self) -> &Self::Target {
&self.read
}
}
impl<R> DerefMut for FrameReader<R> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.read
}
}
/// ```ignore /// ```ignore
/// proto_struct! {} /// proto_struct! {}
/// ``` /// ```
@ -31,7 +57,7 @@ macro_rules! proto_struct {
Ok(()) Ok(())
} }
fn read<R: Read>(r: &mut R) -> crate::Result<Self> { fn read<R: Read>(r: &mut $crate::proto::ser_de::FrameReader<R>) -> crate::Result<Self> {
let ( $( $field_name ),* ) = ($( { crate::proto::ser_de::discard!($field_name); crate::proto::ser_de::Value::read(r)? } ),*); let ( $( $field_name ),* ) = ($( { crate::proto::ser_de::discard!($field_name); crate::proto::ser_de::Value::read(r)? } ),*);
Ok(Self { Ok(Self {
@ -120,7 +146,7 @@ macro_rules! proto_enum {
} }
} }
fn read<R: Read>(r: &mut R) -> crate::Result<Self> { fn read<R: Read>(r: &mut $crate::proto::ser_de::FrameReader<R>) -> crate::Result<Self> {
mod discr_consts { mod discr_consts {
#[allow(unused_imports)] #[allow(unused_imports)]
use super::*; use super::*;
@ -140,8 +166,9 @@ macro_rules! proto_enum {
match kind { match kind {
$( $(
discr_consts::$KindName => { 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 $({ Ok(Self::$KindName $({
$( $(
@ -186,7 +213,7 @@ impl Value for Todo {
todo!() todo!()
} }
fn read<R: Read>(_: &mut R) -> crate::Result<Self> { fn read<R: Read>(_: &mut FrameReader<R>) -> crate::Result<Self> {
todo!() todo!()
} }
@ -211,11 +238,13 @@ impl<T: Debug, Len> Debug for List<T, Len> {
} }
impl<T: Value, Len: Value + Into<usize> + TryFrom<usize> + Default> Value for List<T, Len> { impl<T: Value, Len: Value + Into<usize> + TryFrom<usize> + Default> Value for List<T, Len> {
fn read<R: Read>(r: &mut R) -> crate::Result<Self> { fn read<R: Read>(r: &mut FrameReader<R>) -> crate::Result<Self> {
eprintln!("reading a list");
let mut remaining_byte_size = Len::read(r)?.into(); let mut remaining_byte_size = Len::read(r)?.into();
let mut v = Vec::new(); let mut v = Vec::new();
eprintln!("list.. remaining bytes {remaining_byte_size}");
while remaining_byte_size > 0 { while remaining_byte_size > 0 {
eprintln!("things {remaining_byte_size} {v:?}");
let value = T::read(r)?; let value = T::read(r)?;
remaining_byte_size -= value.byte_size(); remaining_byte_size -= value.byte_size();
v.push(value); v.push(value);
@ -242,7 +271,7 @@ impl<T: Value, Len: Value + Into<usize> + TryFrom<usize> + Default> Value for Li
pub trait Value: Sized + std::fmt::Debug { pub trait Value: Sized + std::fmt::Debug {
fn write<W: Write>(&self, w: &mut W) -> io::Result<()>; fn write<W: Write>(&self, w: &mut W) -> io::Result<()>;
fn read<R: Read>(r: &mut R) -> crate::Result<Self>; fn read<R: Read>(r: &mut FrameReader<R>) -> crate::Result<Self>;
fn byte_size(&self) -> usize; fn byte_size(&self) -> usize;
} }
@ -250,7 +279,7 @@ impl<V: Value, const N: usize> Value for [V; N] {
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> { fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
self.iter().try_for_each(|v| Value::write(v, w)) self.iter().try_for_each(|v| Value::write(v, w))
} }
fn read<R: Read>(r: &mut R) -> crate::Result<Self> { fn read<R: Read>(r: &mut FrameReader<R>) -> crate::Result<Self> {
// ugly :( // ugly :(
let mut values = Vec::with_capacity(N); let mut values = Vec::with_capacity(N);
for _ in 0..N { for _ in 0..N {
@ -268,7 +297,7 @@ impl Value for u8 {
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> { fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
w.write_u8(*self) w.write_u8(*self)
} }
fn read<R: Read>(r: &mut R) -> crate::Result<Self> { fn read<R: Read>(r: &mut FrameReader<R>) -> crate::Result<Self> {
r.read_u8().map_err(Into::into) r.read_u8().map_err(Into::into)
} }
fn byte_size(&self) -> usize { fn byte_size(&self) -> usize {
@ -280,7 +309,7 @@ impl Value for u16 {
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> { fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
w.write_u16::<B>(*self) w.write_u16::<B>(*self)
} }
fn read<R: Read>(r: &mut R) -> crate::Result<Self> { fn read<R: Read>(r: &mut FrameReader<R>) -> crate::Result<Self> {
r.read_u16::<B>().map_err(Into::into) r.read_u16::<B>().map_err(Into::into)
} }
fn byte_size(&self) -> usize { fn byte_size(&self) -> usize {
@ -293,7 +322,7 @@ impl Value for u32 {
w.write_u32::<B>(*self) w.write_u32::<B>(*self)
} }
fn read<R: Read>(r: &mut R) -> crate::Result<Self> { fn read<R: Read>(r: &mut FrameReader<R>) -> crate::Result<Self> {
r.read_u32::<B>().map_err(Into::into) r.read_u32::<B>().map_err(Into::into)
} }
@ -309,7 +338,7 @@ impl<T: Value, U: Value> Value for (T, U) {
Ok(()) Ok(())
} }
fn read<R: Read>(r: &mut R) -> crate::Result<Self> { fn read<R: Read>(r: &mut FrameReader<R>) -> crate::Result<Self> {
Ok((T::read(r)?, U::read(r)?)) Ok((T::read(r)?, U::read(r)?))
} }
@ -327,7 +356,7 @@ impl Value for u24 {
w.write_u24::<B>(self.0) w.write_u24::<B>(self.0)
} }
fn read<R: Read>(r: &mut R) -> crate::Result<Self> { fn read<R: Read>(r: &mut FrameReader<R>) -> crate::Result<Self> {
r.read_u24::<B>().map_err(Into::into).map(u24) r.read_u24::<B>().map_err(Into::into).map(u24)
} }

View file

@ -18,7 +18,7 @@ fn parse_hello_retry_request() {
TLSPlaintext::Handshake { TLSPlaintext::Handshake {
handshake: Handshake::ServerHello { handshake: Handshake::ServerHello {
legacy_version: LEGACY_TLSV12, legacy_version: LEGACY_TLSV12,
random: HELLO_RETRY_REQUEST, random: ServerHelloRandom(HELLO_RETRY_REQUEST),
legacy_session_id_echo: legacy_session_id_echo:
b"\xdd\x0f\x25\x0a\xf0\xa6\xd9\xb0\x1c\x28\x2f\x55\xcb\xab\x07\x94\ 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" \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 selected_version: TLSV13
}, },
ExtensionSH::KeyShare { ExtensionSH::KeyShare {
group: NamedGroup::X25519 key_share: ServerHelloKeyshare::HelloRetryRequest(NamedGroup::X25519)
} }
] ]
.into() .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()
}
}
);
}