mirror of
https://github.com/Noratrieb/tls.git
synced 2026-01-14 08:35:03 +01:00
more
This commit is contained in:
parent
29057b8e92
commit
4b1d2805e6
3 changed files with 130 additions and 31 deletions
36
src/lib.rs
36
src/lib.rs
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
123
src/proto.rs
123
src/proto.rs
|
|
@ -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)*) => {};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue