Skip to content

Commit

Permalink
Use Integer and Monty instead of Uint
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri committed Dec 24, 2023
1 parent 2379c91 commit 3a45eda
Show file tree
Hide file tree
Showing 13 changed files with 433 additions and 332 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/crypto-primes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
strategy:
matrix:
rust:
- 1.65.0 # MSRV
- 1.73.0 # MSRV
- stable
target:
- wasm32-unknown-unknown
Expand Down Expand Up @@ -134,6 +134,6 @@ jobs:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.65.0
toolchain: 1.73.0
profile: minimal
- run: cargo build --all-features --benches
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ description = "Random prime number generation and primality checking library"
repository = "https://github.com/entropyxyz/crypto-primes"
readme = "README.md"
categories = ["cryptography", "no-std"]
rust-version = "1.65"
rust-version = "1.73"

[dependencies]
crypto-bigint = { version = "0.5.4", default-features = false, features = ["rand_core"] }
crypto-bigint = { git = "https://github.com/RustCrypto/crypto-bigint.git", default-features = false, features = ["alloc", "rand_core"], commit = "bc061383cb4f63d69b89b23232fdce3e5b0d898c" }
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 }
Expand Down
60 changes: 30 additions & 30 deletions benches/bench.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use crypto_bigint::{nlimbs, Uint, U1024};
use crypto_bigint::{nlimbs, Odd, Uint, U1024};
use rand_chacha::ChaCha8Rng;
use rand_core::{CryptoRngCore, OsRng, SeedableRng};

Expand All @@ -22,27 +22,27 @@ fn make_rng() -> ChaCha8Rng {
ChaCha8Rng::from_seed(*b"01234567890123456789012345678901")
}

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

fn make_presieved_num<const L: usize>(rng: &mut impl CryptoRngCore) -> Uint<L> {
fn make_presieved_num<const L: usize>(rng: &mut impl CryptoRngCore) -> Odd<Uint<L>> {
let mut sieve = make_sieve(rng);
sieve.next().unwrap()
Odd::new(sieve.next().unwrap()).unwrap()
}

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::<{ nlimbs!(128) }>(&mut OsRng, 128))
b.iter(|| random_odd_uint::<Uint<{ nlimbs!(128) }>>(&mut OsRng, 128))
});

group.bench_function("(U128) creation", |b| {
b.iter_batched(
|| random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128),
|start| Sieve::new(&start, 128, false),
|| random_odd_uint::<Uint<{ nlimbs!(128) }>>(&mut OsRng, 128),
|start| Sieve::new(start.as_ref(), 128, false),
BatchSize::SmallInput,
)
});
Expand All @@ -57,13 +57,13 @@ fn bench_sieve(c: &mut Criterion) {
});

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

