From c57fc79f6bb1a56f0d92c0c0766e84f71cae5123 Mon Sep 17 00:00:00 2001 From: occheung Date: Tue, 3 Nov 2020 11:34:26 +0800 Subject: [PATCH] socket: handle concatenated handshake --- Cargo.toml | 7 +- src/parse.rs | 29 ++++--- src/session.rs | 41 +++++++++ src/tls.rs | 232 +++++++++++++++++++++---------------------------- 4 files changed, 155 insertions(+), 154 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bad2186..fb4ff36 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 diff --git a/src/parse.rs b/src/parse.rs index 41ae525..80a1581 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -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)> { +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 = 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()) - ], - handshake_vec - ) + // // 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)); } } diff --git a/src/session.rs b/src/session.rs index f6fb826..2c0b40e 100644 --- a/src/session.rs +++ b/src/session.rs @@ -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, &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; } diff --git a/src/tls.rs b/src/tls.rs index 02aa6e6..6bd6dda 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -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 TlsSocket { // 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 TlsSocket { // 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 TlsSocket { // 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 TlsSocket { // 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!() @@ -406,48 +454,16 @@ impl TlsSocket { .client_update_for_ee( &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 TlsSocket { &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 TlsSocket { 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 TlsSocket { 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 TlsSocket { // 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> { + fn recv_tls_repr<'a>(&'a self, sockets: &mut SocketSet, byte_array: &'a mut [u8]) -> Result> { let mut tcp_socket = sockets.get::(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 = 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 {