open-standards/jouleclaw/jouleclaw-rs/crates/jouleclaw-policy-receipt/conformance/verify.go
Transaction Science f548464e4e jouleclaw-policy-receipt: 3-vector conformance suite + Python + Go verifiers (wave-18)
The same open-standards posture that turned JCR-1 into a standard
(three independent implementations agreeing byte-for-byte across
canonical CBOR + COSE_Sign1), now applied to policy receipts. A
policy receipt is a real spec, not a Rust house format.

## What ships

  - conformance/vectors-policy.json — 3 vectors spanning the
    decision space: Allow with named rule, Explicit deny with named
    rule, and No-matching-allow (deny-by-default). Each carries the
    receipt JSON, the canonical signed-core CBOR hex, and the
    COSE_Sign1 envelope hex.
  - conformance/verify.py — Python verifier (cbor2 + cryptography
    only). Re-encodes each canonical core, signs a fresh envelope
    under the same seed, asserts byte-equal against committed hex.
  - conformance/verify.go — Go verifier (stdlib only, no go.mod).
    From-scratch RFC 8949 §4.2.1 length-first CBOR encoder + COSE
    reader. Stdlib crypto/ed25519 verifies the envelope.
  - conformance/README.md — vector descriptions, regeneration
    procedure, scope.
  - examples/gen_policy_vectors.rs — Rust regenerator. Run with a
    path argument to write the vectors JSON; argument-less prints
    to stdout for inspection.
  - Embedded POLICY_VECTORS_JSON const + the
    policy_vectors_match_committed_conformance regression test in
    src/lib.rs — a drifting Rust producer fails BEFORE the Python
    and Go verifiers ever run.

## Cross-implementation verification

All three implementations cross-verified locally on the same vectors:

  Rust  (cargo test policy_vectors_match_committed_conformance): pass
  Python (python3 verify.py): pass on 3/3 vectors
  Go     (go run verify.go):  pass on 3/3 vectors

Same seed JCR-1 uses for cascade-receipt vectors, so a third party
can cross-validate both receipt families under a single test key.

## Workspace

102 crates, 2,533 lib tests pass (+1 for the conformance regression),
0 failures.
2026-06-02 05:45:16 -04:00

330 lines
7.5 KiB
Go

