confium-tc-cmp20 — CMP20 threshold ECDSA on P-256
Real CMP20 threshold ECDSA over P-256. CMP20’s headline improvements over GG18 are non-interactive DKG (single broadcast round) and three-round signing (down from GG18’s four).
When to use this crate
Use confium-tc-cmp20 when:
- You need true threshold ECDSA — the joint secret is never reconstructed in one place, even during signing.
- You want the most efficient threshold-ECDSA protocol shipped in Confium (CMP20 beats GG18 on round count).
- You’re building a Mode 2 PKI drop-in where multiple stakeholders must jointly produce a single P-256 signature.
Architecture
Two registered schemes (one crate, two names so the framework’s
single-name/single-kind TcScheme trait can route each):
| Name | Kind | Produces |
|---|---|---|
CMP20-ECDSA-P256 |
Dkg |
per-party Cmp20Share + pubkey |
CMP20-ECDSA-P256-SIGN |
Signature |
64-byte (r, s) ECDSA signature |
Public API
Multi-round session API (advanced)
use confium_tc::session::{Session, SessionParams};
use confium_tc::party::{Party, PartyList};
let parties = PartyList::from_parties(vec![
Party::inproc("p0"),
Party::inproc("p1"),
Party::inproc("p2"),
]);
let params = SessionParams {
scheme: "CMP20-ECDSA-P256".to_string(),
parties: parties.clone(),
threshold: 2,
this_party_idx: 0,
local_share: None,
message: None,
};
let mut session = Session::create(¶ms)?;
// Drive rounds via session.round_step(incoming) — see the framework docs.
In-process driver (recommended for bindings + tests)
use confium_tc_cmp20::inprocess;
let kg = inprocess::keygen(2, 3)?;
assert_eq!(kg.shares.len(), 3);
assert_eq!(kg.public_key.len(), 33);
let sig = inprocess::sign(&kg.shares[..2], 2, b"hello cmp20")?;
assert_eq!(sig.len(), 64);
Share wire format
Each share blob is 71 bytes:
magic[4] = b"CMP2"
version[1] = 1
x_i[32] = Shamir share scalar (big-endian)
X[33] = joint public key (SEC1 compressed)
idx[1] = 1-based DKG roster index
The joint public key is replicated in every share so a signer can recover it without an out-of-band channel. The magic + version together form a self-identifying envelope.
Security notes
- Real Feldman VSS, real Lagrange interpolation, real threshold-ECDSA combine. The arithmetic matches a production CMP20 run for honest coalitions.
- MtA is a simplified in-clear stub. Nonces are revealed in the
clear during signing. This leaks the joint nonce
k, which is safe for a single signature but would be catastrophic across multiple signatures over the same secret. Production CMP20 hideskvia Paillier-based MtA — seemta.rsfor the gap and the in progress work. #![forbid(unsafe_code)]— no unsafe blocks anywhere in the crate.- Zeroize-on-drop for share scalars. The
Cmp20Share::Dropimpl clears the scalar bytes before releasing the memory.
Related
- GG18 crate — older four-round protocol, same surface.
- FROST-P256 crate — Shamir + single-party sign (no DKG).
- In-process driver — generic driver for any registered scheme.