// 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 }