|
|
// 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"
|
|
|
"math/big"
|
|
|
"slices"
|
|
|
)
|
|
|
|
|
|
// 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
|
|
|
}
|
|
|
|
|
|
// The field and the curve of edwards25519: p = 2^255 − 19, d = −121665/121666
|
|
|
// mod p, and the exponent (p − 1)/2 of Euler's criterion.
|
|
|
var curveP, curveD, halfP = func() (p, d, h *big.Int) {
|
|
|
p = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 255), big.NewInt(19))
|
|
|
d = new(big.Int).ModInverse(big.NewInt(121666), p)
|
|
|
d.Mul(d, big.NewInt(-121665)).Mod(d, p)
|
|
|
h = new(big.Int).Rsh(new(big.Int).Sub(p, big.NewInt(1)), 1)
|
|
|
return p, d, h
|
|
|
}()
|
|
|
|
|
|
// OnCurve reports whether the canonical encoding a is a point of the curve:
|
|
|
// whether x² = (y² − 1)/(d·y² + 1) has a solution modulo p (RFC 8032, 5.1.3).
|
|
|
// Verify leaves that check to crypto/ed25519; a parser of keys uses it to
|
|
|
// refuse a key that no signature could verify (spec §29.9, rule 2). d·y² + 1
|
|
|
// is never 0, because −1/d is not a square.
|
|
|
func OnCurve(a []byte) bool {
|
|
|
if len(a) != 32 {
|
|
|
return false
|
|
|
}
|
|
|
be := slices.Clone(a)
|
|
|
be[31] &= 0x7f
|
|
|
slices.Reverse(be)
|
|
|
y := new(big.Int).SetBytes(be)
|
|
|
y2 := new(big.Int).Mul(y, y)
|
|
|
u := new(big.Int).Sub(y2, big.NewInt(1))
|
|
|
v := new(big.Int).Mul(curveD, y2)
|
|
|
v.Add(v, big.NewInt(1)).Mod(v, curveP)
|
|
|
x2 := u.Mul(u, v.ModInverse(v, curveP))
|
|
|
x2.Mod(x2, curveP)
|
|
|
return x2.Sign() == 0 || new(big.Int).Exp(x2, halfP, curveP).Cmp(big.NewInt(1)) == 0
|
|
|
}
|
|
|
|
|
|
// 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 }
|