diff --git a/src/group.rs b/src/group.rs index 88fba28..551403c 100644 --- a/src/group.rs +++ b/src/group.rs @@ -86,8 +86,7 @@ impl Group for RistrettoPoint { element_bits: &GenericArray, ) -> Result { CompressedRistretto::from_slice(element_bits) - .decompress() - .ok_or_else(|| InternalPakeError::PointError) + .decompress().ok_or(InternalPakeError::PointError) } // serialization of a group element fn to_arr(&self) -> GenericArray { @@ -131,8 +130,7 @@ impl Group for EdwardsPoint { element_bits: &GenericArray, ) -> Result { let point = CompressedEdwardsY::from_slice(element_bits) - .decompress() - .ok_or_else(|| InternalPakeError::PointError)?; + .decompress().ok_or(InternalPakeError::PointError)?; if point.is_small_order() { return Err(InternalPakeError::SubGroupError); diff --git a/src/hash.rs b/src/hash.rs index dfe6f9e..03a591e 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -4,19 +4,10 @@ // LICENSE file in the root directory of this source tree. use digest::{BlockInput, FixedOutput, Reset, Update}; -use generic_array::ArrayLength; /// Trait inheriting the requirements from digest::Digest for compatibility with HKDF and HMAC // Associated types could be simplified when they are made as defaults: // https://github.com/rust-lang/rust/issues/29661 -pub trait Hash: Update + BlockInput + FixedOutput + Reset + Default + Clone { - /// The block size for the hash function - type BlockSize: ArrayLength; - /// The output size of the hash function - type OutputSize: ArrayLength; -} +pub trait Hash: Update + BlockInput + FixedOutput + Reset + Default + Clone {} -impl Hash for T { - type BlockSize = T::BlockSize; - type OutputSize = T::OutputSize; -} +impl Hash for T {} diff --git a/src/key_exchange/tripledh.rs b/src/key_exchange/tripledh.rs index 138c69f..af9e61a 100644 --- a/src/key_exchange/tripledh.rs +++ b/src/key_exchange/tripledh.rs @@ -9,13 +9,9 @@ use crate::{ hash::Hash, key_exchange::traits::{KeyExchange, ToBytes}, keypair::{Key, KeyPair, SizedBytes}, - sized_bytes_using_constant_and_try_from, -}; -use digest::Digest; -use generic_array::{ - typenum::{U64, U96}, - GenericArray, }; +use digest::{Digest, FixedOutput}; +use generic_array::{typenum::U32, ArrayLength, GenericArray}; use hkdf::Hkdf; use hmac::{Hmac, Mac, NewMac}; use rand_core::{CryptoRng, RngCore}; @@ -24,6 +20,7 @@ use std::convert::TryFrom; const KEY_LEN: usize = 32; pub(crate) const NONCE_LEN: usize = 32; +pub(crate) type NonceLen = U32; const KE1_STATE_LEN: usize = KEY_LEN + KEY_LEN + NONCE_LEN; const KE2_MESSAGE_LEN: usize = NONCE_LEN + 2 * KEY_LEN; @@ -33,22 +30,25 @@ static STR_3DH: &[u8] = b"3DH keys"; pub struct TripleDH; impl KeyExchange for TripleDH { - type KE1State = KE1State; - type KE2State = KE2State; + type KE1State = KE1State<::OutputSize>; + type KE2State = KE2State<::OutputSize>; type KE1Message = KE1Message; - type KE2Message = KE2Message; - type KE3Message = KE3Message; + type KE2Message = KE2Message<::OutputSize>; + type KE3Message = KE3Message<::OutputSize>; 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 client_nonce: GenericArray = { + let mut client_nonce_bytes = [0u8; NONCE_LEN]; + rng.fill_bytes(&mut client_nonce_bytes); + client_nonce_bytes.into() + }; let ke1_message = KE1Message { - client_nonce: client_nonce.to_vec(), + client_nonce, client_e_pk: client_e_kp.public().clone(), }; @@ -60,8 +60,8 @@ impl KeyExchange for TripleDH { Ok(( KE1State { client_e_sk: client_e_kp.private().clone(), - client_nonce: client_nonce.to_vec(), - hashed_l1: hashed_l1.to_vec(), + client_nonce, + hashed_l1, }, ke1_message, )) @@ -76,8 +76,11 @@ impl KeyExchange for TripleDH { 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 server_nonce: GenericArray = { + let mut server_nonce_bytes = [0u8; NONCE_LEN]; + rng.fill_bytes(&mut server_nonce_bytes); + server_nonce_bytes.into() + }; let (shared_secret, km2, km3) = derive_3dh_keys::( TripleDHComponents { @@ -115,14 +118,14 @@ impl KeyExchange for TripleDH { Ok(( KE2State { - km3: km3.to_vec(), - hashed_transcript: hashed_transcript.to_vec(), - shared_secret: shared_secret.to_vec(), + km3, + hashed_transcript, + shared_secret, }, KE2Message { - server_nonce: server_nonce.to_vec(), + server_nonce, server_e_pk: server_e_kp.public().clone(), - mac: mac.finalize().into_bytes().to_vec(), + mac: mac.finalize().into_bytes(), }, )) } @@ -165,7 +168,7 @@ impl KeyExchange for TripleDH { Hmac::::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?; server_mac.update(&hashed_transcript); - if ke2_message.mac != server_mac.finalize().into_bytes().to_vec() { + if ke2_message.mac != server_mac.finalize().into_bytes() { return Err(ProtocolError::VerificationError( PakeError::KeyExchangeMacValidationError, )); @@ -178,7 +181,7 @@ impl KeyExchange for TripleDH { Ok(( shared_secret.to_vec(), KE3Message { - mac: client_mac.finalize().into_bytes().to_vec(), + mac: client_mac.finalize().into_bytes(), }, )) } @@ -191,7 +194,7 @@ impl KeyExchange for TripleDH { 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() { + if ke3_message.mac != client_mac.finalize().into_bytes() { return Err(ProtocolError::VerificationError( PakeError::KeyExchangeMacValidationError, )); @@ -211,34 +214,40 @@ impl KeyExchange for TripleDH { /// The client state produced after the first key exchange message #[derive(PartialEq, Eq)] -pub struct KE1State { +pub struct KE1State> { client_e_sk: Key, - client_nonce: Vec, - hashed_l1: Vec, + client_nonce: GenericArray, + hashed_l1: GenericArray, } /// The first key exchange message #[derive(PartialEq, Eq)] pub struct KE1Message { - pub(crate) client_nonce: Vec, + pub(crate) client_nonce: GenericArray, pub(crate) client_e_pk: Key, } -impl TryFrom> for KE1State { +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")?; + let checked_bytes = check_slice_size( + &bytes, + KEY_LEN + NONCE_LEN + HashLen::to_usize(), + "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(), + client_nonce: GenericArray::clone_from_slice( + &checked_bytes[KEY_LEN..KEY_LEN + NONCE_LEN], + ), + hashed_l1: GenericArray::clone_from_slice(&checked_bytes[KEY_LEN + NONCE_LEN..]), }) } } -impl ToBytes for KE1State { +impl> ToBytes for KE1State { fn to_bytes(&self) -> Vec { let output: Vec = [ &self.client_e_sk.to_arr(), @@ -250,8 +259,6 @@ impl ToBytes for KE1State { } } -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() @@ -266,29 +273,27 @@ impl TryFrom> for KE1Message { check_slice_size(&ke1_message_bytes, NONCE_LEN + KEY_LEN, "ke1_message")?; Ok(Self { - client_nonce: checked_bytes[..NONCE_LEN].to_vec(), + client_nonce: GenericArray::clone_from_slice(&checked_bytes[..NONCE_LEN]), 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, +pub struct KE2State> { + km3: GenericArray, + hashed_transcript: GenericArray, + shared_secret: GenericArray, } /// The second key exchange message -pub struct KE2Message { - server_nonce: Vec, +pub struct KE2Message> { + server_nonce: GenericArray, server_e_pk: Key, - mac: Vec, + mac: GenericArray, } -impl ToBytes for KE2State { +impl> ToBytes for KE2State { fn to_bytes(&self) -> Vec { let output: Vec = [ &self.km3[..], @@ -300,21 +305,21 @@ impl ToBytes for KE2State { } } -impl TryFrom> for KE2State { +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(), + km3: GenericArray::clone_from_slice(&checked_bytes[..KEY_LEN]), + hashed_transcript: GenericArray::clone_from_slice(&checked_bytes[KEY_LEN..2 * KEY_LEN]), + shared_secret: GenericArray::clone_from_slice(&checked_bytes[2 * KEY_LEN..]), }) } } -impl ToBytes for KE2Message { +impl> ToBytes for KE2Message { fn to_bytes(&self) -> Vec { let output: Vec = [ &self.server_nonce[..], @@ -326,16 +331,16 @@ impl ToBytes for KE2Message { } } -impl TryFrom> for KE2Message { +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_nonce: GenericArray::clone_from_slice(&checked_bytes[..NONCE_LEN]), server_e_pk: Key::from_bytes(&checked_bytes[NONCE_LEN..NONCE_LEN + KEY_LEN])?, - mac: checked_bytes[NONCE_LEN + KEY_LEN..].to_vec(), + mac: GenericArray::clone_from_slice(&checked_bytes[NONCE_LEN + KEY_LEN..]), }) } } @@ -352,17 +357,17 @@ struct TripleDHComponents { // Consists of a shared secret, followed by two mac keys type TripleDHDerivationResult = ( - GenericArray::OutputSize>, - GenericArray::OutputSize>, - GenericArray::OutputSize>, + 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, D: Hash>( dh: TripleDHComponents, - client_nonce: &[u8], - server_nonce: &[u8], + client_nonce: &GenericArray, + server_nonce: &GenericArray, client_s_pk: KeyFormat::Repr, server_s_pk: KeyFormat::Repr, ) -> Result, ProtocolError> { @@ -395,24 +400,24 @@ fn derive_3dh_keys, D: Hash>( } /// The third key exchange message -pub struct KE3Message { - mac: Vec, +pub struct KE3Message> { + mac: GenericArray, } -impl ToBytes for KE3Message { +impl> ToBytes for KE3Message { fn to_bytes(&self) -> Vec { - self.mac.clone() + self.mac.to_vec() } } -impl TryFrom> for KE3Message { +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(), + mac: GenericArray::clone_from_slice(&checked_bytes), }) } } diff --git a/src/keypair.rs b/src/keypair.rs index d68db58..a425808 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -91,35 +91,6 @@ trait KeyPairExt: KeyPair + Debug { #[cfg(test)] impl KeyPairExt for KP where KP: KeyPair + Debug {} -/// This assumes you have defined: -/// - an `impl TryFrom<&[u8b], Error = InternalPakeError>` for a non-generic `T` -/// - an `fn to_bytes(&self) -> Vec` in an `impl T` block -/// and it both of the above to produce a sensible SizedBytes implementation -/// -/// Because SizedBytes has a strong notion of size, and TryFrom/to_bytes does -/// not, it's better to use the macro below rather than this one, where possible. -#[macro_export] -macro_rules! sized_bytes_using_constant_and_try_from { - ($sized_type: ident, $len: ident) => { - impl SizedBytes for $sized_type { - type Len = $len; - - fn to_arr(&self) -> generic_array::GenericArray { - generic_array::GenericArray::clone_from_slice(&self.to_bytes()) - } - - fn from_bytes(bytes: &[u8]) -> Result { - let checked_bytes = check_slice_size( - bytes, - ::to_usize(), - "bytes", - )?; - std::convert::TryFrom::try_from(checked_bytes.to_vec()) - } - } - }; -} - /// This assumes you have defined a SizedBytes instance for a `T`, and defines: /// - an `impl TryFrom<&[u8b], Error = InternalPakeError>` for a non-generic `T` /// - an `fn to_bytes(&self) -> Vec` in an `impl T` block diff --git a/src/tests/opaque_ke_test.rs b/src/tests/opaque_ke_test.rs index dc984a8..a79d23b 100644 --- a/src/tests/opaque_ke_test.rs +++ b/src/tests/opaque_ke_test.rs @@ -558,10 +558,7 @@ fn test_complete_flow( hex::encode(login_export_key) ); } else { - let res = match client_login_result { - Err(ProtocolError::VerificationError(PakeError::InvalidLoginError)) => true, - _ => false, - }; + let res = matches!(client_login_result, Err(ProtocolError::VerificationError(PakeError::InvalidLoginError))); assert!(res); }