Proof Format — occ/1

Normative specification for the occ/1 proof format. Derived from the reference implementation.

Proof JSON schema

{
  "version": "occ/1",                // REQUIRED — exact value
  "artifact": {
    "hashAlg": "sha256",             // REQUIRED — "sha256" only in v1
    "digestB64": "<base64>"          // REQUIRED — SHA-256, 32 decoded bytes
  },
  "commit": {
    "nonceB64": "<base64>",          // REQUIRED — ≥16 decoded bytes
    "counter":  "42",                // OPTIONAL — decimal string, monotonic
    "time":     1700000000000,       // OPTIONAL — Unix ms
    "prevB64":  "<base64>",          // OPTIONAL — chain link, 32 bytes
    "epochId":  "<hex>"              // OPTIONAL — SHA-256 hex
  },
  "signer": {
    "publicKeyB64":  "<base64>",     // REQUIRED — Ed25519, 32 bytes
    "signatureB64":  "<base64>"      // REQUIRED — Ed25519, 64 bytes
  },
  "environment": {
    "enforcement": "measured-tee",   // REQUIRED — "stub"|"hw-key"|"measured-tee"
    "measurement": "<opaque>",       // REQUIRED — non-empty string
    "attestation": {                 // OPTIONAL
      "format":    "aws-nitro",      // REQUIRED when parent present
      "reportB64": "<base64>"        // REQUIRED when parent present
    }
  },
  "agency": {                         // OPTIONAL — actor-bound proof
    "actor": { keyId, publicKeyB64, algorithm, provider },
    "authorization": { purpose, actorKeyId, artifactHash, challenge, timestamp, signatureB64 }
  },
  "timestamps": {                    // OPTIONAL — external timestamps
    "artifact": { TsaToken },
    "proof":    { TsaToken }
  },
  "metadata": { }                    // OPTIONAL — NOT signed, advisory
}

Signed body

The Ed25519 signature covers the canonical serialization of a SignedBody object:

{
  version:           proof.version,
  artifact:          proof.artifact,
  actor:             proof.agency?.actor,        // when present
  commit:            proof.commit,               // ALL fields verbatim
  publicKeyB64:      proof.signer.publicKeyB64,
  enforcement:       proof.environment.enforcement,
  measurement:       proof.environment.measurement,
  attestationFormat: proof.environment.attestation?.format  // when present
}

What is NOT signed

FieldReason
signatureB64The seal — cannot sign itself
attestation.reportB64Vendor-signed, self-authenticating separately
timestampsAdded post-signature by external TSA
metadataAdvisory, not trusted

Canonical serialization

The signed body is serialized to bytes using a deterministic algorithm:

  1. 1. Recursively sort all object keys in Unicode code-point order
  2. 2. Serialize with JSON.stringify() — no whitespace
  3. 3. Encode the resulting string as UTF-8 (no BOM)

Top-level key order after sort:

actor? → artifact → attestationFormat? → commit → enforcement → measurement → publicKeyB64 → version

Field classification

Signed (security-critical)

These fields are in the SignedBody. Tampering invalidates the signature:

version, artifact.*, agency.actor (when present), commit.*, signer.publicKeyB64, environment.enforcement, environment.measurement, attestation.format

Self-authenticating

Not in the signed body, but independently verifiable:

signatureB64 (Ed25519), attestation.reportB64 (vendor-signed)

Advisory (unsigned)

Not signed. Must not be used for security decisions: timestamps, metadata.

Algorithms

PurposeAlgorithmDetails
SignatureEd25519 (RFC 8032)32-byte key, 64-byte signature
HashSHA-256 (FIPS 180-4)32 bytes, Base64 encoded
EncodingBase64 (RFC 4648 §4)Standard, with = padding
CounterDecimal stringBigInt-safe, no leading zeros