]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/ecdh: revamp FIPS ECDH API
authorFilippo Valsorda <filippo@golang.org>
Wed, 20 Nov 2024 17:59:09 +0000 (18:59 +0100)
committerGopher Robot <gobot@golang.org>
Fri, 22 Nov 2024 02:09:10 +0000 (02:09 +0000)
This makes it more similar to the ECDSA API, introducing proper key
types that can correctly "cache" the key check.

The new API also has a better compliance profile. Note how the old
ECDHPnnn functions were not doing the PCT, instead delegating to the
caller an invocation of ImportKeyPnnn.

Change-Id: Ic6cf834427fd790324919b4d92bdaa2aac840016
Reviewed-on: https://go-review.googlesource.com/c/go/+/630098
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
src/crypto/ecdh/ecdh.go
src/crypto/ecdh/nist.go
src/crypto/internal/fips140/ecdh/cast.go
src/crypto/internal/fips140/ecdh/ecdh.go
src/crypto/internal/fips140/ecdh/order_test.go
src/crypto/internal/fips140test/cast_test.go

index e6bfe7c15c23f46e70dcb20fb581b468c22ab25d..231f1ea04c101090a0b1668def0b390c23603020 100644 (file)
@@ -9,6 +9,7 @@ package ecdh
 import (
        "crypto"
        "crypto/internal/boring"
+       "crypto/internal/fips140/ecdh"
        "crypto/subtle"
        "errors"
        "io"
@@ -60,6 +61,7 @@ type PublicKey struct {
        curve     Curve
        publicKey []byte
        boring    *boring.PublicKeyECDH
+       fips      *ecdh.PublicKey
 }
 
 // Bytes returns a copy of the encoding of the public key.
@@ -100,6 +102,7 @@ type PrivateKey struct {
        privateKey []byte
        publicKey  *PublicKey
        boring     *boring.PrivateKeyECDH
+       fips       *ecdh.PrivateKey
 }
 
 // ECDH performs an ECDH exchange and returns the shared secret. The [PrivateKey]
index 903aa2603024e4df221049f365d926e9d7f06f5c..0f4a65e5affb800c2c9f17cb4bbc160472ef62aa 100644 (file)
@@ -13,11 +13,11 @@ import (
 )
 
 type nistCurve struct {
-       name         string
-       generate     func(io.Reader) (privateKey, publicKey []byte, err error)
-       importKey    func([]byte) (publicKey []byte, err error)
-       checkPubkey  func(publicKey []byte) error
-       sharedSecret func(privateKey, publicKey []byte) (sharedSecret []byte, err error)
+       name          string
+       generate      func(io.Reader) (*ecdh.PrivateKey, error)
+       newPrivateKey func([]byte) (*ecdh.PrivateKey, error)
+       newPublicKey  func(publicKey []byte) (*ecdh.PublicKey, error)
+       sharedSecret  func(*ecdh.PrivateKey, *ecdh.PublicKey) (sharedSecret []byte, err error)
 }
 
 func (c *nistCurve) String() string {
@@ -43,15 +43,20 @@ func (c *nistCurve) GenerateKey(rand io.Reader) (*PrivateKey, error) {
                return k, nil
        }
 
-       privateKey, publicKey, err := c.generate(rand)
+       privateKey, err := c.generate(rand)
        if err != nil {
                return nil, err
        }
 
        k := &PrivateKey{
                curve:      c,
-               privateKey: privateKey,
-               publicKey:  &PublicKey{curve: c, publicKey: publicKey},
+               privateKey: privateKey.Bytes(),
+               fips:       privateKey,
+               publicKey: &PublicKey{
+                       curve:     c,
+                       publicKey: privateKey.PublicKey().Bytes(),
+                       fips:      privateKey.PublicKey(),
+               },
        }
        if boring.Enabled {
                bk, err := boring.NewPrivateKeyECDH(c.name, k.privateKey)
@@ -87,15 +92,19 @@ func (c *nistCurve) NewPrivateKey(key []byte) (*PrivateKey, error) {
                return k, nil
        }
 
-       publicKey, err := c.importKey(key)
+       fk, err := c.newPrivateKey(key)
        if err != nil {
                return nil, err
        }
-
        k := &PrivateKey{
                curve:      c,
                privateKey: bytes.Clone(key),
-               publicKey:  &PublicKey{curve: c, publicKey: publicKey},
+               fips:       fk,
+               publicKey: &PublicKey{
+                       curve:     c,
+                       publicKey: fk.PublicKey().Bytes(),
+                       fips:      fk.PublicKey(),
+               },
        }
        return k, nil
 }
@@ -117,9 +126,11 @@ func (c *nistCurve) NewPublicKey(key []byte) (*PublicKey, error) {
                }
                k.boring = bk
        } else {
-               if err := c.checkPubkey(k.publicKey); err != nil {
+               fk, err := c.newPublicKey(key)
+               if err != nil {
                        return nil, err
                }
+               k.fips = fk
        }
        return k, nil
 }
@@ -135,7 +146,7 @@ func (c *nistCurve) ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error) {
        if boring.Enabled {
                return boring.ECDH(local.boring, remote.boring)
        }
-       return c.sharedSecret(local.privateKey, remote.publicKey)
+       return c.sharedSecret(local.fips, remote.fips)
 }
 
 // P256 returns a [Curve] which implements NIST P-256 (FIPS 186-3, section D.2.3),
