From ee9b31e3deda536de69c18a4b8ad4c9720f35fc3 Mon Sep 17 00:00:00 2001 From: occheung Date: Thu, 29 Oct 2020 17:34:03 +0800 Subject: [PATCH] handshake: fin --- Cargo.toml | 1 + src/fake_rng.rs | 25 +++++ src/lib.rs | 1 + src/parse.rs | 35 ++++++- src/session.rs | 227 +++++++++++++++++++++++++++++++++++++++++++--- src/tls.rs | 101 +++++++++++++++++++-- src/tls_packet.rs | 37 +++++++- 7 files changed, 397 insertions(+), 30 deletions(-) create mode 100644 src/fake_rng.rs diff --git a/Cargo.toml b/Cargo.toml index 24359a7..6a69ddf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" hkdf = "0.9.0" sha-1 = { version = "0.9.1", default-features = false } sha2 = { version = "0.9.1", default-features = false } +hmac = "0.10.1" byteorder = { version = "1.3.4", default-features = false } num_enum = { version = "0.5.1", default-features = false } log = "0.4.11" diff --git a/src/fake_rng.rs b/src/fake_rng.rs new file mode 100644 index 0000000..e28af21 --- /dev/null +++ b/src/fake_rng.rs @@ -0,0 +1,25 @@ +// A blank implementor of RngCore that is NOT random +// Justification: RSA padding scheme for verifying PSS signature +// 1. Why is there a static lifetime bound? +// 2. Why need random? It is just signature verification. +// Anyway, the RSAPublicKey::verify() method does NOT care about random at all :) + +use rand_core::{RngCore, Error}; + +pub (crate) struct FakeRandom {} + +impl RngCore for FakeRandom { + fn next_u32(&mut self) -> u32 { + 0 + } + + fn next_u64(&mut self) -> u64 { + 0 + } + + fn fill_bytes(&mut self, dest: &mut [u8]) {} + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index bbf80de..6a5e2c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ pub mod buffer; pub mod key; pub mod session; pub mod certificate; +pub mod fake_rng; use nom::error::ParseError; diff --git a/src/parse.rs b/src/parse.rs index ddb6b7c..d6ae9db 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -177,6 +177,21 @@ pub(crate) fn parse_handshake(bytes: &[u8]) -> IResult<&[u8], HandshakeRepr> { Ok((rest, repr)) }, + Finished => { + // Parse Finished, the size is determined + // Pre-split the slice and then parse for Finished + // i.e. check for completeness + let (rest, possible_verify_data) = take(repr.length)(rest)?; + let (_, handshake_data) = complete( + parse_finished + )(possible_verify_data)?; + + repr.handshake_data = HandshakeData::Finished( + handshake_data + ); + + Ok((rest, repr)) + } _ => todo!() } } @@ -381,17 +396,22 @@ 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(); let signature_length = NetworkEndian::read_u16(signature_length); - let (_, signature) = complete( - take(signature_length) - )(rest)?; + // Take the signature portion out + let (rest, signature) = take(signature_length)(rest)?; Ok(( - &[], + rest, CertificateVerify { algorithm: signature_scheme, signature_length, @@ -400,6 +420,13 @@ fn parse_certificate_verify(bytes: &[u8]) -> IResult<&[u8], CertificateVerify> { )) } +fn parse_finished(bytes: &[u8]) -> IResult<&[u8], Finished> { + Ok(( + &[], + Finished { verify_data: bytes } + )) +} + fn parse_extension(bytes: &[u8], handshake_type: HandshakeType) -> IResult<&[u8], Extension> { let extension_type = take(2_usize); let length = take(2_usize); diff --git a/src/session.rs b/src/session.rs index 2d363e8..6fa8ee8 100644 --- a/src/session.rs +++ b/src/session.rs @@ -1,6 +1,6 @@ use p256::{ EncodedPoint, ecdh::EphemeralSecret }; use heapless::{ Vec, consts::* }; -use sha2::{ Digest, Sha256, Sha384, digest::FixedOutput }; +use sha2::{ Digest, Sha256, Sha384, Sha512, digest::FixedOutput }; use aes_gcm::{ Aes128Gcm, Aes256Gcm, aes::Aes128 }; use aes_gcm::{ AeadInPlace, NewAead, aead::Buffer }; use chacha20poly1305::ChaCha20Poly1305; @@ -8,7 +8,10 @@ use ccm::Ccm; use hkdf::Hkdf; use generic_array::GenericArray; use byteorder::{ByteOrder, NetworkEndian, BigEndian}; -use rsa::RSAPublicKey; +use rsa::{RSAPublicKey, PublicKey, PaddingScheme, Hash as RSAHash}; +use hmac::{ Hmac, Mac, NewMac }; + +use rand_core::RngCore; use core::convert::AsRef; use core::cell::RefCell; @@ -16,7 +19,9 @@ use core::cell::RefCell; use crate::tls::TlsState; use crate::tls_packet::CipherSuite; use crate::key::*; +use crate::tls_packet::SignatureScheme; use crate::Error; +use crate::fake_rng::FakeRandom; type Aes128Ccm = Ccm; @@ -157,13 +162,23 @@ impl Session { let client_handshake_traffic_secret = derive_secret( &handshake_secret_hkdf, "c hs traffic", - self.hash.get_sha256_clone() + self.hash.get_sha256_clone().unwrap() ); let server_handshake_traffic_secret = derive_secret( &handshake_secret_hkdf, "s hs traffic", - self.hash.get_sha256_clone() + self.hash.get_sha256_clone().unwrap() + ); + + // Store client_handshake_traffic_secret and + // server_handshake_traffic_secret + // Initial values of both secrets don't matter + self.client_traffic_secret.replace( + Vec::from_slice(&client_handshake_traffic_secret).unwrap() + ); + self.server_traffic_secret.replace( + Vec::from_slice(&server_handshake_traffic_secret).unwrap() ); let client_handshake_traffic_secret_hkdf = Hkdf::::from_prk(&client_handshake_traffic_secret).unwrap(); @@ -335,13 +350,23 @@ impl Session { let client_handshake_traffic_secret = derive_secret( &handshake_secret_hkdf, "c hs traffic", - self.hash.get_sha384_clone() + self.hash.get_sha384_clone().unwrap() ); let server_handshake_traffic_secret = derive_secret( &handshake_secret_hkdf, "s hs traffic", - self.hash.get_sha384_clone() + self.hash.get_sha384_clone().unwrap() + ); + + // Store client_handshake_traffic_secret and + // server_handshake_traffic_secret + // Initial values of both secrets don't matter + self.client_traffic_secret.replace( + Vec::from_slice(&client_handshake_traffic_secret).unwrap() + ); + self.server_traffic_secret.replace( + Vec::from_slice(&server_handshake_traffic_secret).unwrap() ); let client_handshake_traffic_secret_hkdf = Hkdf::::from_prk(&client_handshake_traffic_secret).unwrap(); @@ -445,6 +470,184 @@ impl Session { self.state = TlsState::WAIT_CV; } + pub(crate) fn client_update_for_wait_cv( + &mut self, + cert_verify_slice: &[u8], + signature_algorithm: SignatureScheme, + signature: &[u8] + ) + { + // Clone the transcript hash from ClientHello all the way to Certificate + let transcript_hash: Vec = if let Ok(sha256) = self.hash.get_sha256_clone() { + Vec::from_slice(&sha256.finalize()).unwrap() + } else if let Ok(sha384) = self.hash.get_sha384_clone() { + Vec::from_slice(&sha384.finalize()).unwrap() + } else { + unreachable!() + }; + + // Handle Ed25519 and p256 separately + // These 2 algorithms have a mandated hash function + if signature_algorithm == SignatureScheme::ecdsa_secp256r1_sha256 || + signature_algorithm == SignatureScheme::ed25519 + { + todo!() + } + + // Get verification hash, and verify the signature + use crate::tls_packet::SignatureScheme::*; + + let get_rsa_padding_scheme = |sig_alg: SignatureScheme| -> PaddingScheme { + match signature_algorithm { + rsa_pkcs1_sha256 => { + PaddingScheme::new_pkcs1v15_sign(Some(RSAHash::SHA2_256)) + }, + rsa_pkcs1_sha384 => { + PaddingScheme::new_pkcs1v15_sign(Some(RSAHash::SHA2_384)) + }, + rsa_pkcs1_sha512 => { + PaddingScheme::new_pkcs1v15_sign(Some(RSAHash::SHA2_512)) + }, + rsa_pss_rsae_sha256 | rsa_pss_pss_sha256 => { + PaddingScheme::new_pss::(FakeRandom{}) + }, + rsa_pss_rsae_sha384 | rsa_pss_pss_sha384 => { + PaddingScheme::new_pss::(FakeRandom{}) + }, + rsa_pss_rsae_sha512 | rsa_pss_pss_sha512 => { + PaddingScheme::new_pss::(FakeRandom{}) + }, + _ => unreachable!() + } + }; + + match signature_algorithm { + rsa_pkcs1_sha256 | rsa_pss_rsae_sha256 | rsa_pss_pss_sha256 => { + let verify_hash = Sha256::new() + .chain(&[0x20; 64]) + .chain("TLS 1.3, server CertificateVerify") + .chain(&[0]) + .chain(&transcript_hash) + .finalize(); + let padding = get_rsa_padding_scheme(signature_algorithm); + 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!() + } + }, + rsa_pkcs1_sha384 | rsa_pss_rsae_sha384 | rsa_pss_pss_sha384 => { + let verify_hash = Sha384::new() + .chain(&[0x20; 64]) + .chain("TLS 1.3, server CertificateVerify") + .chain(&[0]) + .chain(&transcript_hash) + .finalize(); + let padding = get_rsa_padding_scheme(signature_algorithm); + 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!() + } + }, + rsa_pkcs1_sha512 | rsa_pss_rsae_sha512 | rsa_pss_pss_sha512 => { + let verify_hash = Sha512::new() + .chain(&[0x20; 64]) + .chain("TLS 1.3, server CertificateVerify") + .chain(&[0]) + .chain(&transcript_hash) + .finalize(); + let padding = get_rsa_padding_scheme(signature_algorithm); + let verify_result = self.cert_rsa_public_key.take().unwrap().verify( + padding, &verify_hash, signature + ); + if verify_result.is_err() { + todo!() + } + }, + _ => unreachable!() + }; + + // Usual procedures: update hash + self.hash.update(cert_verify_slice); + + // At last, update client state + self.state = TlsState::WAIT_FINISHED; + } + + pub(crate) fn client_update_for_wait_finished( + &mut self, + server_finished_slice: &[u8], + server_verify_data: &[u8] + ) + { + // Take hash from session + if let Ok(sha256) = self.hash.get_sha256_clone() { + let hkdf = Hkdf::::from_prk( + self.server_traffic_secret.as_ref().unwrap() + ).unwrap(); + + // Compute finished_key + let mut okm: GenericArray::::OutputSize> = + Default::default(); + hkdf_expand_label(&hkdf, "finished", "", &mut okm); + + // Get transcript hash + let transcript_hash = sha256.finalize(); + + // Compute verify_data + // let computed_verify_data = Sha256::new() + // .chain(&okm) + // .chain(&transcript_hash) + // .finalize(); + let mut hmac = Hmac::::new_varkey(&okm).unwrap(); + hmac.update(&transcript_hash); + log::info!("HMAC: {:?}", hmac); + log::info!("Received data: {:?}", server_verify_data); + hmac.verify(server_verify_data).unwrap(); + + } else if let Ok(sha384) = self.hash.get_sha384_clone() { + let hkdf = Hkdf::::from_prk( + self.server_traffic_secret.as_ref().unwrap() + ).unwrap(); + + // Compute finished_key + let mut okm: GenericArray::::OutputSize> = + Default::default(); + hkdf_expand_label(&hkdf, "finished", "", &mut okm); + + // Get transcript hash + let transcript_hash = sha384.finalize(); + + // Compute verify_data + // let computed_verify_data = Sha384::new() + // .chain(&okm) + // .chain(&transcript_hash) + // .finalize(); + // log::info!("Computed data: {:?}", computed_verify_data); + // log::info!("Received data: {:?}", server_verify_data); + // assert_eq!(computed_verify_data.as_slice(), server_verify_data); + let mut hmac = Hmac::::new_varkey(&okm).unwrap(); + hmac.update(&transcript_hash); + log::info!("HMAC: {:?}", hmac); + log::info!("Received data: {:?}", server_verify_data); + hmac.verify(server_verify_data).unwrap(); + + } else { + unreachable!() + }; + + // Usual procedures: update hash + self.hash.update(server_finished_slice); + + // At last, update client state + self.state = TlsState::SERVER_CONNECTED; + } + 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 @@ -586,19 +789,19 @@ impl Hash { } } - pub(crate) fn get_sha256_clone(&mut self) -> Sha256 { + pub(crate) fn get_sha256_clone(&mut self) -> Result { if let Self::Sha256 { sha256 } = self { - sha256.clone() + Ok(sha256.clone()) } else { - unreachable!() + Err(()) } } - pub(crate) fn get_sha384_clone(&mut self) -> Sha384 { + pub(crate) fn get_sha384_clone(&mut self) -> Result { if let Self::Sha384 { sha384 } = self { - sha384.clone() + Ok(sha384.clone()) } else { - unreachable!() + Err(()) } } } diff --git a/src/tls.rs b/src/tls.rs index 43c13f1..3d49d44 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -54,6 +54,7 @@ pub(crate) enum TlsState { WAIT_CERT, WAIT_CV, WAIT_FINISHED, + SERVER_CONNECTED, // Additional state, for client to send Finished after server Finished CONNECTED, } @@ -65,7 +66,7 @@ pub struct TlsSocket session: RefCell, } -impl TlsSocket { +impl TlsSocket { pub fn new<'a, 'b, 'c>( sockets: &mut SocketSet<'a, 'b, 'c>, rx_buffer: TcpSocketBuffer<'b>, @@ -174,6 +175,14 @@ impl TlsSocket { // No need to send anything TlsState::WAIT_CV => {}, + // Last step of server authentication + // TLS Client wait for server's Finished handshake + // No need to send anything + TlsState::WAIT_FINISHED => {} + + // Send client Finished to end handshake + TlsState::SERVER_CONNECTED => {} + _ => todo!() } @@ -380,7 +389,7 @@ impl TlsSocket { // Practically, nothing will be done about cookies/server name // Extension processing is therefore skipped // Update hash of the session, get EE by taking appropriate length of data - + // Length of handshake header is 4 let (handshake_slice, ee_slice) = take::<_, _, (&[u8], ErrorKind)>( might_be_ee.length + 4 @@ -440,12 +449,12 @@ impl TlsSocket { // TODO: Process Certificate let cert = might_be_cert.get_asn1_der_certificate().unwrap(); - log::info!("Certificate Acquisition"); - log::info!("Validation {:?}", + log::info!("Certificate validation {:?}", validate_root_certificate(cert) ); // Update session TLS state to WAIT_CV + // Length of handshake header is 4 let (handshake_slice, cert_slice) = take::<_, _, (&[u8], ErrorKind)>( might_be_cert.length + 4 @@ -457,8 +466,6 @@ impl TlsSocket { &cert_slice, cert.return_rsa_public_key().unwrap() ); - - }, // In this stage, server will eventually send a CertificateVerify @@ -471,9 +478,7 @@ impl TlsSocket { } // Pull out the `payload` from TlsRepr, decrypt as CV - // Keep 1 copy to update hash let mut payload = repr.payload.take().unwrap(); - let cert_slice = payload.clone(); // Instantiate associated data and decrypt let mut array: [u8; 5] = [0; 5]; @@ -491,10 +496,86 @@ impl TlsSocket { // Parse the certificate from TLS payload let parse_result = parse_inner_plaintext_for_handshake(&payload); - let (_, mut handshake_vec) = parse_result + let (_, (handshake_slice, mut handshake_vec)) = parse_result .map_err(|_| Error::Unrecognized)?; - // Get hash from session, validate signature + // Ensure that it is CertificateVerify + let might_be_cert_verify = handshake_vec.remove(0); + if might_be_cert_verify.get_msg_type() != HandshakeType::CertificateVerify { + // Process the other handshakes in "handshake_vec" + todo!() + } + + // Take out the portion for CertificateVerify + // Length of handshake header is 4 + let (handshake_slice, cert_verify_slice) = + take::<_, _, (&[u8], ErrorKind)>( + might_be_cert_verify.length + 4 + )(handshake_slice) + .map_err(|_| Error::Unrecognized)?; + + // Perform verification, update TLS state if successful + let (sig_alg, signature) = might_be_cert_verify.get_signature().unwrap(); + self.session.borrow_mut() + .client_update_for_wait_cv( + cert_verify_slice, + sig_alg, + signature + ); + }, + + // 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 + ); + } + 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); + 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); + if might_be_server_finished.get_msg_type() != HandshakeType::Finished { + // Process the other handshakes in "handshake_vec" + todo!() + } + + // Take out the portion for server Finished + // Length of handshake header is 4 + let (handshake_slice, server_finished_slice) = + take::<_, _, (&[u8], ErrorKind)>( + might_be_server_finished.length + 4 + )(handshake_slice) + .map_err(|_| Error::Unrecognized)?; + + // Perform verification, update TLS state if successful + self.session.borrow_mut() + .client_update_for_wait_finished( + server_finished_slice, + might_be_server_finished.get_verify_data().unwrap() + ); } _ => {}, diff --git a/src/tls_packet.rs b/src/tls_packet.rs index db64784..8c43910 100644 --- a/src/tls_packet.rs +++ b/src/tls_packet.rs @@ -114,10 +114,6 @@ impl<'a> TlsRepr<'a> { self.handshake.is_none() && self.payload.is_some() } - - pub(crate) fn decrypt_ee(&self, shared_secret: &SharedSecret) -> HandshakeRepr { - todo!() - } } #[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)] @@ -177,6 +173,32 @@ impl<'a, 'b> HandshakeRepr<'a> { Err(()) } } + + pub(crate) fn get_signature(&self) -> Result<(SignatureScheme, &[u8]), ()> { + if self.msg_type != HandshakeType::CertificateVerify { + return Err(()) + }; + if let HandshakeData::CertificateVerify( + cert_verify + ) = &self.handshake_data { + Ok((cert_verify.algorithm, cert_verify.signature)) + } else { + Err(()) + } + } + + pub(crate) fn get_verify_data(self) -> Result<&'a [u8], ()> { + if self.msg_type != HandshakeType::Finished { + return Err(()) + }; + if let HandshakeData::Finished( + fin + ) = &self.handshake_data { + Ok(fin.verify_data) + } else { + Err(()) + } + } } #[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)] @@ -212,6 +234,8 @@ pub(crate) enum HandshakeData<'a> { EncryptedExtensions(EncryptedExtensions), Certificate(Certificate<'a>), CertificateVerify(CertificateVerify<'a>), + FinishedNeedParse, + Finished(Finished<'a>), } impl<'a> HandshakeData<'a> { @@ -731,3 +755,8 @@ pub(crate) struct CertificateVerify<'a> { pub(crate) signature_length: u16, pub(crate) signature: &'a [u8], } + +#[derive(Debug, Clone)] +pub(crate) struct Finished<'a> { + pub(crate) verify_data: &'a [u8] +}