sm2,smx509: add encoding paths for SM2 ecdh keys

This commit is contained in:
Sun Yimin 2022-11-21 09:31:30 +08:00 committed by GitHub
parent 9805aa448a
commit 984913e228
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 127 additions and 4 deletions

View File

@ -66,6 +66,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 [smx509.ParsePKIXPublicKey] and encoded
// with [smx509.MarshalPKIXPublicKey]. For SM2 curve, it then needs to
// be converted with [sm2.PublicKeyToECDH] after parsing.
type PublicKey struct { type PublicKey struct {
curve Curve curve Curve
publicKey []byte publicKey []byte
@ -129,6 +133,10 @@ func (uv *PublicKey) SM2SharedKey(isResponder bool, kenLen int, sPub, sRemote *P
} }
// PrivateKey is an ECDH private key, usually kept secret. // PrivateKey is an ECDH private key, usually kept secret.
//
// These keys can be parsed with [smx509.ParsePKCS8PrivateKey] and encoded
// with [smx509.MarshalPKCS8PrivateKey]. For SM2 curve, it then needs to
// be converted with [sm2.PrivateKey.ECDH] after parsing.
type PrivateKey struct { type PrivateKey struct {
curve Curve curve Curve
privateKey []byte privateKey []byte

View File

@ -22,6 +22,7 @@ import (
"math/big" "math/big"
"strings" "strings"
"github.com/emmansun/gmsm/ecdh"
"github.com/emmansun/gmsm/internal/randutil" "github.com/emmansun/gmsm/internal/randutil"
"github.com/emmansun/gmsm/internal/subtle" "github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/kdf" "github.com/emmansun/gmsm/kdf"
@ -869,3 +870,41 @@ func IsSM2PublicKey(publicKey interface{}) bool {
func P256() elliptic.Curve { func P256() elliptic.Curve {
return sm2ec.P256() return sm2ec.P256()
} }
// PublicKeyToECDH 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 ecdh.
func PublicKeyToECDH(k *ecdsa.PublicKey) (*ecdh.PublicKey, error) {
c := curveToECDH(k.Curve)
if c == nil {
return nil, errors.New("sm2: unsupported curve by ecdh")
}
if !k.Curve.IsOnCurve(k.X, k.Y) {
return nil, errors.New("sm2: invalid public key")
}
return c.NewPublicKey(elliptic.Marshal(k.Curve, k.X, k.Y))
}
// 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 ecdh.
func (k *PrivateKey) ECDH() (*ecdh.PrivateKey, error) {
c := curveToECDH(k.Curve)
if c == nil {
return nil, errors.New("sm2: unsupported curve by ecdh")
}
size := (k.Curve.Params().N.BitLen() + 7) / 8
if k.D.BitLen() > size*8 {
return nil, errors.New("sm2: invalid private key")
}
return c.NewPrivateKey(k.D.FillBytes(make([]byte, size)))
}
func curveToECDH(c elliptic.Curve) ecdh.Curve {
switch c {
case sm2ec.P256():
return ecdh.P256()
default:
return nil
}
}

View File

@ -7,6 +7,7 @@ import (
"encoding/asn1" "encoding/asn1"
"errors" "errors"
"github.com/emmansun/gmsm/ecdh"
"github.com/emmansun/gmsm/sm2" "github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/sm9" "github.com/emmansun/gmsm/sm9"
) )
@ -117,7 +118,7 @@ func parseSM9PrivateKey(privKey pkcs8) (key interface{}, 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, *ecdsa.PrivateKey, a *sm2.PrivateKey, // The following key types are currently supported: *rsa.PrivateKey, *ecdsa.PrivateKey, a *sm2.PrivateKey, a *ecdh.PrivateKey
// a *sm9.SignMasterPrivateKey, a *sm9.SignPrivateKey, a *sm9.EncryptMasterPrivateKey, a *sm9.EncryptPrivateKey // a *sm9.SignMasterPrivateKey, a *sm9.SignPrivateKey, a *sm9.EncryptMasterPrivateKey, a *sm9.EncryptPrivateKey
// and ed25519.PrivateKey. Unsupported key types result in an error. // and ed25519.PrivateKey. Unsupported key types result in an error.
// //
@ -126,6 +127,8 @@ func MarshalPKCS8PrivateKey(key interface{}) ([]byte, error) {
switch k := key.(type) { switch k := key.(type) {
case *sm2.PrivateKey: case *sm2.PrivateKey:
return marshalPKCS8ECPrivateKey(&k.PrivateKey) return marshalPKCS8ECPrivateKey(&k.PrivateKey)
case *ecdh.PrivateKey:
return marshalPKCS8ECDHPrivateKey(k)
case *sm9.SignPrivateKey: case *sm9.SignPrivateKey:
return marshalPKCS8SM9SignPrivateKey(k) return marshalPKCS8SM9SignPrivateKey(k)
case *sm9.EncryptPrivateKey: case *sm9.EncryptPrivateKey:
@ -280,3 +283,25 @@ func marshalPKCS8ECPrivateKey(k *ecdsa.PrivateKey) ([]byte, error) {
} }
return asn1.Marshal(privKey) return asn1.Marshal(privKey)
} }
func marshalPKCS8ECDHPrivateKey(k *ecdh.PrivateKey) ([]byte, error) {
var privKey pkcs8
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())
}
return asn1.Marshal(privKey)
}

View File

@ -124,6 +124,22 @@ 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.(*sm2.PrivateKey); isEC {
ecdhKey, err := ecKey.ECDH()
if err != nil {
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
}
}
} }
} }

View File

@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/emmansun/gmsm/ecdh"
"github.com/emmansun/gmsm/sm2" "github.com/emmansun/gmsm/sm2"
) )
@ -133,3 +134,13 @@ func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *e
return priv, nil return priv, nil
} }
// marshalECDHPrivateKey marshals an EC private key into ASN.1, DER format
// suitable for SM2 curve.
func marshalECDHPrivateKey(key *ecdh.PrivateKey) ([]byte, error) {
return asn1.Marshal(ecPrivateKey{
Version: 1,
PrivateKey: key.Bytes(),
PublicKey: asn1.BitString{Bytes: key.PublicKey().Bytes()},
})
}

View File

@ -637,7 +637,7 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
for i := 0; i < opts.Intermediates.len(); i++ { for i := 0; i < opts.Intermediates.len(); i++ {
c, err := opts.Intermediates.cert(i) c, err := opts.Intermediates.cert(i)
if err != nil { if err != nil {
return nil, fmt.Errorf("crypto/x509: error fetching intermediate: %w", err) return nil, fmt.Errorf("x509: error fetching intermediate: %w", err)
} }
if len(c.Raw) == 0 { if len(c.Raw) == 0 {
return nil, errNotParsed return nil, errNotParsed

View File

@ -49,6 +49,7 @@ import (
"golang.org/x/crypto/cryptobyte" "golang.org/x/crypto/cryptobyte"
cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
"github.com/emmansun/gmsm/ecdh"
"github.com/emmansun/gmsm/internal/godebug" "github.com/emmansun/gmsm/internal/godebug"
"github.com/emmansun/gmsm/sm2" "github.com/emmansun/gmsm/sm2"
) )
@ -114,6 +115,20 @@ func marshalPublicKey(pub interface{}) (publicKeyBytes []byte, publicKeyAlgorith
case ed25519.PublicKey: case ed25519.PublicKey:
publicKeyBytes = pub publicKeyBytes = pub
publicKeyAlgorithm.Algorithm = oidPublicKeyEd25519 publicKeyAlgorithm.Algorithm = oidPublicKeyEd25519
case *ecdh.PublicKey: //TODO:will add SDK ECDH public key support from golang 1.19 later.
publicKeyBytes = pub.Bytes()
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)
} }
@ -519,6 +534,15 @@ 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.P256():
return oidNamedCurveP256SM2, 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 = x509.KeyUsage type KeyUsage = x509.KeyUsage