Skip to main content

Secret

Struct Secret 

Source
pub struct Secret<T: ZeroizablePrimitive>(/* private fields */);
Expand description

A wrapper that holds a secret value of any primitive or fixed-size array type and guarantees it is securely zeroized on drop, and is protected from accidental logging by implementing redacting fmt::Debug and fmt::Display.

A Secret<T> is created in a zeroed state with Secret::new / Default and populated in place through DerefMut; there is intentionally no by-value constructor (see the module docs). It behaves transparently as a &T / &mut T via Deref/DerefMut, so a Secret<[u8; 32]> can be indexed, sliced, and iterated exactly like the underlying array.

Secret<T> deliberately does not implement Copy (it owns a Drop), which forces move semantics and prevents silent, unscrubbed duplication of secrets. It does implement Clone for the cases where an explicit, intentional copy is required.

Its Debug and Display impls are redacting: they never print the contained bytes, so a secret cannot leak into logs or panic/crash output.

ยงUsage Examples

Secret<i32> and other scalar types

Secret can wrap any of the following scalar types: u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, bool, and char.

use bouncycastle_utils::secret::Secret;

let mut nonce: Secret<u64> = Secret::default();
*nonce = 0xDEAD_BEEF;
assert_eq!(*nonce, 0xDEAD_BEEF);


let mut counter: Secret<i32> = Secret::new();
// Here you have to explicitly Deref to get at the underlying type, but it all works.
*counter += 1;
assert_eq!(*counter, 1);

ยงSecret<[u8; 32]>

Secret can wrap arrays of any of the supported scalar types. There is no bound on how large an array you can make.

Here weโ€™ll construct a zeroed Secret<[u8; 32]> and fill them in place through DerefMut, so the plaintext is never held in a separate, unprotected variable:

use bouncycastle_utils::secret::Secret;

let mut key: Secret<[u8; 32]> = Secret::new();

// Here, .copy_from_slice may still produce a copy in memory, but it's the best we can do in
// illustrative example code.
// In real code an RNG or KDF or network socket read should be directly handed the mut ref to
// Secret so that it can write directly into it.
key.copy_from_slice(&[0x42u8; 32]);

// `Secret<T>` is (mostly) transparent: you can use it exactly as you would the underlying type T
// (possibly requiring a dereference).
// indexing, slicing, `.len()`, iteration, etc all work automatically via `Deref`.
// Just forget the Secret is there
assert_eq!(key[0], 0x42);
assert_eq!(key.len(), 32);
assert!(key.iter().all(|&b| b == 0x42));
// `key` is volatile-scrubbed to zero when it drops at the end of this scope.

ยงOn a custom type

Since ZeroizablePrimitive is a public trait, you can implement it on your own types and then trivially be able to wrap them in a Secret<T>. The only requirement is that there is a well-defined โ€œZEROEDโ€ value for the type.

Toy Example:

use bouncycastle_utils::secret::{Secret, ZeroizablePrimitive};

/// Holds a system user
#[derive(Clone, Copy)]
struct User {
    userid: i32,
    name: [u8; 64],
}

/// Provide the const ZEROED value for the type.
impl ZeroizablePrimitive for User {
    const ZEROED: Self = Self{ userid: 0i32, name: [0u8; 64] };
}

/// We will tag the admins as Secret<User> to give them extra protections against
/// having their info leaked.
struct AllUsers {
    users: Vec<User>,
    admins: Vec<Secret<User>>,
}

Note that Secret<T> is only defined for statically-sized types โ€“ ie types that satisfy Sized. For a justification, see the module docs.

ยงRedacting Debug and Display

Debug and Display are redacting, so a Secret can be logged without leaking its contents:

use bouncycastle_utils::secret::Secret;

let mut secret: Secret<u32> = Secret::new();
*secret = 0x4141_4141; // would render as "AAAA" if leaked
assert_eq!(format!("{secret}"), "<redacted>");
assert_eq!(format!("{secret:?}"), "Secret<u32>(<redacted>)");

This will also work for custom types; letโ€™s say the User struct from the previous example had implโ€™d Display and Debug; mhen you wrap them in Secret<User> then Secretโ€™s Display and Debug are invoked instead of Userโ€™s and you get the "<redacted>" output.

ยงMemory Usage

As a direct wrapper of the type T, a Secret<T> does not add any memory overhead.

use bouncycastle_utils::secret::Secret;

print!("{}\n", size_of::<i32>());          // 4
print!("{}\n", size_of::<Secret<i32>>()); // also 4

print!("{}\n", size_of::<[u8; 32]>());          // 32
print!("{}\n", size_of::<Secret<[u8; 32]>>());  // also 32

ยง๐Ÿšจ Security ๐Ÿšจ

What this does NOT guarantee:

Secret only guarantees that the final scrub of the wrapped value is emitted. It cannot recover copies that the compiler or CPU made. To minimize copies of the underlying bytes in memory, you should be careful with a few things:

  • Create a new Secret instance, then get a mut ref to its internal value via Secret::deref_mut and write to that instead of having a copy in an unprotected variable and then copying it into the Secret.
  • Avoid copying out of Secret for the same reason.

Implementationsยง

Sourceยง

impl<T: ZeroizablePrimitive> Secret<T>

Source

pub const fn new() -> Self

Create a new Secret in a zeroed state.

Populate it afterwards in place via DerefMut (e.g. by having an RNG or KDF write into &mut *secret), which avoids ever materializing an unprotected copy of the secret.

Sourceยง

impl<T: ZeroizablePrimitive> Secret<T>

Source

pub fn zeroize(&mut self)

Securely overwrite the contained value with zeros. After this returns, every byte of the wrapped value has been volatile-written to 0.

This is called automatically on drop; call it directly only if you need to scrub the value early, for example before reusing the same Secret object. Though in many circumstances you are zeroizing because you know youโ€™re done with the object before it goes out of scope, in which case you would be better served calling drop(s) instead since this will still call zeroize() and also move the object, preventing you from accidentally reusing it.

Trait Implementationsยง

Sourceยง

impl<T: ZeroizablePrimitive> Clone for Secret<T>

Sourceยง

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) ยท Sourceยง

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Sourceยง

impl<T: ZeroizablePrimitive> Debug for Secret<T>

Redacting: prints the wrapped type but never its contents.

Sourceยง

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Sourceยง

impl<T: ZeroizablePrimitive> Default for Secret<T>

Sourceยง

fn default() -> Self

Returns the โ€œdefault valueโ€ for a type. Read more
Sourceยง

impl<T: ZeroizablePrimitive> Deref for Secret<T>

Sourceยง

type Target = T

The resulting type after dereferencing.
Sourceยง

fn deref(&self) -> &T

Dereferences the value.
Sourceยง

impl<T: ZeroizablePrimitive> DerefMut for Secret<T>

Sourceยง

fn deref_mut(&mut self) -> &mut T

Mutably dereferences the value.
Sourceยง

impl<T: ZeroizablePrimitive> Display for Secret<T>

Redacting: never prints the contents.

Sourceยง

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Sourceยง

impl<T: ZeroizablePrimitive> Drop for Secret<T>

Sourceยง

fn drop(&mut self)

Executes the destructor for this type. Read more
Sourceยง

fn pin_drop(self: Pin<&mut Self>)

๐Ÿ”ฌThis is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more
Sourceยง

impl<T: ZeroizablePrimitive> PartialEq for Secret<T>

Checks for equality of the secret data by casting to bytes and using a constant-time comparison.

Both operands are the same type T, so both views are exactly size_of::<T>() bytes and the comparison never short-circuits: it always inspects every byte, avoiding a timing side channel that would otherwise leak how many leading bytes of two secrets match.

Sourceยง

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) ยท Sourceยง

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Sourceยง

impl<T: ZeroizablePrimitive> Eq for Secret<T>

Auto Trait Implementationsยง

ยง

impl<T> Freeze for Secret<T>
where T: Freeze,

ยง

impl<T> RefUnwindSafe for Secret<T>
where T: RefUnwindSafe,

ยง

impl<T> Send for Secret<T>
where T: Send,

ยง

impl<T> Sync for Secret<T>
where T: Sync,

ยง

impl<T> Unpin for Secret<T>
where T: Unpin,

ยง

impl<T> UnsafeUnpin for Secret<T>
where T: UnsafeUnpin,

ยง

impl<T> UnwindSafe for Secret<T>
where T: UnwindSafe,

Blanket Implementationsยง

Sourceยง

impl<T> Any for T
where T: 'static + ?Sized,

Sourceยง

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Sourceยง

impl<T> Borrow<T> for T
where T: ?Sized,

Sourceยง

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Sourceยง

impl<T> BorrowMut<T> for T
where T: ?Sized,

Sourceยง

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Sourceยง

impl<T> CloneToUninit for T
where T: Clone,

Sourceยง

unsafe fn clone_to_uninit(&self, dest: *mut u8)

๐Ÿ”ฌThis is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Sourceยง

impl<T> From<T> for T

Sourceยง

fn from(t: T) -> T

Returns the argument unchanged.

Sourceยง

impl<T, U> Into<U> for T
where U: From<T>,

Sourceยง

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Sourceยง

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Sourceยง

type Target = T

๐Ÿ”ฌThis is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Sourceยง

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Sourceยง

type Error = Infallible

The type returned in the event of a conversion error.
Sourceยง

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Sourceยง

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Sourceยง

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Sourceยง

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.