@cello-protocol/protocol-types 0.0.22 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cbor.d.ts CHANGED
@@ -1,4 +1,24 @@
1
- /** Encode to canonical CBOR: byte strings for bytes, maps for objects. Plain RFC 8949. */
1
+ /**
2
+ * Encode to CBOR: byte strings for bytes, maps for objects. Valid RFC 8949.
3
+ *
4
+ * NOT RFC 8949 §4.2 deterministic FOR MAPS, and anything hashed or signed must account for that.
5
+ * Measured (M10 / DOD-CBOR-1, journal Entry 4):
6
+ *
7
+ * encode({b:1, a:2}) -> b9 0002 6162 01 6161 02
8
+ * encode({a:2, b:1}) -> b9 0002 6161 02 6162 01 <- different bytes, same object
9
+ *
10
+ * Two departures from Core Deterministic Encoding, both map-only: keys follow INSERTION ORDER
11
+ * rather than bytewise sort, and the map header is not minimal-length (`b9 0002` for a 2-entry map
12
+ * where CDE requires `a2`).
13
+ *
14
+ * ARRAYS, text strings, byte strings, integers and null ARE minimal and order-fixed. That is why
15
+ * every to-be-signed structure in CELLO is an ARRAY with a domain tag in slot 0 —
16
+ * buildAgentRevocationTbs, buildPrimaryTransferTbs, buildSealTbs, buildParkContentTbs,
17
+ * encodeTrustSignalEnvelope. Two parties encoding the same array always produce the same bytes;
18
+ * two parties encoding the same MAP do not, if they built it in a different field order.
19
+ *
20
+ * So: never hash or sign a CBOR map produced by this encoder. Put the fields in a fixed-order array.
21
+ */
2
22
  export declare function encodeCbor(value: unknown): Uint8Array;
3
23
  /** Decode CBOR. Tolerates the older tag-64 and record encodings so pre-migration data still reads. */
4
24
  export declare function decodeCbor(bytes: Uint8Array): unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"cbor.d.ts","sourceRoot":"","sources":["../src/cbor.ts"],"names":[],"mappings":"AAwBA,0FAA0F;AAC1F,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAErD;AAED,sGAAsG;AACtG,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAErD"}
1
+ {"version":3,"file":"cbor.d.ts","sourceRoot":"","sources":["../src/cbor.ts"],"names":[],"mappings":"AAwBA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAErD;AAED,sGAAsG;AACtG,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAErD"}
package/dist/cbor.js CHANGED
@@ -20,7 +20,27 @@
20
20
  */
21
21
  import { Encoder, decode } from "cbor-x";
22
22
  const ENCODER = new Encoder({ tagUint8Array: false, useRecords: false });
23
- /** Encode to canonical CBOR: byte strings for bytes, maps for objects. Plain RFC 8949. */
23
+ /**
24
+ * Encode to CBOR: byte strings for bytes, maps for objects. Valid RFC 8949.
25
+ *
26
+ * NOT RFC 8949 §4.2 deterministic FOR MAPS, and anything hashed or signed must account for that.
27
+ * Measured (M10 / DOD-CBOR-1, journal Entry 4):
28
+ *
29
+ * encode({b:1, a:2}) -> b9 0002 6162 01 6161 02
30
+ * encode({a:2, b:1}) -> b9 0002 6161 02 6162 01 <- different bytes, same object
31
+ *
32
+ * Two departures from Core Deterministic Encoding, both map-only: keys follow INSERTION ORDER
33
+ * rather than bytewise sort, and the map header is not minimal-length (`b9 0002` for a 2-entry map
34
+ * where CDE requires `a2`).
35
+ *
36
+ * ARRAYS, text strings, byte strings, integers and null ARE minimal and order-fixed. That is why
37
+ * every to-be-signed structure in CELLO is an ARRAY with a domain tag in slot 0 —
38
+ * buildAgentRevocationTbs, buildPrimaryTransferTbs, buildSealTbs, buildParkContentTbs,
39
+ * encodeTrustSignalEnvelope. Two parties encoding the same array always produce the same bytes;
40
+ * two parties encoding the same MAP do not, if they built it in a different field order.
41
+ *
42
+ * So: never hash or sign a CBOR map produced by this encoder. Put the fields in a fixed-order array.
43
+ */
24
44
  export function encodeCbor(value) {
25
45
  return ENCODER.encode(value);
26
46
  }
