--- /dev/null
+pkg crypto/ecdsa, method (*PrivateKey) ECDH() (*ecdh.PrivateKey, error) #56088
+pkg crypto/ecdsa, method (*PublicKey) ECDH() (*ecdh.PublicKey, error) #56088
}
// PublicKey is an ECDH public key, usually a peer's ECDH share sent over the wire.
+//
+// These keys can be parsed with [crypto/x509.ParsePKIXPublicKey] and encoded
+// with [crypto/x509.MarshalPKIXPublicKey]. For NIST curves, they then need to
+// be converted with [crypto/ecdsa.PublicKey.ECDH] after parsing.
type PublicKey struct {
curve Curve
publicKey []byte
}
// PrivateKey is an ECDH private key, usually kept secret.
+//
+// These keys can be parsed with [crypto/x509.ParsePKCS8PrivateKey] and encoded
+// with [crypto/x509.MarshalPKCS8PrivateKey]. For NIST curves, they then need to
+// be converted with [crypto/ecdsa.PrivateKey.ECDH] after parsing.
type PrivateKey struct {
curve Curve
privateKey []byte
"crypto"
"crypto/aes"
"crypto/cipher"
+ "crypto/ecdh"
"crypto/elliptic"
"crypto/internal/boring"
"crypto/internal/boring/bbig"
// Any methods implemented on PublicKey might need to also be implemented on
// PrivateKey, as the latter embeds the former and will expose its methods.
+// ECDH returns k as a [ecdh.PublicKey]. It returns an error if the key is
+// invalid according to the definition of [ecdh.Curve.NewPublicKey], or if the
+// Curve is not supported by crypto/ecdh.
+func (k *PublicKey) ECDH() (*ecdh.PublicKey, error) {
+ c := curveToECDH(k.Curve)
+ if c == nil {
+ return nil, errors.New("ecdsa: unsupported curve by crypto/ecdh")
+ }
+ if !k.Curve.IsOnCurve(k.X, k.Y) {
+ return nil, errors.New("ecdsa: invalid public key")
+ }
+ return c.NewPublicKey(elliptic.Marshal(k.Curve, k.X, k.Y))
+}
+
// Equal reports whether pub and x have the same value.
//
// Two keys are only considered to have the same value if they have the same Curve value.
D *big.Int
}
+// ECDH returns k as a [ecdh.PrivateKey]. It returns an error if the key is
+// invalid according to the definition of [ecdh.Curve.NewPrivateKey], or if the
+// Curve is not supported by crypto/ecdh.
+func (k *PrivateKey) ECDH() (*ecdh.PrivateKey, error) {
+ c := curveToECDH(k.Curve)
+ if c == nil {
+ return nil, errors.New("ecdsa: unsupported curve by crypto/ecdh")
+ }
+ size := (k.Curve.Params().N.BitLen() + 7) / 8
+ if k.D.BitLen() > size*8 {
+ return nil, errors.New("ecdsa: invalid private key")
+ }
+ return c.NewPrivateKey(k.D.FillBytes(make([]byte, size)))
+}
+
+func curveToECDH(c elliptic.Curve) ecdh.Curve {
+ switch c {
+ case elliptic.P256():
+ return ecdh.P256()
+ case elliptic.P384():
+ return ecdh.P384()
+ case elliptic.P521():
+ return ecdh.P521()
+ default:
+ return nil
+ }
+}
+
// Public returns the public key corresponding to priv.
func (priv *PrivateKey) Public() crypto.PublicKey {
return &priv.PublicKey
// MarshalPKCS8PrivateKey converts a private key to PKCS #8, ASN.1 DER form.
//
// The following key types are currently supported: *rsa.PrivateKey,
-// *ecdsa.PrivateKey, ed25519.PrivateKey (not a pointer), and *ecdh.PrivateKey
-// (X25519 only). Unsupported key types result in an error.
+// *ecdsa.PrivateKey, ed25519.PrivateKey (not a pointer), and *ecdh.PrivateKey.
+// Unsupported key types result in an error.
//
// This kind of key is commonly encoded in PEM blocks of type "PRIVATE KEY".
func MarshalPKCS8PrivateKey(key any) ([]byte, error) {
if !ok {
return nil, errors.New("x509: unknown curve while marshaling to PKCS#8")
}
-
oidBytes, err := asn1.Marshal(oid)
if err != nil {
return nil, errors.New("x509: failed to marshal curve OID: " + err.Error())
}
-
privKey.Algo = pkix.AlgorithmIdentifier{
Algorithm: oidPublicKeyECDSA,
Parameters: asn1.RawValue{
FullBytes: oidBytes,
},
}
-
if privKey.PrivateKey, err = marshalECPrivateKeyWithOID(k, nil); err != nil {
return nil, errors.New("x509: failed to marshal EC private key while building PKCS#8: " + err.Error())
}
privKey.PrivateKey = curvePrivateKey
case *ecdh.PrivateKey:
- if k.Curve() != ecdh.X25519() {
- return nil, errors.New("x509: unknown curve while marshaling to PKCS#8")
- }
- privKey.Algo = pkix.AlgorithmIdentifier{
- Algorithm: oidPublicKeyX25519,
+ if k.Curve() == ecdh.X25519() {
+ privKey.Algo = pkix.AlgorithmIdentifier{
+ Algorithm: oidPublicKeyX25519,
+ }
+ var err error
+ if privKey.PrivateKey, err = asn1.Marshal(k.Bytes()); err != nil {
+ return nil, fmt.Errorf("x509: failed to marshal private key: %v", err)
+ }
+ } else {
+ oid, ok := oidFromECDHCurve(k.Curve())
+ if !ok {
+ return nil, errors.New("x509: unknown curve while marshaling to PKCS#8")
+ }
+ oidBytes, err := asn1.Marshal(oid)
+ if err != nil {
+ return nil, errors.New("x509: failed to marshal curve OID: " + err.Error())
+ }
+ privKey.Algo = pkix.AlgorithmIdentifier{
+ Algorithm: oidPublicKeyECDSA,
+ Parameters: asn1.RawValue{
+ FullBytes: oidBytes,
+ },
+ }
+ if privKey.PrivateKey, err = marshalECDHPrivateKey(k); err != nil {
+ return nil, errors.New("x509: failed to marshal EC private key while building PKCS#8: " + err.Error())
+ }
}
- curvePrivateKey, err := asn1.Marshal(k.Bytes())
- if err != nil {
- return nil, fmt.Errorf("x509: failed to marshal private key: %v", err)
- }
- privKey.PrivateKey = curvePrivateKey
default:
return nil, fmt.Errorf("x509: unknown key type while marshaling PKCS#8: %T", key)
t.Errorf("%s: marshaled PKCS#8 didn't match original: got %x, want %x", test.name, reserialised, derBytes)
continue
}
+
+ if ecKey, isEC := privKey.(*ecdsa.PrivateKey); isEC {
+ ecdhKey, err := ecKey.ECDH()
+ if err != nil {
+ if ecKey.Curve != elliptic.P224() {
+ t.Errorf("%s: failed to convert to ecdh: %s", test.name, err)
+ }
+ continue
+ }
+ reserialised, err := MarshalPKCS8PrivateKey(ecdhKey)
+ if err != nil {
+ t.Errorf("%s: failed to marshal into PKCS#8: %s", test.name, err)
+ continue
+ }
+ if !bytes.Equal(derBytes, reserialised) {
+ t.Errorf("%s: marshaled PKCS#8 didn't match original: got %x, want %x", test.name, reserialised, derBytes)
+ continue
+ }
+ }
}
}
package x509
import (
+ "crypto/ecdh"
"crypto/ecdsa"
"crypto/elliptic"
"encoding/asn1"
})
}
+// marshalECPrivateKeyWithOID marshals an EC private key into ASN.1, DER format
+// suitable for NIST curves.
+func marshalECDHPrivateKey(key *ecdh.PrivateKey) ([]byte, error) {
+ return asn1.Marshal(ecPrivateKey{
+ Version: 1,
+ PrivateKey: key.Bytes(),
+ PublicKey: asn1.BitString{Bytes: key.PublicKey().Bytes()},
+ })
+}
+
// parseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
// The OID for the named curve may be provided from another source (such as
// the PKCS8 container) - if it is provided then use this instead of the OID
publicKeyBytes = pub
publicKeyAlgorithm.Algorithm = oidPublicKeyEd25519
case *ecdh.PublicKey:
- if pub.Curve() != ecdh.X25519() {
- return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported ECDH curve")
- }
publicKeyBytes = pub.Bytes()
- publicKeyAlgorithm.Algorithm = oidPublicKeyX25519
+ if pub.Curve() == ecdh.X25519() {
+ publicKeyAlgorithm.Algorithm = oidPublicKeyX25519
+ } else {
+ oid, ok := oidFromECDHCurve(pub.Curve())
+ if !ok {
+ return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported elliptic curve")
+ }
+ publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA
+ var paramBytes []byte
+ paramBytes, err = asn1.Marshal(oid)
+ if err != nil {
+ return
+ }
+ publicKeyAlgorithm.Parameters.FullBytes = paramBytes
+ }
default:
return nil, pkix.AlgorithmIdentifier{}, fmt.Errorf("x509: unsupported public key type: %T", pub)
}
// (see RFC 5280, Section 4.1).
//
// The following key types are currently supported: *rsa.PublicKey,
-// *ecdsa.PublicKey, ed25519.PublicKey (not a pointer), and *ecdh.PublicKey
-// (X25519 only). Unsupported key types result in an error.
+// *ecdsa.PublicKey, ed25519.PublicKey (not a pointer), and *ecdh.PublicKey.
+// Unsupported key types result in an error.
//
// This kind of key is commonly encoded in PEM blocks of type "PUBLIC KEY".
func MarshalPKIXPublicKey(pub any) ([]byte, error) {
return nil, false
}
+func oidFromECDHCurve(curve ecdh.Curve) (asn1.ObjectIdentifier, bool) {
+ switch curve {
+ case ecdh.X25519():
+ return oidPublicKeyX25519, true
+ case ecdh.P256():
+ return oidNamedCurveP256, true
+ case ecdh.P384():
+ return oidNamedCurveP384, true
+ case ecdh.P521():
+ return oidNamedCurveP521, true
+ }
+
+ return nil, false
+}
+
// KeyUsage represents the set of actions that are valid for a given key. It's
// a bitmap of the KeyUsage* constants.
type KeyUsage int