Added type for clearing temporary values.
This commit is contained in:
parent
b48a4b6812
commit
5f09f96345
115
src/lib.rs
115
src/lib.rs
|
@ -26,18 +26,15 @@ extern crate tiny_keccak;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod into_fr;
|
mod into_fr;
|
||||||
pub mod poly;
|
pub mod poly;
|
||||||
|
mod secret;
|
||||||
pub mod serde_impl;
|
pub mod serde_impl;
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::mem::{size_of, size_of_val};
|
|
||||||
use std::ptr::copy_nonoverlapping;
|
use std::ptr::copy_nonoverlapping;
|
||||||
|
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
use errno::errno;
|
|
||||||
use init_with::InitWith;
|
use init_with::InitWith;
|
||||||
use memsec::{memzero, mlock, munlock};
|
|
||||||
use pairing::bls12_381::{Bls12, Fr, G1Affine, G2Affine, G1, G2};
|
use pairing::bls12_381::{Bls12, Fr, G1Affine, G2Affine, G1, G2};
|
||||||
use pairing::{CurveAffine, CurveProjective, Engine, Field};
|
use pairing::{CurveAffine, CurveProjective, Engine, Field};
|
||||||
use rand::{ChaChaRng, OsRng, Rand, Rng, SeedableRng};
|
use rand::{ChaChaRng, OsRng, Rand, Rng, SeedableRng};
|
||||||
|
@ -46,56 +43,7 @@ use tiny_keccak::sha3_256;
|
||||||
use error::{Error, Result};
|
use error::{Error, Result};
|
||||||
use into_fr::IntoFr;
|
use into_fr::IntoFr;
|
||||||
use poly::{Commitment, Poly};
|
use poly::{Commitment, Poly};
|
||||||
|
use secret::{clear_fr, ContainsSecret, MemRange, FR_SIZE};
|
||||||
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 `Fr` field element.
|
|
||||||
static ref FR_SIZE: usize = size_of::<Fr>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overwrites a single field element with zeros.
|
|
||||||
pub(crate) fn clear_fr(fr_ptr: *mut u8) {
|
|
||||||
unsafe { memzero(fr_ptr, *FR_SIZE) };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Marks a type as containing one or more secret prime field elements.
|
|
||||||
pub(crate) trait ContainsSecret {
|
|
||||||
/// Calls the `mlock` system call on the region of memory allocated for the secret prime field
|
|
||||||
/// element or elements. This results in that region of memory not being being copied to disk,
|
|
||||||
/// either in a swap to disk or core dump. This method is called on every created instance of
|
|
||||||
/// a secret type.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// An `Error::MlockFailed` is returned if we failed to `mlock` the secret data.
|
|
||||||
fn mlock_secret_memory(&self) -> Result<()>;
|
|
||||||
|
|
||||||
/// Undoes the `mlock` on the secret region of memory via the `munlock` system call.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// An `Error::MunlockFailed` is returned if we failed to `munlock` the secret data; this
|
|
||||||
/// method is called on each secret type when it goes out of scope.
|
|
||||||
fn munlock_secret_memory(&self) -> Result<()>;
|
|
||||||
|
|
||||||
/// Overwrites the secret prime field element or elements with zeros; this method is called on
|
|
||||||
/// each each secret type when it goes out of scope.
|
|
||||||
fn zero_secret_memory(&self);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrapper for a byte array, whose `Debug` implementation outputs shortened hexadecimal strings.
|
/// Wrapper for a byte array, whose `Debug` implementation outputs shortened hexadecimal strings.
|
||||||
pub struct HexBytes<'a>(pub &'a [u8]);
|
pub struct HexBytes<'a>(pub &'a [u8]);
|
||||||
|
@ -303,15 +251,15 @@ impl Clone for SecretKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zeroes out and unlocks the memory allocated from the `SecretKey`'s field element.
|
/// Zeroes out and unlocks the memory allocated from the `SecretKey`'s field element.
|
||||||
//
|
///
|
||||||
// # Panics
|
/// # Panics
|
||||||
//
|
///
|
||||||
// Panics if we fail to unlock the memory containing the field element.
|
/// Panics if we fail to unlock the memory containing the field element.
|
||||||
impl Drop for SecretKey {
|
impl Drop for SecretKey {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.zero_secret_memory();
|
self.zero_secret();
|
||||||
if let Err(e) = self.munlock_secret_memory() {
|
if let Err(e) = self.munlock_secret() {
|
||||||
panic!("Failed to drop `SecretKey`: {}", e);
|
panic!("Failed to drop `SecretKey`: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,47 +273,10 @@ impl fmt::Debug for SecretKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContainsSecret for SecretKey {
|
impl ContainsSecret for SecretKey {
|
||||||
fn mlock_secret_memory(&self) -> Result<()> {
|
fn secret_memory(&self) -> MemRange {
|
||||||
if !*SHOULD_MLOCK_SECRETS {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let ptr = &*self.0 as *const Fr as *mut u8;
|
let ptr = &*self.0 as *const Fr as *mut u8;
|
||||||
let n_bytes = size_of_val(&*self.0);
|
let n_bytes = *FR_SIZE;
|
||||||
let mlock_succeeded = unsafe { mlock(ptr, n_bytes) };
|
MemRange { ptr, n_bytes }
|
||||||
if mlock_succeeded {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
let e = Error::MlockFailed {
|
|
||||||
errno: errno(),
|
|
||||||
addr: format!("{:?}", ptr),
|
|
||||||
n_bytes,
|
|
||||||
};
|
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn munlock_secret_memory(&self) -> Result<()> {
|
|
||||||
if !*SHOULD_MLOCK_SECRETS {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let ptr = &*self.0 as *const Fr as *mut u8;
|
|
||||||
let n_bytes = size_of_val(&*self.0);
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn zero_secret_memory(&self) {
|
|
||||||
let ptr = &*self.0 as *const Fr as *mut u8;
|
|
||||||
clear_fr(ptr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,7 +326,7 @@ impl SecretKey {
|
||||||
}
|
}
|
||||||
clear_fr(fr_ptr as *mut u8);
|
clear_fr(fr_ptr as *mut u8);
|
||||||
let sk = SecretKey(boxed_fr);
|
let sk = SecretKey(boxed_fr);
|
||||||
sk.mlock_secret_memory()?;
|
sk.mlock_secret()?;
|
||||||
Ok(sk)
|
Ok(sk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
253
src/poly.rs
253
src/poly.rs
|
@ -22,13 +22,13 @@ use std::hash::{Hash, Hasher};
|
||||||
use std::mem::size_of_val;
|
use std::mem::size_of_val;
|
||||||
use std::{cmp, iter, ops};
|
use std::{cmp, iter, ops};
|
||||||
|
|
||||||
use errno::errno;
|
|
||||||
use memsec::{memzero, mlock, munlock};
|
|
||||||
use pairing::bls12_381::{Fr, G1Affine, G1};
|
use pairing::bls12_381::{Fr, G1Affine, G1};
|
||||||
use pairing::{CurveAffine, CurveProjective, Field};
|
use pairing::{CurveAffine, CurveProjective, Field};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use super::{clear_fr, ContainsSecret, Error, IntoFr, Result, FR_SIZE, SHOULD_MLOCK_SECRETS};
|
use error::Result;
|
||||||
|
use into_fr::IntoFr;
|
||||||
|
use secret::{clear_fr, ContainsSecret, MemRange, Safe};
|
||||||
|
|
||||||
/// A univariate polynomial in the prime field.
|
/// A univariate polynomial in the prime field.
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
@ -42,8 +42,8 @@ pub struct Poly {
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if we have hit the system's locked memory limit when `mlock`ing the new instance of
|
/// Panics if we reach the system's locked memory limit when locking the polynomial's coefficients
|
||||||
/// `Poly`.
|
/// 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())
|
||||||
|
@ -60,8 +60,7 @@ impl Debug for Poly {
|
||||||
|
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if we hit the system's locked memory limit or if we fail to unlock memory that has been
|
/// Panics if we reach the system's locked memory limit.
|
||||||
/// truncated from the `coeff` vector.
|
|
||||||
#[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) {
|
||||||
|
@ -69,8 +68,7 @@ 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());
|
||||||
let n_coeffs_added = rhs_len - len;
|
if let Err(e) = self.mlock_secret() {
|
||||||
if let Err(e) = self.extend_mlock(n_coeffs_added) {
|
|
||||||
panic!(
|
panic!(
|
||||||
"Failed to extend `Poly` memory lock during add-assign: {}",
|
"Failed to extend `Poly` memory lock during add-assign: {}",
|
||||||
e
|
e
|
||||||
|
@ -80,9 +78,7 @@ impl<B: Borrow<Poly>> ops::AddAssign<B> for Poly {
|
||||||
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) {
|
||||||
self_c.add_assign(rhs_c);
|
self_c.add_assign(rhs_c);
|
||||||
}
|
}
|
||||||
if let Err(e) = self.remove_zeros() {
|
self.remove_zeros();
|
||||||
panic!("Failed to unlock `Poly` memory during add-assign: {}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,24 +101,19 @@ impl<B: Borrow<Poly>> ops::Add<B> for Poly {
|
||||||
|
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if we hit the system's locked memory limit or if we fail to unlock memory that has been
|
/// Panics if we reach the system's locked memory limit.
|
||||||
/// truncated from the `coeff` vector.
|
|
||||||
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.coeff.is_empty() {
|
if self.is_zero() && !rhs.is_zero() {
|
||||||
if !rhs.is_zero() {
|
|
||||||
self.coeff.push(rhs);
|
self.coeff.push(rhs);
|
||||||
if let Err(e) = self.extend_mlock(1) {
|
if let Err(e) = self.mlock_secret() {
|
||||||
panic!("Failed to extend `Poly` memory lock during add: {}", e);
|
panic!("Failed to extend `Poly` memory lock during add: {}", e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
self.coeff[0].add_assign(&rhs);
|
self.coeff[0].add_assign(&rhs);
|
||||||
if let Err(e) = self.remove_zeros() {
|
self.remove_zeros();
|
||||||
panic!("Failed to unlock `Poly` memory during add: {}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -138,16 +129,14 @@ impl<'a> ops::Add<u64> for Poly {
|
||||||
|
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if we hit the system's locked memory limit or if we fail to unlock memory that has been
|
/// Panics if we reach the system's locked memory limit.
|
||||||
/// truncated from the `coeff` vector.
|
|
||||||
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());
|
||||||
let n_coeffs_added = rhs_len - len;
|
if let Err(e) = self.mlock_secret() {
|
||||||
if let Err(e) = self.extend_mlock(n_coeffs_added) {
|
|
||||||
panic!(
|
panic!(
|
||||||
"Failed to extend `Poly` memory lock during sub-assign: {}",
|
"Failed to extend `Poly` memory lock during sub-assign: {}",
|
||||||
e
|
e
|
||||||
|
@ -157,9 +146,7 @@ impl<B: Borrow<Poly>> ops::SubAssign<B> for Poly {
|
||||||
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) {
|
||||||
self_c.sub_assign(rhs_c);
|
self_c.sub_assign(rhs_c);
|
||||||
}
|
}
|
||||||
if let Err(e) = self.remove_zeros() {
|
self.remove_zeros();
|
||||||
panic!("Failed to unlock `Poly` memory during sub-assign: {}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,8 +188,7 @@ impl<'a> ops::Sub<u64> for Poly {
|
||||||
|
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if we hit the system's locked memory limit or if we fail to unlock memory that has been
|
/// Panics if we reach the system's locked memory limit.
|
||||||
/// truncated from the `coeff` vector.
|
|
||||||
// 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 {
|
||||||
|
@ -210,19 +196,20 @@ impl<'a, B: Borrow<Poly>> ops::Mul<B> for &'a Poly {
|
||||||
|
|
||||||
fn mul(self, rhs: B) -> Self::Output {
|
fn mul(self, rhs: B) -> Self::Output {
|
||||||
let rhs = rhs.borrow();
|
let rhs = rhs.borrow();
|
||||||
if rhs.coeff.is_empty() || self.coeff.is_empty() {
|
if rhs.is_zero() || self.is_zero() {
|
||||||
return Poly::zero();
|
return Poly::zero();
|
||||||
}
|
}
|
||||||
let mut coeff = vec![Fr::zero(); self.coeff.len() + rhs.borrow().coeff.len() - 1];
|
let n_coeffs = self.coeff.len() + rhs.coeff.len() - 1;
|
||||||
let mut s; // TODO: Mlock and zero on drop.
|
let mut coeffs = vec![Fr::zero(); n_coeffs];
|
||||||
for (i, cs) in self.coeff.iter().enumerate() {
|
let mut tmp = Safe::new(Box::new(Fr::zero()));
|
||||||
for (j, cr) in rhs.coeff.iter().enumerate() {
|
for (i, ca) in self.coeff.iter().enumerate() {
|
||||||
s = *cs;
|
for (j, cb) in rhs.coeff.iter().enumerate() {
|
||||||
s.mul_assign(cr);
|
*tmp = *ca;
|
||||||
coeff[i + j].add_assign(&s);
|
tmp.mul_assign(cb);
|
||||||
|
coeffs[i + j].add_assign(&*tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Poly::try_from(coeff)
|
Poly::try_from(coeffs)
|
||||||
.unwrap_or_else(|e| panic!("Failed to create a new `Poly` during muliplication: {}", e))
|
.unwrap_or_else(|e| panic!("Failed to create a new `Poly` during muliplication: {}", e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,7 +231,8 @@ impl<B: Borrow<Self>> ops::MulAssign<B> for Poly {
|
||||||
impl ops::MulAssign<Fr> for Poly {
|
impl ops::MulAssign<Fr> for Poly {
|
||||||
fn mul_assign(&mut self, rhs: Fr) {
|
fn mul_assign(&mut self, rhs: Fr) {
|
||||||
if rhs.is_zero() {
|
if rhs.is_zero() {
|
||||||
*self = Poly::zero();
|
self.zero_secret();
|
||||||
|
self.coeff.clear();
|
||||||
} else {
|
} else {
|
||||||
for c in &mut self.coeff {
|
for c in &mut self.coeff {
|
||||||
c.mul_assign(&rhs);
|
c.mul_assign(&rhs);
|
||||||
|
@ -262,10 +250,7 @@ impl<'a> ops::Mul<&'a Fr> for Poly {
|
||||||
|
|
||||||
fn mul(mut self, rhs: &Fr) -> Self::Output {
|
fn mul(mut self, rhs: &Fr) -> Self::Output {
|
||||||
if rhs.is_zero() {
|
if rhs.is_zero() {
|
||||||
self.zero_secret_memory();
|
self.zero_secret();
|
||||||
if let Err(e) = self.munlock_secret_memory() {
|
|
||||||
panic!("Failed to unlock `Poly` during multiplication: {}", e);
|
|
||||||
}
|
|
||||||
self.coeff.clear();
|
self.coeff.clear();
|
||||||
} else {
|
} else {
|
||||||
self.coeff.iter_mut().for_each(|c| c.mul_assign(rhs));
|
self.coeff.iter_mut().for_each(|c| c.mul_assign(rhs));
|
||||||
|
@ -312,8 +297,8 @@ impl ops::Mul<u64> for Poly {
|
||||||
/// Panics if we fail to munlock the `coeff` vector.
|
/// 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_memory();
|
self.zero_secret();
|
||||||
if let Err(e) = self.munlock_secret_memory() {
|
if let Err(e) = self.munlock_secret() {
|
||||||
panic!("Failed to munlock `Poly` during drop: {}", e);
|
panic!("Failed to munlock `Poly` during drop: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -333,56 +318,10 @@ impl From<Vec<Fr>> for Poly {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContainsSecret for Poly {
|
impl ContainsSecret for Poly {
|
||||||
fn mlock_secret_memory(&self) -> Result<()> {
|
fn secret_memory(&self) -> MemRange {
|
||||||
if !*SHOULD_MLOCK_SECRETS {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let ptr = self.coeff.as_ptr() as *mut u8;
|
let ptr = self.coeff.as_ptr() as *mut u8;
|
||||||
let n_bytes = size_of_val(self.coeff.as_slice());
|
let n_bytes = size_of_val(self.coeff.as_slice());
|
||||||
if n_bytes == 0 {
|
MemRange { ptr, n_bytes }
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn munlock_secret_memory(&self) -> Result<()> {
|
|
||||||
if !*SHOULD_MLOCK_SECRETS {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let ptr = self.coeff.as_ptr() as *mut u8;
|
|
||||||
let n_bytes = size_of_val(self.coeff.as_slice());
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn zero_secret_memory(&self) {
|
|
||||||
let ptr = self.coeff.as_ptr() as *mut u8;
|
|
||||||
let n_bytes = size_of_val(self.coeff.as_slice());
|
|
||||||
unsafe {
|
|
||||||
memzero(ptr, n_bytes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,7 +335,7 @@ impl Poly {
|
||||||
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
|
/// 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_memory()?;
|
poly.mlock_secret()?;
|
||||||
Ok(poly)
|
Ok(poly)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,6 +372,11 @@ impl Poly {
|
||||||
Poly { coeff: vec![] }
|
Poly { coeff: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the polynomial is the constant value `0`.
|
||||||
|
pub fn is_zero(&self) -> bool {
|
||||||
|
self.coeff.iter().all(|coeff| coeff.is_zero())
|
||||||
|
}
|
||||||
|
|
||||||
/// 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 if locking the `coeff`
|
||||||
/// vector into RAM fails, whereas `Poly::try_one()` returns an `Err`.
|
/// vector into RAM fails, whereas `Poly::try_one()` returns an `Err`.
|
||||||
|
@ -596,16 +540,10 @@ impl Poly {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes all trailing zero coefficients.
|
/// Removes all trailing zero coefficients.
|
||||||
///
|
fn remove_zeros(&mut self) {
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// An `Error::MunlockFailed` is returned if we failed to `munlock` the truncated portion of
|
|
||||||
/// the `coeff` vector.
|
|
||||||
fn remove_zeros(&mut self) -> Result<()> {
|
|
||||||
let zeros = self.coeff.iter().rev().take_while(|c| c.is_zero()).count();
|
let zeros = self.coeff.iter().rev().take_while(|c| c.is_zero()).count();
|
||||||
let len = self.coeff.len() - zeros;
|
let len = self.coeff.len() - zeros;
|
||||||
self.coeff.truncate(len);
|
self.coeff.truncate(len);
|
||||||
self.truncate_mlock(zeros)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
|
@ -645,57 +583,6 @@ impl Poly {
|
||||||
Ok(poly)
|
Ok(poly)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes the `mlock` for `len` elements that have been truncated from the `coeff` vector.
|
|
||||||
fn truncate_mlock(&self, len: usize) -> Result<()> {
|
|
||||||
if !*SHOULD_MLOCK_SECRETS {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let n_bytes_truncated = *FR_SIZE * len;
|
|
||||||
if n_bytes_truncated == 0 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
let ptr = self.coeff.as_ptr().offset(self.coeff.len() as isize) as *mut u8;
|
|
||||||
let munlock_succeeded = munlock(ptr, n_bytes_truncated);
|
|
||||||
if munlock_succeeded {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
let e = Error::MunlockFailed {
|
|
||||||
errno: errno(),
|
|
||||||
addr: format!("{:?}", ptr),
|
|
||||||
n_bytes: n_bytes_truncated,
|
|
||||||
};
|
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extends the `mlock` on the `coeff` vector when `len` new elements are added.
|
|
||||||
fn extend_mlock(&self, len: usize) -> Result<()> {
|
|
||||||
if !*SHOULD_MLOCK_SECRETS {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let n_bytes_extended = *FR_SIZE * len;
|
|
||||||
if n_bytes_extended == 0 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let offset = (self.coeff.len() - len) as isize;
|
|
||||||
unsafe {
|
|
||||||
let ptr = self.coeff.as_ptr().offset(offset) as *mut u8;
|
|
||||||
let mlock_succeeded = mlock(ptr, n_bytes_extended);
|
|
||||||
if mlock_succeeded {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
let e = Error::MunlockFailed {
|
|
||||||
errno: errno(),
|
|
||||||
addr: format!("{:?}", ptr),
|
|
||||||
n_bytes: n_bytes_extended,
|
|
||||||
};
|
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a non-redacted debug string. This method differs from
|
/// Generates a non-redacted debug string. This method differs from
|
||||||
/// the `Debug` implementation in that it *does* leak the secret prime
|
/// the `Debug` implementation in that it *does* leak the secret prime
|
||||||
/// field elements.
|
/// field elements.
|
||||||
|
@ -799,7 +686,7 @@ impl Clone for BivarPoly {
|
||||||
degree: self.degree,
|
degree: self.degree,
|
||||||
coeff: self.coeff.clone(),
|
coeff: self.coeff.clone(),
|
||||||
};
|
};
|
||||||
if let Err(e) = poly.mlock_secret_memory() {
|
if let Err(e) = poly.mlock_secret() {
|
||||||
panic!("Failed to clone `BivarPoly`: {}", e);
|
panic!("Failed to clone `BivarPoly`: {}", e);
|
||||||
}
|
}
|
||||||
poly
|
poly
|
||||||
|
@ -811,8 +698,8 @@ impl Clone for BivarPoly {
|
||||||
/// Panics if we fail to munlock the `coeff` vector.
|
/// 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_memory();
|
self.zero_secret();
|
||||||
if let Err(e) = self.munlock_secret_memory() {
|
if let Err(e) = self.munlock_secret() {
|
||||||
panic!("Failed to munlock `BivarPoly` during drop: {}", e);
|
panic!("Failed to munlock `BivarPoly` during drop: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -829,56 +716,10 @@ impl Debug for BivarPoly {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContainsSecret for BivarPoly {
|
impl ContainsSecret for BivarPoly {
|
||||||
fn mlock_secret_memory(&self) -> Result<()> {
|
fn secret_memory(&self) -> MemRange {
|
||||||
if !*SHOULD_MLOCK_SECRETS {
|
let ptr = self.coeff.as_ptr() as *const Fr as *mut u8;
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let ptr = self.coeff.as_ptr() as *mut u8;
|
|
||||||
let n_bytes = size_of_val(self.coeff.as_slice());
|
let n_bytes = size_of_val(self.coeff.as_slice());
|
||||||
if n_bytes == 0 {
|
MemRange { ptr, n_bytes }
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn munlock_secret_memory(&self) -> Result<()> {
|
|
||||||
if !*SHOULD_MLOCK_SECRETS {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let ptr = self.coeff.as_ptr() as *mut u8;
|
|
||||||
let n_bytes = size_of_val(self.coeff.as_slice());
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn zero_secret_memory(&self) {
|
|
||||||
let ptr = self.coeff.as_ptr() as *mut u8;
|
|
||||||
let n_bytes = size_of_val(self.coeff.as_slice());
|
|
||||||
unsafe {
|
|
||||||
memzero(ptr, n_bytes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -907,7 +748,7 @@ impl BivarPoly {
|
||||||
degree,
|
degree,
|
||||||
coeff: (0..coeff_pos(degree + 1, 0)).map(|_| rng.gen()).collect(),
|
coeff: (0..coeff_pos(degree + 1, 0)).map(|_| rng.gen()).collect(),
|
||||||
};
|
};
|
||||||
poly.mlock_secret_memory()?;
|
poly.mlock_secret()?;
|
||||||
Ok(poly)
|
Ok(poly)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
//! Utilities for working with secret values. This module includes functionality for locking (and
|
||||||
|
//! unlocking) memory into RAM and 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 pairing::bls12_381::Fr;
|
||||||
|
|
||||||
|
use error::{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>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overwrites a single field element with zeros.
|
||||||
|
pub(crate) fn clear_fr(fr_ptr: *mut u8) {
|
||||||
|
unsafe { memzero(fr_ptr, *FR_SIZE) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct MemRange {
|
||||||
|
pub ptr: *mut u8,
|
||||||
|
pub n_bytes: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Marks a type as containing some secret value.
|
||||||
|
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`.
|
||||||
|
fn zero_secret(&self) {
|
||||||
|
let MemRange { ptr, n_bytes } = self.secret_memory();
|
||||||
|
unsafe { memzero(ptr, n_bytes) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper around temporary values to ensuer that they are locked into RAM and cleared on drop.
|
||||||
|
///
|
||||||
|
/// `Safe<T>` is meant to be used a wrapper around `T`, where `T` is either an `&mut U` or
|
||||||
|
/// `Box<U>`.
|
||||||
|
pub(crate) struct Safe<T: DerefMut>(T);
|
||||||
|
|
||||||
|
impl<T> Deref for Safe<T>
|
||||||
|
where
|
||||||
|
T: DerefMut,
|
||||||
|
{
|
||||||
|
type Target = T::Target;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&*(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DerefMut for Safe<T>
|
||||||
|
where
|
||||||
|
T: DerefMut,
|
||||||
|
{
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut *(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for Safe<T>
|
||||||
|
where
|
||||||
|
T: DerefMut,
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.zero_secret();
|
||||||
|
if let Err(e) = self.munlock_secret() {
|
||||||
|
panic!("Failed to drop `Safe`: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ContainsSecret for Safe<T>
|
||||||
|
where
|
||||||
|
T: DerefMut,
|
||||||
|
{
|
||||||
|
fn secret_memory(&self) -> MemRange {
|
||||||
|
let ptr = &*self.0 as *const T::Target as *mut u8;
|
||||||
|
let n_bytes = size_of_val(&*self.0);
|
||||||
|
MemRange { ptr, n_bytes }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Safe<T>
|
||||||
|
where
|
||||||
|
T: DerefMut,
|
||||||
|
{
|
||||||
|
pub(crate) fn new(x: T) -> Self {
|
||||||
|
Safe::try_new(x).unwrap_or_else(|e| panic!("Failed to create `Safe`: {}", e))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn try_new(x: T) -> Result<Self> {
|
||||||
|
let safe = Safe(x);
|
||||||
|
safe.mlock_secret()?;
|
||||||
|
Ok(safe)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue