mirror of https://github.com/golang/go.git
crypto/ecdsa: move implementation to crypto/internal/fips/ecdsa
For #69536 Change-Id: I8794d75c11cdadd91e420541b26af35e62006af4 Reviewed-on: https://go-review.googlesource.com/c/go/+/628677 Auto-Submit: Filippo Valsorda <filippo@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Russ Cox <rsc@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
6f5194767e
commit
03f075b56e
|
|
@ -24,7 +24,6 @@ package ecdsa
|
|||
// [SEC 1, Version 2.0]: https://www.secg.org/sec1-v2.pdf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
|
|
@ -32,15 +31,13 @@ import (
|
|||
"crypto/elliptic"
|
||||
"crypto/internal/boring"
|
||||
"crypto/internal/boring/bbig"
|
||||
"crypto/internal/fips/bigmod"
|
||||
"crypto/internal/fips/nistec"
|
||||
"crypto/internal/fips/ecdsa"
|
||||
"crypto/internal/randutil"
|
||||
"crypto/sha512"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"io"
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
"golang.org/x/crypto/cryptobyte/asn1"
|
||||
|
|
@ -173,78 +170,26 @@ func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) {
|
|||
|
||||
switch c.Params() {
|
||||
case elliptic.P224().Params():
|
||||
return generateNISTEC(p224(), rand)
|
||||
return generateFIPS(c, ecdsa.P224(), rand)
|
||||
case elliptic.P256().Params():
|
||||
return generateNISTEC(p256(), rand)
|
||||
return generateFIPS(c, ecdsa.P256(), rand)
|
||||
case elliptic.P384().Params():
|
||||
return generateNISTEC(p384(), rand)
|
||||
return generateFIPS(c, ecdsa.P384(), rand)
|
||||
case elliptic.P521().Params():
|
||||
return generateNISTEC(p521(), rand)
|
||||
return generateFIPS(c, ecdsa.P521(), rand)
|
||||
default:
|
||||
return generateLegacy(c, rand)
|
||||
}
|
||||
}
|
||||
|
||||
func generateNISTEC[Point nistPoint[Point]](c *nistCurve[Point], rand io.Reader) (*PrivateKey, error) {
|
||||
k, Q, err := randomPoint(c, rand)
|
||||
func generateFIPS[P ecdsa.Point[P]](curve elliptic.Curve, c *ecdsa.Curve[P], rand io.Reader) (*PrivateKey, error) {
|
||||
privateKey, err := ecdsa.GenerateKey(c, rand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
priv := new(PrivateKey)
|
||||
priv.PublicKey.Curve = c.curve
|
||||
priv.D = new(big.Int).SetBytes(k.Bytes(c.N))
|
||||
priv.PublicKey.X, priv.PublicKey.Y, err = c.pointToAffine(Q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return priv, nil
|
||||
return privateKeyFromFIPS(curve, privateKey)
|
||||
}
|
||||
|
||||
// randomPoint returns a random scalar and the corresponding point using the
|
||||
// procedure given in FIPS 186-4, Appendix B.5.2 (rejection sampling).
|
||||
func randomPoint[Point nistPoint[Point]](c *nistCurve[Point], rand io.Reader) (k *bigmod.Nat, p Point, err error) {
|
||||
k = bigmod.NewNat()
|
||||
for {
|
||||
b := make([]byte, c.N.Size())
|
||||
if _, err = io.ReadFull(rand, b); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Mask off any excess bits to increase the chance of hitting a value in
|
||||
// (0, N). These are the most dangerous lines in the package and maybe in
|
||||
// the library: a single bit of bias in the selection of nonces would likely
|
||||
// lead to key recovery, but no tests would fail. Look but DO NOT TOUCH.
|
||||
if excess := len(b)*8 - c.N.BitLen(); excess > 0 {
|
||||
// Just to be safe, assert that this only happens for the one curve that
|
||||
// doesn't have a round number of bits.
|
||||
if excess != 0 && c.curve.Params().Name != "P-521" {
|
||||
panic("ecdsa: internal error: unexpectedly masking off bits")
|
||||
}
|
||||
b[0] >>= excess
|
||||
}
|
||||
|
||||
// FIPS 186-4 makes us check k <= N - 2 and then add one.
|
||||
// Checking 0 < k <= N - 1 is strictly equivalent.
|
||||
// None of this matters anyway because the chance of selecting
|
||||
// zero is cryptographically negligible.
|
||||
if _, err = k.SetBytes(b, c.N); err == nil && k.IsZero() == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if testingOnlyRejectionSamplingLooped != nil {
|
||||
testingOnlyRejectionSamplingLooped()
|
||||
}
|
||||
}
|
||||
|
||||
p, err = c.newPoint().ScalarBaseMult(k.Bytes(c.N))
|
||||
return
|
||||
}
|
||||
|
||||
// testingOnlyRejectionSamplingLooped is called when rejection sampling in
|
||||
// randomPoint rejects a candidate for being higher than the modulus.
|
||||
var testingOnlyRejectionSamplingLooped func()
|
||||
|
||||
// errNoAsm is returned by signAsm and verifyAsm when the assembly
|
||||
// implementation is not available.
|
||||
var errNoAsm = errors.New("no assembly implementation available")
|
||||
|
|
@ -280,63 +225,28 @@ func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) {
|
|||
|
||||
switch priv.Curve.Params() {
|
||||
case elliptic.P224().Params():
|
||||
return signNISTEC(p224(), priv, csprng, hash)
|
||||
return signFIPS(ecdsa.P224(), priv, csprng, hash)
|
||||
case elliptic.P256().Params():
|
||||
return signNISTEC(p256(), priv, csprng, hash)
|
||||
return signFIPS(ecdsa.P256(), priv, csprng, hash)
|
||||
case elliptic.P384().Params():
|
||||
return signNISTEC(p384(), priv, csprng, hash)
|
||||
return signFIPS(ecdsa.P384(), priv, csprng, hash)
|
||||
case elliptic.P521().Params():
|
||||
return signNISTEC(p521(), priv, csprng, hash)
|
||||
return signFIPS(ecdsa.P521(), priv, csprng, hash)
|
||||
default:
|
||||
return signLegacy(priv, csprng, hash)
|
||||
}
|
||||
}
|
||||
|
||||
func signNISTEC[Point nistPoint[Point]](c *nistCurve[Point], priv *PrivateKey, csprng io.Reader, hash []byte) (sig []byte, err error) {
|
||||
// SEC 1, Version 2.0, Section 4.1.3
|
||||
|
||||
k, R, err := randomPoint(c, csprng)
|
||||
func signFIPS[P ecdsa.Point[P]](c *ecdsa.Curve[P], priv *PrivateKey, csprng io.Reader, hash []byte) ([]byte, error) {
|
||||
k, err := privateKeyToFIPS(c, priv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// kInv = k⁻¹
|
||||
kInv := bigmod.NewNat()
|
||||
inverse(c, kInv, k)
|
||||
|
||||
Rx, err := R.BytesX()
|
||||
sig, err := ecdsa.Sign(c, k, csprng, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err := bigmod.NewNat().SetOverflowingBytes(Rx, c.N)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The spec wants us to retry here, but the chance of hitting this condition
|
||||
// on a large prime-order group like the NIST curves we support is
|
||||
// cryptographically negligible. If we hit it, something is awfully wrong.
|
||||
if r.IsZero() == 1 {
|
||||
return nil, errors.New("ecdsa: internal error: r is zero")
|
||||
}
|
||||
|
||||
e := bigmod.NewNat()
|
||||
hashToNat(c, e, hash)
|
||||
|
||||
s, err := bigmod.NewNat().SetBytes(priv.D.Bytes(), c.N)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Mul(r, c.N)
|
||||
s.Add(e, c.N)
|
||||
s.Mul(kInv, c.N)
|
||||
|
||||
// Again, the chance of this happening is cryptographically negligible.
|
||||
if s.IsZero() == 1 {
|
||||
return nil, errors.New("ecdsa: internal error: s is zero")
|
||||
}
|
||||
|
||||
return encodeSignature(r.Bytes(c.N), s.Bytes(c.N))
|
||||
return encodeSignature(sig.R, sig.S)
|
||||
}
|
||||
|
||||
func encodeSignature(r, s []byte) ([]byte, error) {
|
||||
|
|
@ -366,50 +276,6 @@ func addASN1IntBytes(b *cryptobyte.Builder, bytes []byte) {
|
|||
})
|
||||
}
|
||||
|
||||
// inverse sets kInv to the inverse of k modulo the order of the curve.
|
||||
func inverse[Point nistPoint[Point]](c *nistCurve[Point], kInv, k *bigmod.Nat) {
|
||||
if c.curve.Params().Name == "P-256" {
|
||||
kBytes, err := nistec.P256OrdInverse(k.Bytes(c.N))
|
||||
// Some platforms don't implement P256OrdInverse, and always return an error.
|
||||
if err == nil {
|
||||
_, err := kInv.SetBytes(kBytes, c.N)
|
||||
if err != nil {
|
||||
panic("ecdsa: internal error: P256OrdInverse produced an invalid value")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the inverse of s in GF(N) using Fermat's method
|
||||
// (exponentiation modulo P - 2, per Euler's theorem)
|
||||
kInv.Exp(k, c.nMinus2, c.N)
|
||||
}
|
||||
|
||||
// hashToNat sets e to the left-most bits of hash, according to
|
||||
// SEC 1, Section 4.1.3, point 5 and Section 4.1.4, point 3.
|
||||
func hashToNat[Point nistPoint[Point]](c *nistCurve[Point], e *bigmod.Nat, hash []byte) {
|
||||
// ECDSA asks us to take the left-most log2(N) bits of hash, and use them as
|
||||
// an integer modulo N. This is the absolute worst of all worlds: we still
|
||||
// have to reduce, because the result might still overflow N, but to take
|
||||
// the left-most bits for P-521 we have to do a right shift.
|
||||
if size := c.N.Size(); len(hash) >= size {
|
||||
hash = hash[:size]
|
||||
if excess := len(hash)*8 - c.N.BitLen(); excess > 0 {
|
||||
hash = bytes.Clone(hash)
|
||||
for i := len(hash) - 1; i >= 0; i-- {
|
||||
hash[i] >>= excess
|
||||
if i > 0 {
|
||||
hash[i] |= hash[i-1] << (8 - excess)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_, err := e.SetOverflowingBytes(hash, c.N)
|
||||
if err != nil {
|
||||
panic("ecdsa: internal error: truncated hash is too long")
|
||||
}
|
||||
}
|
||||
|
||||
// mixedCSPRNG returns a CSPRNG that mixes entropy from rand with the message
|
||||
// and the private key, to protect the key in case rand fails. This is
|
||||
// equivalent in security to RFC 6979 deterministic nonce generation, but still
|
||||
|
|
@ -486,69 +352,31 @@ func VerifyASN1(pub *PublicKey, hash, sig []byte) bool {
|
|||
|
||||
switch pub.Curve.Params() {
|
||||
case elliptic.P224().Params():
|
||||
return verifyNISTEC(p224(), pub, hash, sig)
|
||||
return verifyFIPS(ecdsa.P224(), pub, hash, sig)
|
||||
case elliptic.P256().Params():
|
||||
return verifyNISTEC(p256(), pub, hash, sig)
|
||||
return verifyFIPS(ecdsa.P256(), pub, hash, sig)
|
||||
case elliptic.P384().Params():
|
||||
return verifyNISTEC(p384(), pub, hash, sig)
|
||||
return verifyFIPS(ecdsa.P384(), pub, hash, sig)
|
||||
case elliptic.P521().Params():
|
||||
return verifyNISTEC(p521(), pub, hash, sig)
|
||||
return verifyFIPS(ecdsa.P521(), pub, hash, sig)
|
||||
default:
|
||||
return verifyLegacy(pub, hash, sig)
|
||||
}
|
||||
}
|
||||
|
||||
func verifyNISTEC[Point nistPoint[Point]](c *nistCurve[Point], pub *PublicKey, hash, sig []byte) bool {
|
||||
rBytes, sBytes, err := parseSignature(sig)
|
||||
func verifyFIPS[P ecdsa.Point[P]](c *ecdsa.Curve[P], pub *PublicKey, hash, sig []byte) bool {
|
||||
r, s, err := parseSignature(sig)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
Q, err := c.pointFromAffine(pub.X, pub.Y)
|
||||
k, err := publicKeyToFIPS(c, pub)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// SEC 1, Version 2.0, Section 4.1.4
|
||||
|
||||
r, err := bigmod.NewNat().SetBytes(rBytes, c.N)
|
||||
if err != nil || r.IsZero() == 1 {
|
||||
if err := ecdsa.Verify(c, k, hash, &ecdsa.Signature{R: r, S: s}); err != nil {
|
||||
return false
|
||||
}
|
||||
s, err := bigmod.NewNat().SetBytes(sBytes, c.N)
|
||||
if err != nil || s.IsZero() == 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
e := bigmod.NewNat()
|
||||
hashToNat(c, e, hash)
|
||||
|
||||
// w = s⁻¹
|
||||
w := bigmod.NewNat()
|
||||
inverse(c, w, s)
|
||||
|
||||
// p₁ = [e * s⁻¹]G
|
||||
p1, err := c.newPoint().ScalarBaseMult(e.Mul(w, c.N).Bytes(c.N))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// p₂ = [r * s⁻¹]Q
|
||||
p2, err := Q.ScalarMult(Q, w.Mul(r, c.N).Bytes(c.N))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// BytesX returns an error for the point at infinity.
|
||||
Rx, err := p1.Add(p1, p2).BytesX()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
v, err := bigmod.NewNat().SetOverflowingBytes(Rx, c.N)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return v.Equal(r) == 1
|
||||
return true
|
||||
}
|
||||
|
||||
func parseSignature(sig []byte) (r, s []byte, err error) {
|
||||
|
|
@ -564,32 +392,47 @@ func parseSignature(sig []byte) (r, s []byte, err error) {
|
|||
return r, s, nil
|
||||
}
|
||||
|
||||
type nistCurve[Point nistPoint[Point]] struct {
|
||||
newPoint func() Point
|
||||
curve elliptic.Curve
|
||||
N *bigmod.Modulus
|
||||
nMinus2 []byte
|
||||
func publicKeyFromFIPS(curve elliptic.Curve, pub *ecdsa.PublicKey) (*PublicKey, error) {
|
||||
x, y, err := pointToAffine(curve, pub.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PublicKey{Curve: curve, X: x, Y: y}, nil
|
||||
}
|
||||
|
||||
// nistPoint is a generic constraint for the nistec Point types.
|
||||
type nistPoint[T any] interface {
|
||||
Bytes() []byte
|
||||
BytesX() ([]byte, error)
|
||||
SetBytes([]byte) (T, error)
|
||||
Add(T, T) T
|
||||
ScalarMult(T, []byte) (T, error)
|
||||
ScalarBaseMult([]byte) (T, error)
|
||||
func privateKeyFromFIPS(curve elliptic.Curve, priv *ecdsa.PrivateKey) (*PrivateKey, error) {
|
||||
pub, err := publicKeyFromFIPS(curve, priv.PublicKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PrivateKey{PublicKey: *pub, D: new(big.Int).SetBytes(priv.Bytes())}, nil
|
||||
}
|
||||
|
||||
// pointFromAffine is used to convert the PublicKey to a nistec Point.
|
||||
func (curve *nistCurve[Point]) pointFromAffine(x, y *big.Int) (p Point, err error) {
|
||||
bitSize := curve.curve.Params().BitSize
|
||||
func publicKeyToFIPS[P ecdsa.Point[P]](c *ecdsa.Curve[P], pub *PublicKey) (*ecdsa.PublicKey, error) {
|
||||
Q, err := pointFromAffine(pub.Curve, pub.X, pub.Y)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ecdsa.NewPublicKey(c, Q)
|
||||
}
|
||||
|
||||
func privateKeyToFIPS[P ecdsa.Point[P]](c *ecdsa.Curve[P], priv *PrivateKey) (*ecdsa.PrivateKey, error) {
|
||||
Q, err := pointFromAffine(priv.Curve, priv.X, priv.Y)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ecdsa.NewPrivateKey(c, priv.D.Bytes(), Q)
|
||||
}
|
||||
|
||||
// pointFromAffine is used to convert the PublicKey to a nistec SetBytes input.
|
||||
func pointFromAffine(curve elliptic.Curve, x, y *big.Int) ([]byte, error) {
|
||||
bitSize := curve.Params().BitSize
|
||||
// Reject values that would not get correctly encoded.
|
||||
if x.Sign() < 0 || y.Sign() < 0 {
|
||||
return p, errors.New("negative coordinate")
|
||||
return nil, errors.New("negative coordinate")
|
||||
}
|
||||
if x.BitLen() > bitSize || y.BitLen() > bitSize {
|
||||
return p, errors.New("overflowing coordinate")
|
||||
return nil, errors.New("overflowing coordinate")
|
||||
}
|
||||
// Encode the coordinates and let SetBytes reject invalid points.
|
||||
byteLen := (bitSize + 7) / 8
|
||||
|
|
@ -597,81 +440,17 @@ func (curve *nistCurve[Point]) pointFromAffine(x, y *big.Int) (p Point, err erro
|
|||
buf[0] = 4 // uncompressed point
|
||||
x.FillBytes(buf[1 : 1+byteLen])
|
||||
y.FillBytes(buf[1+byteLen : 1+2*byteLen])
|
||||
return curve.newPoint().SetBytes(buf)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// pointToAffine is used to convert a nistec Point to a PublicKey.
|
||||
func (curve *nistCurve[Point]) pointToAffine(p Point) (x, y *big.Int, err error) {
|
||||
out := p.Bytes()
|
||||
if len(out) == 1 && out[0] == 0 {
|
||||
// pointToAffine is used to convert a nistec Bytes encoding to a PublicKey.
|
||||
func pointToAffine(curve elliptic.Curve, p []byte) (x, y *big.Int, err error) {
|
||||
if len(p) == 1 && p[0] == 0 {
|
||||
// This is the encoding of the point at infinity.
|
||||
return nil, nil, errors.New("ecdsa: public key point is the infinity")
|
||||
}
|
||||
byteLen := (curve.curve.Params().BitSize + 7) / 8
|
||||
x = new(big.Int).SetBytes(out[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(out[1+byteLen:])
|
||||
byteLen := (curve.Params().BitSize + 7) / 8
|
||||
x = new(big.Int).SetBytes(p[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(p[1+byteLen:])
|
||||
return x, y, nil
|
||||
}
|
||||
|
||||
var p224Once sync.Once
|
||||
var _p224 *nistCurve[*nistec.P224Point]
|
||||
|
||||
func p224() *nistCurve[*nistec.P224Point] {
|
||||
p224Once.Do(func() {
|
||||
_p224 = &nistCurve[*nistec.P224Point]{
|
||||
newPoint: func() *nistec.P224Point { return nistec.NewP224Point() },
|
||||
}
|
||||
precomputeParams(_p224, elliptic.P224())
|
||||
})
|
||||
return _p224
|
||||
}
|
||||
|
||||
var p256Once sync.Once
|
||||
var _p256 *nistCurve[*nistec.P256Point]
|
||||
|
||||
func p256() *nistCurve[*nistec.P256Point] {
|
||||
p256Once.Do(func() {
|
||||
_p256 = &nistCurve[*nistec.P256Point]{
|
||||
newPoint: func() *nistec.P256Point { return nistec.NewP256Point() },
|
||||
}
|
||||
precomputeParams(_p256, elliptic.P256())
|
||||
})
|
||||
return _p256
|
||||
}
|
||||
|
||||
var p384Once sync.Once
|
||||
var _p384 *nistCurve[*nistec.P384Point]
|
||||
|
||||
func p384() *nistCurve[*nistec.P384Point] {
|
||||
p384Once.Do(func() {
|
||||
_p384 = &nistCurve[*nistec.P384Point]{
|
||||
newPoint: func() *nistec.P384Point { return nistec.NewP384Point() },
|
||||
}
|
||||
precomputeParams(_p384, elliptic.P384())
|
||||
})
|
||||
return _p384
|
||||
}
|
||||
|
||||
var p521Once sync.Once
|
||||
var _p521 *nistCurve[*nistec.P521Point]
|
||||
|
||||
func p521() *nistCurve[*nistec.P521Point] {
|
||||
p521Once.Do(func() {
|
||||
_p521 = &nistCurve[*nistec.P521Point]{
|
||||
newPoint: func() *nistec.P521Point { return nistec.NewP521Point() },
|
||||
}
|
||||
precomputeParams(_p521, elliptic.P521())
|
||||
})
|
||||
return _p521
|
||||
}
|
||||
|
||||
func precomputeParams[Point nistPoint[Point]](c *nistCurve[Point], curve elliptic.Curve) {
|
||||
params := curve.Params()
|
||||
c.curve = curve
|
||||
var err error
|
||||
c.N, err = bigmod.NewModulus(params.N.Bytes())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c.nMinus2 = new(big.Int).Sub(params.N, big.NewInt(2)).Bytes()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,8 @@ package ecdsa
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/bzip2"
|
||||
"crypto/elliptic"
|
||||
"crypto/internal/fips/bigmod"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
|
|
@ -339,80 +337,6 @@ func testZeroHashSignature(t *testing.T, curve elliptic.Curve) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRandomPoint(t *testing.T) {
|
||||
t.Run("P-224", func(t *testing.T) { testRandomPoint(t, p224()) })
|
||||
t.Run("P-256", func(t *testing.T) { testRandomPoint(t, p256()) })
|
||||
t.Run("P-384", func(t *testing.T) { testRandomPoint(t, p384()) })
|
||||
t.Run("P-521", func(t *testing.T) { testRandomPoint(t, p521()) })
|
||||
}
|
||||
|
||||
func testRandomPoint[Point nistPoint[Point]](t *testing.T, c *nistCurve[Point]) {
|
||||
t.Cleanup(func() { testingOnlyRejectionSamplingLooped = nil })
|
||||
var loopCount int
|
||||
testingOnlyRejectionSamplingLooped = func() { loopCount++ }
|
||||
|
||||
// A sequence of all ones will generate 2^N-1, which should be rejected.
|
||||
// (Unless, for example, we are masking too many bits.)
|
||||
r := io.MultiReader(bytes.NewReader(bytes.Repeat([]byte{0xff}, 100)), rand.Reader)
|
||||
if k, p, err := randomPoint(c, r); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if k.IsZero() == 1 {
|
||||
t.Error("k is zero")
|
||||
} else if p.Bytes()[0] != 4 {
|
||||
t.Error("p is infinity")
|
||||
}
|
||||
if loopCount == 0 {
|
||||
t.Error("overflow was not rejected")
|
||||
}
|
||||
loopCount = 0
|
||||
|
||||
// A sequence of all zeroes will generate zero, which should be rejected.
|
||||
r = io.MultiReader(bytes.NewReader(bytes.Repeat([]byte{0}, 100)), rand.Reader)
|
||||
if k, p, err := randomPoint(c, r); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if k.IsZero() == 1 {
|
||||
t.Error("k is zero")
|
||||
} else if p.Bytes()[0] != 4 {
|
||||
t.Error("p is infinity")
|
||||
}
|
||||
if loopCount == 0 {
|
||||
t.Error("zero was not rejected")
|
||||
}
|
||||
loopCount = 0
|
||||
|
||||
// P-256 has a 2⁻³² chance or randomly hitting a rejection. For P-224 it's
|
||||
// 2⁻¹¹², for P-384 it's 2⁻¹⁹⁴, and for P-521 it's 2⁻²⁶², so if we hit in
|
||||
// tests, something is horribly wrong. (For example, we are masking the
|
||||
// wrong bits.)
|
||||
if c.curve == elliptic.P256() {
|
||||
return
|
||||
}
|
||||
if k, p, err := randomPoint(c, rand.Reader); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if k.IsZero() == 1 {
|
||||
t.Error("k is zero")
|
||||
} else if p.Bytes()[0] != 4 {
|
||||
t.Error("p is infinity")
|
||||
}
|
||||
if loopCount > 0 {
|
||||
t.Error("unexpected rejection")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashToNat(t *testing.T) {
|
||||
t.Run("P-224", func(t *testing.T) { testHashToNat(t, p224()) })
|
||||
t.Run("P-256", func(t *testing.T) { testHashToNat(t, p256()) })
|
||||
t.Run("P-384", func(t *testing.T) { testHashToNat(t, p384()) })
|
||||
t.Run("P-521", func(t *testing.T) { testHashToNat(t, p521()) })
|
||||
}
|
||||
|
||||
func testHashToNat[Point nistPoint[Point]](t *testing.T, c *nistCurve[Point]) {
|
||||
for l := 0; l < 600; l++ {
|
||||
h := bytes.Repeat([]byte{0xff}, l)
|
||||
hashToNat(c, bigmod.NewNat(), h)
|
||||
}
|
||||
}
|
||||
|
||||
func TestZeroSignature(t *testing.T) {
|
||||
testAllCurves(t, testZeroSignature)
|
||||
}
|
||||
|
|
@ -494,25 +418,6 @@ func testRMinusNSignature(t *testing.T, curve elliptic.Curve) {
|
|||
}
|
||||
}
|
||||
|
||||
func randomPointForCurve(curve elliptic.Curve, rand io.Reader) error {
|
||||
switch curve.Params() {
|
||||
case elliptic.P224().Params():
|
||||
_, _, err := randomPoint(p224(), rand)
|
||||
return err
|
||||
case elliptic.P256().Params():
|
||||
_, _, err := randomPoint(p256(), rand)
|
||||
return err
|
||||
case elliptic.P384().Params():
|
||||
_, _, err := randomPoint(p384(), rand)
|
||||
return err
|
||||
case elliptic.P521().Params():
|
||||
_, _, err := randomPoint(p521(), rand)
|
||||
return err
|
||||
default:
|
||||
panic("unknown curve")
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkAllCurves(b *testing.B, f func(*testing.B, elliptic.Curve)) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
|
|||
|
|
@ -0,0 +1,416 @@
|
|||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ecdsa
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/internal/fips/bigmod"
|
||||
"crypto/internal/fips/nistec"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// PrivateKey and PublicKey are not generic to make it possible to use them
|
||||
// in other types without instantiating them with a specific point type.
|
||||
// They are tied to one of the Curve types below through the curveID field.
|
||||
|
||||
type PrivateKey struct {
|
||||
pub PublicKey
|
||||
d []byte // bigmod.(*Nat).Bytes output (fixed length)
|
||||
}
|
||||
|
||||
func (priv *PrivateKey) Bytes() []byte {
|
||||
return priv.d
|
||||
}
|
||||
|
||||
func (priv *PrivateKey) PublicKey() *PublicKey {
|
||||
return &priv.pub
|
||||
}
|
||||
|
||||
type PublicKey struct {
|
||||
curve curveID
|
||||
q []byte // uncompressed nistec Point.Bytes output
|
||||
}
|
||||
|
||||
func (pub *PublicKey) Bytes() []byte {
|
||||
return pub.q
|
||||
}
|
||||
|
||||
type curveID string
|
||||
|
||||
const (
|
||||
p224 curveID = "P-224"
|
||||
p256 curveID = "P-256"
|
||||
p384 curveID = "P-384"
|
||||
p521 curveID = "P-521"
|
||||
)
|
||||
|
||||
type Curve[P Point[P]] struct {
|
||||
curve curveID
|
||||
newPoint func() P
|
||||
ordInverse func([]byte) ([]byte, error)
|
||||
N *bigmod.Modulus
|
||||
nMinus2 []byte
|
||||
}
|
||||
|
||||
// Point is a generic constraint for the [nistec] Point types.
|
||||
type Point[P any] interface {
|
||||
*nistec.P224Point | *nistec.P256Point | *nistec.P384Point | *nistec.P521Point
|
||||
Bytes() []byte
|
||||
BytesX() ([]byte, error)
|
||||
SetBytes([]byte) (P, error)
|
||||
ScalarMult(P, []byte) (P, error)
|
||||
ScalarBaseMult([]byte) (P, error)
|
||||
Add(p1, p2 P) P
|
||||
}
|
||||
|
||||
func precomputeParams[P Point[P]](c *Curve[P], order []byte) {
|
||||
var err error
|
||||
c.N, err = bigmod.NewModulus(order)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
two, _ := bigmod.NewNat().SetBytes([]byte{2}, c.N)
|
||||
c.nMinus2 = bigmod.NewNat().ExpandFor(c.N).Sub(two, c.N).Bytes(c.N)
|
||||
}
|
||||
|
||||
func P224() *Curve[*nistec.P224Point] { return _P224() }
|
||||
|
||||
var _P224 = sync.OnceValue(func() *Curve[*nistec.P224Point] {
|
||||
c := &Curve[*nistec.P224Point]{
|
||||
curve: p224,
|
||||
newPoint: nistec.NewP224Point,
|
||||
}
|
||||
precomputeParams(c, p224Order)
|
||||
return c
|
||||
})
|
||||
|
||||
var p224Order = []byte{
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
|
||||
0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
|
||||
0x5c, 0x5c, 0x2a, 0x3d,
|
||||
}
|
||||
|
||||
func P256() *Curve[*nistec.P256Point] { return _P256() }
|
||||
|
||||
var _P256 = sync.OnceValue(func() *Curve[*nistec.P256Point] {
|
||||
c := &Curve[*nistec.P256Point]{
|
||||
curve: p256,
|
||||
newPoint: nistec.NewP256Point,
|
||||
ordInverse: nistec.P256OrdInverse,
|
||||
}
|
||||
precomputeParams(c, p256Order)
|
||||
return c
|
||||
})
|
||||
|
||||
var p256Order = []byte{
|
||||
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84,
|
||||
0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51}
|
||||
|
||||
func P384() *Curve[*nistec.P384Point] { return _P384() }
|
||||
|
||||
var _P384 = sync.OnceValue(func() *Curve[*nistec.P384Point] {
|
||||
c := &Curve[*nistec.P384Point]{
|
||||
curve: p384,
|
||||
newPoint: nistec.NewP384Point,
|
||||
}
|
||||
precomputeParams(c, p384Order)
|
||||
return c
|
||||
})
|
||||
|
||||
var p384Order = []byte{
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf,
|
||||
0x58, 0x1a, 0x0d, 0xb2, 0x48, 0xb0, 0xa7, 0x7a,
|
||||
0xec, 0xec, 0x19, 0x6a, 0xcc, 0xc5, 0x29, 0x73}
|
||||
|
||||
func P521() *Curve[*nistec.P521Point] { return _P521() }
|
||||
|
||||
var _P521 = sync.OnceValue(func() *Curve[*nistec.P521Point] {
|
||||
c := &Curve[*nistec.P521Point]{
|
||||
curve: p521,
|
||||
newPoint: nistec.NewP521Point,
|
||||
}
|
||||
precomputeParams(c, p521Order)
|
||||
return c
|
||||
})
|
||||
|
||||
var p521Order = []byte{0x01, 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, 0xff, 0xfa,
|
||||
0x51, 0x86, 0x87, 0x83, 0xbf, 0x2f, 0x96, 0x6b,
|
||||
0x7f, 0xcc, 0x01, 0x48, 0xf7, 0x09, 0xa5, 0xd0,
|
||||
0x3b, 0xb5, 0xc9, 0xb8, 0x89, 0x9c, 0x47, 0xae,
|
||||
0xbb, 0x6f, 0xb7, 0x1e, 0x91, 0x38, 0x64, 0x09}
|
||||
|
||||
func NewPrivateKey[P Point[P]](c *Curve[P], D, Q []byte) (*PrivateKey, error) {
|
||||
_, err := c.newPoint().SetBytes(Q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d, err := bigmod.NewNat().SetBytes(D, c.N)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PrivateKey{
|
||||
pub: PublicKey{
|
||||
curve: c.curve,
|
||||
q: Q,
|
||||
},
|
||||
d: d.Bytes(c.N),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewPublicKey[P Point[P]](c *Curve[P], Q []byte) (*PublicKey, error) {
|
||||
_, err := c.newPoint().SetBytes(Q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PublicKey{
|
||||
curve: c.curve,
|
||||
q: Q,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GenerateKey generates a new ECDSA private key pair for the specified curve.
|
||||
func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) {
|
||||
k, Q, err := randomPoint(c, rand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PrivateKey{
|
||||
pub: PublicKey{
|
||||
curve: c.curve,
|
||||
q: Q.Bytes(),
|
||||
},
|
||||
d: k.Bytes(c.N),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// randomPoint returns a random scalar and the corresponding point using the
|
||||
// procedure given in FIPS 186-4, Appendix B.5.2 (rejection sampling).
|
||||
func randomPoint[P Point[P]](c *Curve[P], rand io.Reader) (k *bigmod.Nat, p P, err error) {
|
||||
k = bigmod.NewNat()
|
||||
for {
|
||||
b := make([]byte, c.N.Size())
|
||||
if _, err = io.ReadFull(rand, b); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Mask off any excess bits to increase the chance of hitting a value in
|
||||
// (0, N). These are the most dangerous lines in the package and maybe in
|
||||
// the library: a single bit of bias in the selection of nonces would likely
|
||||
// lead to key recovery, but no tests would fail. Look but DO NOT TOUCH.
|
||||
if excess := len(b)*8 - c.N.BitLen(); excess > 0 {
|
||||
// Just to be safe, assert that this only happens for the one curve that
|
||||
// doesn't have a round number of bits.
|
||||
if excess != 0 && c.curve != p521 {
|
||||
panic("ecdsa: internal error: unexpectedly masking off bits")
|
||||
}
|
||||
b[0] >>= excess
|
||||
}
|
||||
|
||||
// FIPS 186-4 makes us check k <= N - 2 and then add one.
|
||||
// Checking 0 < k <= N - 1 is strictly equivalent.
|
||||
// None of this matters anyway because the chance of selecting
|
||||
// zero is cryptographically negligible.
|
||||
if _, err = k.SetBytes(b, c.N); err == nil && k.IsZero() == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if testingOnlyRejectionSamplingLooped != nil {
|
||||
testingOnlyRejectionSamplingLooped()
|
||||
}
|
||||
}
|
||||
|
||||
p, err = c.newPoint().ScalarBaseMult(k.Bytes(c.N))
|
||||
return
|
||||
}
|
||||
|
||||
// testingOnlyRejectionSamplingLooped is called when rejection sampling in
|
||||
// randomPoint rejects a candidate for being higher than the modulus.
|
||||
var testingOnlyRejectionSamplingLooped func()
|
||||
|
||||
// Signature is an ECDSA signature, where r and s are represented as big-endian
|
||||
// fixed-length byte slices.
|
||||
type Signature struct {
|
||||
R, S []byte
|
||||
}
|
||||
|
||||
// Sign signs a hash (which should be the result of hashing a larger message)
|
||||
// using the private key, priv. If the hash is longer than the bit-length of the
|
||||
// private key's curve order, the hash will be truncated to that length.
|
||||
//
|
||||
// The signature is randomized.
|
||||
func Sign[P Point[P]](c *Curve[P], priv *PrivateKey, csprng io.Reader, hash []byte) (*Signature, error) {
|
||||
if priv.pub.curve != c.curve {
|
||||
return nil, errors.New("ecdsa: private key does not match curve")
|
||||
}
|
||||
|
||||
// SEC 1, Version 2.0, Section 4.1.3
|
||||
|
||||
k, R, err := randomPoint(c, csprng)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// kInv = k⁻¹
|
||||
kInv := bigmod.NewNat()
|
||||
inverse(c, kInv, k)
|
||||
|
||||
Rx, err := R.BytesX()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err := bigmod.NewNat().SetOverflowingBytes(Rx, c.N)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The spec wants us to retry here, but the chance of hitting this condition
|
||||
// on a large prime-order group like the NIST curves we support is
|
||||
// cryptographically negligible. If we hit it, something is awfully wrong.
|
||||
if r.IsZero() == 1 {
|
||||
return nil, errors.New("ecdsa: internal error: r is zero")
|
||||
}
|
||||
|
||||
e := bigmod.NewNat()
|
||||
hashToNat(c, e, hash)
|
||||
|
||||
s, err := bigmod.NewNat().SetBytes(priv.d, c.N)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Mul(r, c.N)
|
||||
s.Add(e, c.N)
|
||||
s.Mul(kInv, c.N)
|
||||
|
||||
// Again, the chance of this happening is cryptographically negligible.
|
||||
if s.IsZero() == 1 {
|
||||
return nil, errors.New("ecdsa: internal error: s is zero")
|
||||
}
|
||||
|
||||
return &Signature{r.Bytes(c.N), s.Bytes(c.N)}, nil
|
||||
}
|
||||
|
||||
// inverse sets kInv to the inverse of k modulo the order of the curve.
|
||||
func inverse[P Point[P]](c *Curve[P], kInv, k *bigmod.Nat) {
|
||||
if c.ordInverse != nil {
|
||||
kBytes, err := c.ordInverse(k.Bytes(c.N))
|
||||
// Some platforms don't implement ordInverse, and always return an error.
|
||||
if err == nil {
|
||||
_, err := kInv.SetBytes(kBytes, c.N)
|
||||
if err != nil {
|
||||
panic("ecdsa: internal error: ordInverse produced an invalid value")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the inverse of s in GF(N) using Fermat's method
|
||||
// (exponentiation modulo P - 2, per Euler's theorem)
|
||||
kInv.Exp(k, c.nMinus2, c.N)
|
||||
}
|
||||
|
||||
// hashToNat sets e to the left-most bits of hash, according to
|
||||
// SEC 1, Section 4.1.3, point 5 and Section 4.1.4, point 3.
|
||||
func hashToNat[P Point[P]](c *Curve[P], e *bigmod.Nat, hash []byte) {
|
||||
// ECDSA asks us to take the left-most log2(N) bits of hash, and use them as
|
||||
// an integer modulo N. This is the absolute worst of all worlds: we still
|
||||
// have to reduce, because the result might still overflow N, but to take
|
||||
// the left-most bits for P-521 we have to do a right shift.
|
||||
if size := c.N.Size(); len(hash) >= size {
|
||||
hash = hash[:size]
|
||||
if excess := len(hash)*8 - c.N.BitLen(); excess > 0 {
|
||||
hash = bytes.Clone(hash)
|
||||
for i := len(hash) - 1; i >= 0; i-- {
|
||||
hash[i] >>= excess
|
||||
if i > 0 {
|
||||
hash[i] |= hash[i-1] << (8 - excess)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_, err := e.SetOverflowingBytes(hash, c.N)
|
||||
if err != nil {
|
||||
panic("ecdsa: internal error: truncated hash is too long")
|
||||
}
|
||||
}
|
||||
|
||||
// Verify verifies the signature, sig, of hash (which should be the result of
|
||||
// hashing a larger message) using the public key, pub. If the hash is longer
|
||||
// than the bit-length of the private key's curve order, the hash will be
|
||||
// truncated to that length.
|
||||
//
|
||||
// The inputs are not considered confidential, and may leak through timing side
|
||||
// channels, or if an attacker has control of part of the inputs.
|
||||
func Verify[P Point[P]](c *Curve[P], pub *PublicKey, hash []byte, sig *Signature) error {
|
||||
if pub.curve != c.curve {
|
||||
return errors.New("ecdsa: public key does not match curve")
|
||||
}
|
||||
|
||||
Q, err := c.newPoint().SetBytes(pub.q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// SEC 1, Version 2.0, Section 4.1.4
|
||||
|
||||
r, err := bigmod.NewNat().SetBytes(sig.R, c.N)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.IsZero() == 1 {
|
||||
return errors.New("ecdsa: invalid signature: r is zero")
|
||||
}
|
||||
s, err := bigmod.NewNat().SetBytes(sig.S, c.N)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.IsZero() == 1 {
|
||||
return errors.New("ecdsa: invalid signature: s is zero")
|
||||
}
|
||||
|
||||
e := bigmod.NewNat()
|
||||
hashToNat(c, e, hash)
|
||||
|
||||
// w = s⁻¹
|
||||
w := bigmod.NewNat()
|
||||
inverse(c, w, s)
|
||||
|
||||
// p₁ = [e * s⁻¹]G
|
||||
p1, err := c.newPoint().ScalarBaseMult(e.Mul(w, c.N).Bytes(c.N))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// p₂ = [r * s⁻¹]Q
|
||||
p2, err := Q.ScalarMult(Q, w.Mul(r, c.N).Bytes(c.N))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// BytesX returns an error for the point at infinity.
|
||||
Rx, err := p1.Add(p1, p2).BytesX()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v, err := bigmod.NewNat().SetOverflowingBytes(Rx, c.N)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v.Equal(r) != 1 {
|
||||
return errors.New("ecdsa: signature did not verify")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ecdsa
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/internal/fips/bigmod"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRandomPoint(t *testing.T) {
|
||||
t.Run("P-224", func(t *testing.T) { testRandomPoint(t, P224()) })
|
||||
t.Run("P-256", func(t *testing.T) { testRandomPoint(t, P256()) })
|
||||
t.Run("P-384", func(t *testing.T) { testRandomPoint(t, P384()) })
|
||||
t.Run("P-521", func(t *testing.T) { testRandomPoint(t, P521()) })
|
||||
}
|
||||
|
||||
func testRandomPoint[P Point[P]](t *testing.T, c *Curve[P]) {
|
||||
t.Cleanup(func() { testingOnlyRejectionSamplingLooped = nil })
|
||||
var loopCount int
|
||||
testingOnlyRejectionSamplingLooped = func() { loopCount++ }
|
||||
|
||||
// A sequence of all ones will generate 2^N-1, which should be rejected.
|
||||
// (Unless, for example, we are masking too many bits.)
|
||||
r := io.MultiReader(bytes.NewReader(bytes.Repeat([]byte{0xff}, 100)), rand.Reader)
|
||||
if k, p, err := randomPoint(c, r); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if k.IsZero() == 1 {
|
||||
t.Error("k is zero")
|
||||
} else if p.Bytes()[0] != 4 {
|
||||
t.Error("p is infinity")
|
||||
}
|
||||
if loopCount == 0 {
|
||||
t.Error("overflow was not rejected")
|
||||
}
|
||||
loopCount = 0
|
||||
|
||||
// A sequence of all zeroes will generate zero, which should be rejected.
|
||||
r = io.MultiReader(bytes.NewReader(bytes.Repeat([]byte{0}, 100)), rand.Reader)
|
||||
if k, p, err := randomPoint(c, r); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if k.IsZero() == 1 {
|
||||
t.Error("k is zero")
|
||||
} else if p.Bytes()[0] != 4 {
|
||||
t.Error("p is infinity")
|
||||
}
|
||||
if loopCount == 0 {
|
||||
t.Error("zero was not rejected")
|
||||
}
|
||||
loopCount = 0
|
||||
|
||||
// P-256 has a 2⁻³² chance or randomly hitting a rejection. For P-224 it's
|
||||
// 2⁻¹¹², for P-384 it's 2⁻¹⁹⁴, and for P-521 it's 2⁻²⁶², so if we hit in
|
||||
// tests, something is horribly wrong. (For example, we are masking the
|
||||
// wrong bits.)
|
||||
if c.curve == p256 {
|
||||
return
|
||||
}
|
||||
if k, p, err := randomPoint(c, rand.Reader); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if k.IsZero() == 1 {
|
||||
t.Error("k is zero")
|
||||
} else if p.Bytes()[0] != 4 {
|
||||
t.Error("p is infinity")
|
||||
}
|
||||
if loopCount > 0 {
|
||||
t.Error("unexpected rejection")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashToNat(t *testing.T) {
|
||||
t.Run("P-224", func(t *testing.T) { testHashToNat(t, P224()) })
|
||||
t.Run("P-256", func(t *testing.T) { testHashToNat(t, P256()) })
|
||||
t.Run("P-384", func(t *testing.T) { testHashToNat(t, P384()) })
|
||||
t.Run("P-521", func(t *testing.T) { testHashToNat(t, P521()) })
|
||||
}
|
||||
|
||||
func testHashToNat[P Point[P]](t *testing.T, c *Curve[P]) {
|
||||
for l := 0; l < 600; l++ {
|
||||
h := bytes.Repeat([]byte{0xff}, l)
|
||||
hashToNat(c, bigmod.NewNat(), h)
|
||||
}
|
||||
}
|
||||
|
|
@ -479,6 +479,7 @@ var depsRules = `
|
|||
< crypto/internal/fips/nistec/fiat
|
||||
< crypto/internal/fips/nistec
|
||||
< crypto/internal/fips/ecdh
|
||||
< crypto/internal/fips/ecdsa
|
||||
< FIPS;
|
||||
|
||||
FIPS < crypto/internal/fips/check/checktest;
|
||||
|
|
|
|||
Loading…
Reference in New Issue