Compare commits
2 Commits
eedee2602d
...
3d566b22e7
Author | SHA1 | Date |
---|---|---|
occheung | 3d566b22e7 | |
occheung | 0a8ce3fd4c |
10
Cargo.toml
10
Cargo.toml
|
@ -45,6 +45,11 @@ version = "0.5.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [ "ecdh", "ecdsa", "arithmetic" ]
|
features = [ "ecdh", "ecdsa", "arithmetic" ]
|
||||||
|
|
||||||
|
[dependencies.ed25519-dalek]
|
||||||
|
version = "1.0.1"
|
||||||
|
default-features = false
|
||||||
|
features = [ "u64_backend" ]
|
||||||
|
|
||||||
# Fetch from master, for "no_std" + "alloc" combination
|
# Fetch from master, for "no_std" + "alloc" combination
|
||||||
[dependencies.rsa]
|
[dependencies.rsa]
|
||||||
git = "https://github.com/RustCrypto/RSA.git"
|
git = "https://github.com/RustCrypto/RSA.git"
|
||||||
|
@ -56,6 +61,11 @@ version = "5.1.2"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = []
|
features = []
|
||||||
|
|
||||||
|
[dependencies.arraydeque]
|
||||||
|
version = "0.4.5"
|
||||||
|
default-features = false
|
||||||
|
features = [ "use_generic_array" ]
|
||||||
|
|
||||||
[dependencies.simple_logger]
|
[dependencies.simple_logger]
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use core::convert::{ TryInto, TryFrom };
|
|
||||||
|
|
||||||
use smoltcp::{ Result, Error };
|
use smoltcp::{ Result, Error };
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use byteorder::{ByteOrder, NetworkEndian, BigEndian};
|
use byteorder::{ByteOrder, NetworkEndian};
|
||||||
|
|
||||||
use crate::tls_packet::*;
|
use crate::tls_packet::*;
|
||||||
use crate::key::*;
|
use crate::key::*;
|
||||||
|
@ -181,7 +180,7 @@ impl<'a> TlsBuffer<'a> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
KeyShareEntry(k) => {
|
KeyShareEntry(k) => {
|
||||||
let mut key_share_entry_into = |buffer: &mut TlsBuffer, entry: crate::tls_packet::KeyShareEntry| {
|
let key_share_entry_into = |buffer: &mut TlsBuffer, entry: crate::tls_packet::KeyShareEntry| {
|
||||||
buffer.write_u16(entry.group.into())?;
|
buffer.write_u16(entry.group.into())?;
|
||||||
buffer.write_u16(entry.length)?;
|
buffer.write_u16(entry.length)?;
|
||||||
buffer.write(entry.key_exchange.as_slice())
|
buffer.write(entry.key_exchange.as_slice())
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use num_enum::IntoPrimitive;
|
use num_enum::IntoPrimitive;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
|
|
||||||
use crate::parse::parse_asn1_der_object;
|
|
||||||
use crate::parse::parse_asn1_der_rsa_public_key;
|
use crate::parse::parse_asn1_der_rsa_public_key;
|
||||||
use crate::Error as TlsError;
|
use crate::Error as TlsError;
|
||||||
|
|
||||||
|
@ -32,6 +31,7 @@ pub struct TBSCertificate<'a> {
|
||||||
pub extensions: Extensions<'a>,
|
pub extensions: Extensions<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Version {
|
pub enum Version {
|
||||||
|
@ -147,7 +147,6 @@ pub fn validate_root_certificate(cert: &Certificate) -> Result<bool, TlsError> {
|
||||||
let mut hasher = Sha1::new();
|
let mut hasher = Sha1::new();
|
||||||
hasher.update(cert.tbs_certificate_encoded);
|
hasher.update(cert.tbs_certificate_encoded);
|
||||||
|
|
||||||
// TODO: invoke method to get public key
|
|
||||||
let (_, (modulus, exponent)) = parse_asn1_der_rsa_public_key(
|
let (_, (modulus, exponent)) = parse_asn1_der_rsa_public_key(
|
||||||
cert.tbs_certificate.subject_public_key_info.subject_public_key
|
cert.tbs_certificate.subject_public_key_info.subject_public_key
|
||||||
).map_err(|_| TlsError::ParsingError)?;
|
).map_err(|_| TlsError::ParsingError)?;
|
||||||
|
@ -163,12 +162,8 @@ pub fn validate_root_certificate(cert: &Certificate) -> Result<bool, TlsError> {
|
||||||
&hasher.finalize(),
|
&hasher.finalize(),
|
||||||
cert.signature_value
|
cert.signature_value
|
||||||
);
|
);
|
||||||
log::info!("Verification result: {:?}", verify_result);
|
|
||||||
Ok(verify_result.is_ok())
|
Ok(verify_result.is_ok())
|
||||||
}
|
}
|
||||||
_ => {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
use sha2::{ Digest, Sha256, Sha384, Sha512 };
|
use sha2::Digest;
|
||||||
use sha2::digest::{BlockInput, FixedOutput, Reset, Update};
|
use sha2::digest::{BlockInput, FixedOutput, Reset, Update};
|
||||||
use generic_array::{ GenericArray, ArrayLength };
|
use generic_array::{ GenericArray, ArrayLength };
|
||||||
use heapless::{ String, Vec, consts::* };
|
use heapless::{ String, consts::* };
|
||||||
|
|
||||||
use crate::buffer::TlsBuffer;
|
use crate::buffer::TlsBuffer;
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,6 @@ pub mod session;
|
||||||
pub mod certificate;
|
pub mod certificate;
|
||||||
pub mod fake_rng;
|
pub mod fake_rng;
|
||||||
|
|
||||||
use nom::error::ParseError;
|
|
||||||
|
|
||||||
// TODO: Implement errors
|
// TODO: Implement errors
|
||||||
// Details: Encapsulate smoltcp & nom errors
|
// Details: Encapsulate smoltcp & nom errors
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
41
src/parse.rs
41
src/parse.rs
|
@ -6,12 +6,9 @@ use nom::combinator::complete;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::sequence::preceded;
|
use nom::sequence::preceded;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
use nom::error::make_error;
|
|
||||||
use nom::error::ErrorKind;
|
use nom::error::ErrorKind;
|
||||||
use smoltcp::Error;
|
|
||||||
use smoltcp::Result;
|
|
||||||
|
|
||||||
use byteorder::{ByteOrder, NetworkEndian, BigEndian};
|
use byteorder::{ByteOrder, NetworkEndian};
|
||||||
|
|
||||||
use crate::tls_packet::*;
|
use crate::tls_packet::*;
|
||||||
|
|
||||||
|
@ -83,7 +80,7 @@ pub(crate) fn parse_inner_plaintext_for_handshake(bytes: &[u8]) -> IResult<&[u8]
|
||||||
let mut remaining_bytes = bytes;
|
let mut remaining_bytes = bytes;
|
||||||
let mut handshake_vec: Vec<HandshakeRepr> = Vec::new();
|
let mut handshake_vec: Vec<HandshakeRepr> = Vec::new();
|
||||||
|
|
||||||
while true {
|
loop {
|
||||||
// Perform check on the number of remaining bytes
|
// Perform check on the number of remaining bytes
|
||||||
// Case 1: At most 4 bytes left, then that must be the content type of the TLS record
|
// Case 1: At most 4 bytes left, then that must be the content type of the TLS record
|
||||||
// Assert that it is indeed handshake (0x16)
|
// Assert that it is indeed handshake (0x16)
|
||||||
|
@ -115,8 +112,32 @@ pub(crate) fn parse_inner_plaintext_for_handshake(bytes: &[u8]) -> IResult<&[u8]
|
||||||
remaining_bytes = rem;
|
remaining_bytes = rem;
|
||||||
handshake_vec.push(handshake_repr);
|
handshake_vec.push(handshake_repr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unreachable!()
|
// Input: The entire inner plaintext including TLS record
|
||||||
|
// (record | content | content_type | zeros)
|
||||||
|
// Get the content_type of inner_plaintext
|
||||||
|
// Also get the (optional) starting index of zero paddings
|
||||||
|
pub(crate) fn get_content_type_inner_plaintext(inner_plaintext: &[u8]) -> (TlsContentType, Option<usize>) {
|
||||||
|
// Approach from the rear, discard zeros until a nonzero byte is found
|
||||||
|
let mut zero_padding_start_index = inner_plaintext.len();
|
||||||
|
while (&inner_plaintext[..zero_padding_start_index]).ends_with(&[0x00]) {
|
||||||
|
// Record wrapper takes the first 5 byte
|
||||||
|
// Worst case scenario there must be a content type
|
||||||
|
if zero_padding_start_index > 6 {
|
||||||
|
zero_padding_start_index -= 1;
|
||||||
|
} else {
|
||||||
|
return (TlsContentType::Invalid, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(
|
||||||
|
TlsContentType::try_from(inner_plaintext[zero_padding_start_index-1]).unwrap(),
|
||||||
|
if zero_padding_start_index < inner_plaintext.len() {
|
||||||
|
Some(zero_padding_start_index)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Redo EE
|
// TODO: Redo EE
|
||||||
|
@ -255,7 +276,7 @@ fn parse_server_hello(bytes: &[u8]) -> IResult<&[u8], HandshakeData> {
|
||||||
// } TLSInnerPlaintext;
|
// } TLSInnerPlaintext;
|
||||||
|
|
||||||
fn parse_encrypted_extensions(bytes: &[u8]) -> IResult<&[u8], EncryptedExtensions> {
|
fn parse_encrypted_extensions(bytes: &[u8]) -> IResult<&[u8], EncryptedExtensions> {
|
||||||
let (mut rest, extension_length) = take(2_usize)(bytes)?;
|
let (rest, extension_length) = take(2_usize)(bytes)?;
|
||||||
let extension_length: u16 = NetworkEndian::read_u16(extension_length);
|
let extension_length: u16 = NetworkEndian::read_u16(extension_length);
|
||||||
let mut extension_length_counter: i32 = extension_length.into();
|
let mut extension_length_counter: i32 = extension_length.into();
|
||||||
let mut extension_vec: Vec<Extension> = Vec::new();
|
let mut extension_vec: Vec<Extension> = Vec::new();
|
||||||
|
@ -396,12 +417,6 @@ fn parse_certificate_verify(bytes: &[u8]) -> IResult<&[u8], CertificateVerify> {
|
||||||
signature_length
|
signature_length
|
||||||
))(bytes)?;
|
))(bytes)?;
|
||||||
|
|
||||||
log::info!("Sig scheme: {:?}, sig:len: {:?}, rest_len: {:?}",
|
|
||||||
signature_scheme,
|
|
||||||
signature_length,
|
|
||||||
rest.len()
|
|
||||||
);
|
|
||||||
|
|
||||||
let signature_scheme = SignatureScheme::try_from(
|
let signature_scheme = SignatureScheme::try_from(
|
||||||
NetworkEndian::read_u16(signature_scheme)
|
NetworkEndian::read_u16(signature_scheme)
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
119
src/session.rs
119
src/session.rs
|
@ -7,15 +7,10 @@ use chacha20poly1305::ChaCha20Poly1305;
|
||||||
use ccm::Ccm;
|
use ccm::Ccm;
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
use generic_array::GenericArray;
|
use generic_array::GenericArray;
|
||||||
use byteorder::{ByteOrder, NetworkEndian, BigEndian};
|
use byteorder::{ByteOrder, NetworkEndian};
|
||||||
use rsa::{RSAPublicKey, PublicKey, PaddingScheme, Hash as RSAHash};
|
use rsa::{RSAPublicKey, PublicKey, PaddingScheme, Hash as RSAHash};
|
||||||
use hmac::{ Hmac, Mac, NewMac };
|
use hmac::{ Hmac, Mac, NewMac };
|
||||||
|
|
||||||
use rand_core::RngCore;
|
|
||||||
|
|
||||||
use core::convert::AsRef;
|
|
||||||
use core::cell::RefCell;
|
|
||||||
|
|
||||||
use crate::tls::TlsState;
|
use crate::tls::TlsState;
|
||||||
use crate::tls_packet::CipherSuite;
|
use crate::tls_packet::CipherSuite;
|
||||||
use crate::key::*;
|
use crate::key::*;
|
||||||
|
@ -505,12 +500,15 @@ impl Session {
|
||||||
|
|
||||||
// Handle Ed25519 and p256 separately
|
// Handle Ed25519 and p256 separately
|
||||||
// These 2 algorithms have a mandated hash function
|
// These 2 algorithms have a mandated hash function
|
||||||
if signature_algorithm == SignatureScheme::ecdsa_secp256r1_sha256 ||
|
if signature_algorithm == SignatureScheme::ecdsa_secp256r1_sha256
|
||||||
signature_algorithm == SignatureScheme::ed25519
|
|
||||||
{
|
{
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if signature_algorithm == SignatureScheme::ed25519 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
// Get verification hash, and verify the signature
|
// Get verification hash, and verify the signature
|
||||||
use crate::tls_packet::SignatureScheme::*;
|
use crate::tls_packet::SignatureScheme::*;
|
||||||
|
|
||||||
|
@ -550,7 +548,6 @@ impl Session {
|
||||||
let verify_result = self.cert_rsa_public_key.take().unwrap().verify(
|
let verify_result = self.cert_rsa_public_key.take().unwrap().verify(
|
||||||
padding, &verify_hash, signature
|
padding, &verify_hash, signature
|
||||||
);
|
);
|
||||||
log::info!("Algorithm {:?} Certificate verify: {:?}", signature_algorithm, verify_result);
|
|
||||||
if verify_result.is_err() {
|
if verify_result.is_err() {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
@ -954,6 +951,10 @@ impl Session {
|
||||||
client_finished_slice: &[u8]
|
client_finished_slice: &[u8]
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
// Will change server & client key to application key,
|
||||||
|
// Reset sequence number
|
||||||
|
self.client_sequence_number = 0;
|
||||||
|
self.server_sequence_number = 0;
|
||||||
self.hash.update(client_finished_slice);
|
self.hash.update(client_finished_slice);
|
||||||
self.state = TlsState::CONNECTED;
|
self.state = TlsState::CONNECTED;
|
||||||
}
|
}
|
||||||
|
@ -1024,6 +1025,38 @@ impl Session {
|
||||||
self.client_handshake_cipher.as_ref().map(|cipher| cipher.get_cipher_suite_type())
|
self.client_handshake_cipher.as_ref().map(|cipher| cipher.get_cipher_suite_type())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Merge decryption methods
|
||||||
|
pub(crate) fn encrypt_application_data_in_place_detached(
|
||||||
|
&self,
|
||||||
|
associated_data: &[u8],
|
||||||
|
buffer: &mut [u8]
|
||||||
|
) -> Result<GenericArray<u8, U16>, Error> {
|
||||||
|
let (seq_num, nonce, cipher): (u64, &Vec<u8, U12>, &Cipher) = match self.role {
|
||||||
|
TlsRole::Client => {(
|
||||||
|
self.client_sequence_number,
|
||||||
|
self.client_application_nonce.as_ref().unwrap(),
|
||||||
|
self.client_application_cipher.as_ref().unwrap()
|
||||||
|
)},
|
||||||
|
TlsRole::Server => {(
|
||||||
|
self.server_sequence_number,
|
||||||
|
self.server_application_nonce.as_ref().unwrap(),
|
||||||
|
self.server_application_cipher.as_ref().unwrap()
|
||||||
|
)},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate XOR'ed nonce
|
||||||
|
let nonce: u128 = NetworkEndian::read_uint128(nonce, 12);
|
||||||
|
let clipped_seq_num: u128 = seq_num.into();
|
||||||
|
let mut processed_nonce: [u8; 12] = [0; 12];
|
||||||
|
NetworkEndian::write_uint128(&mut processed_nonce, nonce ^ clipped_seq_num, 12);
|
||||||
|
|
||||||
|
cipher.encrypt_in_place_detached(
|
||||||
|
&GenericArray::from_slice(&processed_nonce),
|
||||||
|
associated_data,
|
||||||
|
buffer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn encrypt_in_place(
|
pub(crate) fn encrypt_in_place(
|
||||||
&self,
|
&self,
|
||||||
associated_data: &[u8],
|
associated_data: &[u8],
|
||||||
|
@ -1086,6 +1119,43 @@ impl Session {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Merge decryption methods
|
||||||
|
// Take control of the entire decryption, manually invoke detached decryption
|
||||||
|
pub(crate) fn decrypt_application_data_in_place(
|
||||||
|
&self,
|
||||||
|
associated_data: &[u8],
|
||||||
|
buffer: &mut [u8],
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let (seq_num, nonce, cipher): (u64, &Vec<u8, U12>, &Cipher) = match self.role {
|
||||||
|
TlsRole::Client => {(
|
||||||
|
self.server_sequence_number,
|
||||||
|
self.server_application_nonce.as_ref().unwrap(),
|
||||||
|
self.server_application_cipher.as_ref().unwrap()
|
||||||
|
)},
|
||||||
|
TlsRole::Server => {(
|
||||||
|
self.client_sequence_number,
|
||||||
|
self.client_application_nonce.as_ref().unwrap(),
|
||||||
|
self.client_application_cipher.as_ref().unwrap()
|
||||||
|
)},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate XOR'ed nonce
|
||||||
|
let nonce: u128 = NetworkEndian::read_uint128(nonce, 12);
|
||||||
|
let clipped_seq_num: u128 = seq_num.into();
|
||||||
|
let mut processed_nonce: [u8; 12] = [0; 12];
|
||||||
|
NetworkEndian::write_uint128(&mut processed_nonce, nonce ^ clipped_seq_num, 12);
|
||||||
|
|
||||||
|
let buffer_size = buffer.len();
|
||||||
|
let tag = GenericArray::clone_from_slice(&buffer[(buffer_size-16)..]);
|
||||||
|
|
||||||
|
cipher.decrypt_in_place_detached(
|
||||||
|
&GenericArray::from_slice(&processed_nonce),
|
||||||
|
associated_data,
|
||||||
|
&mut buffer[..(buffer_size-16)],
|
||||||
|
&tag
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn decrypt_in_place(
|
pub(crate) fn decrypt_in_place(
|
||||||
&self,
|
&self,
|
||||||
associated_data: &[u8],
|
associated_data: &[u8],
|
||||||
|
@ -1283,6 +1353,37 @@ impl Cipher {
|
||||||
}.map_err(|_| Error::DecryptionError)
|
}.map_err(|_| Error::DecryptionError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn decrypt_in_place_detached(
|
||||||
|
&self,
|
||||||
|
nonce: &GenericArray<u8, U12>,
|
||||||
|
associated_data: &[u8],
|
||||||
|
buffer: &mut [u8],
|
||||||
|
tag: &GenericArray<u8, U16>
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
match self {
|
||||||
|
Cipher::Aes128Gcm { aes128gcm } => {
|
||||||
|
aes128gcm.decrypt_in_place_detached(
|
||||||
|
nonce, associated_data, buffer, tag
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Cipher::Aes256Gcm { aes256gcm } => {
|
||||||
|
aes256gcm.decrypt_in_place_detached(
|
||||||
|
nonce, associated_data, buffer, tag
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Cipher::Chacha20poly1305 { chacha20poly1305 } => {
|
||||||
|
chacha20poly1305.decrypt_in_place_detached(
|
||||||
|
nonce, associated_data, buffer, tag
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Cipher::Ccm { ccm } => {
|
||||||
|
ccm.decrypt_in_place_detached(
|
||||||
|
nonce, associated_data, buffer, tag
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.map_err(|_| Error::DecryptionError)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn get_cipher_suite_type(&self) -> CipherSuite {
|
pub(crate) fn get_cipher_suite_type(&self) -> CipherSuite {
|
||||||
match self {
|
match self {
|
||||||
Cipher::Aes128Gcm { aes128gcm } => {
|
Cipher::Aes128Gcm { aes128gcm } => {
|
||||||
|
|
157
src/tls.rs
157
src/tls.rs
|
@ -1,12 +1,8 @@
|
||||||
use smoltcp::socket::TcpSocket;
|
use smoltcp::socket::TcpSocket;
|
||||||
use smoltcp::socket::TcpState;
|
use smoltcp::socket::TcpState;
|
||||||
use smoltcp::socket::Socket;
|
|
||||||
use smoltcp::socket::AnySocket;
|
|
||||||
use smoltcp::socket::SocketRef;
|
|
||||||
use smoltcp::socket::SocketHandle;
|
use smoltcp::socket::SocketHandle;
|
||||||
use smoltcp::socket::SocketSet;
|
use smoltcp::socket::SocketSet;
|
||||||
use smoltcp::socket::TcpSocketBuffer;
|
use smoltcp::socket::TcpSocketBuffer;
|
||||||
use smoltcp::wire::Ipv4Address;
|
|
||||||
use smoltcp::wire::IpEndpoint;
|
use smoltcp::wire::IpEndpoint;
|
||||||
use smoltcp::Result;
|
use smoltcp::Result;
|
||||||
use smoltcp::Error;
|
use smoltcp::Error;
|
||||||
|
@ -14,33 +10,29 @@ use smoltcp::iface::EthernetInterface;
|
||||||
use smoltcp::time::Instant;
|
use smoltcp::time::Instant;
|
||||||
use smoltcp::phy::Device;
|
use smoltcp::phy::Device;
|
||||||
|
|
||||||
use byteorder::{ByteOrder, NetworkEndian, BigEndian};
|
use byteorder::{ByteOrder, NetworkEndian};
|
||||||
use generic_array::GenericArray;
|
use generic_array::GenericArray;
|
||||||
|
|
||||||
use core::convert::TryInto;
|
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
use p256::{EncodedPoint, AffinePoint, ecdh::EphemeralSecret, ecdh::SharedSecret};
|
use p256::{EncodedPoint, ecdh::EphemeralSecret};
|
||||||
use aes_gcm::{Aes128Gcm, Aes256Gcm};
|
use ccm::consts::*;
|
||||||
use chacha20poly1305::{ChaCha20Poly1305, Key};
|
use aes_gcm::AeadInPlace;
|
||||||
use ccm::{Ccm, consts::*};
|
|
||||||
use aes_gcm::aes::Aes128;
|
|
||||||
use aes_gcm::{AeadInPlace, NewAead};
|
|
||||||
use sha2::{Sha256, Sha384, Sha512, Digest};
|
|
||||||
|
|
||||||
use nom::bytes::complete::take;
|
use nom::bytes::complete::take;
|
||||||
use nom::IResult;
|
|
||||||
use nom::error::make_error;
|
|
||||||
use nom::error::ErrorKind;
|
use nom::error::ErrorKind;
|
||||||
|
|
||||||
use alloc::vec::{ self, Vec };
|
use alloc::vec::Vec;
|
||||||
use heapless::Vec as HeaplessVec;
|
use heapless::Vec as HeaplessVec;
|
||||||
|
|
||||||
use crate::Error as TlsError;
|
|
||||||
use crate::tls_packet::*;
|
use crate::tls_packet::*;
|
||||||
use crate::parse::{ parse_tls_repr, parse_handshake, parse_inner_plaintext_for_handshake };
|
use crate::parse::{
|
||||||
|
parse_tls_repr,
|
||||||
|
parse_inner_plaintext_for_handshake,
|
||||||
|
get_content_type_inner_plaintext
|
||||||
|
};
|
||||||
use crate::buffer::TlsBuffer;
|
use crate::buffer::TlsBuffer;
|
||||||
use crate::session::{Session, TlsRole};
|
use crate::session::{Session, TlsRole};
|
||||||
use crate::certificate::validate_root_certificate;
|
use crate::certificate::validate_root_certificate;
|
||||||
|
@ -183,7 +175,7 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
|
||||||
|
|
||||||
// Send client Finished to end handshake
|
// Send client Finished to end handshake
|
||||||
TlsState::SERVER_CONNECTED => {
|
TlsState::SERVER_CONNECTED => {
|
||||||
let mut inner_plaintext: HeaplessVec<u8, U64> = {
|
let inner_plaintext: HeaplessVec<u8, U64> = {
|
||||||
let verify_data = self.session.borrow()
|
let verify_data = self.session.borrow()
|
||||||
.get_client_finished_verify_data();
|
.get_client_finished_verify_data();
|
||||||
let mut handshake_header: [u8; 4] = [20, 0, 0, 0];
|
let mut handshake_header: [u8; 4] = [20, 0, 0, 0];
|
||||||
|
@ -209,13 +201,14 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
|
||||||
iface.poll(sockets, now);
|
iface.poll(sockets, now);
|
||||||
|
|
||||||
// Read for TLS packet
|
// Read for TLS packet
|
||||||
|
// Proposition: Decouple all data from TLS record layer before processing
|
||||||
let mut array: [u8; 2048] = [0; 2048];
|
let mut array: [u8; 2048] = [0; 2048];
|
||||||
let mut tls_repr_vec = self.recv_tls_repr(sockets, &mut array)?;
|
let mut tls_repr_vec = self.recv_tls_repr(sockets, &mut array)?;
|
||||||
|
|
||||||
// Take the TLS representation out of the vector,
|
// Take the TLS representation out of the vector,
|
||||||
// Process as a queue
|
// Process as a queue
|
||||||
let tls_repr_vec_size = tls_repr_vec.len();
|
let tls_repr_vec_size = tls_repr_vec.len();
|
||||||
for index in 0..tls_repr_vec_size {
|
for _index in 0..tls_repr_vec_size {
|
||||||
let repr = tls_repr_vec.remove(0);
|
let repr = tls_repr_vec.remove(0);
|
||||||
self.process(repr)?;
|
self.process(repr)?;
|
||||||
}
|
}
|
||||||
|
@ -393,7 +386,7 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let parse_result = parse_inner_plaintext_for_handshake(&payload);
|
let parse_result = parse_inner_plaintext_for_handshake(&payload);
|
||||||
let (_, (mut handshake_slice, mut handshake_vec)) =
|
let (_, (handshake_slice, mut handshake_vec)) =
|
||||||
parse_result.map_err(|_| Error::Unrecognized)?;
|
parse_result.map_err(|_| Error::Unrecognized)?;
|
||||||
|
|
||||||
// Verify that it is indeed an EE
|
// Verify that it is indeed an EE
|
||||||
|
@ -409,7 +402,7 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
|
||||||
// Extension processing is therefore skipped
|
// Extension processing is therefore skipped
|
||||||
// Update hash of the session, get EE by taking appropriate length of data
|
// Update hash of the session, get EE by taking appropriate length of data
|
||||||
// Length of handshake header is 4
|
// Length of handshake header is 4
|
||||||
let (handshake_slice, ee_slice) =
|
let (_handshake_slice, ee_slice) =
|
||||||
take::<_, _, (&[u8], ErrorKind)>(
|
take::<_, _, (&[u8], ErrorKind)>(
|
||||||
might_be_ee.length + 4
|
might_be_ee.length + 4
|
||||||
)(handshake_slice)
|
)(handshake_slice)
|
||||||
|
@ -468,13 +461,17 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
|
||||||
|
|
||||||
// TODO: Process Certificate
|
// TODO: Process Certificate
|
||||||
let cert = might_be_cert.get_asn1_der_certificate().unwrap();
|
let cert = might_be_cert.get_asn1_der_certificate().unwrap();
|
||||||
log::info!("Certificate validation {:?}",
|
|
||||||
validate_root_certificate(cert)
|
// TODO: Replace this block after implementing a proper
|
||||||
);
|
// certificate verification procdeure
|
||||||
|
match validate_root_certificate(cert) {
|
||||||
|
Ok(true) => {},
|
||||||
|
_ => panic!("Certificate does not match")
|
||||||
|
}
|
||||||
|
|
||||||
// Update session TLS state to WAIT_CV
|
// Update session TLS state to WAIT_CV
|
||||||
// Length of handshake header is 4
|
// Length of handshake header is 4
|
||||||
let (handshake_slice, cert_slice) =
|
let (_handshake_slice, cert_slice) =
|
||||||
take::<_, _, (&[u8], ErrorKind)>(
|
take::<_, _, (&[u8], ErrorKind)>(
|
||||||
might_be_cert.length + 4
|
might_be_cert.length + 4
|
||||||
)(handshake_slice)
|
)(handshake_slice)
|
||||||
|
@ -527,7 +524,7 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
|
||||||
|
|
||||||
// Take out the portion for CertificateVerify
|
// Take out the portion for CertificateVerify
|
||||||
// Length of handshake header is 4
|
// Length of handshake header is 4
|
||||||
let (handshake_slice, cert_verify_slice) =
|
let (_handshake_slice, cert_verify_slice) =
|
||||||
take::<_, _, (&[u8], ErrorKind)>(
|
take::<_, _, (&[u8], ErrorKind)>(
|
||||||
might_be_cert_verify.length + 4
|
might_be_cert_verify.length + 4
|
||||||
)(handshake_slice)
|
)(handshake_slice)
|
||||||
|
@ -567,7 +564,6 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
|
||||||
&mut payload
|
&mut payload
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
log::info!("decrypted wait_fin payload: {:?}", payload);
|
|
||||||
|
|
||||||
// Parse the TLS inner ciphertext as a Finished handshake
|
// Parse the TLS inner ciphertext as a Finished handshake
|
||||||
let parse_result = parse_inner_plaintext_for_handshake(&payload);
|
let parse_result = parse_inner_plaintext_for_handshake(&payload);
|
||||||
|
@ -596,7 +592,6 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
|
||||||
server_finished_slice,
|
server_finished_slice,
|
||||||
might_be_server_finished.get_verify_data().unwrap()
|
might_be_server_finished.get_verify_data().unwrap()
|
||||||
);
|
);
|
||||||
log::info!("Computed secret");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {},
|
_ => {},
|
||||||
|
@ -662,14 +657,13 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
|
||||||
// 2. Add TLS header in front of application data
|
// 2. Add TLS header in front of application data
|
||||||
// Input should be inner plaintext
|
// Input should be inner plaintext
|
||||||
// Note: Do not put this slice into the transcript hash. It is polluted.
|
// Note: Do not put this slice into the transcript hash. It is polluted.
|
||||||
|
// TODO: Rename this function. It is only good for client finished
|
||||||
fn send_application_slice(&self, sockets: &mut SocketSet, slice: &mut [u8]) -> Result<()> {
|
fn send_application_slice(&self, sockets: &mut SocketSet, slice: &mut [u8]) -> Result<()> {
|
||||||
let mut tcp_socket = sockets.get::<TcpSocket>(self.tcp_handle);
|
let mut tcp_socket = sockets.get::<TcpSocket>(self.tcp_handle);
|
||||||
if !tcp_socket.can_send() {
|
if !tcp_socket.can_send() {
|
||||||
return Err(Error::Illegal);
|
return Err(Error::Illegal);
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("Delivered slice: {:?}", slice);
|
|
||||||
|
|
||||||
// Borrow session in advance
|
// Borrow session in advance
|
||||||
let mut client_session = self.session.borrow_mut();
|
let mut client_session = self.session.borrow_mut();
|
||||||
|
|
||||||
|
@ -708,7 +702,7 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
|
||||||
fn recv_tls_repr<'a>(&'a self, sockets: &mut SocketSet, byte_array: &'a mut [u8]) -> Result<Vec::<TlsRepr>> {
|
fn recv_tls_repr<'a>(&'a self, sockets: &mut SocketSet, byte_array: &'a mut [u8]) -> Result<Vec::<TlsRepr>> {
|
||||||
let mut tcp_socket = sockets.get::<TcpSocket>(self.tcp_handle);
|
let mut tcp_socket = sockets.get::<TcpSocket>(self.tcp_handle);
|
||||||
if !tcp_socket.can_recv() {
|
if !tcp_socket.can_recv() {
|
||||||
return Ok((Vec::new()));
|
return Ok(Vec::new());
|
||||||
}
|
}
|
||||||
let array_size = tcp_socket.recv_slice(byte_array)?;
|
let array_size = tcp_socket.recv_slice(byte_array)?;
|
||||||
let mut vec: Vec<TlsRepr> = Vec::new();
|
let mut vec: Vec<TlsRepr> = Vec::new();
|
||||||
|
@ -727,4 +721,103 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn recv_slice(&self, sockets: &mut SocketSet, data: &mut [u8]) -> Result<usize> {
|
||||||
|
let mut tcp_socket = sockets.get::<TcpSocket>(self.tcp_handle);
|
||||||
|
if !tcp_socket.can_recv() {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let recv_slice_size = tcp_socket.recv_slice(data)?;
|
||||||
|
// Encrypted data need a TLS record wrapper (5 bytes)
|
||||||
|
// Authentication tag (16 bytes, for all supported AEADs)
|
||||||
|
// Content type byte (1 byte)
|
||||||
|
// Zero paddings (>=0 bytes)
|
||||||
|
if recv_slice_size < 22 {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Associated Data
|
||||||
|
let mut session = self.session.borrow_mut();
|
||||||
|
let mut associated_data: [u8; 5] = [0; 5];
|
||||||
|
associated_data.clone_from_slice(&data[..5]);
|
||||||
|
log::info!("Received encrypted appdata: {:?}", &data[..recv_slice_size]);
|
||||||
|
|
||||||
|
// Dump association data (TLS Record wrapper)
|
||||||
|
// Only decrypt application data
|
||||||
|
// Always increment sequence number after decrpytion
|
||||||
|
session.decrypt_application_data_in_place(
|
||||||
|
&associated_data,
|
||||||
|
&mut data[5..recv_slice_size]
|
||||||
|
).unwrap();
|
||||||
|
session.increment_server_sequence_number();
|
||||||
|
|
||||||
|
// Make sure it is application data
|
||||||
|
let (content_type, padding_start_index) =
|
||||||
|
get_content_type_inner_plaintext(&data[..(recv_slice_size-16)]);
|
||||||
|
|
||||||
|
// If it is not application data, handle it internally
|
||||||
|
if content_type != TlsContentType::ApplicationData {
|
||||||
|
// TODO:: Implement key update
|
||||||
|
log::info!("Other decrypted: {:?}", &data[..(recv_slice_size-16)]);
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, it is surely application data.
|
||||||
|
// Prune TLS record wrapper (5 bytes) from data.
|
||||||
|
data.rotate_left(5);
|
||||||
|
|
||||||
|
// Remove extra length:
|
||||||
|
// 5 bytes of TLS record header
|
||||||
|
// 16 bytes of authentication tag (included in zero padding search fn)
|
||||||
|
// 1 byte of content type
|
||||||
|
// zero paddings, variated length
|
||||||
|
let actual_application_data_length = recv_slice_size - 5 - 1
|
||||||
|
- padding_start_index.map_or(16,
|
||||||
|
|start| recv_slice_size - start
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(actual_application_data_length)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_slice(&self, sockets: &mut SocketSet, data: &[u8]) -> Result<()> {
|
||||||
|
// Sending order:
|
||||||
|
// 1. Associated data/ TLS Record layer
|
||||||
|
// 2. Encrypted { Payload (data) | Content type: Application Data }
|
||||||
|
// 3. Authentication tag (16 bytes for all supported AEADs)
|
||||||
|
let mut associated_data: [u8; 5] = [
|
||||||
|
0x17, // Application data
|
||||||
|
0x03, 0x03, // TLS 1.3 record disguised as TLS 1.2
|
||||||
|
0x00, 0x00 // Length of encrypted data, yet to be determined
|
||||||
|
];
|
||||||
|
|
||||||
|
NetworkEndian::write_u16(&mut associated_data[3..5],
|
||||||
|
u16::try_from(data.len()).unwrap() // Payload length
|
||||||
|
+ 1 // Content type length
|
||||||
|
+ 16 // Auth tag length
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: Dynamically size typed Heapless Vec on socket instantiation,
|
||||||
|
// just like MiniMQ
|
||||||
|
let mut vec: HeaplessVec<u8, U1024> = HeaplessVec::from_slice(data).unwrap();
|
||||||
|
vec.push(0x17); // Content type
|
||||||
|
|
||||||
|
let mut session = self.session.borrow_mut();
|
||||||
|
let tag = session.encrypt_application_data_in_place_detached(
|
||||||
|
&associated_data,
|
||||||
|
&mut vec
|
||||||
|
).unwrap();
|
||||||
|
session.increment_client_sequence_number();
|
||||||
|
|
||||||
|
let mut tcp_socket = sockets.get::<TcpSocket>(self.tcp_handle);
|
||||||
|
if !tcp_socket.can_send() {
|
||||||
|
return Err(Error::Illegal);
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp_socket.send_slice(&associated_data)?;
|
||||||
|
tcp_socket.send_slice(&vec)?;
|
||||||
|
tcp_socket.send_slice(&tag)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
use byteorder::{ByteOrder, NetworkEndian, BigEndian};
|
|
||||||
use num_enum::IntoPrimitive;
|
use num_enum::IntoPrimitive;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
|
|
||||||
use rand_core::RngCore;
|
use p256::{EncodedPoint, ecdh::EphemeralSecret};
|
||||||
use rand_core::CryptoRng;
|
|
||||||
|
|
||||||
use p256::{EncodedPoint, AffinePoint, ecdh::{EphemeralSecret, SharedSecret}};
|
|
||||||
|
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
|
@ -242,7 +238,6 @@ pub(crate) enum HandshakeData<'a> {
|
||||||
EncryptedExtensions(EncryptedExtensions),
|
EncryptedExtensions(EncryptedExtensions),
|
||||||
Certificate(Certificate<'a>),
|
Certificate(Certificate<'a>),
|
||||||
CertificateVerify(CertificateVerify<'a>),
|
CertificateVerify(CertificateVerify<'a>),
|
||||||
FinishedNeedParse,
|
|
||||||
Finished(Finished<'a>),
|
Finished(Finished<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +245,7 @@ impl<'a> HandshakeData<'a> {
|
||||||
pub(crate) fn get_length(&self) -> usize {
|
pub(crate) fn get_length(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
HandshakeData::ClientHello(data) => data.get_length(),
|
HandshakeData::ClientHello(data) => data.get_length(),
|
||||||
HandshakeData::ServerHello(data) => todo!(),
|
HandshakeData::ServerHello(_data) => todo!(),
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -288,7 +283,7 @@ impl<'a> ClientHello<'a> {
|
||||||
let mut versions = Vec::new();
|
let mut versions = Vec::new();
|
||||||
versions.push(TlsVersion::Tls13);
|
versions.push(TlsVersion::Tls13);
|
||||||
|
|
||||||
let mut content = SupportedVersions::ClientHello {
|
let content = SupportedVersions::ClientHello {
|
||||||
length,
|
length,
|
||||||
versions,
|
versions,
|
||||||
};
|
};
|
||||||
|
@ -308,7 +303,7 @@ impl<'a> ClientHello<'a> {
|
||||||
pub(crate) fn add_sh_supported_versions(mut self) -> Self {
|
pub(crate) fn add_sh_supported_versions(mut self) -> Self {
|
||||||
let selected_version = TlsVersion::Tls13;
|
let selected_version = TlsVersion::Tls13;
|
||||||
|
|
||||||
let mut content = SupportedVersions::ServerHello {
|
let content = SupportedVersions::ServerHello {
|
||||||
selected_version
|
selected_version
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -570,6 +565,7 @@ pub(crate) struct Cookie {
|
||||||
cookie: Vec<u8>,
|
cookie: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
pub(crate) enum SignatureScheme {
|
pub(crate) enum SignatureScheme {
|
||||||
|
@ -610,6 +606,7 @@ impl SignatureSchemeList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
pub(crate) enum NamedGroup {
|
pub(crate) enum NamedGroup {
|
||||||
|
@ -680,6 +677,7 @@ impl KeyShareEntryContent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
pub(crate) enum NameType {
|
pub(crate) enum NameType {
|
||||||
|
|
Loading…
Reference in New Issue