@cello-protocol/protocol-types 0.0.2
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/connection-package.d.ts +362 -0
- package/dist/connection-package.d.ts.map +1 -0
- package/dist/connection-package.js +426 -0
- package/dist/connection-package.js.map +1 -0
- package/dist/connection-request.d.ts +271 -0
- package/dist/connection-request.d.ts.map +1 -0
- package/dist/connection-request.js +91 -0
- package/dist/connection-request.js.map +1 -0
- package/dist/envelope.d.ts +170 -0
- package/dist/envelope.d.ts.map +1 -0
- package/dist/envelope.js +776 -0
- package/dist/envelope.js.map +1 -0
- package/dist/frost-dkg.d.ts +145 -0
- package/dist/frost-dkg.d.ts.map +1 -0
- package/dist/frost-dkg.js +28 -0
- package/dist/frost-dkg.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/registration.d.ts +155 -0
- package/dist/registration.d.ts.map +1 -0
- package/dist/registration.js +55 -0
- package/dist/registration.js.map +1 -0
- package/dist/session.d.ts +239 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +198 -0
- package/dist/session.js.map +1 -0
- package/dist/structure2.d.ts +75 -0
- package/dist/structure2.d.ts.map +1 -0
- package/dist/structure2.js +138 -0
- package/dist/structure2.js.map +1 -0
- package/dist/types.d.ts +144 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/package.json +37 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO-SESSION-002/MSG-004/SESSION-003/SESSION-004/SESSION-005 — session.ts
|
|
3
|
+
*
|
|
4
|
+
* SessionAssignment: shared wire type used by directory (sender), client (receiver),
|
|
5
|
+
* and relay (verifier). Lives here so client can import it without touching @cello-protocol/directory.
|
|
6
|
+
*
|
|
7
|
+
* SESSION-004 additions to SessionAssignment:
|
|
8
|
+
* signature_type: 'frost' | 'single'
|
|
9
|
+
* - 'frost': the directory_signature field carries the 64-byte combined FROST output
|
|
10
|
+
* - 'single': M1-era single-key directory signature (refused by M2 clients)
|
|
11
|
+
* signer_pubkey?: Uint8Array (32 bytes, present when signature_type === 'frost')
|
|
12
|
+
* - The initiator's primary_pubkey (group FROST key). Embedded so the counterparty
|
|
13
|
+
* can verify without a separate directory round-trip.
|
|
14
|
+
*
|
|
15
|
+
* FROST TBS for session establishment (RFC 9591, domain separation per CONTEXT.md):
|
|
16
|
+
* context: "cello-frost-session-establishment-v1"
|
|
17
|
+
* tbs: canonical CBOR([session_id, agent_A_pubkey, agent_B_pubkey, genesis_prev_root, timestamp])
|
|
18
|
+
* framing: <context>\0<tbs_cbor>
|
|
19
|
+
* Note: signer_pubkey is NOT in the TBS — it is derived from DKG and embedded in the frame.
|
|
20
|
+
*
|
|
21
|
+
* SealPayload: canonical CBOR of [session_id, final_root, close_timestamp, "PENDING"].
|
|
22
|
+
* Per SESSION-003. content_hash = SHA-256(0x00 || SealPayload) per MERKLE-001.
|
|
23
|
+
*
|
|
24
|
+
* computeGenesisPrevRoot: deterministic genesis prev_root for a two-party session.
|
|
25
|
+
*
|
|
26
|
+
* Formula (SI-004):
|
|
27
|
+
* SHA-256(min(A_pubkey, B_pubkey) || max(A_pubkey, B_pubkey) || session_id || timestamp_be8)
|
|
28
|
+
*
|
|
29
|
+
* Pubkeys are sorted bytewise-lexicographically (Buffer.compare).
|
|
30
|
+
* timestamp_be8 is the session_timestamp encoded as an 8-byte big-endian unsigned integer
|
|
31
|
+
* (milliseconds since Unix epoch). Raw byte concatenation — no CBOR at this boundary,
|
|
32
|
+
* which would introduce width ambiguity on the timestamp encoding.
|
|
33
|
+
*
|
|
34
|
+
* Per FIPS 180-4 (SHA-256) and SESSION-002 SI-004.
|
|
35
|
+
*
|
|
36
|
+
* ─── Phase P: SESSION-004 Pseudocode ─────────────────────────────────────────
|
|
37
|
+
*
|
|
38
|
+
* SessionAssignment type changes (MEDIUM-6: discriminated union enforces signer_pubkey):
|
|
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).
|
|
74
|
+
* FROST TBS fields: [session_id, sealed_root, leaf_count, timestamp]
|
|
75
|
+
* Context string: "cello-frost-seal-v1" (per CONTEXT.md)
|
|
76
|
+
* Both sides (directory as signer, client as verifier) must use the same encoding.
|
|
77
|
+
* Per RFC 9591 (FROST), RFC 8949 (CBOR canonical), FIPS 180-4 (SHA-256).
|
|
78
|
+
*/
|
|
79
|
+
export interface ParticipantInfo {
|
|
80
|
+
pubkey: Uint8Array;
|
|
81
|
+
peer_id: string;
|
|
82
|
+
multiaddrs: string[];
|
|
83
|
+
}
|
|
84
|
+
export interface RelayEndpointInfo {
|
|
85
|
+
peer_id: string;
|
|
86
|
+
multiaddrs: string[];
|
|
87
|
+
}
|
|
88
|
+
/** Common fields shared by both SessionAssignment variants. */
|
|
89
|
+
interface SessionAssignmentCommon {
|
|
90
|
+
session_id: Uint8Array;
|
|
91
|
+
participant_a: ParticipantInfo;
|
|
92
|
+
participant_b: ParticipantInfo;
|
|
93
|
+
relay_endpoint: RelayEndpointInfo;
|
|
94
|
+
directory_endpoint: RelayEndpointInfo;
|
|
95
|
+
session_timestamp: number;
|
|
96
|
+
directory_pubkey: Uint8Array;
|
|
97
|
+
directory_signature: Uint8Array;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* SESSION-004: FROST-signed assignment. `signer_pubkey` is the initiator's
|
|
101
|
+
* group public key (primary_pubkey from DKG / trustedDealer commitments[0]).
|
|
102
|
+
* Embedded so the counterparty can verify without a directory round-trip.
|
|
103
|
+
*/
|
|
104
|
+
export interface SessionAssignmentFrost extends SessionAssignmentCommon {
|
|
105
|
+
signature_type: "frost";
|
|
106
|
+
signer_pubkey: Uint8Array;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* M1-era single-key directory signature. Refused by M2+ clients.
|
|
110
|
+
*/
|
|
111
|
+
export interface SessionAssignmentSingle extends SessionAssignmentCommon {
|
|
112
|
+
signature_type: "single";
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Discriminated union. TypeScript enforces `signer_pubkey` is present only
|
|
116
|
+
* when `signature_type === 'frost'`.
|
|
117
|
+
*
|
|
118
|
+
* SESSION-004: M2+ code should only handle 'frost'. M1 assignments with
|
|
119
|
+
* 'single' are rejected with `unsupported_signature_type`.
|
|
120
|
+
*/
|
|
121
|
+
export type SessionAssignment = SessionAssignmentFrost | SessionAssignmentSingle;
|
|
122
|
+
/**
|
|
123
|
+
* Build the FROST to-be-signed bytes for session establishment.
|
|
124
|
+
*
|
|
125
|
+
* Exported from protocol-types so BOTH the directory (signer) and the client
|
|
126
|
+
* (verifier) use identical canonical CBOR encoding. Any drift would silently
|
|
127
|
+
* break verification.
|
|
128
|
+
*
|
|
129
|
+
* TBS = canonical CBOR([session_id, agent_A_pubkey, agent_B_pubkey, genesis_prev_root, timestamp])
|
|
130
|
+
*
|
|
131
|
+
* Per CONTEXT.md: tagUint8Array: false. Timestamp encoded as BigInt when > 0xffffffff.
|
|
132
|
+
*
|
|
133
|
+
* @param sessionId - 16-byte session identifier
|
|
134
|
+
* @param pubA - 32-byte K_local pubkey of participant A
|
|
135
|
+
* @param pubB - 32-byte K_local pubkey of participant B
|
|
136
|
+
* @param genesisPrevRoot - 32-byte genesis prev_root (output of computeGenesisPrevRoot)
|
|
137
|
+
* @param timestamp - session_timestamp in Unix milliseconds
|
|
138
|
+
* @returns canonical CBOR bytes of the TBS array
|
|
139
|
+
*/
|
|
140
|
+
export declare function buildSessionEstablishmentTbs(sessionId: Uint8Array, pubA: Uint8Array, pubB: Uint8Array, genesisPrevRoot: Uint8Array, timestamp: number | bigint): Uint8Array;
|
|
141
|
+
/**
|
|
142
|
+
* SEAL control payload carried as the content_bytes of a ctrl leaf.
|
|
143
|
+
* Canonical CBOR encoding: [session_id, final_root, close_timestamp, "PENDING"].
|
|
144
|
+
* Per SESSION-003 behavior spec.
|
|
145
|
+
*/
|
|
146
|
+
export interface SealPayload {
|
|
147
|
+
session_id: Uint8Array;
|
|
148
|
+
final_root: Uint8Array;
|
|
149
|
+
close_timestamp: number;
|
|
150
|
+
attestation: "PENDING";
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Encode a SealPayload as canonical CBOR: [session_id, final_root, close_timestamp, "PENDING"].
|
|
154
|
+
* Per SESSION-003 behavior spec and RFC 8949 §4.2.1.
|
|
155
|
+
*/
|
|
156
|
+
export declare function encodeSealPayload(payload: SealPayload): Uint8Array;
|
|
157
|
+
/**
|
|
158
|
+
* Decode a SEAL payload CBOR. Returns null on malformed input.
|
|
159
|
+
*/
|
|
160
|
+
export declare function decodeSealPayload(bytes: Uint8Array): SealPayload | null;
|
|
161
|
+
/**
|
|
162
|
+
* Build the FROST to-be-signed bytes for a conversation seal ceremony.
|
|
163
|
+
*
|
|
164
|
+
* FROST TBS for seal (CONTEXT.md): canonical CBOR([session_id, sealed_root, leaf_count, timestamp])
|
|
165
|
+
* Context string: "cello-frost-seal-v1" (domain separation, prevents establishment replay)
|
|
166
|
+
* Per RFC 9591 (FROST) and RFC 8949 §4.2.1 (canonical CBOR).
|
|
167
|
+
*
|
|
168
|
+
* Both the directory (as ceremony participant verifying the signature) and the client
|
|
169
|
+
* (as the coordinator who submits the signature) MUST use this exact encoding.
|
|
170
|
+
*
|
|
171
|
+
* @param sessionId - 16-byte session identifier
|
|
172
|
+
* @param sealedRoot - 32-byte final Merkle root
|
|
173
|
+
* @param leafCount - total number of leaves in the sealed tree
|
|
174
|
+
* @param timestamp - Unix milliseconds at the time of verification
|
|
175
|
+
*/
|
|
176
|
+
export declare function buildSealTbs(sessionId: Uint8Array, sealedRoot: Uint8Array, leafCount: number, timestamp: number): Uint8Array;
|
|
177
|
+
export interface SessionAbandoned {
|
|
178
|
+
type: "session_abandoned";
|
|
179
|
+
session_id: Uint8Array;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* M1 session_sealed frame (single-key directory signature).
|
|
183
|
+
* Refused in M2 clients per SESSION-005 SI-003.
|
|
184
|
+
* @deprecated Use SessionSealedFrost instead in M2+.
|
|
185
|
+
*/
|
|
186
|
+
export interface SessionSealedSingle {
|
|
187
|
+
type: "session_sealed";
|
|
188
|
+
signature_type: "single";
|
|
189
|
+
session_id: Uint8Array;
|
|
190
|
+
sealed_root: Uint8Array;
|
|
191
|
+
directory_signature: Uint8Array;
|
|
192
|
+
close_timestamp: number;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* M2 session_sealed frame (FROST-notarized ceremony signature).
|
|
196
|
+
* SESSION-005: seal_type is 'frost' when the FROST ceremony completes.
|
|
197
|
+
*/
|
|
198
|
+
export interface SessionSealedFrost {
|
|
199
|
+
type: "session_sealed";
|
|
200
|
+
signature_type: "frost";
|
|
201
|
+
session_id: Uint8Array;
|
|
202
|
+
sealed_root: Uint8Array;
|
|
203
|
+
frost_signature: Uint8Array;
|
|
204
|
+
signer_pubkey: Uint8Array;
|
|
205
|
+
close_timestamp: number;
|
|
206
|
+
leaf_count?: number;
|
|
207
|
+
}
|
|
208
|
+
/** Discriminated union: M2 sends SessionSealedFrost; old M1 wire format is SessionSealedSingle. */
|
|
209
|
+
export type SessionSealed = SessionSealedSingle | SessionSealedFrost;
|
|
210
|
+
export type SealRejectionReason = "merkle_root_mismatch" | "leaf_signature_invalid" | "prev_root_chain_broken" | "causal_chain_violated" | "seal_leaves_invalid" | "seal_signature_invalid";
|
|
211
|
+
export interface SessionSealRejected {
|
|
212
|
+
type: "session_seal_rejected";
|
|
213
|
+
session_id: Uint8Array;
|
|
214
|
+
reason: SealRejectionReason;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* seal_verified: directory → seal initiator, after all three verification passes pass.
|
|
218
|
+
* Tells the initiator: "I've verified the tree — coordinate the FROST ceremony now."
|
|
219
|
+
* Per SESSION-005 step 4 in the seal ceremony flow.
|
|
220
|
+
*/
|
|
221
|
+
export interface SealVerified {
|
|
222
|
+
type: "seal_verified";
|
|
223
|
+
session_id: Uint8Array;
|
|
224
|
+
sealed_root: Uint8Array;
|
|
225
|
+
leaf_count: number;
|
|
226
|
+
timestamp: number;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Compute the genesis prev_root for a two-party CELLO session.
|
|
230
|
+
*
|
|
231
|
+
* @param pubkeyA - K_local pubkey of participant A (32 bytes)
|
|
232
|
+
* @param pubkeyB - K_local pubkey of participant B (32 bytes)
|
|
233
|
+
* @param sessionId - session_id from the directory (16 bytes)
|
|
234
|
+
* @param sessionTimestampMs - session_timestamp in milliseconds since Unix epoch
|
|
235
|
+
* @returns 32-byte genesis prev_root (SHA-256 output)
|
|
236
|
+
*/
|
|
237
|
+
export declare function computeGenesisPrevRoot(pubkeyA: Uint8Array, pubkeyB: Uint8Array, sessionId: Uint8Array, sessionTimestampMs: number | bigint): Uint8Array;
|
|
238
|
+
export {};
|
|
239
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO-SESSION-002/MSG-004/SESSION-003/SESSION-004/SESSION-005 — session.ts
|
|
3
|
+
*
|
|
4
|
+
* SessionAssignment: shared wire type used by directory (sender), client (receiver),
|
|
5
|
+
* and relay (verifier). Lives here so client can import it without touching @cello-protocol/directory.
|
|
6
|
+
*
|
|
7
|
+
* SESSION-004 additions to SessionAssignment:
|
|
8
|
+
* signature_type: 'frost' | 'single'
|
|
9
|
+
* - 'frost': the directory_signature field carries the 64-byte combined FROST output
|
|
10
|
+
* - 'single': M1-era single-key directory signature (refused by M2 clients)
|
|
11
|
+
* signer_pubkey?: Uint8Array (32 bytes, present when signature_type === 'frost')
|
|
12
|
+
* - The initiator's primary_pubkey (group FROST key). Embedded so the counterparty
|
|
13
|
+
* can verify without a separate directory round-trip.
|
|
14
|
+
*
|
|
15
|
+
* FROST TBS for session establishment (RFC 9591, domain separation per CONTEXT.md):
|
|
16
|
+
* context: "cello-frost-session-establishment-v1"
|
|
17
|
+
* tbs: canonical CBOR([session_id, agent_A_pubkey, agent_B_pubkey, genesis_prev_root, timestamp])
|
|
18
|
+
* framing: <context>\0<tbs_cbor>
|
|
19
|
+
* Note: signer_pubkey is NOT in the TBS — it is derived from DKG and embedded in the frame.
|
|
20
|
+
*
|
|
21
|
+
* SealPayload: canonical CBOR of [session_id, final_root, close_timestamp, "PENDING"].
|
|
22
|
+
* Per SESSION-003. content_hash = SHA-256(0x00 || SealPayload) per MERKLE-001.
|
|
23
|
+
*
|
|
24
|
+
* computeGenesisPrevRoot: deterministic genesis prev_root for a two-party session.
|
|
25
|
+
*
|
|
26
|
+
* Formula (SI-004):
|
|
27
|
+
* SHA-256(min(A_pubkey, B_pubkey) || max(A_pubkey, B_pubkey) || session_id || timestamp_be8)
|
|
28
|
+
*
|
|
29
|
+
* Pubkeys are sorted bytewise-lexicographically (Buffer.compare).
|
|
30
|
+
* timestamp_be8 is the session_timestamp encoded as an 8-byte big-endian unsigned integer
|
|
31
|
+
* (milliseconds since Unix epoch). Raw byte concatenation — no CBOR at this boundary,
|
|
32
|
+
* which would introduce width ambiguity on the timestamp encoding.
|
|
33
|
+
*
|
|
34
|
+
* Per FIPS 180-4 (SHA-256) and SESSION-002 SI-004.
|
|
35
|
+
*
|
|
36
|
+
* ─── Phase P: SESSION-004 Pseudocode ─────────────────────────────────────────
|
|
37
|
+
*
|
|
38
|
+
* SessionAssignment type changes (MEDIUM-6: discriminated union enforces signer_pubkey):
|
|
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).
|
|
74
|
+
* FROST TBS fields: [session_id, sealed_root, leaf_count, timestamp]
|
|
75
|
+
* Context string: "cello-frost-seal-v1" (per CONTEXT.md)
|
|
76
|
+
* Both sides (directory as signer, client as verifier) must use the same encoding.
|
|
77
|
+
* Per RFC 9591 (FROST), RFC 8949 (CBOR canonical), FIPS 180-4 (SHA-256).
|
|
78
|
+
*/
|
|
79
|
+
import { createHash } from "node:crypto";
|
|
80
|
+
import { Encoder, decode as cborDecode } from "cbor-x";
|
|
81
|
+
const CBOR_ENC = new Encoder({ tagUint8Array: false });
|
|
82
|
+
// ─── Session establishment TBS builder (SESSION-004 / HIGH-5) ─────────────────
|
|
83
|
+
/**
|
|
84
|
+
* Build the FROST to-be-signed bytes for session establishment.
|
|
85
|
+
*
|
|
86
|
+
* Exported from protocol-types so BOTH the directory (signer) and the client
|
|
87
|
+
* (verifier) use identical canonical CBOR encoding. Any drift would silently
|
|
88
|
+
* break verification.
|
|
89
|
+
*
|
|
90
|
+
* TBS = canonical CBOR([session_id, agent_A_pubkey, agent_B_pubkey, genesis_prev_root, timestamp])
|
|
91
|
+
*
|
|
92
|
+
* Per CONTEXT.md: tagUint8Array: false. Timestamp encoded as BigInt when > 0xffffffff.
|
|
93
|
+
*
|
|
94
|
+
* @param sessionId - 16-byte session identifier
|
|
95
|
+
* @param pubA - 32-byte K_local pubkey of participant A
|
|
96
|
+
* @param pubB - 32-byte K_local pubkey of participant B
|
|
97
|
+
* @param genesisPrevRoot - 32-byte genesis prev_root (output of computeGenesisPrevRoot)
|
|
98
|
+
* @param timestamp - session_timestamp in Unix milliseconds
|
|
99
|
+
* @returns canonical CBOR bytes of the TBS array
|
|
100
|
+
*/
|
|
101
|
+
export function buildSessionEstablishmentTbs(sessionId, pubA, pubB, genesisPrevRoot, timestamp) {
|
|
102
|
+
return CBOR_ENC.encode([
|
|
103
|
+
sessionId,
|
|
104
|
+
pubA,
|
|
105
|
+
pubB,
|
|
106
|
+
genesisPrevRoot,
|
|
107
|
+
typeof timestamp === "bigint" || timestamp > 0xffffffff ? BigInt(timestamp) : timestamp,
|
|
108
|
+
]);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Encode a SealPayload as canonical CBOR: [session_id, final_root, close_timestamp, "PENDING"].
|
|
112
|
+
* Per SESSION-003 behavior spec and RFC 8949 §4.2.1.
|
|
113
|
+
*/
|
|
114
|
+
export function encodeSealPayload(payload) {
|
|
115
|
+
return CBOR_ENC.encode([
|
|
116
|
+
payload.session_id,
|
|
117
|
+
payload.final_root,
|
|
118
|
+
payload.close_timestamp > 0xffffffff
|
|
119
|
+
? BigInt(payload.close_timestamp)
|
|
120
|
+
: payload.close_timestamp,
|
|
121
|
+
payload.attestation,
|
|
122
|
+
]);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Decode a SEAL payload CBOR. Returns null on malformed input.
|
|
126
|
+
*/
|
|
127
|
+
export function decodeSealPayload(bytes) {
|
|
128
|
+
let arr;
|
|
129
|
+
try {
|
|
130
|
+
arr = cborDecode(bytes);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
if (!Array.isArray(arr) || arr.length !== 4)
|
|
136
|
+
return null;
|
|
137
|
+
const [_sid, _root, _ts, _attest] = arr;
|
|
138
|
+
const sid = _sid instanceof Uint8Array ? _sid : Buffer.isBuffer(_sid) ? new Uint8Array(_sid) : null;
|
|
139
|
+
const root = _root instanceof Uint8Array ? _root : Buffer.isBuffer(_root) ? new Uint8Array(_root) : null;
|
|
140
|
+
if (!sid || sid.length !== 16)
|
|
141
|
+
return null;
|
|
142
|
+
if (!root || root.length !== 32)
|
|
143
|
+
return null;
|
|
144
|
+
const ts = typeof _ts === "number" ? _ts : typeof _ts === "bigint" ? Number(_ts) : null;
|
|
145
|
+
if (ts === null)
|
|
146
|
+
return null;
|
|
147
|
+
if (_attest !== "PENDING")
|
|
148
|
+
return null;
|
|
149
|
+
return { session_id: sid, final_root: root, close_timestamp: ts, attestation: "PENDING" };
|
|
150
|
+
}
|
|
151
|
+
// ─── buildSealTbs (SESSION-005) ───────────────────────────────────────────────
|
|
152
|
+
/**
|
|
153
|
+
* Build the FROST to-be-signed bytes for a conversation seal ceremony.
|
|
154
|
+
*
|
|
155
|
+
* FROST TBS for seal (CONTEXT.md): canonical CBOR([session_id, sealed_root, leaf_count, timestamp])
|
|
156
|
+
* Context string: "cello-frost-seal-v1" (domain separation, prevents establishment replay)
|
|
157
|
+
* Per RFC 9591 (FROST) and RFC 8949 §4.2.1 (canonical CBOR).
|
|
158
|
+
*
|
|
159
|
+
* Both the directory (as ceremony participant verifying the signature) and the client
|
|
160
|
+
* (as the coordinator who submits the signature) MUST use this exact encoding.
|
|
161
|
+
*
|
|
162
|
+
* @param sessionId - 16-byte session identifier
|
|
163
|
+
* @param sealedRoot - 32-byte final Merkle root
|
|
164
|
+
* @param leafCount - total number of leaves in the sealed tree
|
|
165
|
+
* @param timestamp - Unix milliseconds at the time of verification
|
|
166
|
+
*/
|
|
167
|
+
export function buildSealTbs(sessionId, sealedRoot, leafCount, timestamp) {
|
|
168
|
+
return CBOR_ENC.encode([
|
|
169
|
+
sessionId,
|
|
170
|
+
sealedRoot,
|
|
171
|
+
leafCount,
|
|
172
|
+
timestamp > 0xffffffff ? BigInt(timestamp) : timestamp,
|
|
173
|
+
]);
|
|
174
|
+
}
|
|
175
|
+
// ─── End session outcome notification frame types ─────────────────────────────
|
|
176
|
+
/**
|
|
177
|
+
* Compute the genesis prev_root for a two-party CELLO session.
|
|
178
|
+
*
|
|
179
|
+
* @param pubkeyA - K_local pubkey of participant A (32 bytes)
|
|
180
|
+
* @param pubkeyB - K_local pubkey of participant B (32 bytes)
|
|
181
|
+
* @param sessionId - session_id from the directory (16 bytes)
|
|
182
|
+
* @param sessionTimestampMs - session_timestamp in milliseconds since Unix epoch
|
|
183
|
+
* @returns 32-byte genesis prev_root (SHA-256 output)
|
|
184
|
+
*/
|
|
185
|
+
export function computeGenesisPrevRoot(pubkeyA, pubkeyB, sessionId, sessionTimestampMs) {
|
|
186
|
+
const a = Buffer.from(pubkeyA);
|
|
187
|
+
const b = Buffer.from(pubkeyB);
|
|
188
|
+
const [min, max] = Buffer.compare(a, b) <= 0 ? [a, b] : [b, a];
|
|
189
|
+
const tsBe = Buffer.alloc(8);
|
|
190
|
+
tsBe.writeBigUInt64BE(typeof sessionTimestampMs === "bigint" ? sessionTimestampMs : BigInt(sessionTimestampMs));
|
|
191
|
+
return new Uint8Array(createHash("sha256")
|
|
192
|
+
.update(min)
|
|
193
|
+
.update(max)
|
|
194
|
+
.update(sessionId)
|
|
195
|
+
.update(tsBe)
|
|
196
|
+
.digest());
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO-MERKLE-002 — Structure 2 wire type and codec.
|
|
3
|
+
*
|
|
4
|
+
* Structure 2 is the relay-built Merkle leaf:
|
|
5
|
+
* [sequence_number, sender_pubkey, content_hash, sender_signature, scan_result, prev_root]
|
|
6
|
+
*
|
|
7
|
+
* scan_result in M1 is a sentinel placeholder: {score: null, verdict: "unscanned", model_hash: <32 zero bytes>}.
|
|
8
|
+
* The field is present to keep the wire layout stable for future scanner integration (M4+).
|
|
9
|
+
*
|
|
10
|
+
* Canonical CBOR per RFC 8949 §4.2.1.
|
|
11
|
+
* Ed25519 verification per RFC 8032.
|
|
12
|
+
*
|
|
13
|
+
* Structure 1 TBS (for signature verification):
|
|
14
|
+
* [1, content_hash, sender_pubkey, session_id, last_seen_seq, timestamp]
|
|
15
|
+
* This is what the sender signs; verifyStructure2Signature reconstructs it from Structure 2 fields
|
|
16
|
+
* plus the session_id, last_seen_seq, and timestamp carried through the relay path.
|
|
17
|
+
*/
|
|
18
|
+
import type { EnvelopeError } from "./types.js";
|
|
19
|
+
export interface ScanResultSentinel {
|
|
20
|
+
score: null;
|
|
21
|
+
verdict: "unscanned";
|
|
22
|
+
model_hash: Uint8Array;
|
|
23
|
+
}
|
|
24
|
+
export interface Structure2 {
|
|
25
|
+
sequence_number: number;
|
|
26
|
+
sender_pubkey: Uint8Array;
|
|
27
|
+
content_hash: Uint8Array;
|
|
28
|
+
sender_signature: Uint8Array;
|
|
29
|
+
scan_result: ScanResultSentinel;
|
|
30
|
+
prev_root: Uint8Array;
|
|
31
|
+
}
|
|
32
|
+
export type BuildStructure2Result = {
|
|
33
|
+
ok: true;
|
|
34
|
+
structure2: Structure2;
|
|
35
|
+
} | {
|
|
36
|
+
ok: false;
|
|
37
|
+
error: EnvelopeError;
|
|
38
|
+
};
|
|
39
|
+
/** M1 placeholder scan_result sentinel. The scanner field is stable but unset until M4+. */
|
|
40
|
+
export declare const SCAN_RESULT_SENTINEL: ScanResultSentinel;
|
|
41
|
+
/**
|
|
42
|
+
* Encodes the scan_result sentinel as canonical CBOR (RFC 8949 §4.2.1).
|
|
43
|
+
*
|
|
44
|
+
* Keys are sorted by byte length per RFC 8949 §4.2.1 core deterministic encoding:
|
|
45
|
+
* "score" (5) < "verdict" (7) < "model_hash" (10)
|
|
46
|
+
*
|
|
47
|
+
* cbor-x's default POJO encoding uses non-canonical map-size encoding (uint16 length prefix).
|
|
48
|
+
* We construct the canonical bytes manually to guarantee RFC 8949 §4.2.1 compliance.
|
|
49
|
+
*/
|
|
50
|
+
export declare function encodeScanResultSentinel(): Uint8Array;
|
|
51
|
+
/**
|
|
52
|
+
* Constructs a Structure 2 object after validating all field lengths.
|
|
53
|
+
* scan_result is always the M1 sentinel — callers do not supply it.
|
|
54
|
+
*/
|
|
55
|
+
export declare function buildStructure2(sequence_number: number, sender_pubkey: Uint8Array, content_hash: Uint8Array, sender_signature: Uint8Array, prev_root: Uint8Array): BuildStructure2Result;
|
|
56
|
+
/**
|
|
57
|
+
* Encodes Structure 2 as canonical CBOR (RFC 8949 §4.2.1).
|
|
58
|
+
* Wire format: [sequence_number, sender_pubkey, content_hash, sender_signature, scan_result, prev_root]
|
|
59
|
+
*/
|
|
60
|
+
export declare function encodeStructure2(s2: Structure2): Uint8Array;
|
|
61
|
+
/**
|
|
62
|
+
* Reconstructs canonical CBOR of Structure 1 from Structure 2 fields plus the
|
|
63
|
+
* session_id, last_seen_seq, and timestamp carried through the relay path,
|
|
64
|
+
* then verifies the sender's Ed25519 signature.
|
|
65
|
+
*
|
|
66
|
+
* Structure 1 TBS: [1, content_hash, sender_pubkey, session_id, last_seen_seq, timestamp]
|
|
67
|
+
* Per RFC 8032 (Ed25519) and RFC 8949 §4.2.1 (canonical CBOR).
|
|
68
|
+
*
|
|
69
|
+
* @param timestamp - Unix milliseconds. May be number or bigint. cbor-x decodes uint64
|
|
70
|
+
* timestamps as bigint; relay callers that deserialize Structure 1 CBOR must pass the
|
|
71
|
+
* decoded value directly here rather than converting to number (which is lossy above
|
|
72
|
+
* Number.MAX_SAFE_INTEGER).
|
|
73
|
+
*/
|
|
74
|
+
export declare function verifyStructure2Signature(s2: Structure2, session_id: Uint8Array, last_seen_seq: number, timestamp: number | bigint): boolean;
|
|
75
|
+
//# sourceMappingURL=structure2.d.ts.map
|
|
@@ -0,0 +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;AAMhD,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"}
|