diff --git a/Cargo.toml b/Cargo.toml index 24e7d0c..41178c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ std = ["curve25519-dalek/std", "getrandom", "rand/std", "rand/std_rng", "num-big serialize = ["serde", "base64", "generic-array/serde", "curve25519-dalek/serde"] [dependencies] -argon2 = { version = "0.2", default-features = false, optional = true } +argon2 = { version = "0.3", default-features = false, features = ["alloc"], optional = true } base64 = { version = "0.13", default-features = false, features = ["alloc"], optional = true } constant_time_eq = "0.1" curve25519-dalek = { version = "3", default-features = false } diff --git a/src/group/x25519.rs b/src/group/x25519.rs index a48c740..228c223 100644 --- a/src/group/x25519.rs +++ b/src/group/x25519.rs @@ -127,7 +127,7 @@ fn test() -> Result<(), ProtocolError> { } = client.finish( &mut OsRng, message, - ClientRegistrationFinishParameters::Default, + ClientRegistrationFinishParameters::default(), )?; let server_registration = ServerRegistration::finish(message); @@ -152,7 +152,7 @@ fn test() -> Result<(), ProtocolError> { session_key: client_session_key, export_key: login_export_key, .. - } = client.finish(message, ClientLoginFinishParameters::Default)?; + } = client.finish(message, ClientLoginFinishParameters::default())?; let server_session_key = server.finish(message)?.session_key; assert_eq!(register_export_key, login_export_key); @@ -172,7 +172,7 @@ fn test() -> Result<(), ProtocolError> { )?; assert!(matches!( - client.finish(message, ClientLoginFinishParameters::Default), + client.finish(message, ClientLoginFinishParameters::default()), Err(ProtocolError::InvalidLoginError) )); diff --git a/src/keypair.rs b/src/keypair.rs index 4d6f2a2..caf4b72 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -507,7 +507,7 @@ mod tests { .finish( &mut OsRng, message, - ClientRegistrationFinishParameters::Default, + ClientRegistrationFinishParameters::default(), ) .unwrap(); let file = ServerRegistration::finish(message); @@ -530,7 +530,7 @@ mod tests { ) .unwrap(); let ClientLoginFinishResult { message, .. } = client - .finish(message, ClientLoginFinishParameters::Default) + .finish(message, ClientLoginFinishParameters::default()) .unwrap(); server.finish(message).unwrap(); } diff --git a/src/lib.rs b/src/lib.rs index d6e5d6a..65f4ceb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -578,7 +578,7 @@ //! //! But, for applications that wish to cryptographically bind these identities to //! the registered password file as well as the session key output by the login phase, these custom identifiers can be specified through -//! [ClientRegistrationFinishParameters::WithIdentifiers] in [Client Registration Finish](#client-registration-finish): +//! [ClientRegistrationFinishParameters] in [Client Registration Finish](#client-registration-finish): //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -606,11 +606,12 @@ //! let client_registration_finish_result = client_registration_start_result.state.finish( //! &mut client_rng, //! server_registration_start_result.message, -//! ClientRegistrationFinishParameters::WithIdentifiers( -//! Identifiers::ClientAndServerIdentifiers( +//! ClientRegistrationFinishParameters::new( +//! Some(Identifiers::ClientAndServerIdentifiers( //! b"Alice_the_Cryptographer".to_vec(), //! b"Facebook".to_vec(), -//! ), +//! )), +//! None, //! ), //! )?; //! # Ok::<(), ProtocolError>(()) @@ -641,7 +642,7 @@ //! # let mut server_rng = OsRng; //! # let server_setup = ServerSetup::::new(&mut server_rng); //! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; -//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::WithIdentifiers(Identifiers::ClientAndServerIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec())))?; +//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::new(Some(Identifiers::ClientAndServerIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec())), None))?; //! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize(); //! # let client_login_start_result = ClientLogin::::start( //! # &mut client_rng, @@ -666,7 +667,7 @@ //! # Ok::<(), ProtocolError>(()) //! ``` //! -//! as well as [ClientLoginFinishParameters::WithIdentifiers] in [Client Login Finish](#client-login-finish): +//! as well as [ClientLoginFinishParameters] in [Client Login Finish](#client-login-finish): //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -691,7 +692,7 @@ //! # let mut server_rng = OsRng; //! # let server_setup = ServerSetup::::new(&mut server_rng); //! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; -//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::WithIdentifiers(Identifiers::ClientAndServerIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec())))?; +//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::new(Some(Identifiers::ClientAndServerIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec())), None))?; //! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize(); //! # let client_login_start_result = ClientLogin::::start( //! # &mut client_rng, @@ -705,11 +706,13 @@ //! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"alice@example.com", ServerLoginStartParameters::WithIdentifiers(Identifiers::ClientAndServerIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec())))?; //! let client_login_finish_result = client_login_start_result.state.finish( //! server_login_start_result.message, -//! ClientLoginFinishParameters::WithIdentifiers( -//! Identifiers::ClientAndServerIdentifiers( +//! ClientLoginFinishParameters::new( +//! None, +//! Some(Identifiers::ClientAndServerIdentifiers( //! b"Alice_the_Cryptographer".to_vec(), //! b"Facebook".to_vec(), -//! ), +//! )), +//! None, //! ), //! )?; //! @@ -727,7 +730,7 @@ //! so as to bind the integrity of application-specific data or configuration parameters to the security of the key exchange. //! During the login phase, the client and server can specify this context using: //! - The second login message, where the server can populate [ServerLoginStartParameters::WithContext], and -//! - The third login message, where the client can populate [ClientLoginFinishParameters::WithContext]. +//! - The third login message, where the client can populate [ClientLoginFinishParameters]. //! //! For both of these messages, the `WithContextAndIdentifiers` variant can be used to specify these fields in addition to //! [custom identifiers](#custom-identifiers), with the ordering of the fields as @@ -780,7 +783,8 @@ //! fn public_key( //! &self //! ) -> Result, InternalError> { -//! YourRemoteKey::public_key(self).map(PublicKey::from_arr).map_err(InternalError::Custom) +//! YourRemoteKey::public_key(self).map(PublicKey::from_arr) +//! .map_err(InternalError::Custom) //! } //! //! fn serialize(&self) -> Vec { diff --git a/src/opaque.rs b/src/opaque.rs index b784bbe..1591f6c 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -222,16 +222,29 @@ pub(crate) fn bytestrings_from_identifiers( /// Optional parameters for client registration finish #[derive(Clone)] -pub enum ClientRegistrationFinishParameters { +pub struct ClientRegistrationFinishParameters<'h, CS: CipherSuite> { /// Specifying the identifiers idU and idS - WithIdentifiers(Identifiers), - /// No identifiers or private key specified - Default, + pub identifiers: Option, + /// Specifying a configuration for the slow hash + pub slow_hash: Option<&'h CS::SlowHash>, } -impl Default for ClientRegistrationFinishParameters { +impl<'h, CS: CipherSuite> Default for ClientRegistrationFinishParameters<'h, CS> { fn default() -> Self { - Self::Default + Self { + identifiers: None, + slow_hash: None, + } + } +} + +impl<'h, CS: CipherSuite> ClientRegistrationFinishParameters<'h, CS> { + /// Create a new [`ClientRegistrationFinishParameters`] + pub fn new(identifiers: Option, slow_hash: Option<&'h CS::SlowHash>) -> Self { + Self { + identifiers, + slow_hash, + } } } @@ -312,22 +325,15 @@ impl ClientRegistration { self, rng: &mut R, r2: RegistrationResponse, - params: ClientRegistrationFinishParameters, + params: ClientRegistrationFinishParameters, ) -> Result, ProtocolError> { - let optional_ids = match params { - ClientRegistrationFinishParameters::WithIdentifiers(ids) => Some(ids), - ClientRegistrationFinishParameters::Default => None, - }; - // Check for reflected value from server and halt if detected if self.alpha.ct_equal(&r2.beta) { return Err(ProtocolError::ReflectedValueError); } - let password_derived_key = get_password_derived_key::( - &self.token, - r2.beta, - )?; + let password_derived_key = + get_password_derived_key::(&self.token, r2.beta, params.slow_hash)?; #[cfg_attr(not(test), allow(unused_variables))] let (randomized_pwd, h) = Hkdf::::extract(None, &password_derived_key); @@ -335,8 +341,12 @@ impl ClientRegistration { h.expand(STR_MASKING_KEY, &mut masking_key) .map_err(|_| InternalError::HkdfError)?; - let result = - Envelope::::seal(rng, &password_derived_key, &r2.server_s_pk, optional_ids)?; + let result = Envelope::::seal( + rng, + &password_derived_key, + &r2.server_s_pk, + params.identifiers, + )?; Ok(ClientRegistrationFinishResult { message: RegistrationUpload { @@ -553,21 +563,37 @@ impl Clone for ClientLoginStartResult { /// Optional parameters for client login finish #[derive(Clone)] -pub enum ClientLoginFinishParameters { +pub struct ClientLoginFinishParameters<'h, CS: CipherSuite> { /// Specifying a context field that the server must agree on - WithContext(Vec), + pub context: Option>, /// Specifying a user identifier and server identifier that will be matched against the server - WithIdentifiers(Identifiers), - /// Specifying a context field that the server must agree on, - /// along with a user identifier and server identifier and context that will be matched against the server - WithContextAndIdentifiers(Vec, Identifiers), - /// No custom identifiers and no context - Default, + pub identifiers: Option, + /// Specifying a configuration for the slow hash + pub slow_hash: Option<&'h CS::SlowHash>, } -impl Default for ClientLoginFinishParameters { +impl<'h, CS: CipherSuite> Default for ClientLoginFinishParameters<'h, CS> { fn default() -> Self { - Self::Default + Self { + context: None, + identifiers: None, + slow_hash: None, + } + } +} + +impl<'h, CS: CipherSuite> ClientLoginFinishParameters<'h, CS> { + /// Create a new [`ClientLoginFinishParameters`] + pub fn new( + context: Option>, + identifiers: Option, + slow_hash: Option<&'h CS::SlowHash>, + ) -> Self { + Self { + context, + identifiers, + slow_hash, + } } } @@ -638,18 +664,8 @@ impl ClientLogin { pub fn finish( self, credential_response: CredentialResponse, - params: ClientLoginFinishParameters, + params: ClientLoginFinishParameters, ) -> Result, ProtocolError> { - let (context, optional_ids) = match params { - ClientLoginFinishParameters::Default => (vec![], None), - ClientLoginFinishParameters::WithContext(context) => (context, None), - ClientLoginFinishParameters::WithIdentifiers(ids) => (vec![], Some(ids)), - // add context - ClientLoginFinishParameters::WithContextAndIdentifiers(context, ids) => { - (context, Some(ids)) - } - }; - // Check if beta value from server is equal to alpha value from client let credential_request = CredentialRequest::::deserialize(&self.serialized_credential_request[..])?; @@ -657,9 +673,10 @@ impl ClientLogin { return Err(ProtocolError::ReflectedValueError); } - let password_derived_key = get_password_derived_key::( + let password_derived_key = get_password_derived_key::( &self.token, credential_response.beta, + params.slow_hash, )?; let h = Hkdf::::new(None, &password_derived_key); @@ -679,7 +696,11 @@ impl ClientLogin { let server_s_pk_bytes = server_s_pk.to_arr().to_vec(); let opened_envelope = &envelope - .open(&password_derived_key, &server_s_pk_bytes, &optional_ids) + .open( + &password_derived_key, + &server_s_pk_bytes, + ¶ms.identifiers, + ) .map_err(|e| match e { ProtocolError::LibraryError(InternalError::SealOpenHmacError) => { ProtocolError::InvalidLoginError @@ -702,7 +723,7 @@ impl ClientLogin { opened_envelope.client_static_keypair.private().clone(), opened_envelope.id_u.clone(), opened_envelope.id_s.clone(), - context, + params.context.unwrap_or_default(), )?; Ok(ClientLoginFinishResult { @@ -1007,12 +1028,19 @@ impl Drop for ServerLogin { // Helper functions -fn get_password_derived_key, D: Hash>( - token: &oprf::Token, - beta: G, +fn get_password_derived_key( + token: &oprf::Token, + beta: CS::OprfGroup, + slow_hash: Option<&CS::SlowHash>, ) -> Result, ProtocolError> { - let oprf_output = oprf::finalize::(&token.data, &token.blind, beta)?; - SH::hash(oprf_output).map_err(ProtocolError::from) + let oprf_output = oprf::finalize::(&token.data, &token.blind, beta)?; + + if let Some(slow_hash) = slow_hash { + slow_hash.hash(oprf_output) + } else { + CS::SlowHash::default().hash(oprf_output) + } + .map_err(ProtocolError::from) } fn oprf_key_from_seed( diff --git a/src/slow_hash.rs b/src/slow_hash.rs index 19ce0e9..281dce7 100644 --- a/src/slow_hash.rs +++ b/src/slow_hash.rs @@ -13,33 +13,35 @@ use generic_array::typenum::Unsigned; use generic_array::GenericArray; /// Used for the slow hashing function in OPAQUE -pub trait SlowHash { +pub trait SlowHash: Default { /// Computes the slow hashing function - fn hash(input: GenericArray::OutputSize>) -> Result, InternalError>; + fn hash( + &self, + input: GenericArray::OutputSize>, + ) -> Result, InternalError>; } /// A no-op hash which simply returns its input +#[derive(Default)] pub struct NoOpHash; impl SlowHash for NoOpHash { - fn hash(input: GenericArray::OutputSize>) -> Result, InternalError> { + fn hash( + &self, + input: GenericArray::OutputSize>, + ) -> Result, InternalError> { Ok(input.to_vec()) } } #[cfg(feature = "slow-hash")] impl SlowHash for argon2::Argon2<'_> { - fn hash(input: GenericArray::OutputSize>) -> Result, InternalError> { - let params = argon2::Argon2::default(); + fn hash( + &self, + input: GenericArray::OutputSize>, + ) -> Result, InternalError> { let mut output = alloc::vec![0u8; ::OutputSize::USIZE]; - params - .hash_password_into( - argon2::Algorithm::Argon2id, - &input, - &[0; argon2::MIN_SALT_LENGTH], - &[], - &mut output, - ) + self.hash_password_into(&input, &[0; argon2::MIN_SALT_LEN], &mut output) .map_err(|_| InternalError::SlowHashError)?; Ok(output) } diff --git a/src/tests/full_test.rs b/src/tests/full_test.rs index 28c58cc..a42c8bc 100644 --- a/src/tests/full_test.rs +++ b/src/tests/full_test.rs @@ -347,8 +347,12 @@ fn generate_parameters() -> TestVectorParameters { .finish( &mut finish_registration_rng, server_registration_start_result.message, - ClientRegistrationFinishParameters::WithIdentifiers( - Identifiers::ClientAndServerIdentifiers(id_u.to_vec(), id_s.to_vec()), + ClientRegistrationFinishParameters::new( + Some(Identifiers::ClientAndServerIdentifiers( + id_u.to_vec(), + id_s.to_vec(), + )), + None, ), ) .unwrap(); @@ -402,9 +406,13 @@ fn generate_parameters() -> TestVectorParameters { .state .finish( server_login_start_result.message, - ClientLoginFinishParameters::WithContextAndIdentifiers( - context.to_vec(), - Identifiers::ClientAndServerIdentifiers(id_u.to_vec(), id_s.to_vec()), + ClientLoginFinishParameters::new( + Some(context.to_vec()), + Some(Identifiers::ClientAndServerIdentifiers( + id_u.to_vec(), + id_s.to_vec(), + )), + None, ), ) .unwrap(); @@ -544,8 +552,12 @@ fn test_registration_upload() -> Result<(), ProtocolError> { .finish( &mut finish_registration_rng, RegistrationResponse::deserialize(¶meters.registration_response[..])?, - ClientRegistrationFinishParameters::WithIdentifiers( - Identifiers::ClientAndServerIdentifiers(parameters.id_u, parameters.id_s), + ClientRegistrationFinishParameters::new( + Some(Identifiers::ClientAndServerIdentifiers( + parameters.id_u, + parameters.id_s, + )), + None, ), )?; @@ -662,9 +674,13 @@ fn test_credential_finalization() -> Result<(), ProtocolError> { CredentialResponse::::deserialize( ¶meters.credential_response[..], )?, - ClientLoginFinishParameters::WithContextAndIdentifiers( - parameters.context, - Identifiers::ClientAndServerIdentifiers(parameters.id_u, parameters.id_s), + ClientLoginFinishParameters::new( + Some(parameters.context), + Some(Identifiers::ClientAndServerIdentifiers( + parameters.id_u, + parameters.id_s, + )), + None, ), )?; diff --git a/src/tests/opaque_test_vectors.rs b/src/tests/opaque_test_vectors.rs index 106eb2b..1971813 100644 --- a/src/tests/opaque_test_vectors.rs +++ b/src/tests/opaque_test_vectors.rs @@ -881,8 +881,8 @@ fn test_registration_upload(tvs: &[&str]) -> Result<(), Protoco &mut finish_registration_rng, RegistrationResponse::deserialize(¶meters.registration_response[..]).unwrap(), match parse_identifiers(parameters.client_identity, parameters.server_identity) { - None => ClientRegistrationFinishParameters::Default, - Some(ids) => ClientRegistrationFinishParameters::WithIdentifiers(ids), + None => ClientRegistrationFinishParameters::default(), + Some(ids) => ClientRegistrationFinishParameters::new(Some(ids), None), }, )?; @@ -998,9 +998,9 @@ fn test_ke3(tvs: &[&str]) -> Result<(), ProtocolError> { let client_login_finish_result = client_login_start_result.state.finish( CredentialResponse::::deserialize(¶meters.KE2[..])?, match parse_identifiers(parameters.client_identity, parameters.server_identity) { - None => ClientLoginFinishParameters::WithContext(parameters.context), + None => ClientLoginFinishParameters::new(Some(parameters.context), None, None), Some(ids) => { - ClientLoginFinishParameters::WithContextAndIdentifiers(parameters.context, ids) + ClientLoginFinishParameters::new(Some(parameters.context), Some(ids), None) } }, )?;