From 674c7d7523288f1ce79ccf995cbde87ff5f6642d Mon Sep 17 00:00:00 2001 From: "dobby-yivi-agent[bot]" <275734547+dobby-yivi-agent[bot]@users.noreply.github.com> Date: Thu, 2 Jul 2026 03:08:06 +0000 Subject: [PATCH] docs: document mandatory .zeroize() call for secret types The zeroize feature (#14) derives Zeroize but not ZeroizeOnDrop, because the secret types are Copy. Secret material is therefore not wiped on drop and callers MUST call .zeroize() explicitly. Document this contract in the crate-level docs and add a MUST-call annotation to every SharedSecret, SecretKey and UserSecretKey across all KEM and IBE schemes. Co-Authored-By: Claude Opus 4.8 --- src/ibe/boyen_waters.rs | 14 ++++++++++++++ src/ibe/cgw.rs | 14 ++++++++++++++ src/ibe/waters.rs | 14 ++++++++++++++ src/ibe/waters_naccache.rs | 14 ++++++++++++++ src/kem/cgw_fo.rs | 7 +++++++ src/kem/cgw_kv.rs | 14 ++++++++++++++ src/kem/kiltz_vahlis_one.rs | 14 ++++++++++++++ src/kem/mod.rs | 7 +++++++ src/lib.rs | 37 +++++++++++++++++++++++++++++++++++++ 9 files changed, 135 insertions(+) diff --git a/src/ibe/boyen_waters.rs b/src/ibe/boyen_waters.rs index 212aa11..4065e77 100644 --- a/src/ibe/boyen_waters.rs +++ b/src/ibe/boyen_waters.rs @@ -49,6 +49,13 @@ pub struct PublicKey { } /// Secret key parameter generated by the PKG used to extract user secret keys. +/// +/// # Zeroization +/// +/// With the `zeroize` feature enabled this type derives `Zeroize` but **not** +/// `ZeroizeOnDrop` (it is `Copy`). Secret material is **not** cleared on drop — +/// you **MUST** call `.zeroize()` explicitly once done. See the +/// [crate-level docs](crate#zeroizing-secret-material). #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))] pub struct SecretKey { @@ -60,6 +67,13 @@ pub struct SecretKey { } /// Points on the paired curves that form the user secret key. +/// +/// # Zeroization +/// +/// With the `zeroize` feature enabled this type derives `Zeroize` but **not** +/// `ZeroizeOnDrop` (it is `Copy`). Secret material is **not** cleared on drop — +/// you **MUST** call `.zeroize()` explicitly once done. See the +/// [crate-level docs](crate#zeroizing-secret-material). #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))] pub struct UserSecretKey { diff --git a/src/ibe/cgw.rs b/src/ibe/cgw.rs index 7315089..0a1547c 100644 --- a/src/ibe/cgw.rs +++ b/src/ibe/cgw.rs @@ -48,6 +48,13 @@ pub struct PublicKey { /// Secret key parameter generated by the PKG used to extract user secret keys. /// Also known as MSK. +/// +/// # Zeroization +/// +/// With the `zeroize` feature enabled this type derives `Zeroize` but **not** +/// `ZeroizeOnDrop` (it is `Copy`). Secret material is **not** cleared on drop — +/// you **MUST** call `.zeroize()` explicitly once done. See the +/// [crate-level docs](crate#zeroizing-secret-material). #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))] pub struct SecretKey { @@ -59,6 +66,13 @@ pub struct SecretKey { /// User secret key. Can be used to decrypt the corresponding ciphertext. /// Also known as USK_{id}. +/// +/// # Zeroization +/// +/// With the `zeroize` feature enabled this type derives `Zeroize` but **not** +/// `ZeroizeOnDrop` (it is `Copy`). Secret material is **not** cleared on drop — +/// you **MUST** call `.zeroize()` explicitly once done. See the +/// [crate-level docs](crate#zeroizing-secret-material). #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))] pub struct UserSecretKey { diff --git a/src/ibe/waters.rs b/src/ibe/waters.rs index c73ba9d..df3d393 100644 --- a/src/ibe/waters.rs +++ b/src/ibe/waters.rs @@ -48,6 +48,13 @@ pub struct PublicKey { } /// Secret key parameter generated by the PKG used to extract user secret keys. +/// +/// # Zeroization +/// +/// With the `zeroize` feature enabled this type derives `Zeroize` but **not** +/// `ZeroizeOnDrop` (it is `Copy`). Secret material is **not** cleared on drop — +/// you **MUST** call `.zeroize()` explicitly once done. See the +/// [crate-level docs](crate#zeroizing-secret-material). #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))] pub struct SecretKey { @@ -55,6 +62,13 @@ pub struct SecretKey { } /// Points on the paired curves that form the user secret key. +/// +/// # Zeroization +/// +/// With the `zeroize` feature enabled this type derives `Zeroize` but **not** +/// `ZeroizeOnDrop` (it is `Copy`). Secret material is **not** cleared on drop — +/// you **MUST** call `.zeroize()` explicitly once done. See the +/// [crate-level docs](crate#zeroizing-secret-material). #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))] pub struct UserSecretKey { diff --git a/src/ibe/waters_naccache.rs b/src/ibe/waters_naccache.rs index 16b7325..21eee27 100644 --- a/src/ibe/waters_naccache.rs +++ b/src/ibe/waters_naccache.rs @@ -58,6 +58,13 @@ pub struct PublicKey { pub struct Identity([Scalar; CHUNKS]); /// Secret key parameter generated by the PKG used to extract user secret keys. +/// +/// # Zeroization +/// +/// With the `zeroize` feature enabled this type derives `Zeroize` but **not** +/// `ZeroizeOnDrop` (it is `Copy`). Secret material is **not** cleared on drop — +/// you **MUST** call `.zeroize()` explicitly once done. See the +/// [crate-level docs](crate#zeroizing-secret-material). #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))] pub struct SecretKey { @@ -65,6 +72,13 @@ pub struct SecretKey { } /// Points on the paired curves that form the user secret key. +/// +/// # Zeroization +/// +/// With the `zeroize` feature enabled this type derives `Zeroize` but **not** +/// `ZeroizeOnDrop` (it is `Copy`). Secret material is **not** cleared on drop — +/// you **MUST** call `.zeroize()` explicitly once done. See the +/// [crate-level docs](crate#zeroizing-secret-material). #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))] pub struct UserSecretKey { diff --git a/src/kem/cgw_fo.rs b/src/kem/cgw_fo.rs index 3593122..b34f25a 100644 --- a/src/kem/cgw_fo.rs +++ b/src/kem/cgw_fo.rs @@ -29,6 +29,13 @@ pub const USK_BYTES: usize = CPA_USK_BYTES + ID_BYTES; /// User secret key. Can be used to decaps the corresponding ciphertext. /// Also known as USK_{id}. +/// +/// # Zeroization +/// +/// With the `zeroize` feature enabled this type derives `Zeroize` but **not** +/// `ZeroizeOnDrop` (it is `Copy`). Secret material is **not** cleared on drop — +/// you **MUST** call `.zeroize()` explicitly once done. See the +/// [crate-level docs](crate#zeroizing-secret-material). #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))] pub struct UserSecretKey { diff --git a/src/kem/cgw_kv.rs b/src/kem/cgw_kv.rs index 998e83e..3a1b880 100644 --- a/src/kem/cgw_kv.rs +++ b/src/kem/cgw_kv.rs @@ -47,6 +47,13 @@ pub struct PublicKey { /// Secret key parameter generated by the PKG used to extract user secret keys. /// Also known as MSK. +/// +/// # Zeroization +/// +/// With the `zeroize` feature enabled this type derives `Zeroize` but **not** +/// `ZeroizeOnDrop` (it is `Copy`). Secret material is **not** cleared on drop — +/// you **MUST** call `.zeroize()` explicitly once done. See the +/// [crate-level docs](crate#zeroizing-secret-material). #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))] pub struct SecretKey { @@ -59,6 +66,13 @@ pub struct SecretKey { /// User secret key. Can be used to decaps the corresponding ciphertext. /// Also known as USK_{id}. +/// +/// # Zeroization +/// +/// With the `zeroize` feature enabled this type derives `Zeroize` but **not** +/// `ZeroizeOnDrop` (it is `Copy`). Secret material is **not** cleared on drop — +/// you **MUST** call `.zeroize()` explicitly once done. See the +/// [crate-level docs](crate#zeroizing-secret-material). #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))] pub struct UserSecretKey { diff --git a/src/kem/kiltz_vahlis_one.rs b/src/kem/kiltz_vahlis_one.rs index 0d557d6..d3390ef 100644 --- a/src/kem/kiltz_vahlis_one.rs +++ b/src/kem/kiltz_vahlis_one.rs @@ -44,6 +44,13 @@ pub struct PublicKey { } /// Secret key parameter generated by the PKG used to extract user secret keys. +/// +/// # Zeroization +/// +/// With the `zeroize` feature enabled this type derives `Zeroize` but **not** +/// `ZeroizeOnDrop` (it is `Copy`). Secret material is **not** cleared on drop — +/// you **MUST** call `.zeroize()` explicitly once done. See the +/// [crate-level docs](crate#zeroizing-secret-material). #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))] pub struct SecretKey { @@ -51,6 +58,13 @@ pub struct SecretKey { } /// Points on the paired curves that form the user secret key. +/// +/// # Zeroization +/// +/// With the `zeroize` feature enabled this type derives `Zeroize` but **not** +/// `ZeroizeOnDrop` (it is `Copy`). Secret material is **not** cleared on drop — +/// you **MUST** call `.zeroize()` explicitly once done. See the +/// [crate-level docs](crate#zeroizing-secret-material). #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))] pub struct UserSecretKey { diff --git a/src/kem/mod.rs b/src/kem/mod.rs index 9b3018c..c0123a4 100644 --- a/src/kem/mod.rs +++ b/src/kem/mod.rs @@ -34,6 +34,13 @@ pub const SS_BYTES: usize = 32; /// /// This shared secret has roughly a 127 bits of security. /// This is due to the fact that BLS12-381 targets this security level (optimistically). +/// +/// # Zeroization +/// +/// With the `zeroize` feature enabled this type derives `Zeroize` but **not** +/// `ZeroizeOnDrop` (it is `Copy`). Secret material is **not** cleared on drop — +/// you **MUST** call `.zeroize()` explicitly once done. See the +/// [crate-level docs](crate#zeroizing-secret-material). #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))] pub struct SharedSecret(pub [u8; SS_BYTES]); diff --git a/src/lib.rs b/src/lib.rs index 1a34908..850dd85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,43 @@ //! //! assert_eq!(k, k2); //! ``` +//! +//! # Zeroizing secret material +//! +//! When the `zeroize` feature is enabled, the secret types in this crate — the +//! [`SharedSecret`](crate::kem::SharedSecret) produced by the KEMs, and the +//! `SecretKey` and `UserSecretKey` of every scheme — derive +//! [`Zeroize`](https://docs.rs/zeroize), but **not** `ZeroizeOnDrop`. +//! +//! These types are `Copy`, and a `Copy` type cannot implement `Drop` (Rust +//! forbids `Copy` and `Drop` on the same type), so `ZeroizeOnDrop` cannot be +//! derived for them. As a consequence **secret key material is not wiped from +//! memory automatically when a value goes out of scope**. If you care about +//! clearing secrets, you **MUST** call `.zeroize()` explicitly once you are +//! done with each secret value: +//! +//! ```ignore +//! use ibe::kem::{IBKEM, cgw_kv::CGWKV}; +//! use ibe::Derive; +//! use zeroize::Zeroize; +//! +//! let mut rng = rand::thread_rng(); +//! let id = ::Id::derive_str("alice@example.com"); +//! let (pk, mut sk) = CGWKV::setup(&mut rng); +//! let mut usk = CGWKV::extract_usk(Some(&pk), &sk, &id, &mut rng); +//! let (_ct, mut ss) = CGWKV::encaps(&pk, &id, &mut rng); +//! +//! // ... use sk / usk / ss ... +//! +//! // Wipe the secret material once you are done with it. +//! sk.zeroize(); +//! usk.zeroize(); +//! ss.zeroize(); +//! ``` +//! +//! Making these types `!Copy` so that `ZeroizeOnDrop` can be derived (and the +//! wiping happens automatically) is a breaking API change; it is deferred to a +//! future major release. #![no_std] #![deny(missing_debug_implementations, rust_2018_idioms, missing_docs)]