Implement handshake_secret test

This commit is contained in:
dAxpeDDa
2021-07-30 14:45:27 -07:00
committed by Kevin Lewi
parent 49dc5b9050
commit 4298deadff
5 changed files with 79 additions and 20 deletions
+18 -2
View File
@@ -13,6 +13,22 @@ use crate::{
use rand::{CryptoRng, RngCore};
use zeroize::Zeroize;
#[cfg(not(test))]
pub type GenerateKe2Result<K, D, G> = (
<K as KeyExchange<D, G>>::KE2State,
<K as KeyExchange<D, G>>::KE2Message,
);
#[cfg(test)]
pub type GenerateKe2Result<K, D, G> = (
<K as KeyExchange<D, G>>::KE2State,
<K as KeyExchange<D, G>>::KE2Message,
Vec<u8>,
);
#[cfg(not(test))]
pub type GenerateKe3Result<K, D, G> = (Vec<u8>, <K as KeyExchange<D, G>>::KE3Message);
#[cfg(test)]
pub type GenerateKe3Result<K, D, G> = (Vec<u8>, <K as KeyExchange<D, G>>::KE3Message, Vec<u8>);
pub trait KeyExchange<D: Hash, G: Group> {
type KE1State: FromBytes + ToBytesWithPointers + Zeroize + Clone;
type KE2State: FromBytes + ToBytesWithPointers + Zeroize + Clone;
@@ -35,7 +51,7 @@ pub trait KeyExchange<D: Hash, G: Group> {
id_u: Vec<u8>,
id_s: Vec<u8>,
context: Vec<u8>,
) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError<S::Error>>;
) -> Result<GenerateKe2Result<Self, D, G>, ProtocolError<S::Error>>;
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
fn generate_ke3(
@@ -48,7 +64,7 @@ pub trait KeyExchange<D: Hash, G: Group> {
id_u: Vec<u8>,
id_s: Vec<u8>,
context: Vec<u8>,
) -> Result<(Vec<u8>, Self::KE3Message), ProtocolError>;
) -> Result<GenerateKe3Result<Self, D, G>, ProtocolError>;
#[allow(clippy::type_complexity)]
fn finish_ke(
+28 -12
View File
@@ -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<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
id_u: Vec<u8>,
id_s: Vec<u8>,
context: Vec<u8>,
) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError<S::Error>> {
) -> Result<GenerateKe2Result<Self, D, G>, ProtocolError<S::Error>> {
let server_e_kp = KeyPair::<G>::generate_random(rng);
let server_nonce = generate_nonce::<R>(rng);
@@ -94,7 +96,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
.chain(&server_nonce[..])
.chain(&server_e_kp.public().to_arr());
let (session_key, km2, km3) = derive_3dh_keys::<D, G, S>(
let result = derive_3dh_keys::<D, G, S>(
TripleDHComponents {
pk1: ke1_message.client_e_pk.clone(),
sk1: server_e_kp.private().clone(),
@@ -107,7 +109,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
)?;
let mut mac_hasher =
Hmac::<D>::new_from_slice(&km2).map_err(|_| InternalPakeError::HmacError)?;
Hmac::<D>::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<D: Hash, G: Group> KeyExchange<D, G> 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<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
id_u: Vec<u8>,
id_s: Vec<u8>,
context: Vec<u8>,
) -> Result<(Vec<u8>, Self::KE3Message), ProtocolError> {
) -> Result<GenerateKe3Result<Self, D, G>, ProtocolError> {
let mut transcript_hasher = D::new()
.chain(STR_RFC)
.chain(&serialize(&context, 2)?)
@@ -148,7 +152,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
.chain(&l2_component[..])
.chain(&ke2_message.to_bytes_without_info_or_mac());
let (session_key, km2, km3) = derive_3dh_keys::<D, G, PrivateKey<G>>(
let result = derive_3dh_keys::<D, G, PrivateKey<G>>(
TripleDHComponents {
pk1: ke2_message.server_e_pk.clone(),
sk1: ke1_state.client_e_sk.clone(),
@@ -161,7 +165,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
)?;
let mut server_mac =
Hmac::<D>::new_from_slice(&km2).map_err(|_| InternalPakeError::HmacError)?;
Hmac::<D>::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<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
transcript_hasher.update(ke2_message.mac.to_vec());
let mut client_mac =
Hmac::<D>::new_from_slice(&km3).map_err(|_| InternalPakeError::HmacError)?;
Hmac::<D>::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<G: Group, S: SecretKey<G>> {
sk3: PrivateKey<G>,
}
#[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<D> = (
GenericArray<u8, <D as FixedOutput>::OutputSize>,
GenericArray<u8, <D as FixedOutput>::OutputSize>,
GenericArray<u8, <D as FixedOutput>::OutputSize>,
);
#[cfg(test)]
type TripleDHDerivationResult<D> = (
GenericArray<u8, <D as FixedOutput>::OutputSize>,
GenericArray<u8, <D as FixedOutput>::OutputSize>,
GenericArray<u8, <D as FixedOutput>::OutputSize>,
Vec<u8>,
);
/// The third key exchange message
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
@@ -501,6 +515,8 @@ fn derive_3dh_keys<D: Hash, G: Group, S: SecretKey<G>>(
GenericArray::clone_from_slice(&session_key),
GenericArray::clone_from_slice(&km2),
GenericArray::clone_from_slice(&km3),
#[cfg(test)]
handshake_secret,
))
}
+1
View File
@@ -525,6 +525,7 @@ mod tests {
let ServerLoginStartResult {
message,
state: server,
..
} = ServerLogin::start(
&mut OsRng,
&server_setup,
+22 -6
View File
@@ -573,6 +573,9 @@ pub struct ClientLoginFinishResult<CS: CipherSuite> {
/// Instance of the ClientLogin, only used in tests for checking zeroize
#[cfg(test)]
pub state: ClientLogin<CS>,
/// Handshake secret, only used tests
#[cfg(test)]
pub handshake_secret: Vec<u8>,
}
// Cannot be derived because it would require for CS to be Clone.
@@ -585,6 +588,8 @@ impl<CS: CipherSuite> Clone for ClientLoginFinishResult<CS> {
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<CS: CipherSuite> ClientLogin<CS> {
&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<CS: CipherSuite> ClientLogin<CS> {
)?;
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<CS: CipherSuite> {
pub message: CredentialResponse<CS>,
/// The state that the server must keep in order to finish the protocl
pub state: ServerLogin<CS>,
/// Handshake secret, only used tests
#[cfg(test)]
pub handshake_secret: Vec<u8>,
}
// Cannot be derived because it would require for CS to be Clone.
@@ -744,6 +756,8 @@ impl<CS: CipherSuite> Clone for ServerLoginStartResult<CS> {
Self {
message: self.message.clone(),
state: self.state.clone(),
#[cfg(test)]
handshake_secret: self.handshake_secret.clone(),
}
}
}
@@ -844,7 +858,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
let credential_response_component =
CredentialResponse::<CS>::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<CS: CipherSuite> ServerLogin<CS> {
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,
})
}
+10
View File
@@ -65,6 +65,7 @@ pub struct TestVectorParameters {
pub session_key: Vec<u8>,
pub auth_key: Vec<u8>,
pub randomized_pwd: Vec<u8>,
pub handshake_secret: Vec<u8>,
}
// 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(&parameters.handshake_secret),
hex::encode(server_login_start_result.handshake_secret)
);
assert_eq!(
hex::encode(&parameters.KE2),
hex::encode(server_login_start_result.message.serialize())
@@ -690,6 +696,10 @@ fn test_ke3() -> Result<(), ProtocolError> {
hex::encode(&parameters.session_key),
hex::encode(&client_login_finish_result.session_key)
);
assert_eq!(
hex::encode(&parameters.handshake_secret),
hex::encode(&client_login_finish_result.handshake_secret)
);
assert_eq!(
hex::encode(&parameters.KE3),
hex::encode(client_login_finish_result.message.serialize())