This commit is contained in:
nora 2023-09-09 18:00:34 +02:00
parent 29057b8e92
commit 4b1d2805e6
3 changed files with 130 additions and 31 deletions

View file

@ -11,8 +11,8 @@ type Result<T, E = Error> = std::result::Result<T, E>;
pub struct ClientConnection {}
impl ClientConnection {
pub fn establish(host: impl ToSocketAddrs) -> Result<Self> {
let _setup = ClientSetupConnection::establish(host)?;
pub fn establish(host: &str, port: u16) -> Result<Self> {
let _setup = ClientSetupConnection::establish(host, port)?;
todo!()
}
@ -21,22 +21,28 @@ impl ClientConnection {
struct ClientSetupConnection {}
impl ClientSetupConnection {
fn establish(host: impl ToSocketAddrs) -> Result<Self> {
let mut stream = BufWriter::new(LoggingWriter(TcpStream::connect(host)?));
fn establish(host: &str, port: u16) -> Result<Self> {
let mut stream = BufWriter::new(LoggingWriter(TcpStream::connect((host, port))?));
let handshake = proto::Handshake::ClientHello {
legacy_version: proto::LEGACY_VERSION,
legacy_version: proto::LEGACY_TLSV12,
random: rand::random(),
legacy_session_id: 0,
legacy_session_id: [(); 32].map(|()| rand::random()).to_vec().into(),
cipher_suites: vec![proto::CipherSuite::TlsAes128GcmSha256].into(),
legacy_compressions_methods: 0,
extensions: vec![proto::ExtensionCH::SupportedVersions {
versions: vec![proto::TLSV3].into(),
}]
legacy_compressions_methods: vec![0].into(),
extensions: vec![
proto::ExtensionCH::ServerName {
server_name: vec![proto::ServerName::HostName {
host_name: host.as_bytes().to_vec().into(),
}]
.into(),
},
proto::ExtensionCH::SupportedVersions {
versions: vec![proto::TLSV13].into(),
},
]
.into(),
};
let plaintext = proto::TLSPlaintext::Handshake {
handshake,
};
let plaintext = proto::TLSPlaintext::Handshake { handshake };
plaintext.write(&mut stream)?;
stream.flush()?;
@ -82,7 +88,7 @@ impl<W: io::Write> io::Write for LoggingWriter<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let len = self.0.write(buf);
if let Ok(len) = len {
eprintln!("wrote bytes: {:x?}", &buf[..len]);
eprintln!(" bytes: {:02x?}", &buf[..len]);
}
len
}
@ -96,7 +102,7 @@ impl<R: Read> io::Read for LoggingWriter<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let len = self.0.read(buf);
if let Ok(len) = len {
eprintln!("read bytes: {:x?}", &buf[..len]);
eprintln!("read bytes: {:02x?}", &buf[..len]);
}
len
}

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("nilstrieb.dev", 443).unwrap();
}

View file

@ -2,6 +2,7 @@ use std::{
fmt::Debug,
io::{self, Read, Write},
marker::PhantomData,
num::TryFromIntError,
};
use byteorder::{BigEndian as B, ReadBytesExt, WriteBytesExt};
@ -40,8 +41,16 @@ impl TLSPlaintext {
TLSPlaintext::ChangeCipherSpec => todo!(),
TLSPlaintext::Alert { alert } => todo!(),
TLSPlaintext::Handshake { handshake } => {
Self::HANDSHAKE.write(w)?; // handshake
LEGACY_VERSION.write(w)?;
Self::HANDSHAKE.write(w)?;
// MUST be set to 0x0303 for all records
// generated by a TLS 1.3 implementation other than an initial
// ClientHello (i.e., one not generated after a HelloRetryRequest),
// where it MAY also be 0x0301 for compatibility purposes.
if matches!(handshake, Handshake::ClientHello { .. }) {
LEGACY_TLSV10.write(w)?;
} else {
LEGACY_TLSV12.write(w)?;
}
let len: u16 = handshake.byte_size().try_into().unwrap();
len.write(w)?;
handshake.write(w)?;
@ -80,14 +89,14 @@ pub type Random = [u8; 32];
// https://datatracker.ietf.org/doc/html/rfc8446#section-4
proto_enum! {
#[derive(Debug, Clone)]
pub enum Handshake: u8 {
pub enum Handshake: u8, (length: u24) {
// https://datatracker.ietf.org/doc/html/rfc8446#section-4.1.2
ClientHello {
legacy_version: ProtocolVersion,
random: Random,
legacy_session_id: u8,
legacy_session_id: List<u8, u8>,
cipher_suites: List<CipherSuite, u16>,
legacy_compressions_methods: u8,
legacy_compressions_methods: List<u8, u8>,
extensions: List<ExtensionCH, u16>,
} = 1,
ServerHello {
@ -110,8 +119,9 @@ proto_enum! {
}
}
pub const LEGACY_VERSION: ProtocolVersion = 0x0303; // TLS v1.2
pub const TLSV3: ProtocolVersion = 0x0304;
pub const LEGACY_TLSV10: ProtocolVersion = 0x0301;
pub const LEGACY_TLSV12: ProtocolVersion = 0x0303;
pub const TLSV13: ProtocolVersion = 0x0304;
proto_enum! {
#[derive(Debug, Clone, Copy)]
@ -126,8 +136,10 @@ proto_enum! {
proto_enum! {
#[derive(Debug, Clone)]
pub enum ExtensionCH: u16 {
ServerName = 0,
pub enum ExtensionCH: u16, (length: u16) {
ServerName {
server_name: ServerNameList,
} = 0,
MaxFragmentLength = 1,
StatusRequest = 5,
SupportedGroups = 10,
@ -164,6 +176,18 @@ proto_enum! {
}
}
proto_enum! {
#[derive(Debug, Clone)]
pub enum ServerName: u8 {
HostName {
host_name: HostName,
} = 0,
}
}
type HostName = List<u8, u16>;
type ServerNameList = List<ServerName, u16>;
proto_struct! {
#[derive(Debug, Clone, Copy)]
pub struct Alert {
@ -254,7 +278,7 @@ macro_rules! proto_struct {
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 $( ,(length: $len_ty:ty) )? {
$(
$KindName:ident $({
$(
@ -275,7 +299,9 @@ macro_rules! proto_enum {
}
impl Value for $name {
fn write<W: Write>(&self, mut w: &mut W) -> io::Result<()> {
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
w.flush()?;
eprintln!("{}", stringify!($name));
mod discr_consts {
$(
#[allow(non_upper_case_globals)]
@ -283,15 +309,35 @@ macro_rules! proto_enum {
)*
}
let write_len = |_w: &mut W, _len: usize| -> io::Result<()> {
_w.flush()?;
eprintln!("length");
$(
<$len_ty>::try_from(_len).unwrap().write(_w)?;
)?
Ok(())
};
match self {
$(
Self::$KindName $( {
$( $field_name, )*
} )? => {
Value::write(&discr_consts::$KindName, &mut w)?;
let byte_size = $($( $field_name.byte_size() + )*)? 0;
Value::write(&discr_consts::$KindName, w)?;
write_len(w, byte_size)?;
let w = &mut MeasuringWriter(0, w);
$($(
Value::write($field_name, &mut w)?;
w.flush()?;
eprintln!("{}", stringify!($field_name));
Value::write($field_name, w)?;
)*)?
debug_assert_eq!(w.0, byte_size);
Ok(())
}
)*
@ -307,6 +353,11 @@ macro_rules! proto_enum {
}
let kind: $discr_ty = Value::read(r)?;
$(
let _len = <$len_ty>::read(r)?;
)?
match kind {
$(
discr_consts::$KindName => {
@ -333,7 +384,7 @@ macro_rules! proto_enum {
)*
}
match self {
$( <$len_ty>::default().byte_size() + )? match self {
$(
Self::$KindName $( {
$( $field_name, )*
@ -342,7 +393,6 @@ macro_rules! proto_enum {
}
)*
}
}
}
};
@ -458,6 +508,49 @@ impl<T: Value, U: Value> Value for (T, U) {
}
}
#[derive(Debug, Clone, Copy, Default)]
#[allow(non_camel_case_types)]
struct u24(u32);
impl Value for u24 {
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
w.write_u24::<B>(self.0)
}
fn read<R: Read>(r: &mut R) -> crate::Result<Self> {
r.read_u24::<B>().map_err(Into::into).map(u24)
}
fn byte_size(&self) -> usize {
3
}
}
impl TryFrom<usize> for u24 {
type Error = TryFromIntError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
let value = u32::try_from(value)?;
if value > 2_u32.pow(24) {
return Err(u32::try_from(usize::MAX).unwrap_err());
}
Ok(u24(value))
}
}
struct MeasuringWriter<W>(usize, W);
impl<W: Write> Write for MeasuringWriter<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let len = self.1.write(buf)?;
self.0 += len;
Ok(len)
}
fn flush(&mut self) -> io::Result<()> {
self.1.flush()
}
}
macro_rules! discard {
($($tt:tt)*) => {};
}