From 4298deadffd7ca1d6a65a39be6dc78b057ac2a74 Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Fri, 30 Jul 2021 12:54:16 +0200 Subject: [PATCH] Implement `handshake_secret` test --- src/key_exchange/traits.rs | 20 ++++++++++++++-- src/key_exchange/tripledh.rs | 40 ++++++++++++++++++++++---------- src/keypair.rs | 1 + src/opaque.rs | 28 +++++++++++++++++----- src/tests/opaque_test_vectors.rs | 10 ++++++++ 5 files changed, 79 insertions(+), 20 deletions(-) diff --git a/src/key_exchange/traits.rs b/src/key_exchange/traits.rs index 33add31..6a78872 100644 --- a/src/key_exchange/traits.rs +++ b/src/key_exchange/traits.rs @@ -13,6 +13,22 @@ use crate::{ use rand::{CryptoRng, RngCore}; use zeroize::Zeroize; +#[cfg(not(test))] +pub type GenerateKe2Result = ( + >::KE2State, + >::KE2Message, +); +#[cfg(test)] +pub type GenerateKe2Result = ( + >::KE2State, + >::KE2Message, + Vec, +); +#[cfg(not(test))] +pub type GenerateKe3Result = (Vec, >::KE3Message); +#[cfg(test)] +pub type GenerateKe3Result = (Vec, >::KE3Message, Vec); + pub trait KeyExchange { type KE1State: FromBytes + ToBytesWithPointers + Zeroize + Clone; type KE2State: FromBytes + ToBytesWithPointers + Zeroize + Clone; @@ -35,7 +51,7 @@ pub trait KeyExchange { id_u: Vec, id_s: Vec, context: Vec, - ) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError>; + ) -> Result, ProtocolError>; #[allow(clippy::too_many_arguments, clippy::type_complexity)] fn generate_ke3( @@ -48,7 +64,7 @@ pub trait KeyExchange { id_u: Vec, id_s: Vec, context: Vec, - ) -> Result<(Vec, Self::KE3Message), ProtocolError>; + ) -> Result, ProtocolError>; #[allow(clippy::type_complexity)] fn finish_ke( diff --git a/src/key_exchange/tripledh.rs b/src/key_exchange/tripledh.rs index b5caeb9..77c1260 100644 --- a/src/key_exchange/tripledh.rs +++ b/src/key_exchange/tripledh.rs @@ -12,7 +12,9 @@ use crate::{ }, group::Group, hash::Hash, - key_exchange::traits::{FromBytes, KeyExchange, ToBytes, ToBytesWithPointers}, + key_exchange::traits::{ + FromBytes, GenerateKe2Result, GenerateKe3Result, KeyExchange, ToBytes, ToBytesWithPointers, + }, keypair::{KeyPair, PrivateKey, PublicKey, SecretKey, SizedBytesExt}, serialization::serialize, }; @@ -80,7 +82,7 @@ impl KeyExchange for TripleDH { id_u: Vec, id_s: Vec, context: Vec, - ) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError> { + ) -> Result, ProtocolError> { let server_e_kp = KeyPair::::generate_random(rng); let server_nonce = generate_nonce::(rng); @@ -94,7 +96,7 @@ impl KeyExchange for TripleDH { .chain(&server_nonce[..]) .chain(&server_e_kp.public().to_arr()); - let (session_key, km2, km3) = derive_3dh_keys::( + let result = derive_3dh_keys::( TripleDHComponents { pk1: ke1_message.client_e_pk.clone(), sk1: server_e_kp.private().clone(), @@ -107,7 +109,7 @@ impl KeyExchange for TripleDH { )?; let mut mac_hasher = - Hmac::::new_from_slice(&km2).map_err(|_| InternalPakeError::HmacError)?; + Hmac::::new_from_slice(&result.1).map_err(|_| InternalPakeError::HmacError)?; mac_hasher.update(&transcript_hasher.clone().finalize()); let mac = mac_hasher.finalize().into_bytes(); @@ -115,15 +117,17 @@ impl KeyExchange for TripleDH { Ok(( Ke2State { - km3, + km3: result.2, hashed_transcript: transcript_hasher.finalize(), - session_key, + session_key: result.0, }, Ke2Message { server_nonce, server_e_pk: server_e_kp.public().clone(), mac, }, + #[cfg(test)] + result.3, )) } @@ -138,7 +142,7 @@ impl KeyExchange for TripleDH { id_u: Vec, id_s: Vec, context: Vec, - ) -> Result<(Vec, Self::KE3Message), ProtocolError> { + ) -> Result, ProtocolError> { let mut transcript_hasher = D::new() .chain(STR_RFC) .chain(&serialize(&context, 2)?) @@ -148,7 +152,7 @@ impl KeyExchange for TripleDH { .chain(&l2_component[..]) .chain(&ke2_message.to_bytes_without_info_or_mac()); - let (session_key, km2, km3) = derive_3dh_keys::>( + let result = derive_3dh_keys::>( TripleDHComponents { pk1: ke2_message.server_e_pk.clone(), sk1: ke1_state.client_e_sk.clone(), @@ -161,7 +165,7 @@ impl KeyExchange for TripleDH { )?; let mut server_mac = - Hmac::::new_from_slice(&km2).map_err(|_| InternalPakeError::HmacError)?; + Hmac::::new_from_slice(&result.1).map_err(|_| InternalPakeError::HmacError)?; server_mac.update(&transcript_hasher.clone().finalize()); if server_mac.verify(&ke2_message.mac).is_err() { @@ -173,14 +177,16 @@ impl KeyExchange for TripleDH { transcript_hasher.update(ke2_message.mac.to_vec()); let mut client_mac = - Hmac::::new_from_slice(&km3).map_err(|_| InternalPakeError::HmacError)?; + Hmac::::new_from_slice(&result.2).map_err(|_| InternalPakeError::HmacError)?; client_mac.update(&transcript_hasher.finalize()); Ok(( - session_key.to_vec(), + result.0.to_vec(), Ke3Message { mac: client_mac.finalize().into_bytes(), }, + #[cfg(test)] + result.3, )) } @@ -417,13 +423,21 @@ struct TripleDHComponents> { sk3: PrivateKey, } -#[allow(clippy::upper_case_acronyms)] // Consists of a session key, followed by two mac keys: (session_key, km2, km3) +#[cfg(not(test))] +#[allow(clippy::upper_case_acronyms)] type TripleDHDerivationResult = ( GenericArray::OutputSize>, GenericArray::OutputSize>, GenericArray::OutputSize>, ); +#[cfg(test)] +type TripleDHDerivationResult = ( + GenericArray::OutputSize>, + GenericArray::OutputSize>, + GenericArray::OutputSize>, + Vec, +); /// The third key exchange message #[derive(Clone, Debug, Eq, Hash, PartialEq)] @@ -501,6 +515,8 @@ fn derive_3dh_keys>( GenericArray::clone_from_slice(&session_key), GenericArray::clone_from_slice(&km2), GenericArray::clone_from_slice(&km3), + #[cfg(test)] + handshake_secret, )) } diff --git a/src/keypair.rs b/src/keypair.rs index c60ea80..aa0a186 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -525,6 +525,7 @@ mod tests { let ServerLoginStartResult { message, state: server, + .. } = ServerLogin::start( &mut OsRng, &server_setup, diff --git a/src/opaque.rs b/src/opaque.rs index 6a4a320..bc70714 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -573,6 +573,9 @@ pub struct ClientLoginFinishResult { /// Instance of the ClientLogin, only used in tests for checking zeroize #[cfg(test)] pub state: ClientLogin, + /// Handshake secret, only used tests + #[cfg(test)] + pub handshake_secret: Vec, } // Cannot be derived because it would require for CS to be Clone. @@ -585,6 +588,8 @@ impl Clone for ClientLoginFinishResult { server_s_pk: self.server_s_pk.clone(), #[cfg(test)] state: self.state.clone(), + #[cfg(test)] + handshake_secret: self.handshake_secret.clone(), } } } @@ -675,7 +680,7 @@ impl ClientLogin { &credential_response.masked_response, ); - let (session_key, ke3_message) = CS::KeyExchange::generate_ke3( + let result = CS::KeyExchange::generate_ke3( credential_response_component, credential_response.ke2_message, &self.ke1_state, @@ -688,12 +693,16 @@ impl ClientLogin { )?; Ok(ClientLoginFinishResult { - message: CredentialFinalization { ke3_message }, - session_key, + message: CredentialFinalization { + ke3_message: result.1, + }, + session_key: result.0, export_key: opened_envelope.export_key.clone(), server_s_pk, #[cfg(test)] state: self, + #[cfg(test)] + handshake_secret: result.2, }) } } @@ -736,6 +745,9 @@ pub struct ServerLoginStartResult { pub message: CredentialResponse, /// The state that the server must keep in order to finish the protocl pub state: ServerLogin, + /// Handshake secret, only used tests + #[cfg(test)] + pub handshake_secret: Vec, } // Cannot be derived because it would require for CS to be Clone. @@ -744,6 +756,8 @@ impl Clone for ServerLoginStartResult { Self { message: self.message.clone(), state: self.state.clone(), + #[cfg(test)] + handshake_secret: self.handshake_secret.clone(), } } } @@ -844,7 +858,7 @@ impl ServerLogin { let credential_response_component = CredentialResponse::::serialize_without_ke(&beta, &masking_nonce, &masked_response); - let (ke2_state, ke2_message) = CS::KeyExchange::generate_ke2( + let result = CS::KeyExchange::generate_ke2( rng, l1_bytes.to_vec(), credential_response_component, @@ -860,15 +874,17 @@ impl ServerLogin { beta, masking_nonce, masked_response, - ke2_message, + ke2_message: result.1, }; Ok(ServerLoginStartResult { message: credential_response, state: Self { _cs: PhantomData, - ke2_state, + ke2_state: result.0, }, + #[cfg(test)] + handshake_secret: result.2, }) } diff --git a/src/tests/opaque_test_vectors.rs b/src/tests/opaque_test_vectors.rs index 41ce206..a90e689 100644 --- a/src/tests/opaque_test_vectors.rs +++ b/src/tests/opaque_test_vectors.rs @@ -65,6 +65,7 @@ pub struct TestVectorParameters { pub session_key: Vec, pub auth_key: Vec, pub randomized_pwd: Vec, + pub handshake_secret: Vec, } // Pulled from "OPAQUE-3DH Test Vector 1" and "OPAQUE-3DH Test Vector 6" @@ -486,6 +487,7 @@ fn populate_test_vectors(values: &Value) -> TestVectorParameters { session_key: parse!(values, "session_key"), auth_key: parse!(values, "auth_key"), randomized_pwd: parse!(values, "randomized_pwd"), + handshake_secret: parse!(values, "handshake_secret"), } } @@ -653,6 +655,10 @@ fn test_ke2() -> Result<(), ProtocolError> { ), }, )?; + assert_eq!( + hex::encode(¶meters.handshake_secret), + hex::encode(server_login_start_result.handshake_secret) + ); assert_eq!( hex::encode(¶meters.KE2), hex::encode(server_login_start_result.message.serialize()) @@ -690,6 +696,10 @@ fn test_ke3() -> Result<(), ProtocolError> { hex::encode(¶meters.session_key), hex::encode(&client_login_finish_result.session_key) ); + assert_eq!( + hex::encode(¶meters.handshake_secret), + hex::encode(&client_login_finish_result.handshake_secret) + ); assert_eq!( hex::encode(¶meters.KE3), hex::encode(client_login_finish_result.message.serialize())