@cello-protocol/protocol-types 0.0.21 → 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 +25 -0
- package/dist/cbor.d.ts.map +1 -0
- package/dist/cbor.js +51 -0
- package/dist/cbor.js.map +1 -0
- package/dist/connection-package.d.ts +4 -0
- package/dist/connection-package.d.ts.map +1 -1
- package/dist/connection-package.js +5 -6
- package/dist/connection-package.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/limits.d.ts +10 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +10 -0
- package/dist/limits.js.map +1 -0
- package/dist/manifest.d.ts +2 -4
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +2 -4
- package/dist/manifest.js.map +1 -1
- package/dist/primary-transfer.d.ts +6 -8
- package/dist/primary-transfer.d.ts.map +1 -1
- package/dist/primary-transfer.js +18 -13
- package/dist/primary-transfer.js.map +1 -1
- package/dist/revocation.d.ts.map +1 -1
- package/dist/revocation.js +2 -5
- package/dist/revocation.js.map +1 -1
- package/dist/session-liveness.js +4 -4
- package/dist/session-liveness.js.map +1 -1
- package/dist/session.d.ts +26 -61
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +22 -54
- package/dist/session.js.map +1 -1
- package/dist/structure1.d.ts +16 -0
- package/dist/structure1.d.ts.map +1 -0
- package/dist/structure1.js +33 -0
- package/dist/structure1.js.map +1 -0
- package/dist/structure2.d.ts.map +1 -1
- package/dist/structure2.js +7 -8
- package/dist/structure2.js.map +1 -1
- package/dist/trust-signal.d.ts +94 -0
- package/dist/trust-signal.d.ts.map +1 -0
- package/dist/trust-signal.js +252 -0
- package/dist/trust-signal.js.map +1 -0
- package/package.json +6 -3
- package/test/vectors/connection-package-canonical.json +10 -0
- package/test/vectors/envelope-v1-canonical.json +16 -0
- package/test/vectors/genesis-prev-root.json +53 -0
- package/test/vectors/scan-result-unscanned.json +5 -0
- package/test/vectors/structure1-canonical.json +13 -0
- package/test/vectors/structure2-canonical.json +12 -0
- package/test/vectors/tbs-v0-canonical.json +8 -0
- package/test/vectors/trust-signal-envelope-canonical.json +136 -0
- package/dist/envelope.d.ts +0 -170
- package/dist/envelope.d.ts.map +0 -1
- package/dist/envelope.js +0 -776
- package/dist/envelope.js.map +0 -1
package/dist/session.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* session.ts — session wire types and their to-be-signed encodings.
|
|
3
3
|
*
|
|
4
4
|
* SessionAssignment: shared wire type used by directory (sender), client (receiver),
|
|
5
5
|
* and relay (verifier). Lives here so client can import it without touching @cello-protocol/directory.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* SessionAssignment is a discriminated union on `signature_type`, so TypeScript enforces that
|
|
8
|
+
* `signer_pubkey` is present exactly when the signature is FROST:
|
|
8
9
|
* signature_type: 'frost' | 'single'
|
|
9
10
|
* - 'frost': the directory_signature field carries the 64-byte combined FROST output
|
|
10
|
-
* - 'single':
|
|
11
|
+
* - 'single': legacy single-key directory signature (refused by current clients)
|
|
11
12
|
* signer_pubkey?: Uint8Array (32 bytes, present when signature_type === 'frost')
|
|
12
13
|
* - The initiator's primary_pubkey (group FROST key). Embedded so the counterparty
|
|
13
14
|
* can verify without a separate directory round-trip.
|
|
@@ -19,11 +20,11 @@
|
|
|
19
20
|
* Note: signer_pubkey is NOT in the TBS — it is derived from DKG and embedded in the frame.
|
|
20
21
|
*
|
|
21
22
|
* SealPayload: canonical CBOR of [session_id, final_root, close_timestamp, "PENDING"].
|
|
22
|
-
*
|
|
23
|
+
* content_hash = SHA-256(0x00 || SealPayload).
|
|
23
24
|
*
|
|
24
25
|
* computeGenesisPrevRoot: deterministic genesis prev_root for a two-party session.
|
|
25
26
|
*
|
|
26
|
-
* Formula
|
|
27
|
+
* Formula:
|
|
27
28
|
* SHA-256(min(A_pubkey, B_pubkey) || max(A_pubkey, B_pubkey) || session_id || timestamp_be8)
|
|
28
29
|
*
|
|
29
30
|
* Pubkeys are sorted bytewise-lexicographically (Buffer.compare).
|
|
@@ -31,46 +32,13 @@
|
|
|
31
32
|
* (milliseconds since Unix epoch). Raw byte concatenation — no CBOR at this boundary,
|
|
32
33
|
* which would introduce width ambiguity on the timestamp encoding.
|
|
33
34
|
*
|
|
34
|
-
* Per FIPS 180-4 (SHA-256)
|
|
35
|
+
* Per FIPS 180-4 (SHA-256).
|
|
35
36
|
*
|
|
36
|
-
*
|
|
37
|
+
* buildSessionEstablishmentTbs is exported from protocol-types so that BOTH the directory
|
|
38
|
+
* (signer) and the client (verifier) use identical canonical CBOR encoding. Any encoding
|
|
39
|
+
* drift causes silent verification failures.
|
|
37
40
|
*
|
|
38
|
-
*
|
|
39
|
-
* // Use a discriminated union so TypeScript enforces signer_pubkey is present for 'frost'
|
|
40
|
-
* type SessionAssignmentFrost = { ...common fields..., signature_type: 'frost', signer_pubkey: Uint8Array }
|
|
41
|
-
* type SessionAssignmentSingle = { ...common fields..., signature_type: 'single' }
|
|
42
|
-
* type SessionAssignment = SessionAssignmentFrost | SessionAssignmentSingle
|
|
43
|
-
*
|
|
44
|
-
* // Common fields (all existing SessionAssignment fields plus signature_type + signer_pubkey):
|
|
45
|
-
* session_id, participant_a, participant_b, relay_endpoint, directory_endpoint,
|
|
46
|
-
* session_timestamp, directory_pubkey, directory_signature
|
|
47
|
-
*
|
|
48
|
-
* buildSessionEstablishmentTbs(sessionId, pubA, pubB, genesisPrevRoot, timestamp):
|
|
49
|
-
* // HIGH-5: exported from protocol-types so both directory and client use identical CBOR encoding
|
|
50
|
-
* // Any encoding drift would cause silent verification failures.
|
|
51
|
-
* // TBS = canonical CBOR([session_id, agent_A_pubkey, agent_B_pubkey, genesis_prev_root, timestamp])
|
|
52
|
-
* // Per CONTEXT.md: uses CBOR_ENC.encode() with tagUint8Array: false
|
|
53
|
-
* // timestamp is uint32 or BigInt depending on size (>0xffffffff → BigInt)
|
|
54
|
-
* return CBOR_ENC.encode([sessionId, pubA, pubB, genesisPrevRoot,
|
|
55
|
-
* timestamp > 0xffffffff ? BigInt(timestamp) : timestamp])
|
|
56
|
-
*
|
|
57
|
-
* IMPORTANT-8: Existing test files that build SessionAssignment objects need updating to
|
|
58
|
-
* include signature_type. Files affected (all M1 tests add signature_type: 'single'):
|
|
59
|
-
* - packages/client/src/__tests__/session.test.ts (makeDirectoryAssignment helper)
|
|
60
|
-
* - packages/client/src/__tests__/msg004.test.ts (makeDirectoryAssignment helper)
|
|
61
|
-
* - packages/client/src/__tests__/session003.test.ts (multiple construction sites)
|
|
62
|
-
* - packages/client/src/__tests__/session006.test.ts (makeDirectoryAssignment helper)
|
|
63
|
-
* - packages/e2e-tests/src/__tests__/mcp-002.test.ts (assignment construction)
|
|
64
|
-
* - packages/relay/src/__tests__/relay-node.test.ts (makeAssignment helper)
|
|
65
|
-
* - packages/relay/src/__tests__/relay-incremental.test.ts (makeAssignment helper)
|
|
66
|
-
* - packages/directory/src/directory-frames.ts (decodeOutboundSignalingFrame parser)
|
|
67
|
-
*
|
|
68
|
-
* IMPORTANT-9: ReceiveAssignmentResult in client/src/types.ts needs 'unsupported_signature_type'
|
|
69
|
-
* added to its reason union.
|
|
70
|
-
*
|
|
71
|
-
* ─── End Phase P Pseudocode ───────────────────────────────────────────────────
|
|
72
|
-
*
|
|
73
|
-
* buildSealTbs: FROST TBS for conversation seal (SESSION-005).
|
|
41
|
+
* buildSealTbs: FROST TBS for a conversation seal.
|
|
74
42
|
* FROST TBS fields: [session_id, sealed_root, leaf_count, timestamp]
|
|
75
43
|
* Context string: "cello-frost-seal-v1" (per CONTEXT.md)
|
|
76
44
|
* Both sides (directory as signer, client as verifier) must use the same encoding.
|
|
@@ -103,16 +71,16 @@ interface SessionAssignmentCommon {
|
|
|
103
71
|
transport_mode?: 'direct' | 'relay';
|
|
104
72
|
}
|
|
105
73
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
74
|
+
* FROST-signed assignment. `signer_pubkey` is the initiator's group public key
|
|
75
|
+
* (primary_pubkey from DKG / trustedDealer commitments[0]). Embedded so the
|
|
76
|
+
* counterparty can verify without a directory round-trip.
|
|
109
77
|
*/
|
|
110
78
|
export interface SessionAssignmentFrost extends SessionAssignmentCommon {
|
|
111
79
|
signature_type: "frost";
|
|
112
80
|
signer_pubkey: Uint8Array;
|
|
113
81
|
}
|
|
114
82
|
/**
|
|
115
|
-
*
|
|
83
|
+
* Legacy single-key directory signature. Refused by current clients.
|
|
116
84
|
*/
|
|
117
85
|
export interface SessionAssignmentSingle extends SessionAssignmentCommon {
|
|
118
86
|
signature_type: "single";
|
|
@@ -121,8 +89,8 @@ export interface SessionAssignmentSingle extends SessionAssignmentCommon {
|
|
|
121
89
|
* Discriminated union. TypeScript enforces `signer_pubkey` is present only
|
|
122
90
|
* when `signature_type === 'frost'`.
|
|
123
91
|
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
92
|
+
* Only 'frost' is handled. A 'single' assignment is rejected with
|
|
93
|
+
* `unsupported_signature_type`.
|
|
126
94
|
*/
|
|
127
95
|
export type SessionAssignment = SessionAssignmentFrost | SessionAssignmentSingle;
|
|
128
96
|
/**
|
|
@@ -157,7 +125,6 @@ export declare function buildSessionEstablishmentTbs(sessionId: Uint8Array, pubA
|
|
|
157
125
|
/**
|
|
158
126
|
* SEAL control payload carried as the content_bytes of a ctrl leaf.
|
|
159
127
|
* Canonical CBOR encoding: [session_id, final_root, close_timestamp, "PENDING"].
|
|
160
|
-
* Per SESSION-003 behavior spec.
|
|
161
128
|
*/
|
|
162
129
|
export interface SealPayload {
|
|
163
130
|
session_id: Uint8Array;
|
|
@@ -167,7 +134,7 @@ export interface SealPayload {
|
|
|
167
134
|
}
|
|
168
135
|
/**
|
|
169
136
|
* Encode a SealPayload as canonical CBOR: [session_id, final_root, close_timestamp, "PENDING"].
|
|
170
|
-
* Per
|
|
137
|
+
* Per RFC 8949 §4.2.1.
|
|
171
138
|
*/
|
|
172
139
|
export declare function encodeSealPayload(payload: SealPayload): Uint8Array;
|
|
173
140
|
/**
|
|
@@ -195,9 +162,8 @@ export interface SessionAbandoned {
|
|
|
195
162
|
session_id: Uint8Array;
|
|
196
163
|
}
|
|
197
164
|
/**
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
* @deprecated Use SessionSealedFrost instead in M2+.
|
|
165
|
+
* Legacy session_sealed frame (single-key directory signature). Refused by current clients.
|
|
166
|
+
* @deprecated Use SessionSealedFrost.
|
|
201
167
|
*/
|
|
202
168
|
export interface SessionSealedSingle {
|
|
203
169
|
type: "session_sealed";
|
|
@@ -209,8 +175,8 @@ export interface SessionSealedSingle {
|
|
|
209
175
|
legibility?: SealLegibility;
|
|
210
176
|
}
|
|
211
177
|
/**
|
|
212
|
-
*
|
|
213
|
-
*
|
|
178
|
+
* session_sealed frame carrying the FROST-notarized ceremony signature.
|
|
179
|
+
* signature_type is 'frost' when the FROST ceremony completes.
|
|
214
180
|
*/
|
|
215
181
|
export interface SessionSealedFrost {
|
|
216
182
|
type: "session_sealed";
|
|
@@ -223,16 +189,16 @@ export interface SessionSealedFrost {
|
|
|
223
189
|
leaf_count?: number;
|
|
224
190
|
legibility?: SealLegibility;
|
|
225
191
|
}
|
|
226
|
-
/** Discriminated union:
|
|
192
|
+
/** Discriminated union: current senders emit SessionSealedFrost; SessionSealedSingle is the legacy wire format. */
|
|
227
193
|
export type SessionSealed = SessionSealedSingle | SessionSealedFrost;
|
|
228
194
|
/**
|
|
229
195
|
* Per-attestation marker.
|
|
230
196
|
* 'live' — the participant produced their SEAL acknowledgement leaf
|
|
231
197
|
* contemporaneously in this ceremony (full-bilateral / present-party).
|
|
232
198
|
* 'absent' — the participant produced no acknowledgement (counterparty ABSENT;
|
|
233
|
-
*
|
|
234
|
-
* 'recovered' — the acknowledgement was added post-hoc on return (
|
|
235
|
-
* bilateral-upgrade flow
|
|
199
|
+
* set by the unilateral-notarization flow).
|
|
200
|
+
* 'recovered' — the acknowledgement was added post-hoc on return (set by the
|
|
201
|
+
* bilateral-upgrade flow).
|
|
236
202
|
*/
|
|
237
203
|
export type AttestationMode = "live" | "recovered" | "absent";
|
|
238
204
|
/**
|
|
@@ -289,7 +255,6 @@ export interface SessionSealRejected {
|
|
|
289
255
|
/**
|
|
290
256
|
* seal_verified: directory → seal initiator, after all three verification passes pass.
|
|
291
257
|
* Tells the initiator: "I've verified the tree — coordinate the FROST ceremony now."
|
|
292
|
-
* Per SESSION-005 step 4 in the seal ceremony flow.
|
|
293
258
|
*/
|
|
294
259
|
export interface SealVerified {
|
|
295
260
|
type: "seal_verified";
|
package/dist/session.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
package/dist/session.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* session.ts — session wire types and their to-be-signed encodings.
|
|
3
3
|
*
|
|
4
4
|
* SessionAssignment: shared wire type used by directory (sender), client (receiver),
|
|
5
5
|
* and relay (verifier). Lives here so client can import it without touching @cello-protocol/directory.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* SessionAssignment is a discriminated union on `signature_type`, so TypeScript enforces that
|
|
8
|
+
* `signer_pubkey` is present exactly when the signature is FROST:
|
|
8
9
|
* signature_type: 'frost' | 'single'
|
|
9
10
|
* - 'frost': the directory_signature field carries the 64-byte combined FROST output
|
|
10
|
-
* - 'single':
|
|
11
|
+
* - 'single': legacy single-key directory signature (refused by current clients)
|
|
11
12
|
* signer_pubkey?: Uint8Array (32 bytes, present when signature_type === 'frost')
|
|
12
13
|
* - The initiator's primary_pubkey (group FROST key). Embedded so the counterparty
|
|
13
14
|
* can verify without a separate directory round-trip.
|
|
@@ -19,11 +20,11 @@
|
|
|
19
20
|
* Note: signer_pubkey is NOT in the TBS — it is derived from DKG and embedded in the frame.
|
|
20
21
|
*
|
|
21
22
|
* SealPayload: canonical CBOR of [session_id, final_root, close_timestamp, "PENDING"].
|
|
22
|
-
*
|
|
23
|
+
* content_hash = SHA-256(0x00 || SealPayload).
|
|
23
24
|
*
|
|
24
25
|
* computeGenesisPrevRoot: deterministic genesis prev_root for a two-party session.
|
|
25
26
|
*
|
|
26
|
-
* Formula
|
|
27
|
+
* Formula:
|
|
27
28
|
* SHA-256(min(A_pubkey, B_pubkey) || max(A_pubkey, B_pubkey) || session_id || timestamp_be8)
|
|
28
29
|
*
|
|
29
30
|
* Pubkeys are sorted bytewise-lexicographically (Buffer.compare).
|
|
@@ -31,55 +32,22 @@
|
|
|
31
32
|
* (milliseconds since Unix epoch). Raw byte concatenation — no CBOR at this boundary,
|
|
32
33
|
* which would introduce width ambiguity on the timestamp encoding.
|
|
33
34
|
*
|
|
34
|
-
* Per FIPS 180-4 (SHA-256)
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* type SessionAssignmentSingle = { ...common fields..., signature_type: 'single' }
|
|
42
|
-
* type SessionAssignment = SessionAssignmentFrost | SessionAssignmentSingle
|
|
43
|
-
*
|
|
44
|
-
* // Common fields (all existing SessionAssignment fields plus signature_type + signer_pubkey):
|
|
45
|
-
* session_id, participant_a, participant_b, relay_endpoint, directory_endpoint,
|
|
46
|
-
* session_timestamp, directory_pubkey, directory_signature
|
|
47
|
-
*
|
|
48
|
-
* buildSessionEstablishmentTbs(sessionId, pubA, pubB, genesisPrevRoot, timestamp):
|
|
49
|
-
* // HIGH-5: exported from protocol-types so both directory and client use identical CBOR encoding
|
|
50
|
-
* // Any encoding drift would cause silent verification failures.
|
|
51
|
-
* // TBS = canonical CBOR([session_id, agent_A_pubkey, agent_B_pubkey, genesis_prev_root, timestamp])
|
|
52
|
-
* // Per CONTEXT.md: uses CBOR_ENC.encode() with tagUint8Array: false
|
|
53
|
-
* // timestamp is uint32 or BigInt depending on size (>0xffffffff → BigInt)
|
|
54
|
-
* return CBOR_ENC.encode([sessionId, pubA, pubB, genesisPrevRoot,
|
|
55
|
-
* timestamp > 0xffffffff ? BigInt(timestamp) : timestamp])
|
|
56
|
-
*
|
|
57
|
-
* IMPORTANT-8: Existing test files that build SessionAssignment objects need updating to
|
|
58
|
-
* include signature_type. Files affected (all M1 tests add signature_type: 'single'):
|
|
59
|
-
* - packages/client/src/__tests__/session.test.ts (makeDirectoryAssignment helper)
|
|
60
|
-
* - packages/client/src/__tests__/msg004.test.ts (makeDirectoryAssignment helper)
|
|
61
|
-
* - packages/client/src/__tests__/session003.test.ts (multiple construction sites)
|
|
62
|
-
* - packages/client/src/__tests__/session006.test.ts (makeDirectoryAssignment helper)
|
|
63
|
-
* - packages/e2e-tests/src/__tests__/mcp-002.test.ts (assignment construction)
|
|
64
|
-
* - packages/relay/src/__tests__/relay-node.test.ts (makeAssignment helper)
|
|
65
|
-
* - packages/relay/src/__tests__/relay-incremental.test.ts (makeAssignment helper)
|
|
66
|
-
* - packages/directory/src/directory-frames.ts (decodeOutboundSignalingFrame parser)
|
|
67
|
-
*
|
|
68
|
-
* IMPORTANT-9: ReceiveAssignmentResult in client/src/types.ts needs 'unsupported_signature_type'
|
|
69
|
-
* added to its reason union.
|
|
70
|
-
*
|
|
71
|
-
* ─── End Phase P Pseudocode ───────────────────────────────────────────────────
|
|
72
|
-
*
|
|
73
|
-
* buildSealTbs: FROST TBS for conversation seal (SESSION-005).
|
|
35
|
+
* Per FIPS 180-4 (SHA-256).
|
|
36
|
+
*
|
|
37
|
+
* buildSessionEstablishmentTbs is exported from protocol-types so that BOTH the directory
|
|
38
|
+
* (signer) and the client (verifier) use identical canonical CBOR encoding. Any encoding
|
|
39
|
+
* drift causes silent verification failures.
|
|
40
|
+
*
|
|
41
|
+
* buildSealTbs: FROST TBS for a conversation seal.
|
|
74
42
|
* FROST TBS fields: [session_id, sealed_root, leaf_count, timestamp]
|
|
75
43
|
* Context string: "cello-frost-seal-v1" (per CONTEXT.md)
|
|
76
44
|
* Both sides (directory as signer, client as verifier) must use the same encoding.
|
|
77
45
|
* Per RFC 9591 (FROST), RFC 8949 (CBOR canonical), FIPS 180-4 (SHA-256).
|
|
78
46
|
*/
|
|
79
47
|
import { createHash } from "node:crypto";
|
|
80
|
-
import {
|
|
81
|
-
|
|
82
|
-
// ─── Session establishment TBS builder
|
|
48
|
+
import { decode as cborDecode } from "cbor-x";
|
|
49
|
+
import { encodeCbor } from "./cbor.js";
|
|
50
|
+
// ─── Session establishment TBS builder ────────────────────────────────────────
|
|
83
51
|
/**
|
|
84
52
|
* Build the FROST to-be-signed bytes for session establishment.
|
|
85
53
|
*
|
|
@@ -116,7 +84,7 @@ export function buildSessionEstablishmentTbs(sessionId, pubA, pubB, genesisPrevR
|
|
|
116
84
|
counterpartySessionPeerId !== undefined &&
|
|
117
85
|
counterpartySessionAddrs !== undefined &&
|
|
118
86
|
transportMode !== undefined) {
|
|
119
|
-
return
|
|
87
|
+
return encodeCbor([
|
|
120
88
|
sessionId,
|
|
121
89
|
pubA,
|
|
122
90
|
pubB,
|
|
@@ -130,7 +98,7 @@ export function buildSessionEstablishmentTbs(sessionId, pubA, pubB, genesisPrevR
|
|
|
130
98
|
]);
|
|
131
99
|
}
|
|
132
100
|
// Legacy (M1–M6): encode only the original 5 fields
|
|
133
|
-
return
|
|
101
|
+
return encodeCbor([
|
|
134
102
|
sessionId,
|
|
135
103
|
pubA,
|
|
136
104
|
pubB,
|
|
@@ -140,10 +108,10 @@ export function buildSessionEstablishmentTbs(sessionId, pubA, pubB, genesisPrevR
|
|
|
140
108
|
}
|
|
141
109
|
/**
|
|
142
110
|
* Encode a SealPayload as canonical CBOR: [session_id, final_root, close_timestamp, "PENDING"].
|
|
143
|
-
* Per
|
|
111
|
+
* Per RFC 8949 §4.2.1.
|
|
144
112
|
*/
|
|
145
113
|
export function encodeSealPayload(payload) {
|
|
146
|
-
return
|
|
114
|
+
return encodeCbor([
|
|
147
115
|
payload.session_id,
|
|
148
116
|
payload.final_root,
|
|
149
117
|
payload.close_timestamp > 0xffffffff
|
|
@@ -179,7 +147,7 @@ export function decodeSealPayload(bytes) {
|
|
|
179
147
|
return null;
|
|
180
148
|
return { session_id: sid, final_root: root, close_timestamp: ts, attestation: "PENDING" };
|
|
181
149
|
}
|
|
182
|
-
// ─── buildSealTbs
|
|
150
|
+
// ─── buildSealTbs ─────────────────────────────────────────────────────────────
|
|
183
151
|
/**
|
|
184
152
|
* Build the FROST to-be-signed bytes for a conversation seal ceremony.
|
|
185
153
|
*
|
|
@@ -196,7 +164,7 @@ export function decodeSealPayload(bytes) {
|
|
|
196
164
|
* @param timestamp - Unix milliseconds at the time of verification
|
|
197
165
|
*/
|
|
198
166
|
export function buildSealTbs(sessionId, sealedRoot, leafCount, timestamp) {
|
|
199
|
-
return
|
|
167
|
+
return encodeCbor([
|
|
200
168
|
sessionId,
|
|
201
169
|
sealedRoot,
|
|
202
170
|
leafCount,
|
package/dist/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** The Structure 1 version tag. Bound as the FIRST field, so a v1 claim can never read as a v2 one. */
|
|
2
|
+
export declare const STRUCTURE1_VERSION = 1;
|
|
3
|
+
/**
|
|
4
|
+
* Canonical Structure 1 bytes: [version, content_hash, sender_pubkey, session_id, last_seen_seq, timestamp].
|
|
5
|
+
*
|
|
6
|
+
* A timestamp above 2^32-1 is encoded as a CBOR bigint — a plain number would silently lose
|
|
7
|
+
* precision, and the sender's signature is over THESE bytes.
|
|
8
|
+
*/
|
|
9
|
+
export declare function encodeStructure1(fields: {
|
|
10
|
+
contentHash: Uint8Array;
|
|
11
|
+
senderPubkey: Uint8Array;
|
|
12
|
+
sessionId: Uint8Array;
|
|
13
|
+
lastSeenSeq: number;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
}): Uint8Array;
|
|
16
|
+
//# sourceMappingURL=structure1.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure1.d.ts","sourceRoot":"","sources":["../src/structure1.ts"],"names":[],"mappings":"AAcA,uGAAuG;AACvG,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,WAAW,EAAE,UAAU,CAAC;IACxB,YAAY,EAAE,UAAU,CAAC;IACzB,SAAS,EAAE,UAAU,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,UAAU,CAUb"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structure 1 — the sender's signed ordering claim, canonical CBOR.
|
|
3
|
+
*
|
|
4
|
+
* The daemon PARSES Structure 1 (it arrives from the relay, the ordering authority) but does not
|
|
5
|
+
* build one, so this encoder has no production caller. It is kept because it is the only written
|
|
6
|
+
* definition of the Structure 1 FIELD ORDER, and `structure1-canonical.json` is the only vector in
|
|
7
|
+
* either repo pinning the resulting bytes. Delete it and the wire format is defined solely by
|
|
8
|
+
* whatever the relay happens to emit, with nothing to catch a drift.
|
|
9
|
+
*
|
|
10
|
+
* Field order is LOAD-BEARING: it is signed over. Reorder a field and signatures produced by any
|
|
11
|
+
* other implementation stop verifying against ours.
|
|
12
|
+
*/
|
|
13
|
+
import { encodeCbor } from "./cbor.js";
|
|
14
|
+
/** The Structure 1 version tag. Bound as the FIRST field, so a v1 claim can never read as a v2 one. */
|
|
15
|
+
export const STRUCTURE1_VERSION = 1;
|
|
16
|
+
/**
|
|
17
|
+
* Canonical Structure 1 bytes: [version, content_hash, sender_pubkey, session_id, last_seen_seq, timestamp].
|
|
18
|
+
*
|
|
19
|
+
* A timestamp above 2^32-1 is encoded as a CBOR bigint — a plain number would silently lose
|
|
20
|
+
* precision, and the sender's signature is over THESE bytes.
|
|
21
|
+
*/
|
|
22
|
+
export function encodeStructure1(fields) {
|
|
23
|
+
const ts = fields.timestamp > 0xffffffff ? BigInt(fields.timestamp) : fields.timestamp;
|
|
24
|
+
return encodeCbor([
|
|
25
|
+
STRUCTURE1_VERSION,
|
|
26
|
+
fields.contentHash,
|
|
27
|
+
fields.senderPubkey,
|
|
28
|
+
fields.sessionId,
|
|
29
|
+
fields.lastSeenSeq,
|
|
30
|
+
ts,
|
|
31
|
+
]);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=structure1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure1.js","sourceRoot":"","sources":["../src/structure1.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,uGAAuG;AACvG,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAEpC;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAMhC;IACC,MAAM,EAAE,GAAG,MAAM,CAAC,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC;IACvF,OAAO,UAAU,CAAC;QAChB,kBAAkB;QAClB,MAAM,CAAC,WAAW;QAClB,MAAM,CAAC,YAAY;QACnB,MAAM,CAAC,SAAS;QAChB,MAAM,CAAC,WAAW;QAClB,EAAE;KACH,CAAC,CAAC;AACL,CAAC"}
|
package/dist/structure2.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structure2.d.ts","sourceRoot":"","sources":["../src/structure2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"structure2.d.ts","sourceRoot":"","sources":["../src/structure2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAKhD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,IAAI,CAAC;IACZ,OAAO,EAAE,WAAW,CAAC;IACrB,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,UAAU,CAAC;IAC1B,YAAY,EAAE,UAAU,CAAC;IACzB,gBAAgB,EAAE,UAAU,CAAC;IAC7B,WAAW,EAAE,kBAAkB,CAAC;IAChC,SAAS,EAAE,UAAU,CAAC;CACvB;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,UAAU,CAAA;CAAE,GACpC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC;AAIxC,4FAA4F;AAC5F,eAAO,MAAM,oBAAoB,EAAE,kBAIjC,CAAC;AAEH;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,IAAI,UAAU,CAiBrD;AAID;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,eAAe,EAAE,MAAM,EACvB,aAAa,EAAE,UAAU,EACzB,YAAY,EAAE,UAAU,EACxB,gBAAgB,EAAE,UAAU,EAC5B,SAAS,EAAE,UAAU,GACpB,qBAAqB,CAqCvB;AAID;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,UAAU,GAAG,UAAU,CAc3D;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,UAAU,EACd,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,GAAG,MAAM,GACzB,OAAO,CAWT"}
|
package/dist/structure2.js
CHANGED
|
@@ -15,9 +15,8 @@
|
|
|
15
15
|
* This is what the sender signs; verifyStructure2Signature reconstructs it from Structure 2 fields
|
|
16
16
|
* plus the session_id, last_seen_seq, and timestamp carried through the relay path.
|
|
17
17
|
*/
|
|
18
|
-
import {
|
|
18
|
+
import { encodeCbor } from "./cbor.js";
|
|
19
19
|
import { verify } from "@cello-protocol/crypto";
|
|
20
|
-
const CBOR_ENC = new Encoder({ tagUint8Array: false });
|
|
21
20
|
// ─── Sentinel ─────────────────────────────────────────────────────────────────
|
|
22
21
|
/** M1 placeholder scan_result sentinel. The scanner field is stable but unset until M4+. */
|
|
23
22
|
export const SCAN_RESULT_SENTINEL = Object.freeze({
|
|
@@ -102,11 +101,11 @@ export function encodeStructure2(s2) {
|
|
|
102
101
|
// We must embed the pre-encoded sentinel bytes as raw CBOR, not re-encode the object.
|
|
103
102
|
// Construct the 6-element array manually to preserve canonical byte representation.
|
|
104
103
|
const arrayHeader = new Uint8Array([0x86]); // 6-element CBOR array
|
|
105
|
-
const seqBytes =
|
|
106
|
-
const pubBytes =
|
|
107
|
-
const hashBytes =
|
|
108
|
-
const sigBytes =
|
|
109
|
-
const prevBytes =
|
|
104
|
+
const seqBytes = encodeCbor(s2.sequence_number);
|
|
105
|
+
const pubBytes = encodeCbor(s2.sender_pubkey);
|
|
106
|
+
const hashBytes = encodeCbor(s2.content_hash);
|
|
107
|
+
const sigBytes = encodeCbor(s2.sender_signature);
|
|
108
|
+
const prevBytes = encodeCbor(s2.prev_root);
|
|
110
109
|
return new Uint8Array(Buffer.concat([arrayHeader, seqBytes, pubBytes, hashBytes, sigBytes, sentinelBytes, prevBytes]));
|
|
111
110
|
}
|
|
112
111
|
// ─── Verify ───────────────────────────────────────────────────────────────────
|
|
@@ -125,7 +124,7 @@ export function encodeStructure2(s2) {
|
|
|
125
124
|
*/
|
|
126
125
|
export function verifyStructure2Signature(s2, session_id, last_seen_seq, timestamp) {
|
|
127
126
|
const tsEncoded = typeof timestamp === "bigint" ? timestamp : timestamp > 0xffffffff ? BigInt(timestamp) : timestamp;
|
|
128
|
-
const tbs =
|
|
127
|
+
const tbs = encodeCbor([
|
|
129
128
|
1,
|
|
130
129
|
s2.content_hash,
|
|
131
130
|
s2.sender_pubkey,
|
package/dist/structure2.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structure2.js","sourceRoot":"","sources":["../src/structure2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"structure2.js","sourceRoot":"","sources":["../src/structure2.ts"],"names":[],"mappings":"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"}
|
|
@@ -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"}
|