internal/ed25519strict verifies the author signature of alg 1 with the strict profile of the spec v0.11 draft (29.9): A canonical and not of small order, checked with an encoding check and the table of the eight points of small order, then crypto/ed25519 for S, R and the equation without the cofactor. No arithmetic on points and no new module. testdata/vectors/ed25519_strict.json has 18 signatures after the cases of «Taming the many EdDSAs», built by testkit with arithmetic on the curve in math/big, only for the vectors, which also checks the table of small order. crypto/ed25519 accepts 11 of them that the profile rejects: the eight points of small order and the non-canonical keys as A. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>v0.11
parent
48b496fdf2
commit
c402857b95
@ -0,0 +1,105 @@
|
||||
package ed25519strict_test
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/hex"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"g.activething.com/go/DateKeys/internal/ed25519strict"
|
||||
"g.activething.com/go/DateKeys/internal/testkit"
|
||||
)
|
||||
|
||||
// The table of the points of small order is what the arithmetic of testkit
|
||||
// computes from the curve.
|
||||
func TestSmallOrderTable(t *testing.T) {
|
||||
var got, want [][32]byte
|
||||
for _, p := range ed25519strict.SmallOrderPoints() {
|
||||
got = append(got, p)
|
||||
}
|
||||
want = testkit.Ed25519Torsion()
|
||||
cmp := func(a, b [32]byte) int { return slices.Compare(a[:], b[:]) }
|
||||
slices.SortFunc(got, cmp)
|
||||
slices.SortFunc(want, cmp)
|
||||
if !slices.Equal(got, want) {
|
||||
t.Fatalf("table %x, want %x", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanonical(t *testing.T) {
|
||||
enc := func(s string) []byte {
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil || len(b) != 32 {
|
||||
t.Fatalf("bad test encoding %s", s)
|
||||
}
|
||||
return b
|
||||
}
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
a string
|
||||
want bool
|
||||
}{
|
||||
{"y = 0", "0000000000000000000000000000000000000000000000000000000000000000", true},
|
||||
{"y = 0, sign set: x is not 0", "0000000000000000000000000000000000000000000000000000000000000080", true},
|
||||
{"y = 1", "0100000000000000000000000000000000000000000000000000000000000000", true},
|
||||
{"y = 1, sign set: x is 0", "0100000000000000000000000000000000000000000000000000000000000080", false},
|
||||
{"y = p - 1", "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", true},
|
||||
{"y = p - 1, sign set", "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", false},
|
||||
{"y = p", "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", false},
|
||||
{"y = 2^255 - 1", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", false},
|
||||
{"y = p - 2, sign set", "ebffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", true},
|
||||
} {
|
||||
if got := ed25519strict.Canonical(enc(c.a)); got != c.want {
|
||||
t.Errorf("%s: Canonical = %v, want %v", c.name, got, c.want)
|
||||
}
|
||||
}
|
||||
if ed25519strict.Canonical(make([]byte, 31)) || ed25519strict.SmallOrder(make([]byte, 33)) {
|
||||
t.Error("a length other than 32 is accepted")
|
||||
}
|
||||
}
|
||||
|
||||
// crypto/ed25519 accepts any message with A = 01 00…00, R the identity and S
|
||||
// = 0; Verify does not, nor a key or a signature of another length.
|
||||
func TestVerifyRejectsWhatStdlibAccepts(t *testing.T) {
|
||||
a := make([]byte, 32)
|
||||
a[0] = 1
|
||||
sig := make([]byte, 64)
|
||||
sig[0] = 1
|
||||
msg := []byte("anything")
|
||||
if !ed25519.Verify(a, msg, sig) {
|
||||
t.Fatal("crypto/ed25519 no longer accepts the forgery: review the comment of the package")
|
||||
}
|
||||
if ed25519strict.Verify(a, msg, sig) {
|
||||
t.Error("Verify accepts a key of small order")
|
||||
}
|
||||
pub, priv, _ := ed25519.GenerateKey(nil)
|
||||
good := ed25519.Sign(priv, msg)
|
||||
if !ed25519strict.Verify(pub, msg, good) {
|
||||
t.Error("Verify rejects a valid signature")
|
||||
}
|
||||
if ed25519strict.Verify(pub[:31], msg, good) || ed25519strict.Verify(pub, msg, good[:63]) {
|
||||
t.Error("Verify accepts another length")
|
||||
}
|
||||
}
|
||||
|
||||
// The committed vectors give their result.
|
||||
func TestVectors(t *testing.T) {
|
||||
var f testkit.Ed25519StrictFile
|
||||
if err := testkit.ReadJSON("../../testdata/vectors/ed25519_strict.json", &f); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(f.Vectors) == 0 {
|
||||
t.Fatal("no vectors")
|
||||
}
|
||||
for _, v := range f.Vectors {
|
||||
msg, _ := hex.DecodeString(v.Message)
|
||||
pub, _ := hex.DecodeString(v.PublicKey)
|
||||
sig, _ := hex.DecodeString(v.Signature)
|
||||
if got := ed25519strict.Verify(pub, msg, sig); got != v.Valid {
|
||||
t.Errorf("%s: Verify = %v, want %v", v.Name, got, v.Valid)
|
||||
}
|
||||
if got := ed25519.Verify(pub, msg, sig); got != v.Stdlib {
|
||||
t.Errorf("%s: crypto/ed25519 = %v, recorded %v", v.Name, got, v.Stdlib)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue