parent
b48a4b6812
commit
5f09f96345
@ -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