diff --git a/CHANGELOG.md b/CHANGELOG.md index 27af55f..bdf3900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ major series. ## 4.x series +* Make `rand_core` an optional feature * Add target u32/u64 backend overrides * Migrate documentation to docs.rs hosted * Fix backend documentation generation diff --git a/Cargo.toml b/Cargo.toml index 81dca59..2066af1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ exclude = [ [package.metadata.docs.rs] rustdoc-args = ["--html-in-header", "docs/assets/rustdoc-include-katex-header.html", "--cfg", "docsrs"] -features = ["serde", "simd_backend"] +features = ["serde", "simd_backend", "rand_core"] [badges] travis-ci = { repository = "dalek-cryptography/curve25519-dalek", branch = "master"} @@ -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"] } diff --git a/Makefile b/Makefile index 7eddc3c..6cdc463 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -FEATURES := simd_backend serde +FEATURES := simd_backend serde rand_core doc: cargo +nightly rustdoc --features "$(FEATURES)" -- --html-in-header docs/assets/rustdoc-include-katex-header.html --cfg docsrs diff --git a/src/edwards.rs b/src/edwards.rs index f8374d7..d1608d3 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 e831b4f..64bef91 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(crate) mod mocks; diff --git a/src/mocks.rs b/src/mocks.rs new file mode 100644 index 0000000..d04ca3d --- /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 b948be1..a20479c 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -374,6 +374,7 @@ mod test { use super::*; use crate::constants; + #[cfg(feature = "rand_core")] use rand_core::OsRng; #[test] @@ -458,6 +459,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 2063f7e..0d15924 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; @@ -501,7 +502,8 @@ impl RistrettoPoint { /// \mathrm{enc}( \[2\]P\_1), \ldots, \mathrm{enc}( \[2\]P\_n ) \\) /// in a batch. /// - /// ``` + #[cfg_attr(feature = "rand_core", doc = "```")] + #[cfg_attr(not(feature = "rand_core"), doc = "```ignore")] /// # use curve25519_dalek::ristretto::RistrettoPoint; /// use rand_core::OsRng; /// @@ -509,6 +511,7 @@ impl RistrettoPoint { /// # // See https://doc.rust-lang.org/book/documentation.html#documentation-as-tests /// # fn main() { /// let mut rng = OsRng; + /// /// let points: Vec = /// (0..32).map(|_| RistrettoPoint::random(&mut rng)).collect(); /// @@ -662,6 +665,7 @@ impl RistrettoPoint { ) } + #[cfg(feature = "rand_core")] /// Return a `RistrettoPoint` chosen uniformly at random using a user-provided RNG. /// /// # Inputs @@ -1343,7 +1347,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 point in P_coset { assert_eq!(P, RistrettoPoint(point)); @@ -1670,7 +1674,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 +1687,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 +1706,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 86eee4c..bc21367 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; @@ -566,6 +567,7 @@ impl Zeroize for Scalar { } impl Scalar { + #[cfg(feature = "rand_core")] /// Return a `Scalar` chosen uniformly at random using a user-provided RNG. /// /// # Inputs @@ -1404,7 +1406,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); }