Start on error + BN refactor

This commit is contained in:
Steven Fackler 2016-10-16 15:54:09 -07:00
parent fdb4131064
commit 8f89f0bfa9
4 changed files with 210 additions and 215 deletions

View File

@ -2,9 +2,10 @@ use libc::c_long;
use std::{ptr, fmt};
use std::marker::PhantomData;
use std::ops::Deref;
use bio::MemBio;
use ffi;
use {cvt, cvt_p};
use bio::MemBio;
use error::ErrorStack;
/// Corresponds to the ASN.1 structure Time defined in RFC5280
@ -20,7 +21,7 @@ impl Asn1Time {
ffi::init();
unsafe {
let handle = try_ssl_null!(ffi::X509_gmtime_adj(ptr::null_mut(), period));
let handle = try!(cvt_p(ffi::X509_gmtime_adj(ptr::null_mut(), period)));
Ok(Asn1Time::from_ptr(handle))
}
}
@ -58,7 +59,7 @@ impl<'a> fmt::Display for Asn1TimeRef<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mem_bio = try!(MemBio::new());
let as_str = unsafe {
try_ssl!(ffi::ASN1_TIME_print(mem_bio.as_ptr(), self.0));
try!(cvt(ffi::ASN1_TIME_print(mem_bio.as_ptr(), self.0)));
String::from_utf8_unchecked(mem_bio.get_buf().to_owned())
};
write!(f, "{}", as_str)

View File

