socket: handle concatenated handshake

This commit is contained in:
occheung 2020-11-03 11:34:26 +08:00
parent 59dc5873c5
commit c57fc79f6b
4 changed files with 155 additions and 154 deletions

View File

@ -26,7 +26,7 @@ default-features = true
features = [ "heapless" ]
[dependencies.smoltcp]
version = "0.6.0"
git = "https://github.com/smoltcp-rs/smoltcp.git"
default-features = false
features = ["ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp", "alloc"]
@ -61,11 +61,6 @@ version = "5.1.2"
default-features = false
features = []
[dependencies.arraydeque]
version = "0.4.5"
default-features = false
features = [ "use_generic_array" ]
[dependencies.simple_logger]
version = "1.11.0"
optional = true

View File

@ -31,7 +31,8 @@ use core::convert::TryInto;
use alloc::vec::Vec;
pub(crate) fn parse_tls_repr(bytes: &[u8]) -> IResult<&[u8], TlsRepr> {
// Return handshake/payload slice and TLS Record
pub(crate) fn parse_tls_repr(bytes: &[u8]) -> IResult<&[u8], (&[u8], TlsRepr)> {
let content_type = take(1_usize);
let version = take(2_usize);
let length = take(2_usize);
@ -51,6 +52,9 @@ pub(crate) fn parse_tls_repr(bytes: &[u8]) -> IResult<&[u8], TlsRepr> {
handshake: None,
};
let (rest, bytes) = take(repr.length)(rest)?;
// Store a copy of the TLS Handshake slice to return
let repr_slice_clone = bytes;
{
use crate::tls_packet::TlsContentType::*;
match repr.content_type {
@ -68,7 +72,7 @@ pub(crate) fn parse_tls_repr(bytes: &[u8]) -> IResult<&[u8], TlsRepr> {
_ => todo!()
}
}
Ok((rest, repr))
Ok((rest, (repr_slice_clone, repr)))
}
// Convert TlsInnerPlainText in RFC 8446 into Handshake
@ -76,9 +80,9 @@ pub(crate) fn parse_tls_repr(bytes: &[u8]) -> IResult<&[u8], TlsRepr> {
// 1. Handshake can coalesced into a larger TLS record
// 2. Content type and zero paddings at the end
// Return handshake slice for hashing
pub(crate) fn parse_inner_plaintext_for_handshake(bytes: &[u8]) -> IResult<&[u8], (&[u8], Vec<HandshakeRepr>)> {
pub(crate) fn parse_inner_plaintext_for_handshake(bytes: &[u8]) -> IResult<&[u8], Vec<(&[u8], HandshakeRepr)>> {
let mut remaining_bytes = bytes;
let mut handshake_vec: Vec<HandshakeRepr> = Vec::new();
let mut handshake_vec: Vec<(&[u8], HandshakeRepr)> = Vec::new();
loop {
// Perform check on the number of remaining bytes
@ -97,20 +101,19 @@ pub(crate) fn parse_inner_plaintext_for_handshake(bytes: &[u8]) -> IResult<&[u8]
)(remaining_bytes)?;
return Ok((
&[],
(
// A concatenation of all handshakes received
// The remaining content_type byte and zero paddings are stripped
&bytes[
..(bytes.len()-remaining_bytes.len())
],
// // A concatenation of all handshakes received
// // The remaining content_type byte and zero paddings are stripped
// &bytes[
// ..(bytes.len()-remaining_bytes.len())
// ],
handshake_vec
)
));
}
let (rem, handshake_repr) = parse_handshake(remaining_bytes)?;
let handshake_slice = &remaining_bytes[..(remaining_bytes.len()-rem.len())];
remaining_bytes = rem;
handshake_vec.push(handshake_repr);
handshake_vec.push((handshake_slice, handshake_repr));
}
}

View File

@ -1121,6 +1121,7 @@ impl Session {
// TODO: Merge decryption methods
// Take control of the entire decryption, manually invoke detached decryption
// TODO: Bad naming, it should say the KEY of application data
pub(crate) fn decrypt_application_data_in_place(
&self,
associated_data: &[u8],
@ -1145,6 +1146,7 @@ impl Session {
let mut processed_nonce: [u8; 12] = [0; 12];
NetworkEndian::write_uint128(&mut processed_nonce, nonce ^ clipped_seq_num, 12);
// Duplicate authentication tag
let buffer_size = buffer.len();
let tag = GenericArray::clone_from_slice(&buffer[(buffer_size-16)..]);
@ -1156,6 +1158,7 @@ impl Session {
)
}
// Decryption using handshake keys
pub(crate) fn decrypt_in_place(
&self,
associated_data: &[u8],
@ -1187,6 +1190,44 @@ impl Session {
)
}
// A veriant for handshake decryption in-place and detached
// Caller need to manually discard the authentication bytes
pub(crate) fn decrypt_in_place_detached(
&self,
associated_data: &[u8],
buffer: &mut [u8]
) -> Result<(), Error> {
let (seq_num, nonce, cipher): (u64, &Vec<u8, U12>, &Cipher) = match self.role {
TlsRole::Server => {(
self.client_sequence_number,
self.client_handshake_nonce.as_ref().unwrap(),
self.client_handshake_cipher.as_ref().unwrap()
)},
TlsRole::Client => {(
self.server_sequence_number,
self.server_handshake_nonce.as_ref().unwrap(),
self.server_handshake_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);
// Duplicate authentication tag
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 increment_client_sequence_number(&mut self) {
self.client_sequence_number += 1;
}

View File

@ -23,6 +23,7 @@ use aes_gcm::AeadInPlace;
use nom::bytes::complete::take;
use nom::error::ErrorKind;
use nom::combinator::complete;
use alloc::vec::Vec;
use heapless::Vec as HeaplessVec;
@ -202,6 +203,7 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
// Read for TLS packet
// Proposition: Decouple all data from TLS record layer before processing
// Recouple a brand new TLS record wrapper
let mut array: [u8; 2048] = [0; 2048];
let mut tls_repr_vec = self.recv_tls_repr(sockets, &mut array)?;
@ -209,15 +211,84 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
// Process as a queue
let tls_repr_vec_size = tls_repr_vec.len();
for _index in 0..tls_repr_vec_size {
let repr = tls_repr_vec.remove(0);
self.process(repr)?;
let (repr_slice, mut repr) = tls_repr_vec.remove(0);
// TODO:
// Check TLS content type
// If application data, decrypt before process.
// If there are multiple handshakes within a record,
// give each of them a unique TLS record wrapper and process
// If a pure application data is found, sliently ignore
// Otherwise process directly
log::info!("Record type: {:?}", repr.content_type);
if repr.content_type == TlsContentType::ApplicationData {
log::info!("Found application data");
// Take the payload out of TLS Record and decrypt
let mut app_data = repr.payload.take().unwrap();
let mut associated_data = [0; 5];
associated_data[0] = repr.content_type.into();
NetworkEndian::write_u16(
&mut associated_data[1..3],
repr.version.into()
);
NetworkEndian::write_u16(
&mut associated_data[3..5],
repr.length
);
{
let mut session = self.session.borrow_mut();
session.decrypt_in_place_detached(
&associated_data,
&mut app_data
).unwrap();
// log::info!("Decypted data: {:?}", app_data);
session.increment_server_sequence_number();
}
// Discard last 16 bytes (auth tag)
let inner_plaintext = &app_data[..app_data.len()-16];
let (inner_content_type, _) = get_content_type_inner_plaintext(
inner_plaintext
);
if inner_content_type != TlsContentType::Handshake {
// Silently ignore non-handshakes
continue;
}
let (_, mut inner_handshakes) = complete(
parse_inner_plaintext_for_handshake
)(inner_plaintext).unwrap();
// Sequentially process all handshakes
let num_of_handshakes = inner_handshakes.len();
for _ in 0..num_of_handshakes {
let (handshake_slice, handshake_repr) = inner_handshakes.remove(0);
self.process(
handshake_slice,
TlsRepr {
content_type: TlsContentType::Handshake,
version: repr.version,
length: u16::try_from(handshake_repr.length).unwrap() + 4,
payload: None,
handshake: Some(handshake_repr)
}
)?;
}
}
else {
self.process(repr_slice, repr)?;
log::info!("Processed record");
}
}
Ok(self.session.borrow().has_completed_handshake())
}
// Process TLS ingress during handshake
fn process(&self, mut repr: TlsRepr) -> Result<()> {
// The slice should ONLY include handshake overhead
// i.e. No 5 bytes of TLS Record
// YES 4 bytes of HandshakeRepr, everything within the same handshake
fn process(&self, handshake_slice: &[u8], mut repr: TlsRepr) -> Result<()> {
// Change_cipher_spec check:
// Must receive CCS before recv peer's FINISH message
// i.e. Must happen after START and before CONNECTED
@ -226,8 +297,10 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
// Drop the message and update `received_change_cipher_spec`
// Note: CSS doesn't count as a proper record, no need to increment sequence number
if repr.is_change_cipher_spec() {
let mut session = self.session.borrow_mut();
log::info!("Change Cipher Spec");
let mut session = self.session.try_borrow_mut().expect("Cannot borrow mut");
session.receive_change_cipher_spec();
log::info!("Changed Cipher Spec");
return Ok(())
}
@ -357,34 +430,9 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
// Expect encrypted extensions after receiving SH
TlsState::WAIT_EE => {
// Check that the packet is classified as application data
if !repr.is_application_data() {
// Abort communication, this affect IV calculation
todo!()
}
// ExcepytedExtensions are disguised as ApplicationData
// Pull out the `payload` from TlsRepr, decrypt as EE
let mut payload = repr.payload.take().unwrap();
let mut array: [u8; 5] = [0; 5];
let mut buffer = TlsBuffer::new(&mut array);
buffer.write_u8(repr.content_type.into())?;
buffer.write_u16(repr.version.into())?;
buffer.write_u16(repr.length)?;
let associated_data: &[u8] = buffer.into();
{
self.session.borrow_mut().decrypt_in_place(
associated_data,
&mut payload
);
}
let parse_result = parse_inner_plaintext_for_handshake(&payload);
let (_, (handshake_slice, mut handshake_vec)) =
parse_result.map_err(|_| Error::Unrecognized)?;
// Verify that it is indeed an EE
let might_be_ee = handshake_vec.remove(0);
// let might_be_ee = handshake_vec.remove(0);
let might_be_ee = repr.handshake.take().unwrap();
if might_be_ee.get_msg_type() != HandshakeType::EncryptedExtensions {
// Process the other handshakes in "handshake_vec"
todo!()
@ -407,47 +455,15 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
&ee_slice
);
// TODO: Handle in WAIT_CERT_CR if there are still unprocessed handshakes
// Ideas: 1. Split off WAIT_CERT_CR handling into a separate function
// so WAIT_EE branch can jsut call WAIT_CERT_CR branch
// if there are extra handshake unprocessed
// 2. Merge state dependent listeners into 1 branch, execute conditionally
log::info!("Received EE");
},
// In this stage, wait for a certificate from server
// Parse the certificate and check its content
TlsState::WAIT_CERT_CR => {
// Check that the packet is classified as application data
// Certificates transfer is disguised as application data
if !repr.is_application_data() {
// Abort communication, this affect IV calculation
todo!()
}
// Pull out the `payload` from TlsRepr, decrypt as CERT
let mut payload = repr.payload.take().unwrap();
// Instantiate associated data and decrypt
let mut array: [u8; 5] = [0; 5];
let mut buffer = TlsBuffer::new(&mut array);
buffer.write_u8(repr.content_type.into())?;
buffer.write_u16(repr.version.into())?;
buffer.write_u16(repr.length)?;
let associated_data: &[u8] = buffer.into();
{
self.session.borrow_mut().decrypt_in_place(
associated_data,
&mut payload
);
}
// Parse the certificate from TLS payload
let parse_result = parse_inner_plaintext_for_handshake(&payload);
let (_, (handshake_slice, mut handshake_vec)) = parse_result
.map_err(|_| Error::Unrecognized)?;
// Verify that it is indeed an Certificate
let might_be_cert = handshake_vec.remove(0);
// let might_be_cert = handshake_vec.remove(0);
let might_be_cert = repr.handshake.take().unwrap();
if might_be_cert.get_msg_type() != HandshakeType::Certificate {
// Process the other handshakes in "handshake_vec"
todo!()
@ -476,41 +492,15 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
&cert_slice,
cert.return_rsa_public_key().unwrap()
);
log::info!("Received WAIT_CERT_CR");
},
// In this stage, server will eventually send a CertificateVerify
// Verify that the signature is indeed correct
TlsState::WAIT_CV => {
// CertificateVerify is disguised as Application Data
if !repr.is_application_data() {
// Abort communication, this affect IV calculation
todo!()
}
// Pull out the `payload` from TlsRepr, decrypt as CV
let mut payload = repr.payload.take().unwrap();
// Instantiate associated data and decrypt
let mut array: [u8; 5] = [0; 5];
let mut buffer = TlsBuffer::new(&mut array);
buffer.write_u8(repr.content_type.into())?;
buffer.write_u16(repr.version.into())?;
buffer.write_u16(repr.length)?;
let associated_data: &[u8] = buffer.into();
{
self.session.borrow_mut().decrypt_in_place(
associated_data,
&mut payload
);
}
// Parse the certificate from TLS payload
let parse_result = parse_inner_plaintext_for_handshake(&payload);
let (_, (handshake_slice, mut handshake_vec)) = parse_result
.map_err(|_| Error::Unrecognized)?;
// Ensure that it is CertificateVerify
let might_be_cert_verify = handshake_vec.remove(0);
// let might_be_cert_verify = handshake_vec.remove(0);
let might_be_cert_verify = repr.handshake.take().unwrap();
if might_be_cert_verify.get_msg_type() != HandshakeType::CertificateVerify {
// Process the other handshakes in "handshake_vec"
todo!()
@ -532,40 +522,14 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
sig_alg,
signature
);
log::info!("Received CV");
},
// Client will receive a Finished handshake from server
TlsState::WAIT_FINISHED => {
// Finished is disguised as Application Data
if !repr.is_application_data() {
// Abort communication, this affect IV calculation
todo!()
}
// Pull out the `payload` from TlsRepr, decrypt as Finished
let mut payload = repr.payload.take().unwrap();
// Instantiate associated data and decrypt
let mut array: [u8; 5] = [0; 5];
let mut buffer = TlsBuffer::new(&mut array);
buffer.write_u8(repr.content_type.into())?;
buffer.write_u16(repr.version.into())?;
buffer.write_u16(repr.length)?;
let associated_data: &[u8] = buffer.into();
{
self.session.borrow_mut().decrypt_in_place(
associated_data,
&mut payload
);
}
// Parse the TLS inner ciphertext as a Finished handshake
let parse_result = parse_inner_plaintext_for_handshake(&payload);
let (_, (handshake_slice, mut handshake_vec)) = parse_result
.map_err(|_| Error::Unrecognized)?;
// Ensure that it is Finished
let might_be_server_finished = handshake_vec.remove(0);
// let might_be_server_finished = handshake_vec.remove(0);
let might_be_server_finished = repr.handshake.take().unwrap();
if might_be_server_finished.get_msg_type() != HandshakeType::Finished {
// Process the other handshakes in "handshake_vec"
todo!()
@ -586,15 +550,11 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
server_finished_slice,
might_be_server_finished.get_verify_data().unwrap()
);
log::info!("Received server FIN");
}
_ => {},
}
// A TLS Record was received and processed and verified
// Increment sequence number
self.session.borrow_mut().increment_server_sequence_number();
Ok(())
}
@ -693,18 +653,20 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
// Generic inner recv method, through TCP socket
// A TCP packet can contain multiple TLS records (including 0)
// Therefore, sequence nubmer incrementation is not completed here
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<(&[u8], TlsRepr)>> {
let mut tcp_socket = sockets.get::<TcpSocket>(self.tcp_handle);
if !tcp_socket.can_recv() {
return Ok(Vec::new());
}
let array_size = tcp_socket.recv_slice(byte_array)?;
let mut vec: Vec<TlsRepr> = Vec::new();
let mut vec: Vec<(&[u8], TlsRepr)> = Vec::new();
let mut bytes: &[u8] = &byte_array[..array_size];
loop {
match parse_tls_repr(bytes) {
Ok((rest, repr)) => {
vec.push(repr);
Ok((rest, (repr_slice, repr))) => {
vec.push(
(repr_slice, repr)
);
if rest.len() == 0 {
return Ok(vec);
} else {