You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ut...@apache.org on 2019/06/26 17:34:18 UTC

[mynewt-newt] 01/06: Add support for signing images with ed25519

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

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

commit 53f9e0ca5a28efbcfe8aa2202a49ad64111b7d20
Author: Fabio Utzig <ut...@apache.org>
AuthorDate: Thu May 9 12:35:43 2019 -0300

    Add support for signing images with ed25519
---
 artifact/image/create.go | 23 +++++++++++-
 artifact/image/image.go  |  5 ++-
 artifact/sec/key.go      | 96 +++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 111 insertions(+), 13 deletions(-)

diff --git a/artifact/image/create.go b/artifact/image/create.go
index cbbc410..fbe88d9 100644
--- a/artifact/image/create.go
+++ b/artifact/image/create.go
@@ -29,6 +29,7 @@ import (
 	"encoding/asn1"
 	"encoding/binary"
 	"encoding/hex"
+	"golang.org/x/crypto/ed25519"
 	"io/ioutil"
 	"math/big"
 
@@ -80,7 +81,7 @@ func sigTlvType(key sec.SignKey) uint8 {
 		default:
 			return 0
 		}
-	} else {
+	} else if key.Ec != nil {
 		switch key.Ec.Curve.Params().Name {
 		case "P-224":
 			return IMAGE_TLV_ECDSA224
@@ -89,6 +90,10 @@ func sigTlvType(key sec.SignKey) uint8 {
 		default:
 			return 0
 		}
+	} else if key.Ed25519 != nil {
+		return IMAGE_TLV_ED25519
+	} else {
+		panic("invalid key; neither RSA nor ECC nor ED25519")
 	}
 }
 
@@ -153,13 +158,27 @@ func GenerateSigEc(key sec.SignKey, hash []byte) ([]byte, error) {
 	return signature, nil
 }
 
+func GenerateSigEd25519(key sec.SignKey, hash []byte) ([]byte, error) {
+	sig := ed25519.Sign(*key.Ed25519, hash)
+
+	if len(sig) != ed25519.SignatureSize {
+		return nil, util.FmtNewtError("Something is really wrong\n")
+	}
+
+	return sig, nil
+}
+
 func GenerateSig(key sec.SignKey, 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 if key.Ed25519 != nil {
+		return GenerateSigEd25519(key, hash)
+	} else {
+		panic("invalid key; neither RSA nor ECC nor ED25519")
 	}
 }
 
diff --git a/artifact/image/image.go b/artifact/image/image.go
index 9855223..ab57b36 100644
--- a/artifact/image/image.go
+++ b/artifact/image/image.go
@@ -64,6 +64,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
 )
@@ -75,6 +76,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",
 }
@@ -141,7 +143,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 ParseVersion(versStr string) (ImageVersion, error) {
diff --git a/artifact/sec/key.go b/artifact/sec/key.go
index 9d073bd..5914b20 100644
--- a/artifact/sec/key.go
+++ b/artifact/sec/key.go
@@ -27,9 +27,11 @@ import (
 	"crypto/rsa"
 	"crypto/sha256"
 	"crypto/x509"
+	"crypto/x509/pkix"
 	"encoding/asn1"
 	"encoding/base64"
 	"encoding/pem"
+	"golang.org/x/crypto/ed25519"
 	"io/ioutil"
 
 	keywrap "github.com/NickBall/go-aes-key-wrap"
@@ -39,8 +41,56 @@ import (
 
 type SignKey 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 ed25519Pkcs struct {
+	Version int
+	Algo    pkix.AlgorithmIdentifier
+	SeedKey []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 ed25519Pkcs
+	if _, err := asn1.Unmarshal(der, &privKey); err != nil {
+		return nil, util.FmtNewtError("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, util.FmtNewtError("Unexpected size for Ed25519 private key")
+		}
+		key := ed25519.NewKeyFromSeed(privKey.SeedKey[2:])
+		return &key, nil
+	default:
+		return nil, util.FmtNewtError("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
+	}
+}
+
+type pkixPublicKey struct {
+	Algo      pkix.AlgorithmIdentifier
+	BitString asn1.BitString
+}
+
+func marshalEd25519(pubbytes []uint8) []uint8 {
+	pkix := pkixPublicKey{
+		Algo: pkix.AlgorithmIdentifier{
+			Algorithm: oidPrivateKeyEd25519,
+		},
+		BitString: asn1.BitString{
+			Bytes:     pubbytes,
+			BitLength: 8 * len(pubbytes),
+		},
+	}
+
+	ret, _ := asn1.Marshal(pkix)
+	return ret
 }
 
 func ParsePrivateKey(keyBytes []byte) (interface{}, error) {
@@ -83,8 +133,13 @@ func ParsePrivateKey(keyBytes []byte) (interface{}, error) {
 		// the key itself.
 		privKey, err = x509.ParsePKCS8PrivateKey(block.Bytes)
 		if err != nil {
-			return nil, util.FmtNewtError(
-				"Private key parsing failed: %s", err)
+			var _privKey interface{}
+			_privKey, err = parseEd25519Pkcs8(block.Bytes)
+			if err != nil {
+				return nil, util.FmtNewtError(
+					"Private key parsing failed: %s", err)
+			}
+			privKey = _privKey
 		}
 	}
 	if block != nil && block.Type == "ENCRYPTED PRIVATE KEY" {
@@ -118,6 +173,8 @@ func BuildPrivateKey(keyBytes []byte) (SignKey, error) {
 		key.Rsa = priv
 	case *ecdsa.PrivateKey:
 		key.Ec = priv
+	case *ed25519.PrivateKey:
+		key.Ed25519 = priv
 	default:
 		return key, util.NewNewtError("Unknown private key format")
 	}
@@ -151,12 +208,22 @@ func ReadKeys(filenames []string) ([]SignKey, error) {
 }
 
 func (key *SignKey) 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")
 	}
 
-	if key.Rsa != nil && key.Ec != nil {
-		panic("invalid key; neither RSA nor ECC")
+	total := 0
+	if key.Rsa != nil {
+		total++
+	}
+	if key.Ec != nil {
+		total++
+	}
+	if key.Ed25519 != nil {
+		total++
+	}
+	if total != 1 {
+		panic("invalid key; neither RSA nor ECC nor ED25519")
 	}
 }
 
@@ -167,7 +234,7 @@ func (key *SignKey) PubBytes() ([]uint8, error) {
 
 	if key.Rsa != nil {
 		pubkey, _ = asn1.Marshal(key.Rsa.PublicKey)
-	} else {
+	} else if key.Ec != nil {
 		switch key.Ec.Curve.Params().Name {
 		case "P-224":
 			fallthrough
@@ -176,6 +243,11 @@ func (key *SignKey) PubBytes() ([]uint8, error) {
 		default:
 			return nil, util.NewNewtError("Unsupported ECC curve")
 		}
+	} else if key.Ed25519 != nil {
+		bytes := key.Ed25519.Public().(ed25519.PublicKey)
+		pubkey = marshalEd25519(bytes)
+	} else {
+		panic("invalid key; neither RSA nor ECC nor ED25519")
 	}
 
 	return pubkey, nil
@@ -192,7 +264,7 @@ func (key *SignKey) 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
@@ -201,6 +273,10 @@ func (key *SignKey) SigLen() uint16 {
 		default:
 			return 0
 		}
+	} else if key.Ed25519 != nil {
+		return ed25519.SignatureSize
+	} else {
+		panic("invalid key; neither RSA nor ECC nor ED25519")
 	}
 }