ceremonyclient/feldman/feldman.go
2023-04-15 00:05:26 -04:00

541 lines
16 KiB
Go

package feldman
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"fmt"
"source.quilibrium.com/quilibrium/ceremonyclient/ec/bls48581"
)
// This will not be used in the initial ceremony, but will be used in a end of ceremony event
type FeldmanECP struct {
threshold int
total int
id int
fragsForCounterparties map[int][]byte
fragsFromCounterparties map[int]*bls48581.BIG
zkpok *bls48581.BIG
secret *bls48581.BIG
scalar *bls48581.BIG
publicKey *bls48581.ECP
point *bls48581.ECP
randomCommitmentPoint *bls48581.ECP
round FeldmanRound
zkcommitsFromCounterparties map[int][]byte
pointsFromCounterparties map[int]*bls48581.ECP
}
type FeldmanReveal struct {
Point []byte
RandomCommitmentPoint []byte
ZKPoK []byte
}
type FeldmanECP8 struct {
threshold int
total int
id int
fragsForCounterparties map[int][]byte
fragsFromCounterparties map[int]*bls48581.BIG
zkpok *bls48581.BIG
secret *bls48581.BIG
scalar *bls48581.BIG
publicKey *bls48581.ECP8
point *bls48581.ECP8
randomCommitmentPoint *bls48581.ECP8
round FeldmanRound
zkcommitsFromCounterparties map[int][]byte
pointsFromCounterparties map[int]*bls48581.ECP8
}
type FeldmanRound int
const (
FELDMAN_ROUND_UNINITIALIZED = FeldmanRound(0)
FELDMAN_ROUND_INITIALIZED = FeldmanRound(1)
FELDMAN_ROUND_COMMITTED = FeldmanRound(2)
FELDMAN_ROUND_REVEALED = FeldmanRound(3)
FELDMAN_ROUND_RECONSTRUCTED = FeldmanRound(4)
)
func NewFeldmanECP(threshold, total, id int, secret *bls48581.BIG) (*FeldmanECP, error) {
return &FeldmanECP{
threshold: threshold,
total: total,
id: id,
fragsForCounterparties: make(map[int][]byte),
fragsFromCounterparties: make(map[int]*bls48581.BIG),
zkpok: nil,
secret: secret,
scalar: nil,
publicKey: bls48581.ECP_generator(),
point: bls48581.ECP_generator(),
round: FELDMAN_ROUND_UNINITIALIZED,
zkcommitsFromCounterparties: make(map[int][]byte),
pointsFromCounterparties: make(map[int]*bls48581.ECP),
}, nil
}
func NewFeldmanECP8(threshold, total, id int, secret *bls48581.BIG) (*FeldmanECP8, error) {
return &FeldmanECP8{
threshold: threshold,
total: total,
id: id,
fragsForCounterparties: make(map[int][]byte),
fragsFromCounterparties: make(map[int]*bls48581.BIG),
zkpok: nil,
secret: secret,
scalar: nil,
publicKey: bls48581.ECP8_generator(),
point: bls48581.ECP8_generator(),
round: FELDMAN_ROUND_UNINITIALIZED,
zkcommitsFromCounterparties: make(map[int][]byte),
pointsFromCounterparties: make(map[int]*bls48581.ECP8),
}, nil
}
func (f *FeldmanECP) SamplePolynomial() {
coeffs := append([]*bls48581.BIG{}, f.secret)
for i := 0; i < f.threshold-1; i++ {
secretBytes := make([]byte, int(bls48581.MODBYTES))
rand.Read(secretBytes)
secret := bls48581.FromBytes(secretBytes)
coeffs = append(coeffs, secret)
}
for i := 1; i <= f.total; i++ {
result := coeffs[len(coeffs)-1]
for j := len(coeffs) - 2; j >= 0; j-- {
result = bls48581.Modadd(
coeffs[j],
bls48581.Modmul(
result,
bls48581.NewBIGint(i),
bls48581.NewBIGints(bls48581.CURVE_Order),
),
bls48581.NewBIGints(bls48581.CURVE_Order),
)
}
if i == f.id {
f.scalar = result
} else {
fragBytes := make([]byte, int(bls48581.MODBYTES))
result.ToBytes(fragBytes)
f.fragsForCounterparties[i] = fragBytes
}
}
f.round = FELDMAN_ROUND_INITIALIZED
}
func (f *FeldmanECP) Scalar() *bls48581.BIG {
return f.scalar
}
func (f *FeldmanECP) GetPolyFrags() map[int][]byte {
return f.fragsForCounterparties
}
func (f *FeldmanECP) SetPolyFragForParty(id int, frag []byte) []byte {
f.fragsFromCounterparties[id] = bls48581.FromBytes(frag)
if len(f.fragsFromCounterparties) == f.total-1 {
for _, v := range f.fragsFromCounterparties {
f.scalar = bls48581.Modadd(f.scalar, v, bls48581.NewBIGints(bls48581.CURVE_Order))
}
f.point = f.point.Mul(f.scalar)
randCommitmentBytes := make([]byte, int(bls48581.MODBYTES))
rand.Read(randCommitmentBytes)
randCommitment := bls48581.FromBytes(randCommitmentBytes)
f.randomCommitmentPoint = f.publicKey.Mul(randCommitment)
publicPointBytes := make([]byte, bls48581.MODBYTES+1)
randCommitmentPointBytes := make([]byte, bls48581.MODBYTES+1)
f.point.ToBytes(publicPointBytes, true)
f.randomCommitmentPoint.ToBytes(randCommitmentPointBytes, true)
challenge := sha256.Sum256(append(append([]byte{}, publicPointBytes...), randCommitmentPointBytes...))
challengeBig := bls48581.FromBytes(challenge[:])
challengeBig.Mod(bls48581.NewBIGints(bls48581.CURVE_Order))
f.zkpok = bls48581.Modadd(
bls48581.Modmul(
f.scalar,
challengeBig,
bls48581.NewBIGints(bls48581.CURVE_Order),
),
randCommitment,
bls48581.NewBIGints(bls48581.CURVE_Order),
)
zkpokBytes := make([]byte, int(bls48581.MODBYTES))
f.zkpok.ToBytes(zkpokBytes)
zkcommit := sha256.Sum256(append(append([]byte{}, randCommitmentPointBytes...), zkpokBytes...))
f.round = FELDMAN_ROUND_COMMITTED
return zkcommit[:]
}
return []byte{}
}
func (f *FeldmanECP) ReceiveCommitments(id int, zkcommit []byte) *FeldmanReveal {
f.zkcommitsFromCounterparties[id] = zkcommit
if len(f.zkcommitsFromCounterparties) == f.total-1 {
publicPointBytes := make([]byte, bls48581.MODBYTES+1)
randCommitmentPointBytes := make([]byte, bls48581.MODBYTES+1)
f.point.ToBytes(publicPointBytes, true)
f.randomCommitmentPoint.ToBytes(randCommitmentPointBytes, true)
f.round = FELDMAN_ROUND_REVEALED
zkpokBytes := make([]byte, int(bls48581.MODBYTES))
f.zkpok.ToBytes(zkpokBytes)
return &FeldmanReveal{
Point: publicPointBytes,
RandomCommitmentPoint: randCommitmentPointBytes,
ZKPoK: zkpokBytes,
}
}
return nil
}
func (f *FeldmanECP) Recombine(id int, reveal *FeldmanReveal) {
counterpartyPoint := bls48581.ECP_fromBytes(reveal.Point)
if counterpartyPoint.Equals(bls48581.ECP_generator()) {
fmt.Printf("invalid point from %d", id)
return
}
counterpartyRandomCommitmentPoint := bls48581.ECP_fromBytes(reveal.RandomCommitmentPoint)
if counterpartyRandomCommitmentPoint.Equals(bls48581.ECP_generator()) {
fmt.Printf("invalid commitment point from %d", id)
return
}
counterpartyZKPoK := bls48581.FromBytes(reveal.ZKPoK)
counterpartyZKCommit := f.zkcommitsFromCounterparties[id]
challenge := sha256.Sum256(append(append([]byte{}, reveal.Point...), reveal.RandomCommitmentPoint...))
challengeBig := bls48581.FromBytes(challenge[:])
challengeBig.Mod(bls48581.NewBIGints(bls48581.CURVE_Order))
proof := f.publicKey.Mul(counterpartyZKPoK)
counterpartyRandomCommitmentPoint.Add(counterpartyPoint.Mul(challengeBig))
if !proof.Equals(counterpartyRandomCommitmentPoint) {
fmt.Printf("invalid proof from %d", id)
return
}
verifier := sha256.Sum256(append(append([]byte{}, reveal.RandomCommitmentPoint...), reveal.ZKPoK...))
if !bytes.Equal(counterpartyZKCommit, verifier[:]) {
fmt.Printf("%d changed zkpok after commit", id)
return
}
f.pointsFromCounterparties[id] = counterpartyPoint
if len(f.pointsFromCounterparties) == f.total-1 {
f.pointsFromCounterparties[f.id] = f.point
for i := 1; i <= f.total-f.threshold; i++ {
reconstructedSum := bls48581.ECP_generator()
for j := i; j <= f.threshold+i; j++ {
coefficientNumerator := bls48581.NewBIGint(1)
coefficientDenominator := bls48581.NewBIGint(1)
for k := i; k <= f.threshold+i; k++ {
if j != k {
k := bls48581.NewBIGint(k)
coefficientNumerator = bls48581.Modmul(
coefficientNumerator,
k,
bls48581.NewBIGints(bls48581.CURVE_Order),
)
kj := bls48581.Modadd(
k,
bls48581.NewBIGints(bls48581.CURVE_Order).Minus(bls48581.NewBIGint(j)),
bls48581.NewBIGints(bls48581.CURVE_Order),
)
coefficientDenominator = bls48581.Modmul(
coefficientDenominator,
kj,
bls48581.NewBIGints(bls48581.CURVE_Order),
)
}
}
coefficientDenominator.Invmodp(bls48581.NewBIGints(bls48581.CURVE_Order))
reconstructedFragment := f.pointsFromCounterparties[j].Mul(
bls48581.Modmul(
coefficientNumerator,
coefficientDenominator,
bls48581.NewBIGints(bls48581.CURVE_Order),
),
)
if reconstructedSum.Equals(bls48581.ECP_generator()) {
reconstructedSum = reconstructedFragment
} else {
reconstructedSum.Add(reconstructedFragment)
}
}
if f.publicKey.Equals(bls48581.ECP_generator()) {
f.publicKey = reconstructedSum
} else if !f.publicKey.Equals(reconstructedSum) {
fmt.Println("key mismatch")
fmt.Println(f.publicKey.ToString())
fmt.Println(reconstructedSum.ToString())
return
}
}
f.round = FELDMAN_ROUND_RECONSTRUCTED
}
}
func (f *FeldmanECP) PublicKey() *bls48581.ECP {
key := bls48581.NewECP()
key.Copy(f.publicKey)
return key
}
func (f *FeldmanECP) PublicKeyBytes() []byte {
publicKeyBytes := make([]byte, bls48581.MODBYTES+1)
f.publicKey.ToBytes(publicKeyBytes, true)
return publicKeyBytes
}
func (f *FeldmanECP8) SamplePolynomial() {
coeffs := append([]*bls48581.BIG{}, f.secret)
for i := 0; i < f.threshold-1; i++ {
secretBytes := make([]byte, int(bls48581.MODBYTES))
rand.Read(secretBytes)
secret := bls48581.FromBytes(secretBytes)
coeffs = append(coeffs, secret)
}
for i := 1; i <= f.total; i++ {
result := coeffs[len(coeffs)-1]
for j := len(coeffs) - 2; j >= 0; j-- {
result = bls48581.Modadd(
coeffs[j],
bls48581.Modmul(
result,
bls48581.NewBIGint(i),
bls48581.NewBIGints(bls48581.CURVE_Order),
),
bls48581.NewBIGints(bls48581.CURVE_Order),
)
}
if i == f.id {
f.scalar = result
} else {
fragBytes := make([]byte, int(bls48581.MODBYTES))
result.ToBytes(fragBytes)
f.fragsForCounterparties[i] = fragBytes
}
}
f.round = FELDMAN_ROUND_INITIALIZED
}
func (f *FeldmanECP8) GetPolyFrags() map[int][]byte {
return f.fragsForCounterparties
}
func (f *FeldmanECP8) SetPolyFragForParty(id int, frag []byte) []byte {
f.fragsFromCounterparties[id] = bls48581.FromBytes(frag)
if len(f.fragsFromCounterparties) == f.total-1 {
for _, v := range f.fragsFromCounterparties {
f.scalar = bls48581.Modadd(f.scalar, v, bls48581.NewBIGints(bls48581.CURVE_Order))
}
f.point = f.point.Mul(f.scalar)
randCommitmentBytes := make([]byte, int(bls48581.MODBYTES))
rand.Read(randCommitmentBytes)
randCommitment := bls48581.FromBytes(randCommitmentBytes)
f.randomCommitmentPoint = f.publicKey.Mul(randCommitment)
publicPointBytes := make([]byte, bls48581.MODBYTES*8+1)
randCommitmentPointBytes := make([]byte, bls48581.MODBYTES*8+1)
f.point.ToBytes(publicPointBytes, true)
f.randomCommitmentPoint.ToBytes(randCommitmentPointBytes, true)
challenge := sha256.Sum256(append(append([]byte{}, publicPointBytes...), randCommitmentPointBytes...))
challengeBig := bls48581.FromBytes(challenge[:])
challengeBig.Mod(bls48581.NewBIGints(bls48581.CURVE_Order))
f.zkpok = bls48581.Modadd(
bls48581.Modmul(
f.scalar,
challengeBig,
bls48581.NewBIGints(bls48581.CURVE_Order),
),
randCommitment,
bls48581.NewBIGints(bls48581.CURVE_Order),
)
zkpokBytes := make([]byte, int(bls48581.MODBYTES))
f.zkpok.ToBytes(zkpokBytes)
zkcommit := sha256.Sum256(append(append([]byte{}, randCommitmentPointBytes...), zkpokBytes...))
f.round = FELDMAN_ROUND_COMMITTED
return zkcommit[:]
}
return []byte{}
}
func (f *FeldmanECP8) Scalar() *bls48581.BIG {
return f.scalar
}
func (f *FeldmanECP8) ReceiveCommitments(id int, zkcommit []byte) *FeldmanReveal {
f.zkcommitsFromCounterparties[id] = zkcommit
if len(f.zkcommitsFromCounterparties) == f.total-1 {
publicPointBytes := make([]byte, bls48581.MODBYTES*8+1)
randCommitmentPointBytes := make([]byte, bls48581.MODBYTES*8+1)
f.point.ToBytes(publicPointBytes, true)
f.randomCommitmentPoint.ToBytes(randCommitmentPointBytes, true)
f.round = FELDMAN_ROUND_REVEALED
zkpokBytes := make([]byte, int(bls48581.MODBYTES))
f.zkpok.ToBytes(zkpokBytes)
return &FeldmanReveal{
Point: publicPointBytes,
RandomCommitmentPoint: randCommitmentPointBytes,
ZKPoK: zkpokBytes,
}
}
return nil
}
func (f *FeldmanECP8) Recombine(id int, reveal *FeldmanReveal) {
counterpartyPoint := bls48581.ECP8_fromBytes(reveal.Point)
if counterpartyPoint.Equals(bls48581.ECP8_generator()) {
fmt.Printf("invalid point from %d", id)
return
}
counterpartyRandomCommitmentPoint := bls48581.ECP8_fromBytes(reveal.RandomCommitmentPoint)
if counterpartyRandomCommitmentPoint.Equals(bls48581.ECP8_generator()) {
fmt.Printf("invalid commitment point from %d", id)
return
}
counterpartyZKPoK := bls48581.FromBytes(reveal.ZKPoK)
counterpartyZKCommit := f.zkcommitsFromCounterparties[id]
challenge := sha256.Sum256(append(append([]byte{}, reveal.Point...), reveal.RandomCommitmentPoint...))
challengeBig := bls48581.FromBytes(challenge[:])
challengeBig.Mod(bls48581.NewBIGints(bls48581.CURVE_Order))
proof := f.publicKey.Mul(counterpartyZKPoK)
counterpartyRandomCommitmentPoint.Add(counterpartyPoint.Mul(challengeBig))
if !proof.Equals(counterpartyRandomCommitmentPoint) {
fmt.Printf("invalid proof from %d", id)
return
}
verifier := sha256.Sum256(append(append([]byte{}, reveal.RandomCommitmentPoint...), reveal.ZKPoK...))
if !bytes.Equal(counterpartyZKCommit, verifier[:]) {
fmt.Printf("%d changed zkpok after commit", id)
return
}
f.pointsFromCounterparties[id] = counterpartyPoint
if len(f.pointsFromCounterparties) == f.total-1 {
f.pointsFromCounterparties[f.id] = f.point
for i := 1; i <= f.total-f.threshold; i++ {
reconstructedSum := bls48581.ECP8_generator()
for j := i; j <= f.threshold+i; j++ {
coefficientNumerator := bls48581.NewBIGint(1)
coefficientDenominator := bls48581.NewBIGint(1)
for k := i; k <= f.threshold+i; k++ {
if j != k {
k := bls48581.NewBIGint(k)
coefficientNumerator = bls48581.Modmul(
coefficientNumerator,
k,
bls48581.NewBIGints(bls48581.CURVE_Order),
)
kj := bls48581.Modadd(
k,
bls48581.NewBIGints(bls48581.CURVE_Order).Minus(bls48581.NewBIGint(j)),
bls48581.NewBIGints(bls48581.CURVE_Order),
)
coefficientDenominator = bls48581.Modmul(
coefficientDenominator,
kj,
bls48581.NewBIGints(bls48581.CURVE_Order),
)
}
}
coefficientDenominator.Invmodp(bls48581.NewBIGints(bls48581.CURVE_Order))
reconstructedFragment := f.pointsFromCounterparties[j].Mul(
bls48581.Modmul(
coefficientNumerator,
coefficientDenominator,
bls48581.NewBIGints(bls48581.CURVE_Order),
),
)
if reconstructedSum.Equals(bls48581.ECP8_generator()) {
reconstructedSum = reconstructedFragment
} else {
reconstructedSum.Add(reconstructedFragment)
}
}
if f.publicKey.Equals(bls48581.ECP8_generator()) {
f.publicKey = reconstructedSum
} else if !f.publicKey.Equals(reconstructedSum) {
fmt.Println("key mismatch")
fmt.Println(f.publicKey.ToString())
fmt.Println(reconstructedSum.ToString())
return
}
}
f.round = FELDMAN_ROUND_RECONSTRUCTED
}
}
func (f *FeldmanECP8) PublicKey() *bls48581.ECP8 {
key := bls48581.NewECP8()
key.Copy(f.publicKey)
return key
}
func (f *FeldmanECP8) PublicKeyBytes() []byte {
publicKeyBytes := make([]byte, bls48581.MODBYTES*8+1)
f.publicKey.ToBytes(publicKeyBytes, true)
return publicKeyBytes
}