Expand description
A transparent wrapper type, Secret, which is a wrapper that holds a secret value and
guarantees it is securely zeroized on drop, and protected from accidintal logging by implementing
redacting fmt::Debug and fmt::Display.
§Why write_volatile
Plain writes such as slice.fill(0) are ordinary, non-observable memory accesses. Under
optimization the compiler may prove that a buffer is never read again after it is scrubbed –
precisely the situation just before a drop – and elide the scrub as a dead store. To prevent
that, Secret erases through core::ptr::write_volatile, whose accesses are defined by the
language as observable side effects and therefore may not be elided or coalesced. Each scrub is
followed by a compiler_fence with SeqCst ordering so the volatile
writes are not reordered with respect to later memory operations.
§Why Sized?
The ZeroizablePrimitive is bounded on Sized, which explicitly forbids
instantiating Secret<T> over something like Vec<T> whose size is not known at compile time.
The reason is that an implementation of .zeroize() that is guaranteed not be optimized away
by the compiler requires the use of unsafe{ write_volatile() } to directly write the T::ZEROED
byte pattern over top of the provided memory block. With a Sized type, this is a single line of
unsafe code and it is easy to prove that it is writing the number of bytes that it should be.
For a dynamically-sized value such as Vec<T>, this is substantially trickier.
Taking Vec as an example, it is not a flat piece of memory that can be trivially over-written
with a static value; Vec is actually a stack of structs that implement a smart-pointer that
tracks both length and capacity of the memory referenced by the pointer.
Properly zeroizing this means following the pointer, filling the referenced memory with 0x00 up
to the capacity, then setting length=0 without changing capacity or the pointer.
Zeroizing a Vec would require a substantial amount of unsafe code that is Vec-specific and
tricky to prove the correctness of.
Doing this for arbitrary heap-allocated objects (which may contain nested heap-allocated objects)
sounds like a whole research project.
This is not to say that bc-rust will never attempt this challenge, but since bc-rust is a [no_std]
library that keeps all of its secrets in stack-allocated variables, this is not a problem that
needs to be solved for internal library use.
Structs§
- Secret
- 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::Debugandfmt::Display.
Traits§
- Zeroizable
Primitive - A
Copytype whose all-zero value is meaningful and valid, so that aSecretof it can be default-constructed in a zeroed state.