secure-gate 0.9.0-rc.13

Secure wrappers for secrets with explicit access and mandatory zeroization — no_std-compatible, zero-overhead library with audit-friendly access patterns.
Documentation
//! Opt-in marker trait for safe, explicit cloning of secrets.
//!
//! This trait serves as a deliberate opt-in mechanism to enable the `Clone`
//! implementation on secret wrapper types (`Fixed<T>`, `Dynamic<T>`, aliases)
//! while preserving core security invariants:
//!
//! - **Zeroization preserved**: All clones zeroize their contents on drop.
//! - **No accidental cloning**: Cloning is impossible unless the inner type
//!   explicitly implements `CloneableSecret`.
//! - **Auditable risk**: Cloning increases the exposure surface (more copies
//!   of the secret exist in memory); this trait forces developers to acknowledge
//!   and accept that risk.
//!
//! Requires the `cloneable` feature to be enabled.
//!
//! # When to Use
//!
//! Implement `CloneableSecret` on inner types only when duplication is
//! **truly necessary** (e.g., session keys passed to multiple threads,
//! backup/export scenarios). Prefer move semantics or single-instance ownership
//! whenever possible to minimize attack surface.
//!
//! # A newtype inner type is required (orphan rule)
//!
//! Rust's coherence (orphan) rules prevent downstream crates from implementing
//! this marker for foreign types: `impl CloneableSecret for String`,
//! `Vec<u8>`, or `[u8; 32]` does **not compile** outside this crate, because
//! both the trait and the type would be foreign. Consequently `Dynamic<String>`,
//! `Dynamic<Vec<u8>>`, and `Fixed<[u8; N]>` can never be cloned directly.
//!
//! This is deliberate, not an oversight: if the marker were pre-implemented for
//! the standard container types, enabling the `cloneable` feature would silently
//! make *every* `Dynamic<String>` in a dependency graph cloneable. Requiring a
//! local newtype (as in the example below) keeps each cloneable secret type
//! defined — and auditable — in your own code.
//!
//! The pattern is fully supported: [`RevealSecret`](crate::RevealSecret) and
//! [`RevealSecretMut`](crate::RevealSecretMut) are implemented for **every**
//! inner type, so a `Fixed<SessionKey>` built this way is readable, mutable,
//! and (with a [`ConstantTimeEq`](crate::ConstantTimeEq) impl on the inner
//! type) comparable — not just cloneable. Only
//! [`SecretLen`](crate::SecretLen) stays narrow, since a custom inner type has
//! no meaningful length.
//!
//! # Example
//!
//! ```rust
//! use secure_gate::{CloneableSecret, Fixed, FixedStorage, RevealSecret};
//! use zeroize::Zeroize;
//!
//! #[derive(Clone)]
//! struct SessionKey([u8; 32]);
//!
//! impl Zeroize for SessionKey {
//!     fn zeroize(&mut self) { self.0.zeroize(); }
//! }
//!
//! // Every impl is a deliberate security decision — audit all usages.
//! impl CloneableSecret for SessionKey {}
//! // Required by `Fixed::new`: asserts this type owns no buffer that can be reallocated.
//! impl FixedStorage for SessionKey {}
//!
//! let original = Fixed::new(SessionKey([0u8; 32]));
//! let copy = original.clone();   // Opt-in cloning: each copy is independently zeroized.
//! drop(original);  // zeroized on drop
//! drop(copy);      // independently zeroized on drop
//! ```
//!
//! # One diagnostic that points the wrong way
//!
//! If you call `.clone()` on a **reference** to a wrapper whose inner type lacks this
//! marker, and you do not annotate the target type, the call resolves to
//! `<&T as Clone>::clone` instead. It compiles, copies the reference rather than the
//! secret, and produces rustc's `noop_method_call` warning — whose `help:` suggests
//! adding `#[derive(Clone)]` to `Dynamic`, which is to say it suggests deleting this gate:
//!
//! ```text
//! warning: call to `.clone()` on a reference in this situation does nothing
//!    = note: the type `Dynamic<Vec<u8>>` does not implement `Clone`, so calling `clone`
//!            on `&Dynamic<Vec<u8>>` copies the reference, which does not do anything
//! help: if you meant to clone `Dynamic<Vec<u8>>`, implement `Clone` for it
//! 184 + #[derive(Clone)]
//! ```
//!
//! Nothing is duplicated, so no secret escapes — the opposite, the call does nothing at
//! all. It is recorded here because the suggested fix is wrong for this crate and a reader
//! who follows it removes the opt-in. The gate itself still holds: an owned receiver, or
//! an annotated target, gives the real error instead —
//! `error[E0277]: the trait bound ... CloneableSecret is not satisfied`.
//!
//! # Security Notes
//!
//! - Cloning **does not** bypass zeroization — **every** copy is independently zeroized on drop.
//!   However, each clone **increases the number of simultaneous in-memory copies**, expanding
//!   the window for memory-extraction attacks (cold-boot, scraping, etc.).
//! - Audit all `CloneableSecret` impls to ensure the inner type correctly implements
//!   `Clone` and `Zeroize`.
//!
//! This trait is a **marker only** — it has no methods and adds no runtime behavior.
//! It exists solely to gate the `Clone` impl on wrapper types.

/// Marker trait that opts a secret type into cloning.
///
/// No methods — its only purpose is to gate the `Clone` impl on
/// [`Fixed<T>`](crate::Fixed) and [`Dynamic<T>`](crate::Dynamic).
/// Requires the `cloneable` feature.
#[cfg(feature = "cloneable")]
#[diagnostic::on_unimplemented(
    message = "`{Self}` has not opted in to cloning, so a secret wrapping it cannot be cloned",
    label = "`{Self}` does not implement `CloneableSecret`",
    note = "Cloning a secret makes a second full copy in memory, and every copy then has to be zeroized. This crate will not make that decision for you, so `Clone` on `Fixed` and `Dynamic` is gated behind this marker instead of being derived.",
    note = "For a type you define, add an empty `impl CloneableSecret` block for it, next to its `Zeroize` impl. Like this crate's other markers it is an assertion the compiler cannot check: you are saying the inner type's `Clone` yields a value whose own zeroization is equally sound.",
    note = "For a type you do not own, the orphan rule forbids that impl and rejects it with E0117 — wrap it in a newtype of your own and implement the marker for that.",
    note = "If you arrived here from `#[derive(Clone)]` on a generated newtype, the derive forwards to the wrapper's `Clone`, which is what requires this marker. The `derive: [Clone]` argument is refused outright with a fuller explanation; writing `impl Clone` by hand keeps the decision visible in your own code."
)]
pub trait CloneableSecret: Clone + zeroize::Zeroize {}