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:
```
$ 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
Run examples from the [`examples`](examples) directory using:
```
$ MLOCK_SECRETS=false cargo run --example <example name>
$ cargo run --example <example name>
```
Also see the
[distributed_key_generation](https://github.com/poanetwork/threshold_crypto/blob/d81953b55d181311c2a4eed2b6c34059fcf3fdae/src/poly.rs#L967)
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
The basic usage outline is:

@ -1,7 +1,5 @@
//! Crypto errors.
use errno::Errno;
/// A crypto error.
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum Error {
@ -11,32 +9,6 @@ pub enum Error {
DuplicateEntry,
#[fail(display = "The degree is too high for the coefficients to be indexed by usize.")]
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 {}

@ -227,11 +227,6 @@ impl fmt::Debug for SignatureShare {
pub struct SecretKey(Box<Fr>);
/// 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 {
fn default() -> Self {
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
/// 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 {
fn rand<R: Rng>(rng: &mut R) -> Self {
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.
///
/// # Panics
///
/// Panics if we have reached the system's locked memory limit when locking the secret field
/// element into RAM.
impl Clone for SecretKey {
fn clone(&self) -> Self {
let mut fr = *self.0;
@ -277,12 +262,8 @@ impl Clone for SecretKey {
impl Drop for SecretKey {
fn drop(&mut self) {
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.
impl fmt::Debug for SecretKey {
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
/// 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
/// 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 {
SecretKey::try_from_mut(fr)
.unwrap_or_else(|e| panic!("Falied to create `SecretKey`: {}", e))
@ -326,16 +298,10 @@ impl SecretKey {
/// stored in the returned `SecretKey`.
///
/// 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
/// `SecretKey::from_mut()` will panic.
/// constructor will return an `Err` where `SecretKey::from_mut()` would panic.
///
/// *WARNING* this constructor will overwrite the referenced `Fr` element with zeros after it
/// 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> {
let fr_ptr = fr as *mut Fr;
let mut boxed_fr = Box::new(Fr::zero());
@ -344,7 +310,6 @@ impl SecretKey {
}
clear_fr(fr_ptr);
let sk = SecretKey(boxed_fr);
sk.mlock_secret()?;
Ok(sk)
}
@ -354,15 +319,6 @@ impl SecretKey {
/// `SecretKey::try_random()` constructors, which use
/// [`rand::thead_rng()`](https://docs.rs/rand/0.4.3/rand/fn.thread_rng.html) internally as
/// 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 {
let mut rng = rand::thread_rng();
SecretKey::rand(&mut rng)
@ -374,14 +330,6 @@ impl SecretKey {
/// `SecretKey::try_random()` constructors, which use
/// [`rand::thead_rng()`](https://docs.rs/rand/0.4.3/rand/fn.thread_rng.html) internally as
/// 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> {
let mut rng = rand::thread_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
/// `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
/// 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 {
match SecretKey::try_from_mut(fr) {
Ok(sk) => SecretKeyShare(sk),
@ -463,17 +403,6 @@ impl SecretKeyShare {
/// 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
/// `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> {
SecretKey::try_from_mut(fr).map(SecretKeyShare)
}
@ -629,11 +558,7 @@ impl From<Poly> for SecretKeySet {
impl SecretKeySet {
/// 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
/// way except that this constructor panics if locking secret values into RAM fails.
///
/// # Panics
///
/// Panics if we reach the system's locked memory limit.
/// way except that this constructor panics if the other returns an error.
pub fn random<R: Rng>(threshold: usize, rng: &mut R) -> Self {
SecretKeySet::try_random(threshold, rng)
.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
/// 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.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
/// way except that this constructor returns an `Err` where the `random` would panic.
pub fn try_random<R: Rng>(threshold: usize, rng: &mut R) -> Result<Self> {
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
/// `.try_secret_key_share()` in every way except that this method panics if
/// locking secret values into memory fails, whereas `.try_secret_key_share()`
/// returns an `Err`.
///
/// # Panics
///
/// Panics if we reach the system's locked memory limit.
/// where `.try_secret_key_share()` would return an `Err`.
pub fn secret_key_share<T: IntoFr>(&self, i: T) -> SecretKeyShare {
self.try_secret_key_share(i)
.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
/// `.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
/// panic.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
/// where `.secret_key_share()` would panic.
pub fn try_secret_key_share<T: IntoFr>(&self, i: T) -> Result<SecretKeyShare> {
let mut fr = self.poly.evaluate(into_fr_plus_1(i));
SecretKeyShare::try_from_mut(&mut fr)
@ -689,12 +600,7 @@ impl SecretKeySet {
}
}
/// Returns the secret master key. Panics if mlocking fails.
///
/// # Panics
///
/// Panics if we have hit the system's locked memory limit when `mlock`ing the new instance of
/// `SecretKey`.
/// Returns the secret master key.
#[cfg(test)]
fn secret_key(&self) -> SecretKey {
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.
///
/// # Panics
///
/// Panics if we reach the system's locked memory limit when locking the polynomial's coefficients
/// into RAM.
impl Clone for Poly {
fn clone(&self) -> Self {
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))]
impl<B: Borrow<Poly>> ops::AddAssign<B> for Poly {
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();
if rhs_len > len {
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) {
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 {
type Output = Poly;
fn add(mut self, rhs: Fr) -> Self::Output {
if self.is_zero() && !rhs.is_zero() {
self.coeff.push(rhs);
if let Err(e) = self.mlock_secret() {
panic!("Failed to extend `Poly` memory lock during add: {}", e);
}
} else {
self.coeff[0].add_assign(&rhs);
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 {
fn sub_assign(&mut self, rhs: B) {
let len = self.coeff.len();
let rhs_len = rhs.borrow().coeff.len();
if rhs_len > len {
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) {
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.
#[cfg_attr(feature = "cargo-clippy", allow(suspicious_arithmetic_impl))]
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 {
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 {
fn drop(&mut self) {
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
/// coefficients of the polynomial. We lock the region of the heap where the polynomial
/// coefficients are allocated.
///
/// # Panics
///
/// Panics if we have reached the system's locked memory limit.
/// coefficients of the polynomial.
impl From<Vec<Fr>> for Poly {
fn from(coeffs: Vec<Fr>) -> Self {
Poly::try_from(coeffs).unwrap_or_else(|e| panic!("Failed to create `Poly`: {}", e))
@ -327,38 +280,23 @@ impl ContainsSecret for Poly {
impl Poly {
/// 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 are allocated.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
/// coefficients of the polynomial.
pub fn try_from(coeff: Vec<Fr>) -> Result<Self> {
let poly = Poly { coeff };
poly.mlock_secret()?;
Ok(poly)
}
/// 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
/// coefficients into RAM fails.
///
/// # Panics
///
/// Panics if we have reached the system's locked memory limit.
/// constructor in every way except that this constructor will panic where `random` would
/// return an error.
pub fn random<R: Rng>(degree: usize, rng: &mut R) -> Self {
Poly::try_random(degree, rng)
.unwrap_or_else(|e| panic!("Failed to create random `Poly`: {}", e))
}
/// 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
/// polynomial coefficients into RAM fails.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit or if
/// the degree is `usize::max_value()`.
/// constructor in every way except that this constructor will return an `Err` where
/// `try_random` would return an error.
pub fn try_random<R: Rng>(degree: usize, rng: &mut R) -> Result<Self> {
if degree == usize::max_value() {
return Err(Error::DegreeTooHigh);
@ -368,10 +306,6 @@ impl Poly {
}
/// 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 {
Poly { coeff: vec![] }
}
@ -382,33 +316,21 @@ impl Poly {
}
/// 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`
/// vector into RAM fails, whereas `Poly::try_one()` returns an `Err`.
///
/// # Panics
///
/// Panics if we have reached the system's locked memory limit.
/// `Poly::try_one()` in every way except that this constructor panics where `Poly::try_one()`
/// would return an `Err`.
pub fn one() -> Self {
Poly::try_one()
.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
/// `Poly::one()` in every way except that this constructor returns `Err` if locking the
/// `coeff` vector into RAM fails, whereas `Poly::one()` panics.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
/// `Poly::one()` in every way except that this constructor returns `Err` if where `Poly::one()`
/// would panic.
pub fn try_one() -> Result<Self> {
Poly::try_constant(Fr::one())
}
/// Returns the polynomial with constant value `c`. Panics if memory locking fails.
///
/// # Panics
///
/// Panics if we have reached the systems's locked memory limit.
/// Returns the polynomial with constant value `c`.
pub fn constant(c: Fr) -> Self {
// 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
@ -421,12 +343,8 @@ impl Poly {
}
/// 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
/// the polynomial coefficients into RAM fails.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
/// `Poly::constant()` in every way except that this constructor returns an `Err` where
/// `constant` would panic.
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
// overwrite that portion of memory with zeros once we have copied the element onto the
@ -437,30 +355,17 @@ impl Poly {
res
}
/// Returns the identity function, i.e. the polynomial "`x`". Panics if mlocking fails.
///
/// # Panics
///
/// Panics if we have reached the system's locked memory limit.
/// Returns the identity function, i.e. the polynomial "`x`".
pub fn identity() -> Self {
Poly::monomial(1)
}
/// Returns the identity function, i.e. the polynomial `x`. Returns an `Err` if mlocking
/// fails.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
/// Returns the identity function, i.e. the polynomial `x`.
pub fn try_identity() -> Result<Self> {
Poly::try_monomial(1)
}
/// Returns the (monic) monomial: `x.pow(degree)`. Panics if mlocking fails.
///
/// # Panics
///
/// Panics if we have reached the systems's locked memory limit.
/// Returns the (monic) monomial: `x.pow(degree)`.
pub fn monomial(degree: usize) -> Self {
Poly::try_monomial(degree).unwrap_or_else(|e| {
panic!(
@ -470,11 +375,7 @@ impl Poly {
})
}
/// Returns the (monic) monomial: `x.pow(degree)`. Returns an `Err` if mlocking fails.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
/// Returns the (monic) monomial: `x.pow(degree)`.
pub fn try_monomial(degree: usize) -> Result<Self> {
let coeff: Vec<Fr> = iter::repeat(Fr::zero())
.take(degree)
@ -485,10 +386,6 @@ impl Poly {
/// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values
/// `(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
where
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
/// `(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>
where
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
/// `(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> {
if samples.is_empty() {
return Ok(Poly::zero());
@ -680,32 +569,18 @@ pub struct BivarPoly {
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 {
fn clone(&self) -> Self {
let poly = BivarPoly {
BivarPoly {
degree: self.degree,
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 {
fn drop(&mut self) {
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 }
}
}
impl BivarPoly {
/// 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 {
BivarPoly::try_random(degree, rng).unwrap_or_else(|e| {
panic!(
@ -743,10 +613,6 @@ impl BivarPoly {
}
/// 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> {
let len = coeff_pos(degree, degree)
.and_then(|l| l.checked_add(1))
@ -755,7 +621,6 @@ impl BivarPoly {
degree,
coeff: (0..len).map(|_| rng.gen()).collect(),
};
poly.mlock_secret()?;
Ok(poly)
}
@ -783,21 +648,12 @@ impl BivarPoly {
}
/// 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 {
self.try_row(x)
.unwrap_or_else(|e| panic!("Failed to create `Poly` from row of `BivarPoly: {}`", e))
}
/// 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> {
let x_pow = self.powers(x);
let coeff: Vec<Fr> = (0..=self.degree)

@ -1,32 +1,15 @@
//! Utilities for working with secret values. This module includes functionality for locking (and
//! unlocking) memory into RAM and overwriting memory with zeros.
//! Utilities for working with secret values. This module includes functionality for overwriting
//! memory with zeros.
use std::env;
use std::mem::{size_of, size_of_val};
use std::ops::{Deref, DerefMut};
use errno::errno;
use memsec::{memzero, mlock, munlock};
use memsec::memzero;
use Fr;
use error::{Error, Result};
use error::Result;
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.
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.
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.
///
/// 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
/// `Box<U>`.
@ -154,9 +70,6 @@ where
{
fn drop(&mut self) {
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> {
let safe = Safe(x);
safe.mlock_secret()?;
Ok(safe)
Ok(Safe(x))
}
}

Loading…
Cancel
Save