sanitization-derive 2.0.0

Optional derive macros for the sanitization crate.
Documentation

sanitization-derive

Optional derive macros for the sanitization crate.

Use through the main crate:

sanitization = { version = "2.0.0", features = ["derive"] }

The derive crate only generates calls to traits from sanitization; it does not implement memory wiping, comparison, or selection logic itself.

The runtime crate exact-pins this proc-macro crate to the same release. The macros generate references to runtime traits, so independently mixing an older runtime with a newer derive crate is unsupported and intentionally prevented by Cargo resolution.

Available derives:

  • SecureSanitize
  • SecureSanitizeOnDrop
  • ConstantTimeEq
  • ConditionallySelectable

ConstantTimeEq and ConditionallySelectable are conservative field-wise derives for structs. They never compare raw struct bytes, so they do not read padding or representation details.

use sanitization::ct::{ConditionallySelectable as _, ConstantTimeEq as _};
use sanitization::{ConditionallySelectable, ConstantTimeEq};

#[derive(ConstantTimeEq, ConditionallySelectable)]
struct Tag {
    left: [u8; 16],
    right: [u8; 16],
}

#[sanitization(skip, reason = "...")] is supported for SecureSanitize and ConstantTimeEq when a field is public or intentionally ignored. The reason must be non-empty. Skips are rejected for ConditionallySelectable because constructing the selected output requires every field.

Enums

All derives reject enums. Safe generated code can reach only the active variant and cannot clear bytes retained from a previously active, larger variant. Use a struct with stable secret storage and an explicit public state tag. A reviewed manual enum implementation must call sanitization::secure_replace(&mut value, replacement) before every transition.

Duplicate, malformed, empty, and misplaced helper options are rejected. reason is valid only together with skip. Enums and unions are rejected.

Generic SecureSanitizeOnDrop

For structs with type parameters that hold sanitizable data, put the SecureSanitize + Unpin bounds on the struct declaration:

use sanitization::{SecureSanitize, SecureSanitizeOnDrop};

#[derive(SecureSanitize, SecureSanitizeOnDrop)]
struct Wrapper<T: SecureSanitize + Unpin> {
    inner: T,
}

The generated Drop impl cannot add a stricter bound than the struct itself. It requires the complete struct to be Unpin because a destructor may receive a logically pinned value. It also requires DropSafeSanitize and invokes the complete SecureSanitize implementation so reviewed aggregate cleanup is not lost. #[derive(SecureSanitize)] supplies the marker for its generated field-wise implementation. A manual aggregate sanitizer must implement DropSafeSanitize explicitly after review; recursive self-replacement remains a contract violation.