Skip to content

VerificationReceipt.get_signature is an unkeyed hash over 5/15 fields — receipts tamperable in place despite regulator-grade claims #44

Description

Summary

VerificationReceipt.get_signature() is presented as "cryptographic proof of verification… required for regulatory compliance (SEC, OCC, FinCEN)" but is an unkeyed SHA-256 over only 5 of 15 fieldscomputed_value, llm_value, difference, status, violations, proof_steps, formula_used, metadata are all outside it. Receipts can be tampered in place, or regenerated wholesale by anyone.

Found in the 2026-08-22 adversarial audit of v2.1.0 @ 26d6dd0. This was S-09 in closed #16 (filed P3); elevated to P1 because receipts are the only evidence artifact this package emits and they feed CrossGuard/UCP audit trails marketed as regulator-facing. Input on the format decision already posted on #37.

Location

qwed_finance/models/receipt.py:99-111 — signed payload = {receipt_id, timestamp, input_hash, verified, engine_used} only.

Evidence (CONFIRMED BY EXECUTION)

After calling get_signature():

  1. mutate rec.computed_value ($100.00 → $1.00)
  2. append fake entries to rec.violations and rec.proof_steps
  3. flip rec.status to REJECTED

get_signature() returns the identical hash. None of those mutations are detected.

Impact

Any holder of a receipt (including an attacker who modified it) can alter the verified value, violations, proof steps, and status without invalidating the signature. For the stated use cases (regulator submission, dispute resolution) this is worse than no signature — it creates a false sense of tamper-evidence.

Fix

Minimum stopgap: HMAC-SHA256 over the full canonical-JSON receipt (all fields), key held by the verifier instance.
Target state (per #37): align with qwed-verification's ES256 attestation so receipts carry issuer identity and third-party verifiability — one envelope across the ecosystem (ties into the VC v1.1 signature/issuer decision, see QWED-AI/qwed-verification#319).
Until either lands, soften the docstring claims.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Priority 1: architecture and verification correctnessbugSomething isn't workingsecuritysecurity

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions