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/01/04 18:21:19 UTC

[mynewt-newt] 14/17: larva: Add `image {de,en}crypt` commands

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-newt.git

commit a6095d7db80304e75b5cc03e0438460954bc7c22
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Tue Dec 18 18:18:18 2018 -0800

    larva: Add `image {de,en}crypt` commands
---
 larva/cli/image_cmds.go |  94 ++++++++++++++++++++++++++++++
 larva/cli/mfg_cmds.go   | 151 +++++++++++++++++++++++++++++++++++++-----------
 larva/cli/util.go       |  36 +++++++++++-
 larva/lvimg/lvimg.go    |  72 +++++++++++++++++++++++
 larva/lvmfg/lvmfg.go    |   6 +-
 5 files changed, 321 insertions(+), 38 deletions(-)

diff --git a/larva/cli/image_cmds.go b/larva/cli/image_cmds.go
index cdcac33..846c137 100644
--- a/larva/cli/image_cmds.go
+++ b/larva/cli/image_cmds.go
@@ -399,6 +399,74 @@ func runAddsigCmd(cmd *cobra.Command, args []string) {
 	}
 }
 
+func runDecryptCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 2 {
+		LarvaUsage(cmd, nil)
+	}
+
+	imgFilename := args[0]
+	keyFilename := args[1]
+
+	outFilename, err := CalcOutFilename(imgFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	img, err := readImage(imgFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	keyBytes, err := ioutil.ReadFile(keyFilename)
+	if err != nil {
+		LarvaUsage(cmd, util.FmtNewtError(
+			"Error reading key file: %s", err.Error()))
+	}
+
+	img, err = lvimg.DecryptImage(img, keyBytes)
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	if err := writeImage(img, outFilename); err != nil {
+		LarvaUsage(nil, err)
+	}
+}
+
+func runEncryptCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 2 {
+		LarvaUsage(cmd, nil)
+	}
+
+	imgFilename := args[0]
+	keyFilename := args[1]
+
+	outFilename, err := CalcOutFilename(imgFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	img, err := readImage(imgFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	keyBytes, err := ioutil.ReadFile(keyFilename)
+	if err != nil {
+		LarvaUsage(cmd, util.FmtNewtError(
+			"Error reading key file: %s", err.Error()))
+	}
+
+	img, err = lvimg.EncryptImage(img, keyBytes)
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	if err := writeImage(img, outFilename); err != nil {
+		LarvaUsage(nil, err)
+	}
+}
+
 func AddImageCommands(cmd *cobra.Command) {
 	imageCmd := &cobra.Command{
 		Use:   "image",
@@ -499,4 +567,30 @@ func AddImageCommands(cmd *cobra.Command) {
 		"Replace input file")
 
 	imageCmd.AddCommand(addsigCmd)
+
+	decryptCmd := &cobra.Command{
+		Use:   "decrypt <image> <priv-key-der>",
+		Short: "Decrypts an encrypted Mynewt image file",
+		Run:   runDecryptCmd,
+	}
+
+	decryptCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o",
+		"", "File to write to")
+	decryptCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+		"Replace input file")
+
+	imageCmd.AddCommand(decryptCmd)
+
+	encryptCmd := &cobra.Command{
+		Use:   "encrypt <image> <priv-key-der>",
+		Short: "Encrypts a Mynewt image file",
+		Run:   runEncryptCmd,
+	}
+
+	encryptCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o",
+		"", "File to write to")
+	encryptCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+		"Replace input file")
+
+	imageCmd.AddCommand(encryptCmd)
 }
diff --git a/larva/cli/mfg_cmds.go b/larva/cli/mfg_cmds.go
index 1a90b12..45c6372 100644
--- a/larva/cli/mfg_cmds.go
+++ b/larva/cli/mfg_cmds.go
@@ -30,6 +30,7 @@ import (
 	"mynewt.apache.org/newt/artifact/flash"
 	"mynewt.apache.org/newt/artifact/manifest"
 	"mynewt.apache.org/newt/artifact/mfg"
+	"mynewt.apache.org/newt/artifact/misc"
 	"mynewt.apache.org/newt/larva/lvmfg"
 	"mynewt.apache.org/newt/util"
 )
