mirror of
https://github.com/Noratrieb/tls.git
synced 2026-01-16 09:35:02 +01:00
record
This commit is contained in:
parent
93053aa6eb
commit
c71fc68d8e
2 changed files with 195 additions and 41 deletions
50
src/lib.rs
50
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<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
pub struct ClientConnection {}
|
||||
|
|
@ -24,19 +22,26 @@ struct ClientSetupConnection {}
|
|||
|
||||
impl ClientSetupConnection {
|
||||
fn establish(host: impl ToSocketAddrs) -> Result<Self> {
|
||||
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<ErrorKind> for Error {
|
|||
Self { kind: value }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LoggingWriter<W>(W);
|
||||
|
||||
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]);
|
||||
}
|
||||
len
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
len
|
||||
}
|
||||
}
|
||||
|
|
|
|||
186
src/proto.rs
186
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<u8, u16>,
|
||||
},
|
||||
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<CipherSuite, u16>,
|
||||
legacy_compressions_methods: u8,
|
||||
extensions: List<Extension, u16>,
|
||||
extensions: List<ExtensionCH, u16>,
|
||||
} = 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<Extension, u16>,
|
||||
extensions: List<ExtensionSH, u16>,
|
||||
} = 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<u8, u16>);
|
||||
|
||||
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<ProtocolVersion, u8>,
|
||||
} = 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<W: Write>(&self, mut w: &mut W) -> io::Result<()> {
|
||||
$(
|
||||
Value::write(&self.$field_name, &mut w)?;
|
||||
)*
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read<R: Read>(r: &mut R) -> crate::Result<Self> {
|
||||
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<T: Debug, Len> Debug for List<T, Len> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Value, Len: Value + Into<usize> + TryFrom<usize>> 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> {
|
||||
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<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
let byte_size = self.0.iter().map(Value::byte_size).sum::<usize>();
|
||||
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::<usize>()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_handshake<W: Write>(w: &mut W, handshake: Handshake) -> io::Result<()> {
|
||||
handshake.write(w)
|
||||
}
|
||||
|
||||
pub fn read_handshake<R: Read>(r: &mut R) -> crate::Result<Handshake> {
|
||||
Handshake::read(r)
|
||||
}
|
||||
|
||||
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 byte_size(&self) -> usize;
|
||||
}
|
||||
|
||||
impl<V: Value, const N: usize> Value for [V; N] {
|
||||
|
|
@ -221,6 +330,9 @@ impl<V: Value, const N: usize> 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: Read>(r: &mut R) -> crate::Result<Self> {
|
||||
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: Read>(r: &mut R) -> crate::Result<Self> {
|
||||
r.read_u16::<B>().map_err(Into::into)
|
||||
}
|
||||
fn byte_size(&self) -> usize {
|
||||
2
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Value, U: Value> Value for (T, U) {
|
||||
|
|
@ -251,6 +369,10 @@ impl<T: Value, U: Value> Value for (T, U) {
|
|||
fn read<R: Read>(r: &mut R) -> crate::Result<Self> {
|
||||
Ok((T::read(r)?, U::read(r)?))
|
||||
}
|
||||
|
||||
fn byte_size(&self) -> usize {
|
||||
self.0.byte_size() + self.1.byte_size()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! discard {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue