@cello-protocol/crypto 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.
Files changed (49) hide show
  1. package/dist/checkpoint.d.ts +52 -0
  2. package/dist/checkpoint.d.ts.map +1 -0
  3. package/dist/checkpoint.js +70 -0
  4. package/dist/checkpoint.js.map +1 -0
  5. package/dist/ed25519.d.ts +25 -0
  6. package/dist/ed25519.d.ts.map +1 -0
  7. package/dist/ed25519.js +120 -0
  8. package/dist/ed25519.js.map +1 -0
  9. package/dist/frost/frost-threshold-signer.d.ts +178 -0
  10. package/dist/frost/frost-threshold-signer.d.ts.map +1 -0
  11. package/dist/frost/frost-threshold-signer.js +478 -0
  12. package/dist/frost/frost-threshold-signer.js.map +1 -0
  13. package/dist/frost/index.d.ts +23 -0
  14. package/dist/frost/index.d.ts.map +1 -0
  15. package/dist/frost/index.js +22 -0
  16. package/dist/frost/index.js.map +1 -0
  17. package/dist/frost/stubs.d.ts +82 -0
  18. package/dist/frost/stubs.d.ts.map +1 -0
  19. package/dist/frost/stubs.js +157 -0
  20. package/dist/frost/stubs.js.map +1 -0
  21. package/dist/frost/types.d.ts +173 -0
  22. package/dist/frost/types.d.ts.map +1 -0
  23. package/dist/frost/types.js +21 -0
  24. package/dist/frost/types.js.map +1 -0
  25. package/dist/hashing.d.ts +17 -0
  26. package/dist/hashing.d.ts.map +1 -0
  27. package/dist/hashing.js +50 -0
  28. package/dist/hashing.js.map +1 -0
  29. package/dist/index.d.ts +14 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +14 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/merkle.d.ts +162 -0
  34. package/dist/merkle.d.ts.map +1 -0
  35. package/dist/merkle.js +240 -0
  36. package/dist/merkle.js.map +1 -0
  37. package/dist/ml-dsa.d.ts +100 -0
  38. package/dist/ml-dsa.d.ts.map +1 -0
  39. package/dist/ml-dsa.js +257 -0
  40. package/dist/ml-dsa.js.map +1 -0
  41. package/dist/relay-registration.d.ts +62 -0
  42. package/dist/relay-registration.d.ts.map +1 -0
  43. package/dist/relay-registration.js +87 -0
  44. package/dist/relay-registration.js.map +1 -0
  45. package/dist/types.d.ts +11 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +2 -0
  48. package/dist/types.js.map +1 -0
  49. package/package.json +49 -0
