diff --git a/Cargo.toml b/Cargo.toml index 2d5879c..322ae24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,11 @@ version = "1.0.1" default-features = false features = [ "u64_backend" ] +[dependencies.x25519-dalek] +version = "1.1.0" +default-features = false +features = [ "u64_backend" ] + # Fetch from master, for "no_std" + "alloc" combination [dependencies.rsa] git = "https://github.com/RustCrypto/RSA.git" diff --git a/src/session.rs b/src/session.rs index c0914cb..c7c3ded 100644 --- a/src/session.rs +++ b/src/session.rs @@ -34,7 +34,7 @@ pub(crate) struct Session { // Hash functions needed hash: Hash, // Ephemeral secret for ECDHE key exchange - ecdhe_secret: Option, + ecdhe_secret: Option<(EphemeralSecret, x25519_dalek::EphemeralSecret)>, // Block ciphers for client & server client_handshake_cipher: Option, server_handshake_cipher: Option, @@ -101,6 +101,7 @@ impl Session { pub(crate) fn client_update_for_ch( &mut self, ecdhe_secret: EphemeralSecret, + x25519_secret: x25519_dalek::EphemeralSecret, session_id: [u8; 32], ch_slice: &[u8] ) { @@ -108,7 +109,7 @@ impl Session { if self.state != TlsState::START || self.role != TlsRole::Client { todo!() } - self.ecdhe_secret = Some(ecdhe_secret); + self.ecdhe_secret = Some((ecdhe_secret, x25519_secret)); self.session_id = Some(session_id); self.hash.update(ch_slice); self.state = TlsState::WAIT_SH; @@ -120,7 +121,8 @@ impl Session { pub(crate) fn client_update_for_sh( &mut self, cipher_suite: CipherSuite, - encoded_point: EncodedPoint, + encoded_point: Option, + x25519_shared: Option, sh_slice: &[u8] ) { // Handle inappropriate call to move state @@ -129,12 +131,34 @@ impl Session { } // Generate ECDHE shared secret // Remove private secret - let ecdhe_shared_secret = - self.ecdhe_secret - .take() - .unwrap() - .diffie_hellman(&encoded_point) - .unwrap(); + // let ecdhe_shared_secret = + // self.ecdhe_secret + // .take() + // .unwrap() + // .diffie_hellman(&encoded_point) + // .unwrap(); + + let mut shared_secret_bytes: [u8; 32] = [0; 32]; + if encoded_point.is_some() { + let p256_shared_secret = + self.ecdhe_secret + .take() + .unwrap() + .0 + .diffie_hellman(&encoded_point.unwrap()) + .unwrap(); + shared_secret_bytes.clone_from_slice(p256_shared_secret.as_bytes()); + } else if x25519_shared.is_some() { + let x25519_shared_secret = + self.ecdhe_secret + .take() + .unwrap() + .1 + .diffie_hellman(&x25519_shared.unwrap()); + shared_secret_bytes.clone_from_slice(x25519_shared_secret.as_bytes()); + } else { + todo!() + } // Generate Handshake secret match cipher_suite { @@ -161,7 +185,7 @@ impl Session { let (handshake_secret, handshake_secret_hkdf) = Hkdf::::extract( Some(&derived_secret), - ecdhe_shared_secret.as_bytes() + &shared_secret_bytes ); // Store the handshake secret @@ -349,7 +373,7 @@ impl Session { let (handshake_secret, handshake_secret_hkdf) = Hkdf::::extract( Some(&derived_secret), - ecdhe_shared_secret.as_bytes() + &shared_secret_bytes ); // Store the handshake secret diff --git a/src/tls.rs b/src/tls.rs index 4d6fcb6..8faeee3 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -126,12 +126,13 @@ impl TlsSocket { // Prepare field that is randomised, // Supply it to the TLS repr builder. let ecdh_secret = EphemeralSecret::random(&mut self.rng); + let x25519_secret = x25519_dalek::EphemeralSecret::new(&mut self.rng); let mut random: [u8; 32] = [0; 32]; let mut session_id: [u8; 32] = [0; 32]; self.rng.fill_bytes(&mut random); self.rng.fill_bytes(&mut session_id); let repr = TlsRepr::new() - .client_hello(&ecdh_secret, random, session_id.clone()); + .client_hello(&ecdh_secret, &x25519_secret, random, session_id.clone()); // Update hash function with client hello handshake let mut array = [0; 512]; @@ -145,6 +146,7 @@ impl TlsSocket { // Update TLS session self.session.borrow_mut().client_update_for_ch( ecdh_secret, + x25519_secret, session_id, &slice[5..] ); @@ -313,7 +315,6 @@ impl TlsSocket { if repr.is_server_hello() { // Check SH content: // random: Cannot represent HelloRequestRetry - // (TODO: Support other key shares, e.g. X25519) // session_id_echo: should be same as the one sent by client // cipher_suite: Store // (TODO: Check if such suite was offered) @@ -321,11 +322,11 @@ impl TlsSocket { // // Check extensions: // supported_version: Must be TLS 1.3 - // key_share: Store key, must be in secp256r1 - // (TODO: Support other key shares ^) + // key_share: Store key, must be in secp256r1 or x25519 // "Cache" for ECDHE server public info - let mut server_public: Option = None; + let mut p256_public: Option = None; + let mut x25519_public: Option = None; let mut selected_cipher: Option = None; // Process the handshake data within ServerHello @@ -377,22 +378,25 @@ impl TlsSocket { server_share } ) = &extension.extension_data { - // TODO: Use legitimate checking to ensure the chosen - // group is indeed acceptable, when allowing more (EC)DHE - // key sharing - if server_share.group != NamedGroup::secp256r1 { - // Abort for wrong key sharing - todo!() + match server_share.group { + NamedGroup::secp256r1 => { + p256_public.replace( + EncodedPoint::from_untagged_bytes( + GenericArray::from_slice(&server_share.key_exchange[1..]) + ) + ); + }, + NamedGroup::x25519 => { + let mut x25519_server_key: [u8; 32] = [0; 32]; + x25519_server_key.clone_from_slice(&server_share.key_exchange); + x25519_public.replace( + x25519_dalek::PublicKey::from( + x25519_server_key + ) + ); + } + _ => todo!() } - // Store key - // 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 - server_public.replace( - EncodedPoint::from_untagged_bytes( - GenericArray::from_slice(&server_share.key_exchange[1..]) - ) - ); } } } @@ -402,8 +406,8 @@ impl TlsSocket { todo!() } - // Check that both selected_cipher and server_public were received - if selected_cipher.is_none() || server_public.is_none() { + // Check that both selected_cipher and (p256_public XNOR x25519_public) were received + if selected_cipher.is_none() || (p256_public.is_none() && x25519_public.is_none()) { // Abort communication todo!() } @@ -412,7 +416,8 @@ impl TlsSocket { let mut session = self.session.borrow_mut(); session.client_update_for_sh( selected_cipher.unwrap(), - server_public.unwrap(), + p256_public, + x25519_public, handshake_slice ); // Key exchange occurred, seq_num is set to 0 diff --git a/src/tls_packet.rs b/src/tls_packet.rs index 5ec2190..66c6d92 100644 --- a/src/tls_packet.rs +++ b/src/tls_packet.rs @@ -59,14 +59,14 @@ impl<'a> TlsRepr<'a> { } } - pub(crate) fn client_hello(mut self, secret: &EphemeralSecret, random: [u8; 32], session_id: [u8; 32]) -> Self { + pub(crate) fn client_hello(mut self, p256_secret: &EphemeralSecret, x25519_secret: &x25519_dalek::EphemeralSecret, random: [u8; 32], session_id: [u8; 32]) -> Self { self.content_type = TlsContentType::Handshake; self.version = TlsVersion::Tls10; let handshake_repr = { let mut repr = HandshakeRepr::new(); repr.msg_type = HandshakeType::ClientHello; repr.handshake_data = HandshakeData::ClientHello({ - ClientHello::new(secret, random, session_id) + ClientHello::new(p256_secret, x25519_secret, random, session_id) }); repr.length = repr.handshake_data.get_length().try_into().unwrap(); repr @@ -252,7 +252,7 @@ impl<'a> HandshakeData<'a> { } impl<'a> ClientHello<'a> { - pub(self) fn new(secret: &EphemeralSecret, random: [u8; 32], session_id: [u8; 32]) -> Self { + pub(self) fn new(p256_secret: &EphemeralSecret, x25519_secret: &x25519_dalek::EphemeralSecret, random: [u8; 32], session_id: [u8; 32]) -> Self { let mut client_hello = ClientHello { version: TlsVersion::Tls12, random, @@ -274,7 +274,7 @@ impl<'a> ClientHello<'a> { client_hello.add_ch_supported_versions() .add_sig_algs() - .add_client_groups_with_key_shares(secret) + .add_client_groups_with_key_shares(p256_secret, x25519_secret) .finalise() } @@ -354,9 +354,10 @@ impl<'a> ClientHello<'a> { self } - pub(crate) fn add_client_groups_with_key_shares(mut self, ecdh_secret: &EphemeralSecret) -> Self { + pub(crate) fn add_client_groups_with_key_shares(mut self, ecdh_secret: &EphemeralSecret, x25519_secret: &x25519_dalek::EphemeralSecret) -> Self { // List out all supported groups let mut list = Vec::new(); + list.push(NamedGroup::x25519); list.push(NamedGroup::secp256r1); let length = list.len()*2; @@ -384,6 +385,19 @@ impl<'a> ClientHello<'a> { key_exchange } }, + + NamedGroup::x25519 => { + let x25519_public = x25519_dalek::PublicKey::from(x25519_secret); + key_exchange.extend_from_slice(x25519_public.as_bytes()); + + let key_exchange_length = key_exchange.len(); + + KeyShareEntry { + group: *named_group, + length: key_exchange_length.try_into().unwrap(), + key_exchange + } + } // TODO: Implement keygen for other named groups _ => todo!(), };