From f8285c60ba8a385d72b6681223023bdf322ac795 Mon Sep 17 00:00:00 2001 From: Kevin Lewi Date: Mon, 13 Jul 2020 15:23:29 -0700 Subject: [PATCH] Introducing a trait for key exchange (#20) --- src/ciphersuite.rs | 10 + src/key_exchange.rs | 417 ----------------------------------- src/key_exchange/mod.rs | 10 + src/key_exchange/traits.rs | 55 +++++ src/key_exchange/tripledh.rs | 417 +++++++++++++++++++++++++++++++++++ src/keypair.rs | 2 +- src/lib.rs | 15 +- src/opaque.rs | 151 ++++++++----- src/tests/opaque_ke_test.rs | 32 ++- src/tests/serialization.rs | 8 +- 10 files changed, 627 insertions(+), 490 deletions(-) delete mode 100644 src/key_exchange.rs create mode 100644 src/key_exchange/mod.rs create mode 100644 src/key_exchange/traits.rs create mode 100644 src/key_exchange/tripledh.rs diff --git a/src/ciphersuite.rs b/src/ciphersuite.rs index c9db67b..ad7f256 100644 --- a/src/ciphersuite.rs +++ b/src/ciphersuite.rs @@ -7,6 +7,7 @@ use crate::{ errors::InternalPakeError, + key_exchange::traits::KeyExchange, keypair::{Key, KeyPair}, map_to_curve::GroupWithMapToCurve, slow_hash::SlowHash, @@ -15,6 +16,13 @@ use crate::{ use rand_core::{CryptoRng, RngCore}; /// Configures the underlying primitives used in OPAQUE +/// * `Group`: a finite cyclic group along with a point representation, along +/// with an extension trait PasswordToCurve that allows some customization on +/// how to hash a password to a curve point. See `group::Group` and +/// `map_to_curve::GroupWithMapToCurve`. +/// * `KeyFormat`: a keypair type composed of public and private components +/// * `KeyExchange`: The key exchange protocol to use in the login step +/// * `SlowHash`: a slow hashing function, typically used for password hashing pub trait CipherSuite { /// A finite cyclic group along with a point representation along with /// an extension trait PasswordToCurve that allows some customization on @@ -23,6 +31,8 @@ pub trait CipherSuite { type Group: GroupWithMapToCurve; /// A keypair type composed of public and private components type KeyFormat: KeyPair + PartialEq; + /// A key exchange protocol + type KeyExchange: KeyExchange; /// A slow hashing function, typically used for password hashing type SlowHash: SlowHash; diff --git a/src/key_exchange.rs b/src/key_exchange.rs deleted file mode 100644 index 494f5a9..0000000 --- a/src/key_exchange.rs +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under the MIT license found in the -// LICENSE file in the root directory of this source tree. - -use crate::{ - errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError}, - keypair::{Key, KeyPair, SizedBytes}, - sized_bytes_using_constant_and_try_from, -}; -use generic_array::{ - typenum::{U32, U64, U96}, - GenericArray, -}; -use hkdf::Hkdf; -use hmac::{Hmac, Mac, NewMac}; -use rand_core::{CryptoRng, RngCore}; - -use sha2::{Digest, Sha256}; -use std::convert::TryFrom; - -/// This module is a somewhat minimalistic implementation of a key Exchange -/// protocol based on 3DH. It assumes a pre-exchange has allowed client and -/// server to learn each other's static public key. -/// -/// This private module may undergo significant changes in the near term. - -const KEY_LEN: usize = 32; -pub(crate) const NONCE_LEN: usize = 32; -pub(crate) const KE1_STATE_LEN: usize = KEY_LEN + KEY_LEN + NONCE_LEN; -pub(crate) const KE2_MESSAGE_LEN: usize = NONCE_LEN + 2 * KEY_LEN; - -static STR_3DH: &[u8] = b"3DH keys"; - -#[derive(PartialEq, Eq)] -pub(crate) struct KE1State { - client_e_sk: Key, - client_nonce: Vec, - hashed_l1: Vec, -} - -#[derive(PartialEq, Eq)] -pub(crate) struct KE1Message { - pub(crate) client_nonce: Vec, - pub(crate) client_e_pk: Key, -} - -impl TryFrom<&[u8]> for KE1State { - type Error = InternalPakeError; - - fn try_from(bytes: &[u8]) -> Result { - let checked_bytes = check_slice_size(bytes, KE1_STATE_LEN, "ke1_state")?; - - Ok(Self { - client_e_sk: Key::from_bytes(&checked_bytes[..KEY_LEN])?, - client_nonce: checked_bytes[KEY_LEN..KEY_LEN + NONCE_LEN].to_vec(), - hashed_l1: checked_bytes[KEY_LEN + NONCE_LEN..].to_vec(), - }) - } -} - -impl KE1State { - pub fn to_bytes(&self) -> Vec { - let output: Vec = [ - &self.client_e_sk.to_arr(), - &self.client_nonce[..], - &self.hashed_l1[..], - ] - .concat(); - output - } -} - -sized_bytes_using_constant_and_try_from!(KE1State, U96); - -impl KE1Message { - pub fn to_bytes(&self) -> Vec { - [&self.client_nonce[..], &self.client_e_pk.to_arr()].concat() - } -} - -impl TryFrom<&[u8]> for KE1Message { - type Error = InternalPakeError; - - fn try_from(ke1_message_bytes: &[u8]) -> Result { - let checked_bytes = - check_slice_size(ke1_message_bytes, NONCE_LEN + KEY_LEN, "ke1_message")?; - - Ok(Self { - client_nonce: checked_bytes[..NONCE_LEN].to_vec(), - client_e_pk: Key::from_bytes(&checked_bytes[NONCE_LEN..])?, - }) - } -} - -sized_bytes_using_constant_and_try_from!(KE1Message, U64); - -pub(crate) fn generate_ke1>( - l1_component: Vec, - rng: &mut R, -) -> Result<(KE1State, KE1Message), ProtocolError> { - let client_e_kp = KeyFormat::generate_random(rng)?; - let mut client_nonce = [0u8; NONCE_LEN]; - rng.fill_bytes(&mut client_nonce); - - let ke1_message = KE1Message { - client_nonce: client_nonce.to_vec(), - client_e_pk: client_e_kp.public().clone(), - }; - - let l1_data: Vec = [&l1_component[..], &ke1_message.to_bytes()].concat(); - let mut hasher = Sha256::new(); - hasher.update(&l1_data); - let hashed_l1 = hasher.finalize(); - - Ok(( - KE1State { - client_e_sk: client_e_kp.private().clone(), - client_nonce: client_nonce.to_vec(), - hashed_l1: hashed_l1.to_vec(), - }, - ke1_message, - )) -} - -pub(crate) struct KE2State { - km3: Vec, - hashed_transcript: Vec, - shared_secret: Vec, -} - -pub(crate) struct KE2Message { - server_nonce: Vec, - server_e_pk: Key, - mac: Vec, -} - -impl KE2State { - pub fn to_bytes(&self) -> Vec { - let output: Vec = [ - &self.km3[..], - &self.hashed_transcript[..], - &self.shared_secret[..], - ] - .concat(); - output - } -} - -impl TryFrom<&[u8]> for KE2State { - type Error = ProtocolError; - - fn try_from(ke1_message_bytes: &[u8]) -> Result { - let checked_bytes = check_slice_size(ke1_message_bytes, 3 * KEY_LEN, "ke2_state")?; - - Ok(Self { - km3: checked_bytes[..KEY_LEN].to_vec(), - hashed_transcript: checked_bytes[KEY_LEN..2 * KEY_LEN].to_vec(), - shared_secret: checked_bytes[2 * KEY_LEN..].to_vec(), - }) - } -} - -impl KE2Message { - pub fn to_bytes(&self) -> Vec { - let output: Vec = [ - &self.server_nonce[..], - &self.server_e_pk.to_arr(), - &self.mac[..], - ] - .concat(); - output - } -} - -impl TryFrom<&[u8]> for KE2Message { - type Error = ProtocolError; - - fn try_from(ke1_message_bytes: &[u8]) -> Result { - let checked_bytes = check_slice_size(ke1_message_bytes, KE2_MESSAGE_LEN, "ke2_message")?; - - Ok(Self { - server_nonce: checked_bytes[..NONCE_LEN].to_vec(), - server_e_pk: Key::from_bytes(&checked_bytes[NONCE_LEN..NONCE_LEN + KEY_LEN])?, - mac: checked_bytes[NONCE_LEN + KEY_LEN..].to_vec(), - }) - } -} - -// The triple of public and private components used in the 3DH computation -struct TripleDHComponents { - pk1: Key, - sk1: Key, - pk2: Key, - sk2: Key, - pk3: Key, - sk3: Key, -} - -// Consists of a shared secret, followed by two mac keys -type TripleDHDerivationResult = ( - GenericArray, - GenericArray, - GenericArray, -); - -// Internal function which takes the public and private components of the client and server keypairs, along -// with some auxiliary metadata, to produce the shared secret and two MAC keys -fn derive_3dh_keys>( - dh: TripleDHComponents, - client_nonce: &[u8], - server_nonce: &[u8], - client_s_pk: KeyFormat::Repr, - server_s_pk: KeyFormat::Repr, -) -> Result { - let ikm: Vec = [ - &KeyFormat::diffie_hellman(dh.pk1, dh.sk1)[..], - &KeyFormat::diffie_hellman(dh.pk2, dh.sk2)[..], - &KeyFormat::diffie_hellman(dh.pk3, dh.sk3)[..], - ] - .concat(); - - let info: Vec = [ - STR_3DH, - &client_nonce, - &server_nonce, - &client_s_pk.to_arr(), - &server_s_pk.to_arr(), - ] - .concat(); - - const OUTPUT_SIZE: usize = 32; - let mut okm = [0u8; 3 * OUTPUT_SIZE]; - let h = Hkdf::::new(None, &ikm); - h.expand(&info, &mut okm) - .map_err(|_| InternalPakeError::HkdfError)?; - Ok(( - *GenericArray::from_slice(&okm[..OUTPUT_SIZE]), - *GenericArray::from_slice(&okm[OUTPUT_SIZE..2 * OUTPUT_SIZE]), - *GenericArray::from_slice(&okm[2 * OUTPUT_SIZE..]), - )) -} - -pub(crate) fn generate_ke2>( - rng: &mut R, - l1_bytes: Vec, - l2_bytes: Vec, - client_e_pk: KeyFormat::Repr, - client_s_pk: KeyFormat::Repr, - server_s_sk: KeyFormat::Repr, - client_nonce: Vec, -) -> Result<(KE2State, KE2Message), ProtocolError> { - let server_e_kp = KeyFormat::generate_random(rng)?; - let mut server_nonce = [0u8; NONCE_LEN]; - rng.fill_bytes(&mut server_nonce); - - let (shared_secret, km2, km3) = derive_3dh_keys::( - TripleDHComponents { - pk1: client_e_pk.clone(), - sk1: server_e_kp.private().clone(), - pk2: client_e_pk, - sk2: server_s_sk.clone(), - pk3: client_s_pk.clone(), - sk3: server_e_kp.private().clone(), - }, - &client_nonce, - &server_nonce, - client_s_pk, - KeyFormat::public_from_private(&server_s_sk), - )?; - - let mut hasher = Sha256::new(); - hasher.update(&l1_bytes); - let hashed_l1 = hasher.finalize(); - - let transcript2: Vec = [ - &hashed_l1[..], - &l2_bytes[..], - &server_nonce[..], - &server_e_kp.public().to_arr(), - ] - .concat(); - - let mut hasher2 = Sha256::new(); - hasher2.update(&transcript2); - let hashed_transcript = hasher2.finalize(); - - let mut mac = Hmac::::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?; - mac.update(&hashed_transcript); - - Ok(( - KE2State { - km3: km3.to_vec(), - hashed_transcript: hashed_transcript.to_vec(), - shared_secret: shared_secret.to_vec(), - }, - KE2Message { - server_nonce: server_nonce.to_vec(), - server_e_pk: server_e_kp.public().clone(), - mac: mac.finalize().into_bytes().to_vec(), - }, - )) -} - -pub(crate) struct KE3State { - pub(crate) shared_secret: Vec, -} - -pub(crate) struct KE3Message { - mac: Vec, -} - -impl TryFrom<&[u8]> for KE3State { - type Error = ProtocolError; - - fn try_from(bytes: &[u8]) -> Result { - let checked_bytes = check_slice_size(bytes, KEY_LEN, "ke3_state")?; - - Ok(Self { - shared_secret: checked_bytes.to_vec(), - }) - } -} - -impl KE3Message { - pub fn to_bytes(&self) -> Vec { - self.mac.clone() - } -} - -impl TryFrom<&[u8]> for KE3Message { - type Error = ProtocolError; - - fn try_from(bytes: &[u8]) -> Result { - let checked_bytes = check_slice_size(bytes, KEY_LEN, "ke3_message")?; - - Ok(Self { - mac: checked_bytes.to_vec(), - }) - } -} - -pub(crate) fn generate_ke3>( - l2_component: Vec, - ke2_message: KE2Message, - ke1_state: &KE1State, - server_s_pk: KeyFormat::Repr, - client_s_sk: KeyFormat::Repr, -) -> Result<(KE3State, KE3Message), ProtocolError> { - let (shared_secret, km2, km3) = derive_3dh_keys::( - TripleDHComponents { - pk1: ke2_message.server_e_pk.clone(), - sk1: ke1_state.client_e_sk.clone(), - pk2: server_s_pk.clone(), - sk2: ke1_state.client_e_sk.clone(), - pk3: ke2_message.server_e_pk.clone(), - sk3: client_s_sk.clone(), - }, - &ke1_state.client_nonce, - &ke2_message.server_nonce, - KeyFormat::public_from_private(&client_s_sk), - server_s_pk, - )?; - - let transcript: Vec = [ - &ke1_state.hashed_l1[..], - &l2_component[..], - &ke2_message.server_nonce[..], - &ke2_message.server_e_pk[..], - ] - .concat(); - - let mut hasher = Sha256::new(); - hasher.update(&transcript); - let hashed_transcript = hasher.finalize(); - - let mut server_mac = - Hmac::::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?; - server_mac.update(&hashed_transcript); - - if ke2_message.mac != server_mac.finalize().into_bytes().to_vec() { - return Err(ProtocolError::VerificationError( - PakeError::KeyExchangeMacValidationError, - )); - } - - let mut client_mac = - Hmac::::new_varkey(&km3).map_err(|_| InternalPakeError::HmacError)?; - client_mac.update(&hashed_transcript); - - Ok(( - KE3State { - shared_secret: shared_secret.to_vec(), - }, - KE3Message { - mac: client_mac.finalize().into_bytes().to_vec(), - }, - )) -} - -// Outputs a shared secret -pub(crate) fn finish_ke( - ke3_message: KE3Message, - ke2_state: &KE2State, -) -> Result, ProtocolError> { - let mut client_mac = - Hmac::::new_varkey(&ke2_state.km3).map_err(|_| InternalPakeError::HmacError)?; - client_mac.update(&ke2_state.hashed_transcript); - - if ke3_message.mac != client_mac.finalize().into_bytes().to_vec() { - return Err(ProtocolError::VerificationError( - PakeError::KeyExchangeMacValidationError, - )); - } - - Ok(ke2_state.shared_secret.to_vec()) -} diff --git a/src/key_exchange/mod.rs b/src/key_exchange/mod.rs new file mode 100644 index 0000000..3f1daa7 --- /dev/null +++ b/src/key_exchange/mod.rs @@ -0,0 +1,10 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +//! Includes instantiations of key exchange protocols used in the +//! login step for OPAQUE + +pub(crate) mod traits; +pub mod tripledh; diff --git a/src/key_exchange/traits.rs b/src/key_exchange/traits.rs new file mode 100644 index 0000000..771a31b --- /dev/null +++ b/src/key_exchange/traits.rs @@ -0,0 +1,55 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +use crate::{ + errors::{InternalPakeError, ProtocolError}, + keypair::{Key, KeyPair}, +}; +use rand_core::{CryptoRng, RngCore}; + +use std::convert::TryFrom; + +pub trait KeyExchange { + type KE1State: TryFrom, Error = InternalPakeError> + ToBytes; + type KE2State: TryFrom, Error = ProtocolError> + ToBytes; + type KE1Message: TryFrom, Error = InternalPakeError> + ToBytes; + type KE2Message: TryFrom, Error = ProtocolError> + ToBytes; + type KE3Message: TryFrom, Error = ProtocolError> + ToBytes; + + fn generate_ke1>( + l1_component: Vec, + rng: &mut R, + ) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>; + + fn generate_ke2>( + rng: &mut R, + l1_bytes: Vec, + l2_bytes: Vec, + ke1_message: Self::KE1Message, + client_s_pk: KeyFormat::Repr, + server_s_sk: KeyFormat::Repr, + ) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError>; + + fn generate_ke3>( + l2_component: Vec, + ke2_message: Self::KE2Message, + ke1_state: &Self::KE1State, + server_s_pk: KeyFormat::Repr, + client_s_sk: KeyFormat::Repr, + ) -> Result<(Vec, Self::KE3Message), ProtocolError>; + + fn finish_ke( + ke3_message: Self::KE3Message, + ke2_state: &Self::KE2State, + ) -> Result, ProtocolError>; + + fn ke1_state_size() -> usize; + + fn ke2_message_size() -> usize; +} + +pub trait ToBytes { + fn to_bytes(&self) -> Vec; +} diff --git a/src/key_exchange/tripledh.rs b/src/key_exchange/tripledh.rs new file mode 100644 index 0000000..a22cd0b --- /dev/null +++ b/src/key_exchange/tripledh.rs @@ -0,0 +1,417 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +//! An implementation of the Triple Diffie-Hellman key exchange protocol +use crate::{ + errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError}, + key_exchange::traits::{KeyExchange, ToBytes}, + keypair::{Key, KeyPair, SizedBytes}, + sized_bytes_using_constant_and_try_from, +}; +use generic_array::{ + typenum::{U64, U96}, + GenericArray, +}; +use hkdf::Hkdf; +use hmac::{Hmac, Mac, NewMac}; +use rand_core::{CryptoRng, RngCore}; + +use sha2::{Digest, Sha256}; +use std::convert::TryFrom; + +const KEY_LEN: usize = 32; +pub(crate) const NONCE_LEN: usize = 32; +const KE1_STATE_LEN: usize = KEY_LEN + KEY_LEN + NONCE_LEN; +const KE2_MESSAGE_LEN: usize = NONCE_LEN + 2 * KEY_LEN; + +static STR_3DH: &[u8] = b"3DH keys"; + +/// The Triple Diffie-Hellman key exchange implementation +pub struct TripleDH {} + +impl KeyExchange for TripleDH { + type KE1State = KE1State; + type KE2State = KE2State; + type KE1Message = KE1Message; + type KE2Message = KE2Message; + type KE3Message = KE3Message; + + fn generate_ke1>( + l1_component: Vec, + rng: &mut R, + ) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> { + let client_e_kp = KeyFormat::generate_random(rng)?; + let mut client_nonce = [0u8; NONCE_LEN]; + rng.fill_bytes(&mut client_nonce); + + let ke1_message = KE1Message { + client_nonce: client_nonce.to_vec(), + client_e_pk: client_e_kp.public().clone(), + }; + + let l1_data: Vec = [&l1_component[..], &ke1_message.to_bytes()].concat(); + let mut hasher = Sha256::new(); + hasher.update(&l1_data); + let hashed_l1 = hasher.finalize(); + + Ok(( + KE1State { + client_e_sk: client_e_kp.private().clone(), + client_nonce: client_nonce.to_vec(), + hashed_l1: hashed_l1.to_vec(), + }, + ke1_message, + )) + } + + fn generate_ke2>( + rng: &mut R, + l1_bytes: Vec, + l2_bytes: Vec, + ke1_message: Self::KE1Message, + client_s_pk: KeyFormat::Repr, + server_s_sk: KeyFormat::Repr, + ) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError> { + let server_e_kp = KeyFormat::generate_random(rng)?; + let mut server_nonce = [0u8; NONCE_LEN]; + rng.fill_bytes(&mut server_nonce); + + let (shared_secret, km2, km3) = derive_3dh_keys::( + TripleDHComponents { + pk1: ke1_message.client_e_pk.clone(), + sk1: server_e_kp.private().clone(), + pk2: ke1_message.client_e_pk, + sk2: server_s_sk.clone(), + pk3: client_s_pk.clone(), + sk3: server_e_kp.private().clone(), + }, + &ke1_message.client_nonce, + &server_nonce, + client_s_pk, + KeyFormat::public_from_private(&server_s_sk), + )?; + + let mut hasher = Sha256::new(); + hasher.update(&l1_bytes); + let hashed_l1 = hasher.finalize(); + + let transcript2: Vec = [ + &hashed_l1[..], + &l2_bytes[..], + &server_nonce[..], + &server_e_kp.public().to_arr(), + ] + .concat(); + + let mut hasher2 = Sha256::new(); + hasher2.update(&transcript2); + let hashed_transcript = hasher2.finalize(); + + let mut mac = Hmac::::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?; + mac.update(&hashed_transcript); + + Ok(( + KE2State { + km3: km3.to_vec(), + hashed_transcript: hashed_transcript.to_vec(), + shared_secret: shared_secret.to_vec(), + }, + KE2Message { + server_nonce: server_nonce.to_vec(), + server_e_pk: server_e_kp.public().clone(), + mac: mac.finalize().into_bytes().to_vec(), + }, + )) + } + + fn generate_ke3>( + l2_component: Vec, + ke2_message: Self::KE2Message, + ke1_state: &Self::KE1State, + server_s_pk: KeyFormat::Repr, + client_s_sk: KeyFormat::Repr, + ) -> Result<(Vec, Self::KE3Message), ProtocolError> { + let (shared_secret, km2, km3) = derive_3dh_keys::( + TripleDHComponents { + pk1: ke2_message.server_e_pk.clone(), + sk1: ke1_state.client_e_sk.clone(), + pk2: server_s_pk.clone(), + sk2: ke1_state.client_e_sk.clone(), + pk3: ke2_message.server_e_pk.clone(), + sk3: client_s_sk.clone(), + }, + &ke1_state.client_nonce, + &ke2_message.server_nonce, + KeyFormat::public_from_private(&client_s_sk), + server_s_pk, + )?; + + let transcript: Vec = [ + &ke1_state.hashed_l1[..], + &l2_component[..], + &ke2_message.server_nonce[..], + &ke2_message.server_e_pk[..], + ] + .concat(); + + let mut hasher = Sha256::new(); + hasher.update(&transcript); + let hashed_transcript = hasher.finalize(); + + let mut server_mac = + Hmac::::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?; + server_mac.update(&hashed_transcript); + + if ke2_message.mac != server_mac.finalize().into_bytes().to_vec() { + return Err(ProtocolError::VerificationError( + PakeError::KeyExchangeMacValidationError, + )); + } + + let mut client_mac = + Hmac::::new_varkey(&km3).map_err(|_| InternalPakeError::HmacError)?; + client_mac.update(&hashed_transcript); + + Ok(( + shared_secret.to_vec(), + KE3Message { + mac: client_mac.finalize().into_bytes().to_vec(), + }, + )) + } + + fn finish_ke( + ke3_message: Self::KE3Message, + ke2_state: &Self::KE2State, + ) -> Result, ProtocolError> { + let mut client_mac = + Hmac::::new_varkey(&ke2_state.km3).map_err(|_| InternalPakeError::HmacError)?; + client_mac.update(&ke2_state.hashed_transcript); + + if ke3_message.mac != client_mac.finalize().into_bytes().to_vec() { + return Err(ProtocolError::VerificationError( + PakeError::KeyExchangeMacValidationError, + )); + } + + Ok(ke2_state.shared_secret.to_vec()) + } + + fn ke1_state_size() -> usize { + KE1_STATE_LEN + } + + fn ke2_message_size() -> usize { + KE2_MESSAGE_LEN + } +} + +/// The client state produced after the first key exchange message +#[derive(PartialEq, Eq)] +pub struct KE1State { + client_e_sk: Key, + client_nonce: Vec, + hashed_l1: Vec, +} + +/// The first key exchange message +#[derive(PartialEq, Eq)] +pub struct KE1Message { + pub(crate) client_nonce: Vec, + pub(crate) client_e_pk: Key, +} + +impl TryFrom> for KE1State { + type Error = InternalPakeError; + + fn try_from(bytes: Vec) -> Result { + let checked_bytes = check_slice_size(&bytes, KE1_STATE_LEN, "ke1_state")?; + + Ok(Self { + client_e_sk: Key::from_bytes(&checked_bytes[..KEY_LEN])?, + client_nonce: checked_bytes[KEY_LEN..KEY_LEN + NONCE_LEN].to_vec(), + hashed_l1: checked_bytes[KEY_LEN + NONCE_LEN..].to_vec(), + }) + } +} + +impl ToBytes for KE1State { + fn to_bytes(&self) -> Vec { + let output: Vec = [ + &self.client_e_sk.to_arr(), + &self.client_nonce[..], + &self.hashed_l1[..], + ] + .concat(); + output + } +} + +sized_bytes_using_constant_and_try_from!(KE1State, U96); + +impl ToBytes for KE1Message { + fn to_bytes(&self) -> Vec { + [&self.client_nonce[..], &self.client_e_pk.to_arr()].concat() + } +} + +impl TryFrom> for KE1Message { + type Error = InternalPakeError; + + fn try_from(ke1_message_bytes: Vec) -> Result { + let checked_bytes = + check_slice_size(&ke1_message_bytes, NONCE_LEN + KEY_LEN, "ke1_message")?; + + Ok(Self { + client_nonce: checked_bytes[..NONCE_LEN].to_vec(), + client_e_pk: Key::from_bytes(&checked_bytes[NONCE_LEN..])?, + }) + } +} + +sized_bytes_using_constant_and_try_from!(KE1Message, U64); + +/// The server state produced after the second key exchange message +pub struct KE2State { + km3: Vec, + hashed_transcript: Vec, + shared_secret: Vec, +} + +/// The second key exchange message +pub struct KE2Message { + server_nonce: Vec, + server_e_pk: Key, + mac: Vec, +} + +impl ToBytes for KE2State { + fn to_bytes(&self) -> Vec { + let output: Vec = [ + &self.km3[..], + &self.hashed_transcript[..], + &self.shared_secret[..], + ] + .concat(); + output + } +} + +impl TryFrom> for KE2State { + type Error = ProtocolError; + + fn try_from(ke1_message_bytes: Vec) -> Result { + let checked_bytes = check_slice_size(&ke1_message_bytes, 3 * KEY_LEN, "ke2_state")?; + + Ok(Self { + km3: checked_bytes[..KEY_LEN].to_vec(), + hashed_transcript: checked_bytes[KEY_LEN..2 * KEY_LEN].to_vec(), + shared_secret: checked_bytes[2 * KEY_LEN..].to_vec(), + }) + } +} + +impl ToBytes for KE2Message { + fn to_bytes(&self) -> Vec { + let output: Vec = [ + &self.server_nonce[..], + &self.server_e_pk.to_arr(), + &self.mac[..], + ] + .concat(); + output + } +} + +impl TryFrom> for KE2Message { + type Error = ProtocolError; + + fn try_from(ke2_message_bytes: Vec) -> Result { + let checked_bytes = check_slice_size(&ke2_message_bytes, KE2_MESSAGE_LEN, "ke2_message")?; + + Ok(Self { + server_nonce: checked_bytes[..NONCE_LEN].to_vec(), + server_e_pk: Key::from_bytes(&checked_bytes[NONCE_LEN..NONCE_LEN + KEY_LEN])?, + mac: checked_bytes[NONCE_LEN + KEY_LEN..].to_vec(), + }) + } +} + +// The triple of public and private components used in the 3DH computation +struct TripleDHComponents { + pk1: Key, + sk1: Key, + pk2: Key, + sk2: Key, + pk3: Key, + sk3: Key, +} + +// Consists of a shared secret, followed by two mac keys +type TripleDHDerivationResult = ( + GenericArray::OutputSize>, + GenericArray::OutputSize>, + GenericArray::OutputSize>, +); + +// Internal function which takes the public and private components of the client and server keypairs, along +// with some auxiliary metadata, to produce the shared secret and two MAC keys +fn derive_3dh_keys>( + dh: TripleDHComponents, + client_nonce: &[u8], + server_nonce: &[u8], + client_s_pk: KeyFormat::Repr, + server_s_pk: KeyFormat::Repr, +) -> Result { + let ikm: Vec = [ + &KeyFormat::diffie_hellman(dh.pk1, dh.sk1)[..], + &KeyFormat::diffie_hellman(dh.pk2, dh.sk2)[..], + &KeyFormat::diffie_hellman(dh.pk3, dh.sk3)[..], + ] + .concat(); + + let info: Vec = [ + STR_3DH, + &client_nonce, + &server_nonce, + &client_s_pk.to_arr(), + &server_s_pk.to_arr(), + ] + .concat(); + + const OUTPUT_SIZE: usize = 32; + let mut okm = [0u8; 3 * OUTPUT_SIZE]; + let h = Hkdf::::new(None, &ikm); + h.expand(&info, &mut okm) + .map_err(|_| InternalPakeError::HkdfError)?; + Ok(( + *GenericArray::from_slice(&okm[..OUTPUT_SIZE]), + *GenericArray::from_slice(&okm[OUTPUT_SIZE..2 * OUTPUT_SIZE]), + *GenericArray::from_slice(&okm[2 * OUTPUT_SIZE..]), + )) +} + +/// The third key exchange message +pub struct KE3Message { + mac: Vec, +} + +impl ToBytes for KE3Message { + fn to_bytes(&self) -> Vec { + self.mac.clone() + } +} + +impl TryFrom> for KE3Message { + type Error = ProtocolError; + + fn try_from(bytes: Vec) -> Result { + let checked_bytes = check_slice_size(&bytes, KEY_LEN, "ke3_message")?; + + Ok(Self { + mac: checked_bytes.to_vec(), + }) + } +} diff --git a/src/keypair.rs b/src/keypair.rs index 85625a2..d68db58 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -114,7 +114,7 @@ macro_rules! sized_bytes_using_constant_and_try_from { ::to_usize(), "bytes", )?; - std::convert::TryFrom::try_from(checked_bytes) + std::convert::TryFrom::try_from(checked_bytes.to_vec()) } } }; diff --git a/src/lib.rs b/src/lib.rs index c96ba89..95eb464 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,8 @@ //! OPAQUE is a protocol between a client and a server. They must first agree on a collection of primitives //! to be kept consistent throughout protocol execution. These include: //! * a finite cyclic group along with a point representation, -//! * a keypair type, and +//! * a keypair type, +//! * a key exchange protocol, and //! * a slow hashing function. //! //! We will use the following choices in this example: @@ -23,6 +24,7 @@ //! impl CipherSuite for Default { //! type Group = curve25519_dalek::ristretto::RistrettoPoint; //! type KeyFormat = opaque_ke::keypair::X25519KeyPair; +//! type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! type SlowHash = opaque_ke::slow_hash::NoOpHash; //! } //! ``` @@ -43,6 +45,7 @@ //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! use rand_core::{OsRng, RngCore}; @@ -74,6 +77,7 @@ //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! use rand_core::{OsRng, RngCore}; @@ -102,6 +106,7 @@ //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -133,6 +138,7 @@ //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -166,6 +172,7 @@ //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -207,6 +214,7 @@ //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -235,6 +243,7 @@ //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -278,6 +287,7 @@ //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -333,6 +343,7 @@ //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -387,7 +398,7 @@ mod envelope; mod group; mod map_to_curve; -mod key_exchange; +pub mod key_exchange; pub mod keypair; mod oprf; diff --git a/src/opaque.rs b/src/opaque.rs index b0eeb35..e36e312 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -10,10 +10,7 @@ use crate::{ envelope::{Envelope, ExportKeySize}, errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError}, group::Group, - key_exchange::{ - finish_ke, generate_ke1, generate_ke2, generate_ke3, KE1Message, KE1State, KE2Message, - KE2State, KE3Message, KE1_STATE_LEN, KE2_MESSAGE_LEN, - }, + key_exchange::traits::{KeyExchange, ToBytes}, keypair::{Key, KeyPair, SizedBytes}, oprf, oprf::OprfClientBytes, @@ -133,27 +130,29 @@ where } /// The message sent by the user to the server, to initiate registration -pub struct LoginFirstMessage { +pub struct LoginFirstMessage { /// blinded password information - alpha: Grp, - ke1_message: KE1Message, + alpha: CS::Group, + ke1_message: ::KE1Message, } -impl TryFrom<&[u8]> for LoginFirstMessage { +impl TryFrom<&[u8]> for LoginFirstMessage { type Error = ProtocolError; fn try_from(first_message_bytes: &[u8]) -> Result { // Check that the message is actually containing an element of the // correct subgroup - let elem_len = Grp::ElemLen::to_usize(); + let elem_len = ::ElemLen::to_usize(); let arr = GenericArray::from_slice(&first_message_bytes[..elem_len]); - let alpha = Grp::from_element_slice(arr)?; + let alpha = CS::Group::from_element_slice(arr)?; - let ke1_message = KE1Message::try_from(&first_message_bytes[elem_len..])?; + let ke1_message = ::KE1Message::try_from( + first_message_bytes[elem_len..].to_vec(), + )?; Ok(Self { alpha, ke1_message }) } } -impl LoginFirstMessage { +impl LoginFirstMessage { /// byte representation for the login request pub fn to_bytes(&self) -> Vec { [&self.alpha.to_arr()[..], &self.ke1_message.to_bytes()].concat() @@ -162,19 +161,25 @@ impl LoginFirstMessage { /// The answer sent by the server to the user, upon reception of the /// login attempt. -pub struct LoginSecondMessage { +pub struct LoginSecondMessage +where + KeyFormat: KeyPair, + KE: KeyExchange, +{ _key_format: PhantomData, + _key_exchange: PhantomData, /// the server's oprf output beta: Grp, /// the user's sealed information, envelope: Envelope, - ke2_message: KE2Message, + ke2_message: KE::KE2Message, } -impl LoginSecondMessage +impl LoginSecondMessage where Grp: Group, - KeyFormat: KeyPair, + KeyFormat: KeyPair, + KE: KeyExchange, { /// byte representation for the login response pub fn to_bytes(&self) -> Vec { @@ -187,19 +192,21 @@ where } } -impl TryFrom<&[u8]> for LoginSecondMessage +impl TryFrom<&[u8]> for LoginSecondMessage where Grp: Group, - KeyFormat: KeyPair, + KeyFormat: KeyPair, + KE: KeyExchange, { type Error = ProtocolError; fn try_from(second_message_bytes: &[u8]) -> Result { let key_len = ::Len::to_usize(); let envelope_size = key_len + Envelope::additional_size(); let elem_len = Grp::ElemLen::to_usize(); + let ke2_message_size = KE::ke2_message_size(); let checked_slice = check_slice_size( second_message_bytes, - elem_len + envelope_size + KE2_MESSAGE_LEN, + elem_len + envelope_size + ke2_message_size, "login_second_message_bytes", )?; @@ -210,10 +217,13 @@ where let beta = Grp::from_element_slice(arr)?; let envelope = Envelope::from_bytes(&checked_slice[elem_len..elem_len + envelope_size])?; - let ke2_message = KE2Message::try_from(&checked_slice[elem_len + envelope_size..])?; + + let ke2_message = + KE::KE2Message::try_from(checked_slice[elem_len + envelope_size..].to_vec())?; Ok(Self { _key_format: PhantomData, + _key_exchange: PhantomData, beta, envelope, ke2_message, @@ -223,20 +233,20 @@ where /// The answer sent by the client to the server, upon reception of the /// sealed envelope -pub struct LoginThirdMessage { - ke3_message: KE3Message, +pub struct LoginThirdMessage { + ke3_message: ::KE3Message, } -impl TryFrom<&[u8]> for LoginThirdMessage { +impl TryFrom<&[u8]> for LoginThirdMessage { type Error = ProtocolError; fn try_from(bytes: &[u8]) -> Result { - let ke3_message = KE3Message::try_from(&bytes[..])?; + let ke3_message = ::KE3Message::try_from(bytes.to_vec())?; Ok(Self { ke3_message }) } } -impl LoginThirdMessage { +impl LoginThirdMessage { /// byte representation for the login finalization pub fn to_bytes(&self) -> Vec { self.ke3_message.to_bytes() @@ -299,6 +309,7 @@ impl ClientRegistration { /// impl CipherSuite for Default { /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; + /// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut rng = OsRng; @@ -349,6 +360,7 @@ impl ClientRegistration { /// impl CipherSuite for Default { /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; + /// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; @@ -513,6 +525,7 @@ where /// impl CipherSuite for Default { /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; + /// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; @@ -559,6 +572,7 @@ where /// impl CipherSuite for Default { /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; + /// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; @@ -596,7 +610,7 @@ pub struct ClientLogin { blinding_factor: ::Scalar, /// The user's password password: Vec, - ke1_state: KE1State, + ke1_state: ::KE1State, } impl TryFrom<&[u8]> for ClientLogin { @@ -605,8 +619,11 @@ impl TryFrom<&[u8]> for ClientLogin { let scalar_len = ::ScalarLen::to_usize(); let blinding_factor_bytes = GenericArray::from_slice(&bytes[..scalar_len]); let blinding_factor = CS::Group::from_scalar_slice(blinding_factor_bytes)?; - let ke1_state = KE1State::try_from(&bytes[scalar_len..scalar_len + KE1_STATE_LEN])?; - let password = bytes[scalar_len + KE1_STATE_LEN..].to_vec(); + let ke1_state_size = ::ke1_state_size(); + let ke1_state = ::KE1State::try_from( + bytes[scalar_len..scalar_len + ke1_state_size].to_vec(), + )?; + let password = bytes[scalar_len + ke1_state_size..].to_vec(); Ok(Self { _key_format: PhantomData, blinding_factor, @@ -629,7 +646,11 @@ impl ClientLogin { } } -type ClientLoginFinishResult = (LoginThirdMessage, Vec, GenericArray); +type ClientLoginFinishResult = ( + LoginThirdMessage, + Vec, + GenericArray, +); impl ClientLogin { /// Returns an initial "blinded" password request to send to the server, as well as a ClientLogin @@ -648,6 +669,7 @@ impl ClientLogin { /// impl CipherSuite for Default { /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; + /// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; @@ -658,14 +680,14 @@ impl ClientLogin { password: &[u8], pepper: Option<&[u8]>, rng: &mut R, - ) -> Result<(LoginFirstMessage, Self), ProtocolError> { + ) -> Result<(LoginFirstMessage, Self), ProtocolError> { let OprfClientBytes { alpha, blinding_factor, } = oprf::generate_oprf1::(&password, pepper, rng)?; let (ke1_state, ke1_message) = - generate_ke1::<_, CS::KeyFormat>(alpha.to_arr().to_vec(), rng)?; + CS::KeyExchange::generate_ke1::<_, CS::KeyFormat>(alpha.to_arr().to_vec(), rng)?; let l1 = LoginFirstMessage { alpha, ke1_message }; @@ -699,6 +721,7 @@ impl ClientLogin { /// impl CipherSuite for Default { /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; + /// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; @@ -715,10 +738,10 @@ impl ClientLogin { /// ``` pub fn finish( self, - l2: LoginSecondMessage, - server_s_pk: &::Repr, + l2: LoginSecondMessage, + server_s_pk: &<::KeyFormat as KeyPair>::Repr, _client_e_sk_rng: &mut R, - ) -> Result { + ) -> Result, ProtocolError> { let l2_bytes: Vec = [&l2.beta.to_arr()[..], &l2.envelope.to_bytes()].concat(); let password_derived_key = get_password_derived_key::( @@ -735,7 +758,7 @@ impl ClientLogin { err => PakeError::from(err), })?; - let (ke3_state, ke3_message) = generate_ke3::( + let (shared_secret, ke3_message) = CS::KeyExchange::generate_ke3::( l2_bytes, l2.ke2_message, &self.ke1_state, @@ -745,32 +768,38 @@ impl ClientLogin { Ok(( LoginThirdMessage { ke3_message }, - ke3_state.shared_secret, + shared_secret, *export_key, )) } } /// The state elements the server holds to record a login -pub struct ServerLogin { - ke2_state: KE2State, +pub struct ServerLogin { + ke2_state: ::KE2State, + _cs: PhantomData, } -impl TryFrom<&[u8]> for ServerLogin { +impl TryFrom<&[u8]> for ServerLogin { type Error = ProtocolError; fn try_from(bytes: &[u8]) -> Result { Ok(Self { - ke2_state: KE2State::try_from(&bytes[..])?, + _cs: PhantomData, + ke2_state: ::KE2State::try_from(bytes.to_vec())?, }) } } type ServerLoginStartResult = ( - LoginSecondMessage<::Group, ::KeyFormat>, - ServerLogin, + LoginSecondMessage< + ::Group, + ::KeyFormat, + ::KeyExchange, + >, + ServerLogin, ); -impl ServerLogin { +impl ServerLogin { /// byte representation for the server's login state pub fn to_bytes(&self) -> Vec { self.ke2_state.to_bytes() @@ -795,6 +824,7 @@ impl ServerLogin { /// impl CipherSuite for Default { /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; + /// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; @@ -809,10 +839,10 @@ impl ServerLogin { /// let (login_m2, server_login_state) = ServerLogin::start(p_file, &server_kp.private(), login_m1, &mut server_rng)?; /// # Ok::<(), ProtocolError>(()) /// ``` - pub fn start( + pub fn start( password_file: ServerRegistration, server_s_sk: &Key, - l1: LoginFirstMessage, + l1: LoginFirstMessage, rng: &mut R, ) -> Result, ProtocolError> { let l1_bytes = &l1.to_bytes(); @@ -825,24 +855,30 @@ impl ServerLogin { let l2_component: Vec = [&beta.to_arr()[..], &envelope.to_bytes()].concat(); - let (ke2_state, ke2_message) = generate_ke2::<_, CS::KeyFormat>( + let (ke2_state, ke2_message) = CS::KeyExchange::generate_ke2::<_, CS::KeyFormat>( rng, l1_bytes.to_vec(), l2_component, - l1.ke1_message.client_e_pk, + l1.ke1_message, client_s_pk, server_s_sk.clone(), - l1.ke1_message.client_nonce.to_vec(), )?; let l2 = LoginSecondMessage { _key_format: PhantomData, + _key_exchange: PhantomData, beta, envelope, ke2_message, }; - Ok((l2, Self { ke2_state })) + Ok(( + l2, + Self { + _cs: PhantomData, + ke2_state, + }, + )) } /// From the client's second & final message, check the client's @@ -864,6 +900,7 @@ impl ServerLogin { /// impl CipherSuite for Default { /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; + /// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; @@ -880,13 +917,15 @@ impl ServerLogin { /// let mut server_transport = server_login_state.finish(login_m3)?; /// # Ok::<(), ProtocolError>(()) /// ``` - pub fn finish(&self, message: LoginThirdMessage) -> Result, ProtocolError> { - finish_ke(message.ke3_message, &self.ke2_state).map_err(|e| match e { - ProtocolError::VerificationError(PakeError::KeyExchangeMacValidationError) => { - ProtocolError::VerificationError(PakeError::InvalidLoginError) - } - err => err, - }) + pub fn finish(&self, message: LoginThirdMessage) -> Result, ProtocolError> { + ::finish_ke(message.ke3_message, &self.ke2_state).map_err( + |e| match e { + ProtocolError::VerificationError(PakeError::KeyExchangeMacValidationError) => { + ProtocolError::VerificationError(PakeError::InvalidLoginError) + } + err => err, + }, + ) } } diff --git a/src/tests/opaque_ke_test.rs b/src/tests/opaque_ke_test.rs index fc13a3f..bd7726c 100644 --- a/src/tests/opaque_ke_test.rs +++ b/src/tests/opaque_ke_test.rs @@ -7,7 +7,7 @@ use crate::{ ciphersuite::CipherSuite, errors::*, group::Group, - key_exchange::NONCE_LEN, + key_exchange::tripledh::{TripleDH, NONCE_LEN}, keypair::{Key, KeyPair, X25519KeyPair}, opaque::*, slow_hash::NoOpHash, @@ -25,6 +25,7 @@ struct X255193dhNoSlowHash; impl CipherSuite for X255193dhNoSlowHash { type Group = EdwardsPoint; type KeyFormat = X25519KeyPair; + type KeyExchange = TripleDH; type SlowHash = NoOpHash; } @@ -282,7 +283,7 @@ fn generate_parameters() -> TestVectorParameters { let client_login_state = client_login.to_bytes().to_vec(); let mut server_e_sk_rng = CycleRng::new(server_e_kp.private().to_vec()); - let (l2, server_login) = ServerLogin::start( + let (l2, server_login) = ServerLogin::::start( password_file, server_s_kp.private(), l1, @@ -448,10 +449,10 @@ fn test_l2() -> Result<(), PakeError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let mut server_e_sk_rng = CycleRng::new(parameters.server_e_sk); - let (l2, server_login) = ServerLogin::start::( + let (l2, server_login) = ServerLogin::::start( ServerRegistration::try_from(¶meters.password_file[..]).unwrap(), &Key::try_from(¶meters.server_s_sk[..]).unwrap(), - LoginFirstMessage::::try_from(¶meters.l1[..]).unwrap(), + LoginFirstMessage::::try_from(¶meters.l1[..]).unwrap(), &mut server_e_sk_rng, ) .unwrap(); @@ -473,8 +474,10 @@ fn test_l3() -> Result<(), PakeError> { ClientLogin::::try_from(¶meters.client_login_state[..]) .unwrap() .finish( - LoginSecondMessage::::try_from(¶meters.l2[..]) - .unwrap(), + LoginSecondMessage::::try_from( + ¶meters.l2[..], + ) + .unwrap(), &Key::try_from(¶meters.server_s_pk[..])?, &mut client_e_sk_rng, ) @@ -497,10 +500,11 @@ fn test_l3() -> Result<(), PakeError> { fn test_server_login_finish() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); - let shared_secret = ServerLogin::try_from(¶meters.server_login_state[..]) - .unwrap() - .finish(LoginThirdMessage::try_from(¶meters.l3[..])?) - .unwrap(); + let shared_secret = + ServerLogin::::try_from(¶meters.server_login_state[..]) + .unwrap() + .finish(LoginThirdMessage::try_from(¶meters.l3[..])?) + .unwrap(); assert_eq!( hex::encode(parameters.shared_secret), @@ -529,8 +533,12 @@ fn test_complete_flow( let p_file = server_state.finish(register_m3)?; let (login_m1, client_login_state) = ClientLogin::::start(login_password, None, &mut client_rng)?; - let (login_m2, server_login_state) = - ServerLogin::start(p_file, &server_kp.private(), login_m1, &mut server_rng)?; + let (login_m2, server_login_state) = ServerLogin::::start( + p_file, + &server_kp.private(), + login_m1, + &mut server_rng, + )?; let client_login_result = client_login_state.finish(login_m2, &server_kp.public(), &mut client_rng); diff --git a/src/tests/serialization.rs b/src/tests/serialization.rs index 0a755fe..3034337 100644 --- a/src/tests/serialization.rs +++ b/src/tests/serialization.rs @@ -7,7 +7,10 @@ use crate::{ ciphersuite::CipherSuite, envelope::Envelope, group::Group, - key_exchange::{KE1Message, NONCE_LEN}, + key_exchange::{ + traits::{KeyExchange, ToBytes}, + tripledh::{TripleDH, NONCE_LEN}, + }, keypair::{KeyPair, SizedBytes, X25519KeyPair}, opaque::*, }; @@ -23,6 +26,7 @@ struct Default; impl CipherSuite for Default { type Group = RistrettoPoint; type KeyFormat = crate::keypair::X25519KeyPair; + type KeyExchange = TripleDH; type SlowHash = crate::slow_hash::NoOpHash; } @@ -160,7 +164,7 @@ fn login_first_message_roundtrip() { rng.fill_bytes(&mut client_nonce); let ke1m: Vec = [&client_nonce[..], &client_e_kp.public()].concat(); - let reg = KE1Message::try_from(&ke1m[..]).unwrap(); + let reg = ::KE1Message::try_from(ke1m[..].to_vec()).unwrap(); let reg_bytes = reg.to_bytes(); assert_eq!(reg_bytes, ke1m); }