mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-12-29 10:05:16 +00:00
239 lines
6.2 KiB
Go
239 lines
6.2 KiB
Go
|
//
|
||
|
// Copyright Coinbase, Inc. All Rights Reserved.
|
||
|
//
|
||
|
// SPDX-License-Identifier: Apache-2.0
|
||
|
//
|
||
|
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"crypto/sha512"
|
||
|
"flag"
|
||
|
"fmt"
|
||
|
|
||
|
"filippo.io/edwards25519"
|
||
|
|
||
|
"source.quilibrium.com/quilibrium/monorepo/nekryptology/internal"
|
||
|
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
|
||
|
dkg "source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/dkg/gennaro"
|
||
|
v1 "source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/sharing/v1"
|
||
|
)
|
||
|
|
||
|
const LIMIT = 4
|
||
|
const THRESHOLD = 2
|
||
|
|
||
|
func main() {
|
||
|
var threshold int
|
||
|
var limit int
|
||
|
var help bool
|
||
|
flag.IntVar(&threshold, "t", THRESHOLD, "the minimum number of participants to sign")
|
||
|
flag.IntVar(&threshold, "threshold", THRESHOLD, "the minimum number of participants to sign")
|
||
|
flag.IntVar(&limit, "n", LIMIT, "the total number of participants")
|
||
|
flag.IntVar(&limit, "limit", LIMIT, "the total number of participants")
|
||
|
flag.BoolVar(&help, "h", false, "Print this menu")
|
||
|
flag.BoolVar(&help, "help", false, "Print this menu")
|
||
|
flag.Parse()
|
||
|
|
||
|
if help {
|
||
|
printHelp()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fmt.Printf("Threshold is %d\n", threshold)
|
||
|
fmt.Printf("Total participants is %d\n", limit)
|
||
|
|
||
|
// DEMO doing Gennaro DKG and that signers can compute a signature
|
||
|
participants := createDkgParticipants(threshold, limit)
|
||
|
|
||
|
// DKG Round 1
|
||
|
rnd1Bcast, rnd1P2p := round1(participants)
|
||
|
|
||
|
// DKG Round 2
|
||
|
rnd2Bcast := round2(participants, rnd1Bcast, rnd1P2p)
|
||
|
|
||
|
// DKG Round 3
|
||
|
verificationKey, signingShares := round3(participants, rnd2Bcast)
|
||
|
|
||
|
// Signing common setup for all participants
|
||
|
msg := []byte("All my bitcoin is stored here")
|
||
|
curve := v1.Ed25519()
|
||
|
scheme, _ := v1.NewShamir(threshold, limit, curves.NewField(curve.N))
|
||
|
shares := make([]*v1.ShamirShare, 0, threshold)
|
||
|
cnt := 0
|
||
|
for _, share := range signingShares {
|
||
|
if cnt == threshold {
|
||
|
break
|
||
|
}
|
||
|
cnt++
|
||
|
shares = append(shares, share)
|
||
|
}
|
||
|
sk, err := scheme.Combine(shares...)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
sk = internal.ReverseScalarBytes(sk)
|
||
|
var ska [32]byte
|
||
|
copy(ska[:], sk[:])
|
||
|
|
||
|
skC, err := edwards25519.NewScalar().SetCanonicalBytes(ska[:])
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
vk := edwards25519.NewIdentityPoint().ScalarBaseMult(skC)
|
||
|
vk2, err := internal.BigInt2Ed25519Point(verificationKey.Y)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
if vk.Equal(vk2) == 0 {
|
||
|
panic("not equal")
|
||
|
}
|
||
|
|
||
|
r, s := sign(skC, vk2.Bytes(), []byte("no nonce"), msg)
|
||
|
|
||
|
ok := verify(vk2.Bytes(), msg, r, s)
|
||
|
|
||
|
fmt.Printf("Signature verification - %v\n", ok)
|
||
|
}
|
||
|
|
||
|
func sign(skC *edwards25519.Scalar, pubKey, nonce, msg []byte) (*edwards25519.Point, *edwards25519.Scalar) {
|
||
|
h := sha512.New()
|
||
|
_, _ = h.Write(nonce)
|
||
|
_, _ = h.Write(msg)
|
||
|
digest := h.Sum(nil)
|
||
|
|
||
|
digestReduced, err := edwards25519.NewScalar().SetUniformBytes(digest)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
r := edwards25519.NewGeneratorPoint().ScalarBaseMult(digestReduced)
|
||
|
|
||
|
encodedR := r.Bytes()
|
||
|
h.Reset()
|
||
|
_, _ = h.Write(encodedR)
|
||
|
_, _ = h.Write(pubKey)
|
||
|
_, _ = h.Write(msg)
|
||
|
|
||
|
k, err := edwards25519.NewScalar().SetUniformBytes(h.Sum(nil))
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
s := edwards25519.NewScalar().MultiplyAdd(k, skC, digestReduced)
|
||
|
return r, s
|
||
|
}
|
||
|
|
||
|
func verify(pk, msg []byte, r *edwards25519.Point, s *edwards25519.Scalar) bool {
|
||
|
h := sha512.New()
|
||
|
_, _ = h.Write(r.Bytes())
|
||
|
_, _ = h.Write(pk)
|
||
|
_, _ = h.Write(msg)
|
||
|
k, err := edwards25519.NewScalar().SetUniformBytes(h.Sum(nil))
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
minusA, _ := edwards25519.NewIdentityPoint().SetBytes(pk)
|
||
|
minusA.Negate(minusA)
|
||
|
|
||
|
lhs := edwards25519.NewIdentityPoint().VarTimeDoubleScalarBaseMult(k, minusA, s)
|
||
|
|
||
|
return lhs.Equal(r) == 1
|
||
|
}
|
||
|
|
||
|
func round1(participants map[uint32]*dkg.Participant) (map[uint32]dkg.Round1Bcast, map[uint32]dkg.Round1P2PSend) {
|
||
|
// DKG Round 1
|
||
|
rnd1Bcast := make(map[uint32]dkg.Round1Bcast, len(participants))
|
||
|
rnd1P2p := make(map[uint32]dkg.Round1P2PSend, len(participants))
|
||
|
for id, p := range participants {
|
||
|
fmt.Printf("Computing DKG Round 1 for participant %d\n", id)
|
||
|
bcast, p2psend, err := p.Round1(nil)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
rnd1Bcast[id] = bcast
|
||
|
rnd1P2p[id] = p2psend
|
||
|
}
|
||
|
|
||
|
return rnd1Bcast, rnd1P2p
|
||
|
}
|
||
|
|
||
|
func round2(participants map[uint32]*dkg.Participant,
|
||
|
rnd1Bcast map[uint32]dkg.Round1Bcast,
|
||
|
rnd1P2p map[uint32]dkg.Round1P2PSend,
|
||
|
) map[uint32]dkg.Round2Bcast {
|
||
|
rnd2Bcast := make(map[uint32]dkg.Round2Bcast, len(participants))
|
||
|
for id := range rnd1Bcast {
|
||
|
fmt.Printf("Computing DKG Round 2 for participant %d\n", id)
|
||
|
|
||
|
rnd1P2pForP := make(map[uint32]*dkg.Round1P2PSendPacket)
|
||
|
for jid := range rnd1P2p {
|
||
|
if jid == id {
|
||
|
continue
|
||
|
}
|
||
|
rnd1P2pForP[jid] = rnd1P2p[jid][id]
|
||
|
}
|
||
|
bcast, err := participants[id].Round2(rnd1Bcast, rnd1P2pForP)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
rnd2Bcast[id] = bcast
|
||
|
}
|
||
|
|
||
|
return rnd2Bcast
|
||
|
}
|
||
|
|
||
|
func round3(participants map[uint32]*dkg.Participant, rnd2Bcast map[uint32]dkg.Round2Bcast) (*curves.EcPoint, map[uint32]*v1.ShamirShare) {
|
||
|
signingShares := make(map[uint32]*v1.ShamirShare, len(participants))
|
||
|
var verificationKey *curves.EcPoint
|
||
|
for id := range rnd2Bcast {
|
||
|
fmt.Printf("Computing DKG Round 3 for participant %d\n", id)
|
||
|
pk, sk, err := participants[id].Round3(rnd2Bcast)
|
||
|
verificationKey = pk
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
fmt.Printf("DKG completed for participant %d\n", id)
|
||
|
signingShares[id] = sk
|
||
|
}
|
||
|
return verificationKey, signingShares
|
||
|
}
|
||
|
|
||
|
func createDkgParticipants(thresh, limit int) map[uint32]*dkg.Participant {
|
||
|
curve := v1.Ed25519()
|
||
|
gx, gy := curve.Hash([]byte("Fair is foul, and foul is fair: Hover through the fog and filthy air."))
|
||
|
generator := &curves.EcPoint{
|
||
|
Curve: curve,
|
||
|
X: gx,
|
||
|
Y: gy,
|
||
|
}
|
||
|
participants := make(map[uint32]*dkg.Participant, limit)
|
||
|
for i := 1; i <= limit; i++ {
|
||
|
otherIds := make([]uint32, limit-1)
|
||
|
idx := 0
|
||
|
for j := 1; j <= limit; j++ {
|
||
|
if i == j {
|
||
|
continue
|
||
|
}
|
||
|
otherIds[idx] = uint32(j)
|
||
|
idx++
|
||
|
}
|
||
|
p, err := dkg.NewParticipant(uint32(i), uint32(thresh), generator, curves.NewEd25519Scalar(), otherIds...)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
participants[uint32(i)] = p
|
||
|
}
|
||
|
return participants
|
||
|
}
|
||
|
|
||
|
func printHelp() {
|
||
|
fmt.Printf(`
|
||
|
bls INPUT
|
||
|
Simulate a DKG using BLS keys
|
||
|
FLAGS:
|
||
|
-h, --help Show this help message and exit
|
||
|
-n, --limit The total number of participants
|
||
|
-t, --treshold The minimum number of participants needed to sign
|
||
|
`)
|
||
|
}
|