152 lines
4.0 KiB
Go
152 lines
4.0 KiB
Go
package ed25519consensus
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"crypto/rand"
|
|
"crypto/sha512"
|
|
|
|
"filippo.io/edwards25519"
|
|
)
|
|
|
|
// BatchVerifier accumulates batch entries with Add, before performing batch
|
|
// verification with Verify.
|
|
type BatchVerifier struct {
|
|
entries []entry
|
|
}
|
|
|
|
// entry represents a batch entry with the public key, signature and scalar
|
|
// which the caller wants to verify.
|
|
type entry struct {
|
|
good bool // good is true if the Add inputs were valid
|
|
pubkey [ed25519.PublicKeySize]byte
|
|
signature [ed25519.SignatureSize]byte
|
|
digest [64]byte
|
|
}
|
|
|
|
// NewBatchVerifier creates an empty BatchVerifier.
|
|
func NewBatchVerifier() BatchVerifier {
|
|
return BatchVerifier{
|
|
entries: []entry{},
|
|
}
|
|
}
|
|
|
|
// NewPreallocatedBatchVerifier creates a new BatchVerifier with
|
|
// a preallocated capacity. If you know the size of the batch you plan
|
|
// to create ahead of time, this can prevent needless memory copies.
|
|
func NewPreallocatedBatchVerifier(size int) BatchVerifier {
|
|
return BatchVerifier{
|
|
entries: make([]entry, 0, size),
|
|
}
|
|
}
|
|
|
|
// Add adds a (public key, message, sig) triple to the current batch. It retains
|
|
// no reference to the inputs.
|
|
func (v *BatchVerifier) Add(publicKey ed25519.PublicKey, message, sig []byte) {
|
|
// Compute the challenge upfront to store it in the fixed-size entry
|
|
// structure that can get allocated on the caller stack and avoid heap
|
|
// allocations. Also, avoid holding any reference to the arguments.
|
|
|
|
v.entries = append(v.entries, entry{})
|
|
e := &v.entries[len(v.entries)-1]
|
|
|
|
if len(publicKey) != ed25519.PublicKeySize || len(sig) != ed25519.SignatureSize {
|
|
return
|
|
}
|
|
|
|
h := sha512.New()
|
|
h.Write(sig[:32])
|
|
h.Write(publicKey)
|
|
h.Write(message)
|
|
h.Sum(e.digest[:0])
|
|
|
|
copy(e.pubkey[:], publicKey)
|
|
copy(e.signature[:], sig)
|
|
|
|
e.good = true
|
|
}
|
|
|
|
// Verify checks all entries in the current batch, returning true if all entries
|
|
// are valid and false if any one entry is invalid.
|
|
//
|
|
// If a failure arises it is unknown which entry failed, the caller must verify
|
|
// each entry individually.
|
|
//
|
|
// Calling Verify on an empty batch returns false.
|
|
func (v *BatchVerifier) Verify() bool {
|
|
vl := len(v.entries)
|
|
// Abort early on an empty batch, which probably indicates a bug
|
|
if vl == 0 {
|
|
return false
|
|
}
|
|
|
|
// The batch verification equation is
|
|
//
|
|
// [-sum(z_i * s_i)]B + sum([z_i]R_i) + sum([z_i * k_i]A_i) = 0.
|
|
// where for each signature i,
|
|
// - A_i is the verification key;
|
|
// - R_i is the signature's R value;
|
|
// - s_i is the signature's s value;
|
|
// - k_i is the hash of the message and other data;
|
|
// - z_i is a random 128-bit Scalar.
|
|
svals := make([]edwards25519.Scalar, 1+vl+vl)
|
|
scalars := make([]*edwards25519.Scalar, 1+vl+vl)
|
|
|
|
// Populate scalars variable with concrete scalars to reduce heap allocation
|
|
for i := range scalars {
|
|
scalars[i] = &svals[i]
|
|
}
|
|
|
|
Bcoeff := scalars[0]
|
|
Rcoeffs := scalars[1 : 1+vl]
|
|
Acoeffs := scalars[1+vl:]
|
|
|
|
pvals := make([]edwards25519.Point, 1+vl+vl)
|
|
points := make([]*edwards25519.Point, 1+vl+vl)
|
|
for i := range points {
|
|
points[i] = &pvals[i]
|
|
}
|
|
B := points[0]
|
|
Rs := points[1 : 1+vl]
|
|
As := points[1+vl:]
|
|
|
|
buf := make([]byte, 32)
|
|
B.Set(edwards25519.NewGeneratorPoint())
|
|
for i, entry := range v.entries {
|
|
if !entry.good {
|
|
return false
|
|
}
|
|
|
|
if _, err := Rs[i].SetBytes(entry.signature[:32]); err != nil {
|
|
return false
|
|
}
|
|
|
|
if _, err := As[i].SetBytes(entry.pubkey[:]); err != nil {
|
|
return false
|
|
}
|
|
|
|
if _, err := rand.Read(buf[:16]); err != nil {
|
|
return false
|
|
}
|
|
if _, err := Rcoeffs[i].SetCanonicalBytes(buf); err != nil {
|
|
return false
|
|
}
|
|
|
|
s, err := new(edwards25519.Scalar).SetCanonicalBytes(entry.signature[32:])
|
|
if err != nil {
|
|
return false
|
|
}
|
|
Bcoeff.MultiplyAdd(Rcoeffs[i], s, Bcoeff)
|
|
|
|
k, err := new(edwards25519.Scalar).SetUniformBytes(entry.digest[:])
|
|
if err != nil {
|
|
return false
|
|
}
|
|
Acoeffs[i].Multiply(Rcoeffs[i], k)
|
|
}
|
|
Bcoeff.Negate(Bcoeff) // this term is subtracted in the summation
|
|
|
|
check := new(edwards25519.Point).VarTimeMultiScalarMult(scalars, points)
|
|
check.MultByCofactor(check)
|
|
return check.Equal(edwards25519.NewIdentityPoint()) == 1
|
|
}
|