@@ -146,11 +157,19 @@ func (c *nistCurve) ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error) {
 func P256() Curve { return p256 }
 
 var p256 = &nistCurve{
-       name:         "P-256",
-       generate:     ecdh.GenerateKeyP256,
-       importKey:    ecdh.ImportKeyP256,
-       checkPubkey:  ecdh.CheckPublicKeyP256,
-       sharedSecret: ecdh.ECDHP256,
+       name: "P-256",
+       generate: func(r io.Reader) (*ecdh.PrivateKey, error) {
+               return ecdh.GenerateKey(ecdh.P256(), r)
+       },
+       newPrivateKey: func(b []byte) (*ecdh.PrivateKey, error) {
+               return ecdh.NewPrivateKey(ecdh.P256(), b)
+       },
+       newPublicKey: func(publicKey []byte) (*ecdh.PublicKey, error) {
+               return ecdh.NewPublicKey(ecdh.P256(), publicKey)
+       },
+       sharedSecret: func(priv *ecdh.PrivateKey, pub *ecdh.PublicKey) (sharedSecret []byte, err error) {
+               return ecdh.ECDH(ecdh.P256(), priv, pub)
+       },
 }
 
 // P384 returns a [Curve] which implements NIST P-384 (FIPS 186-3, section D.2.4),
@@ -161,11 +180,19 @@ var p256 = &nistCurve{
 func P384() Curve { return p384 }
 
 var p384 = &nistCurve{
-       name:         "P-384",
-       generate:     ecdh.GenerateKeyP384,
-       importKey:    ecdh.ImportKeyP384,
-       checkPubkey:  ecdh.CheckPublicKeyP384,
-       sharedSecret: ecdh.ECDHP384,
+       name: "P-384",
+       generate: func(r io.Reader) (*ecdh.PrivateKey, error) {
+               return ecdh.GenerateKey(ecdh.P384(), r)
+       },
+       newPrivateKey: func(b []byte) (*ecdh.PrivateKey, error) {
+               return ecdh.NewPrivateKey(ecdh.P384(), b)
+       },
+       newPublicKey: func(publicKey []byte) (*ecdh.PublicKey, error) {
+               return ecdh.NewPublicKey(ecdh.P384(), publicKey)
+       },
+       sharedSecret: func(priv *ecdh.PrivateKey, pub *ecdh.PublicKey) (sharedSecret []byte, err error) {
+               return ecdh.ECDH(ecdh.P384(), priv, pub)
+       },
 }
 
 // P521 returns a [Curve] which implements NIST P-521 (FIPS 186-3, section D.2.5),
@@ -176,9 +203,17 @@ var p384 = &nistCurve{
 func P521() Curve { return p521 }
 
 var p521 = &nistCurve{
-       name:         "P-521",
-       generate:     ecdh.GenerateKeyP521,
-       importKey:    ecdh.ImportKeyP521,
-       checkPubkey:  ecdh.CheckPublicKeyP521,
-       sharedSecret: ecdh.ECDHP521,
+       name: "P-521",
+       generate: func(r io.Reader) (*ecdh.PrivateKey, error) {
+               return ecdh.GenerateKey(ecdh.P521(), r)
+       },
+       newPrivateKey: func(b []byte) (*ecdh.PrivateKey, error) {
+               return ecdh.NewPrivateKey(ecdh.P521(), b)
+       },
+       newPublicKey: func(publicKey []byte) (*ecdh.PublicKey, error) {
+               return ecdh.NewPublicKey(ecdh.P521(), publicKey)
+       },
+       sharedSecret: func(priv *ecdh.PrivateKey, pub *ecdh.PublicKey) (sharedSecret []byte, err error) {
+               return ecdh.ECDH(ecdh.P521(), priv, pub)
+       },
 }
index b9b2def3218221940d59dc57fe863d0bc6814989..d63058fdabb2983aea84bc81d49a80dfcf4cc22c 100644 (file)
@@ -8,7 +8,6 @@ import (
        "bytes"
        "crypto/internal/fips140"
        _ "crypto/internal/fips140/check"
-       "crypto/internal/fips140/nistec"
        "errors"
        "sync"
 )
@@ -39,7 +38,9 @@ var fipsSelfTest = sync.OnceFunc(func() {
                        0x83, 0x48, 0x40, 0x56, 0x69, 0xa1, 0x95, 0xfa,
                        0xc5, 0x35, 0x04, 0x06, 0xba, 0x76, 0xbc, 0xce,
                }
-               got, err := ecdh(privateKey, publicKey, nistec.NewP256Point)
+               k := &PrivateKey{d: privateKey, pub: PublicKey{curve: p256}}
+               peer := &PublicKey{curve: p256, q: publicKey}
+               got, err := ecdh(P256(), k, peer)
                if err != nil {
                        return err
                }
index d2757bbf165d8c190652edcc64d23303c3324dab..19a45c00db9ce972dd30fd70b9e3e14aeb0569aa 100644 (file)
@@ -16,72 +16,142 @@ import (
        "math/bits"
 )
 
-// point is a generic constraint for the [nistec] point types.
-type point[T any] interface {
+// 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.
+
+// All this is duplicated from crypto/internal/fips/ecdsa, but the standards are
+// different and FIPS 140 does not allow reusing keys across them.
+
+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
+       N        []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) (T, error)
-       ScalarMult(T, []byte) (T, error)
-       ScalarBaseMult([]byte) (T, error)
+       SetBytes([]byte) (P, error)
+       ScalarMult(P, []byte) (P, error)
+       ScalarBaseMult([]byte) (P, error)
 }
 
-// GenerateKeyP224 generates a random P-224 private key for ECDH.
-//
-// If FIPS mode is disabled, privateKey is generated from rand. If FIPS mode is
-// enabled, rand is ignored and the key pair is generated using the approved
-// DRBG (and the function runs considerably slower).
-func GenerateKeyP224(rand io.Reader) (privateKey, publicKey []byte, err error) {
-       fipsSelfTest()
-       fips140.RecordApproved()
-       return generateKey(rand, nistec.NewP224Point, p224Order)
+func P224() *Curve[*nistec.P224Point] {
+       return &Curve[*nistec.P224Point]{
+               curve:    p224,
+               newPoint: nistec.NewP224Point,
+               N:        p224Order,
+       }
 }
 
-// GenerateKeyP256 generates a random P-256 private key for ECDH.
-//
-// If FIPS mode is disabled, privateKey is generated from rand. If FIPS mode is
-// enabled, rand is ignored and the key pair is generated using the approved
-// DRBG (and the function runs considerably slower).
-func GenerateKeyP256(rand io.Reader) (privateKey, publicKey []byte, err error) {
-       fipsSelfTest()
-       fips140.RecordApproved()
-       return generateKey(rand, nistec.NewP256Point, p256Order)
+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,
 }
 
-// GenerateKeyP384 generates a random P-384 private key for ECDH.
-//
-// If FIPS mode is disabled, privateKey is generated from rand. If FIPS mode is
-// enabled, rand is ignored and the key pair is generated using the approved
-// DRBG (and the function runs considerably slower).
-func GenerateKeyP384(rand io.Reader) (privateKey, publicKey []byte, err error) {
-       fipsSelfTest()
-       fips140.RecordApproved()
-       return generateKey(rand, nistec.NewP384Point, p384Order)
+func P256() *Curve[*nistec.P256Point] {
+       return &Curve[*nistec.P256Point]{
+               curve:    p256,
+               newPoint: nistec.NewP256Point,
+               N:        p256Order,
+       }
 }
 
-// GenerateKeyP521 generates a random P-521 private key for ECDH.
-//
-// If FIPS mode is disabled, privateKey is generated from rand. If FIPS mode is
-// enabled, rand is ignored and the key pair is generated using the approved
-// DRBG (and the function runs considerably slower).
-func GenerateKeyP521(rand io.Reader) (privateKey, publicKey []byte, err error) {
-       fipsSelfTest()
-       fips140.RecordApproved()
-       return generateKey(rand, nistec.NewP521Point, p521Order)
+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 &Curve[*nistec.P384Point]{
+               curve:    p384,
+               newPoint: nistec.NewP384Point,
+               N:        p384Order,
+       }
+}
+
+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 generateKey[P point[P]](rand io.Reader, newPoint func() P, scalarOrder []byte) (privateKey, publicKey []byte, err error) {
+func P521() *Curve[*nistec.P521Point] {
+       return &Curve[*nistec.P521Point]{
+               curve:    p521,
+               newPoint: nistec.NewP521Point,
+               N:        p521Order,
+       }
+}
+
+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,
+}
+
+// GenerateKey generates a new ECDSA private key pair for the specified curve.
+//
+// In FIPS mode, rand is ignored.
+func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) {
+       fips140.RecordApproved()
        // This procedure is equivalent to Key Pair Generation by Testing
        // Candidates, specified in NIST SP 800-56A Rev. 3, Section 5.6.1.2.2.
 
        for {
-               key := make([]byte, len(scalarOrder))
+               key := make([]byte, len(c.N))
                if fips140.Enabled {
                        drbg.Read(key)
                } else {
                        randutil.MaybeReadByte(rand)
                        if _, err := io.ReadFull(rand, key); err != nil {
-                               return nil, nil, err
+                               return nil, err
                        }
                        // In tests, rand will return all zeros and NewPrivateKey will reject
                        // the zero key as it generates the identity as a public key. This also
@@ -91,55 +161,34 @@ func generateKey[P point[P]](rand io.Reader, newPoint func() P, scalarOrder []by
 
                // Mask off any excess bits if the size of the underlying field is not a
                // whole number of bytes, which is only the case for P-521.
-               if len(scalarOrder) == len(p521Order) && scalarOrder[0]&0b1111_1110 == 0 {
+               if c.curve == p521 && c.N[0]&0b1111_1110 == 0 {
                        key[0] &= 0b0000_0001
                }
 
-               publicKey, err := checkKeyAndComputePublicKey(key, newPoint, scalarOrder)
+               privateKey, err := NewPrivateKey(c, key)
                if err != nil {
                        continue
                }
-
-               return key, publicKey, nil
+               return privateKey, nil
        }
 }
 
-func ImportKeyP224(privateKey []byte) (publicKey []byte, err error) {
-       fips140.RecordNonApproved()
-       return checkKeyAndComputePublicKey(privateKey, nistec.NewP224Point, p224Order)
-}
-
-func ImportKeyP256(privateKey []byte) (publicKey []byte, err error) {
-       fips140.RecordNonApproved()
-       return checkKeyAndComputePublicKey(privateKey, nistec.NewP256Point, p256Order)
-}
-
-func ImportKeyP384(privateKey []byte) (publicKey []byte, err error) {
-       fips140.RecordNonApproved()
-       return checkKeyAndComputePublicKey(privateKey, nistec.NewP384Point, p384Order)
-}
-
-func ImportKeyP521(privateKey []byte) (publicKey []byte, err error) {
-       fips140.RecordNonApproved()
-       return checkKeyAndComputePublicKey(privateKey, nistec.NewP521Point, p521Order)
-}
-
-func checkKeyAndComputePublicKey[P point[P]](key []byte, newPoint func() P, scalarOrder []byte) (publicKey []byte, err error) {
+func NewPrivateKey[P Point[P]](c *Curve[P], key []byte) (*PrivateKey, error) {
        // SP 800-56A Rev. 3, Section 5.6.1.2.2 checks that c <= n – 2 and then
        // returns d = c + 1. Note that it follows that 0 < d < n. Equivalently,
        // we check that 0 < d < n, and return d.
-       if len(key) != len(scalarOrder) || isZero(key) || !isLess(key, scalarOrder) {
+       if len(key) != len(c.N) || isZero(key) || !isLess(key, c.N) {
                return nil, errors.New("crypto/ecdh: invalid private key")
        }
 
-       p, err := newPoint().ScalarBaseMult(key)
+       p, err := c.newPoint().ScalarBaseMult(key)
        if err != nil {
                // This is unreachable because the only error condition of
                // ScalarBaseMult is if the input is not the right size.
                panic("crypto/ecdh: internal error: nistec ScalarBaseMult failed for a fixed-size input")
        }
 
-       publicKey = p.Bytes()
+       publicKey := p.Bytes()
        if len(publicKey) == 1 {
                // The encoding of the identity is a single 0x00 byte. This is
                // unreachable because the only scalar that generates the identity is
@@ -157,7 +206,7 @@ func checkKeyAndComputePublicKey[P point[P]](key []byte, newPoint func() P, scal
        // consistent [...], even if the underlying standard does not require a
        // PCT". So we do it. And make ECDH nearly 50% slower (only) in FIPS mode.
        if err := fips140.PCT("ECDH PCT", func() error {
-               p1, err := newPoint().ScalarBaseMult(key)
+               p1, err := c.newPoint().ScalarBaseMult(key)
                if err != nil {
                        return err
                }
@@ -169,92 +218,58 @@ func checkKeyAndComputePublicKey[P point[P]](key []byte, newPoint func() P, scal
                panic(err)
        }
 
-       return publicKey, nil
-}
-
-func CheckPublicKeyP224(publicKey []byte) error {
-       fipsSelfTest()
-       fips140.RecordApproved()
-       return checkPublicKey(publicKey, nistec.NewP224Point)
-}
-
-func CheckPublicKeyP256(publicKey []byte) error {
-       fipsSelfTest()
-       fips140.RecordApproved()
-       return checkPublicKey(publicKey, nistec.NewP256Point)
-}
-
-func CheckPublicKeyP384(publicKey []byte) error {
-       fipsSelfTest()
-       fips140.RecordApproved()
-       return checkPublicKey(publicKey, nistec.NewP384Point)
+       k := &PrivateKey{d: bytes.Clone(key), pub: PublicKey{curve: c.curve, q: publicKey}}
+       return k, nil
 }
 
-func CheckPublicKeyP521(publicKey []byte) error {
-       fipsSelfTest()
-       fips140.RecordApproved()
-       return checkPublicKey(publicKey, nistec.NewP521Point)
-}
-
-func checkPublicKey[P point[P]](key []byte, newPoint func() P) error {
+func NewPublicKey[P Point[P]](c *Curve[P], key []byte) (*PublicKey, error) {
        // Reject the point at infinity and compressed encodings.
        if len(key) == 0 || key[0] != 4 {
-               return errors.New("crypto/ecdh: invalid public key")
+               return nil, errors.New("crypto/ecdh: invalid public key")
        }
 
        // SetBytes checks that x and y are in the interval [0, p - 1], and that
        // the point is on the curve. Along with the rejection of the point at
        // infinity (the identity element) above, this fulfills the requirements
        // of NIST SP 800-56A Rev. 3, Section 5.6.2.3.4.
-       if _, err := newPoint().SetBytes(key); err != nil {
-               return err
+       if _, err := c.newPoint().SetBytes(key); err != nil {
+               return nil, err
        }
 
-       return nil
+       return &PublicKey{curve: c.curve, q: bytes.Clone(key)}, nil
 }
 
-func ECDHP224(privateKey, publicKey []byte) ([]byte, error) {
+func ECDH[P Point[P]](c *Curve[P], k *PrivateKey, peer *PublicKey) ([]byte, error) {
        fipsSelfTest()
        fips140.RecordApproved()
-       return ecdh(privateKey, publicKey, nistec.NewP224Point)
+       return ecdh(c, k, peer)
 }
 
-func ECDHP256(privateKey, publicKey []byte) ([]byte, error) {
-       fipsSelfTest()
-       fips140.RecordApproved()
-       return ecdh(privateKey, publicKey, nistec.NewP256Point)
-}
-
-func ECDHP384(privateKey, publicKey []byte) ([]byte, error) {
-       fipsSelfTest()
-       fips140.RecordApproved()
-       return ecdh(privateKey, publicKey, nistec.NewP384Point)
-}
-
-func ECDHP521(privateKey, publicKey []byte) ([]byte, error) {
-       fipsSelfTest()
-       fips140.RecordApproved()
-       return ecdh(privateKey, publicKey, nistec.NewP521Point)
-}
+func ecdh[P Point[P]](c *Curve[P], k *PrivateKey, peer *PublicKey) ([]byte, error) {
+       if c.curve != k.pub.curve {
+               return nil, errors.New("crypto/ecdh: mismatched curves")
+       }
+       if k.pub.curve != peer.curve {
+               return nil, errors.New("crypto/ecdh: mismatched curves")
+       }
 
-func ecdh[P point[P]](privateKey, publicKey []byte, newPoint func() P) ([]byte, error) {
        // This applies the Shared Secret Computation of the Ephemeral Unified Model
        // scheme specified in NIST SP 800-56A Rev. 3, Section 6.1.2.2.
 
        // Per Section 5.6.2.3.4, Step 1, reject the identity element (0x00).
-       if len(publicKey) == 1 {
+       if len(k.pub.q) == 1 {
                return nil, errors.New("crypto/ecdh: public key is the identity element")
        }
 
        // SetBytes checks that (x, y) are reduced modulo p, and that they are on
        // the curve, performing Steps 2-3 of Section 5.6.2.3.4.
-       p, err := newPoint().SetBytes(publicKey)
+       p, err := c.newPoint().SetBytes(peer.q)
        if err != nil {
                return nil, err
        }
 
        // Compute P according to Section 5.7.1.2.
-       if _, err := p.ScalarMult(p, privateKey); err != nil {
+       if _, err := p.ScalarMult(p, k.d); err != nil {
                return nil, err
        }
 
@@ -300,34 +315,3 @@ func isLess(a, b []byte) bool {
        // If there is a borrow at the end of the operation, then a < b.
        return borrow == 1
 }
-
-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,
-}
-
-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}
-
-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}
-
-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}
index 772c42c81357b4f7709e1b027bd3a2038187dadf..83453c5ac39b3a77d4c29b96587ddd46eb1a95cb 100644 (file)
@@ -11,16 +11,16 @@ import (
 )
 
 func TestOrders(t *testing.T) {
-       if !bytes.Equal(elliptic.P224().Params().N.Bytes(), p224Order) {
+       if !bytes.Equal(elliptic.P224().Params().N.Bytes(), P224().N) {
                t.Errorf("P-224 order mismatch")
        }
-       if !bytes.Equal(elliptic.P256().Params().N.Bytes(), p256Order) {
+       if !bytes.Equal(elliptic.P256().Params().N.Bytes(), P256().N) {
                t.Errorf("P-256 order mismatch")
        }
-       if !bytes.Equal(elliptic.P384().Params().N.Bytes(), p384Order) {
+       if !bytes.Equal(elliptic.P384().Params().N.Bytes(), P384().N) {
                t.Errorf("P-384 order mismatch")
        }
-       if !bytes.Equal(elliptic.P521().Params().N.Bytes(), p521Order) {
+       if !bytes.Equal(elliptic.P521().Params().N.Bytes(), P521().N) {
                t.Errorf("P-521 order mismatch")
        }
 }
index 0c5cc63e3f06015eb7bd2c739df1956528de83e4..0ef75afcbef9c72b88d312176bfcd46ef773e610 100644 (file)
@@ -73,12 +73,16 @@ func findAllCASTs(t *testing.T) map[string]struct{} {
 // TestConditionals causes the conditional CASTs and PCTs to be invoked.
 func TestConditionals(t *testing.T) {
        mlkem.GenerateKey768()
-       ecdh.GenerateKeyP256(rand.Reader)
-       k, err := ecdsa.GenerateKey(ecdsa.P256(), rand.Reader)
+       k, err := ecdh.GenerateKey(ecdh.P256(), rand.Reader)
        if err != nil {
                t.Fatal(err)
        }
-       ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, k, make([]byte, 32))
+       ecdh.ECDH(ecdh.P256(), k, k.PublicKey())
+       kDSA, err := ecdsa.GenerateKey(ecdsa.P256(), rand.Reader)
+       if err != nil {
+               t.Fatal(err)
+       }
+       ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, kDSA, make([]byte, 32))
        k25519, err := ed25519.GenerateKey(rand.Reader)
        if err != nil {
                t.Fatal(err)