Added type for clearing temporary values.

This commit is contained in:
DrPeterVanNostrand 2018-09-10 19:36:53 +00:00
parent a2fa8a4b8f
commit 7149a84104
3 changed files with 249 additions and 310 deletions

View File

@ -26,18 +26,15 @@ extern crate tiny_keccak;
pub mod error;
mod into_fr;
pub mod poly;
mod secret;
pub mod serde_impl;
use std::env;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem::{size_of, size_of_val};
use std::ptr::copy_nonoverlapping;
use byteorder::{BigEndian, ByteOrder};
use errno::errno;
use init_with::InitWith;
use memsec::{memzero, mlock, munlock};
use pairing::bls12_381::{Bls12, Fr, G1Affine, G2Affine, G1, G2};
use pairing::{CurveAffine, CurveProjective, Engine, Field};
use rand::{ChaChaRng, OsRng, Rand, Rng, SeedableRng};
@ -46,56 +43,7 @@ use tiny_keccak::sha3_256;
use error::{Error, Result};
use into_fr::IntoFr;
use poly::{Commitment, Poly};
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);
}
use secret::{clear_fr, ContainsSecret, MemRange, FR_SIZE};
/// Wrapper for a byte array, whose `Debug` implementation outputs shortened hexadecimal strings.
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.
//
// # Panics
//
// Panics if we fail to unlock the memory containing the field element.
/// Zeroes out and unlocks the memory allocated from the `SecretKey`'s field element.
///
/// # Panics
///
/// Panics if we fail to unlock the memory containing the field element.
impl Drop for SecretKey {
fn drop(&mut self) {
self.zero_secret_memory();
if let Err(e) = self.munlock_secret_memory() {
self.zero_secret();
if let Err(e) = self.munlock_secret() {
panic!("Failed to drop `SecretKey`: {}", e);
}
}
@ -325,47 +273,10 @@ impl fmt::Debug for SecretKey {
}
impl ContainsSecret for SecretKey {
fn mlock_secret_memory(&self) -> Result<()> {
if !*SHOULD_MLOCK_SECRETS {
return Ok(());
}
fn secret_memory(&self) -> MemRange {
let ptr = &*self.0 as *const Fr as *mut u8;
let n_bytes = size_of_val(&*self.0);
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.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);
let n_bytes = *FR_SIZE;
MemRange { ptr, n_bytes }
}
}
@ -415,7 +326,7 @@ impl SecretKey {
}
clear_fr(fr_ptr as *mut u8);
let sk = SecretKey(boxed_fr);
sk.mlock_secret_memory()?;
sk.mlock_secret()?;
Ok(sk)
}

View File

@ -22,13 +22,13 @@ use std::hash::{Hash, Hasher};
use std::mem::size_of_val;
use std::{cmp, iter, ops};
use errno::errno;
use memsec::{memzero, mlock, munlock};
use pairing::bls12_381::{Fr, G1Affine, G1};
use pairing::{CurveAffine, CurveProjective, Field};
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.
#[derive(Serialize, Deserialize, PartialEq, Eq)]
@ -42,8 +42,8 @@ pub struct Poly {
///
/// # Panics
///
/// Panics if we have hit the system's locked memory limit when `mlock`ing the new instance of
/// `Poly`.
/// 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())
@ -60,8 +60,7 @@ impl Debug for Poly {
/// # Panics
///
/// Panics if we hit the system's locked memory limit or if we fail to unlock memory that has been
/// truncated from the `coeff` vector.
/// 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) {
@ -69,8 +68,7 @@ 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());
let n_coeffs_added = rhs_len - len;
if let Err(e) = self.extend_mlock(n_coeffs_added) {
if let Err(e) = self.mlock_secret() {
panic!(
"Failed to extend `Poly` memory lock during add-assign: {}",
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) {
self_c.add_assign(rhs_c);
}
if let Err(e) = self.remove_zeros() {
panic!("Failed to unlock `Poly` memory during add-assign: {}", e);
}
self.remove_zeros();
}
}
@ -105,24 +101,19 @@ impl<B: Borrow<Poly>> ops::Add<B> for Poly {
/// # Panics
///
/// Panics if we hit the system's locked memory limit or if we fail to unlock memory that has been
/// truncated from the `coeff` vector.
/// 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.coeff.is_empty() {
if !rhs.is_zero() {
self.coeff.push(rhs);
if let Err(e) = self.extend_mlock(1) {
panic!("Failed to extend `Poly` memory lock during add: {}", e);
}
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);
if let Err(e) = self.remove_zeros() {
panic!("Failed to unlock `Poly` memory during add: {}", e);
}
self.remove_zeros();
}
self
}
@ -138,16 +129,14 @@ impl<'a> ops::Add<u64> for Poly {
/// # Panics
///
/// Panics if we hit the system's locked memory limit or if we fail to unlock memory that has been
/// truncated from the `coeff` vector.
/// 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());
let n_coeffs_added = rhs_len - len;
if let Err(e) = self.extend_mlock(n_coeffs_added) {
if let Err(e) = self.mlock_secret() {
panic!(
"Failed to extend `Poly` memory lock during sub-assign: {}",
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) {
self_c.sub_assign(rhs_c);
}
if let Err(e) = self.remove_zeros() {
panic!("Failed to unlock `Poly` memory during sub-assign: {}", e);
}
self.remove_zeros();
}
}
@ -201,8 +188,7 @@ impl<'a> ops::Sub<u64> for Poly {
/// # Panics
///
/// Panics if we hit the system's locked memory limit or if we fail to unlock memory that has been
/// truncated from the `coeff` vector.
/// 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 {
@ -210,19 +196,20 @@ impl<'a, B: Borrow<Poly>> ops::Mul<B> for &'a Poly {
fn mul(self, rhs: B) -> Self::Output {
let rhs = rhs.borrow();
if rhs.coeff.is_empty() || self.coeff.is_empty() {
if rhs.is_zero() || self.is_zero() {
return Poly::zero();
}
let mut coeff = vec![Fr::zero(); self.coeff.len() + rhs.borrow().coeff.len() - 1];
let mut s; // TODO: Mlock and zero on drop.
for (i, cs) in self.coeff.iter().enumerate() {
for (j, cr) in rhs.coeff.iter().enumerate() {
s = *cs;
s.mul_assign(cr);
coeff[i + j].add_assign(&s);
let n_coeffs = self.coeff.len() + rhs.coeff.len() - 1;
let mut coeffs = vec![Fr::zero(); n_coeffs];
let mut tmp = Safe::new(Box::new(Fr::zero()));
for (i, ca) in self.coeff.iter().enumerate() {
for (j, cb) in rhs.coeff.iter().enumerate() {
*tmp = *ca;
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))
}
}
@ -244,7 +231,8 @@ impl<B: Borrow<Self>> ops::MulAssign<B> for Poly {
impl ops::MulAssign<Fr> for Poly {
fn mul_assign(&mut self, rhs: Fr) {
if rhs.is_zero() {
*self = Poly::zero();
self.zero_secret();
self.coeff.clear();
} else {
for c in &mut self.coeff {
c.mul_assign(&rhs);
@ -262,10 +250,7 @@ impl<'a> ops::Mul<&'a Fr> for Poly {
fn mul(mut self, rhs: &Fr) -> Self::Output {
if rhs.is_zero() {
self.zero_secret_memory();
if let Err(e) = self.munlock_secret_memory() {
panic!("Failed to unlock `Poly` during multiplication: {}", e);
}
self.zero_secret();
self.coeff.clear();
} else {
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.
impl Drop for Poly {
fn drop(&mut self) {
self.zero_secret_memory();
if let Err(e) = self.munlock_secret_memory() {
self.zero_secret();
if let Err(e) = self.munlock_secret() {
panic!("Failed to munlock `Poly` during drop: {}", e);
}
}
@ -333,56 +318,10 @@ impl From<Vec<Fr>> for Poly {
}
impl ContainsSecret for Poly {
fn mlock_secret_memory(&self) -> Result<()> {
if !*SHOULD_MLOCK_SECRETS {
return Ok(());
}
fn secret_memory(&self) -> MemRange {
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 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);
}
MemRange { ptr, n_bytes }
}
}
@ -396,7 +335,7 @@ impl Poly {
/// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit.
pub fn try_from(coeff: Vec<Fr>) -> Result<Self> {
let poly = Poly { coeff };
poly.mlock_secret_memory()?;
poly.mlock_secret()?;
Ok(poly)
}
@ -433,6 +372,11 @@ impl Poly {
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
/// `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`.
@ -596,16 +540,10 @@ impl Poly {
}
/// Removes all trailing zero coefficients.
///
/// # Errors
///
/// An `Error::MunlockFailed` is returned if we failed to `munlock` the truncated portion of
/// the `coeff` vector.
fn remove_zeros(&mut self) -> Result<()> {
fn remove_zeros(&mut self) {
let zeros = self.coeff.iter().rev().take_while(|c| c.is_zero()).count();
let len = self.coeff.len() - zeros;
self.coeff.truncate(len);
self.truncate_mlock(zeros)
}
/// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values
@ -645,57 +583,6 @@ impl 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
/// the `Debug` implementation in that it *does* leak the secret prime
/// field elements.
@ -799,7 +686,7 @@ impl Clone for BivarPoly {
degree: self.degree,
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);
}
poly
@ -811,8 +698,8 @@ impl Clone for BivarPoly {
/// Panics if we fail to munlock the `coeff` vector.
impl Drop for BivarPoly {
fn drop(&mut self) {
self.zero_secret_memory();
if let Err(e) = self.munlock_secret_memory() {
self.zero_secret();
if let Err(e) = self.munlock_secret() {
panic!("Failed to munlock `BivarPoly` during drop: {}", e);
}
}
@ -829,56 +716,10 @@ impl Debug for BivarPoly {
}
impl ContainsSecret for BivarPoly {
fn mlock_secret_memory(&self) -> Result<()> {
if !*SHOULD_MLOCK_SECRETS {
return Ok(());
}
let ptr = self.coeff.as_ptr() as *mut u8;
fn secret_memory(&self) -> MemRange {
let ptr = self.coeff.as_ptr() as *const Fr as *mut u8;
let n_bytes = size_of_val(self.coeff.as_slice());
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)
}
}
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);
}
MemRange { ptr, n_bytes }
}
}
@ -907,7 +748,7 @@ impl BivarPoly {
degree,
coeff: (0..coeff_pos(degree + 1, 0)).map(|_| rng.gen()).collect(),
};
poly.mlock_secret_memory()?;
poly.mlock_secret()?;
Ok(poly)
}

187
src/secret.rs Normal file
View File

@ -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)
}
}