@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.
@@ -0,0 +1,271 @@
1
+ /**
2
+ * CELLO Connection Request Wire Types — CONNREQ-002
3
+ *
4
+ * Phase P — Pseudocode
5
+ * ─────────────────────────────────────────────────────────────────────────────
6
+ * ROUND 1 flow (always happens):
7
+ *
8
+ * CLIENT A → DIRECTORY (on authenticated signaling stream):
9
+ * 1. A sends connection_request { target_pubkey, package_cbor }
10
+ *
11
+ * DIRECTORY pre-checks:
12
+ * a. sender is registered → else connection_request_error { reason: 'not_registered' }
13
+ * b. target has active profile → else connection_request_error { reason: 'target_not_found' }
14
+ * c. no existing active connection → else connection_request_error { reason: 'already_connected' }
15
+ * d. fetch target's registered_at and is_provisional
16
+ * e. relay to target's stream as connection_request_inbound { from_pubkey, package_cbor,
17
+ * sender_registered_at, sender_is_provisional }
18
+ * If target offline → queue (up to 32), deliver on reconnect; if > 32, drop oldest, send
19
+ * connection_request_error { reason: 'target_unavailable' } to sender
20
+ *
21
+ * TARGET CLIENT receives connection_request_inbound:
22
+ * 1. Check whitelist → if sender whitelisted, send connection_response { verdict: 'accept' }
23
+ * without calling evaluateConnectionPackage()
24
+ * 2. validateConnectionPackage (CONNREQ-001: pseudonym binding, signatures, expiry)
25
+ * 3. Build DirectoryContext from sender_registered_at, sender_is_provisional
26
+ * 4. evaluateConnectionPackage() (CONNPOL-001)
27
+ * 5. Act on ConnectionReport verdict:
28
+ * - auto_accept → send connection_response { verdict: 'accept' }
29
+ * - auto_reject → send connection_response { verdict: 'reject', reason }
30
+ * - auto_insufficient → send connection_response { verdict: 'insufficient', unmet_requirements }
31
+ * - pending_agent_review → queue report, fire onConnectionPendingReview
32
+ *
33
+ * DIRECTORY receives connection_response { verdict: 'accept' }:
34
+ * 1. Generate 16-byte CSPRNG connection_id (FIPS 180-4 randomness)
35
+ * 2. Create connection records in DirectoryStore
36
+ * 3. Push connection_established { counterparty_pubkey, connection_id } to BOTH clients
37
+ *
38
+ * DIRECTORY receives connection_response { verdict: 'reject', reason }:
39
+ * Relay connection_rejected { target_pubkey, reason } to sender. No record created.
40
+ *
41
+ * DIRECTORY receives connection_response { verdict: 'insufficient', unmet_requirements }:
42
+ * Relay connection_insufficient { target_pubkey, unmet_requirements } to sender. No record.
43
+ *
44
+ * ROUND 2 (only when inference mode + agent calls cello_request_more_disclosure):
45
+ *
46
+ * TARGET calls cello_request_more_disclosure({ connection_request_id, requested_items[] }):
47
+ * Verify Round 1 (not already Round 2) → else { error: 'max_rounds_reached' }
48
+ * Send disclosure_request { connection_request_id, requested_items[] } to directory.
49
+ * Directory relays as disclosure_request_inbound { from_pubkey, connection_request_id,
50
+ * requested_items[] } to sender's stream.
51
+ * Start 2-minute Round 2 silence timer (round2TimeoutMs).
52
+ *
53
+ * SENDER receives disclosure_request_inbound:
54
+ * Fire onDisclosureRequested.
55
+ * cello_request_connection returns { result: 'disclosure_requested', connection_request_id,
56
+ * requested_items[] } — UNBLOCKS sender.
57
+ *
58
+ * SENDER calls cello_respond_to_disclosure_request({ connection_request_id, ... }):
59
+ * Construct package_v2, send disclosure_response { connection_request_id, package_cbor }.
60
+ * Directory relays as disclosure_response_inbound { connection_request_id, package_cbor }.
61
+ *
62
+ * TARGET receives disclosure_response_inbound:
63
+ * Validate package_v2, re-run evaluateConnectionPackage().
64
+ * Queue updated ConnectionReport; return via cello_await_connection_request.
65
+ * Agent may only call cello_accept_connection or cello_reject_connection now.
66
+ * If agent attempts cello_request_more_disclosure on Round 2 → { error: 'max_rounds_reached' }.
67
+ *
68
+ * ROUND 2 SILENCE TIMEOUT (2-minute timer at target):
69
+ * Target sends connection_response { verdict: 'reject', reason: 'disclosure_timeout' }.
70
+ * Directory relays connection_rejected { reason: 'disclosure_timeout' } to sender.
71
+ * cello_request_connection returns { result: 'rejected', reason: 'disclosure_timeout' }.
72
+ *
73
+ * 5-MINUTE OVERALL TIMEOUT (at sender):
74
+ * If no final response arrives within connectionTimeoutMs (default 300_000ms),
75
+ * cello_request_connection returns { result: 'timeout' }.
76
+ *
77
+ * SESSION-006 — session_request connection gate:
78
+ * client.initiateSession() checks local #connections map first.
79
+ * If no connection → return { error: { reason: 'no_connection', target_pubkey } }.
80
+ * session_request frame gains required connection_id field.
81
+ * Directory verifies connection_id matches active connection between initiator + target.
82
+ * Reject with no_connection or connection_id_required before any FROST ceremony.
83
+ *
84
+ * Crypto refs:
85
+ * ML-DSA-44 package signatures: NIST FIPS 204
86
+ * SHA-256 for connection_id entropy mix (if needed): FIPS 180-4
87
+ * FROST threshold signing: RFC 9591
88
+ * ─────────────────────────────────────────────────────────────────────────────
89
+ */
90
+ /**
91
+ * Sent by the initiator on its authenticated signaling stream.
92
+ * package_cbor is the CBOR-encoded ConnectionPackage (CONNREQ-001).
93
+ * The directory does NOT inspect package_cbor — it is opaque (ML-DSA signed by sender).
94
+ */
95
+ export interface ConnectionRequest {
96
+ type: "connection_request";
97
+ /** Hex-encoded 32-byte K_local pubkey of the desired target */
98
+ target_pubkey: string;
99
+ /** CBOR-encoded ConnectionPackage bytes (opaque to directory) */
100
+ package_cbor: Uint8Array;
101
+ }
102
+ /**
103
+ * Relayed by the directory to the target's signaling stream.
104
+ * Augmented with sender's directory context fields.
105
+ */
106
+ export interface ConnectionRequestInbound {
107
+ type: "connection_request_inbound";
108
+ /** Hex-encoded 32-byte K_local pubkey of the sender */
109
+ from_pubkey: string;
110
+ /** Connection request ID (directory-assigned, 16-byte hex) for Round 2 correlation */
111
+ connection_request_id: string;
112
+ /** CBOR-encoded ConnectionPackage (opaque relay — cannot be modified by directory) */
113
+ package_cbor: Uint8Array;
114
+ /** Unix ms timestamp when sender registered (from directory profile) */
115
+ sender_registered_at: number;
116
+ /** Whether sender's profile is provisional */
117
+ sender_is_provisional: boolean;
118
+ }
119
+ /** Verdict variants for the target client's response */
120
+ export type ConnectionResponseVerdict = {
121
+ verdict: "accept";
122
+ } | {
123
+ verdict: "reject";
124
+ reason: string;
125
+ } | {
126
+ verdict: "insufficient";
127
+ unmet_requirements: unknown[];
128
+ };
129
+ export interface ConnectionResponse {
130
+ type: "connection_response";
131
+ /** Connection request ID assigned by directory at relay time */
132
+ connection_request_id: string;
133
+ verdict: "accept" | "reject" | "insufficient";
134
+ /** Present when verdict === 'reject' */
135
+ reason?: string;
136
+ /** Present when verdict === 'insufficient' */
137
+ unmet_requirements?: unknown[];
138
+ }
139
+ /**
140
+ * Delivered to both A and B after directory creates the connection record.
141
+ * counterparty_pubkey is the other side's K_local (A receives B's, B receives A's).
142
+ */
143
+ export interface ConnectionEstablished {
144
+ type: "connection_established";
145
+ /** Hex-encoded counterparty K_local pubkey */
146
+ counterparty_pubkey: string;
147
+ /** Hex-encoded 16-byte CSPRNG connection ID */
148
+ connection_id: string;
149
+ }
150
+ export interface ConnectionRejected {
151
+ type: "connection_rejected";
152
+ /** Hex-encoded 32-byte target K_local pubkey */
153
+ target_pubkey: string;
154
+ reason: string;
155
+ }
156
+ export interface ConnectionInsufficient {
157
+ type: "connection_insufficient";
158
+ /** Hex-encoded 32-byte target K_local pubkey */
159
+ target_pubkey: string;
160
+ unmet_requirements: unknown[];
161
+ }
162
+ export interface ConnectionRequestError {
163
+ type: "connection_request_error";
164
+ reason: ConnectionRequestErrorReason;
165
+ /** Only set when reason === "already_connected" — the existing connection_id so the client can hydrate and proceed immediately */
166
+ connection_id?: string;
167
+ }
168
+ export type ConnectionRequestErrorReason = "not_registered" | "target_not_found" | "already_connected" | "target_unavailable";
169
+ /**
170
+ * Target → directory: initiate Round 2 disclosure request.
171
+ */
172
+ export interface DisclosureRequest {
173
+ type: "disclosure_request";
174
+ connection_request_id: string;
175
+ requested_items: DisclosureRequestItem[];
176
+ }
177
+ export interface DisclosureRequestItem {
178
+ type: "endorsement" | "attestation";
179
+ /** For endorsement: minimum count required */
180
+ min_count?: number;
181
+ /** For attestation: attestation type string */
182
+ attestation_type?: string;
183
+ }
184
+ /**
185
+ * Directory → sender: relayed disclosure request.
186
+ */
187
+ export interface DisclosureRequestInbound {
188
+ type: "disclosure_request_inbound";
189
+ /** Hex-encoded target K_local pubkey */
190
+ from_pubkey: string;
191
+ connection_request_id: string;
192
+ requested_items: DisclosureRequestItem[];
193
+ }
194
+ /**
195
+ * Sender → directory: provide additional package items (or empty to decline).
196
+ */
197
+ export interface DisclosureResponse {
198
+ type: "disclosure_response";
199
+ connection_request_id: string;
200
+ /** CBOR-encoded updated ConnectionPackage (package_v2) */
201
+ package_cbor: Uint8Array;
202
+ }
203
+ /**
204
+ * Directory → target: relayed disclosure response.
205
+ */
206
+ export interface DisclosureResponseInbound {
207
+ type: "disclosure_response_inbound";
208
+ connection_request_id: string;
209
+ /** CBOR-encoded updated ConnectionPackage (package_v2) */
210
+ package_cbor: Uint8Array;
211
+ }
212
+ /**
213
+ * Connection record stored in the directory.
214
+ * Indexed by both participants' pubkeys and by connection_id.
215
+ */
216
+ export interface ConnectionRecord {
217
+ /** Hex-encoded 16-byte CSPRNG connection ID */
218
+ connection_id: string;
219
+ /** Hex-encoded K_local pubkey of participant A */
220
+ participant_a: string;
221
+ /** Hex-encoded K_local pubkey of participant B */
222
+ participant_b: string;
223
+ /** Unix ms timestamp when the connection was established */
224
+ established_at: number;
225
+ status: "active";
226
+ }
227
+ /**
228
+ * Pending connection request queued for an offline target.
229
+ */
230
+ export interface PendingConnectionRequest {
231
+ /** Connection request ID (directory-assigned) */
232
+ connection_request_id: string;
233
+ /** Hex-encoded sender K_local pubkey */
234
+ sender_pubkey: string;
235
+ /** The inbound frame to deliver when target reconnects */
236
+ frame: ConnectionRequestInbound;
237
+ /** Unix ms timestamp when queued (oldest = first to drop) */
238
+ queued_at: number;
239
+ }
240
+ /**
241
+ * CLIENT-SIDE connection record (CONNREQ-002).
242
+ * Cached so future session establishments can use the connection_id without re-querying.
243
+ */
244
+ export interface ClientConnectionRecord {
245
+ /** Hex-encoded 16-byte CSPRNG connection ID */
246
+ connection_id: string;
247
+ /** Hex-encoded counterparty K_local pubkey */
248
+ counterparty_pubkey: string;
249
+ /** Hex-encoded counterparty FROST primary_pubkey (from the connection exchange) */
250
+ counterparty_primary_pubkey: string;
251
+ /** Hex-encoded counterparty ML-DSA public key (from the connection package) */
252
+ counterparty_ml_dsa_pubkey: string;
253
+ /** Unix ms timestamp when established */
254
+ established_at: number;
255
+ status: "active";
256
+ /** True when directory was unreachable during ml_dsa_pubkey cross-check */
257
+ profile_unchecked?: boolean;
258
+ }
259
+ /**
260
+ * M3 session_request wire type — gains required connection_id field.
261
+ * Defined here so protocol-types owns the canonical wire schema.
262
+ */
263
+ export interface SessionRequestM3 {
264
+ type: "session_request";
265
+ /** Hex-encoded 32-byte K_local pubkey of the desired counterparty */
266
+ target_pubkey: Uint8Array;
267
+ /** Hex-encoded 16-byte connection ID — REQUIRED in M3 */
268
+ connection_id: string;
269
+ }
270
+ export type SessionRequestM3ErrorReason = "no_connection" | "connection_id_required";
271
+ //# sourceMappingURL=connection-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-request.d.ts","sourceRoot":"","sources":["../src/connection-request.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * CELLO Connection Request Wire Types — CONNREQ-002
3
+ *
4
+ * Phase P — Pseudocode
5
+ * ─────────────────────────────────────────────────────────────────────────────
6
+ * ROUND 1 flow (always happens):
7
+ *
8
+ * CLIENT A → DIRECTORY (on authenticated signaling stream):
9
+ * 1. A sends connection_request { target_pubkey, package_cbor }
10
+ *
11
+ * DIRECTORY pre-checks:
12
+ * a. sender is registered → else connection_request_error { reason: 'not_registered' }
13
+ * b. target has active profile → else connection_request_error { reason: 'target_not_found' }
14
+ * c. no existing active connection → else connection_request_error { reason: 'already_connected' }
15
+ * d. fetch target's registered_at and is_provisional
16
+ * e. relay to target's stream as connection_request_inbound { from_pubkey, package_cbor,
17
+ * sender_registered_at, sender_is_provisional }
18
+ * If target offline → queue (up to 32), deliver on reconnect; if > 32, drop oldest, send
19
+ * connection_request_error { reason: 'target_unavailable' } to sender
20
+ *
21
+ * TARGET CLIENT receives connection_request_inbound:
22
+ * 1. Check whitelist → if sender whitelisted, send connection_response { verdict: 'accept' }
23
+ * without calling evaluateConnectionPackage()
24
+ * 2. validateConnectionPackage (CONNREQ-001: pseudonym binding, signatures, expiry)
25
+ * 3. Build DirectoryContext from sender_registered_at, sender_is_provisional
26
+ * 4. evaluateConnectionPackage() (CONNPOL-001)
27
+ * 5. Act on ConnectionReport verdict:
28
+ * - auto_accept → send connection_response { verdict: 'accept' }
29
+ * - auto_reject → send connection_response { verdict: 'reject', reason }
30
+ * - auto_insufficient → send connection_response { verdict: 'insufficient', unmet_requirements }
31
+ * - pending_agent_review → queue report, fire onConnectionPendingReview
32
+ *
33
+ * DIRECTORY receives connection_response { verdict: 'accept' }:
34
+ * 1. Generate 16-byte CSPRNG connection_id (FIPS 180-4 randomness)
35
+ * 2. Create connection records in DirectoryStore
36
+ * 3. Push connection_established { counterparty_pubkey, connection_id } to BOTH clients
37
+ *
38
+ * DIRECTORY receives connection_response { verdict: 'reject', reason }:
39
+ * Relay connection_rejected { target_pubkey, reason } to sender. No record created.
40
+ *
41
+ * DIRECTORY receives connection_response { verdict: 'insufficient', unmet_requirements }:
42
+ * Relay connection_insufficient { target_pubkey, unmet_requirements } to sender. No record.
43
+ *
44
+ * ROUND 2 (only when inference mode + agent calls cello_request_more_disclosure):
45
+ *
46
+ * TARGET calls cello_request_more_disclosure({ connection_request_id, requested_items[] }):
47
+ * Verify Round 1 (not already Round 2) → else { error: 'max_rounds_reached' }
48
+ * Send disclosure_request { connection_request_id, requested_items[] } to directory.
49
+ * Directory relays as disclosure_request_inbound { from_pubkey, connection_request_id,
50
+ * requested_items[] } to sender's stream.
51
+ * Start 2-minute Round 2 silence timer (round2TimeoutMs).
52
+ *
53
+ * SENDER receives disclosure_request_inbound:
54
+ * Fire onDisclosureRequested.
55
+ * cello_request_connection returns { result: 'disclosure_requested', connection_request_id,
56
+ * requested_items[] } — UNBLOCKS sender.
57
+ *
58
+ * SENDER calls cello_respond_to_disclosure_request({ connection_request_id, ... }):
59
+ * Construct package_v2, send disclosure_response { connection_request_id, package_cbor }.
60
+ * Directory relays as disclosure_response_inbound { connection_request_id, package_cbor }.
61
+ *
62
+ * TARGET receives disclosure_response_inbound:
63
+ * Validate package_v2, re-run evaluateConnectionPackage().
64
+ * Queue updated ConnectionReport; return via cello_await_connection_request.
65
+ * Agent may only call cello_accept_connection or cello_reject_connection now.
66
+ * If agent attempts cello_request_more_disclosure on Round 2 → { error: 'max_rounds_reached' }.
67
+ *
68
+ * ROUND 2 SILENCE TIMEOUT (2-minute timer at target):
69
+ * Target sends connection_response { verdict: 'reject', reason: 'disclosure_timeout' }.
70
+ * Directory relays connection_rejected { reason: 'disclosure_timeout' } to sender.
71
+ * cello_request_connection returns { result: 'rejected', reason: 'disclosure_timeout' }.
72
+ *
73
+ * 5-MINUTE OVERALL TIMEOUT (at sender):
74
+ * If no final response arrives within connectionTimeoutMs (default 300_000ms),
75
+ * cello_request_connection returns { result: 'timeout' }.
76
+ *
77
+ * SESSION-006 — session_request connection gate:
78
+ * client.initiateSession() checks local #connections map first.
79
+ * If no connection → return { error: { reason: 'no_connection', target_pubkey } }.
80
+ * session_request frame gains required connection_id field.
81
+ * Directory verifies connection_id matches active connection between initiator + target.
82
+ * Reject with no_connection or connection_id_required before any FROST ceremony.
83
+ *
84
+ * Crypto refs:
85
+ * ML-DSA-44 package signatures: NIST FIPS 204
86
+ * SHA-256 for connection_id entropy mix (if needed): FIPS 180-4
87
+ * FROST threshold signing: RFC 9591
88
+ * ─────────────────────────────────────────────────────────────────────────────
89
+ */
90
+ export {};
91
+ //# sourceMappingURL=connection-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-request.js","sourceRoot":"","sources":["../src/connection-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * @cello-protocol/protocol-types — CELLO-MSG-001 (v0) + CELLO-MSG-003 (v1)
3
+ * Envelope construction, validation, serialization, and deserialization.
4
+ *
5
+ * ──── v0 Pseudocode (CELLO-MSG-001) ────
6
+ *
7
+ * buildEnvelope(content, keyProvider, timestamp):
8
+ * 1. Reject if content.length > MAX_CONTENT_BYTES (1,048,576) → content_too_large
9
+ * 2. Compute content_hash = msgLeafHash(content) [FIPS 180-4: SHA-256(0x00||content)]
10
+ * 3. Fetch sender_pubkey = await keyProvider.getPublicKey()
11
+ * 4. Build TBS positional array: [0, content_hash, sender_pubkey, timestamp]
12
+ * — timestamp encoded as BigInt so cbor-x emits uint64, not float64
13
+ * 5. tbs_bytes = encodeTBS([0, content_hash, sender_pubkey, BigInt(timestamp)])
14
+ * [RFC 8949 §4.2.1: canonical CBOR, Encoder({tagUint8Array:false})]
15
+ * 6. sender_signature = await keyProvider.sign(tbs_bytes) [RFC 8032: Ed25519]
16
+ * 7. Return envelope with all six fields populated
17
+ *
18
+ * validateEnvelope(envelope):
19
+ * 1. Check protocol_version === 0 → unsupported_version
20
+ * 2. Check presence + exact byte lengths of all typed fields:
21
+ * sender_pubkey: 32 bytes → missing_field / invalid_field
22
+ * content_hash: 32 bytes → missing_field / invalid_field
23
+ * sender_signature: 64 bytes → missing_field / invalid_field
24
+ * 3. Check timestamp >= 0 → invalid_field
25
+ * 4. Check content.length <= MAX_CONTENT_BYTES → content_too_large
26
+ * 5. Recompute expected_hash = msgLeafHash(content)
27
+ * Compare byte-by-byte to content_hash → content_hash_mismatch (BEFORE sig check)
28
+ * 6. Rebuild TBS bytes (same as step 5 in build)
29
+ * 7. verify(sender_pubkey, tbs_bytes, sender_signature) → invalid_field('sender_signature')
30
+ * 8. Return ok: true
31
+ *
32
+ * ──── v1 Pseudocode (CELLO-MSG-003) ────
33
+ *
34
+ * buildEnvelopeV1(content, keyProvider, timestamp, session_id, last_seen_seq):
35
+ * 1. Reject if content.length > MAX_CONTENT_BYTES → content_too_large
36
+ * [no hash/sig computed before this check — AC-009]
37
+ * 2. Compute content_hash = msgLeafHash(content) [FIPS 180-4: SHA-256(0x00||content)]
38
+ * Caller-supplied hashes ignored — SI-001
39
+ * 3. Fetch sender_pubkey = await keyProvider.getPublicKey()
40
+ * 4. Build Structure 1 (TBS) positional 6-element array:
41
+ * [1, content_hash, sender_pubkey, session_id, last_seen_seq, timestamp]
42
+ * — protocol_version=1 (CBOR uint)
43
+ * — content_hash: 32-byte CBOR bstr (SHA-256(0x00||content))
44
+ * — sender_pubkey: 32-byte CBOR bstr (Ed25519 public key, RFC 8032)
45
+ * — session_id: 16-byte CBOR bstr
46
+ * — last_seen_seq: CBOR uint (non-negative integer)
47
+ * — timestamp: CBOR uint (Unix ms; BigInt when > 0xFFFFFFFF for minimal encoding
48
+ * per RFC 8949 §4.2.1)
49
+ * 5. tbs_bytes = CBOR_ENC.encode(tbs_array)
50
+ * [RFC 8949 §4.2.1: canonical CBOR, Encoder({tagUint8Array:false})]
51
+ * 6. sender_signature = await keyProvider.sign(tbs_bytes) [RFC 8032: Ed25519]
52
+ * 7. Return MessageEnvelopeV1 with all eight fields populated
53
+ *
54
+ * validateEnvelopeV1(envelope):
55
+ * 1. Check protocol_version === 1 → unsupported_version (AC-003, AC-004)
56
+ * Hard-reject: no v0 fallback, no negotiation (M1 drops v0)
57
+ * 2. Field presence + byte-length checks:
58
+ * sender_pubkey: 32 bytes → missing_field / invalid_field
59
+ * content_hash: 32 bytes → missing_field / invalid_field
60
+ * session_id: 16 bytes → missing_field / invalid_field
61
+ * sender_signature: 64 bytes → missing_field / invalid_field
62
+ * 3. Check last_seen_seq >= 0, integer → invalid_field
63
+ * 4. Check timestamp >= 0, integer → invalid_field
64
+ * 5. Check content.length <= MAX_CONTENT_BYTES → content_too_large
65
+ * 6. Recompute expected_hash = msgLeafHash(content)
66
+ * Compare byte-by-byte to content_hash → content_hash_mismatch (BEFORE sig check)
67
+ * 7. Rebuild Structure 1 TBS bytes (same as step 5 in build)
68
+ * 8. verify(sender_pubkey, tbs_bytes, sender_signature) → invalid_field('sender_signature')
69
+ * 9. Return ok: true
70
+ *
71
+ * extractStructure1(envelope: MessageEnvelopeV1) → Uint8Array:
72
+ * 1. Build positional 6-element array from envelope fields
73
+ * 2. Return CBOR_ENC.encode([1, content_hash, sender_pubkey, session_id, last_seen_seq, timestamp])
74
+ * This is the exact bytes the relay uses to build Structure 2 (MERKLE-002)
75
+ *
76
+ * References:
77
+ * RFC 8949 §4.2.1 — Core Deterministic Encoding Requirements
78
+ * RFC 8032 — Edwards-Curve Digital Signature Algorithm (EdDSA)
79
+ * FIPS 180-4 — SHA-256 (used by msgLeafHash for content_hash computation)
80
+ */
81
+ import type { KeyProvider } from "@cello-protocol/crypto";
82
+ import type { MessageEnvelope, MessageEnvelopeV1, BuildResult, BuildResultV1, ValidateResult, ValidateResultV1, DeserializeResult, DeserializeResultV1 } from "./types.js";
83
+ /** Maximum allowed content size: 1 MiB (AC-009, AC-010, AC-011). */
84
+ export declare const MAX_CONTENT_BYTES = 1048576;
85
+ /**
86
+ * Build a signed MessageEnvelope.
87
+ *
88
+ * SI-001: content_hash is ALWAYS recomputed; any caller-supplied value is ignored.
89
+ * AC-010: content > 1 MiB → content_too_large, no hash or signature computed.
90
+ *
91
+ * @param content - Raw message bytes
92
+ * @param keyProvider - Signing key abstraction (K_local)
93
+ * @param timestamp - Unix milliseconds (non-negative)
94
+ */
95
+ export declare function buildEnvelope(content: Uint8Array, keyProvider: KeyProvider, timestamp: number): Promise<BuildResult>;
96
+ /**
97
+ * Validate a MessageEnvelope.
98
+ *
99
+ * Validation order (fail-fast):
100
+ * 1. protocol_version check (AC-013, SI-004) — before any other check
101
+ * 2. Field presence and byte-length checks (AC-003, AC-004)
102
+ * 3. timestamp range check (AC-005)
103
+ * 4. content size check (AC-011)
104
+ * 5. content_hash recomputation and comparison (AC-012) — BEFORE signature check
105
+ * 6. Signature verification (AC-002, AC-007, SI-003)
106
+ */
107
+ export declare function validateEnvelope(envelope: MessageEnvelope): ValidateResult;
108
+ /**
109
+ * Serialize a MessageEnvelope to canonical CBOR bytes (RFC 8949 §4.2.1).
110
+ *
111
+ * The envelope is encoded as a CBOR map with string keys.
112
+ * timestamp uses minimal encoding per RFC 8949 §4.2.1 — see encodeTBS comment.
113
+ */
114
+ export declare function serializeEnvelope(envelope: MessageEnvelope): Uint8Array;
115
+ /**
116
+ * Deserialize a MessageEnvelope from CBOR bytes.
117
+ *
118
+ * Performs structural validation only (field presence, types, sizes).
119
+ * Does NOT re-validate the signature or content_hash — call validateEnvelope for that.
120
+ */
121
+ export declare function deserializeEnvelope(bytes: Uint8Array): DeserializeResult;
122
+ /**
123
+ * Build a signed v1 MessageEnvelopeV1 (CELLO-MSG-003).
124
+ *
125
+ * SI-001: content_hash is ALWAYS recomputed from content; any caller-supplied value is ignored.
126
+ * AC-009: content > 1 MiB → content_too_large, no hash or signature computed.
127
+ *
128
+ * @param content - Raw message bytes (up to 1 MiB)
129
+ * @param keyProvider - Signing key abstraction (K_local)
130
+ * @param timestamp - Unix milliseconds (non-negative)
131
+ * @param session_id - 16-byte session identifier
132
+ * @param last_seen_seq - Highest canonical seq number seen from relay (0 for first message)
133
+ */
134
+ export declare function buildEnvelopeV1(content: Uint8Array, keyProvider: KeyProvider, timestamp: number, session_id: Uint8Array, last_seen_seq: number): Promise<BuildResultV1>;
135
+ /**
136
+ * Validate a v1 MessageEnvelopeV1 (CELLO-MSG-003).
137
+ *
138
+ * Validation order (fail-fast):
139
+ * 1. protocol_version === 1 check — hard-reject v0 and any other version (AC-003, AC-004)
140
+ * 2. Field presence and byte-length checks
141
+ * 3. last_seen_seq and timestamp range checks
142
+ * 4. content size check
143
+ * 5. content_hash recomputation (BEFORE signature check) (AC-006)
144
+ * 6. Signature verification over Structure 1 TBS
145
+ */
146
+ export declare function validateEnvelopeV1(envelope: MessageEnvelopeV1): ValidateResultV1;
147
+ /**
148
+ * Serialize a v1 MessageEnvelopeV1 to canonical CBOR bytes (RFC 8949 §4.2.1).
149
+ *
150
+ * The envelope is encoded as a CBOR map with string keys.
151
+ * timestamp uses minimal encoding per RFC 8949 §4.2.1.
152
+ */
153
+ export declare function serializeEnvelopeV1(envelope: MessageEnvelopeV1): Uint8Array;
154
+ /**
155
+ * Deserialize a v1 MessageEnvelopeV1 from CBOR bytes.
156
+ *
157
+ * Performs structural validation only (field presence, types, sizes).
158
+ * Does NOT re-validate the signature or content_hash — call validateEnvelopeV1 for that.
159
+ */
160
+ export declare function deserializeEnvelopeV1(bytes: Uint8Array): DeserializeResultV1;
161
+ /**
162
+ * Extract Structure 1 from a v1 envelope — returns the canonical CBOR bytes that were signed.
163
+ *
164
+ * Structure 1: canonical_CBOR([1, content_hash, sender_pubkey, session_id, last_seen_seq, timestamp])
165
+ *
166
+ * This is the exact bytes the relay uses to build Structure 2 (CELLO-MERKLE-002).
167
+ * Per RFC 8949 §4.2.1 canonical CBOR and RFC 8032 Ed25519.
168
+ */
169
+ export declare function extractStructure1(envelope: MessageEnvelopeV1): Uint8Array;
170
+ //# sourceMappingURL=envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+EG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAEpB,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,UAAY,CAAC;AAsC3C;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,UAAU,EACnB,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CAiCtB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,eAAe,GAAG,cAAc,CA+H1E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,GAAG,UAAU,CAUvE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,iBAAiB,CA8FxE;AA8BD;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,UAAU,EACnB,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,aAAa,CAAC,CA+CxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,gBAAgB,CAuJhF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,UAAU,CAY3E;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,GAAG,mBAAmB,CAmH5E;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,UAAU,CAQzE"}