@ -1,11 +1,12 @@
use ffi;
use libc::{c_int, c_void};
use std::ffi::{CStr, CString};
use std::cmp::Ordering;
use std::ffi::{CStr, CString};
use std::{fmt, ptr};
use std::marker::PhantomData;
use std::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub, Deref, DerefMut};
use ffi;
use {cvt, cvt_p, cvt_n};
use error::ErrorStack;
/// Specifies the desired properties of a randomly generated `BigNum`.
@ -74,199 +75,141 @@ macro_rules! with_bn_in_ctx(
});
);
/// A borrowed, signed, arbitrary-precision integer.
#[derive(Copy, Clone)]
pub struct BigNumRef<'a>(*mut ffi::BIGNUM, PhantomData<&'a ()>);
/// A context object for `BigNum` operations.
pub struct BnCtx(*mut ffi::BN_CTX);
impl<'a> BigNumRef<'a> {
pub unsafe fn from_ptr(handle: *mut ffi::BIGNUM) -> BigNumRef<'a> {
BigNumRef(handle, PhantomData)
}
/// Returns the square of `self`.
///
/// ```
/// # use openssl::bn::BigNum;
/// let ref n = BigNum::new_from(10).unwrap();
/// let squared = BigNum::new_from(100).unwrap();
///
/// assert_eq!(n.checked_sqr().unwrap(), squared);
/// assert_eq!(n * n, squared);
/// ```
pub fn checked_sqr(&self) -> Result<BigNum, ErrorStack> {
impl Drop for BnCtx {
fn drop(&mut self) {
unsafe {
with_bn_in_ctx!(r, ctx, {
ffi::BN_sqr(r.as_ptr(), self.as_ptr(), ctx) == 1
})
ffi::BN_CTX_free(self.0);
}
}
}
/// Returns the unsigned remainder of the division `self / n`.
pub fn checked_nnmod(&self, n: &BigNumRef) -> Result<BigNum, ErrorStack> {
impl BnCtx {
/// Returns a new `BnCtx`.
pub fn new() -> Result<BnCtx, ErrorStack> {
unsafe {
with_bn_in_ctx!(r, ctx, {
ffi::BN_nnmod(r.as_ptr(), self.as_ptr(), n.as_ptr(), ctx) == 1
})
cvt_p(ffi::BN_CTX_new()).map(BnCtx)
}
}
/// Equivalent to `(self + a) mod n`.
///
/// ```
/// # use openssl::bn::BigNum;
/// let ref s = BigNum::new_from(10).unwrap();
/// let ref a = BigNum::new_from(20).unwrap();
/// let ref n = BigNum::new_from(29).unwrap();
/// let result = BigNum::new_from(1).unwrap();
///
/// assert_eq!(s.checked_mod_add(a, n).unwrap(), result);
/// ```
pub fn checked_mod_add(&self, a: &BigNumRef, n: &BigNumRef) -> Result<BigNum, ErrorStack> {
/// Places the result of `a²` in `r`.
pub fn sqr(&mut self, r: &mut BigNumRef, a: &BigNumRef) -> Result<(), ErrorStack> {
unsafe {
with_bn_in_ctx!(r, ctx, {
ffi::BN_mod_add(r.as_ptr(), self.as_ptr(), a.as_ptr(), n.as_ptr(), ctx) == 1
})
cvt(ffi::BN_sqr(r.as_ptr(), a.as_ptr(), self.0)).map(|_| ())
}
}
/// Equivalent to `(self - a) mod n`.
pub fn checked_mod_sub(&self, a: &BigNumRef, n: &BigNumRef) -> Result<BigNum, ErrorStack> {
/// Places the result of `a mod m` in `r`.
pub fn nnmod(&mut self,
r: &mut BigNumRef,
a: &BigNumRef,
m: &BigNumRef) -> Result<(), ErrorStack> {
unsafe {
with_bn_in_ctx!(r, ctx, {
ffi::BN_mod_sub(r.as_ptr(), self.as_ptr(), a.as_ptr(), n.as_ptr(), ctx) == 1
})
cvt(ffi::BN_nnmod(r.as_ptr(), a.as_ptr(), m.as_ptr(), self.0)).map(|_| ())
}
}
/// Equivalent to `(self * a) mod n`.
pub fn checked_mod_mul(&self, a: &BigNumRef, n: &BigNumRef) -> Result<BigNum, ErrorStack> {
/// Places the result of `(a + b) mod m` in `r`.
pub fn mod_add(&mut self,
r: &mut BigNumRef,
a: &BigNumRef,
b: &BigNumRef,
m: &BigNumRef)
-> Result<(), ErrorStack> {
unsafe {
with_bn_in_ctx!(r, ctx, {
ffi::BN_mod_mul(r.as_ptr(), self.as_ptr(), a.as_ptr(), n.as_ptr(), ctx) == 1
})
cvt(ffi::BN_mod_add(r.as_ptr(), a.as_ptr(), b.as_ptr(), m.as_ptr(), self.0)).map(|_| ())
}
}
/// Equivalent to `self² mod n`.
pub fn checked_mod_sqr(&self, n: &BigNumRef) -> Result<BigNum, ErrorStack> {
/// Places the result of `(a - b) mod m` in `r`.
pub fn mod_sub(&mut self,
r: &mut BigNumRef,
a: &BigNumRef,
b: &BigNumRef,
m: &BigNumRef)
-> Result<(), ErrorStack> {
unsafe {
with_bn_in_ctx!(r, ctx, {
ffi::BN_mod_sqr(r.as_ptr(), self.as_ptr(), n.as_ptr(), ctx) == 1
})
cvt(ffi::BN_mod_sub(r.as_ptr(), a.as_ptr(), b.as_ptr(), m.as_ptr(), self.0)).map(|_| ())
}
}
/// Raises `self` to the `p`th power.
pub fn checked_exp(&self, p: &BigNumRef) -> Result<BigNum, ErrorStack> {
/// Places the result of `(a * b) mod m` in `r`.
pub fn mod_mul(&mut self,
r: &mut BigNumRef,
a: &BigNumRef,
b: &BigNumRef,
m: &BigNumRef)
-> Result<(), ErrorStack> {
unsafe {
with_bn_in_ctx!(r, ctx, {
ffi::BN_exp(r.as_ptr(), self.as_ptr(), p.as_ptr(), ctx) == 1
})
cvt(ffi::BN_mod_mul(r.as_ptr(), a.as_ptr(), b.as_ptr(), m.as_ptr(), self.0)).map(|_| ())
}
}
/// Equivalent to `self.checked_exp(p) mod n`.
pub fn checked_mod_exp(&self, p: &BigNumRef, n: &BigNumRef) -> Result<BigNum, ErrorStack> {
/// Places the result of `a² mod m` in `r`.
pub fn mod_sqr(&mut self,
r: &mut BigNumRef,
a: &BigNumRef,
m: &BigNumRef) -> Result<(), ErrorStack> {
unsafe {
with_bn_in_ctx!(r, ctx, {
ffi::BN_mod_exp(r.as_ptr(), self.as_ptr(), p.as_ptr(), n.as_ptr(), ctx) == 1
})
cvt(ffi::BN_mod_sqr(r.as_ptr(), a.as_ptr(), m.as_ptr(), self.0)).map(|_| ())
}
}
/// Calculates the modular multiplicative inverse of `self` modulo `n`, that is, an integer `r`
/// such that `(self * r) % n == 1`.
pub fn checked_mod_inv(&self, n: &BigNumRef) -> Result<BigNum, ErrorStack> {
/// Places the result of `a^p` in `r`.
pub fn exp(&mut self,
r: &mut BigNumRef,
a: &BigNumRef,
p: &BigNumRef) -> Result<(), ErrorStack> {
unsafe{
cvt(ffi::BN_exp(r.as_ptr(), a.as_ptr(), p.as_ptr(), self.0)).map(|_| ())
}
}
/// Places the result of `a^p mod m` in `r`.
pub fn mod_exp(&mut self,
r: &mut BigNumRef,
a: &BigNumRef,
p: &BigNumRef,
m: &BigNumRef) -> Result<(), ErrorStack> {
unsafe {
with_bn_in_ctx!(r, ctx, {
!ffi::BN_mod_inverse(r.as_ptr(), self.as_ptr(), n.as_ptr(), ctx).is_null()
})
cvt(ffi::BN_mod_exp(r.as_ptr(), a.as_ptr(), p.as_ptr(), m.as_ptr(), self.0)).map(|_| ())
}
}
/// Add a `u32` to `self`. This is more efficient than adding a
/// `BigNum`.
pub fn add_word(&mut self, w: u32) -> Result<(), ErrorStack> {
/// Places the inverse of `a` modulo `n` in `r`.
pub fn mod_inverse(&mut self,
r: &mut BigNumRef,
a: &BigNumRef,
n: &BigNumRef) -> Result<(), ErrorStack> {
unsafe {
if ffi::BN_add_word(self.as_ptr(), w as ffi::BN_ULONG) == 1 {
Ok(())
} else {
Err(ErrorStack::get())
}
cvt_p(ffi::BN_mod_inverse(r.0, a.0, n.0, self.0)).map(|_| ())
}
}
pub fn sub_word(&mut self, w: u32) -> Result<(), ErrorStack> {
/// Places the greatest common denominator of `a` and `b` in `r`.
pub fn gcd(&mut self,
r: &mut BigNumRef,
a: &BigNumRef,
b: &BigNumRef) -> Result<(), ErrorStack> {
unsafe {
if ffi::BN_sub_word(self.as_ptr(), w as ffi::BN_ULONG) == 1 {
Ok(())
} else {
Err(ErrorStack::get())
}
}
}
pub fn mul_word(&mut self, w: u32) -> Result<(), ErrorStack> {
unsafe {
if ffi::BN_mul_word(self.as_ptr(), w as ffi::BN_ULONG) == 1 {
Ok(())
} else {
Err(ErrorStack::get())
}
}
}
pub fn div_word(&mut self, w: u32) -> Result<u64, ErrorStack> {
unsafe {
let result = ffi::BN_div_word(self.as_ptr(), w as ffi::BN_ULONG);
if result != !0 {
Ok(result.into())
} else {
Err(ErrorStack::get())
}
}
}
pub fn mod_word(&self, w: u32) -> Result<u64, ErrorStack> {
unsafe {
let result = ffi::BN_mod_word(self.as_ptr(), w as ffi::BN_ULONG);
if result != !0 {
Ok(result as u64)
} else {
Err(ErrorStack::get())
}
}
}
/// Computes the greatest common denominator of `self` and `a`.
pub fn checked_gcd(&self, a: &BigNumRef) -> Result<BigNum, ErrorStack> {
unsafe {
with_bn_in_ctx!(r, ctx, {
ffi::BN_gcd(r.as_ptr(), self.as_ptr(), a.as_ptr(), ctx) == 1
})
cvt(ffi::BN_gcd(r.0, a.0, b.0, self.0)).map(|_| ())
}
}
/// Checks whether `self` is prime.
/// Checks whether `p` is prime.
///
/// Performs a Miller-Rabin probabilistic primality test with `checks` iterations.
///
/// # Return Value
///
/// Returns `true` if `self` is prime with an error probability of less than `0.25 ^ checks`.
pub fn is_prime(&self, checks: i32) -> Result<bool, ErrorStack> {
/// Returns `true` if `p` is prime with an error probability of less than `0.25 ^ checks`.
pub fn is_prime(&mut self, p: &BigNumRef, checks: i32) -> Result<bool, ErrorStack> {
unsafe {
with_ctx!(ctx, {
Ok(ffi::BN_is_prime_ex(self.as_ptr(),
checks as c_int,
ctx,
ptr::null_mut()) == 1)
})
cvt_n(ffi::BN_is_prime_ex(p.0, checks.into(), self.0, ptr::null_mut())).map(|r| r != 0)
}
}
/// Checks whether `self` is prime with optional trial division.
/// Checks whether `p` is prime with optional trial division.
///
/// If `do_trial_division` is `true`, first performs trial division by a number of small primes.
/// Then, like `is_prime`, performs a Miller-Rabin probabilistic primality test with `checks`
@ -274,35 +217,88 @@ impl<'a> BigNumRef<'a> {
///
/// # Return Value
///
/// Returns `true` if `self` is prime with an error probability of less than `0.25 ^ checks`.
pub fn is_prime_fast(&self, checks: i32, do_trial_division: bool) -> Result<bool, ErrorStack> {
/// Returns `true` if `p` is prime with an error probability of less than `0.25 ^ checks`.
pub fn is_prime_fasttest(&mut self,
p: &BigNumRef,
checks: i32,
do_trial_division: bool) -> Result<bool, ErrorStack> {
unsafe {
with_ctx!(ctx, {
Ok(ffi::BN_is_prime_fasttest_ex(self.as_ptr(),
checks as c_int,
ctx,
cvt_n(ffi::BN_is_prime_fasttest_ex(p.0,
checks.into(),
self.0,
do_trial_division as c_int,
ptr::null_mut()) == 1)
})
ptr::null_mut()))
.map(|r| r != 0)
}
}
}
/// A borrowed, signed, arbitrary-precision integer.
#[derive(Copy, Clone)]
pub struct BigNumRef<'a>(*mut ffi::BIGNUM, PhantomData<&'a ()>);
impl<'a> BigNumRef<'a> {
pub unsafe fn from_ptr(handle: *mut ffi::BIGNUM) -> BigNumRef<'a> {
BigNumRef(handle, PhantomData)
}
/// Adds a `u32` to `self`.
pub fn add_word(&mut self, w: u32) -> Result<(), ErrorStack> {
unsafe {
cvt(ffi::BN_add_word(self.0, w as ffi::BN_ULONG)).map(|_| ())
}
}
/// Generates a cryptographically strong pseudo-random `BigNum` `r` in the range
/// `0 <= r < self`.
pub fn checked_rand_in_range(&self) -> Result<BigNum, ErrorStack> {
/// Subtracts a `u32` from `self`.
pub fn sub_word(&mut self, w: u32) -> Result<(), ErrorStack> {
unsafe {
with_bn_in_ctx!(r, ctx, {
ffi::BN_rand_range(r.as_ptr(), self.as_ptr()) == 1
})
cvt(ffi::BN_sub_word(self.0, w as ffi::BN_ULONG)).map(|_| ())
}
}
/// The cryptographically weak counterpart to `checked_rand_in_range`.
pub fn checked_pseudo_rand_in_range(&self) -> Result<BigNum, ErrorStack> {
/// Multiplies a `u32` by `self`.
pub fn mul_word(&mut self, w: u32) -> Result<(), ErrorStack> {
unsafe {
with_bn_in_ctx!(r, ctx, {
ffi::BN_pseudo_rand_range(r.as_ptr(), self.as_ptr()) == 1
})
cvt(ffi::BN_mul_word(self.0, w as ffi::BN_ULONG)).map(|_| ())
}
}
/// Divides `self` by a `u32`, returning the remainder.
pub fn div_word(&mut self, w: u32) -> Result<u64, ErrorStack> {
unsafe {
let r = ffi::BN_div_word(self.0, w.into());
if r == ffi::BN_ULONG::max_value() {
Err(ErrorStack::get())
} else {
Ok(r.into())
}
}
}
/// Returns the result of `self` modulo `w`.
pub fn mod_word(&self, w: u32) -> Result<u64, ErrorStack> {
unsafe {
let r = ffi::BN_mod_word(self.0, w.into());
if r == ffi::BN_ULONG::max_value() {
Err(ErrorStack::get())
} else {
Ok(r.into())
}
}
}
/// Places a cryptographically-secure pseudo-random number nonnegative
/// number less than `self` in `rnd`.
pub fn rand_in_range(&self, rnd: &mut BigNumRef) -> Result<(), ErrorStack> {
unsafe {
cvt(ffi::BN_rand_range(self.0, rnd.0)).map(|_| ())
}
}
/// The cryptographically weak counterpart to `rand_in_range`.
pub fn pseudo_rand_in_range(&self, rnd: &mut BigNumRef) -> Result<(), ErrorStack> {
unsafe {
cvt(ffi::BN_pseudo_rand_range(self.0, rnd.0)).map(|_| ())
}
}
@ -311,11 +307,7 @@ impl<'a> BigNumRef<'a> {
/// When setting a bit outside of `self`, it is expanded.
pub fn set_bit(&mut self, n: i32) -> Result<(), ErrorStack> {
unsafe {
if ffi::BN_set_bit(self.as_ptr(), n as c_int) == 1 {
Ok(())
} else {
Err(ErrorStack::get())
}
cvt(ffi::BN_set_bit(self.0, n.into())).map(|_| ())
}
}
@ -324,17 +316,15 @@ impl<'a> BigNumRef<'a> {
/// When clearing a bit outside of `self`, an error is returned.
pub fn clear_bit(&mut self, n: i32) -> Result<(), ErrorStack> {
unsafe {
if ffi::BN_clear_bit(self.as_ptr(), n as c_int) == 1 {
Ok(())
} else {
Err(ErrorStack::get())
}
cvt(ffi::BN_clear_bit(self.0, n.into())).map(|_| ())
}
}
/// Returns `true` if the `n`th bit of `self` is set to 1, `false` otherwise.
pub fn is_bit_set(&self, n: i32) -> bool {
unsafe { ffi::BN_is_bit_set(self.as_ptr(), n as c_int) == 1 }
unsafe {
ffi::BN_is_bit_set(self.0, n.into()) == 1
}
}
/// Truncates `self` to the lowest `n` bits.
@ -342,46 +332,21 @@ impl<'a> BigNumRef<'a> {
/// An error occurs if `self` is already shorter than `n` bits.
pub fn mask_bits(&mut self, n: i32) -> Result<(), ErrorStack> {
unsafe {
if ffi::BN_mask_bits(self.as_ptr(), n as c_int) == 1 {
Ok(())
} else {
Err(ErrorStack::get())
}
}
}
/// Returns `self`, shifted left by 1 bit. `self` may be negative.
///
/// ```
/// # use openssl::bn::BigNum;
/// let ref s = BigNum::new_from(0b0100).unwrap();
/// let result = BigNum::new_from(0b1000).unwrap();
///
/// assert_eq!(s.checked_shl1().unwrap(), result);
/// ```
///
/// ```
/// # use openssl::bn::BigNum;
/// let ref s = -BigNum::new_from(8).unwrap();
/// let result = -BigNum::new_from(16).unwrap();
///
/// // (-8) << 1 == -16
/// assert_eq!(s.checked_shl1().unwrap(), result);
/// ```
pub fn checked_shl1(&self) -> Result<BigNum, ErrorStack> {
unsafe {
with_bn!(r, {
ffi::BN_lshift1(r.as_ptr(), self.as_ptr()) == 1
})
cvt(ffi::BN_mask_bits(self.0, n.into())).map(|_| ())
}
}
/// Returns `self`, shifted right by 1 bit. `self` may be negative.
pub fn checked_shr1(&self) -> Result<BigNum, ErrorStack> {
/// Places `self << 1` in `r`.
pub fn lshift1(&self, r: &mut BigNumRef) -> Result<(), ErrorStack> {
unsafe {
with_bn!(r, {
ffi::BN_rshift1(r.as_ptr(), self.as_ptr()) == 1
})
cvt(ffi::BN_lshift1(r.0, self.0)).map(|_| ())
}
}
/// Places `self >> 1` in `r`.
pub fn rshift1(&self, r: &mut BigNumRef) -> Result<(), ErrorStack> {
unsafe {
cvt(ffi::BN_rshift1(r.0, self.0)).map(|_| ())
}
}
@ -1006,7 +971,7 @@ impl Neg for BigNum {
#[cfg(test)]
mod tests {
use bn::BigNum;
use bn::{BnCtx, BigNum};
#[test]
fn test_to_from_slice() {
@ -1031,7 +996,8 @@ mod tests {
let a = BigNum::new_from(19029017).unwrap();
let p = BigNum::checked_generate_prime(128, true, None, Some(&a)).unwrap();
assert!(p.is_prime(100).unwrap());
assert!(p.is_prime_fast(100, true).unwrap());
let mut ctx = BnCtx::new().unwrap();
assert!(ctx.is_prime(&p, 100).unwrap());
assert!(ctx.is_prime_fasttest(&p, 100, true).unwrap());
}
}

View File

@ -135,7 +135,7 @@ mod tests {
#[test]
fn test_dh_from_pem() {
let mut ctx = SslContext::new(SslMethod::tls()).unwrap();
let params = include_bytes!("../../test/dhparams.pem");
let params = include_bytes!("../test/dhparams.pem");
let dh = DH::from_pem(params).ok().expect("Failed to load PEM");
ctx.set_tmp_dh(&dh).unwrap();
}

View File

@ -16,6 +16,10 @@ extern crate tempdir;
#[doc(inline)]
pub use ffi::init;
use libc::c_int;
use error::ErrorStack;
mod macros;
pub mod asn1;
@ -28,3 +32,27 @@ pub mod nid;
pub mod ssl;
pub mod version;
pub mod x509;
pub fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> {
if r.is_null() {
Err(ErrorStack::get())
} else {
Ok(r)
}
}
pub fn cvt(r: c_int) -> Result<c_int, ErrorStack> {
if r <= 0 {
Err(ErrorStack::get())
} else {
Ok(r)
}
}
pub fn cvt_n(r: c_int) -> Result<c_int, ErrorStack> {
if r < 0 {
Err(ErrorStack::get())
} else {
Ok(r)
}
}