Implemented custom Error and Result types to be returned

Like a good library ought to be, Pumpkin now returns actual Errors, rather
than panicking.
This commit is contained in:
Zach Dziura 2016-06-08 23:05:06 -04:00
parent b6134a6cfd
commit ec63237e41
5 changed files with 115 additions and 84 deletions

View file

@ -1,6 +1,8 @@
use ramp::{Int, RandomInt};
use rand::{OsRng, thread_rng};
use error::{Error, Result};
static SMALL_PRIMES: [u32; 999] = [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127,
131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191,
@ -101,45 +103,46 @@ static SMALL_PRIMES: [u32; 999] = [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
/// Constructs a new prime number with the size of `bit_length` bits, sourced
/// from an already-initialized `OsRng`.
pub fn gen_prime(bit_length: usize, rngesus: &mut OsRng) -> Int {
debug_assert!(bit_length >= 512);
let mut candidate: Int;
pub fn gen_prime(bit_length: usize, rngesus: &mut OsRng) -> Result {
if bit_length < 512 {
Err(Error::BitLength(bit_length))
} else {
let mut candidate: Int;
// 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);
// 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.
candidate.set_bit(bit_length as u32, true);
candidate.set_bit((bit_length-1) as u32, true);
// 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.
candidate.set_bit(bit_length as u32, true);
candidate.set_bit((bit_length-1) as u32, true);
// 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.set_bit(1, true);
// 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.set_bit(1, true);
// Now run through the actual primality check!
while !is_prime(&candidate) {
candidate += 2_usize;
counter += 1;
// Now run through the actual primality check!
while !is_prime(&candidate) {
candidate += 2_usize;
counter += 1;
if counter > 499 {
found_prime = false;
break;
if counter > 499 {
found_prime = false;
break;
}
}
if found_prime {
return Ok(candidate);
}
}
if found_prime {
break;
}
}
candidate
}
/// Runs the following three tests on a given `candidate` to determine

46
src/error.rs Normal file
View file

@ -0,0 +1,46 @@
use ramp::Int;
use std::{error, fmt, io, result};
pub type Result = result::Result<Int, Error>;
#[derive(Debug)]
pub enum Error {
OsRngInitialization(io::Error),
BitLength(usize)
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::OsRngInitialization(ref err) => {
write!(f,
"Error initializing the random number generator: {}",
err
)
},
Error::BitLength(length) => {
write!(f,
"The given bit length is too small; must be at least 512: {}",
length
)
}
}
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::OsRngInitialization(ref err) => err.description(),
Error::BitLength(_) => {
"The given bit length was less than 512"
}
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::OsRngInitialization(err)
}
}

View file

@ -54,6 +54,7 @@ extern crate rand;
extern crate test;
mod common;
mod error;
pub mod prime;
pub mod safe_prime;

View file

@ -1,9 +1,9 @@
//! Generates cryptographically secure prime numbers.
use ramp::Int;
use rand::OsRng;
pub use common::gen_prime as from_rng;
use error::{Error, Result};
/// Constructs a new prime number with a size of `bit_length` bits.
///
@ -11,40 +11,22 @@ pub use common::gen_prime as from_rng;
/// `from_rng()` function.
///
/// Note: the `bit_length` MUST be at least 512-bits.
pub fn new(bit_length: usize) -> Int {
assert!(bit_length >= 512);
let mut rngesus = match OsRng::new() {
Ok(rng) => rng,
Err(reason) => panic!("Error initializing RNG: {}", reason),
};
from_rng(bit_length, &mut rngesus)
pub fn new(bit_length: usize) -> Result {
if bit_length < 512 {
Err(Error::BitLength(bit_length))
} else {
let mut rngesus = try!(OsRng::new());
Ok(try!(from_rng(bit_length, &mut rngesus)))
}
}
#[cfg(test)]
mod tests {
use ramp::Int;
use super::{fermat, miller_rabin};
#[test]
fn test_fermat_pass() {
assert!(fermat(&Int::from(7919)));
}
use super::{new, from_rng};
#[test]
#[should_panic]
fn test_fermat_fail() {
assert!(fermat(&Int::from(7920)));
}
#[test]
fn test_miller_rabin_pass() {
assert!(miller_rabin(&Int::from(7919), 5));
}
#[test]
#[should_panic]
fn test_miller_rabin_fail() {
assert!(miller_rabin(&Int::from(7920), 5));
fn test_prime_bad_bit_length() {
new(511);
}
}

View file

@ -4,6 +4,7 @@ use ramp::Int;
use rand::OsRng;
pub use common::{gen_prime, is_prime};
use error::{Error, Result};
/// Constructs a new `SafePrime` with a size of `bit_length` bits.
///
@ -11,37 +12,35 @@ pub use common::{gen_prime, is_prime};
/// `SafePrime::from_rng()` method.
///
/// Note: the `bit_length` MUST be at least 512-bits.
pub fn new(bit_length: usize) -> Int {
debug_assert!(bit_length >= 512);
let mut rngesus = match OsRng::new() {
Ok(rng) => rng,
Err(reason) => panic!("Error initializing RNG: {}", reason),
};
from_rng(bit_length, &mut rngesus)
pub fn new(bit_length: usize) -> Result {
if bit_length < 512 {
Err(Error::BitLength(bit_length))
} else {
let mut rngesus = try!(OsRng::new());
Ok(try!(from_rng(bit_length, &mut rngesus)))
}
}
/// Constructs a new `SafePrime` with the size of `bit_length` bits, sourced
/// from an already-initialized `OsRng`.
pub fn from_rng(bit_length: usize, mut rngesus: &mut OsRng) -> Int {
debug_assert!(bit_length >= 512);
let mut candidate: Int;
pub fn from_rng(bit_length: usize, mut rngesus: &mut OsRng) -> Result {
if bit_length < 512 {
Err(Error::BitLength(bit_length))
} else {
let mut candidate: Int;
// Circumvent uninitialized warning (technically valid but compiler
// cannot determine that `clone_from` will fill the value.
let mut candidate_p: Int = Int::zero();
loop {
candidate = try!(gen_prime(bit_length, &mut rngesus));
loop {
candidate = gen_prime(bit_length, &mut rngesus);
let mut candidate_p = (&candidate).clone();
candidate_p -= 1_usize;
candidate_p /= 2_usize;
candidate_p.clone_from(&candidate);
candidate_p -= 1_usize;
candidate_p /= 2_usize;
if is_prime(&candidate_p) {
break;
if is_prime(&candidate_p) {
break;
}
}
}
candidate
Ok(candidate)
}
}