You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2019/06/27 20:06:41 UTC

[mynewt-artifact] branch master updated: Add support for ed25519 encrypted signing keys

This is an automated email from the ASF dual-hosted git repository.

ccollins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-artifact.git


The following commit(s) were added to refs/heads/master by this push:
     new 49a78be  Add support for ed25519 encrypted signing keys
49a78be is described below

commit 49a78be1e9eb942a502d31d9af3a445d0d18e3eb
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Thu Jun 27 11:34:30 2019 -0700

    Add support for ed25519 encrypted signing keys
    
    This is almost a straight copy of code from the newt repo, written by
    utzig@apache.org.
---
 image/create.go |  21 +++++++++-
 image/image.go  |   5 ++-
 sec/pkcs.go     |  13 +++++-
 sec/sign.go     | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++------
 sec/util.go     |  17 +++++++-
 5 files changed, 160 insertions(+), 17 deletions(-)

diff --git a/image/create.go b/image/create.go
index 6167453..1bdf2d1 100644
--- a/image/create.go
+++ b/image/create.go
@@ -33,6 +33,7 @@ import (
 
 	"github.com/apache/mynewt-artifact/errors"
 	"github.com/apache/mynewt-artifact/sec"
+	"golang.org/x/crypto/ed25519"
 )
 
 type ImageCreator struct {
@@ -79,7 +80,7 @@ func sigTlvType(key sec.PrivSignKey) uint8 {
 		default:
 			return 0
 		}
-	} else {
+	} else if key.Ec != nil {
 		switch key.Ec.Curve.Params().Name {
 		case "P-224":
 			return IMAGE_TLV_ECDSA224
@@ -88,6 +89,8 @@ func sigTlvType(key sec.PrivSignKey) uint8 {
 		default:
 			return 0
 		}
+	} else {
+		return IMAGE_TLV_ED25519
 	}
 }
 
@@ -152,13 +155,27 @@ func GenerateSigEc(key sec.PrivSignKey, hash []byte) ([]byte, error) {
 	return signature, nil
 }
 
+func GenerateSigEd25519(key sec.PrivSignKey, hash []byte) ([]byte, error) {
+	sig := ed25519.Sign(*key.Ed25519, hash)
+
+	if len(sig) != ed25519.SignatureSize {
+		return nil, errors.Errorf(
+			"ed25519 signature has wrong length: have=%d want=%d",
+			len(sig), ed25519.SignatureSize)
+	}
+
+	return sig, nil
+}
+
 func GenerateSig(key sec.PrivSignKey, hash []byte) ([]byte, error) {
 	key.AssertValid()
 
 	if key.Rsa != nil {
 		return GenerateSigRsa(key, hash)
-	} else {
+	} else if key.Ec != nil {
 		return GenerateSigEc(key, hash)
+	} else {
+		return GenerateSigEd25519(key, hash)
 	}
 }
 
diff --git a/image/image.go b/image/image.go
index dff4a00..f5dd1bb 100644
--- a/image/image.go
+++ b/image/image.go
@@ -60,6 +60,7 @@ const (
 	IMAGE_TLV_ECDSA224 = 0x21
 	IMAGE_TLV_ECDSA256 = 0x22
 	IMAGE_TLV_RSA3072  = 0x23
+	IMAGE_TLV_ED25519  = 0x24
 	IMAGE_TLV_ENC_RSA  = 0x30
 	IMAGE_TLV_ENC_KEK  = 0x31
 )
@@ -71,6 +72,7 @@ var imageTlvTypeNameMap = map[uint8]string{
 	IMAGE_TLV_ECDSA224: "ECDSA224",
 	IMAGE_TLV_ECDSA256: "ECDSA256",
 	IMAGE_TLV_RSA3072:  "RSA3072",
+	IMAGE_TLV_ED25519:  "ED25519",
 	IMAGE_TLV_ENC_RSA:  "ENC_RSA",
 	IMAGE_TLV_ENC_KEK:  "ENC_KEK",
 }
@@ -142,7 +144,8 @@ func ImageTlvTypeIsSig(tlvType uint8) bool {
 	return tlvType == IMAGE_TLV_RSA2048 ||
 		tlvType == IMAGE_TLV_RSA3072 ||
 		tlvType == IMAGE_TLV_ECDSA224 ||
-		tlvType == IMAGE_TLV_ECDSA256
+		tlvType == IMAGE_TLV_ECDSA256 ||
+		tlvType == IMAGE_TLV_ED25519
 }
 
 func ImageTlvTypeIsSecret(tlvType uint8) bool {
diff --git a/sec/pkcs.go b/sec/pkcs.go
index 3cf709f..bbd5ac8 100644
--- a/sec/pkcs.go
+++ b/sec/pkcs.go
@@ -78,7 +78,6 @@ type hashFunc func() hash.Hash
 
 func parseEncryptedPrivateKey(der []byte) (key interface{}, err error) {
 	var wrapper pkcs5
-	fmt.Printf("unmarshalling %v\n", der)
 	if _, err = asn1.Unmarshal(der, &wrapper); err != nil {
 		return nil, err
 	}
@@ -153,7 +152,17 @@ func unwrapPbes2Pbkdf2(param *pbkdf2Param, size int, iv []byte, hashNew hashFunc
 		return nil, err
 	}
 
-	return x509.ParsePKCS8PrivateKey(plain)
+	privKey, err := x509.ParsePKCS8PrivateKey(plain)
+	if err != nil {
+		var _privKey interface{}
+		_privKey, _err := ParseEd25519Pkcs8(plain)
+		// If this is not an ed25519 key, return
+		// error from x509 parser
+		if _err == nil {
+			return _privKey, _err
+		}
+	}
+	return privKey, err
 }
 
 // Verify that PKCS#7 padding is correct on this plaintext message.
diff --git a/sec/sign.go b/sec/sign.go
index 75ebd74..c27060e 100644
--- a/sec/sign.go
+++ b/sec/sign.go
@@ -25,21 +25,25 @@ import (
 	"crypto/ecdsa"
 	"crypto/rsa"
 	"crypto/x509"
+	"crypto/x509/pkix"
 	"encoding/asn1"
 	"encoding/pem"
 
 	"github.com/apache/mynewt-artifact/errors"
+	"golang.org/x/crypto/ed25519"
 )
 
 type PrivSignKey struct {
 	// Only one of these members is non-nil.
-	Rsa *rsa.PrivateKey
-	Ec  *ecdsa.PrivateKey
+	Rsa     *rsa.PrivateKey
+	Ec      *ecdsa.PrivateKey
+	Ed25519 *ed25519.PrivateKey
 }
 
 type PubSignKey struct {
-	Rsa *rsa.PublicKey
-	Ec  *ecdsa.PublicKey
+	Rsa     *rsa.PublicKey
+	Ec      *ecdsa.PublicKey
+	Ed25519 ed25519.PublicKey
 }
 
 type Sig struct {
@@ -47,6 +51,32 @@ type Sig struct {
 	Data    []byte
 }
 
+var oidPrivateKeyEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112}
+
+// Parse an ed25519 PKCS#8 certificate
+func ParseEd25519Pkcs8(der []byte) (key *ed25519.PrivateKey, err error) {
+	var privKey struct {
+		Version int
+		Algo    pkix.AlgorithmIdentifier
+		SeedKey []byte
+	}
+
+	if _, err := asn1.Unmarshal(der, &privKey); err != nil {
+		return nil, errors.Errorf("error parsing ASN1 key")
+	}
+	switch {
+	case privKey.Algo.Algorithm.Equal(oidPrivateKeyEd25519):
+		// ASN1 header (type+length) + seed
+		if len(privKey.SeedKey) != ed25519.SeedSize+2 {
+			return nil, errors.Errorf("unexpected size for Ed25519 private key")
+		}
+		key := ed25519.NewKeyFromSeed(privKey.SeedKey[2:])
+		return &key, nil
+	default:
+		return nil, errors.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
+	}
+}
+
 func parsePrivSignKeyItf(keyBytes []byte) (interface{}, error) {
 	var privKey interface{}
 	var err error
@@ -88,6 +118,20 @@ func parsePrivSignKeyItf(keyBytes []byte) (interface{}, error) {
 			return nil, errors.Wrapf(err, "Priv key parsing failed")
 		}
 	}
+	if block != nil && block.Type == "PRIVATE KEY" {
+		// This indicates a PKCS#8 unencrypted private key.
+		// The particular type of key will be indicated within
+		// the key itself.
+		privKey, err = x509.ParsePKCS8PrivateKey(block.Bytes)
+		if err != nil {
+			var _privKey interface{}
+			_privKey, err = ParseEd25519Pkcs8(block.Bytes)
+			if err != nil {
+				return nil, errors.Wrapf(err, "private key parsing failed")
+			}
+			privKey = _privKey
+		}
+	}
 	if block != nil && block.Type == "ENCRYPTED PRIVATE KEY" {
 		// This indicates a PKCS#8 key wrapped with PKCS#5
 		// encryption.
@@ -119,6 +163,8 @@ func ParsePubSignKey(keyBytes []byte) (PubSignKey, error) {
 		key.Rsa = pub
 	case *ecdsa.PublicKey:
 		key.Ec = pub
+	case ed25519.PublicKey:
+		key.Ed25519 = pub
 	default:
 		return key, errors.Errorf("unknown public signing key type: %T", pub)
 	}
@@ -139,6 +185,8 @@ func ParsePrivSignKey(keyBytes []byte) (PrivSignKey, error) {
 		key.Rsa = priv
 	case *ecdsa.PrivateKey:
 		key.Ec = priv
+	case *ed25519.PrivateKey:
+		key.Ed25519 = priv
 	default:
 		return key, errors.Errorf("unknown private key type: %T", itf)
 	}
@@ -147,8 +195,8 @@ func ParsePrivSignKey(keyBytes []byte) (PrivSignKey, error) {
 }
 
 func (key *PrivSignKey) AssertValid() {
-	if key.Rsa == nil && key.Ec == nil {
-		panic("invalid key; neither RSA nor ECC")
+	if key.Rsa == nil && key.Ec == nil && key.Ed25519 == nil {
+		panic("invalid key; neither RSA nor ECC nor ED25519")
 	}
 }
 
@@ -157,8 +205,11 @@ func (key *PrivSignKey) PubKey() PubSignKey {
 
 	if key.Rsa != nil {
 		return PubSignKey{Rsa: &key.Rsa.PublicKey}
-	} else {
+	} else if key.Ec != nil {
 		return PubSignKey{Ec: &key.Ec.PublicKey}
+	} else {
+		x := PubSignKey{Ed25519: key.Ed25519.Public().(ed25519.PublicKey)}
+		return x
 	}
 }
 
@@ -173,7 +224,7 @@ func (key *PrivSignKey) SigLen() uint16 {
 	if key.Rsa != nil {
 		pubk := key.Rsa.Public().(*rsa.PublicKey)
 		return uint16(pubk.Size())
-	} else {
+	} else if key.Ec != nil {
 		switch key.Ec.Curve.Params().Name {
 		case "P-224":
 			return 68
@@ -182,15 +233,57 @@ func (key *PrivSignKey) SigLen() uint16 {
 		default:
 			return 0
 		}
+	} else {
+		return ed25519.SignatureSize
 	}
 }
 
 func (key *PubSignKey) AssertValid() {
-	if key.Rsa == nil && key.Ec == nil {
-		panic("invalid public key; neither RSA nor ECC")
+	if key.Rsa == nil && key.Ec == nil && key.Ed25519 == nil {
+		panic("invalid public key; neither RSA nor ECC nor ED25519")
+	}
+
+	if key.Ed25519 != nil {
+		if _, err := marshalEd25519(key.Ed25519); err != nil {
+			panic("invalid public ed25519 key")
+		}
 	}
 }
 
+type pkixPublicKey struct {
+	Algo      pkix.AlgorithmIdentifier
+	BitString asn1.BitString
+}
+
+func marshalEd25519(pubbytes []uint8) ([]uint8, error) {
+	pkix := pkixPublicKey{
+		Algo: pkix.AlgorithmIdentifier{
+			Algorithm: oidPrivateKeyEd25519,
+		},
+		BitString: asn1.BitString{
+			Bytes:     pubbytes,
+			BitLength: 8 * len(pubbytes),
+		},
+	}
+
+	ret, err := asn1.Marshal(pkix)
+	if err != nil {
+		return nil, errors.Wrapf(err, "failed to encode ed25519 public key")
+	}
+
+	return ret, nil
+}
+
+func unmarshalEd25519(pubbytes []uint8) (pkixPublicKey, error) {
+	var pkix pkixPublicKey
+
+	if _, err := asn1.Unmarshal(pubbytes, &pkix); err != nil {
+		return pkix, errors.Wrapf(err, "failed to parse ed25519 public key")
+	}
+
+	return pkix, nil
+}
+
 func (key *PubSignKey) Bytes() ([]byte, error) {
 	key.AssertValid()
 
@@ -202,7 +295,7 @@ func (key *PubSignKey) Bytes() ([]byte, error) {
 		if err != nil {
 			return nil, err
 		}
-	} else {
+	} else if key.Ec != nil {
 		switch key.Ec.Curve.Params().Name {
 		case "P-224":
 			fallthrough
@@ -211,6 +304,8 @@ func (key *PubSignKey) Bytes() ([]byte, error) {
 		default:
 			return nil, errors.Errorf("unsupported ECC curve")
 		}
+	} else {
+		b, _ = marshalEd25519([]byte(key.Ed25519))
 	}
 
 	return b, nil
@@ -240,6 +335,10 @@ func checkOneKeyOneSig(k PubSignKey, sig Sig, hash []byte) (bool, error) {
 			"ecdsa signature verification not supported")
 	}
 
+	if k.Ed25519 != nil {
+		return ed25519.Verify(k.Ed25519, hash, sig.Data), nil
+	}
+
 	return false, nil
 }
 
diff --git a/sec/util.go b/sec/util.go
index 4eb7fbe..58b5017 100644
--- a/sec/util.go
+++ b/sec/util.go
@@ -5,6 +5,7 @@ import (
 	"encoding/pem"
 
 	"github.com/apache/mynewt-artifact/errors"
+	"golang.org/x/crypto/ed25519"
 )
 
 func parsePubPemKey(data []byte) (interface{}, error) {
@@ -21,7 +22,21 @@ func parsePubPemKey(data []byte) (interface{}, error) {
 
 	itf, err := x509.ParsePKIXPublicKey(p.Bytes)
 	if err != nil {
-		return nil, errors.Wrapf(err, "error parsing public key")
+		// Not x509; assume ed25519.
+		pkix, err := unmarshalEd25519(p.Bytes)
+		if err != nil {
+			return nil, errors.Errorf(
+				"error parsing public key: unrecognized format")
+		}
+
+		if len(pkix.BitString.Bytes) != ed25519.PublicKeySize {
+			return nil, errors.Errorf(
+				"error parsing public key: "+
+					"ed25519 public key has wrong size: have=%d want=%d",
+				len(pkix.BitString.Bytes), ed25519.PublicKeySize)
+		}
+
+		itf = ed25519.PublicKey(pkix.BitString.Bytes)
 	}
 
 	return itf, nil