diff --git a/crypto/batchverifier.go b/crypto/batchverifier.go
index 65b2febeaa..cd0168ae1f 100644
--- a/crypto/batchverifier.go
+++ b/crypto/batchverifier.go
@@ -73,16 +73,35 @@ func ed25519_randombytes_unsafe(p unsafe.Pointer, len C.size_t) {
const minBatchVerifierAlloc = 16
const useSingleVerifierDefault = true
-// MakeBatchVerifier creates a BatchVerifier instance with the provided options.
+// ed25519BatchVerifierFactory is the global singleton used for batch signature verification.
+// By default it uses the libsodium implementation. This can be changed during initialization
+// (e.g., by the config package when algod loads) to use the ed25519consensus implementation.
+var ed25519BatchVerifierFactory func(hint int) BatchVerifier = makeLibsodiumBatchVerifier
+
+// SetEd25519BatchVerifier allows the config package to switch the implementation
+// at startup based on configuration. Pass true to use ed25519consensus, false for libsodium.
+func SetEd25519BatchVerifier(useEd25519Consensus bool) {
+ if useEd25519Consensus {
+ ed25519BatchVerifierFactory = makeEd25519ConsensusBatchVerifier
+ } else {
+ ed25519BatchVerifierFactory = makeLibsodiumBatchVerifier
+ }
+}
+
+// MakeBatchVerifier creates a BatchVerifier instance.
func MakeBatchVerifier() BatchVerifier {
- return MakeBatchVerifierWithHint(minBatchVerifierAlloc)
+ return ed25519BatchVerifierFactory(minBatchVerifierAlloc)
}
-// MakeBatchVerifierWithHint creates a cgoBatchVerifier instance. This function pre-allocates
-// amount of free space to enqueue signatures without expanding
+// MakeBatchVerifierWithHint creates a BatchVerifier instance. This function pre-allocates
+// space to enqueue signatures without expanding.
func MakeBatchVerifierWithHint(hint int) BatchVerifier {
+ return ed25519BatchVerifierFactory(hint)
+}
+
+func makeLibsodiumBatchVerifier(hint int) BatchVerifier {
// preallocate enough storage for the expected usage. We will reallocate as needed.
- if hint < minBatchVerifierAlloc {
+ if hint <= 0 {
hint = minBatchVerifierAlloc
}
return &cgoBatchVerifier{
@@ -152,7 +171,7 @@ func (b *cgoBatchVerifier) VerifyWithFeedback() (failed []bool, err error) {
}
allValid, failed := cgoBatchVerificationImpl(messages, msgLengths, b.publicKeys, b.signatures)
if allValid {
- return failed, nil
+ return nil, nil
}
return failed, ErrBatchHasFailedSigs
}
@@ -170,7 +189,7 @@ func (b *cgoBatchVerifier) singleVerify() (failed []bool, err error) {
if containsFailed {
return failed, ErrBatchHasFailedSigs
}
- return failed, nil
+ return nil, nil
}
// cgoBatchVerificationImpl invokes the ed25519 batch verification algorithm.
@@ -185,18 +204,26 @@ func cgoBatchVerificationImpl(messages []byte, msgLengths []uint64, publicKeys [
signatures2D := make([]*C.uchar, numberOfSignatures)
// call the batch verifier
+ // Use unsafe.SliceData to safely get pointers to underlying arrays
allValid := C.ed25519_batch_wrapper(
- &messages2D[0], &publicKeys2D[0], &signatures2D[0],
- (*C.uchar)(&messages[0]),
- (*C.ulonglong)(&msgLengths[0]),
- (*C.uchar)(&publicKeys[0][0]),
- (*C.uchar)(&signatures[0][0]),
+ (**C.uchar)(unsafe.SliceData(messages2D)),
+ (**C.uchar)(unsafe.SliceData(publicKeys2D)),
+ (**C.uchar)(unsafe.SliceData(signatures2D)),
+ (*C.uchar)(unsafe.SliceData(messages)),
+ (*C.ulonglong)(unsafe.SliceData(msgLengths)),
+ (*C.uchar)(unsafe.SliceData(publicKeys[0][:])),
+ (*C.uchar)(unsafe.SliceData(signatures[0][:])),
C.size_t(numberOfSignatures),
- (*C.int)(&valid[0]))
+ (*C.int)(unsafe.SliceData(valid)))
+
+ if allValid == 0 { // all signatures valid
+ return true, nil
+ }
+ // not all signatures valid, identify the failed signatures
failed = make([]bool, numberOfSignatures)
for i := 0; i < numberOfSignatures; i++ {
failed[i] = (valid[i] == 0)
}
- return allValid == 0, failed
+ return false, failed
}
diff --git a/crypto/batchverifier_bench_test.go b/crypto/batchverifier_bench_test.go
new file mode 100644
index 0000000000..c676a5265e
--- /dev/null
+++ b/crypto/batchverifier_bench_test.go
@@ -0,0 +1,123 @@
+// Copyright (C) 2019-2025 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see .
+
+package crypto
+
+import (
+ cryptorand "crypto/rand"
+ "io"
+ "testing"
+
+ "github.com/algorand/go-algorand/test/partitiontest"
+ "github.com/stretchr/testify/require"
+)
+
+func randSignedMsg(t testing.TB, r io.Reader) (SignatureVerifier, Hashable, Signature) {
+ mlen := 100
+ msg := TestingHashable{data: make([]byte, mlen)}
+ n, err := r.Read(msg.data)
+ require.NoError(t, err)
+ require.Equal(t, n, mlen)
+ var s Seed
+ n, err = r.Read(s[:])
+ require.NoError(t, err)
+ require.Equal(t, 32, n)
+ secrets := GenerateSignatureSecrets(s)
+ return secrets.SignatureVerifier, msg, secrets.Sign(msg)
+}
+
+// BenchmarkBatchVerifierImpls benchmarks different batch verification implementations
+// with realistic batch sizes (100 batches of 64 signatures each)
+func BenchmarkBatchVerifierImpls(b *testing.B) {
+ partitiontest.PartitionTest(b)
+
+ numBatches := 100
+ batchSize := 64
+ msgs := make([][]Hashable, numBatches)
+ pks := make([][]SignatureVerifier, numBatches)
+ sigs := make([][]Signature, numBatches)
+ r := cryptorand.Reader
+ for i := 0; i < numBatches; i++ {
+ for j := 0; j < batchSize; j++ {
+ pk, msg, sig := randSignedMsg(b, r)
+ msgs[i] = append(msgs[i], msg)
+ pks[i] = append(pks[i], pk)
+ sigs[i] = append(sigs[i], sig)
+ }
+ }
+
+ b.Log("running with", b.N, "iterations using", len(msgs), "batches of", batchSize, "signatures")
+ runImpl := func(b *testing.B, bv BatchVerifier,
+ msgs [][]Hashable, pks [][]SignatureVerifier, sigs [][]Signature) {
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ batchIdx := i % numBatches
+ for j := range msgs[batchIdx] {
+ bv.EnqueueSignature(pks[batchIdx][j], msgs[batchIdx][j], sigs[batchIdx][j])
+ }
+ require.NoError(b, bv.Verify())
+ }
+ }
+
+ b.Run("libsodium_single", func(b *testing.B) {
+ bv := makeLibsodiumBatchVerifier(batchSize)
+ bv.(*cgoBatchVerifier).useSingle = true
+ runImpl(b, bv, msgs, pks, sigs)
+ })
+ b.Run("libsodium_batch", func(b *testing.B) {
+ bv := makeLibsodiumBatchVerifier(batchSize)
+ bv.(*cgoBatchVerifier).useSingle = false
+ runImpl(b, bv, msgs, pks, sigs)
+ })
+ b.Run("ed25519consensus", func(b *testing.B) {
+ bv := makeEd25519ConsensusBatchVerifier(batchSize)
+ runImpl(b, bv, msgs, pks, sigs)
+ })
+}
+
+func BenchmarkCanonicalityCheck(b *testing.B) {
+ partitiontest.PartitionTest(b)
+
+ const maxN = 10000
+ pubkeys := make([]SignatureVerifier, maxN)
+ sigs := make([]Signature, maxN)
+ for i := 0; i < maxN; i++ {
+ var s Seed
+ RandBytes(s[:])
+ sigSecrets := GenerateSignatureSecrets(s)
+ pubkeys[i] = sigSecrets.SignatureVerifier
+ msg := randString()
+ sigs[i] = sigSecrets.Sign(msg)
+ }
+
+ b.Run("pubkey_check", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ _ = isCanonicalPoint(pubkeys[i%maxN])
+ }
+ })
+
+ b.Run("signature_R_check", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ _ = isCanonicalPoint([32]byte(sigs[i%maxN][:32]))
+ }
+ })
+
+ b.Run("both_checks", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ _ = !isCanonicalPoint(pubkeys[i%maxN]) || !isCanonicalPoint([32]byte(sigs[i%maxN][:32]))
+ }
+ })
+}
diff --git a/crypto/batchverifier_test.go b/crypto/batchverifier_test.go
index 6f3c5954fc..ac91c19023 100644
--- a/crypto/batchverifier_test.go
+++ b/crypto/batchverifier_test.go
@@ -27,10 +27,42 @@ import (
"github.com/algorand/go-algorand/test/partitiontest"
)
+// runnableTB is an interface constraint for types that have both testing.TB methods and Run
+type runnableTB[T any] interface {
+ testing.TB
+ Run(string, func(T)) bool
+}
+
+// runBatchVerifierImpls runs testing.{T,B}.Run against 3 batch verifier implementations as subtests.
+func runBatchVerifierImpls[T runnableTB[T]](tb T, runFunc func(T, func(int) BatchVerifier)) {
+ tb.Run("libsodium_single", func(t T) {
+ runFunc(t, func(hint int) BatchVerifier {
+ bv := makeLibsodiumBatchVerifier(hint)
+ bv.(*cgoBatchVerifier).useSingle = true
+ return bv
+ })
+ })
+ tb.Run("libsodium_batch", func(t T) {
+ runFunc(t, func(hint int) BatchVerifier {
+ bv := makeLibsodiumBatchVerifier(hint)
+ bv.(*cgoBatchVerifier).useSingle = false
+ return bv
+ })
+ })
+ tb.Run("ed25519consensus", func(t T) {
+ runFunc(t, func(hint int) BatchVerifier {
+ return makeEd25519ConsensusBatchVerifier(hint)
+ })
+ })
+}
+
func TestBatchVerifierSingle(t *testing.T) {
partitiontest.PartitionTest(t)
+ runBatchVerifierImpls(t, testBatchVerifierSingle)
+}
+func testBatchVerifierSingle(t *testing.T, makeBV func(int) BatchVerifier) {
// test expected success
- bv := MakeBatchVerifier()
+ bv := makeBV(0)
msg := randString()
var s Seed
RandBytes(s[:])
@@ -40,7 +72,7 @@ func TestBatchVerifierSingle(t *testing.T) {
require.NoError(t, bv.Verify())
// test expected failure
- bv = MakeBatchVerifier()
+ bv = makeBV(0)
msg = randString()
RandBytes(s[:])
sigSecrets = GenerateSignatureSecrets(s)
@@ -53,9 +85,12 @@ func TestBatchVerifierSingle(t *testing.T) {
func TestBatchVerifierBulk(t *testing.T) {
partitiontest.PartitionTest(t)
+ runBatchVerifierImpls(t, testBatchVerifierBulk)
+}
+func testBatchVerifierBulk(t *testing.T, makeBV func(int) BatchVerifier) {
for i := 1; i < 64*2+3; i++ {
n := i
- bv := MakeBatchVerifierWithHint(n)
+ bv := makeBV(n)
var s Seed
for i := 0; i < n; i++ {
@@ -68,13 +103,15 @@ func TestBatchVerifierBulk(t *testing.T) {
require.Equal(t, n, bv.GetNumberOfEnqueuedSignatures())
require.NoError(t, bv.Verify())
}
-
}
func TestBatchVerifierBulkWithExpand(t *testing.T) {
partitiontest.PartitionTest(t)
+ runBatchVerifierImpls(t, testBatchVerifierBulkWithExpand)
+}
+func testBatchVerifierBulkWithExpand(t *testing.T, makeBV func(int) BatchVerifier) {
n := 64
- bv := MakeBatchVerifier()
+ bv := makeBV(0) // Start with no hint to test expansion
var s Seed
RandBytes(s[:])
@@ -89,8 +126,11 @@ func TestBatchVerifierBulkWithExpand(t *testing.T) {
func TestBatchVerifierWithInvalidSiganture(t *testing.T) {
partitiontest.PartitionTest(t)
+ runBatchVerifierImpls(t, testBatchVerifierWithInvalidSignature)
+}
+func testBatchVerifierWithInvalidSignature(t *testing.T, makeBV func(int) BatchVerifier) {
n := 64
- bv := MakeBatchVerifier()
+ bv := makeBV(0)
var s Seed
RandBytes(s[:])
@@ -111,8 +151,11 @@ func TestBatchVerifierWithInvalidSiganture(t *testing.T) {
}
func BenchmarkBatchVerifier(b *testing.B) {
+ runBatchVerifierImpls(b, benchmarkBatchVerifier)
+}
+func benchmarkBatchVerifier(b *testing.B, makeBV func(int) BatchVerifier) {
c := makeCurve25519Secret()
- bv := MakeBatchVerifierWithHint(1)
+ bv := makeBV(1)
for i := 0; i < b.N; i++ {
str := randString()
bv.EnqueueSignature(c.SignatureVerifier, str, c.Sign(str))
@@ -125,9 +168,12 @@ func BenchmarkBatchVerifier(b *testing.B) {
// BenchmarkBatchVerifierBig with b.N over 1000 will report the expected performance
// gain as the batchsize increases. All sigs are valid.
func BenchmarkBatchVerifierBig(b *testing.B) {
+ runBatchVerifierImpls(b, benchmarkBatchVerifierBig)
+}
+func benchmarkBatchVerifierBig(b *testing.B, makeBV func(int) BatchVerifier) {
c := makeCurve25519Secret()
for batchSize := 1; batchSize <= 96; batchSize++ {
- bv := MakeBatchVerifierWithHint(batchSize)
+ bv := makeBV(batchSize)
for i := 0; i < batchSize; i++ {
str := randString()
bv.EnqueueSignature(c.SignatureVerifier, str, c.Sign(str))
@@ -149,16 +195,23 @@ func BenchmarkBatchVerifierBig(b *testing.B) {
// invalid sigs to even numbered batch sizes. This shows the impact of invalid sigs on the
// performance. Basically, all the gains from batching disappear.
func BenchmarkBatchVerifierBigWithInvalid(b *testing.B) {
+ runBatchVerifierImpls(b, benchmarkBatchVerifierBigWithInvalid)
+}
+func benchmarkBatchVerifierBigWithInvalid(b *testing.B, makeBV func(int) BatchVerifier) {
c := makeCurve25519Secret()
badSig := Signature{}
for batchSize := 1; batchSize <= 96; batchSize++ {
- bv := MakeBatchVerifierWithHint(batchSize)
+ bv := makeBV(batchSize)
+ sigs := make([]Signature, batchSize)
for i := 0; i < batchSize; i++ {
str := randString()
if batchSize%2 == 0 && (i == 0 || rand.Float32() < 0.1) {
bv.EnqueueSignature(c.SignatureVerifier, str, badSig)
+ sigs[i] = badSig
} else {
- bv.EnqueueSignature(c.SignatureVerifier, str, c.Sign(str))
+ sig := c.Sign(str)
+ bv.EnqueueSignature(c.SignatureVerifier, str, sig)
+ sigs[i] = sig
}
}
b.Run(fmt.Sprintf("running batchsize %d", batchSize), func(b *testing.B) {
@@ -170,13 +223,16 @@ func BenchmarkBatchVerifierBigWithInvalid(b *testing.B) {
for x := 0; x < count; x++ {
failed, err := bv.VerifyWithFeedback()
if err != nil {
+ require.Len(b, failed, batchSize)
for i, f := range failed {
- if bv.(*cgoBatchVerifier).signatures[i] == badSig {
+ if sigs[i] == badSig {
require.True(b, f)
} else {
require.False(b, f)
}
}
+ } else {
+ require.Nil(b, failed)
}
}
})
@@ -185,22 +241,27 @@ func BenchmarkBatchVerifierBigWithInvalid(b *testing.B) {
func TestEmpty(t *testing.T) {
partitiontest.PartitionTest(t)
- bv := MakeBatchVerifier()
+ runBatchVerifierImpls(t, testEmpty)
+}
+func testEmpty(t *testing.T, makeBV func(int) BatchVerifier) {
+ bv := makeBV(0)
require.NoError(t, bv.Verify())
failed, err := bv.VerifyWithFeedback()
require.NoError(t, err)
- require.Empty(t, failed)
+ require.Nil(t, failed)
}
// TestBatchVerifierIndividualResults tests that VerifyWithFeedback
// returns the correct failed signature indexes
func TestBatchVerifierIndividualResults(t *testing.T) {
partitiontest.PartitionTest(t)
-
+ runBatchVerifierImpls(t, testBatchVerifierIndividualResults)
+}
+func testBatchVerifierIndividualResults(t *testing.T, makeBV func(int) BatchVerifier) {
for i := 1; i < 64*2+3; i++ {
n := i
- bv := MakeBatchVerifierWithHint(n)
+ bv := makeBV(n)
var s Seed
badSigs := make([]bool, n, n)
hasBadSig := false
@@ -221,12 +282,13 @@ func TestBatchVerifierIndividualResults(t *testing.T) {
failed, err := bv.VerifyWithFeedback()
if hasBadSig {
require.ErrorIs(t, err, ErrBatchHasFailedSigs)
+ require.Equal(t, len(badSigs), len(failed))
+ for i := range badSigs {
+ require.Equal(t, badSigs[i], failed[i])
+ }
} else {
require.NoError(t, err)
- }
- require.Equal(t, len(badSigs), len(failed))
- for i := range badSigs {
- require.Equal(t, badSigs[i], failed[i])
+ require.Nil(t, failed)
}
}
}
@@ -235,10 +297,12 @@ func TestBatchVerifierIndividualResults(t *testing.T) {
// returns the correct failed signature indexes when all are valid
func TestBatchVerifierIndividualResultsAllValid(t *testing.T) {
partitiontest.PartitionTest(t)
-
+ runBatchVerifierImpls(t, testBatchVerifierIndividualResultsAllValid)
+}
+func testBatchVerifierIndividualResultsAllValid(t *testing.T, makeBV func(int) BatchVerifier) {
for i := 1; i < 64*2+3; i++ {
n := i
- bv := MakeBatchVerifierWithHint(n)
+ bv := makeBV(n)
var s Seed
for i := 0; i < n; i++ {
msg := randString()
@@ -250,10 +314,7 @@ func TestBatchVerifierIndividualResultsAllValid(t *testing.T) {
require.Equal(t, n, bv.GetNumberOfEnqueuedSignatures())
failed, err := bv.VerifyWithFeedback()
require.NoError(t, err)
- require.Equal(t, bv.GetNumberOfEnqueuedSignatures(), len(failed))
- for _, f := range failed {
- require.False(t, f)
- }
+ require.Nil(t, failed)
}
}
@@ -265,7 +326,7 @@ func TestBatchVerifierGC(t *testing.T) {
t.Run("", func(t *testing.T) {
t.Parallel()
- bv := MakeBatchVerifierWithHint(n)
+ bv := makeLibsodiumBatchVerifier(n)
var s Seed
for i := 0; i < n; i++ {
diff --git a/crypto/gobatchverifier.go b/crypto/gobatchverifier.go
new file mode 100644
index 0000000000..46fcd0cae5
--- /dev/null
+++ b/crypto/gobatchverifier.go
@@ -0,0 +1,203 @@
+// Copyright (C) 2019-2025 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see .
+
+package crypto
+
+import (
+ "bytes"
+
+ "github.com/hdevalence/ed25519consensus"
+)
+
+// ed25519ConsensusVerifySingle performs single signature verification using ed25519consensus,
+// with additional checks to reject non-canonical encodings and small-order public keys.
+func ed25519ConsensusVerifySingle(publicKey [32]byte, message []byte, signature [64]byte) bool {
+ // Check for non-canonical public key or R (first 32 bytes of signature), and reject small-order public keys
+ if !isCanonicalPoint(publicKey) || !isCanonicalPoint([32]byte(signature[:32])) || hasSmallOrder(publicKey) {
+ return false
+ }
+
+ return ed25519consensus.Verify(publicKey[:], message, signature[:])
+}
+
+type ed25519ConsensusVerifyEntry struct {
+ msgHashRep []byte
+ publicKey SignatureVerifier
+ signature Signature
+ failedChecks bool
+}
+
+type ed25519ConsensusBatchVerifier struct {
+ entries []ed25519ConsensusVerifyEntry // used in VerifyWithFeedback to identify failed signatures
+ failedChecks bool // true if any entry failed non-canonical or small-order checks
+ bv ed25519consensus.BatchVerifier
+}
+
+func makeEd25519ConsensusBatchVerifier(hint int) BatchVerifier {
+ if hint <= 0 {
+ hint = minBatchVerifierAlloc
+ }
+ return &ed25519ConsensusBatchVerifier{
+ entries: make([]ed25519ConsensusVerifyEntry, 0, hint),
+ bv: ed25519consensus.NewPreallocatedBatchVerifier(hint),
+ }
+}
+
+func (b *ed25519ConsensusBatchVerifier) EnqueueSignature(sigVerifier SignatureVerifier, message Hashable, sig Signature) {
+ msgHashRep := HashRep(message)
+ failedChecks := !isCanonicalPoint(sigVerifier) || !isCanonicalPoint([32]byte(sig[:32])) || hasSmallOrder(sigVerifier)
+
+ entry := ed25519ConsensusVerifyEntry{
+ msgHashRep: msgHashRep,
+ publicKey: sigVerifier,
+ signature: sig,
+ failedChecks: failedChecks,
+ }
+ b.entries = append(b.entries, entry)
+
+ if failedChecks {
+ b.failedChecks = true
+ } else {
+ b.bv.Add(sigVerifier[:], msgHashRep, sig[:])
+ }
+}
+
+func (b *ed25519ConsensusBatchVerifier) GetNumberOfEnqueuedSignatures() int {
+ return len(b.entries)
+}
+
+func (b *ed25519ConsensusBatchVerifier) Verify() error {
+ if len(b.entries) == 0 {
+ return nil
+ }
+
+ // Fail if any pre-checks failed or if batch verification fails
+ if b.failedChecks || !b.bv.Verify() {
+ return ErrBatchHasFailedSigs
+ }
+ return nil
+}
+
+func (b *ed25519ConsensusBatchVerifier) VerifyWithFeedback() (failed []bool, err error) {
+ if len(b.entries) == 0 {
+ return nil, nil
+ }
+
+ if !b.failedChecks && b.bv.Verify() {
+ return nil, nil
+ }
+
+ failed = make([]bool, len(b.entries))
+ for i := range b.entries {
+ if b.entries[i].failedChecks {
+ failed[i] = true
+ } else {
+ failed[i] = !ed25519ConsensusVerifySingle(b.entries[i].publicKey, b.entries[i].msgHashRep, b.entries[i].signature)
+ }
+ }
+
+ return failed, ErrBatchHasFailedSigs
+}
+
+// Check that Y is canonical, using the succeed-fast algorithm from
+// the "Taming the many EdDSAs" paper.
+func isCanonicalY(p [32]byte) bool {
+ if p[0] < 237 {
+ return true
+ }
+ for i := 1; i < 31; i++ {
+ if p[i] != 255 {
+ return true
+ }
+ }
+ return (p[31] | 128) != 255
+}
+
+// isCanonicalPoint is a variable-time check that returns true if the
+// 32-byte ed25519 point encoding is canonical.
+func isCanonicalPoint(p [32]byte) bool {
+ if !isCanonicalY(p) {
+ return false
+ }
+
+ // Test for the two cases with a non-canonical sign bit not caught by the
+ // non-canonical y-coordinate check above. They are points number 9 and 10
+ // from Table 1 of the "Taming the many EdDSAs" paper.
+ if p == [32]byte{ // (−0, 1)
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ } || p == [32]byte{ // (-0, 2^255-20)
+ 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ } {
+ return false
+ }
+
+ return true
+}
+
+// from libsodium/crypto_core/ed25519/ref10/ed25519_ref10.c ge25519_has_small_order
+var smallOrderPoints = [][32]byte{
+ /* 0 (order 4) */ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ /* 1 (order 1) */ {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ /* 2707385501144840649318225287225658788936804267575313519463743609750303402022
+ (order 8) */{
+ 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4,
+ 0x89, 0xf2, 0xef, 0x98, 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6,
+ 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05},
+ /* 55188659117513257062467267217118295137698188065244968500265048394206261417927
+ (order 8) */{
+ 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b,
+ 0x76, 0x0d, 0x10, 0x67, 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39,
+ 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a},
+ /* p-1 (order 2) */ {
+ 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f},
+ /* p (=0, order 4) */ {
+ 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f},
+ /* p+1 (=1, order 1) */ {
+ 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f},
+}
+
+// hasSmallOrder checks if a point is in the small-order blacklist.
+// Based on libsodium ge25519_has_small_order, but this version is variable-time.
+func hasSmallOrder(p [32]byte) bool {
+ for _, point := range smallOrderPoints {
+ if !bytes.Equal(p[:31], point[:31]) {
+ continue
+ }
+ // For the last byte, ignore the sign bit (bit 7)
+ if (p[31] & 0x7f) == point[31] {
+ return true
+ }
+ }
+ return false
+}
diff --git a/crypto/gobatchverifier_test.go b/crypto/gobatchverifier_test.go
new file mode 100644
index 0000000000..36ed799fab
--- /dev/null
+++ b/crypto/gobatchverifier_test.go
@@ -0,0 +1,1184 @@
+// Copyright (C) 2019-2025 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see .
+
+package crypto
+
+import (
+ "bufio"
+ "compress/gzip"
+ "crypto/ed25519"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "math/rand"
+ "os"
+ "regexp"
+ "slices"
+ "strconv"
+ "strings"
+ "testing"
+
+ "github.com/algorand/go-algorand/protocol"
+ "github.com/algorand/go-algorand/test/partitiontest"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+// ensure internal ed25519 types match the expected []byte lengths used by ed25519consensus package
+func TestEd25519ConsensusBatchVerifierTypes(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ require.Len(t, ed25519PublicKey{}, ed25519.PublicKeySize)
+ require.Len(t, ed25519Signature{}, ed25519.SignatureSize)
+}
+
+// Test vectors for 12 edge cases listed in Appendix C of "Taming the many EdDSAs" https://eprint.iacr.org/2020/1244
+// These are also checked in test_edge_cases in go-algorand/crypto/libsodium-fork/test/default/batch.c
+func TestBatchVerifierTamingEdDSAsEdgeCases(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ hexVecs := make([]batchTestCaseHex, len(tamingEdDSAsTestVectors))
+ expectedFail := make([]bool, len(tamingEdDSAsTestVectors))
+ for i, tc := range tamingEdDSAsTestVectors {
+ hexVecs[i] = batchTestCaseHex{pkHex: tc.pk, sigHex: tc.sig, msgHex: tc.msg}
+ expectedFail[i] = tc.expectedFail
+ }
+ runBatchVerifierImpls(t, func(t *testing.T, makeBV func(int) BatchVerifier) {
+ testBatchVectors(t, makeBV, decodeHexTestCases(t, hexVecs), expectedFail)
+ })
+}
+
+// Test vectors from "It's 255:19AM" blog post about ZIP-215 development, also used to create the
+// 14x14 visualizations of different criteria across implementations in Henry de Valence's blog post
+// "It's 255:19AM..." https://hdevalence.ca/blog/2020-10-04-its-25519am/
+func TestBatchVerifierEd25519ConsensusTestData(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ const msgHex = "5a63617368" // used for all signatures in this test
+ hexVecs := make([]batchTestCaseHex, len(ed25519consensusCases))
+ for i, tc := range ed25519consensusCases {
+ hexVecs[i] = batchTestCaseHex{pkHex: tc.pk, sigHex: tc.sig, msgHex: msgHex}
+ }
+ // All of these test vectors should fail, matching our strict criteria
+ expectedFail := make([]bool, len(hexVecs))
+ for i := range expectedFail {
+ expectedFail[i] = true
+ }
+ runBatchVerifierImpls(t, func(t *testing.T, makeBV func(int) BatchVerifier) {
+ testBatchVectors(t, makeBV, decodeHexTestCases(t, hexVecs), expectedFail)
+ })
+}
+
+// Test vectors from unit tests for our libsodium- and ed25519-donna-based batch verification implementation
+// introduced in PR #3031.
+func TestBatchVerifierLibsodiumTestData(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ // read vectors hard-coded in test source file
+ const testVectorFile = "./libsodium-fork/test/default/batch.c"
+ const testVectorSize = 1025
+ f, err := os.Open(testVectorFile)
+ if err != nil {
+ panic(err)
+ }
+ defer f.Close()
+ scanner := bufio.NewScanner(f)
+
+ type testCase struct {
+ seed, pk, sig []byte
+ m string
+ }
+ var testCases []testCase
+ // each line is {{sk},{pk},{sig},"m"} where sk, pk, sig are comma-delimited lists of hex-encoded bytes
+ re := regexp.MustCompile(`\{\{(.*?)\},\{(.*?)\},\{(.*?)\},(.*?)\}`)
+ for i := 0; scanner.Scan(); i++ {
+ var tc testCase
+ line := scanner.Text()
+ matches := re.FindStringSubmatch(line)
+ if matches == nil || len(matches) != 5 {
+ continue
+ }
+ tc.seed = decodeCByteArray(matches[1], ed25519.SeedSize)
+ tc.pk = decodeCByteArray(matches[2], ed25519.PublicKeySize)
+ tc.sig = decodeCByteArray(matches[3], ed25519.SignatureSize)
+ tc.m, err = strconv.Unquote(matches[4])
+ require.NoError(t, err)
+ testCases = append(testCases, tc)
+ }
+ t.Logf("loaded %d test vectors from %s", len(testCases), testVectorFile)
+ require.Len(t, testCases, testVectorSize, "not enough test vectors found")
+
+ // check test data with libsodium-based ed25519Verify
+ for _, tc := range testCases {
+ require.True(t, ed25519Verify(ed25519PublicKey(tc.pk), []byte(tc.m), ed25519Signature(tc.sig)))
+ }
+
+ // assert signing with test vector sk produces sig
+ for _, tc := range testCases {
+ pk, sk := ed25519GenerateKeySeed(ed25519Seed(tc.seed))
+ require.Equal(t, tc.pk, []byte(pk[:]))
+ sig := ed25519Sign(sk, []byte(tc.m))
+ require.Equal(t, tc.sig, []byte(sig[:]))
+ }
+
+ // test different BatchVerifier implementations and batch sizes
+ testVectors := make([]batchTestCase, len(testCases))
+ for i, tc := range testCases {
+ testVectors[i] = batchTestCase{pk: tc.pk, sig: tc.sig, msg: []byte(tc.m)}
+ }
+ expectedFail := make([]bool, len(testVectors)) // all should pass
+ runBatchVerifierImpls(t, func(t *testing.T, makeBV func(int) BatchVerifier) {
+ testBatchVectors(t, makeBV, testVectors, expectedFail)
+ })
+}
+
+// based on TestEd25519Vectors from go/src/crypto/ed25519/ed25519vectors_test.go
+// which uses test vectors from filippo.io/mostly-harmless/ed25519vectors
+func TestBatchVerifierFilippoVectors(t *testing.T) {
+ var vectors []struct {
+ A, R, S, M string
+ Flags []string
+ }
+ f, err := os.Open("./testdata/ed25519vectors.json.gz")
+ require.NoError(t, err)
+ defer f.Close()
+ rd, err := gzip.NewReader(f)
+ require.NoError(t, err)
+ defer rd.Close()
+ err = json.NewDecoder(rd).Decode(&vectors)
+ require.NoError(t, err)
+
+ expectedFail := make([]bool, len(vectors))
+ hexVecs := make([]batchTestCaseHex, len(vectors))
+ for i, v := range vectors {
+ for _, f := range v.Flags {
+ switch f {
+ case "LowOrderA": // reject small-order A
+ expectedFail[i] = true
+ case "NonCanonicalA", "NonCanonicalR": // reject non-canonical A or R
+ expectedFail[i] = true
+ case "LowOrderR": // small-order R allowed
+ case "LowOrderComponentR", "LowOrderComponentA": // torsion component allowed
+ case "LowOrderResidue": // cofactorless batch verification
+ default:
+ require.Fail(t, "unknown flag %q in test vector %d", f, i)
+ }
+ }
+ hexVecs[i] = batchTestCaseHex{pkHex: v.A, sigHex: v.R + v.S, msgHex: hex.EncodeToString([]byte(v.M))}
+ }
+ runBatchVerifierImpls(t, func(t *testing.T, makeBV func(int) BatchVerifier) {
+ testBatchVectors(t, makeBV, decodeHexTestCases(t, hexVecs), expectedFail)
+ })
+
+ // test isCanonicalPoint and hasSmallOrder against A and R
+ t.Run("ARchecks", func(t *testing.T) {
+ for _, v := range vectors {
+ A, err := hex.DecodeString(v.A)
+ require.NoError(t, err)
+ require.Equal(t, !slices.Contains(v.Flags, "NonCanonicalA"), isCanonicalPoint([32]byte(A)))
+ require.Equal(t, slices.Contains(v.Flags, "LowOrderA"), hasSmallOrder([32]byte(A)))
+
+ R, err := hex.DecodeString(v.R)
+ require.NoError(t, err)
+ require.Equal(t, !slices.Contains(v.Flags, "NonCanonicalR"), isCanonicalPoint([32]byte(R)))
+ require.Equal(t, slices.Contains(v.Flags, "LowOrderR"), hasSmallOrder([32]byte(R)))
+ }
+ })
+
+}
+
+// testBatchVectors tests a batch of signatures with expected pass/fail results using various batch sizes
+func testBatchVectors(t *testing.T, makeBV func(int) BatchVerifier, testVectors []batchTestCase, expectedFail []bool) {
+ require.Len(t, expectedFail, len(testVectors))
+
+ // run a single batch of test vectors and compare to expected failures
+ runBatch := func(t *testing.T, vecs []batchTestCase, expFail []bool) {
+ bv := makeBV(len(vecs))
+ for _, tv := range vecs {
+ bv.EnqueueSignature(SignatureVerifier(tv.pk), noHashID(tv.msg), Signature(tv.sig))
+ }
+ failed, err := bv.VerifyWithFeedback()
+ if slices.Contains(expFail, true) { // some failures expected
+ require.Error(t, err)
+ require.NotNil(t, failed)
+ require.Len(t, failed, len(vecs))
+ for i := range expFail {
+ assert.Equal(t, expFail[i], failed[i])
+ }
+ } else { // no failures expected
+ require.NoError(t, err)
+ require.Nil(t, failed)
+ }
+ }
+
+ // run all the test vectors in a single batch
+ t.Run("all", func(t *testing.T) { runBatch(t, testVectors, expectedFail) })
+
+ // split into multiple batches of different sizes, optionally shuffled
+ runBatchSizes := func(shuffle bool, vecs []batchTestCase, expFail []bool) {
+ if shuffle {
+ vecs, expFail = slices.Clone(vecs), slices.Clone(expFail)
+ rand.Shuffle(len(vecs), func(i, j int) {
+ vecs[i], vecs[j], expFail[i], expFail[j] = vecs[j], vecs[i], expFail[j], expFail[i]
+ })
+ }
+
+ for _, batchSize := range []int{1, 2, 4, 8, 16, 32, 64, 100, 128, 256, 512, 1024} {
+ if batchSize > len(vecs) {
+ continue
+ }
+ t.Run(fmt.Sprintf("batchSize=%d", batchSize), func(t *testing.T) {
+ vectorBatches := splitBatches(vecs, batchSize)
+ failBatches := splitBatches(expFail, batchSize)
+ require.Equal(t, len(vectorBatches), len(failBatches))
+ //t.Logf("Testing with batch size %d: %d total signatures in %d batches", batchSize, n, len(vectorBatches))
+ for i, batch := range vectorBatches {
+ batchExpectedFail := failBatches[i]
+ //t.Logf("Batch %d/%d: signatures [%d-%d), size=%d", i+1, len(vectorBatches), i*batchSize, i*batchSize+len(batch), len(batch))
+ runBatch(t, batch, batchExpectedFail)
+ }
+ })
+ }
+ }
+
+ t.Run("unshuffled", func(t *testing.T) { runBatchSizes(false, testVectors, expectedFail) })
+ t.Run("shuffled", func(t *testing.T) { runBatchSizes(true, testVectors, expectedFail) })
+}
+
+// splitBatches splits items into batches of the specified size
+func splitBatches[T any](items []T, batchSize int) [][]T {
+ if batchSize <= 0 {
+ return nil
+ }
+ numBatches := len(items) / batchSize
+ if len(items)%batchSize != 0 {
+ numBatches++
+ }
+ batches := make([][]T, numBatches)
+
+ for i, item := range items {
+ batchIdx := i / batchSize
+ batches[batchIdx] = append(batches[batchIdx], item)
+ }
+
+ return batches
+}
+
+// decodeCByteArray decodes a string like "0x27,0x81," into a byte array of length n
+func decodeCByteArray(hexList string, n int) []byte {
+ bytes := make([]byte, n)
+ words := strings.Split(hexList, ",")
+ // remove trailing empty string
+ if words[len(words)-1] == "" {
+ words = words[:len(words)-1]
+ } else {
+ panic("missing trailing comma")
+ }
+ if len(words) != n {
+ panic("wrong number of words")
+ }
+ for i, word := range words {
+ _, err := fmt.Sscanf(word, "0x%02x", &bytes[i])
+ if err != nil {
+ panic(err)
+ }
+ }
+ return bytes
+}
+
+type batchTestCaseHex struct{ pkHex, sigHex, msgHex string }
+type batchTestCase struct{ pk, sig, msg []byte }
+
+// decodeHexTestCases converts hex-encoded test cases to byte arrays
+func decodeHexTestCases(t *testing.T, hexCases []batchTestCaseHex) []batchTestCase {
+ cases := make([]batchTestCase, len(hexCases))
+ for i, hc := range hexCases {
+ pk, err := hex.DecodeString(hc.pkHex)
+ require.NoError(t, err)
+ require.Len(t, pk, ed25519.PublicKeySize)
+
+ sig, err := hex.DecodeString(hc.sigHex)
+ require.NoError(t, err)
+ require.Len(t, sig, ed25519.SignatureSize)
+
+ msg, err := hex.DecodeString(hc.msgHex)
+ require.NoError(t, err)
+
+ cases[i] = batchTestCase{pk: pk, sig: sig, msg: msg}
+ }
+ return cases
+}
+
+// noHashID implements Hashable but returns an empty protocol.HashID for use
+// with the test vectors, which should not be prefixed
+type noHashID []byte
+
+func (n noHashID) ToBeHashed() (protocol.HashID, []byte) { return "", n }
+
+// Test vectors from Appendix C of "Taming the many EdDSAs" https://eprint.iacr.org/2020/1244
+var tamingEdDSAsTestVectors = []struct {
+ desc, msg, pk, sig string
+ expectedFail bool // Algorand-specific criteria
+}{
+ {"S = 0, small-order A, small-order R",
+ "8c93255d71dcab10e8f379c26200f3c7bd5f09d9bc3068d3ef4edeb4853022b6",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ true},
+ {"0 < S < L, small-order A, mixed-order R",
+ "9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
+ true},
+ {"0 < S < L, mixed-order A, small-order R",
+ "aebf3f2601a0c8c5d39cc7d8911642f740b78168218da8471772b35f9d35b9ab",
+ "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8c4bd45aecaca5b24fb97bc10ac27ac8751a7dfe1baff8b953ec9f5833ca260e",
+ false},
+ {"0 < S < L, mixed-order A, mixed-order R",
+ "9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79",
+ "cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
+ "9046a64750444938de19f227bb80485e92b83fdb4b6506c160484c016cc1852f87909e14428a7a1d62e9f22f3d3ad7802db02eb2e688b6c52fcd6648a98bd009",
+ false},
+ {"0 < S < L, mixed-order A, mixed-order R, SB != R + hA",
+ "e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c",
+ "cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
+ "160a1cb0dc9c0258cd0a7d23e94d8fa878bcb1925f2c64246b2dee1796bed5125ec6bc982a269b723e0668e540911a9a6a58921d6925e434ab10aa7940551a09",
+ false},
+ {`0 < S < L, mixed-order A, L-order R, SB != R + hA ("#5 fails any cofactored verification that pre-reduces scalar 8h")`,
+ "e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c",
+ "cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
+ "21122a84e0b5fca4052f5b1235c80a537878b38f3142356b2c2384ebad4668b7e40bc836dac0f71076f9abe3a53f9c03c1ceeeddb658d0030494ace586687405",
+ false},
+ {"S > L, L-order A, L-order R",
+ "85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40",
+ "442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623",
+ "e96f66be976d82e60150baecff9906684aebb1ef181f67a7189ac78ea23b6c0e547f7690a0e2ddcd04d87dbc3490dc19b3b3052f7ff0538cb68afb369ba3a514",
+ true},
+ {`S >> L, L-order A, L-order R ("#7 fails bitwise tests that S > L")`,
+ "85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40",
+ "442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623",
+ "8ce5b96c8f26d0ab6c47958c9e68b937104cd36e13c33566acd2fe8d38aa19427e71f98a4734e74f2f13f06f97c20d58cc3f54b8bd0d272f42b695dd7e89a8c2",
+ true},
+ {`0 < S < L, mixed-order A, small-order R ("#8-9 have non-canonical R; implementations that reduce R before hashing will accept #8 and reject #9, while those that do not will reject #8 and accept #9")`,
+ "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
+ "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f",
+ true},
+ {`0 < S < L, mixed-order A, small-order R ("#8-9 have non-canonical R; implementations that reduce R before hashing will accept #8 and reject #9, while those that do not will reject #8 and accept #9")`,
+ "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
+ "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908",
+ true},
+ {`0 < S < L, small-order A, mixed-order R ("#10-11 have a non-canonical A; implementations that reduce A before hashing will accept #10 and reject #11, while those that do not will reject #10 and accept #11")`,
+ "e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
+ true},
+ {`0 < S < L, small-order A, mixed-order R ("#10-11 have a non-canonical A; implementations that reduce A before hashing will accept #10 and reject #11, while those that do not will reject #10 and accept #11")`,
+ "39a591f5321bbe07fd5a23dc2f39d025d74526615746727ceefd6e82ae65c06f",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
+ true},
+}
+
+// "It's 255:19AM" blog post test vectors, from the ed25519consensus package
+var ed25519consensusCases = [196]struct{ pk, sig string }{
+ {
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
+ "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
+ "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
+ "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000080",
+ "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000080",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000080",
+ "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000080",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000080",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000080",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000080",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000080",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000080",
+ "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000080",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000080",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000080",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000080",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000080",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
+ "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
+ "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
+ "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
+ "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
+ "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
+ "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000080",
+ "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000080",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000080",
+ "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000080",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000080",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000080",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000080",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000080",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000080",
+ "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000080",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000080",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000080",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000080",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "0100000000000000000000000000000000000000000000000000000000000080",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ {
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
+ },
+}
diff --git a/crypto/libsodium-fork/src/libsodium/crypto_sign/ed25519/ref10/batch.c b/crypto/libsodium-fork/src/libsodium/crypto_sign/ed25519/ref10/batch.c
index 83f93a857d..030c64592d 100644
--- a/crypto/libsodium-fork/src/libsodium/crypto_sign/ed25519/ref10/batch.c
+++ b/crypto/libsodium-fork/src/libsodium/crypto_sign/ed25519/ref10/batch.c
@@ -118,7 +118,7 @@ heap_get_top2(batch_heap *heap, heap_index_t *max1, heap_index_t *max2, size_t l
/* */
void ge25519_multi_scalarmult_vartime_final(ge25519_p3 *r, ge25519_p3 *point, sc25519 scalar) {
- const sc25519_element_t topbit = ((sc25519_element_t)1 << (SC25519_LIMB_SIZE - 1));
+ const sc25519_element_t topbit = ((sc25519_element_t)1 << (SC25519_BITS_PER_LIMB - 1));
size_t limb = limb128bits;
sc25519_element_t flag;
ge25519_p1p1 p1p1_r;
diff --git a/crypto/onetimesig.go b/crypto/onetimesig.go
index df22138c23..420ae442e8 100644
--- a/crypto/onetimesig.go
+++ b/crypto/onetimesig.go
@@ -391,25 +391,11 @@ func (v OneTimeSignatureVerifier) Verify(id OneTimeSignatureIdentifier, message
}
func (v OneTimeSignatureVerifier) batchVerify(batchID OneTimeSignatureSubkeyBatchID, offsetID OneTimeSignatureSubkeyOffsetID, message Hashable, sig OneTimeSignature) bool {
- // serialize encoded batchID, offsetID, message into a continuous memory buffer with the layout
- // hashRep(batchID)... hashRep(offsetID)... hashRep(message)...
- const estimatedSize = 256
- messageBuffer := make([]byte, 0, estimatedSize)
-
- messageBuffer = HashRepToBuff(batchID, messageBuffer)
- batchIDLen := uint64(len(messageBuffer))
- messageBuffer = HashRepToBuff(offsetID, messageBuffer)
- offsetIDLen := uint64(len(messageBuffer)) - batchIDLen
- messageBuffer = HashRepToBuff(message, messageBuffer)
- messageLen := uint64(len(messageBuffer)) - offsetIDLen - batchIDLen
- msgLengths := []uint64{batchIDLen, offsetIDLen, messageLen}
- allValid, _ := cgoBatchVerificationImpl(
- messageBuffer,
- msgLengths,
- []PublicKey{PublicKey(v), PublicKey(batchID.SubKeyPK), PublicKey(offsetID.SubKeyPK)},
- []Signature{Signature(sig.PK2Sig), Signature(sig.PK1Sig), Signature(sig.Sig)},
- )
- return allValid
+ bv := MakeBatchVerifierWithHint(3)
+ bv.EnqueueSignature(PublicKey(v), batchID, Signature(sig.PK2Sig))
+ bv.EnqueueSignature(PublicKey(batchID.SubKeyPK), offsetID, Signature(sig.PK1Sig))
+ bv.EnqueueSignature(PublicKey(offsetID.SubKeyPK), message, Signature(sig.Sig))
+ return bv.Verify() == nil
}
// DeleteBeforeFineGrained deletes ephemeral keys before (but not including) the given id.
diff --git a/crypto/testdata/ed25519vectors.json.gz b/crypto/testdata/ed25519vectors.json.gz
new file mode 100644
index 0000000000..52605c9690
Binary files /dev/null and b/crypto/testdata/ed25519vectors.json.gz differ
diff --git a/go.mod b/go.mod
index c3ccdf86d4..f50967dc09 100644
--- a/go.mod
+++ b/go.mod
@@ -28,6 +28,7 @@ require (
github.com/google/go-querystring v1.0.0
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
+ github.com/hdevalence/ed25519consensus v0.2.0
github.com/ipfs/go-log v1.0.5
github.com/ipfs/go-log/v2 v2.5.1
github.com/jmoiron/sqlx v1.2.0
@@ -63,6 +64,7 @@ require (
)
require (
+ filippo.io/edwards25519 v1.0.0 // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
diff --git a/go.sum b/go.sum
index 0d7b065bca..26ef319213 100644
--- a/go.sum
+++ b/go.sum
@@ -6,6 +6,8 @@ dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
+filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
+filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -259,6 +261,8 @@ github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU=
+github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
diff --git a/tools/block-generator/go.mod b/tools/block-generator/go.mod
index 59961ea55a..567085d74f 100644
--- a/tools/block-generator/go.mod
+++ b/tools/block-generator/go.mod
@@ -18,6 +18,7 @@ require (
)
require (
+ filippo.io/edwards25519 v1.0.0 // indirect
github.com/DataDog/zstd v1.5.2 // indirect
github.com/algorand/falcon v0.1.0 // indirect
github.com/algorand/go-sumhash v0.1.0 // indirect
@@ -64,6 +65,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
+ github.com/hdevalence/ed25519consensus v0.2.0 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/ipfs/boxo v0.24.3 // indirect
diff --git a/tools/block-generator/go.sum b/tools/block-generator/go.sum
index d7caa73f45..c24cb3a494 100644
--- a/tools/block-generator/go.sum
+++ b/tools/block-generator/go.sum
@@ -6,6 +6,8 @@ dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
+filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
+filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -236,6 +238,8 @@ github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU=
+github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=