diff --git a/src/cipher_suite.rs b/src/cipher_suite.rs deleted file mode 100644 index 59263d4..0000000 --- a/src/cipher_suite.rs +++ /dev/null @@ -1,226 +0,0 @@ -use p256::{EncodedPoint, AffinePoint, ecdh::EphemeralSecret, ecdh::SharedSecret}; -use aes_gcm::{Aes128Gcm, Aes256Gcm}; -use chacha20poly1305::{ChaCha20Poly1305, Key}; -use ccm::{Ccm, consts::*}; -use aes_gcm::aes::Aes128; -use aes_gcm::{AeadInPlace, NewAead}; -use generic_array::GenericArray; -use rand_core::{ RngCore, CryptoRng }; -use sha2::{ Digest, Sha256, Sha384, Sha512 }; -use heapless::Vec; -use hkdf::Hkdf; - -use crate::Error as TlsError; -use crate::tls_packet::CipherSuite as CipherSuiteField; -use crate::key::*; - -// A structure representing the block cipher and the hashes -pub(crate) enum CipherSuite { - // Handshake is still proceeding, no cipher can be produced yet, - // Though hashes still has to be prepared for deriving key later, - // This enum offers all possible hashes that could be needed - // i.e. SHA256 and SHA384 - Undetermined { - sha_256: Sha256, - sha_384: Sha384, - }, - - // Established cipher suites - // Contains a block cipher (GCM/CCM/ChaChaPoly) - // Contains a hash function (SHA256/SHA384) - TLS_AES_128_GCM_SHA256 { - aes_128_gcm: Aes128Gcm, - sha_256: Sha256, - }, - TLS_AES_256_GCM_SHA384 { - aes_256_gcm: Aes256Gcm, - sha_384: Sha384, - }, - TLS_CHACHA20_POLY1305_SHA256 { - chacha20_poly1305: ChaCha20Poly1305, - sha_256: Sha256, - }, - TLS_AES_128_CCM_SHA256 { - ccm: Ccm, - sha_256: Sha256, - }, -} - - -impl CipherSuite { - pub(crate) fn new() -> Self { - CipherSuite::Undetermined { - sha_256: Sha256::new(), - sha_384: Sha384::new(), - } - } - - // Assume no PSK, establish ciphersuite along side handshake secret - // Need to update hash function before calling - pub(crate) fn establish( - self, - field: CipherSuiteField, - ecdhe_shared: SharedSecret - ) -> Self { - use CipherSuiteField::*; - - let (sha_256, sha_384) = { - if let CipherSuite::Undetermined { - sha_256, - sha_384, - } = self { - (sha_256, sha_384) - } else { - // TODO: Implement key change - return self; - } - }; - - match field { - TLS_AES_128_GCM_SHA256 | TLS_CHACHA20_POLY1305_SHA256 | - TLS_AES_128_CCM_SHA256 => { - // Compute early_secret in HKDF, without PSK - let empty_hash = Sha256::new().chain(""); - let early_secret = Hkdf::::new(None, &[0; 32]); - - // Calculate derived secret - let derived_secret = derive_secret( - &early_secret, - "derived", - empty_hash - ); - - // Calculate handshake secret in HKDF - let handshake_secret = Hkdf::::new( - Some(&derived_secret), - ecdhe_shared.as_bytes() - ); - - // Calculate client_handshake_traffic_secret - let client_handshake_traffic_secret = derive_secret( - &handshake_secret, - "c hs traffic", - sha_256.clone() - ); - - // Calculate server_handshake_traffic_secret - let server_handshake_traffic_secret = derive_secret( - &handshake_secret, - "c hs traffic", - sha_256.clone() - ); - - // let client_write_key = hkdf_expand_label( - // - // ); - } - _ => todo!() - } - - todo!() - - // // Compute HKDF - // let (hash, empty_hash, hkdf) = match field { - // TLS_AES_128_GCM_SHA256 | - // TLS_CHACHA20_POLY1305_SHA256 | - // TLS_AES_128_CCM_SHA256 => { - // ( - // sha_256, - // Sha256::new().chain(""), - // Hkdf::::new(None, &[0; 32]) - // ) - // }, - // TLS_AES_256_GCM_SHA384 => { - // ( - // sha_384, - // Sha384::new().chain(""), - // Hkdf::::new(None, &[0; 48]) - // ) - // } - // }; - - // // get_derived_secret, then insert ECDHE shared secret - // let derived_secret = derive_secret(hkdf, "derived", empty_hash); - - // let (key, iv) = match field { - // TLS_AES_128_GCM_SHA256 | - // TLS_CHACHA20_POLY1305_SHA256 | - // TLS_AES_128_CCM_SHA256 => { - // let hkdf = Hkdf::::new( - // Some(&derived_secret), - // ecdhe_shared.as_bytes() - // ); - // let client_handshake_traffic_secret = derive_secret( - // hkdf, - // "c hs traffic", - // sha256.clone(), - // ); - // }, - // TLS_AES_256_GCM_SHA384 => { - // Hkdf::::new( - // Some(&derived_secret), - // ecdhe_shared.as_bytes() - // ) - // } - // }; - } -} - -// macro_rules! impl_cipher { -// ($($cipher_name: ident),+) => { -// impl Cipher { -// pub(crate) fn encrypt(&self, rng: &mut T, associated_data: &[u8], buffer: &mut Vec) -> core::result::Result<(), TlsError> -// where -// T: RngCore + CryptoRng -// { -// // All 4 supported Ciphers use a nonce of 12 bytes -// let mut nonce_array: [u8; 12] = [0; 12]; -// rng.fill_bytes(&mut nonce_array); -// use Cipher::*; -// match self { -// $( -// $cipher_name(cipher) => { -// cipher.encrypt_in_place( -// &GenericArray::from_slice(&nonce_array), -// associated_data, -// buffer -// ).map_err( -// |_| TlsError::EncryptionError -// ) -// } -// )+ -// } -// } - -// pub(crate) fn decrypt(&self, rng: &mut T, associated_data: &[u8], buffer: &mut Vec) -> core::result::Result<(), TlsError> -// where -// T: RngCore + CryptoRng -// { -// // All 4 supported Ciphers use a nonce of 12 bytes -// let mut nonce_array: [u8; 12] = [0; 12]; -// rng.fill_bytes(&mut nonce_array); -// use Cipher::*; -// match self { -// $( -// $cipher_name(cipher) => { -// cipher.decrypt_in_place( -// &GenericArray::from_slice(&nonce_array), -// associated_data, -// buffer -// ).map_err( -// |_| TlsError::EncryptionError -// ) -// } -// )+ -// } -// } -// } -// } -// } - -// impl_cipher!( -// TLS_AES_128_GCM_SHA256, -// TLS_AES_256_GCM_SHA384, -// TLS_CHACHA20_POLY1305_SHA256, -// TLS_AES_128_CCM_SHA256 -// ); \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 941d685..224df61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,6 @@ extern crate alloc; pub mod tls; pub mod tls_packet; pub mod parse; -pub mod cipher_suite; pub mod buffer; pub mod key; pub mod session; diff --git a/src/session.rs b/src/session.rs index 42ddcb5..1d71bbc 100644 --- a/src/session.rs +++ b/src/session.rs @@ -411,6 +411,14 @@ impl Session { pub(crate) fn get_tls_state(&self) -> TlsState { self.state } + + pub(crate) fn has_completed_handshake(&self) -> bool { + self.state == TlsState::CONNECTED + } + + pub(crate) fn receive_change_cipher_spec(&mut self) { + self.changed_cipher_spec = true; + } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] diff --git a/src/tls.rs b/src/tls.rs index 31e6c0e..d3534f7 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -35,7 +35,6 @@ use alloc::vec::{ self, Vec }; use crate::Error as TlsError; use crate::tls_packet::*; use crate::parse::parse_tls_repr; -use crate::cipher_suite::CipherSuite; use crate::buffer::TlsBuffer; use crate::session::{Session, TlsRole}; @@ -55,14 +54,8 @@ pub(crate) enum TlsState { // TODO: Group up all session_specific parameters into a separate structure pub struct TlsSocket { - state: RefCell, tcp_handle: SocketHandle, rng: R, - secret: RefCell>, // Used enum Option to allow later init - session_id: RefCell>, // init session specific field later - received_change_cipher_spec: RefCell>, - cipher: RefCell>, - handshake_sha256: RefCell, session: RefCell, } @@ -79,14 +72,8 @@ impl TlsSocket { let tcp_socket = TcpSocket::new(rx_buffer, tx_buffer); let tcp_handle = sockets.add(tcp_socket); TlsSocket { - state: RefCell::new(TlsState::START), tcp_handle, rng, - secret: RefCell::new(None), - session_id: RefCell::new(None), - received_change_cipher_spec: RefCell::new(None), - cipher: RefCell::new(None), - handshake_sha256: RefCell::new(Sha256::new()), session: RefCell::new( Session::new(TlsRole::Client) ), @@ -130,8 +117,7 @@ impl TlsSocket { } // Handle TLS handshake through TLS states - let state = self.state.clone().into_inner(); - match state { + match self.session.borrow().get_tls_state() { // Initiate TLS handshake TlsState::START => { // Prepare field that is randomised, @@ -142,7 +128,7 @@ impl TlsSocket { self.rng.fill_bytes(&mut random); self.rng.fill_bytes(&mut session_id); let repr = TlsRepr::new() - .client_hello(&ecdh_secret, random, session_id); + .client_hello(&ecdh_secret, random, session_id.clone()); // Update hash function with client hello handshake let mut array = [0; 2048]; @@ -150,29 +136,27 @@ impl TlsSocket { buffer.enqueue_tls_repr(repr)?; let slice: &[u8] = buffer.into(); - // Update hash by handshake - // Update with entire packet except the record layer - { - self.handshake_sha256.borrow_mut().update(&slice[5..]); - } + // Send the packet self.send_tls_slice(sockets, slice)?; - // Store session settings, i.e. secret, session_id - self.secret.replace(Some(ecdh_secret)); - self.session_id.replace(Some(session_id)); - self.received_change_cipher_spec.replace(Some(false)); - - // Update the TLS state - self.state.replace(TlsState::WAIT_SH); + // Update TLS session + self.session.borrow_mut().client_update_for_ch( + ecdh_secret, + session_id, + &slice[5..] + ); }, + // TLS Client wait for Server Hello // No need to send anything TlsState::WAIT_SH => {}, + // TLS Client wait for certificate from TLS server // No need to send anything - // Note: TLS server should normall send SH alongside EE + // Note: TLS server should normally send SH alongside EE // TLS client should jump from WAIT_SH directly to WAIT_CERT_CR directly. TlsState::WAIT_EE => {}, + _ => todo!() } @@ -191,13 +175,11 @@ impl TlsSocket { self.process(repr)?; } - Ok(self.state.clone().into_inner() == TlsState::CONNECTED) + Ok(self.session.borrow().has_completed_handshake()) } // Process TLS ingress during handshake fn process(&self, repr: TlsRepr) -> Result<()> { - let state = self.state.clone().into_inner(); - // Change_cipher_spec check: // Must receive CCS before recv peer's FINISH message // i.e. Must happen after START and before CONNECTED @@ -205,11 +187,11 @@ impl TlsSocket { // CCS message only exist for compatibility reason, // Drop the message and update `received_change_cipher_spec` if repr.is_change_cipher_spec() { - self.received_change_cipher_spec.replace(Some(true)); + self.session.borrow_mut().receive_change_cipher_spec(); return Ok(()) } - match state { + match self.session.borrow().get_tls_state() { // During WAIT_SH for a TLS client, client should wait for ServerHello TlsState::WAIT_SH => { // Legacy_protocol must be TLS 1.2 @@ -232,25 +214,37 @@ impl TlsSocket { // supported_version: Must be TLS 1.3 // key_share: Store key, must be in secp256r1 // (TODO: Support other key shares ^) + + // "Cache" for ECDHE server public info + let mut server_public: Option = None; + let mut selected_cipher: Option = None; + + // Process the handshake data within ServerHello let handshake_data = &repr.handshake.as_ref().unwrap().handshake_data; if let HandshakeData::ServerHello(server_hello) = handshake_data { + // Check random: Cannot be SHA-256 of "HelloRetryRequest" if server_hello.random == HRR_RANDOM { // Abort communication todo!() } + // Check session_id_echo // The socket should have a session_id after moving from START state - if self.session_id.clone().into_inner().unwrap() != server_hello.session_id_echo { + if !self.session.borrow().verify_session_id_echo(server_hello.session_id_echo) { // Abort communication todo!() } - // Store the cipher suite - let selected_cipher = server_hello.cipher_suite; + + // Note the selected cipher suite + selected_cipher.replace(server_hello.cipher_suite); + + // TLSv13 forbidden key compression if server_hello.compression_method != 0 { // Abort communciation todo!() } + for extension in server_hello.extensions.iter() { if extension.extension_type == ExtensionType::SupportedVersions { if let ExtensionData::SupportedVersions( @@ -285,43 +279,11 @@ impl TlsSocket { // It is surely from secp256r1, no other groups are permitted // Convert untagged bytes into encoded point on p256 eliptic curve // Slice the first byte out of the bytes - let server_public = EncodedPoint::from_untagged_bytes( - GenericArray::from_slice(&server_share.key_exchange[1..]) + server_public.replace( + EncodedPoint::from_untagged_bytes( + GenericArray::from_slice(&server_share.key_exchange[1..]) + ) ); - // TODO: Handle improper shared key - // Right now is causes a panic, only socket abort is needed - let secret = self.secret.replace(None); - let shared = secret.unwrap() - .diffie_hellman(&server_public) - .expect("Unsupported key"); - // let cipher = match selected_cipher { - // CipherSuite::TLS_AES_128_GCM_SHA256 => { - // Cipher::TLS_AES_128_GCM_SHA256( - // todo!() - // ) - // }, - // CipherSuite::TLS_AES_256_GCM_SHA384 => { - // Cipher::TLS_AES_256_GCM_SHA384( - // Aes256Gcm::new(shared.as_bytes()) - // ) - // }, - // CipherSuite::TLS_CHACHA20_POLY1305_SHA256 => { - // Cipher::TLS_CHACHA20_POLY1305_SHA256( - // ChaCha20Poly1305::new(shared.as_bytes()) - // ) - // }, - // CipherSuite::TLS_AES_128_CCM_SHA256 => { - // Cipher::TLS_AES_128_CCM_SHA256( - // todo!() - // ) - // }, - // // CCM_8 is not supported - // // TODO: Abort communication - // CipherSuite::TLS_AES_128_CCM_8_SHA256 => { - // todo!() - // } - // }; - // self.cipher.replace(Some(cipher)); } } } @@ -331,21 +293,24 @@ impl TlsSocket { todo!() } + // Check that both selected_cipher and server_public were received + if selected_cipher.is_none() || server_public.is_none() { + // Abort communication + todo!() + } + // This is indeed a desirable ServerHello TLS repr // Reprocess ServerHello into a slice - // Update SHA256 hasher with the slice + // Update session with required parameter let mut array = [0; 2048]; let mut buffer = TlsBuffer::new(&mut array); buffer.enqueue_tls_repr(repr); let slice: &[u8] = buffer.into(); - { - self.handshake_sha256 - .borrow_mut() - .update(&slice[5..]); - } - - // Update TLS session state - self.state.replace(TlsState::WAIT_EE); + self.session.borrow_mut().client_update_for_sh( + selected_cipher.unwrap(), + server_public.unwrap(), + &slice[5..] + ); } },