Skip to content

Commit

Permalink
Merge pull request #16 from jellevos/openssl_bench
Browse files Browse the repository at this point in the history
Compare safe prime generation against OpenSSL and use their sieving technique
  • Loading branch information
jellevos authored Feb 28, 2022
2 parents ef082ff + e07f86e commit f5fd9db
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 16 deletions.
67 changes: 67 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions scicrypt-numbertheory/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ primal = "0.3"
criterion = "0.3"
glass_pumpkin = "1.0.0"
rand = "0.8"
openssl = "0.10"

[package.metadata.docs.rs]
rustdoc-args = [ "--html-in-header", "katex-header.html" ]
Expand Down
14 changes: 14 additions & 0 deletions scicrypt-numbertheory/benches/prime_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use glass_pumpkin::safe_prime::from_rng;
use openssl::bn::BigNum;
use rand::rngs;
use rand_core::OsRng;
use scicrypt_numbertheory::gen_safe_prime;
Expand Down Expand Up @@ -33,6 +34,19 @@ pub fn safe_prime_benchmark(c: &mut Criterion) {
b.iter(|| from_rng(black_box(bits), &mut rng));
},
);

// Benchmark `openssl`'s safe prime generation
let mut rng = rand::rngs::OsRng;
group.bench_with_input(
BenchmarkId::new("openssl", bit_length),
bit_length,
|b, &bits| {
b.iter(|| {
let mut big = BigNum::new().unwrap();
big.generate_prime(bits as i32, true, None, None);
});
},
);
}

group.finish();
Expand Down
91 changes: 75 additions & 16 deletions scicrypt-numbertheory/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
//! Number theoretic functions, particularly suited for cryptography. Functions include extremely
//! fast (safe) prime generation.
mod primes;

use crate::primes::FIRST_PRIMES;
use rug::integer::IsPrime;
use rug::Integer;
use scicrypt_traits::randomness::SecureRng;
Expand All @@ -18,12 +21,40 @@ pub fn gen_prime<R: rand_core::RngCore + rand_core::CryptoRng>(
bit_length: u32,
rng: &mut SecureRng<R>,
) -> Integer {
loop {
'outer: loop {
let mut candidate = Integer::from(Integer::random_bits(bit_length, &mut rng.rug_rng()));

let set_bits = (Integer::from(1) << (bit_length - 1)) + Integer::from(1);
candidate |= set_bits;

candidate.set_bit(bit_length - 1, true);
candidate.set_bit(0, true);

// A heuristic that closely follows OpenSSL (https://github.com/openssl/openssl/blob/4cedf30e995f9789cf6bb103e248d33285a84067/crypto/bn/bn_prime.c)
let prime_count: usize = bit_length as usize / 3;
let mods: Vec<u32> = FIRST_PRIMES[..prime_count]
.iter()
.map(|p| candidate.mod_u(*p))
.collect();

let mut delta = 0;
let max_delta = u32::MAX - FIRST_PRIMES.last().unwrap();
candidate = 'sieve: loop {
for i in 1..prime_count {
if (mods[i] + delta) % FIRST_PRIMES[i] == 0 {
// For candidate x and prime p, if x % p = 0 then x is not prime
// So, we go to the next odd number and try again
delta += 2;

if delta > max_delta {
continue 'outer;
}

continue 'sieve;
}
}

// If we have passed all prime_count first primes, then we are fairly certain this is a prime!
break candidate + delta;
};

// Ensure that we have a prime with a stronger primality test
if candidate.is_probably_prime(REPS) != IsPrime::No {
return candidate;
}
Expand All @@ -36,18 +67,46 @@ pub fn gen_safe_prime<R: rand_core::RngCore + rand_core::CryptoRng>(
bit_length: u32,
rng: &mut SecureRng<R>,
) -> Integer {
loop {
let mut candidate = gen_prime(bit_length - 1, rng);

candidate <<= 1;
candidate |= Integer::from(1);

if !candidate.mod_u(3) == 2 {
continue;
}

'outer: loop {
let mut candidate = Integer::from(Integer::random_bits(bit_length, &mut rng.rug_rng()));
candidate.set_bit(bit_length - 1, true);
candidate.set_bit(0, true);

// A heuristic that closely follows OpenSSL (https://github.com/openssl/openssl/blob/4cedf30e995f9789cf6bb103e248d33285a84067/crypto/bn/bn_prime.c)
let prime_count: usize = bit_length as usize / 3;
let mods: Vec<u32> = FIRST_PRIMES[..prime_count]
.iter()
.map(|p| candidate.mod_u(*p))
.collect();

let mut delta = 0;
let max_delta = u32::MAX - FIRST_PRIMES[prime_count - 1];
candidate = 'sieve: loop {
for i in 1..prime_count {
if (mods[i] + delta) % FIRST_PRIMES[i] <= 1 {
// For candidate x and prime p, if x % p = 0 then x is not prime
// So, we go to the next odd number and try again
delta += 4;

if delta > max_delta {
continue 'outer;
}

continue 'sieve;
}
}

// If we have passed all prime_count first primes, then we are fairly certain this is a prime!
break candidate + delta;
};

// Ensure that we have a prime with a stronger primality test
if candidate.is_probably_prime(REPS) != IsPrime::No {
return candidate;
// Ensure that p for 2p = 1 is also a prime with the stronger primality test
let candidate_reduced = Integer::from(&candidate >> 1);
if candidate_reduced.is_probably_prime(REPS) != IsPrime::No {
return candidate;
}
}
}
}
Expand Down
Loading

0 comments on commit f5fd9db

Please sign in to comment.