mirror of
https://github.com/Noratrieb/tls.git
synced 2026-01-14 16:45:02 +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 {}
|
pub struct ClientConnection {}
|
||||||
|
|
||||||
impl ClientConnection {
|
impl ClientConnection {
|
||||||
pub fn establish(host: impl ToSocketAddrs) -> Result<Self> {
|
pub fn establish(host: &str, port: u16) -> Result<Self> {
|
||||||
let _setup = ClientSetupConnection::establish(host)?;
|
let _setup = ClientSetupConnection::establish(host, port)?;
|
||||||
|
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
@ -21,22 +21,28 @@ impl ClientConnection {
|
||||||
struct ClientSetupConnection {}
|
struct ClientSetupConnection {}
|
||||||
|
|
||||||
impl ClientSetupConnection {
|
impl ClientSetupConnection {
|
||||||
fn establish(host: impl ToSocketAddrs) -> Result<Self> {
|
fn establish(host: &str, port: u16) -> Result<Self> {
|
||||||
let mut stream = BufWriter::new(LoggingWriter(TcpStream::connect(host)?));
|
let mut stream = BufWriter::new(LoggingWriter(TcpStream::connect((host, port))?));
|
||||||
let handshake = proto::Handshake::ClientHello {
|
let handshake = proto::Handshake::ClientHello {
|
||||||
legacy_version: proto::LEGACY_VERSION,
|
legacy_version: proto::LEGACY_TLSV12,
|
||||||
random: rand::random(),
|
random: rand::random(),
|
||||||
legacy_session_id: 0,
|
legacy_session_id: [(); 32].map(|()| rand::random()).to_vec().into(),
|
||||||
cipher_suites: vec![proto::CipherSuite::TlsAes128GcmSha256].into(),
|
cipher_suites: vec![proto::CipherSuite::TlsAes128GcmSha256].into(),
|
||||||
legacy_compressions_methods: 0,
|
legacy_compressions_methods: vec![0].into(),
|
||||||
extensions: vec![proto::ExtensionCH::SupportedVersions {
|
extensions: vec![
|
||||||
versions: vec![proto::TLSV3].into(),
|
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(),
|
.into(),
|
||||||
};
|
};
|
||||||
let plaintext = proto::TLSPlaintext::Handshake {
|
let plaintext = proto::TLSPlaintext::Handshake { handshake };
|
||||||
handshake,
|
|
||||||
};
|
|
||||||
plaintext.write(&mut stream)?;
|
plaintext.write(&mut stream)?;
|
||||||
stream.flush()?;
|
stream.flush()?;
|
||||||
|
|
||||||
|
|
@ -82,7 +88,7 @@ impl<W: io::Write> io::Write for LoggingWriter<W> {
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
let len = self.0.write(buf);
|
let len = self.0.write(buf);
|
||||||
if let Ok(len) = len {
|
if let Ok(len) = len {
|
||||||
eprintln!("wrote bytes: {:x?}", &buf[..len]);
|
eprintln!(" bytes: {:02x?}", &buf[..len]);
|
||||||
}
|
}
|
||||||
len
|
len
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +102,7 @@ impl<R: Read> io::Read for LoggingWriter<R> {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
let len = self.0.read(buf);
|
let len = self.0.read(buf);
|
||||||
if let Ok(len) = len {
|
if let Ok(len) = len {
|
||||||
eprintln!("read bytes: {:x?}", &buf[..len]);
|
eprintln!("read bytes: {:02x?}", &buf[..len]);
|
||||||
}
|
}
|
||||||
len
|
len
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// An example program that makes a shitty HTTP/1.1 request.
|
// An example program that makes a shitty HTTP/1.1 request.
|
||||||
fn main() {
|
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,
|
fmt::Debug,
|
||||||
io::{self, Read, Write},
|
io::{self, Read, Write},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
|
num::TryFromIntError,
|
||||||
};
|
};
|
||||||
|
|
||||||
use byteorder::{BigEndian as B, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian as B, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
@ -40,8 +41,16 @@ impl TLSPlaintext {
|
||||||
TLSPlaintext::ChangeCipherSpec => todo!(),
|
TLSPlaintext::ChangeCipherSpec => todo!(),
|
||||||
TLSPlaintext::Alert { alert } => todo!(),
|
TLSPlaintext::Alert { alert } => todo!(),
|
||||||
TLSPlaintext::Handshake { handshake } => {
|
TLSPlaintext::Handshake { handshake } => {
|
||||||
Self::HANDSHAKE.write(w)?; // handshake
|
Self::HANDSHAKE.write(w)?;
|
||||||
LEGACY_VERSION.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();
|
let len: u16 = handshake.byte_size().try_into().unwrap();
|
||||||
len.write(w)?;
|
len.write(w)?;
|
||||||
handshake.write(w)?;
|
handshake.write(w)?;
|
||||||
|
|
@ -80,14 +89,14 @@ 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, (length: u24) {
|
||||||
// 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: ProtocolVersion,
|
legacy_version: ProtocolVersion,
|
||||||
random: Random,
|
random: Random,
|
||||||
legacy_session_id: u8,
|
legacy_session_id: List<u8, u8>,
|
||||||
cipher_suites: List<CipherSuite, u16>,
|
cipher_suites: List<CipherSuite, u16>,
|
||||||
legacy_compressions_methods: u8,
|
legacy_compressions_methods: List<u8, u8>,
|
||||||
extensions: List<ExtensionCH, u16>,
|
extensions: List<ExtensionCH, u16>,
|
||||||
} = 1,
|
} = 1,
|
||||||
ServerHello {
|
ServerHello {
|
||||||
|
|
@ -110,8 +119,9 @@ proto_enum! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const LEGACY_VERSION: ProtocolVersion = 0x0303; // TLS v1.2
|
pub const LEGACY_TLSV10: ProtocolVersion = 0x0301;
|
||||||
pub const TLSV3: ProtocolVersion = 0x0304;
|
pub const LEGACY_TLSV12: ProtocolVersion = 0x0303;
|
||||||
|
pub const TLSV13: ProtocolVersion = 0x0304;
|
||||||
|
|
||||||
proto_enum! {
|
proto_enum! {
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -126,8 +136,10 @@ proto_enum! {
|
||||||
|
|
||||||
proto_enum! {
|
proto_enum! {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ExtensionCH: u16 {
|
pub enum ExtensionCH: u16, (length: u16) {
|
||||||
ServerName = 0,
|
ServerName {
|
||||||
|
server_name: ServerNameList,
|
||||||
|
} = 0,
|
||||||
MaxFragmentLength = 1,
|
MaxFragmentLength = 1,
|
||||||
StatusRequest = 5,
|
StatusRequest = 5,
|
||||||
SupportedGroups = 10,
|
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! {
|
proto_struct! {
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Alert {
|
pub struct Alert {
|
||||||
|
|
@ -254,7 +278,7 @@ macro_rules! proto_struct {
|
||||||
use proto_struct;
|
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 $( ,(length: $len_ty:ty) )? {
|
||||||
$(
|
$(
|
||||||
$KindName:ident $({
|
$KindName:ident $({
|
||||||
$(
|
$(
|
||||||
|
|
@ -275,7 +299,9 @@ macro_rules! proto_enum {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value for $name {
|
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 {
|
mod discr_consts {
|
||||||
$(
|
$(
|
||||||
#[allow(non_upper_case_globals)]
|
#[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 {
|
match self {
|
||||||
$(
|
$(
|
||||||
Self::$KindName $( {
|
Self::$KindName $( {
|
||||||
$( $field_name, )*
|
$( $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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
|
@ -307,6 +353,11 @@ macro_rules! proto_enum {
|
||||||
}
|
}
|
||||||
|
|
||||||
let kind: $discr_ty = Value::read(r)?;
|
let kind: $discr_ty = Value::read(r)?;
|
||||||
|
|
||||||
|
$(
|
||||||
|
let _len = <$len_ty>::read(r)?;
|
||||||
|
)?
|
||||||
|
|
||||||
match kind {
|
match kind {
|
||||||
$(
|
$(
|
||||||
discr_consts::$KindName => {
|
discr_consts::$KindName => {
|
||||||
|
|
@ -333,7 +384,7 @@ macro_rules! proto_enum {
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
|
||||||
match self {
|
$( <$len_ty>::default().byte_size() + )? match self {
|
||||||
$(
|
$(
|
||||||
Self::$KindName $( {
|
Self::$KindName $( {
|
||||||
$( $field_name, )*
|
$( $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 {
|
macro_rules! discard {
|
||||||
($($tt:tt)*) => {};
|
($($tt:tt)*) => {};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue