// 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) } }