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
| Field | Reason |
|---|---|
signatureB64 | The seal — cannot sign itself |
attestation.reportB64 | Vendor-signed, self-authenticating separately |
timestamps | Added post-signature by external TSA |
metadata | Advisory, not trusted |
Canonical serialization
The signed body is serialized to bytes using a deterministic algorithm:
- 1. Recursively sort all object keys in Unicode code-point order
- 2. Serialize with
JSON.stringify()— no whitespace - 3. Encode the resulting string as UTF-8 (no BOM)
Top-level key order after sort:
actor? → artifact → attestationFormat? → commit → enforcement → measurement → publicKeyB64 → versionField 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.formatSelf-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
| Purpose | Algorithm | Details |
|---|---|---|
| Signature | Ed25519 (RFC 8032) | 32-byte key, 64-byte signature |
| Hash | SHA-256 (FIPS 180-4) | 32 bytes, Base64 encoded |
| Encoding | Base64 (RFC 4648 §4) | Standard, with = padding |
| Counter | Decimal string | BigInt-safe, no leading zeros |