Implement handshake_secret test
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -525,6 +525,7 @@ mod tests {
|
||||
let ServerLoginStartResult {
|
||||
message,
|
||||
state: server,
|
||||
..
|
||||
} = ServerLogin::start(
|
||||
&mut OsRng,
|
||||
&server_setup,
|
||||
|
||||
+22
-6
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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(¶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())
|
||||
|
||||
Reference in New Issue
Block a user