Remove mlock.

It currently causes too many problems to be practical. We will re-enable
it once we have a dedicated allocator for locked memory.
master
Andreas Fackler 7 years ago committed by Andreas Fackler
parent 191cf0b741
commit ad11ceaed6
  1. 16
      README.md
  2. 28
      src/error.rs
  3. 106
      src/lib.rs
  4. 180
      src/poly.rs
  5. 101
      src/secret.rs

@ -51,33 +51,21 @@ fn main() {
Run tests using the following command: Run tests using the following command:
``` ```
$ MLOCK_SECRETS=false cargo test $ cargo test
``` ```
The test suite runs quickly without setting the envvar `MLOCK_SECRETS=false`,
but runs even faster when it is set. Setting this envvar also ensures that
tests won't fail if we reach the testing system's locked memory limit (which
we won't hit unless the system's locked memory limit is set unreasonably low,
i.e. 1-2 pages or the equivalent, 4-8 kilobytes).
### Examples ### Examples
Run examples from the [`examples`](examples) directory using: Run examples from the [`examples`](examples) directory using:
``` ```
$ MLOCK_SECRETS=false cargo run --example <example name> $ cargo run --example <example name>
``` ```
Also see the Also see the
[distributed_key_generation](https://github.com/poanetwork/threshold_crypto/blob/d81953b55d181311c2a4eed2b6c34059fcf3fdae/src/poly.rs#L967) [distributed_key_generation](https://github.com/poanetwork/threshold_crypto/blob/d81953b55d181311c2a4eed2b6c34059fcf3fdae/src/poly.rs#L967)
test. test.
### Environment Variables
[`MLOCK_SECRETS`](https://github.com/poanetwork/threshold_crypto/blob/master/src/lib.rs#L51): Sets whether or not the Unix syscall [`mlock`](http://man7.org/linux/man-pages/man2/mlock.2.html) or WinAPI function [`VirtualLock`](https://msdn.microsoft.com/en-us/library/windows/desktop/aa366895(v=vs.85).aspx) is called on portions of memory containing secret values. This option is enabled by default (`MLOCK_SECRETS=true`). Disabling memory locking (`MLOCK_SECRETS=false`) allows secret values to be copied to disk, where they will not be zeroed on drop and may persist indefinitely. **Disabling memory locking should only be done in development and testing.**
Disabling memory locking is useful because it removes the possibility of tests failing due to reaching the testing system's locked memory limit. For example, if your crate uses `threshold_crypto` and you write a test that maintains hundreds or thousands of secrets in memory simultaneously, you run the risk of reaching your system's allowed number of locked pages, which will cause this library to fail.
## Application Details ## Application Details
The basic usage outline is: The basic usage outline is:

@ -1,7 +1,5 @@
//! Crypto errors. //! Crypto errors.
use errno::Errno;
/// A crypto error. /// A crypto error.
#[derive(Clone, Eq, PartialEq, Debug, Fail)] #[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum Error { pub enum Error {
@ -11,32 +9,6 @@ pub enum Error {
DuplicateEntry, DuplicateEntry,
#[fail(display = "The degree is too high for the coefficients to be indexed by usize.")] #[fail(display = "The degree is too high for the coefficients to be indexed by usize.")]
DegreeTooHigh, DegreeTooHigh,
#[fail(
display = "Failed to `mlock` {} bytes starting at address: {}",
n_bytes,
addr
)]
MlockFailed {
// The errno set by the failed `mlock` syscall.
errno: Errno,
// The address for the first byte in the range of memory that was attempted to be locked.
addr: String,
// The number of bytes that were attempted to be locked.
n_bytes: usize,
},
#[fail(
display = "Failed to `munlock` {} bytes starting at address: {}",
n_bytes,
addr
)]
MunlockFailed {
// The errno set by the failed `munlock` syscall.
errno: Errno,
// The address for the first byte in the range of memory that was attempted to be unlocked.
addr: String,
// The number of bytes that were attempted to be unlocked.
n_bytes: usize,
},
} }
unsafe impl Send for Error {} unsafe impl Send for Error {}

