v0.3
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:
parent
248e2549dd
commit
53329646ab
6 changed files with 155 additions and 72 deletions
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pumpkin"
|
name = "pumpkin"
|
||||||
version = "0.2.1"
|
version = "0.3.0"
|
||||||
authors = ["Zach Dziura <zcdziura@gmail.com>"]
|
authors = ["Zach Dziura <zcdziura@gmail.com>"]
|
||||||
description = "A cryptographically secure prime number generator"
|
description = "A cryptographically secure prime number generator"
|
||||||
repository = "https://github.com/zcdziura/pumpkin"
|
repository = "https://github.com/zcdziura/pumpkin"
|
||||||
|
@ -16,6 +16,7 @@ rand = "0.3.*"
|
||||||
[lib]
|
[lib]
|
||||||
name = "pumpkin"
|
name = "pumpkin"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
doctest = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
unstable = []
|
unstable = []
|
||||||
|
|
47
README.md
47
README.md
|
@ -1,31 +1,28 @@
|
||||||
# Pumpkin
|
# Pumpkin
|
||||||
|
|
||||||
A cryptographically secure pseudo-random number generator for generating large
|
A random number generator for generating large prime numbers, suitable for cryptography.
|
||||||
prime.
|
|
||||||
|
|
||||||
## What's up with the name?
|
## What's up with the name?
|
||||||
|
|
||||||
Since I began writing this library around Halloween of 2015, I wanted to choose
|
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"
|
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
|
both begin with the letter 'p', I decided to use that.
|
||||||
really is to it!
|
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
`pumpkin` is a cryptographically-secure pseudo-random number generator, which is
|
`pumpkin` is a cryptographically-secure, random number generator, useful for
|
||||||
useful for generating large prime numbers for cryptography. In fact, `pumpkin`
|
generating large prime numbers (at least 512-bits long). On the back-end,
|
||||||
can ONLY be used to generate prime numbers. On the back-end, `pumpkin` uses the
|
`pumpkin` uses the wonderful [ramp](https://crates.io/crates/ramp) library for
|
||||||
wonderful [ramp](https://crates.io/crates/ramp) library for storing the large
|
storing the large numbers. `pumpkin` generates primes very quickly. In our
|
||||||
numbers. `pumpkin` generates numbers very quickly, so you can be sure that your
|
testing, primes were generated anywhere between 1s and 5s on average, though
|
||||||
program will be performative. In our testing, primes were generated anywhere
|
of course your mileage may vary.
|
||||||
between 1s and 5s on average, though of course your mileage may vary.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Add the following to your `Cargo.toml` file:
|
Add the following to your `Cargo.toml` file:
|
||||||
|
|
||||||
```
|
```
|
||||||
pumpkin = "0.2.0"
|
pumpkin = "0.3.*"
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that `pumpkin` requires the `nightly` Rust compiler.
|
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
|
You can also initialize your own `OsRng` and generate `Prime`s from that.
|
||||||
so will reduce some runtime overhead.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
extern crate pumpkin;
|
extern crate pumpkin;
|
||||||
|
@ -83,12 +79,10 @@ fn main() {
|
||||||
```
|
```
|
||||||
|
|
||||||
## Explanation
|
## Explanation
|
||||||
`Prime`s are generated much the same way that large primes are generated by
|
`Primes` are generated in much the same way as primes generated by `GnuPG`:
|
||||||
`GnuPG`:
|
|
||||||
|
|
||||||
1) Create a large candidate number of size based on the input given to the
|
1) Create a large candidate number of a given bit-length. All `Primes` must
|
||||||
`Prime::new()` method. All `Prime`s must be at least 2-bits long (thoug it
|
be at least 512-bits long.
|
||||||
wouldn't make much sense to be that small.
|
|
||||||
|
|
||||||
2) Divide the candidate number by the first 1,000 prime numbers.
|
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
|
4) Finally, run five iterations of the [Miller-Rabin Primality
|
||||||
Test](https://www.wikiwand.com/en/Miller%E2%80%93Rabin_primality_test).
|
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
|
`Primes` are seeded by `rand::OsRng`, which receives its entropy via the
|
||||||
operating system's own entropy source (such as `/dev/urandom`). Thus, because we
|
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
|
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
|
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.
|
iterations of the Miller-Rabin test to ensure primality.
|
||||||
|
|
||||||
`Prime`s are simple "newtype" structs; that is, it is a tuple-like struct
|
`Primes` 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
|
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
|
operators implemented, thus allowing you to do any operation that you would
|
||||||
require.
|
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
|
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
|
freely by all. I did so with this project, and it would mean a lot if you did
|
||||||
too!
|
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
19
WAIVER.sigs
Normal 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-----
|
|
@ -5,7 +5,8 @@ use pumpkin::Prime;
|
||||||
fn main() {
|
fn main() {
|
||||||
let p = Prime::new(2048);
|
let p = Prime::new(2048);
|
||||||
let q = Prime::new(2048);
|
let q = Prime::new(2048);
|
||||||
let s = p * q;
|
|
||||||
|
|
||||||
println!("{}", s);
|
println!("{}", p);
|
||||||
|
println!("{}", q);
|
||||||
|
println!("\n{}", p * q);
|
||||||
}
|
}
|
||||||
|
|
51
src/lib.rs
51
src/lib.rs
|
@ -1,6 +1,4 @@
|
||||||
#![feature(augmented_assignments)]
|
#![feature(test)]
|
||||||
#![feature(core)]
|
|
||||||
#![feature(custom_derive)]
|
|
||||||
|
|
||||||
/// # The Pumpkin Prime Number Generator
|
/// # The Pumpkin Prime Number Generator
|
||||||
///
|
///
|
||||||
|
@ -10,6 +8,9 @@
|
||||||
/// secure source of entrophy and are verified using three different primality
|
/// secure source of entrophy and are verified using three different primality
|
||||||
/// tests.
|
/// tests.
|
||||||
///
|
///
|
||||||
|
/// Primes have to be AT LEAST 512-bits long. Any lower bit-length will
|
||||||
|
/// immediately fail.
|
||||||
|
///
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -27,11 +28,53 @@
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
extern crate core;
|
|
||||||
#[macro_use] extern crate custom_derive;
|
#[macro_use] extern crate custom_derive;
|
||||||
#[macro_use] extern crate newtype_derive;
|
#[macro_use] extern crate newtype_derive;
|
||||||
extern crate ramp;
|
extern crate ramp;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
mod prime;
|
mod prime;
|
||||||
pub use prime::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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
102
src/prime.rs
102
src/prime.rs
|
@ -1,5 +1,3 @@
|
||||||
use core::ops::{Add, BitAnd, Mul, Rem, Shr, Sub};
|
|
||||||
|
|
||||||
use ramp::{Int, RandomInt};
|
use ramp::{Int, RandomInt};
|
||||||
|
|
||||||
use rand::{OsRng, thread_rng};
|
use rand::{OsRng, thread_rng};
|
||||||
|
@ -108,10 +106,12 @@ custom_derive! {
|
||||||
impl Prime {
|
impl Prime {
|
||||||
/// Constructs a new `Prime` with a size of `bit_length` bits.
|
/// 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
|
/// This will initialize an `OsRng` instance and call the
|
||||||
/// to only generate a 2-bit random number, them's the rules.
|
/// `Prime::from_rng()` method.
|
||||||
|
///
|
||||||
|
/// Note: the `bit_length` MUST be at least 512-bits.
|
||||||
pub fn new(bit_length: usize) -> Prime {
|
pub fn new(bit_length: usize) -> Prime {
|
||||||
debug_assert!(bit_length >= 2);
|
debug_assert!(bit_length >= 512);
|
||||||
let mut rngesus = match OsRng::new() {
|
let mut rngesus = match OsRng::new() {
|
||||||
Ok(rng) => rng,
|
Ok(rng) => rng,
|
||||||
Err(reason) => panic!("Error initializing RNG: {}", reason)
|
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
|
/// from an already-created `OsRng`. Not that you can **ONLY** use an
|
||||||
/// `OsRng`, as it uses the operating system's secure source of entropy.
|
/// `OsRng`, as it uses the operating system's secure source of entropy.
|
||||||
pub fn from_rng(bit_length: usize, rngesus: &mut OsRng) -> Prime {
|
pub fn from_rng(bit_length: usize, rngesus: &mut OsRng) -> Prime {
|
||||||
debug_assert!(bit_length >= 2);
|
debug_assert!(bit_length >= 512);
|
||||||
let one = Int::one();
|
let mut candidate: Int;
|
||||||
let two = &one + &one;
|
|
||||||
let mut candidate = rngesus.gen_uint(bit_length);
|
|
||||||
|
|
||||||
// Make sure candidate is odd before continuing...
|
// In order to remove as much bias from the system as possible, test
|
||||||
if &candidate & (&one) == 0 {
|
// 500 potential candidates at a time before re-seeding the candidate
|
||||||
candidate += &one;
|
// with a new random number.
|
||||||
}
|
loop {
|
||||||
while !is_prime(&candidate) {
|
let mut counter = 0;
|
||||||
candidate += &two;
|
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)
|
Prime(candidate)
|
||||||
|
@ -152,13 +177,12 @@ fn is_prime(candidate: &Int) -> bool {
|
||||||
// First, iterate through the array of small primes and divide the
|
// First, iterate through the array of small primes and divide the
|
||||||
// candidate. If the candidate divides any of them, then we know the number
|
// 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.
|
// is a multiple of that prime; that is, the candidate is composite.
|
||||||
let zero = Int::zero();
|
|
||||||
|
|
||||||
for p in SMALL_PRIMES.into_iter() {
|
for p in SMALL_PRIMES.into_iter() {
|
||||||
let prime: Int = Int::from(*p);
|
let prime: Int = Int::from(*p);
|
||||||
let (_, r) = candidate.divmod(&prime);
|
let (_, r) = candidate.divmod(&prime);
|
||||||
|
|
||||||
if r != zero {
|
if r != 0_usize {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -181,12 +205,11 @@ fn is_prime(candidate: &Int) -> bool {
|
||||||
fn fermat(candidate: &Int) -> bool {
|
fn fermat(candidate: &Int) -> bool {
|
||||||
// Perform Fermat's little theorem on the candidate to determine probable
|
// Perform Fermat's little theorem on the candidate to determine probable
|
||||||
// primality.
|
// primality.
|
||||||
let one = Int::one();
|
let random = thread_rng().gen_int_range(&Int::one(), candidate);
|
||||||
let random = thread_rng().gen_int_range(&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
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -196,21 +219,19 @@ fn fermat(candidate: &Int) -> bool {
|
||||||
fn miller_rabin(candidate: &Int) -> bool {
|
fn miller_rabin(candidate: &Int) -> bool {
|
||||||
// Perform five iterations of the Miller-Rabin test on the candidate.
|
// Perform five iterations of the Miller-Rabin test on the candidate.
|
||||||
let (s, d) = rewrite(candidate);
|
let (s, d) = rewrite(candidate);
|
||||||
let one = Int::one();
|
|
||||||
let two = (&one).add(&one);
|
|
||||||
|
|
||||||
for _ in 0..5 {
|
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);
|
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;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
for _ in one.clone() .. s.sub(&one) {
|
for _ in Int::one() .. s - 1_usize {
|
||||||
x = mod_exp(&x, &two, candidate);
|
x = mod_exp(&x, &Int::from(2), candidate);
|
||||||
if x == one.clone() {
|
if x == 1_usize {
|
||||||
return false;
|
return false;
|
||||||
} else if x == (candidate.sub(&one)) {
|
} else if x == candidate - 1_usize {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,32 +243,29 @@ fn miller_rabin(candidate: &Int) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mod_exp(base: &Int, exponent: &Int, modulus: &Int) -> Int {
|
fn mod_exp(base: &Int, exponent: &Int, modulus: &Int) -> Int {
|
||||||
let (zero, one) = (Int::zero(), Int::one());
|
let mut result = Int::one();
|
||||||
let mut result = one.clone();
|
|
||||||
let mut base = base.clone();
|
let mut base = base.clone();
|
||||||
let mut exponent = exponent.clone();
|
let mut exponent = exponent.clone();
|
||||||
|
|
||||||
while &exponent > &zero {
|
while exponent > 0_usize {
|
||||||
if (&exponent).bitand(&one) == (one.clone()) {
|
if &exponent & 1_usize == 1_usize {
|
||||||
result = ((&result).mul(&base)).rem(modulus);
|
result = (&base * result) % modulus;
|
||||||
}
|
}
|
||||||
|
|
||||||
base = ((&base).mul(&base)).rem(modulus);
|
base = (&base.pow(2)) % modulus;
|
||||||
exponent = exponent.clone().shr(1);
|
exponent = &exponent >> 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rewrite(candidate: &Int) -> (Int, Int) {
|
fn rewrite(candidate: &Int) -> (Int, Int) {
|
||||||
let one = Int::one();
|
let mut d = candidate - 1_usize;
|
||||||
|
|
||||||
let mut d = candidate.sub(&one);
|
|
||||||
let mut s = Int::zero();
|
let mut s = Int::zero();
|
||||||
|
|
||||||
while (&d).bitand(&one) == one {
|
while &d & 1 == 1_usize {
|
||||||
d = d.clone().shr(1);
|
d = &d >> 1_usize;
|
||||||
s = (&s).add(&one);
|
s = &s + 1_usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
(s, d)
|
(s, d)
|
||||||
|
|
Loading…
Add table
Reference in a new issue