aboutsummaryrefslogtreecommitdiff
path: root/src/crypto
diff options
context:
space:
mode:
authorFilippo Valsorda <filippo@golang.org>2024-11-20 18:59:09 +0100
committerGopher Robot <gobot@golang.org>2024-11-22 02:09:10 +0000
commitfb432782f0017f59f6f2c5f620dfd8c67b5e35e3 (patch)
tree7c14025c9735d3fabda9e0908d376b46a86656f5 /src/crypto
parentd524c954b14c861e6a442e09abd38ba074ad376d (diff)
downloadgo-fb432782f0017f59f6f2c5f620dfd8c67b5e35e3.tar.xz
crypto/ecdh: revamp FIPS ECDH API
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>
Diffstat (limited to 'src/crypto')
-rw-r--r--src/crypto/ecdh/ecdh.go3
-rw-r--r--src/crypto/ecdh/nist.go91
-rw-r--r--src/crypto/internal/fips140/ecdh/cast.go5
-rw-r--r--src/crypto/internal/fips140/ecdh/ecdh.go298
-rw-r--r--src/crypto/internal/fips140/ecdh/order_test.go8
-rw-r--r--src/crypto/internal/fips140test/cast_test.go10
6 files changed, 221 insertions, 194 deletions
diff --git a/src/crypto/ecdh/ecdh.go b/src/crypto/ecdh/ecdh.go
index e6bfe7c15c..231f1ea04c 100644
--- a/src/crypto/ecdh/ecdh.go
+++ b/src/crypto/ecdh/ecdh.go
@@ -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]
diff --git a/src/crypto/ecdh/nist.go b/src/crypto/ecdh/nist.go
index 903aa26030..0f4a65e5af 100644
--- a/src/crypto/ecdh/nist.go
+++ b/src/crypto/ecdh/nist.go
@@ -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)
+ },
}
diff --git a/src/crypto/internal/fips140/ecdh/cast.go b/src/crypto/internal/fips140/ecdh/cast.go
index b9b2def321..d63058fdab 100644
--- a/src/crypto/internal/fips140/ecdh/cast.go
+++ b/src/crypto/internal/fips140/ecdh/cast.go
@@ -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
}
diff --git a/src/crypto/internal/fips140/ecdh/ecdh.go b/src/crypto/internal/fips140/ecdh/ecdh.go
index d2757bbf16..19a45c00db 100644
--- a/src/crypto/internal/fips140/ecdh/ecdh.go
+++ b/src/crypto/internal/fips140/ecdh/ecdh.go
@@ -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}
diff --git a/src/crypto/internal/fips140/ecdh/order_test.go b/src/crypto/internal/fips140/ecdh/order_test.go
index 772c42c813..83453c5ac3 100644
--- a/src/crypto/internal/fips140/ecdh/order_test.go
+++ b/src/crypto/internal/fips140/ecdh/order_test.go
@@ -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")
}
}
diff --git a/src/crypto/internal/fips140test/cast_test.go b/src/crypto/internal/fips140test/cast_test.go
index 0c5cc63e3f..0ef75afcbe 100644
--- a/src/crypto/internal/fips140test/cast_test.go
+++ b/src/crypto/internal/fips140test/cast_test.go
@@ -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)