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 zeroizing Drop 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 implement Zeroize and instead implement zeroizing Drop.
- 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.
Right now I see the following issues with public API of
zeroize:Zeroizecan be inefficient since they rely on zeroization of fields one by one or on zeroizingDropimpls.Zeroizead rely on manual implementations.ZeroizeOnDropis a somewhat useless trait, I haven't seen it used in practice.Zeroizingusefulness is limited. It can be used only for "primitive" types (e.g. raw keys), complex types do not implementZeroizeand instead implement zeroizingDrop.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:
UPD:
FlatPodandFlatZodare 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:
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 referenceszeroizein docs.