Skip to content

[identity+crypto] Zeroize SecretKey and ChannelKey on drop #127

@intendednull

Description

@intendednull

Parent: #108

Problem

crates/identity/src/lib.rs:75-78:

#[derive(Clone)]
pub struct Identity {
    secret_key: SecretKey,
}

crates/crypto/src/lib.rs:54-70:

pub struct ChannelKey(pub(crate) [u8; 32]);

Neither type implements Drop to zeroize its bytes. Rust's default drop leaves the secret material in the freed allocation, where it can persist in RAM (and potentially swap) until overwritten. For long-lived processes like the relay or worker nodes, this is a real forensic concern.

Fix

  1. Add zeroize = { version = "1", features = ["derive"] } to the workspace dependencies.

  2. In willow-crypto:

    use zeroize::{Zeroize, ZeroizeOnDrop};
    
    #[derive(Clone, ZeroizeOnDrop)]
    pub struct ChannelKey(#[zeroize] pub(crate) [u8; 32]);
  3. In willow-identity:

    #[derive(Clone, ZeroizeOnDrop)]
    pub struct Identity {
        #[zeroize(skip)]   // SecretKey already impls zeroize itself (check iroh-base)
        secret_key: SecretKey,
    }

    If iroh-base::SecretKey does not already zeroize, wrap the bytes in a zeroizing newtype and convert on the boundary.

  4. Also zeroize any temporary Vec<u8> returned from Identity::to_bytes() before drop — consider a callback-based API:

    pub fn with_secret_bytes<F, R>(&self, f: F) -> R
    where
        F: FnOnce(&[u8]) -> R,
    {
        let mut bytes = self.secret_key.to_bytes().to_vec();
        let r = f(&bytes);
        bytes.zeroize();
        r
    }

Test

Not really testable in safe Rust (we can't introspect freed memory). Worth adding a doc-test / compile-pass test that both types implement ZeroizeOnDrop:

fn _assert_zeroize_on_drop() {
    fn assert<T: zeroize::ZeroizeOnDrop>() {}
    assert::<ChannelKey>();
    assert::<Identity>();
}

Out of scope

  • Zeroizing transient serialized forms in the transport crate.
  • Preventing the OS from swapping secret memory (mlock). Separate issue if needed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions