From 27800091d4ff21cdbc9e8a0b802dcd4e11fcf7da Mon Sep 17 00:00:00 2001 From: occheung Date: Thu, 26 Nov 2020 12:30:16 +0800 Subject: [PATCH] tls: add client hello processing --- src/session.rs | 61 +++++++++++++++++++++++++++++ src/tls.rs | 101 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 144 insertions(+), 18 deletions(-) diff --git a/src/session.rs b/src/session.rs index d5b4f30..bf36d17 100644 --- a/src/session.rs +++ b/src/session.rs @@ -44,6 +44,10 @@ pub(crate) struct Session<'a> { hash: Hash, // Ephemeral secret for ECDHE key exchange ecdhe_secret: Option<(EphemeralSecret, x25519_dalek::EphemeralSecret)>, + // Public key received from client side when listening + ecdhe_public: Option, + // Server selected cipher_suite, but without enough keying info + server_selected_cipher: Option, // Block ciphers for client & server client_handshake_cipher: Option, server_handshake_cipher: Option, @@ -98,6 +102,8 @@ impl<'a> Session<'a> { latest_secret: None, hash, ecdhe_secret: None, + ecdhe_public: None, + server_selected_cipher: None, client_handshake_cipher: None, server_handshake_cipher: None, client_application_cipher: None, @@ -1156,6 +1162,42 @@ impl<'a> Session<'a> { self.state = TlsState::CLIENT_CONNECTED; } + pub(crate) fn server_update_for_begin( + &mut self, + cipher_suite: CipherSuite, + client_ecdhe_public_key: DiffieHellmanPublicKey, + session_id: [u8; 32], + server_signature_algorithm: SignatureScheme, + client_hello_slice: &[u8] + ) + { + self.ecdhe_public.replace(client_ecdhe_public_key); + self.session_id.replace(session_id); + self.client_cert_verify_sig_alg.replace(server_signature_algorithm); + self.server_selected_cipher.replace(cipher_suite); + + // Choose a hash algorithm based on the selected cipher_suite + self.hash = match cipher_suite { + CipherSuite::TLS_AES_128_CCM_SHA256 | + CipherSuite::TLS_CHACHA20_POLY1305_SHA256 | + CipherSuite::TLS_AES_128_GCM_SHA256 => { + Hash::select_sha256(self.hash.clone()) + }, + CipherSuite::TLS_AES_256_GCM_SHA384 => { + Hash::select_sha384(self.hash.clone()) + }, + // The remaining CCM_8 is not supported + _ => { + unreachable!() + } + }; + // Update hash with the received client hello + self.hash.update(client_hello_slice); + + // Update FSM state of TLS + self.state = TlsState::NEGOTIATED; + } + pub(crate) fn verify_session_id_echo(&self, session_id_echo: &[u8]) -> bool { if let Some(session_id_inner) = self.session_id { session_id_inner == session_id_echo @@ -1196,6 +1238,16 @@ impl<'a> Session<'a> { } } + // Motivation: To know if offered signature algorithms + // can indeed be supported + pub(crate) fn get_certificate_private_key(&self) -> Option<&CertificatePrivateKey> { + if let Some((private_key, _)) = &self.cert_private_key { + Some(private_key) + } else { + None + } + } + pub(crate) fn get_client_certificate_verify_signature(&self, rng: &mut R) -> (crate::tls_packet::SignatureScheme, alloc::vec::Vec) { @@ -1910,3 +1962,12 @@ pub enum CertificatePrivateKey { cert_eddsa_key: ed25519_dalek::SecretKey } } + +pub enum DiffieHellmanPublicKey { + SECP256R1 { + encoded_point: p256::EncodedPoint + }, + X25519 { + public_key: x25519_dalek::PublicKey + } +} diff --git a/src/tls.rs b/src/tls.rs index 4d8bf93..9625f3a 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -31,7 +31,7 @@ use crate::parse::{ get_content_type_inner_plaintext }; use crate::buffer::TlsBuffer; -use crate::session::{Session, TlsRole}; +use crate::session::{Session, TlsRole, DiffieHellmanPublicKey}; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[allow(non_camel_case_types)] @@ -952,9 +952,10 @@ impl<'s> TlsSocket<'s> { let mut version_check = false; let mut offered_p256 = false; let mut offered_x25519 = false; - let mut p256_public_key: Option = None; - let mut x25519_public_key: Option = None; - let mut signature_schemes: Vec = Vec::new(); +// let mut p256_public_key: Option = None; +// let mut x25519_public_key: Option = None; + let mut ecdhe_public_key: Option = None; + let mut signature_algorithm: Option = None; // Verify that TLS 1.3 is offered by the client if let Some(supported_version_extension) = client_hello.extensions.iter().find( @@ -1035,18 +1036,20 @@ impl<'s> TlsSocket<'s> { key.group == NamedGroup::secp256r1 } ) { - p256_public_key.replace( - p256::EncodedPoint::from_untagged_bytes( - GenericArray::from_slice( - &p256_key.key_exchange[1..] + ecdhe_public_key.replace( + DiffieHellmanPublicKey::SECP256R1 { + encoded_point: p256::EncodedPoint::from_untagged_bytes( + GenericArray::from_slice( + &p256_key.key_exchange[1..] + ) ) - ) + } ); } } // Then try X25519, if P-256 key is not found and x25519 is offered - if offered_x25519 && p256_public_key.is_none() { + if offered_x25519 && ecdhe_public_key.is_none() { if let Some(x25519_key) = client_shares.iter().find( |key| { key.group == NamedGroup::x25519 @@ -1056,17 +1059,19 @@ impl<'s> TlsSocket<'s> { let mut key_content: [u8; 32] = [0; 32]; key_content.clone_from_slice(&x25519_key.key_exchange); - x25519_public_key.replace( - x25519_dalek::PublicKey::from( - key_content - ) + ecdhe_public_key.replace( + DiffieHellmanPublicKey::X25519 { + public_key: x25519_dalek::PublicKey::from( + key_content + ) + } ); } } // If there are no applicable offered client key, // consider sending a ClientHelloRetry - if p256_public_key.is_none() && x25519_public_key.is_none() { + if ecdhe_public_key.is_none() { todo!() } } else { @@ -1087,16 +1092,76 @@ impl<'s> TlsSocket<'s> { if let ExtensionData::SignatureAlgorithms( SignatureSchemeList { supported_signature_algorithms, .. } ) = &signature_algorithms.extension_data { - signature_schemes.extend_from_slice(supported_signature_algorithms); + // Check compatibility of signature algorithms + if let Some(certificate_private_key) = self.session.borrow().get_certificate_private_key() { + use crate::session::CertificatePrivateKey::*; + if let Some(server_signature_algorithm) = match certificate_private_key { + // Try RSA keys: + RSA { .. } => { + supported_signature_algorithms.iter().find( + |&&signature_algorithm| { + signature_algorithm == SignatureScheme::rsa_pkcs1_sha256 || + signature_algorithm == SignatureScheme::rsa_pkcs1_sha384 || + signature_algorithm == SignatureScheme::rsa_pkcs1_sha512 || + signature_algorithm == SignatureScheme::rsa_pss_rsae_sha256 || + signature_algorithm == SignatureScheme::rsa_pss_rsae_sha384 || + signature_algorithm == SignatureScheme::rsa_pss_rsae_sha512 || + signature_algorithm == SignatureScheme::rsa_pss_pss_sha256 || + signature_algorithm == SignatureScheme::rsa_pss_pss_sha384 || + signature_algorithm == SignatureScheme::rsa_pss_pss_sha512 + } + ) + }, + ECDSA_SECP256R1_SHA256 { .. } => { + supported_signature_algorithms.iter().find( + |&&signature_algorithm| { + signature_algorithm == SignatureScheme::ecdsa_secp256r1_sha256 + } + ) + }, + ED25519 { .. } => { + supported_signature_algorithms.iter().find( + |&&signature_algorithm| { + signature_algorithm == SignatureScheme::ed25519 + } + ) + } + } { + signature_algorithm = Some(*server_signature_algorithm); + } else { + // Cannot find a suitable signature algorithm for the server side + // Terminate the negotiation with alert + todo!() + } + + } else { + // Server must have a certificate ready + // Through this should be enforced when entering listening stage + unreachable!() + } + } else { + // Malformed packet, type does not match content + todo!() } } else { // Will only accept authentication through certificate // Send alert if there are no signature algorithms extension todo!() } - } - log::info!("Received client hello"); + { + let mut session = self.session.borrow_mut(); + session.server_update_for_begin( + *accepted_cipher_suite, + ecdhe_public_key.unwrap(), + session_id, + signature_algorithm.unwrap(), + handshake_slice + ) + } + + log::info!("Processed client hello") + } } _ => {},