From 0a8ce3fd4cf4de69f994bef0bd39c08b48c0408c Mon Sep 17 00:00:00 2001 From: occheung Date: Mon, 2 Nov 2020 15:47:23 +0800 Subject: [PATCH] socket interface: init --- src/certificate.rs | 1 - src/parse.rs | 32 +++++++++--- src/session.rs | 105 ++++++++++++++++++++++++++++++++++++++- src/tls.rs | 121 ++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 243 insertions(+), 16 deletions(-) diff --git a/src/certificate.rs b/src/certificate.rs index 91c93a2..6c8c1b6 100644 --- a/src/certificate.rs +++ b/src/certificate.rs @@ -163,7 +163,6 @@ pub fn validate_root_certificate(cert: &Certificate) -> Result { &hasher.finalize(), cert.signature_value ); - log::info!("Verification result: {:?}", verify_result); Ok(verify_result.is_ok()) } _ => { diff --git a/src/parse.rs b/src/parse.rs index d6ae9db..3b8be87 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -119,6 +119,32 @@ pub(crate) fn parse_inner_plaintext_for_handshake(bytes: &[u8]) -> IResult<&[u8] 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) { + // 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 // Not very appropriate to classify EE as proper handshake // It may include multiple handshakes @@ -396,12 +422,6 @@ fn parse_certificate_verify(bytes: &[u8]) -> IResult<&[u8], CertificateVerify> { signature_length ))(bytes)?; - log::info!("Sig scheme: {:?}, sig:len: {:?}, rest_len: {:?}", - signature_scheme, - signature_length, - rest.len() - ); - let signature_scheme = SignatureScheme::try_from( NetworkEndian::read_u16(signature_scheme) ).unwrap(); diff --git a/src/session.rs b/src/session.rs index 59b7c62..d749bf3 100644 --- a/src/session.rs +++ b/src/session.rs @@ -550,7 +550,6 @@ impl Session { let verify_result = self.cert_rsa_public_key.take().unwrap().verify( padding, &verify_hash, signature ); - log::info!("Algorithm {:?} Certificate verify: {:?}", signature_algorithm, verify_result); if verify_result.is_err() { todo!() } @@ -954,6 +953,10 @@ impl Session { 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.state = TlsState::CONNECTED; } @@ -1024,6 +1027,38 @@ impl Session { 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, Error> { + let (seq_num, nonce, cipher): (u64, &Vec, &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( &self, associated_data: &[u8], @@ -1086,6 +1121,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, &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( &self, associated_data: &[u8], @@ -1283,6 +1355,37 @@ impl Cipher { }.map_err(|_| Error::DecryptionError) } + pub(crate) fn decrypt_in_place_detached( + &self, + nonce: &GenericArray, + associated_data: &[u8], + buffer: &mut [u8], + tag: &GenericArray + ) -> 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 { match self { Cipher::Aes128Gcm { aes128gcm } => { diff --git a/src/tls.rs b/src/tls.rs index 205cd11..2867001 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -40,7 +40,12 @@ use heapless::Vec as HeaplessVec; use crate::Error as TlsError; use crate::tls_packet::*; -use crate::parse::{ parse_tls_repr, parse_handshake, parse_inner_plaintext_for_handshake }; +use crate::parse::{ + parse_tls_repr, + parse_handshake, + parse_inner_plaintext_for_handshake, + get_content_type_inner_plaintext +}; use crate::buffer::TlsBuffer; use crate::session::{Session, TlsRole}; use crate::certificate::validate_root_certificate; @@ -468,9 +473,13 @@ impl TlsSocket { // TODO: Process Certificate 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 // Length of handshake header is 4 @@ -567,7 +576,6 @@ impl TlsSocket { &mut payload ); } - log::info!("decrypted wait_fin payload: {:?}", payload); // Parse the TLS inner ciphertext as a Finished handshake let parse_result = parse_inner_plaintext_for_handshake(&payload); @@ -596,7 +604,6 @@ impl TlsSocket { server_finished_slice, might_be_server_finished.get_verify_data().unwrap() ); - log::info!("Computed secret"); } _ => {}, @@ -662,14 +669,13 @@ impl TlsSocket { // 2. Add TLS header in front of application data // Input should be inner plaintext // 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<()> { let mut tcp_socket = sockets.get::(self.tcp_handle); if !tcp_socket.can_send() { return Err(Error::Illegal); } - log::info!("Delivered slice: {:?}", slice); - // Borrow session in advance let mut client_session = self.session.borrow_mut(); @@ -727,4 +733,103 @@ impl TlsSocket { }; } } + + pub fn recv_slice(&self, sockets: &mut SocketSet, data: &mut [u8]) -> Result { + let mut tcp_socket = sockets.get::(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 = 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::(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(()) + } }