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/06/28 16:35:27 UTC

[mynewt-imgmod] branch master updated (be4ad90 -> 8dc3201)

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

ccollins pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-imgmod.git.


    from be4ad90  Merge pull request #2 from ccollins476ad/crypt-full
     new ae6c866  Use external artifact package
     new 8dc3201  Remove all dependencies on the newt repo

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 cli/image_cmds.go | 212 +++++++++++++++++++++++++---------
 cli/mfg_cmds.go   | 334 +++++++++++++++++++++++++++++++++++++++---------------
 cli/util.go       |  44 ++++---
 go.mod            |   8 +-
 go.sum            |  57 +++++++++-
 iimg/iimg.go      | 102 +++++------------
 imfg/imfg.go      |  35 +++---
 imgmod.go         |  34 ++----
 iutil/iutil.go    |  37 ++++++
 9 files changed, 569 insertions(+), 294 deletions(-)
 create mode 100644 iutil/iutil.go


[mynewt-imgmod] 02/02: Remove all dependencies on the newt repo

Posted by cc...@apache.org.
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-imgmod.git

commit 8dc320157933ebc82c86a9a3be97ab0807f445f7
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Thu Jun 27 13:23:36 2019 -0700

    Remove all dependencies on the newt repo
    
    The imgmod tool was using the util package in the newt repo
    (mynewt.apache.org/newt/util).  This commit removes this dependency.
---
 cli/image_cmds.go | 103 ++++++++++++++++++++++++------------------------------
 cli/mfg_cmds.go   |  79 +++++++++++++++++------------------------
 cli/util.go       |  36 +++++++++----------
 go.mod            |   9 +++--
 go.sum            |  35 +++++++++++++++++++
 iimg/iimg.go      |   9 +++--
 imfg/imfg.go      |  31 ++++++++--------
 imgmod.go         |  34 ++++--------------
 iutil/iutil.go    |  37 ++++++++++++++++++++
 9 files changed, 196 insertions(+), 177 deletions(-)

