mirror of
https://github.com/Noratrieb/tls.git
synced 2026-01-14 16:45:02 +01:00
more
This commit is contained in:
parent
5429fb2e7c
commit
6eb89b926c
2 changed files with 169 additions and 51 deletions
13
src/lib.rs
13
src/lib.rs
|
|
@ -1,10 +1,13 @@
|
||||||
pub mod proto;
|
pub mod proto;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
io::{self, Read},
|
fmt::Debug,
|
||||||
|
io::{self},
|
||||||
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 {}
|
||||||
|
|
@ -26,9 +29,9 @@ impl ClientSetupConnection {
|
||||||
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: [0; 2],
|
cipher_suites: vec![CipherSuite::TlsAes128GcmSha256].into(),
|
||||||
legacy_compressions_methods: 0,
|
legacy_compressions_methods: 0,
|
||||||
extensions: 0,
|
extensions: vec![].into(),
|
||||||
};
|
};
|
||||||
proto::write_handshake(&mut stream, handshake)?;
|
proto::write_handshake(&mut stream, handshake)?;
|
||||||
|
|
||||||
|
|
@ -41,12 +44,12 @@ impl ClientSetupConnection {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
kind: ErrorKind,
|
pub kind: ErrorKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ErrorKind {
|
pub enum ErrorKind {
|
||||||
InvalidHandshake(u8),
|
InvalidHandshake(Box<dyn Debug>),
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
207
src/proto.rs
207
src/proto.rs
|
|
@ -1,82 +1,108 @@
|
||||||
use std::io::{self, Read, Write};
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
|
io::{self, Read, Write},
|
||||||
|
marker::PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
use byteorder::{BigEndian as B, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian as B, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use crate::ErrorKind;
|
use crate::ErrorKind;
|
||||||
|
|
||||||
// https://datatracker.ietf.org/doc/html/rfc8446#section-4
|
// https://datatracker.ietf.org/doc/html/rfc8446#section-4
|
||||||
macro_rules! handshake {
|
macro_rules! proto_enum {
|
||||||
($(#[$meta:meta])* pub enum Handshake {
|
($(#[$meta:meta])* pub enum $name:ident: $discr_ty:ty {
|
||||||
$(
|
$(
|
||||||
$KindName:ident {
|
$KindName:ident $({
|
||||||
$(
|
$(
|
||||||
$field_name:ident : $field_ty:ty,
|
$field_name:ident : $field_ty:ty,
|
||||||
)*
|
)*
|
||||||
} = $discriminant:expr,
|
})? = $discriminant:expr,
|
||||||
)*
|
)*
|
||||||
}) => {
|
}) => {
|
||||||
$(#[$meta])*
|
$(#[$meta])*
|
||||||
pub enum Handshake {
|
pub enum $name {
|
||||||
$(
|
$(
|
||||||
$KindName {
|
$KindName $({
|
||||||
$(
|
$(
|
||||||
$field_name: $field_ty,
|
$field_name: $field_ty,
|
||||||
)*
|
)*
|
||||||
},
|
})?,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value for $name {
|
||||||
|
fn write<W: Write>(&self, mut w: W) -> io::Result<()> {
|
||||||
|
mod discr_consts {
|
||||||
|
$(
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub(super) const $KindName: $discr_ty = $discriminant;
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handshake {
|
|
||||||
fn write(self, w: &mut impl Write) -> io::Result<()> {
|
|
||||||
match self {
|
match self {
|
||||||
$(
|
$(
|
||||||
Self::$KindName {
|
Self::$KindName $( {
|
||||||
$( $field_name, )*
|
$( $field_name, )*
|
||||||
} => {
|
} )? => {
|
||||||
$(
|
Value::write(&discr_consts::$KindName, &mut w)?;
|
||||||
Value::write($field_name, &mut *w)?;
|
$($(
|
||||||
)*
|
Value::write($field_name, &mut w)?;
|
||||||
|
)*)?
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read(r: &mut impl Read) -> crate::Result<Self> {
|
fn read<R: Read>(mut r: R) -> crate::Result<Self> {
|
||||||
let kind = r.read_u8()?;
|
mod discr_consts {
|
||||||
|
$(
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub(super) const $KindName: $discr_ty = $discriminant;
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
let kind: $discr_ty = Value::read(&mut r)?;
|
||||||
match kind {
|
match kind {
|
||||||
$(
|
$(
|
||||||
$discriminant => {
|
discr_consts::$KindName => {
|
||||||
let ( $( $field_name ),* ) = ($( { discard!($field_name); Value::read(&mut *r)? } ),*);
|
$(let ( $( $field_name ),* ) = ($( { discard!($field_name); Value::read(&mut r)? } ),*);)?
|
||||||
|
|
||||||
Ok(Self::$KindName {
|
Ok(Self::$KindName $({
|
||||||
$(
|
$(
|
||||||
$field_name,
|
$field_name,
|
||||||
)*
|
)*
|
||||||
})
|
})*)
|
||||||
},
|
},
|
||||||
)*
|
)*
|
||||||
|
|
||||||
_ => Err(ErrorKind::InvalidHandshake(kind).into()),
|
_ => Err(ErrorKind::InvalidHandshake(Box::new(kind)).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
handshake! {
|
proto_enum! {
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Handshake {
|
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: u16,
|
||||||
random: [u8; 32],
|
random: [u8; 32],
|
||||||
legacy_session_id: u8,
|
legacy_session_id: u8,
|
||||||
cipher_suites: [u8; 2],
|
cipher_suites: List<CipherSuite, u16>,
|
||||||
legacy_compressions_methods: u8,
|
legacy_compressions_methods: u8,
|
||||||
extensions: u16,
|
extensions: List<Extension, u16>,
|
||||||
} = 1,
|
} = 1,
|
||||||
ServerHello {} = 2,
|
ServerHello {
|
||||||
|
legacy_version: u16,
|
||||||
|
random: [u8; 32],
|
||||||
|
legacy_session_id_echo: u8,
|
||||||
|
cipher_suite: CipherSuite,
|
||||||
|
legacy_compression_method: u8,
|
||||||
|
extensions: List<Extension, u16>,
|
||||||
|
} = 2,
|
||||||
NewSessionTicket {} = 4,
|
NewSessionTicket {} = 4,
|
||||||
EndOfEarlyData {} = 5,
|
EndOfEarlyData {} = 5,
|
||||||
EncryptedExtensions {} = 8,
|
EncryptedExtensions {} = 8,
|
||||||
|
|
@ -91,6 +117,83 @@ handshake! {
|
||||||
|
|
||||||
pub const LEGACY_VERSION: u16 = 0x0303; // TLS v1.2
|
pub const LEGACY_VERSION: u16 = 0x0303; // TLS v1.2
|
||||||
|
|
||||||
|
proto_enum! {
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum CipherSuite: [u8; 2] {
|
||||||
|
TlsAes128GcmSha256 = [0x13, 0x01],
|
||||||
|
TlsAes256GcmSha384 = [0x13, 0x02],
|
||||||
|
TlsChacha20Poly1305Sha256 = [0x13, 0x03],
|
||||||
|
TlsAes128CcmSha256 = [0x13, 0x04],
|
||||||
|
TlsAes128Ccm8Sha256 = [0x13, 0x05],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Extension = (ExtensionType, List<u8, u16>);
|
||||||
|
|
||||||
|
proto_enum! {
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum ExtensionType: u16 {
|
||||||
|
ServerName = 0,
|
||||||
|
MaxFragmentLength = 1,
|
||||||
|
StatusRequest = 5,
|
||||||
|
SupportedGroups = 10,
|
||||||
|
SignatureAlgorithms = 13,
|
||||||
|
UseSrtp = 14,
|
||||||
|
Heartbeat = 15,
|
||||||
|
ApplicationLayerProtocolNegotiation = 16,
|
||||||
|
SignedCertificateTimestamp = 18,
|
||||||
|
ClientCertificateType = 19,
|
||||||
|
ServerCertificateType = 20,
|
||||||
|
Padding = 21,
|
||||||
|
PreSharedKey = 41,
|
||||||
|
EarlyData = 42,
|
||||||
|
SupportedVersions = 43,
|
||||||
|
Cookie = 44,
|
||||||
|
PskKeyExchangeModes = 45,
|
||||||
|
CertificateAuthorities = 47,
|
||||||
|
OidFilters = 48,
|
||||||
|
PostHandshakeAuth = 49,
|
||||||
|
SignatureAlgorithmsCert = 50,
|
||||||
|
KeyShare = 51,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct List<T, Len>(Vec<T>, PhantomData<Len>);
|
||||||
|
|
||||||
|
impl<T, Len: Value> From<Vec<T>> for List<T, Len> {
|
||||||
|
fn from(value: Vec<T>) -> Self {
|
||||||
|
Self(value, PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Debug, Len> Debug for List<T, Len> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_list().entries(self.0.iter()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Value, Len: Value + Into<usize> + TryFrom<usize>> Value for List<T, Len> {
|
||||||
|
fn read<R: io::Read>(mut r: R) -> crate::Result<Self> {
|
||||||
|
let len: usize = Len::read(&mut r)?.into();
|
||||||
|
let mut v = Vec::with_capacity(len.max(1000));
|
||||||
|
for _ in 0..len {
|
||||||
|
v.push(T::read(&mut r)?);
|
||||||
|
}
|
||||||
|
Ok(Self(v, PhantomData))
|
||||||
|
}
|
||||||
|
fn write<W: io::Write>(&self, w: W) -> io::Result<()> {
|
||||||
|
Len::write(
|
||||||
|
&self
|
||||||
|
.0
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.unwrap_or_else(|_| panic!("list is too large for domain: {}", self.0.len())),
|
||||||
|
w,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write_handshake<W: Write>(w: &mut W, handshake: Handshake) -> io::Result<()> {
|
pub fn write_handshake<W: Write>(w: &mut W, handshake: Handshake) -> io::Result<()> {
|
||||||
handshake.write(w)
|
handshake.write(w)
|
||||||
}
|
}
|
||||||
|
|
@ -99,41 +202,53 @@ pub fn read_handshake<R: Read>(r: &mut R) -> crate::Result<Handshake> {
|
||||||
Handshake::read(r)
|
Handshake::read(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Value: Sized + Copy {
|
pub trait Value: Sized + std::fmt::Debug {
|
||||||
fn write<W: io::Write>(self, w: W) -> io::Result<()>;
|
fn write<W: io::Write>(&self, w: W) -> io::Result<()>;
|
||||||
fn read<R: io::Read>(r: R) -> io::Result<Self>;
|
fn read<R: io::Read>(r: R) -> crate::Result<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Value, const N: usize> Value for [V; N] {
|
impl<V: Value, const N: usize> Value for [V; N] {
|
||||||
fn write<W: io::Write>(self, mut w: W) -> io::Result<()> {
|
fn write<W: io::Write>(&self, mut w: W) -> io::Result<()> {
|
||||||
self.into_iter().map(|v| Value::write(v, &mut w)).collect()
|
self.iter().map(|v| Value::write(v, &mut w)).collect()
|
||||||
}
|
}
|
||||||
fn read<R: io::Read>(mut r: R) -> io::Result<Self> {
|
fn read<R: io::Read>(mut r: R) -> crate::Result<Self> {
|
||||||
// ugly :(
|
// ugly :(
|
||||||
let mut values = [None; N];
|
let mut values = Vec::with_capacity(N);
|
||||||
for i in 0..N {
|
for _ in 0..N {
|
||||||
let value = V::read(&mut r)?;
|
let value = V::read(&mut r)?;
|
||||||
values[i] = Some(value);
|
values.push(value);
|
||||||
}
|
}
|
||||||
Ok(values.map(Option::unwrap))
|
Ok(values.try_into().unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value for u8 {
|
impl Value for u8 {
|
||||||
fn write<W: io::Write>(self, mut w: W) -> io::Result<()> {
|
fn write<W: io::Write>(&self, mut w: W) -> io::Result<()> {
|
||||||
w.write_u8(self)
|
w.write_u8(*self)
|
||||||
}
|
}
|
||||||
fn read<R: io::Read>(mut r: R) -> io::Result<Self> {
|
fn read<R: io::Read>(mut r: R) -> crate::Result<Self> {
|
||||||
r.read_u8()
|
r.read_u8().map_err(Into::into)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value for u16 {
|
impl Value for u16 {
|
||||||
fn write<W: io::Write>(self, mut w: W) -> io::Result<()> {
|
fn write<W: io::Write>(&self, mut w: W) -> io::Result<()> {
|
||||||
w.write_u16::<B>(self)
|
w.write_u16::<B>(*self)
|
||||||
}
|
}
|
||||||
fn read<R: io::Read>(mut r: R) -> io::Result<Self> {
|
fn read<R: io::Read>(mut r: R) -> crate::Result<Self> {
|
||||||
r.read_u16::<B>()
|
r.read_u16::<B>().map_err(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Value, U: Value> Value for (T, U) {
|
||||||
|
fn write<W: io::Write>(&self, mut w: W) -> io::Result<()> {
|
||||||
|
T::write(&self.0, &mut w)?;
|
||||||
|
T::write(&self.0, &mut w)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read<R: io::Read>(mut r: R) -> crate::Result<Self> {
|
||||||
|
Ok((T::read(&mut r)?, U::read(&mut r)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue