docs(cggmp21): add package README (#6)

CGGMP21 threshold ECDSA is Lux's canonical native MPC library
(production bridge default per luxfi/bridge#405). Package had no
README — adds Overview + Public API + Paillier note + security
model + operational constraints (non-custodial, no cosigner path
by default).

Pure docs. No behavior change.
This commit is contained in:
Abhishek Krishna
2026-07-04 10:48:52 -07:00
committed by GitHub
parent 447c43314d
commit a6cc9670f7
+149
View File
@@ -0,0 +1,149 @@
# cggmp21
Threshold-ECDSA protocol scaffolding for the CGGMP21 construction
(Canetti, Gennaro, Goldfeder, Makriyannis, Peled, 2021 — *"UC
Non-Interactive, Proactive, Threshold ECDSA with Identifiable
Aborts"*, [eprint 2021/060](https://eprint.iacr.org/2021/060)).
CGGMP21 is Lux's canonical native-MPC construction for production
signing paths (see the bridge policy in
[luxfi/bridge#405](https://github.com/luxfi/bridge/pull/405): *"Production
stays pure native MPC (CGGMP21 / FROST via luxfi/mpc)"*). This
package holds the in-tree types + protocol-round skeleton used by
higher-level threshold code; the fully-verified production signer
lives at [`github.com/luxfi/threshold`](../threshold) and consumes
the same primitives.
## Scope
This package provides:
- Party / session state machines for the four-round CGGMP21 flow
(Commit → Reveal → Multiply → Open).
- A Paillier keypair + additively-homomorphic encryption used by the
MtA (multiplicative-to-additive) share-conversion step, including
a Schnorr-style ZK-proof-of-plaintext-knowledge skeleton.
- ECDSA signature and identifiable-abort record types.
- A `VerifySignature` helper for the final aggregated signature.
This package does **not** provide:
- A production-ready `Finalize` — the current `Finalize` intentionally
returns an error and delegates to `github.com/luxfi/threshold` for
the full MtA reconciliation, consistency checks, and
identifiable-abort proofs. Do not call this package's
`Finalize` from signing paths.
- The full CGGMP21 ZK proof suite. `ProveKnowledge` /
`VerifyKnowledge` here are a simplified Schnorr-style sketch (with
a non-Fiat-Shamir placeholder challenge) intended to illustrate
shape; production code MUST use the proofs specified in the CGGMP21
paper (Πenc, Πlog*, Πaff-g, Πmul, Πdec, etc.).
- Distributed key-generation networking. `Party.KeyGen` populates
local share state only; a transport layer supplies peer shares.
- Dispute resolution or on-chain glue — those live above this package
in the bridge / threshold layers.
## Public API
Verified against `cggmp21.go` and `paillier.go` at file time.
### Core types
| Symbol | Purpose |
| --- | --- |
| `Signature` | ECDSA `(R, S)` output. |
| `Config` | Threshold `t`, total parties `n`, curve, round timeout. |
| `Party` | Per-node state (secret share, Paillier keys, sessions). |
| `SigningSession` | Per-session state across all four rounds. |
| `ECPoint` | `(X, Y)` on the configured curve. |
| `Round2Message`, `Round3Message`, `Round4Message` | Wire messages between rounds. |
| `IdentifiableAbort` | Blame record for a misbehaving party. |
### Party lifecycle
| Symbol | Purpose |
| --- | --- |
| `NewParty(id, index, cfg, log)` | Constructor. Generates a 2048-bit Paillier keypair. |
| `(*Party).KeyGen(parties)` | Local DKG step — samples `x_i`, computes `[x_i]G`. |
| `(*Party).InitiateSign(sessionID, message)` | Opens a signing session; hashes message with SHA-256. |
| `(*Party).Round1_Commitment(sessionID)` | Samples `k_i`, `γ_i`; returns `H(i, [γ_i]G)`. |
| `(*Party).Round2_Reveal(sessionID, commitments)` | Reveals `[γ_i]G` after receiving peer commitments. |
| `(*Party).Round3_Multiply(sessionID, reveals)` | Verifies commitments, computes `δ_i = k_i·γ_i` and `χ_i = x_i·k_i`. |
| `(*Party).Round4_Open(sessionID, round3msgs)` | Broadcasts `δ_i` and collects peer `[δ_j]G`. |
| `(*Party).Finalize(sessionID, round4msgs)` | **Stub** — returns an error; use `luxfi/threshold`. |
### Helpers
| Symbol | Purpose |
| --- | --- |
| `VerifySignature(pubKey, message, sig)` | Verifies a completed threshold signature against a public key. |
| `ScalarBaseMult(curve, k)` | Convenience wrapper returning an `*ECPoint`. |
### Paillier (in `paillier.go`)
| Symbol | Purpose |
| --- | --- |
| `PaillierPublicKey`, `PaillierPrivateKey` | Keypair types. |
| `GeneratePaillierKeyPair(bits)` | Samples two primes, returns keypair. |
| `(*PaillierPublicKey).Encrypt(m)` | Standard Paillier encryption. |
| `(*PaillierPrivateKey).Decrypt(c)` | Standard Paillier decryption. |
| `(*PaillierPublicKey).Add(c1, c2)` | Homomorphic addition (`c1·c2 mod n²`). |
| `(*PaillierPublicKey).Multiply(c, k)` | Homomorphic constant-multiplication (`c^k mod n²`). |
| `L(x, n)` | Standard Paillier `L(x) = (x1)/n`. |
| `ZKProof`, `ProveKnowledge`, `VerifyKnowledge` | Schnorr-style plaintext-knowledge proof **sketch** — see caveat above. |
## Paillier usage
CGGMP21 uses Paillier as the additively-homomorphic backbone of the
MtA conversion — every `Party` holds a private Paillier key and the
peers' public keys, so a share `k_i` can be multiplied against a
peer's `γ_j` under encryption and the result reshared additively.
The keypair is generated eagerly inside `NewParty` at 2048 bits; the
current tree does not gate that on a config option.
## Security notes
- **Threshold model.** `t`-of-`n` with the identifiable-abort variant
from CGGMP21: any party deviating from the protocol can be
cryptographically blamed via `IdentifiableAbort`; the honest
majority does not lose their key material on abort.
- **No key extraction on abort.** Aborts surface at message-level
proof failure, not by revealing secret shares.
- **Curve.** Configured through `Config.Curve` — production callers
should pass `secp256k1` (or the equivalent `luxfi/crypto/secp256k1`)
and never mix curves across a session.
- **Randomness.** All sampling uses `crypto/rand`; do not substitute
a deterministic source for testing outside of dedicated test
builds.
## Operational constraints
Per the bridge signing policy
([luxfi/bridge#405](https://github.com/luxfi/bridge/pull/405)):
- **Non-custodial by default.** Production signing paths must remain
pure native MPC — CGGMP21 (this package + `luxfi/threshold`) for
ECDSA and FROST (via `luxfi/mpc`) for Schnorr / Ed25519.
- **No cosigner path.** Cosigner / MPC-provider fallback is
explicitly opt-in and must not be reachable from the default bridge
configuration.
- **`Finalize` is not wired.** Any caller reaching this package's
`Finalize` is a bug — route through `luxfi/threshold`.
## References
- Canetti, Gennaro, Goldfeder, Makriyannis, Peled. *UC
Non-Interactive, Proactive, Threshold ECDSA with Identifiable
Aborts.* CCS 2020 / eprint 2021/060.
<https://eprint.iacr.org/2021/060>
- Sibling packages in this repo:
- [`../threshold`](../threshold) — production threshold-signing
entrypoint that this package feeds.
- [`../bls`](../bls) — BLS threshold (separate construction, not
ECDSA).
- Ecosystem:
- [`luxfi/mpc`](https://github.com/luxfi/mpc) — hosts the FROST
Schnorr/Ed25519 side of the native-MPC stack.
- [`luxfi/bridge`](https://github.com/luxfi/bridge) — canonical
consumer; PR #405 sets the production-signing policy this
package supports.