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]
|
||||
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 = []
|
||||
|
|
47
README.md
47
README.md
|
@ -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
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() {
|
||||
let p = 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(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));
|
||||
}
|
||||
}
|
||||
|
|
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 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)
|
||||
|
|
Loading…
Add table
Reference in a new issue