Normalize generic parameters for LoginSecondMessage
(aka use the CipherSuite just like the others) Repair the generics in KE trait: it makes sense that generate_ke[1-3] should operate on the same key representation.
This commit is contained in:
+1
-1
@@ -34,7 +34,7 @@ pub trait CipherSuite {
|
||||
/// A keypair type composed of public and private components
|
||||
type KeyFormat: KeyPair<Repr = Key> + PartialEq;
|
||||
/// A key exchange protocol
|
||||
type KeyExchange: KeyExchange<Self::Hash>;
|
||||
type KeyExchange: KeyExchange<Self::Hash, Self::KeyFormat>;
|
||||
/// The main hash function use (for HKDF computations and hashing transcripts)
|
||||
type Hash: Hash;
|
||||
/// A slow hashing function, typically used for password hashing
|
||||
|
||||
@@ -12,19 +12,19 @@ use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub trait KeyExchange<D: Hash> {
|
||||
pub trait KeyExchange<D: Hash, KeyFormat: KeyPair<Repr = Key>> {
|
||||
type KE1State: TryFrom<Vec<u8>, Error = InternalPakeError> + ToBytes;
|
||||
type KE2State: TryFrom<Vec<u8>, Error = ProtocolError> + ToBytes;
|
||||
type KE1Message: TryFrom<Vec<u8>, Error = InternalPakeError> + ToBytes;
|
||||
type KE2Message: TryFrom<Vec<u8>, Error = ProtocolError> + ToBytes;
|
||||
type KE3Message: TryFrom<Vec<u8>, Error = ProtocolError> + ToBytes;
|
||||
|
||||
fn generate_ke1<R: RngCore + CryptoRng, KeyFormat: KeyPair<Repr = Key>>(
|
||||
fn generate_ke1<R: RngCore + CryptoRng>(
|
||||
l1_component: Vec<u8>,
|
||||
rng: &mut R,
|
||||
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>;
|
||||
|
||||
fn generate_ke2<R: RngCore + CryptoRng, KeyFormat: KeyPair<Repr = Key>>(
|
||||
fn generate_ke2<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
l1_bytes: Vec<u8>,
|
||||
l2_bytes: Vec<u8>,
|
||||
@@ -33,7 +33,7 @@ pub trait KeyExchange<D: Hash> {
|
||||
server_s_sk: KeyFormat::Repr,
|
||||
) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError>;
|
||||
|
||||
fn generate_ke3<KeyFormat: KeyPair<Repr = Key>>(
|
||||
fn generate_ke3(
|
||||
l2_component: Vec<u8>,
|
||||
ke2_message: Self::KE2Message,
|
||||
ke1_state: &Self::KE1State,
|
||||
|
||||
@@ -31,14 +31,14 @@ static STR_3DH: &[u8] = b"3DH keys";
|
||||
/// The Triple Diffie-Hellman key exchange implementation
|
||||
pub struct TripleDH;
|
||||
|
||||
impl<D: Hash> KeyExchange<D> for TripleDH {
|
||||
impl<D: Hash, KeyFormat: KeyPair<Repr = Key>> KeyExchange<D, KeyFormat> for TripleDH {
|
||||
type KE1State = KE1State<<D as FixedOutput>::OutputSize>;
|
||||
type KE2State = KE2State<<D as FixedOutput>::OutputSize>;
|
||||
type KE1Message = KE1Message;
|
||||
type KE2Message = KE2Message<<D as FixedOutput>::OutputSize>;
|
||||
type KE3Message = KE3Message<<D as FixedOutput>::OutputSize>;
|
||||
|
||||
fn generate_ke1<R: RngCore + CryptoRng, KeyFormat: KeyPair<Repr = Key>>(
|
||||
fn generate_ke1<R: RngCore + CryptoRng>(
|
||||
l1_component: Vec<u8>,
|
||||
rng: &mut R,
|
||||
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> {
|
||||
@@ -69,7 +69,7 @@ impl<D: Hash> KeyExchange<D> for TripleDH {
|
||||
))
|
||||
}
|
||||
|
||||
fn generate_ke2<R: RngCore + CryptoRng, KeyFormat: KeyPair<Repr = Key>>(
|
||||
fn generate_ke2<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
l1_bytes: Vec<u8>,
|
||||
l2_bytes: Vec<u8>,
|
||||
@@ -132,7 +132,7 @@ impl<D: Hash> KeyExchange<D> for TripleDH {
|
||||
))
|
||||
}
|
||||
|
||||
fn generate_ke3<KeyFormat: KeyPair<Repr = Key>>(
|
||||
fn generate_ke3(
|
||||
l2_component: Vec<u8>,
|
||||
ke2_message: Self::KE2Message,
|
||||
ke1_state: &Self::KE1State,
|
||||
|
||||
+51
-72
@@ -141,7 +141,7 @@ where
|
||||
pub struct LoginFirstMessage<CS: CipherSuite> {
|
||||
/// blinded password information
|
||||
alpha: CS::Group,
|
||||
ke1_message: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE1Message,
|
||||
ke1_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE1Message,
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> TryFrom<&[u8]> for LoginFirstMessage<CS> {
|
||||
@@ -163,9 +163,10 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for LoginFirstMessage<CS> {
|
||||
let arr = GenericArray::from_slice(&checked_slice[..elem_len]);
|
||||
let alpha = CS::Group::from_element_slice(arr)?;
|
||||
|
||||
let ke1_message = <CS::KeyExchange as KeyExchange<CS::Hash>>::KE1Message::try_from(
|
||||
checked_slice[elem_len..].to_vec(),
|
||||
)?;
|
||||
let ke1_message =
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE1Message::try_from(
|
||||
checked_slice[elem_len..].to_vec(),
|
||||
)?;
|
||||
Ok(Self { alpha, ke1_message })
|
||||
}
|
||||
}
|
||||
@@ -179,28 +180,15 @@ impl<CS: CipherSuite> LoginFirstMessage<CS> {
|
||||
|
||||
/// The answer sent by the server to the user, upon reception of the
|
||||
/// login attempt.
|
||||
pub struct LoginSecondMessage<Grp, KeyFormat, KE, D>
|
||||
where
|
||||
KeyFormat: KeyPair,
|
||||
KE: KeyExchange<D>,
|
||||
D: Hash,
|
||||
{
|
||||
_key_format: PhantomData<KeyFormat>,
|
||||
_key_exchange: PhantomData<KE>,
|
||||
pub struct LoginSecondMessage<CS: CipherSuite> {
|
||||
/// the server's oprf output
|
||||
beta: Grp,
|
||||
beta: CS::Group,
|
||||
/// the user's sealed information,
|
||||
envelope: Envelope<D>,
|
||||
ke2_message: KE::KE2Message,
|
||||
envelope: Envelope<CS::Hash>,
|
||||
ke2_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE2Message,
|
||||
}
|
||||
|
||||
impl<Grp, KeyFormat, KE, D> LoginSecondMessage<Grp, KeyFormat, KE, D>
|
||||
where
|
||||
Grp: Group,
|
||||
KeyFormat: KeyPair,
|
||||
KE: KeyExchange<D>,
|
||||
D: Hash,
|
||||
{
|
||||
impl<CS: CipherSuite> LoginSecondMessage<CS> {
|
||||
/// byte representation for the login response
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
[
|
||||
@@ -212,19 +200,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Grp, KeyFormat, KE, D> TryFrom<&[u8]> for LoginSecondMessage<Grp, KeyFormat, KE, D>
|
||||
where
|
||||
Grp: Group,
|
||||
KeyFormat: KeyPair,
|
||||
KE: KeyExchange<D>,
|
||||
D: Hash,
|
||||
{
|
||||
impl<CS: CipherSuite> TryFrom<&[u8]> for LoginSecondMessage<CS> {
|
||||
type Error = ProtocolError;
|
||||
fn try_from(second_message_bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
let key_len = <KeyFormat::Repr as SizedBytes>::Len::to_usize();
|
||||
let envelope_size = key_len + Envelope::<D>::additional_size();
|
||||
let elem_len = Grp::ElemLen::to_usize();
|
||||
let ke2_message_size = KE::ke2_message_size();
|
||||
let key_len = <<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len::to_usize();
|
||||
let envelope_size = key_len + Envelope::<CS::Hash>::additional_size();
|
||||
let elem_len = <CS::Group as Group>::ElemLen::to_usize();
|
||||
let ke2_message_size = CS::KeyExchange::ke2_message_size();
|
||||
let checked_slice = check_slice_size(
|
||||
second_message_bytes,
|
||||
elem_len + envelope_size + ke2_message_size,
|
||||
@@ -235,17 +217,17 @@ where
|
||||
// correct subgroup
|
||||
let beta_bytes = &checked_slice[..elem_len];
|
||||
let arr = GenericArray::from_slice(beta_bytes);
|
||||
let beta = Grp::from_element_slice(arr)?;
|
||||
let beta = CS::Group::from_element_slice(arr)?;
|
||||
|
||||
let envelope =
|
||||
Envelope::<D>::from_bytes(&checked_slice[elem_len..elem_len + envelope_size])?;
|
||||
Envelope::<CS::Hash>::from_bytes(&checked_slice[elem_len..elem_len + envelope_size])?;
|
||||
|
||||
let ke2_message =
|
||||
KE::KE2Message::try_from(checked_slice[elem_len + envelope_size..].to_vec())?;
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE2Message::try_from(
|
||||
checked_slice[elem_len + envelope_size..].to_vec(),
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
_key_format: PhantomData,
|
||||
_key_exchange: PhantomData,
|
||||
beta,
|
||||
envelope,
|
||||
ke2_message,
|
||||
@@ -256,7 +238,7 @@ where
|
||||
/// The answer sent by the client to the server, upon reception of the
|
||||
/// sealed envelope
|
||||
pub struct LoginThirdMessage<CS: CipherSuite> {
|
||||
ke3_message: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE3Message,
|
||||
ke3_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE3Message,
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> TryFrom<&[u8]> for LoginThirdMessage<CS> {
|
||||
@@ -264,7 +246,9 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for LoginThirdMessage<CS> {
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
let ke3_message =
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash>>::KE3Message::try_from(bytes.to_vec())?;
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE3Message::try_from(
|
||||
bytes.to_vec(),
|
||||
)?;
|
||||
Ok(Self { ke3_message })
|
||||
}
|
||||
}
|
||||
@@ -644,14 +628,15 @@ pub struct ClientLogin<CS: CipherSuite> {
|
||||
blinding_factor: <CS::Group as Group>::Scalar,
|
||||
/// The user's password
|
||||
password: Vec<u8>,
|
||||
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE1State,
|
||||
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE1State,
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
|
||||
type Error = ProtocolError;
|
||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
let scalar_len = <CS::Group as Group>::ScalarLen::to_usize();
|
||||
let ke1_state_size = <CS::KeyExchange as KeyExchange<CS::Hash>>::ke1_state_size();
|
||||
let ke1_state_size =
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::ke1_state_size();
|
||||
|
||||
let min_expected_len = scalar_len + ke1_state_size;
|
||||
let checked_slice = (if bytes.len() <= min_expected_len {
|
||||
@@ -666,9 +651,10 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
|
||||
|
||||
let blinding_factor_bytes = GenericArray::from_slice(&checked_slice[..scalar_len]);
|
||||
let blinding_factor = CS::Group::from_scalar_slice(blinding_factor_bytes)?;
|
||||
let ke1_state = <CS::KeyExchange as KeyExchange<CS::Hash>>::KE1State::try_from(
|
||||
checked_slice[scalar_len..scalar_len + ke1_state_size].to_vec(),
|
||||
)?;
|
||||
let ke1_state =
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE1State::try_from(
|
||||
checked_slice[scalar_len..scalar_len + ke1_state_size].to_vec(),
|
||||
)?;
|
||||
let password = bytes[scalar_len + ke1_state_size..].to_vec();
|
||||
Ok(Self {
|
||||
blinding_factor,
|
||||
@@ -732,8 +718,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
blinding_factor,
|
||||
} = oprf::generate_oprf1::<R, CS::Group>(&password, pepper, rng)?;
|
||||
|
||||
let (ke1_state, ke1_message) =
|
||||
CS::KeyExchange::generate_ke1::<_, CS::KeyFormat>(alpha.to_arr().to_vec(), rng)?;
|
||||
let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1(alpha.to_arr().to_vec(), rng)?;
|
||||
|
||||
let l1 = LoginFirstMessage { alpha, ke1_message };
|
||||
|
||||
@@ -784,7 +769,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
/// ```
|
||||
pub fn finish<R: RngCore + CryptoRng>(
|
||||
self,
|
||||
l2: LoginSecondMessage<CS::Group, CS::KeyFormat, CS::KeyExchange, CS::Hash>,
|
||||
l2: LoginSecondMessage<CS>,
|
||||
server_s_pk: &<<CS as CipherSuite>::KeyFormat as KeyPair>::Repr,
|
||||
_client_e_sk_rng: &mut R,
|
||||
) -> Result<ClientLoginFinishResult<CS>, ProtocolError> {
|
||||
@@ -804,7 +789,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
err => PakeError::from(err),
|
||||
})?;
|
||||
|
||||
let (shared_secret, ke3_message) = CS::KeyExchange::generate_ke3::<CS::KeyFormat>(
|
||||
let (shared_secret, ke3_message) = CS::KeyExchange::generate_ke3(
|
||||
l2_bytes,
|
||||
l2.ke2_message,
|
||||
&self.ke1_state,
|
||||
@@ -822,7 +807,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
|
||||
/// The state elements the server holds to record a login
|
||||
pub struct ServerLogin<CS: CipherSuite> {
|
||||
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE2State,
|
||||
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE2State,
|
||||
_cs: PhantomData<CS>,
|
||||
}
|
||||
|
||||
@@ -831,22 +816,15 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ServerLogin<CS> {
|
||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
_cs: PhantomData,
|
||||
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE2State::try_from(
|
||||
bytes.to_vec(),
|
||||
)?,
|
||||
ke2_state:
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE2State::try_from(
|
||||
bytes.to_vec(),
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type ServerLoginStartResult<CS> = (
|
||||
LoginSecondMessage<
|
||||
<CS as CipherSuite>::Group,
|
||||
<CS as CipherSuite>::KeyFormat,
|
||||
<CS as CipherSuite>::KeyExchange,
|
||||
<CS as CipherSuite>::Hash,
|
||||
>,
|
||||
ServerLogin<CS>,
|
||||
);
|
||||
type ServerLoginStartResult<CS> = (LoginSecondMessage<CS>, ServerLogin<CS>);
|
||||
|
||||
impl<CS: CipherSuite> ServerLogin<CS> {
|
||||
/// byte representation for the server's login state
|
||||
@@ -905,7 +883,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
||||
|
||||
let l2_component: Vec<u8> = [&beta.to_arr()[..], &envelope.to_bytes()].concat();
|
||||
|
||||
let (ke2_state, ke2_message) = CS::KeyExchange::generate_ke2::<_, CS::KeyFormat>(
|
||||
let (ke2_state, ke2_message) = CS::KeyExchange::generate_ke2(
|
||||
rng,
|
||||
l1_bytes.to_vec(),
|
||||
l2_component,
|
||||
@@ -915,8 +893,6 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
||||
)?;
|
||||
|
||||
let l2 = LoginSecondMessage {
|
||||
_key_format: PhantomData,
|
||||
_key_exchange: PhantomData,
|
||||
beta,
|
||||
envelope,
|
||||
ke2_message,
|
||||
@@ -969,13 +945,16 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
||||
/// # Ok::<(), ProtocolError>(())
|
||||
/// ```
|
||||
pub fn finish(&self, message: LoginThirdMessage<CS>) -> Result<Vec<u8>, ProtocolError> {
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash>>::finish_ke(message.ke3_message, &self.ke2_state)
|
||||
.map_err(|e| match e {
|
||||
ProtocolError::VerificationError(PakeError::KeyExchangeMacValidationError) => {
|
||||
ProtocolError::VerificationError(PakeError::InvalidLoginError)
|
||||
}
|
||||
err => err,
|
||||
})
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::finish_ke(
|
||||
message.ke3_message,
|
||||
&self.ke2_state,
|
||||
)
|
||||
.map_err(|e| match e {
|
||||
ProtocolError::VerificationError(PakeError::KeyExchangeMacValidationError) => {
|
||||
ProtocolError::VerificationError(PakeError::InvalidLoginError)
|
||||
}
|
||||
err => err,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -471,19 +471,15 @@ fn test_l3() -> Result<(), PakeError> {
|
||||
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||
|
||||
let mut client_e_sk_rng = CycleRng::new(parameters.client_e_sk.to_vec());
|
||||
let (l3, shared_secret, export_key_login) = ClientLogin::<X255193dhNoSlowHash>::try_from(
|
||||
¶meters.client_login_state[..],
|
||||
)
|
||||
.unwrap()
|
||||
.finish(
|
||||
LoginSecondMessage::<EdwardsPoint, X25519KeyPair, TripleDH, sha2::Sha256>::try_from(
|
||||
¶meters.l2[..],
|
||||
)
|
||||
.unwrap(),
|
||||
&Key::try_from(¶meters.server_s_pk[..])?,
|
||||
&mut client_e_sk_rng,
|
||||
)
|
||||
.unwrap();
|
||||
let (l3, shared_secret, export_key_login) =
|
||||
ClientLogin::<X255193dhNoSlowHash>::try_from(¶meters.client_login_state[..])
|
||||
.unwrap()
|
||||
.finish(
|
||||
LoginSecondMessage::<X255193dhNoSlowHash>::try_from(¶meters.l2[..]).unwrap(),
|
||||
&Key::try_from(¶meters.server_s_pk[..])?,
|
||||
&mut client_e_sk_rng,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.shared_secret),
|
||||
|
||||
@@ -167,8 +167,10 @@ fn login_first_message_roundtrip() {
|
||||
rng.fill_bytes(&mut client_nonce);
|
||||
|
||||
let ke1m: Vec<u8> = [&client_nonce[..], &client_e_kp.public()].concat();
|
||||
let reg =
|
||||
<TripleDH as KeyExchange<sha2::Sha256>>::KE1Message::try_from(ke1m[..].to_vec()).unwrap();
|
||||
let reg = <TripleDH as KeyExchange<sha2::Sha256, crate::keypair::X25519KeyPair>>::KE1Message::try_from(
|
||||
ke1m[..].to_vec(),
|
||||
)
|
||||
.unwrap();
|
||||
let reg_bytes = reg.to_bytes();
|
||||
assert_eq!(reg_bytes, ke1m);
|
||||
}
|
||||
@@ -197,7 +199,7 @@ proptest! {
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_login_second_message(bytes in vec(any::<u8>(), 0..500)) {
|
||||
LoginSecondMessage::<RistrettoPoint, crate::keypair::X25519KeyPair, TripleDH, sha2::Sha512>::try_from(&bytes[..]).map_or(true, |_| true);
|
||||
LoginSecondMessage::<Default>::try_from(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user