// Independent Go verifier for JouleClaw policy-receipt vectors.
//
// Stdlib only — no go.mod required. From-scratch canonical CBOR encoder
// (RFC 8949 §4.2.1 length-first map ordering), minimal COSE_Sign1
// reader, crypto/ed25519. Three independent implementations (Rust +
// Python + Go) MUST produce bit-identical bytes on every vector.
//
// Usage: go run verify.go [vectors-policy.json]
package main
import (
"bytes"
"crypto/ed25519"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"sort"
"strconv"
)
// ---------- canonical CBOR encoder (subset: uint, text, bstr, array, map) ----
func cborUint(buf *bytes.Buffer, major byte, n uint64) {
mt := major << 5
switch {
case n < 24:
buf.WriteByte(mt | byte(n))
case n < 1<<8:
buf.WriteByte(mt | 24)
buf.WriteByte(byte(n))
case n < 1<<16:
buf.WriteByte(mt | 25)
buf.WriteByte(byte(n >> 8))
buf.WriteByte(byte(n))
case n < 1<<32:
buf.WriteByte(mt | 26)
for s := 24; s >= 0; s -= 8 {
buf.WriteByte(byte(n >> uint(s)))
}
default:
buf.WriteByte(mt | 27)
for s := 56; s >= 0; s -= 8 {
buf.WriteByte(byte(n >> uint(s)))
}
}
}
func cborText(buf *bytes.Buffer, s string) {
cborUint(buf, 3, uint64(len(s)))
buf.WriteString(s)
}
func cborBytes(buf *bytes.Buffer, b []byte) {
cborUint(buf, 2, uint64(len(b)))
buf.Write(b)
}
func encodeValue(buf *bytes.Buffer, v interface{}) error {
switch x := v.(type) {
case string:
cborText(buf, x)
case json.Number:
u, err := strconv.ParseUint(x.String(), 10, 64)
if err != nil {
return fmt.Errorf("non-integer in signed core: %s", x.String())
}
cborUint(buf, 0, u)
case bool, float64, nil:
return fmt.Errorf("disallowed type in signed core: %T", x)
case []interface{}:
cborUint(buf, 4, uint64(len(x)))
for _, e := range x {
if err := encodeValue(buf, e); err != nil {
return err
}
}
case map[string]interface{}:
return encodeMap(buf, x)
default:
return fmt.Errorf("unsupported type %T", x)
}
return nil
}
// RFC 8949 §4.2.1 ordering — length-first, then bytewise on key.
func encodeMap(buf *bytes.Buffer, m map[string]interface{}) error {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
if len(keys[i]) != len(keys[j]) {
return len(keys[i]) < len(keys[j])
}
return keys[i] < keys[j]
})
cborUint(buf, 5, uint64(len(keys)))
for _, k := range keys {
cborText(buf, k)
if err := encodeValue(buf, m[k]); err != nil {
return err
}
}
return nil
}
// signedCore: canonical-encode the policy-receipt map verbatim. Unlike
// JCR-1 v1, there are no excluded fields in policy/1.
func signedCore(receipt map[string]interface{}) ([]byte, error) {
var buf bytes.Buffer
if err := encodeMap(&buf, receipt); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// ---------- minimal CBOR reader (only what COSE_Sign1 needs) ----------------
type reader struct {
b []byte
pos int
}
func (r *reader) byte() (byte, error) {
if r.pos >= len(r.b) {
return 0, fmt.Errorf("eof")
}
c := r.b[r.pos]
r.pos++
return c, nil
}
func (r *reader) head() (major byte, arg uint64, err error) {
c, err := r.byte()
if err != nil {
return
}
major = c >> 5
ai := c & 0x1f
switch {
case ai < 24:
arg = uint64(ai)
case ai == 24:
var v byte
v, err = r.byte()
arg = uint64(v)
case ai == 25:
for i := 0; i < 2; i++ {
var v byte
if v, err = r.byte(); err != nil {
return
}
arg = arg<<8 | uint64(v)
}
case ai == 26:
for i := 0; i < 4; i++ {
var v byte
if v, err = r.byte(); err != nil {
return
}
arg = arg<<8 | uint64(v)
}
case ai == 27:
for i := 0; i < 8; i++ {
var v byte
if v, err = r.byte(); err != nil {
return
}
arg = arg<<8 | uint64(v)
}
default:
err = fmt.Errorf("unsupported additional info %d", ai)
}
return
}
func (r *reader) bstr() ([]byte, error) {
major, n, err := r.head()
if err != nil {
return nil, err
}
if major != 2 {
return nil, fmt.Errorf("expected bstr, got major %d", major)
}
if r.pos+int(n) > len(r.b) {
return nil, fmt.Errorf("bstr overrun")
}
out := r.b[r.pos : r.pos+int(n)]
r.pos += int(n)
return out, nil
}
func parseCOSESign1(b []byte) (protected, payload, signature []byte, err error) {
r := &reader{b: b}
major, tag, err := r.head()
if err != nil {
return
}
if major == 6 {
if tag != 18 {
err = fmt.Errorf("unexpected tag %d", tag)
return
}
major, tag, err = r.head()
if err != nil {
return
}
}
if major != 4 || tag != 4 {
err = fmt.Errorf("expected 4-element array, got major %d len %d", major, tag)
return
}
if protected, err = r.bstr(); err != nil {
return
}
if m, a, e := r.head(); e != nil || m != 5 {
err = fmt.Errorf("expected map for unprotected header (major %d)", m)
_ = a
return
}
if payload, err = r.bstr(); err != nil {
return
}
signature, err = r.bstr()
return
}
// ---------- verification --------------------------------------------------
type TestKey struct {
SeedHex string `json:"ed25519_seed_hex"`
PubHex string `json:"ed25519_public_raw_hex"`
Kid string `json:"steward_kid"`
}
type Vectors struct {
SchemaID string `json:"schema_id"`
VectorCount int `json:"vector_count"`
Envelope string `json:"envelope"`
TestKey TestKey `json:"test_key"`
Vectors []struct {
N int `json:"n"`
Description string `json:"description"`
Receipt map[string]interface{} `json:"receipt"`
CoreHex string `json:"signed_core_cbor_hex"`
CoseHex string `json:"cose_sign1_hex"`
} `json:"vectors"`
}
func verify(rec map[string]interface{}, coreHex, coseHex string, pub ed25519.PublicKey) []string {
var errs []string
core, err := signedCore(rec)
if err != nil {
errs = append(errs, "encode error: "+err.Error())
}
want, _ := hex.DecodeString(coreHex)
if !bytes.Equal(core, want) {
errs = append(errs, "signed core re-encode mismatch")
}
coseBytes, _ := hex.DecodeString(coseHex)
protected, payload, signature, perr := parseCOSESign1(coseBytes)
if perr != nil {
errs = append(errs, "cose parse: "+perr.Error())
} else {
if !bytes.Equal(payload, want) {
errs = append(errs, "envelope payload != signed core")
}
var sig bytes.Buffer
sig.WriteByte(0x84) // array(4)
cborText(&sig, "Signature1")
cborBytes(&sig, protected)
cborBytes(&sig, []byte{})
cborBytes(&sig, payload)
if !ed25519.Verify(pub, sig.Bytes(), signature) {
errs = append(errs, "ed25519 verification failed")
}
}
return errs
}
func mustDecodeJSON(path string, v interface{}) {
f, err := os.Open(path)
if err != nil {
panic(err)
}
defer f.Close()
dec := json.NewDecoder(f)
dec.UseNumber()
if err := dec.Decode(v); err != nil {
panic(err)
}
}
func main() {
vecPath := "vectors-policy.json"
if len(os.Args) > 1 {
vecPath = os.Args[1]
}
var vs Vectors
mustDecodeJSON(vecPath, &vs)
pubBytes, _ := hex.DecodeString(vs.TestKey.PubHex)
pub := ed25519.PublicKey(pubBytes)
fmt.Printf("JouleClaw policy-receipt independent Go verifier — %d vectors — schema %s — %s\n\n",
vs.VectorCount, vs.SchemaID, vs.Envelope)
pass := 0
for _, v := range vs.Vectors {
errs := verify(v.Receipt, v.CoreHex, v.CoseHex, pub)
ok := len(errs) == 0
if ok {
pass++
}
status := "PASS"
if !ok {
status = "FAIL"
}
fmt.Printf(" [%s] vector %2d: %s\n", status, v.N, v.Description)
for _, e := range errs {
fmt.Printf(" - %s\n", e)
}
}
fmt.Printf("\n%d/%d vectors verified by the independent Go implementation\n", pass, vs.VectorCount)
if pass != vs.VectorCount {
os.Exit(1)
}
}