diff --git a/cli/image_cmds.go b/cli/image_cmds.go
index 2b5852e..a010f1b 100644
--- a/cli/image_cmds.go
+++ b/cli/image_cmds.go
@@ -25,16 +25,17 @@ import (
 	"io/ioutil"
 	"os"
 	"sort"
+	"strconv"
 
-	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 
+	"github.com/apache/mynewt-artifact/errors"
 	"github.com/apache/mynewt-artifact/image"
 	"github.com/apache/mynewt-artifact/manifest"
 	"github.com/apache/mynewt-artifact/sec"
 	"mynewt.apache.org/imgmod/iimg"
-	"mynewt.apache.org/newt/util"
+	"mynewt.apache.org/imgmod/iutil"
 )
 
 func tlvStr(tlv image.ImageTlv) string {
@@ -62,21 +63,21 @@ func writeImage(img image.Image, filename string) error {
 		return err
 	}
 
-	util.StatusMessage(util.VERBOSITY_DEFAULT, "Wrote image %s\n", filename)
+	iutil.Printf("Wrote image %s\n", filename)
 	return nil
 }
 
 func parseTlvArgs(typeArg string, filenameArg string) (image.ImageTlv, error) {
-	tlvType, err := util.AtoiNoOct(typeArg)
+	tlvType, err := strconv.Atoi(typeArg)
 	if err != nil || tlvType < 0 {
-		return image.ImageTlv{}, util.FmtNewtError(
-			"Invalid TLV type integer: %s", typeArg)
+		return image.ImageTlv{}, errors.Errorf(
+			"invalid TLV type integer: %s", typeArg)
 	}
 
 	data, err := ioutil.ReadFile(filenameArg)
 	if err != nil {
-		return image.ImageTlv{}, util.FmtNewtError(
-			"Error reading TLV data file: %s", err.Error())
+		return image.ImageTlv{}, errors.Wrapf(err,
+			"error reading TLV data file")
 	}
 
 	return image.ImageTlv{
@@ -103,7 +104,7 @@ func runShowCmd(cmd *cobra.Command, args []string) {
 	if err != nil {
 		ImgmodUsage(nil, err)
 	}
-	fmt.Printf("%s\n", s)
+	iutil.Printf("%s\n", s)
 }
 
 func runBriefCmd(cmd *cobra.Command, args []string) {
@@ -121,15 +122,15 @@ func runBriefCmd(cmd *cobra.Command, args []string) {
 		ImgmodUsage(nil, err)
 	}
 
-	fmt.Printf("%8d| Header\n", offsets.Header)
-	fmt.Printf("%8d| Body\n", offsets.Body)
-	fmt.Printf("%8d| Trailer\n", offsets.Trailer)
+	iutil.Printf("%8d| Header\n", offsets.Header)
+	iutil.Printf("%8d| Body\n", offsets.Body)
+	iutil.Printf("%8d| Trailer\n", offsets.Trailer)
 	for i, tlv := range img.Tlvs {
-		fmt.Printf("%8d| TLV%d: type=%s(%d)\n",
+		iutil.Printf("%8d| TLV%d: type=%s(%d)\n",
 			offsets.Tlvs[i], i, image.ImageTlvTypeName(tlv.Header.Type),
 			tlv.Header.Type)
 	}
-	fmt.Printf("Total=%d\n", offsets.TotalSize)
+	iutil.Printf("Total=%d\n", offsets.TotalSize)
 }
 
 func runSignCmd(cmd *cobra.Command, args []string) {
@@ -155,8 +156,8 @@ func runSignCmd(cmd *cobra.Command, args []string) {
 
 	hash, err := img.Hash()
 	if err != nil {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Failed to read hash from specified image: %s", err.Error()))
+		ImgmodUsage(cmd, errors.Wrapf(err,
+			"failed to read hash from specified image"))
 	}
 
 	tlvs, err := image.BuildSigTlvs(keys, hash)
@@ -189,8 +190,8 @@ func runAddTlvsCmd(cmd *cobra.Command, args []string) {
 
 	tlvArgs := args[1:]
 	if len(tlvArgs)%2 != 0 {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Invalid argument count; each TLV requires two arguments"))
+		ImgmodUsage(cmd, errors.Errorf(
+			"invalid argument count; each TLV requires two arguments"))
 	}
 
 	tlvs := []image.ImageTlv{}
@@ -229,20 +230,20 @@ func runRmtlvsCmd(cmd *cobra.Command, args []string) {
 	tlvIndices := []int{}
 	idxMap := map[int]struct{}{}
 	for _, arg := range args[1:] {
-		idx, err := util.AtoiNoOct(arg)
+		idx, err := strconv.Atoi(arg)
 		if err != nil {
-			ImgmodUsage(cmd, util.FmtNewtError("Invalid TLV index: %s", arg))
+			ImgmodUsage(cmd, errors.Errorf("invalid TLV index: %s", arg))
 		}
 
 		if idx < 0 || idx >= len(img.Tlvs) {
-			ImgmodUsage(nil, util.FmtNewtError(
+			ImgmodUsage(nil, errors.Errorf(
 				"TLV index %s out of range; "+
 					"must be in range [0, %d] for this image",
 				arg, len(img.Tlvs)-1))
 		}
 
 		if _, ok := idxMap[idx]; ok {
-			ImgmodUsage(nil, util.FmtNewtError(
+			ImgmodUsage(nil, errors.Errorf(
 				"TLV index %d specified more than once", idx))
 		}
 		idxMap[idx] = struct{}{}
@@ -254,8 +255,7 @@ func runRmtlvsCmd(cmd *cobra.Command, args []string) {
 	sort.Sort(sort.Reverse(sort.IntSlice(tlvIndices)))
 	for _, idx := range tlvIndices {
 		tlv := img.Tlvs[idx]
-		util.StatusMessage(util.VERBOSITY_DEFAULT,
-			"Removing TLV%d: %s\n", idx, tlvStr(tlv))
+		iutil.Printf("Removing TLV%d: %s\n", idx, tlvStr(tlv))
 
 		img.Tlvs = append(img.Tlvs[0:idx], img.Tlvs[idx+1:]...)
 	}
@@ -301,7 +301,7 @@ func runHashableCmd(cmd *cobra.Command, args []string) {
 	}
 
 	if OptOutFilename == "" {
-		ImgmodUsage(cmd, util.FmtNewtError("--outfile (-o) option required"))
+		ImgmodUsage(cmd, errors.Errorf("--outfile (-o) option required"))
 	}
 
 	inFilename := args[0]
@@ -313,28 +313,25 @@ func runHashableCmd(cmd *cobra.Command, args []string) {
 	}
 
 	if (img.Header.Flags & image.IMAGE_F_ENCRYPTED) != 0 {
-		util.StatusMessage(util.VERBOSITY_QUIET,
+		fmt.Fprintf(os.Stderr,
 			"* Warning: extracting hashable content from an encrypted image\n")
 	}
 
 	f, err := os.Create(outFilename)
 	if err != nil {
-		ImgmodUsage(nil, util.ChildNewtError(err))
+		ImgmodUsage(nil, errors.Wrapf(err, "failed to create hashable output"))
 	}
 	defer f.Close()
 
 	if err := binary.Write(f, binary.LittleEndian, &img.Header); err != nil {
-		ImgmodUsage(nil, util.FmtNewtError(
-			"Error writing image header: %s", err.Error()))
+		ImgmodUsage(nil, errors.Wrapf(err, "error writing image header"))
 	}
 	_, err = f.Write(img.Body)
 	if err != nil {
-		ImgmodUsage(nil, util.FmtNewtError(
-			"Error writing image body: %s", err.Error()))
+		ImgmodUsage(nil, errors.Wrapf(err, "error writing image body"))
 	}
 
-	util.StatusMessage(util.VERBOSITY_DEFAULT,
-		"Wrote hashable content to %s\n", outFilename)
+	iutil.Printf("Wrote hashable content to %s\n", outFilename)
 }
 
 func runAddsigCmd(cmd *cobra.Command, args []string) {
@@ -346,12 +343,11 @@ func runAddsigCmd(cmd *cobra.Command, args []string) {
 	keyFilename := args[1]
 	sigFilename := args[2]
 
-	sigType, err := util.AtoiNoOct(args[3])
+	sigType, err := strconv.Atoi(args[3])
 	if err != nil || sigType < 0 || sigType > 255 ||
 		!image.ImageTlvTypeIsSig(uint8(sigType)) {
 
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Invalid signature type: %s", args[3]))
+		ImgmodUsage(cmd, errors.Errorf("invalid signature type: %s", args[3]))
 	}
 
 	outFilename, err := CalcOutFilename(imgFilename)
@@ -366,14 +362,12 @@ func runAddsigCmd(cmd *cobra.Command, args []string) {
 
 	keyData, err := ioutil.ReadFile(keyFilename)
 	if err != nil {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Error reading key file: %s", err.Error()))
+		ImgmodUsage(cmd, errors.Wrapf(err, "error reading key file"))
 	}
 
 	sigData, err := ioutil.ReadFile(sigFilename)
 	if err != nil {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Error reading signature file: %s", err.Error()))
+		ImgmodUsage(cmd, errors.Wrapf(err, "error reading signature file"))
 	}
 
 	// ECDSA256 signatures need to be padded out to >=72 bytes.
@@ -386,8 +380,7 @@ func runAddsigCmd(cmd *cobra.Command, args []string) {
 
 	// Build and append key hash TLV.
 	keyHashTlv := image.BuildKeyHashTlv(keyData)
-	util.StatusMessage(util.VERBOSITY_DEFAULT, "Adding TLV%d (%s)\n",
-		len(img.Tlvs), tlvStr(keyHashTlv))
+	iutil.Printf("Adding TLV%d (%s)\n", len(img.Tlvs), tlvStr(keyHashTlv))
 	img.Tlvs = append(img.Tlvs, keyHashTlv)
 
 	// Build and append signature TLV.
@@ -398,8 +391,7 @@ func runAddsigCmd(cmd *cobra.Command, args []string) {
 		},
 		Data: sigData,
 	}
-	util.StatusMessage(util.VERBOSITY_DEFAULT, "Adding TLV%d (%s)\n",
-		len(img.Tlvs), tlvStr(sigTlv))
+	iutil.Printf("Adding TLV%d (%s)\n", len(img.Tlvs), tlvStr(sigTlv))
 	img.Tlvs = append(img.Tlvs, sigTlv)
 
 	if err := writeImage(img, outFilename); err != nil {
@@ -427,8 +419,7 @@ func runDecryptCmd(cmd *cobra.Command, args []string) {
 
 	keyBytes, err := ioutil.ReadFile(keyFilename)
 	if err != nil {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Error reading key file: %s", err.Error()))
+		ImgmodUsage(cmd, errors.Wrapf(err, "error reading key file"))
 	}
 
 	img, err = iimg.DecryptImage(img, keyBytes)
@@ -461,8 +452,7 @@ func runDecryptFullCmd(cmd *cobra.Command, args []string) {
 
 	keyBytes, err := ioutil.ReadFile(keyFilename)
 	if err != nil {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Error reading key file: %s", err.Error()))
+		ImgmodUsage(cmd, errors.Wrapf(err, "error reading key file"))
 	}
 
 	img, err = iimg.DecryptImageFull(img, keyBytes)
@@ -494,8 +484,7 @@ func runEncryptCmd(cmd *cobra.Command, args []string) {
 
 	keyBytes, err := ioutil.ReadFile(keyFilename)
 	if err != nil {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Error reading key file: %s", err.Error()))
+		ImgmodUsage(cmd, errors.Wrapf(err, "error reading key file"))
 	}
 
 	img, err = iimg.EncryptImage(img, keyBytes)
@@ -528,8 +517,7 @@ func runEncryptFullCmd(cmd *cobra.Command, args []string) {
 
 	keyBytes, err := ioutil.ReadFile(keyFilename)
 	if err != nil {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Error reading key file: %s", err.Error()))
+		ImgmodUsage(cmd, errors.Wrapf(err, "error reading key file"))
 	}
 
 	img, err = iimg.EncryptImageFull(img, keyBytes)
@@ -588,8 +576,7 @@ func runVerifyCmd(cmd *cobra.Command, args []string) {
 
 	iss, err := sec.ReadPubSignKeys(OptSignKeys)
 	if err != nil {
-		ImgmodUsage(nil, errors.Wrapf(err,
-			"error reading signing key file"))
+		ImgmodUsage(nil, errors.Wrapf(err, "error reading signing key file"))
 	}
 
 	sigs, err := img.CollectSigs()
@@ -627,10 +614,10 @@ func runVerifyCmd(cmd *cobra.Command, args []string) {
 		}
 	}
 
-	fmt.Printf(" structure: %s\n", st)
-	fmt.Printf("      hash: %s\n", ha)
-	fmt.Printf("signatures: %s\n", si)
-	fmt.Printf("  manifest: %s\n", ma)
+	iutil.Printf(" structure: %s\n", st)
+	iutil.Printf("      hash: %s\n", ha)
+	iutil.Printf("signatures: %s\n", si)
+	iutil.Printf("  manifest: %s\n", ma)
 
 	if anyFails {
 		os.Exit(94) // EBADMSG
diff --git a/cli/mfg_cmds.go b/cli/mfg_cmds.go
index 9ce7108..1cd95fc 100644
--- a/cli/mfg_cmds.go
+++ b/cli/mfg_cmds.go
@@ -25,6 +25,7 @@ import (
 	"io/ioutil"
 	"os"
 	"sort"
+	"strconv"
 
 	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
@@ -35,7 +36,7 @@ import (
 	"github.com/apache/mynewt-artifact/mfg"
 	"github.com/apache/mynewt-artifact/sec"
 	"mynewt.apache.org/imgmod/imfg"
-	"mynewt.apache.org/newt/util"
+	"mynewt.apache.org/imgmod/iutil"
 )
 
 const MAX_SIG_LEN = 1024 // Bytes.
@@ -43,8 +44,7 @@ const MAX_SIG_LEN = 1024 // Bytes.
 func readMfgBin(filename string) ([]byte, error) {
 	bin, err := ioutil.ReadFile(filename)
 	if err != nil {
-		return nil, util.FmtChildNewtError(err,
-			"Failed to read manufacturing image: %s", err.Error())
+		return nil, errors.Wrapf(err, "failed to read manufacturing image")
 	}
 
 	return bin, nil
@@ -89,13 +89,13 @@ func extractFlashAreas(mman manifest.MfgManifest) ([]flash.FlashArea, error) {
 	areas := flash.SortFlashAreasByDevOff(mman.FlashAreas)
 
 	if len(areas) == 0 {
-		ImgmodUsage(nil, util.FmtNewtError(
+		ImgmodUsage(nil, errors.Errorf(
 			"Boot loader manifest does not contain flash map"))
 	}
 
 	overlaps, conflicts := flash.DetectErrors(areas)
 	if len(overlaps) > 0 || len(conflicts) > 0 {
-		return nil, util.NewNewtError(flash.ErrorText(overlaps, conflicts))
+		return nil, errors.New(flash.ErrorText(overlaps, conflicts))
 	}
 
 	if err := imfg.VerifyAreas(areas); err != nil {
@@ -115,8 +115,8 @@ func createNameBlobMap(binDir string,
 		filename := fmt.Sprintf("%s/%s.bin", binDir, area.Name)
 		bin, err := readMfgBin(filename)
 		if err != nil {
-			if !util.IsNotExist(err) {
-				return nil, util.ChildNewtError(err)
+			if !os.IsNotExist(errors.Cause(err)) {
+				return nil, errors.Wrapf(err, "could not read mfgimage binary")
 			}
 		} else {
 			mm[area.Name] = bin
@@ -132,10 +132,9 @@ func runMfgShowCmd(cmd *cobra.Command, args []string) {
 	}
 	inFilename := args[0]
 
-	metaEndOff, err := util.AtoiNoOct(args[1])
+	metaEndOff, err := strconv.Atoi(args[1])
 	if err != nil {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"invalid meta offset \"%s\"", args[1]))
+		ImgmodUsage(cmd, errors.Errorf("invalid meta offset \"%s\"", args[1]))
 	}
 
 	bin, err := readMfgBin(inFilename)
@@ -149,16 +148,15 @@ func runMfgShowCmd(cmd *cobra.Command, args []string) {
 	}
 
 	if m.Meta == nil {
-		util.StatusMessage(util.VERBOSITY_DEFAULT,
-			"Manufacturing image %s does not contain an MMR\n", inFilename)
+		iutil.Printf("Manufacturing image %s does not contain an MMR\n",
+			inFilename)
 	} else {
 		s, err := m.Meta.Json(metaEndOff)
 		if err != nil {
 			ImgmodUsage(nil, err)
 		}
-		util.StatusMessage(util.VERBOSITY_DEFAULT,
-			"Manufacturing image %s contains an MMR with "+
-				"the following properties:\n%s\n", inFilename, s)
+		iutil.Printf("Manufacturing image %s contains an MMR with "+
+			"the following properties:\n%s\n", inFilename, s)
 	}
 }
 
@@ -191,7 +189,7 @@ func runSplitCmd(cmd *cobra.Command, args []string) {
 	}
 
 	if err := os.Mkdir(outDir, os.ModePerm); err != nil {
-		ImgmodUsage(nil, util.ChildNewtError(err))
+		ImgmodUsage(nil, errors.Wrapf(err, "failed to make output directory"))
 	}
 
 	for name, data := range nbmap {
@@ -215,11 +213,6 @@ func runJoinCmd(cmd *cobra.Command, args []string) {
 	splitDir := args[0]
 	outDir := args[1]
 
-	if util.NodeExist(outDir) {
-		ImgmodUsage(nil, util.FmtNewtError(
-			"Destination \"%s\" already exists", outDir))
-	}
-
 	mm, err := readManifest(splitDir + "/mfg")
 	if err != nil {
 		ImgmodUsage(cmd, err)
@@ -246,8 +239,8 @@ func runJoinCmd(cmd *cobra.Command, args []string) {
 
 	infos, err := ioutil.ReadDir(splitDir + "/mfg")
 	if err != nil {
-		ImgmodUsage(nil, util.FmtNewtError(
-			"Error reading source mfg directory: %s", err.Error()))
+		ImgmodUsage(nil, errors.Wrapf(err,
+			"Error reading source mfg directory: %s"))
 	}
 	for _, info := range infos {
 		if info.Name() != mfg.MFG_BIN_IMG_FILENAME {
@@ -291,20 +284,18 @@ func genSwapKeyCmd(cmd *cobra.Command, args []string, isKek bool) {
 
 	bin, err := readMfgBin(mfgimgFilename)
 	if err != nil {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Failed to read mfgimg file: %s", err.Error()))
+		ImgmodUsage(cmd, errors.Wrapf(err,
+			"failed to read mfgimg file: %s"))
 	}
 
 	okey, err := ioutil.ReadFile(okeyFilename)
 	if err != nil {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Failed to read old key der: %s", err.Error()))
+		ImgmodUsage(cmd, errors.Wrapf(err, "failed to read old key der: %s"))
 	}
 
 	nkey, err := ioutil.ReadFile(nkeyFilename)
 	if err != nil {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Failed to read new key der: %s", err.Error()))
+		ImgmodUsage(cmd, errors.Wrapf(err, "failed to read new key der: %s"))
 	}
 
 	if isKek {
@@ -335,7 +326,7 @@ func runMfgHashableCmd(cmd *cobra.Command, args []string) {
 	}
 
 	if OptOutFilename == "" {
-		ImgmodUsage(cmd, util.FmtNewtError("--outfile (-o) option required"))
+		ImgmodUsage(cmd, errors.Errorf("--outfile (-o) option required"))
 	}
 
 	mfgDir := args[0]
@@ -475,18 +466,16 @@ func runAddsigMfgCmd(cmd *cobra.Command, args []string) {
 	// Read public key.
 	keyBytes, err := ioutil.ReadFile(keyFilename)
 	if err != nil {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Error reading key file: %s", err.Error()))
+		ImgmodUsage(cmd, errors.Wrapf(err, "error reading key file"))
 	}
 
 	// Read signature.
 	sig, err := ioutil.ReadFile(sigFilename)
 	if err != nil {
-		ImgmodUsage(cmd, util.FmtChildNewtError(err,
-			"Failed to read signature: %s", err.Error()))
+		ImgmodUsage(cmd, errors.Wrapf(err, "failed to read signature"))
 	}
 	if len(sig) > MAX_SIG_LEN {
-		ImgmodUsage(nil, util.FmtNewtError(
+		ImgmodUsage(nil, errors.Errorf(
 			"signature larger than arbitrary maximum length (%d > %d)",
 			len(sig), MAX_SIG_LEN))
 	}
@@ -539,20 +528,20 @@ func runRmtlvsMfgCmd(cmd *cobra.Command, args []string) {
 	tlvIndices := []int{}
 	idxMap := map[int]struct{}{}
 	for _, arg := range args[1:] {
-		idx, err := util.AtoiNoOct(arg)
+		idx, err := strconv.Atoi(arg)
 		if err != nil {
-			ImgmodUsage(cmd, util.FmtNewtError("Invalid TLV index: %s", arg))
+			ImgmodUsage(cmd, errors.Errorf("invalid TLV index: %s", arg))
 		}
 
 		if idx < 0 || idx >= numTlvs {
-			ImgmodUsage(nil, util.FmtNewtError(
+			ImgmodUsage(nil, errors.Errorf(
 				"TLV index %s out of range; "+
 					"must be in range [0, %d] for this mfgimage",
 				arg, numTlvs-1))
 		}
 
 		if _, ok := idxMap[idx]; ok {
-			ImgmodUsage(nil, util.FmtNewtError(
+			ImgmodUsage(nil, errors.Errorf(
 				"TLV index %d specified more than once", idx))
 		}
 		idxMap[idx] = struct{}{}
@@ -564,8 +553,7 @@ func runRmtlvsMfgCmd(cmd *cobra.Command, args []string) {
 	sort.Sort(sort.Reverse(sort.IntSlice(tlvIndices)))
 	for _, idx := range tlvIndices {
 		tlv := m.Meta.Tlvs[idx]
-		util.StatusMessage(util.VERBOSITY_DEFAULT,
-			"Removing TLV%d: %s\n", idx, mfgTlvStr(tlv))
+		iutil.Printf("Removing TLV%d: %s\n", idx, mfgTlvStr(tlv))
 
 		tlvSz := mfg.META_TLV_HEADER_SZ + len(tlv.Data)
 		m.MetaOff += tlvSz
@@ -622,8 +610,7 @@ func runVerifyMfgCmd(cmd *cobra.Command, args []string) {
 
 	iss, err := sec.ReadPubSignKeys(OptSignKeys)
 	if err != nil {
-		ImgmodUsage(nil, errors.Wrapf(err,
-			"error reading signing key file"))
+		ImgmodUsage(nil, errors.Wrapf(err, "error reading signing key file"))
 	}
 
 	si := ""
@@ -641,9 +628,9 @@ func runVerifyMfgCmd(cmd *cobra.Command, args []string) {
 		}
 	}
 
-	fmt.Printf(" structure: %s\n", st)
-	fmt.Printf("signatures: %s\n", si)
-	fmt.Printf("  manifest: %s\n", ma)
+	iutil.Printf(" structure: %s\n", st)
+	iutil.Printf("signatures: %s\n", si)
+	iutil.Printf("  manifest: %s\n", ma)
 
 	if anyFails {
 		os.Exit(94) // EBADMSG
diff --git a/cli/util.go b/cli/util.go
index 74d8ebf..ae934fd 100644
--- a/cli/util.go
+++ b/cli/util.go
@@ -24,10 +24,11 @@ import (
 	"io/ioutil"
 	"os"
 
+	"github.com/apache/mynewt-artifact/errors"
+	"github.com/otiai10/copy"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
-
-	"mynewt.apache.org/newt/util"
+	"mynewt.apache.org/imgmod/iutil"
 )
 
 var OptOutFilename string
@@ -43,7 +44,7 @@ func ImgmodUsage(cmd *cobra.Command, err error) {
 	}
 
 	if cmd != nil {
-		fmt.Printf("%s - ", cmd.Name())
+		fmt.Fprintf(os.Stderr, "%s - ", cmd.Name())
 		cmd.Help()
 	}
 	os.Exit(1)
@@ -52,7 +53,7 @@ func ImgmodUsage(cmd *cobra.Command, err error) {
 func CalcOutFilename(inFilename string) (string, error) {
 	if OptOutFilename != "" {
 		if OptInPlace {
-			return "", util.FmtNewtError(
+			return "", errors.Errorf(
 				"Only one of --outfile (-o) or --inplace (-i) options allowed")
 		}
 
@@ -60,7 +61,7 @@ func CalcOutFilename(inFilename string) (string, error) {
 	}
 
 	if !OptInPlace {
-		return "", util.FmtNewtError(
+		return "", errors.Errorf(
 			"--outfile (-o) or --inplace (-i) option required")
 	}
 
@@ -68,14 +69,12 @@ func CalcOutFilename(inFilename string) (string, error) {
 }
 
 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())
+	if err := copy.Copy(src, dst); err != nil {
+		return errors.Wrapf(err,
+			"failed to copy directory \"%s\" to \"%s\"", src, dst)
 	}
 
-	util.StatusMessage(util.VERBOSITY_DEFAULT,
-		"Copied directory \"%s\" to \"%s\"\n", src, dst)
+	iutil.Printf("Copied directory \"%s\" to \"%s\"\n", src, dst)
 	return nil
 }
 
@@ -91,23 +90,20 @@ func EnsureOutDir(inDir, outDir string) error {
 }
 
 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())
+	if err := copy.Copy(src, dst); err != nil {
+		return errors.Wrapf(err,
+			"failed to copy file \"%s\" to \"%s\"", src, dst)
 	}
 
-	util.StatusMessage(util.VERBOSITY_DEFAULT,
-		"Copied file \"%s\" to \"%s\"\n", src, dst)
+	iutil.Printf("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())
+		return errors.Wrapf(err, "failed to write file \"%s\"")
 	}
 
-	util.StatusMessage(util.VERBOSITY_DEFAULT, "Wrote file \"%s\"\n", filename)
+	iutil.Printf("Wrote file \"%s\"\n", filename)
 	return nil
 }
diff --git a/go.mod b/go.mod
index 795ba11..dd3a838 100644
--- a/go.mod
+++ b/go.mod
@@ -1,10 +1,9 @@
 module mynewt.apache.org/imgmod
 
 require (
-	github.com/apache/mynewt-artifact v0.0.2
+	github.com/apache/mynewt-artifact v0.0.3
+	github.com/otiai10/copy v1.0.1
 	github.com/pkg/errors v0.8.1
-	github.com/sirupsen/logrus v1.4.0
-	github.com/spf13/cobra v0.0.3
-	github.com/spf13/pflag v1.0.3 // indirect
-	mynewt.apache.org/newt v0.0.0-20190529170335-75b2c282a77d
+	github.com/sirupsen/logrus v1.4.2
+	github.com/spf13/cobra v0.0.5
 )
diff --git a/go.sum b/go.sum
index 6df6f34..fc13c8d 100644
--- a/go.sum
+++ b/go.sum
@@ -1,37 +1,67 @@
+bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/NickBall/go-aes-key-wrap v0.0.0-20170929221519-1c3aa3e4dfc5 h1:5BIUS5hwyLM298mOf8e8TEgD3cCYqc86uaJdQCYZo/o=
 github.com/NickBall/go-aes-key-wrap v0.0.0-20170929221519-1c3aa3e4dfc5/go.mod h1:w5D10RxC0NmPYxmQ438CC1S07zaC1zpvuNW7s5sUk2Q=
 github.com/apache/mynewt-artifact v0.0.2 h1:2tBlf84kyAifrEilcw1kQFQ8kpq4wBSudXpO8BEOXhw=
 github.com/apache/mynewt-artifact v0.0.2/go.mod h1:vFUd47t74KPQMzSBhQ2qp5Hc7D29OU/Tl3xHtFwN3k8=
+github.com/apache/mynewt-artifact v0.0.3 h1:760wpGruGSOPjslEo0fgs9PYJ58IAvyjuJqno1t4iOc=
+github.com/apache/mynewt-artifact v0.0.3/go.mod h1:vFUd47t74KPQMzSBhQ2qp5Hc7D29OU/Tl3xHtFwN3k8=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/otiai10/copy v1.0.1 h1:gtBjD8aq4nychvRZ2CyJvFWAw0aja+VHazDdruZKGZA=
+github.com/otiai10/copy v1.0.1/go.mod h1:8bMCJrAqOtN/d9oyh5HR7HhLQMvcGMpGdwRDYsfOCHc=
+github.com/otiai10/mint v1.2.3/go.mod h1:YnfyPNhBvnY8bW4SGQHCs/aAFhkgySlMZbrF5U0bOVw=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
 github.com/sirupsen/logrus v1.4.0 h1:yKenngtzGh+cUSSh6GWbxW2abRqhYUSR/t/6+2QqNvE=
 github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
 golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -41,9 +71,14 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/sys v0.0.0-20180707002001-3c6ecd8f22c6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 mynewt.apache.org/newt v0.0.0-20190529170335-75b2c282a77d h1:dnv0EBSSD88NahC0Pow+5DOH7gy4D5xj5n1iCIxA2NY=
 mynewt.apache.org/newt v0.0.0-20190529170335-75b2c282a77d/go.mod h1:lFsPYOHxMMWA11pydOeh0GVFiXtx0A9VnzOQ6SiRR88=
diff --git a/iimg/iimg.go b/iimg/iimg.go
index 29dd8a2..d4e7c9a 100644
--- a/iimg/iimg.go
+++ b/iimg/iimg.go
@@ -27,7 +27,6 @@ import (
 	"github.com/apache/mynewt-artifact/errors"
 	"github.com/apache/mynewt-artifact/image"
 	"github.com/apache/mynewt-artifact/sec"
-	"mynewt.apache.org/newt/util"
 )
 
 func GetDupSigs(img image.Image) []string {
@@ -77,7 +76,7 @@ func DetectInvalidSigTlvs(img image.Image) error {
 	}
 
 	if len(errStrs) > 0 {
-		return util.FmtNewtError("%s", strings.Join(errStrs, "\n"))
+		return errors.New(strings.Join(errStrs, "\n"))
 	}
 
 	return nil
@@ -98,7 +97,7 @@ func VerifyImage(img image.Image) error {
 			s += fmt.Sprintf("\n    %s", d)
 		}
 
-		return util.FmtNewtError("%s", s)
+		return errors.New(s)
 	}
 
 	return nil
@@ -106,7 +105,7 @@ func VerifyImage(img image.Image) error {
 
 func PadEcdsa256Sig(sig []byte) ([]byte, error) {
 	if len(sig) < 70 {
-		return nil, util.FmtNewtError(
+		return nil, errors.Errorf(
 			"Invalid ECDSA256 signature; length (%d) less than 70", len(sig))
 	}
 
@@ -121,7 +120,7 @@ func PadEcdsa256Sig(sig []byte) ([]byte, error) {
 func ExtractSecret(img *image.Image) ([]byte, error) {
 	tlvs := img.RemoveTlvsWithType(image.IMAGE_TLV_ENC_RSA)
 	if len(tlvs) != 1 {
-		return nil, util.FmtNewtError(
+		return nil, errors.Errorf(
 			"Image contains invalid count of ENC_RSA TLVs: %d; must contain 1",
 			len(tlvs))
 	}
diff --git a/imfg/imfg.go b/imfg/imfg.go
index 28dab7a..93a0187 100644
--- a/imfg/imfg.go
+++ b/imfg/imfg.go
@@ -26,9 +26,10 @@ import (
 	"sort"
 	"strings"
 
+	"github.com/apache/mynewt-artifact/errors"
 	"github.com/apache/mynewt-artifact/flash"
 	"github.com/apache/mynewt-artifact/mfg"
-	"mynewt.apache.org/newt/util"
+	"mynewt.apache.org/imgmod/iutil"
 )
 
 type NameBlobMap map[string][]byte
@@ -43,7 +44,7 @@ func errInvalidArea(areaName string, format string,
 	args ...interface{}) error {
 
 	suffix := fmt.Sprintf(format, args...)
-	return util.FmtNewtError("Invalid flash area \"%s\": %s", areaName, suffix)
+	return errors.Errorf("invalid flash area \"%s\": %s", areaName, suffix)
 }
 
 func verifyArea(area flash.FlashArea, minOffset int) error {
@@ -84,7 +85,7 @@ func Split(mfgBin []byte, deviceNum int,
 
 	for _, area := range areas {
 		if _, ok := mm[area.Name]; ok {
-			return nil, util.FmtNewtError(
+			return nil, errors.Errorf(
 				"two or more flash areas with same name: \"%s\"", area.Name)
 		}
 
@@ -136,8 +137,7 @@ func Join(mm NameBlobMap, eraseVal byte,
 			if len(bin) >= 4 {
 				binstr = fmt.Sprintf("%x", bin[:4])
 			}
-			util.StatusMessage(util.VERBOSITY_DEFAULT,
-				"inserting %s (%s) at offset %d (0x%x)\n",
+			iutil.Printf("inserting %s (%s) at offset %d (0x%x)\n",
 				area.Name, binstr, len(joined), len(joined))
 			joined = append(joined, bin...)
 		}
@@ -151,7 +151,7 @@ func Join(mm NameBlobMap, eraseVal byte,
 		}
 		sort.Strings(names)
 
-		return nil, util.FmtNewtError(
+		return nil, errors.Errorf(
 			"unprocessed flash areas: %s", strings.Join(names, ", "))
 	}
 
@@ -163,23 +163,22 @@ func Join(mm NameBlobMap, eraseVal byte,
 
 func replaceKey(mfgBin []byte, okey []byte, nkey []byte) (int, error) {
 	if len(okey) > len(mfgBin) {
-		return 0, util.FmtNewtError(
+		return 0, errors.Errorf(
 			"key longer than flash section (%d > %d)", len(okey), len(mfgBin))
 	}
 
 	idx := bytes.Index(mfgBin, okey)
 	if idx == -1 {
-		return 0, util.FmtNewtError("old key not present in flash section")
+		return 0, errors.Errorf("old key not present in flash section")
 	}
 
 	lastIdx := bytes.LastIndex(mfgBin, okey)
 	if idx != lastIdx {
-		return 0, util.FmtNewtError(
+		return 0, errors.Errorf(
 			"multiple instances of old key in flash section")
 	}
 
-	util.StatusMessage(util.VERBOSITY_VERBOSE,
-		"Replacing key at offset %d\n", idx)
+	iutil.PrintfVerbose("Replacing key at offset %d\n", idx)
 
 	copy(mfgBin[idx:idx+len(okey)], nkey)
 
@@ -188,7 +187,7 @@ func replaceKey(mfgBin []byte, okey []byte, nkey []byte) (int, error) {
 
 func ReplaceIsk(mfgBin []byte, okey []byte, nkey []byte) error {
 	if len(nkey) != len(okey) {
-		return util.FmtNewtError(
+		return errors.Errorf(
 			"key lengths differ (%d != %d)", len(nkey), len(okey))
 	}
 
@@ -201,7 +200,7 @@ func ReplaceIsk(mfgBin []byte, okey []byte, nkey []byte) error {
 
 func ReplaceKek(mfgBin []byte, okey []byte, nkey []byte) error {
 	if len(nkey) > len(okey) {
-		return util.FmtNewtError(
+		return errors.Errorf(
 			"new key longer than old key (%d > %d)", len(nkey), len(okey))
 	}
 
@@ -215,11 +214,11 @@ func ReplaceKek(mfgBin []byte, okey []byte, nkey []byte) error {
 	klIdx := keyIdx - 4
 	buf := bytes.NewBuffer(mfgBin[klIdx : klIdx+4])
 	if err := binary.Read(buf, binary.LittleEndian, &kl); err != nil {
-		return util.ChildNewtError(err)
+		return errors.Wrapf(err, "failed to read key length")
 	}
 
 	if int(kl) != len(okey) {
-		return util.FmtNewtError(
+		return errors.Errorf(
 			"embedded key length (off=%d) has unexpected value; "+
 				"want=%d have=%d",
 			klIdx, len(okey), kl)
@@ -228,7 +227,7 @@ func ReplaceKek(mfgBin []byte, okey []byte, nkey []byte) error {
 	buf = &bytes.Buffer{}
 	kl = uint32(len(nkey))
 	if err := binary.Write(buf, binary.LittleEndian, kl); err != nil {
-		return util.ChildNewtError(err)
+		return errors.Wrapf(err, "failed to write key length")
 	}
 
 	copy(mfgBin[klIdx:klIdx+4], buf.Bytes())
diff --git a/imgmod.go b/imgmod.go
index 1677525..14f3692 100644
--- a/imgmod.go
+++ b/imgmod.go
@@ -20,19 +20,14 @@
 package main
 
 import (
-	"fmt"
-
+	"github.com/apache/mynewt-artifact/errors"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 
 	"mynewt.apache.org/imgmod/cli"
-	"mynewt.apache.org/newt/util"
+	"mynewt.apache.org/imgmod/iutil"
 )
 
-var ImgmodLogLevel log.Level
-var imgmodSilent bool
-var imgmodQuiet bool
-var imgmodVerbose bool
 var imgmodVersion = "0.0.2"
 
 func main() {
@@ -46,24 +41,11 @@ func main() {
 		Long:    imgmodHelpText,
 		Example: imgmodHelpEx,
 		PersistentPreRun: func(cmd *cobra.Command, args []string) {
-			verbosity := util.VERBOSITY_DEFAULT
-			if imgmodSilent {
-				verbosity = util.VERBOSITY_SILENT
-			} else if imgmodQuiet {
-				verbosity = util.VERBOSITY_QUIET
-			} else if imgmodVerbose {
-				verbosity = util.VERBOSITY_VERBOSE
-			}
-
 			logLevel, err := log.ParseLevel(logLevelStr)
 			if err != nil {
-				cli.ImgmodUsage(nil, util.ChildNewtError(err))
-			}
-			ImgmodLogLevel = logLevel
-
-			if err := util.Init(ImgmodLogLevel, "", verbosity); err != nil {
-				cli.ImgmodUsage(nil, err)
+				cli.ImgmodUsage(nil, errors.Wrapf(err, "failed to parse log level"))
 			}
+			log.SetLevel(logLevel)
 		},
 
 		Run: func(cmd *cobra.Command, args []string) {
@@ -71,12 +53,10 @@ func main() {
 		},
 	}
 
-	imgmodCmd.PersistentFlags().BoolVarP(&imgmodVerbose, "verbose", "v", false,
+	imgmodCmd.PersistentFlags().BoolVarP(&iutil.Verbose, "verbose", "v", false,
 		"Enable verbose output when executing commands")
-	imgmodCmd.PersistentFlags().BoolVarP(&imgmodQuiet, "quiet", "q", false,
+	imgmodCmd.PersistentFlags().BoolVarP(&iutil.Quiet, "quiet", "q", false,
 		"Be quiet; only display error output")
-	imgmodCmd.PersistentFlags().BoolVarP(&imgmodSilent, "silent", "s", false,
-		"Be silent; don't output anything")
 	imgmodCmd.PersistentFlags().StringVarP(&logLevelStr, "loglevel", "l",
 		"WARN", "Log level")
 
@@ -88,7 +68,7 @@ func main() {
 		Long:    versHelpText,
 		Example: versHelpEx,
 		Run: func(cmd *cobra.Command, args []string) {
-			fmt.Printf("%s\n", imgmodVersion)
+			iutil.Printf("%s\n", imgmodVersion)
 		},
 	}
 	imgmodCmd.AddCommand(versCmd)
diff --git a/iutil/iutil.go b/iutil/iutil.go
new file mode 100644
index 0000000..4e07607
--- /dev/null
+++ b/iutil/iutil.go
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package iutil
+
+import "fmt"
+
+var Quiet bool
+var Verbose bool
+
+func Printf(format string, args ...interface{}) {
+	if !Quiet {
+		fmt.Printf(format, args...)
+	}
+}
+
+func PrintfVerbose(format string, args ...interface{}) {
+	if Verbose {
+		fmt.Printf(format, args...)
+	}
+}


[mynewt-imgmod] 01/02: Use external artifact package

Posted by cc...@apache.org.
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-imgmod.git

commit ae6c8665b400d6bd3a2afbd5c417b45ff40594a9
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Wed Jun 26 14:52:28 2019 -0700

    Use external artifact package
    
    The artifact package has been moved out of the newt repo and into its
    own repo (github.com/apache/mynewt-artifact).  Use this external package
    rather than the deprecated one in the newt repo.
---
 cli/image_cmds.go | 123 +++++++++++++++++++++++-
 cli/mfg_cmds.go   | 277 ++++++++++++++++++++++++++++++++++++++++++------------
 cli/util.go       |   8 +-
 go.mod            |   3 +
 go.sum            |  22 ++++-
 iimg/iimg.go      |  93 ++++++------------
 imfg/imfg.go      |   4 +-
 7 files changed, 393 insertions(+), 137 deletions(-)

diff --git a/cli/image_cmds.go b/cli/image_cmds.go
index 76f7c54..2b5852e 100644
--- a/cli/image_cmds.go
+++ b/cli/image_cmds.go
@@ -26,12 +26,14 @@ import (
 	"os"
 	"sort"
 
+	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 
+	"github.com/apache/mynewt-artifact/image"
+	"github.com/apache/mynewt-artifact/manifest"
+	"github.com/apache/mynewt-artifact/sec"
 	"mynewt.apache.org/imgmod/iimg"
-	"mynewt.apache.org/newt/artifact/image"
-	"mynewt.apache.org/newt/artifact/sec"
 	"mynewt.apache.org/newt/util"
 )
 
@@ -146,7 +148,7 @@ func runSignCmd(cmd *cobra.Command, args []string) {
 		ImgmodUsage(cmd, err)
 	}
 
-	keys, err := sec.ReadKeys(args[1:])
+	keys, err := sec.ReadPrivSignKeys(args[1:])
 	if err != nil {
 		ImgmodUsage(cmd, err)
 	}
@@ -467,7 +469,6 @@ func runDecryptFullCmd(cmd *cobra.Command, args []string) {
 	if err != nil {
 		ImgmodUsage(nil, err)
 	}
-
 	if err := writeImage(img, outFilename); err != nil {
 		ImgmodUsage(nil, err)
 	}
@@ -541,6 +542,101 @@ func runEncryptFullCmd(cmd *cobra.Command, args []string) {
 	}
 }
 
+func runVerifyCmd(cmd *cobra.Command, args []string) {
+	anyFails := false
+
+	if len(args) < 1 {
+		ImgmodUsage(cmd, nil)
+	}
+
+	imgFilename := args[0]
+
+	img, err := readImage(imgFilename)
+	if err != nil {
+		ImgmodUsage(cmd, err)
+	}
+
+	st := ""
+	if err := img.VerifyStructure(); err != nil {
+		st = fmt.Sprintf("BAD (%s)", err.Error())
+		anyFails = true
+	} else {
+		st = "good"
+	}
+
+	kes, err := sec.ReadPrivEncKeys(OptEncKeys)
+	if err != nil {
+		ImgmodUsage(nil, errors.Wrapf(err,
+			"error reading encryption key file"))
+	}
+
+	ha := ""
+	if img.IsEncrypted() && len(kes) == 0 {
+		ha = "not checked (image encrypted; no keys specified)"
+	} else {
+		keyIdx, err := img.VerifyHash(kes)
+		if err != nil {
+			ha = fmt.Sprintf("BAD (%s)", err.Error())
+			anyFails = true
+		} else {
+			ha = "good"
+			if keyIdx != -1 {
+				ha += fmt.Sprintf(" (%s)", OptEncKeys[keyIdx])
+			}
+		}
+	}
+
+	iss, err := sec.ReadPubSignKeys(OptSignKeys)
+	if err != nil {
+		ImgmodUsage(nil, errors.Wrapf(err,
+			"error reading signing key file"))
+	}
+
+	sigs, err := img.CollectSigs()
+	if err != nil {
+		ImgmodUsage(nil, err)
+	}
+
+	si := ""
+	if len(sigs) == 0 {
+		si = "n/a"
+	} else if len(iss) == 0 {
+		si = "not checked"
+	} else {
+		idx, err := img.VerifySigs(iss)
+		if err != nil {
+			si = fmt.Sprintf("BAD (%s)", err.Error())
+			anyFails = true
+		} else {
+			si = fmt.Sprintf("good (%s)", OptSignKeys[idx])
+		}
+	}
+
+	ma := "n/a"
+	if OptManifest != "" {
+		man, err := manifest.ReadManifest(OptManifest)
+		if err != nil {
+			ImgmodUsage(nil, err)
+		}
+
+		if err := img.VerifyManifest(man); err != nil {
+			ma = fmt.Sprintf("BAD (%s)", err.Error())
+			anyFails = true
+		} else {
+			ma = "good"
+		}
+	}
+
+	fmt.Printf(" structure: %s\n", st)
+	fmt.Printf("      hash: %s\n", ha)
+	fmt.Printf("signatures: %s\n", si)
+	fmt.Printf("  manifest: %s\n", ma)
+
+	if anyFails {
+		os.Exit(94) // EBADMSG
+	}
+}
+
 func AddImageCommands(cmd *cobra.Command) {
 	imageCmd := &cobra.Command{
 		Use:   "image",
@@ -579,7 +675,7 @@ func AddImageCommands(cmd *cobra.Command) {
 	imageCmd.AddCommand(signCmd)
 
 	addTlvsCmd := &cobra.Command{
-		Use: "addTlvs <img-file> <tlv-type> <data-filename> " +
+		Use: "addtlvs <img-file> <tlv-type> <data-filename> " +
 			"[tlv-type] [data-filename] [...]",
 		Short: "Adds the specified TLVs to a Mynewt image file",
 		Run:   runAddTlvsCmd,
@@ -658,6 +754,8 @@ func AddImageCommands(cmd *cobra.Command) {
 	decryptCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
 		"Replace input file")
 
+	imageCmd.AddCommand(decryptCmd)
+
 	decryptFullCmd := &cobra.Command{
 		Use:   "decryptfull <image> <priv-key-der>",
 		Short: "Decrypts an encrypted Mynewt image file (full)",
@@ -705,4 +803,19 @@ func AddImageCommands(cmd *cobra.Command) {
 		"Replace input file")
 
 	imageCmd.AddCommand(encryptFullCmd)
+
+	verifyCmd := &cobra.Command{
+		Use:   "verify <image>",
+		Short: "Verifies an Mynewt image's integrity",
+		Run:   runVerifyCmd,
+	}
+
+	verifyCmd.PersistentFlags().StringSliceVar(&OptSignKeys, "signkey",
+		nil, "Public signing key (.pem) (can be repeated)")
+	verifyCmd.PersistentFlags().StringSliceVar(&OptEncKeys, "enckey",
+		nil, "Private encryption key (.der) (can be repeated)")
+	verifyCmd.PersistentFlags().StringVar(&OptManifest, "manifest",
+		"", "Manifest file")
+
+	imageCmd.AddCommand(verifyCmd)
 }
diff --git a/cli/mfg_cmds.go b/cli/mfg_cmds.go
index afc8461..9ce7108 100644
--- a/cli/mfg_cmds.go
+++ b/cli/mfg_cmds.go
@@ -24,16 +24,17 @@ import (
 	"fmt"
 	"io/ioutil"
 	"os"
+	"sort"
 
+	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 
+	"github.com/apache/mynewt-artifact/flash"
+	"github.com/apache/mynewt-artifact/manifest"
+	"github.com/apache/mynewt-artifact/mfg"
+	"github.com/apache/mynewt-artifact/sec"
 	"mynewt.apache.org/imgmod/imfg"
-	"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/artifact/sec"
 	"mynewt.apache.org/newt/util"
 )
 
@@ -53,6 +54,37 @@ func readManifest(mfgDir string) (manifest.MfgManifest, error) {
 	return manifest.ReadMfgManifest(mfgDir + "/" + mfg.MANIFEST_FILENAME)
 }
 
+func readMfgDir(mfgDir string) (mfg.Mfg, manifest.MfgManifest, error) {
+	man, err := readManifest(mfgDir)
+	if err != nil {
+		return mfg.Mfg{}, manifest.MfgManifest{}, err
+	}
+
+	binPath := fmt.Sprintf("%s/%s", mfgDir, man.BinPath)
+	bin, err := readMfgBin(binPath)
+	if err != nil {
+		return mfg.Mfg{}, manifest.MfgManifest{}, errors.Wrapf(err,
+			"failed to read \"%s\"", binPath)
+	}
+
+	metaOff := -1
+	if man.Meta != nil {
+		metaOff = man.Meta.EndOffset
+	}
+	m, err := mfg.Parse(bin, metaOff, man.EraseVal)
+	if err != nil {
+		return mfg.Mfg{}, manifest.MfgManifest{}, err
+	}
+
+	return m, man, nil
+}
+
+func mfgTlvStr(tlv mfg.MetaTlv) string {
+	return fmt.Sprintf("%s,0x%02x",
+		mfg.MetaTlvTypeName(tlv.Header.Type),
+		tlv.Header.Type)
+}
+
 func extractFlashAreas(mman manifest.MfgManifest) ([]flash.FlashArea, error) {
 	areas := flash.SortFlashAreasByDevOff(mman.FlashAreas)
 
@@ -138,24 +170,22 @@ func runSplitCmd(cmd *cobra.Command, args []string) {
 	mfgDir := args[0]
 	outDir := args[1]
 
-	mm, err := readManifest(mfgDir)
+	m, man, err := readMfgDir(mfgDir)
 	if err != nil {
 		ImgmodUsage(cmd, err)
 	}
 
-	areas, err := extractFlashAreas(mm)
+	bin, err := m.Bytes(man.EraseVal)
 	if err != nil {
 		ImgmodUsage(nil, err)
 	}
 
-	binPath := fmt.Sprintf("%s/%s", mfgDir, mm.BinPath)
-	bin, err := readMfgBin(binPath)
+	areas, err := extractFlashAreas(man)
 	if err != nil {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Failed to read \"%s\": %s", binPath, err.Error()))
+		ImgmodUsage(nil, err)
 	}
 
-	nbmap, err := imfg.Split(bin, mm.Device, areas, 0xff)
+	nbmap, err := imfg.Split(bin, man.Device, areas, man.EraseVal)
 	if err != nil {
 		ImgmodUsage(nil, err)
 	}
@@ -204,12 +234,12 @@ func runJoinCmd(cmd *cobra.Command, args []string) {
 		ImgmodUsage(nil, err)
 	}
 
-	bin, err := imfg.Join(nbmap, 0xff, areas)
+	bin, err := imfg.Join(nbmap, mm.EraseVal, areas)
 	if err != nil {
 		ImgmodUsage(nil, err)
 	}
 
-	m, err := mfg.Parse(bin, mm.Meta.EndOffset, 0xff)
+	m, err := mfg.Parse(bin, mm.Meta.EndOffset, mm.EraseVal)
 	if err != nil {
 		ImgmodUsage(nil, err)
 	}
@@ -234,7 +264,7 @@ func runJoinCmd(cmd *cobra.Command, args []string) {
 		}
 	}
 
-	finalBin, err := m.Bytes(0xff)
+	finalBin, err := m.Bytes(mm.EraseVal)
 	if err != nil {
 		ImgmodUsage(nil, err)
 	}
@@ -311,32 +341,18 @@ func runMfgHashableCmd(cmd *cobra.Command, args []string) {
 	mfgDir := args[0]
 	outFilename := OptOutFilename
 
-	// Read manifest and mfgimg.bin.
-	mman, err := readManifest(mfgDir)
+	m, man, err := readMfgDir(mfgDir)
 	if err != nil {
 		ImgmodUsage(cmd, err)
 	}
 
-	binPath := fmt.Sprintf("%s/%s", mfgDir, mman.BinPath)
-	bin, err := readMfgBin(binPath)
-	if err != nil {
-		ImgmodUsage(cmd, util.FmtNewtError(
-			"Failed to read \"%s\": %s", binPath, err.Error()))
-	}
-
-	metaOff := -1
-	if mman.Meta != nil {
-		metaOff = mman.Meta.EndOffset
-	}
-	m, err := mfg.Parse(bin, metaOff, 0xff)
-	if err != nil {
-		ImgmodUsage(nil, err)
-	}
 	// Zero-out hash so that the hash can be recalculated.
-	m.Meta.ClearHash()
+	if m.Meta != nil {
+		m.Meta.ClearHash()
+	}
 
 	// Write hashable content to disk.
-	newBin, err := m.Bytes(0xff)
+	newBin, err := m.Bytes(man.EraseVal)
 	if err != nil {
 		ImgmodUsage(nil, err)
 	}
@@ -357,48 +373,30 @@ func runRehashCmd(cmd *cobra.Command, args []string) {
 		ImgmodUsage(cmd, err)
 	}
 
-	// Read manifest and mfgimg.bin.
-	mman, err := readManifest(mfgDir)
+	m, man, err := readMfgDir(mfgDir)
 	if err != nil {
 		ImgmodUsage(cmd, err)
 	}
 
-	binPath := fmt.Sprintf("%s/%s", mfgDir, mman.BinPath)
-	bin, err := readMfgBin(binPath)
-	if err != nil {
-		ImgmodUsage(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 {
-		ImgmodUsage(nil, err)
-	}
-
-	if err := m.RecalcHash(0xff); err != nil {
+	if err := m.RefillHash(man.EraseVal); err != nil {
 		ImgmodUsage(nil, err)
 	}
 
-	hash, err := m.Hash()
+	hash, err := m.Hash(man.EraseVal)
 	if err != nil {
 		ImgmodUsage(nil, err)
 	}
 
 	// Update manifest.
-	mman.MfgHash = misc.HashString(hash)
+	man.MfgHash = hex.EncodeToString(hash)
 
 	// Write new artifacts.
 	if err := EnsureOutDir(mfgDir, outDir); err != nil {
 		ImgmodUsage(nil, err)
 	}
-	binPath = fmt.Sprintf("%s/%s", outDir, mman.BinPath)
+	binPath := fmt.Sprintf("%s/%s", outDir, man.BinPath)
 
-	newBin, err := m.Bytes(0xff)
+	newBin, err := m.Bytes(man.EraseVal)
 	if err != nil {
 		ImgmodUsage(nil, err)
 	}
@@ -406,7 +404,7 @@ func runRehashCmd(cmd *cobra.Command, args []string) {
 		ImgmodUsage(nil, err)
 	}
 
-	json, err := mman.MarshalJson()
+	json, err := man.MarshalJson()
 	if err != nil {
 		ImgmodUsage(nil, err)
 	}
@@ -515,6 +513,143 @@ func runAddsigMfgCmd(cmd *cobra.Command, args []string) {
 	}
 }
 
+func runRmtlvsMfgCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 2 {
+		ImgmodUsage(cmd, nil)
+	}
+
+	mfgDir := args[0]
+
+	outFilename, err := CalcOutFilename(
+		mfgDir + "/" + mfg.MFG_BIN_IMG_FILENAME)
+	if err != nil {
+		ImgmodUsage(cmd, err)
+	}
+
+	m, man, err := readMfgDir(mfgDir)
+	if err != nil {
+		ImgmodUsage(cmd, err)
+	}
+
+	numTlvs := 0
+	if m.Meta != nil {
+		numTlvs = len(m.Meta.Tlvs)
+	}
+
+	tlvIndices := []int{}
+	idxMap := map[int]struct{}{}
+	for _, arg := range args[1:] {
+		idx, err := util.AtoiNoOct(arg)
+		if err != nil {
+			ImgmodUsage(cmd, util.FmtNewtError("Invalid TLV index: %s", arg))
+		}
+
+		if idx < 0 || idx >= numTlvs {
+			ImgmodUsage(nil, util.FmtNewtError(
+				"TLV index %s out of range; "+
+					"must be in range [0, %d] for this mfgimage",
+				arg, numTlvs-1))
+		}
+
+		if _, ok := idxMap[idx]; ok {
+			ImgmodUsage(nil, util.FmtNewtError(
+				"TLV index %d specified more than once", idx))
+		}
+		idxMap[idx] = struct{}{}
+
+		tlvIndices = append(tlvIndices, idx)
+	}
+
+	// Remove TLVs in reverse order to preserve index mapping.
+	sort.Sort(sort.Reverse(sort.IntSlice(tlvIndices)))
+	for _, idx := range tlvIndices {
+		tlv := m.Meta.Tlvs[idx]
+		util.StatusMessage(util.VERBOSITY_DEFAULT,
+			"Removing TLV%d: %s\n", idx, mfgTlvStr(tlv))
+
+		tlvSz := mfg.META_TLV_HEADER_SZ + len(tlv.Data)
+		m.MetaOff += tlvSz
+		m.Meta.Footer.Size -= uint16(tlvSz)
+
+		m.Meta.Tlvs = append(m.Meta.Tlvs[0:idx], m.Meta.Tlvs[idx+1:]...)
+	}
+
+	// Rehash.
+	if err := m.RefillHash(man.EraseVal); err != nil {
+		ImgmodUsage(nil, err)
+	}
+
+	// Write new artifacts.
+	newBin, err := m.Bytes(man.EraseVal)
+	if err != nil {
+		ImgmodUsage(nil, err)
+	}
+	if err := WriteFile(newBin, outFilename); err != nil {
+		ImgmodUsage(nil, err)
+	}
+}
+
+func runVerifyMfgCmd(cmd *cobra.Command, args []string) {
+	anyFails := false
+
+	if len(args) < 1 {
+		ImgmodUsage(cmd, nil)
+	}
+
+	mfgDir := args[0]
+
+	// Read mfgimg.bin and manifest.
+	m, man, err := readMfgDir(mfgDir)
+	if err != nil {
+		ImgmodUsage(cmd, err)
+	}
+
+	st := ""
+	if err := m.VerifyStructure(man.EraseVal); err != nil {
+		st = fmt.Sprintf("BAD (%s)", err.Error())
+		anyFails = true
+	} else {
+		st = "good"
+	}
+
+	ma := ""
+	if err := m.VerifyManifest(man); err != nil {
+		ma = fmt.Sprintf("BAD (%s)", err.Error())
+		anyFails = true
+	} else {
+		ma = "good"
+	}
+
+	iss, err := sec.ReadPubSignKeys(OptSignKeys)
+	if err != nil {
+		ImgmodUsage(nil, errors.Wrapf(err,
+			"error reading signing key file"))
+	}
+
+	si := ""
+	if len(man.Signatures) == 0 {
+		si = "n/a"
+	} else if len(iss) == 0 {
+		si = "not checked"
+	} else {
+		idx, err := mfg.VerifySigs(man, iss)
+		if err != nil {
+			si = fmt.Sprintf("BAD (%s)", err.Error())
+			anyFails = true
+		} else {
+			si = fmt.Sprintf("good (%s)", OptSignKeys[idx])
+		}
+	}
+
+	fmt.Printf(" structure: %s\n", st)
+	fmt.Printf("signatures: %s\n", si)
+	fmt.Printf("  manifest: %s\n", ma)
+
+	if anyFails {
+		os.Exit(94) // EBADMSG
+	}
+}
+
 func AddMfgCommands(cmd *cobra.Command) {
 	mfgCmd := &cobra.Command{
 		Use:   "mfg",
@@ -620,4 +755,28 @@ func AddMfgCommands(cmd *cobra.Command) {
 		"Replace input files")
 
 	mfgCmd.AddCommand(addsigCmd)
+
+	rmtlvsCmd := &cobra.Command{
+		Use:   "rmtlvs <mfgimage-dir> <tlv-index> [tlv-index] [...]",
+		Short: "Removes the specified TLVs from a Mynewt mfgimage",
+		Run:   runRmtlvsMfgCmd,
+	}
+
+	rmtlvsCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o", "",
+		"File to write to")
+	rmtlvsCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+		"Replace input file")
+
+	mfgCmd.AddCommand(rmtlvsCmd)
+
+	verifyCmd := &cobra.Command{
+		Use:   "verify <mfgimage-dir>",
+		Short: "Verifies an Mynewt mfgimage's integrity",
+		Run:   runVerifyMfgCmd,
+	}
+
+	verifyCmd.PersistentFlags().StringSliceVar(&OptSignKeys, "signkey",
+		nil, "Public signing key (.pem) (can be repeated)")
+
+	mfgCmd.AddCommand(verifyCmd)
 }
diff --git a/cli/util.go b/cli/util.go
index a2729ad..74d8ebf 100644
--- a/cli/util.go
+++ b/cli/util.go
@@ -32,12 +32,14 @@ import (
 
 var OptOutFilename string
 var OptInPlace bool
+var OptSignKeys []string
+var OptEncKeys []string
+var OptManifest string
 
 func ImgmodUsage(cmd *cobra.Command, err error) {
 	if err != nil {
-		sErr := err.(*util.NewtError)
-		log.Debugf("%s", sErr.StackTrace)
-		fmt.Fprintf(os.Stderr, "Error: %s\n", sErr.Text)
+		log.Debugf("%+v", err)
+		fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
 	}
 
 	if cmd != nil {
diff --git a/go.mod b/go.mod
index a1505df..795ba11 100644
--- a/go.mod
+++ b/go.mod
@@ -1,7 +1,10 @@
 module mynewt.apache.org/imgmod
 
 require (
+	github.com/apache/mynewt-artifact v0.0.2
+	github.com/pkg/errors v0.8.1
 	github.com/sirupsen/logrus v1.4.0
 	github.com/spf13/cobra v0.0.3
+	github.com/spf13/pflag v1.0.3 // indirect
 	mynewt.apache.org/newt v0.0.0-20190529170335-75b2c282a77d
 )
diff --git a/go.sum b/go.sum
index ef709dd..6df6f34 100644
--- a/go.sum
+++ b/go.sum
@@ -1,11 +1,16 @@
 github.com/NickBall/go-aes-key-wrap v0.0.0-20170929221519-1c3aa3e4dfc5 h1:5BIUS5hwyLM298mOf8e8TEgD3cCYqc86uaJdQCYZo/o=
 github.com/NickBall/go-aes-key-wrap v0.0.0-20170929221519-1c3aa3e4dfc5/go.mod h1:w5D10RxC0NmPYxmQ438CC1S07zaC1zpvuNW7s5sUk2Q=
+github.com/apache/mynewt-artifact v0.0.2 h1:2tBlf84kyAifrEilcw1kQFQ8kpq4wBSudXpO8BEOXhw=
+github.com/apache/mynewt-artifact v0.0.2/go.mod h1:vFUd47t74KPQMzSBhQ2qp5Hc7D29OU/Tl3xHtFwN3k8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
@@ -16,16 +21,29 @@ github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443 h1:IcSOAf4PyMp3U3XbIEj1/xJ2BjNN2jWv7JoyOsMxXUU=
+golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/sys v0.0.0-20180707002001-3c6ecd8f22c6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-mynewt.apache.org/newt v0.0.0-20190314184600-c5632c20daf3 h1:U9HanOWwCKSPTCZu7ti0MlmMlkWpTiR856BR2bRzHaU=
-mynewt.apache.org/newt v0.0.0-20190314184600-c5632c20daf3/go.mod h1:lFsPYOHxMMWA11pydOeh0GVFiXtx0A9VnzOQ6SiRR88=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 mynewt.apache.org/newt v0.0.0-20190529170335-75b2c282a77d h1:dnv0EBSSD88NahC0Pow+5DOH7gy4D5xj5n1iCIxA2NY=
 mynewt.apache.org/newt v0.0.0-20190529170335-75b2c282a77d/go.mod h1:lFsPYOHxMMWA11pydOeh0GVFiXtx0A9VnzOQ6SiRR88=
diff --git a/iimg/iimg.go b/iimg/iimg.go
index 7c28ed7..29dd8a2 100644
--- a/iimg/iimg.go
+++ b/iimg/iimg.go
@@ -24,8 +24,9 @@ import (
 	"fmt"
 	"strings"
 
-	"mynewt.apache.org/newt/artifact/image"
-	"mynewt.apache.org/newt/artifact/sec"
+	"github.com/apache/mynewt-artifact/errors"
+	"github.com/apache/mynewt-artifact/image"
+	"github.com/apache/mynewt-artifact/sec"
 	"mynewt.apache.org/newt/util"
 )
 
@@ -84,7 +85,7 @@ func DetectInvalidSigTlvs(img image.Image) error {
 
 func VerifyImage(img image.Image) error {
 	if len(img.Tlvs) == 0 || img.Tlvs[0].Header.Type != image.IMAGE_TLV_SHA256 {
-		return util.FmtNewtError("First TLV must be SHA256")
+		return errors.Errorf("First TLV must be SHA256")
 	}
 
 	if err := DetectInvalidSigTlvs(img); err != nil {
@@ -128,33 +129,6 @@ func ExtractSecret(img *image.Image) ([]byte, error) {
 	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 := sec.ParsePrivKeDer(privKeBytes)
-	if err != nil {
-		return img, err
-	}
-
-	plainSecret, err := sec.DecryptSecretRsa(privKe, cipherSecret)
-	if err != nil {
-		return img, err
-	}
-
-	body, err := sec.EncryptAES(img.Body, plainSecret)
-	if err != nil {
-		return img, err
-	}
-
-	img.Body = body
-
-	return img, nil
-}
-
 func recalcHash(img image.Image) (image.Image, error) {
 	hash, err := img.CalcHash()
 	if err != nil {
@@ -162,18 +136,32 @@ func recalcHash(img image.Image) (image.Image, error) {
 	}
 
 	img.RemoveTlvsWithType(image.IMAGE_TLV_SHA256)
-	img.Tlvs = append(img.Tlvs, image.ImageTlv{
+
+	tlv := image.ImageTlv{
 		Header: image.ImageTlvHdr{
 			Type: image.IMAGE_TLV_SHA256,
 			Pad:  0,
 			Len:  uint16(len(hash)),
 		},
 		Data: hash,
-	})
+	}
+
+	// The SHA256 TLV must come first.
+	img.Tlvs = append([]image.ImageTlv{tlv}, img.Tlvs...)
 
 	return img, nil
 }
 
+// XXX: Only RSA supported for now.
+func DecryptImage(img image.Image, privKeBytes []byte) (image.Image, error) {
+	key, err := sec.ParsePrivEncKey(privKeBytes)
+	if err != nil {
+		return img, err
+	}
+
+	return image.Decrypt(img, key)
+}
+
 func DecryptImageFull(img image.Image,
 	privKeBytes []byte) (image.Image, error) {
 
@@ -195,54 +183,27 @@ func DecryptImageFull(img image.Image,
 }
 
 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 := sec.EncryptAES(img.Body, plainSecret)
+	key, err := sec.ParsePubEncKey(pubKeBytes)
 	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)
-
-	img.Header.Flags |= image.IMAGE_F_ENCRYPTED
-
-	return img, nil
+	return image.Encrypt(img, key)
 }
 
 func EncryptImageFull(img image.Image,
 	pubKeBytes []byte) (image.Image, error) {
 
+	img.Header.Flags |= image.IMAGE_F_ENCRYPTED
+
+	// The hash needs to be recalculated now that the header has changed.
 	var err error
-	img, err = EncryptImage(img, pubKeBytes)
+	img, err = recalcHash(img)
 	if err != nil {
 		return img, err
 	}
 
-	img.Header.Flags |= image.IMAGE_F_ENCRYPTED
-
-	// The hash needs to be recalculated now that the header has changed.
-	img, err = recalcHash(img)
+	img, err = EncryptImage(img, pubKeBytes)
 	if err != nil {
 		return img, err
 	}
diff --git a/imfg/imfg.go b/imfg/imfg.go
index 9472ee1..28dab7a 100644
--- a/imfg/imfg.go
+++ b/imfg/imfg.go
@@ -26,8 +26,8 @@ import (
 	"sort"
 	"strings"
 
-	"mynewt.apache.org/newt/artifact/flash"
-	"mynewt.apache.org/newt/artifact/mfg"
+	"github.com/apache/mynewt-artifact/flash"
+	"github.com/apache/mynewt-artifact/mfg"
 	"mynewt.apache.org/newt/util"
 )