zeroclaw-labs/zeroclaw · info

HMAC accepts any key length

Error message

HMAC accepts any key length

What it means

TuiIdentityRegistry::sign() signs a TUI id with HMAC-SHA256 when a signing key is configured. new_from_slice() is expected to succeed with "HMAC accepts any key length"; HMAC imposes no key-length restriction, so this is a static invariant and the expect cannot realistically fire.

Source

Thrown at crates/zeroclaw-runtime/src/rpc/tui_identity.rs:99

    /// Generate a TUI ID that is not currently in the registry.
    pub fn generate_unique_tui_id(&self) -> String {
        let connected = self.connected.lock().unwrap_or_else(|e| e.into_inner());
        loop {
            let id = Self::generate_tui_id();
            if !connected.contains_key(&id) {
                return id;
            }
        }
    }

    // ── HMAC signing ─────────────────────────────────────────────

    /// Sign a TUI ID with HMAC-SHA256. Returns `None` if signing is
    /// disabled.
    pub fn sign(&self, tui_id: &str) -> Option<String> {
        let key = self.signing_key.as_ref()?;
        let mut mac = HmacSha256::new_from_slice(key).expect("HMAC accepts any key length");
        mac.update(tui_id.as_bytes());
        Some(hex::encode(mac.finalize().into_bytes()))
    }

    /// Verify a TUI ID + signature. Returns `true` if:
    /// - Signing is disabled (trust all), OR
    /// - The signature is valid.
    pub fn verify(&self, tui_id: &str, sig: &str) -> bool {
        let Some(ref key) = self.signing_key else {
            return true;
        };
        let Ok(sig_bytes) = hex::decode(sig) else {
            return false;
        };
        let mut mac = HmacSha256::new_from_slice(key).expect("HMAC accepts any key length");
        mac.update(tui_id.as_bytes());
        mac.verify_slice(&sig_bytes).is_ok()
    }

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. No action needed at runtime; if seen, inspect local modifications to TuiIdentityRegistry key handling.
  2. Keep the configured signing key a non-empty secret as good practice.
  3. Add a roundtrip unit test (sign_verify_roundtrip exists) when modifying this code.
Defensive patterns

Strategy: validation

Validate before calling

// At config load, reject empty signing keys before they reach the registry:
if let Some(key) = &config.tui_signing_key {
    anyhow::ensure!(!key.is_empty(), "tui signing key must be a non-empty secret");
}

Prevention

When it happens

Trigger: Calling sign() while TUI-id signing is enabled (signing_key is Some). No key material can make new_from_slice fail; only a refactor that bypasses HMAC's key contract could trigger it.

Common situations: None for users. Maintainers touching the signing_key type (e.g. changing it from Vec<u8>) should re-check the invariant.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/b2cfb52640e9e610. Report an issue: GitHub.