zeroclaw-labs/zeroclaw · critical
signature verification failed at line {} (sequence {}): sign
Error message
signature verification failed at line {} (sequence {}): signature mismatch What it means
For a signed record, the HMAC-SHA256 signature recomputed over the entry_hash (using ZEROCLAW_AUDIT_SIGNING_KEY) does not match the stored signature. Either the record was tampered with, or verification is running with a different key than the one that signed the records.
Source
Thrown at crates/zeroclaw-runtime/src/security/audit.rs:525
{
use hmac::{Hmac, Mac};
use sha2::Sha256;
let mut mac = Hmac::<Sha256>::new_from_slice(key_bytes).map_err(|e| {
::zeroclaw_log::record!(
ERROR,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
.with_outcome(::zeroclaw_log::EventOutcome::Failure)
.with_attrs(::serde_json::json!({"error": format!("{e}")})),
"audit log: HMAC-SHA256 verify rejected key length"
);
anyhow::Error::msg(format!("Invalid HMAC key length during verification: {e}"))
})?;
mac.update(entry.entry_hash.as_bytes());
let expected_sig = hex::encode(mac.finalize().into_bytes());
if signature != &expected_sig {
bail!(
"signature verification failed at line {} (sequence {}): signature mismatch",
line_idx + 1,
entry.sequence
);
}
}
// If signature present but key not available, skip verification (backward compat)
expected_prev_hash = entry.entry_hash.clone();
expected_sequence += 1;
}
Ok(expected_sequence)
}
#[cfg(test)]
mod tests {
use super::*;View on GitHub (pinned to 88bb9c8533)
Solutions
- Confirm you are verifying with the exact key that signed the records (check rotation history/secret manager entries).
- If the key is right and the hash chain (sequence/prev_hash/entry_hash) also checks, treat the record as tampered — quarantine and alert.
- If keys were rotated, verify old segments with the archived old key; unsigned records are skipped by design.
- Keep a key-history mapping (period -> key) alongside the audit logs.
Defensive patterns
Strategy: try-catch
Try / catch
if let Err(e) = audit.verify_chain() {
if e.to_string().contains("signature verification failed") {
// first rule out key mismatch (rotated/wrong env), then treat as tampering
}
} Prevention
- Maintain a key-history (time period -> signing key) and pass the key in use during each period when verifying old segments.
- Verify immediately after each rotation to catch key mismatches early.
- Keep signing keys out of config files; load from a secret manager at startup.
When it happens
Trigger: Verifying with a rotated or wrong ZEROCLAW_AUDIT_SIGNING_KEY; an attacker edited a signed line and could not re-sign (no key); records signed by key A verified after the env var was changed to key B; key pasted with whitespace/case differences (hex decode is case-tolerant, but a different key is not).
Common situations: Key rotated without archiving the old value, so older signed segments no longer verify; verification job running in a different environment (cron, CI) with a stale or wrong env var; two daemon instances configured with different signing keys writing to copies of the log; key pasted with trailing whitespace into the verifier's environment.
Related errors
- sequence gap at line {}: expected {}, got {}
- prev_hash mismatch at line {} (sequence {}): expected {}, go
- entry_hash mismatch at line {} (sequence {}): expected {}, g
- ZEROCLAW_AUDIT_SIGNING_KEY must be 32 bytes (64 hex chars),
- purge_namespace not supported by this memory backend
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/fb716c39416c9396.
Report an issue: GitHub.