You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by GitBox <gi...@apache.org> on 2018/05/29 10:08:11 UTC

[GitHub] utzig closed pull request #164: Add support for declaring a key file under target

utzig closed pull request #164: Add support for declaring a key file under target
URL: https://github.com/apache/mynewt-newt/pull/164
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/newt/builder/targetbuild.go b/newt/builder/targetbuild.go
index cc2b6be7..d7a7a193 100644
--- a/newt/builder/targetbuild.go
+++ b/newt/builder/targetbuild.go
@@ -20,7 +20,12 @@
 package builder
 
 import (
+	"bufio"
+	"crypto/ecdsa"
+	"crypto/rsa"
+	"crypto/x509"
 	"encoding/json"
+	"encoding/pem"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -60,6 +65,7 @@ type TargetBuilder struct {
 	LoaderBuilder *Builder
 	LoaderList    interfaces.PackageList
 
+	keyFile          string
 	injectedSettings map[string]string
 
 	res *resolve.Resolution
@@ -89,6 +95,7 @@ func NewTargetTester(target *target.Target,
 		compilerPkg:      compilerPkg,
 		appPkg:           target.App(),
 		loaderPkg:        target.Loader(),
+		keyFile:          target.KeyFile,
 		testPkg:          testPkg,
 		injectedSettings: map[string]string{},
 	}
@@ -347,6 +354,75 @@ func (t *TargetBuilder) buildLoader() error {
 
 }
 
+/// Generates a .c source file with public key information required by the
+/// bootloader.
+///
+/// The input filename should be supplied by the user in the target.yml file,
+/// using the `target.key_file` option. This file can be either a private key
+/// in PEM format, an extracted public key in PEM format or a DER file.
+///
+/// To extract a PEM public key from the private key:
+///   `openssl ec -in ec_pk.pem -pubout -out pubkey.pub`
+///   `openssl rsa -in rsa_pk.pem -RSAPublicKey_out -out pubkey.pub`
+func (t *TargetBuilder) autogenKeys() error {
+	keyBytes, err := ioutil.ReadFile(t.keyFile)
+	if err != nil {
+		return util.NewNewtError(fmt.Sprintf("Error reading key file: %s", err))
+	}
+
+	// Initially try parsing a private key in PEM format, if it fails try
+	// parsing as PEM public key, otherwise accepted as raw key data (DER)
+
+	privKey, err := image.ParsePrivateKey(keyBytes)
+	if err == nil {
+		switch pk := privKey.(type) {
+		case *rsa.PrivateKey:
+			keyBytes = x509.MarshalPKCS1PublicKey(&pk.PublicKey)
+		case *ecdsa.PrivateKey:
+			keyBytes, err = x509.MarshalPKIXPublicKey(&pk.PublicKey)
+			if err != nil {
+				return util.NewNewtError("Failed parsing EC public key")
+			}
+		default:
+			return util.NewNewtError("Unknown private key format")
+		}
+	} else {
+		b, _ := pem.Decode(keyBytes)
+		if b != nil && (b.Type == "PUBLIC KEY" || b.Type == "RSA PUBLIC KEY") {
+			keyBytes = b.Bytes
+		}
+	}
+
+	srcDir := GeneratedSrcDir(t.target.Name())
+
+	f, _ := os.Create(srcDir + "/pubkey-autogen.c")
+	w := bufio.NewWriter(f)
+
+	fmt.Fprintln(w, "/* Autogenerated, do not edit. */")
+	fmt.Fprintln(w, "#include <bootutil/sign_key.h>")
+	fmt.Fprintf(w, "const unsigned char key[] = {")
+	for count, b := range keyBytes {
+		if count % 8 == 0 {
+			fmt.Fprintf(w, "\n    ")
+		} else {
+			fmt.Fprintf(w, " ")
+		}
+		fmt.Fprintf(w, "0x%02x,", b)
+	}
+	fmt.Fprintf(w, "\n};\n")
+	fmt.Fprintf(w, "const unsigned int key_len = %v;\n", len(keyBytes))
+	fmt.Fprintln(w, "const struct bootutil_key bootutil_keys[] = {")
+	fmt.Fprintln(w, "    [0] = {")
+	fmt.Fprintln(w, "        .key = key,")
+	fmt.Fprintln(w, "        .len = &key_len,")
+	fmt.Fprintln(w, "    },")
+	fmt.Fprintln(w, "};")
+	fmt.Fprintln(w, "const int bootutil_key_cnt = 1;");
+	w.Flush()
+
+	return nil
+}
+
 func (t *TargetBuilder) Build() error {
 	if err := t.PrepBuild(); err != nil {
 		return err
@@ -359,6 +435,13 @@ func (t *TargetBuilder) Build() error {
 		return err
 	}
 
+	if t.keyFile != "" {
+		err := t.autogenKeys()
+		if err != nil {
+			return err
+		}
+	}
+
 	if err := t.AppBuilder.Build(); err != nil {
 		return err
 	}
diff --git a/newt/image/image.go b/newt/image/image.go
index 176939b4..f43d5b91 100644
--- a/newt/image/image.go
+++ b/newt/image/image.go
@@ -297,13 +297,11 @@ func (image *Image) SetVersion(versStr string) error {
 	return nil
 }
 
-func (image *Image) SetSigningKey(fileName string, keyId uint8) error {
-	data, err := ioutil.ReadFile(fileName)
-	if err != nil {
-		return util.NewNewtError(fmt.Sprintf("Error reading key file: %s", err))
-	}
+func ParsePrivateKey(keyBytes []byte) (interface{}, error) {
+	var privKey interface{}
+	var err error
 
-	block, data := pem.Decode(data)
+	block, data := pem.Decode(keyBytes)
 	if block != nil && block.Type == "EC PARAMETERS" {
 		/*
 		 * Openssl prepends an EC PARAMETERS block before the
@@ -317,65 +315,61 @@ func (image *Image) SetSigningKey(fileName string, keyId uint8) error {
 		 * ParsePKCS1PrivateKey returns an RSA private key from its ASN.1
 		 * PKCS#1 DER encoded form.
 		 */
-		privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
+		privKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
 		if err != nil {
-			return util.NewNewtError(fmt.Sprintf("Private key parsing "+
+			return nil, util.NewNewtError(fmt.Sprintf("Private key parsing "+
 				"failed: %s", err))
 		}
-		image.SigningRSA = privateKey
 	}
 	if block != nil && block.Type == "EC PRIVATE KEY" {
 		/*
 		 * ParseECPrivateKey returns a EC private key
 		 */
-		privateKey, err := x509.ParseECPrivateKey(block.Bytes)
+		privKey, err = x509.ParseECPrivateKey(block.Bytes)
 		if err != nil {
-			return util.NewNewtError(fmt.Sprintf("Private key parsing "+
+			return nil, util.NewNewtError(fmt.Sprintf("Private key parsing "+
 				"failed: %s", err))
 		}
-		image.SigningEC = privateKey
 	}
 	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.
-		privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
+		privKey, err = x509.ParsePKCS8PrivateKey(block.Bytes)
 		if err != nil {
-			return util.NewNewtError(fmt.Sprintf("Private key parsing "+
+			return nil, util.NewNewtError(fmt.Sprintf("Private key parsing "+
 				"failed: %s", err))
 		}
-
-		err = image.storeKey(privateKey)
-		if err != nil {
-			return err
-		}
 	}
 	if block != nil && block.Type == "ENCRYPTED PRIVATE KEY" {
 		// This indicates a PKCS#8 key wrapped with PKCS#5
 		// encryption.
-		privateKey, err := parseEncryptedPrivateKey(block.Bytes)
-		if err != nil {
-			return util.FmtNewtError("Unable to decode encrypted private key: %s", err)
-		}
-
-		err = image.storeKey(privateKey)
+		privKey, err = parseEncryptedPrivateKey(block.Bytes)
 		if err != nil {
-			return err
+			return nil, util.FmtNewtError("Unable to decode encrypted private key: %s", err)
 		}
 	}
-	if image.SigningEC == nil && image.SigningRSA == nil {
-		return util.NewNewtError("Unknown private key format, EC/RSA private " +
+	if privKey == nil {
+		return nil, util.NewNewtError("Unknown private key format, EC/RSA private " +
 			"key in PEM format only.")
 	}
-	image.KeyId = keyId
 
-	return nil
+	return privKey, nil
 }
 
-// Store the given key in this image, determining the type of key and
-// storing it in the appropriate field.
-func (image *Image) storeKey(key interface{}) (err error) {
-	switch priv := key.(type) {
+func (image *Image) SetSigningKey(fileName string, keyId uint8) error {
+	keyBytes, err := ioutil.ReadFile(fileName)
+	if err != nil {
+		return util.NewNewtError(fmt.Sprintf("Error reading key file: %s", err))
+	}
+
+	image.KeyId = keyId
+	privKey, err := ParsePrivateKey(keyBytes)
+	if err != nil {
+		return err
+	}
+
+	switch priv := privKey.(type) {
 	case *rsa.PrivateKey:
 		image.SigningRSA = priv
 	case *ecdsa.PrivateKey:
diff --git a/newt/target/target.go b/newt/target/target.go
index d814efa4..7cd0025b 100644
--- a/newt/target/target.go
+++ b/newt/target/target.go
@@ -49,6 +49,7 @@ type Target struct {
 	LoaderName   string
 	BuildProfile string
 	HeaderSize   uint32
+	KeyFile      string
 
 	// target.yml configuration structure
 	Vars map[string]string
@@ -103,6 +104,8 @@ func (target *Target) Load(basePkg *pkg.LocalPackage) error {
 		}
 	}
 
+	target.KeyFile = target.Vars["target.key_file"]
+
 	// Note: App not required in the case of unit tests.
 
 	// Remember the name of the configuration file so that it can be specified


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services