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))