group.bench_function("(U1024) creation", |b| {
b.iter_batched(
|| random_odd_uint::<{ nlimbs!(1024) }>(&mut OsRng, 1024),
|start| Sieve::new(&start, 1024, false),
|| random_odd_uint::<Uint<{ nlimbs!(1024) }>>(&mut OsRng, 1024),
|start| Sieve::new(start.as_ref(), 1024, false),
BatchSize::SmallInput,
)
});
Expand All @@ -84,7 +84,7 @@ fn bench_miller_rabin(c: &mut Criterion) {

group.bench_function("(U128) creation", |b| {
b.iter_batched(
|| random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128),
|| random_odd_uint::<Uint<{ nlimbs!(128) }>>(&mut OsRng, 128),
|start| MillerRabin::new(&start),
BatchSize::SmallInput,
)
Expand All @@ -100,7 +100,7 @@ fn bench_miller_rabin(c: &mut Criterion) {

group.bench_function("(U1024) creation", |b| {
b.iter_batched(
|| random_odd_uint::<{ nlimbs!(1024) }>(&mut OsRng, 1024),
|| random_odd_uint::<Uint<{ nlimbs!(1024) }>>(&mut OsRng, 1024),
|start| MillerRabin::new(&start),
BatchSize::SmallInput,
)
Expand All @@ -122,7 +122,7 @@ fn bench_lucas(c: &mut Criterion) {
group.bench_function("(U128) Selfridge base, strong check (pre-sieved)", |b| {
b.iter_batched(
|| make_presieved_num::<{ nlimbs!(128) }>(&mut rng),
|n| lucas_test(&n, SelfridgeBase, LucasCheck::Strong),
|n| lucas_test(n.as_ref(), SelfridgeBase, LucasCheck::Strong),
BatchSize::SmallInput,
)
});
Expand All @@ -131,7 +131,7 @@ fn bench_lucas(c: &mut Criterion) {
group.bench_function("(U1024) Selfridge base, strong check (pre-sieved)", |b| {
b.iter_batched(
|| make_presieved_num::<{ nlimbs!(1024) }>(&mut rng),
|n| lucas_test(&n, SelfridgeBase, LucasCheck::Strong),
|n| lucas_test(n.as_ref(), SelfridgeBase, LucasCheck::Strong),
BatchSize::SmallInput,
)
});
Expand All @@ -140,7 +140,7 @@ fn bench_lucas(c: &mut Criterion) {
group.bench_function("(U1024) A* base, Lucas-V check (pre-sieved)", |b| {
b.iter_batched(
|| make_presieved_num::<{ nlimbs!(1024) }>(&mut rng),
|n| lucas_test(&n, AStarBase, LucasCheck::LucasV),
|n| lucas_test(n.as_ref(), AStarBase, LucasCheck::LucasV),
BatchSize::SmallInput,
)
});
Expand All @@ -151,7 +151,7 @@ fn bench_lucas(c: &mut Criterion) {
|b| {
b.iter_batched(
|| make_presieved_num::<{ nlimbs!(1024) }>(&mut rng),
|n| lucas_test(&n, BruteForceBase, LucasCheck::AlmostExtraStrong),
|n| lucas_test(n.as_ref(), BruteForceBase, LucasCheck::AlmostExtraStrong),
BatchSize::SmallInput,
)
},
Expand All @@ -161,7 +161,7 @@ fn bench_lucas(c: &mut Criterion) {
group.bench_function("(U1024) brute force base, extra strong (pre-sieved)", |b| {
b.iter_batched(
|| make_presieved_num::<{ nlimbs!(1024) }>(&mut rng),
|n| lucas_test(&n, BruteForceBase, LucasCheck::ExtraStrong),
|n| lucas_test(n.as_ref(), BruteForceBase, LucasCheck::ExtraStrong),
BatchSize::SmallInput,
)
});
Expand Down Expand Up @@ -192,39 +192,39 @@ fn bench_presets(c: &mut Criterion) {

group.bench_function("(U128) Prime test", |b| {
b.iter_batched(
|| random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128),
|num| is_prime_with_rng(&mut OsRng, &num),
|| random_odd_uint::<Uint<{ nlimbs!(128) }>>(&mut OsRng, 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::<{ nlimbs!(128) }>(&mut OsRng, 128),
|num| is_safe_prime_with_rng(&mut OsRng, &num),
|| random_odd_uint::<Uint<{ nlimbs!(128) }>>(&mut OsRng, 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::<{ nlimbs!(128) }>(&mut rng, None))
b.iter(|| generate_prime_with_rng::<Uint<{ nlimbs!(128) }>>(&mut rng, 128))
});

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

let mut rng = make_rng();
group.bench_function("(U128) Random safe prime", |b| {
b.iter(|| generate_safe_prime_with_rng::<{ nlimbs!(128) }>(&mut rng, None))
b.iter(|| generate_safe_prime_with_rng::<Uint<{ nlimbs!(128) }>>(&mut rng, 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::<{ nlimbs!(1024) }>(&mut rng, None))
b.iter(|| generate_safe_prime_with_rng::<Uint<{ nlimbs!(1024) }>>(&mut rng, 1024))
});

group.finish();
Expand All @@ -234,19 +234,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::<{ nlimbs!(128) }>(&mut rng, None))
b.iter(|| generate_safe_prime_with_rng::<Uint<{ nlimbs!(128) }>>(&mut rng, 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::<{ nlimbs!(256) }>(&mut rng, Some(128)))
b.iter(|| generate_safe_prime_with_rng::<Uint<{ nlimbs!(256) }>>(&mut rng, 128))
});

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

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

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

Expand Down
38 changes: 20 additions & 18 deletions src/hazmat/gcd.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
use crypto_bigint::{Limb, NonZero, Uint};
use crypto_bigint::{Integer, Limb, NonZero};

/// Calculates the greatest common divisor of `n` and `m`.
/// By definition, `gcd(0, m) == m`.
/// `n` must be non-zero.
pub(crate) fn gcd<const L: usize>(n: &Uint<L>, m: u32) -> u32 {
// This is an internal function, and it will never be called with `m = 0`.
// Allowing `m = 0` would require us to have the return type of `Uint<L>`
pub(crate) fn gcd_vartime<T: Integer>(n: &T, m: u32) -> u32 {
// Allowing `rhs = 0` would require us to have the return type of `T`
// (since `gcd(n, 0) = n`).
debug_assert!(m != 0);

// This we can check since it doesn't affect the return type,
// even though `n` will not be 0 either in the application.
if n == &Uint::<L>::ZERO {
if n.is_zero().into() {
return m;
}

// Normalize input: the resulting (a, b) are both small, a >= b, and b != 0.
let (mut a, mut b): (u32, u32) = if n.bits() > (u32::BITS as usize) {
// Normalize input: the resulting (a, b) are both small, `a >= b`, and `b != 0`.
let (mut a, mut b): (u32, u32) = if n.bits_vartime() > u32::BITS {
// `m` is non-zero, so we can unwrap.
let (_quo, n) = n.div_rem_limb(NonZero::new(Limb::from(m)).unwrap());
// `n` is a remainder of a division by `u32`, so it can be safely cast to `u32`.
let b: u32 = n.0.try_into().unwrap();
let r = n.rem_limb(NonZero::new(Limb::from(m)).unwrap());
// `r` is a remainder of a division by `u32`, so it can be safely cast to `u32`.
let b: u32 = r.0.try_into().unwrap();
(m, b)
} else {
// In this branch `n` is 32 bits or shorter,
// so we can safely take the first limb and cast it to u32.
let n: u32 = n.as_words()[0].try_into().unwrap();
let n: u32 = n.as_ref()[0].0.try_into().unwrap();
if n > m {
(n, m)
} else {
Expand All @@ -47,19 +46,22 @@ pub(crate) fn gcd<const L: usize>(n: &Uint<L>, m: u32) -> u32 {

#[cfg(test)]
mod tests {
use crypto_bigint::{Encoding, U128};
use crypto_bigint::U128;
use num_bigint::BigUint;
use num_integer::Integer;
use proptest::prelude::*;

use super::gcd;
use super::gcd_vartime;

#[test]
fn corner_cases() {
assert_eq!(gcd(&U128::from(0u64), 5), 5);
assert_eq!(gcd(&U128::from(1u64), 11 * 13 * 19), 1);
assert_eq!(gcd(&U128::from(7u64 * 11 * 13), 1), 1);
assert_eq!(gcd(&U128::from(7u64 * 11 * 13), 11 * 13 * 19), 11 * 13);
assert_eq!(gcd_vartime(&U128::from(0u64), 5), 5);
assert_eq!(gcd_vartime(&U128::from(1u64), 11 * 13 * 19), 1);
assert_eq!(gcd_vartime(&U128::from(7u64 * 11 * 13), 1), 1);
assert_eq!(
gcd_vartime(&U128::from(7u64 * 11 * 13), 11 * 13 * 19),
11 * 13
);
}

prop_compose! {
Expand All @@ -79,7 +81,7 @@ mod tests {
let n_bi = BigUint::from_bytes_be(n.to_be_bytes().as_ref());
let gcd_ref: u32 = n_bi.gcd(&m_bi).try_into().unwrap();

let gcd_test = gcd(&n, m);
let gcd_test = gcd_vartime(&n, m);
assert_eq!(gcd_test, gcd_ref);
}
}
Expand Down
Loading

0 comments on commit 3a45eda

Please sign in to comment.