Increased required Prime bit-length to at least 512-bits. Also updated the
candidate checking process to increment the candidate by 2 only 500 times
before the candidate is reseeded from the OS.
This commit is contained in:
Zach Dziura 2016-05-05 13:05:22 -04:00
parent 248e2549dd
commit 53329646ab
6 changed files with 155 additions and 72 deletions

View file

@ -1,6 +1,6 @@
[package]
name = "pumpkin"
version = "0.2.1"
version = "0.3.0"
authors = ["Zach Dziura <zcdziura@gmail.com>"]
description = "A cryptographically secure prime number generator"
repository = "https://github.com/zcdziura/pumpkin"
@ -16,6 +16,7 @@ rand = "0.3.*"
[lib]
name = "pumpkin"
path = "src/lib.rs"
doctest = false
[features]
unstable = []

View file

@ -1,31 +1,28 @@
# Pumpkin
A cryptographically secure pseudo-random number generator for generating large
prime.
A random number generator for generating large prime numbers, suitable for cryptography.
## What's up with the name?
Since I began writing this library around Halloween of 2015, I wanted to choose
a name that was vaguely related to the holiday. Because "pumpkin" and "prime"
both begin with the letter 'p', I decided to use that. And that's all there
really is to it!
both begin with the letter 'p', I decided to use that.
## Purpose
`pumpkin` is a cryptographically-secure pseudo-random number generator, which is
useful for generating large prime numbers for cryptography. In fact, `pumpkin`
can ONLY be used to generate prime numbers. On the back-end, `pumpkin` uses the
wonderful [ramp](https://crates.io/crates/ramp) library for storing the large
numbers. `pumpkin` generates numbers very quickly, so you can be sure that your
program will be performative. In our testing, primes were generated anywhere
between 1s and 5s on average, though of course your mileage may vary.
`pumpkin` is a cryptographically-secure, random number generator, useful for
generating large prime numbers (at least 512-bits long). On the back-end,
`pumpkin` uses the wonderful [ramp](https://crates.io/crates/ramp) library for
storing the large numbers. `pumpkin` generates primes very quickly. In our
testing, primes were generated anywhere between 1s and 5s on average, though
of course your mileage may vary.
## Installation
Add the following to your `Cargo.toml` file:
```
pumpkin = "0.2.0"
pumpkin = "0.3.*"
```
Note that `pumpkin` requires the `nightly` Rust compiler.
@ -51,8 +48,7 @@ fn main() {
}
```
You can also initialize your own `OsRng` and generate `Prime`s from that. Doing
so will reduce some runtime overhead.
You can also initialize your own `OsRng` and generate `Prime`s from that.
```rust
extern crate pumpkin;
@ -83,12 +79,10 @@ fn main() {
```
## Explanation
`Prime`s are generated much the same way that large primes are generated by
`GnuPG`:
`Primes` are generated in much the same way as primes generated by `GnuPG`:
1) Create a large candidate number of size based on the input given to the
`Prime::new()` method. All `Prime`s must be at least 2-bits long (thoug it
wouldn't make much sense to be that small.
1) Create a large candidate number of a given bit-length. All `Primes` must
be at least 512-bits long.
2) Divide the candidate number by the first 1,000 prime numbers.
@ -98,14 +92,14 @@ Theorem](https://www.wikiwand.com/en/Fermat's_little_theorem).
4) Finally, run five iterations of the [Miller-Rabin Primality
Test](https://www.wikiwand.com/en/Miller%E2%80%93Rabin_primality_test).
`Prime`s are seeded by `rand::OsRng`, which receives its entropy via the
operating system's own entropy source (such as `/dev/urandom`). Thus, because we
`Primes` are seeded by `rand::OsRng`, which receives its entropy via the
operating system's entropy source (such as `/dev/urandom`). Thus, because we
can be confident that the generated candidate number is truly random (or as
close to truly random as the user can hope), we don't need to do more than five
iterations of the Miller-Rabin test to ensure primality.
`Prime`s are simple "newtype" structs; that is, it is a tuple-like struct
surrounding an `Int` type. `Prime`s have all of the basic algebraic and logical
`Primes` are simple "newtype" structs; that is, it is a tuple-like struct
surrounding `ramp's` `Int` type. `Primes` have all of the basic algebraic and logical
operators implemented, thus allowing you to do any operation that you would
require.
@ -117,3 +111,10 @@ file with your cryptographic digital signature (as allowed by your country's
laws). Doing so will release your changes back into the public domain to be used
freely by all. I did so with this project, and it would mean a lot if you did
too!
To sign the `WAIVER`, execute the following commands:
```
$ gpg -sba -u $YOUR_SIGNING_KEY WAIVER
$ cat WAIVER.asc >> WAIVER.sigs && rm WAIVER.asc
```

19
WAIVER.sigs Normal file
View file

@ -0,0 +1,19 @@
@zcdziura's signature
keyserver: sks-keyservers.net
fingerprint: 0xC936A812AF607321
-----BEGIN PGP SIGNATURE-----
iQIcBAABCgAGBQJXK3Z2AAoJENNfKjTk1jvtfTQP/0PnIWai7tpg+8YfTf+FTBjI
hH8X1KquM6Kt58m+9bSpJAyaA42MYRmPd0UlyKq0UfR9EV9Dyme72cQCUQlNEkuh
tXp1mqQ9wSTy7SGy43vGje2F76WPjGYR9zwrS/+EN771LbQEaYvsI75uIc5ynWnN
op6w9XA9jLDdLPiXb+lli/7uAiSOZgLBqj6M8nTs6ii94kkHgEgZyHpyYe8kJNRq
SUbldrJmH//vKbS7HSvgkPJ/ok2c04ZmZ7B3sa0997r36ubwM/SscqWTvZkGy3uj
PCJh4m+gcHLiNasWucSL/y2oym7Px7DzmCibvT2pA0wa7zcjhIRc5ksnSDXfYafe
XrTeuy/6cP5Trep9jzDf4wnAWhRzLZzy0LGHGYsSd5//6n3qFn2XFNyy769qYLu6
8anCbpk40XNDLyEDlHB+lFCCe0KG/K5sg49CoxTsdLoiwyKqGpC7cjEQ15RoFtRQ
JEvVJKs36fcAYalkfa4+OWt5TeoTAm/kg1erz6SwnNiL9n4MQ+0hLjm0WtpEpJqG
V2XYi7tSIpfVFi+lvtxfoOGL+9nZpd6s6JarhdBPFFFang70I9wpXwVZDbSML89q
KYnx3XVTxBhV6sumlJQ40wBGAisXLfJ4VpS1uj5JIx01d36Lgv6z80i6CiHTnIvz
HFbm9XcS6pooPZOOxTrp
=LwYG
-----END PGP SIGNATURE-----

View file

@ -5,7 +5,8 @@ use pumpkin::Prime;
fn main() {
let p = Prime::new(2048);
let q = Prime::new(2048);
let s = p * q;
println!("{}", s);
println!("{}", p);
println!("{}", q);
println!("\n{}", p * q);
}

View file

@ -1,6 +1,4 @@
#![feature(augmented_assignments)]
#![feature(core)]
#![feature(custom_derive)]
#![feature(test)]
/// # The Pumpkin Prime Number Generator
///
@ -10,6 +8,9 @@
/// secure source of entrophy and are verified using three different primality
/// tests.
///
/// Primes have to be AT LEAST 512-bits long. Any lower bit-length will
/// immediately fail.
///
/// ## Examples
///
/// ```
@ -27,11 +28,53 @@
/// }
/// ```
extern crate core;
#[macro_use] extern crate custom_derive;
#[macro_use] extern crate newtype_derive;
extern crate ramp;
extern crate rand;
extern crate test;
mod prime;
pub use prime::Prime;
#[cfg(test)]
mod tests {
use rand::OsRng;
use super::*;
use test::Bencher;
#[test]
#[should_panic]
fn test_new_small_prime() {
Prime::new(511);
}
#[test]
#[should_panic]
fn test_new_small_prime_from_rng() {
let mut rngesus = match OsRng::new() {
Ok(rng) => rng,
Err(reason) => panic!("An error occurred when initializing the RNG: {}", reason)
};
Prime::from_rng(511, &mut rngesus);
}
#[bench]
#[ignore]
fn bench_generate_512_bit_prime(b: &mut Bencher) {
b.iter(|| Prime::new(512));
}
#[bench]
#[ignore]
fn bench_generate_1024_bit_prime(b: &mut Bencher) {
b.iter(|| Prime::new(1024));
}
#[bench]
#[ignore]
fn bench_generate_2048_bit_prime(b: &mut Bencher) {
b.iter(|| Prime::new(2048));
}
}

View file

@ -1,5 +1,3 @@
use core::ops::{Add, BitAnd, Mul, Rem, Shr, Sub};
use ramp::{Int, RandomInt};
use rand::{OsRng, thread_rng};
@ -108,10 +106,12 @@ custom_derive! {
impl Prime {
/// Constructs a new `Prime` with a size of `bit_length` bits.
///
/// The `bit_length` must be at least 2. While it doesn't make much sense
/// to only generate a 2-bit random number, them's the rules.
/// This will initialize an `OsRng` instance and call the
/// `Prime::from_rng()` method.
///
/// Note: the `bit_length` MUST be at least 512-bits.
pub fn new(bit_length: usize) -> Prime {
debug_assert!(bit_length >= 2);
debug_assert!(bit_length >= 512);
let mut rngesus = match OsRng::new() {
Ok(rng) => rng,
Err(reason) => panic!("Error initializing RNG: {}", reason)
@ -124,17 +124,42 @@ impl Prime {
/// from an already-created `OsRng`. Not that you can **ONLY** use an
/// `OsRng`, as it uses the operating system's secure source of entropy.
pub fn from_rng(bit_length: usize, rngesus: &mut OsRng) -> Prime {
debug_assert!(bit_length >= 2);
let one = Int::one();
let two = &one + &one;
let mut candidate = rngesus.gen_uint(bit_length);
debug_assert!(bit_length >= 512);
let mut candidate: Int;
// Make sure candidate is odd before continuing...
if &candidate & (&one) == 0 {
candidate += &one;
}
while !is_prime(&candidate) {
candidate += &two;
// In order to remove as much bias from the system as possible, test
// 500 potential candidates at a time before re-seeding the candidate
// with a new random number.
loop {
let mut counter = 0;
let mut found_prime = true;
candidate = rngesus.gen_uint(bit_length);
// We first want to make sure that the candidate is in the appropriate
// size range before continuing. This can easily be done by setting the
// two most significant bits of the candidate number to 1.
// Note that Ints are stored in most-significant-bit format, so we
// will right-shift in order to set the two most significant bits.
candidate = &candidate | (Int::from(3) >> (bit_length - 2));
// Next, flip the least significant bit to 1, to make sure the candidate
// is odd (no sense in testing primality on an even number, after all).
candidate = &candidate | 1_usize;
// Now run through the actual primality check!
while !is_prime(&candidate) {
candidate += 2_usize;
counter += 1;
if counter > 499 {
found_prime = false;
break;
}
}
if found_prime {
break;
}
}
Prime(candidate)
@ -152,13 +177,12 @@ fn is_prime(candidate: &Int) -> bool {
// First, iterate through the array of small primes and divide the
// candidate. If the candidate divides any of them, then we know the number
// is a multiple of that prime; that is, the candidate is composite.
let zero = Int::zero();
for p in SMALL_PRIMES.into_iter() {
let prime: Int = Int::from(*p);
let (_, r) = candidate.divmod(&prime);
if r != zero {
if r != 0_usize {
continue;
} else {
return false;
@ -181,12 +205,11 @@ fn is_prime(candidate: &Int) -> bool {
fn fermat(candidate: &Int) -> bool {
// Perform Fermat's little theorem on the candidate to determine probable
// primality.
let one = Int::one();
let random = thread_rng().gen_int_range(&one, candidate);
let random = thread_rng().gen_int_range(&Int::one(), candidate);
let result = mod_exp(&random, &candidate.sub(&one), candidate);
let result = mod_exp(&random, &(candidate - 1_usize), candidate);
if result == one {
if result == 1_usize {
true
} else {
false
@ -196,21 +219,19 @@ fn fermat(candidate: &Int) -> bool {
fn miller_rabin(candidate: &Int) -> bool {
// Perform five iterations of the Miller-Rabin test on the candidate.
let (s, d) = rewrite(candidate);
let one = Int::one();
let two = (&one).add(&one);
for _ in 0..5 {
let basis = thread_rng().gen_int_range(&two, candidate);
let basis = thread_rng().gen_int_range(&Int::from(2), candidate);
let mut x = mod_exp(&basis, &d, candidate);
if x.eq(&one) || x.eq(&(candidate.sub(&one))) {
if x == 1_usize || x == (candidate - 1_usize) {
continue;
} else {
for _ in one.clone() .. s.sub(&one) {
x = mod_exp(&x, &two, candidate);
if x == one.clone() {
for _ in Int::one() .. s - 1_usize {
x = mod_exp(&x, &Int::from(2), candidate);
if x == 1_usize {
return false;
} else if x == (candidate.sub(&one)) {
} else if x == candidate - 1_usize {
break;
}
}
@ -222,32 +243,29 @@ fn miller_rabin(candidate: &Int) -> bool {
}
fn mod_exp(base: &Int, exponent: &Int, modulus: &Int) -> Int {
let (zero, one) = (Int::zero(), Int::one());
let mut result = one.clone();
let mut result = Int::one();
let mut base = base.clone();
let mut exponent = exponent.clone();
while &exponent > &zero {
if (&exponent).bitand(&one) == (one.clone()) {
result = ((&result).mul(&base)).rem(modulus);
while exponent > 0_usize {
if &exponent & 1_usize == 1_usize {
result = (&base * result) % modulus;
}
base = ((&base).mul(&base)).rem(modulus);
exponent = exponent.clone().shr(1);
base = (&base.pow(2)) % modulus;
exponent = &exponent >> 1;
}
result
}
fn rewrite(candidate: &Int) -> (Int, Int) {
let one = Int::one();
let mut d = candidate.sub(&one);
let mut d = candidate - 1_usize;
let mut s = Int::zero();
while (&d).bitand(&one) == one {
d = d.clone().shr(1);
s = (&s).add(&one);
while &d & 1 == 1_usize {
d = &d >> 1_usize;
s = &s + 1_usize;
}
(s, d)