diff --git a/Cargo.toml b/Cargo.toml index cdde552..d98615f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pumpkin" -version = "0.2.1" +version = "0.3.0" authors = ["Zach Dziura "] 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 = [] diff --git a/README.md b/README.md index d995c06..9d8956b 100644 --- a/README.md +++ b/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 +``` diff --git a/WAIVER.sigs b/WAIVER.sigs new file mode 100644 index 0000000..07dc0de --- /dev/null +++ b/WAIVER.sigs @@ -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----- diff --git a/examples/multiply.rs b/examples/multiply.rs index ccfef9c..96c0bac 100644 --- a/examples/multiply.rs +++ b/examples/multiply.rs @@ -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); } diff --git a/src/lib.rs b/src/lib.rs index aa39758..4f0a703 100644 --- a/src/lib.rs +++ b/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)); + } +} diff --git a/src/prime.rs b/src/prime.rs index d5d10d8..a3b4012 100644 --- a/src/prime.rs +++ b/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)