@cello-protocol/protocol-types 0.0.15 → 0.0.17
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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/primary-transfer.d.ts +126 -0
- package/dist/primary-transfer.d.ts.map +1 -0
- package/dist/primary-transfer.js +96 -0
- package/dist/primary-transfer.js.map +1 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export type { SessionAssignment, SessionAssignmentFrost, SessionAssignmentSingle
|
|
|
7
7
|
export type { MlDsaKeyProvider, MlDsaVerifier, PseudonymBinding, Endorsement, Attestation, ConnectionPackage, EndorsementValidationStatus, AttestationValidationStatus, ValidatedEndorsement, ValidatedAttestation, PackageValidationResult, BuildPseudonymBindingResult, SignalCondition, SignalRequirement, ConnectionPolicy, DirectoryContext, } from "./connection-package.js";
|
|
8
8
|
export { MAX_PSEUDONYM_LABEL_BYTES, ML_DSA_PUBKEY_BYTES, ML_DSA_SIGNATURE_BYTES, buildPseudonymBinding, verifyPseudonymBinding, verifyEndorsement, verifyAttestation, validateConnectionPackage, encodeConnectionPackage, decodeConnectionPackage, } from "./connection-package.js";
|
|
9
9
|
export type { RegisterRequest, DkgComplete, DkgReady, RegisterSuccess, RegisterError, RegisterErrorReason, AgentProfile, RegistrationState, } from "./registration.js";
|
|
10
|
+
export type { PrimaryTransferRequest, PrimaryTransferAck, PrimaryTransferError, } from "./primary-transfer.js";
|
|
11
|
+
export { buildPrimaryTransferTbs, PRIMARY_TRANSFER_DOMAIN } from "./primary-transfer.js";
|
|
10
12
|
export { buildAgentRevocationTbs, AGENT_REVOCATION_DOMAIN } from "./revocation.js";
|
|
11
13
|
export type { RevokeAgentRequest, AgentRevocationAck, AgentRevocationError, AgentRevocationErrorReason, } from "./revocation.js";
|
|
12
14
|
export type { ConnectionRequest, ConnectionRequestInbound, ConnectionResponse, ConnectionResponseVerdict, ConnectionEstablished, ConnectionRejected, ConnectionInsufficient, ConnectionRequestError, ConnectionRequestErrorReason, DisclosureRequest, DisclosureRequestItem, DisclosureRequestInbound, DisclosureResponse, DisclosureResponseInbound, ConnectionRecord, PendingConnectionRequest, ClientConnectionRecord, SessionRequestM3, SessionRequestM3ErrorReason, } from "./connection-request.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ export { MAX_CONTENT_BYTES, buildEnvelope, serializeEnvelope, deserializeEnvelop
|
|
|
2
2
|
export { SCAN_RESULT_SENTINEL, buildStructure2, encodeStructure2, encodeScanResultSentinel, verifyStructure2Signature, } from "./structure2.js";
|
|
3
3
|
export { computeGenesisPrevRoot, encodeSealPayload, decodeSealPayload, buildSessionEstablishmentTbs, buildSealTbs, SEAL_RECEIPT_DISCLAIMER } from "./session.js";
|
|
4
4
|
export { MAX_PSEUDONYM_LABEL_BYTES, ML_DSA_PUBKEY_BYTES, ML_DSA_SIGNATURE_BYTES, buildPseudonymBinding, verifyPseudonymBinding, verifyEndorsement, verifyAttestation, validateConnectionPackage, encodeConnectionPackage, decodeConnectionPackage, } from "./connection-package.js";
|
|
5
|
+
export { buildPrimaryTransferTbs, PRIMARY_TRANSFER_DOMAIN } from "./primary-transfer.js";
|
|
5
6
|
export { buildAgentRevocationTbs, AGENT_REVOCATION_DOMAIN } from "./revocation.js";
|
|
6
7
|
export { MANIFEST_SIGNATURE_INVALID, MANIFEST_VERSION_ROLLBACK, MANIFEST_EXPIRED, } from "./manifest.js";
|
|
7
8
|
export { IT_LENGTH_PREFIX_DEFAULT_MAX, CONTENT_PARK_PROTOCOL_ID, CONTENT_PARK_AUTH_DOMAIN, buildContentParkAuthMsg, isContentDeliveryAck, isContentResendRequest, isContentParkDeposit, } from "./content-delivery.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AA0BjK,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AA0BjK,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AAmBjC,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAEzF,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AA6DnF,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AAuBvB,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAO/B,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO Primary/Standby Transfer Wire Types — M8C-PRIMARY-1
|
|
3
|
+
*
|
|
4
|
+
* Design: docs/planning/user-stories/m8c/M8C-PRIMARY-DESIGN.md (Decision 4, revised across 3
|
|
5
|
+
* passes: adversarial security review, a code-level correction to the real client-coordinated-
|
|
6
|
+
* per-node pattern, and a release-attestation crypto fix reusing the existing FROST ceremony).
|
|
7
|
+
* Mirrors registration.ts's DKG protocol shape exactly — CELLO has no cross-node RPC/consensus
|
|
8
|
+
* anywhere; "exactly one Primary" is enforced by the SAME pattern DKG already uses: the daemon
|
|
9
|
+
* (client) dials each of T-of-N directory nodes holding the agent's FROST shares directly, and
|
|
10
|
+
* each node independently verifies + records the claim locally. There is no directory-to-directory
|
|
11
|
+
* frame here — only client-to-directory, repeated per node.
|
|
12
|
+
*
|
|
13
|
+
* Phase P — Pseudocode
|
|
14
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
15
|
+
* Primary-transfer flow (on the existing authenticated /cello/signaling/1.0.0 stream, once per
|
|
16
|
+
* directory node the daemon dials):
|
|
17
|
+
*
|
|
18
|
+
* CLIENT (new Primary) → DIRECTORY (one node):
|
|
19
|
+
* 1. Client sends primary_transfer_request { k_local_pubkey, new_daemon_id, old_daemon_id,
|
|
20
|
+
* nonce, timestamp, release_signature }. release_signature is a REAL FROST threshold
|
|
21
|
+
* signature (core/crypto's participateInCeremony, context CONTEXT_PRIMARY_RELEASE — see
|
|
22
|
+
* core/crypto/src/frost/types.ts) produced by the OLD Primary over canonical-bytes
|
|
23
|
+
* (k_local_pubkey, new_daemon_id, old_daemon_id, nonce, timestamp). This is NOT a K_local
|
|
24
|
+
* signature (both daemons share K_local after pairing, per Decision 1 — a K_local signature
|
|
25
|
+
* cannot distinguish which physical device produced it). A FROST signature under this
|
|
26
|
+
* context can ONLY be produced by a device that currently holds the agent's FROST share —
|
|
27
|
+
* exactly the property that distinguishes a genuine old Primary from a Standby that never
|
|
28
|
+
* received the share (Decision 3).
|
|
29
|
+
*
|
|
30
|
+
* DIRECTORY validates (per node, independently):
|
|
31
|
+
* a. old_daemon_id matches THIS node's own currently-recorded primary_holder.holding_daemon_id
|
|
32
|
+
* → else primary_transfer_error { reason: "not_registered" } (a stale/wrong claim about who
|
|
33
|
+
* is releasing is rejected before any cryptographic check)
|
|
34
|
+
* b. release_signature verifies via verifySignature(release_signature, tbs, CONTEXT_PRIMARY_RELEASE,
|
|
35
|
+
* primary_pubkey) against THIS node's own recorded primary_pubkey for k_local_pubkey → else
|
|
36
|
+
* primary_transfer_error { reason: "release_not_verified" }
|
|
37
|
+
* c. nonce not already consumed at this node (single-use, mirrors the pre-auth capability's
|
|
38
|
+
* consumed_at pattern) → else primary_transfer_error { reason: "nonce_reused" }
|
|
39
|
+
* d. timestamp within a bounded freshness window (replay defense) → else
|
|
40
|
+
* primary_transfer_error { reason: "stale_request" }
|
|
41
|
+
*
|
|
42
|
+
* DIRECTORY on success (this node only):
|
|
43
|
+
* - Upserts its own local primary_holder row: holding_daemon_id = new_daemon_id,
|
|
44
|
+
* last_attested_at = now()
|
|
45
|
+
* - Sends primary_transfer_ack { node_id } — the daemon tallies acks per Decision 4; T-of-N acks
|
|
46
|
+
* is what "transfer complete" means, never a cross-node signal
|
|
47
|
+
*
|
|
48
|
+
* CLIENT tallies primary_transfer_ack across its T-of-N dial attempts (mirrors exactly how it
|
|
49
|
+
* already tallies DKG-round successes when driving DKG per-node) and does not proceed to resume as
|
|
50
|
+
* Primary (attempt a FROST ceremony, accept new sessions) until T acks are collected.
|
|
51
|
+
*
|
|
52
|
+
* Ceremony-gate check (separate, existing FROST signing-request path, extended): a directory node
|
|
53
|
+
* refuses to let a ceremony proceed through its own participation unless the requesting
|
|
54
|
+
* connection's daemon_id matches that node's own primary_holder.holding_daemon_id.
|
|
55
|
+
*
|
|
56
|
+
* Security invariants:
|
|
57
|
+
* SI-001: release_signature MUST be a real FROST threshold signature under CONTEXT_PRIMARY_RELEASE
|
|
58
|
+
* (verified against the node's own recorded primary_pubkey) — NEVER a K_local signature or any
|
|
59
|
+
* signature a Standby (K_local-holder, non-share-holder) could produce.
|
|
60
|
+
* SI-002: nonce is single-use per node (replay defense, mirrors the existing pre-auth capability
|
|
61
|
+
* `consumed_at` pattern already proven in production).
|
|
62
|
+
* SI-003: this frame set carries NO secret material — no FROST share, no K_local seed. The share
|
|
63
|
+
* itself is never transmitted to the directory (M8C-PRIMARY-DESIGN Decision 3); the directory
|
|
64
|
+
* only ever sees a threshold signature it can verify, never anything it could use to forge one.
|
|
65
|
+
* SI-004: old_daemon_id is checked against this node's OWN recorded holder before any
|
|
66
|
+
* cryptographic verification — an attacker cannot substitute a fabricated old_daemon_id and
|
|
67
|
+
* hope the signature check alone catches it (defense in depth, cheap check first).
|
|
68
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
+
*/
|
|
70
|
+
/** Domain separation tag — bound as the first TBS field so a primary-transfer release signature
|
|
71
|
+
* can never be replayed as any other CELLO signature (and vice-versa) even before FROST's own
|
|
72
|
+
* CONTEXT_PRIMARY_RELEASE domain separation is applied — belt and suspenders, matching the
|
|
73
|
+
* existing AGENT_REVOCATION_DOMAIN convention (revocation.ts). */
|
|
74
|
+
export declare const PRIMARY_TRANSFER_DOMAIN = "CELLO-PRIMARY-TRANSFER-v1";
|
|
75
|
+
/**
|
|
76
|
+
* Canonical to-be-signed bytes for a primary-transfer release attestation (M8C-PRIMARY-DESIGN
|
|
77
|
+
* Decision 3, Pass 3). Field order is LOAD-BEARING — both the signer (the old Primary daemon,
|
|
78
|
+
* via participateInCeremony) and every verifier (each directory node, via verifySignature) must
|
|
79
|
+
* produce IDENTICAL bytes, or verification never succeeds regardless of whether the ceremony
|
|
80
|
+
* itself was genuine. Mirrors buildAgentRevocationTbs's exact structure (revocation.ts).
|
|
81
|
+
*/
|
|
82
|
+
export declare function buildPrimaryTransferTbs(kLocalPubkeyHex: string, newDaemonId: string, oldDaemonId: string, nonce: string, timestamp: number): Uint8Array;
|
|
83
|
+
/**
|
|
84
|
+
* Step 1: the new Primary attests a transfer to one directory node. Sent on that node's
|
|
85
|
+
* authenticated signaling stream. The daemon repeats this per node it dials.
|
|
86
|
+
*/
|
|
87
|
+
export interface PrimaryTransferRequest {
|
|
88
|
+
type: "primary_transfer_request";
|
|
89
|
+
/** Hex-encoded K_local public key (32 bytes) — the shared identity both daemons hold. */
|
|
90
|
+
k_local_pubkey: string;
|
|
91
|
+
/** Fresh per-device UUID identifying the daemon CLAIMING Primary status. */
|
|
92
|
+
new_daemon_id: string;
|
|
93
|
+
/** The per-device UUID of the daemon RELINQUISHING Primary status. */
|
|
94
|
+
old_daemon_id: string;
|
|
95
|
+
/**
|
|
96
|
+
* Hex-encoded 64-byte FROST threshold signature (core/crypto's participateInCeremony, context
|
|
97
|
+
* CONTEXT_PRIMARY_RELEASE) over canonical-bytes(k_local_pubkey, new_daemon_id, old_daemon_id,
|
|
98
|
+
* nonce, timestamp), produced by the OLD Primary. Verified via verifySignature(...,
|
|
99
|
+
* CONTEXT_PRIMARY_RELEASE, primary_pubkey) — proves genuine possession of the FROST share at
|
|
100
|
+
* release time (SI-001). NOT a K_local signature — see the module doc above for why.
|
|
101
|
+
*/
|
|
102
|
+
release_signature: string;
|
|
103
|
+
/** Single-use nonce (SI-002) — mirrors the existing pre-auth capability pattern. */
|
|
104
|
+
nonce: string;
|
|
105
|
+
/** Unix ms timestamp — bounds replay window (freshness check, not itself the security boundary). */
|
|
106
|
+
timestamp: number;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* This node has recorded new_daemon_id as the current Primary holder for k_local_pubkey. This is
|
|
110
|
+
* a PER-NODE acknowledgment, never a cross-node commit signal — the client tallies these across
|
|
111
|
+
* its T-of-N dial attempts (M8C-PRIMARY-DESIGN Decision 4).
|
|
112
|
+
*/
|
|
113
|
+
export interface PrimaryTransferAck {
|
|
114
|
+
type: "primary_transfer_ack";
|
|
115
|
+
/** The stable manifest label of the node that accepted (e.g. "us-east-1"). */
|
|
116
|
+
node_id: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* This node rejected the transfer request. SI-001/SI-002/SI-004/freshness failures all surface
|
|
120
|
+
* here with a distinct, named reason — never a generic failure.
|
|
121
|
+
*/
|
|
122
|
+
export interface PrimaryTransferError {
|
|
123
|
+
type: "primary_transfer_error";
|
|
124
|
+
reason: "release_not_verified" | "nonce_reused" | "stale_request" | "not_registered";
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=primary-transfer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primary-transfer.d.ts","sourceRoot":"","sources":["../src/primary-transfer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AAQH;;;mEAGmE;AACnE,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAEnE;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,UAAU,CASZ;AAID;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,0BAA0B,CAAC;IACjC,yFAAyF;IACzF,cAAc,EAAE,MAAM,CAAC;IACvB,4EAA4E;IAC5E,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAC;IACd,oGAAoG;IACpG,SAAS,EAAE,MAAM,CAAC;CACnB;AAID;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,wBAAwB,CAAC;IAC/B,MAAM,EAAE,sBAAsB,GAAG,cAAc,GAAG,eAAe,GAAG,gBAAgB,CAAC;CACtF"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO Primary/Standby Transfer Wire Types — M8C-PRIMARY-1
|
|
3
|
+
*
|
|
4
|
+
* Design: docs/planning/user-stories/m8c/M8C-PRIMARY-DESIGN.md (Decision 4, revised across 3
|
|
5
|
+
* passes: adversarial security review, a code-level correction to the real client-coordinated-
|
|
6
|
+
* per-node pattern, and a release-attestation crypto fix reusing the existing FROST ceremony).
|
|
7
|
+
* Mirrors registration.ts's DKG protocol shape exactly — CELLO has no cross-node RPC/consensus
|
|
8
|
+
* anywhere; "exactly one Primary" is enforced by the SAME pattern DKG already uses: the daemon
|
|
9
|
+
* (client) dials each of T-of-N directory nodes holding the agent's FROST shares directly, and
|
|
10
|
+
* each node independently verifies + records the claim locally. There is no directory-to-directory
|
|
11
|
+
* frame here — only client-to-directory, repeated per node.
|
|
12
|
+
*
|
|
13
|
+
* Phase P — Pseudocode
|
|
14
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
15
|
+
* Primary-transfer flow (on the existing authenticated /cello/signaling/1.0.0 stream, once per
|
|
16
|
+
* directory node the daemon dials):
|
|
17
|
+
*
|
|
18
|
+
* CLIENT (new Primary) → DIRECTORY (one node):
|
|
19
|
+
* 1. Client sends primary_transfer_request { k_local_pubkey, new_daemon_id, old_daemon_id,
|
|
20
|
+
* nonce, timestamp, release_signature }. release_signature is a REAL FROST threshold
|
|
21
|
+
* signature (core/crypto's participateInCeremony, context CONTEXT_PRIMARY_RELEASE — see
|
|
22
|
+
* core/crypto/src/frost/types.ts) produced by the OLD Primary over canonical-bytes
|
|
23
|
+
* (k_local_pubkey, new_daemon_id, old_daemon_id, nonce, timestamp). This is NOT a K_local
|
|
24
|
+
* signature (both daemons share K_local after pairing, per Decision 1 — a K_local signature
|
|
25
|
+
* cannot distinguish which physical device produced it). A FROST signature under this
|
|
26
|
+
* context can ONLY be produced by a device that currently holds the agent's FROST share —
|
|
27
|
+
* exactly the property that distinguishes a genuine old Primary from a Standby that never
|
|
28
|
+
* received the share (Decision 3).
|
|
29
|
+
*
|
|
30
|
+
* DIRECTORY validates (per node, independently):
|
|
31
|
+
* a. old_daemon_id matches THIS node's own currently-recorded primary_holder.holding_daemon_id
|
|
32
|
+
* → else primary_transfer_error { reason: "not_registered" } (a stale/wrong claim about who
|
|
33
|
+
* is releasing is rejected before any cryptographic check)
|
|
34
|
+
* b. release_signature verifies via verifySignature(release_signature, tbs, CONTEXT_PRIMARY_RELEASE,
|
|
35
|
+
* primary_pubkey) against THIS node's own recorded primary_pubkey for k_local_pubkey → else
|
|
36
|
+
* primary_transfer_error { reason: "release_not_verified" }
|
|
37
|
+
* c. nonce not already consumed at this node (single-use, mirrors the pre-auth capability's
|
|
38
|
+
* consumed_at pattern) → else primary_transfer_error { reason: "nonce_reused" }
|
|
39
|
+
* d. timestamp within a bounded freshness window (replay defense) → else
|
|
40
|
+
* primary_transfer_error { reason: "stale_request" }
|
|
41
|
+
*
|
|
42
|
+
* DIRECTORY on success (this node only):
|
|
43
|
+
* - Upserts its own local primary_holder row: holding_daemon_id = new_daemon_id,
|
|
44
|
+
* last_attested_at = now()
|
|
45
|
+
* - Sends primary_transfer_ack { node_id } — the daemon tallies acks per Decision 4; T-of-N acks
|
|
46
|
+
* is what "transfer complete" means, never a cross-node signal
|
|
47
|
+
*
|
|
48
|
+
* CLIENT tallies primary_transfer_ack across its T-of-N dial attempts (mirrors exactly how it
|
|
49
|
+
* already tallies DKG-round successes when driving DKG per-node) and does not proceed to resume as
|
|
50
|
+
* Primary (attempt a FROST ceremony, accept new sessions) until T acks are collected.
|
|
51
|
+
*
|
|
52
|
+
* Ceremony-gate check (separate, existing FROST signing-request path, extended): a directory node
|
|
53
|
+
* refuses to let a ceremony proceed through its own participation unless the requesting
|
|
54
|
+
* connection's daemon_id matches that node's own primary_holder.holding_daemon_id.
|
|
55
|
+
*
|
|
56
|
+
* Security invariants:
|
|
57
|
+
* SI-001: release_signature MUST be a real FROST threshold signature under CONTEXT_PRIMARY_RELEASE
|
|
58
|
+
* (verified against the node's own recorded primary_pubkey) — NEVER a K_local signature or any
|
|
59
|
+
* signature a Standby (K_local-holder, non-share-holder) could produce.
|
|
60
|
+
* SI-002: nonce is single-use per node (replay defense, mirrors the existing pre-auth capability
|
|
61
|
+
* `consumed_at` pattern already proven in production).
|
|
62
|
+
* SI-003: this frame set carries NO secret material — no FROST share, no K_local seed. The share
|
|
63
|
+
* itself is never transmitted to the directory (M8C-PRIMARY-DESIGN Decision 3); the directory
|
|
64
|
+
* only ever sees a threshold signature it can verify, never anything it could use to forge one.
|
|
65
|
+
* SI-004: old_daemon_id is checked against this node's OWN recorded holder before any
|
|
66
|
+
* cryptographic verification — an attacker cannot substitute a fabricated old_daemon_id and
|
|
67
|
+
* hope the signature check alone catches it (defense in depth, cheap check first).
|
|
68
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
+
*/
|
|
70
|
+
import { Encoder } from "cbor-x";
|
|
71
|
+
// Same config as session.ts/revocation.ts's own TBS builders — tagUint8Array:false so byte
|
|
72
|
+
// strings encode as definite-length CBOR byte strings (stable, encoder-independent layout).
|
|
73
|
+
const CBOR_ENC = new Encoder({ tagUint8Array: false });
|
|
74
|
+
/** Domain separation tag — bound as the first TBS field so a primary-transfer release signature
|
|
75
|
+
* can never be replayed as any other CELLO signature (and vice-versa) even before FROST's own
|
|
76
|
+
* CONTEXT_PRIMARY_RELEASE domain separation is applied — belt and suspenders, matching the
|
|
77
|
+
* existing AGENT_REVOCATION_DOMAIN convention (revocation.ts). */
|
|
78
|
+
export const PRIMARY_TRANSFER_DOMAIN = "CELLO-PRIMARY-TRANSFER-v1";
|
|
79
|
+
/**
|
|
80
|
+
* Canonical to-be-signed bytes for a primary-transfer release attestation (M8C-PRIMARY-DESIGN
|
|
81
|
+
* Decision 3, Pass 3). Field order is LOAD-BEARING — both the signer (the old Primary daemon,
|
|
82
|
+
* via participateInCeremony) and every verifier (each directory node, via verifySignature) must
|
|
83
|
+
* produce IDENTICAL bytes, or verification never succeeds regardless of whether the ceremony
|
|
84
|
+
* itself was genuine. Mirrors buildAgentRevocationTbs's exact structure (revocation.ts).
|
|
85
|
+
*/
|
|
86
|
+
export function buildPrimaryTransferTbs(kLocalPubkeyHex, newDaemonId, oldDaemonId, nonce, timestamp) {
|
|
87
|
+
return CBOR_ENC.encode([
|
|
88
|
+
PRIMARY_TRANSFER_DOMAIN,
|
|
89
|
+
kLocalPubkeyHex,
|
|
90
|
+
newDaemonId,
|
|
91
|
+
oldDaemonId,
|
|
92
|
+
nonce,
|
|
93
|
+
timestamp,
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=primary-transfer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primary-transfer.js","sourceRoot":"","sources":["../src/primary-transfer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEjC,2FAA2F;AAC3F,4FAA4F;AAC5F,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC;AAEvD;;;mEAGmE;AACnE,MAAM,CAAC,MAAM,uBAAuB,GAAG,2BAA2B,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACrC,eAAuB,EACvB,WAAmB,EACnB,WAAmB,EACnB,KAAa,EACb,SAAiB;IAEjB,OAAO,QAAQ,CAAC,MAAM,CAAC;QACrB,uBAAuB;QACvB,eAAe;QACf,WAAW;QACX,WAAW;QACX,KAAK;QACL,SAAS;KACV,CAAe,CAAC;AACnB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cello-protocol/protocol-types",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"cbor-x": "^1.6.4",
|
|
26
|
-
"@cello-protocol/crypto": "0.0.
|
|
26
|
+
"@cello-protocol/crypto": "0.0.18"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@claude-flow/testing": "3.0.0-alpha.6",
|