@substrate-system/cggmp
v0.0.0
Published
Distributed key generation
Maintainers
Readme
cggmp
CGGMP
CGGMP (Canetti, Gennaro, Goldfeder, Makriyannis, Peled) distributed key generation protocol
CGGMP is a threshold signature protocol. Multiple parties are able to collaboratively generate a shared cryptographic key, and no single party ever sees the full private key.
3 rounds:
- Round 1: Each party generates a random polynomial, creates Feldman VSS commitments, generates a Paillier keypair for homomorphic encryption, and distributes encrypted shares to other parties along with zero-knowledge proofs.
- Round 2: Parties verify received shares against VSS commitments and open their Round 1 commitments to ensure all data was committed before shares were revealed
- Round 3: A complaint round where parties can identify and disqualify malicious participants who provided invalid shares
Upon completion, each party holds a secret share and all parties agree on the same public key. The secret shares can later be used in the CGGMP signing protocol to produce threshold signatures without reconstructing the private key.
install
npm i -S @substrate-system/cggmpAPI
This exposes ESM and common JS via
package.json exports field.
ESM
import '@substrate-system/cggmp'Common JS
require('@substrate-system/cggmp')use
JS
import '@substrate-system/cggmp'pre-built JS
This package exposes minified JS files too. Copy them to a location that is accessible to your web server, then link to them in HTML.
copy
cp ./node_modules/@substrate-system/cggmp/dist/index.min.js ./public/cggmp.jsHTML
<script type="module" src="./cggmp.min.js"></script>Example
Distributed Key Generation
Generate a threshold key where any 3 of 5 parties can sign. All parties are running locally for example purposes.
import { DKGCoordinator } from '@substrate-system/cggmp'
// Create a coordinator for 5 parties with threshold 3
const n = 5 // total parties
const t = 3 // threshold (minimum parties needed to sign)
const coordinator = await DKGCoordinator.init(n, t)
const outputs = coordinator.output!
// Each party now has:
// - A secret share
// - The shared public key (same for all parties)
// - Public key shares for all parties
// - Paillier encryption keys for all parties
for (const [partyId, output] of outputs) {
console.log(`Party ${partyId}:`)
console.log(` Secret share: ${output.secretShare}`)
console.log(` Public key: ${Buffer.from(output.publicKey).toString('hex')}`)
console.log(` Has ${output.paillierKeys.size} Paillier keys`)
}
// All parties computed the same public key
const [output1, output2] = Array.from(outputs.values())
const samePublicKey = Buffer.from(output1.publicKey)
.equals(Buffer.from(output2.publicKey))
console.log(`All parties agree on public key: ${samePublicKey}`)Distributed Setup
In real life, each party runs on a separate machine.
[!IMPORTANT]
Parties must coordinate to establish their IDs.
import { DKGParty } from '@substrate-system/cggmp'
// Each party creates their own DKGParty instance
const party = new DKGParty({
n: 5,
t: 3,
partyId: 1, // This party's ID (1-5) (should coordinate with other parties)
paillierBits: 2048
})
// Round 1: Generate and broadcast commitments
const { broadcast, p2pMessages } = await party.round1()
// Send broadcast to all parties via your network layer
await network.broadcast(broadcast)
// Send P2P messages to specific parties
for (const msg of p2pMessages) {
await network.sendTo(msg.toParty, msg)
}
// Receive and process broadcasts from other parties
for (const otherBroadcast of await network.receiveBroadcasts()) {
const success = party.processRound1Broadcast(otherBroadcast)
if (!success) {
throw new Error('Invalid broadcast received')
}
}
// Receive and process P2P messages
for (const p2pMsg of await network.receiveP2PMessages()) {
party.processRound1P2P(p2pMsg)
}
// Round 2: Verify shares and open commitments
const round2Broadcast = party.round2()
await network.broadcast(round2Broadcast)
// Process Round 2 broadcasts...
for (const otherBroadcast of await network.receiveBroadcasts()) {
party.processRound2Broadcast(otherBroadcast)
}
// Round 3: Complaints (if any)
const round3Broadcast = party.round3()
await network.broadcast(round3Broadcast)
// Finalize and get output
const output = party.finalize()
// Now this party has:
// - output.secretShare: This party's secret share (keep private!)
// - output.publicKey: The shared public key
// - output.paillierKeys: Paillier keys for all partiesThreshold Signing
Threshold signing is not implemented in this library.
To sign, any t parties (3 in the example above) could cooperate to
sign messages without reconstructing the full private key. Each party would use
their secretShare from the DKG output to participate in the signing protocol.
