Skip to content

Commit

Permalink
Merge pull request entropyxyz#40 from fjarri/boxed-support
Browse files Browse the repository at this point in the history
`BoxedUint` support
  • Loading branch information
fjarri authored Dec 29, 2023
2 parents 752bdee + 2b44a1f commit 875622c
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 87 deletions.
13 changes: 8 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Bumped `crypto-bigint` to 0.6.0-pre.6. ([#38])
- Bumped MSRV to 1.73. (#[38])
- `MillerRabin::new()` takes an `Odd`-wrapped integer. `random_odd_uint()` is renamed to `random_odd_integer()`, takes a `NonZeroU32` for `bit_length`, and returns an `Odd`-wrapped integer. `LucasBase::generate()` takes an `Odd`-wrapped integer. `lucas_test` takes an `Odd`-wrapped integer. (#[38])
- All bit length-type parameters take `u32` instead of `usize`. (#[38])
- All the API is based on the `Integer` trait instead of `Uint` specifically. (#[38])
- Bumped `crypto-bigint` to 0.6.0-pre.7. ([#40])
- Bumped MSRV to 1.73. ([#36])
- `MillerRabin::new()` takes an `Odd`-wrapped integer by value. `random_odd_uint()` returns an `Odd`-wrapped integer. `LucasBase::generate()` takes an `Odd`-wrapped integer. `lucas_test` takes an `Odd`-wrapped integer. ([#36])
- `random_odd_uint()` is renamed to `random_odd_integer()`, takes a `NonZeroU32` for `bit_length`. ([#38])
- All bit length-type parameters take `u32` instead of `usize`. ([#36])
- All the API is based on the `Integer` trait instead of `Uint` specifically. ([#38])
- High-level generation/checking functions take an additional `bits_precision` argument. ([#40])


[#36]: https://github.com/entropyxyz/crypto-primes/pull/36
[#38]: https://github.com/entropyxyz/crypto-primes/pull/38
[#40]: https://github.com/entropyxyz/crypto-primes/pull/40


## [0.5.0] - 2023-08-20
Expand Down
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ categories = ["cryptography", "no-std"]
rust-version = "1.73"

[dependencies]
crypto-bigint = { version = "0.6.0-pre.6", default-features = false, features = ["rand_core"] }
crypto-bigint = { version = "0.6.0-pre.7", default-features = false, features = ["rand_core"] }
rand_core = { version = "0.6.4", default-features = false }
openssl = { version = "0.10.39", optional = true, features = ["vendored"] }
rug = { version = "1.18", default-features = false, features = ["integer"], optional = true }

[dev-dependencies]
# need `crypto-bigint` with `alloc` to test `BoxedUint`
crypto-bigint = { version = "0.6.0-pre.7", default-features = false, features = ["alloc"] }
rand_chacha = "0.3"
criterion = { version = "0.4", features = ["html_reports"] }
num-modular = { version = "0.5", features = ["num-bigint"] }
Expand Down
50 changes: 31 additions & 19 deletions benches/bench.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use core::num::NonZeroU32;

use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use crypto_bigint::{nlimbs, Integer, Odd, RandomBits, Uint, U1024, U128, U256};
use crypto_bigint::{nlimbs, BoxedUint, Integer, Odd, RandomBits, Uint, U1024, U128, U256};
use rand_chacha::ChaCha8Rng;
use rand_core::{CryptoRngCore, OsRng, SeedableRng};

Expand All @@ -27,12 +27,13 @@ fn make_rng() -> ChaCha8Rng {
fn random_odd_uint<T: RandomBits + Integer>(
rng: &mut impl CryptoRngCore,
bit_length: u32,
bits_precision: u32,
) -> Odd<T> {
random_odd_integer::<T>(rng, NonZeroU32::new(bit_length).unwrap())
random_odd_integer::<T>(rng, NonZeroU32::new(bit_length).unwrap(), bits_precision)
}

fn make_sieve<const L: usize>(rng: &mut impl CryptoRngCore) -> Sieve<Uint<L>> {
let start = random_odd_uint::<Uint<L>>(rng, Uint::<L>::BITS);
let start = random_odd_uint::<Uint<L>>(rng, Uint::<L>::BITS, Uint::<L>::BITS);
Sieve::new(&start, NonZeroU32::new(Uint::<L>::BITS).unwrap(), false)
}

Expand All @@ -45,12 +46,12 @@ fn bench_sieve(c: &mut Criterion) {
let mut group = c.benchmark_group("Sieve");

group.bench_function("(U128) random start", |b| {
b.iter(|| random_odd_uint::<U128>(&mut OsRng, 128))
b.iter(|| random_odd_uint::<U128>(&mut OsRng, 128, 128))
});

group.bench_function("(U128) creation", |b| {
b.iter_batched(
|| random_odd_uint::<U128>(&mut OsRng, 128),
|| random_odd_uint::<U128>(&mut OsRng, 128, 128),
|start| Sieve::new(start.as_ref(), NonZeroU32::new(128).unwrap(), false),
BatchSize::SmallInput,
)
Expand All @@ -66,12 +67,12 @@ fn bench_sieve(c: &mut Criterion) {
});

group.bench_function("(U1024) random start", |b| {
b.iter(|| random_odd_uint::<U1024>(&mut OsRng, 1024))
b.iter(|| random_odd_uint::<U1024>(&mut OsRng, 1024, 1024))
});

group.bench_function("(U1024) creation", |b| {
b.iter_batched(
|| random_odd_uint::<U1024>(&mut OsRng, 1024),
|| random_odd_uint::<U1024>(&mut OsRng, 1024, 1024),
|start| Sieve::new(start.as_ref(), NonZeroU32::new(1024).unwrap(), false),
BatchSize::SmallInput,
)
Expand All @@ -93,7 +94,7 @@ fn bench_miller_rabin(c: &mut Criterion) {

group.bench_function("(U128) creation", |b| {
b.iter_batched(
|| random_odd_uint::<U128>(&mut OsRng, 128),
|| random_odd_uint::<U128>(&mut OsRng, 128, 128),
|n| MillerRabin::new(&n),
BatchSize::SmallInput,
)
Expand All @@ -109,7 +110,7 @@ fn bench_miller_rabin(c: &mut Criterion) {

group.bench_function("(U1024) creation", |b| {
b.iter_batched(
|| random_odd_uint::<U1024>(&mut OsRng, 1024),
|| random_odd_uint::<U1024>(&mut OsRng, 1024, 1024),
|n| MillerRabin::new(&n),
BatchSize::SmallInput,
)
Expand Down Expand Up @@ -202,39 +203,50 @@ fn bench_presets(c: &mut Criterion) {

group.bench_function("(U128) Prime test", |b| {
b.iter_batched(
|| random_odd_uint::<U128>(&mut OsRng, 128),
|| random_odd_uint::<U128>(&mut OsRng, 128, 128),
|num| is_prime_with_rng(&mut OsRng, num.as_ref()),
BatchSize::SmallInput,
)
});

group.bench_function("(U128) Safe prime test", |b| {
b.iter_batched(
|| random_odd_uint::<U128>(&mut OsRng, 128),
|| random_odd_uint::<U128>(&mut OsRng, 128, 128),
|num| is_safe_prime_with_rng(&mut OsRng, num.as_ref()),
BatchSize::SmallInput,
)
});

let mut rng = make_rng();
group.bench_function("(U128) Random prime", |b| {
b.iter(|| generate_prime_with_rng::<U128>(&mut rng, 128))
b.iter(|| generate_prime_with_rng::<U128>(&mut rng, 128, 128))
});

let mut rng = make_rng();
group.bench_function("(U1024) Random prime", |b| {
b.iter(|| generate_prime_with_rng::<U1024>(&mut rng, 1024))
b.iter(|| generate_prime_with_rng::<U1024>(&mut rng, 1024, 1024))
});

let mut rng = make_rng();
group.bench_function("(U128) Random safe prime", |b| {
b.iter(|| generate_safe_prime_with_rng::<U128>(&mut rng, 128))
b.iter(|| generate_safe_prime_with_rng::<U128>(&mut rng, 128, 128))
});

group.sample_size(20);
let mut rng = make_rng();
group.bench_function("(U1024) Random safe prime", |b| {
b.iter(|| generate_safe_prime_with_rng::<U1024>(&mut rng, 1024))
b.iter(|| generate_safe_prime_with_rng::<U1024>(&mut rng, 1024, 1024))
});

let mut rng = make_rng();
group.bench_function("(Boxed128) Random safe prime", |b| {
b.iter(|| generate_safe_prime_with_rng::<BoxedUint>(&mut rng, 128, 128))
});

group.sample_size(20);
let mut rng = make_rng();
group.bench_function("(Boxed1024) Random safe prime", |b| {
b.iter(|| generate_safe_prime_with_rng::<BoxedUint>(&mut rng, 1024, 1024))
});

group.finish();
Expand All @@ -244,19 +256,19 @@ fn bench_presets(c: &mut Criterion) {

let mut rng = make_rng();
group.bench_function("(U128) Random safe prime", |b| {
b.iter(|| generate_safe_prime_with_rng::<U128>(&mut rng, 128))
b.iter(|| generate_safe_prime_with_rng::<U128>(&mut rng, 128, 128))
});

// The performance should scale with the prime size, not with the Uint size.
// So we should strive for this test's result to be as close as possible
// to that of the previous one and as far away as possible from the next one.
group.bench_function("(U256) Random 128 bit safe prime", |b| {
b.iter(|| generate_safe_prime_with_rng::<U256>(&mut rng, 128))
b.iter(|| generate_safe_prime_with_rng::<U256>(&mut rng, 128, 256))
});

// The upper bound for the previous test.
group.bench_function("(U256) Random 256 bit safe prime", |b| {
b.iter(|| generate_safe_prime_with_rng::<U256>(&mut rng, 256))
b.iter(|| generate_safe_prime_with_rng::<U256>(&mut rng, 256, 256))
});

group.finish();
Expand All @@ -267,7 +279,7 @@ fn bench_gmp(c: &mut Criterion) {
let mut group = c.benchmark_group("GMP");

fn random<const L: usize>(rng: &mut impl CryptoRngCore) -> GmpInteger {
let num = random_odd_uint::<Uint<L>>(rng, Uint::<L>::BITS).get();
let num = random_odd_uint::<Uint<L>>(rng, Uint::<L>::BITS, Uint::<L>::BITS).get();
GmpInteger::from_digits(num.as_words(), Order::Lsf)
}

Expand Down
2 changes: 1 addition & 1 deletion src/hazmat/jacobi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub(crate) fn jacobi_symbol_vartime<T: Integer>(
};

// A degenerate case.
if abs_a == 1 || p_long.as_ref() == &T::one() {
if abs_a == 1 || p_long.as_ref() == &T::one_like(p_long) {
return result;
}

Expand Down
20 changes: 12 additions & 8 deletions src/hazmat/lucas.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Lucas primality test.
use crypto_bigint::{Integer, Monty, Odd, Square, Word};
use crypto_bigint::{Integer, Limb, Monty, Odd, Square, Word};

use super::{
gcd::gcd_vartime,
Expand Down Expand Up @@ -161,7 +161,7 @@ impl LucasBase for BruteForceBase {
// Since the loop proceeds in increasing P and starts with P - 2 == 1,
// the shared prime factor must be P + 2.
// If P + 2 == n, then n is prime; otherwise P + 2 is a proper factor of n.
let primality = if n.as_ref() == &T::from(p + 2) {
let primality = if n.as_ref() == &T::from_limb_like(Limb::from(p + 2), n.as_ref()) {
Primality::Prime
} else {
Primality::Composite
Expand All @@ -182,6 +182,7 @@ fn decompose<T: Integer>(n: &Odd<T>) -> (u32, Odd<T>) {
// Need to be careful here since `n + 1` can overflow.
// Instead of adding 1 and counting trailing 0s, we count trailing ones on the original `n`.

let one = T::one_like(n);
let s = n.trailing_ones_vartime();
let d = if s < n.bits_precision() {
// The shift won't overflow because of the check above.
Expand All @@ -190,10 +191,10 @@ fn decompose<T: Integer>(n: &Odd<T>) -> (u32, Odd<T>) {
n.as_ref()
.overflowing_shr_vartime(s)
.expect("shift should be within range by construction")
.checked_add(&T::one())
.checked_add(&one)
.expect("addition should not overflow by construction")
} else {
T::one()
one
};

(s, Odd::new(d).expect("`d` should be odd by construction"))
Expand Down Expand Up @@ -293,6 +294,9 @@ pub fn lucas_test<T: Integer>(
// R. Crandall, C. Pomerance, "Prime numbers: a computational perspective",
// 2nd ed., Springer (2005) (ISBN: 0-387-25282-7, 978-0387-25282-7)

// A word-to-big integer conversion helper
let to_integer = |x: Word| T::from_limb_like(Limb::from(x), candidate.as_ref());

// Find the base for the Lucas sequence.
let (p, abs_q, q_is_negative) = match base.generate(candidate) {
Ok(pq) => pq,
Expand Down Expand Up @@ -328,7 +332,7 @@ pub fn lucas_test<T: Integer>(
// it does not noticeably affect the performance.
if abs_q != 1
&& gcd_vartime(candidate.as_ref(), abs_q) != 1
&& candidate.as_ref() > &T::from(abs_q)
&& candidate.as_ref() > &to_integer(abs_q)
{
return Primality::Composite;
}
Expand All @@ -351,7 +355,7 @@ pub fn lucas_test<T: Integer>(
let q = if q_is_one {
one.clone()
} else {
let abs_q = <T as Integer>::Monty::new(T::from(abs_q), params.clone());
let abs_q = <T as Integer>::Monty::new(to_integer(abs_q), params.clone());
if q_is_negative {
-abs_q
} else {
Expand All @@ -364,7 +368,7 @@ pub fn lucas_test<T: Integer>(
let p = if p_is_one {
one.clone()
} else {
<T as Integer>::Monty::new(T::from(p), params.clone())
<T as Integer>::Monty::new(to_integer(p), params.clone())
};

// Compute d-th element of Lucas sequence (U_d(P, Q), V_d(P, Q)), where:
Expand All @@ -387,7 +391,7 @@ pub fn lucas_test<T: Integer>(
let mut qk = one.clone(); // keeps Q^k

// D in Montgomery representation - note that it can be negative.
let abs_d = <T as Integer>::Monty::new(T::from(abs_d), params);
let abs_d = <T as Integer>::Monty::new(to_integer(abs_d), params);
let d_m = if d_is_negative { -abs_d } else { abs_d };

for i in (0..d.bits_vartime()).rev() {
Expand Down
23 changes: 13 additions & 10 deletions src/hazmat/miller_rabin.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Miller-Rabin primality test.

use crypto_bigint::{Integer, Monty, NonZero, Odd, PowBoundedExp, RandomMod, Square};
use crypto_bigint::{Integer, Limb, Monty, NonZero, Odd, PowBoundedExp, RandomMod, Square};
use rand_core::CryptoRngCore;

use super::Primality;
Expand Down Expand Up @@ -28,14 +28,16 @@ impl<T: Integer + RandomMod> MillerRabin<T> {
/// Initializes a Miller-Rabin test for `candidate`.
pub fn new(candidate: &Odd<T>) -> Self {
let params = <T as Integer>::Monty::new_params_vartime(candidate.clone());
let one = <T as Integer>::Monty::one(params.clone());
let minus_one = -one.clone();
let m_one = <T as Integer>::Monty::one(params.clone());
let m_minus_one = -m_one.clone();

let one = T::one_like(candidate.as_ref());

// Find `s` and odd `d` such that `candidate - 1 == 2^s * d`.
let (s, d) = if candidate.as_ref() == &T::one() {
(0, T::one())
let (s, d) = if candidate.as_ref() == &one {
(0, one)
} else {
let candidate_minus_one = candidate.wrapping_sub(&T::one());
let candidate_minus_one = candidate.wrapping_sub(&one);
let s = candidate_minus_one.trailing_zeros_vartime();
// Will not overflow because `candidate` is odd and greater than 1.
let d = candidate_minus_one
Expand All @@ -48,8 +50,8 @@ impl<T: Integer + RandomMod> MillerRabin<T> {
candidate: candidate.as_ref().clone(),
bit_length: candidate.bits_vartime(),
montgomery_params: params,
one,
minus_one,
one: m_one,
minus_one: m_minus_one,
s,
d,
}
Expand Down Expand Up @@ -85,7 +87,7 @@ impl<T: Integer + RandomMod> MillerRabin<T> {

/// Perform a Miller-Rabin check with base 2.
pub fn test_base_two(&self) -> Primality {
self.test(&T::from(2u32))
self.test(&T::from_limb_like(Limb::from(2u32), &self.candidate))
}

/// Perform a Miller-Rabin check with a random base (in the range `[3, candidate-2]`)
Expand Down Expand Up @@ -189,7 +191,8 @@ mod tests {
#[test]
fn trivial() {
let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901");
let start = random_odd_integer::<U1024>(&mut rng, NonZeroU32::new(1024).unwrap());
let start =
random_odd_integer::<U1024>(&mut rng, NonZeroU32::new(1024).unwrap(), U1024::BITS);
for num in Sieve::new(start.as_ref(), NonZeroU32::new(1024).unwrap(), false).take(10) {
let mr = MillerRabin::new(&Odd::new(num).unwrap());

Expand Down
Loading

0 comments on commit 875622c

Please sign in to comment.