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
Secretinstance, then get a mut ref to its internal value viaSecret::deref_mutand 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>
impl<T: ZeroizablePrimitive> Secret<T>
Sourceยงimpl<T: ZeroizablePrimitive> Secret<T>
impl<T: ZeroizablePrimitive> Secret<T>
Sourcepub fn zeroize(&mut self)
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>
impl<T: ZeroizablePrimitive> Clone for Secret<T>
Sourceยงimpl<T: ZeroizablePrimitive> Debug for Secret<T>
Redacting: prints the wrapped type but never its contents.
impl<T: ZeroizablePrimitive> Debug for Secret<T>
Redacting: prints the wrapped type but never its contents.
Sourceยงimpl<T: ZeroizablePrimitive> Default for Secret<T>
impl<T: ZeroizablePrimitive> Default for Secret<T>
Sourceยงimpl<T: ZeroizablePrimitive> Deref for Secret<T>
impl<T: ZeroizablePrimitive> Deref for Secret<T>
Sourceยงimpl<T: ZeroizablePrimitive> DerefMut for Secret<T>
impl<T: ZeroizablePrimitive> DerefMut for Secret<T>
Sourceยงimpl<T: ZeroizablePrimitive> Display for Secret<T>
Redacting: never prints the contents.
impl<T: ZeroizablePrimitive> Display for Secret<T>
Redacting: never prints the contents.
Sourceยงimpl<T: ZeroizablePrimitive> Drop for Secret<T>
impl<T: ZeroizablePrimitive> Drop for Secret<T>
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.
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.