diff --git a/Cargo.toml b/Cargo.toml index 5fb9336..00139db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,12 +34,12 @@ elliptic-curve = { version = "0.13", features = [ "voprf", ] } generic-array = "1" -rand_core = { version = "0.6", default-features = false } +rand_core = { version = "0.9", default-features = false } serde = { version = "1", default-features = false, features = [ "derive", ], optional = true } sha2 = { version = "0.10", default-features = false, optional = true } -subtle = { version = "2.3", default-features = false } +subtle = { version = "2.6", default-features = false } zeroize = { version = "1.5", default-features = false } [dev-dependencies] @@ -58,7 +58,7 @@ p521 = { version = "0.13.3", default-features = false, features = [ "voprf", ] } proptest = "1" -rand = "0.8" +rand = "0.9" regex = "1" serde_json = "1" sha2 = "0.10" diff --git a/src/common.rs b/src/common.rs index 3224791..b398d30 100644 --- a/src/common.rs +++ b/src/common.rs @@ -16,7 +16,7 @@ use digest::{Digest, Output, OutputSizeUser}; use generic_array::sequence::Concat; use generic_array::typenum::{IsLess, Unsigned, U2, U256, U9}; use generic_array::{ArrayLength, GenericArray}; -use rand_core::{CryptoRng, RngCore}; +use rand_core::{TryCryptoRng, TryRngCore}; use subtle::ConstantTimeEq; #[cfg(feature = "serde")] @@ -128,7 +128,7 @@ pub struct Proof { /// Can only fail with [`Error::Batch`]. #[allow(clippy::many_single_char_names)] -pub(crate) fn generate_proof( +pub(crate) fn generate_proof( rng: &mut R, k: ::Scalar, a: ::Elem, diff --git a/src/group/elliptic_curve.rs b/src/group/elliptic_curve.rs index bb57083..98d69db 100644 --- a/src/group/elliptic_curve.rs +++ b/src/group/elliptic_curve.rs @@ -6,6 +6,7 @@ // of this source tree. You may select, at your option, one of the above-listed // licenses. +use core::num::NonZeroU32; use core::ops::Add; use digest::core_api::BlockSizeUser; @@ -19,7 +20,7 @@ use elliptic_curve::{ }; use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256}; use generic_array::{ArrayLength, GenericArray}; -use rand_core::{CryptoRng, RngCore}; +use rand_core::{TryCryptoRng, TryRngCore}; use super::Group; use crate::{Error, InternalError, Result}; @@ -93,8 +94,8 @@ where .map_err(|_| Error::Deserialization) } - fn random_scalar(rng: &mut R) -> Self::Scalar { - *SecretKey::::random(rng).to_nonzero_scalar() + fn random_scalar(rng: &mut R) -> Self::Scalar { + *SecretKey::::random(&mut CompatRng(rng)).to_nonzero_scalar() } fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar { @@ -123,3 +124,39 @@ where .map_err(|_| Error::Deserialization) } } + +/// Adapter allowing `rand_core 0.9` RNGs to satisfy the `elliptic_curve` 0.13 +/// requirement for `rand_core 0.6` traits. +struct CompatRng<'a, R>(&'a mut R); + +impl<'a, R> elliptic_curve::rand_core::RngCore for CompatRng<'a, R> +where + R: TryRngCore, +{ + fn next_u32(&mut self) -> u32 { + self.0.try_next_u32().expect("RNG failure") + } + + fn next_u64(&mut self) -> u64 { + self.0.try_next_u64().expect("RNG failure") + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.0 + .try_fill_bytes(dest) + .expect("RNG failure while filling bytes"); + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), elliptic_curve::rand_core::Error> { + self.0.try_fill_bytes(dest).map_err(|_| compat_error())?; + Ok(()) + } +} + +impl<'a, R> elliptic_curve::rand_core::CryptoRng for CompatRng<'a, R> where R: TryCryptoRng {} + +fn compat_error() -> elliptic_curve::rand_core::Error { + let code = NonZeroU32::new(elliptic_curve::rand_core::Error::CUSTOM_START) + .expect("CUSTOM_START must be non-zero"); + elliptic_curve::rand_core::Error::from(code) +} diff --git a/src/group/mod.rs b/src/group/mod.rs index e5b3631..8197dbf 100644 --- a/src/group/mod.rs +++ b/src/group/mod.rs @@ -18,7 +18,7 @@ use digest::core_api::BlockSizeUser; use digest::{FixedOutput, HashMarker}; use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256}; use generic_array::{ArrayLength, GenericArray}; -use rand_core::{CryptoRng, RngCore}; +use rand_core::{TryCryptoRng, TryRngCore}; #[cfg(feature = "ristretto255")] pub use ristretto::Ristretto255; use subtle::{Choice, ConstantTimeEq}; @@ -101,7 +101,7 @@ where fn deserialize_elem(element_bits: &[u8]) -> Result; /// picks a scalar at random - fn random_scalar(rng: &mut R) -> Self::Scalar; + fn random_scalar(rng: &mut R) -> Self::Scalar; /// The multiplicative inverse of this scalar fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar; diff --git a/src/group/ristretto.rs b/src/group/ristretto.rs index 68c7523..c9bbe10 100644 --- a/src/group/ristretto.rs +++ b/src/group/ristretto.rs @@ -15,7 +15,7 @@ use digest::{FixedOutput, HashMarker}; use elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXmd, Expander}; use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32, U64}; use generic_array::GenericArray; -use rand_core::{CryptoRng, RngCore}; +use rand_core::{TryCryptoRng, TryRngCore}; use subtle::ConstantTimeEq; use super::Group; @@ -94,10 +94,11 @@ impl Group for Ristretto255 { .ok_or(Error::Deserialization) } - fn random_scalar(rng: &mut R) -> Self::Scalar { + fn random_scalar(rng: &mut R) -> Self::Scalar { loop { let mut scalar_bytes = [0u8; 32]; - rng.fill_bytes(&mut scalar_bytes); + rng.try_fill_bytes(&mut scalar_bytes) + .expect("RNG failure while filling scalar bytes"); if let Ok(scalar) = Self::deserialize_scalar(&scalar_bytes) { break scalar; diff --git a/src/oprf.rs b/src/oprf.rs index d3e2f48..541f2ea 100644 --- a/src/oprf.rs +++ b/src/oprf.rs @@ -14,7 +14,7 @@ use derive_where::derive_where; use digest::{Digest, Output}; use generic_array::typenum::Unsigned; use generic_array::GenericArray; -use rand_core::{CryptoRng, RngCore}; +use rand_core::{TryCryptoRng, TryRngCore}; use crate::common::{ derive_key_internal, deterministic_blind_unchecked, hash_to_group, i2osp_2, @@ -73,7 +73,7 @@ impl OprfClient { /// /// # Errors /// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`]. - pub fn blind( + pub fn blind( input: &[u8], blinding_factor_rng: &mut R, ) -> Result> { @@ -146,9 +146,9 @@ impl OprfServer { /// /// # Errors /// [`Error::Protocol`] if the protocol fails and can't be completed. - pub fn new(rng: &mut R) -> Result { + pub fn new(rng: &mut R) -> Result { let mut seed = GenericArray::<_, ::ScalarLen>::default(); - rng.fill_bytes(&mut seed); + rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?; Self::new_from_seed(&seed, &[]) } @@ -268,6 +268,7 @@ mod tests { use core::ptr; use rand::rngs::OsRng; + use rand::TryRngCore; use super::*; use crate::common::{Dst, STR_HASH_TO_GROUP}; @@ -304,7 +305,7 @@ mod tests { fn base_inversion_unsalted() { let mut rng = OsRng; let mut input = [0u8; 64]; - rng.fill_bytes(&mut input); + rng.try_fill_bytes(&mut input).unwrap(); let client_blind_result = OprfClient::::blind(&input, &mut rng).unwrap(); let client_finalize_result = client_blind_result .state diff --git a/src/poprf.rs b/src/poprf.rs index fc7d789..9b33fd8 100644 --- a/src/poprf.rs +++ b/src/poprf.rs @@ -16,7 +16,7 @@ use derive_where::derive_where; use digest::{Digest, Output, OutputSizeUser}; use generic_array::typenum::Unsigned; use generic_array::{ArrayLength, GenericArray}; -use rand_core::{CryptoRng, RngCore}; +use rand_core::{TryCryptoRng, TryRngCore}; use crate::common::{ derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2, @@ -75,7 +75,7 @@ impl PoprfClient { /// /// # Errors /// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`]. - pub fn blind( + pub fn blind( input: &[u8], blinding_factor_rng: &mut R, ) -> Result> { @@ -189,9 +189,9 @@ impl PoprfServer { /// /// # Errors /// [`Error::Protocol`] if the protocol fails and can't be completed. - pub fn new(rng: &mut R) -> Result { + pub fn new(rng: &mut R) -> Result { let mut seed = GenericArray::<_, ::ScalarLen>::default(); - rng.fill_bytes(&mut seed); + rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?; Self::new_from_seed(&seed, &[]) } @@ -235,7 +235,7 @@ impl PoprfServer { /// # Errors /// - [`Error::Info`] if the `info` is longer than `u16::MAX`. /// - [`Error::Protocol`] if the protocol fails and can't be completed. - pub fn blind_evaluate( + pub fn blind_evaluate( &self, rng: &mut R, blinded_element: &BlindedElement, @@ -273,7 +273,7 @@ impl PoprfServer { /// - [`Error::Info`] if the `info` is longer than `u16::MAX`. /// - [`Error::Protocol`] if the protocol fails and can't be completed. #[cfg(feature = "alloc")] - pub fn batch_blind_evaluate<'a, R: RngCore + CryptoRng, IE>( + pub fn batch_blind_evaluate<'a, R: TryRngCore + TryCryptoRng, IE>( &self, rng: &mut R, blinded_elements: &'a IE, @@ -346,7 +346,7 @@ impl PoprfServer { pub fn batch_blind_evaluate_finish< 'a, 'b, - R: RngCore + CryptoRng, + R: TryRngCore + TryCryptoRng, IB: Iterator> + ExactSizeIterator, IE, >( diff --git a/src/tests/mock_rng.rs b/src/tests/mock_rng.rs index eea5442..3268d41 100644 --- a/src/tests/mock_rng.rs +++ b/src/tests/mock_rng.rs @@ -9,7 +9,7 @@ use alloc::vec::Vec; use core::cmp::min; -use rand_core::{CryptoRng, Error, RngCore}; +use rand_core::{CryptoRng, RngCore}; /// A simple implementation of `RngCore` for testing purposes. /// @@ -54,12 +54,6 @@ impl RngCore for CycleRng { dest[..len].copy_from_slice(&self.v[..len]); rotate_left(&mut self.v, len); } - - #[inline] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.fill_bytes(dest); - Ok(()) - } } // This is meant for testing only diff --git a/src/voprf.rs b/src/voprf.rs index d5f031e..c3440bb 100644 --- a/src/voprf.rs +++ b/src/voprf.rs @@ -16,7 +16,7 @@ use derive_where::derive_where; use digest::{Digest, Output}; use generic_array::typenum::Unsigned; use generic_array::GenericArray; -use rand_core::{CryptoRng, RngCore}; +use rand_core::{TryCryptoRng, TryRngCore}; use crate::common::{ derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2, @@ -75,7 +75,7 @@ impl VoprfClient { /// /// # Errors /// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`]. - pub fn blind( + pub fn blind( input: &[u8], blinding_factor_rng: &mut R, ) -> Result> { @@ -196,9 +196,9 @@ impl VoprfServer { /// /// # Errors /// [`Error::Protocol`] if the protocol fails and can't be completed. - pub fn new(rng: &mut R) -> Result { + pub fn new(rng: &mut R) -> Result { let mut seed = GenericArray::<_, ::ScalarLen>::default(); - rng.fill_bytes(&mut seed); + rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?; // This can't fail as the hash output is type constrained. Self::new_from_seed(&seed, &[]) } @@ -238,7 +238,7 @@ impl VoprfServer { /// Computes the second step for the multiplicative blinding version of /// DH-OPRF. This message is sent from the server (who holds the OPRF key) /// to the client. - pub fn blind_evaluate( + pub fn blind_evaluate( &self, rng: &mut R, blinded_element: &BlindedElement, @@ -271,7 +271,7 @@ impl VoprfServer { /// [`Error::Batch`] if the number of `blinded_elements` and /// `evaluation_elements` don't match or is longer then [`u16::MAX`] #[cfg(feature = "alloc")] - pub fn batch_blind_evaluate<'a, R: RngCore + CryptoRng, I>( + pub fn batch_blind_evaluate<'a, R: TryRngCore + TryCryptoRng, I>( &self, rng: &mut R, blinded_elements: &'a I, @@ -322,7 +322,7 @@ impl VoprfServer { pub fn batch_blind_evaluate_finish< 'a, 'b, - R: RngCore + CryptoRng, + R: TryRngCore + TryCryptoRng, IB: Iterator> + ExactSizeIterator, IE, >( @@ -599,7 +599,7 @@ mod tests { let num_iterations = 10; for _ in 0..num_iterations { let mut input = [0u8; 32]; - rng.fill_bytes(&mut input); + rng.try_fill_bytes(&mut input).unwrap(); let client_blind_result = VoprfClient::::blind(&input, &mut rng).unwrap(); inputs.push(input); client_states.push(client_blind_result.state); @@ -643,7 +643,7 @@ mod tests { let num_iterations = 10; for _ in 0..num_iterations { let mut input = [0u8; 32]; - rng.fill_bytes(&mut input); + rng.try_fill_bytes(&mut input).unwrap(); let client_blind_result = VoprfClient::::blind(&input, &mut rng).unwrap(); inputs.push(input); client_states.push(client_blind_result.state);