Implement handshake_secret test
This commit is contained in:
@@ -13,6 +13,22 @@ use crate::{
|
|||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use zeroize::Zeroize;
|
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> {
|
pub trait KeyExchange<D: Hash, G: Group> {
|
||||||
type KE1State: FromBytes + ToBytesWithPointers + Zeroize + Clone;
|
type KE1State: FromBytes + ToBytesWithPointers + Zeroize + Clone;
|
||||||
type KE2State: 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_u: Vec<u8>,
|
||||||
id_s: Vec<u8>,
|
id_s: Vec<u8>,
|
||||||
context: 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)]
|
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
|
||||||
fn generate_ke3(
|
fn generate_ke3(
|
||||||
@@ -48,7 +64,7 @@ pub trait KeyExchange<D: Hash, G: Group> {
|
|||||||
id_u: Vec<u8>,
|
id_u: Vec<u8>,
|
||||||
id_s: Vec<u8>,
|
id_s: Vec<u8>,
|
||||||
context: Vec<u8>,
|
context: Vec<u8>,
|
||||||
) -> Result<(Vec<u8>, Self::KE3Message), ProtocolError>;
|
) -> Result<GenerateKe3Result<Self, D, G>, ProtocolError>;
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn finish_ke(
|
fn finish_ke(
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ use crate::{
|
|||||||
},
|
},
|
||||||
group::Group,
|
group::Group,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
key_exchange::traits::{FromBytes, KeyExchange, ToBytes, ToBytesWithPointers},
|
key_exchange::traits::{
|
||||||
|
FromBytes, GenerateKe2Result, GenerateKe3Result, KeyExchange, ToBytes, ToBytesWithPointers,
|
||||||
|
},
|
||||||
keypair::{KeyPair, PrivateKey, PublicKey, SecretKey, SizedBytesExt},
|
keypair::{KeyPair, PrivateKey, PublicKey, SecretKey, SizedBytesExt},
|
||||||
serialization::serialize,
|
serialization::serialize,
|
||||||
};
|
};
|
||||||
@@ -80,7 +82,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
|
|||||||
id_u: Vec<u8>,
|
id_u: Vec<u8>,
|
||||||
id_s: Vec<u8>,
|
id_s: Vec<u8>,
|
||||||
context: 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_e_kp = KeyPair::<G>::generate_random(rng);
|
||||||
let server_nonce = generate_nonce::<R>(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_nonce[..])
|
||||||
.chain(&server_e_kp.public().to_arr());
|
.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 {
|
TripleDHComponents {
|
||||||
pk1: ke1_message.client_e_pk.clone(),
|
pk1: ke1_message.client_e_pk.clone(),
|
||||||
sk1: server_e_kp.private().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 =
|
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());
|
mac_hasher.update(&transcript_hasher.clone().finalize());
|
||||||
let mac = mac_hasher.finalize().into_bytes();
|
let mac = mac_hasher.finalize().into_bytes();
|
||||||
|
|
||||||
@@ -115,15 +117,17 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
|
|||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
Ke2State {
|
Ke2State {
|
||||||
km3,
|
km3: result.2,
|
||||||
hashed_transcript: transcript_hasher.finalize(),
|
hashed_transcript: transcript_hasher.finalize(),
|
||||||
session_key,
|
session_key: result.0,
|
||||||
},
|
},
|
||||||
Ke2Message {
|
Ke2Message {
|
||||||
server_nonce,
|
server_nonce,
|
||||||
server_e_pk: server_e_kp.public().clone(),
|
server_e_pk: server_e_kp.public().clone(),
|
||||||
mac,
|
mac,
|
||||||
},
|
},
|
||||||
|
#[cfg(test)]
|
||||||
|
result.3,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +142,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
|
|||||||
id_u: Vec<u8>,
|
id_u: Vec<u8>,
|
||||||
id_s: Vec<u8>,
|
id_s: Vec<u8>,
|
||||||
context: Vec<u8>,
|
context: Vec<u8>,
|
||||||
) -> Result<(Vec<u8>, Self::KE3Message), ProtocolError> {
|
) -> Result<GenerateKe3Result<Self, D, G>, ProtocolError> {
|
||||||
let mut transcript_hasher = D::new()
|
let mut transcript_hasher = D::new()
|
||||||
.chain(STR_RFC)
|
.chain(STR_RFC)
|
||||||
.chain(&serialize(&context, 2)?)
|
.chain(&serialize(&context, 2)?)
|
||||||
@@ -148,7 +152,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
|
|||||||
.chain(&l2_component[..])
|
.chain(&l2_component[..])
|
||||||
.chain(&ke2_message.to_bytes_without_info_or_mac());
|
.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 {
|
TripleDHComponents {
|
||||||
pk1: ke2_message.server_e_pk.clone(),
|
pk1: ke2_message.server_e_pk.clone(),
|
||||||
sk1: ke1_state.client_e_sk.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 =
|
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());
|
server_mac.update(&transcript_hasher.clone().finalize());
|
||||||
|
|
||||||
if server_mac.verify(&ke2_message.mac).is_err() {
|
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());
|
transcript_hasher.update(ke2_message.mac.to_vec());
|
||||||
|
|
||||||
let mut client_mac =
|
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());
|
client_mac.update(&transcript_hasher.finalize());
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
session_key.to_vec(),
|
result.0.to_vec(),
|
||||||
Ke3Message {
|
Ke3Message {
|
||||||
mac: client_mac.finalize().into_bytes(),
|
mac: client_mac.finalize().into_bytes(),
|
||||||
},
|
},
|
||||||
|
#[cfg(test)]
|
||||||
|
result.3,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,13 +423,21 @@ struct TripleDHComponents<G: Group, S: SecretKey<G>> {
|
|||||||
sk3: PrivateKey<G>,
|
sk3: PrivateKey<G>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
|
||||||
// Consists of a session key, followed by two mac keys: (session_key, km2, km3)
|
// 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> = (
|
type TripleDHDerivationResult<D> = (
|
||||||
GenericArray<u8, <D as FixedOutput>::OutputSize>,
|
GenericArray<u8, <D as FixedOutput>::OutputSize>,
|
||||||
GenericArray<u8, <D as FixedOutput>::OutputSize>,
|
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
|
/// The third key exchange message
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
#[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(&session_key),
|
||||||
GenericArray::clone_from_slice(&km2),
|
GenericArray::clone_from_slice(&km2),
|
||||||
GenericArray::clone_from_slice(&km3),
|
GenericArray::clone_from_slice(&km3),
|
||||||
|
#[cfg(test)]
|
||||||
|
handshake_secret,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -525,6 +525,7 @@ mod tests {
|
|||||||
let ServerLoginStartResult {
|
let ServerLoginStartResult {
|
||||||
message,
|
message,
|
||||||
state: server,
|
state: server,
|
||||||
|
..
|
||||||
} = ServerLogin::start(
|
} = ServerLogin::start(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
&server_setup,
|
&server_setup,
|
||||||
|
|||||||
+22
-6
@@ -573,6 +573,9 @@ pub struct ClientLoginFinishResult<CS: CipherSuite> {
|
|||||||
/// Instance of the ClientLogin, only used in tests for checking zeroize
|
/// Instance of the ClientLogin, only used in tests for checking zeroize
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub state: ClientLogin<CS>,
|
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.
|
// 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(),
|
server_s_pk: self.server_s_pk.clone(),
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
state: self.state.clone(),
|
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,
|
&credential_response.masked_response,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (session_key, ke3_message) = CS::KeyExchange::generate_ke3(
|
let result = CS::KeyExchange::generate_ke3(
|
||||||
credential_response_component,
|
credential_response_component,
|
||||||
credential_response.ke2_message,
|
credential_response.ke2_message,
|
||||||
&self.ke1_state,
|
&self.ke1_state,
|
||||||
@@ -688,12 +693,16 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(ClientLoginFinishResult {
|
Ok(ClientLoginFinishResult {
|
||||||
message: CredentialFinalization { ke3_message },
|
message: CredentialFinalization {
|
||||||
session_key,
|
ke3_message: result.1,
|
||||||
|
},
|
||||||
|
session_key: result.0,
|
||||||
export_key: opened_envelope.export_key.clone(),
|
export_key: opened_envelope.export_key.clone(),
|
||||||
server_s_pk,
|
server_s_pk,
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
state: self,
|
state: self,
|
||||||
|
#[cfg(test)]
|
||||||
|
handshake_secret: result.2,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -736,6 +745,9 @@ pub struct ServerLoginStartResult<CS: CipherSuite> {
|
|||||||
pub message: CredentialResponse<CS>,
|
pub message: CredentialResponse<CS>,
|
||||||
/// The state that the server must keep in order to finish the protocl
|
/// The state that the server must keep in order to finish the protocl
|
||||||
pub state: ServerLogin<CS>,
|
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.
|
// Cannot be derived because it would require for CS to be Clone.
|
||||||
@@ -744,6 +756,8 @@ impl<CS: CipherSuite> Clone for ServerLoginStartResult<CS> {
|
|||||||
Self {
|
Self {
|
||||||
message: self.message.clone(),
|
message: self.message.clone(),
|
||||||
state: self.state.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 =
|
let credential_response_component =
|
||||||
CredentialResponse::<CS>::serialize_without_ke(&beta, &masking_nonce, &masked_response);
|
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,
|
rng,
|
||||||
l1_bytes.to_vec(),
|
l1_bytes.to_vec(),
|
||||||
credential_response_component,
|
credential_response_component,
|
||||||
@@ -860,15 +874,17 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
|||||||
beta,
|
beta,
|
||||||
masking_nonce,
|
masking_nonce,
|
||||||
masked_response,
|
masked_response,
|
||||||
ke2_message,
|
ke2_message: result.1,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ServerLoginStartResult {
|
Ok(ServerLoginStartResult {
|
||||||
message: credential_response,
|
message: credential_response,
|
||||||
state: Self {
|
state: Self {
|
||||||
_cs: PhantomData,
|
_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 session_key: Vec<u8>,
|
||||||
pub auth_key: Vec<u8>,
|
pub auth_key: Vec<u8>,
|
||||||
pub randomized_pwd: 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"
|
// 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"),
|
session_key: parse!(values, "session_key"),
|
||||||
auth_key: parse!(values, "auth_key"),
|
auth_key: parse!(values, "auth_key"),
|
||||||
randomized_pwd: parse!(values, "randomized_pwd"),
|
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!(
|
assert_eq!(
|
||||||
hex::encode(¶meters.KE2),
|
hex::encode(¶meters.KE2),
|
||||||
hex::encode(server_login_start_result.message.serialize())
|
hex::encode(server_login_start_result.message.serialize())
|
||||||
@@ -690,6 +696,10 @@ fn test_ke3() -> Result<(), ProtocolError> {
|
|||||||
hex::encode(¶meters.session_key),
|
hex::encode(¶meters.session_key),
|
||||||
hex::encode(&client_login_finish_result.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!(
|
assert_eq!(
|
||||||
hex::encode(¶meters.KE3),
|
hex::encode(¶meters.KE3),
|
||||||
hex::encode(client_login_finish_result.message.serialize())
|
hex::encode(client_login_finish_result.message.serialize())
|
||||||
|
|||||||
Reference in New Issue
Block a user