@ -227,11 +227,6 @@ impl fmt::Debug for SignatureShare {
pub struct SecretKey(Box<Fr>); pub struct SecretKey(Box<Fr>);
/// Creates a `SecretKey` containing the zero prime field element. /// Creates a `SecretKey` containing the zero prime field element.
///
/// # Panics
///
/// Panics if we have reached the system's locked memory limit when locking the secret field
/// element in RAM.
impl Default for SecretKey { impl Default for SecretKey {
fn default() -> Self { fn default() -> Self {
let mut fr = Fr::zero(); let mut fr = Fr::zero();
@ -242,11 +237,6 @@ impl Default for SecretKey {
/// Creates a random `SecretKey` from a given RNG. If you do not need to specify your own RNG, you /// Creates a random `SecretKey` from a given RNG. If you do not need to specify your own RNG, you
/// should use `SecretKey::random()` or `SecretKey::try_random()` as your constructor instead. /// should use `SecretKey::random()` or `SecretKey::try_random()` as your constructor instead.
///
/// # Panics
///
/// Panics if we have reached the system's locked memory limit when locking the secret field
/// element in RAM.
impl Rand for SecretKey { impl Rand for SecretKey {
fn rand<R: Rng>(rng: &mut R) -> Self { fn rand<R: Rng>(rng: &mut R) -> Self {
let mut fr = Fr::rand(rng); let mut fr = Fr::rand(rng);
@ -256,11 +246,6 @@ impl Rand for SecretKey {
} }
/// Creates a new `SecretKey` by cloning another `SecretKey`'s prime field element. /// Creates a new `SecretKey` by cloning another `SecretKey`'s prime field element.
///
/// # Panics
///
/// Panics if we have reached the system's locked memory limit when locking the secret field
/// element into RAM.
impl Clone for SecretKey { impl Clone for SecretKey {
fn clone(&self) -> Self { fn clone(&self) -> Self {
let mut fr = *self.0; let mut fr = *self.0;
@ -277,12 +262,8 @@ impl Clone for SecretKey {
impl Drop for SecretKey { impl Drop for SecretKey {
fn drop(&mut self) { fn drop(&mut self) {
self.zero_secret(); self.zero_secret();
if let Err(e) = self.munlock_secret() {
panic!("Failed to drop `SecretKey`: {}", e);
}
} }
} }
/// A debug statement where the secret prime field element is redacted. /// A debug statement where the secret prime field element is redacted.
impl fmt::Debug for SecretKey { impl fmt::Debug for SecretKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -304,17 +285,8 @@ impl SecretKey {
/// element). The field element is copied bytewise onto the heap, the resulting `Box` is /// element). The field element is copied bytewise onto the heap, the resulting `Box` is
/// stored in the returned `SecretKey`. /// stored in the returned `SecretKey`.
/// ///
/// This constructor is identical to `SecretKey::try_from_mut()` in every way except that this
/// constructor will panic if locking memory into RAM fails, whereas
/// `SecretKey::try_from_mut()` returns an `Err`.
///
/// *WARNING* this constructor will overwrite the referenced `Fr` element with zeros after it /// *WARNING* this constructor will overwrite the referenced `Fr` element with zeros after it
/// has been copied onto the heap. /// has been copied onto the heap.
///
/// # Panics
///
/// Panics if we reach the system's locked memory limit when locking the secret field element
/// into RAM.
pub fn from_mut(fr: &mut Fr) -> Self { pub fn from_mut(fr: &mut Fr) -> Self {
SecretKey::try_from_mut(fr) SecretKey::try_from_mut(fr)
.unwrap_or_else(|e| panic!("Falied to create `SecretKey`: {}", e)) .unwrap_or_else(|e| panic!("Falied to create `SecretKey`: {}", e))
@ -326,16 +298,10 @@ impl SecretKey {
/// stored in the returned `SecretKey`. /// stored in the returned `SecretKey`.
/// ///
/// This constructor is identical to `SecretKey::from_mut()` in every way except that this /// This constructor is identical to `SecretKey::from_mut()` in every way except that this
/// constructor will return an `Err` if locking memory into RAM fails, whereas /// constructor will return an `Err` where `SecretKey::from_mut()` would panic.
/// `SecretKey::from_mut()` will panic.
/// ///
/// *WARNING* this constructor will overwrite the referenced `Fr` element with zeros after it /// *WARNING* this constructor will overwrite the referenced `Fr` element with zeros after it
/// has been copied onto the heap. /// has been copied onto the heap.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we reached the system's locked memory limit when locking
/// the secret field element into RAM.
pub fn try_from_mut(fr: &mut Fr) -> Result<Self> { pub fn try_from_mut(fr: &mut Fr) -> Result<Self> {
let fr_ptr = fr as *mut Fr; let fr_ptr = fr as *mut Fr;
let mut boxed_fr = Box::new(Fr::zero()); let mut boxed_fr = Box::new(Fr::zero());
@ -344,7 +310,6 @@ impl SecretKey {
} }
clear_fr(fr_ptr); clear_fr(fr_ptr);
let sk = SecretKey(boxed_fr); let sk = SecretKey(boxed_fr);
sk.mlock_secret()?;
Ok(sk) Ok(sk)
} }
@ -354,15 +319,6 @@ impl SecretKey {
/// `SecretKey::try_random()` constructors, which use /// `SecretKey::try_random()` constructors, which use
/// [`rand::thead_rng()`](https://docs.rs/rand/0.4.3/rand/fn.thread_rng.html) internally as /// [`rand::thead_rng()`](https://docs.rs/rand/0.4.3/rand/fn.thread_rng.html) internally as
/// their RNG. /// their RNG.
///
/// This constructor panics if it is unable to lock `SecretKey` memory into RAM, otherwise it
/// is identical to the constructor: `SecretKey::try_random()` (which instead of panicing
/// returns an `Err`).
///
/// # Panics
///
/// Panics if we have hit the system's locked memory limit when `mlock`ing the new instance of
/// `SecretKey`.
pub fn random() -> Self { pub fn random() -> Self {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
SecretKey::rand(&mut rng) SecretKey::rand(&mut rng)
@ -374,14 +330,6 @@ impl SecretKey {
/// `SecretKey::try_random()` constructors, which use /// `SecretKey::try_random()` constructors, which use
/// [`rand::thead_rng()`](https://docs.rs/rand/0.4.3/rand/fn.thread_rng.html) internally as /// [`rand::thead_rng()`](https://docs.rs/rand/0.4.3/rand/fn.thread_rng.html) internally as
/// their RNG. /// their RNG.
///
/// This constructor returns an `Err` if it is unable to lock `SecretKey` memory into RAM,
/// otherwise it is identical to the constructor: `SecretKey::random()` (which will panic
/// instead of returning an `Err`).
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
pub fn try_random() -> Result<Self> { pub fn try_random() -> Result<Self> {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let mut fr = Fr::rand(&mut rng); let mut fr = Fr::rand(&mut rng);
@ -439,16 +387,8 @@ impl SecretKeyShare {
/// field elements. The field element will be copied bytewise onto the heap, the resulting /// field elements. The field element will be copied bytewise onto the heap, the resulting
/// `Box` is stored in the `SecretKey` which is then wrapped in a `SecretKeyShare`. /// `Box` is stored in the `SecretKey` which is then wrapped in a `SecretKeyShare`.
/// ///
/// This constructor is identical to `SecretKeyShare::try_from_mut()` in every way except that
/// this constructor will panic if locking memory into RAM fails, whereas
/// `SecretKeyShare::try_from_mut()` will return an `Err`.
///
/// *WARNING* this constructor will overwrite the pointed to `Fr` element with zeros once it /// *WARNING* this constructor will overwrite the pointed to `Fr` element with zeros once it
/// has been copied into a new `SecretKeyShare`. /// has been copied into a new `SecretKeyShare`.
///
/// # Panics
///
/// Panics if we reach the systems locked memory limit.
pub fn from_mut(fr: &mut Fr) -> Self { pub fn from_mut(fr: &mut Fr) -> Self {
match SecretKey::try_from_mut(fr) { match SecretKey::try_from_mut(fr) {
Ok(sk) => SecretKeyShare(sk), Ok(sk) => SecretKeyShare(sk),
@ -463,17 +403,6 @@ impl SecretKeyShare {
/// constructor takes a reference to avoid any unnecessary stack copying/moving of secrets /// constructor takes a reference to avoid any unnecessary stack copying/moving of secrets
/// field elements. The field element will be copied bytewise onto the heap, the resulting /// field elements. The field element will be copied bytewise onto the heap, the resulting
/// `Box` is stored in the `SecretKey` which is then wrapped in a `SecretKeyShare`. /// `Box` is stored in the `SecretKey` which is then wrapped in a `SecretKeyShare`.
///
/// This constructor is identical to `SecretKeyShare::from_mut()` in every way except that this
/// constructor will return an `Err` if locking memory into RAM fails, whereas
/// `SecretKeyShare::from_mut()` will panic.
///
/// *WARNING* this constructor will overwrite the pointed to `Fr` element with zeros once it
/// has been copied into a new `SecretKeyShare`.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
pub fn try_from_mut(fr: &mut Fr) -> Result<Self> { pub fn try_from_mut(fr: &mut Fr) -> Result<Self> {
SecretKey::try_from_mut(fr).map(SecretKeyShare) SecretKey::try_from_mut(fr).map(SecretKeyShare)
} }
@ -629,11 +558,7 @@ impl From<Poly> for SecretKeySet {
impl SecretKeySet { impl SecretKeySet {
/// Creates a set of secret key shares, where any `threshold + 1` of them can collaboratively /// Creates a set of secret key shares, where any `threshold + 1` of them can collaboratively
/// sign and decrypt. This constuctor is identical to the `SecretKey::try_random()` in every /// sign and decrypt. This constuctor is identical to the `SecretKey::try_random()` in every
/// way except that this constructor panics if locking secret values into RAM fails. /// way except that this constructor panics if the other returns an error.
///
/// # Panics
///
/// Panics if we reach the system's locked memory limit.
pub fn random<R: Rng>(threshold: usize, rng: &mut R) -> Self { pub fn random<R: Rng>(threshold: usize, rng: &mut R) -> Self {
SecretKeySet::try_random(threshold, rng) SecretKeySet::try_random(threshold, rng)
.unwrap_or_else(|e| panic!("Failed to create random `SecretKeySet`: {}", e)) .unwrap_or_else(|e| panic!("Failed to create random `SecretKeySet`: {}", e))
@ -641,11 +566,7 @@ impl SecretKeySet {
/// Creates a set of secret key shares, where any `threshold + 1` of them can collaboratively /// Creates a set of secret key shares, where any `threshold + 1` of them can collaboratively
/// sign and decrypt. This constuctor is identical to the `SecretKey::random()` in every /// sign and decrypt. This constuctor is identical to the `SecretKey::random()` in every
/// way except that this constructor return an `Err` if locking secret values into RAM fails. /// way except that this constructor returns an `Err` where the `random` would panic.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
pub fn try_random<R: Rng>(threshold: usize, rng: &mut R) -> Result<Self> { pub fn try_random<R: Rng>(threshold: usize, rng: &mut R) -> Result<Self> {
Poly::try_random(threshold, rng).map(SecretKeySet::from) Poly::try_random(threshold, rng).map(SecretKeySet::from)
} }
@ -658,12 +579,7 @@ impl SecretKeySet {
/// Returns the `i`-th secret key share. This method is identical to the /// Returns the `i`-th secret key share. This method is identical to the
/// `.try_secret_key_share()` in every way except that this method panics if /// `.try_secret_key_share()` in every way except that this method panics if
/// locking secret values into memory fails, whereas `.try_secret_key_share()` /// where `.try_secret_key_share()` would return an `Err`.
/// returns an `Err`.
///
/// # Panics
///
/// Panics if we reach the system's locked memory limit.
pub fn secret_key_share<T: IntoFr>(&self, i: T) -> SecretKeyShare { pub fn secret_key_share<T: IntoFr>(&self, i: T) -> SecretKeyShare {
self.try_secret_key_share(i) self.try_secret_key_share(i)
.unwrap_or_else(|e| panic!("Failed to create `SecretKeyShare`: {}", e)) .unwrap_or_else(|e| panic!("Failed to create `SecretKeyShare`: {}", e))
@ -671,12 +587,7 @@ impl SecretKeySet {
/// Returns the `i`-th secret key share. This method is identical to the method /// Returns the `i`-th secret key share. This method is identical to the method
/// `.secret_key_share()` in every way except that this method returns an `Err` if /// `.secret_key_share()` in every way except that this method returns an `Err` if
/// locking secret values into memory fails, whereas `.secret_key_share()` will /// where `.secret_key_share()` would panic.
/// panic.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
pub fn try_secret_key_share<T: IntoFr>(&self, i: T) -> Result<SecretKeyShare> { pub fn try_secret_key_share<T: IntoFr>(&self, i: T) -> Result<SecretKeyShare> {
let mut fr = self.poly.evaluate(into_fr_plus_1(i)); let mut fr = self.poly.evaluate(into_fr_plus_1(i));
SecretKeyShare::try_from_mut(&mut fr) SecretKeyShare::try_from_mut(&mut fr)
@ -689,12 +600,7 @@ impl SecretKeySet {
} }
} }
/// Returns the secret master key. Panics if mlocking fails. /// Returns the secret master key.
///
/// # Panics
///
/// Panics if we have hit the system's locked memory limit when `mlock`ing the new instance of
/// `SecretKey`.
#[cfg(test)] #[cfg(test)]
fn secret_key(&self) -> SecretKey { fn secret_key(&self) -> SecretKey {
let mut fr = self.poly.evaluate(0); let mut fr = self.poly.evaluate(0);

@ -39,11 +39,6 @@ pub struct Poly {
} }
/// Creates a new `Poly` with the same coefficients as another polynomial. /// Creates a new `Poly` with the same coefficients as another polynomial.
///
/// # Panics
///
/// Panics if we reach the system's locked memory limit when locking the polynomial's coefficients
/// into RAM.
impl Clone for Poly { impl Clone for Poly {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Poly::try_from(self.coeff.clone()) Poly::try_from(self.coeff.clone())
@ -58,9 +53,6 @@ impl Debug for Poly {
} }
} }
/// # Panics
///
/// Panics if we reach the system's locked memory limit.
#[cfg_attr(feature = "cargo-clippy", allow(suspicious_op_assign_impl))] #[cfg_attr(feature = "cargo-clippy", allow(suspicious_op_assign_impl))]
impl<B: Borrow<Poly>> ops::AddAssign<B> for Poly { impl<B: Borrow<Poly>> ops::AddAssign<B> for Poly {
fn add_assign(&mut self, rhs: B) { fn add_assign(&mut self, rhs: B) {
@ -68,12 +60,6 @@ impl<B: Borrow<Poly>> ops::AddAssign<B> for Poly {
let rhs_len = rhs.borrow().coeff.len(); let rhs_len = rhs.borrow().coeff.len();
if rhs_len > len { if rhs_len > len {
self.coeff.resize(rhs_len, Fr::zero()); self.coeff.resize(rhs_len, Fr::zero());
if let Err(e) = self.mlock_secret() {
panic!(
"Failed to extend `Poly` memory lock during add-assign: {}",
e
);
}
} }
for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) { for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) {
Field::add_assign(self_c, rhs_c); Field::add_assign(self_c, rhs_c);
@ -99,18 +85,12 @@ impl<B: Borrow<Poly>> ops::Add<B> for Poly {
} }
} }
/// # Panics
///
/// Panics if we reach the system's locked memory limit.
impl<'a> ops::Add<Fr> for Poly { impl<'a> ops::Add<Fr> for Poly {
type Output = Poly; type Output = Poly;
fn add(mut self, rhs: Fr) -> Self::Output { fn add(mut self, rhs: Fr) -> Self::Output {
if self.is_zero() && !rhs.is_zero() { if self.is_zero() && !rhs.is_zero() {
self.coeff.push(rhs); self.coeff.push(rhs);
if let Err(e) = self.mlock_secret() {
panic!("Failed to extend `Poly` memory lock during add: {}", e);
}
} else { } else {
self.coeff[0].add_assign(&rhs); self.coeff[0].add_assign(&rhs);
self.remove_zeros(); self.remove_zeros();
@ -127,21 +107,12 @@ impl<'a> ops::Add<u64> for Poly {
} }
} }
/// # Panics
///
/// Panics if we reach the system's locked memory limit.
impl<B: Borrow<Poly>> ops::SubAssign<B> for Poly { impl<B: Borrow<Poly>> ops::SubAssign<B> for Poly {
fn sub_assign(&mut self, rhs: B) { fn sub_assign(&mut self, rhs: B) {
let len = self.coeff.len(); let len = self.coeff.len();
let rhs_len = rhs.borrow().coeff.len(); let rhs_len = rhs.borrow().coeff.len();
if rhs_len > len { if rhs_len > len {
self.coeff.resize(rhs_len, Fr::zero()); self.coeff.resize(rhs_len, Fr::zero());
if let Err(e) = self.mlock_secret() {
panic!(
"Failed to extend `Poly` memory lock during sub-assign: {}",
e
);
}
} }
for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) { for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) {
Field::sub_assign(self_c, rhs_c); Field::sub_assign(self_c, rhs_c);
@ -186,9 +157,6 @@ impl<'a> ops::Sub<u64> for Poly {
} }
} }
/// # Panics
///
/// Panics if we reach the system's locked memory limit.
// Clippy thinks using any `+` and `-` in a `Mul` implementation is suspicious. // Clippy thinks using any `+` and `-` in a `Mul` implementation is suspicious.
#[cfg_attr(feature = "cargo-clippy", allow(suspicious_arithmetic_impl))] #[cfg_attr(feature = "cargo-clippy", allow(suspicious_arithmetic_impl))]
impl<'a, B: Borrow<Poly>> ops::Mul<B> for &'a Poly { impl<'a, B: Borrow<Poly>> ops::Mul<B> for &'a Poly {
@ -241,10 +209,6 @@ impl ops::MulAssign<Fr> for Poly {
} }
} }
/// # Panics
///
/// This operation may panic if: when multiplying the polynomial by a zero field element, we fail
/// to munlock the cleared `coeff` vector.
impl<'a> ops::Mul<&'a Fr> for Poly { impl<'a> ops::Mul<&'a Fr> for Poly {
type Output = Poly; type Output = Poly;
@ -292,25 +256,14 @@ impl ops::Mul<u64> for Poly {
} }
} }
/// # Panics
///
/// Panics if we fail to munlock the `coeff` vector.
impl Drop for Poly { impl Drop for Poly {
fn drop(&mut self) { fn drop(&mut self) {
self.zero_secret(); self.zero_secret();
if let Err(e) = self.munlock_secret() {
panic!("Failed to munlock `Poly` during drop: {}", e);
}
} }
} }
/// Creates a new `Poly` instance from a vector of prime field elements representing the /// Creates a new `Poly` instance from a vector of prime field elements representing the
/// coefficients of the polynomial. We lock the region of the heap where the polynomial /// coefficients of the polynomial.
/// coefficients are allocated.
///
/// # Panics
///
/// Panics if we have reached the system's locked memory limit.
impl From<Vec<Fr>> for Poly { impl From<Vec<Fr>> for Poly {
fn from(coeffs: Vec<Fr>) -> Self { fn from(coeffs: Vec<Fr>) -> Self {
Poly::try_from(coeffs).unwrap_or_else(|e| panic!("Failed to create `Poly`: {}", e)) Poly::try_from(coeffs).unwrap_or_else(|e| panic!("Failed to create `Poly`: {}", e))
@ -327,38 +280,23 @@ impl ContainsSecret for Poly {
impl Poly { impl Poly {
/// Creates a new `Poly` instance from a vector of prime field elements representing the /// Creates a new `Poly` instance from a vector of prime field elements representing the
/// coefficients of the polynomial. We lock the region of the heap where the polynomial /// coefficients of the polynomial.
/// coefficients are allocated.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
pub fn try_from(coeff: Vec<Fr>) -> Result<Self> { pub fn try_from(coeff: Vec<Fr>) -> Result<Self> {
let poly = Poly { coeff }; let poly = Poly { coeff };
poly.mlock_secret()?;
Ok(poly) Ok(poly)
} }
/// Creates a random polynomial. This constructor is identical to the `Poly::try_random()` /// Creates a random polynomial. This constructor is identical to the `Poly::try_random()`
/// constructor in every way except that this constructor will panic if locking the polynomial /// constructor in every way except that this constructor will panic where `random` would
/// coefficients into RAM fails. /// return an error.
///
/// # Panics
///
/// Panics if we have reached the system's locked memory limit.
pub fn random<R: Rng>(degree: usize, rng: &mut R) -> Self { pub fn random<R: Rng>(degree: usize, rng: &mut R) -> Self {
Poly::try_random(degree, rng) Poly::try_random(degree, rng)
.unwrap_or_else(|e| panic!("Failed to create random `Poly`: {}", e)) .unwrap_or_else(|e| panic!("Failed to create random `Poly`: {}", e))
} }
/// Creates a random polynomial. This constructor is identical to the `Poly::random()` /// Creates a random polynomial. This constructor is identical to the `Poly::random()`
/// constructor in every way except that this constructor will return an `Err` if locking the /// constructor in every way except that this constructor will return an `Err` where
/// polynomial coefficients into RAM fails. /// `try_random` would return an error.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit or if
/// the degree is `usize::max_value()`.
pub fn try_random<R: Rng>(degree: usize, rng: &mut R) -> Result<Self> { pub fn try_random<R: Rng>(degree: usize, rng: &mut R) -> Result<Self> {
if degree == usize::max_value() { if degree == usize::max_value() {
return Err(Error::DegreeTooHigh); return Err(Error::DegreeTooHigh);
@ -368,10 +306,6 @@ impl Poly {
} }
/// Returns the polynomial with constant value `0`. /// Returns the polynomial with constant value `0`.
///
/// This constructor does not return a `Result` because the polynomial's `coeff` vector is
/// empty, which does not require memory locking. Memory locking will occur when the first
/// coefficient is added to the `coeff` vector.
pub fn zero() -> Self { pub fn zero() -> Self {
Poly { coeff: vec![] } Poly { coeff: vec![] }
} }
@ -382,33 +316,21 @@ impl Poly {
} }
/// Returns the polynomial with constant value `1`. This constructor is identical to /// Returns the polynomial with constant value `1`. This constructor is identical to
/// `Poly::try_one()` in every way except that this constructor panics if locking the `coeff` /// `Poly::try_one()` in every way except that this constructor panics where `Poly::try_one()`
/// vector into RAM fails, whereas `Poly::try_one()` returns an `Err`. /// would return an `Err`.
///
/// # Panics
///
/// Panics if we have reached the system's locked memory limit.
pub fn one() -> Self { pub fn one() -> Self {
Poly::try_one() Poly::try_one()
.unwrap_or_else(|e| panic!("Failed to create constant `Poly` of value 1: {}", e)) .unwrap_or_else(|e| panic!("Failed to create constant `Poly` of value 1: {}", e))
} }
/// Returns the polynomial with constant value `1`. This constructor is identical to /// Returns the polynomial with constant value `1`. This constructor is identical to
/// `Poly::one()` in every way except that this constructor returns `Err` if locking the /// `Poly::one()` in every way except that this constructor returns `Err` if where `Poly::one()`
/// `coeff` vector into RAM fails, whereas `Poly::one()` panics. /// would panic.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
pub fn try_one() -> Result<Self> { pub fn try_one() -> Result<Self> {
Poly::try_constant(Fr::one()) Poly::try_constant(Fr::one())
} }
/// Returns the polynomial with constant value `c`. Panics if memory locking fails. /// Returns the polynomial with constant value `c`.
///
/// # Panics
///
/// Panics if we have reached the systems's locked memory limit.
pub fn constant(c: Fr) -> Self { pub fn constant(c: Fr) -> Self {
// We create a raw pointer to the field element within this method's stack frame so we can // We create a raw pointer to the field element within this method's stack frame so we can
// overwrite that portion of memory with zeros once we have copied the element onto the // overwrite that portion of memory with zeros once we have copied the element onto the
@ -421,12 +343,8 @@ impl Poly {
} }
/// Returns the polynomial with constant value `c`. This constructor is identical to /// Returns the polynomial with constant value `c`. This constructor is identical to
/// `Poly::constant()` in every way except that this constructor returns an `Err` if locking /// `Poly::constant()` in every way except that this constructor returns an `Err` where
/// the polynomial coefficients into RAM fails. /// `constant` would panic.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
pub fn try_constant(c: Fr) -> Result<Self> { pub fn try_constant(c: Fr) -> Result<Self> {
// We create a raw pointer to the field element within this method's stack frame so we can // We create a raw pointer to the field element within this method's stack frame so we can
// overwrite that portion of memory with zeros once we have copied the element onto the // overwrite that portion of memory with zeros once we have copied the element onto the
@ -437,30 +355,17 @@ impl Poly {
res res
} }
/// Returns the identity function, i.e. the polynomial "`x`". Panics if mlocking fails. /// Returns the identity function, i.e. the polynomial "`x`".
///
/// # Panics
///
/// Panics if we have reached the system's locked memory limit.
pub fn identity() -> Self { pub fn identity() -> Self {
Poly::monomial(1) Poly::monomial(1)
} }
/// Returns the identity function, i.e. the polynomial `x`. Returns an `Err` if mlocking /// Returns the identity function, i.e. the polynomial `x`.
/// fails.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
pub fn try_identity() -> Result<Self> { pub fn try_identity() -> Result<Self> {
Poly::try_monomial(1) Poly::try_monomial(1)
} }
/// Returns the (monic) monomial: `x.pow(degree)`. Panics if mlocking fails. /// Returns the (monic) monomial: `x.pow(degree)`.
///
/// # Panics
///
/// Panics if we have reached the systems's locked memory limit.
pub fn monomial(degree: usize) -> Self { pub fn monomial(degree: usize) -> Self {
Poly::try_monomial(degree).unwrap_or_else(|e| { Poly::try_monomial(degree).unwrap_or_else(|e| {
panic!( panic!(
@ -470,11 +375,7 @@ impl Poly {
}) })
} }
/// Returns the (monic) monomial: `x.pow(degree)`. Returns an `Err` if mlocking fails. /// Returns the (monic) monomial: `x.pow(degree)`.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
pub fn try_monomial(degree: usize) -> Result<Self> { pub fn try_monomial(degree: usize) -> Result<Self> {
let coeff: Vec<Fr> = iter::repeat(Fr::zero()) let coeff: Vec<Fr> = iter::repeat(Fr::zero())
.take(degree) .take(degree)
@ -485,10 +386,6 @@ impl Poly {
/// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values /// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values
/// `(x, f(x))`. /// `(x, f(x))`.
///
/// # Panics
///
/// Panics if we have reached the systems's locked memory limit.
pub fn interpolate<T, U, I>(samples_repr: I) -> Self pub fn interpolate<T, U, I>(samples_repr: I) -> Self
where where
I: IntoIterator<Item = (T, U)>, I: IntoIterator<Item = (T, U)>,
@ -501,10 +398,6 @@ impl Poly {
/// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values /// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values
/// `(x, f(x))`. /// `(x, f(x))`.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
pub fn try_interpolate<T, U, I>(samples_repr: I) -> Result<Self> pub fn try_interpolate<T, U, I>(samples_repr: I) -> Result<Self>
where where
I: IntoIterator<Item = (T, U)>, I: IntoIterator<Item = (T, U)>,
@ -552,10 +445,6 @@ impl Poly {
/// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values /// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values
/// `(x, f(x))`. /// `(x, f(x))`.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we hit the system's locked memory limit.
fn compute_interpolation(samples: &[(Fr, Fr)]) -> Result<Self> { fn compute_interpolation(samples: &[(Fr, Fr)]) -> Result<Self> {
if samples.is_empty() { if samples.is_empty() {
return Ok(Poly::zero()); return Ok(Poly::zero());
@ -680,32 +569,18 @@ pub struct BivarPoly {
coeff: Vec<Fr>, coeff: Vec<Fr>,
} }
/// # Panics
///
/// Panics if we have hit the system's locked memory limit when `mlock`ing the new instance of
/// `BivarPoly`.
impl Clone for BivarPoly { impl Clone for BivarPoly {
fn clone(&self) -> Self { fn clone(&self) -> Self {
let poly = BivarPoly { BivarPoly {
degree: self.degree, degree: self.degree,
coeff: self.coeff.clone(), coeff: self.coeff.clone(),
};
if let Err(e) = poly.mlock_secret() {
panic!("Failed to clone `BivarPoly`: {}", e);
} }
poly
} }
} }
/// # Panics
///
/// Panics if we fail to munlock the `coeff` vector.
impl Drop for BivarPoly { impl Drop for BivarPoly {
fn drop(&mut self) { fn drop(&mut self) {
self.zero_secret(); self.zero_secret();
if let Err(e) = self.munlock_secret() {
panic!("Failed to munlock `BivarPoly` during drop: {}", e);
}
} }
} }
@ -726,13 +601,8 @@ impl ContainsSecret for BivarPoly {
MemRange { ptr, n_bytes } MemRange { ptr, n_bytes }
} }
} }
impl BivarPoly { impl BivarPoly {
/// Creates a random polynomial. /// Creates a random polynomial.
///
/// # Panics
///
/// Panics if we have hit the system's locked memory limit.
pub fn random<R: Rng>(degree: usize, rng: &mut R) -> Self { pub fn random<R: Rng>(degree: usize, rng: &mut R) -> Self {
BivarPoly::try_random(degree, rng).unwrap_or_else(|e| { BivarPoly::try_random(degree, rng).unwrap_or_else(|e| {
panic!( panic!(
@ -743,10 +613,6 @@ impl BivarPoly {
} }
/// Creates a random polynomial. /// Creates a random polynomial.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
pub fn try_random<R: Rng>(degree: usize, rng: &mut R) -> Result<Self> { pub fn try_random<R: Rng>(degree: usize, rng: &mut R) -> Result<Self> {
let len = coeff_pos(degree, degree) let len = coeff_pos(degree, degree)
.and_then(|l| l.checked_add(1)) .and_then(|l| l.checked_add(1))
@ -755,7 +621,6 @@ impl BivarPoly {
degree, degree,
coeff: (0..len).map(|_| rng.gen()).collect(), coeff: (0..len).map(|_| rng.gen()).collect(),
}; };
poly.mlock_secret()?;
Ok(poly) Ok(poly)
} }
@ -783,21 +648,12 @@ impl BivarPoly {
} }
/// Returns the `x`-th row, as a univariate polynomial. /// Returns the `x`-th row, as a univariate polynomial.
///
/// # Panics
///
/// Panics if we have reached the system's locked memory limit.
pub fn row<T: IntoFr>(&self, x: T) -> Poly { pub fn row<T: IntoFr>(&self, x: T) -> Poly {
self.try_row(x) self.try_row(x)
.unwrap_or_else(|e| panic!("Failed to create `Poly` from row of `BivarPoly: {}`", e)) .unwrap_or_else(|e| panic!("Failed to create `Poly` from row of `BivarPoly: {}`", e))
} }
/// Returns the `x`-th row, as a univariate polynomial. /// Returns the `x`-th row, as a univariate polynomial.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit when
/// creating the new `Poly` instance.
pub fn try_row<T: IntoFr>(&self, x: T) -> Result<Poly> { pub fn try_row<T: IntoFr>(&self, x: T) -> Result<Poly> {
let x_pow = self.powers(x); let x_pow = self.powers(x);
let coeff: Vec<Fr> = (0..=self.degree) let coeff: Vec<Fr> = (0..=self.degree)

@ -1,32 +1,15 @@
//! Utilities for working with secret values. This module includes functionality for locking (and //! Utilities for working with secret values. This module includes functionality for overwriting
//! unlocking) memory into RAM and overwriting memory with zeros. //! memory with zeros.
use std::env;
use std::mem::{size_of, size_of_val}; use std::mem::{size_of, size_of_val};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use errno::errno; use memsec::memzero;
use memsec::{memzero, mlock, munlock};
use Fr; use Fr;
use error::{Error, Result}; use error::Result;
lazy_static! { lazy_static! {
/// Sets whether or not `mlock`ing is enabled. Memory locking is enabled by default; it can be
/// disabled by setting the environment variable `MLOCK_SECRETS=false`. This is useful when you
/// are running on a system where you do not have the ability to increase the systems locked
/// memory limit (which can be found using the Unix command: `ulimit -l`). For example, we
/// disable `mlock`ing of secrets when testing crates that depend on `threshold_crypto` when
/// running in Travis CI because Travis has a locked memory limit of 64kb, which we may exceed
/// while running `cargo test`. Disabling `mlock`ing for secret values allows secret keys to
/// be swapped/core-dumped to disk, resulting in unmanaged copies of secrets to hang around in
/// memory; this is significantly less secure than enabling memory locking (the default). Only
/// set `MLOCK_SECRETS=false` in development/testing.
pub(crate) static ref SHOULD_MLOCK_SECRETS: bool = match env::var("MLOCK_SECRETS") {
Ok(s) => s.parse().unwrap_or(true),
_ => true,
};
/// The size in bytes of a single field element. /// The size in bytes of a single field element.
pub(crate) static ref FR_SIZE: usize = size_of::<Fr>(); pub(crate) static ref FR_SIZE: usize = size_of::<Fr>();
} }
@ -46,73 +29,6 @@ pub(crate) trait ContainsSecret {
/// Returns the range of memory marked as secret. /// Returns the range of memory marked as secret.
fn secret_memory(&self) -> MemRange; fn secret_memory(&self) -> MemRange;
/// Locks a region of memory marked as secret into RAM.
///
/// The region of memory marked as secret will not be copied to disk, for example, during a
/// swap-to-disk or core dump. This method should be called upon instantiation of every type
/// that implements `ContainsSecret`.
///
/// Operating systems set a limit on the ammount of memory that a process may lock into RAM.
/// Due to this limitation, this method returns a `Result` in the event that memory locking
/// fails.
///
/// # Errors
///
/// An `Error::MlockFailed` is returned if we reach the system's locked memory limit or if we
/// attempt to lock an invalid region of memory.
fn mlock_secret(&self) -> Result<()> {
if !*SHOULD_MLOCK_SECRETS {
return Ok(());
}
let MemRange { ptr, n_bytes } = self.secret_memory();
if n_bytes == 0 {
return Ok(());
}
let mlock_succeeded = unsafe { mlock(ptr, n_bytes) };
if mlock_succeeded {
Ok(())
} else {
let e = Error::MlockFailed {
errno: errno(),
addr: format!("{:?}", ptr),
n_bytes,
};
Err(e)
}
}
/// Unlocks the memory lock for a region of memory marked as secret. If the secret region of
/// memory had not previosly been locked via the `.mlock_secret()` method, then this method
/// does nothing.
///
/// Once this method has been called, the secret region of memory will no longer be protected
/// from being copied to disk. This method should be called upon destruction of every type that
/// implements `ContainsSecret`.
///
/// # Errors
///
/// An `Error::MlockFailed` is returned if we attempt to lock an invalid region memory.
fn munlock_secret(&self) -> Result<()> {
if !*SHOULD_MLOCK_SECRETS {
return Ok(());
}
let MemRange { ptr, n_bytes } = self.secret_memory();
if n_bytes == 0 {
return Ok(());
}
let munlock_succeeded = unsafe { munlock(ptr, n_bytes) };
if munlock_succeeded {
Ok(())
} else {
let e = Error::MunlockFailed {
errno: errno(),
addr: format!("{:?}", ptr),
n_bytes,
};
Err(e)
}
}
/// Overwrites the secret region of memory with zeros. /// Overwrites the secret region of memory with zeros.
/// ///
/// This method should be called upon destruction of every type that implements `ContainsSecret`. /// This method should be called upon destruction of every type that implements `ContainsSecret`.
@ -122,7 +38,7 @@ pub(crate) trait ContainsSecret {
} }
} }
/// A wrapper around temporary values to ensuer that they are locked into RAM and cleared on drop. /// A wrapper around temporary values to ensuer that they are cleared on drop.
/// ///
/// `Safe<T>` is meant to be used a wrapper around `T`, where `T` is either an `&mut U` or /// `Safe<T>` is meant to be used a wrapper around `T`, where `T` is either an `&mut U` or
/// `Box<U>`. /// `Box<U>`.
@ -154,9 +70,6 @@ where
{ {
fn drop(&mut self) { fn drop(&mut self) {
self.zero_secret(); self.zero_secret();
if let Err(e) = self.munlock_secret() {
panic!("Failed to drop `Safe`: {}", e);
}
} }
} }
@ -180,8 +93,6 @@ where
} }
pub(crate) fn try_new(x: T) -> Result<Self> { pub(crate) fn try_new(x: T) -> Result<Self> {
let safe = Safe(x); Ok(Safe(x))
safe.mlock_secret()?;
Ok(safe)
} }
} }

Loading…
Cancel
Save