Skip to main content

auths_crypto/
secret.rs

1//! The `Secret` marker trait — type-level annotation for secret-bearing types.
2//!
3//! A type that implements `Secret` is declaring two invariants:
4//! 1. Its in-memory representation MUST be zeroized on drop. This is
5//!    enforced at the type level via the `ZeroizeOnDrop` supertrait bound
6//!    (sealed so the workspace owns every implementor).
7//! 2. Its `Debug` / `Display` / `Serialize` impls MUST NOT emit the inner
8//!    bytes. This is enforced by convention + an xtask scanner rule
9//!    (`check-constant-time`, extended in fn-128.T5) that flags:
10//!    - `#[derive(PartialEq)]` / `#[derive(Eq)]` on `Secret` types
11//!      (equality on secrets must go through `subtle::ConstantTimeEq`)
12//!    - `==` / `!=` on `Secret`-typed operands
13//!    - any `impl Secret for T` that lacks a parallel `ZeroizeOnDrop` impl
14//!
15//! Sealed: the super-trait `sealed::Sealed` lives in a private module, so
16//! downstream crates cannot unilaterally extend the `Secret` family. New
17//! secret types are added by editing the `sealed::Sealed` impl block in
18//! this crate.
19//!
20//! Usage:
21//! ```ignore
22//! use auths_crypto::{Secret, SecureSeed};
23//!
24//! fn drop_secret_safely<S: Secret>(_s: S) {
25//!     // `S` is ZeroizeOnDrop by the trait bound.
26//!     // Compiler + xtask scanner enforce the rest.
27//! }
28//! ```
29
30use zeroize::ZeroizeOnDrop;
31
32/// Workspace-internal extension point for the [`Secret`] marker.
33///
34/// The `Sealed` trait is `pub` so sibling crates in this workspace
35/// (auths-pairing-protocol, auths-id) can impl `Secret` for their own
36/// secret-bearing types. The containing module is named `__private` so
37/// downstream third parties reading the crate's docs see the intent:
38/// this is an implementation detail, not part of the public API.
39///
40/// To add a new `Secret` type in a workspace crate:
41/// ```ignore
42/// impl auths_crypto::secret::__private::Sealed for MyType {}
43/// impl auths_crypto::Secret for MyType {}
44/// ```
45///
46/// `Sealed` is not `doc(hidden)`-ed because xtask scanners and cargo-deny
47/// need to see the impl sites; the `__private` module name is the social
48/// contract.
49pub mod __private {
50    /// Super-trait limiting the `Secret` family to types that have
51    /// intentionally opted in.
52    pub trait Sealed {}
53}
54
55/// Marker trait for types holding secret key material.
56///
57/// Implementors MUST:
58/// - Implement `__private::Sealed` explicitly (opt-in; cannot be forgotten).
59/// - Implement [`zeroize::ZeroizeOnDrop`] (enforced at the trait level).
60/// - Not derive `PartialEq` / `Eq` / `Ord` (enforced by xtask scanner).
61/// - Redact their `Debug` output (convention; scanner WIP).
62///
63/// The trait has no methods — its presence is the declaration.
64pub trait Secret: __private::Sealed + ZeroizeOnDrop {}
65
66// -------------------------------------------------------------------------
67// Implementors (every secret-bearing type in this crate). Adding a new one:
68// 1) Derive `ZeroizeOnDrop` (or wrap in `Zeroizing<T>` at field level).
69// 2) Add an `impl sealed::Sealed for T {}` below.
70// 3) Add an `impl Secret for T {}` below.
71// 4) Ensure xtask `check-constant-time` does not flag the type (no
72//    derived PartialEq/Eq; no raw `==` / `!=` on the type).
73// -------------------------------------------------------------------------
74
75impl __private::Sealed for crate::provider::SecureSeed {}
76impl Secret for crate::provider::SecureSeed {}
77
78impl __private::Sealed for crate::key_ops::TypedSeed {}
79impl Secret for crate::key_ops::TypedSeed {}
80
81impl __private::Sealed for crate::pkcs8::Pkcs8Der {}
82impl Secret for crate::pkcs8::Pkcs8Der {}
83
84impl __private::Sealed for crate::key_ops::TypedSignerKey {}
85impl Secret for crate::key_ops::TypedSignerKey {}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90    use crate::provider::SecureSeed;
91
92    /// Compile-time assertion that the types we expect to be `Secret` actually
93    /// implement the trait. If someone removes the impl, this fails at
94    /// compile time — not at runtime.
95    fn _assert_impl<T: Secret>() {}
96
97    #[test]
98    fn secret_marker_is_implemented_by_expected_types() {
99        _assert_impl::<SecureSeed>();
100        _assert_impl::<crate::key_ops::TypedSeed>();
101        _assert_impl::<crate::pkcs8::Pkcs8Der>();
102        _assert_impl::<crate::key_ops::TypedSignerKey>();
103    }
104}