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 <ccoll...@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))