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(),
},
// 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<io::Error> for Error {
fn from(value: io::Error) -> Self {
panic!("error: {value}");
Self {
kind: ErrorKind::Io(value),
}
@ -105,6 +106,7 @@ impl From<io::Error> for Error {
impl From<ErrorKind> for Error {
fn from(value: ErrorKind) -> Self {
panic!("error:{value:?}");
Self { kind: value }
}
}

View file

@ -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();
}

View file

@ -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<Self> {
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<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 {
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},
marker::PhantomData,
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
/// proto_struct! {}
/// ```
@ -31,7 +57,7 @@ macro_rules! proto_struct {
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)? } ),*);
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 {
#[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<R: Read>(_: &mut R) -> crate::Result<Self> {
fn read<R: Read>(_: &mut FrameReader<R>) -> crate::Result<Self> {
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> {
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 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<T: Value, Len: Value + Into<usize> + TryFrom<usize> + Default> Value for Li
pub trait Value: Sized + std::fmt::Debug {
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;
}
@ -250,7 +279,7 @@ impl<V: Value, const N: usize> Value for [V; N] {
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
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 :(
let mut values = Vec::with_capacity(N);
for _ in 0..N {
@ -268,7 +297,7 @@ impl Value for u8 {
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
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)
}
fn byte_size(&self) -> usize {
@ -280,7 +309,7 @@ impl Value for u16 {
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
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)
}
fn byte_size(&self) -> usize {
@ -293,7 +322,7 @@ impl Value for u32 {
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)
}
@ -309,7 +338,7 @@ impl<T: Value, U: Value> Value for (T, U) {
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)?))
}
@ -327,7 +356,7 @@ impl Value for u24 {
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)
}

View file

@ -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()
}
}
);
}