Skip to main content

SipHashMAC

Struct SipHashMAC 

Source
pub struct SipHashMAC { /* private fields */ }
Expand description

SipHash-1-3 keyed MAC over the (next_idx, slot_addr) pair.

The 16-byte key is initialized once at Slab construction. On std builds use SipHashMAC::new to seed; in no_std contexts use SipHashMAC::with_key and supply entropy from a hardware RNG or boot-time source.

§Threat model and limitations

MAC width — 32 bits. The MAC type is u32 (matches the on-disk FreeLink layout in Slab’s freelist — FreeLink is private to the slab module). 32-bit MACs give a blind-forgery probability of 2^-32 per guess. Widening to 64 bits requires changing FreelistProtection::sign/verify to u64 and growing FreeLink by 4 bytes per slot (4–25% size overhead depending on T). For applications that need stronger guarantees, use PacMAC on aarch64 (hardware-backed pointer-authentication signature) once its instruction-level implementation is complete (currently a stub).

MAC-failure behavior in Slab. Today, a MAC mismatch (or a next_idx > capacity defense-in-depth tripwire) causes Slab to abandon the in-band freelist (*head_ptr = 0) and fall through to a fresh allocation from next_uncarved. The attacker-controlled link chain is torn down — they cannot navigate the slab to a chosen address — but the event is not propagated to the caller as an error, and Quarantine (when composed) does not see it: the allocate call returns Ok with a slot carved from the uncorrupted uncarved region. Debug builds additionally panic via debug_assert! so the regression surfaces in tests. For loud-fail- on-corruption semantics in production, the slot the attacker poisoned is leaked until slab drop — no slot is handed back to the caller from the corrupted chain. MAC-failure events are counted by the allocator and exposed via crate::Allocator::corruption_events; operators can monitor that counter to detect silent disarms at scale.

Truncation in SipHashMAC::mac (private): we take the low 32 bits of the SipHash-1-3 output. This is uniform random across forgeries provided the SipHash construction is sound (it is), so collision probability is 2^-32 per blind guess regardless of which 32 bits we keep.

Entropy source for SipHashMAC::new: std’s HashMap::RandomState. This goes through the same OS RNG path that hardens stdlib’s HashMap against algorithmic-complexity DoS — on Linux/macOS/Windows it draws from getrandom/arc4random/RtlGenRandom respectively. For deployments that mandate getrandom directly (FIPS-flow audit trail, no-HashMap-dep environments, or pre-stdlib init paths), supply the key via SipHashMAC::with_key and source the bytes from your project’s existing CSPRNG. The dep is intentionally not added to this crate to keep the no_std footprint minimal.

Implementations§

Source§

impl SipHashMAC

Source

pub const fn with_key(key: [u8; 16]) -> SipHashMAC

Construct with an explicit 16-byte key. Suitable for no_std.

Source

pub fn new() -> SipHashMAC

Seed from the OS entropy source.

Trait Implementations§

Source§

impl Clone for SipHashMAC

Available on crate feature siphasher only.

Manual Clone with per-byte volatile copy — the derived Clone lowered to a memcpy that the optimizer was free to coalesce, fuse with surrounding writes, or vectorize through registers, leaving transient byte-aligned copies of the key in caller stack frames outside the original key slot (which Drop then could not zeroize). The manual loop forces each byte through a volatile load/store pair, defeating that smear.

The compiler_fence at the end prevents the optimizer from reordering subsequent code into the middle of the copy and from fusing the writes with later non-volatile stores.

Source§

fn clone(&self) -> SipHashMAC

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SipHashMAC

Available on crate feature siphasher only.
Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for SipHashMAC

Available on crate features siphasher and std only.
Source§

fn default() -> SipHashMAC

Returns the “default value” for a type. Read more
Source§

impl Drop for SipHashMAC

Available on crate feature siphasher only.

Zeroize the key on drop so cloned instances don’t leave the secret in deallocated stack frames after a typed slab tears down.

Note: Copy is not derived (a typed slab takes the MAC by value at construction; explicit .clone() is required to install the same key in two slabs — that’s the intended security posture, and it lets us implement Drop to zeroize).

§Caveats — when Drop does not run

Drop-based zeroize protects only against keys that leave scope. It does not fire for:

  • static SipHashMAC / OnceCell<SipHashMAC> — process- lifetime statics never drop; the key persists until the process exits, observable via core dumps or /proc/<pid>/mem reads. Construct on the stack inside the function that uses it (e.g. a per-request slab) where the natural scope-exit fires Drop.
  • panic = "abort" builds (and the Quarantine abort-on-corruption path) — Drop is skipped on abort; if a SipHashMAC-using slab is live at the time, its key remains in freed-but-unmapped memory until the OS reclaims the address space. The OS-level free isn’t observable by other processes, but a same-process attacker who triggered the abort can still peek at the bytes if the abort handler reads memory before teardown.
  • mem::forget(slab) or Box::leak on a slab containing the MAC — explicit destructor suppression. Don’t combine these with security-critical wrappers.
Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more
Source§

impl FreelistProtection for SipHashMAC

Available on crate feature siphasher only.
Source§

fn sign(&self, next_idx: u32, slot_addr: usize) -> u32

Sign a freelist link. next_idx is the 1-based slot index being stored, or 0 for the end-of-list sentinel (so the input range is 0..=u32::MAX). slot_addr is the virtual address of the slot containing the link (used as a nonce so that a copy of a freelist link to a different slot won’t verify).
Source§

fn verify( &self, next_idx: u32, stored_mac: u32, slot_addr: usize, ) -> Result<(), FreelistCorruption>

Verify a stored MAC. Returns Ok(()) on a valid link, Err(FreelistCorruption) on a mismatch.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.