diff --git a/Cargo.toml b/Cargo.toml index c621a192a..49c0277a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ bincode = "1" criterion = { version = "0.4.0", features = ["html_reports"] } hex = "0.4.2" rand = "0.8" +rand_core = { version = "0.6", default-features = false } [[bench]] name = "dalek_benchmarks" @@ -43,7 +44,7 @@ harness = false [dependencies] cfg-if = "1" -rand_core = { version = "0.6", default-features = false } +rand_core = { version = "0.6", default-features = false, optional = true } digest = { version = "0.10", default-features = false } subtle = { version = "^2.2.1", default-features = false } serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } @@ -58,6 +59,7 @@ nightly = ["subtle/nightly"] default = ["std"] std = ["alloc", "subtle/std", "rand_core/std"] alloc = ["zeroize/alloc"] +rand_core = ["dep:rand_core"] # fiat-crypto backend with formally-verified field arithmetic fiat_backend = ["fiat-crypto"] diff --git a/src/edwards.rs b/src/edwards.rs index 8dcdc2db9..c7dc8b859 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -1507,7 +1507,7 @@ mod test { // Construct random coefficients x0, ..., x_{n-1}, // followed by some extra hardcoded ones. let xs = (0..n) - .map(|_| Scalar::random(&mut rng)) + .map(|_| crate::mocks::MockScalar::random(&mut rng)) // The largest scalar allowed by the type system, 2^255-1 .chain(iter::once(Scalar::from_bits([0xff; 32]))) .collect::>(); @@ -1577,11 +1577,11 @@ mod test { let B = &crate::constants::ED25519_BASEPOINT_TABLE; let static_scalars = (0..128) - .map(|_| Scalar::random(&mut rng)) + .map(|_| crate::mocks::MockScalar::random(&mut rng)) .collect::>(); let dynamic_scalars = (0..128) - .map(|_| Scalar::random(&mut rng)) + .map(|_| crate::mocks::MockScalar::random(&mut rng)) .collect::>(); let check_scalar: Scalar = static_scalars diff --git a/src/lib.rs b/src/lib.rs index e831b4f78..222aef3dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,3 +83,6 @@ pub(crate) mod prelude; // Generic code for window lookups pub(crate) mod window; + +#[cfg(test)] +pub mod mocks; diff --git a/src/mocks.rs b/src/mocks.rs new file mode 100644 index 000000000..d04ca3d4b --- /dev/null +++ b/src/mocks.rs @@ -0,0 +1,42 @@ +//! This is used mocking / proxying the below for tests: +//! - random_test() to Scalar::random() depending on feature `rand_core` + +use crate::ristretto::RistrettoPoint; +use crate::scalar::Scalar; + +use rand_core::{CryptoRng, RngCore}; + +pub struct MockScalar; +impl MockScalar { + #[cfg(feature = "rand_core")] + /// Proxy Scalar::random() for random_test + pub fn random(rng: &mut R) -> Scalar { + Scalar::random(rng) + } + + #[cfg(not(feature = "rand_core"))] + /// Mock Scalar::random() for random_test + pub fn random(rng: &mut R) -> Scalar { + let mut scalar_bytes = [0u8; 64]; + rng.fill_bytes(&mut scalar_bytes); + Scalar::from_bytes_mod_order_wide(&scalar_bytes) + } +} + +pub struct MockRistrettoPoint; +impl MockRistrettoPoint { + #[cfg(feature = "rand_core")] + /// Proxy RistrettoPoint::random() for random_test + pub fn random(rng: &mut R) -> RistrettoPoint { + RistrettoPoint::random(rng) + } + + #[cfg(not(feature = "rand_core"))] + /// Mock RistrettoPoint::random() for random_test + pub fn random(rng: &mut R) -> RistrettoPoint { + let mut uniform_bytes = [0u8; 64]; + rng.fill_bytes(&mut uniform_bytes); + + RistrettoPoint::from_uniform_bytes(&uniform_bytes) + } +} diff --git a/src/montgomery.rs b/src/montgomery.rs index a34330bb1..d70fc9fd9 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -366,6 +366,7 @@ mod test { use super::*; use crate::constants; + #[cfg(feature = "rand_core")] use rand_core::OsRng; #[test] @@ -450,6 +451,7 @@ mod test { } #[test] + #[cfg(feature = "rand_core")] fn montgomery_ladder_matches_edwards_scalarmult() { let mut csprng: OsRng = OsRng; diff --git a/src/ristretto.rs b/src/ristretto.rs index d4beec10a..4e7fef0ea 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -113,7 +113,7 @@ //! used to implement //! //! * `RistrettoPoint::random()`, which generates random points from an -//! RNG; +//! RNG - enabled by rand_core feature; //! //! * `RistrettoPoint::from_hash()` and //! `RistrettoPoint::hash_from_bytes()`, which perform hashing to the @@ -165,6 +165,7 @@ use core::ops::{Add, Neg, Sub}; use core::ops::{AddAssign, SubAssign}; use core::ops::{Mul, MulAssign}; +#[cfg(feature = "rand_core")] use rand_core::{CryptoRng, RngCore}; use digest::generic_array::typenum::U64; @@ -509,8 +510,13 @@ impl RistrettoPoint { /// # // See https://doc.rust-lang.org/book/documentation.html#documentation-as-tests /// # fn main() { /// let mut rng = OsRng; + /// + /// let mut uniform_bytes = [0u8; 64]; + /// rng.fill_bytes(&mut uniform_bytes); + /// let random_rp = RistrettoPoint::from_uniform_bytes(&uniform_bytes) + /// /// let points: Vec = - /// (0..32).map(|_| RistrettoPoint::random(&mut rng)).collect(); + /// (0..32).map(|_| random_rp).collect(); /// /// let compressed = RistrettoPoint::double_and_compress_batch(&points); /// @@ -662,6 +668,7 @@ impl RistrettoPoint { ) } + #[cfg(feature = "rand_core")] /// Return a `RistrettoPoint` chosen uniformly at random using a user-provided RNG. /// /// # Inputs @@ -1343,7 +1350,7 @@ mod test { fn four_torsion_random() { let mut rng = OsRng; let B = &constants::RISTRETTO_BASEPOINT_TABLE; - let P = B * &Scalar::random(&mut rng); + let P = B * &crate::mocks::MockScalar::random(&mut rng); let P_coset = P.coset4(); for i in 0..4 { assert_eq!(P, RistrettoPoint(P_coset[i])); @@ -1670,7 +1677,7 @@ mod test { let mut rng = OsRng; let B = &constants::RISTRETTO_BASEPOINT_TABLE; for _ in 0..100 { - let P = B * &Scalar::random(&mut rng); + let P = B * &crate::mocks::MockScalar::random(&mut rng); let compressed_P = P.compress(); let Q = compressed_P.decompress().unwrap(); assert_eq!(P, Q); @@ -1683,7 +1690,7 @@ mod test { let mut rng = OsRng; let mut points: Vec = (0..1024) - .map(|_| RistrettoPoint::random(&mut rng)) + .map(|_| crate::mocks::MockRistrettoPoint::random(&mut rng)) .collect(); points[500] = RistrettoPoint::identity(); @@ -1702,11 +1709,11 @@ mod test { let B = &crate::constants::RISTRETTO_BASEPOINT_TABLE; let static_scalars = (0..128) - .map(|_| Scalar::random(&mut rng)) + .map(|_| crate::mocks::MockScalar::random(&mut rng)) .collect::>(); let dynamic_scalars = (0..128) - .map(|_| Scalar::random(&mut rng)) + .map(|_| crate::mocks::MockScalar::random(&mut rng)) .collect::>(); let check_scalar: Scalar = static_scalars diff --git a/src/scalar.rs b/src/scalar.rs index 4b3a7bd0e..0b87f4a85 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -152,6 +152,7 @@ use crate::prelude::*; use cfg_if::cfg_if; +#[cfg(feature = "rand_core")] use rand_core::{CryptoRng, RngCore}; use digest::generic_array::typenum::U64; @@ -564,6 +565,7 @@ impl Zeroize for Scalar { } impl Scalar { + #[cfg(feature = "rand_core")] /// Return a `Scalar` chosen uniformly at random using a user-provided RNG. /// /// # Inputs @@ -1401,7 +1403,7 @@ mod test { fn non_adjacent_form_random() { let mut rng = rand::thread_rng(); for _ in 0..1_000 { - let x = Scalar::random(&mut rng); + let x = crate::mocks::MockScalar::random(&mut rng); for w in &[5, 6, 7, 8] { non_adjacent_form_iter(*w, &x); }