mirror of
https://github.com/Noratrieb/tls.git
synced 2026-01-15 17:15:04 +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::{
|
use std::{
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
io::{self},
|
io::{self, BufWriter, Read, Write},
|
||||||
net::{TcpStream, ToSocketAddrs},
|
net::{TcpStream, ToSocketAddrs},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::proto::CipherSuite;
|
|
||||||
|
|
||||||
type Result<T, E = Error> = std::result::Result<T, E>;
|
type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
|
|
||||||
pub struct ClientConnection {}
|
pub struct ClientConnection {}
|
||||||
|
|
@ -24,19 +22,26 @@ struct ClientSetupConnection {}
|
||||||
|
|
||||||
impl ClientSetupConnection {
|
impl ClientSetupConnection {
|
||||||
fn establish(host: impl ToSocketAddrs) -> Result<Self> {
|
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 {
|
let handshake = proto::Handshake::ClientHello {
|
||||||
legacy_version: proto::LEGACY_VERSION,
|
legacy_version: proto::LEGACY_VERSION,
|
||||||
random: rand::random(),
|
random: rand::random(),
|
||||||
legacy_session_id: 0,
|
legacy_session_id: 0,
|
||||||
cipher_suites: vec![CipherSuite::TlsAes128GcmSha256].into(),
|
cipher_suites: vec![proto::CipherSuite::TlsAes128GcmSha256].into(),
|
||||||
legacy_compressions_methods: 0,
|
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)?;
|
// let res: proto::TLSPlaintext = proto::Value::read(&mut stream.get_mut())?;
|
||||||
dbg!(res);
|
// dbg!(res);
|
||||||
|
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
@ -66,3 +71,30 @@ impl From<ErrorKind> for Error {
|
||||||
Self { kind: value }
|
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;
|
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
|
// https://datatracker.ietf.org/doc/html/rfc8446#section-4
|
||||||
proto_enum! {
|
proto_enum! {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Handshake: u8 {
|
pub enum Handshake: u8 {
|
||||||
// https://datatracker.ietf.org/doc/html/rfc8446#section-4.1.2
|
// https://datatracker.ietf.org/doc/html/rfc8446#section-4.1.2
|
||||||
ClientHello {
|
ClientHello {
|
||||||
legacy_version: u16,
|
legacy_version: ProtocolVersion,
|
||||||
random: [u8; 32],
|
random: Random,
|
||||||
legacy_session_id: u8,
|
legacy_session_id: u8,
|
||||||
cipher_suites: List<CipherSuite, u16>,
|
cipher_suites: List<CipherSuite, u16>,
|
||||||
legacy_compressions_methods: u8,
|
legacy_compressions_methods: u8,
|
||||||
extensions: List<Extension, u16>,
|
extensions: List<ExtensionCH, u16>,
|
||||||
} = 1,
|
} = 1,
|
||||||
ServerHello {
|
ServerHello {
|
||||||
legacy_version: u16,
|
legacy_version: ProtocolVersion,
|
||||||
random: [u8; 32],
|
random: Random,
|
||||||
legacy_session_id_echo: u8,
|
legacy_session_id_echo: u8,
|
||||||
cipher_suite: CipherSuite,
|
cipher_suite: CipherSuite,
|
||||||
legacy_compression_method: u8,
|
legacy_compression_method: u8,
|
||||||
extensions: List<Extension, u16>,
|
extensions: List<ExtensionSH, u16>,
|
||||||
} = 2,
|
} = 2,
|
||||||
NewSessionTicket {} = 4,
|
NewSessionTicket {} = 4,
|
||||||
EndOfEarlyData {} = 5,
|
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! {
|
proto_enum! {
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -54,11 +94,9 @@ proto_enum! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Extension = (ExtensionType, List<u8, u16>);
|
|
||||||
|
|
||||||
proto_enum! {
|
proto_enum! {
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ExtensionType: u16 {
|
pub enum ExtensionCH: u16 {
|
||||||
ServerName = 0,
|
ServerName = 0,
|
||||||
MaxFragmentLength = 1,
|
MaxFragmentLength = 1,
|
||||||
StatusRequest = 5,
|
StatusRequest = 5,
|
||||||
|
|
@ -73,19 +111,67 @@ proto_enum! {
|
||||||
Padding = 21,
|
Padding = 21,
|
||||||
PreSharedKey = 41,
|
PreSharedKey = 41,
|
||||||
EarlyData = 42,
|
EarlyData = 42,
|
||||||
SupportedVersions = 43,
|
SupportedVersions {
|
||||||
|
versions: List<ProtocolVersion, u8>,
|
||||||
|
} = 43,
|
||||||
Cookie = 44,
|
Cookie = 44,
|
||||||
PskKeyExchangeModes = 45,
|
PskKeyExchangeModes = 45,
|
||||||
CertificateAuthorities = 47,
|
CertificateAuthorities = 47,
|
||||||
OidFilters = 48,
|
|
||||||
PostHandshakeAuth = 49,
|
PostHandshakeAuth = 49,
|
||||||
SignatureAlgorithmsCert = 50,
|
SignatureAlgorithmsCert = 50,
|
||||||
KeyShare = 51,
|
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 {
|
macro_rules! proto_enum {
|
||||||
($(#[$meta:meta])* pub enum $name:ident: $discr_ty:ty {
|
{$(#[$meta:meta])* pub enum $name:ident: $discr_ty:ty {
|
||||||
$(
|
$(
|
||||||
$KindName:ident $({
|
$KindName:ident $({
|
||||||
$(
|
$(
|
||||||
|
|
@ -93,7 +179,7 @@ macro_rules! proto_enum {
|
||||||
)*
|
)*
|
||||||
})? = $discriminant:expr,
|
})? = $discriminant:expr,
|
||||||
)*
|
)*
|
||||||
}) => {
|
}} => {
|
||||||
$(#[$meta])*
|
$(#[$meta])*
|
||||||
pub enum $name {
|
pub enum $name {
|
||||||
$(
|
$(
|
||||||
|
|
@ -141,6 +227,7 @@ macro_rules! proto_enum {
|
||||||
match kind {
|
match kind {
|
||||||
$(
|
$(
|
||||||
discr_consts::$KindName => {
|
discr_consts::$KindName => {
|
||||||
|
#[allow(unused_parens)]
|
||||||
$(let ( $( $field_name ),* ) = ($( { discard!($field_name); Value::read(r)? } ),*);)?
|
$(let ( $( $field_name ),* ) = ($( { discard!($field_name); Value::read(r)? } ),*);)?
|
||||||
|
|
||||||
Ok(Self::$KindName $({
|
Ok(Self::$KindName $({
|
||||||
|
|
@ -154,6 +241,26 @@ macro_rules! proto_enum {
|
||||||
_ => Err(ErrorKind::InvalidHandshake(Box::new(kind)).into()),
|
_ => 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> {
|
fn read<R: Read>(r: &mut R) -> crate::Result<Self> {
|
||||||
let len: usize = Len::read(r)?.into();
|
let mut remaining_byte_size = Len::read(r)?.into();
|
||||||
let mut v = Vec::with_capacity(len.max(1000));
|
let mut v = Vec::new();
|
||||||
for _ in 0..len {
|
|
||||||
v.push(T::read(r)?);
|
while remaining_byte_size > 0 {
|
||||||
|
let value = T::read(r)?;
|
||||||
|
remaining_byte_size -= value.byte_size();
|
||||||
|
v.push(value);
|
||||||
}
|
}
|
||||||
Ok(Self(v, PhantomData))
|
Ok(Self(v, PhantomData))
|
||||||
}
|
}
|
||||||
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
|
let byte_size = self.0.iter().map(Value::byte_size).sum::<usize>();
|
||||||
Len::write(
|
Len::write(
|
||||||
&self
|
&byte_size
|
||||||
.0
|
|
||||||
.len()
|
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap_or_else(|_| panic!("list is too large for domain: {}", self.0.len())),
|
.unwrap_or_else(|_| panic!("list is too large for domain: {}", self.0.len())),
|
||||||
w,
|
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 {
|
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 R) -> crate::Result<Self>;
|
||||||
|
fn byte_size(&self) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Value, const N: usize> Value for [V; N] {
|
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())
|
Ok(values.try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
fn byte_size(&self) -> usize {
|
||||||
|
self.iter().map(Value::byte_size).sum()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value for u8 {
|
impl Value for u8 {
|
||||||
|
|
@ -230,6 +342,9 @@ impl Value for u8 {
|
||||||
fn read<R: Read>(r: &mut R) -> crate::Result<Self> {
|
fn read<R: Read>(r: &mut R) -> crate::Result<Self> {
|
||||||
r.read_u8().map_err(Into::into)
|
r.read_u8().map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
fn byte_size(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value for u16 {
|
impl Value for u16 {
|
||||||
|
|
@ -239,6 +354,9 @@ impl Value for u16 {
|
||||||
fn read<R: Read>(r: &mut R) -> crate::Result<Self> {
|
fn read<R: Read>(r: &mut 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 {
|
||||||
|
2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Value, U: Value> Value for (T, U) {
|
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> {
|
fn read<R: Read>(r: &mut R) -> crate::Result<Self> {
|
||||||
Ok((T::read(r)?, U::read(r)?))
|
Ok((T::read(r)?, U::read(r)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn byte_size(&self) -> usize {
|
||||||
|
self.0.byte_size() + self.1.byte_size()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! discard {
|
macro_rules! discard {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue