Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added concept of expose_mut. reference to protected #2

Merged
merged 12 commits into from
Sep 8, 2024
Merged
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ jobs:
ci:
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
runner: [ubuntu-latest, macos-latest, windows-latest]
fail-fast: false
steps:
- uses: actions/checkout@v4

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/target
/.idea
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ version = "0.1.0"
edition = "2021"

[dependencies]
libc = "0.2.158"
memsec = "0.7.0"
zeroize = "1.8.1"

[target.'cfg(unix)'.dependencies]
libc = "0.2.158"
180 changes: 78 additions & 102 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! This is a fork of the [secrets](https://github.com/stouset/secrets) crate.
//! This crate adds `mlock` and `mprotect` to lock the secret's page in memory
//! and read only when exposed
//! This crate adds `mlock` to lock the secret's page in memory

#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![warn(missing_docs, rust_2018_idioms, unused_qualifications)]
Expand All @@ -9,45 +8,34 @@ use core::{
any,
fmt::{self, Debug},
};
use libc::{PROT_NONE, PROT_READ, PROT_WRITE};
use memsec::{mlock, mprotect, munlock};
use std::{mem, ptr::NonNull};

use zeroize::{Zeroize, ZeroizeOnDrop};

use memsec::{mlock, munlock};
use std::ops::{Deref, DerefMut};
pub use zeroize;
use zeroize::{Zeroize, ZeroizeOnDrop};

/// Wrapper for the inner secret. Can be exposed by [`ExposeSecret`]
pub struct SecretBox<S: Zeroize> {
inner_secret: Box<S>,
protected: bool,
}

impl<S: Zeroize> Zeroize for SecretBox<S> {
fn zeroize(&mut self) {
let len = mem::size_of_val(&*self.inner_secret);
self.inner_secret.as_mut().zeroize()
}
}

impl<S: Zeroize> Drop for SecretBox<S> {
fn drop(&mut self) {
let len = std::mem::size_of_val(&*self.inner_secret);

let secret_ptr = self.inner_secret.as_ref() as *const S;

unsafe {
if !munlock(secret_ptr as *mut u8, len) {
eprintln!("Unable to munlock variable")
}

if !mprotect(
NonNull::new(secret_ptr as *mut S).expect("Unable to convert ptr to NonNull"),
PROT_READ | PROT_WRITE,
) {
eprintln!("Unable to unprotect variable")
panic!("Unable to munlock variable")
}
}

self.inner_secret.as_mut().zeroize()
}
}

impl<S: Zeroize> Drop for SecretBox<S> {
fn drop(&mut self) {
self.zeroize()
}
}
Expand All @@ -63,37 +51,27 @@ impl<S: Zeroize> From<Box<S>> for SecretBox<S> {
impl<S: Zeroize> SecretBox<S> {
/// Create a secret value using a pre-boxed value.
pub fn new(boxed_secret: Box<S>) -> Self {
let len = mem::size_of_val(&*boxed_secret);
let len = std::mem::size_of_val(&*boxed_secret);

let secret_ptr = Box::into_raw(boxed_secret);

unsafe {
if !mlock(secret_ptr as *mut u8, len) {
panic!("Unable to mlock variable ")
}

if !mprotect(
NonNull::new(secret_ptr).expect("Unable to convert box to NonNull"),
PROT_NONE,
) {
panic!("Unable to protect secret")
}
}

let inner_secret = unsafe { Box::from_raw(secret_ptr) };

Self {
inner_secret,
protected: true,
}
Self { inner_secret }
}
}

impl<S: Zeroize + Default> SecretBox<S> {
/// Create a secret value using a function that can initialize the vale in-place.
pub fn new_with_mut(ctr: impl FnOnce(&mut S)) -> Self {
let mut secret = Self::default();
ctr(secret.expose_secret().inner_secret_mut());
ctr(&mut *secret.expose_secret_mut());
secret
}
}
Expand Down Expand Up @@ -129,10 +107,8 @@ impl<S: Zeroize + Clone> SecretBox<S> {

impl<S: Zeroize + Default> Default for SecretBox<S> {
fn default() -> Self {
Self {
inner_secret: Box::<S>::default(),
protected: false,
}
let inner_secret = Box::<S>::default();
SecretBox::new(inner_secret)
}
}

Expand All @@ -147,82 +123,78 @@ where
S: CloneableSecret,
{
fn clone(&self) -> Self {
SecretBox {
inner_secret: self.inner_secret.clone(),
protected: false,
}
SecretBox::new(self.inner_secret.clone())
}
}

impl<S: Zeroize> ExposeSecret<S> for SecretBox<S> {
fn expose_secret(&mut self) -> SecretGuard<'_, S> {
SecretGuard::new(self.inner_secret.as_mut(), self.protected)
SecretGuard::new(&self.inner_secret)
}

fn expose_secret_mut(&mut self) -> SecretGuardMut<'_, S> {
SecretGuardMut::new(&mut self.inner_secret)
}
}

/// Secret Guard that holds a mutable to reference to the secret
/// Secret Guard that holds a reference to the secret.
pub struct SecretGuard<'a, S>
where
S: Zeroize,
{
data: &'a mut S,
protected: bool,
data: &'a S,
}

impl<'a, S: Zeroize> SecretGuard<'a, S> {
/// Create a new SecretGuard instance.
pub fn new(data: &'a mut S, protected: bool) -> Self {
Self { data, protected }
}
/// Get a shared reference to the inner secret
pub fn inner_secret(&mut self) -> &S {
let secret_ptr = self.data as *const S;

if self.protected {
unsafe {
if !mprotect(
NonNull::new(secret_ptr as *mut S).expect("Unable to convert ptr to NonNull"),
PROT_READ,
) {
panic!("Unable to protect secret_guard")
}
}
}
self.protected = false;
impl<S> Deref for SecretGuard<'_, S>
where
S: Zeroize,
{
type Target = S;

fn deref(&self) -> &Self::Target {
self.data
}
}

/// Get an exclusive reference to the inner secret
pub fn inner_secret_mut(&mut self) -> &mut S {
let secret_ptr = self.data as *const S;
/// Secret Guard that holds a mutable to reference to the secret.
pub struct SecretGuardMut<'a, S>
where
S: Zeroize,
{
data: &'a mut S,
}

unsafe {
if !mprotect(
NonNull::new(secret_ptr as *mut S).expect("Unable to convert ptr to NonNull"),
PROT_READ | PROT_WRITE,
) {
panic!("Unable to protect secret_guard")
}
}
self.protected = false;
impl<S> Deref for SecretGuardMut<'_, S>
where
S: Zeroize,
{
type Target = S;

fn deref(&self) -> &Self::Target {
self.data
}
}

impl<'a, S: Zeroize> Drop for SecretGuard<'a, S> {
fn drop(&mut self) {
let secret_ptr = self.data as *const S;

if !self.protected {
unsafe {
if !mprotect(
NonNull::new(secret_ptr as *mut S).expect("Unable to convert ptr to NonNull"),
PROT_NONE,
) {
panic!("Unable to protect memory")
}
}
}
impl<S> DerefMut for SecretGuardMut<'_, S>
where
S: Zeroize,
{
fn deref_mut(&mut self) -> &mut Self::Target {
self.data
}
}

impl<'a, S: Zeroize> SecretGuard<'a, S> {
/// Create a new SecretGuard instance.
pub fn new(data: &'a S) -> Self {
Self { data }
}
}

impl<'a, S: Zeroize> SecretGuardMut<'a, S> {
/// Create a new SecretGuard instance.
pub fn new(data: &'a mut S) -> Self {
Self { data }
}
}

Expand All @@ -231,13 +203,17 @@ pub trait CloneableSecret: Clone + Zeroize {}

/// Create a SecretGuard that holds a reference to the secret
pub trait ExposeSecret<S: Zeroize> {
/// Expose secret: this is the only method providing access to a secret.
/// Expose secret as non-mutable.
fn expose_secret(&mut self) -> SecretGuard<'_, S>;

/// Expose secret as mutable.
fn expose_secret_mut(&mut self) -> SecretGuardMut<'_, S>;
}

#[cfg(test)]
mod tests {
use super::*;

#[derive(Debug, Clone, Default)]
struct TestSecret {
data: Vec<u8>,
Expand Down Expand Up @@ -269,7 +245,7 @@ mod tests {
fn test_secret_box_drop_zeroizes() {
let secret = Box::new(TestSecret::new(10));
let mut secret_box = SecretBox::new(secret);
assert!(secret_box.expose_secret().inner_secret().check_non_zero());
assert!((*secret_box.expose_secret()).check_non_zero());

drop(secret_box);

Expand All @@ -284,17 +260,17 @@ mod tests {
let secret = Box::new(TestSecret::new(10));
let mut secret_box = SecretBox::new(secret);
{
let mut exposed = secret_box.expose_secret();
exposed.inner_secret_mut().data[0] = 42;
let mut exposed = secret_box.expose_secret_mut();
(*exposed).data[0] = 42;
}

assert_eq!(secret_box.expose_secret().inner_secret().data[0], 42);
assert_eq!((*secret_box.expose_secret()).data[0], 42);
}

#[test]
fn test_secret_box_new_with_ctr() {
let mut secret_box = SecretBox::new_with_ctr(|| TestSecret::new(10));
assert!(secret_box.expose_secret().inner_secret().check_non_zero());
assert!((*secret_box.expose_secret()).check_non_zero());
}

#[test]
Expand All @@ -303,7 +279,7 @@ mod tests {
SecretBox::try_new_with_ctr(|| Ok(TestSecret::new(10)));

match result {
Ok(mut secret_box) => assert!(secret_box.expose_secret().inner_secret().check_non_zero()),
Ok(mut secret_box) => assert!((*secret_box.expose_secret()).check_non_zero()),
Err(_) => panic!("Expected Ok variant"),
}
}
Expand Down
Loading