Signature plan, step 1: strict Ed25519 and its vectors

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
dev 1 week ago
parent 48b496fdf2
commit c402857b95

@ -0,0 +1,97 @@
// Package ed25519strict verifies Ed25519 signatures with the strict profile
// of the author signature (spec v0.11, §29.9): the equation of RFC 8032
// without the cofactor, with the public key A and R in their canonical
// encodings, S below ℓ, and A not of small order.
//
// crypto/ed25519 checks S and R, and computes the equation without the
// cofactor, but it accepts a non-canonical A and an A of small order: with A
// = 01 00…00, R the identity and S = 0 it accepts any message. Verify checks A
// first, with an encoding check and the table of the eight points of small
// order, so that no arithmetic on points is written here.
package ed25519strict
import "crypto/ed25519"
// smallOrder are the canonical encodings of the eight points of small order
// of edwards25519: the identity, the point of order 2, the two of order 4 and
// the four of order 8. A canonical A of small order is one of them; the tests
// compute them again.
var smallOrder = [8][32]byte{
{0x00},
{31: 0x80},
{0x01},
{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},
{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, 0x85},
{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},
{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, 0xfa},
{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},
}
// Verify reports whether sig is a valid signature of msg by the public key
// pub under the strict profile (spec §29.9). A key or a signature of another
// length is not valid either; the caller tells that case apart (F1).
func Verify(pub, msg, sig []byte) bool {
if len(pub) != ed25519.PublicKeySize || len(sig) != ed25519.SignatureSize {
return false
}
if !Canonical(pub) || SmallOrder(pub) {
return false
}
// crypto/ed25519 rejects sig[63] & 0xE0 != 0 and S >= ℓ, and compares the
// encoding of [S]B − [k]A with R, which therefore must be canonical.
return ed25519.Verify(ed25519.PublicKey(pub), msg, sig)
}
// Canonical reports whether the 32 bytes a are a canonical encoding: their y,
// the low 255 bits, is below p = 2^255 − 19, and their sign bit is clear when
// y is 1 or p − 1, the two values whose x is 0 (spec §29.9, rule 1). It does
// not tell whether y belongs to a point of the curve.
func Canonical(a []byte) bool {
if len(a) != 32 {
return false
}
high := a[31] & 0x7f
ones := true
for _, b := range a[1:31] {
if b != 0xff {
ones = false
break
}
}
// y >= p: 7f ff…ff and a first byte of 0xed or more.
if high == 0x7f && ones && a[0] >= 0xed {
return false
}
if a[31]&0x80 == 0 {
return true
}
// x = 0: y = 1, 01 00…00, or y = p − 1, ec ff…ff 7f.
zeros := high == 0
for _, b := range a[1:31] {
if b != 0 {
zeros = false
break
}
}
isOne := zeros && a[0] == 0x01
isMinusOne := high == 0x7f && ones && a[0] == 0xec
return !isOne && !isMinusOne
}
// SmallOrder reports whether the canonical encoding a is one of the eight
// points of small order (spec §29.9, rule 2).
func SmallOrder(a []byte) bool {
if len(a) != 32 {
return false
}
for _, s := range smallOrder {
if [32]byte(a) == s {
return true
}
}
return false
}
// SmallOrderPoints returns the canonical encodings of the eight points of
// small order, for the tests and the vectors.
func SmallOrderPoints() [8][32]byte { return smallOrder }

@ -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)
}
}
}

@ -0,0 +1,296 @@
package testkit
import (
"crypto/ed25519"
"crypto/sha256"
"crypto/sha512"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"math/big"
"slices"
"g.activething.com/go/DateKeys/internal/ed25519strict"
)
// Ed25519StrictFile is testdata/vectors/ed25519_strict.json: Ed25519
// signatures with the result of the strict profile of the author signature
// (spec v0.11, §29.9), built after the cases of «Taming the many EdDSAs»
// (Chalkias, Garillot, Nikolaenko, 2020): small-order and non-canonical
// keys, non-canonical R and S, keys of mixed order, and signatures that only
// the equation with the cofactor accepts. Stdlib is what crypto/ed25519 of
// Go says, for the record: where it says true and Valid is false, an
// implementation needs the checks of package ed25519strict.
type Ed25519StrictFile struct {
Spec string `json:"spec"`
Description string `json:"description"`
Vectors []Ed25519StrictVector `json:"vectors"`
}
// Ed25519StrictVector is one signature, its message and its public key, in
// hexadecimal.
type Ed25519StrictVector struct {
Name string `json:"name"`
Message string `json:"message"`
PublicKey string `json:"public_key"`
Signature string `json:"signature"`
Valid bool `json:"valid"`
Stdlib bool `json:"stdlib"`
}
// Ed25519StrictVectors builds ed25519_strict.json, and fails if
// ed25519strict.Verify does not give the result each case is built for.
func Ed25519StrictVectors() (Ed25519StrictFile, error) {
f := Ed25519StrictFile{
Spec: SpecVersion,
Description: "Ed25519 signatures and the result of the strict profile of the author signature (spec v0.11, §29.9), after the cases of " +
"«Taming the many EdDSAs»; stdlib is the result of crypto/ed25519 of Go, for the record. Generated by the reference implementation. See testdata/README.md.",
}
var errs []error
for _, c := range ed25519Cases() {
if got := ed25519strict.Verify(c.pub, c.msg, c.sig); got != c.valid {
errs = append(errs, fmt.Errorf("ed25519 %q: Verify = %v, want %v", c.name, got, c.valid))
}
f.Vectors = append(f.Vectors, Ed25519StrictVector{
Name: c.name,
Message: hex.EncodeToString(c.msg),
PublicKey: hex.EncodeToString(c.pub),
Signature: hex.EncodeToString(c.sig),
Valid: c.valid,
Stdlib: ed25519.Verify(ed25519.PublicKey(c.pub), c.msg, c.sig),
})
}
return f, errors.Join(errs...)
}
type ed25519Case struct {
name string
msg, pub, sig []byte
valid bool
}
func ed25519Cases() []ed25519Case {
seed := sha256.Sum256([]byte("DateKeys ed25519_strict vectors"))
priv := ed25519.NewKeyFromSeed(seed[:])
pub := []byte(priv.Public().(ed25519.PublicKey))
msg := []byte("DateKeys")
sig := ed25519.Sign(priv, msg)
cases := []ed25519Case{{"a valid signature", msg, pub, sig, true}}
// S + ℓ is below 2^253, so its top bits are clear, but S is not canonical.
s := leInt(sig[32:])
s.Add(s, edL)
cases = append(cases, ed25519Case{"S + ℓ", msg, pub, slices.Concat(sig[:32], leBytes(s)), false})
high := slices.Clone(sig)
high[63] |= 0x20
cases = append(cases, ed25519Case{"S with bit 253 set", msg, pub, high, false})
r := slices.Clone(sig)
copy(r[:32], nonCanonicalZero(0))
cases = append(cases, ed25519Case{"R not canonical", msg, pub, r, false})
// A of small order, R the identity and S = 0: [S]B − [k]A = −[k]A is
// the identity when the order of A divides k, so a message is searched.
identity := edEncode(edPoint{big.NewInt(0), big.NewInt(1)})
forged := slices.Concat(identity, make([]byte, 32))
for i, t := range edTorsion() {
a := edEncode(t)
m := messageFor(identity, a, func(k *big.Int) bool { return new(big.Int).Mod(k, big.NewInt(8)).Sign() == 0 })
cases = append(cases, ed25519Case{fmt.Sprintf("A of small order, the point %d of the torsion, R the identity and S = 0", i), m, a, forged, false})
}
// The same with A not canonical: y = p + 0, a point of order 4, with
// either sign; and the identity with its sign bit set.
for _, sign := range []byte{0, 0x80} {
a := nonCanonicalZero(sign)
m := messageFor(identity, a, func(k *big.Int) bool { return new(big.Int).Mod(k, big.NewInt(8)).Sign() == 0 })
cases = append(cases, ed25519Case{fmt.Sprintf("A not canonical, y = p, sign %d, R the identity and S = 0", sign>>7), m, a, forged, false})
}
negZero := slices.Clone(identity)
negZero[31] |= 0x80
m := messageFor(identity, negZero, func(*big.Int) bool { return true })
cases = append(cases, ed25519Case{"A the identity with the sign bit, R the identity and S = 0", m, negZero, forged, false})
// A of mixed order, [a]B plus a point of order 8: the equation without
// the cofactor holds only when [k]T is the identity, that is, when 8
// divides k; the equation with the cofactor holds always.
a := new(big.Int).Mod(leInt(seed[:]), edL)
t8 := edTorsion()[edOrder8]
mixed := edAdd(edMul(a, edBase()), t8)
am := edEncode(mixed)
rr := new(big.Int).Mod(leInt(slices.Concat(seed[:], seed[:])), edL)
rp := edEncode(edMul(rr, edBase()))
for _, holds := range []bool{true, false} {
m := messageFor(rp, am, func(k *big.Int) bool { return (new(big.Int).Mod(k, big.NewInt(8)).Sign() == 0) == holds })
k := hramScalar(rp, am, m)
sv := new(big.Int).Mod(new(big.Int).Add(rr, new(big.Int).Mul(k, a)), edL)
name := "A of mixed order, 8 divides k: the equation without the cofactor holds"
if !holds {
name = "A of mixed order, 8 does not divide k: only the equation with the cofactor holds"
}
cases = append(cases, ed25519Case{name, m, am, slices.Concat(rp, leBytes(sv)), holds})
}
// R the identity with A of prime order: S = k·a makes [S]B − [k]A the
// identity, which is R; libsodium rejects an R of small order, and this
// profile does not.
ap := edEncode(edMul(a, edBase()))
m = []byte("DateKeys: R the identity")
k := hramScalar(identity, ap, m)
sv := new(big.Int).Mod(new(big.Int).Mul(k, a), edL)
cases = append(cases, ed25519Case{"R the identity, A of prime order", m, ap, slices.Concat(identity, leBytes(sv)), true})
return cases
}
// messageFor returns the first message "DateKeys n", n = 0, 1, ..., whose k =
// SHA-512(R ‖ A ‖ M) mod ℓ satisfies ok.
func messageFor(r, a []byte, ok func(k *big.Int) bool) []byte {
for n := uint64(0); ; n++ {
m := binary.BigEndian.AppendUint64([]byte("DateKeys "), n)
if ok(hramScalar(r, a, m)) {
return m
}
}
}
func hramScalar(r, a, m []byte) *big.Int {
h := sha512.Sum512(slices.Concat(r, a, m))
return new(big.Int).Mod(leInt(h[:]), edL)
}
// nonCanonicalZero is y = p, the non-canonical encoding of y = 0, with the
// sign bit given.
func nonCanonicalZero(sign byte) []byte {
b := make([]byte, 32)
b[0] = 0xed
for i := 1; i < 31; i++ {
b[i] = 0xff
}
b[31] = 0x7f | sign
return b
}
// Arithmetic on edwards25519 with math/big, slow and simple, only to build
// these vectors: the reference never computes on points itself.
var (
edP = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 255), big.NewInt(19))
edL = func() *big.Int {
l, _ := new(big.Int).SetString("7237005577332262213973186563042994240857116359379907606001950938285454250989", 10)
return l
}()
edD = func() *big.Int {
d := new(big.Int).Mul(big.NewInt(-121665), edInv(big.NewInt(121666)))
return d.Mod(d, edP)
}()
edSqrtM1 = new(big.Int).Exp(big.NewInt(2), new(big.Int).Rsh(new(big.Int).Sub(edP, big.NewInt(1)), 2), edP)
)
// edOrder8 is the index in edTorsion of a point of order 8.
const edOrder8 = 1
type edPoint struct{ x, y *big.Int }
func edInv(x *big.Int) *big.Int {
return new(big.Int).Exp(x, new(big.Int).Sub(edP, big.NewInt(2)), edP)
}
func edAdd(a, b edPoint) edPoint {
t := new(big.Int).Mul(edD, a.x)
t.Mul(t, b.x).Mul(t, a.y).Mul(t, b.y).Mod(t, edP)
x := new(big.Int).Add(new(big.Int).Mul(a.x, b.y), new(big.Int).Mul(b.x, a.y))
x.Mul(x, edInv(new(big.Int).Add(big.NewInt(1), t))).Mod(x, edP)
y := new(big.Int).Add(new(big.Int).Mul(a.y, b.y), new(big.Int).Mul(a.x, b.x))
y.Mul(y, edInv(new(big.Int).Mod(new(big.Int).Sub(big.NewInt(1), t), edP))).Mod(y, edP)
return edPoint{x, y}
}
func edMul(k *big.Int, a edPoint) edPoint {
r := edPoint{big.NewInt(0), big.NewInt(1)}
for i := k.BitLen() - 1; i >= 0; i-- {
r = edAdd(r, r)
if k.Bit(i) == 1 {
r = edAdd(r, a)
}
}
return r
}
// edX recovers x from y and its sign bit, or nil when y is not on the curve.
func edX(y *big.Int, sign uint) *big.Int {
yy := new(big.Int).Mul(y, y)
num := new(big.Int).Sub(yy, big.NewInt(1))
den := new(big.Int).Add(new(big.Int).Mul(edD, yy), big.NewInt(1))
xx := new(big.Int).Mul(num, edInv(den.Mod(den, edP)))
xx.Mod(xx, edP)
if xx.Sign() == 0 {
return big.NewInt(0)
}
x := new(big.Int).Exp(xx, new(big.Int).Rsh(new(big.Int).Add(edP, big.NewInt(3)), 3), edP)
if new(big.Int).Mod(new(big.Int).Sub(new(big.Int).Mul(x, x), xx), edP).Sign() != 0 {
x.Mul(x, edSqrtM1).Mod(x, edP)
}
if new(big.Int).Mod(new(big.Int).Sub(new(big.Int).Mul(x, x), xx), edP).Sign() != 0 {
return nil
}
if x.Bit(0) != sign {
x.Sub(edP, x)
}
return x
}
func edBase() edPoint {
y := new(big.Int).Mul(big.NewInt(4), edInv(big.NewInt(5)))
y.Mod(y, edP)
return edPoint{edX(y, 0), y}
}
func edEncode(a edPoint) []byte {
b := leBytes(a.y)
b[31] |= byte(a.x.Bit(0)) << 7
return b
}
// edTorsion returns the eight points of small order, [i]T for a point T of
// order 8 and i from 0 to 7: T is [ℓ]P for the first point P, by y, whose
// [ℓ]P is not of order 4 or less.
func edTorsion() []edPoint {
for y := int64(2); ; y++ {
x := edX(big.NewInt(y), 0)
if x == nil {
continue
}
t := edMul(edL, edPoint{x, big.NewInt(y)})
if q := edMul(big.NewInt(4), t); q.x.Sign() == 0 && q.y.Cmp(big.NewInt(1)) == 0 {
continue
}
out := make([]edPoint, 8)
out[0] = edPoint{big.NewInt(0), big.NewInt(1)}
for i := 1; i < 8; i++ {
out[i] = edAdd(out[i-1], t)
}
return out
}
}
func leInt(b []byte) *big.Int {
be := slices.Clone(b)
slices.Reverse(be)
return new(big.Int).SetBytes(be)
}
func leBytes(x *big.Int) []byte {
b := x.FillBytes(make([]byte, 32))
slices.Reverse(b)
return b
}
// Ed25519Torsion returns the canonical encodings of the eight points of small
// order, computed from the curve, to check the table of ed25519strict.
func Ed25519Torsion() [][32]byte {
var out [][32]byte
for _, p := range edTorsion() {
out = append(out, [32]byte(edEncode(p)))
}
return out
}

@ -178,7 +178,11 @@ func format3Vectors(dir string) error {
if err != nil {
return err
}
for name, v := range map[string]any{"paths.json": paths, "path_fold.json": fold, "head_schema.json": heads, "security.json": security} {
strict, err := testkit.Ed25519StrictVectors()
if err != nil {
return err
}
for name, v := range map[string]any{"paths.json": paths, "path_fold.json": fold, "head_schema.json": heads, "security.json": security, "ed25519_strict.json": strict} {
if err := testkit.WriteJSON(filepath.Join(dir, name), v); err != nil {
return err
}

@ -253,6 +253,10 @@ func TestVectorFilesAreCurrent(t *testing.T) {
if err != nil {
t.Fatal(err)
}
strict, err := testkit.Ed25519StrictVectors()
if err != nil {
t.Fatal(err)
}
for _, v := range []struct {
file string
want any
@ -264,6 +268,7 @@ func TestVectorFilesAreCurrent(t *testing.T) {
{"path_fold.json", fold, &testkit.PathFoldFile{}},
{"head_schema.json", heads, &testkit.HeadSchemaFile{}},
{"security.json", security, &testkit.SecurityVectorFile{}},
{"ed25519_strict.json", strict, &testkit.Ed25519StrictFile{}},
} {
if err := testkit.ReadJSON("../../testdata/vectors/"+v.file, v.got); err != nil {
t.Fatal(err)

19
testdata/README.md vendored

@ -44,6 +44,7 @@ Conventions for every file:
| `vectors/path_fold.json` | the key of R7 of segments, and their NFD | §29.5, §29.5.1 |
| `vectors/head_schema.json` | heads of format 3 and the result of decoding them | §29.4 to §29.6, §69.1 |
| `vectors/security.json` | security areas of format 3 and their verdicts | §29.3, §29.7 |
| `vectors/ed25519_strict.json` | Ed25519 signatures and the result of the strict profile of the author signature | v0.11 §29.9 |
| `vectors/mutations.json` | the mutation corpus: the 169 mutations of §64 and further cases | §63, §64 |
| `vectors/inspect_differential.json` | 5110 mutations of fourteen fixtures with the verdict of steps 1 to 8 | §63 |
| `fixtures/<name>.dkc`, `<name>.json` | official capsules and every intermediate value | §67 |
@ -380,6 +381,24 @@ signature and the seal are evaluated apart, and the first row of the table of
give F1 with the seal intact, and a seal that breaks its schema gives S2 even
with an unknown `seal_type`, which is read only from a seal that meets it.
## `vectors/ed25519_strict.json`
Ed25519 signatures, in hexadecimal, and whether the strict profile of the
author signature accepts them (spec v0.11, §29.9): the equation of RFC 8032
without the cofactor, A and R canonical, S below ℓ and A not of small order.
They follow the cases of «Taming the many EdDSAs»: S + ℓ, the top bits of S, a
non-canonical R, the eight points of small order as A, non-canonical
encodings of A, a key of mixed order with and without the cofactor, and an R
of small order with a key of prime order, which this profile accepts.
```json
{ "name": "A of small order, the point 0 of the torsion, R the identity and S = 0", "message": "…", "public_key": "0100…", "signature": "0100…", "valid": false, "stdlib": true }
```
`stdlib` is what `crypto/ed25519` of Go answers, for the record: where it is
true and `valid` is false, an implementation needs the checks of the profile
before the equation, as `internal/ed25519strict` does.
## `vectors/mutations.json`
The mutation corpus of spec §64, as frozen data. Each case is a `.dkc`, what

@ -0,0 +1,150 @@
{
"spec": "0.10",
"description": "Ed25519 signatures and the result of the strict profile of the author signature (spec v0.11, §29.9), after the cases of «Taming the many EdDSAs»; stdlib is the result of crypto/ed25519 of Go, for the record. Generated by the reference implementation. See testdata/README.md.",
"vectors": [
{
"name": "a valid signature",
"message": "446174654b657973",
"public_key": "10770005d22145b75334902d3dbd10491a6f210afa80d3937d761a56c308d5ea",
"signature": "5119b0822de6d25331d7c21c528f4945b8b77253241cc6b83c16ba265409ff0f1fdd979c37e18f4bd6bc9c1c18d4b8746ffdee1ce723e404feb1ca90288c3900",
"valid": true,
"stdlib": true
},
{
"name": "S + ℓ",
"message": "446174654b657973",
"public_key": "10770005d22145b75334902d3dbd10491a6f210afa80d3937d761a56c308d5ea",
"signature": "5119b0822de6d25331d7c21c528f4945b8b77253241cc6b83c16ba265409ff0f0cb18df95144a2a3ac5994bff6cd97896ffdee1ce723e404feb1ca90288c3910",
"valid": false,
"stdlib": false
},
{
"name": "S with bit 253 set",
"message": "446174654b657973",
"public_key": "10770005d22145b75334902d3dbd10491a6f210afa80d3937d761a56c308d5ea",
"signature": "5119b0822de6d25331d7c21c528f4945b8b77253241cc6b83c16ba265409ff0f1fdd979c37e18f4bd6bc9c1c18d4b8746ffdee1ce723e404feb1ca90288c3920",
"valid": false,
"stdlib": false
},
{
"name": "R not canonical",
"message": "446174654b657973",
"public_key": "10770005d22145b75334902d3dbd10491a6f210afa80d3937d761a56c308d5ea",
"signature": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f1fdd979c37e18f4bd6bc9c1c18d4b8746ffdee1ce723e404feb1ca90288c3900",
"valid": false,
"stdlib": false
},
{
"name": "A of small order, the point 0 of the torsion, R the identity and S = 0",
"message": "446174654b657973200000000000000002",
"public_key": "0100000000000000000000000000000000000000000000000000000000000000",
"signature": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"valid": false,
"stdlib": true
},
{
"name": "A of small order, the point 1 of the torsion, R the identity and S = 0",
"message": "446174654b65797320000000000000000d",
"public_key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
"signature": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"valid": false,
"stdlib": true
},
{
"name": "A of small order, the point 2 of the torsion, R the identity and S = 0",
"message": "446174654b657973200000000000000001",
"public_key": "0000000000000000000000000000000000000000000000000000000000000080",
"signature": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"valid": false,
"stdlib": true
},
{
"name": "A of small order, the point 3 of the torsion, R the identity and S = 0",
"message": "446174654b657973200000000000000002",
"public_key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
"signature": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"valid": false,
"stdlib": true
},
{
"name": "A of small order, the point 4 of the torsion, R the identity and S = 0",
"message": "446174654b65797320000000000000000f",
"public_key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"signature": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"valid": false,
"stdlib": true
},
{
"name": "A of small order, the point 5 of the torsion, R the identity and S = 0",
"message": "446174654b657973200000000000000019",
"public_key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
"signature": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"valid": false,
"stdlib": true
},
{
"name": "A of small order, the point 6 of the torsion, R the identity and S = 0",
"message": "446174654b657973200000000000000002",
"public_key": "0000000000000000000000000000000000000000000000000000000000000000",
"signature": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"valid": false,
"stdlib": true
},
{
"name": "A of small order, the point 7 of the torsion, R the identity and S = 0",
"message": "446174654b657973200000000000000001",
"public_key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
"signature": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"valid": false,
"stdlib": true
},
{
"name": "A not canonical, y = p, sign 0, R the identity and S = 0",
"message": "446174654b657973200000000000000002",
"public_key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"signature": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"valid": false,
"stdlib": true
},
{
"name": "A not canonical, y = p, sign 1, R the identity and S = 0",
"message": "446174654b657973200000000000000001",
"public_key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"signature": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"valid": false,
"stdlib": true
},
{
"name": "A the identity with the sign bit, R the identity and S = 0",
"message": "446174654b657973200000000000000000",
"public_key": "0100000000000000000000000000000000000000000000000000000000000080",
"signature": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"valid": false,
"stdlib": true
},
{
"name": "A of mixed order, 8 divides k: the equation without the cofactor holds",
"message": "446174654b657973200000000000000006",
"public_key": "b95f1903e3141ba54412bd1138119fbc7ab21aba35be980650ec3337b0a5c5e1",
"signature": "57890b19a0fb99cfa5fae15f2f8364a4d2d70474bea04f66c611aa5e250026a60ccf279cc11141ef368a21cfdad37a727e43f59936c20d753dea73a0e508d60e",
"valid": true,
"stdlib": true
},
{
"name": "A of mixed order, 8 does not divide k: only the equation with the cofactor holds",
"message": "446174654b657973200000000000000000",
"public_key": "b95f1903e3141ba54412bd1138119fbc7ab21aba35be980650ec3337b0a5c5e1",
"signature": "57890b19a0fb99cfa5fae15f2f8364a4d2d70474bea04f66c611aa5e250026a642a681ccd920aa6da440dc535484ec7ec2904a332dd86256e024a37f6040900f",
"valid": false,
"stdlib": false
},
{
"name": "R the identity, A of prime order",
"message": "446174654b6579733a205220746865206964656e74697479",
"public_key": "0368d6193d5242089bb4a5c019cceffe6a55d83495e57de52d7711ed864ca77d",
"signature": "01000000000000000000000000000000000000000000000000000000000000007b2ee545c254e47cbe2f2774f74d640944034cc59d1ba903de7ecb767cf95304",
"valid": true,
"stdlib": true
}
]
}
Loading…
Cancel
Save

Powered by TurnKey Linux.