Description
Right now I see the following issues with public API of zeroize
:
- Derived and manually implemented
Zeroize
can be inefficient since they rely on zeroization of fields one by one or on zeroizingDrop
impls. - Procedural macros are relatively heavy compile time-wise, so usually we do not derive
Zeroize
ad rely on manual implementations. ZeroizeOnDrop
is a somewhat useless trait, I haven't seen it used in practice.Zeroizing
usefulness is limited. It can be used only for "primitive" types (e.g. raw keys), complex types do not implementZeroize
and instead implement zeroizingDrop
.- Zeroization is enabled for a whole crate using features. It's not possible to use selective zeroization and zeroization is not reflected in any way in user code.
I think most structures should be zeroized using the "flat" zeroization (see #1045) and that it can be useful to have explicit indication in user code of structs being zeroized on drop.
So I would like to suggest roughly this API:
/// Zeroizes memory pointed by `data_ptr`, but not memory
/// potentially reference by `T`.
pub unsafe fn zeroize_flat<T>(data_ptr: *mut T) {
// ...
}
/// Zeroize-on-drop wrapper.
///
/// Note that this wrapper zeroizes only data owned by `T`
/// and does nothing with data referenced by it.
#[repr(transparent)]
pub struct ZeroizeOnDrop<T, const IS_ENABLED: bool = true>(T);
/// Abbreviated alias for `ZeroizeOnDrop`.
pub type Zod<T, const IS_ENABLED: bool = true> = ZeroizeOnDrop<T, IS_ENABLED>;
impl<T, const IS_ENABLED: bool> Drop for ZeroizeOnDrop<T, IS_ENABLED> {
fn drop (&mut self) {
unsafe {
std::ptr::drop_in_place(&mut self.0);
if IS_ENABLED { zeroize_flat(&mut self.0); }
}
}
}
// Impl Deref and DerefMut for `ZeroizeOnDrop`
UPD: FlatPod
and FlatZod
are removed.
It can be introduced in a backward-compatible way, but for clarity it's probably worth to release it as v2.0.
Users would write code like this:
pub struct Foo {
secret_cipher: Zod<Aes128>,
secret_key: Box<Zod<[u8; 16]>>,
non_secret_hasher: Sha256,
// other fields
}
const ZOD: bool = cfg!(feature = "zeroize");
pub struct Bar1 {
// This field will be zeroized only if `zeroize` feature is enabled
cipher: Zod<Aes128, ZOD>,
}
// Alternatively:
pub struct Bar2<const ZOD: bool = true> {
cipher: Zod<Aes128, ZOD>,
}
While it will be a bit less convinient, I think it's useful to have explicit indication in source code that secret types will be zeroized on drop. We may provide aliases like type ZodAes128 = Zod<Aes128>
to improve visibility, but I don't think they are worth the trouble and it should be sufficient to simply references zeroize
in docs.