Skip to content

Commit

Permalink
Hidden data handling
Browse files Browse the repository at this point in the history
  • Loading branch information
AaronFeickert committed Nov 10, 2022
1 parent 6d6800c commit fe49469
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 79 deletions.
116 changes: 75 additions & 41 deletions src/hidden.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020. The Tari Project
// Copyright 2022. The Tari Project
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
// following conditions are met:
Expand All @@ -20,74 +20,108 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

//! A wrapper to conceal secrets when output into logs or displayed.
//! A generic type that hides data from being displayed or written, keeps it on the heap, zeroizes it when it goes away,
//! limits access, and allows for type enforcement.

use std::fmt;
use std::{any::type_name, fmt, marker::PhantomData};

use serde::{Deserialize, Serialize};
use zeroize::Zeroize;

/// A simple struct with a single inner value to wrap content of any type.
#[derive(Copy, Clone, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
/// A marker trait for labeling different hidden types
pub trait HiddenLabel {}

/// Create a hidden type label
#[macro_export]
macro_rules! hidden_label {
($name:ident) => {
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct $name;

impl HiddenLabel for $name {}
};
}

// A default hidden type label; only use this if you're absolutely sure you don't care about type enforcement
hidden_label!(DefaultHiddenLabel);

/// Data that needs to be kept hidden, needs to be zeroized when it goes away, and is resistant to misuse
#[derive(Clone, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(transparent)]
pub struct Hidden<T> {
inner: T,
pub struct Hidden<T, L = DefaultHiddenLabel>
where
T: Zeroize,
L: HiddenLabel,
{
inner: Box<T>,
#[serde(skip)]
_type: PhantomData<L>,
}

impl<T> Hidden<T> {
/// Hides a value.
impl<T, L> Hidden<T, L>
where
T: Zeroize,
L: HiddenLabel,
{
/// Hide the data
pub fn hide(inner: T) -> Self {
Self { inner }
}

/// Returns ownership of the inner value discarding the wrapper.
pub fn into_inner(self) -> T {
self.inner
Self {
inner: Box::new(inner),
_type: PhantomData,
}
}

/// Provides access to the inner value (immutable).
/// Reveal the hidden data as an immutable reference
pub fn reveal(&self) -> &T {
&self.inner
}

/// Provides access to the inner value (mutable).
/// Reveal the hidden data as a mutable reference
pub fn reveal_mut(&mut self) -> &mut T {
&mut self.inner
}
}

impl<T> From<T> for Hidden<T> {
fn from(inner: T) -> Self {
Self::hide(inner)
}
}

/// Defines a masked value for the type to output as debug information. Concealing the secrets within.
impl<T> fmt::Debug for Hidden<T> {
/// Only output masked data for debugging
impl<T, L> fmt::Debug for Hidden<T, L>
where
T: Zeroize,
L: HiddenLabel,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Hidden<{}>", std::any::type_name::<T>())
write!(f, "Hidden<{}, {}>", type_name::<T>(), type_name::<L>(),)
}
}

/// Defines a masked value for the type to display. Concealing the secrets within.
impl<T> fmt::Display for Hidden<T> {
/// Only display masked data
impl<T, L> fmt::Display for Hidden<T, L>
where
T: Zeroize,
L: HiddenLabel,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Hidden<{}>", std::any::type_name::<T>())
write!(f, "Hidden<{}, {}>", type_name::<T>(), type_name::<L>(),)
}
}

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

#[test]
fn into_applies_wrapper_deref_removes_it() {
let wrapped: Hidden<u8> = Hidden::hide(42);
assert_eq!(42, *wrapped.reveal());
/// Zeroize the hidden data
impl<T, L> Zeroize for Hidden<T, L>
where
T: Zeroize,
L: HiddenLabel,
{
fn zeroize(&mut self) {
self.inner.zeroize();
}
}

#[test]
fn hidden_value_derived_trats() {
let wrapped: Hidden<u8> = 0.into();
assert_eq!(wrapped, Hidden::default());
/// Zeroize the hidden data when dropped
impl<T, L> Drop for Hidden<T, L>
where
T: Zeroize,
L: HiddenLabel,
{
fn drop(&mut self) {
self.zeroize();
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ pub mod fixed_set;
pub mod hash;
pub mod hex;
#[macro_use]
pub mod locks;
pub mod hidden;
pub mod locks;
pub mod message_format;
pub mod password;
pub mod serde;
Expand Down
61 changes: 24 additions & 37 deletions src/password.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,25 @@
//! A module with a safe password wrapper.

use std::{error::Error, fmt, str::FromStr};
use std::{error::Error, fmt::Display, str::FromStr};

use serde::{Deserialize, Serialize};
use zeroize::Zeroize;
use crate::{
hidden::{Hidden, HiddenLabel},
hidden_label,
};

use crate::Hidden;
hidden_label!(SafePasswordLabel);

/// A hidden string that implements [`Zeroize`].
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(transparent)]
pub struct SafePassword {
password: Hidden<Box<[u8]>>,
}
/// A hidden password type that zeroizes when it goes away
pub type SafePassword = Hidden<Vec<u8>, SafePasswordLabel>;

impl<S: Into<String>> From<S> for SafePassword {
fn from(s: S) -> Self {
Self {
password: Hidden::from(s.into().into_bytes().into_boxed_slice()),
}
}
}

impl Drop for SafePassword {
fn drop(&mut self) {
self.password.reveal_mut().zeroize();
}
}

/// An error for parsing a password from string.
/// An error for parsing a password from a string
#[derive(Debug)]
pub struct PasswordError;

impl Error for PasswordError {}

impl fmt::Display for PasswordError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl Display for PasswordError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "PasswordError")
}
}
Expand All @@ -44,26 +28,29 @@ impl FromStr for SafePassword {
type Err = PasswordError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::from(s.to_owned()))
Ok(Self::hide(s.as_bytes().to_vec()))
}
}

impl SafePassword {
/// Gets a reference to bytes of a passphrase.
pub fn reveal(&self) -> &[u8] {
self.password.reveal()
impl<S: Into<String>> From<S> for SafePassword {
fn from(s: S) -> Self {
Self::hide(s.into().as_bytes().to_vec())
}
}

#[cfg(test)]
mod tests {
use std::str::FromStr;

use crate::SafePassword;

#[test]
fn test_password() {
assert_eq!(
SafePassword::from("secret_must_match".to_string()),
SafePassword::from("secret_must_match")
);
fn test_string() {
let _1 = SafePassword::from_str("test");
SafePassword::from("test");
SafePassword::from("test".to_string());

let _test: SafePassword = "test".into();
let _test2: SafePassword = "test".to_string().into();
}
}

0 comments on commit fe49469

Please sign in to comment.