@@ -37,7 +38,7 @@ import (
 func readMfgBin(filename string) ([]byte, error) {
 	bin, err := ioutil.ReadFile(filename)
 	if err != nil {
-		return nil, util.FmtNewtError(
+		return nil, util.FmtChildNewtError(err,
 			"Failed to read manufacturing image: %s", err.Error())
 	}
 
@@ -76,9 +77,9 @@ func createNameBlobMap(binDir string,
 
 	for _, area := range areas {
 		filename := fmt.Sprintf("%s/%s.bin", binDir, area.Name)
-		bin, err := ioutil.ReadFile(filename)
+		bin, err := readMfgBin(filename)
 		if err != nil {
-			if !os.IsNotExist(err) {
+			if !util.IsNotExist(err) {
 				return nil, util.ChildNewtError(err)
 			}
 		} else {
@@ -144,7 +145,7 @@ func runSplitCmd(cmd *cobra.Command, args []string) {
 	}
 
 	binPath := fmt.Sprintf("%s/%s", mfgDir, mm.BinPath)
-	bin, err := ioutil.ReadFile(binPath)
+	bin, err := readMfgBin(binPath)
 	if err != nil {
 		LarvaUsage(cmd, util.FmtNewtError(
 			"Failed to read \"%s\": %s", binPath, err.Error()))
@@ -161,17 +162,13 @@ func runSplitCmd(cmd *cobra.Command, args []string) {
 
 	for name, data := range nbmap {
 		filename := fmt.Sprintf("%s/%s.bin", outDir, name)
-		if err := ioutil.WriteFile(filename, data,
-			os.ModePerm); err != nil {
-
-			LarvaUsage(nil, util.ChildNewtError(err))
+		if err := WriteFile(data, filename); err != nil {
+			LarvaUsage(nil, err)
 		}
 	}
 
 	mfgDstDir := fmt.Sprintf("%s/mfg", outDir)
-	util.StatusMessage(util.VERBOSITY_DEFAULT,
-		"Copying source mfg directory to %s\n", mfgDstDir)
-	if err := util.CopyDir(mfgDir, mfgDstDir); err != nil {
+	if err := CopyDir(mfgDir, mfgDstDir); err != nil {
 		LarvaUsage(nil, err)
 	}
 }
@@ -223,9 +220,9 @@ func runJoinCmd(cmd *cobra.Command, args []string) {
 			src := splitDir + "/mfg/" + info.Name()
 			dst := outDir + "/" + info.Name()
 			if info.IsDir() {
-				err = util.CopyDir(src, dst)
+				err = CopyDir(src, dst)
 			} else {
-				err = util.CopyFile(src, dst)
+				err = CopyFile(src, dst)
 			}
 			if err != nil {
 				LarvaUsage(nil, err)
@@ -238,31 +235,30 @@ func runJoinCmd(cmd *cobra.Command, args []string) {
 		LarvaUsage(nil, err)
 	}
 
-	if err := ioutil.WriteFile(outDir+"/"+mfg.MFG_IMG_FILENAME, finalBin,
-		os.ModePerm); err != nil {
-
-		LarvaUsage(nil, util.ChildNewtError(err))
+	binPath := fmt.Sprintf("%s/%s", outDir, mfg.MFG_IMG_FILENAME)
+	if err := WriteFile(finalBin, binPath); err != nil {
+		LarvaUsage(nil, err)
 	}
 }
 
-func runBootKeyCmd(cmd *cobra.Command, args []string) {
+func runSwapKeyCmd(cmd *cobra.Command, args []string) {
 	if len(args) < 3 {
 		LarvaUsage(cmd, nil)
 	}
 
-	sec0Filename := args[0]
+	mfgimgFilename := args[0]
 	okeyFilename := args[1]
 	nkeyFilename := args[2]
 
-	outFilename, err := CalcOutFilename(sec0Filename)
+	outFilename, err := CalcOutFilename(mfgimgFilename)
 	if err != nil {
 		LarvaUsage(cmd, err)
 	}
 
-	sec0, err := ioutil.ReadFile(sec0Filename)
+	bin, err := readMfgBin(mfgimgFilename)
 	if err != nil {
 		LarvaUsage(cmd, util.FmtNewtError(
-			"Failed to read sec0 file: %s", err.Error()))
+			"Failed to read mfgimg file: %s", err.Error()))
 	}
 
 	okey, err := ioutil.ReadFile(okeyFilename)
@@ -277,12 +273,87 @@ func runBootKeyCmd(cmd *cobra.Command, args []string) {
 			"Failed to read new key der: %s", err.Error()))
 	}
 
-	if err := lvmfg.ReplaceBootKey(sec0, okey, nkey); err != nil {
+	if err := lvmfg.ReplaceKey(bin, okey, nkey); err != nil {
 		LarvaUsage(nil, err)
 	}
 
-	if err := ioutil.WriteFile(outFilename, sec0, os.ModePerm); err != nil {
-		LarvaUsage(nil, util.ChildNewtError(err))
+	if err := WriteFile(bin, outFilename); err != nil {
+		LarvaUsage(nil, err)
+	}
+}
+
+func runRehashCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 1 {
+		LarvaUsage(cmd, nil)
+	}
+
+	mfgDir := args[0]
+
+	outDir, err := CalcOutFilename(mfgDir)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	// Read manifest and mfgimg.bin.
+	mman, err := readManifest(mfgDir)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	binPath := fmt.Sprintf("%s/%s", mfgDir, mman.BinPath)
+	bin, err := readMfgBin(binPath)
+	if err != nil {
+		LarvaUsage(cmd, util.FmtNewtError(
+			"Failed to read \"%s\": %s", binPath, err.Error()))
+	}
+
+	// Calculate accurate hash.
+	metaOff := -1
+	if mman.Meta != nil {
+		metaOff = mman.Meta.EndOffset
+	}
+	m, err := mfg.Parse(bin, metaOff, 0xff)
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	if err := m.RecalcHash(0xff); err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	hash, err := m.Hash()
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	// Update manifest.
+	mman.MfgHash = misc.HashString(hash)
+
+	// Write new artifacts.
+	if outDir != mfgDir {
+		// Not an in-place operation; copy input directory.
+		if err := CopyDir(mfgDir, outDir); err != nil {
+			LarvaUsage(nil, err)
+		}
+		binPath = fmt.Sprintf("%s/%s", outDir, mman.BinPath)
+	}
+
+	newBin, err := m.Bytes(0xff)
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+	if err := WriteFile(newBin, binPath); err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	json, err := mman.MarshalJson()
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	manPath := fmt.Sprintf("%s/%s", outDir, mfg.MANIFEST_FILENAME)
+	if err := WriteFile(json, manPath); err != nil {
+		LarvaUsage(nil, err)
 	}
 }
 
@@ -305,7 +376,7 @@ func AddMfgCommands(cmd *cobra.Command) {
 	mfgCmd.AddCommand(showCmd)
 
 	splitCmd := &cobra.Command{
-		Use:   "split <mfg-image-dir> <out-dir>",
+		Use:   "split <mfgimage-dir> <out-dir>",
 		Short: "Splits a Mynewt mfg section into several files",
 		Run:   runSplitCmd,
 	}
@@ -320,16 +391,28 @@ func AddMfgCommands(cmd *cobra.Command) {
 
 	mfgCmd.AddCommand(joinCmd)
 
-	bootKeyCmd := &cobra.Command{
-		Use:   "bootkey <sec0-bin> <cur-key-der> <new-key-der>",
-		Short: "Replaces the boot key in a manufacturing image",
-		Run:   runBootKeyCmd,
+	swapKeyCmd := &cobra.Command{
+		Use:   "swapkey <mfgimg-bin> <cur-key-der> <new-key-der>",
+		Short: "Replaces a key in a manufacturing image",
+		Run:   runSwapKeyCmd,
 	}
 
-	bootKeyCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o", "",
-		"File to write to")
-	bootKeyCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+	swapKeyCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o",
+		"", "File to write to")
+	swapKeyCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
 		"Replace input file")
 
-	mfgCmd.AddCommand(bootKeyCmd)
+	mfgCmd.AddCommand(swapKeyCmd)
+
+	rehashCmd := &cobra.Command{
+		Use:   "rehash <mfgimage-dir>",
+		Short: "Replaces an outdated mfgimage hash with an accurate one",
+		Run:   runRehashCmd,
+	}
+	rehashCmd.PersistentFlags().StringVarP(&OptOutFilename, "outdir", "o",
+		"", "Directory to write to")
+	rehashCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+		"Replace input files")
+
+	mfgCmd.AddCommand(rehashCmd)
 }
diff --git a/larva/cli/util.go b/larva/cli/util.go
index 20c3d5e..ce574e3 100644
--- a/larva/cli/util.go
+++ b/larva/cli/util.go
@@ -21,6 +21,7 @@ package cli
 
 import (
 	"fmt"
+	"io/ioutil"
 	"os"
 
 	log "github.com/Sirupsen/logrus"
@@ -40,7 +41,6 @@ func LarvaUsage(cmd *cobra.Command, err error) {
 	}
 
 	if cmd != nil {
-		fmt.Printf("\n")
 		fmt.Printf("%s - ", cmd.Name())
 		cmd.Help()
 	}
@@ -64,3 +64,37 @@ func CalcOutFilename(inFilename string) (string, error) {
 
 	return inFilename, nil
 }
+
+func CopyDir(src string, dst string) error {
+	if err := util.CopyDir(src, dst); err != nil {
+		return util.FmtNewtError(
+			"Failed to copy directory \"%s\" to \"%s\": %s",
+			src, dst, err.Error())
+	}
+
+	util.StatusMessage(util.VERBOSITY_DEFAULT,
+		"Copied directory \"%s\" to \"%s\"\n", src, dst)
+	return nil
+}
+
+func CopyFile(src string, dst string) error {
+	if err := util.CopyFile(src, dst); err != nil {
+		return util.FmtNewtError(
+			"Failed to copy file \"%s\" to \"%s\": %s",
+			src, dst, err.Error())
+	}
+
+	util.StatusMessage(util.VERBOSITY_DEFAULT,
+		"Copied file \"%s\" to \"%s\"\n", src, dst)
+	return nil
+}
+
+func WriteFile(data []byte, filename string) error {
+	if err := ioutil.WriteFile(filename, data, os.ModePerm); err != nil {
+		return util.FmtNewtError(
+			"Failed to write file \"%s\": %s", filename, err.Error())
+	}
+
+	util.StatusMessage(util.VERBOSITY_DEFAULT, "Wrote file \"%s\"\n", filename)
+	return nil
+}
diff --git a/larva/lvimg/lvimg.go b/larva/lvimg/lvimg.go
index 60d5ef4..8522199 100644
--- a/larva/lvimg/lvimg.go
+++ b/larva/lvimg/lvimg.go
@@ -114,3 +114,75 @@ func PadEcdsa256Sig(sig []byte) ([]byte, error) {
 
 	return sig, nil
 }
+
+// XXX: Only RSA supported for now.
+func ExtractSecret(img *image.Image) ([]byte, error) {
+	tlvs := img.RemoveTlvsWithType(image.IMAGE_TLV_ENC_RSA)
+	if len(tlvs) != 1 {
+		return nil, util.FmtNewtError(
+			"Image contains invalid count of ENC_RSA TLVs: %d; must contain 1",
+			len(tlvs))
+	}
+
+	return tlvs[0].Data, nil
+}
+
+// XXX: Only RSA supported for now.
+func DecryptImage(img image.Image, privKeBytes []byte) (image.Image, error) {
+	cipherSecret, err := ExtractSecret(&img)
+	if err != nil {
+		return img, err
+	}
+
+	privKe, err := image.ParsePrivKeDer(privKeBytes)
+	if err != nil {
+		return img, err
+	}
+
+	plainSecret, err := image.DecryptSecretRsa(privKe, cipherSecret)
+	if err != nil {
+		return img, err
+	}
+
+	body, err := image.EncryptImageBody(img.Body, plainSecret)
+	if err != nil {
+		return img, err
+	}
+
+	img.Body = body
+	return img, nil
+}
+
+func EncryptImage(img image.Image, pubKeBytes []byte) (image.Image, error) {
+	tlvp, err := img.FindUniqueTlv(image.IMAGE_TLV_ENC_RSA)
+	if err != nil {
+		return img, err
+	}
+	if tlvp != nil {
+		return img, util.FmtNewtError("Image already contains an ENC_RSA TLV")
+	}
+
+	plainSecret, err := image.GeneratePlainSecret()
+	if err != nil {
+		return img, err
+	}
+
+	cipherSecret, err := image.GenerateCipherSecret(pubKeBytes, plainSecret)
+	if err != nil {
+		return img, err
+	}
+
+	body, err := image.EncryptImageBody(img.Body, plainSecret)
+	if err != nil {
+		return img, err
+	}
+	img.Body = body
+
+	tlv, err := image.GenerateEncTlv(cipherSecret)
+	if err != nil {
+		return img, err
+	}
+	img.Tlvs = append(img.Tlvs, tlv)
+
+	return img, nil
+}
diff --git a/larva/lvmfg/lvmfg.go b/larva/lvmfg/lvmfg.go
index dbc0168..8bb6a22 100644
--- a/larva/lvmfg/lvmfg.go
+++ b/larva/lvmfg/lvmfg.go
@@ -136,7 +136,7 @@ func Join(mm NameBlobMap, eraseVal byte,
 				binstr = fmt.Sprintf("%x", bin[:4])
 			}
 			util.StatusMessage(util.VERBOSITY_DEFAULT,
-				"inserting %s (%x) at offset %d (0x%x)\n",
+				"inserting %s (%s) at offset %d (0x%x)\n",
 				area.Name, binstr, len(joined), len(joined))
 			joined = append(joined, bin...)
 		}
@@ -154,13 +154,13 @@ func Join(mm NameBlobMap, eraseVal byte,
 			"unprocessed flash areas: %s", strings.Join(names, ", "))
 	}
 
-	// Strip padding from the end of the joined bianry.
+	// Strip padding from the end of the joined binary.
 	joined = StripPadding(joined, eraseVal)
 
 	return joined, nil
 }
 
-func ReplaceBootKey(sec0 []byte, okey []byte, nkey []byte) error {
+func ReplaceKey(sec0 []byte, okey []byte, nkey []byte) error {
 	if len(okey) != len(nkey) {
 		return util.FmtNewtError(
 			"key lengths differ (%d != %d)", len(okey), len(nkey))