From 9d40aa7659d70f6718b9c034664cfa2940a41611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Fri, 3 Jul 2020 18:28:35 -0400 Subject: [PATCH] As a way to roll back the genericity, implement an extension trait of group for password-to-curve hashing, This supersedes #18. Co-authored-by: KevinLewi --- Cargo.lock | 1 - Cargo.toml | 1 - src/ciphersuite.rs | 16 +++++++--------- src/lib.rs | 14 ++++---------- src/map_to_curve.rs | 23 ++++++++++++++++++++++ src/opaque.rs | 16 ++-------------- src/oprf.rs | 38 +++++++------------------------------ src/tests/opaque_ke_test.rs | 6 +++--- src/tests/serialization.rs | 1 - 9 files changed, 46 insertions(+), 70 deletions(-) create mode 100644 src/map_to_curve.rs diff --git a/Cargo.lock b/Cargo.lock index 9a0872c..b7d61d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -202,7 +202,6 @@ dependencies = [ "aead 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "curve25519-dalek 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "hkdf 0.9.0-alpha.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 6966080..0881562 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ slow-hash = ["scrypt"] [dependencies] aead = "0.3.1" curve25519-dalek = "2.1.0" -digest = "0.9" generic-array = "0.14.2" hkdf = "0.9.0-alpha.0" hmac = "0.8.0" diff --git a/src/ciphersuite.rs b/src/ciphersuite.rs index 10e2938..ce159e2 100644 --- a/src/ciphersuite.rs +++ b/src/ciphersuite.rs @@ -7,24 +7,22 @@ use crate::{ errors::InternalPakeError, - group::Group, keypair::{Key, KeyPair}, - oprf::HkdfDigest, + map_to_curve::GroupWithMapToCurve, slow_hash::SlowHash, }; -use digest::FixedOutput; -use generic_array::typenum::{U32}; + use rand_core::{CryptoRng, RngCore}; /// Configures the underlying primitives used in OPAQUE -/// * `Digest`: a digest suitable for use in an Hkdf, with an output length equal -/// to the input of the hash-to-curve function of the `Group` parameter. -/// * `Group`: a finite cyclic group along with a point representation +/// * `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 /// * `SlowHash`: a slow hashing function, typically used for password hashing pub trait CipherSuite { - type Digest: HkdfDigest; - type Group: Group::OutputSize>; + type Group: GroupWithMapToCurve; type KeyFormat: KeyPair + PartialEq; type SlowHash: SlowHash; diff --git a/src/lib.rs b/src/lib.rs index 897889d..2a824eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,6 @@ //! use opaque_ke::ciphersuite::CipherSuite; //! struct Default; //! impl CipherSuite for Default { -//! type Digest = sha2::Sha512; //! type Group = curve25519_dalek::ristretto::RistrettoPoint; //! type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -42,7 +41,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -74,7 +72,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -103,7 +100,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -135,7 +131,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -169,7 +164,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -211,7 +205,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -240,7 +233,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -284,7 +276,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -340,7 +331,6 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { -//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -390,9 +380,13 @@ pub mod opaque; pub mod ciphersuite; mod envelope; + mod group; +mod map_to_curve; + mod key_exchange; pub mod keypair; + mod oprf; pub mod slow_hash; diff --git a/src/map_to_curve.rs b/src/map_to_curve.rs new file mode 100644 index 0000000..8992d9a --- /dev/null +++ b/src/map_to_curve.rs @@ -0,0 +1,23 @@ +use crate::group::Group; +use curve25519_dalek::{edwards::EdwardsPoint, ristretto::RistrettoPoint}; + +use generic_array::GenericArray; +use hkdf::Hkdf; + +pub trait GroupWithMapToCurve: Group { + fn map_to_curve(password: &[u8], pepper: Option<&[u8]>) -> Self; +} + +impl GroupWithMapToCurve for RistrettoPoint { + fn map_to_curve(password: &[u8], pepper: Option<&[u8]>) -> Self { + let (hashed_input, _) = Hkdf::::extract(pepper, password); + ::hash_to_curve(GenericArray::from_slice(&hashed_input)) + } +} + +impl GroupWithMapToCurve for EdwardsPoint { + fn map_to_curve(password: &[u8], pepper: Option<&[u8]>) -> Self { + let (hashed_input, _) = Hkdf::::extract(pepper, password); + ::hash_to_curve(GenericArray::from_slice(&hashed_input)) + } +} diff --git a/src/opaque.rs b/src/opaque.rs index 82493e9..5b306cc 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -290,7 +290,6 @@ impl ClientRegistration { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -307,11 +306,7 @@ impl ClientRegistration { let OprfClientBytes { alpha, blinding_factor, - } = oprf::generate_oprf1::( - &password, - pepper, - blinding_factor_rng, - )?; + } = oprf::generate_oprf1::(&password, pepper, blinding_factor_rng)?; Ok(( RegisterFirstMessage:: { alpha }, @@ -345,7 +340,6 @@ impl ClientRegistration { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -509,7 +503,6 @@ where /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -556,7 +549,6 @@ where /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -645,7 +637,6 @@ impl ClientLogin { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -662,7 +653,7 @@ impl ClientLogin { let OprfClientBytes { alpha, blinding_factor, - } = oprf::generate_oprf1::(&password, pepper, rng)?; + } = oprf::generate_oprf1::(&password, pepper, rng)?; let (ke1_state, ke1_message) = generate_ke1::<_, CS::KeyFormat>(alpha.to_arr().to_vec(), rng)?; @@ -697,7 +688,6 @@ impl ClientLogin { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -793,7 +783,6 @@ impl ServerLogin { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -863,7 +852,6 @@ impl ServerLogin { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { - /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; diff --git a/src/oprf.rs b/src/oprf.rs index c9542c8..e4f145d 100644 --- a/src/oprf.rs +++ b/src/oprf.rs @@ -3,9 +3,8 @@ // 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, group::Group}; -use digest::{BlockInput, FixedOutput, Reset, Update}; -use generic_array::{typenum::U32, ArrayLength, GenericArray}; +use crate::{errors::InternalPakeError, group::Group, map_to_curve::GroupWithMapToCurve}; +use generic_array::{typenum::U32, GenericArray}; use hkdf::Hkdf; use rand_core::{CryptoRng, RngCore}; @@ -14,41 +13,18 @@ pub(crate) struct OprfClientBytes { pub(crate) blinding_factor: Grp::Scalar, } -/// The `HkDFDigest` trait specifies the interface required for a parameter of `hkdf::Hkdf`. -/// -/// It's a convenience wrapper around [`Blockinput`], [`Update`], [`FixedOutput`], [`Reset`], -/// [`Clone`], and [`Default`] traits. -pub trait HkdfDigest: Update + BlockInput + FixedOutput + Reset + Default + Clone -where - Self::BlockSize: ArrayLength, - Self::OutputSize: ArrayLength, -{ -} - -impl HkdfDigest for T -where - T: Update + BlockInput + FixedOutput + Reset + Default + Clone, - T::BlockSize: ArrayLength, - T::OutputSize: ArrayLength, -{ -} - /// Computes the first step for the multiplicative blinding version of DH-OPRF. This /// message is sent from the client (who holds the input) to the server (who holds the OPRF key). /// The client can also pass in an optional "pepper" string to be mixed in with the input through /// an HKDF computation. -pub(crate) fn generate_oprf1< - R: RngCore + CryptoRng, - D: HkdfDigest, - G: Group, ->( +pub(crate) fn generate_oprf1( input: &[u8], pepper: Option<&[u8]>, blinding_factor_rng: &mut R, ) -> Result, InternalPakeError> { - let (hashed_input, _) = Hkdf::::extract(pepper, &input); + let mapped_point = G::map_to_curve(input, pepper); let blinding_factor = G::random_scalar(blinding_factor_rng); - let alpha = G::hash_to_curve(GenericArray::from_slice(&hashed_input)) * &blinding_factor; + let alpha = mapped_point * &blinding_factor; Ok(OprfClientBytes { alpha, blinding_factor, @@ -112,7 +88,7 @@ mod tests { let OprfClientBytes { alpha, blinding_factor, - } = generate_oprf1::<_, Sha512, RistrettoPoint>(&input[..], None, &mut rng)?; + } = generate_oprf1::<_, RistrettoPoint>(&input[..], None, &mut rng)?; let salt_bytes = arr![ u8; 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, @@ -133,7 +109,7 @@ mod tests { let OprfClientBytes { alpha, blinding_factor, - } = generate_oprf1::<_, Sha512, RistrettoPoint>(&input, None, &mut rng).unwrap(); + } = generate_oprf1::<_, RistrettoPoint>(&input, None, &mut rng).unwrap(); let res = generate_oprf3::(&input, alpha, &blinding_factor).unwrap(); let (hashed_input, _) = Hkdf::::extract(None, &input); diff --git a/src/tests/opaque_ke_test.rs b/src/tests/opaque_ke_test.rs index 2d5e9ec..fc13a3f 100644 --- a/src/tests/opaque_ke_test.rs +++ b/src/tests/opaque_ke_test.rs @@ -23,7 +23,6 @@ use std::convert::TryFrom; struct X255193dhNoSlowHash; impl CipherSuite for X255193dhNoSlowHash { - type Digest = sha2::Sha256; type Group = EdwardsPoint; type KeyFormat = X25519KeyPair; type SlowHash = NoOpHash; @@ -248,13 +247,14 @@ fn generate_parameters() -> TestVectorParameters { ) .unwrap(); let r1_bytes = r1.to_bytes().to_vec(); - let blinding_factor_bytes = *CS::Group::scalar_as_bytes(&client_registration.blinding_factor); + let blinding_factor_bytes = + CS::Group::scalar_as_bytes(&client_registration.blinding_factor).clone(); let client_registration_state = client_registration.to_bytes().to_vec(); let mut oprf_key_rng = CycleRng::new(oprf_key_raw.to_vec()); let (r2, server_registration) = ServerRegistration::::start(r1, &mut oprf_key_rng).unwrap(); let r2_bytes = r2.to_bytes().to_vec(); - let oprf_key_bytes = *CS::Group::scalar_as_bytes(&server_registration.oprf_key); + let oprf_key_bytes = CS::Group::scalar_as_bytes(&server_registration.oprf_key).clone(); let server_registration_state = server_registration.to_bytes().to_vec(); let mut client_s_sk_and_nonce: Vec = Vec::new(); diff --git a/src/tests/serialization.rs b/src/tests/serialization.rs index 3690dfd..0a755fe 100644 --- a/src/tests/serialization.rs +++ b/src/tests/serialization.rs @@ -21,7 +21,6 @@ use std::convert::TryFrom; struct Default; impl CipherSuite for Default { - type Digest = sha2::Sha512; type Group = RistrettoPoint; type KeyFormat = crate::keypair::X25519KeyPair; type SlowHash = crate::slow_hash::NoOpHash;