mirror of
https://github.com/golang/go
synced 2024-11-26 07:17:59 -07:00
crypto/ecdsa,crypto/x509: add encoding paths for NIST crypto/ecdh keys
Fixes #56088 Updates #52221 Change-Id: Id2f806a116100a160be7daafc3e4c0be2acdd6a9 Reviewed-on: https://go-review.googlesource.com/c/go/+/450816 Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Joedian Reid <joedian@golang.org> Reviewed-by: Roland Shoemaker <roland@golang.org>
This commit is contained in:
parent
e84ce0802d
commit
5f60f844be
2
api/next/56088.txt
Normal file
2
api/next/56088.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pkg crypto/ecdsa, method (*PrivateKey) ECDH() (*ecdh.PrivateKey, error) #56088
|
||||||
|
pkg crypto/ecdsa, method (*PublicKey) ECDH() (*ecdh.PublicKey, error) #56088
|
@ -56,6 +56,10 @@ type Curve interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PublicKey is an ECDH public key, usually a peer's ECDH share sent over the wire.
|
// 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 {
|
type PublicKey struct {
|
||||||
curve Curve
|
curve Curve
|
||||||
publicKey []byte
|
publicKey []byte
|
||||||
@ -91,6 +95,10 @@ func (k *PublicKey) Curve() Curve {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PrivateKey is an ECDH private key, usually kept secret.
|
// 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 {
|
type PrivateKey struct {
|
||||||
curve Curve
|
curve Curve
|
||||||
privateKey []byte
|
privateKey []byte
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"crypto"
|
"crypto"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/ecdh"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/internal/boring"
|
"crypto/internal/boring"
|
||||||
"crypto/internal/boring/bbig"
|
"crypto/internal/boring/bbig"
|
||||||
@ -61,6 +62,20 @@ type PublicKey struct {
|
|||||||
// Any methods implemented on PublicKey might need to also be implemented on
|
// Any methods implemented on PublicKey might need to also be implemented on
|
||||||
// PrivateKey, as the latter embeds the former and will expose its methods.
|
// 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.
|
// 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.
|
// Two keys are only considered to have the same value if they have the same Curve value.
|
||||||
@ -85,6 +100,34 @@ type PrivateKey struct {
|
|||||||
D *big.Int
|
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.
|
// Public returns the public key corresponding to priv.
|
||||||
func (priv *PrivateKey) Public() crypto.PublicKey {
|
func (priv *PrivateKey) Public() crypto.PublicKey {
|
||||||
return &priv.PublicKey
|
return &priv.PublicKey
|
||||||
|
@ -94,8 +94,8 @@ func ParsePKCS8PrivateKey(der []byte) (key any, err error) {
|
|||||||
// MarshalPKCS8PrivateKey converts a private key to PKCS #8, ASN.1 DER form.
|
// MarshalPKCS8PrivateKey converts a private key to PKCS #8, ASN.1 DER form.
|
||||||
//
|
//
|
||||||
// The following key types are currently supported: *rsa.PrivateKey,
|
// The following key types are currently supported: *rsa.PrivateKey,
|
||||||
// *ecdsa.PrivateKey, ed25519.PrivateKey (not a pointer), and *ecdh.PrivateKey
|
// *ecdsa.PrivateKey, ed25519.PrivateKey (not a pointer), and *ecdh.PrivateKey.
|
||||||
// (X25519 only). Unsupported key types result in an error.
|
// Unsupported key types result in an error.
|
||||||
//
|
//
|
||||||
// This kind of key is commonly encoded in PEM blocks of type "PRIVATE KEY".
|
// This kind of key is commonly encoded in PEM blocks of type "PRIVATE KEY".
|
||||||
func MarshalPKCS8PrivateKey(key any) ([]byte, error) {
|
func MarshalPKCS8PrivateKey(key any) ([]byte, error) {
|
||||||
@ -114,19 +114,16 @@ func MarshalPKCS8PrivateKey(key any) ([]byte, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("x509: unknown curve while marshaling to PKCS#8")
|
return nil, errors.New("x509: unknown curve while marshaling to PKCS#8")
|
||||||
}
|
}
|
||||||
|
|
||||||
oidBytes, err := asn1.Marshal(oid)
|
oidBytes, err := asn1.Marshal(oid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("x509: failed to marshal curve OID: " + err.Error())
|
return nil, errors.New("x509: failed to marshal curve OID: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
privKey.Algo = pkix.AlgorithmIdentifier{
|
privKey.Algo = pkix.AlgorithmIdentifier{
|
||||||
Algorithm: oidPublicKeyECDSA,
|
Algorithm: oidPublicKeyECDSA,
|
||||||
Parameters: asn1.RawValue{
|
Parameters: asn1.RawValue{
|
||||||
FullBytes: oidBytes,
|
FullBytes: oidBytes,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if privKey.PrivateKey, err = marshalECPrivateKeyWithOID(k, nil); err != nil {
|
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())
|
return nil, errors.New("x509: failed to marshal EC private key while building PKCS#8: " + err.Error())
|
||||||
}
|
}
|
||||||
@ -142,17 +139,33 @@ func MarshalPKCS8PrivateKey(key any) ([]byte, error) {
|
|||||||
privKey.PrivateKey = curvePrivateKey
|
privKey.PrivateKey = curvePrivateKey
|
||||||
|
|
||||||
case *ecdh.PrivateKey:
|
case *ecdh.PrivateKey:
|
||||||
if k.Curve() != ecdh.X25519() {
|
if k.Curve() == ecdh.X25519() {
|
||||||
return nil, errors.New("x509: unknown curve while marshaling to PKCS#8")
|
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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
privKey.Algo = pkix.AlgorithmIdentifier{
|
|
||||||
Algorithm: oidPublicKeyX25519,
|
|
||||||
}
|
|
||||||
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:
|
default:
|
||||||
return nil, fmt.Errorf("x509: unknown key type while marshaling PKCS#8: %T", key)
|
return nil, fmt.Errorf("x509: unknown key type while marshaling PKCS#8: %T", key)
|
||||||
|
@ -131,6 +131,25 @@ func TestPKCS8(t *testing.T) {
|
|||||||
t.Errorf("%s: marshaled PKCS#8 didn't match original: got %x, want %x", test.name, reserialised, derBytes)
|
t.Errorf("%s: marshaled PKCS#8 didn't match original: got %x, want %x", test.name, reserialised, derBytes)
|
||||||
continue
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package x509
|
package x509
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ecdh"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
@ -66,6 +67,16 @@ func marshalECPrivateKeyWithOID(key *ecdsa.PrivateKey, oid asn1.ObjectIdentifier
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
// 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 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
|
// the PKCS8 container) - if it is provided then use this instead of the OID
|
||||||
|
@ -115,11 +115,22 @@ func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.A
|
|||||||
publicKeyBytes = pub
|
publicKeyBytes = pub
|
||||||
publicKeyAlgorithm.Algorithm = oidPublicKeyEd25519
|
publicKeyAlgorithm.Algorithm = oidPublicKeyEd25519
|
||||||
case *ecdh.PublicKey:
|
case *ecdh.PublicKey:
|
||||||
if pub.Curve() != ecdh.X25519() {
|
|
||||||
return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported ECDH curve")
|
|
||||||
}
|
|
||||||
publicKeyBytes = pub.Bytes()
|
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:
|
default:
|
||||||
return nil, pkix.AlgorithmIdentifier{}, fmt.Errorf("x509: unsupported public key type: %T", pub)
|
return nil, pkix.AlgorithmIdentifier{}, fmt.Errorf("x509: unsupported public key type: %T", pub)
|
||||||
}
|
}
|
||||||
@ -132,8 +143,8 @@ func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.A
|
|||||||
// (see RFC 5280, Section 4.1).
|
// (see RFC 5280, Section 4.1).
|
||||||
//
|
//
|
||||||
// The following key types are currently supported: *rsa.PublicKey,
|
// The following key types are currently supported: *rsa.PublicKey,
|
||||||
// *ecdsa.PublicKey, ed25519.PublicKey (not a pointer), and *ecdh.PublicKey
|
// *ecdsa.PublicKey, ed25519.PublicKey (not a pointer), and *ecdh.PublicKey.
|
||||||
// (X25519 only). Unsupported key types result in an error.
|
// Unsupported key types result in an error.
|
||||||
//
|
//
|
||||||
// This kind of key is commonly encoded in PEM blocks of type "PUBLIC KEY".
|
// This kind of key is commonly encoded in PEM blocks of type "PUBLIC KEY".
|
||||||
func MarshalPKIXPublicKey(pub any) ([]byte, error) {
|
func MarshalPKIXPublicKey(pub any) ([]byte, error) {
|
||||||
@ -542,6 +553,21 @@ func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) {
|
|||||||
return nil, false
|
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
|
// KeyUsage represents the set of actions that are valid for a given key. It's
|
||||||
// a bitmap of the KeyUsage* constants.
|
// a bitmap of the KeyUsage* constants.
|
||||||
type KeyUsage int
|
type KeyUsage int
|
||||||
|
Loading…
Reference in New Issue
Block a user