Threshold ECDSA¶
Overview¶
Threshold ECDSA enables t-of-n distributed signing, where any t parties out of n can collaboratively produce a valid ECDSA signature without any single party ever holding the complete private key. This is essential for:
Cryptocurrency wallets — Multi-signature security for Bitcoin, Ethereum, XRPL
Key management — Distributed custody without single points of failure
Regulatory compliance — Separation of duties for signing authority
Charm provides three production-ready threshold ECDSA implementations:
GG18 — Classic Paillier-based scheme (Gennaro & Goldfeder 2018)
CGGMP21 — UC-secure with identifiable aborts (Canetti et al. 2021)
DKLS23 — Non-interactive presigning with OT-based MtA (Doerner et al. 2023)
All schemes support secp256k1 (Bitcoin, XRPL) and other elliptic curves.
Scheme Comparison¶
Feature |
GG18 |
CGGMP21 |
DKLS23 |
|---|---|---|---|
Security Model |
ROM (Random Oracle) |
UC (Composable) |
ROM (Random Oracle) |
Assumption |
DCR + ROM |
DCR + Strong RSA |
DDH + ROM |
DKG Rounds |
3 |
3 |
3 |
Signing Rounds |
4 (interactive) |
3 presign + 1 sign |
3 presign + 1 sign |
Presigning |
❌ No |
✅ Yes |
✅ Yes |
Identifiable Aborts |
❌ No |
✅ Yes |
❌ No |
MtA Protocol |
Paillier-based |
Paillier-based |
OT-based |
Best For |
Simple deployments |
High security needs |
Low-latency signing |
When to use each scheme:
GG18: Simple threshold signing without presigning requirements
CGGMP21: When you need UC security, identifiable aborts, or proactive refresh
DKLS23: When you need fast online signing with pre-computed presignatures
Quick Start¶
GG18 (2-of-3 threshold signing):
from charm.toolbox.ecgroup import ECGroup
from charm.toolbox.eccurve import secp256k1
from charm.schemes.threshold import GG18
group = ECGroup(secp256k1)
gg18 = GG18(group, threshold=2, num_parties=3)
# Distributed key generation (3 rounds)
key_shares, public_key = gg18.keygen()
# Interactive signing with 2 parties (4 rounds)
message = b"Bitcoin transaction hash"
signature = gg18.sign(key_shares[:2], message)
# Standard ECDSA verification
assert gg18.verify(public_key, message, signature)
CGGMP21 with presigning:
from charm.schemes.threshold import CGGMP21
cggmp = CGGMP21(group, threshold=2, num_parties=3)
key_shares, public_key = cggmp.keygen()
# Presigning (can be done offline, 3 rounds)
presignatures = cggmp.presign(key_shares[:2])
# Fast online signing (1 round)
message = b"XRPL payment transaction"
signature = cggmp.sign(key_shares[:2], message, presignatures)
assert cggmp.verify(public_key, message, signature)
DKLS23 with XRPL integration:
from charm.schemes.threshold import DKLS23
from charm.schemes.threshold.xrpl_wallet import (
XRPLThresholdWallet, XRPLClient
)
dkls = DKLS23(group, threshold=2, num_parties=3)
key_shares, public_key = dkls.keygen()
# Create XRPL wallet from threshold public key
wallet = XRPLThresholdWallet(group, public_key)
client = XRPLClient(is_testnet=True)
# See examples/xrpl_memo_demo.py for complete flow
GG18 Protocol Details¶
Reference: Gennaro & Goldfeder 2018
GG18 is a classic threshold ECDSA scheme using Paillier encryption for the Multiplicative-to-Additive (MtA) protocol. It provides a straightforward implementation without presigning.
Key Features:
Paillier-based MtA: Secure multiplication using homomorphic encryption
Feldman VSS: Verifiable secret sharing for key distribution
Interactive Signing: 4-round protocol for signature generation
Protocol Phases:
Distributed Key Generation (3 rounds)
Round 1: Commit to secret shares
Round 2: Reveal commitments, distribute Feldman VSS shares
Round 3: Verify shares, compute public key X = g^x
Interactive Signing (4 rounds)
Round 1: Generate k_i, γ_i; broadcast commitments
Round 2: MtA for k*γ and k*x products
Round 3: Reveal δ_i, compute R = g^{1/k}
Round 4: Compute and combine signature shares s_i
Security: ROM (Random Oracle Model) with DCR assumption.
CGGMP21 Protocol Details¶
Reference: Canetti et al. 2021
CGGMP21 provides UC-secure threshold ECDSA with identifiable aborts. If a party misbehaves, the protocol can identify the malicious party with cryptographic proof.
Key Features:
UC Security: Composable security in the Universal Composability framework
Identifiable Aborts: Malicious parties are identified with evidence
Presigning: Offline computation for fast online signing
Ring-Pedersen Parameters: Used for ZK proofs (Π^{log}, Π^{aff-g}, Π^{mul})
Protocol Phases:
Distributed Key Generation (3 rounds) - Uses Pedersen VSS for verifiable secret sharing - Generates Ring-Pedersen parameters for ZK proofs - All parties compute consistent public key X = g^x
Presigning (3 rounds, optional) - Round 1: Generate k_i, γ_i; Paillier encrypt k_i - Round 2: MtA with ZK proofs for k*γ and k*x - Round 3: Compute δ_i, verify proofs, output presignature
Online Signing (1 round) - Use presignature to compute signature share - Combine shares for final signature
Identifiable Aborts:
from charm.schemes.threshold.cggmp21_sign import SecurityAbort
try:
signature = cggmp.sign(key_shares[:2], message, presigs)
except SecurityAbort as e:
print(f"Malicious party: {e.party_id}")
print(f"Evidence: {e.evidence}")
DKLS23 Protocol Details¶
Reference: Doerner et al. 2023
DKLS23 uses OT-based (Oblivious Transfer) MtA instead of Paillier encryption, providing efficient presigning with non-interactive online signing.
Key Features:
OT-based MtA: Uses Silent OT for efficient multiplication
Non-interactive Presigning: Presignatures can be computed independently
Fast Online Phase: Single round for signature generation
Protocol Phases:
Distributed Key Generation (3 rounds) - Similar to GG18 with Feldman VSS - Outputs key shares and public key
Presigning (3 rounds) - Uses OT extension for MtA protocol - Generates presignature (R, k-share, χ-share)
Online Signing (1 round) - Compute signature share from presignature - Combine for final ECDSA signature
API Reference¶
GG18¶
- class charm.schemes.threshold.GG18(group, threshold, num_parties)[source]¶
GG18 threshold ECDSA scheme.
- Parameters:
group – ECGroup instance (e.g., secp256k1)
threshold – Minimum parties required to sign (t)
num_parties – Total number of parties (n)
CGGMP21¶
- class charm.schemes.threshold.CGGMP21(group, threshold, num_parties)[source]¶
CGGMP21 UC-secure threshold ECDSA with identifiable aborts.
- Parameters:
group – ECGroup instance
threshold – Minimum parties required (t)
num_parties – Total parties (n)
- presign(key_shares)[source]¶
Generate presignatures (offline, 3 rounds).
- Parameters:
key_shares – List of t key shares
- Returns:
List of presignature objects
- sign(key_shares, message, presignatures=None)[source]¶
Generate signature. Uses presignatures if provided.
- Parameters:
key_shares – List of t key shares
message – Message bytes
presignatures – Optional presignatures from presign()
- Returns:
ThresholdSignature object
- Raises:
SecurityAbort – If malicious behavior detected
DKLS23¶
- class charm.schemes.threshold.DKLS23(group, threshold, num_parties)[source]¶
DKLS23 threshold ECDSA with OT-based MtA.
- Parameters:
group – ECGroup instance
threshold – Minimum parties required (t)
num_parties – Total parties (n)
References¶
GG18: R. Gennaro and S. Goldfeder, “Fast Multiparty Threshold ECDSA with Fast Trustless Setup,” ACM CCS 2018. ePrint 2019/114
CGGMP21: R. Canetti, R. Gennaro, S. Goldfeder, N. Makriyannis, and U. Peled, “UC Non-Interactive, Proactive, Threshold ECDSA with Identifiable Aborts,” ACM CCS 2020. ePrint 2021/060
DKLS23: J. Doerner, Y. Kondi, E. Lee, and A. Shelat, “Threshold ECDSA in Three Rounds,” IEEE S&P 2023. ePrint 2023/765
See Also¶
Implemented Schemes — All implemented cryptographic schemes
ZKP Compiler — ZKP compiler (used by CGGMP21)
examples/xrpl_memo_demo.py— XRPL testnet integration example