package/dist/cbor.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cbor.js","sourceRoot":"","sources":["../src/cbor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEzC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;AAEzE,0FAA0F;AAC1F,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAe,CAAC;AAC7C,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,UAAU,CAAC,KAAiB;IAC1C,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"cbor.js","sourceRoot":"","sources":["../src/cbor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEzC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAe,CAAC;AAC7C,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,UAAU,CAAC,KAAiB;IAC1C,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export { encodeCbor, decodeCbor } from "./cbor.js";
2
+ export { TRUST_SIGNAL_DOMAIN, encodeTrustSignalEnvelope, hashTrustSignalEnvelope, verifyTrustSignalHash, } from "./trust-signal.js";
3
+ export type { TrustSignalEnvelope, SignalSubjectKind, SignalIssuerKind, } from "./trust-signal.js";
2
4
  export type { MessageEnvelope, MessageEnvelopeV1, EnvelopeError, BuildResult, BuildResultV1, ValidateResult, ValidateResultV1, DeserializeResult, DeserializeResultV1, } from "./types.js";
3
5
  export { MAX_CONTENT_BYTES } from "./limits.js";
4
6
  export { encodeStructure1, STRUCTURE1_VERSION } from "./structure1.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,4 +1,8 @@
1
1
  export { encodeCbor, decodeCbor } from "./cbor.js";
2
+ // M10 / DOD-CBOR-1 — the canonical trust-signal envelope. ONE implementation, consumed by the
3
+ // portal (mint), the directory (submission + presentation), and both daemons (receipt +
4
+ // verification) — M10-D16.
5
+ export { TRUST_SIGNAL_DOMAIN, encodeTrustSignalEnvelope, hashTrustSignalEnvelope, verifyTrustSignalHash, } from "./trust-signal.js";
2
6
  export { MAX_CONTENT_BYTES } from "./limits.js";
3
7
  export { encodeStructure1, STRUCTURE1_VERSION } from "./structure1.js";
4
8
  export { SCAN_RESULT_SENTINEL, buildStructure2, encodeStructure2, encodeScanResultSentinel, verifyStructure2Signature, } from "./structure2.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAcnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGvE,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AA0BjK,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AAmBjC,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAEzF,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AA6DnF,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AAuBvB,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAO/B,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,uBAAuB,CAAC;AAE/B,wFAAwF;AACxF,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEnD,8FAA8F;AAC9F,wFAAwF;AACxF,2BAA2B;AAC3B,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAmB3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGvE,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AA0BjK,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AAmBjC,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAEzF,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AA6DnF,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AAuBvB,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAO/B,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,uBAAuB,CAAC;AAE/B,wFAAwF;AACxF,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
@@ -77,7 +77,7 @@ export declare const PRIMARY_TRANSFER_DOMAIN = "CELLO-PRIMARY-TRANSFER-v1";
77
77
  * produce IDENTICAL bytes, or verification never succeeds regardless of whether the ceremony
78
78
  * itself was genuine. Mirrors buildAgentRevocationTbs's exact structure (revocation.ts).
79
79
  */
80
- export declare function buildPrimaryTransferTbs(kLocalPubkeyHex: string, newDaemonId: string, oldDaemonId: string, nonce: string, timestamp: number): Uint8Array;
80
+ export declare function buildPrimaryTransferTbs(kLocalPubkeyHex: string, newDaemonId: string, oldDaemonId: string, nonce: string, timestamp: number | bigint): Uint8Array;
81
81
  /**
82
82
  * Step 1: the new Primary attests a transfer to one directory node. Sent on that node's
83
83
  * authenticated signaling stream. The daemon repeats this per node it dials.
@@ -1 +1 @@
1
- {"version":3,"file":"primary-transfer.d.ts","sourceRoot":"","sources":["../src/primary-transfer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AAKH;;;mEAGmE;AACnE,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAEnE;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,UAAU,CASZ;AAID;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,0BAA0B,CAAC;IACjC,yFAAyF;IACzF,cAAc,EAAE,MAAM,CAAC;IACvB,4EAA4E;IAC5E,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAC;IACd,oGAAoG;IACpG,SAAS,EAAE,MAAM,CAAC;CACnB;AAID;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,wBAAwB,CAAC;IAC/B,MAAM,EAAE,sBAAsB,GAAG,cAAc,GAAG,eAAe,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;CACzG"}
1
+ {"version":3,"file":"primary-transfer.d.ts","sourceRoot":"","sources":["../src/primary-transfer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AAKH;;;mEAGmE;AACnE,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAEnE;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAAG,MAAM,GACzB,UAAU,CAmBZ;AAID;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,0BAA0B,CAAC;IACjC,yFAAyF;IACzF,cAAc,EAAE,MAAM,CAAC;IACvB,4EAA4E;IAC5E,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAC;IACd,oGAAoG;IACpG,SAAS,EAAE,MAAM,CAAC;CACnB;AAID;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,wBAAwB,CAAC;IAC/B,MAAM,EAAE,sBAAsB,GAAG,cAAc,GAAG,eAAe,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;CACzG"}
@@ -79,13 +79,23 @@ export const PRIMARY_TRANSFER_DOMAIN = "CELLO-PRIMARY-TRANSFER-v1";
79
79
  * itself was genuine. Mirrors buildAgentRevocationTbs's exact structure (revocation.ts).
80
80
  */
81
81
  export function buildPrimaryTransferTbs(kLocalPubkeyHex, newDaemonId, oldDaemonId, nonce, timestamp) {
82
+ // Past 0xffffffff, cbor-x encodes a JS `number` as an IEEE float64 (`fb`), not a uint64 (`1b`) —
83
+ // so a MILLISECOND timestamp (Date.now() ≈ 1.77e12, which is what a caller will reach for) would
84
+ // be hashed as a float, and any non-cbor-x verifier would compute different TBS bytes and reject a
85
+ // genuine transfer. buildSealTbs (session.ts:160,271) and buildAgentRevocationTbs
86
+ // (revocation.ts:39) both carry this coercion; this builder shipped without it.
87
+ //
88
+ // Safe to add now: there is no client-side sender yet (`primary_transfer_request` has no producer
89
+ // in cello-client), so no signature exists over these bytes, and every timestamp below 2^32
90
+ // encodes byte-identically to before.
91
+ const tsEncoded = typeof timestamp === "bigint" || timestamp > 0xffffffff ? BigInt(timestamp) : timestamp;
82
92
  return encodeCbor([
83
93
  PRIMARY_TRANSFER_DOMAIN,
84
94
  kLocalPubkeyHex,
85
95
  newDaemonId,
86
96
  oldDaemonId,
87
97
  nonce,
88
- timestamp,
98
+ tsEncoded,
89
99
  ]);
90
100
  }
91
101
  //# sourceMappingURL=primary-transfer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"primary-transfer.js","sourceRoot":"","sources":["../src/primary-transfer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGvC;;;mEAGmE;AACnE,MAAM,CAAC,MAAM,uBAAuB,GAAG,2BAA2B,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACrC,eAAuB,EACvB,WAAmB,EACnB,WAAmB,EACnB,KAAa,EACb,SAAiB;IAEjB,OAAO,UAAU,CAAC;QAChB,uBAAuB;QACvB,eAAe;QACf,WAAW;QACX,WAAW;QACX,KAAK;QACL,SAAS;KACV,CAAe,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"primary-transfer.js","sourceRoot":"","sources":["../src/primary-transfer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGvC;;;mEAGmE;AACnE,MAAM,CAAC,MAAM,uBAAuB,GAAG,2BAA2B,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACrC,eAAuB,EACvB,WAAmB,EACnB,WAAmB,EACnB,KAAa,EACb,SAA0B;IAE1B,iGAAiG;IACjG,iGAAiG;IACjG,mGAAmG;IACnG,kFAAkF;IAClF,gFAAgF;IAChF,EAAE;IACF,kGAAkG;IAClG,4FAA4F;IAC5F,sCAAsC;IACtC,MAAM,SAAS,GAAG,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1G,OAAO,UAAU,CAAC;QAChB,uBAAuB;QACvB,eAAe;QACf,WAAW;QACX,WAAW;QACX,KAAK;QACL,SAAS;KACV,CAAe,CAAC;AACnB,CAAC"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The canonical trust-signal envelope (M10 / DOD-CBOR-1): serialize, hash, verify.
3
+ *
4
+ * Four parties independently re-derive this hash — the portal at mint, the directory at submission
5
+ * (INV-CHOKEPOINT's re-hash before store), the directory again at presentation (dumb check 1), and
6
+ * the holder/recipient daemons on receipt and at verification. They must agree byte-for-byte,
7
+ * forever, across three repos and two languages' worth of tooling. A single divergent byte turns a
8
+ * valid signal into a false `hash_mismatch` — unpresentable, and censorship-shaped, because it can
9
+ * differ per node or per client version.
10
+ *
11
+ * THE PREIMAGE IS AN ARRAY, NOT A MAP, AND THAT IS LOAD-BEARING (M10-D15).
12
+ * The shared encoder (`cbor.ts`) is not RFC 8949 §4.2 deterministic for MAPS: map keys follow
13
+ * INSERTION ORDER, and map headers are not minimal-length (a 2-entry map emits `b9 0002`, not `a2`).
14
+ * Arrays, text strings, byte strings, integers and null are all minimal and order-fixed. Encoding
15
+ * the preimage as a fixed-order array therefore makes determinism STRUCTURAL — a property of the
16
+ * shape, not of the encoder's configuration — and costs no encoder change and no migration of the
17
+ * blobs already on disk. Every signed TBS in CELLO is an array for this same reason
18
+ * (`buildAgentRevocationTbs`, `buildPrimaryTransferTbs`, `buildSealTbs`, `buildParkContentTbs`).
19
+ *
20
+ * Consequences, all enforced below:
21
+ * - FIELD ORDER IS THE WIRE FORMAT. Reordering the array is a breaking hash change, not a
22
+ * refactor. Retrofitting a canonical form later breaks every hash already minted (spec §5).
23
+ * - THE FIELD SET IS CLOSED (spec §4). An unknown field is REJECTED, never ignored: silently
24
+ * dropping it would let two parties who disagree about the field set still agree on the hash,
25
+ * making the extra field unauthenticated data riding inside a "verified" signal.
26
+ * - `status` / `class` / `verified_at` ARE NOT IN THE PREIMAGE. They are mutable after minting —
27
+ * that is the entire point of excluding them. If `status` were hashed, revoking a signal would
28
+ * change its hash and the directory could never find it again.
29
+ * - OPTIONAL FIELDS ARE AN EXPLICIT `null` IN A FIXED SLOT, NEVER OMITTED (M10-D17). In an array,
30
+ * omitting a field shifts every later field and changes the arity, so an absent `expires_at`
31
+ * becomes confusable with a misaligned `supersedes_hash`.
32
+ * - NO FLOATING POINT. Timestamps are integer epoch SECONDS. A float would not survive
33
+ * byte-agreement across languages.
34
+ * - `payload` IS OPAQUE BYTES, never decoded or re-encoded. A payload parsed and re-serialized
35
+ * would change bytes under a different encoder version. This is also what INV-ZERO-BUMP rests
36
+ * on: a type this code has never seen must hash and flow through untouched.
37
+ */
38
+ /** Domain separation, bound as element 0 of the preimage array — the house convention
39
+ * (`AGENT_REVOCATION_DOMAIN`, `PRIMARY_TRANSFER_DOMAIN`). A trust-signal hash can therefore never
40
+ * collide with, or be replayed as, any other hashed/signed CELLO structure. */
41
+ export declare const TRUST_SIGNAL_DOMAIN = "CELLO-TSIG-v1";
42
+ /** Whose fact this is. Account-subject signals (phone, email, social) are presentable by every agent
43
+ * under the account; agent-subject signals (track record) belong to one agent. Both are HASHED, so
44
+ * the scope cannot be changed after minting (M10-D5). */
45
+ export type SignalSubjectKind = "account" | "agent";
46
+ /** Who attested it. Drives FRAMING at the consuming LLM (INV-FRAMING): `portal` is portal-attested;
47
+ * `agent` is quoted-untrusted ("Bob says:"). Hashed, so framing cannot be laundered by a relabel. */
48
+ export type SignalIssuerKind = "portal" | "agent";
49
+ /**
50
+ * The hashed part of a trust signal. This is the WHOLE preimage and nothing else — see spec §4's
51
+ * mandatory-disclosure set. `type` is an OPAQUE STRING here and everywhere in the client and the
52
+ * directory: never an enum, never switched on (INV-ZERO-BUMP).
53
+ */
54
+ export interface TrustSignalEnvelope {
55
+ subject_kind: SignalSubjectKind;
56
+ /** The account id or agent id this fact is about. */
57
+ subject: string;
58
+ issuer_kind: SignalIssuerKind;
59
+ /** Hex-encoded Ed25519 public key of the issuer. Verified against the directory's authorized set. */
60
+ issuer_pubkey: string;
61
+ /** OPAQUE type string. Adding a type must require no code change anywhere but the portal. */
62
+ type: string;
63
+ schema_version: number;
64
+ /** OPAQUE payload bytes. Never parsed, never re-encoded, never hoisted into a column. */
65
+ payload: Uint8Array;
66
+ /** Integer epoch SECONDS. */
67
+ issued_at: number;
68
+ /** Integer epoch SECONDS, or null for a signal that never expires. */
69
+ expires_at: number | null;
70
+ /** The 32-byte hash this envelope replaces, or null for a first mint. */
71
+ supersedes_hash: Uint8Array | null;
72
+ }
73
+ /** The canonical preimage bytes. Deterministic across every party that runs this code. */
74
+ export declare function encodeTrustSignalEnvelope(envelope: TrustSignalEnvelope): Uint8Array;
75
+ /** The signal's identity: SHA-256 over the canonical preimage. This is the `signal_hash` that the
76
+ * directory stores, the holder presents, and the recipient re-derives. */
77
+ export declare function hashTrustSignalEnvelope(envelope: TrustSignalEnvelope): Uint8Array;
78
+ /**
79
+ * Does this envelope actually hash to `expected`? Length is checked first, so a truncated hash can
80
+ * never pass by matching a prefix. The comparison is constant-time in the length of the input: a
81
+ * signal hash is public, but the habit is cheap and the alternative invites a timing oracle the day
82
+ * one of these stops being public.
83
+ *
84
+ * ⚠️ THIS RETURNS `false` FOR A WRONG HASH BUT **THROWS** FOR A MALFORMED ENVELOPE, AND THAT
85
+ * ASYMMETRY IS DELIBERATE. A wrong hash is a normal, expected answer ("no, this doesn't match"). A
86
+ * malformed envelope is not an answer at all — it is an envelope that cannot be canonicalized, so
87
+ * there is no hash to compare and any boolean would be a lie.
88
+ *
89
+ * The directory calls this on hostile input. DO NOT wrap it in `try { … } catch { return false }`:
90
+ * that converts "this envelope is unhashable garbage" into "this envelope simply didn't match",
91
+ * which is the silent swallow that hides the attack. Let it throw, and log the cause.
92
+ */
93
+ export declare function verifyTrustSignalHash(envelope: TrustSignalEnvelope, expected: Uint8Array): boolean;
94
+ //# sourceMappingURL=trust-signal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust-signal.d.ts","sourceRoot":"","sources":["../src/trust-signal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAKH;;gFAEgF;AAChF,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AAEnD;;0DAE0D;AAC1D,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,OAAO,CAAC;AAEpD;sGACsG;AACtG,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElD;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,iBAAiB,CAAC;IAChC,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,gBAAgB,CAAC;IAC9B,qGAAqG;IACrG,aAAa,EAAE,MAAM,CAAC;IACtB,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,yFAAyF;IACzF,OAAO,EAAE,UAAU,CAAC;IACpB,6BAA6B;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,yEAAyE;IACzE,eAAe,EAAE,UAAU,GAAG,IAAI,CAAC;CACpC;AAmND,0FAA0F;AAC1F,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,UAAU,CAEnF;AAED;2EAC2E;AAC3E,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,UAAU,CAEjF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,mBAAmB,EAAE,QAAQ,EAAE,UAAU,GAAG,OAAO,CAMlG"}
@@ -0,0 +1,252 @@
1
+ /**
2
+ * The canonical trust-signal envelope (M10 / DOD-CBOR-1): serialize, hash, verify.
3
+ *
4
+ * Four parties independently re-derive this hash — the portal at mint, the directory at submission
5
+ * (INV-CHOKEPOINT's re-hash before store), the directory again at presentation (dumb check 1), and
6
+ * the holder/recipient daemons on receipt and at verification. They must agree byte-for-byte,
7
+ * forever, across three repos and two languages' worth of tooling. A single divergent byte turns a
8
+ * valid signal into a false `hash_mismatch` — unpresentable, and censorship-shaped, because it can
9
+ * differ per node or per client version.
10
+ *
11
+ * THE PREIMAGE IS AN ARRAY, NOT A MAP, AND THAT IS LOAD-BEARING (M10-D15).
12
+ * The shared encoder (`cbor.ts`) is not RFC 8949 §4.2 deterministic for MAPS: map keys follow
13
+ * INSERTION ORDER, and map headers are not minimal-length (a 2-entry map emits `b9 0002`, not `a2`).
14
+ * Arrays, text strings, byte strings, integers and null are all minimal and order-fixed. Encoding
15
+ * the preimage as a fixed-order array therefore makes determinism STRUCTURAL — a property of the
16
+ * shape, not of the encoder's configuration — and costs no encoder change and no migration of the
17
+ * blobs already on disk. Every signed TBS in CELLO is an array for this same reason
18
+ * (`buildAgentRevocationTbs`, `buildPrimaryTransferTbs`, `buildSealTbs`, `buildParkContentTbs`).
19
+ *
20
+ * Consequences, all enforced below:
21
+ * - FIELD ORDER IS THE WIRE FORMAT. Reordering the array is a breaking hash change, not a
22
+ * refactor. Retrofitting a canonical form later breaks every hash already minted (spec §5).
23
+ * - THE FIELD SET IS CLOSED (spec §4). An unknown field is REJECTED, never ignored: silently
24
+ * dropping it would let two parties who disagree about the field set still agree on the hash,
25
+ * making the extra field unauthenticated data riding inside a "verified" signal.
26
+ * - `status` / `class` / `verified_at` ARE NOT IN THE PREIMAGE. They are mutable after minting —
27
+ * that is the entire point of excluding them. If `status` were hashed, revoking a signal would
28
+ * change its hash and the directory could never find it again.
29
+ * - OPTIONAL FIELDS ARE AN EXPLICIT `null` IN A FIXED SLOT, NEVER OMITTED (M10-D17). In an array,
30
+ * omitting a field shifts every later field and changes the arity, so an absent `expires_at`
31
+ * becomes confusable with a misaligned `supersedes_hash`.
32
+ * - NO FLOATING POINT. Timestamps are integer epoch SECONDS. A float would not survive
33
+ * byte-agreement across languages.
34
+ * - `payload` IS OPAQUE BYTES, never decoded or re-encoded. A payload parsed and re-serialized
35
+ * would change bytes under a different encoder version. This is also what INV-ZERO-BUMP rests
36
+ * on: a type this code has never seen must hash and flow through untouched.
37
+ */
38
+ import { hash as sha256 } from "@cello-protocol/crypto";
39
+ import { encodeCbor } from "./cbor.js";
40
+ /** Domain separation, bound as element 0 of the preimage array — the house convention
41
+ * (`AGENT_REVOCATION_DOMAIN`, `PRIMARY_TRANSFER_DOMAIN`). A trust-signal hash can therefore never
42
+ * collide with, or be replayed as, any other hashed/signed CELLO structure. */
43
+ export const TRUST_SIGNAL_DOMAIN = "CELLO-TSIG-v1";
44
+ /** The preimage field order. THIS ARRAY IS THE WIRE FORMAT — reordering it breaks every hash ever
45
+ * minted. It also doubles as the closed-set definition used to reject unknown fields. */
46
+ const PREIMAGE_FIELDS = [
47
+ "subject_kind",
48
+ "subject",
49
+ "issuer_kind",
50
+ "issuer_pubkey",
51
+ "type",
52
+ "schema_version",
53
+ "payload",
54
+ "issued_at",
55
+ "expires_at",
56
+ "supersedes_hash",
57
+ ];
58
+ /** Sanity bound on epoch-SECOND timestamps. A millisecond timestamp (~1.7e12) is the classic
59
+ * cross-implementation skew: it is a valid integer, so nothing would complain, and it would mint a
60
+ * signal that expires roughly thirty thousand years from now. Refuse it at the door. */
61
+ const MAX_EPOCH_SECONDS = 100_000_000_000; // ~year 5138
62
+ /** Above this, a JS `number` is encoded by cbor-x as an IEEE **float64** (major type 7), not as a
63
+ * uint64 — see `asCborInteger`. Anything hashed must cross this boundary as a BigInt. */
64
+ const CBOR_UINT32_MAX = 0xffff_ffff;
65
+ /** SHA-256. Named separately from SUPERSEDES_HASH_BYTES even though both are 32 today: they are
66
+ * independent sizes, and coupling them means a future change to one silently breaks the other. */
67
+ const SIGNAL_HASH_BYTES = 32;
68
+ const SUPERSEDES_HASH_BYTES = 32;
69
+ /**
70
+ * Integers above 2³² MUST cross into CBOR as BigInt, or they are hashed as a FLOAT.
71
+ *
72
+ * Measured with this package's encoder:
73
+ *
74
+ * encode(1768000000) -> 1a 69618a00 uint32 (fine)
75
+ * encode(4920000000) -> fb 41f25413e0000000 FLOAT64 (WRONG)
76
+ * encode(BigInt(4920000000)) -> 1b 0000000125413e00 uint64 (what RFC 8949 requires)
77
+ *
78
+ * cbor-x emits any JS `number` above 0xffffffff as major type 7 float64. A conforming CBOR
79
+ * implementation in Rust, Go, or Python emits a uint64 — so the two disagree on the preimage, and
80
+ * therefore on the hash, permanently and unfixably (spec §5: retrofitting the canonical form breaks
81
+ * every hash already minted).
82
+ *
83
+ * This is NOT hypothetical and NOT a 2106 problem: an `expires_at` a hundred years out is
84
+ * 1.768e9 + 3.15e9 = 4.92e9, well past 2³², and reachable by the first long-dated signal the portal
85
+ * mints. `buildSealTbs` (session.ts) and `buildAgentRevocationTbs` (revocation.ts) both carry this
86
+ * coercion. The trust-signal envelope shipped without it and a reviewer caught it.
87
+ */
88
+ function asCborInteger(value) {
89
+ return value > CBOR_UINT32_MAX ? BigInt(value) : value;
90
+ }
91
+ /** A non-negative integer safe to hash — rejected if it is a float, NaN, Infinity, or negative, and
92
+ * coerced to BigInt past 2³² so it never encodes as a float64. */
93
+ function requireHashableInteger(name, value, max, hint) {
94
+ if (typeof value !== "number" || !Number.isFinite(value)) {
95
+ throw new Error(`trust-signal envelope: ${name} must be a finite integer, got ${String(value)}`);
96
+ }
97
+ if (!Number.isInteger(value)) {
98
+ throw new Error(`trust-signal envelope: ${name} must be an integer — no floating point is hashable across implementations, got ${value}`);
99
+ }
100
+ if (value < 0) {
101
+ throw new Error(`trust-signal envelope: ${name} must not be negative, got ${value}`);
102
+ }
103
+ if (value > Number.MAX_SAFE_INTEGER) {
104
+ // Past 2^53 a JS number cannot even represent consecutive integers, so the value the caller
105
+ // meant and the value we hash may already differ. Refuse rather than hash an approximation.
106
+ throw new Error(`trust-signal envelope: ${name} exceeds Number.MAX_SAFE_INTEGER (${value}) — it cannot be hashed exactly`);
107
+ }
108
+ if (value >= max) {
109
+ throw new Error(`trust-signal envelope: ${name} is out of range (${value}) — ${hint}`);
110
+ }
111
+ return asCborInteger(value);
112
+ }
113
+ const requireEpochSeconds = (name, value) => requireHashableInteger(name, value, MAX_EPOCH_SECONDS, "this looks like a millisecond timestamp; the contract is epoch SECONDS");
114
+ /**
115
+ * Every hashed STRING must be NFC-normalized, and we REFUSE a string that is not — we never
116
+ * normalize it silently.
117
+ *
118
+ * Spec §5's list of cross-language hash-breakers names Unicode normalization explicitly. The same
119
+ * logical string differs on the wire: "é" is `62 c3a9` in NFC and `63 65cc81` in NFD. CBOR does not
120
+ * normalize, and RFC 8949's deterministic profile does not require NFC, so nothing downstream saves
121
+ * us.
122
+ *
123
+ * Refusing rather than normalizing is deliberate: silently normalizing would make two DIFFERENT
124
+ * inputs hash to the SAME signal — a collision we manufactured ourselves, which is strictly worse
125
+ * than refusing the odd one at the door.
126
+ */
127
+ function requireNfcString(name, value) {
128
+ if (typeof value !== "string" || value.length === 0) {
129
+ throw new Error(`trust-signal envelope: ${name} must be a non-empty string, got ${String(value)}`);
130
+ }
131
+ if (value.normalize("NFC") !== value) {
132
+ throw new Error(`trust-signal envelope: ${name} must be Unicode NFC-normalized — the same logical string hashes ` +
133
+ `differently in NFD ("é" is c3a9 in NFC, 65cc81 in NFD), so a non-NFC value would produce a ` +
134
+ `signal no other implementation can reproduce. Normalize it before minting.`);
135
+ }
136
+ return value;
137
+ }
138
+ /**
139
+ * Validate against the CLOSED preimage set and return the fixed-order array to encode.
140
+ *
141
+ * Every rejection here is deliberate and loud. An envelope that cannot be canonicalized must never
142
+ * be hashed "best effort" — a best-effort hash is a hash two parties can disagree about.
143
+ */
144
+ function toPreimage(envelope) {
145
+ if (envelope === null || typeof envelope !== "object") {
146
+ throw new Error(`trust-signal envelope: expected an object, got ${String(envelope)}`);
147
+ }
148
+ // Closed set, both directions: no unknown fields, no missing fields.
149
+ const known = new Set(PREIMAGE_FIELDS);
150
+ for (const key of Object.keys(envelope)) {
151
+ if (!known.has(key)) {
152
+ throw new Error(`trust-signal envelope: unknown envelope field "${key}". The preimage is a CLOSED set (spec §4) — ` +
153
+ `an unhashed field riding inside a verified signal is unauthenticated data. ` +
154
+ `Note status/class/verified_at are deliberately NOT hashed (they are mutable after minting) ` +
155
+ `and do not belong on this object.`);
156
+ }
157
+ }
158
+ for (const field of PREIMAGE_FIELDS) {
159
+ if (!(field in envelope)) {
160
+ throw new Error(`trust-signal envelope: missing mandatory field "${field}" (the preimage is a closed set — spec §4)`);
161
+ }
162
+ }
163
+ const { subject_kind, subject, issuer_kind, issuer_pubkey, type, schema_version, payload, expires_at, supersedes_hash } = envelope;
164
+ if (subject_kind !== "account" && subject_kind !== "agent") {
165
+ throw new Error(`trust-signal envelope: subject_kind must be "account" or "agent", got ${String(subject_kind)}`);
166
+ }
167
+ if (issuer_kind !== "portal" && issuer_kind !== "agent") {
168
+ throw new Error(`trust-signal envelope: issuer_kind must be "portal" or "agent", got ${String(issuer_kind)}`);
169
+ }
170
+ requireNfcString("subject", subject);
171
+ requireNfcString("type", type);
172
+ // issuer_pubkey is hex, and hex has a CASE. "AABB" and "aabb" are the same key and DIFFERENT bytes,
173
+ // so a portal that stores it uppercase and a directory that lowercases it on read would produce
174
+ // two different signal_hashes for one signal — a false hash_mismatch, per-node and per-version.
175
+ // Lowercase-only removes the ambiguity at zero cost. (Length/validity is NOT checked here: this
176
+ // component's job is byte-agreement, and the key's validity is the directory's to judge when it
177
+ // checks it against the authorized-issuer set.)
178
+ requireNfcString("issuer_pubkey", issuer_pubkey);
179
+ if (!/^[0-9a-f]+$/.test(issuer_pubkey) || issuer_pubkey.length % 2 !== 0) {
180
+ throw new Error(`trust-signal envelope: issuer_pubkey must be LOWERCASE hex with an even length, got "${issuer_pubkey}". ` +
181
+ `Hex has a case, and "AABB" and "aabb" are the same key but different preimage bytes — which would ` +
182
+ `mint two different hashes for one signal.`);
183
+ }
184
+ const schemaVersion = requireHashableInteger("schema_version", schema_version, CBOR_UINT32_MAX + 1, "a schema version is a small counter, not an identifier");
185
+ // The payload must be RAW BYTES. An object here would be encoded as a CBOR map — the one
186
+ // non-deterministic shape in the encoder — and the hash would silently depend on key insertion
187
+ // order. Callers serialize their own payload and hand us the bytes.
188
+ if (!(payload instanceof Uint8Array)) {
189
+ throw new Error(`trust-signal envelope: payload must be raw bytes (Uint8Array), got ${payload === null ? "null" : typeof payload}. ` +
190
+ `An object payload would be map-encoded, and CBOR maps are not deterministic under this encoder — ` +
191
+ `serialize the payload yourself and pass the bytes.`);
192
+ }
193
+ const issuedAt = requireEpochSeconds("issued_at", envelope.issued_at);
194
+ const expiresAt = expires_at === null ? null : requireEpochSeconds("expires_at", expires_at);
195
+ if (supersedes_hash !== null) {
196
+ if (!(supersedes_hash instanceof Uint8Array)) {
197
+ throw new Error(`trust-signal envelope: supersedes_hash must be 32 raw bytes or null, got ${typeof supersedes_hash}`);
198
+ }
199
+ if (supersedes_hash.length !== SUPERSEDES_HASH_BYTES) {
200
+ throw new Error(`trust-signal envelope: supersedes_hash must be exactly ${SUPERSEDES_HASH_BYTES} bytes, got ${supersedes_hash.length}`);
201
+ }
202
+ }
203
+ // FIXED ORDER. Element 0 is the domain tag; the nullable slots keep their position so arity is
204
+ // always 11 and no field can be mistaken for another (M10-D17).
205
+ return [
206
+ TRUST_SIGNAL_DOMAIN,
207
+ subject_kind,
208
+ subject,
209
+ issuer_kind,
210
+ issuer_pubkey,
211
+ type,
212
+ schemaVersion,
213
+ payload,
214
+ issuedAt,
215
+ expiresAt,
216
+ supersedes_hash,
217
+ ];
218
+ }
219
+ /** The canonical preimage bytes. Deterministic across every party that runs this code. */
220
+ export function encodeTrustSignalEnvelope(envelope) {
221
+ return encodeCbor(toPreimage(envelope));
222
+ }
223
+ /** The signal's identity: SHA-256 over the canonical preimage. This is the `signal_hash` that the
224
+ * directory stores, the holder presents, and the recipient re-derives. */
225
+ export function hashTrustSignalEnvelope(envelope) {
226
+ return sha256(encodeTrustSignalEnvelope(envelope));
227
+ }
228
+ /**
229
+ * Does this envelope actually hash to `expected`? Length is checked first, so a truncated hash can
230
+ * never pass by matching a prefix. The comparison is constant-time in the length of the input: a
231
+ * signal hash is public, but the habit is cheap and the alternative invites a timing oracle the day
232
+ * one of these stops being public.
233
+ *
234
+ * ⚠️ THIS RETURNS `false` FOR A WRONG HASH BUT **THROWS** FOR A MALFORMED ENVELOPE, AND THAT
235
+ * ASYMMETRY IS DELIBERATE. A wrong hash is a normal, expected answer ("no, this doesn't match"). A
236
+ * malformed envelope is not an answer at all — it is an envelope that cannot be canonicalized, so
237
+ * there is no hash to compare and any boolean would be a lie.
238
+ *
239
+ * The directory calls this on hostile input. DO NOT wrap it in `try { … } catch { return false }`:
240
+ * that converts "this envelope is unhashable garbage" into "this envelope simply didn't match",
241
+ * which is the silent swallow that hides the attack. Let it throw, and log the cause.
242
+ */
243
+ export function verifyTrustSignalHash(envelope, expected) {
244
+ if (!(expected instanceof Uint8Array) || expected.length !== SIGNAL_HASH_BYTES)
245
+ return false;
246
+ const actual = hashTrustSignalEnvelope(envelope);
247
+ let diff = 0;
248
+ for (let i = 0; i < actual.length; i++)
249
+ diff |= actual[i] ^ expected[i];
250
+ return diff === 0;
251
+ }
252
+ //# sourceMappingURL=trust-signal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust-signal.js","sourceRoot":"","sources":["../src/trust-signal.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cello-protocol/protocol-types",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {
@@ -15,15 +15,18 @@
15
15
  ".": {
16
16
  "import": "./dist/index.js",
17
17
  "types": "./dist/index.d.ts"
18
- }
18
+ },
19
+ "./test/vectors/*": "./test/vectors/*",
20
+ "./package.json": "./package.json"
19
21
  },