@@ -0,0 +1,478 @@
1
+ /**
2
+ * FrostThresholdSigner — FROST threshold signing over Ed25519 (RFC 9591)
3
+ *
4
+ * CELLO-CRYPTO-003
5
+ *
6
+ * Crypto reference: FROST / RFC 9591, Ed25519 / RFC 8032
7
+ * Implementation: @noble/curves/ed25519 — ed25519_FROST
8
+ *
9
+ * ─── Phase P: Pseudocode ─────────────────────────────────────────────────────
10
+ *
11
+ * bootstrapKeyShares(agentPubkey, config):
12
+ * // TEST-ONLY shortcut — NOT real multi-round DKG (that's M3)
13
+ * // Guard: only callable in NODE_ENV=test
14
+ * assert NODE_ENV === 'test'
15
+ * identifiers = [derive(stub.id) for stub in stubs]
16
+ * dealerShares = trustedDealer({ min: threshold, max: participants }, identifiers)
17
+ * // Distribute each stub its share (persists in stub.#key)
18
+ * for each stub, share in dealerShares.secretShares:
19
+ * stub.receiveShare(share, dealerShares.public)
20
+ * // primary_pubkey = commitments[0] from the shared public package
21
+ * primary_pubkey = dealerShares.public.commitments[0]
22
+ * // Persist client's local key share (stored in module-level map keyed by agentPubkey)
23
+ * storeLocalShare(agentPubkey, { share: agentShare, pub: dealerShares.public })
24
+ * return { primaryPubkey: primary_pubkey }
25
+ * // NOTE: signing share NEVER returned or logged
26
+ *
27
+ * FrostThresholdSigner.participateInCeremony(ceremonyId, tbs, context):
28
+ * // Coordinator flow — RFC 9591 Section 5
29
+ * frame(tbs, context) → msg = encode(context + '\0' + tbs) // domain separation
30
+ * localShare = loadLocalShare(agentPubkey)
31
+ * if localShare is null: throw "not bootstrapped"
32
+ * availableStubs = stubs (or directoryNodes via stream in production)
33
+ *
34
+ * // Attempt loop — up to maxRetries
35
+ * for attempt in 1..maxRetries (honoring ceremonyTimeout):
36
+ * reachableStubs = stubs where not excluded
37
+ * if |reachableStubs| < threshold: return DIRECTORY_BELOW_THRESHOLD
38
+ * selectedStubs = reachableStubs[0..threshold-1]
39
+ *
40
+ * // Round 1 — collect commitments (coordinator generates local nonce too)
41
+ * localNonce = commit(localShare.secret)
42
+ * commitments = [localNonce.commitments]
43
+ * for each stub in selectedStubs:
44
+ * c = stub.generateCommitment()
45
+ * commitments.push(c.commitment)
46
+ * commitmentList = sort(commitments, by identifier)
47
+ *
48
+ * // Round 2 — collect partial signatures (with per-node timeout)
49
+ * validShares = {}
50
+ * excludedInThisRound = []
51
+ * for each (stub, stubCommitment) in selectedStubs (with roundTimeout):
52
+ * partialSig = await stub.signRound(pub, commitmentList, msg) // timeout → null
53
+ * if partialSig is null: excludedInThisRound.push(stub); continue
54
+ * if !verifyShare(pub, commitmentList, msg, stub.id, partialSig):
55
+ * excludedInThisRound.push(stub); continue // invalid partial — same as timeout
56
+ * validShares[stub.id] = partialSig
57
+ *
58
+ * // Also compute coordinator's own partial sig
59
+ * coordinatorPartialSig = signShare(localShare.secret, pub, localNonce.nonces, commitmentList, msg)
60
+ * validShares[coordinatorId] = coordinatorPartialSig
61
+ *
62
+ * if |validShares| >= threshold:
63
+ * signature = aggregate(pub, commitmentList, msg, validShares)
64
+ * return { ok: true, signature }
65
+ * else:
66
+ * // Not enough valid shares — retry with different participants
67
+ * continue
68
+ *
69
+ * return { ok: false, error: { reason: 'CEREMONY_EXHAUSTED' } }
70
+ *
71
+ * primary_pubkey derivation:
72
+ * // In trustedDealer mode: commitments[0] is the group public key
73
+ * // This is the constant term of the dealer's Shamir polynomial commitment
74
+ * // It is deterministic given the dealer's secret polynomial
75
+ * // All participants' public shares verify against the same commitments[0]
76
+ * primary_pubkey = dealerShares.public.commitments[0]
77
+ *
78
+ * Round timeout / node replacement:
79
+ * // Each stub.signRound wraps in Promise.race([real, timeoutPromise])
80
+ * // If timeout wins → stub treated as excluded (same path as invalid sig)
81
+ * // On retry, excluded stubs are filtered from available pool
82
+ * // If available < threshold → DIRECTORY_BELOW_THRESHOLD (not retried)
83
+ *
84
+ * ─── End Pseudocode ──────────────────────────────────────────────────────────
85
+ */
86
+ import { ed25519_FROST } from "@noble/curves/ed25519.js";
87
+ import { CONTEXT_SESSION_ESTABLISHMENT, CONTEXT_SEAL } from "./types.js";
88
+ // Module-level store for local key shares, keyed by agent pubkey hex.
89
+ // This keeps the signing share within the package boundary.
90
+ // Private (#) module-level map — not exported.
91
+ const _localShares = new Map();
92
+ /**
93
+ * Clear all local key shares stored in the module-level map.
94
+ *
95
+ * TEST-ONLY: guards against accumulation of key material across test cases.
96
+ * Call in afterEach/afterAll to evict shares seeded by bootstrapKeyShares.
97
+ *
98
+ * Throws if called outside NODE_ENV=test so it can never be misused in
99
+ * production (where it would collapse multi-party FROST to zero-share state).
100
+ */
101
+ export function clearTestShares() {
102
+ if (process.env.NODE_ENV !== "test") {
103
+ throw new Error("clearTestShares is test-only and must not be called in production");
104
+ }
105
+ _localShares.clear();
106
+ }
107
+ /**
108
+ * Store the client's local DKG key share after a real DKG ceremony completes.
109
+ *
110
+ * Production path: called after runNetworkDkg completes successfully.
111
+ * No NODE_ENV guard — this is the production key storage path (unlike bootstrapKeyShares).
112
+ *
113
+ * SECURITY: validates the secret against the public before storing.
114
+ * The FrostSecret is stored in the module-level map keyed by agentPubkeyHex.
115
+ */
116
+ export function storeDkgResult(agentPubkeyHex, secret, pub) {
117
+ ed25519_FROST.validateSecret(secret, pub);
118
+ _localShares.set(agentPubkeyHex, { secret, pub });
119
+ }
120
+ // ─── Message framing for domain separation ────────────────────────────────────
121
+ /**
122
+ * Frame a TBS with a context string for domain separation.
123
+ *
124
+ * Encoding: `<context>\0<tbs>`
125
+ * The null byte separator ensures unambiguous parsing: a context string
126
+ * cannot contain \0, so there is no collision between different contexts
127
+ * even when TBS bytes have the same content.
128
+ */
129
+ function frameMessage(context, tbs) {
130
+ const enc = new TextEncoder();
131
+ const ctxBytes = enc.encode(context);
132
+ // context + 0x00 separator + tbs
133
+ const framed = new Uint8Array(ctxBytes.length + 1 + tbs.length);
134
+ framed.set(ctxBytes, 0);
135
+ framed[ctxBytes.length] = 0x00; // null separator
136
+ framed.set(tbs, ctxBytes.length + 1);
137
+ return framed;
138
+ }
139
+ // ─── Agent pubkey serialization ───────────────────────────────────────────────
140
+ function toHex(bytes) {
141
+ return Buffer.from(bytes).toString("hex");
142
+ }
143
+ // ─── bootstrapKeyShares ───────────────────────────────────────────────────────
144
+ /**
145
+ * Test-harness shortcut for key share bootstrap.
146
+ *
147
+ * GUARD: Only callable in NODE_ENV=test. This is NOT a real multi-round DKG
148
+ * (that comes in M3). It uses trustedDealer to pre-populate shares for testing.
149
+ *
150
+ * Behavior:
151
+ * 1. Generate FROST shares via trustedDealer for all n stub nodes
152
+ * 2. Distribute each stub its signing share
153
+ * 3. One of the identifiers is the "client" (coordinator) — its share is stored locally
154
+ * 4. primary_pubkey = commitments[0] from the shared public package
155
+ * 5. Return { primaryPubkey } — no key material beyond the group public key
156
+ */
157
+ export async function bootstrapKeyShares(agentPubkey, config) {
158
+ if (process.env.NODE_ENV !== "test") {
159
+ throw new Error("bootstrapKeyShares is a test-only shortcut. " +
160
+ "Real DKG (M3) is required in production.");
161
+ }
162
+ const stubs = config.directoryNodeStubs;
163
+ if (!stubs || stubs.length === 0) {
164
+ throw new Error("bootstrapKeyShares requires directoryNodeStubs in test mode");
165
+ }
166
+ const { threshold, participants } = config;
167
+ // Identifiers: one per stub node + one for the client (coordinator)
168
+ // The client acts as participant "client:<agentPubkeyHex>"
169
+ const clientIdStr = `client:${toHex(agentPubkey)}`;
170
+ const clientIdentifier = ed25519_FROST.Identifier.derive(clientIdStr);
171
+ const stubIdentifiers = stubs.map((stub) => ed25519_FROST.Identifier.derive(stub.id));
172
+ const allIdentifiers = [clientIdentifier, ...stubIdentifiers];
173
+ // trustedDealer: produces one FrostPublic + per-participant FrostSecret shares
174
+ // The dealer polynomial's constant term commitment is the group public key.
175
+ const deal = ed25519_FROST.trustedDealer({ min: threshold, max: participants + 1 }, // +1 for the client
176
+ allIdentifiers);
177
+ // Distribute shares to stubs
178
+ for (const stub of stubs) {
179
+ const stubId = ed25519_FROST.Identifier.derive(stub.id);
180
+ const stubShare = deal.secretShares[stubId];
181
+ if (!stubShare) {
182
+ throw new Error(`No share generated for stub ${stub.id}`);
183
+ }
184
+ await stub.receiveShare(stubShare, deal.public);
185
+ }
186
+ // Store client's local share
187
+ const clientShare = deal.secretShares[clientIdentifier];
188
+ if (!clientShare) {
189
+ throw new Error("No share generated for client");
190
+ }
191
+ _localShares.set(toHex(agentPubkey), {
192
+ secret: clientShare,
193
+ pub: deal.public,
194
+ });
195
+ // primary_pubkey = commitments[0] = the group public key (32-byte Ed25519 point)
196
+ const primaryPubkey = deal.public.commitments[0];
197
+ // Return ONLY primaryPubkey — no key material
198
+ return { primaryPubkey: new Uint8Array(primaryPubkey) };
199
+ }
200
+ // ─── FrostThresholdSigner ─────────────────────────────────────────────────────
201
+ export class FrostThresholdSigner {
202
+ #config;
203
+ #agentPubkey;
204
+ static #DEFAULT_ROUND_TIMEOUT_MS = 3000;
205
+ static #DEFAULT_CEREMONY_TIMEOUT_MS = 30000;
206
+ static #DEFAULT_MAX_RETRIES = 3;
207
+ constructor(config, agentPubkey) {
208
+ this.#config = {
209
+ threshold: config.threshold,
210
+ participants: config.participants,
211
+ directoryNodes: config.directoryNodes,
212
+ directoryNodeStubs: config.directoryNodeStubs,
213
+ roundTimeoutMs: config.roundTimeoutMs ?? FrostThresholdSigner.#DEFAULT_ROUND_TIMEOUT_MS,
214
+ ceremonyTimeoutMs: config.ceremonyTimeoutMs ??
215
+ FrostThresholdSigner.#DEFAULT_CEREMONY_TIMEOUT_MS,
216
+ maxRetries: config.maxRetries ?? FrostThresholdSigner.#DEFAULT_MAX_RETRIES,
217
+ };
218
+ this.#agentPubkey = agentPubkey;
219
+ }
220
+ /**
221
+ * Get the group public key (primary_pubkey) for this signer.
222
+ * Returns the key from the stored local share (populated by bootstrapKeyShares).
223
+ */
224
+ getPrimaryPubkey() {
225
+ const localShare = _localShares.get(toHex(this.#agentPubkey));
226
+ if (!localShare) {
227
+ throw new Error("Not bootstrapped: call bootstrapKeyShares before using FrostThresholdSigner");
228
+ }
229
+ return new Uint8Array(localShare.pub.commitments[0]);
230
+ }
231
+ /**
232
+ * Verify a threshold signature against a public key and TBS with context.
233
+ * Used by callers (and tests) to verify signatures produced by participateInCeremony.
234
+ */
235
+ verifySignature(signature, tbs, context, publicKey) {
236
+ try {
237
+ const msg = frameMessage(context, tbs);
238
+ return ed25519_FROST.verify(signature, msg, publicKey);
239
+ }
240
+ catch {
241
+ return false;
242
+ }
243
+ }
244
+ /**
245
+ * Participate in a FROST threshold signing ceremony as coordinator.
246
+ *
247
+ * RFC 9591 Section 5 coordinator flow with in-process stubs.
248
+ * Full round coordination, timeout handling, and participant exclusion.
249
+ */
250
+ async participateInCeremony(ceremonyId, tbs, context, onProgress) {
251
+ const localShare = _localShares.get(toHex(this.#agentPubkey));
252
+ if (!localShare) {
253
+ throw new Error("Not bootstrapped: call bootstrapKeyShares before using FrostThresholdSigner");
254
+ }
255
+ const stubs = this.#config.directoryNodeStubs ?? [];
256
+ const { threshold, roundTimeoutMs, ceremonyTimeoutMs, maxRetries } = this.#config;
257
+ // Frame message for domain separation: context\0tbs
258
+ const msg = frameMessage(context, tbs);
259
+ const { pub } = localShare;
260
+ const clientId = ed25519_FROST.Identifier.derive(`client:${toHex(this.#agentPubkey)}`);
261
+ // Ceremony-level timeout: a deadline across all retry attempts
262
+ const ceremonyDeadline = Date.now() + ceremonyTimeoutMs;
263
+ // Pre-ceremony availability check (RFC 9591 §5 coordinator pre-check):
264
+ // Count nodes reachable at initiation. If fewer than (threshold - 1) are
265
+ // reachable, fail immediately — the ceremony cannot possibly succeed.
266
+ // (threshold - 1 because the client itself is one of the threshold signers)
267
+ const reachableAtInitiation = stubs.filter((s) => s.isReachable());
268
+ if (reachableAtInitiation.length < threshold - 1) {
269
+ return {
270
+ ok: false,
271
+ error: { reason: "DIRECTORY_BELOW_THRESHOLD" },
272
+ };
273
+ }
274
+ // Within-ceremony exclusion: stubs excluded during THIS ceremony.
275
+ // A stub is excluded (within this ceremony) when it: (a) times out or
276
+ // (b) returns an invalid partial signature (treated identically per story spec).
277
+ // On each new participateInCeremony call, the exclusion set resets.
278
+ const ceremonyExcluded = new Set();
279
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
280
+ // Check ceremony-level timeout at the start of each attempt
281
+ if (Date.now() >= ceremonyDeadline) {
282
+ return {
283
+ ok: false,
284
+ error: { reason: "CEREMONY_TIMEOUT" },
285
+ };
286
+ }
287
+ // Available stubs: reachable at initiation and not excluded in this ceremony
288
+ const available = reachableAtInitiation.filter((s) => !ceremonyExcluded.has(s.id));
289
+ // Select (threshold - 1) stubs for this round (client counts as one signer)
290
+ const selected = available.slice(0, threshold - 1);
291
+ if (selected.length < threshold - 1) {
292
+ // Not enough non-excluded nodes remain for this attempt (fewer than
293
+ // threshold - 1 stubs available after excluding failed nodes). Since the
294
+ // client counts as one of the threshold signers, fewer than (threshold - 1)
295
+ // stub participants means the ceremony cannot possibly succeed regardless of
296
+ // further retries. Return DIRECTORY_BELOW_THRESHOLD immediately — same as
297
+ // the pre-ceremony check — rather than sleeping and timing out with
298
+ // CEREMONY_TIMEOUT (which would diverge from the spec).
299
+ return {
300
+ ok: false,
301
+ error: { reason: "DIRECTORY_BELOW_THRESHOLD" },
302
+ };
303
+ }
304
+ // Round 1: generate fresh nonces + commitment list
305
+ const localNonce = ed25519_FROST.commit(localShare.secret);
306
+ const stubCommits = await Promise.all(selected.map((s) => s.generateCommitment()));
307
+ const commitmentList = [
308
+ localNonce.commitments,
309
+ ...stubCommits.map((c) => c.nonceCommitment),
310
+ ];
311
+ // total = selected stubs + 1 client
312
+ const totalParticipants = selected.length + 1;
313
+ for (let ci = 0; ci < totalParticipants; ci++) {
314
+ onProgress?.({ type: "commit_collected", index: ci + 1, total: totalParticipants });
315
+ }
316
+ // Round 2: collect partial signatures with per-node timeout
317
+ const sigShares = {};
318
+ let anyFailedThisRound = false;
319
+ let partialSigCount = 0;
320
+ for (let i = 0; i < selected.length; i++) {
321
+ const stub = selected[i];
322
+ // Check ceremony timeout before each node
323
+ if (Date.now() >= ceremonyDeadline) {
324
+ return {
325
+ ok: false,
326
+ error: { reason: "CEREMONY_TIMEOUT" },
327
+ };
328
+ }
329
+ // Race stub response against per-node timeout
330
+ const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve(null), roundTimeoutMs));
331
+ let partialSig;
332
+ try {
333
+ partialSig = await Promise.race([
334
+ stub.signRound({ pub, commitmentList, msg, ceremonyId }),
335
+ timeoutPromise,
336
+ ]);
337
+ }
338
+ catch {
339
+ partialSig = null;
340
+ }
341
+ if (partialSig === null) {
342
+ // Timeout — exclude this stub for the rest of this ceremony;
343
+ // pick replacement (different stub) in the next attempt
344
+ ceremonyExcluded.add(stub.id);
345
+ anyFailedThisRound = true;
346
+ continue;
347
+ }
348
+ // Verify partial sig — invalid treated identically to timeout (story spec §AC-006)
349
+ // The invalid partial is NEVER passed to the combiner/aggregator.
350
+ let isValid;
351
+ try {
352
+ const stubId = ed25519_FROST.Identifier.derive(stub.id);
353
+ isValid = ed25519_FROST.verifyShare(pub, commitmentList, msg, stubId, partialSig);
354
+ }
355
+ catch {
356
+ isValid = false;
357
+ }
358
+ if (!isValid) {
359
+ // Invalid partial sig — exclude for rest of this ceremony (same path as timeout)
360
+ ceremonyExcluded.add(stub.id);
361
+ anyFailedThisRound = true;
362
+ continue;
363
+ }
364
+ const stubId = ed25519_FROST.Identifier.derive(stub.id);
365
+ // String(stubId) makes the bigint→string coercion explicit: @noble/curves
366
+ // Identifier is a bigint, and Record<string,…> keys are strings. Using
367
+ // String() here documents the conversion rather than relying on implicit
368
+ // JS coercion, and must match how aggregate() looks up shares.
369
+ sigShares[String(stubId)] = partialSig;
370
+ partialSigCount++;
371
+ onProgress?.({ type: "partial_sig_collected", index: partialSigCount, total: totalParticipants });
372
+ }
373
+ // If any stub failed, this round's commitment list is inconsistent —
374
+ // the failed stubs committed but did not sign valid shares.
375
+ // Restart with a fresh participant set on the next attempt.
376
+ if (anyFailedThisRound) {
377
+ continue;
378
+ }
379
+ // All selected stubs provided valid partial sigs. Compute client's partial sig.
380
+ let clientSig;
381
+ try {
382
+ clientSig = ed25519_FROST.signShare(localShare.secret, pub, localNonce.nonces, commitmentList, msg);
383
+ sigShares[String(clientId)] = clientSig;
384
+ partialSigCount++;
385
+ onProgress?.({ type: "partial_sig_collected", index: partialSigCount, total: totalParticipants });
386
+ }
387
+ catch {
388
+ continue;
389
+ }
390
+ if (Object.keys(sigShares).length < threshold) {
391
+ continue;
392
+ }
393
+ // Aggregate
394
+ let signature;
395
+ try {
396
+ signature = ed25519_FROST.aggregate(pub, commitmentList, msg, sigShares);
397
+ }
398
+ catch {
399
+ continue;
400
+ }
401
+ return { ok: true, signature };
402
+ }
403
+ // Check if we timed out or exhausted retries
404
+ if (Date.now() >= ceremonyDeadline) {
405
+ return {
406
+ ok: false,
407
+ error: { reason: "CEREMONY_TIMEOUT" },
408
+ };
409
+ }
410
+ return {
411
+ ok: false,
412
+ error: { reason: "CEREMONY_EXHAUSTED" },
413
+ };
414
+ }
415
+ }
416
+ // ─── Standalone FROST signature verification ─────────────────────────────────
417
+ /**
418
+ * Verify a FROST threshold signature without a signer instance.
419
+ *
420
+ * Used by the client when it is the counterparty (participant B) and has no
421
+ * IThresholdSigner injected. Verifies the domain-separated FROST signature
422
+ * against the provided public key.
423
+ *
424
+ * @param signature - 64-byte threshold signature from the SessionAssignment
425
+ * @param tbs - to-be-signed bytes (from buildSessionEstablishmentTbs)
426
+ * @param context - domain context string (e.g. CONTEXT_SESSION_ESTABLISHMENT)
427
+ * @param publicKey - 32-byte Ed25519 group public key to verify against
428
+ * @returns true if signature verifies; false otherwise (never throws)
429
+ */
430
+ export function verifyFrostSignature(signature, tbs, context, publicKey) {
431
+ try {
432
+ const msg = frameMessage(context, tbs);
433
+ return ed25519_FROST.verify(signature, msg, publicKey);
434
+ }
435
+ catch {
436
+ return false;
437
+ }
438
+ }
439
+ // ─── MockThresholdSigner ──────────────────────────────────────────────────────
440
+ /**
441
+ * MockThresholdSigner: confirms the IThresholdSigner swap point is real.
442
+ *
443
+ * Implements IThresholdSigner without any FROST crypto. Used by AC-010 to
444
+ * verify that callers work correctly against the interface without knowing
445
+ * the concrete implementation.
446
+ *
447
+ * Returns a deterministic 64-byte signature (not cryptographically valid,
448
+ * but structurally correct for interface tests).
449
+ */
450
+ export class MockThresholdSigner {
451
+ async participateInCeremony(_ceremonyId, tbs, _context, _onProgress) {
452
+ // Return a 64-byte mock signature derived from the TBS
453
+ // Not cryptographically valid — only for interface shape testing
454
+ const sig = new Uint8Array(64);
455
+ for (let i = 0; i < Math.min(tbs.length, 32); i++) {
456
+ sig[i] = tbs[i];
457
+ }
458
+ sig[63] = 0x42; // marker
459
+ return { ok: true, signature: sig };
460
+ }
461
+ /**
462
+ * Return a deterministic 32-byte mock primary pubkey.
463
+ * Not a real Ed25519 point — only for interface shape testing.
464
+ */
465
+ getPrimaryPubkey() {
466
+ const key = new Uint8Array(32);
467
+ key[0] = 0x02; // marker byte indicating mock
468
+ key[31] = 0x4d; // 'M' for Mock
469
+ return key;
470
+ }
471
+ verifySignature(_signature, _tbs, _context, _publicKey) {
472
+ // Mock: always returns false (not a real verifier)
473
+ return false;
474
+ }
475
+ }
476
+ // Re-export context constants for convenience
477
+ export { CONTEXT_SESSION_ESTABLISHMENT, CONTEXT_SEAL };
478
+ //# sourceMappingURL=frost-threshold-signer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frost-threshold-signer.js","sourceRoot":"","sources":["../../src/frost/frost-threshold-signer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * FROST threshold signing public API — CELLO-CRYPTO-003
3
+ *
4
+ * Exports:
5
+ * - IThresholdSigner: the swap point interface
6
+ * - ThresholdSignature: result type
7
+ * - FrostThresholdSigner: FROST/Ed25519 implementation
8
+ * - MockThresholdSigner: test double confirming the swap point
9
+ * - CONTEXT_SESSION_ESTABLISHMENT / CONTEXT_SEAL: domain context constants
10
+ *
11
+ * Note: bootstrapKeyShares and clearTestShares are test-only — NOT exported
12
+ * from this barrel. Test files import them directly from
13
+ * ./frost-threshold-signer.js to prevent accidental use in production.
14
+ *
15
+ * Note: createInProcessStubs and stub types (DirectoryNodeStub, StubSignParams,
16
+ * StubCommitment) are NOT exported from this barrel. Test files import them
17
+ * directly from ./stubs.js and ./types.js to keep test infrastructure out of
18
+ * the production package surface.
19
+ */
20
+ export type { IThresholdSigner, ThresholdSignature, ThresholdSignatureOk, ThresholdSignatureError, FrostThresholdSignerConfig, FrostContext, BootstrapResult, } from "./types.js";
21
+ export { CONTEXT_SESSION_ESTABLISHMENT, CONTEXT_SEAL, } from "./types.js";
22
+ export { FrostThresholdSigner, MockThresholdSigner, } from "./frost-threshold-signer.js";
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/frost/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,0BAA0B,EAC1B,YAAY,EACZ,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,6BAA6B,EAC7B,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * FROST threshold signing public API — CELLO-CRYPTO-003
3
+ *
4
+ * Exports:
5
+ * - IThresholdSigner: the swap point interface
6
+ * - ThresholdSignature: result type
7
+ * - FrostThresholdSigner: FROST/Ed25519 implementation
8
+ * - MockThresholdSigner: test double confirming the swap point
9
+ * - CONTEXT_SESSION_ESTABLISHMENT / CONTEXT_SEAL: domain context constants
10
+ *
11
+ * Note: bootstrapKeyShares and clearTestShares are test-only — NOT exported
12
+ * from this barrel. Test files import them directly from
13
+ * ./frost-threshold-signer.js to prevent accidental use in production.
14
+ *
15
+ * Note: createInProcessStubs and stub types (DirectoryNodeStub, StubSignParams,
16
+ * StubCommitment) are NOT exported from this barrel. Test files import them
17
+ * directly from ./stubs.js and ./types.js to keep test infrastructure out of
18
+ * the production package surface.
19
+ */
20
+ export { CONTEXT_SESSION_ESTABLISHMENT, CONTEXT_SEAL, } from "./types.js";
21
+ export { FrostThresholdSigner, MockThresholdSigner, } from "./frost-threshold-signer.js";
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/frost/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAYH,OAAO,EACL,6BAA6B,EAC7B,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * In-process directory node stubs for FROST tests.
3
+ *
4
+ * CELLO-CRYPTO-003
5
+ *
6
+ * These stubs mimic the behavior of a real directory node's /cello/frost/1.0.0
7
+ * protocol handler. They exercise the same ceremony protocol logic as real nodes
8
+ * but run in-process for fast, deterministic tests.
9
+ *
10
+ * Stub configuration options:
11
+ * - Normal: responds with valid partial signatures
12
+ * - Unreachable: node is not reachable at initiation (caught by pre-ceremony check)
13
+ * - Unresponsive: reachable at initiation, but signRound times out (returns null)
14
+ * - InvalidResponse: returns random bytes (simulates malformed partial sig)
15
+ */
16
+ import type { FrostPublic, FrostSecret } from "@noble/curves/abstract/frost.js";
17
+ import type { DirectoryNodeStub, StubSignParams, StubCommitment } from "./types.js";
18
+ export declare class InProcessDirectoryNodeStub implements DirectoryNodeStub {
19
+ #private;
20
+ readonly id: string;
21
+ constructor(id: string);
22
+ /**
23
+ * Receive a FROST signing share from the bootstrap ceremony.
24
+ * Called by bootstrapKeyShares during setup.
25
+ */
26
+ receiveShare(secret: FrostSecret, pub: FrostPublic): Promise<void>;
27
+ /**
28
+ * TEST-ONLY: Return the stored FROST key pair for use by external test harnesses
29
+ * (e.g., to inject a share into a FrostDirectoryHandler via injectShareForTest).
30
+ *
31
+ * This deliberately exposes the raw FrostSecret for test setup purposes.
32
+ * MUST NOT be called outside of test code.
33
+ */
34
+ getShareForTest(): {
35
+ secret: FrostSecret;
36
+ pub: FrostPublic;
37
+ } | null;
38
+ /**
39
+ * Mark this node as unreachable at ceremony initiation.
40
+ * isReachable() will return false, causing the coordinator to exclude
41
+ * this node before any rounds begin.
42
+ */
43
+ setUnreachable(flag: boolean): void;
44
+ /**
45
+ * Mark this node as unresponsive during signing rounds.
46
+ * isReachable() returns true (node is connectable) but signRound returns null
47
+ * (simulates a timeout after connection is established).
48
+ */
49
+ setUnresponsive(flag: boolean): void;
50
+ /** Make this stub return random bytes (simulate malformed partial sig) */
51
+ setInvalidResponse(flag: boolean): void;
52
+ /**
53
+ * Returns whether this node is reachable at ceremony initiation.
54
+ * The coordinator uses this for the pre-ceremony availability check:
55
+ * if fewer than `threshold` nodes are reachable, fail immediately
56
+ * with DIRECTORY_BELOW_THRESHOLD.
57
+ */
58
+ isReachable(): boolean;
59
+ /**
60
+ * Generate a nonce commitment for the next signing round.
61
+ * Called by the coordinator before collecting partial signatures.
62
+ * Caches the nonce scalars for use in signRound().
63
+ */
64
+ generateCommitment(): Promise<StubCommitment>;
65
+ /**
66
+ * Participate in a signing round.
67
+ *
68
+ * Uses the nonce cached from the preceding generateCommitment() call.
69
+ * Returns a never-resolving Promise to simulate timeout (the coordinator's
70
+ * per-node timer will fire first). Returns random bytes to simulate
71
+ * an invalid partial sig.
72
+ *
73
+ * @returns Partial signature bytes, or a hanging Promise (simulate timeout)
74
+ */
75
+ signRound(params: StubSignParams): Promise<Uint8Array | null>;
76
+ }
77
+ /**
78
+ * Create n in-process directory node stubs.
79
+ * Each stub has a unique deterministic ID.
80
+ */
81
+ export declare function createInProcessStubs(n: number): InProcessDirectoryNodeStub[];
82
+ //# sourceMappingURL=stubs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stubs.d.ts","sourceRoot":"","sources":["../../src/frost/stubs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EAEZ,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,cAAc,EACf,MAAM,YAAY,CAAC;AAIpB,qBAAa,0BAA2B,YAAW,iBAAiB;;IAClE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBAaR,EAAE,EAAE,MAAM;IAMtB;;;OAGG;IACG,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxE;;;;;;OAMG;IACH,eAAe,IAAI;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,GAAG,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI;IAMnE;;;;OAIG;IACH,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAInC;;;;OAIG;IACH,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAIpC,0EAA0E;IAC1E,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAMvC;;;;;OAKG;IACH,WAAW,IAAI,OAAO;IAQtB;;;;OAIG;IACG,kBAAkB,IAAI,OAAO,CAAC,cAAc,CAAC;IAcnD;;;;;;;;;OASG;IACG,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;CAyCpE;AAID;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,MAAM,GAAG,0BAA0B,EAAE,CAM5E"}