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

  1. Confirm you are verifying with the exact key that signed the records (check rotation history/secret manager entries).
  2. If the key is right and the hash chain (sequence/prev_hash/entry_hash) also checks, treat the record as tampered — quarantine and alert.
  3. If keys were rotated, verify old segments with the archived old key; unsigned records are skipped by design.
  4. 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

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


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