20
22
  "files": [
21
23
  "dist/",
24
+ "test/vectors/",
22
25
  "package.json"
23
26
  ],
24
27
  "dependencies": {
25
28
  "cbor-x": "^1.6.4",
26
- "@cello-protocol/crypto": "0.0.21"
29
+ "@cello-protocol/crypto": "0.0.22"
27
30
  },
28
31
  "devDependencies": {
29
32
  "@claude-flow/testing": "3.0.0-alpha.6",
@@ -0,0 +1,10 @@
1
+ {
2
+ "description": "CELLO-CONNREQ-001: connection package canonical CBOR fixture",
3
+ "seed_hex": "deadbeefcafe0102030405060708090a0b0c0d0e0f101112131415161718191a",
4
+ "pseudonym_label": "ac009-agent",
5
+ "klocal_pubkey_derive": "ac009-klocal",
6
+ "primary_pubkey_derive": "ac009-primary",
7
+ "created_at": 1746057600000,
8
+ "expected_cbor_hex": "b900037170736575646f6e796d5f62696e64696e67b900066f70736575646f6e796d5f6c6162656c6b61633030392d6167656e746e6b5f6c6f63616c5f7075626b657958204c4dc1cdb4434b1cf0efc479f57f71c3e095ca28835ebf0fdd295fe48b6247916e7072696d6172795f7075626b657958409a8f5157d02c2588c6468da9e74b8414fe3719cd254de95689a045e01afa3bf69a8f5157d02c2588c6468da9e74b8414fe3719cd254de95689a045e01afa3bf66d6d6c5f6473615f7075626b65795905202e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad6a637265617465645f61741b0000019689249c00706d6c5f6473615f7369676e617475726559097494de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d608306c656e646f7273656d656e7473806c6174746573746174696f6e7380",
9
+ "_encoding": "Plain CBOR (RFC 8949). Byte fields are CBOR byte strings; objects are CBOR maps. NOT cbor-x's record extension (tag 57343) \u2014 that is a cbor-x-private format no other CBOR reader can parse."
10
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "description": "CELLO-MSG-003 AC-007 canonical v1 envelope fixture. Cross-implementation regression guard. RFC 8949 §4.2.1.",
3
+ "_note": "full_envelope_cbor_hex excludes sender_signature (keypair-dependent). Tests verify tbs_cbor_hex and content_hash_hex; signature is tested separately with a generated keypair.",
4
+ "inputs": {
5
+ "protocol_version": 1,
6
+ "content_hex": "68656c6c6f",
7
+ "sender_pubkey_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
8
+ "session_id_hex": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
9
+ "last_seen_seq": 0,
10
+ "timestamp": 1700000000000
11
+ },
12
+ "expected": {
13
+ "content_hash_hex": "8a2a5c9b768827de5a9552c38a044c66959c68f6d2f21b5260af54d2f87db827",
14
+ "tbs_cbor_hex": "860158208a2a5c9b768827de5a9552c38a044c66959c68f6d2f21b5260af54d2f87db8275820aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa50bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb001b0000018bcfe56800"
15
+ }
16
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ "description": "CELLO-SESSION-002 SI-004: genesis prev_root fixture. SHA-256(min(A,B) || max(A,B) || session_id || session_timestamp_be8) where pubkeys are sorted bytewise-lexicographically and timestamp is 8-byte big-endian unsigned integer (milliseconds since Unix epoch).",
3
+ "fixtures": [
4
+ {
5
+ "_note": "A < B (0xaa < 0xbb) — no reordering needed",
6
+ "inputs": {
7
+ "pubkey_a_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
8
+ "pubkey_b_hex": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
9
+ "session_id_hex": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
10
+ "session_timestamp_ms": 1700000000000
11
+ },
12
+ "intermediate": {
13
+ "min_pubkey_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
14
+ "max_pubkey_hex": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
15
+ "timestamp_be8_hex": "0000018bcfe56800",
16
+ "preimage_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000018bcfe56800"
17
+ },
18
+ "expected_genesis_prev_root_hex": "e2f3e8e2eff39907ea59df96aa4f63dbdae3ca8cebe35ed5e5240b1543161932"
19
+ },
20
+ {
21
+ "_note": "B < A (0xaa < 0xbb) — args supplied in reversed order; result must be identical to fixture 1 with same effective min/max",
22
+ "inputs": {
23
+ "pubkey_a_hex": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
24
+ "pubkey_b_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
25
+ "session_id_hex": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
26
+ "session_timestamp_ms": 1700000000000
27
+ },
28
+ "intermediate": {
29
+ "min_pubkey_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
30
+ "max_pubkey_hex": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
31
+ "timestamp_be8_hex": "0000018bcfe56800",
32
+ "preimage_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000018bcfe56800"
33
+ },
34
+ "expected_genesis_prev_root_hex": "e2f3e8e2eff39907ea59df96aa4f63dbdae3ca8cebe35ed5e5240b1543161932"
35
+ },
36
+ {
37
+ "_note": "Different pubkeys and timestamp — cross-check for distinct inputs",
38
+ "inputs": {
39
+ "pubkey_a_hex": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
40
+ "pubkey_b_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
41
+ "session_id_hex": "11111111111111111111111111111111",
42
+ "session_timestamp_ms": 1746000000000
43
+ },
44
+ "intermediate": {
45
+ "min_pubkey_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
46
+ "max_pubkey_hex": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
47
+ "timestamp_be8_hex": "0000019685b5b400",
48
+ "preimage_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb111111111111111111111111111111110000019685b5b400"
49
+ },
50
+ "expected_genesis_prev_root_hex": "b78b94839803ec7f1654518c0fbb796f1ba2992f7f40d611cca7948b92ff2848"
51
+ }
52
+ ]
53
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "description": "CELLO-MERKLE-002 AC-007: M1 scan_result sentinel — canonical CBOR map. RFC 8949 §4.2.1.",
3
+ "_note": "CBOR map with 3 keys sorted by byte-length (RFC 8949 §4.2.1): score(5) < verdict(7) < model_hash(10). score=null, verdict='unscanned' (9 bytes), model_hash=<32 zero bytes>.",
4
+ "expected_cbor_hex": "a36573636f7265f6677665726469637469756e7363616e6e65646a6d6f64656c5f6861736858200000000000000000000000000000000000000000000000000000000000000000"
5
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "description": "CELLO-MERKLE-002 AC-001: Structure 1 canonical CBOR fixture. RFC 8949 §4.2.1.",
3
+ "_note": "Canonical CBOR of the 6-element positional TBS array for protocol_version=1.",
4
+ "inputs": {
5
+ "protocol_version": 1,
6
+ "content_hash_hex": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
7
+ "sender_pubkey_hex": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
8
+ "session_id_hex": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
9
+ "last_seen_seq": 3,
10
+ "timestamp": 1700000000000
11
+ },
12
+ "expected_cbor_hex": "86015820cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc5820dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd50eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee031b0000018bcfe56800"
13
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "description": "CELLO-MERKLE-002 AC-002: Structure 2 canonical CBOR fixture. RFC 8949 §4.2.1.",
3
+ "_note": "Canonical CBOR of the 6-element positional relay-built leaf: [sequence_number, sender_pubkey, content_hash, sender_signature, scan_result, prev_root]. scan_result is the M1 sentinel: {score:null, verdict:'unscanned', model_hash:<32 zero bytes>}.",
4
+ "inputs": {
5
+ "sequence_number": 1,
6
+ "sender_pubkey_hex": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
7
+ "content_hash_hex": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
8
+ "sender_signature_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
9
+ "prev_root_hex": "0000000000000000000000000000000000000000000000000000000000000000"
10
+ },
11
+ "expected_cbor_hex": "86015820dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd5820cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa36573636f7265f6677665726469637469756e7363616e6e65646a6d6f64656c5f686173685820000000000000000000000000000000000000000000000000000000000000000058200000000000000000000000000000000000000000000000000000000000000000"
12
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "_comment": "CELLO-MSG-001 TBS v0 canonical CBOR fixture. Cross-implementation regression guard. RFC 8949 \u00a74.2.1.",
3
+ "protocol_version": 0,
4
+ "content_hash_hex": "0000000000000000000000000000000000000000000000000000000000000000",
5
+ "sender_pubkey_hex": "abababababababababababababababababababababababababababababababab",
6
+ "timestamp": 1746057600000,
7
+ "expected_cbor_hex": "8400582000000000000000000000000000000000000000000000000000000000000000005820abababababababababababababababababababababababababababababababab1b0000019689249c00"
8
+ }
@@ -0,0 +1,136 @@
1
+ {
2
+ "_comment": [
3
+ "DOD-CBOR-1 / DOD-INV-CANONICAL — FROZEN cross-party vectors for the trust-signal envelope.",
4
+ "Every party that derives a signal_hash — the portal at mint, the directory at submission and at",
5
+ "presentation, the holder and recipient daemons — MUST reproduce these bytes exactly. All three",
6
+ "repos check their SHIPPED @cello-protocol/protocol-types against this file, so a version skew",
7
+ "between repos fails loud here instead of silently forking the hash in production.",
8
+ "Preimage (array, fixed order, domain tag in slot 0 — M10-D15/D17):",
9
+ " [ 'CELLO-TSIG-v1', subject_kind, subject, issuer_kind, issuer_pubkey, type, schema_version,",
10
+ " payload, issued_at, expires_at|null, supersedes_hash|null ]",
11
+ "payload and supersedes_hash are hex-encoded HERE for legibility only; they are RAW BYTES in the",
12
+ "preimage. status/class/verified_at are deliberately NOT hashed (mutable after minting).",
13
+ "CHANGING A BYTE IN THIS FILE IS A PROTOCOL BREAK — it invalidates every signal ever minted."
14
+ ],
15
+ "vectors": [
16
+ {
17
+ "name": "reference — agent subject, portal issuer, no expiry, first mint",
18
+ "envelope": {
19
+ "subject_kind": "agent",
20
+ "subject": "agent-1",
21
+ "issuer_kind": "portal",
22
+ "issuer_pubkey": "aabb",
23
+ "type": "phone",
24
+ "schema_version": 1,
25
+ "payload": "01020304",
26
+ "issued_at": 1000000000,
27
+ "expires_at": null,
28
+ "supersedes_hash": null
29
+ },
30
+ "preimage_hex": "8b6d43454c4c4f2d545349472d7631656167656e74676167656e742d3166706f7274616c64616162626570686f6e650144010203041a3b9aca00f6f6",
31
+ "signal_hash_hex": "760af64a292f96bffde522e21a8fcae0c31fc7826c4e2c947939a2da50803672"
32
+ },
33
+ {
34
+ "name": "account subject — one envelope, every agent under the account presents it (M10-D5)",
35
+ "envelope": {
36
+ "subject_kind": "account",
37
+ "subject": "acct_7f3a",
38
+ "issuer_kind": "portal",
39
+ "issuer_pubkey": "3d4f6a2b8c1e",
40
+ "type": "email",
41
+ "schema_version": 1,
42
+ "payload": "a1646b696e64",
43
+ "issued_at": 1768000000,
44
+ "expires_at": 1799536000,
45
+ "supersedes_hash": null
46
+ },
47
+ "preimage_hex": "8b6d43454c4c4f2d545349472d7631676163636f756e7469616363745f3766336166706f7274616c6c33643466366132623863316565656d61696c0146a1646b696e641a69618a001a6b42bd80f6",
48
+ "signal_hash_hex": "2b7ace095f07efbd1c8f5efef701a8969d2c6c8efb3f6291b27eefcd287fba16"
49
+ },
50
+ {
51
+ "name": "supersession — a re-minted track-record signal replacing an earlier one",
52
+ "envelope": {
53
+ "subject_kind": "agent",
54
+ "subject": "agent-1",
55
+ "issuer_kind": "portal",
56
+ "issuer_pubkey": "aabb",
57
+ "type": "session_count",
58
+ "schema_version": 2,
59
+ "payload": "deadbeef",
60
+ "issued_at": 1768000123,
61
+ "expires_at": 1768604923,
62
+ "supersedes_hash": "1111111111111111111111111111111111111111111111111111111111111111"
63
+ },
64
+ "preimage_hex": "8b6d43454c4c4f2d545349472d7631656167656e74676167656e742d3166706f7274616c64616162626d73657373696f6e5f636f756e740244deadbeef1a69618a7b1a696ac4fb58201111111111111111111111111111111111111111111111111111111111111111",
65
+ "signal_hash_hex": "0184ae76433d9c4caf892d438bff3f64d44f0eed57470f0c141fee86b18e0bbb"
66
+ },
67
+ {
68
+ "name": "agent issuer — quoted-untrusted framing at the LLM (INV-FRAMING)",
69
+ "envelope": {
70
+ "subject_kind": "agent",
71
+ "subject": "agent-2",
72
+ "issuer_kind": "agent",
73
+ "issuer_pubkey": "ff00ff00",
74
+ "type": "endorsement",
75
+ "schema_version": 1,
76
+ "payload": "",
77
+ "issued_at": 1768000000,
78
+ "expires_at": null,
79
+ "supersedes_hash": null
80
+ },
81
+ "preimage_hex": "8b6d43454c4c4f2d545349472d7631656167656e74676167656e742d32656167656e746866663030666630306b656e646f7273656d656e7401401a69618a00f6f6",
82
+ "signal_hash_hex": "39d148391745fa3ea26b8fe57b5633c4bec7064a896527abb9300355b02ec5a6"
83
+ },
84
+ {
85
+ "name": "UNKNOWN type — a type this code has never seen must hash identically (INV-TYPE-CARRY)",
86
+ "envelope": {
87
+ "subject_kind": "agent",
88
+ "subject": "agent-1",
89
+ "issuer_kind": "portal",
90
+ "issuer_pubkey": "aabb",
91
+ "type": "some_type_invented_next_year",
92
+ "schema_version": 1,
93
+ "payload": "00",
94
+ "issued_at": 1768000000,
95
+ "expires_at": null,
96
+ "supersedes_hash": null
97
+ },
98
+ "preimage_hex": "8b6d43454c4c4f2d545349472d7631656167656e74676167656e742d3166706f7274616c6461616262781c736f6d655f747970655f696e76656e7465645f6e6578745f796561720141001a69618a00f6f6",
99
+ "signal_hash_hex": "9a637f2ca5b4fbb0f78c9eb2d33cfc433706290a7c4a4e6bf2628d258b90f1c9"
100
+ },
101
+ {
102
+ "name": "payload that LOOKS like CBOR — embedded verbatim, never parsed",
103
+ "envelope": {
104
+ "subject_kind": "agent",
105
+ "subject": "agent-1",
106
+ "issuer_kind": "portal",
107
+ "issuer_pubkey": "aabb",
108
+ "type": "canary_test",
109
+ "schema_version": 1,
110
+ "payload": "d840009fff",
111
+ "issued_at": 1768000000,
112
+ "expires_at": null,
113
+ "supersedes_hash": null
114
+ },
115
+ "preimage_hex": "8b6d43454c4c4f2d545349472d7631656167656e74676167656e742d3166706f7274616c64616162626b63616e6172795f746573740145d840009fff1a69618a00f6f6",
116
+ "signal_hash_hex": "5dc67366d7b8fc515a6d638b432d90e29a2f9841101a3ac1745ca7b602e2f837"
117
+ },
118
+ {
119
+ "name": "FAR-FUTURE expiry past 2^32 — must hash as a CBOR uint64 (1b...), NEVER a float64 (fb...)",
120
+ "envelope": {
121
+ "subject_kind": "agent",
122
+ "subject": "agent-1",
123
+ "issuer_kind": "portal",
124
+ "issuer_pubkey": "aabb",
125
+ "type": "phone",
126
+ "schema_version": 1,
127
+ "payload": "01020304",
128
+ "issued_at": 1768000000,
129
+ "expires_at": 4920000000,
130
+ "supersedes_hash": null
131
+ },
132
+ "preimage_hex": "8b6d43454c4c4f2d545349472d7631656167656e74676167656e742d3166706f7274616c64616162626570686f6e650144010203041a69618a001b0000000125413e00f6",
133
+ "signal_hash_hex": "3c95520077ca1bb25f360c886008691bab1114e1ecad43ba90558df5d5a0b79e"
134
+ }
135
+ ]
136
+ }