parser: init
This commit is contained in:
parent
bb70038c7c
commit
ba40c0780c
28
Cargo.toml
28
Cargo.toml
|
@ -4,30 +4,40 @@ version = "0.1.0"
|
||||||
authors = ["occheung <occheung@connect.ust.hk>"]
|
authors = ["occheung <occheung@connect.ust.hk>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aes-gcm = "0.7.0"
|
aes-gcm = "0.7.0"
|
||||||
chacha20poly1305 = "0.6.0"
|
chacha20poly1305 = "0.6.0"
|
||||||
byteorder = "1.3.4"
|
sha2 = { version = "0.9.1", default-features = false }
|
||||||
num_enum = "0.5.1"
|
byteorder = { version = "1.3.4", default-features = false }
|
||||||
|
num_enum = { version = "0.5.1", default-features = false }
|
||||||
|
log = {version = "0.4.11"}
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
[dependencies.smoltcp]
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["proto-ipv4", "proto-ipv6", "socket-tcp"]
|
features = ["proto-ipv4", "proto-ipv6", "socket-tcp"]
|
||||||
|
|
||||||
[dependencies.rand_chacha]
|
[dependencies.rand_core]
|
||||||
version = "0.2.2"
|
version = "0.5.1"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = []
|
features = []
|
||||||
|
|
||||||
[dependencies.rand]
|
[dependencies.p256]
|
||||||
version = "0.7.3"
|
version = "0.5.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["getrandom"]
|
features = [ "ecdh", "ecdsa", "arithmetic" ]
|
||||||
|
|
||||||
|
[dependencies.rsa]
|
||||||
|
git = "https://github.com/RustCrypto/RSA.git"
|
||||||
|
default-features = false
|
||||||
|
features = [ "alloc" ]
|
||||||
|
|
||||||
[dependencies.heapless]
|
[dependencies.heapless]
|
||||||
version = "0.5.6"
|
version = "0.5.6"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = []
|
features = []
|
||||||
|
|
||||||
|
[dependencies.nom]
|
||||||
|
version = "5.1.2"
|
||||||
|
default-features = false
|
||||||
|
features= [ "regex", "lexical" ]
|
||||||
|
|
|
@ -2,3 +2,9 @@
|
||||||
|
|
||||||
pub mod tls;
|
pub mod tls;
|
||||||
pub mod tls_packet;
|
pub mod tls_packet;
|
||||||
|
pub mod parse;
|
||||||
|
|
||||||
|
pub enum Error {
|
||||||
|
PropagatedError(smoltcp::Error),
|
||||||
|
ParsingError()
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
use nom::IResult;
|
||||||
|
use nom::bytes::complete::take;
|
||||||
|
use nom::combinator::complete;
|
||||||
|
use nom::sequence::tuple;
|
||||||
|
use nom::error::ErrorKind;
|
||||||
|
use smoltcp::Error;
|
||||||
|
use smoltcp::Result;
|
||||||
|
|
||||||
|
use byteorder::{ByteOrder, NetworkEndian, BigEndian};
|
||||||
|
|
||||||
|
use crate::tls_packet::*;
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
|
||||||
|
use heapless::{ Vec, consts::* };
|
||||||
|
|
||||||
|
fn parse_tls(bytes: &[u8]) -> IResult<&[u8], TlsRepr> {
|
||||||
|
let content_type = take(1_usize);
|
||||||
|
let version = take(2_usize);
|
||||||
|
let length = take(2_usize);
|
||||||
|
|
||||||
|
let (rest, (content_type, version, length)) =
|
||||||
|
tuple((content_type, version, length))(bytes)?;
|
||||||
|
|
||||||
|
let mut repr = TlsRepr {
|
||||||
|
content_type: TlsContentType::try_from(content_type[0])
|
||||||
|
.unwrap(),
|
||||||
|
|
||||||
|
version: TlsVersion::try_from(NetworkEndian::read_u16(version))
|
||||||
|
.unwrap(),
|
||||||
|
|
||||||
|
length: NetworkEndian::read_u16(length),
|
||||||
|
payload: None,
|
||||||
|
handshake: None,
|
||||||
|
};
|
||||||
|
{
|
||||||
|
use crate::tls_packet::TlsContentType::*;
|
||||||
|
match repr.content_type {
|
||||||
|
Handshake => {
|
||||||
|
let (rest, handshake) = parse_handshake(rest)?;
|
||||||
|
repr.handshake = Some(handshake);
|
||||||
|
Ok((rest, repr))
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
let (rest, payload) = take(repr.length)(rest)?;
|
||||||
|
repr.payload = Some(payload);
|
||||||
|
Ok((rest, repr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_handshake(bytes: &[u8]) -> IResult<&[u8], HandshakeRepr> {
|
||||||
|
let handshake_type = take(1_usize);
|
||||||
|
let length = take(3_usize);
|
||||||
|
|
||||||
|
let (rest, (handshake_type, length)) =
|
||||||
|
tuple((handshake_type, length))(bytes)?;
|
||||||
|
|
||||||
|
let mut repr = HandshakeRepr {
|
||||||
|
msg_type: HandshakeType::try_from(handshake_type[0]).unwrap(),
|
||||||
|
length: NetworkEndian::read_u24(length),
|
||||||
|
handshake_data: HandshakeData::Uninitialized,
|
||||||
|
};
|
||||||
|
{
|
||||||
|
use crate::tls_packet::HandshakeType::*;
|
||||||
|
match repr.msg_type {
|
||||||
|
ServerHello => {
|
||||||
|
todo!()
|
||||||
|
},
|
||||||
|
_ => todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_server_hello(bytes: &[u8]) -> IResult<&[u8], HandshakeData> {
|
||||||
|
let version = take(2_usize);
|
||||||
|
let random = take(32_usize);
|
||||||
|
let session_id_echo_length = take(1_usize);
|
||||||
|
|
||||||
|
let (rest, (version, random, session_id_echo_length)) =
|
||||||
|
tuple((version, random, session_id_echo_length))(bytes)?;
|
||||||
|
|
||||||
|
let session_id_echo_length = session_id_echo_length[0];
|
||||||
|
let (rest, session_id_echo) = take(session_id_echo_length)(rest)?;
|
||||||
|
|
||||||
|
let cipher_suite = take(2_usize);
|
||||||
|
let compression_method = take(1_usize);
|
||||||
|
let extension_length = take(2_usize);
|
||||||
|
|
||||||
|
let (mut rest, (cipher_suite, compression_method, extension_length)) =
|
||||||
|
tuple((cipher_suite, compression_method, extension_length))(rest)?;
|
||||||
|
|
||||||
|
let mut extension_length = NetworkEndian::read_u16(extension_length);
|
||||||
|
|
||||||
|
let mut server_hello = ServerHello {
|
||||||
|
version: TlsVersion::try_from(NetworkEndian::read_u16(version)).unwrap(),
|
||||||
|
random,
|
||||||
|
session_id_echo_length: session_id_echo_length,
|
||||||
|
session_id_echo,
|
||||||
|
cipher_suite: CipherSuite::try_from(NetworkEndian::read_u16(cipher_suite)).unwrap(),
|
||||||
|
compression_method: compression_method[0],
|
||||||
|
extension_length,
|
||||||
|
extensions: &[]
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut extension_vec: Vec<Extension, U32> = Vec::new();
|
||||||
|
while extension_length >= 0 {
|
||||||
|
let (rem, extension) = parse_extension(rest)?;
|
||||||
|
rest = rem;
|
||||||
|
extension_length -= extension.get_length();
|
||||||
|
|
||||||
|
// Todo:: Proper error
|
||||||
|
if extension_vec.push(extension).is_err() || extension_length < 0 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server_hello.extensions = extension_vec;
|
||||||
|
Ok((rest, HandshakeData::ServerHello(server_hello)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_extension(bytes: &[u8]) -> IResult<&[u8], Extension> {
|
||||||
|
let extension_type = take(2_usize);
|
||||||
|
let length = take(2_usize);
|
||||||
|
|
||||||
|
let (rest, (extension_type, length)) =
|
||||||
|
tuple((extension_type, length))(bytes)?;
|
||||||
|
|
||||||
|
let length = NetworkEndian::read_u16(length);
|
||||||
|
|
||||||
|
let (rest, extension_data) = take(length)(rest)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
Extension {
|
||||||
|
extension_type: ExtensionType::try_from(NetworkEndian::read_u16(extension_type)).unwrap(),
|
||||||
|
length,
|
||||||
|
extension_data
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
238
src/tls.rs
238
src/tls.rs
|
@ -11,10 +11,7 @@ use smoltcp::wire::IpEndpoint;
|
||||||
use smoltcp::Result;
|
use smoltcp::Result;
|
||||||
use smoltcp::Error;
|
use smoltcp::Error;
|
||||||
|
|
||||||
use byteorder::{ByteOrder, NetworkEndian, BigEndian, WriteBytesExt};
|
use byteorder::{ByteOrder, NetworkEndian, BigEndian};
|
||||||
|
|
||||||
use rand::prelude::*;
|
|
||||||
use rand_chacha::ChaCha20Rng;
|
|
||||||
|
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
use heapless::consts::*;
|
use heapless::consts::*;
|
||||||
|
@ -22,7 +19,11 @@ use heapless::consts::*;
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
|
|
||||||
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
use p256::{EncodedPoint, AffinePoint, ecdh::EphemeralSecret};
|
||||||
|
|
||||||
use crate::tls_packet::*;
|
use crate::tls_packet::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
enum TlsState {
|
enum TlsState {
|
||||||
|
@ -36,18 +37,19 @@ enum TlsState {
|
||||||
CONNECTED,
|
CONNECTED,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TlsSocket
|
pub struct TlsSocket<R: 'static + RngCore + CryptoRng>
|
||||||
{
|
{
|
||||||
state: TlsState,
|
state: TlsState,
|
||||||
tcp_handle: SocketHandle,
|
tcp_handle: SocketHandle,
|
||||||
random: ChaCha20Rng,
|
rng: R,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TlsSocket {
|
impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
||||||
pub fn new<'a, 'b, 'c>(
|
pub fn new<'a, 'b, 'c>(
|
||||||
sockets: &mut SocketSet<'a, 'b, 'c>,
|
sockets: &mut SocketSet<'a, 'b, 'c>,
|
||||||
rx_buffer: TcpSocketBuffer<'b>,
|
rx_buffer: TcpSocketBuffer<'b>,
|
||||||
tx_buffer: TcpSocketBuffer<'b>,
|
tx_buffer: TcpSocketBuffer<'b>,
|
||||||
|
rng: R,
|
||||||
) -> Self
|
) -> Self
|
||||||
where
|
where
|
||||||
'b: 'c,
|
'b: 'c,
|
||||||
|
@ -57,7 +59,7 @@ impl TlsSocket {
|
||||||
TlsSocket {
|
TlsSocket {
|
||||||
state: TlsState::START,
|
state: TlsState::START,
|
||||||
tcp_handle,
|
tcp_handle,
|
||||||
random: ChaCha20Rng::from_entropy(),
|
rng,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,13 +74,18 @@ impl TlsSocket {
|
||||||
U: Into<IpEndpoint>,
|
U: Into<IpEndpoint>,
|
||||||
{
|
{
|
||||||
let mut tcp_socket = sockets.get::<TcpSocket>(self.tcp_handle);
|
let mut tcp_socket = sockets.get::<TcpSocket>(self.tcp_handle);
|
||||||
|
if tcp_socket.state() == TcpState::Established {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
tcp_socket.connect(remote_endpoint, local_endpoint)
|
tcp_socket.connect(remote_endpoint, local_endpoint)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tls_connect(&mut self, sockets: &mut SocketSet) -> Result<bool> {
|
pub fn tls_connect(&mut self, sockets: &mut SocketSet) -> Result<bool> {
|
||||||
// Check tcp_socket connectivity
|
// Check tcp_socket connectivity
|
||||||
{
|
{
|
||||||
let tcp_socket = sockets.get::<TcpSocket>(self.tcp_handle);
|
let mut tcp_socket = sockets.get::<TcpSocket>(self.tcp_handle);
|
||||||
|
tcp_socket.set_keep_alive(Some(smoltcp::time::Duration::from_millis(1000)));
|
||||||
if tcp_socket.state() != TcpState::Established {
|
if tcp_socket.state() != TcpState::Established {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
@ -87,11 +94,11 @@ impl TlsSocket {
|
||||||
if self.state == TlsState::START {
|
if self.state == TlsState::START {
|
||||||
// Create TLS representation, length and payload not finalised
|
// Create TLS representation, length and payload not finalised
|
||||||
let mut random: [u8; 32] = [0; 32];
|
let mut random: [u8; 32] = [0; 32];
|
||||||
|
self.rng.fill_bytes(&mut random);
|
||||||
let mut session_id: [u8; 32] = [0; 32];
|
let mut session_id: [u8; 32] = [0; 32];
|
||||||
self.random.fill_bytes(&mut random);
|
self.rng.fill_bytes(&mut session_id);
|
||||||
self.random.fill_bytes(&mut session_id);
|
|
||||||
|
|
||||||
let cipher_suites_length = 3;
|
let cipher_suites_length = 6;
|
||||||
let cipher_suites = [
|
let cipher_suites = [
|
||||||
CipherSuite::TLS_AES_128_GCM_SHA256,
|
CipherSuite::TLS_AES_128_GCM_SHA256,
|
||||||
CipherSuite::TLS_AES_256_GCM_SHA384,
|
CipherSuite::TLS_AES_256_GCM_SHA384,
|
||||||
|
@ -101,15 +108,72 @@ impl TlsSocket {
|
||||||
// Length: to be determined
|
// Length: to be determined
|
||||||
let supported_versions_extension = Extension {
|
let supported_versions_extension = Extension {
|
||||||
extension_type: ExtensionType::SupportedVersions,
|
extension_type: ExtensionType::SupportedVersions,
|
||||||
length: 3,
|
length: 5,
|
||||||
extension_data: &[
|
extension_data: &[
|
||||||
2, // Number of supported versions * 2
|
4, // Number of supported versions * 2
|
||||||
// Need 2 bytes to contain a version
|
// Need 2 bytes to contain a version
|
||||||
0x03, 0x04 // 0x0303: TLS Version 1.3
|
0x03, 0x04, // 0x0304: TLS Version 1.3
|
||||||
|
0x03, 0x03, // 0x0303: TLS version 1.2
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
let client_hello = ClientHello {
|
let signature_algorithms_extension = Extension {
|
||||||
|
extension_type: ExtensionType::SignatureAlgorithms,
|
||||||
|
length: 24,
|
||||||
|
extension_data: &[
|
||||||
|
0x00, 22, // Length in bytes
|
||||||
|
0x04, 0x03, // ecdsa_secp256r1_sha256
|
||||||
|
0x08, 0x07, // ed25519
|
||||||
|
0x08, 0x09, // rsa_pss_pss_sha256
|
||||||
|
0x04, 0x01, // rsa_pkcs1_sha256
|
||||||
|
0x08, 0x04, // rsa_pss_rsae_sha256
|
||||||
|
0x08, 0x0a, // rsa_pss_pss_sha384
|
||||||
|
0x05, 0x01, // rsa_pkcs1_sha384
|
||||||
|
0x08, 0x05, // rsa_pss_rsae_sha384
|
||||||
|
0x08, 0x0b, // rsa_pss_pss_sha512
|
||||||
|
0x06, 0x01, // rsa_pkcs1_sha512
|
||||||
|
0x08, 0x06, // rsa_pss_rsae_sha512
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let supported_groups_extension = Extension {
|
||||||
|
extension_type: ExtensionType::SupportedGroups,
|
||||||
|
length: 4,
|
||||||
|
extension_data: &[
|
||||||
|
0x00, 0x02, // Length in bytes
|
||||||
|
0x00, 0x17, // secp256r1
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let key_share_extension = Extension {
|
||||||
|
extension_type: ExtensionType::KeyShare,
|
||||||
|
length: 71,
|
||||||
|
extension_data: &{
|
||||||
|
let ecdh_secret = unsafe { EphemeralSecret::random(&mut self.rng) };
|
||||||
|
let ecdh_public = EncodedPoint::from(&ecdh_secret);
|
||||||
|
let x_coor = ecdh_public.x();
|
||||||
|
let y_coor = ecdh_public.y().unwrap();
|
||||||
|
let mut data: [u8; 71] = [0; 71];
|
||||||
|
data[0..2].copy_from_slice(&[0x00, 69]); // Length in bytes
|
||||||
|
data[2..4].copy_from_slice(&[0x00, 0x17]); // secp256r1
|
||||||
|
data[4..6].copy_from_slice(&[0x00, 65]); // key exchange length
|
||||||
|
data[6..7].copy_from_slice(&[0x04]); // Fixed legacy value
|
||||||
|
data[7..39].copy_from_slice(&x_coor);
|
||||||
|
data[39..71].copy_from_slice(&y_coor);
|
||||||
|
data
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let psk_key_exchange_modes_extension = Extension {
|
||||||
|
extension_type: ExtensionType::PSKKeyExchangeModes,
|
||||||
|
length: 2,
|
||||||
|
extension_data: &[
|
||||||
|
0x01, // Length in bytes
|
||||||
|
0x01, // psk_dhe_ke
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut client_hello = ClientHello {
|
||||||
version: TlsVersion::Tls12,
|
version: TlsVersion::Tls12,
|
||||||
random,
|
random,
|
||||||
session_id_length: 32,
|
session_id_length: 32,
|
||||||
|
@ -119,7 +183,21 @@ impl TlsSocket {
|
||||||
compression_method_length: 1,
|
compression_method_length: 1,
|
||||||
compression_methods: 0,
|
compression_methods: 0,
|
||||||
extension_length: supported_versions_extension.get_length(),
|
extension_length: supported_versions_extension.get_length(),
|
||||||
extensions: &[supported_versions_extension],
|
extensions: &[
|
||||||
|
supported_versions_extension,
|
||||||
|
signature_algorithms_extension,
|
||||||
|
supported_groups_extension,
|
||||||
|
psk_key_exchange_modes_extension,
|
||||||
|
key_share_extension,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
client_hello.extension_length = {
|
||||||
|
let mut sum = 0;
|
||||||
|
for ext in client_hello.extensions.iter() {
|
||||||
|
sum += ext.get_length();
|
||||||
|
}
|
||||||
|
sum
|
||||||
};
|
};
|
||||||
|
|
||||||
let handshake_repr = HandshakeRepr {
|
let handshake_repr = HandshakeRepr {
|
||||||
|
@ -130,12 +208,14 @@ impl TlsSocket {
|
||||||
|
|
||||||
let repr = TlsRepr {
|
let repr = TlsRepr {
|
||||||
content_type: TlsContentType::Handshake,
|
content_type: TlsContentType::Handshake,
|
||||||
version: TlsVersion::Tls13,
|
version: TlsVersion::Tls10,
|
||||||
length: 0,
|
length: handshake_repr.get_length(),
|
||||||
payload: None,
|
payload: None,
|
||||||
handshake: Some(handshake_repr),
|
handshake: Some(handshake_repr),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
log::info!("{:?}", repr);
|
||||||
|
|
||||||
self.send_tls_repr(sockets, repr)?;
|
self.send_tls_repr(sockets, repr)?;
|
||||||
self.state = TlsState::WAIT_SH;
|
self.state = TlsState::WAIT_SH;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
@ -164,11 +244,10 @@ impl TlsSocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic inner recv method, through TCP socket
|
// Generic inner recv method, through TCP socket
|
||||||
fn recv_tls_repr<'a>(&'a mut self, sockets: &mut SocketSet, byte_array: &'a mut [u8]) -> Result<TlsRepr<'a, '_>> {
|
fn recv_tls_repr<'a>(&'a mut self, sockets: &mut SocketSet, byte_array: &'a mut [u8]) -> Result<TlsRepr<'a>> {
|
||||||
let mut tcp_socket = sockets.get::<TcpSocket>(self.tcp_handle);
|
let mut tcp_socket = sockets.get::<TcpSocket>(self.tcp_handle);
|
||||||
let size = tcp_socket.recv_slice(byte_array)?;
|
let size = tcp_socket.recv_slice(byte_array)?;
|
||||||
let buffer = TlsBuffer::new(&mut byte_array[..size]);
|
todo!()
|
||||||
buffer.dequeue_tls_repr()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,121 +372,6 @@ impl<'a> TlsBuffer<'a> {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dequeue_tls_repr<'b>(mut self) -> Result<TlsRepr<'a, 'b>> {
|
|
||||||
// Create a TLS Representation layer
|
|
||||||
// Modify the representation along the way
|
|
||||||
let mut repr = TlsRepr {
|
|
||||||
content_type: TlsContentType::Invalid,
|
|
||||||
version: TlsVersion::Tls10,
|
|
||||||
length: 0,
|
|
||||||
payload: None,
|
|
||||||
handshake: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
repr.content_type = TlsContentType::try_from(self.read_u8()?)
|
|
||||||
.map_err(|_| Error::Unrecognized)?;
|
|
||||||
repr.version = TlsVersion::try_from(self.read_u16()?)
|
|
||||||
.map_err(|_| Error::Unrecognized)?;
|
|
||||||
repr.length = self.read_u16()?;
|
|
||||||
|
|
||||||
use TlsContentType::*;
|
|
||||||
match repr.content_type {
|
|
||||||
Invalid => Err(Error::Unrecognized),
|
|
||||||
ChangeCipherSpec | Alert => unimplemented!(),
|
|
||||||
Handshake => todo!(),
|
|
||||||
ApplicationData => {
|
|
||||||
repr.payload = Some(self.read_all());
|
|
||||||
Ok(repr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dequeue_handshake<'b>(mut self) -> Result<HandshakeRepr<'a, 'b>> {
|
|
||||||
// Create a Handshake header representation
|
|
||||||
// Fill in proper value afterwards
|
|
||||||
let mut repr = HandshakeRepr {
|
|
||||||
msg_type: HandshakeType::ClientHello,
|
|
||||||
length: 0,
|
|
||||||
handshake_data: HandshakeData::Uninitialized,
|
|
||||||
};
|
|
||||||
|
|
||||||
repr.msg_type = HandshakeType::try_from(self.read_u8()?)
|
|
||||||
.map_err(|_| Error::Unrecognized)?;
|
|
||||||
repr.length = self.read_u24()?;
|
|
||||||
|
|
||||||
use HandshakeType::*;
|
|
||||||
match repr.msg_type {
|
|
||||||
ClientHello => unimplemented!(),
|
|
||||||
ServerHello => todo!(),
|
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dequeue_server_hello(mut self) -> Result<ServerHello<'static, 'static>> {
|
|
||||||
// Create a Server Hello representation
|
|
||||||
// Fill in proper value afterwards
|
|
||||||
let mut server_hello = ServerHello {
|
|
||||||
version: TlsVersion::Tls10,
|
|
||||||
random: [0; 32],
|
|
||||||
session_id_echo_length: 0,
|
|
||||||
session_id_echo: [0; 32],
|
|
||||||
cipher_suite: CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
|
|
||||||
compression_method: 0,
|
|
||||||
extension_length: 0,
|
|
||||||
extensions: &[],
|
|
||||||
};
|
|
||||||
|
|
||||||
server_hello.version = TlsVersion::try_from(self.read_u16()?)
|
|
||||||
.map_err(|_| Error::Unrecognized)?;
|
|
||||||
for random_byte in &mut server_hello.random[..] {
|
|
||||||
*random_byte = self.read_u8()?;
|
|
||||||
}
|
|
||||||
server_hello.session_id_echo_length = self.read_u8()?;
|
|
||||||
for id_byte in &mut server_hello.session_id_echo[
|
|
||||||
..usize::try_from(server_hello.session_id_echo_length)
|
|
||||||
.map_err(|_| Error::Exhausted)?
|
|
||||||
] {
|
|
||||||
*id_byte = self.read_u8()?;
|
|
||||||
}
|
|
||||||
server_hello.cipher_suite = CipherSuite::try_from(self.read_u16()?)
|
|
||||||
.map_err(|_| Error::Unrecognized)?;
|
|
||||||
server_hello.compression_method = self.read_u8()?;
|
|
||||||
server_hello.extension_length = self.read_u16()?;
|
|
||||||
|
|
||||||
let mut remaining_length = server_hello.extension_length;
|
|
||||||
let mut extension_counter = 0;
|
|
||||||
let mut extension_vec: Vec<Extension, U32> = Vec::new();
|
|
||||||
while remaining_length != 0 {
|
|
||||||
extension_vec.push(self.dequeue_extension()?.clone())
|
|
||||||
.map_err(|_| Error::Exhausted)?;
|
|
||||||
// Deduct base length of an extension (ext_type, len)
|
|
||||||
remaining_length -= 4;
|
|
||||||
remaining_length -= extension_vec[extension_counter].length;
|
|
||||||
extension_counter += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(server_hello)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dequeue_extension(&self) -> Result<Extension<'_>> {
|
|
||||||
// Create an Extension representation
|
|
||||||
// Fill in proper value afterwards
|
|
||||||
let mut extension = Extension {
|
|
||||||
extension_type: ExtensionType::ServerName,
|
|
||||||
length: 0,
|
|
||||||
extension_data: &[],
|
|
||||||
};
|
|
||||||
|
|
||||||
extension.extension_type = ExtensionType::try_from(self.read_u16()?)
|
|
||||||
.map_err(|_| Error::Unrecognized)?;
|
|
||||||
extension.length = self.read_u16()?;
|
|
||||||
extension.extension_data = self.read_slice(
|
|
||||||
usize::try_from(extension.length)
|
|
||||||
.map_err(|_| Error::Exhausted)?
|
|
||||||
)?;
|
|
||||||
Ok(extension)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! export_byte_order_fn {
|
macro_rules! export_byte_order_fn {
|
||||||
|
|
|
@ -2,10 +2,12 @@ use byteorder::{ByteOrder, NetworkEndian, BigEndian};
|
||||||
use num_enum::IntoPrimitive;
|
use num_enum::IntoPrimitive;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
|
use core::convert::TryInto;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub(crate) enum TlsContentType {
|
pub(crate) enum TlsContentType {
|
||||||
|
#[num_enum(default)]
|
||||||
Invalid = 0,
|
Invalid = 0,
|
||||||
ChangeCipherSpec = 20,
|
ChangeCipherSpec = 20,
|
||||||
Alert = 21,
|
Alert = 21,
|
||||||
|
@ -16,24 +18,28 @@ pub(crate) enum TlsContentType {
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
pub(crate) enum TlsVersion {
|
pub(crate) enum TlsVersion {
|
||||||
|
#[num_enum(default)]
|
||||||
|
Unknown = 0x0000,
|
||||||
Tls10 = 0x0301,
|
Tls10 = 0x0301,
|
||||||
Tls11 = 0x0302,
|
Tls11 = 0x0302,
|
||||||
Tls12 = 0x0303,
|
Tls12 = 0x0303,
|
||||||
Tls13 = 0x0304,
|
Tls13 = 0x0304,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub(crate) struct TlsRepr<'a, 'b> {
|
pub(crate) struct TlsRepr<'a> {
|
||||||
pub(crate) content_type: TlsContentType,
|
pub(crate) content_type: TlsContentType,
|
||||||
pub(crate) version: TlsVersion,
|
pub(crate) version: TlsVersion,
|
||||||
pub(crate) length: u16,
|
pub(crate) length: u16,
|
||||||
pub(crate) payload: Option<&'a[u8]>,
|
pub(crate) payload: Option<&'a[u8]>,
|
||||||
pub(crate) handshake: Option<HandshakeRepr<'a, 'b>>
|
pub(crate) handshake: Option<HandshakeRepr<'a>>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub(crate) enum HandshakeType {
|
pub(crate) enum HandshakeType {
|
||||||
|
#[num_enum(default)]
|
||||||
|
Unknown = 0,
|
||||||
ClientHello = 1,
|
ClientHello = 1,
|
||||||
ServerHello = 2,
|
ServerHello = 2,
|
||||||
NewSessionTicket = 4,
|
NewSessionTicket = 4,
|
||||||
|
@ -47,11 +53,20 @@ pub(crate) enum HandshakeType {
|
||||||
MessageHash = 254,
|
MessageHash = 254,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub(crate) struct HandshakeRepr<'a, 'b> {
|
pub(crate) struct HandshakeRepr<'a> {
|
||||||
pub(crate) msg_type: HandshakeType,
|
pub(crate) msg_type: HandshakeType,
|
||||||
pub(crate) length: u32,
|
pub(crate) length: u32,
|
||||||
pub(crate) handshake_data: HandshakeData<'a, 'b>,
|
pub(crate) handshake_data: HandshakeData<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> HandshakeRepr<'a> {
|
||||||
|
pub(crate) fn get_length(&self) -> u16 {
|
||||||
|
let mut length :u16 = 1; // Handshake Type
|
||||||
|
length += 3; // Length of Handshake data
|
||||||
|
length += u16::try_from(self.handshake_data.get_length()).unwrap();
|
||||||
|
length
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
||||||
|
@ -64,8 +79,8 @@ pub(crate) enum CipherSuite {
|
||||||
TLS_AES_128_CCM_8_SHA256 = 0x1305,
|
TLS_AES_128_CCM_8_SHA256 = 0x1305,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub(crate) struct ClientHello<'a, 'b> {
|
pub(crate) struct ClientHello<'a> {
|
||||||
pub(crate) version: TlsVersion, // Legacy: Must be Tls12 (0x0303)
|
pub(crate) version: TlsVersion, // Legacy: Must be Tls12 (0x0303)
|
||||||
pub(crate) random: [u8; 32],
|
pub(crate) random: [u8; 32],
|
||||||
pub(crate) session_id_length: u8, // Legacy: Keep it 32
|
pub(crate) session_id_length: u8, // Legacy: Keep it 32
|
||||||
|
@ -75,17 +90,27 @@ pub(crate) struct ClientHello<'a, 'b> {
|
||||||
pub(crate) compression_method_length: u8, // Legacy: Must be 1, to contain a byte
|
pub(crate) compression_method_length: u8, // Legacy: Must be 1, to contain a byte
|
||||||
pub(crate) compression_methods: u8, // Legacy: Must be 1 byte of 0
|
pub(crate) compression_methods: u8, // Legacy: Must be 1 byte of 0
|
||||||
pub(crate) extension_length: u16,
|
pub(crate) extension_length: u16,
|
||||||
pub(crate) extensions: &'a[Extension<'b>],
|
pub(crate) extensions: &'a[Extension<'a>],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub(crate) enum HandshakeData<'a, 'b> {
|
pub(crate) enum HandshakeData<'a> {
|
||||||
Uninitialized,
|
Uninitialized,
|
||||||
ClientHello(ClientHello<'a, 'b>),
|
ClientHello(ClientHello<'a>),
|
||||||
ServerHello(ServerHello<'a, 'b>),
|
ServerHello(ServerHello<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> ClientHello<'a, 'b> {
|
impl<'a> HandshakeData<'a> {
|
||||||
|
pub(crate) fn get_length(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
HandshakeData::ClientHello(data) => data.get_length(),
|
||||||
|
HandshakeData::ServerHello(data) => todo!(),
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ClientHello<'a> {
|
||||||
pub(crate) fn get_length(&self) -> u32 {
|
pub(crate) fn get_length(&self) -> u32 {
|
||||||
let mut length :u32 = 2; // TlsVersion size
|
let mut length :u32 = 2; // TlsVersion size
|
||||||
length += 32; // Random size
|
length += 32; // Random size
|
||||||
|
@ -103,16 +128,16 @@ impl<'a, 'b> ClientHello<'a, 'b> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub(crate) struct ServerHello<'a, 'b> {
|
pub(crate) struct ServerHello<'a> {
|
||||||
pub(crate) version: TlsVersion,
|
pub(crate) version: TlsVersion,
|
||||||
pub(crate) random: [u8; 32],
|
pub(crate) random: &'a[u8],
|
||||||
pub(crate) session_id_echo_length: u8,
|
pub(crate) session_id_echo_length: u8,
|
||||||
pub(crate) session_id_echo: [u8; 32],
|
pub(crate) session_id_echo: &'a[u8],
|
||||||
pub(crate) cipher_suite: CipherSuite,
|
pub(crate) cipher_suite: CipherSuite,
|
||||||
pub(crate) compression_method: u8, // Always 0
|
pub(crate) compression_method: u8, // Always 0
|
||||||
pub(crate) extension_length: u16,
|
pub(crate) extension_length: u16,
|
||||||
pub(crate) extensions: &'a[Extension<'b>],
|
pub(crate) extensions: &'a[Extension<'a>],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
||||||
|
@ -148,7 +173,7 @@ impl ExtensionType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub(crate) struct Extension<'a> {
|
pub(crate) struct Extension<'a> {
|
||||||
pub(crate) extension_type: ExtensionType,
|
pub(crate) extension_type: ExtensionType,
|
||||||
pub(crate) length: u16,
|
pub(crate) length: u16,
|
||||||
|
|
Loading…
Reference in New Issue