CC-BY-4.0  ·  Specification v1

SignedReceipt Specification v1

Normative specification for the SignedReceipt signed privacy-attestation envelope. JSON Canonical Form (RFC 8785), ECDSA P-256, chain linking, public-key discovery.

View on GitHub Envelope format Signing Chain linking

1. Overview

An SignedReceipt is a tamper-evident JSON document that cryptographically proves three things: (a) what data was processed on the issuing device before egress; (b) what processing occurred (tokenization level, detector firings, hashes); and (c) that the document has not been altered. Receipts are chained so that the full history of a session can be verified without any central authority.

This specification is normative. Implementations that deviate from it are non-conformant regardless of what label they carry. The conformance test suite at signedreceipt.org/conformance is the executable form of this specification.

2. Envelope format

Receipts are UTF-8 JSON with LF line endings and no BOM. Post-canonicalization key order:

FieldTypeRequiredDescription
vstringyesVersion tag. "v1" at launch.
algstringyesSignature algorithm. "ecdsa-p256-sha256" at v1.
kidstringyesKey identifier. Resolved via /.well-known/signedreceipt-pubkey.pem.
issstring (URL)yesIssuer base URL. MUST NOT embed PII.
substringyesOpaque subject identifier. MUST NOT embed PII.
iatintegeryesIssued-at Unix epoch seconds.
jtistring (ULID)yesUnique receipt identifier.
chainobjectyesChain-linking metadata. See §4.
claimsobjectyesStructured claims. See §3.
sigstring (base64url)yesSignature over canonicalized receipt with sig removed.

3. Canonicalization (RFC 8785 JCS)

Before signing and before verification, the receipt MUST be serialized to JSON Canonical Form per RFC 8785. The sig field is removed before hashing. Any implementation that produces non-canonical output is non-conformant. The conformance fixture corpus ships byte-stable expected_canonical.bin files against which implementations are tested.

4. Signing algorithm

v1: ECDSA over P-256 with SHA-256 per FIPS 186-5. Deterministic nonces per RFC 6979 to produce byte-stable signatures given the same canonical bytes and key. Signature encoding: raw r || s (64 bytes), base64url, no padding. DER encoding is rejected.

v2 reservation: ML-DSA-65 (Dilithium) is reserved under "mldsa-65-sha3-256". Dual-signing ("ecdsa-p256-sha256+mldsa-65-sha3-256") is permitted during migration windows.

Keys MUST be generated on a FIPS-validated module for FIPS-certified deployments.

5. Chain linking

Each receipt's chain object carries:

FieldDescription
prev_hashSHA-256 hex of previous receipt canonical bytes including sig. null for the first receipt.
chain_idULID unique to the session/device chain. Immutable.
seqMonotonic integer starting at 0. MUST increment by exactly 1 per receipt.

A chain is valid iff every prev_hash matches its predecessor, seq is gapless, and every signature verifies. Forked chains (two receipts with the same seq and chain_id) MUST be rejected.

6. Public-key discovery

Issuers publish current and retired keys at <iss>/.well-known/signedreceipt-pubkey.pem — PEM-encoded SubjectPublicKeyInfo blocks, one per key, each preceded by an armoured comment carrying kid, iat, and optionally retired. Transport: HTTPS only. Verifiers MAY pin a local trust bundle via --trust-bundle <path>.