From 093a15f59782fc19a44067477a099eecf1df2eed Mon Sep 17 00:00:00 2001 From: Kevin Lewi Date: Mon, 18 Oct 2021 13:24:15 -0700 Subject: [PATCH] Adding deterministic_blind_unchecked functionality under danger feature (#30) --- src/group/ristretto.rs | 17 ++------ src/tests/voprf_test_vectors.rs | 20 ++++++---- src/voprf.rs | 70 +++++++++++++++++++++++++++++++-- 3 files changed, 83 insertions(+), 24 deletions(-) diff --git a/src/group/ristretto.rs b/src/group/ristretto.rs index 9790805..1209c91 100644 --- a/src/group/ristretto.rs +++ b/src/group/ristretto.rs @@ -87,20 +87,9 @@ impl Group for RistrettoPoint { fn random_nonzero_scalar(rng: &mut R) -> Self::Scalar { loop { let scalar = { - #[cfg(not(test))] - { - let mut scalar_bytes = [0u8; 64]; - rng.fill_bytes(&mut scalar_bytes); - Scalar::from_bytes_mod_order_wide(&scalar_bytes) - } - - // Tests need an exact conversion from bytes to scalar, sampling only 32 bytes from rng - #[cfg(test)] - { - let mut scalar_bytes = [0u8; 32]; - rng.fill_bytes(&mut scalar_bytes); - Scalar::from_bytes_mod_order(scalar_bytes) - } + let mut scalar_bytes = [0u8; 64]; + rng.fill_bytes(&mut scalar_bytes); + Scalar::from_bytes_mod_order_wide(&scalar_bytes) }; if scalar != Scalar::zero() { diff --git a/src/tests/voprf_test_vectors.rs b/src/tests/voprf_test_vectors.rs index 3c7c51a..956654b 100644 --- a/src/tests/voprf_test_vectors.rs +++ b/src/tests/voprf_test_vectors.rs @@ -173,9 +173,12 @@ fn test_base_blind( ) -> Result<(), InternalError> { for parameters in tvs { for i in 0..parameters.input.len() { - let mut rng = CycleRng::new(parameters.blind[i].to_vec()); - let client_result = - NonVerifiableClient::::blind(parameters.input[i].clone(), &mut rng)?; + let blind = + G::from_scalar_slice(&GenericArray::clone_from_slice(¶meters.blind[i]))?; + let client_result = NonVerifiableClient::::deterministic_blind_unchecked( + parameters.input[i].clone(), + blind, + )?; assert_eq!( ¶meters.blind[i], @@ -196,9 +199,12 @@ fn test_verifiable_blind( ) -> Result<(), InternalError> { for parameters in tvs { for i in 0..parameters.input.len() { - let mut rng = CycleRng::new(parameters.blind[i].to_vec()); - let client_blind_result = - VerifiableClient::::blind(parameters.input[i].clone(), &mut rng)?; + let blind = + G::from_scalar_slice(&GenericArray::clone_from_slice(¶meters.blind[i]))?; + let client_blind_result = VerifiableClient::::deterministic_blind_unchecked( + parameters.input[i].clone(), + blind, + )?; assert_eq!( ¶meters.blind[i], @@ -291,7 +297,7 @@ fn test_verifiable_finalize( for parameters in tvs { let mut clients = vec![]; for i in 0..parameters.input.len() { - let client = VerifiableClient::::from_data_and_blind( + let client = VerifiableClient::::from_data_and_blind_and_element( ¶meters.input[i], ::from_scalar_slice(&GenericArray::clone_from_slice( ¶meters.blind[i], diff --git a/src/voprf.rs b/src/voprf.rs index b123fae..f9ce763 100644 --- a/src/voprf.rs +++ b/src/voprf.rs @@ -165,6 +165,32 @@ impl NonVerifiableClient { }) } + #[cfg(feature = "danger")] + /// Computes the first step for the multiplicative blinding version of DH-OPRF, + /// taking a blinding factor scalar as input instead of sampling from an RNG. + /// + /// # Caution + /// + /// This should be used with caution, since + /// it does not perform any checks on the validity of the blinding factor! + pub fn deterministic_blind_unchecked( + input: Vec, + blind: ::Scalar, + ) -> Result, InternalError> { + let blinded_element = deterministic_blind_unchecked::(&input, &blind, Mode::Base)?; + Ok(NonVerifiableClientBlindResult { + state: Self { + data: input, + blind, + hash: PhantomData, + }, + message: BlindedElement { + value: blinded_element, + hash: PhantomData, + }, + }) + } + /// Computes the third step for the multiplicative blinding version of DH-OPRF, in which /// the client unblinds the server's message. pub fn finalize( @@ -221,6 +247,34 @@ impl VerifiableClient { }) } + #[cfg(feature = "danger")] + /// Computes the first step for the multiplicative blinding version of DH-OPRF, + /// taking a blinding factor scalar as input instead of sampling from an RNG. + /// + /// # Caution + /// + /// This should be used with caution, since + /// it does not perform any checks on the validity of the blinding factor! + pub fn deterministic_blind_unchecked( + input: Vec, + blind: ::Scalar, + ) -> Result, InternalError> { + let blinded_element = + deterministic_blind_unchecked::(&input, &blind, Mode::Verifiable)?; + Ok(VerifiableClientBlindResult { + state: Self { + data: input, + blind, + blinded_element, + hash: PhantomData, + }, + message: BlindedElement { + value: blinded_element, + hash: PhantomData, + }, + }) + } + /// Computes the third step for the multiplicative blinding version of DH-OPRF, in which /// the client unblinds the server's message. pub fn finalize( @@ -303,7 +357,7 @@ impl VerifiableClient { #[cfg(test)] /// Only used for test functions - pub fn from_data_and_blind( + pub fn from_data_and_blind_and_element( data: &[u8], blind: ::Scalar, blinded_element: G, @@ -622,10 +676,20 @@ fn blind( ) -> Result<(::Scalar, G), InternalError> { // Choose a random scalar that must be non-zero let blind = ::random_nonzero_scalar(blinding_factor_rng); + let blinded_element = deterministic_blind_unchecked::(input, &blind, mode)?; + Ok((blind, blinded_element)) +} + +// Inner function for blind that assumes that the blinding factor has already been chosen, +// and therefore takes it as input. Does not check if the blinding factor is non-zero. +fn deterministic_blind_unchecked( + input: &[u8], + blind: &::Scalar, + mode: Mode, +) -> Result { let dst = GenericArray::from(*STR_HASH_TO_GROUP).concat(get_context_string::(mode)?); let hashed_point = ::hash_to_curve::(input, dst)?; - let blinded_element = hashed_point * &blind; - Ok((blind, blinded_element)) + Ok(hashed_point * blind) } fn verifiable_unblind<'a, G: 'a + Group, H: 'a + BlockInput + Digest, I>(