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/21 21:25:24 UTC

[mynewt-artifact] branch master updated (b7033fa -> 441a611)

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


    from b7033fa  Add .travis.yml
     new 697ec59  Add "errors" package
     new c15c49e  Add "verify" functions to API
     new 58017f7  "Verify" unit tests
     new fea4ea7  Turn `artifact` into a Go module
     new fb24b39  Some code reorginazation and comments
     new 5b2c4f8  Add missing Apache licenses
     new 9515a56  Add .gitignore
     new b980059  Add NOTICE file
     new ced5960  Add README.md
     new 441a611  Merge pull request #1 from ccollins476ad/verify

The 23 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:
 .travis.yml => .gitignore                    |   24 +-
 LICENSE                                      |  311 ++++
 NOTICE                                       |    8 +
 .travis.yml => README.md                     |   27 +-
 errors/errors.go                             |   92 ++
 flash/flash.go                               |    6 +-
 go.mod                                       |   10 +
 go.sum                                       |   19 +
 image/create.go                              |   35 +-
 image/image.go                               |  347 +---
 image/image_test.go                          |  218 +++
 image/keys_test.go                           |   61 +-
 image/map.go                                 |  100 ++
 image/parse.go                               |  219 +++
 image/testdata/bad-hash.img                  |  Bin 0 -> 9412 bytes
 image/testdata/bad-signature.img             |  Bin 0 -> 9680 bytes
 image/testdata/bad-signature.json            | 2267 ++++++++++++++++++++++++++
 image/testdata/garbage.img                   |    1 +
 image/testdata/good-signed.img               |  Bin 0 -> 9680 bytes
 image/testdata/good-signed.json              | 2267 ++++++++++++++++++++++++++
 image/testdata/good-unsigned.img             |  Bin 0 -> 9412 bytes
 image/testdata/good-unsigned.json            | 2267 ++++++++++++++++++++++++++
 image/testdata/mismatch-hash.img             |  Bin 0 -> 9412 bytes
 image/testdata/mismatch-hash.json            | 2267 ++++++++++++++++++++++++++
 image/testdata/mismatch-version.img          |  Bin 0 -> 9412 bytes
 image/testdata/mismatch-version.json         | 2267 ++++++++++++++++++++++++++
 image/testdata/sign-key-pub.der              |  Bin 0 -> 270 bytes
 image/testdata/sign-key.pem                  |   27 +
 image/testdata/truncated.img                 |  Bin 0 -> 9000 bytes
 image/v1.go                                  |   57 +-
 image/verify.go                              |  106 ++
 manifest/manifest.go                         |   29 +-
 manifest/mfg_manifest.go                     |  108 +-
 mfg/map_meta.go                              |   19 +-
 mfg/meta.go                                  |  149 +-
 mfg/mfg.go                                   |  139 +-
 mfg/mfg_test.go                              |  248 +++
 mfg/parse.go                                 |  137 ++
 mfg/testdata/garbage.bin                     |    1 +
 mfg/testdata/garbage.json                    |   78 +
 mfg/testdata/hash1-fm1-ext0-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-ext0-tgts1-sign0.json |   78 +
 mfg/testdata/hash1-fm1-ext1-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-ext1-tgts1-sign0.json |   85 +
 mfg/testdata/hash1-fm1-ext1-tgts1-sign1.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-ext1-tgts1-sign1.json |   91 ++
 mfg/testdata/hash1-fm1-ext1-tgtsm-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-ext1-tgtsm-sign0.json |   92 ++
 mfg/testdata/hash1-fm1-extm-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-extm-tgts1-sign0.json |   85 +
 mfg/testdata/hash1-fmm-ext1-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fmm-ext1-tgts1-sign0.json |   85 +
 mfg/testdata/hashm-fm1-ext0-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hashm-fm1-ext0-tgts1-sign0.json |   78 +
 mfg/testdata/hashx-fm1-ext0-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hashx-fm1-ext0-tgts1-sign0.json |   78 +
 mfg/testdata/sign-key-pub.der                |  Bin 0 -> 270 bytes
 mfg/testdata/sign-key.pem                    |   27 +
 mfg/testdata/unknown-tlv.bin                 |  Bin 0 -> 42448 bytes
 mfg/testdata/unknown-tlv.json                |   78 +
 mfg/verify.go                                |  221 +++
 misc/misc.go                                 |   28 -
 sec/encrypt.go                               |   10 +-
 sec/key.go                                   |  111 +-
 sec/sig.go                                   |   74 +
 65 files changed, 14495 insertions(+), 637 deletions(-)
 copy .travis.yml => .gitignore (74%)
 create mode 100644 LICENSE
 create mode 100644 NOTICE
 copy .travis.yml => README.md (65%)
 create mode 100644 errors/errors.go
 create mode 100644 go.mod
 create mode 100644 go.sum
 create mode 100644 image/image_test.go
 create mode 100644 image/map.go
 create mode 100644 image/parse.go
 create mode 100644 image/testdata/bad-hash.img
 create mode 100644 image/testdata/bad-signature.img
 create mode 100644 image/testdata/bad-signature.json
 create mode 100644 image/testdata/garbage.img
 create mode 100644 image/testdata/good-signed.img
 create mode 100644 image/testdata/good-signed.json
 create mode 100644 image/testdata/good-unsigned.img
 create mode 100644 image/testdata/good-unsigned.json
 create mode 100644 image/testdata/mismatch-hash.img
 create mode 100644 image/testdata/mismatch-hash.json
 create mode 100644 image/testdata/mismatch-version.img
 create mode 100644 image/testdata/mismatch-version.json
 create mode 100644 image/testdata/sign-key-pub.der
 create mode 100644 image/testdata/sign-key.pem
 create mode 100644 image/testdata/truncated.img
 create mode 100644 image/verify.go
 create mode 100644 mfg/mfg_test.go
 create mode 100644 mfg/parse.go
 create mode 100644 mfg/testdata/garbage.bin
 create mode 100644 mfg/testdata/garbage.json
 create mode 100644 mfg/testdata/hash1-fm1-ext0-tgts1-sign0.bin
 create mode 100644 mfg/testdata/hash1-fm1-ext0-tgts1-sign0.json
 create mode 100644 mfg/testdata/hash1-fm1-ext1-tgts1-sign0.bin
 create mode 100644 mfg/testdata/hash1-fm1-ext1-tgts1-sign0.json
 create mode 100644 mfg/testdata/hash1-fm1-ext1-tgts1-sign1.bin
 create mode 100644 mfg/testdata/hash1-fm1-ext1-tgts1-sign1.json
 create mode 100644 mfg/testdata/hash1-fm1-ext1-tgtsm-sign0.bin
 create mode 100644 mfg/testdata/hash1-fm1-ext1-tgtsm-sign0.json
 create mode 100644 mfg/testdata/hash1-fm1-extm-tgts1-sign0.bin
 create mode 100644 mfg/testdata/hash1-fm1-extm-tgts1-sign0.json
 create mode 100644 mfg/testdata/hash1-fmm-ext1-tgts1-sign0.bin
 create mode 100644 mfg/testdata/hash1-fmm-ext1-tgts1-sign0.json
 create mode 100644 mfg/testdata/hashm-fm1-ext0-tgts1-sign0.bin
 create mode 100644 mfg/testdata/hashm-fm1-ext0-tgts1-sign0.json
 create mode 100644 mfg/testdata/hashx-fm1-ext0-tgts1-sign0.bin
 create mode 100644 mfg/testdata/hashx-fm1-ext0-tgts1-sign0.json
 create mode 100644 mfg/testdata/sign-key-pub.der
 create mode 100644 mfg/testdata/sign-key.pem
 create mode 100644 mfg/testdata/unknown-tlv.bin
 create mode 100644 mfg/testdata/unknown-tlv.json
 create mode 100644 mfg/verify.go
 delete mode 100644 misc/misc.go
 create mode 100644 sec/sig.go


[mynewt-artifact] 15/23: Add "verify" functions to API

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

commit c15c49efe5926ba4ad41fd902061ff23a1b76a06
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Thu Jun 20 14:07:44 2019 -0700

    Add "verify" functions to API
    
    After reading an image or mfgimage, the `Verify()` and
    `VerifyManifest()` functions can be used to check the object's
    integrity.
---
 image/create.go          |  35 ++++----
 image/image.go           | 121 +++++++++++++++++++---------
 image/keys_test.go       |  61 +-------------
 image/v1.go              |  57 ++++++-------
 image/verify.go          |  87 ++++++++++++++++++++
 manifest/manifest.go     |  25 ++++--
 manifest/mfg_manifest.go |  95 +++++++++++++++++++---
 mfg/map_meta.go          |  15 ++--
 mfg/meta.go              |  87 ++++++++++++++++----
 mfg/mfg.go               | 128 +++++++++++++++++++++++++-----
 mfg/verify.go            | 202 +++++++++++++++++++++++++++++++++++++++++++++++
 sec/encrypt.go           |  10 +--
 sec/key.go               | 112 +++++++++++++++-----------
 sec/sig.go               |  55 +++++++++++++
 14 files changed, 822 insertions(+), 268 deletions(-)

diff --git a/image/create.go b/image/create.go
index cbbc410..f51d77b 100644
--- a/image/create.go
+++ b/image/create.go
@@ -28,12 +28,11 @@ import (
 	"crypto/sha256"
 	"encoding/asn1"
 	"encoding/binary"
-	"encoding/hex"
 	"io/ioutil"
 	"math/big"
 
-	"mynewt.apache.org/newt/artifact/sec"
-	"mynewt.apache.org/newt/util"
+	"github.com/apache/mynewt-artifact/errors"
+	"github.com/apache/mynewt-artifact/sec"
 )
 
 type ImageCreator struct {
@@ -100,7 +99,7 @@ func GenerateEncTlv(cipherSecret []byte) (ImageTlv, error) {
 	} else if len(cipherSecret) == 24 {
 		encType = IMAGE_TLV_ENC_KEK
 	} else {
-		return ImageTlv{}, util.FmtNewtError("Invalid enc TLV size ")
+		return ImageTlv{}, errors.Errorf("invalid enc TLV size: %d", len(cipherSecret))
 	}
 
 	return ImageTlv{
@@ -120,7 +119,7 @@ func GenerateSigRsa(key sec.SignKey, hash []byte) ([]byte, error) {
 	signature, err := rsa.SignPSS(
 		rand.Reader, key.Rsa, crypto.SHA256, hash, &opts)
 	if err != nil {
-		return nil, util.FmtNewtError("Failed to compute signature: %s", err)
+		return nil, errors.Wrapf(err, "failed to compute signature")
 	}
 
 	return signature, nil
@@ -129,7 +128,7 @@ func GenerateSigRsa(key sec.SignKey, hash []byte) ([]byte, error) {
 func GenerateSigEc(key sec.SignKey, hash []byte) ([]byte, error) {
 	r, s, err := ecdsa.Sign(rand.Reader, key.Ec, hash)
 	if err != nil {
-		return nil, util.FmtNewtError("Failed to compute signature: %s", err)
+		return nil, errors.Wrapf(err, "failed to compute signature")
 	}
 
 	ECDSA := ECDSASig{
@@ -139,12 +138,12 @@ func GenerateSigEc(key sec.SignKey, hash []byte) ([]byte, error) {
 
 	signature, err := asn1.Marshal(ECDSA)
 	if err != nil {
-		return nil, util.FmtNewtError("Failed to construct signature: %s", err)
+		return nil, errors.Wrapf(err, "failed to construct signature")
 	}
 
 	sigLen := key.SigLen()
 	if len(signature) > int(sigLen) {
-		return nil, util.FmtNewtError("Something is really wrong\n")
+		return nil, errors.Errorf("signature truncated")
 	}
 
 	pad := make([]byte, int(sigLen)-len(signature))
@@ -210,8 +209,7 @@ func BuildSigTlvs(keys []sec.SignKey, hash []byte) ([]ImageTlv, error) {
 func GeneratePlainSecret() ([]byte, error) {
 	plainSecret := make([]byte, 16)
 	if _, err := rand.Read(plainSecret); err != nil {
-		return nil, util.FmtNewtError(
-			"Random generation error: %s\n", err)
+		return nil, errors.Wrapf(err, "random generation error")
 	}
 
 	return plainSecret, nil
@@ -238,7 +236,7 @@ func GenerateCipherSecret(pubKeBytes []byte,
 		return sec.EncryptSecretAes(aesPubKe, plainSecret)
 	}
 
-	return nil, util.FmtNewtError("Invalid image-crypt key")
+	return nil, errors.Errorf("invalid image-crypt key")
 }
 
 func GenerateImage(opts ImageCreateOpts) (Image, error) {
@@ -246,8 +244,7 @@ func GenerateImage(opts ImageCreateOpts) (Image, error) {
 
 	srcBin, err := ioutil.ReadFile(opts.SrcBinFilename)
 	if err != nil {
-		return Image{}, util.FmtNewtError(
-			"Can't read app binary: %s", err.Error())
+		return Image{}, errors.Wrapf(err, "Can't read app binary")
 	}
 
 	ic.Body = srcBin
@@ -269,8 +266,7 @@ func GenerateImage(opts ImageCreateOpts) (Image, error) {
 
 		pubKeBytes, err := ioutil.ReadFile(opts.SrcEncKeyFilename)
 		if err != nil {
-			return Image{}, util.FmtNewtError(
-				"Error reading pubkey file: %s", err.Error())
+			return Image{}, errors.Wrapf(err, "error reading pubkey file")
 		}
 
 		cipherSecret, err := GenerateCipherSecret(pubKeBytes, plainSecret)
@@ -301,7 +297,7 @@ func calcHash(initialHash []byte, hdr ImageHdr, pad []byte,
 			return err
 		}
 		if err := binary.Write(hash, binary.LittleEndian, itf); err != nil {
-			return util.FmtNewtError("Failed to hash data: %s", err.Error())
+			return errors.Wrapf(err, "failed to hash data")
 		}
 
 		return nil
@@ -364,8 +360,8 @@ func (ic *ImageCreator) Create() (Image, error) {
 		// between the header and the start of the image when it is padded.
 		extra := ic.HeaderSize - IMAGE_HEADER_SIZE
 		if extra < 0 {
-			return img, util.FmtNewtError("Image header must be at "+
-				"least %d bytes", IMAGE_HEADER_SIZE)
+			return img, errors.Errorf(
+				"image header must be at least %d bytes", IMAGE_HEADER_SIZE)
 		}
 
 		img.Header.HdrSz = uint16(ic.HeaderSize)
@@ -388,9 +384,6 @@ func (ic *ImageCreator) Create() (Image, error) {
 		img.Body = append(img.Body, ic.Body...)
 	}
 
-	util.StatusMessage(util.VERBOSITY_VERBOSE,
-		"Computed Hash for image as %s\n", hex.EncodeToString(hashBytes))
-
 	// Hash TLV.
 	tlv := ImageTlv{
 		Header: ImageTlvHdr{
diff --git a/image/image.go b/image/image.go
index 9855223..5c489c5 100644
--- a/image/image.go
+++ b/image/image.go
@@ -31,7 +31,8 @@ import (
 	"strconv"
 	"strings"
 
-	"mynewt.apache.org/newt/util"
+	"github.com/apache/mynewt-artifact/errors"
+	"github.com/apache/mynewt-artifact/sec"
 )
 
 const (
@@ -128,6 +129,11 @@ type ImageOffsets struct {
 	TotalSize int
 }
 
+func ImageTlvTypeIsValid(tlvType uint8) bool {
+	_, ok := imageTlvTypeNameMap[tlvType]
+	return ok
+}
+
 func ImageTlvTypeName(tlvType uint8) string {
 	name, ok := imageTlvTypeNameMap[tlvType]
 	if !ok {
@@ -155,24 +161,24 @@ func ParseVersion(versStr string) (ImageVersion, error) {
 	components := strings.Split(versStr, ".")
 	major, err = strconv.ParseUint(components[0], 10, 8)
 	if err != nil {
-		return ver, util.FmtNewtError("Invalid version string %s", versStr)
+		return ver, errors.Errorf("invalid version string %s", versStr)
 	}
 	if len(components) > 1 {
 		minor, err = strconv.ParseUint(components[1], 10, 8)
 		if err != nil {
-			return ver, util.FmtNewtError("Invalid version string %s", versStr)
+			return ver, errors.Errorf("invalid version string %s", versStr)
 		}
 	}
 	if len(components) > 2 {
 		rev, err = strconv.ParseUint(components[2], 10, 16)
 		if err != nil {
-			return ver, util.FmtNewtError("Invalid version string %s", versStr)
+			return ver, errors.Errorf("invalid version string %s", versStr)
 		}
 	}
 	if len(components) > 3 {
 		buildNum, err = strconv.ParseUint(components[3], 10, 32)
 		if err != nil {
-			return ver, util.FmtNewtError("Invalid version string %s", versStr)
+			return ver, errors.Errorf("invalid version string %s", versStr)
 		}
 	}
 
@@ -252,7 +258,7 @@ func (img *Image) Json() (string, error) {
 
 	b, err := json.MarshalIndent(m, "", "    ")
 	if err != nil {
-		return "", util.ChildNewtError(err)
+		return "", errors.Wrapf(err, "failed to marshal image")
 	}
 
 	return string(b), nil
@@ -263,13 +269,13 @@ func (tlv *ImageTlv) Write(w io.Writer) (int, error) {
 
 	err := binary.Write(w, binary.LittleEndian, &tlv.Header)
 	if err != nil {
-		return totalSize, util.ChildNewtError(err)
+		return totalSize, errors.Wrapf(err, "failed to write image TLV header")
 	}
 	totalSize += IMAGE_TLV_SIZE
 
 	size, err := w.Write(tlv.Data)
 	if err != nil {
-		return totalSize, util.ChildNewtError(err)
+		return totalSize, errors.Wrapf(err, "failed to write image TLV data")
 	}
 	totalSize += size
 
@@ -294,7 +300,7 @@ func (i *Image) FindUniqueTlv(tlvType uint8) (*ImageTlv, error) {
 		return nil, nil
 	}
 	if len(tlvs) > 1 {
-		return nil, util.FmtNewtError("Image contains %d TLVs with type %d",
+		return nil, errors.Errorf("image contains %d TLVs with type %d",
 			len(tlvs), tlvType)
 	}
 
@@ -338,11 +344,12 @@ func (img *Image) Trailer() ImageTrailer {
 func (i *Image) Hash() ([]byte, error) {
 	tlv, err := i.FindUniqueTlv(IMAGE_TLV_SHA256)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrapf(err, "failed to retrieve image hash")
 	}
 
 	if tlv == nil {
-		return nil, util.FmtNewtError("Image does not contain hash TLV")
+		return nil, errors.Errorf(
+			"failed to retrieve image hash: image does not contain hash TLV")
 	}
 
 	return tlv.Data, nil
@@ -360,20 +367,20 @@ func (i *Image) WritePlusOffsets(w io.Writer) (ImageOffsets, error) {
 
 	err := binary.Write(w, binary.LittleEndian, &i.Header)
 	if err != nil {
-		return offs, util.ChildNewtError(err)
+		return offs, errors.Wrapf(err, "failed to write image header")
 	}
 	offset += IMAGE_HEADER_SIZE
 
 	err = binary.Write(w, binary.LittleEndian, i.Pad)
 	if err != nil {
-		return offs, util.ChildNewtError(err)
+		return offs, errors.Wrapf(err, "failed to write image padding")
 	}
 	offset += len(i.Pad)
 
 	offs.Body = offset
 	size, err := w.Write(i.Body)
 	if err != nil {
-		return offs, util.ChildNewtError(err)
+		return offs, errors.Wrapf(err, "failed to write image body")
 	}
 	offset += size
 
@@ -381,7 +388,7 @@ func (i *Image) WritePlusOffsets(w io.Writer) (ImageOffsets, error) {
 	offs.Trailer = offset
 	err = binary.Write(w, binary.LittleEndian, &trailer)
 	if err != nil {
-		return offs, util.ChildNewtError(err)
+		return offs, errors.Wrapf(err, "failed to write image trailer")
 	}
 	offset += IMAGE_TRAILER_SIZE
 
@@ -389,7 +396,7 @@ func (i *Image) WritePlusOffsets(w io.Writer) (ImageOffsets, error) {
 		offs.Tlvs = append(offs.Tlvs, offset)
 		size, err := tlv.Write(w)
 		if err != nil {
-			return offs, util.ChildNewtError(err)
+			return offs, errors.Wrapf(err, "failed to write image TLV")
 		}
 		offset += size
 	}
@@ -423,16 +430,47 @@ func (i *Image) Write(w io.Writer) (int, error) {
 func (i *Image) WriteToFile(filename string) error {
 	f, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
 	if err != nil {
-		return util.ChildNewtError(err)
+		return errors.Wrapf(err, "failed to open image destination file")
 	}
 
 	if _, err := i.Write(f); err != nil {
-		return util.ChildNewtError(err)
+		return errors.Wrapf(err, "failed to write image")
 	}
 
 	return nil
 }
 
+func (img *Image) CollectSigs() ([]sec.Sig, error) {
+	var sigs []sec.Sig
+
+	var keyHashTlv *ImageTlv
+	for i, _ := range img.Tlvs {
+		t := &img.Tlvs[i]
+
+		if t.Header.Type == IMAGE_TLV_KEYHASH {
+			if keyHashTlv != nil {
+				return nil, errors.Errorf(
+					"image contains keyhash tlv without subsequent signature")
+			}
+			keyHashTlv = t
+		} else if ImageTlvTypeIsSig(t.Header.Type) {
+			if keyHashTlv == nil {
+				return nil, errors.Errorf(
+					"image contains signature tlv without preceding keyhash")
+			}
+
+			sigs = append(sigs, sec.Sig{
+				KeyHash: keyHashTlv.Data,
+				Data:    t.Data,
+			})
+
+			keyHashTlv = nil
+		}
+	}
+
+	return sigs, nil
+}
+
 func parseRawHeader(imgData []byte, offset int) (ImageHdr, int, error) {
 	var hdr ImageHdr
 
@@ -440,20 +478,19 @@ func parseRawHeader(imgData []byte, offset int) (ImageHdr, int, error) {
 	r.Seek(int64(offset), io.SeekStart)
 
 	if err := binary.Read(r, binary.LittleEndian, &hdr); err != nil {
-		return hdr, 0, util.FmtNewtError(
-			"Error reading image header: %s", err.Error())
+		return hdr, 0, errors.Wrapf(err, "error reading image header")
 	}
 
 	if hdr.Magic != IMAGE_MAGIC {
-		return hdr, 0, util.FmtNewtError(
-			"Image magic incorrect; expected 0x%08x, got 0x%08x",
+		return hdr, 0, errors.Errorf(
+			"image magic incorrect; expected 0x%08x, got 0x%08x",
 			uint32(IMAGE_MAGIC), hdr.Magic)
 	}
 
 	remLen := len(imgData) - offset
 	if remLen < int(hdr.HdrSz) {
-		return hdr, 0, util.FmtNewtError(
-			"Image header incomplete; expected %d bytes, got %d bytes",
+		return hdr, 0, errors.Errorf(
+			"image header incomplete; expected %d bytes, got %d bytes",
 			hdr.HdrSz, remLen)
 	}
 
@@ -467,8 +504,8 @@ func parseRawBody(imgData []byte, hdr ImageHdr,
 	remLen := len(imgData) - offset
 
 	if remLen < imgSz {
-		return nil, 0, util.FmtNewtError(
-			"Image body incomplete; expected %d bytes, got %d bytes",
+		return nil, 0, errors.Errorf(
+			"image body incomplete; expected %d bytes, got %d bytes",
 			imgSz, remLen)
 	}
 
@@ -482,9 +519,8 @@ func parseRawTrailer(imgData []byte, offset int) (ImageTrailer, int, error) {
 	r.Seek(int64(offset), io.SeekStart)
 
 	if err := binary.Read(r, binary.LittleEndian, &trailer); err != nil {
-		return trailer, 0, util.FmtNewtError(
-			"Image contains invalid trailer at offset %d: %s",
-			offset, err.Error())
+		return trailer, 0, errors.Wrapf(err,
+			"image contains invalid trailer at offset %d", offset)
 	}
 
 	return trailer, IMAGE_TRAILER_SIZE, nil
@@ -497,14 +533,14 @@ func parseRawTlv(imgData []byte, offset int) (ImageTlv, int, error) {
 	r.Seek(int64(offset), io.SeekStart)
 
 	if err := binary.Read(r, binary.LittleEndian, &tlv.Header); err != nil {
-		return tlv, 0, util.FmtNewtError(
-			"Image contains invalid TLV at offset %d: %s", offset, err.Error())
+		return tlv, 0, errors.Wrapf(err,
+			"image contains invalid TLV at offset %d", offset)
 	}
 
 	tlv.Data = make([]byte, tlv.Header.Len)
 	if _, err := r.Read(tlv.Data); err != nil {
-		return tlv, 0, util.FmtNewtError(
-			"Image contains invalid TLV at offset %d: %s", offset, err.Error())
+		return tlv, 0, errors.Wrapf(err,
+			"image contains invalid TLV at offset %d", offset)
 	}
 
 	return tlv, IMAGE_TLV_SIZE + int(tlv.Header.Len), nil
@@ -532,6 +568,15 @@ func ParseImage(imgData []byte) (Image, error) {
 	}
 	offset += size
 
+	totalLen := IMAGE_HEADER_SIZE + len(body) + int(trailer.TlvTotLen)
+	if len(imgData) < totalLen {
+		return img, errors.Errorf("image data truncated: have=%d want=%d",
+			len(imgData), totalLen)
+	}
+
+	// Trim excess data following image trailer.
+	imgData = imgData[:totalLen]
+
 	var tlvs []ImageTlv
 	tlvLen := IMAGE_TRAILER_SIZE
 	for offset < len(imgData) {
@@ -541,13 +586,17 @@ func ParseImage(imgData []byte) (Image, error) {
 		}
 
 		tlvs = append(tlvs, tlv)
+
 		offset += size
+		if offset > len(imgData) {
+			return img, errors.Errorf("TLVs extend beyond end of image")
+		}
 
-		tlvLen += IMAGE_TLV_SIZE + int(tlv.Header.Len)
+		tlvLen += size
 	}
 
 	if int(trailer.TlvTotLen) != tlvLen {
-		return img, util.FmtNewtError(
+		return img, errors.Errorf(
 			"invalid image: trailer indicates TLV-length=%d; actual=%d",
 			trailer.TlvTotLen, tlvLen)
 	}
@@ -564,7 +613,7 @@ func ReadImage(filename string) (Image, error) {
 
 	imgData, err := ioutil.ReadFile(filename)
 	if err != nil {
-		return ri, util.ChildNewtError(err)
+		return ri, errors.Wrapf(err, "failed to read image from file")
 	}
 
 	return ParseImage(imgData)
diff --git a/image/keys_test.go b/image/keys_test.go
index 769848a..0119098 100644
--- a/image/keys_test.go
+++ b/image/keys_test.go
@@ -24,8 +24,8 @@ import (
 	"os"
 	"testing"
 
-	"mynewt.apache.org/newt/artifact/image"
-	"mynewt.apache.org/newt/artifact/sec"
+	"mynewt.apache.org/artifact/image"
+	"mynewt.apache.org/artifact/sec"
 )
 
 func TestRSA(t *testing.T) {
@@ -44,18 +44,6 @@ func TestPlainEcdsaPkcs8(t *testing.T) {
 	signatureTest(t, ecdsaPkcs8Private)
 }
 
-func TestEncryptedRSA(t *testing.T) {
-	sec.KeyPassword = []byte("sample")
-	signatureTest(t, rsaEncryptedPrivate)
-	sec.KeyPassword = []byte{}
-}
-
-func TestEncryptedEcdsa(t *testing.T) {
-	sec.KeyPassword = []byte("sample")
-	signatureTest(t, ecdsaEncryptedPrivate)
-	sec.KeyPassword = []byte{}
-}
-
 func signatureTest(t *testing.T, privateKey []byte) {
 	tmpdir, err := ioutil.TempDir("", "newttest")
 	if err != nil {
@@ -170,48 +158,3 @@ j0AQZlBs7f4r67668eDCUB8aDR2hRANCAATyZPzsx+xn9JtlxdspevTrYisiMTjl
 YuBJCrV1FZj2HkplEgO+ZIMuD7eRvyTEBS2bw6F1aCeKOMUmYVImAbpc
 -----END PRIVATE KEY-----
 `)
-
-// A password-protected RSA private key in PKCS#5/8 format.  The
-// password for this key is "sample".
-var rsaEncryptedPrivate = []byte(`-----BEGIN ENCRYPTED PRIVATE KEY-----
-MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIRMifqJThk8kCAggA
-MB0GCWCGSAFlAwQBKgQQTMUBoFpzjJ5UNRnCIeqf4QSCBNDkQvXnUNmss8erKiDo
-Uqs2tf9ZD8MjDThLBmF/gV1dg1q6aDY+3fI2E4yLXJb2PmKcUq82YZ0FDeoCvJRJ
-BCurzM9slur5akpNBTFoFwtFsdHz7nKNS4MHUul22rGBnVFUUNTySmpjl/m+dxWO
-fa6tWpGTAr7tsCy9gF5PxpSw7NR/NpIL0PmpydHWhTs1tl2csqBqK6Tp014Kefi/
-pmmeb2eRl5cmprxW32rW2QBMtv4z91SsbnlVdz4r8txTG+3S4td9v9jD5kqcIiC2
-KQHrbH9y7okUk/ISsp9ANKPJt10fbYDxORiMK57XssXy1enGjpkIIrUGz1TMydkD
-USfwqkmPuIrrzOXnbxU4ef2wC/pA/h9Smby3WWYo8725/1kZyIediNDcgi/Qgrs4
-1VQAYzsD6duwyUNSo+tgmYVFGvZhsottus3fMWe/Ay1biJ6z6Vk8gqKWI1VV/REJ
-zK/I9hgKGxj2N2Ff6E/YkcwQenHWj/iDWLjvokyOBnPFNqzzM2Qqo1XFpzj4EO5D
-0WD4EzZYvUhk3lZZNydvXiuy8RrCVLLJMS08XgOqQaiFqqxj2hjRwv3nBesk7iA8
-5Tv8GMa5QkNrISCnp4/uGBh+v/CjwVRqPTcK3/mctPN2nLhI6H4pF4Y6apXkz1TN
-NMQqxaxmVVg8fyLaS4/xfUr8LAmiEtOwvs0XOhcqCTvvlsO4N+yec4VD4gmsTDY9
-/2b/+YwSlGMpA+GQQbg0FraaF8NyJRG1mSER6WiUGGM1cuKK44nzBbykQbZwzDSA
-kkhjDaadkhv/NPKAUR3sNy2GXVaNL/ItCpQUHRKKcIPp0HhdXsl0YebuwRlHjw/6
-UOdzNYe23e40X/Xl3vmOKRbzhLP/qO2DV21o0wI4ujF8Xu5h1h8s49HPp58G1ldy
-/hJ6durYKX8T5khiR2iXYewoy0YObuccV//Ov1/ySOp/x0/QuCl/swvs8Jf7awnu
-rpRrHPArpCvMmXmt5Y+TFYXFjkJGwsxTew5TcwBebBlIET2XNbo2pbz4WqJ3eVlK
-CNZVDEZ8mMrGT00FBi759Vfw9rhrnqXnLlNtJZ5VCXFUw8Tos302sLaQWXzHYyf8
-4awM8G9PSu5Q9lFcN9od4H95YrAAv/l8F+pcGgEKD8ZuzsgFIalqgx5wzmUMDcPM
-NKV5u9mtHjI92ru6NB8rGesM6sy6kBGvpotsDWawpV2SoCrkbyEkk+kXaGS+fsG7
-D2H37GfktN8R5Ktc0Uf/JJiNfDzq8lk1J4r7LBQlWUbhKbfGMYxt+7Xo0GsqAsLp
-PKSUwx+hTZb3BmW6s4Q6vivI1MdQbWVT1zh41StvfRSNlo70iOFxOM0lU1jjY989
-UKo+gcolddvZbMNwip0ILPO3dsa+he1jJ/gbo9qBHLy7plfsBLLakZP1Nu6xdlqQ
-TSSobaE8uxUMZk+wMWClA9AOZ1TcUr2yRV5GVj/bxG9ab+H37vF9F8vFE+jjJ7yN
-6pjdohm4gXeSVx7ON4SeZLsVwNYkCVYS89E81qLx1jP9F57+6IUGDZN5EMC0aJLT
-ny75MCCLT00KD7BFsb0KDLXxp++eu/L2hinorT3p6dXp/9mUoxmy6wJqEyqCFniZ
-N2GZN7+LDTIbHUxCijVWamU2DQ==
------END ENCRYPTED PRIVATE KEY-----
-`)
-
-// A password-protected ECDSA private key in PKCS#5/8 format.  The
-// password for this key is "sample"
-var ecdsaEncryptedPrivate = []byte(`-----BEGIN ENCRYPTED PRIVATE KEY-----
-MIHeMEkGCSqGSIb3DQEFDTA8MBsGCSqGSIb3DQEFDDAOBAjlKrDSKNg9QQICCAAw
-HQYJYIZIAWUDBAEqBBDliPNzQTNpdlppTcYpmuhWBIGQVhfWaVSzUvi/qIZLiZVn
-Nulfw5jDOlbn3UBX9kp/Z9Pro582Q0kjzLfm5UahvDINEJWxL4pc/28UnGQTBr0Q
-nSEg+RbqpuD099C38H0Gq/YkIM+RDG4aiQrkmzHXyVsHshIbG+z2LsLTIwmU69/Z
-v0nX6/hGErVR8YWcrOne086rCvfJVrxyO5+EUqrkLhEr
------END ENCRYPTED PRIVATE KEY-----
-`)
diff --git a/image/v1.go b/image/v1.go
index 67a451f..0b82086 100644
--- a/image/v1.go
+++ b/image/v1.go
@@ -30,12 +30,11 @@ import (
 	"crypto/rsa"
 	"crypto/sha256"
 	"encoding/binary"
-	"encoding/hex"
 	"io"
 	"io/ioutil"
 
-	"mynewt.apache.org/newt/artifact/sec"
-	"mynewt.apache.org/newt/util"
+	"github.com/apache/mynewt-artifact/errors"
+	"github.com/apache/mynewt-artifact/sec"
 )
 
 const IMAGEv1_MAGIC = 0x96f3b83c /* Image header magic */
@@ -95,10 +94,10 @@ func (img *ImageV1) FindTlvs(tlvType uint8) []ImageTlv {
 func (img *ImageV1) Hash() ([]byte, error) {
 	tlvs := img.FindTlvs(IMAGEv1_TLV_SHA256)
 	if len(tlvs) == 0 {
-		return nil, util.FmtNewtError("Image does not contain hash TLV")
+		return nil, errors.Errorf("image does not contain hash TLV")
 	}
 	if len(tlvs) > 1 {
-		return nil, util.FmtNewtError("Image contains %d hash TLVs", len(tlvs))
+		return nil, errors.Errorf("image contains %d hash TLVs", len(tlvs))
 	}
 
 	return tlvs[0].Data, nil
@@ -112,14 +111,14 @@ func (img *ImageV1) WritePlusOffsets(w io.Writer) (ImageOffsets, error) {
 
 	err := binary.Write(w, binary.LittleEndian, &img.Header)
 	if err != nil {
-		return offs, util.ChildNewtError(err)
+		return offs, errors.Wrapf(err, "failed to write image header")
 	}
 	offset += IMAGE_HEADER_SIZE
 
 	offs.Body = offset
 	size, err := w.Write(img.Body)
 	if err != nil {
-		return offs, util.ChildNewtError(err)
+		return offs, errors.Wrapf(err, "failed to write image body")
 	}
 	offset += size
 
@@ -127,7 +126,7 @@ func (img *ImageV1) WritePlusOffsets(w io.Writer) (ImageOffsets, error) {
 		offs.Tlvs = append(offs.Tlvs, offset)
 		size, err := tlv.Write(w)
 		if err != nil {
-			return offs, util.ChildNewtError(err)
+			return offs, errors.Wrapf(err, "failed to write image TLV")
 		}
 		offset += size
 	}
@@ -174,7 +173,7 @@ func sigHdrTypeV1(key sec.SignKey) (uint32, error) {
 		case "P-256":
 			return IMAGEv1_F_ECDSA256_SHA256, nil
 		default:
-			return 0, util.FmtNewtError("Unsupported ECC curve")
+			return 0, errors.Errorf("unsupported ECC curve")
 		}
 	}
 }
@@ -211,7 +210,7 @@ func generateV1SigRsa(key *rsa.PrivateKey, hash []byte) ([]byte, error) {
 			rand.Reader, key, crypto.SHA256, hash)
 	}
 	if err != nil {
-		return nil, util.FmtNewtError("Failed to compute signature: %s", err)
+		return nil, errors.Wrapf(err, "failed to compute signature")
 	}
 
 	return signature, nil
@@ -241,20 +240,19 @@ func generateV1SigTlvEc(key sec.SignKey, hash []byte) (ImageTlv, error) {
 
 	sigLen := key.SigLen()
 	if len(sig) > int(sigLen) {
-		return ImageTlv{}, util.FmtNewtError("Something is really wrong\n")
+		return ImageTlv{}, errors.Errorf("signature truncated")
 	}
 
 	b := &bytes.Buffer{}
 
 	if _, err := b.Write(sig); err != nil {
-		return ImageTlv{},
-			util.FmtNewtError("Failed to append sig: %s", err.Error())
+		return ImageTlv{}, errors.Wrapf(err, "failed to append sig")
 	}
 
 	pad := make([]byte, int(sigLen)-len(sig))
 	if _, err := b.Write(pad); err != nil {
-		return ImageTlv{}, util.FmtNewtError(
-			"Failed to serialize image trailer: %s", err.Error())
+		return ImageTlv{}, errors.Wrapf(err,
+			"failed to serialize image trailer")
 	}
 
 	return ImageTlv{
@@ -284,7 +282,7 @@ func calcHashV1(initialHash []byte, hdr ImageHdrV1,
 
 	add := func(itf interface{}) error {
 		if err := binary.Write(hash, binary.LittleEndian, itf); err != nil {
-			return util.FmtNewtError("Failed to hash data: %s", err.Error())
+			return errors.Wrapf(err, "failed to hash data")
 		}
 
 		return nil
@@ -319,8 +317,8 @@ func (ic *ImageCreator) CreateV1() (ImageV1, error) {
 	ri := ImageV1{}
 
 	if len(ic.SigKeys) > 1 {
-		return ri, util.FmtNewtError(
-			"V1 image format only allows one key, %d keys specified",
+		return ri, errors.Errorf(
+			"v1 image format only allows one key, %d keys specified",
 			len(ic.SigKeys))
 	}
 
@@ -349,8 +347,8 @@ func (ic *ImageCreator) CreateV1() (ImageV1, error) {
 		 * the image when it is padded.
 		 */
 		if ic.HeaderSize < IMAGE_HEADER_SIZE {
-			return ri, util.FmtNewtError("Image header must be at "+
-				"least %d bytes", IMAGE_HEADER_SIZE)
+			return ri, errors.Errorf(
+				"image header must be at least %d bytes", IMAGE_HEADER_SIZE)
 		}
 
 		hdr.HdrSz = uint16(ic.HeaderSize)
@@ -372,8 +370,8 @@ func (ic *ImageCreator) CreateV1() (ImageV1, error) {
 		// the image when it is padded.
 		extra := ic.HeaderSize - IMAGE_HEADER_SIZE
 		if extra < 0 {
-			return ri, util.FmtNewtError("Image header must be at "+
-				"least %d bytes", IMAGE_HEADER_SIZE)
+			return ri, errors.Errorf(
+				"image header must be at least %d bytes", IMAGE_HEADER_SIZE)
 		}
 
 		hdr.HdrSz = uint16(ic.HeaderSize)
@@ -387,9 +385,6 @@ func (ic *ImageCreator) CreateV1() (ImageV1, error) {
 		return ri, err
 	}
 
-	util.StatusMessage(util.VERBOSITY_VERBOSE,
-		"Computed Hash for image as %s\n", hex.EncodeToString(hashBytes))
-
 	/*
 	 * Followed by data.
 	 */
@@ -399,16 +394,14 @@ func (ic *ImageCreator) CreateV1() (ImageV1, error) {
 	for {
 		cnt, err := r.Read(dataBuf)
 		if err != nil && err != io.EOF {
-			return ri, util.FmtNewtError(
-				"Failed to read from image body: %s", err.Error())
+			return ri, errors.Wrapf(err, "failed to read from image body")
 		}
 		if cnt == 0 {
 			break
 		}
 
 		if _, err = w.Write(dataBuf[0:cnt]); err != nil {
-			return ri, util.FmtNewtError(
-				"Failed to write to image body: %s", err.Error())
+			return ri, errors.Wrapf(err, "failed to write to image body")
 		}
 	}
 	ri.Body = w.Bytes()
@@ -448,8 +441,7 @@ func GenerateV1Image(opts ImageCreateOpts) (ImageV1, error) {
 
 	srcBin, err := ioutil.ReadFile(opts.SrcBinFilename)
 	if err != nil {
-		return ImageV1{}, util.FmtNewtError(
-			"Can't read app binary: %s", err.Error())
+		return ImageV1{}, errors.Wrapf(err, "can't read app binary")
 	}
 
 	ic.Body = srcBin
@@ -471,8 +463,7 @@ func GenerateV1Image(opts ImageCreateOpts) (ImageV1, error) {
 
 		pubKeBytes, err := ioutil.ReadFile(opts.SrcEncKeyFilename)
 		if err != nil {
-			return ImageV1{}, util.FmtNewtError(
-				"Error reading pubkey file: %s", err.Error())
+			return ImageV1{}, errors.Wrapf(err, "error reading pubkey file")
 		}
 		cipherSecret, err := GenerateCipherSecret(pubKeBytes, plainSecret)
 		if err != nil {
diff --git a/image/verify.go b/image/verify.go
new file mode 100644
index 0000000..91ddf28
--- /dev/null
+++ b/image/verify.go
@@ -0,0 +1,87 @@
+package image
+
+import (
+	"bytes"
+	"encoding/hex"
+
+	"github.com/apache/mynewt-artifact/errors"
+	"github.com/apache/mynewt-artifact/manifest"
+)
+
+// VerifyManifest compares an image's structure to its manifest.  It returns
+// an error if the image doesn't match the manifest.
+func (img *Image) VerifyManifest(man manifest.Manifest) error {
+	ver, err := ParseVersion(man.Version)
+	if err != nil {
+		return errors.Wrapf(err, "manifest contains invalid `version` field")
+	}
+
+	if ver.Major != img.Header.Vers.Major ||
+		ver.Minor != img.Header.Vers.Minor ||
+		ver.Rev != img.Header.Vers.Rev ||
+		ver.BuildNum != img.Header.Vers.BuildNum {
+
+		return errors.Errorf(
+			"manifest version different from image header: man=%s img=%s",
+			ver.String(), img.Header.Vers.String())
+	}
+
+	var imgHash string
+	if hash, err := img.Hash(); err == nil {
+		imgHash = hex.EncodeToString(hash)
+	}
+
+	// A manifest contains two image hashes: `id` and `image_hash`.  Check
+	// both.
+
+	checkHash := func(manHash string) error {
+		if imgHash != manHash {
+			return errors.Errorf(
+				"manifest image hash different from image TLV: man=%s img=%s",
+				manHash, imgHash)
+		}
+		return nil
+	}
+
+	if err := checkHash(man.BuildID); err != nil {
+		return err
+	}
+	if err := checkHash(man.ImageHash); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Verify checks an image's structure and internal consistency.  It returns
+// an error if the image is incorrect.
+func (img *Image) Verify() error {
+	// Verify the hash.
+
+	haveHash, err := img.Hash()
+	if err != nil {
+		return err
+	}
+
+	wantHash, err := img.CalcHash()
+	if err != nil {
+		return err
+	}
+
+	if !bytes.Equal(haveHash, wantHash) {
+		return errors.Errorf(
+			"image manifest contains incorrect hash: have=%x want=%x",
+			haveHash, wantHash)
+	}
+
+	// Verify that each TLV has a valid "type" field.
+	for _, t := range img.Tlvs {
+		if !ImageTlvTypeIsValid(t.Header.Type) {
+			return errors.Errorf(
+				"image contains TLV with invalid `type` field: %d",
+				t.Header.Type)
+		}
+	}
+
+	return nil
+}
diff --git a/manifest/manifest.go b/manifest/manifest.go
index 95c76c6..3a7298a 100644
--- a/manifest/manifest.go
+++ b/manifest/manifest.go
@@ -23,8 +23,9 @@ import (
 	"encoding/json"
 	"io"
 	"io/ioutil"
+	"strings"
 
-	"mynewt.apache.org/newt/util"
+	"github.com/apache/mynewt-artifact/errors"
 )
 
 /*
@@ -86,13 +87,12 @@ func ReadManifest(path string) (Manifest, error) {
 
 	content, err := ioutil.ReadFile(path)
 	if err != nil {
-		return m, util.ChildNewtError(err)
+		return m, errors.Wrapf(err, "failed to read manifest file")
 	}
 
 	if err := json.Unmarshal(content, &m); err != nil {
-		return m, util.FmtNewtError(
-			"Failure decoding manifest with path \"%s\": %s",
-			path, err.Error())
+		return m, errors.Wrapf(
+			err, "failure decoding manifest with path \"%s\"", path)
 	}
 
 	return m, nil
@@ -101,13 +101,24 @@ func ReadManifest(path string) (Manifest, error) {
 func (m *Manifest) Write(w io.Writer) (int, error) {
 	buffer, err := json.MarshalIndent(m, "", "  ")
 	if err != nil {
-		return 0, util.FmtNewtError("Cannot encode manifest: %s", err.Error())
+		return 0, errors.Wrapf(err, "Cannot encode manifest")
 	}
 
 	cnt, err := w.Write(buffer)
 	if err != nil {
-		return 0, util.FmtNewtError("Cannot write manifest: %s", err.Error())
+		return 0, errors.Wrapf(err, "Cannot write manifest")
 	}
 
 	return cnt, nil
 }
+
+func (m *Manifest) FindTargetVar(key string) string {
+	for _, tv := range m.TgtVars {
+		parts := strings.SplitN(tv, "=", 2)
+		if len(parts) == 2 && parts[0] == key {
+			return parts[1]
+		}
+	}
+
+	return ""
+}
diff --git a/manifest/mfg_manifest.go b/manifest/mfg_manifest.go
index f2c506c..3ebd22a 100644
--- a/manifest/mfg_manifest.go
+++ b/manifest/mfg_manifest.go
@@ -20,11 +20,13 @@
 package manifest
 
 import (
+	"encoding/hex"
 	"encoding/json"
 	"io/ioutil"
 
-	"mynewt.apache.org/newt/artifact/flash"
-	"mynewt.apache.org/newt/util"
+	"github.com/apache/mynewt-artifact/errors"
+	"github.com/apache/mynewt-artifact/flash"
+	"github.com/apache/mynewt-artifact/sec"
 )
 
 type MfgManifestTarget struct {
@@ -48,7 +50,6 @@ type MfgManifestMeta struct {
 	Hash      bool                 `json:"hash_present"`
 	FlashMap  bool                 `json:"flash_map_present"`
 	Mmrs      []MfgManifestMetaMmr `json:"mmrs,omitempty"`
-	// XXX: refhash
 }
 
 type MfgManifestSig struct {
@@ -66,6 +67,7 @@ type MfgManifest struct {
 	BinPath    string            `json:"bin_path"`
 	HexPath    string            `json:"hex_path"`
 	Bsp        string            `json:"bsp"`
+	EraseVal   byte              `json:"erase_val"`
 	Signatures []MfgManifestSig  `json:"signatures,omitempty"`
 	FlashAreas []flash.FlashArea `json:"flash_map"`
 
@@ -73,18 +75,29 @@ type MfgManifest struct {
 	Meta    *MfgManifestMeta    `json:"meta,omitempty"`
 }
 
-func ReadMfgManifest(path string) (MfgManifest, error) {
-	m := MfgManifest{}
+func ParseMfgManifest(jsonText []byte) (MfgManifest, error) {
+	m := MfgManifest{
+		// Backwards compatibility: assume 0xff if unspecified.
+		EraseVal: 0xff,
+	}
+
+	if err := json.Unmarshal(jsonText, &m); err != nil {
+		return m, errors.Wrapf(err, "failure decoding mfg manifest")
+	}
 
+	return m, nil
+}
+
+func ReadMfgManifest(path string) (MfgManifest, error) {
 	content, err := ioutil.ReadFile(path)
 	if err != nil {
-		return m, util.ChildNewtError(err)
+		return MfgManifest{}, errors.Wrapf(err,
+			"failed to read mfg manifest file")
 	}
 
-	if err := json.Unmarshal(content, &m); err != nil {
-		return m, util.FmtNewtError(
-			"Failure decoding mfg manifest with path \"%s\": %s",
-			path, err.Error())
+	m, err := ParseMfgManifest(content)
+	if err != nil {
+		return m, errors.Wrapf(err, "path=%s", path)
 	}
 
 	return m, nil
@@ -93,9 +106,67 @@ func ReadMfgManifest(path string) (MfgManifest, error) {
 func (m *MfgManifest) MarshalJson() ([]byte, error) {
 	buffer, err := json.MarshalIndent(m, "", "  ")
 	if err != nil {
-		return nil, util.FmtNewtError(
-			"Cannot encode mfg manifest: %s", err.Error())
+		return nil, errors.Wrapf(err, "cannot encode mfg manifest")
 	}
 
 	return buffer, nil
 }
+
+func (m *MfgManifest) FindFlashArea(device int, offset int) *flash.FlashArea {
+	for i, _ := range m.FlashAreas {
+		fa := &m.FlashAreas[i]
+		if fa.Device == device && fa.Offset == offset {
+			return fa
+		}
+	}
+
+	return nil
+}
+
+func (m *MfgManifest) FindFlashAreaName(name string) *flash.FlashArea {
+	for i, _ := range m.FlashAreas {
+		fa := &m.FlashAreas[i]
+		if fa.Name == name {
+			return fa
+		}
+	}
+
+	return nil
+}
+
+func (ms *MfgManifestSig) SecSig() (sec.Sig, error) {
+	keyHash, err := hex.DecodeString(ms.Key)
+	if err != nil {
+		return sec.Sig{}, errors.Errorf(
+			"invalid hex-encoded key hash: %s", ms.Key)
+	}
+
+	data, err := hex.DecodeString(ms.Sig)
+	if err != nil {
+		return sec.Sig{}, errors.Errorf(
+			"invalid hex-encoded signature: %s", ms.Sig)
+	}
+
+	return sec.Sig{
+		KeyHash: keyHash,
+		Data:    data,
+	}, nil
+}
+
+func (m *MfgManifest) SecSigs() ([]sec.Sig, error) {
+	var sigs []sec.Sig
+	for _, ms := range m.Signatures {
+		s, err := ms.SecSig()
+		if err != nil {
+			return nil, err
+		}
+
+		sigs = append(sigs, s)
+	}
+
+	return sigs, nil
+}
+
+func (mt *MfgManifestTarget) IsBoot() bool {
+	return mt.BinPath != ""
+}
diff --git a/mfg/map_meta.go b/mfg/map_meta.go
index b16c37e..19b1ee8 100644
--- a/mfg/map_meta.go
+++ b/mfg/map_meta.go
@@ -25,7 +25,7 @@ import (
 	"encoding/hex"
 	"encoding/json"
 
-	"mynewt.apache.org/newt/util"
+	"github.com/apache/mynewt-artifact/errors"
 )
 
 func (t *MetaTlv) bodyMap() (map[string]interface{}, error) {
@@ -33,8 +33,7 @@ func (t *MetaTlv) bodyMap() (map[string]interface{}, error) {
 
 	readBody := func(dst interface{}) error {
 		if err := binary.Read(r, binary.LittleEndian, dst); err != nil {
-			return util.FmtNewtError(
-				"Error parsing TLV data: %s", err.Error())
+			return errors.Wrapf(err, "error parsing TLV data")
 		}
 		return nil
 	}
@@ -62,8 +61,7 @@ func (t *MetaTlv) bodyMap() (map[string]interface{}, error) {
 		return body.Map(), nil
 
 	default:
-		return nil, util.FmtNewtError(
-			"Unknown meta TLV type: %d", t.Header.Type)
+		return nil, errors.Errorf("unknown meta TLV type: %d", t.Header.Type)
 	}
 }
 
@@ -88,7 +86,7 @@ func (b *MetaTlvBodyMmrRef) Map() map[string]interface{} {
 	}
 }
 
-func (t *MetaTlv) Map(offset int) map[string]interface{} {
+func (t *MetaTlv) Map(index int, offset int) map[string]interface{} {
 	hmap := map[string]interface{}{
 		"_type_name": MetaTlvTypeName(t.Header.Type),
 		"type":       t.Header.Type,
@@ -105,6 +103,7 @@ func (t *MetaTlv) Map(offset int) map[string]interface{} {
 	}
 
 	return map[string]interface{}{
+		"_index":  index,
 		"_offset": offset,
 		"header":  hmap,
 		"data":    body,
@@ -126,7 +125,7 @@ func (m *Meta) Map(endOffset int) map[string]interface{} {
 
 	tlvs := []map[string]interface{}{}
 	for i, t := range m.Tlvs {
-		tlv := t.Map(startOffset + offsets.Tlvs[i])
+		tlv := t.Map(i, startOffset+offsets.Tlvs[i])
 		tlvs = append(tlvs, tlv)
 	}
 
@@ -146,7 +145,7 @@ func (m *Meta) Json(offset int) (string, error) {
 
 	bin, err := json.MarshalIndent(mmap, "", "    ")
 	if err != nil {
-		return "", util.ChildNewtError(err)
+		return "", errors.Wrapf(err, "failed to marshal MMR")
 	}
 
 	return string(bin), nil
diff --git a/mfg/meta.go b/mfg/meta.go
index 4521a89..b4786bc 100644
--- a/mfg/meta.go
+++ b/mfg/meta.go
@@ -25,7 +25,7 @@ import (
 	"io"
 	"io/ioutil"
 
-	"mynewt.apache.org/newt/util"
+	"github.com/apache/mynewt-artifact/errors"
 )
 
 // The "manufacturing meta region" is located at the end of the boot loader
@@ -139,7 +139,7 @@ func MetaTlvTypeName(typ uint8) string {
 func writeElem(elem interface{}, w io.Writer) error {
 	/* XXX: Assume target platform uses little endian. */
 	if err := binary.Write(w, binary.LittleEndian, elem); err != nil {
-		return util.ChildNewtError(err)
+		return errors.Wrapf(err, "failed to write MMR element")
 	}
 	return nil
 }
@@ -160,6 +160,43 @@ func (tlv *MetaTlv) Write(w io.Writer) (int, error) {
 	return sz, nil
 }
 
+func (tlv *MetaTlv) StructuredBody() (interface{}, error) {
+	r := bytes.NewReader(tlv.Data)
+
+	readBody := func(dst interface{}) error {
+		if err := binary.Read(r, binary.LittleEndian, dst); err != nil {
+			return errors.Wrapf(err, "error parsing TLV data")
+		}
+		return nil
+	}
+
+	switch tlv.Header.Type {
+	case META_TLV_TYPE_HASH:
+		var body MetaTlvBodyHash
+		if err := readBody(&body); err != nil {
+			return nil, err
+		}
+		return &body, nil
+
+	case META_TLV_TYPE_FLASH_AREA:
+		var body MetaTlvBodyFlashArea
+		if err := readBody(&body); err != nil {
+			return nil, err
+		}
+		return &body, nil
+
+	case META_TLV_TYPE_MMR_REF:
+		var body MetaTlvBodyMmrRef
+		if err := readBody(&body); err != nil {
+			return nil, err
+		}
+		return &body, nil
+
+	default:
+		return nil, errors.Errorf("unknown meta TLV type: %d", tlv.Header.Type)
+	}
+}
+
 func (meta *Meta) WritePlusOffsets(w io.Writer) (MetaOffsets, error) {
 	mo := MetaOffsets{}
 	sz := 0
@@ -270,25 +307,41 @@ func (meta *Meta) Hash() []byte {
 	return tlv.Data
 }
 
+func (meta *Meta) Clone() Meta {
+	var tlvs []MetaTlv
+	for _, src := range meta.Tlvs {
+		dst := MetaTlv{
+			Header: src.Header,
+			Data:   make([]byte, len(src.Data)),
+		}
+		copy(dst.Data, src.Data)
+		tlvs = append(tlvs, dst)
+	}
+
+	return Meta{
+		Tlvs:   tlvs,
+		Footer: meta.Footer,
+	}
+}
+
 func parseMetaTlv(bin []byte) (MetaTlv, int, error) {
 	r := bytes.NewReader(bin)
 
 	tlv := MetaTlv{}
 	if err := binary.Read(r, binary.LittleEndian, &tlv.Header); err != nil {
-		return tlv, 0, util.FmtNewtError(
-			"Error reading TLV header: %s", err.Error())
+		return tlv, 0, errors.Wrapf(err, "error reading TLV header")
 	}
 
 	data := make([]byte, tlv.Header.Size)
 	sz, err := r.Read(data)
 	if err != nil {
-		return tlv, 0, util.FmtNewtError(
-			"Error reading %d bytes of TLV data: %s",
-			tlv.Header.Size, err.Error())
+		return tlv, 0, errors.Wrapf(err,
+			"error reading %d bytes of TLV data",
+			tlv.Header.Size)
 	}
 	if sz != len(data) {
-		return tlv, 0, util.FmtNewtError(
-			"Error reading %d bytes of TLV data: incomplete read",
+		return tlv, 0, errors.Errorf(
+			"error reading %d bytes of TLV data: incomplete read",
 			tlv.Header.Size)
 	}
 	tlv.Data = data
@@ -301,13 +354,13 @@ func parseMetaFooter(bin []byte) (MetaFooter, int, error) {
 
 	var ftr MetaFooter
 	if err := binary.Read(r, binary.LittleEndian, &ftr); err != nil {
-		return ftr, 0, util.FmtNewtError(
-			"Error reading meta footer: %s", err.Error())
+		return ftr, 0, errors.Wrapf(err,
+			"error reading meta footer")
 	}
 
 	if ftr.Magic != META_MAGIC {
-		return ftr, 0, util.FmtNewtError(
-			"Meta footer contains invalid magic; exp:0x%08x, got:0x%08x",
+		return ftr, 0, errors.Errorf(
+			"meta footer contains invalid magic; exp:0x%08x, got:0x%08x",
 			META_MAGIC, ftr.Magic)
 	}
 
@@ -316,8 +369,8 @@ func parseMetaFooter(bin []byte) (MetaFooter, int, error) {
 
 func ParseMeta(bin []byte) (Meta, int, error) {
 	if len(bin) < META_FOOTER_SZ {
-		return Meta{}, 0, util.FmtNewtError(
-			"Binary too small to accommodate meta footer; "+
+		return Meta{}, 0, errors.Errorf(
+			"binary too small to accommodate meta footer; "+
 				"bin-size=%d ftr-size=%d", len(bin), META_FOOTER_SZ)
 	}
 
@@ -327,8 +380,8 @@ func ParseMeta(bin []byte) (Meta, int, error) {
 	}
 
 	if int(ftr.Size) > len(bin) {
-		return Meta{}, 0, util.FmtNewtError(
-			"Binary too small to accommodate meta region; "+
+		return Meta{}, 0, errors.Errorf(
+			"binary too small to accommodate meta region; "+
 				"bin-size=%d meta-size=%d", len(bin), ftr.Size)
 	}
 
diff --git a/mfg/mfg.go b/mfg/mfg.go
index 2d5df83..eea6abf 100644
--- a/mfg/mfg.go
+++ b/mfg/mfg.go
@@ -20,9 +20,11 @@
 package mfg
 
 import (
+	"bytes"
 	"crypto/sha256"
 
-	"mynewt.apache.org/newt/util"
+	"github.com/apache/mynewt-artifact/errors"
+	"github.com/apache/mynewt-artifact/flash"
 )
 
 const MFG_BIN_IMG_FILENAME = "mfgimg.bin"
@@ -43,12 +45,22 @@ func Parse(data []byte, metaEndOff int, eraseVal byte) (Mfg, error) {
 	}
 
 	if metaEndOff >= 0 {
+		if metaEndOff > len(data) {
+			return m, errors.Errorf(
+				"MMR offset (%d) beyond end of mfgimage (%d)",
+				metaEndOff, len(data))
+		}
+
 		meta, _, err := ParseMeta(data[:metaEndOff])
 		if err != nil {
 			return m, err
 		}
 		m.Meta = &meta
 		m.MetaOff = metaEndOff - int(meta.Footer.Size)
+
+		for i := 0; i < int(meta.Footer.Size); i++ {
+			m.Bin[m.MetaOff+i] = eraseVal
+		}
 	}
 
 	return m, nil
@@ -85,52 +97,78 @@ func CalcHash(bin []byte) []byte {
 	return hash[:]
 }
 
-func (m *Mfg) RecalcHash(eraseVal byte) error {
+func (m *Mfg) RecalcHash(eraseVal byte) ([]byte, error) {
+	// The hash TLV needs to be zeroed out in order to calculate the mfg
+	// hash.  Duplicate the mfg object so that we don't modify the
+	// original.
+	dup := m.Clone()
+	if dup.Meta != nil {
+		dup.Meta.ClearHash()
+	}
+
+	bin, err := dup.Bytes(eraseVal)
+	if err != nil {
+		return nil, err
+	}
+
+	return CalcHash(bin), nil
+}
+
+func (m *Mfg) RefillHash(eraseVal byte) error {
 	if m.Meta == nil || m.Meta.Hash() == nil {
 		return nil
 	}
+	tlv := m.Meta.FindFirstTlv(META_TLV_TYPE_HASH)
+	if tlv == nil {
+		return nil
+	}
 
-	// First, write with zeroed hash.
-	m.Meta.ClearHash()
-	bin, err := m.Bytes(eraseVal)
+	// Calculate hash.
+	hash, err := m.RecalcHash(eraseVal)
 	if err != nil {
 		return err
 	}
 
-	// Calculate hash and fill TLV.
-	tlv := m.Meta.FindFirstTlv(META_TLV_TYPE_HASH)
-	if tlv != nil {
-		hashData := CalcHash(bin)
-		copy(tlv.Data, hashData)
-
-		hashOff := m.MetaOff + m.Meta.HashOffset()
-		if hashOff+META_HASH_SZ > len(bin) {
-			return util.FmtNewtError(
-				"unexpected error: hash extends beyond end " +
-					"of manufacturing image")
-		}
-	}
+	// Fill hash TLV.
+	copy(tlv.Data, hash)
 
 	return nil
 }
 
-func (m *Mfg) Hash() ([]byte, error) {
+func (m *Mfg) Hash(eraseVal byte) ([]byte, error) {
 	var hashBytes []byte
+
 	if m.Meta != nil {
 		hashBytes = m.Meta.Hash()
 	}
+
 	if hashBytes == nil {
 		// No hash TLV; calculate hash manually.
-		bin, err := m.Bytes(0xff)
+		b, err := m.RecalcHash(eraseVal)
 		if err != nil {
 			return nil, err
 		}
-		hashBytes = CalcHash(bin)
+		hashBytes = b
 	}
 
 	return hashBytes, nil
 }
 
+func (m *Mfg) HashIsValid(eraseVal byte) (bool, error) {
+	// If the mfg doesn't contain a hash TLV, then there is nothing to verify.
+	tlv := m.Meta.FindFirstTlv(META_TLV_TYPE_HASH)
+	if tlv == nil {
+		return true, nil
+	}
+
+	hash, err := m.RecalcHash(eraseVal)
+	if err != nil {
+		return false, err
+	}
+
+	return bytes.Equal(hash, tlv.Data), nil
+}
+
 func (m *Mfg) Bytes(eraseVal byte) ([]byte, error) {
 	binCopy := make([]byte, len(m.Bin))
 	copy(binCopy, m.Bin)
@@ -149,3 +187,51 @@ func (m *Mfg) Bytes(eraseVal byte) ([]byte, error) {
 
 	return binCopy, nil
 }
+
+func (m *Mfg) Clone() Mfg {
+	var meta *Meta
+	if m.Meta != nil {
+		metaDup := m.Meta.Clone()
+		meta = &metaDup
+	}
+
+	bin := make([]byte, len(m.Bin))
+	copy(bin, m.Bin)
+
+	return Mfg{
+		Bin:     bin,
+		Meta:    meta,
+		MetaOff: m.MetaOff,
+	}
+}
+
+func (m *Mfg) ExtractFlashArea(area flash.FlashArea, eraseVal byte) ([]byte, error) {
+	b, err := m.Bytes(eraseVal)
+	if err != nil {
+		return nil, err
+	}
+
+	if area.Offset >= len(b) {
+		return nil, errors.Errorf(
+			"flash area in mmr (\"%s\") is beyond end of mfgimage "+
+				"(offset=%d mfgimg_len=%d)",
+			area.Name, area.Offset, len(b))
+	}
+
+	// If the end of the target contains unwritten bytes, it gets truncated
+	// from the mfgimage.
+	end := area.Offset + area.Size
+	if end > len(b) {
+		end = len(b)
+	}
+
+	return b[area.Offset:end], nil
+}
+
+func (m *Mfg) Tlvs() []MetaTlv {
+	if m.Meta == nil {
+		return nil
+	} else {
+		return m.Meta.Tlvs
+	}
+}
diff --git a/mfg/verify.go b/mfg/verify.go
new file mode 100644
index 0000000..17a5a76
--- /dev/null
+++ b/mfg/verify.go
@@ -0,0 +1,202 @@
+package mfg
+
+import (
+	"bytes"
+	"encoding/hex"
+
+	"github.com/apache/mynewt-artifact/errors"
+	"github.com/apache/mynewt-artifact/flash"
+	"github.com/apache/mynewt-artifact/image"
+	"github.com/apache/mynewt-artifact/manifest"
+)
+
+func (m *Mfg) validateManFlashMap(man manifest.MfgManifest) error {
+	idAreaMap := map[int]flash.FlashArea{}
+	for _, area := range man.FlashAreas {
+		if _, dup := idAreaMap[area.Id]; dup {
+			return errors.Errorf(
+				"mfg manifest contains duplicate flash area: %d", area.Id)
+		}
+
+		idAreaMap[area.Id] = area
+	}
+
+	seen := map[int]struct{}{}
+
+	mmrHasFlash := man.Meta != nil && man.Meta.FlashMap
+
+	for _, t := range m.Tlvs() {
+		if t.Header.Type == META_TLV_TYPE_FLASH_AREA {
+			if !mmrHasFlash {
+				return errors.Errorf(
+					"mmr contains flash map; manifest indicates otherwise")
+			}
+
+			body, err := t.StructuredBody()
+			if err != nil {
+				return err
+			}
+			flashBody := body.(*MetaTlvBodyFlashArea)
+			if _, ok := idAreaMap[int(flashBody.Area)]; !ok {
+				return errors.Errorf(
+					"flash area %d missing from mfg manifest", flashBody.Area)
+			}
+
+			seen[int(flashBody.Area)] = struct{}{}
+		}
+	}
+
+	if mmrHasFlash {
+		for _, area := range man.FlashAreas {
+			if _, ok := seen[area.Id]; !ok {
+				return errors.Errorf("flash area %d missing from mmr", area.Id)
+			}
+		}
+	}
+
+	return nil
+}
+
+func (m *Mfg) validateManMmrs(man manifest.MfgManifest) error {
+	areaMap := map[int]struct{}{}
+	if man.Meta != nil {
+		for _, mmr := range man.Meta.Mmrs {
+			fa := man.FindFlashAreaName(mmr.Area)
+			if fa == nil {
+				return errors.Errorf(
+					"flash area %s missing from mfg manifest", mmr.Area)
+			}
+
+			if _, dup := areaMap[fa.Id]; dup {
+				return errors.Errorf(
+					"mfg manifest contains duplicate mmr ref: %s", mmr.Area)
+			}
+
+			areaMap[fa.Id] = struct{}{}
+		}
+	}
+
+	seen := map[int]struct{}{}
+	for _, t := range m.Tlvs() {
+		if t.Header.Type == META_TLV_TYPE_MMR_REF {
+			body, err := t.StructuredBody()
+			if err != nil {
+				return err
+			}
+
+			mmrBody := body.(*MetaTlvBodyMmrRef)
+			if _, ok := areaMap[int(mmrBody.Area)]; !ok {
+				return errors.Errorf(
+					"mmr ref %d missing from mfg manifest", mmrBody.Area)
+			}
+
+			seen[int(mmrBody.Area)] = struct{}{}
+		}
+	}
+
+	for area, _ := range areaMap {
+		if _, ok := seen[area]; !ok {
+			return errors.Errorf("mmr ref %d missing from mmr", area)
+		}
+	}
+
+	return nil
+}
+
+func (m *Mfg) validateManTargets(man manifest.MfgManifest) error {
+	for _, t := range man.Targets {
+		fa := man.FindFlashArea(man.Device, t.Offset)
+		if fa == nil {
+			return errors.Errorf(
+				"no flash area in mfgimage corresponding to target \"%s\"",
+				t.Name)
+		}
+
+		data, err := m.ExtractFlashArea(*fa, man.EraseVal)
+		if err != nil {
+			return err
+		}
+
+		if !t.IsBoot() {
+			img, err := image.ParseImage(data)
+			if err != nil {
+				return errors.Wrapf(err,
+					"error parsing build \"%s\" embedded in mfgimage", t.Name)
+			}
+
+			if err := img.Verify(); err != nil {
+				return errors.Wrapf(err,
+					"mfgimage contains invalid build \"%s\"", t.Name)
+			}
+		}
+	}
+
+	return nil
+}
+
+// VerifyManifest compares an mfgimage's structure to its manifest.  It
+// returns an error if the mfgimage doesn't match the manifest.
+func (m *Mfg) VerifyManifest(man manifest.MfgManifest) error {
+	if man.Format != 2 {
+		return errors.Errorf(
+			"only mfgimage format 2 supported (have=%d)", man.Format)
+	}
+
+	mfgHash, err := m.Hash(man.EraseVal)
+	if err != nil {
+		return err
+	}
+	hashStr := hex.EncodeToString(mfgHash)
+	if hashStr != man.MfgHash {
+		return errors.Errorf(
+			"manifest mfg hash different from mmr: man=%s mfg=%s",
+			man.MfgHash, hashStr)
+	}
+
+	if err := m.validateManFlashMap(man); err != nil {
+		return err
+	}
+
+	if err := m.validateManMmrs(man); err != nil {
+		return err
+	}
+
+	// Verify each embedded build.
+	if err := m.validateManTargets(man); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Verify checks an mfgimage's structure and internal consistency.  It
+// returns an error if the mfgimage is incorrect.
+func (m *Mfg) Verify(eraseVal byte) error {
+	for _, t := range m.Tlvs() {
+		// Verify that TLV has a valid `type` field.
+		body, err := t.StructuredBody()
+		if err != nil {
+			return err
+		}
+
+		// Verify contents of hash TLV.
+		switch t.Header.Type {
+		case META_TLV_TYPE_HASH:
+			hashBody := body.(*MetaTlvBodyHash)
+
+			hash, err := m.RecalcHash(eraseVal)
+			if err != nil {
+				return err
+			}
+
+			if !bytes.Equal(hash, hashBody.Hash[:]) {
+				return errors.Errorf(
+					"mmr contains incorrect hash: have=%s want=%s",
+					hex.EncodeToString(hashBody.Hash[:]),
+					hex.EncodeToString(hash))
+			}
+		}
+	}
+
+	return nil
+}
diff --git a/sec/encrypt.go b/sec/encrypt.go
index 35ecdd7..d58806c 100644
--- a/sec/encrypt.go
+++ b/sec/encrypt.go
@@ -25,13 +25,13 @@ import (
 	"crypto/cipher"
 	"io"
 
-	"mynewt.apache.org/newt/util"
+	"github.com/apache/mynewt-artifact/errors"
 )
 
 func EncryptAES(plain []byte, secret []byte) ([]byte, error) {
 	block, err := aes.NewCipher(secret)
 	if err != nil {
-		return nil, util.NewNewtError("Failed to create block cipher")
+		return nil, errors.Errorf("Failed to create block cipher")
 	}
 	nonce := make([]byte, 16)
 	stream := cipher.NewCTR(block, nonce)
@@ -43,8 +43,7 @@ func EncryptAES(plain []byte, secret []byte) ([]byte, error) {
 	for {
 		cnt, err := r.Read(dataBuf)
 		if err != nil && err != io.EOF {
-			return nil, util.FmtNewtError(
-				"Failed to read from plaintext: %s", err.Error())
+			return nil, errors.Wrapf(err, "Failed to read from plaintext")
 		}
 		if cnt == 0 {
 			break
@@ -52,8 +51,7 @@ func EncryptAES(plain []byte, secret []byte) ([]byte, error) {
 
 		stream.XORKeyStream(encBuf, dataBuf[0:cnt])
 		if _, err = w.Write(encBuf[0:cnt]); err != nil {
-			return nil, util.FmtNewtError(
-				"Failed to write ciphertext: %s", err.Error())
+			return nil, errors.Wrapf(err, "failed to write ciphertext")
 		}
 	}
 
diff --git a/sec/key.go b/sec/key.go
index 9d073bd..473ef62 100644
--- a/sec/key.go
+++ b/sec/key.go
@@ -33,8 +33,7 @@ import (
 	"io/ioutil"
 
 	keywrap "github.com/NickBall/go-aes-key-wrap"
-
-	"mynewt.apache.org/newt/util"
+	"github.com/apache/mynewt-artifact/errors"
 )
 
 type SignKey struct {
@@ -43,6 +42,11 @@ type SignKey struct {
 	Ec  *ecdsa.PrivateKey
 }
 
+type PubSignKey struct {
+	Rsa *rsa.PublicKey
+	Ec  *ecdsa.PublicKey
+}
+
 func ParsePrivateKey(keyBytes []byte) (interface{}, error) {
 	var privKey interface{}
 	var err error
@@ -63,8 +67,7 @@ func ParsePrivateKey(keyBytes []byte) (interface{}, error) {
 		 */
 		privKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
 		if err != nil {
-			return nil, util.FmtNewtError(
-				"Private key parsing failed: %s", err)
+			return nil, errors.Wrapf(err, "Private key parsing failed")
 		}
 	}
 	if block != nil && block.Type == "EC PRIVATE KEY" {
@@ -73,8 +76,7 @@ func ParsePrivateKey(keyBytes []byte) (interface{}, error) {
 		 */
 		privKey, err = x509.ParseECPrivateKey(block.Bytes)
 		if err != nil {
-			return nil, util.FmtNewtError(
-				"Private key parsing failed: %s", err)
+			return nil, errors.Wrapf(err, "Private key parsing failed")
 		}
 	}
 	if block != nil && block.Type == "PRIVATE KEY" {
@@ -83,8 +85,7 @@ func ParsePrivateKey(keyBytes []byte) (interface{}, error) {
 		// the key itself.
 		privKey, err = x509.ParsePKCS8PrivateKey(block.Bytes)
 		if err != nil {
-			return nil, util.FmtNewtError(
-				"Private key parsing failed: %s", err)
+			return nil, errors.Wrapf(err, "Private key parsing failed")
 		}
 	}
 	if block != nil && block.Type == "ENCRYPTED PRIVATE KEY" {
@@ -92,12 +93,12 @@ func ParsePrivateKey(keyBytes []byte) (interface{}, error) {
 		// encryption.
 		privKey, err = parseEncryptedPrivateKey(block.Bytes)
 		if err != nil {
-			return nil, util.FmtNewtError(
-				"Unable to decode encrypted private key: %s", err)
+			return nil, errors.Wrapf(
+				err, "Unable to decode encrypted private key")
 		}
 	}
 	if privKey == nil {
-		return nil, util.NewNewtError(
+		return nil, errors.Errorf(
 			"Unknown private key format, EC/RSA private " +
 				"key in PEM format only.")
 	}
@@ -119,7 +120,7 @@ func BuildPrivateKey(keyBytes []byte) (SignKey, error) {
 	case *ecdsa.PrivateKey:
 		key.Ec = priv
 	default:
-		return key, util.NewNewtError("Unknown private key format")
+		return key, errors.Errorf("Unknown private key format")
 	}
 
 	return key, nil
@@ -128,8 +129,7 @@ func BuildPrivateKey(keyBytes []byte) (SignKey, error) {
 func ReadKey(filename string) (SignKey, error) {
 	keyBytes, err := ioutil.ReadFile(filename)
 	if err != nil {
-		return SignKey{}, util.FmtNewtError(
-			"Error reading key file: %s", err)
+		return SignKey{}, errors.Wrapf(err, "Error reading key file")
 	}
 
 	return BuildPrivateKey(keyBytes)
@@ -154,31 +154,21 @@ func (key *SignKey) AssertValid() {
 	if key.Rsa == nil && key.Ec == nil {
 		panic("invalid key; neither RSA nor ECC")
 	}
-
-	if key.Rsa != nil && key.Ec != nil {
-		panic("invalid key; neither RSA nor ECC")
-	}
 }
 
-func (key *SignKey) PubBytes() ([]uint8, error) {
+func (key *SignKey) PubKey() PubSignKey {
 	key.AssertValid()
 
-	var pubkey []byte
-
 	if key.Rsa != nil {
-		pubkey, _ = asn1.Marshal(key.Rsa.PublicKey)
+		return PubSignKey{Rsa: &key.Rsa.PublicKey}
 	} else {
-		switch key.Ec.Curve.Params().Name {
-		case "P-224":
-			fallthrough
-		case "P-256":
-			pubkey, _ = x509.MarshalPKIXPublicKey(&key.Ec.PublicKey)
-		default:
-			return nil, util.NewNewtError("Unsupported ECC curve")
-		}
+		return PubSignKey{Ec: &key.Ec.PublicKey}
 	}
+}
 
-	return pubkey, nil
+func (key *SignKey) PubBytes() ([]byte, error) {
+	pk := key.PubKey()
+	return pk.PubBytes()
 }
 
 func RawKeyHash(pubKeyBytes []byte) []byte {
@@ -211,13 +201,12 @@ func ParsePubKePem(keyBytes []byte) (*rsa.PublicKey, error) {
 	}
 
 	if b.Type != "PUBLIC KEY" && b.Type != "RSA PUBLIC KEY" {
-		return nil, util.NewNewtError("Invalid PEM file")
+		return nil, errors.Errorf("Invalid PEM file")
 	}
 
 	pub, err := x509.ParsePKIXPublicKey(b.Bytes)
 	if err != nil {
-		return nil, util.FmtNewtError(
-			"Error parsing pubkey file: %s", err.Error())
+		return nil, errors.Wrapf(err, "Error parsing pubkey file")
 	}
 
 	var pubk *rsa.PublicKey
@@ -225,8 +214,7 @@ func ParsePubKePem(keyBytes []byte) (*rsa.PublicKey, error) {
 	case *rsa.PublicKey:
 		pubk = pub.(*rsa.PublicKey)
 	default:
-		return nil, util.FmtNewtError(
-			"Error parsing pubkey file: %s", err.Error())
+		return nil, errors.Wrapf(err, "Error parsing pubkey file")
 	}
 
 	return pubk, nil
@@ -235,8 +223,7 @@ func ParsePubKePem(keyBytes []byte) (*rsa.PublicKey, error) {
 func ParsePrivKeDer(keyBytes []byte) (*rsa.PrivateKey, error) {
 	privKey, err := x509.ParsePKCS1PrivateKey(keyBytes)
 	if err != nil {
-		return nil, util.FmtNewtError(
-			"Error parsing private key file: %s", err.Error())
+		return nil, errors.Wrapf(err, "Error parsing private key file")
 	}
 
 	return privKey, nil
@@ -247,8 +234,7 @@ func EncryptSecretRsa(pubk *rsa.PublicKey, plainSecret []byte) ([]byte, error) {
 	cipherSecret, err := rsa.EncryptOAEP(
 		sha256.New(), rng, pubk, plainSecret, nil)
 	if err != nil {
-		return nil, util.FmtNewtError(
-			"Error from encryption: %s\n", err.Error())
+		return nil, errors.Wrapf(err, "Error from encryption")
 	}
 
 	return cipherSecret, nil
@@ -261,8 +247,7 @@ func DecryptSecretRsa(privk *rsa.PrivateKey,
 	plainSecret, err := rsa.DecryptOAEP(
 		sha256.New(), rng, privk, cipherSecret, nil)
 	if err != nil {
-		return nil, util.FmtNewtError(
-			"Error from encryption: %s\n", err.Error())
+		return nil, errors.Wrapf(err, "Error from encryption")
 	}
 
 	return plainSecret, nil
@@ -271,18 +256,18 @@ func DecryptSecretRsa(privk *rsa.PrivateKey,
 func ParseKeBase64(keyBytes []byte) (cipher.Block, error) {
 	kek, err := base64.StdEncoding.DecodeString(string(keyBytes))
 	if err != nil {
-		return nil, util.FmtNewtError(
-			"Error decoding kek: %s", err.Error())
+		return nil, errors.Wrapf(err,
+			"Error decoding kek: %s")
 	}
 	if len(kek) != 16 {
-		return nil, util.FmtNewtError(
+		return nil, errors.Errorf(
 			"Unexpected key size: %d != 16", len(kek))
 	}
 
 	cipher, err := aes.NewCipher(kek)
 	if err != nil {
-		return nil, util.FmtNewtError(
-			"Error creating keywrap cipher: %s", err.Error())
+		return nil, errors.Wrapf(err,
+			"Error creating keywrap cipher")
 	}
 
 	return cipher, nil
@@ -291,8 +276,39 @@ func ParseKeBase64(keyBytes []byte) (cipher.Block, error) {
 func EncryptSecretAes(c cipher.Block, plainSecret []byte) ([]byte, error) {
 	cipherSecret, err := keywrap.Wrap(c, plainSecret)
 	if err != nil {
-		return nil, util.FmtNewtError("Error key-wrapping: %s", err.Error())
+		return nil, errors.Wrapf(err, "Error key-wrapping")
 	}
 
 	return cipherSecret, nil
 }
+
+func (key *PubSignKey) AssertValid() {
+	if key.Rsa == nil && key.Ec == nil {
+		panic("invalid public key; neither RSA nor ECC")
+	}
+}
+
+func (key *PubSignKey) PubBytes() ([]byte, error) {
+	key.AssertValid()
+
+	var pubBytes []byte
+
+	if key.Rsa != nil {
+		var err error
+		pubBytes, err = asn1.Marshal(*key.Rsa)
+		if err != nil {
+			return nil, err
+		}
+	} else {
+		switch key.Ec.Curve.Params().Name {
+		case "P-224":
+			fallthrough
+		case "P-256":
+			pubBytes, _ = x509.MarshalPKIXPublicKey(*key.Ec)
+		default:
+			return nil, errors.Errorf("unsupported ECC curve")
+		}
+	}
+
+	return pubBytes, nil
+}
diff --git a/sec/sig.go b/sec/sig.go
new file mode 100644
index 0000000..8aa0b32
--- /dev/null
+++ b/sec/sig.go
@@ -0,0 +1,55 @@
+package sec
+
+import (
+	"bytes"
+	"crypto"
+	"crypto/rsa"
+
+	"github.com/apache/mynewt-artifact/errors"
+)
+
+type Sig struct {
+	KeyHash []byte
+	Data    []byte
+}
+
+func checkOneKeyOneSig(k PubSignKey, sig Sig, hash []byte) (bool, error) {
+	pubBytes, err := k.PubBytes()
+	if err != nil {
+		return false, errors.WithStack(err)
+	}
+	keyHash := RawKeyHash(pubBytes)
+
+	if !bytes.Equal(keyHash, sig.KeyHash) {
+		return false, nil
+	}
+
+	if k.Rsa != nil {
+		opts := rsa.PSSOptions{
+			SaltLength: rsa.PSSSaltLengthEqualsHash,
+		}
+		err := rsa.VerifyPSS(k.Rsa, crypto.SHA256, hash, sig.Data, &opts)
+		return err == nil, nil
+	}
+
+	if k.Ec != nil {
+		return false, errors.Errorf(
+			"ecdsa signature verification not supported")
+	}
+
+	return false, nil
+}
+
+func VerifySigs(key PubSignKey, sigs []Sig, hash []byte) (int, error) {
+	for i, s := range sigs {
+		match, err := checkOneKeyOneSig(key, s, hash)
+		if err != nil {
+			return -1, err
+		}
+		if match {
+			return i, nil
+		}
+	}
+
+	return -1, nil
+}


[mynewt-artifact] 07/23: newt: Allow mfgimages to be signed

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

commit 40452e878381db8cf381dcd742a8d38259d56efa
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Thu Jan 10 14:58:08 2019 -0800

    newt: Allow mfgimages to be signed
    
    Mfgimages are signed with the same keys as their constituent images.
---
 manifest/mfg_manifest.go | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/manifest/mfg_manifest.go b/manifest/mfg_manifest.go
index bb27adc..b7f00e8 100644
--- a/manifest/mfg_manifest.go
+++ b/manifest/mfg_manifest.go
@@ -31,6 +31,11 @@ type MfgManifestMeta struct {
 	// XXX: refhash
 }
 
+type MfgManifestSig struct {
+	Key string `json:"key"`
+	Sig string `json:"sig"`
+}
+
 type MfgManifest struct {
 	Name       string            `json:"name"`
 	BuildTime  string            `json:"build_time"`
@@ -40,6 +45,7 @@ type MfgManifest struct {
 	Device     int               `json:"device"`
 	BinPath    string            `json:"bin_path"`
 	Bsp        string            `json:"bsp"`
+	Signatures []MfgManifestSig  `json:"signatures,omitempty"`
 	FlashAreas []flash.FlashArea `json:"flash_map"`
 
 	Targets []MfgManifestTarget `json:"targets"`


[mynewt-artifact] 12/23: artifact: add image.CalcHash() function

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

commit db596e7f6825de31b6f2091741c8841362d4a1f3
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Fri May 24 12:37:22 2019 -0700

    artifact: add image.CalcHash() function
    
    Prior to this commit, the only way to calculate an image's hash was to
    use image.calcHash() (in `artifact/image/create.go`).  This function is
    private to the `image` package, and it takes a set of parameters that
    are is not convenient for most cases.
    
    This commit adds a new public function to the `image.Image` type:
    ```
    func (i *Image) CalcHash() ([]byte, error)
    ```
---
 image/image.go | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/image/image.go b/image/image.go
index 7f74cbc..9855223 100644
--- a/image/image.go
+++ b/image/image.go
@@ -348,6 +348,10 @@ func (i *Image) Hash() ([]byte, error) {
 	return tlv.Data, nil
 }
 
+func (i *Image) CalcHash() ([]byte, error) {
+	return calcHash(nil, i.Header, i.Pad, i.Body)
+}
+
 func (i *Image) WritePlusOffsets(w io.Writer) (ImageOffsets, error) {
 	offs := ImageOffsets{}
 	offset := 0


[mynewt-artifact] 06/23: Create new package: artifact/sec

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

commit 1447da7b29f30f7909aa739ae455744d1d850a42
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Thu Jan 10 13:08:51 2019 -0800

    Create new package: artifact/sec
    
    This package contains generic security functionality.
    
    Since mfgimages now require signatures (not just normal images), it
    doesn't make sense to keep all the security functionality in the `image`
    package.
---
 image/create.go                   | 111 ++++++++++++++++++++++----------------
 image/keys_test.go                |  63 +++++++---------------
 image/v1.go                       |  27 +++++-----
 sec/encrypt.go                    |  42 +++++++++++++++
 {image => sec}/key.go             |  98 ++++++++++-----------------------
 image/encrypted.go => sec/pkcs.go |   3 +-
 6 files changed, 167 insertions(+), 177 deletions(-)

diff --git a/image/create.go b/image/create.go
index 76e701d..3ec8770 100644
--- a/image/create.go
+++ b/image/create.go
@@ -22,8 +22,6 @@ package image
 import (
 	"bytes"
 	"crypto"
-	"crypto/aes"
-	"crypto/cipher"
 	"crypto/ecdsa"
 	"crypto/rand"
 	"crypto/rsa"
@@ -31,17 +29,17 @@ import (
 	"encoding/asn1"
 	"encoding/binary"
 	"encoding/hex"
-	"io"
 	"io/ioutil"
 	"math/big"
 
+	"mynewt.apache.org/newt/artifact/sec"
 	"mynewt.apache.org/newt/util"
 )
 
 type ImageCreator struct {
 	Body         []byte
 	Version      ImageVersion
-	SigKeys      []ImageSigKey
+	SigKeys      []sec.SignKey
 	PlainSecret  []byte
 	CipherSecret []byte
 	HeaderSize   int
@@ -53,7 +51,7 @@ type ImageCreateOpts struct {
 	SrcBinFilename    string
 	SrcEncKeyFilename string
 	Version           ImageVersion
-	SigKeys           []ImageSigKey
+	SigKeys           []sec.SignKey
 	LoaderHash        []byte
 }
 
@@ -69,6 +67,23 @@ func NewImageCreator() ImageCreator {
 	}
 }
 
+func sigTlvType(key sec.SignKey) uint8 {
+	key.AssertValid()
+
+	if key.Rsa != nil {
+		return IMAGE_TLV_RSA2048
+	} else {
+		switch key.Ec.Curve.Params().Name {
+		case "P-224":
+			return IMAGE_TLV_ECDSA224
+		case "P-256":
+			return IMAGE_TLV_ECDSA256
+		default:
+			return 0
+		}
+	}
+}
+
 func GenerateEncTlv(cipherSecret []byte) (ImageTlv, error) {
 	var encType uint8
 
@@ -90,7 +105,7 @@ func GenerateEncTlv(cipherSecret []byte) (ImageTlv, error) {
 	}, nil
 }
 
-func GenerateSigRsa(key ImageSigKey, hash []byte) ([]byte, error) {
+func GenerateSigRsa(key sec.SignKey, hash []byte) ([]byte, error) {
 	opts := rsa.PSSOptions{
 		SaltLength: rsa.PSSSaltLengthEqualsHash,
 	}
@@ -103,7 +118,7 @@ func GenerateSigRsa(key ImageSigKey, hash []byte) ([]byte, error) {
 	return signature, nil
 }
 
-func GenerateSigEc(key ImageSigKey, hash []byte) ([]byte, error) {
+func GenerateSigEc(key sec.SignKey, hash []byte) ([]byte, error) {
 	r, s, err := ecdsa.Sign(rand.Reader, key.Ec, hash)
 	if err != nil {
 		return nil, util.FmtNewtError("Failed to compute signature: %s", err)
@@ -119,7 +134,7 @@ func GenerateSigEc(key ImageSigKey, hash []byte) ([]byte, error) {
 		return nil, util.FmtNewtError("Failed to construct signature: %s", err)
 	}
 
-	sigLen := key.sigLen()
+	sigLen := key.SigLen()
 	if len(signature) > int(sigLen) {
 		return nil, util.FmtNewtError("Something is really wrong\n")
 	}
@@ -130,8 +145,8 @@ func GenerateSigEc(key ImageSigKey, hash []byte) ([]byte, error) {
 	return signature, nil
 }
 
-func GenerateSig(key ImageSigKey, hash []byte) ([]byte, error) {
-	key.assertValid()
+func GenerateSig(key sec.SignKey, hash []byte) ([]byte, error) {
+	key.AssertValid()
 
 	if key.Rsa != nil {
 		return GenerateSigRsa(key, hash)
@@ -141,7 +156,7 @@ func GenerateSig(key ImageSigKey, hash []byte) ([]byte, error) {
 }
 
 func BuildKeyHashTlv(keyBytes []byte) ImageTlv {
-	data := RawKeyHash(keyBytes)
+	data := sec.RawKeyHash(keyBytes)
 	return ImageTlv{
 		Header: ImageTlvHdr{
 			Type: IMAGE_TLV_KEYHASH,
@@ -152,11 +167,11 @@ func BuildKeyHashTlv(keyBytes []byte) ImageTlv {
 	}
 }
 
-func BuildSigTlvs(keys []ImageSigKey, hash []byte) ([]ImageTlv, error) {
+func BuildSigTlvs(keys []sec.SignKey, hash []byte) ([]ImageTlv, error) {
 	var tlvs []ImageTlv
 
 	for _, key := range keys {
-		key.assertValid()
+		key.AssertValid()
 
 		// Key hash TLV.
 		pubKey, err := key.PubBytes()
@@ -173,7 +188,7 @@ func BuildSigTlvs(keys []ImageSigKey, hash []byte) ([]ImageTlv, error) {
 		}
 		tlv = ImageTlv{
 			Header: ImageTlvHdr{
-				Type: key.sigTlvType(),
+				Type: sigTlvType(key),
 				Len:  uint16(len(sig)),
 			},
 			Data: sig,
@@ -184,6 +199,40 @@ func BuildSigTlvs(keys []ImageSigKey, hash []byte) ([]ImageTlv, error) {
 	return tlvs, nil
 }
 
+func GeneratePlainSecret() ([]byte, error) {
+	plainSecret := make([]byte, 16)
+	if _, err := rand.Read(plainSecret); err != nil {
+		return nil, util.FmtNewtError(
+			"Random generation error: %s\n", err)
+	}
+
+	return plainSecret, nil
+}
+
+func GenerateCipherSecret(pubKeBytes []byte,
+	plainSecret []byte) ([]byte, error) {
+
+	// Try reading as PEM (asymetric key).
+	rsaPubKe, err := sec.ParsePubKePem(pubKeBytes)
+	if err != nil {
+		return nil, err
+	}
+	if rsaPubKe != nil {
+		return sec.EncryptSecretRsa(rsaPubKe, plainSecret)
+	}
+
+	// Not PEM; assume this is a base64 encoded symetric key
+	aesPubKe, err := sec.ParseKeBase64(pubKeBytes)
+	if err != nil {
+		return nil, err
+	}
+	if aesPubKe != nil {
+		return sec.EncryptSecretAes(aesPubKe, plainSecret)
+	}
+
+	return nil, util.FmtNewtError("Invalid image-crypt key")
+}
+
 func GenerateImage(opts ImageCreateOpts) (Image, error) {
 	ic := NewImageCreator()
 
@@ -279,38 +328,6 @@ func calcHash(initialHash []byte, hdr ImageHdr, pad []byte,
 	return hash.Sum(nil), nil
 }
 
-func EncryptImageBody(imageBody []byte, secret []byte) ([]byte, error) {
-	block, err := aes.NewCipher(secret)
-	if err != nil {
-		return nil, util.NewNewtError("Failed to create block cipher")
-	}
-	nonce := make([]byte, 16)
-	stream := cipher.NewCTR(block, nonce)
-
-	dataBuf := make([]byte, 16)
-	encBuf := make([]byte, 16)
-	r := bytes.NewReader(imageBody)
-	w := bytes.Buffer{}
-	for {
-		cnt, err := r.Read(dataBuf)
-		if err != nil && err != io.EOF {
-			return nil, util.FmtNewtError(
-				"Failed to read from image body: %s", err.Error())
-		}
-		if cnt == 0 {
-			break
-		}
-
-		stream.XORKeyStream(encBuf, dataBuf[0:cnt])
-		if _, err = w.Write(encBuf[0:cnt]); err != nil {
-			return nil, util.FmtNewtError(
-				"Failed to write to image body: %s", err.Error())
-		}
-	}
-
-	return w.Bytes(), nil
-}
-
 func (ic *ImageCreator) Create() (Image, error) {
 	img := Image{}
 
@@ -354,7 +371,7 @@ func (ic *ImageCreator) Create() (Image, error) {
 
 	// Followed by data.
 	if ic.CipherSecret != nil {
-		encBody, err := EncryptImageBody(ic.Body, ic.PlainSecret)
+		encBody, err := sec.EncryptAES(ic.Body, ic.PlainSecret)
 		if err != nil {
 			return img, err
 		}
diff --git a/image/keys_test.go b/image/keys_test.go
index 577d9cd..769848a 100644
--- a/image/keys_test.go
+++ b/image/keys_test.go
@@ -22,8 +22,10 @@ package image_test
 import (
 	"io/ioutil"
 	"os"
-	"path"
 	"testing"
+
+	"mynewt.apache.org/newt/artifact/image"
+	"mynewt.apache.org/newt/artifact/sec"
 )
 
 func TestRSA(t *testing.T) {
@@ -43,15 +45,15 @@ func TestPlainEcdsaPkcs8(t *testing.T) {
 }
 
 func TestEncryptedRSA(t *testing.T) {
-	image.KeyPassword = []byte("sample")
+	sec.KeyPassword = []byte("sample")
 	signatureTest(t, rsaEncryptedPrivate)
-	image.KeyPassword = []byte{}
+	sec.KeyPassword = []byte{}
 }
 
 func TestEncryptedEcdsa(t *testing.T) {
-	image.KeyPassword = []byte("sample")
+	sec.KeyPassword = []byte("sample")
 	signatureTest(t, ecdsaEncryptedPrivate)
-	image.KeyPassword = []byte{}
+	sec.KeyPassword = []byte{}
 }
 
 func signatureTest(t *testing.T, privateKey []byte) {
@@ -65,58 +67,29 @@ func signatureTest(t *testing.T, privateKey []byte) {
 	// much, since the header will be placed on it by the image
 	// tool.
 
-	simpleName := path.Join(tmpdir, "simple.bin")
-	hashedName := path.Join(tmpdir, "simple-hashed.bin")
-	signedName := path.Join(tmpdir, "simple-signed.bin")
-	keyName := path.Join(tmpdir, "private.pem")
-
-	tmp := make([]byte, 256)
-	for i := 0; i < len(tmp); i++ {
-		tmp[i] = byte(i & 0xFF)
-	}
-	err = ioutil.WriteFile(simpleName, tmp, 0644)
-	if err != nil {
-		t.Fatal(err)
+	body := make([]byte, 256)
+	for i := 0; i < len(body); i++ {
+		body[i] = byte(i)
 	}
 
-	img, err := image.NewImage(simpleName, hashedName)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	img.SetVersion("1.5")
-	if err != nil {
-		t.Fatal(err)
-	}
+	ic := image.NewImageCreator()
+	ic.Version = image.ImageVersion{1, 5, 0, 0}
+	ic.Body = body
 
-	img.Generate(nil)
-	if err != nil {
+	if _, err := ic.Create(); err != nil {
 		t.Fatal(err)
 	}
 
 	// Now try with a signature.
-	err = ioutil.WriteFile(keyName, privateKey, 0644)
+	key, err := sec.BuildPrivateKey(privateKey)
 	if err != nil {
 		t.Fatal(err)
 	}
+	ic.SigKeys = append(ic.SigKeys, key)
 
-	img, err = image.NewImage(simpleName, signedName)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	err = img.SetSigningKey(keyName, 0)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	err = img.SetVersion("1.6")
-	if err != nil {
-		t.Fatal(err)
-	}
+	ic.Version = image.ImageVersion{1, 6, 0, 0}
 
-	err = img.Generate(nil)
-	if err != nil {
+	if _, err := ic.Create(); err != nil {
 		t.Fatal(err)
 	}
 }
diff --git a/image/v1.go b/image/v1.go
index 0dc10a5..67a451f 100644
--- a/image/v1.go
+++ b/image/v1.go
@@ -34,6 +34,7 @@ import (
 	"io"
 	"io/ioutil"
 
+	"mynewt.apache.org/newt/artifact/sec"
 	"mynewt.apache.org/newt/util"
 )
 
@@ -157,8 +158,8 @@ func (img *ImageV1) Write(w io.Writer) (int, error) {
 	return offs.TotalSize, nil
 }
 
-func (key *ImageSigKey) sigHdrTypeV1() (uint32, error) {
-	key.assertValid()
+func sigHdrTypeV1(key sec.SignKey) (uint32, error) {
+	key.AssertValid()
 
 	if key.Rsa != nil {
 		if UseRsaPss {
@@ -178,8 +179,8 @@ func (key *ImageSigKey) sigHdrTypeV1() (uint32, error) {
 	}
 }
 
-func (key *ImageSigKey) sigTlvTypeV1() uint8 {
-	key.assertValid()
+func sigTlvTypeV1(key sec.SignKey) uint8 {
+	key.AssertValid()
 
 	if key.Rsa != nil {
 		return IMAGEv1_TLV_RSA2048
@@ -216,7 +217,7 @@ func generateV1SigRsa(key *rsa.PrivateKey, hash []byte) ([]byte, error) {
 	return signature, nil
 }
 
-func generateV1SigTlvRsa(key ImageSigKey, hash []byte) (ImageTlv, error) {
+func generateV1SigTlvRsa(key sec.SignKey, hash []byte) (ImageTlv, error) {
 	sig, err := generateV1SigRsa(key.Rsa, hash)
 	if err != nil {
 		return ImageTlv{}, err
@@ -224,7 +225,7 @@ func generateV1SigTlvRsa(key ImageSigKey, hash []byte) (ImageTlv, error) {
 
 	return ImageTlv{
 		Header: ImageTlvHdr{
-			Type: key.sigTlvTypeV1(),
+			Type: sigTlvTypeV1(key),
 			Pad:  0,
 			Len:  256, /* 2048 bits */
 		},
@@ -232,13 +233,13 @@ func generateV1SigTlvRsa(key ImageSigKey, hash []byte) (ImageTlv, error) {
 	}, nil
 }
 
-func generateV1SigTlvEc(key ImageSigKey, hash []byte) (ImageTlv, error) {
+func generateV1SigTlvEc(key sec.SignKey, hash []byte) (ImageTlv, error) {
 	sig, err := GenerateSigEc(key, hash)
 	if err != nil {
 		return ImageTlv{}, err
 	}
 
-	sigLen := key.sigLen()
+	sigLen := key.SigLen()
 	if len(sig) > int(sigLen) {
 		return ImageTlv{}, util.FmtNewtError("Something is really wrong\n")
 	}
@@ -258,7 +259,7 @@ func generateV1SigTlvEc(key ImageSigKey, hash []byte) (ImageTlv, error) {
 
 	return ImageTlv{
 		Header: ImageTlvHdr{
-			Type: key.sigTlvTypeV1(),
+			Type: sigTlvTypeV1(key),
 			Pad:  0,
 			Len:  sigLen + uint16(len(pad)),
 		},
@@ -266,8 +267,8 @@ func generateV1SigTlvEc(key ImageSigKey, hash []byte) (ImageTlv, error) {
 	}, nil
 }
 
-func generateV1SigTlv(key ImageSigKey, hash []byte) (ImageTlv, error) {
-	key.assertValid()
+func generateV1SigTlv(key sec.SignKey, hash []byte) (ImageTlv, error) {
+	key.AssertValid()
 
 	if key.Rsa != nil {
 		return generateV1SigTlvRsa(key, hash)
@@ -356,12 +357,12 @@ func (ic *ImageCreator) CreateV1() (ImageV1, error) {
 	}
 
 	if len(ic.SigKeys) > 0 {
-		keyFlag, err := ic.SigKeys[0].sigHdrTypeV1()
+		keyFlag, err := sigHdrTypeV1(ic.SigKeys[0])
 		if err != nil {
 			return ri, err
 		}
 		hdr.Flags |= keyFlag
-		hdr.TlvSz = 4 + ic.SigKeys[0].sigLen()
+		hdr.TlvSz = 4 + ic.SigKeys[0].SigLen()
 	}
 	hdr.TlvSz += 4 + 32
 
diff --git a/sec/encrypt.go b/sec/encrypt.go
new file mode 100644
index 0000000..bd99594
--- /dev/null
+++ b/sec/encrypt.go
@@ -0,0 +1,42 @@
+package sec
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"io"
+
+	"mynewt.apache.org/newt/util"
+)
+
+func EncryptAES(plain []byte, secret []byte) ([]byte, error) {
+	block, err := aes.NewCipher(secret)
+	if err != nil {
+		return nil, util.NewNewtError("Failed to create block cipher")
+	}
+	nonce := make([]byte, 16)
+	stream := cipher.NewCTR(block, nonce)
+
+	dataBuf := make([]byte, 16)
+	encBuf := make([]byte, 16)
+	r := bytes.NewReader(plain)
+	w := bytes.Buffer{}
+	for {
+		cnt, err := r.Read(dataBuf)
+		if err != nil && err != io.EOF {
+			return nil, util.FmtNewtError(
+				"Failed to read from plaintext: %s", err.Error())
+		}
+		if cnt == 0 {
+			break
+		}
+
+		stream.XORKeyStream(encBuf, dataBuf[0:cnt])
+		if _, err = w.Write(encBuf[0:cnt]); err != nil {
+			return nil, util.FmtNewtError(
+				"Failed to write ciphertext: %s", err.Error())
+		}
+	}
+
+	return w.Bytes(), nil
+}
diff --git a/image/key.go b/sec/key.go
similarity index 77%
rename from image/key.go
rename to sec/key.go
index a343e2d..89b5f49 100644
--- a/image/key.go
+++ b/sec/key.go
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package image
+package sec
 
 import (
 	"crypto/aes"
@@ -37,7 +37,7 @@ import (
 	"mynewt.apache.org/newt/util"
 )
 
-type ImageSigKey struct {
+type SignKey struct {
 	// Only one of these members is non-nil.
 	Rsa *rsa.PrivateKey
 	Ec  *ecdsa.PrivateKey
@@ -92,24 +92,21 @@ func ParsePrivateKey(keyBytes []byte) (interface{}, error) {
 		// encryption.
 		privKey, err = parseEncryptedPrivateKey(block.Bytes)
 		if err != nil {
-			return nil, util.FmtNewtError("Unable to decode encrypted private key: %s", err)
+			return nil, util.FmtNewtError(
+				"Unable to decode encrypted private key: %s", err)
 		}
 	}
 	if privKey == nil {
-		return nil, util.NewNewtError("Unknown private key format, EC/RSA private " +
-			"key in PEM format only.")
+		return nil, util.NewNewtError(
+			"Unknown private key format, EC/RSA private " +
+				"key in PEM format only.")
 	}
 
 	return privKey, nil
 }
 
-func ReadKey(filename string) (ImageSigKey, error) {
-	key := ImageSigKey{}
-
-	keyBytes, err := ioutil.ReadFile(filename)
-	if err != nil {
-		return key, util.FmtNewtError("Error reading key file: %s", err)
-	}
+func BuildPrivateKey(keyBytes []byte) (SignKey, error) {
+	key := SignKey{}
 
 	privKey, err := ParsePrivateKey(keyBytes)
 	if err != nil {
@@ -128,8 +125,18 @@ func ReadKey(filename string) (ImageSigKey, error) {
 	return key, nil
 }
 
-func ReadKeys(filenames []string) ([]ImageSigKey, error) {
-	keys := make([]ImageSigKey, len(filenames))
+func ReadKey(filename string) (SignKey, error) {
+	keyBytes, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return SignKey{}, util.FmtNewtError(
+			"Error reading key file: %s", err)
+	}
+
+	return BuildPrivateKey(keyBytes)
+}
+
+func ReadKeys(filenames []string) ([]SignKey, error) {
+	keys := make([]SignKey, len(filenames))
 
 	for i, filename := range filenames {
 		key, err := ReadKey(filename)
@@ -143,7 +150,7 @@ func ReadKeys(filenames []string) ([]ImageSigKey, error) {
 	return keys, nil
 }
 
-func (key *ImageSigKey) assertValid() {
+func (key *SignKey) AssertValid() {
 	if key.Rsa == nil && key.Ec == nil {
 		panic("invalid key; neither RSA nor ECC")
 	}
@@ -153,8 +160,8 @@ func (key *ImageSigKey) assertValid() {
 	}
 }
 
-func (key *ImageSigKey) PubBytes() ([]uint8, error) {
-	key.assertValid()
+func (key *SignKey) PubBytes() ([]uint8, error) {
+	key.AssertValid()
 
 	var pubkey []byte
 
@@ -179,8 +186,8 @@ func RawKeyHash(pubKeyBytes []byte) []byte {
 	return sum[:4]
 }
 
-func (key *ImageSigKey) sigLen() uint16 {
-	key.assertValid()
+func (key *SignKey) SigLen() uint16 {
+	key.AssertValid()
 
 	if key.Rsa != nil {
 		return 256
@@ -196,23 +203,6 @@ func (key *ImageSigKey) sigLen() uint16 {
 	}
 }
 
-func (key *ImageSigKey) sigTlvType() uint8 {
-	key.assertValid()
-
-	if key.Rsa != nil {
-		return IMAGE_TLV_RSA2048
-	} else {
-		switch key.Ec.Curve.Params().Name {
-		case "P-224":
-			return IMAGE_TLV_ECDSA224
-		case "P-256":
-			return IMAGE_TLV_ECDSA256
-		default:
-			return 0
-		}
-	}
-}
-
 func ParsePubKePem(keyBytes []byte) (*rsa.PublicKey, error) {
 	b, _ := pem.Decode(keyBytes)
 	if b == nil {
@@ -297,7 +287,7 @@ func ParseKeBase64(keyBytes []byte) (cipher.Block, error) {
 	return cipher, nil
 }
 
-func encryptSecretAes(c cipher.Block, plainSecret []byte) ([]byte, error) {
+func EncryptSecretAes(c cipher.Block, plainSecret []byte) ([]byte, error) {
 	cipherSecret, err := keywrap.Wrap(c, plainSecret)
 	if err != nil {
 		return nil, util.FmtNewtError("Error key-wrapping: %s", err.Error())
@@ -305,37 +295,3 @@ func encryptSecretAes(c cipher.Block, plainSecret []byte) ([]byte, error) {
 
 	return cipherSecret, nil
 }
-
-func GeneratePlainSecret() ([]byte, error) {
-	plainSecret := make([]byte, 16)
-	if _, err := rand.Read(plainSecret); err != nil {
-		return nil, util.FmtNewtError(
-			"Random generation error: %s\n", err)
-	}
-
-	return plainSecret, nil
-}
-
-func GenerateCipherSecret(pubKeBytes []byte,
-	plainSecret []byte) ([]byte, error) {
-
-	// Try reading as PEM (asymetric key).
-	rsaPubKe, err := ParsePubKePem(pubKeBytes)
-	if err != nil {
-		return nil, err
-	}
-	if rsaPubKe != nil {
-		return EncryptSecretRsa(rsaPubKe, plainSecret)
-	}
-
-	// Not PEM; assume this is a base64 encoded symetric key
-	aesPubKe, err := ParseKeBase64(pubKeBytes)
-	if err != nil {
-		return nil, err
-	}
-	if aesPubKe != nil {
-		return encryptSecretAes(aesPubKe, plainSecret)
-	}
-
-	return nil, util.FmtNewtError("Invalid image-crypt key")
-}
diff --git a/image/encrypted.go b/sec/pkcs.go
similarity index 99%
rename from image/encrypted.go
rename to sec/pkcs.go
index 0547e2f..3cf709f 100644
--- a/image/encrypted.go
+++ b/sec/pkcs.go
@@ -18,7 +18,7 @@
  */
 
 // Decoder for PKCS#5 encrypted PKCS#8 private keys.
-package image
+package sec
 
 import (
 	"crypto/aes"
@@ -78,6 +78,7 @@ type hashFunc func() hash.Hash
 
 func parseEncryptedPrivateKey(der []byte) (key interface{}, err error) {
 	var wrapper pkcs5
+	fmt.Printf("unmarshalling %v\n", der)
 	if _, err = asn1.Unmarshal(der, &wrapper); err != nil {
 		return nil, err
 	}


[mynewt-artifact] 11/23: Add RSA3072 support

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

commit dc98b6ddc9af4a4cb4e69e36bb5b84fd6326b9ad
Author: Fabio Utzig <ut...@apache.org>
AuthorDate: Wed May 8 12:18:39 2019 -0300

    Add RSA3072 support
---
 image/create.go | 10 +++++++++-
 image/image.go  |  3 +++
 sec/key.go      |  3 ++-
 3 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/image/create.go b/image/create.go
index 3ec8770..cbbc410 100644
--- a/image/create.go
+++ b/image/create.go
@@ -71,7 +71,15 @@ func sigTlvType(key sec.SignKey) uint8 {
 	key.AssertValid()
 
 	if key.Rsa != nil {
-		return IMAGE_TLV_RSA2048
+		pubk := key.Rsa.Public().(*rsa.PublicKey)
+		switch pubk.Size() {
+		case 256:
+			return IMAGE_TLV_RSA2048
+		case 384:
+			return IMAGE_TLV_RSA3072
+		default:
+			return 0
+		}
 	} else {
 		switch key.Ec.Curve.Params().Name {
 		case "P-224":
diff --git a/image/image.go b/image/image.go
index 6bd0050..7f74cbc 100644
--- a/image/image.go
+++ b/image/image.go
@@ -63,6 +63,7 @@ const (
 	IMAGE_TLV_RSA2048  = 0x20
 	IMAGE_TLV_ECDSA224 = 0x21
 	IMAGE_TLV_ECDSA256 = 0x22
+	IMAGE_TLV_RSA3072  = 0x23
 	IMAGE_TLV_ENC_RSA  = 0x30
 	IMAGE_TLV_ENC_KEK  = 0x31
 )
@@ -73,6 +74,7 @@ var imageTlvTypeNameMap = map[uint8]string{
 	IMAGE_TLV_RSA2048:  "RSA2048",
 	IMAGE_TLV_ECDSA224: "ECDSA224",
 	IMAGE_TLV_ECDSA256: "ECDSA256",
+	IMAGE_TLV_RSA3072:  "RSA3072",
 	IMAGE_TLV_ENC_RSA:  "ENC_RSA",
 	IMAGE_TLV_ENC_KEK:  "ENC_KEK",
 }
@@ -137,6 +139,7 @@ func ImageTlvTypeName(tlvType uint8) string {
 
 func ImageTlvTypeIsSig(tlvType uint8) bool {
 	return tlvType == IMAGE_TLV_RSA2048 ||
+		tlvType == IMAGE_TLV_RSA3072 ||
 		tlvType == IMAGE_TLV_ECDSA224 ||
 		tlvType == IMAGE_TLV_ECDSA256
 }
diff --git a/sec/key.go b/sec/key.go
index 89b5f49..9d073bd 100644
--- a/sec/key.go
+++ b/sec/key.go
@@ -190,7 +190,8 @@ func (key *SignKey) SigLen() uint16 {
 	key.AssertValid()
 
 	if key.Rsa != nil {
-		return 256
+		pubk := key.Rsa.Public().(*rsa.PublicKey)
+		return uint16(pubk.Size())
 	} else {
 		switch key.Ec.Curve.Params().Name {
 		case "P-224":


[mynewt-artifact] 13/23: Add .travis.yml

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

commit b7033faf34a680cb87664975b0fb707901fa1fc7
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Fri Jun 21 10:21:33 2019 -0700

    Add .travis.yml
---
 .travis.yml | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..77e2d73
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,31 @@
+# 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.
+
+language: go
+
+go:
+  - "1.x"
+
+env:
+  - GO111MODULE=on
+
+before_script:
+  - printenv
+  - go version
+
+script:
+  - go test -v ./...


[mynewt-artifact] 16/23: "Verify" unit tests

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

commit 58017f7dc6efeeee8f900f2ec576550f45a4c04d
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Thu Jun 20 14:00:25 2019 -0700

    "Verify" unit tests
---
 image/image_test.go                          |  199 +++
 image/keys_test.go                           |    4 +-
 image/testdata/bad-hash.img                  |  Bin 0 -> 9412 bytes
 image/testdata/bad-signature.img             |  Bin 0 -> 9680 bytes
 image/testdata/bad-signature.json            | 2267 ++++++++++++++++++++++++++
 image/testdata/garbage.img                   |    1 +
 image/testdata/good-signed.img               |  Bin 0 -> 9680 bytes
 image/testdata/good-signed.json              | 2267 ++++++++++++++++++++++++++
 image/testdata/good-unsigned.img             |  Bin 0 -> 9412 bytes
 image/testdata/good-unsigned.json            | 2267 ++++++++++++++++++++++++++
 image/testdata/mismatch-hash.img             |  Bin 0 -> 9412 bytes
 image/testdata/mismatch-hash.json            | 2267 ++++++++++++++++++++++++++
 image/testdata/mismatch-version.img          |  Bin 0 -> 9412 bytes
 image/testdata/mismatch-version.json         | 2267 ++++++++++++++++++++++++++
 image/testdata/sign-key-pub.der              |  Bin 0 -> 270 bytes
 image/testdata/sign-key.pem                  |   27 +
 image/testdata/truncated.img                 |  Bin 0 -> 9000 bytes
 mfg/mfg_test.go                              |  229 +++
 mfg/testdata/garbage.bin                     |    1 +
 mfg/testdata/garbage.json                    |   78 +
 mfg/testdata/hash1-fm1-ext0-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-ext0-tgts1-sign0.json |   78 +
 mfg/testdata/hash1-fm1-ext1-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-ext1-tgts1-sign0.json |   85 +
 mfg/testdata/hash1-fm1-ext1-tgts1-sign1.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-ext1-tgts1-sign1.json |   91 ++
 mfg/testdata/hash1-fm1-ext1-tgtsm-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-ext1-tgtsm-sign0.json |   92 ++
 mfg/testdata/hash1-fm1-extm-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-extm-tgts1-sign0.json |   85 +
 mfg/testdata/hash1-fmm-ext1-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fmm-ext1-tgts1-sign0.json |   85 +
 mfg/testdata/hashm-fm1-ext0-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hashm-fm1-ext0-tgts1-sign0.json |   78 +
 mfg/testdata/hashx-fm1-ext0-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hashx-fm1-ext0-tgts1-sign0.json |   78 +
 mfg/testdata/sign-key-pub.der                |  Bin 0 -> 270 bytes
 mfg/testdata/sign-key.pem                    |   27 +
 mfg/testdata/unknown-tlv.bin                 |  Bin 0 -> 42448 bytes
 mfg/testdata/unknown-tlv.json                |   78 +
 40 files changed, 12649 insertions(+), 2 deletions(-)

diff --git a/image/image_test.go b/image/image_test.go
new file mode 100644
index 0000000..132e6fb
--- /dev/null
+++ b/image/image_test.go
@@ -0,0 +1,199 @@
+package image
+
+import (
+	"fmt"
+	"io/ioutil"
+	"testing"
+
+	"github.com/apache/mynewt-artifact/manifest"
+	"github.com/apache/mynewt-artifact/sec"
+)
+
+const testdataPath = "testdata"
+
+type entry struct {
+	basename  string
+	form      bool
+	integrity bool
+	man       bool
+	sign      bool
+}
+
+func readImageData(basename string) []byte {
+	path := fmt.Sprintf("%s/%s.img", testdataPath, basename)
+
+	data, err := ioutil.ReadFile(path)
+	if err != nil {
+		panic("failed to read image file " + path)
+	}
+
+	return data
+}
+
+func readManifest(basename string) manifest.Manifest {
+	path := fmt.Sprintf("%s/%s.json", testdataPath, basename)
+
+	man, err := manifest.ReadManifest(path)
+	if err != nil {
+		panic("failed to read manifest file " + path)
+	}
+
+	return man
+}
+
+func readPubKey() sec.PubSignKey {
+	path := fmt.Sprintf("%s/sign-key.pem", testdataPath)
+
+	key, err := sec.ReadKey(path)
+	if err != nil {
+		panic("failed to read key file " + path)
+	}
+
+	return key.PubKey()
+}
+
+func testOne(t *testing.T, e entry) {
+	fatalErr := func(field string, have string, want string, err error) {
+		s := fmt.Sprintf("image \"%s\" has unexpected `%s` status: "+
+			"have=%s want=%s", e.basename, field, have, want)
+		if err != nil {
+			s += "; " + err.Error()
+		}
+
+		t.Fatal(s)
+	}
+
+	imgData := readImageData(e.basename)
+
+	img, err := ParseImage(imgData)
+	if !e.form {
+		if err == nil {
+			fatalErr("form", "good", "bad", nil)
+		}
+		return
+	} else {
+		if err != nil {
+			fatalErr("form", "bad", "good", err)
+			return
+		}
+	}
+
+	err = img.VerifyIntegrity()
+	if !e.integrity {
+		if err == nil {
+			fatalErr("integrity", "good", "bad", nil)
+		}
+		return
+	} else {
+		if err != nil {
+			fatalErr("integrity", "bad", "good", err)
+			return
+		}
+	}
+
+	man := readManifest(e.basename)
+
+	err = img.ValidateManifest(man)
+	if !e.man {
+		if err == nil {
+			fatalErr("manifest", "good", "bad", nil)
+		}
+		return
+	} else {
+		if err != nil {
+			fatalErr("manifest", "bad", "good", err)
+			return
+		}
+	}
+
+	key := readPubKey()
+
+	sigs, err := img.CollectSigs()
+	if err != nil {
+		t.Fatalf("failed to collect image signatures: %s", err.Error())
+		return
+	}
+
+	hash, err := img.Hash()
+	if err != nil {
+		t.Fatalf("failed to read image hash: %s", err.Error())
+		return
+	}
+
+	idx, err := sec.VerifySigs(key, sigs, hash)
+	if !e.sign {
+		if err == nil && idx != -1 {
+			fatalErr("signature", "good", "bad", nil)
+		}
+		return
+	} else {
+		if err != nil || idx == -1 {
+			fatalErr("signature", "bad", "good", err)
+		}
+	}
+}
+
+func TestImageVerify(t *testing.T) {
+	entries := []entry{
+		entry{
+			basename:  "garbage",
+			form:      false,
+			integrity: false,
+			man:       false,
+			sign:      false,
+		},
+		entry{
+			basename:  "truncated",
+			form:      false,
+			integrity: false,
+			man:       false,
+			sign:      false,
+		},
+		entry{
+			basename:  "bad-hash",
+			form:      true,
+			integrity: false,
+			man:       false,
+			sign:      false,
+		},
+		entry{
+			basename:  "mismatch-hash",
+			form:      true,
+			integrity: true,
+			man:       false,
+			sign:      false,
+		},
+		entry{
+			basename:  "mismatch-version",
+			form:      true,
+			integrity: true,
+			man:       false,
+			sign:      false,
+		},
+		entry{
+			basename:  "bad-signature",
+			form:      true,
+			integrity: true,
+			man:       true,
+			sign:      false,
+		},
+		entry{
+			basename:  "good-unsigned",
+			form:      true,
+			integrity: true,
+			man:       true,
+			sign:      false,
+		},
+		entry{
+			basename:  "good-signed",
+			form:      true,
+			integrity: true,
+			man:       true,
+			sign:      true,
+		},
+	}
+
+	for _, e := range entries {
+		testOne(t, e)
+	}
+}
diff --git a/image/keys_test.go b/image/keys_test.go
index 0119098..0953570 100644
--- a/image/keys_test.go
+++ b/image/keys_test.go
@@ -24,8 +24,8 @@ import (
 	"os"
 	"testing"
 
-	"mynewt.apache.org/artifact/image"
-	"mynewt.apache.org/artifact/sec"
+	"github.com/apache/mynewt-artifact/image"
+	"github.com/apache/mynewt-artifact/sec"
 )
 
 func TestRSA(t *testing.T) {
diff --git a/image/testdata/bad-hash.img b/image/testdata/bad-hash.img
new file mode 100644
index 0000000..f2630ee
Binary files /dev/null and b/image/testdata/bad-hash.img differ
diff --git a/image/testdata/bad-signature.img b/image/testdata/bad-signature.img
new file mode 100644
index 0000000..6d532b0
Binary files /dev/null and b/image/testdata/bad-signature.img differ
diff --git a/image/testdata/bad-signature.json b/image/testdata/bad-signature.json
new file mode 100644
index 0000000..7cfcc65
--- /dev/null
+++ b/image/testdata/bad-signature.json
@@ -0,0 +1,2267 @@
+{
+  "name": "targets/blinky-nordic_pca10040",
+  "build_time": "2019-06-17T18:15:11-07:00",
+  "build_version": "1.0.0.0",
+  "id": "8eb006d574ace63cce18a1f2d8f0f2645f1a0e8630a39fb86bbfbb805d4cd3b9",
+  "image": "/Users/ccollins/proj/myproj/bin/targets/blinky-nordic_pca10040/app/apps/blinky/blinky.img",
+  "image_hash": "8eb006d574ace63cce18a1f2d8f0f2645f1a0e8630a39fb86bbfbb805d4cd3b9",
+  "loader": "",
+  "loader_hash": "",
+  "pkgs": [
+    {
+      "name": "apps/blinky",
+      "repo": "my_project"
+    },
+    {
+      "name": "blinky-nordic_pca10040-sysinit-app",
+      "repo": "my_project"
+    },
+    {
+      "name": "@apache-mynewt-core/compiler/arm-none-eabi-m4",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/bsp/nordic_pca10040",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/cmsis-core",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart/uart_hal",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/hal",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/mcu/nordic",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/mcu/nordic/nrf52xxx",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/kernel/os",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/libc/baselibc",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/console/stub",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/defs",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/flash_map",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/common",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/modlog",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/stub",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/mfg",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sys",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sysdown",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sysinit",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "targets/blinky-nordic_pca10040",
+      "repo": "my_project"
+    },
+    {
+      "name": "@apache-mynewt-core/util/mem",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/util/rwlock",
+      "repo": "apache-mynewt-core"
+    }
+  ],
+  "target": [
+    "target.app=apps/blinky",
+    "target.bsp=@apache-mynewt-core/hw/bsp/nordic_pca10040",
+    "target.build_profile=optimized"
+  ],
+  "repos": [
+    {
+      "name": "apache-mynewt-core",
+      "commit": "fa5e4c31b0bea2b107747e3b0d40002d3bef4132",
+      "url": "git@github.com:apache/mynewt-core.git"
+    },
+    {
+      "name": "my_project",
+      "commit": "UNKNOWN"
+    }
+  ],
+  "syscfg": {
+    "ADC_0": "0",
+    "ADC_0_REFMV_0": "0",
+    "APP_NAME": "\"blinky\"",
+    "APP_blinky": "1",
+    "ARCH_NAME": "\"cortex_m4\"",
+    "ARCH_cortex_m4": "1",
+    "BASELIBC_ASSERT_FILE_LINE": "0",
+    "BASELIBC_PRESENT": "1",
+    "BSP_NAME": "\"nordic_pca10040\"",
+    "BSP_NRF52": "1",
+    "BSP_nordic_pca10040": "1",
+    "CONSOLE_UART_BAUD": "115200",
+    "CONSOLE_UART_DEV": "\"uart0\"",
+    "CONSOLE_UART_FLOW_CONTROL": "UART_FLOW_CTL_NONE",
+    "CRYPTO": "0",
+    "DEBUG_PANIC_ENABLED": "1",
+    "ENC_FLASH_DEV": "0",
+    "FLASH_MAP_MAX_AREAS": "10",
+    "FLASH_MAP_SYSINIT_STAGE": "2",
+    "FLOAT_USER": "0",
+    "GPIO_AS_PIN_RESET": "0",
+    "HAL_FLASH_VERIFY_BUF_SZ": "16",
+    "HAL_FLASH_VERIFY_ERASES": "0",
+    "HAL_FLASH_VERIFY_WRITES": "0",
+    "HAL_SYSTEM_RESET_CB": "0",
+    "HARDFLOAT": "0",
+    "I2C_0": "0",
+    "I2C_0_FREQ_KHZ": "100",
+    "I2C_0_PIN_SCL": "27",
+    "I2C_0_PIN_SDA": "26",
+    "I2C_1": "0",
+    "I2C_1_FREQ_KHZ": "100",
+    "I2C_1_PIN_SCL": "",
+    "I2C_1_PIN_SDA": "",
+    "LOG_CONSOLE": "1",
+    "LOG_FCB": "0",
+    "LOG_FCB_SLOT1": "0",
+    "LOG_LEVEL": "255",
+    "MCU_BUS_DRIVER_I2C_USE_TWIM": "0",
+    "MCU_DCDC_ENABLED": "1",
+    "MCU_DEBUG_IGNORE_BKPT": "0",
+    "MCU_FLASH_MIN_WRITE_SIZE": "1",
+    "MCU_GPIO_USE_PORT_EVENT": "0",
+    "MCU_I2C_RECOVERY_DELAY_USEC": "100",
+    "MCU_LFCLK_SOURCE": "LFXO",
+    "MCU_NRF52832": "0",
+    "MCU_NRF52840": "0",
+    "MCU_TARGET": "nRF52832",
+    "MFG_LOG_MODULE": "128",
+    "MFG_MAX_MMRS": "2",
+    "MFG_SYSINIT_STAGE": "100",
+    "MODLOG_CONSOLE_DFLT": "1",
+    "MODLOG_LOG_MACROS": "0",
+    "MODLOG_MAX_MAPPINGS": "16",
+    "MODLOG_MAX_PRINTF_LEN": "128",
+    "MODLOG_SYSINIT_STAGE": "100",
+    "MSYS_1_BLOCK_COUNT": "12",
+    "MSYS_1_BLOCK_SIZE": "292",
+    "MSYS_1_SANITY_MIN_COUNT": "0",
+    "MSYS_2_BLOCK_COUNT": "0",
+    "MSYS_2_BLOCK_SIZE": "0",
+    "MSYS_2_SANITY_MIN_COUNT": "0",
+    "MSYS_SANITY_TIMEOUT": "60000",
+    "NEWT_FEATURE_LOGCFG": "1",
+    "NEWT_FEATURE_SYSDOWN": "1",
+    "NFC_PINS_AS_GPIO": "1",
+    "OS_ASSERT_CB": "0",
+    "OS_CLI": "0",
+    "OS_COREDUMP": "0",
+    "OS_CPUTIME_FREQ": "1000000",
+    "OS_CPUTIME_TIMER_NUM": "0",
+    "OS_CRASH_FILE_LINE": "0",
+    "OS_CRASH_LOG": "0",
+    "OS_CRASH_RESTORE_REGS": "0",
+    "OS_CRASH_STACKTRACE": "0",
+    "OS_CTX_SW_STACK_CHECK": "0",
+    "OS_CTX_SW_STACK_GUARD": "4",
+    "OS_DEBUG_MODE": "0",
+    "OS_EVENTQ_DEBUG": "0",
+    "OS_EVENTQ_MONITOR": "0",
+    "OS_IDLE_TICKLESS_MS_MAX": "600000",
+    "OS_IDLE_TICKLESS_MS_MIN": "100",
+    "OS_MAIN_STACK_SIZE": "1024",
+    "OS_MAIN_TASK_PRIO": "127",
+    "OS_MAIN_TASK_SANITY_ITVL_MS": "0",
+    "OS_MEMPOOL_CHECK": "0",
+    "OS_MEMPOOL_GUARD": "0",
+    "OS_MEMPOOL_POISON": "0",
+    "OS_SCHEDULING": "1",
+    "OS_SYSINIT_STAGE": "0",
+    "OS_SYSVIEW": "0",
+    "OS_SYSVIEW_TRACE_CALLOUT": "1",
+    "OS_SYSVIEW_TRACE_EVENTQ": "1",
+    "OS_SYSVIEW_TRACE_MBUF": "0",
+    "OS_SYSVIEW_TRACE_MEMPOOL": "0",
+    "OS_SYSVIEW_TRACE_MUTEX": "1",
+    "OS_SYSVIEW_TRACE_SEM": "1",
+    "OS_TIME_DEBUG": "0",
+    "OS_WATCHDOG_MONITOR": "0",
+    "PWM_0": "0",
+    "PWM_1": "0",
+    "PWM_2": "0",
+    "PWM_3": "0",
+    "QSPI_ADDRMODE": "0",
+    "QSPI_DPMCONFIG": "0",
+    "QSPI_ENABLE": "0",
+    "QSPI_FLASH_PAGE_SIZE": "0",
+    "QSPI_FLASH_SECTOR_COUNT": "-1",
+    "QSPI_FLASH_SECTOR_SIZE": "0",
+    "QSPI_PIN_CS": "-1",
+    "QSPI_PIN_DIO0": "-1",
+    "QSPI_PIN_DIO1": "-1",
+    "QSPI_PIN_DIO2": "-1",
+    "QSPI_PIN_DIO3": "-1",
+    "QSPI_PIN_SCK": "-1",
+    "QSPI_READOC": "0",
+    "QSPI_SCK_DELAY": "0",
+    "QSPI_SCK_FREQ": "0",
+    "QSPI_SPI_MODE": "0",
+    "QSPI_WRITEOC": "0",
+    "RAM_RESIDENT": "0",
+    "RWLOCK_DEBUG": "0",
+    "SANITY_INTERVAL": "15000",
+    "SOFT_PWM": "0",
+    "SPI_0_MASTER": "0",
+    "SPI_0_MASTER_PIN_MISO": "25",
+    "SPI_0_MASTER_PIN_MOSI": "24",
+    "SPI_0_MASTER_PIN_SCK": "23",
+    "SPI_0_SLAVE": "0",
+    "SPI_0_SLAVE_PIN_MISO": "25",
+    "SPI_0_SLAVE_PIN_MOSI": "24",
+    "SPI_0_SLAVE_PIN_SCK": "23",
+    "SPI_0_SLAVE_PIN_SS": "22",
+    "SPI_1_MASTER": "0",
+    "SPI_1_MASTER_PIN_MISO": "",
+    "SPI_1_MASTER_PIN_MOSI": "",
+    "SPI_1_MASTER_PIN_SCK": "",
+    "SPI_1_SLAVE": "0",
+    "SPI_1_SLAVE_PIN_MISO": "",
+    "SPI_1_SLAVE_PIN_MOSI": "",
+    "SPI_1_SLAVE_PIN_SCK": "",
+    "SPI_1_SLAVE_PIN_SS": "",
+    "SPI_2_MASTER": "0",
+    "SPI_2_MASTER_PIN_MISO": "",
+    "SPI_2_MASTER_PIN_MOSI": "",
+    "SPI_2_MASTER_PIN_SCK": "",
+    "SPI_2_SLAVE": "0",
+    "SPI_2_SLAVE_PIN_MISO": "",
+    "SPI_2_SLAVE_PIN_MOSI": "",
+    "SPI_2_SLAVE_PIN_SCK": "",
+    "SPI_2_SLAVE_PIN_SS": "",
+    "SPI_3_MASTER": "0",
+    "SPI_3_MASTER_PIN_MISO": "",
+    "SPI_3_MASTER_PIN_MOSI": "",
+    "SPI_3_MASTER_PIN_SCK": "",
+    "SPI_3_SLAVE": "0",
+    "SPI_3_SLAVE_PIN_MISO": "",
+    "SPI_3_SLAVE_PIN_MOSI": "",
+    "SPI_3_SLAVE_PIN_SCK": "",
+    "SPI_3_SLAVE_PIN_SS": "",
+    "SYSDOWN_CONSTRAIN_DOWN": "1",
+    "SYSDOWN_PANIC_FILE_LINE": "0",
+    "SYSDOWN_PANIC_MESSAGE": "0",
+    "SYSDOWN_TIMEOUT_MS": "10000",
+    "SYSINIT_CONSTRAIN_INIT": "1",
+    "SYSINIT_PANIC_FILE_LINE": "0",
+    "SYSINIT_PANIC_MESSAGE": "0",
+    "TARGET_NAME": "\"blinky-nordic_pca10040\"",
+    "TARGET_blinky_nordic_pca10040": "1",
+    "TIMER_0": "1",
+    "TIMER_1": "0",
+    "TIMER_2": "0",
+    "TIMER_3": "0",
+    "TIMER_4": "0",
+    "TIMER_5": "0",
+    "TRNG": "0",
+    "UARTBB_0": "0",
+    "UARTBB_0_PIN_RX": "-1",
+    "UARTBB_0_PIN_TX": "-1",
+    "UART_0": "1",
+    "UART_0_PIN_CTS": "7",
+    "UART_0_PIN_RTS": "5",
+    "UART_0_PIN_RX": "8",
+    "UART_0_PIN_TX": "6",
+    "UART_1": "0",
+    "UART_1_PIN_CTS": "-1",
+    "UART_1_PIN_RTS": "-1",
+    "UART_1_PIN_RX": "",
+    "UART_1_PIN_TX": "",
+    "WATCHDOG_INTERVAL": "30000",
+    "XTAL_32768": "0",
+    "XTAL_32768_SYNTH": "0",
+    "XTAL_RC": "0"
+  },
+  "pkgsz": [
+    {
+      "name": "*fill*",
+      "files": [
+        {
+          "name": "",
+          "sym": [
+            {
+              "name": "*fill*",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 63
+                }
+              ]
+            },
+            {
+              "name": "*fill*",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 233
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "apps/blinky",
+      "files": [
+        {
+          "name": "main.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "g_task1_loops",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "startup",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 56
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/bsp/nordic_pca10040",
+      "files": [
+        {
+          "name": "sbrk.o",
+          "sym": [
+            {
+              "name": ".data",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "_sbrkInit",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "gcc_startup_nrf52.o",
+          "sym": [
+            {
+              "name": ".isr_vector",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 216
+                }
+              ]
+            },
+            {
+              "name": ".stack",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 432
+                }
+              ]
+            },
+            {
+              "name": ".text",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 112
+                }
+              ]
+            },
+            {
+              "name": "exidx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 8
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_bsp.o",
+          "sym": [
+            {
+              "name": "hal_bsp_flash_dev",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "hal_bsp_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/cmsis-core",
+      "files": [
+        {
+          "name": "cmsis_nvic.o",
+          "sym": [
+            {
+              "name": "NVIC_Relocate",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart/uart_hal",
+      "files": [
+        {
+          "name": "uart_hal.o",
+          "sym": [
+            {
+              "name": "uart_hal_blocking_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_close",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_open",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 114
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_resume",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 44
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_start_rx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_start_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_suspend",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "unlikely",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/hal",
+      "files": [
+        {
+          "name": "hal_flash.o",
+          "sym": [
+            {
+              "name": "hal_flash_check_addr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "hal_flash_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "hal_flash_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 68
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/mcu/nordic/nrf52xxx",
+      "files": [
+        {
+          "name": "hal_os_tick.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "nrf52_os_tick_set_ocmp",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            },
+            {
+              "name": "nrf52_timer_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 80
+                }
+              ]
+            },
+            {
+              "name": "os_tick_idle",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 68
+                }
+              ]
+            },
+            {
+              "name": "os_tick_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 148
+                }
+              ]
+            },
+            {
+              "name": "sub24",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 46
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "system_nrf52.o",
+          "sym": [
+            {
+              "name": "SystemCoreClock",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "SystemInit",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 480
+                }
+              ]
+            },
+            {
+              "name": "errata_16",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 52
+                }
+              ]
+            },
+            {
+              "name": "errata_31",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_timer.o",
+          "sym": [
+            {
+              "name": "__NVIC_SetPendingIRQ",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "hal_timer_config",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 212
+                }
+              ]
+            },
+            {
+              "name": "hal_timer_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 88
+                }
+              ]
+            },
+            {
+              "name": "hal_timer_read_bsptimer",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 56
+                }
+              ]
+            },
+            {
+              "name": "nrf52_hal_timers",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "nrf52_timer0_irq_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 168
+                }
+              ]
+            },
+            {
+              "name": "nrf_timer_set_ocmp",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 126
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_system.o",
+          "sym": [
+            {
+              "name": "hal_debugger_connected",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "hal_system_clock_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "hal_system_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "hal_system_reset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_gpio.o",
+          "sym": [
+            {
+              "name": "hal_gpio_init_out",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 38
+                }
+              ]
+            },
+            {
+              "name": "hal_gpio_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 34
+                }
+              ]
+            },
+            {
+              "name": "hal_gpio_toggle",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 26
+                }
+              ]
+            },
+            {
+              "name": "hal_gpio_write",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_uart.o",
+          "sym": [
+            {
+              "name": "hal_uart_blocking_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 80
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_close",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 52
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_config",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 408
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_init_cbs",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_start_rx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 56
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_start_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 72
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_tx_fill_buf",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "uart0",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "uart0_irq_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 128
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_watchdog.o",
+          "sym": [
+            {
+              "name": "hal_watchdog_enable",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "hal_watchdog_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 112
+                }
+              ]
+            },
+            {
+              "name": "hal_watchdog_tickle",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "nrf52_wdt_irq_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "nrf52_periph.o",
+          "sym": [
+            {
+              "name": "nrf52_periph_create",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 90
+                }
+              ]
+            },
+            {
+              "name": "os_bsp_uart0",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "os_bsp_uart0_cfg",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 4
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_flash.o",
+          "sym": [
+            {
+              "name": "nrf52k_flash_dev",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_erase_sector",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 76
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_funcs",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_sector_info",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_wait_ready",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_write",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 200
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/kernel/os",
+      "files": [
+        {
+          "name": "HAL_CM4.o",
+          "sym": [
+            {
+              "name": ".text",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 200
+                }
+              ]
+            },
+            {
+              "name": "exidx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 217
+                }
+              ]
+            },
+            {
+              "name": "g_idle_task_stack",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 256
+                }
+              ]
+            },
+            {
+              "name": "g_os_main_stack",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4096
+                }
+              ]
+            },
+            {
+              "name": "os_idle_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 124
+                }
+              ]
+            },
+            {
+              "name": "os_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 149
+                }
+              ]
+            },
+            {
+              "name": "os_init_idle_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 93
+                }
+              ]
+            },
+            {
+              "name": "os_main",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 22
+                }
+              ]
+            },
+            {
+              "name": "os_pkg_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            },
+            {
+              "name": "os_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 26
+                }
+              ]
+            },
+            {
+              "name": "os_started",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_fault.o",
+          "sym": [
+            {
+              "name": "__assert_func",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "os_default_irq",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_time.o",
+          "sym": [
+            {
+              "name": "basetod",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 48
+                }
+              ]
+            },
+            {
+              "name": "os_deltatime",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 76
+                }
+              ]
+            },
+            {
+              "name": "os_time_advance",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 120
+                }
+              ]
+            },
+            {
+              "name": "os_time_delay",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "os_time_get",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_msys.o",
+          "sym": [
+            {
+              "name": "g_msys_pool_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_msys_1_data",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 3504
+                }
+              ]
+            },
+            {
+              "name": "os_msys_1_mbuf_pool",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_msys_1_mempool",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "os_msys_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 103
+                }
+              ]
+            },
+            {
+              "name": "os_msys_register",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            },
+            {
+              "name": "os_msys_reset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_dev.o",
+          "sym": [
+            {
+              "name": "g_os_dev_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_dev_create",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 136
+                }
+              ]
+            },
+            {
+              "name": "os_dev_initialize",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "os_dev_initialize_all",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "os_dev_reset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_sched.o",
+          "sym": [
+            {
+              "name": "g_os_run_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "g_os_sleep_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_sched",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "os_sched_ctx_sw_hook",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 44
+                }
+              ]
+            },
+            {
+              "name": "os_sched_get_current_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_sched_insert",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 76
+                }
+              ]
+            },
+            {
+              "name": "os_sched_next_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_sched_os_timer_exp",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "os_sched_resort",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "os_sched_set_current_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_sched_sleep",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 116
+                }
+              ]
+            },
+            {
+              "name": "os_sched_wakeup",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            },
+            {
+              "name": "os_sched_wakeup_ticks",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 52
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_sanity.o",
+          "sym": [
+            {
+              "name": "g_os_sanity_check_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_list_lock",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_list_unlock",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_register",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_run",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 80
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_arch_arm.o",
+          "sym": [
+            {
+              "name": "os_arch_ctx_sw",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "os_arch_in_critical",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 10
+                }
+              ]
+            },
+            {
+              "name": "os_arch_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "os_arch_os_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 148
+                }
+              ]
+            },
+            {
+              "name": "os_arch_os_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 84
+                }
+              ]
+            },
+            {
+              "name": "os_arch_restore_sr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "os_arch_save_sr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_arch_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            },
+            {
+              "name": "os_arch_task_stack_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 54
+                }
+              ]
+            },
+            {
+              "name": "os_flags",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "timer_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_callout.o",
+          "sym": [
+            {
+              "name": "os_callout_tick",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 84
+                }
+              ]
+            },
+            {
+              "name": "os_callout_wakeup_ticks",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_cputime.o",
+          "sym": [
+            {
+              "name": "os_cputime_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 8
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_eventq.o",
+          "sym": [
+            {
+              "name": "os_eventq_dflt_get",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_eventq_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "os_eventq_main",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "os_eventq_put",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_mbuf.o",
+          "sym": [
+            {
+              "name": "os_mbuf_pool_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 10
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_mempool.o",
+          "sym": [
+            {
+              "name": "os_mempool_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 18
+                }
+              ]
+            },
+            {
+              "name": "os_mempool_init_internal",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            },
+            {
+              "name": "os_mempool_module_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_mutex.o",
+          "sym": [
+            {
+              "name": "os_mutex_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "os_mutex_pend",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 220
+                }
+              ]
+            },
+            {
+              "name": "os_mutex_release",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 160
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_task.o",
+          "sym": [
+            {
+              "name": "os_task_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 228
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/libc/baselibc",
+      "files": [
+        {
+          "name": "start.o",
+          "sym": [
+            {
+              "name": "_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 2
+                }
+              ]
+            },
+            {
+              "name": "_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "memcpy.o",
+          "sym": [
+            {
+              "name": "memcpy",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 26
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "memset.o",
+          "sym": [
+            {
+              "name": "memset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "strlen.o",
+          "sym": [
+            {
+              "name": "strlen",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/flash_map",
+      "files": [
+        {
+          "name": "flash_map.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "flash_area_open",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "flash_area_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "flash_map_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 172
+                }
+              ]
+            },
+            {
+              "name": "mfg_areas",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 120
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/modlog",
+      "files": [
+        {
+          "name": "modlog.o",
+          "sym": [
+            {
+              "name": "modlog_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 2
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/mfg",
+      "files": [
+        {
+          "name": "mfg.o",
+          "sym": [
+            {
+              "name": "mfg_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 92
+                }
+              ]
+            },
+            {
+              "name": "mfg_initialized",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 1
+                }
+              ]
+            },
+            {
+              "name": "mfg_mmrs",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "mfg_num_mmrs",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "mfg_open",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_next_mmr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 164
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_tlv_body",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 100
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_tlv_flash_area",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_tlv_mmr_ref",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "mfg_seek_next",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 128
+                }
+              ]
+            },
+            {
+              "name": "mfg_seek_next_with_type",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 22
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sysinit",
+      "files": [
+        {
+          "name": "sysinit.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 1
+                }
+              ]
+            },
+            {
+              "name": "sysinit_dflt_panic_cb",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "sysinit_end",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "sysinit_panic_cb",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "sysinit_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/util/mem",
+      "files": [
+        {
+          "name": "mem.o",
+          "sym": [
+            {
+              "name": "mem_init_mbuf_pool",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 50
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "blinky-nordic_pca10040-sysinit-app",
+      "files": [
+        {
+          "name": "blinky-nordic_pca10040-sysflash.o",
+          "sym": [
+            {
+              "name": "sysflash_map_dflt",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 72
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "blinky-nordic_pca10040-sysinit-app.o",
+          "sym": [
+            {
+              "name": "sysinit_app",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 22
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/image/testdata/garbage.img b/image/testdata/garbage.img
new file mode 100644
index 0000000..ce01362
--- /dev/null
+++ b/image/testdata/garbage.img
@@ -0,0 +1 @@
+hello
diff --git a/image/testdata/good-signed.img b/image/testdata/good-signed.img
new file mode 100644
index 0000000..5c34fe8
Binary files /dev/null and b/image/testdata/good-signed.img differ
diff --git a/image/testdata/good-signed.json b/image/testdata/good-signed.json
new file mode 100644
index 0000000..7cfcc65
--- /dev/null
+++ b/image/testdata/good-signed.json
@@ -0,0 +1,2267 @@
+{
+  "name": "targets/blinky-nordic_pca10040",
+  "build_time": "2019-06-17T18:15:11-07:00",
+  "build_version": "1.0.0.0",
+  "id": "8eb006d574ace63cce18a1f2d8f0f2645f1a0e8630a39fb86bbfbb805d4cd3b9",
+  "image": "/Users/ccollins/proj/myproj/bin/targets/blinky-nordic_pca10040/app/apps/blinky/blinky.img",
+  "image_hash": "8eb006d574ace63cce18a1f2d8f0f2645f1a0e8630a39fb86bbfbb805d4cd3b9",
+  "loader": "",
+  "loader_hash": "",
+  "pkgs": [
+    {
+      "name": "apps/blinky",
+      "repo": "my_project"
+    },
+    {
+      "name": "blinky-nordic_pca10040-sysinit-app",
+      "repo": "my_project"
+    },
+    {
+      "name": "@apache-mynewt-core/compiler/arm-none-eabi-m4",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/bsp/nordic_pca10040",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/cmsis-core",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart/uart_hal",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/hal",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/mcu/nordic",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/mcu/nordic/nrf52xxx",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/kernel/os",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/libc/baselibc",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/console/stub",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/defs",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/flash_map",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/common",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/modlog",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/stub",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/mfg",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sys",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sysdown",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sysinit",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "targets/blinky-nordic_pca10040",
+      "repo": "my_project"
+    },
+    {
+      "name": "@apache-mynewt-core/util/mem",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/util/rwlock",
+      "repo": "apache-mynewt-core"
+    }
+  ],
+  "target": [
+    "target.app=apps/blinky",
+    "target.bsp=@apache-mynewt-core/hw/bsp/nordic_pca10040",
+    "target.build_profile=optimized"
+  ],
+  "repos": [
+    {
+      "name": "apache-mynewt-core",
+      "commit": "fa5e4c31b0bea2b107747e3b0d40002d3bef4132",
+      "url": "git@github.com:apache/mynewt-core.git"
+    },
+    {
+      "name": "my_project",
+      "commit": "UNKNOWN"
+    }
+  ],
+  "syscfg": {
+    "ADC_0": "0",
+    "ADC_0_REFMV_0": "0",
+    "APP_NAME": "\"blinky\"",
+    "APP_blinky": "1",
+    "ARCH_NAME": "\"cortex_m4\"",
+    "ARCH_cortex_m4": "1",
+    "BASELIBC_ASSERT_FILE_LINE": "0",
+    "BASELIBC_PRESENT": "1",
+    "BSP_NAME": "\"nordic_pca10040\"",
+    "BSP_NRF52": "1",
+    "BSP_nordic_pca10040": "1",
+    "CONSOLE_UART_BAUD": "115200",
+    "CONSOLE_UART_DEV": "\"uart0\"",
+    "CONSOLE_UART_FLOW_CONTROL": "UART_FLOW_CTL_NONE",
+    "CRYPTO": "0",
+    "DEBUG_PANIC_ENABLED": "1",
+    "ENC_FLASH_DEV": "0",
+    "FLASH_MAP_MAX_AREAS": "10",
+    "FLASH_MAP_SYSINIT_STAGE": "2",
+    "FLOAT_USER": "0",
+    "GPIO_AS_PIN_RESET": "0",
+    "HAL_FLASH_VERIFY_BUF_SZ": "16",
+    "HAL_FLASH_VERIFY_ERASES": "0",
+    "HAL_FLASH_VERIFY_WRITES": "0",
+    "HAL_SYSTEM_RESET_CB": "0",
+    "HARDFLOAT": "0",
+    "I2C_0": "0",
+    "I2C_0_FREQ_KHZ": "100",
+    "I2C_0_PIN_SCL": "27",
+    "I2C_0_PIN_SDA": "26",
+    "I2C_1": "0",
+    "I2C_1_FREQ_KHZ": "100",
+    "I2C_1_PIN_SCL": "",
+    "I2C_1_PIN_SDA": "",
+    "LOG_CONSOLE": "1",
+    "LOG_FCB": "0",
+    "LOG_FCB_SLOT1": "0",
+    "LOG_LEVEL": "255",
+    "MCU_BUS_DRIVER_I2C_USE_TWIM": "0",
+    "MCU_DCDC_ENABLED": "1",
+    "MCU_DEBUG_IGNORE_BKPT": "0",
+    "MCU_FLASH_MIN_WRITE_SIZE": "1",
+    "MCU_GPIO_USE_PORT_EVENT": "0",
+    "MCU_I2C_RECOVERY_DELAY_USEC": "100",
+    "MCU_LFCLK_SOURCE": "LFXO",
+    "MCU_NRF52832": "0",
+    "MCU_NRF52840": "0",
+    "MCU_TARGET": "nRF52832",
+    "MFG_LOG_MODULE": "128",
+    "MFG_MAX_MMRS": "2",
+    "MFG_SYSINIT_STAGE": "100",
+    "MODLOG_CONSOLE_DFLT": "1",
+    "MODLOG_LOG_MACROS": "0",
+    "MODLOG_MAX_MAPPINGS": "16",
+    "MODLOG_MAX_PRINTF_LEN": "128",
+    "MODLOG_SYSINIT_STAGE": "100",
+    "MSYS_1_BLOCK_COUNT": "12",
+    "MSYS_1_BLOCK_SIZE": "292",
+    "MSYS_1_SANITY_MIN_COUNT": "0",
+    "MSYS_2_BLOCK_COUNT": "0",
+    "MSYS_2_BLOCK_SIZE": "0",
+    "MSYS_2_SANITY_MIN_COUNT": "0",
+    "MSYS_SANITY_TIMEOUT": "60000",
+    "NEWT_FEATURE_LOGCFG": "1",
+    "NEWT_FEATURE_SYSDOWN": "1",
+    "NFC_PINS_AS_GPIO": "1",
+    "OS_ASSERT_CB": "0",
+    "OS_CLI": "0",
+    "OS_COREDUMP": "0",
+    "OS_CPUTIME_FREQ": "1000000",
+    "OS_CPUTIME_TIMER_NUM": "0",
+    "OS_CRASH_FILE_LINE": "0",
+    "OS_CRASH_LOG": "0",
+    "OS_CRASH_RESTORE_REGS": "0",
+    "OS_CRASH_STACKTRACE": "0",
+    "OS_CTX_SW_STACK_CHECK": "0",
+    "OS_CTX_SW_STACK_GUARD": "4",
+    "OS_DEBUG_MODE": "0",
+    "OS_EVENTQ_DEBUG": "0",
+    "OS_EVENTQ_MONITOR": "0",
+    "OS_IDLE_TICKLESS_MS_MAX": "600000",
+    "OS_IDLE_TICKLESS_MS_MIN": "100",
+    "OS_MAIN_STACK_SIZE": "1024",
+    "OS_MAIN_TASK_PRIO": "127",
+    "OS_MAIN_TASK_SANITY_ITVL_MS": "0",
+    "OS_MEMPOOL_CHECK": "0",
+    "OS_MEMPOOL_GUARD": "0",
+    "OS_MEMPOOL_POISON": "0",
+    "OS_SCHEDULING": "1",
+    "OS_SYSINIT_STAGE": "0",
+    "OS_SYSVIEW": "0",
+    "OS_SYSVIEW_TRACE_CALLOUT": "1",
+    "OS_SYSVIEW_TRACE_EVENTQ": "1",
+    "OS_SYSVIEW_TRACE_MBUF": "0",
+    "OS_SYSVIEW_TRACE_MEMPOOL": "0",
+    "OS_SYSVIEW_TRACE_MUTEX": "1",
+    "OS_SYSVIEW_TRACE_SEM": "1",
+    "OS_TIME_DEBUG": "0",
+    "OS_WATCHDOG_MONITOR": "0",
+    "PWM_0": "0",
+    "PWM_1": "0",
+    "PWM_2": "0",
+    "PWM_3": "0",
+    "QSPI_ADDRMODE": "0",
+    "QSPI_DPMCONFIG": "0",
+    "QSPI_ENABLE": "0",
+    "QSPI_FLASH_PAGE_SIZE": "0",
+    "QSPI_FLASH_SECTOR_COUNT": "-1",
+    "QSPI_FLASH_SECTOR_SIZE": "0",
+    "QSPI_PIN_CS": "-1",
+    "QSPI_PIN_DIO0": "-1",
+    "QSPI_PIN_DIO1": "-1",
+    "QSPI_PIN_DIO2": "-1",
+    "QSPI_PIN_DIO3": "-1",
+    "QSPI_PIN_SCK": "-1",
+    "QSPI_READOC": "0",
+    "QSPI_SCK_DELAY": "0",
+    "QSPI_SCK_FREQ": "0",
+    "QSPI_SPI_MODE": "0",
+    "QSPI_WRITEOC": "0",
+    "RAM_RESIDENT": "0",
+    "RWLOCK_DEBUG": "0",
+    "SANITY_INTERVAL": "15000",
+    "SOFT_PWM": "0",
+    "SPI_0_MASTER": "0",
+    "SPI_0_MASTER_PIN_MISO": "25",
+    "SPI_0_MASTER_PIN_MOSI": "24",
+    "SPI_0_MASTER_PIN_SCK": "23",
+    "SPI_0_SLAVE": "0",
+    "SPI_0_SLAVE_PIN_MISO": "25",
+    "SPI_0_SLAVE_PIN_MOSI": "24",
+    "SPI_0_SLAVE_PIN_SCK": "23",
+    "SPI_0_SLAVE_PIN_SS": "22",
+    "SPI_1_MASTER": "0",
+    "SPI_1_MASTER_PIN_MISO": "",
+    "SPI_1_MASTER_PIN_MOSI": "",
+    "SPI_1_MASTER_PIN_SCK": "",
+    "SPI_1_SLAVE": "0",
+    "SPI_1_SLAVE_PIN_MISO": "",
+    "SPI_1_SLAVE_PIN_MOSI": "",
+    "SPI_1_SLAVE_PIN_SCK": "",
+    "SPI_1_SLAVE_PIN_SS": "",
+    "SPI_2_MASTER": "0",
+    "SPI_2_MASTER_PIN_MISO": "",
+    "SPI_2_MASTER_PIN_MOSI": "",
+    "SPI_2_MASTER_PIN_SCK": "",
+    "SPI_2_SLAVE": "0",
+    "SPI_2_SLAVE_PIN_MISO": "",
+    "SPI_2_SLAVE_PIN_MOSI": "",
+    "SPI_2_SLAVE_PIN_SCK": "",
+    "SPI_2_SLAVE_PIN_SS": "",
+    "SPI_3_MASTER": "0",
+    "SPI_3_MASTER_PIN_MISO": "",
+    "SPI_3_MASTER_PIN_MOSI": "",
+    "SPI_3_MASTER_PIN_SCK": "",
+    "SPI_3_SLAVE": "0",
+    "SPI_3_SLAVE_PIN_MISO": "",
+    "SPI_3_SLAVE_PIN_MOSI": "",
+    "SPI_3_SLAVE_PIN_SCK": "",
+    "SPI_3_SLAVE_PIN_SS": "",
+    "SYSDOWN_CONSTRAIN_DOWN": "1",
+    "SYSDOWN_PANIC_FILE_LINE": "0",
+    "SYSDOWN_PANIC_MESSAGE": "0",
+    "SYSDOWN_TIMEOUT_MS": "10000",
+    "SYSINIT_CONSTRAIN_INIT": "1",
+    "SYSINIT_PANIC_FILE_LINE": "0",
+    "SYSINIT_PANIC_MESSAGE": "0",
+    "TARGET_NAME": "\"blinky-nordic_pca10040\"",
+    "TARGET_blinky_nordic_pca10040": "1",
+    "TIMER_0": "1",
+    "TIMER_1": "0",
+    "TIMER_2": "0",
+    "TIMER_3": "0",
+    "TIMER_4": "0",
+    "TIMER_5": "0",
+    "TRNG": "0",
+    "UARTBB_0": "0",
+    "UARTBB_0_PIN_RX": "-1",
+    "UARTBB_0_PIN_TX": "-1",
+    "UART_0": "1",
+    "UART_0_PIN_CTS": "7",
+    "UART_0_PIN_RTS": "5",
+    "UART_0_PIN_RX": "8",
+    "UART_0_PIN_TX": "6",
+    "UART_1": "0",
+    "UART_1_PIN_CTS": "-1",
+    "UART_1_PIN_RTS": "-1",
+    "UART_1_PIN_RX": "",
+    "UART_1_PIN_TX": "",
+    "WATCHDOG_INTERVAL": "30000",
+    "XTAL_32768": "0",
+    "XTAL_32768_SYNTH": "0",
+    "XTAL_RC": "0"
+  },
+  "pkgsz": [
+    {
+      "name": "*fill*",
+      "files": [
+        {
+          "name": "",
+          "sym": [
+            {
+              "name": "*fill*",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 63
+                }
+              ]
+            },
+            {
+              "name": "*fill*",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 233
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "apps/blinky",
+      "files": [
+        {
+          "name": "main.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "g_task1_loops",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "startup",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 56
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/bsp/nordic_pca10040",
+      "files": [
+        {
+          "name": "sbrk.o",
+          "sym": [
+            {
+              "name": ".data",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "_sbrkInit",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "gcc_startup_nrf52.o",
+          "sym": [
+            {
+              "name": ".isr_vector",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 216
+                }
+              ]
+            },
+            {
+              "name": ".stack",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 432
+                }
+              ]
+            },
+            {
+              "name": ".text",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 112
+                }
+              ]
+            },
+            {
+              "name": "exidx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 8
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_bsp.o",
+          "sym": [
+            {
+              "name": "hal_bsp_flash_dev",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "hal_bsp_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/cmsis-core",
+      "files": [
+        {
+          "name": "cmsis_nvic.o",
+          "sym": [
+            {
+              "name": "NVIC_Relocate",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart/uart_hal",
+      "files": [
+        {
+          "name": "uart_hal.o",
+          "sym": [
+            {
+              "name": "uart_hal_blocking_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_close",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_open",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 114
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_resume",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 44
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_start_rx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_start_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_suspend",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "unlikely",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/hal",
+      "files": [
+        {
+          "name": "hal_flash.o",
+          "sym": [
+            {
+              "name": "hal_flash_check_addr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "hal_flash_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "hal_flash_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 68
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/mcu/nordic/nrf52xxx",
+      "files": [
+        {
+          "name": "hal_os_tick.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "nrf52_os_tick_set_ocmp",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            },
+            {
+              "name": "nrf52_timer_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 80
+                }
+              ]
+            },
+            {
+              "name": "os_tick_idle",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 68
+                }
+              ]
+            },
+            {
+              "name": "os_tick_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 148
+                }
+              ]
+            },
+            {
+              "name": "sub24",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 46
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "system_nrf52.o",
+          "sym": [
+            {
+              "name": "SystemCoreClock",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "SystemInit",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 480
+                }
+              ]
+            },
+            {
+              "name": "errata_16",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 52
+                }
+              ]
+            },
+            {
+              "name": "errata_31",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_timer.o",
+          "sym": [
+            {
+              "name": "__NVIC_SetPendingIRQ",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "hal_timer_config",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 212
+                }
+              ]
+            },
+            {
+              "name": "hal_timer_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 88
+                }
+              ]
+            },
+            {
+              "name": "hal_timer_read_bsptimer",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 56
+                }
+              ]
+            },
+            {
+              "name": "nrf52_hal_timers",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "nrf52_timer0_irq_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 168
+                }
+              ]
+            },
+            {
+              "name": "nrf_timer_set_ocmp",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 126
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_system.o",
+          "sym": [
+            {
+              "name": "hal_debugger_connected",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "hal_system_clock_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "hal_system_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "hal_system_reset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_gpio.o",
+          "sym": [
+            {
+              "name": "hal_gpio_init_out",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 38
+                }
+              ]
+            },
+            {
+              "name": "hal_gpio_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 34
+                }
+              ]
+            },
+            {
+              "name": "hal_gpio_toggle",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 26
+                }
+              ]
+            },
+            {
+              "name": "hal_gpio_write",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_uart.o",
+          "sym": [
+            {
+              "name": "hal_uart_blocking_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 80
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_close",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 52
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_config",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 408
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_init_cbs",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_start_rx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 56
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_start_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 72
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_tx_fill_buf",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "uart0",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "uart0_irq_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 128
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_watchdog.o",
+          "sym": [
+            {
+              "name": "hal_watchdog_enable",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "hal_watchdog_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 112
+                }
+              ]
+            },
+            {
+              "name": "hal_watchdog_tickle",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "nrf52_wdt_irq_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "nrf52_periph.o",
+          "sym": [
+            {
+              "name": "nrf52_periph_create",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 90
+                }
+              ]
+            },
+            {
+              "name": "os_bsp_uart0",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "os_bsp_uart0_cfg",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 4
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_flash.o",
+          "sym": [
+            {
+              "name": "nrf52k_flash_dev",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_erase_sector",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 76
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_funcs",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_sector_info",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_wait_ready",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_write",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 200
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/kernel/os",
+      "files": [
+        {
+          "name": "HAL_CM4.o",
+          "sym": [
+            {
+              "name": ".text",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 200
+                }
+              ]
+            },
+            {
+              "name": "exidx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 217
+                }
+              ]
+            },
+            {
+              "name": "g_idle_task_stack",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 256
+                }
+              ]
+            },
+            {
+              "name": "g_os_main_stack",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4096
+                }
+              ]
+            },
+            {
+              "name": "os_idle_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 124
+                }
+              ]
+            },
+            {
+              "name": "os_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 149
+                }
+              ]
+            },
+            {
+              "name": "os_init_idle_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 93
+                }
+              ]
+            },
+            {
+              "name": "os_main",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 22
+                }
+              ]
+            },
+            {
+              "name": "os_pkg_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            },
+            {
+              "name": "os_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 26
+                }
+              ]
+            },
+            {
+              "name": "os_started",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_fault.o",
+          "sym": [
+            {
+              "name": "__assert_func",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "os_default_irq",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_time.o",
+          "sym": [
+            {
+              "name": "basetod",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 48
+                }
+              ]
+            },
+            {
+              "name": "os_deltatime",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 76
+                }
+              ]
+            },
+            {
+              "name": "os_time_advance",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 120
+                }
+              ]
+            },
+            {
+              "name": "os_time_delay",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "os_time_get",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_msys.o",
+          "sym": [
+            {
+              "name": "g_msys_pool_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_msys_1_data",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 3504
+                }
+              ]
+            },
+            {
+              "name": "os_msys_1_mbuf_pool",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_msys_1_mempool",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "os_msys_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 103
+                }
+              ]
+            },
+            {
+              "name": "os_msys_register",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            },
+            {
+              "name": "os_msys_reset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_dev.o",
+          "sym": [
+            {
+              "name": "g_os_dev_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_dev_create",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 136
+                }
+              ]
+            },
+            {
+              "name": "os_dev_initialize",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "os_dev_initialize_all",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "os_dev_reset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_sched.o",
+          "sym": [
+            {
+              "name": "g_os_run_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "g_os_sleep_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_sched",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "os_sched_ctx_sw_hook",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 44
+                }
+              ]
+            },
+            {
+              "name": "os_sched_get_current_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_sched_insert",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 76
+                }
+              ]
+            },
+            {
+              "name": "os_sched_next_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_sched_os_timer_exp",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "os_sched_resort",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "os_sched_set_current_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_sched_sleep",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 116
+                }
+              ]
+            },
+            {
+              "name": "os_sched_wakeup",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            },
+            {
+              "name": "os_sched_wakeup_ticks",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 52
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_sanity.o",
+          "sym": [
+            {
+              "name": "g_os_sanity_check_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_list_lock",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_list_unlock",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_register",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_run",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 80
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_arch_arm.o",
+          "sym": [
+            {
+              "name": "os_arch_ctx_sw",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "os_arch_in_critical",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 10
+                }
+              ]
+            },
+            {
+              "name": "os_arch_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "os_arch_os_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 148
+                }
+              ]
+            },
+            {
+              "name": "os_arch_os_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 84
+                }
+              ]
+            },
+            {
+              "name": "os_arch_restore_sr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "os_arch_save_sr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_arch_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            },
+            {
+              "name": "os_arch_task_stack_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 54
+                }
+              ]
+            },
+            {
+              "name": "os_flags",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "timer_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_callout.o",
+          "sym": [
+            {
+              "name": "os_callout_tick",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 84
+                }
+              ]
+            },
+            {
+              "name": "os_callout_wakeup_ticks",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_cputime.o",
+          "sym": [
+            {
+              "name": "os_cputime_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 8
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_eventq.o",
+          "sym": [
+            {
+              "name": "os_eventq_dflt_get",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_eventq_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "os_eventq_main",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "os_eventq_put",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_mbuf.o",
+          "sym": [
+            {
+              "name": "os_mbuf_pool_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 10
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_mempool.o",
+          "sym": [
+            {
+              "name": "os_mempool_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 18
+                }
+              ]
+            },
+            {
+              "name": "os_mempool_init_internal",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            },
+            {
+              "name": "os_mempool_module_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_mutex.o",
+          "sym": [
+            {
+              "name": "os_mutex_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "os_mutex_pend",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 220
+                }
+              ]
+            },
+            {
+              "name": "os_mutex_release",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 160
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_task.o",
+          "sym": [
+            {
+              "name": "os_task_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 228
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/libc/baselibc",
+      "files": [
+        {
+          "name": "start.o",
+          "sym": [
+            {
+              "name": "_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 2
+                }
+              ]
+            },
+            {
+              "name": "_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "memcpy.o",
+          "sym": [
+            {
+              "name": "memcpy",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 26
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "memset.o",
+          "sym": [
+            {
+              "name": "memset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "strlen.o",
+          "sym": [
+            {
+              "name": "strlen",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/flash_map",
+      "files": [
+        {
+          "name": "flash_map.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "flash_area_open",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "flash_area_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "flash_map_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 172
+                }
+              ]
+            },
+            {
+              "name": "mfg_areas",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 120
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/modlog",
+      "files": [
+        {
+          "name": "modlog.o",
+          "sym": [
+            {
+              "name": "modlog_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 2
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/mfg",
+      "files": [
+        {
+          "name": "mfg.o",
+          "sym": [
+            {
+              "name": "mfg_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 92
+                }
+              ]
+            },
+            {
+              "name": "mfg_initialized",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 1
+                }
+              ]
+            },
+            {
+              "name": "mfg_mmrs",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "mfg_num_mmrs",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "mfg_open",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_next_mmr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 164
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_tlv_body",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 100
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_tlv_flash_area",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_tlv_mmr_ref",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "mfg_seek_next",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 128
+                }
+              ]
+            },
+            {
+              "name": "mfg_seek_next_with_type",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 22
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sysinit",
+      "files": [
+        {
+          "name": "sysinit.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 1
+                }
+              ]
+            },
+            {
+              "name": "sysinit_dflt_panic_cb",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "sysinit_end",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "sysinit_panic_cb",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "sysinit_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/util/mem",
+      "files": [
+        {
+          "name": "mem.o",
+          "sym": [
+            {
+              "name": "mem_init_mbuf_pool",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 50
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "blinky-nordic_pca10040-sysinit-app",
+      "files": [
+        {
+          "name": "blinky-nordic_pca10040-sysflash.o",
+          "sym": [
+            {
+              "name": "sysflash_map_dflt",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 72
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "blinky-nordic_pca10040-sysinit-app.o",
+          "sym": [
+            {
+              "name": "sysinit_app",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 22
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/image/testdata/good-unsigned.img b/image/testdata/good-unsigned.img
new file mode 100644
index 0000000..2e5c47a
Binary files /dev/null and b/image/testdata/good-unsigned.img differ
diff --git a/image/testdata/good-unsigned.json b/image/testdata/good-unsigned.json
new file mode 100644
index 0000000..cb9aa19
--- /dev/null
+++ b/image/testdata/good-unsigned.json
@@ -0,0 +1,2267 @@
+{
+  "name": "targets/blinky-nordic_pca10040",
+  "build_time": "2019-06-17T17:16:49-07:00",
+  "build_version": "1.0.0.0",
+  "id": "8eb006d574ace63cce18a1f2d8f0f2645f1a0e8630a39fb86bbfbb805d4cd3b9",
+  "image": "/Users/ccollins/proj/myproj/bin/targets/blinky-nordic_pca10040/app/apps/blinky/blinky.img",
+  "image_hash": "8eb006d574ace63cce18a1f2d8f0f2645f1a0e8630a39fb86bbfbb805d4cd3b9",
+  "loader": "",
+  "loader_hash": "",
+  "pkgs": [
+    {
+      "name": "apps/blinky",
+      "repo": "my_project"
+    },
+    {
+      "name": "blinky-nordic_pca10040-sysinit-app",
+      "repo": "my_project"
+    },
+    {
+      "name": "@apache-mynewt-core/compiler/arm-none-eabi-m4",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/bsp/nordic_pca10040",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/cmsis-core",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart/uart_hal",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/hal",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/mcu/nordic",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/mcu/nordic/nrf52xxx",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/kernel/os",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/libc/baselibc",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/console/stub",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/defs",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/flash_map",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/common",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/modlog",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/stub",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/mfg",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sys",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sysdown",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sysinit",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "targets/blinky-nordic_pca10040",
+      "repo": "my_project"
+    },
+    {
+      "name": "@apache-mynewt-core/util/mem",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/util/rwlock",
+      "repo": "apache-mynewt-core"
+    }
+  ],
+  "target": [
+    "target.app=apps/blinky",
+    "target.bsp=@apache-mynewt-core/hw/bsp/nordic_pca10040",
+    "target.build_profile=optimized"
+  ],
+  "repos": [
+    {
+      "name": "apache-mynewt-core",
+      "commit": "fa5e4c31b0bea2b107747e3b0d40002d3bef4132",
+      "url": "git@github.com:apache/mynewt-core.git"
+    },
+    {
+      "name": "my_project",
+      "commit": "UNKNOWN"
+    }
+  ],
+  "syscfg": {
+    "ADC_0": "0",
+    "ADC_0_REFMV_0": "0",
+    "APP_NAME": "\"blinky\"",
+    "APP_blinky": "1",
+    "ARCH_NAME": "\"cortex_m4\"",
+    "ARCH_cortex_m4": "1",
+    "BASELIBC_ASSERT_FILE_LINE": "0",
+    "BASELIBC_PRESENT": "1",
+    "BSP_NAME": "\"nordic_pca10040\"",
+    "BSP_NRF52": "1",
+    "BSP_nordic_pca10040": "1",
+    "CONSOLE_UART_BAUD": "115200",
+    "CONSOLE_UART_DEV": "\"uart0\"",
+    "CONSOLE_UART_FLOW_CONTROL": "UART_FLOW_CTL_NONE",
+    "CRYPTO": "0",
+    "DEBUG_PANIC_ENABLED": "1",
+    "ENC_FLASH_DEV": "0",
+    "FLASH_MAP_MAX_AREAS": "10",
+    "FLASH_MAP_SYSINIT_STAGE": "2",
+    "FLOAT_USER": "0",
+    "GPIO_AS_PIN_RESET": "0",
+    "HAL_FLASH_VERIFY_BUF_SZ": "16",
+    "HAL_FLASH_VERIFY_ERASES": "0",
+    "HAL_FLASH_VERIFY_WRITES": "0",
+    "HAL_SYSTEM_RESET_CB": "0",
+    "HARDFLOAT": "0",
+    "I2C_0": "0",
+    "I2C_0_FREQ_KHZ": "100",
+    "I2C_0_PIN_SCL": "27",
+    "I2C_0_PIN_SDA": "26",
+    "I2C_1": "0",
+    "I2C_1_FREQ_KHZ": "100",
+    "I2C_1_PIN_SCL": "",
+    "I2C_1_PIN_SDA": "",
+    "LOG_CONSOLE": "1",
+    "LOG_FCB": "0",
+    "LOG_FCB_SLOT1": "0",
+    "LOG_LEVEL": "255",
+    "MCU_BUS_DRIVER_I2C_USE_TWIM": "0",
+    "MCU_DCDC_ENABLED": "1",
+    "MCU_DEBUG_IGNORE_BKPT": "0",
+    "MCU_FLASH_MIN_WRITE_SIZE": "1",
+    "MCU_GPIO_USE_PORT_EVENT": "0",
+    "MCU_I2C_RECOVERY_DELAY_USEC": "100",
+    "MCU_LFCLK_SOURCE": "LFXO",
+    "MCU_NRF52832": "0",
+    "MCU_NRF52840": "0",
+    "MCU_TARGET": "nRF52832",
+    "MFG_LOG_MODULE": "128",
+    "MFG_MAX_MMRS": "2",
+    "MFG_SYSINIT_STAGE": "100",
+    "MODLOG_CONSOLE_DFLT": "1",
+    "MODLOG_LOG_MACROS": "0",
+    "MODLOG_MAX_MAPPINGS": "16",
+    "MODLOG_MAX_PRINTF_LEN": "128",
+    "MODLOG_SYSINIT_STAGE": "100",
+    "MSYS_1_BLOCK_COUNT": "12",
+    "MSYS_1_BLOCK_SIZE": "292",
+    "MSYS_1_SANITY_MIN_COUNT": "0",
+    "MSYS_2_BLOCK_COUNT": "0",
+    "MSYS_2_BLOCK_SIZE": "0",
+    "MSYS_2_SANITY_MIN_COUNT": "0",
+    "MSYS_SANITY_TIMEOUT": "60000",
+    "NEWT_FEATURE_LOGCFG": "1",
+    "NEWT_FEATURE_SYSDOWN": "1",
+    "NFC_PINS_AS_GPIO": "1",
+    "OS_ASSERT_CB": "0",
+    "OS_CLI": "0",
+    "OS_COREDUMP": "0",
+    "OS_CPUTIME_FREQ": "1000000",
+    "OS_CPUTIME_TIMER_NUM": "0",
+    "OS_CRASH_FILE_LINE": "0",
+    "OS_CRASH_LOG": "0",
+    "OS_CRASH_RESTORE_REGS": "0",
+    "OS_CRASH_STACKTRACE": "0",
+    "OS_CTX_SW_STACK_CHECK": "0",
+    "OS_CTX_SW_STACK_GUARD": "4",
+    "OS_DEBUG_MODE": "0",
+    "OS_EVENTQ_DEBUG": "0",
+    "OS_EVENTQ_MONITOR": "0",
+    "OS_IDLE_TICKLESS_MS_MAX": "600000",
+    "OS_IDLE_TICKLESS_MS_MIN": "100",
+    "OS_MAIN_STACK_SIZE": "1024",
+    "OS_MAIN_TASK_PRIO": "127",
+    "OS_MAIN_TASK_SANITY_ITVL_MS": "0",
+    "OS_MEMPOOL_CHECK": "0",
+    "OS_MEMPOOL_GUARD": "0",
+    "OS_MEMPOOL_POISON": "0",
+    "OS_SCHEDULING": "1",
+    "OS_SYSINIT_STAGE": "0",
+    "OS_SYSVIEW": "0",
+    "OS_SYSVIEW_TRACE_CALLOUT": "1",
+    "OS_SYSVIEW_TRACE_EVENTQ": "1",
+    "OS_SYSVIEW_TRACE_MBUF": "0",
+    "OS_SYSVIEW_TRACE_MEMPOOL": "0",
+    "OS_SYSVIEW_TRACE_MUTEX": "1",
+    "OS_SYSVIEW_TRACE_SEM": "1",
+    "OS_TIME_DEBUG": "0",
+    "OS_WATCHDOG_MONITOR": "0",
+    "PWM_0": "0",
+    "PWM_1": "0",
+    "PWM_2": "0",
+    "PWM_3": "0",
+    "QSPI_ADDRMODE": "0",
+    "QSPI_DPMCONFIG": "0",
+    "QSPI_ENABLE": "0",
+    "QSPI_FLASH_PAGE_SIZE": "0",
+    "QSPI_FLASH_SECTOR_COUNT": "-1",
+    "QSPI_FLASH_SECTOR_SIZE": "0",
+    "QSPI_PIN_CS": "-1",
+    "QSPI_PIN_DIO0": "-1",
+    "QSPI_PIN_DIO1": "-1",
+    "QSPI_PIN_DIO2": "-1",
+    "QSPI_PIN_DIO3": "-1",
+    "QSPI_PIN_SCK": "-1",
+    "QSPI_READOC": "0",
+    "QSPI_SCK_DELAY": "0",
+    "QSPI_SCK_FREQ": "0",
+    "QSPI_SPI_MODE": "0",
+    "QSPI_WRITEOC": "0",
+    "RAM_RESIDENT": "0",
+    "RWLOCK_DEBUG": "0",
+    "SANITY_INTERVAL": "15000",
+    "SOFT_PWM": "0",
+    "SPI_0_MASTER": "0",
+    "SPI_0_MASTER_PIN_MISO": "25",
+    "SPI_0_MASTER_PIN_MOSI": "24",
+    "SPI_0_MASTER_PIN_SCK": "23",
+    "SPI_0_SLAVE": "0",
+    "SPI_0_SLAVE_PIN_MISO": "25",
+    "SPI_0_SLAVE_PIN_MOSI": "24",
+    "SPI_0_SLAVE_PIN_SCK": "23",
+    "SPI_0_SLAVE_PIN_SS": "22",
+    "SPI_1_MASTER": "0",
+    "SPI_1_MASTER_PIN_MISO": "",
+    "SPI_1_MASTER_PIN_MOSI": "",
+    "SPI_1_MASTER_PIN_SCK": "",
+    "SPI_1_SLAVE": "0",
+    "SPI_1_SLAVE_PIN_MISO": "",
+    "SPI_1_SLAVE_PIN_MOSI": "",
+    "SPI_1_SLAVE_PIN_SCK": "",
+    "SPI_1_SLAVE_PIN_SS": "",
+    "SPI_2_MASTER": "0",
+    "SPI_2_MASTER_PIN_MISO": "",
+    "SPI_2_MASTER_PIN_MOSI": "",
+    "SPI_2_MASTER_PIN_SCK": "",
+    "SPI_2_SLAVE": "0",
+    "SPI_2_SLAVE_PIN_MISO": "",
+    "SPI_2_SLAVE_PIN_MOSI": "",
+    "SPI_2_SLAVE_PIN_SCK": "",
+    "SPI_2_SLAVE_PIN_SS": "",
+    "SPI_3_MASTER": "0",
+    "SPI_3_MASTER_PIN_MISO": "",
+    "SPI_3_MASTER_PIN_MOSI": "",
+    "SPI_3_MASTER_PIN_SCK": "",
+    "SPI_3_SLAVE": "0",
+    "SPI_3_SLAVE_PIN_MISO": "",
+    "SPI_3_SLAVE_PIN_MOSI": "",
+    "SPI_3_SLAVE_PIN_SCK": "",
+    "SPI_3_SLAVE_PIN_SS": "",
+    "SYSDOWN_CONSTRAIN_DOWN": "1",
+    "SYSDOWN_PANIC_FILE_LINE": "0",
+    "SYSDOWN_PANIC_MESSAGE": "0",
+    "SYSDOWN_TIMEOUT_MS": "10000",
+    "SYSINIT_CONSTRAIN_INIT": "1",
+    "SYSINIT_PANIC_FILE_LINE": "0",
+    "SYSINIT_PANIC_MESSAGE": "0",
+    "TARGET_NAME": "\"blinky-nordic_pca10040\"",
+    "TARGET_blinky_nordic_pca10040": "1",
+    "TIMER_0": "1",
+    "TIMER_1": "0",
+    "TIMER_2": "0",
+    "TIMER_3": "0",
+    "TIMER_4": "0",
+    "TIMER_5": "0",
+    "TRNG": "0",
+    "UARTBB_0": "0",
+    "UARTBB_0_PIN_RX": "-1",
+    "UARTBB_0_PIN_TX": "-1",
+    "UART_0": "1",
+    "UART_0_PIN_CTS": "7",
+    "UART_0_PIN_RTS": "5",
+    "UART_0_PIN_RX": "8",
+    "UART_0_PIN_TX": "6",
+    "UART_1": "0",
+    "UART_1_PIN_CTS": "-1",
+    "UART_1_PIN_RTS": "-1",
+    "UART_1_PIN_RX": "",
+    "UART_1_PIN_TX": "",
+    "WATCHDOG_INTERVAL": "30000",
+    "XTAL_32768": "0",
+    "XTAL_32768_SYNTH": "0",
+    "XTAL_RC": "0"
+  },
+  "pkgsz": [
+    {
+      "name": "*fill*",
+      "files": [
+        {
+          "name": "",
+          "sym": [
+            {
+              "name": "*fill*",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 63
+                }
+              ]
+            },
+            {
+              "name": "*fill*",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 233
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "apps/blinky",
+      "files": [
+        {
+          "name": "main.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "g_task1_loops",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "startup",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 56
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/bsp/nordic_pca10040",
+      "files": [
+        {
+          "name": "sbrk.o",
+          "sym": [
+            {
+              "name": ".data",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "_sbrkInit",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "gcc_startup_nrf52.o",
+          "sym": [
+            {
+              "name": ".isr_vector",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 216
+                }
+              ]
+            },
+            {
+              "name": ".stack",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 432
+                }
+              ]
+            },
+            {
+              "name": ".text",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 112
+                }
+              ]
+            },
+            {
+              "name": "exidx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 8
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_bsp.o",
+          "sym": [
+            {
+              "name": "hal_bsp_flash_dev",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "hal_bsp_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/cmsis-core",
+      "files": [
+        {
+          "name": "cmsis_nvic.o",
+          "sym": [
+            {
+              "name": "NVIC_Relocate",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart/uart_hal",
+      "files": [
+        {
+          "name": "uart_hal.o",
+          "sym": [
+            {
+              "name": "uart_hal_blocking_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_close",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_open",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 114
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_resume",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 44
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_start_rx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_start_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_suspend",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "unlikely",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/hal",
+      "files": [
+        {
+          "name": "hal_flash.o",
+          "sym": [
+            {
+              "name": "hal_flash_check_addr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "hal_flash_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "hal_flash_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 68
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/mcu/nordic/nrf52xxx",
+      "files": [
+        {
+          "name": "hal_os_tick.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "nrf52_os_tick_set_ocmp",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            },
+            {
+              "name": "nrf52_timer_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 80
+                }
+              ]
+            },
+            {
+              "name": "os_tick_idle",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 68
+                }
+              ]
+            },
+            {
+              "name": "os_tick_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 148
+                }
+              ]
+            },
+            {
+              "name": "sub24",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 46
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "system_nrf52.o",
+          "sym": [
+            {
+              "name": "SystemCoreClock",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "SystemInit",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 480
+                }
+              ]
+            },
+            {
+              "name": "errata_16",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 52
+                }
+              ]
+            },
+            {
+              "name": "errata_31",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_timer.o",
+          "sym": [
+            {
+              "name": "__NVIC_SetPendingIRQ",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "hal_timer_config",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 212
+                }
+              ]
+            },
+            {
+              "name": "hal_timer_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 88
+                }
+              ]
+            },
+            {
+              "name": "hal_timer_read_bsptimer",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 56
+                }
+              ]
+            },
+            {
+              "name": "nrf52_hal_timers",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "nrf52_timer0_irq_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 168
+                }
+              ]
+            },
+            {
+              "name": "nrf_timer_set_ocmp",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 126
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_system.o",
+          "sym": [
+            {
+              "name": "hal_debugger_connected",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "hal_system_clock_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "hal_system_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "hal_system_reset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_gpio.o",
+          "sym": [
+            {
+              "name": "hal_gpio_init_out",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 38
+                }
+              ]
+            },
+            {
+              "name": "hal_gpio_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 34
+                }
+              ]
+            },
+            {
+              "name": "hal_gpio_toggle",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 26
+                }
+              ]
+            },
+            {
+              "name": "hal_gpio_write",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_uart.o",
+          "sym": [
+            {
+              "name": "hal_uart_blocking_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 80
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_close",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 52
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_config",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 408
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_init_cbs",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_start_rx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 56
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_start_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 72
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_tx_fill_buf",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "uart0",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "uart0_irq_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 128
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_watchdog.o",
+          "sym": [
+            {
+              "name": "hal_watchdog_enable",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "hal_watchdog_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 112
+                }
+              ]
+            },
+            {
+              "name": "hal_watchdog_tickle",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "nrf52_wdt_irq_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "nrf52_periph.o",
+          "sym": [
+            {
+              "name": "nrf52_periph_create",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 90
+                }
+              ]
+            },
+            {
+              "name": "os_bsp_uart0",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "os_bsp_uart0_cfg",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 4
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_flash.o",
+          "sym": [
+            {
+              "name": "nrf52k_flash_dev",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_erase_sector",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 76
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_funcs",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_sector_info",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_wait_ready",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_write",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 200
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/kernel/os",
+      "files": [
+        {
+          "name": "HAL_CM4.o",
+          "sym": [
+            {
+              "name": ".text",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 200
+                }
+              ]
+            },
+            {
+              "name": "exidx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 217
+                }
+              ]
+            },
+            {
+              "name": "g_idle_task_stack",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 256
+                }
+              ]
+            },
+            {
+              "name": "g_os_main_stack",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4096
+                }
+              ]
+            },
+            {
+              "name": "os_idle_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 124
+                }
+              ]
+            },
+            {
+              "name": "os_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 149
+                }
+              ]
+            },
+            {
+              "name": "os_init_idle_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 93
+                }
+              ]
+            },
+            {
+              "name": "os_main",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 22
+                }
+              ]
+            },
+            {
+              "name": "os_pkg_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            },
+            {
+              "name": "os_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 26
+                }
+              ]
+            },
+            {
+              "name": "os_started",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_fault.o",
+          "sym": [
+            {
+              "name": "__assert_func",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "os_default_irq",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_time.o",
+          "sym": [
+            {
+              "name": "basetod",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 48
+                }
+              ]
+            },
+            {
+              "name": "os_deltatime",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 76
+                }
+              ]
+            },
+            {
+              "name": "os_time_advance",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 120
+                }
+              ]
+            },
+            {
+              "name": "os_time_delay",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "os_time_get",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_msys.o",
+          "sym": [
+            {
+              "name": "g_msys_pool_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_msys_1_data",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 3504
+                }
+              ]
+            },
+            {
+              "name": "os_msys_1_mbuf_pool",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_msys_1_mempool",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "os_msys_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 103
+                }
+              ]
+            },
+            {
+              "name": "os_msys_register",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            },
+            {
+              "name": "os_msys_reset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_dev.o",
+          "sym": [
+            {
+              "name": "g_os_dev_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_dev_create",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 136
+                }
+              ]
+            },
+            {
+              "name": "os_dev_initialize",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "os_dev_initialize_all",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "os_dev_reset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_sched.o",
+          "sym": [
+            {
+              "name": "g_os_run_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "g_os_sleep_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_sched",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "os_sched_ctx_sw_hook",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 44
+                }
+              ]
+            },
+            {
+              "name": "os_sched_get_current_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_sched_insert",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 76
+                }
+              ]
+            },
+            {
+              "name": "os_sched_next_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_sched_os_timer_exp",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "os_sched_resort",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "os_sched_set_current_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_sched_sleep",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 116
+                }
+              ]
+            },
+            {
+              "name": "os_sched_wakeup",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            },
+            {
+              "name": "os_sched_wakeup_ticks",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 52
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_sanity.o",
+          "sym": [
+            {
+              "name": "g_os_sanity_check_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_list_lock",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_list_unlock",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_register",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_run",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 80
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_arch_arm.o",
+          "sym": [
+            {
+              "name": "os_arch_ctx_sw",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "os_arch_in_critical",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 10
+                }
+              ]
+            },
+            {
+              "name": "os_arch_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "os_arch_os_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 148
+                }
+              ]
+            },
+            {
+              "name": "os_arch_os_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 84
+                }
+              ]
+            },
+            {
+              "name": "os_arch_restore_sr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "os_arch_save_sr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_arch_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            },
+            {
+              "name": "os_arch_task_stack_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 54
+                }
+              ]
+            },
+            {
+              "name": "os_flags",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "timer_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_callout.o",
+          "sym": [
+            {
+              "name": "os_callout_tick",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 84
+                }
+              ]
+            },
+            {
+              "name": "os_callout_wakeup_ticks",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_cputime.o",
+          "sym": [
+            {
+              "name": "os_cputime_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 8
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_eventq.o",
+          "sym": [
+            {
+              "name": "os_eventq_dflt_get",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_eventq_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "os_eventq_main",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "os_eventq_put",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_mbuf.o",
+          "sym": [
+            {
+              "name": "os_mbuf_pool_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 10
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_mempool.o",
+          "sym": [
+            {
+              "name": "os_mempool_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 18
+                }
+              ]
+            },
+            {
+              "name": "os_mempool_init_internal",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            },
+            {
+              "name": "os_mempool_module_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_mutex.o",
+          "sym": [
+            {
+              "name": "os_mutex_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "os_mutex_pend",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 220
+                }
+              ]
+            },
+            {
+              "name": "os_mutex_release",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 160
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_task.o",
+          "sym": [
+            {
+              "name": "os_task_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 228
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/libc/baselibc",
+      "files": [
+        {
+          "name": "start.o",
+          "sym": [
+            {
+              "name": "_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 2
+                }
+              ]
+            },
+            {
+              "name": "_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "memcpy.o",
+          "sym": [
+            {
+              "name": "memcpy",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 26
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "memset.o",
+          "sym": [
+            {
+              "name": "memset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "strlen.o",
+          "sym": [
+            {
+              "name": "strlen",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/flash_map",
+      "files": [
+        {
+          "name": "flash_map.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "flash_area_open",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "flash_area_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "flash_map_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 172
+                }
+              ]
+            },
+            {
+              "name": "mfg_areas",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 120
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/modlog",
+      "files": [
+        {
+          "name": "modlog.o",
+          "sym": [
+            {
+              "name": "modlog_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 2
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/mfg",
+      "files": [
+        {
+          "name": "mfg.o",
+          "sym": [
+            {
+              "name": "mfg_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 92
+                }
+              ]
+            },
+            {
+              "name": "mfg_initialized",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 1
+                }
+              ]
+            },
+            {
+              "name": "mfg_mmrs",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "mfg_num_mmrs",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "mfg_open",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_next_mmr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 164
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_tlv_body",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 100
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_tlv_flash_area",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_tlv_mmr_ref",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "mfg_seek_next",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 128
+                }
+              ]
+            },
+            {
+              "name": "mfg_seek_next_with_type",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 22
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sysinit",
+      "files": [
+        {
+          "name": "sysinit.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 1
+                }
+              ]
+            },
+            {
+              "name": "sysinit_dflt_panic_cb",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "sysinit_end",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "sysinit_panic_cb",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "sysinit_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/util/mem",
+      "files": [
+        {
+          "name": "mem.o",
+          "sym": [
+            {
+              "name": "mem_init_mbuf_pool",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 50
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "blinky-nordic_pca10040-sysinit-app",
+      "files": [
+        {
+          "name": "blinky-nordic_pca10040-sysflash.o",
+          "sym": [
+            {
+              "name": "sysflash_map_dflt",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 72
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "blinky-nordic_pca10040-sysinit-app.o",
+          "sym": [
+            {
+              "name": "sysinit_app",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 22
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/image/testdata/mismatch-hash.img b/image/testdata/mismatch-hash.img
new file mode 100644
index 0000000..2e5c47a
Binary files /dev/null and b/image/testdata/mismatch-hash.img differ
diff --git a/image/testdata/mismatch-hash.json b/image/testdata/mismatch-hash.json
new file mode 100644
index 0000000..5da3a66
--- /dev/null
+++ b/image/testdata/mismatch-hash.json
@@ -0,0 +1,2267 @@
+{
+  "name": "targets/blinky-nordic_pca10040",
+  "build_time": "2019-06-17T17:16:49-07:00",
+  "build_version": "1.0.0.0",
+  "id": "aab006d574ace63cce18a1f2d8f0f2645f1a0e8630a39fb86bbfbb805d4cd3b9",
+  "image": "/Users/ccollins/proj/myproj/bin/targets/blinky-nordic_pca10040/app/apps/blinky/blinky.img",
+  "image_hash": "aab006d574ace63cce18a1f2d8f0f2645f1a0e8630a39fb86bbfbb805d4cd3b9",
+  "loader": "",
+  "loader_hash": "",
+  "pkgs": [
+    {
+      "name": "apps/blinky",
+      "repo": "my_project"
+    },
+    {
+      "name": "blinky-nordic_pca10040-sysinit-app",
+      "repo": "my_project"
+    },
+    {
+      "name": "@apache-mynewt-core/compiler/arm-none-eabi-m4",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/bsp/nordic_pca10040",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/cmsis-core",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart/uart_hal",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/hal",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/mcu/nordic",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/mcu/nordic/nrf52xxx",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/kernel/os",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/libc/baselibc",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/console/stub",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/defs",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/flash_map",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/common",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/modlog",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/stub",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/mfg",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sys",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sysdown",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sysinit",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "targets/blinky-nordic_pca10040",
+      "repo": "my_project"
+    },
+    {
+      "name": "@apache-mynewt-core/util/mem",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/util/rwlock",
+      "repo": "apache-mynewt-core"
+    }
+  ],
+  "target": [
+    "target.app=apps/blinky",
+    "target.bsp=@apache-mynewt-core/hw/bsp/nordic_pca10040",
+    "target.build_profile=optimized"
+  ],
+  "repos": [
+    {
+      "name": "apache-mynewt-core",
+      "commit": "fa5e4c31b0bea2b107747e3b0d40002d3bef4132",
+      "url": "git@github.com:apache/mynewt-core.git"
+    },
+    {
+      "name": "my_project",
+      "commit": "UNKNOWN"
+    }
+  ],
+  "syscfg": {
+    "ADC_0": "0",
+    "ADC_0_REFMV_0": "0",
+    "APP_NAME": "\"blinky\"",
+    "APP_blinky": "1",
+    "ARCH_NAME": "\"cortex_m4\"",
+    "ARCH_cortex_m4": "1",
+    "BASELIBC_ASSERT_FILE_LINE": "0",
+    "BASELIBC_PRESENT": "1",
+    "BSP_NAME": "\"nordic_pca10040\"",
+    "BSP_NRF52": "1",
+    "BSP_nordic_pca10040": "1",
+    "CONSOLE_UART_BAUD": "115200",
+    "CONSOLE_UART_DEV": "\"uart0\"",
+    "CONSOLE_UART_FLOW_CONTROL": "UART_FLOW_CTL_NONE",
+    "CRYPTO": "0",
+    "DEBUG_PANIC_ENABLED": "1",
+    "ENC_FLASH_DEV": "0",
+    "FLASH_MAP_MAX_AREAS": "10",
+    "FLASH_MAP_SYSINIT_STAGE": "2",
+    "FLOAT_USER": "0",
+    "GPIO_AS_PIN_RESET": "0",
+    "HAL_FLASH_VERIFY_BUF_SZ": "16",
+    "HAL_FLASH_VERIFY_ERASES": "0",
+    "HAL_FLASH_VERIFY_WRITES": "0",
+    "HAL_SYSTEM_RESET_CB": "0",
+    "HARDFLOAT": "0",
+    "I2C_0": "0",
+    "I2C_0_FREQ_KHZ": "100",
+    "I2C_0_PIN_SCL": "27",
+    "I2C_0_PIN_SDA": "26",
+    "I2C_1": "0",
+    "I2C_1_FREQ_KHZ": "100",
+    "I2C_1_PIN_SCL": "",
+    "I2C_1_PIN_SDA": "",
+    "LOG_CONSOLE": "1",
+    "LOG_FCB": "0",
+    "LOG_FCB_SLOT1": "0",
+    "LOG_LEVEL": "255",
+    "MCU_BUS_DRIVER_I2C_USE_TWIM": "0",
+    "MCU_DCDC_ENABLED": "1",
+    "MCU_DEBUG_IGNORE_BKPT": "0",
+    "MCU_FLASH_MIN_WRITE_SIZE": "1",
+    "MCU_GPIO_USE_PORT_EVENT": "0",
+    "MCU_I2C_RECOVERY_DELAY_USEC": "100",
+    "MCU_LFCLK_SOURCE": "LFXO",
+    "MCU_NRF52832": "0",
+    "MCU_NRF52840": "0",
+    "MCU_TARGET": "nRF52832",
+    "MFG_LOG_MODULE": "128",
+    "MFG_MAX_MMRS": "2",
+    "MFG_SYSINIT_STAGE": "100",
+    "MODLOG_CONSOLE_DFLT": "1",
+    "MODLOG_LOG_MACROS": "0",
+    "MODLOG_MAX_MAPPINGS": "16",
+    "MODLOG_MAX_PRINTF_LEN": "128",
+    "MODLOG_SYSINIT_STAGE": "100",
+    "MSYS_1_BLOCK_COUNT": "12",
+    "MSYS_1_BLOCK_SIZE": "292",
+    "MSYS_1_SANITY_MIN_COUNT": "0",
+    "MSYS_2_BLOCK_COUNT": "0",
+    "MSYS_2_BLOCK_SIZE": "0",
+    "MSYS_2_SANITY_MIN_COUNT": "0",
+    "MSYS_SANITY_TIMEOUT": "60000",
+    "NEWT_FEATURE_LOGCFG": "1",
+    "NEWT_FEATURE_SYSDOWN": "1",
+    "NFC_PINS_AS_GPIO": "1",
+    "OS_ASSERT_CB": "0",
+    "OS_CLI": "0",
+    "OS_COREDUMP": "0",
+    "OS_CPUTIME_FREQ": "1000000",
+    "OS_CPUTIME_TIMER_NUM": "0",
+    "OS_CRASH_FILE_LINE": "0",
+    "OS_CRASH_LOG": "0",
+    "OS_CRASH_RESTORE_REGS": "0",
+    "OS_CRASH_STACKTRACE": "0",
+    "OS_CTX_SW_STACK_CHECK": "0",
+    "OS_CTX_SW_STACK_GUARD": "4",
+    "OS_DEBUG_MODE": "0",
+    "OS_EVENTQ_DEBUG": "0",
+    "OS_EVENTQ_MONITOR": "0",
+    "OS_IDLE_TICKLESS_MS_MAX": "600000",
+    "OS_IDLE_TICKLESS_MS_MIN": "100",
+    "OS_MAIN_STACK_SIZE": "1024",
+    "OS_MAIN_TASK_PRIO": "127",
+    "OS_MAIN_TASK_SANITY_ITVL_MS": "0",
+    "OS_MEMPOOL_CHECK": "0",
+    "OS_MEMPOOL_GUARD": "0",
+    "OS_MEMPOOL_POISON": "0",
+    "OS_SCHEDULING": "1",
+    "OS_SYSINIT_STAGE": "0",
+    "OS_SYSVIEW": "0",
+    "OS_SYSVIEW_TRACE_CALLOUT": "1",
+    "OS_SYSVIEW_TRACE_EVENTQ": "1",
+    "OS_SYSVIEW_TRACE_MBUF": "0",
+    "OS_SYSVIEW_TRACE_MEMPOOL": "0",
+    "OS_SYSVIEW_TRACE_MUTEX": "1",
+    "OS_SYSVIEW_TRACE_SEM": "1",
+    "OS_TIME_DEBUG": "0",
+    "OS_WATCHDOG_MONITOR": "0",
+    "PWM_0": "0",
+    "PWM_1": "0",
+    "PWM_2": "0",
+    "PWM_3": "0",
+    "QSPI_ADDRMODE": "0",
+    "QSPI_DPMCONFIG": "0",
+    "QSPI_ENABLE": "0",
+    "QSPI_FLASH_PAGE_SIZE": "0",
+    "QSPI_FLASH_SECTOR_COUNT": "-1",
+    "QSPI_FLASH_SECTOR_SIZE": "0",
+    "QSPI_PIN_CS": "-1",
+    "QSPI_PIN_DIO0": "-1",
+    "QSPI_PIN_DIO1": "-1",
+    "QSPI_PIN_DIO2": "-1",
+    "QSPI_PIN_DIO3": "-1",
+    "QSPI_PIN_SCK": "-1",
+    "QSPI_READOC": "0",
+    "QSPI_SCK_DELAY": "0",
+    "QSPI_SCK_FREQ": "0",
+    "QSPI_SPI_MODE": "0",
+    "QSPI_WRITEOC": "0",
+    "RAM_RESIDENT": "0",
+    "RWLOCK_DEBUG": "0",
+    "SANITY_INTERVAL": "15000",
+    "SOFT_PWM": "0",
+    "SPI_0_MASTER": "0",
+    "SPI_0_MASTER_PIN_MISO": "25",
+    "SPI_0_MASTER_PIN_MOSI": "24",
+    "SPI_0_MASTER_PIN_SCK": "23",
+    "SPI_0_SLAVE": "0",
+    "SPI_0_SLAVE_PIN_MISO": "25",
+    "SPI_0_SLAVE_PIN_MOSI": "24",
+    "SPI_0_SLAVE_PIN_SCK": "23",
+    "SPI_0_SLAVE_PIN_SS": "22",
+    "SPI_1_MASTER": "0",
+    "SPI_1_MASTER_PIN_MISO": "",
+    "SPI_1_MASTER_PIN_MOSI": "",
+    "SPI_1_MASTER_PIN_SCK": "",
+    "SPI_1_SLAVE": "0",
+    "SPI_1_SLAVE_PIN_MISO": "",
+    "SPI_1_SLAVE_PIN_MOSI": "",
+    "SPI_1_SLAVE_PIN_SCK": "",
+    "SPI_1_SLAVE_PIN_SS": "",
+    "SPI_2_MASTER": "0",
+    "SPI_2_MASTER_PIN_MISO": "",
+    "SPI_2_MASTER_PIN_MOSI": "",
+    "SPI_2_MASTER_PIN_SCK": "",
+    "SPI_2_SLAVE": "0",
+    "SPI_2_SLAVE_PIN_MISO": "",
+    "SPI_2_SLAVE_PIN_MOSI": "",
+    "SPI_2_SLAVE_PIN_SCK": "",
+    "SPI_2_SLAVE_PIN_SS": "",
+    "SPI_3_MASTER": "0",
+    "SPI_3_MASTER_PIN_MISO": "",
+    "SPI_3_MASTER_PIN_MOSI": "",
+    "SPI_3_MASTER_PIN_SCK": "",
+    "SPI_3_SLAVE": "0",
+    "SPI_3_SLAVE_PIN_MISO": "",
+    "SPI_3_SLAVE_PIN_MOSI": "",
+    "SPI_3_SLAVE_PIN_SCK": "",
+    "SPI_3_SLAVE_PIN_SS": "",
+    "SYSDOWN_CONSTRAIN_DOWN": "1",
+    "SYSDOWN_PANIC_FILE_LINE": "0",
+    "SYSDOWN_PANIC_MESSAGE": "0",
+    "SYSDOWN_TIMEOUT_MS": "10000",
+    "SYSINIT_CONSTRAIN_INIT": "1",
+    "SYSINIT_PANIC_FILE_LINE": "0",
+    "SYSINIT_PANIC_MESSAGE": "0",
+    "TARGET_NAME": "\"blinky-nordic_pca10040\"",
+    "TARGET_blinky_nordic_pca10040": "1",
+    "TIMER_0": "1",
+    "TIMER_1": "0",
+    "TIMER_2": "0",
+    "TIMER_3": "0",
+    "TIMER_4": "0",
+    "TIMER_5": "0",
+    "TRNG": "0",
+    "UARTBB_0": "0",
+    "UARTBB_0_PIN_RX": "-1",
+    "UARTBB_0_PIN_TX": "-1",
+    "UART_0": "1",
+    "UART_0_PIN_CTS": "7",
+    "UART_0_PIN_RTS": "5",
+    "UART_0_PIN_RX": "8",
+    "UART_0_PIN_TX": "6",
+    "UART_1": "0",
+    "UART_1_PIN_CTS": "-1",
+    "UART_1_PIN_RTS": "-1",
+    "UART_1_PIN_RX": "",
+    "UART_1_PIN_TX": "",
+    "WATCHDOG_INTERVAL": "30000",
+    "XTAL_32768": "0",
+    "XTAL_32768_SYNTH": "0",
+    "XTAL_RC": "0"
+  },
+  "pkgsz": [
+    {
+      "name": "*fill*",
+      "files": [
+        {
+          "name": "",
+          "sym": [
+            {
+              "name": "*fill*",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 63
+                }
+              ]
+            },
+            {
+              "name": "*fill*",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 233
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "apps/blinky",
+      "files": [
+        {
+          "name": "main.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "g_task1_loops",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "startup",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 56
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/bsp/nordic_pca10040",
+      "files": [
+        {
+          "name": "sbrk.o",
+          "sym": [
+            {
+              "name": ".data",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "_sbrkInit",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "gcc_startup_nrf52.o",
+          "sym": [
+            {
+              "name": ".isr_vector",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 216
+                }
+              ]
+            },
+            {
+              "name": ".stack",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 432
+                }
+              ]
+            },
+            {
+              "name": ".text",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 112
+                }
+              ]
+            },
+            {
+              "name": "exidx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 8
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_bsp.o",
+          "sym": [
+            {
+              "name": "hal_bsp_flash_dev",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "hal_bsp_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/cmsis-core",
+      "files": [
+        {
+          "name": "cmsis_nvic.o",
+          "sym": [
+            {
+              "name": "NVIC_Relocate",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart/uart_hal",
+      "files": [
+        {
+          "name": "uart_hal.o",
+          "sym": [
+            {
+              "name": "uart_hal_blocking_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_close",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_open",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 114
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_resume",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 44
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_start_rx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_start_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_suspend",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "unlikely",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/hal",
+      "files": [
+        {
+          "name": "hal_flash.o",
+          "sym": [
+            {
+              "name": "hal_flash_check_addr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "hal_flash_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "hal_flash_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 68
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/mcu/nordic/nrf52xxx",
+      "files": [
+        {
+          "name": "hal_os_tick.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "nrf52_os_tick_set_ocmp",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            },
+            {
+              "name": "nrf52_timer_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 80
+                }
+              ]
+            },
+            {
+              "name": "os_tick_idle",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 68
+                }
+              ]
+            },
+            {
+              "name": "os_tick_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 148
+                }
+              ]
+            },
+            {
+              "name": "sub24",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 46
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "system_nrf52.o",
+          "sym": [
+            {
+              "name": "SystemCoreClock",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "SystemInit",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 480
+                }
+              ]
+            },
+            {
+              "name": "errata_16",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 52
+                }
+              ]
+            },
+            {
+              "name": "errata_31",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_timer.o",
+          "sym": [
+            {
+              "name": "__NVIC_SetPendingIRQ",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "hal_timer_config",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 212
+                }
+              ]
+            },
+            {
+              "name": "hal_timer_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 88
+                }
+              ]
+            },
+            {
+              "name": "hal_timer_read_bsptimer",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 56
+                }
+              ]
+            },
+            {
+              "name": "nrf52_hal_timers",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "nrf52_timer0_irq_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 168
+                }
+              ]
+            },
+            {
+              "name": "nrf_timer_set_ocmp",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 126
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_system.o",
+          "sym": [
+            {
+              "name": "hal_debugger_connected",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "hal_system_clock_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "hal_system_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "hal_system_reset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_gpio.o",
+          "sym": [
+            {
+              "name": "hal_gpio_init_out",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 38
+                }
+              ]
+            },
+            {
+              "name": "hal_gpio_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 34
+                }
+              ]
+            },
+            {
+              "name": "hal_gpio_toggle",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 26
+                }
+              ]
+            },
+            {
+              "name": "hal_gpio_write",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_uart.o",
+          "sym": [
+            {
+              "name": "hal_uart_blocking_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 80
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_close",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 52
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_config",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 408
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_init_cbs",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_start_rx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 56
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_start_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 72
+                }
+              ]
+            },
+            {
+              "name": "hal_uart_tx_fill_buf",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "uart0",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "uart0_irq_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 128
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_watchdog.o",
+          "sym": [
+            {
+              "name": "hal_watchdog_enable",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "hal_watchdog_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 112
+                }
+              ]
+            },
+            {
+              "name": "hal_watchdog_tickle",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "nrf52_wdt_irq_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "nrf52_periph.o",
+          "sym": [
+            {
+              "name": "nrf52_periph_create",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 90
+                }
+              ]
+            },
+            {
+              "name": "os_bsp_uart0",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "os_bsp_uart0_cfg",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 4
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_flash.o",
+          "sym": [
+            {
+              "name": "nrf52k_flash_dev",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_erase_sector",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 76
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_funcs",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_sector_info",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_wait_ready",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "nrf52k_flash_write",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 200
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/kernel/os",
+      "files": [
+        {
+          "name": "HAL_CM4.o",
+          "sym": [
+            {
+              "name": ".text",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 200
+                }
+              ]
+            },
+            {
+              "name": "exidx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 217
+                }
+              ]
+            },
+            {
+              "name": "g_idle_task_stack",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 256
+                }
+              ]
+            },
+            {
+              "name": "g_os_main_stack",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4096
+                }
+              ]
+            },
+            {
+              "name": "os_idle_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 124
+                }
+              ]
+            },
+            {
+              "name": "os_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 149
+                }
+              ]
+            },
+            {
+              "name": "os_init_idle_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 93
+                }
+              ]
+            },
+            {
+              "name": "os_main",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 22
+                }
+              ]
+            },
+            {
+              "name": "os_pkg_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            },
+            {
+              "name": "os_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 26
+                }
+              ]
+            },
+            {
+              "name": "os_started",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_fault.o",
+          "sym": [
+            {
+              "name": "__assert_func",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "os_default_irq",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_time.o",
+          "sym": [
+            {
+              "name": "basetod",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 48
+                }
+              ]
+            },
+            {
+              "name": "os_deltatime",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 76
+                }
+              ]
+            },
+            {
+              "name": "os_time_advance",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 120
+                }
+              ]
+            },
+            {
+              "name": "os_time_delay",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "os_time_get",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_msys.o",
+          "sym": [
+            {
+              "name": "g_msys_pool_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_msys_1_data",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 3504
+                }
+              ]
+            },
+            {
+              "name": "os_msys_1_mbuf_pool",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_msys_1_mempool",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "os_msys_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 103
+                }
+              ]
+            },
+            {
+              "name": "os_msys_register",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 64
+                }
+              ]
+            },
+            {
+              "name": "os_msys_reset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_dev.o",
+          "sym": [
+            {
+              "name": "g_os_dev_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_dev_create",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 136
+                }
+              ]
+            },
+            {
+              "name": "os_dev_initialize",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "os_dev_initialize_all",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "os_dev_reset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_sched.o",
+          "sym": [
+            {
+              "name": "g_os_run_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "g_os_sleep_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_sched",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "os_sched_ctx_sw_hook",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 44
+                }
+              ]
+            },
+            {
+              "name": "os_sched_get_current_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_sched_insert",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 76
+                }
+              ]
+            },
+            {
+              "name": "os_sched_next_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_sched_os_timer_exp",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "os_sched_resort",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "os_sched_set_current_task",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_sched_sleep",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 116
+                }
+              ]
+            },
+            {
+              "name": "os_sched_wakeup",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            },
+            {
+              "name": "os_sched_wakeup_ticks",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 52
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_sanity.o",
+          "sym": [
+            {
+              "name": "g_os_sanity_check_list",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_list_lock",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 28
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_list_unlock",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_check_register",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 32
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "os_sanity_run",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 80
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_arch_arm.o",
+          "sym": [
+            {
+              "name": "os_arch_ctx_sw",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "os_arch_in_critical",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 10
+                }
+              ]
+            },
+            {
+              "name": "os_arch_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "os_arch_os_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 148
+                }
+              ]
+            },
+            {
+              "name": "os_arch_os_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 84
+                }
+              ]
+            },
+            {
+              "name": "os_arch_restore_sr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "os_arch_save_sr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "os_arch_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            },
+            {
+              "name": "os_arch_task_stack_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 54
+                }
+              ]
+            },
+            {
+              "name": "os_flags",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "timer_handler",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_callout.o",
+          "sym": [
+            {
+              "name": "os_callout_tick",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 84
+                }
+              ]
+            },
+            {
+              "name": "os_callout_wakeup_ticks",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 48
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_cputime.o",
+          "sym": [
+            {
+              "name": "os_cputime_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 8
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_eventq.o",
+          "sym": [
+            {
+              "name": "os_eventq_dflt_get",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "os_eventq_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "os_eventq_main",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "os_eventq_put",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_mbuf.o",
+          "sym": [
+            {
+              "name": "os_mbuf_pool_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 10
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_mempool.o",
+          "sym": [
+            {
+              "name": "os_mempool_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 18
+                }
+              ]
+            },
+            {
+              "name": "os_mempool_init_internal",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            },
+            {
+              "name": "os_mempool_module_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_mutex.o",
+          "sym": [
+            {
+              "name": "os_mutex_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "os_mutex_pend",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 220
+                }
+              ]
+            },
+            {
+              "name": "os_mutex_release",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 160
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "os_task.o",
+          "sym": [
+            {
+              "name": "os_task_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 228
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/libc/baselibc",
+      "files": [
+        {
+          "name": "start.o",
+          "sym": [
+            {
+              "name": "_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 2
+                }
+              ]
+            },
+            {
+              "name": "_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "memcpy.o",
+          "sym": [
+            {
+              "name": "memcpy",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 26
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "memset.o",
+          "sym": [
+            {
+              "name": "memset",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "strlen.o",
+          "sym": [
+            {
+              "name": "strlen",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/flash_map",
+      "files": [
+        {
+          "name": "flash_map.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 8
+                }
+              ]
+            },
+            {
+              "name": "flash_area_open",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 60
+                }
+              ]
+            },
+            {
+              "name": "flash_area_read",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 36
+                }
+              ]
+            },
+            {
+              "name": "flash_map_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 172
+                }
+              ]
+            },
+            {
+              "name": "mfg_areas",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 120
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/modlog",
+      "files": [
+        {
+          "name": "modlog.o",
+          "sym": [
+            {
+              "name": "modlog_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 2
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/mfg",
+      "files": [
+        {
+          "name": "mfg.o",
+          "sym": [
+            {
+              "name": "mfg_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 92
+                }
+              ]
+            },
+            {
+              "name": "mfg_initialized",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 1
+                }
+              ]
+            },
+            {
+              "name": "mfg_mmrs",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 24
+                }
+              ]
+            },
+            {
+              "name": "mfg_num_mmrs",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "mfg_open",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_next_mmr",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 164
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_tlv_body",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 100
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_tlv_flash_area",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "mfg_read_tlv_mmr_ref",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "mfg_seek_next",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 128
+                }
+              ]
+            },
+            {
+              "name": "mfg_seek_next_with_type",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 22
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sysinit",
+      "files": [
+        {
+          "name": "sysinit.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 1
+                }
+              ]
+            },
+            {
+              "name": "sysinit_dflt_panic_cb",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 6
+                }
+              ]
+            },
+            {
+              "name": "sysinit_end",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "sysinit_panic_cb",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "sysinit_start",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 12
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/util/mem",
+      "files": [
+        {
+          "name": "mem.o",
+          "sym": [
+            {
+              "name": "mem_init_mbuf_pool",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 50
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "blinky-nordic_pca10040-sysinit-app",
+      "files": [
+        {
+          "name": "blinky-nordic_pca10040-sysflash.o",
+          "sym": [
+            {
+              "name": "sysflash_map_dflt",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 72
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "blinky-nordic_pca10040-sysinit-app.o",
+          "sym": [
+            {
+              "name": "sysinit_app",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 22
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
diff --git a/image/testdata/mismatch-version.img b/image/testdata/mismatch-version.img
new file mode 100644
index 0000000..2e5c47a
Binary files /dev/null and b/image/testdata/mismatch-version.img differ
diff --git a/image/testdata/mismatch-version.json b/image/testdata/mismatch-version.json
new file mode 100644
index 0000000..c8b4872
--- /dev/null
+++ b/image/testdata/mismatch-version.json
@@ -0,0 +1,2267 @@
+{
+  "name": "targets/blinky-nordic_pca10040",
+  "build_time": "2019-06-17T17:16:49-07:00",
+  "build_version": "9.0.0.0",
+  "id": "8eb006d574ace63cce18a1f2d8f0f2645f1a0e8630a39fb86bbfbb805d4cd3b9",
+  "image": "/Users/ccollins/proj/myproj/bin/targets/blinky-nordic_pca10040/app/apps/blinky/blinky.img",
+  "image_hash": "8eb006d574ace63cce18a1f2d8f0f2645f1a0e8630a39fb86bbfbb805d4cd3b9",
+  "loader": "",
+  "loader_hash": "",
+  "pkgs": [
+    {
+      "name": "apps/blinky",
+      "repo": "my_project"
+    },
+    {
+      "name": "blinky-nordic_pca10040-sysinit-app",
+      "repo": "my_project"
+    },
+    {
+      "name": "@apache-mynewt-core/compiler/arm-none-eabi-m4",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/bsp/nordic_pca10040",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/cmsis-core",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart/uart_hal",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/hal",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/mcu/nordic",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/hw/mcu/nordic/nrf52xxx",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/kernel/os",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/libc/baselibc",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/console/stub",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/defs",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/flash_map",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/common",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/modlog",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/log/stub",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/mfg",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sys",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sysdown",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/sys/sysinit",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "targets/blinky-nordic_pca10040",
+      "repo": "my_project"
+    },
+    {
+      "name": "@apache-mynewt-core/util/mem",
+      "repo": "apache-mynewt-core"
+    },
+    {
+      "name": "@apache-mynewt-core/util/rwlock",
+      "repo": "apache-mynewt-core"
+    }
+  ],
+  "target": [
+    "target.app=apps/blinky",
+    "target.bsp=@apache-mynewt-core/hw/bsp/nordic_pca10040",
+    "target.build_profile=optimized"
+  ],
+  "repos": [
+    {
+      "name": "apache-mynewt-core",
+      "commit": "fa5e4c31b0bea2b107747e3b0d40002d3bef4132",
+      "url": "git@github.com:apache/mynewt-core.git"
+    },
+    {
+      "name": "my_project",
+      "commit": "UNKNOWN"
+    }
+  ],
+  "syscfg": {
+    "ADC_0": "0",
+    "ADC_0_REFMV_0": "0",
+    "APP_NAME": "\"blinky\"",
+    "APP_blinky": "1",
+    "ARCH_NAME": "\"cortex_m4\"",
+    "ARCH_cortex_m4": "1",
+    "BASELIBC_ASSERT_FILE_LINE": "0",
+    "BASELIBC_PRESENT": "1",
+    "BSP_NAME": "\"nordic_pca10040\"",
+    "BSP_NRF52": "1",
+    "BSP_nordic_pca10040": "1",
+    "CONSOLE_UART_BAUD": "115200",
+    "CONSOLE_UART_DEV": "\"uart0\"",
+    "CONSOLE_UART_FLOW_CONTROL": "UART_FLOW_CTL_NONE",
+    "CRYPTO": "0",
+    "DEBUG_PANIC_ENABLED": "1",
+    "ENC_FLASH_DEV": "0",
+    "FLASH_MAP_MAX_AREAS": "10",
+    "FLASH_MAP_SYSINIT_STAGE": "2",
+    "FLOAT_USER": "0",
+    "GPIO_AS_PIN_RESET": "0",
+    "HAL_FLASH_VERIFY_BUF_SZ": "16",
+    "HAL_FLASH_VERIFY_ERASES": "0",
+    "HAL_FLASH_VERIFY_WRITES": "0",
+    "HAL_SYSTEM_RESET_CB": "0",
+    "HARDFLOAT": "0",
+    "I2C_0": "0",
+    "I2C_0_FREQ_KHZ": "100",
+    "I2C_0_PIN_SCL": "27",
+    "I2C_0_PIN_SDA": "26",
+    "I2C_1": "0",
+    "I2C_1_FREQ_KHZ": "100",
+    "I2C_1_PIN_SCL": "",
+    "I2C_1_PIN_SDA": "",
+    "LOG_CONSOLE": "1",
+    "LOG_FCB": "0",
+    "LOG_FCB_SLOT1": "0",
+    "LOG_LEVEL": "255",
+    "MCU_BUS_DRIVER_I2C_USE_TWIM": "0",
+    "MCU_DCDC_ENABLED": "1",
+    "MCU_DEBUG_IGNORE_BKPT": "0",
+    "MCU_FLASH_MIN_WRITE_SIZE": "1",
+    "MCU_GPIO_USE_PORT_EVENT": "0",
+    "MCU_I2C_RECOVERY_DELAY_USEC": "100",
+    "MCU_LFCLK_SOURCE": "LFXO",
+    "MCU_NRF52832": "0",
+    "MCU_NRF52840": "0",
+    "MCU_TARGET": "nRF52832",
+    "MFG_LOG_MODULE": "128",
+    "MFG_MAX_MMRS": "2",
+    "MFG_SYSINIT_STAGE": "100",
+    "MODLOG_CONSOLE_DFLT": "1",
+    "MODLOG_LOG_MACROS": "0",
+    "MODLOG_MAX_MAPPINGS": "16",
+    "MODLOG_MAX_PRINTF_LEN": "128",
+    "MODLOG_SYSINIT_STAGE": "100",
+    "MSYS_1_BLOCK_COUNT": "12",
+    "MSYS_1_BLOCK_SIZE": "292",
+    "MSYS_1_SANITY_MIN_COUNT": "0",
+    "MSYS_2_BLOCK_COUNT": "0",
+    "MSYS_2_BLOCK_SIZE": "0",
+    "MSYS_2_SANITY_MIN_COUNT": "0",
+    "MSYS_SANITY_TIMEOUT": "60000",
+    "NEWT_FEATURE_LOGCFG": "1",
+    "NEWT_FEATURE_SYSDOWN": "1",
+    "NFC_PINS_AS_GPIO": "1",
+    "OS_ASSERT_CB": "0",
+    "OS_CLI": "0",
+    "OS_COREDUMP": "0",
+    "OS_CPUTIME_FREQ": "1000000",
+    "OS_CPUTIME_TIMER_NUM": "0",
+    "OS_CRASH_FILE_LINE": "0",
+    "OS_CRASH_LOG": "0",
+    "OS_CRASH_RESTORE_REGS": "0",
+    "OS_CRASH_STACKTRACE": "0",
+    "OS_CTX_SW_STACK_CHECK": "0",
+    "OS_CTX_SW_STACK_GUARD": "4",
+    "OS_DEBUG_MODE": "0",
+    "OS_EVENTQ_DEBUG": "0",
+    "OS_EVENTQ_MONITOR": "0",
+    "OS_IDLE_TICKLESS_MS_MAX": "600000",
+    "OS_IDLE_TICKLESS_MS_MIN": "100",
+    "OS_MAIN_STACK_SIZE": "1024",
+    "OS_MAIN_TASK_PRIO": "127",
+    "OS_MAIN_TASK_SANITY_ITVL_MS": "0",
+    "OS_MEMPOOL_CHECK": "0",
+    "OS_MEMPOOL_GUARD": "0",
+    "OS_MEMPOOL_POISON": "0",
+    "OS_SCHEDULING": "1",
+    "OS_SYSINIT_STAGE": "0",
+    "OS_SYSVIEW": "0",
+    "OS_SYSVIEW_TRACE_CALLOUT": "1",
+    "OS_SYSVIEW_TRACE_EVENTQ": "1",
+    "OS_SYSVIEW_TRACE_MBUF": "0",
+    "OS_SYSVIEW_TRACE_MEMPOOL": "0",
+    "OS_SYSVIEW_TRACE_MUTEX": "1",
+    "OS_SYSVIEW_TRACE_SEM": "1",
+    "OS_TIME_DEBUG": "0",
+    "OS_WATCHDOG_MONITOR": "0",
+    "PWM_0": "0",
+    "PWM_1": "0",
+    "PWM_2": "0",
+    "PWM_3": "0",
+    "QSPI_ADDRMODE": "0",
+    "QSPI_DPMCONFIG": "0",
+    "QSPI_ENABLE": "0",
+    "QSPI_FLASH_PAGE_SIZE": "0",
+    "QSPI_FLASH_SECTOR_COUNT": "-1",
+    "QSPI_FLASH_SECTOR_SIZE": "0",
+    "QSPI_PIN_CS": "-1",
+    "QSPI_PIN_DIO0": "-1",
+    "QSPI_PIN_DIO1": "-1",
+    "QSPI_PIN_DIO2": "-1",
+    "QSPI_PIN_DIO3": "-1",
+    "QSPI_PIN_SCK": "-1",
+    "QSPI_READOC": "0",
+    "QSPI_SCK_DELAY": "0",
+    "QSPI_SCK_FREQ": "0",
+    "QSPI_SPI_MODE": "0",
+    "QSPI_WRITEOC": "0",
+    "RAM_RESIDENT": "0",
+    "RWLOCK_DEBUG": "0",
+    "SANITY_INTERVAL": "15000",
+    "SOFT_PWM": "0",
+    "SPI_0_MASTER": "0",
+    "SPI_0_MASTER_PIN_MISO": "25",
+    "SPI_0_MASTER_PIN_MOSI": "24",
+    "SPI_0_MASTER_PIN_SCK": "23",
+    "SPI_0_SLAVE": "0",
+    "SPI_0_SLAVE_PIN_MISO": "25",
+    "SPI_0_SLAVE_PIN_MOSI": "24",
+    "SPI_0_SLAVE_PIN_SCK": "23",
+    "SPI_0_SLAVE_PIN_SS": "22",
+    "SPI_1_MASTER": "0",
+    "SPI_1_MASTER_PIN_MISO": "",
+    "SPI_1_MASTER_PIN_MOSI": "",
+    "SPI_1_MASTER_PIN_SCK": "",
+    "SPI_1_SLAVE": "0",
+    "SPI_1_SLAVE_PIN_MISO": "",
+    "SPI_1_SLAVE_PIN_MOSI": "",
+    "SPI_1_SLAVE_PIN_SCK": "",
+    "SPI_1_SLAVE_PIN_SS": "",
+    "SPI_2_MASTER": "0",
+    "SPI_2_MASTER_PIN_MISO": "",
+    "SPI_2_MASTER_PIN_MOSI": "",
+    "SPI_2_MASTER_PIN_SCK": "",
+    "SPI_2_SLAVE": "0",
+    "SPI_2_SLAVE_PIN_MISO": "",
+    "SPI_2_SLAVE_PIN_MOSI": "",
+    "SPI_2_SLAVE_PIN_SCK": "",
+    "SPI_2_SLAVE_PIN_SS": "",
+    "SPI_3_MASTER": "0",
+    "SPI_3_MASTER_PIN_MISO": "",
+    "SPI_3_MASTER_PIN_MOSI": "",
+    "SPI_3_MASTER_PIN_SCK": "",
+    "SPI_3_SLAVE": "0",
+    "SPI_3_SLAVE_PIN_MISO": "",
+    "SPI_3_SLAVE_PIN_MOSI": "",
+    "SPI_3_SLAVE_PIN_SCK": "",
+    "SPI_3_SLAVE_PIN_SS": "",
+    "SYSDOWN_CONSTRAIN_DOWN": "1",
+    "SYSDOWN_PANIC_FILE_LINE": "0",
+    "SYSDOWN_PANIC_MESSAGE": "0",
+    "SYSDOWN_TIMEOUT_MS": "10000",
+    "SYSINIT_CONSTRAIN_INIT": "1",
+    "SYSINIT_PANIC_FILE_LINE": "0",
+    "SYSINIT_PANIC_MESSAGE": "0",
+    "TARGET_NAME": "\"blinky-nordic_pca10040\"",
+    "TARGET_blinky_nordic_pca10040": "1",
+    "TIMER_0": "1",
+    "TIMER_1": "0",
+    "TIMER_2": "0",
+    "TIMER_3": "0",
+    "TIMER_4": "0",
+    "TIMER_5": "0",
+    "TRNG": "0",
+    "UARTBB_0": "0",
+    "UARTBB_0_PIN_RX": "-1",
+    "UARTBB_0_PIN_TX": "-1",
+    "UART_0": "1",
+    "UART_0_PIN_CTS": "7",
+    "UART_0_PIN_RTS": "5",
+    "UART_0_PIN_RX": "8",
+    "UART_0_PIN_TX": "6",
+    "UART_1": "0",
+    "UART_1_PIN_CTS": "-1",
+    "UART_1_PIN_RTS": "-1",
+    "UART_1_PIN_RX": "",
+    "UART_1_PIN_TX": "",
+    "WATCHDOG_INTERVAL": "30000",
+    "XTAL_32768": "0",
+    "XTAL_32768_SYNTH": "0",
+    "XTAL_RC": "0"
+  },
+  "pkgsz": [
+    {
+      "name": "*fill*",
+      "files": [
+        {
+          "name": "",
+          "sym": [
+            {
+              "name": "*fill*",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 63
+                }
+              ]
+            },
+            {
+              "name": "*fill*",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 233
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "apps/blinky",
+      "files": [
+        {
+          "name": "main.o",
+          "sym": [
+            {
+              "name": "COMMON",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "g_task1_loops",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 4
+                }
+              ]
+            },
+            {
+              "name": "startup",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 56
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/bsp/nordic_pca10040",
+      "files": [
+        {
+          "name": "sbrk.o",
+          "sym": [
+            {
+              "name": ".data",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 12
+                }
+              ]
+            },
+            {
+              "name": "_sbrkInit",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "gcc_startup_nrf52.o",
+          "sym": [
+            {
+              "name": ".isr_vector",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 216
+                }
+              ]
+            },
+            {
+              "name": ".stack",
+              "areas": [
+                {
+                  "name": "RAM",
+                  "size": 432
+                }
+              ]
+            },
+            {
+              "name": ".text",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 112
+                }
+              ]
+            },
+            {
+              "name": "exidx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 8
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "name": "hal_bsp.o",
+          "sym": [
+            {
+              "name": "hal_bsp_flash_dev",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 16
+                }
+              ]
+            },
+            {
+              "name": "hal_bsp_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/cmsis-core",
+      "files": [
+        {
+          "name": "cmsis_nvic.o",
+          "sym": [
+            {
+              "name": "NVIC_Relocate",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/drivers/uart/uart_hal",
+      "files": [
+        {
+          "name": "uart_hal.o",
+          "sym": [
+            {
+              "name": "uart_hal_blocking_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_close",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_init",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 104
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_open",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 114
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_resume",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 44
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_start_rx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_start_tx",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 20
+                }
+              ]
+            },
+            {
+              "name": "uart_hal_suspend",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 40
+                }
+              ]
+            },
+            {
+              "name": "unlikely",
+              "areas": [
+                {
+                  "name": "FLASH",
+                  "size": 14
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "@apache-mynewt-core/hw/hal",
+      "files": [
+        {
+          "name": "hal_flash.o",
+          "sym": [
+            {
+              "name": "hal_flash_check_addr",
+              "areas": [
+                {
... 2915 lines suppressed ...


[mynewt-artifact] 05/23: Fix int overflow on 32-bit platforms.

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

commit 353f34383c32b3c935cbe9c3e410527c4c26a184
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Wed Jan 2 13:15:38 2019 -0800

    Fix int overflow on 32-bit platforms.
---
 image/image.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/image/image.go b/image/image.go
index 705abe6..6bd0050 100644
--- a/image/image.go
+++ b/image/image.go
@@ -440,7 +440,7 @@ func parseRawHeader(imgData []byte, offset int) (ImageHdr, int, error) {
 	if hdr.Magic != IMAGE_MAGIC {
 		return hdr, 0, util.FmtNewtError(
 			"Image magic incorrect; expected 0x%08x, got 0x%08x",
-			IMAGE_MAGIC, hdr.Magic)
+			uint32(IMAGE_MAGIC), hdr.Magic)
 	}
 
 	remLen := len(imgData) - offset


[mynewt-artifact] 20/23: Add .gitignore

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

commit 9515a56a55a5a3556403f39ee189a28fb4177881
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Fri Jun 21 10:26:44 2019 -0700

    Add .gitignore
---
 .gitignore | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..21e99b0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+*.swp
+*.swo
+*~
+tags


[mynewt-artifact] 01/23: "artifact" library

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

commit 3025a27accf1a7fc773c0d811c4f9fc4df5a4daa
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Tue Nov 20 17:41:07 2018 -0800

    "artifact" library
    
    This library subsumes functionality that is currently tied to the newt
    tool: Parsing, manipulation, and writing of artifacts that aren't
    described by a Mynewt project.
    
    Specifically:
    * `.img` file parsing and generation
    * Manifest file parsing
---
 flash/flash.go       | 160 +++++++++++++++
 image/create.go      | 427 ++++++++++++++++++++++++++++++++++++++++
 image/encrypted.go   | 196 +++++++++++++++++++
 image/image.go       | 544 +++++++++++++++++++++++++++++++++++++++++++++++++++
 image/key.go         | 294 ++++++++++++++++++++++++++++
 image/keys_test.go   | 244 +++++++++++++++++++++++
 image/v1.go          | 487 +++++++++++++++++++++++++++++++++++++++++++++
 manifest/manifest.go |  95 +++++++++
 8 files changed, 2447 insertions(+)

diff --git a/flash/flash.go b/flash/flash.go
new file mode 100644
index 0000000..c37d2dd
--- /dev/null
+++ b/flash/flash.go
@@ -0,0 +1,160 @@
+/**
+ * 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 flash
+
+import (
+	"fmt"
+	"sort"
+)
+
+const FLASH_AREA_NAME_BOOTLOADER = "FLASH_AREA_BOOTLOADER"
+const FLASH_AREA_NAME_IMAGE_0 = "FLASH_AREA_IMAGE_0"
+const FLASH_AREA_NAME_IMAGE_1 = "FLASH_AREA_IMAGE_1"
+const FLASH_AREA_NAME_IMAGE_SCRATCH = "FLASH_AREA_IMAGE_SCRATCH"
+const AREA_USER_ID_MIN = 16
+
+var SYSTEM_AREA_NAME_ID_MAP = map[string]int{
+	FLASH_AREA_NAME_BOOTLOADER:    0,
+	FLASH_AREA_NAME_IMAGE_0:       1,
+	FLASH_AREA_NAME_IMAGE_1:       2,
+	FLASH_AREA_NAME_IMAGE_SCRATCH: 3,
+}
+
+type FlashArea struct {
+	Name   string
+	Id     int
+	Device int
+	Offset int
+	Size   int
+}
+
+type areaOffSorter struct {
+	areas []FlashArea
+}
+
+func (s areaOffSorter) Len() int {
+	return len(s.areas)
+}
+func (s areaOffSorter) Swap(i, j int) {
+	s.areas[i], s.areas[j] = s.areas[j], s.areas[i]
+}
+func (s areaOffSorter) Less(i, j int) bool {
+	ai := s.areas[i]
+	aj := s.areas[j]
+
+	if ai.Device < aj.Device {
+		return true
+	}
+	if ai.Device > aj.Device {
+		return false
+	}
+	return ai.Offset < aj.Offset
+}
+
+func SortFlashAreasByDevOff(areas []FlashArea) []FlashArea {
+	sorter := areaOffSorter{
+		areas: make([]FlashArea, len(areas)),
+	}
+
+	for i, a := range areas {
+		sorter.areas[i] = a
+	}
+
+	sort.Sort(sorter)
+	return sorter.areas
+}
+
+func SortFlashAreasById(areas []FlashArea) []FlashArea {
+	idMap := make(map[int]FlashArea, len(areas))
+	ids := make([]int, 0, len(areas))
+	for _, area := range areas {
+		idMap[area.Id] = area
+		ids = append(ids, area.Id)
+	}
+	sort.Ints(ids)
+
+	sorted := make([]FlashArea, len(ids))
+	for i, id := range ids {
+		sorted[i] = idMap[id]
+	}
+
+	return sorted
+}
+
+func areasDistinct(a FlashArea, b FlashArea) bool {
+	var lo FlashArea
+	var hi FlashArea
+
+	if a.Offset < b.Offset {
+		lo = a
+		hi = b
+	} else {
+		lo = b
+		hi = a
+	}
+
+	return lo.Device != hi.Device || lo.Offset+lo.Size <= hi.Offset
+}
+
+// @return overlapping-areas, id-conflicts.
+func DetectErrors(areas []FlashArea) ([][]FlashArea, [][]FlashArea) {
+	var overlaps [][]FlashArea
+	var conflicts [][]FlashArea
+
+	for i := 0; i < len(areas)-1; i++ {
+		iarea := areas[i]
+		for j := i + 1; j < len(areas); j++ {
+			jarea := areas[j]
+
+			if !areasDistinct(iarea, jarea) {
+				overlaps = append(overlaps, []FlashArea{iarea, jarea})
+			}
+
+			if iarea.Id == jarea.Id {
+				conflicts = append(conflicts, []FlashArea{iarea, jarea})
+			}
+		}
+	}
+
+	return overlaps, conflicts
+}
+
+func ErrorText(overlaps [][]FlashArea, conflicts [][]FlashArea) string {
+	str := ""
+
+	if len(conflicts) > 0 {
+		str += "Conflicting flash area IDs detected:\n"
+
+		for _, pair := range conflicts {
+			str += fmt.Sprintf("    (%d) %s =/= %s\n",
+				pair[0].Id-AREA_USER_ID_MIN, pair[0].Name, pair[1].Name)
+		}
+	}
+
+	if len(overlaps) > 0 {
+		str += "Overlapping flash areas detected:\n"
+
+		for _, pair := range overlaps {
+			str += fmt.Sprintf("    %s =/= %s\n", pair[0].Name, pair[1].Name)
+		}
+	}
+
+	return str
+}
diff --git a/image/create.go b/image/create.go
new file mode 100644
index 0000000..5b28120
--- /dev/null
+++ b/image/create.go
@@ -0,0 +1,427 @@
+/**
+ * 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 image
+
+import (
+	"bytes"
+	"crypto"
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/ecdsa"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha256"
+	"encoding/asn1"
+	"encoding/binary"
+	"encoding/hex"
+	"io"
+	"io/ioutil"
+	"math/big"
+
+	"mynewt.apache.org/newt/util"
+)
+
+type ImageCreator struct {
+	Body         []byte
+	Version      ImageVersion
+	SigKeys      []ImageSigKey
+	PlainSecret  []byte
+	CipherSecret []byte
+	HeaderSize   int
+	InitialHash  []byte
+	Bootable     bool
+}
+
+type ImageCreateOpts struct {
+	SrcBinFilename    string
+	SrcEncKeyFilename string
+	Version           ImageVersion
+	SigKeys           []ImageSigKey
+	LoaderHash        []byte
+}
+
+type ECDSASig struct {
+	R *big.Int
+	S *big.Int
+}
+
+func NewImageCreator() ImageCreator {
+	return ImageCreator{
+		HeaderSize: IMAGE_HEADER_SIZE,
+		Bootable:   true,
+	}
+}
+
+func generateEncTlv(cipherSecret []byte) (ImageTlv, error) {
+	var encType uint8
+
+	if len(cipherSecret) == 256 {
+		encType = IMAGE_TLV_ENC_RSA
+	} else if len(cipherSecret) == 24 {
+		encType = IMAGE_TLV_ENC_KEK
+	} else {
+		return ImageTlv{}, util.FmtNewtError("Invalid enc TLV size ")
+	}
+
+	return ImageTlv{
+		Header: ImageTlvHdr{
+			Type: encType,
+			Pad:  0,
+			Len:  uint16(len(cipherSecret)),
+		},
+		Data: cipherSecret,
+	}, nil
+}
+
+func generateSigRsa(key *rsa.PrivateKey, hash []byte) ([]byte, error) {
+	opts := rsa.PSSOptions{
+		SaltLength: rsa.PSSSaltLengthEqualsHash,
+	}
+	signature, err := rsa.SignPSS(
+		rand.Reader, key, crypto.SHA256, hash, &opts)
+	if err != nil {
+		return nil, util.FmtNewtError("Failed to compute signature: %s", err)
+	}
+
+	return signature, nil
+}
+
+func generateSigTlvRsa(key ImageSigKey, hash []byte) (ImageTlv, error) {
+	sig, err := generateSigRsa(key.Rsa, hash)
+	if err != nil {
+		return ImageTlv{}, err
+	}
+
+	return ImageTlv{
+		Header: ImageTlvHdr{
+			Type: key.sigTlvType(),
+			Pad:  0,
+			Len:  256, /* 2048 bits */
+		},
+		Data: sig,
+	}, nil
+}
+
+func generateSigEc(key *ecdsa.PrivateKey, hash []byte) ([]byte, error) {
+	r, s, err := ecdsa.Sign(rand.Reader, key, hash)
+	if err != nil {
+		return nil, util.FmtNewtError("Failed to compute signature: %s", err)
+	}
+
+	ECDSA := ECDSASig{
+		R: r,
+		S: s,
+	}
+
+	signature, err := asn1.Marshal(ECDSA)
+	if err != nil {
+		return nil, util.FmtNewtError("Failed to construct signature: %s", err)
+	}
+
+	return signature, nil
+}
+
+func generateSigTlvEc(key ImageSigKey, hash []byte) (ImageTlv, error) {
+	sig, err := generateSigEc(key.Ec, hash)
+	if err != nil {
+		return ImageTlv{}, err
+	}
+
+	sigLen := key.sigLen()
+	if len(sig) > int(sigLen) {
+		return ImageTlv{}, util.FmtNewtError("Something is really wrong\n")
+	}
+
+	b := &bytes.Buffer{}
+
+	if _, err := b.Write(sig); err != nil {
+		return ImageTlv{},
+			util.FmtNewtError("Failed to append sig: %s", err.Error())
+	}
+
+	pad := make([]byte, int(sigLen)-len(sig))
+	if _, err := b.Write(pad); err != nil {
+		return ImageTlv{}, util.FmtNewtError(
+			"Failed to serialize image trailer: %s", err.Error())
+	}
+
+	return ImageTlv{
+		Header: ImageTlvHdr{
+			Type: key.sigTlvType(),
+			Pad:  0,
+			Len:  sigLen,
+		},
+		Data: b.Bytes(),
+	}, nil
+}
+
+func generateSigTlv(key ImageSigKey, hash []byte) (ImageTlv, error) {
+	key.assertValid()
+
+	if key.Rsa != nil {
+		return generateSigTlvRsa(key, hash)
+	} else {
+		return generateSigTlvEc(key, hash)
+	}
+}
+
+func generateKeyHashTlv(key ImageSigKey) (ImageTlv, error) {
+	key.assertValid()
+
+	keyHash, err := key.sigKeyHash()
+	if err != nil {
+		return ImageTlv{}, util.FmtNewtError(
+			"Failed to compute hash of the public key: %s", err.Error())
+	}
+
+	return ImageTlv{
+		Header: ImageTlvHdr{
+			Type: IMAGE_TLV_KEYHASH,
+			Pad:  0,
+			Len:  uint16(len(keyHash)),
+		},
+		Data: keyHash,
+	}, nil
+}
+
+func GenerateSigTlvs(keys []ImageSigKey, hash []byte) ([]ImageTlv, error) {
+	var tlvs []ImageTlv
+
+	for _, key := range keys {
+		key.assertValid()
+
+		tlv, err := generateKeyHashTlv(key)
+		if err != nil {
+			return nil, err
+		}
+		tlvs = append(tlvs, tlv)
+
+		tlv, err = generateSigTlv(key, hash)
+		if err != nil {
+			return nil, err
+		}
+		tlvs = append(tlvs, tlv)
+	}
+
+	return tlvs, nil
+}
+
+func GenerateImage(opts ImageCreateOpts) (Image, error) {
+	ic := NewImageCreator()
+
+	srcBin, err := ioutil.ReadFile(opts.SrcBinFilename)
+	if err != nil {
+		return Image{}, util.FmtNewtError(
+			"Can't read app binary: %s", err.Error())
+	}
+
+	ic.Body = srcBin
+	ic.Version = opts.Version
+	ic.SigKeys = opts.SigKeys
+
+	if opts.LoaderHash != nil {
+		ic.InitialHash = opts.LoaderHash
+		ic.Bootable = false
+	} else {
+		ic.Bootable = true
+	}
+
+	if opts.SrcEncKeyFilename != "" {
+		plainSecret := make([]byte, 16)
+		if _, err := rand.Read(plainSecret); err != nil {
+			return Image{}, util.FmtNewtError(
+				"Random generation error: %s\n", err)
+		}
+
+		cipherSecret, err := ReadEncKey(opts.SrcEncKeyFilename, plainSecret)
+		if err != nil {
+			return Image{}, err
+		}
+
+		ic.PlainSecret = plainSecret
+		ic.CipherSecret = cipherSecret
+	}
+
+	ri, err := ic.Create()
+	if err != nil {
+		return Image{}, err
+	}
+
+	return ri, nil
+}
+
+func calcHash(initialHash []byte, hdr ImageHdr,
+	plainBody []byte) ([]byte, error) {
+
+	hash := sha256.New()
+
+	add := func(itf interface{}) error {
+		b := &bytes.Buffer{}
+		if err := binary.Write(b, binary.LittleEndian, itf); err != nil {
+			return err
+		}
+		if err := binary.Write(hash, binary.LittleEndian, itf); err != nil {
+			return util.FmtNewtError("Failed to hash data: %s", err.Error())
+		}
+
+		return nil
+	}
+
+	if initialHash != nil {
+		if err := add(initialHash); err != nil {
+			return nil, err
+		}
+	}
+
+	if err := add(hdr); err != nil {
+		return nil, err
+	}
+
+	extra := hdr.HdrSz - IMAGE_HEADER_SIZE
+	if extra > 0 {
+		b := make([]byte, extra)
+		if err := add(b); err != nil {
+			return nil, err
+		}
+	}
+
+	if err := add(plainBody); err != nil {
+		return nil, err
+	}
+
+	return hash.Sum(nil), nil
+}
+
+func (ic *ImageCreator) Create() (Image, error) {
+	ri := Image{}
+
+	// First the header
+	hdr := ImageHdr{
+		Magic: IMAGE_MAGIC,
+		Pad1:  0,
+		HdrSz: IMAGE_HEADER_SIZE,
+		Pad2:  0,
+		ImgSz: uint32(len(ic.Body)),
+		Flags: 0,
+		Vers:  ic.Version,
+		Pad3:  0,
+	}
+
+	if !ic.Bootable {
+		hdr.Flags |= IMAGE_F_NON_BOOTABLE
+	}
+
+	if ic.CipherSecret != nil {
+		hdr.Flags |= IMAGE_F_ENCRYPTED
+	}
+
+	if ic.HeaderSize != 0 {
+		// Pad the header out to the given size.  There will
+		// just be zeros between the header and the start of
+		// the image when it is padded.
+		extra := ic.HeaderSize - IMAGE_HEADER_SIZE
+		if extra < 0 {
+			return ri, util.FmtNewtError("Image header must be at "+
+				"least %d bytes", IMAGE_HEADER_SIZE)
+		}
+
+		hdr.HdrSz = uint16(ic.HeaderSize)
+		for i := 0; i < extra; i++ {
+			ri.Body = append(ri.Body, 0)
+		}
+	}
+
+	ri.Header = hdr
+
+	hashBytes, err := calcHash(ic.InitialHash, hdr, ic.Body)
+	if err != nil {
+		return ri, err
+	}
+
+	var stream cipher.Stream
+	if ic.CipherSecret != nil {
+		block, err := aes.NewCipher(ic.PlainSecret)
+		if err != nil {
+			return ri, util.NewNewtError("Failed to create block cipher")
+		}
+		nonce := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+		stream = cipher.NewCTR(block, nonce)
+	}
+
+	/*
+	 * Followed by data.
+	 */
+	dataBuf := make([]byte, 16)
+	encBuf := make([]byte, 16)
+	r := bytes.NewReader(ic.Body)
+	w := bytes.Buffer{}
+	for {
+		cnt, err := r.Read(dataBuf)
+		if err != nil && err != io.EOF {
+			return ri, util.FmtNewtError(
+				"Failed to read from image body: %s", err.Error())
+		}
+		if cnt == 0 {
+			break
+		}
+
+		if ic.CipherSecret == nil {
+			_, err = w.Write(dataBuf[0:cnt])
+		} else {
+			stream.XORKeyStream(encBuf, dataBuf[0:cnt])
+			_, err = w.Write(encBuf[0:cnt])
+		}
+		if err != nil {
+			return ri, util.FmtNewtError(
+				"Failed to write to image body: %s", err.Error())
+		}
+	}
+	ri.Body = append(ri.Body, w.Bytes()...)
+
+	util.StatusMessage(util.VERBOSITY_VERBOSE,
+		"Computed Hash for image as %s\n", hex.EncodeToString(hashBytes))
+
+	// Hash TLV.
+	tlv := ImageTlv{
+		Header: ImageTlvHdr{
+			Type: IMAGE_TLV_SHA256,
+			Pad:  0,
+			Len:  uint16(len(hashBytes)),
+		},
+		Data: hashBytes,
+	}
+	ri.Tlvs = append(ri.Tlvs, tlv)
+
+	tlvs, err := GenerateSigTlvs(ic.SigKeys, hashBytes)
+	if err != nil {
+		return ri, err
+	}
+	ri.Tlvs = append(ri.Tlvs, tlvs...)
+
+	if ic.CipherSecret != nil {
+		tlv, err := generateEncTlv(ic.CipherSecret)
+		if err != nil {
+			return ri, err
+		}
+		ri.Tlvs = append(ri.Tlvs, tlv)
+	}
+
+	return ri, nil
+}
diff --git a/image/encrypted.go b/image/encrypted.go
new file mode 100644
index 0000000..0547e2f
--- /dev/null
+++ b/image/encrypted.go
@@ -0,0 +1,196 @@
+/**
+ * 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.
+ */
+
+// Decoder for PKCS#5 encrypted PKCS#8 private keys.
+package image
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/sha1"
+	"crypto/sha256"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/asn1"
+	"fmt"
+	"hash"
+
+	"golang.org/x/crypto/pbkdf2"
+	"golang.org/x/crypto/ssh/terminal"
+)
+
+var (
+	oidPbes2          = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 13}
+	oidPbkdf2         = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 12}
+	oidHmacWithSha1   = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 7}
+	oidHmacWithSha224 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 8}
+	oidHmacWithSha256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 9}
+	oidAes128CBC      = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2}
+	oidAes256CBC      = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42}
+)
+
+// We only support a narrow set of possible key types, namely the type
+// generated by either MCUboot's `imgtool.py` command, or using an
+// OpenSSL command such as:
+//
+//     openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
+//         -aes-256-cbc > keyfile.pem
+//
+// or similar for ECDSA.  Specifically, the encryption must be done
+// with PBES2, and PBKDF2, and aes-256-cbc used as the cipher.
+type pkcs5 struct {
+	Algo      pkix.AlgorithmIdentifier
+	Encrypted []byte
+}
+
+// The parameters when the algorithm in pkcs5 is oidPbes2
+type pbes2 struct {
+	KeyDerivationFunc pkix.AlgorithmIdentifier
+	EncryptionScheme  pkix.AlgorithmIdentifier
+}
+
+// Salt is given as a choice, but we will only support the inlined
+// octet string.
+type pbkdf2Param struct {
+	Salt      []byte
+	IterCount int
+	HashFunc  pkix.AlgorithmIdentifier
+	// Optional and default values omitted, and unsupported.
+}
+
+type hashFunc func() hash.Hash
+
+func parseEncryptedPrivateKey(der []byte) (key interface{}, err error) {
+	var wrapper pkcs5
+	if _, err = asn1.Unmarshal(der, &wrapper); err != nil {
+		return nil, err
+	}
+	if !wrapper.Algo.Algorithm.Equal(oidPbes2) {
+		return nil, fmt.Errorf("pkcs5: Unknown PKCS#5 wrapper algorithm: %v", wrapper.Algo.Algorithm)
+	}
+
+	var pbparm pbes2
+	if _, err = asn1.Unmarshal(wrapper.Algo.Parameters.FullBytes, &pbparm); err != nil {
+		return nil, err
+	}
+	if !pbparm.KeyDerivationFunc.Algorithm.Equal(oidPbkdf2) {
+		return nil, fmt.Errorf("pkcs5: Unknown KDF: %v", pbparm.KeyDerivationFunc.Algorithm)
+	}
+
+	var kdfParam pbkdf2Param
+	if _, err = asn1.Unmarshal(pbparm.KeyDerivationFunc.Parameters.FullBytes, &kdfParam); err != nil {
+		return nil, err
+	}
+
+	var hashNew hashFunc
+	switch {
+	case kdfParam.HashFunc.Algorithm.Equal(oidHmacWithSha1):
+		hashNew = sha1.New
+	case kdfParam.HashFunc.Algorithm.Equal(oidHmacWithSha224):
+		hashNew = sha256.New224
+	case kdfParam.HashFunc.Algorithm.Equal(oidHmacWithSha256):
+		hashNew = sha256.New
+	default:
+		return nil, fmt.Errorf("pkcs5: Unsupported hash: %v", pbparm.EncryptionScheme.Algorithm)
+	}
+
+	// Get the encryption used.
+	size := 0
+	var iv []byte
+	switch {
+	case pbparm.EncryptionScheme.Algorithm.Equal(oidAes256CBC):
+		size = 32
+		if _, err = asn1.Unmarshal(pbparm.EncryptionScheme.Parameters.FullBytes, &iv); err != nil {
+			return nil, err
+		}
+	case pbparm.EncryptionScheme.Algorithm.Equal(oidAes128CBC):
+		size = 16
+		if _, err = asn1.Unmarshal(pbparm.EncryptionScheme.Parameters.FullBytes, &iv); err != nil {
+			return nil, err
+		}
+	default:
+		return nil, fmt.Errorf("pkcs5: Unsupported cipher: %v", pbparm.EncryptionScheme.Algorithm)
+	}
+
+	return unwrapPbes2Pbkdf2(&kdfParam, size, iv, hashNew, wrapper.Encrypted)
+}
+
+func unwrapPbes2Pbkdf2(param *pbkdf2Param, size int, iv []byte, hashNew hashFunc, encrypted []byte) (key interface{}, err error) {
+	pass, err := getPassword()
+	if err != nil {
+		return nil, err
+	}
+	cryptoKey := pbkdf2.Key(pass, param.Salt, param.IterCount, size, hashNew)
+
+	block, err := aes.NewCipher(cryptoKey)
+	if err != nil {
+		return nil, err
+	}
+	enc := cipher.NewCBCDecrypter(block, iv)
+
+	plain := make([]byte, len(encrypted))
+	enc.CryptBlocks(plain, encrypted)
+
+	plain, err = checkPkcs7Padding(plain)
+	if err != nil {
+		return nil, err
+	}
+
+	return x509.ParsePKCS8PrivateKey(plain)
+}
+
+// Verify that PKCS#7 padding is correct on this plaintext message.
+// Returns a new slice with the padding removed.
+func checkPkcs7Padding(buf []byte) ([]byte, error) {
+	if len(buf) < 16 {
+		return nil, fmt.Errorf("Invalid padded buffer")
+	}
+
+	padLen := int(buf[len(buf)-1])
+	if padLen < 1 || padLen > 16 {
+		return nil, fmt.Errorf("Invalid padded buffer")
+	}
+
+	if padLen > len(buf) {
+		return nil, fmt.Errorf("Invalid padded buffer")
+	}
+
+	for pos := len(buf) - padLen; pos < len(buf); pos++ {
+		if int(buf[pos]) != padLen {
+			return nil, fmt.Errorf("Invalid padded buffer")
+		}
+	}
+
+	return buf[:len(buf)-padLen], nil
+}
+
+// For testing, a key can be set here.  If this is empty, the key will
+// be queried via prompt.
+var KeyPassword = []byte{}
+
+// Prompt the user for a password, unless we have stored one for
+// testing.
+func getPassword() ([]byte, error) {
+	if len(KeyPassword) != 0 {
+		return KeyPassword, nil
+	}
+
+	fmt.Printf("key password: ")
+	return terminal.ReadPassword(0)
+}
diff --git a/image/image.go b/image/image.go
new file mode 100644
index 0000000..244cfed
--- /dev/null
+++ b/image/image.go
@@ -0,0 +1,544 @@
+/**
+ * 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 image
+
+import (
+	"bytes"
+	"encoding/binary"
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"strconv"
+	"strings"
+
+	"mynewt.apache.org/newt/util"
+)
+
+const (
+	IMAGE_MAGIC         = 0x96f3b83d /* Image header magic */
+	IMAGE_TRAILER_MAGIC = 0x6907     /* Image tlv info magic */
+)
+
+const (
+	IMAGE_HEADER_SIZE  = 32
+	IMAGE_TRAILER_SIZE = 4
+	IMAGE_TLV_SIZE     = 4 /* Plus `value` field. */
+)
+
+/*
+ * Image header flags.
+ */
+const (
+	IMAGE_F_PIC          = 0x00000001
+	IMAGE_F_NON_BOOTABLE = 0x00000002 /* non bootable image */
+	IMAGE_F_ENCRYPTED    = 0x00000004 /* encrypted image */
+)
+
+/*
+ * Image trailer TLV types.
+ */
+const (
+	IMAGE_TLV_KEYHASH  = 0x01
+	IMAGE_TLV_SHA256   = 0x10
+	IMAGE_TLV_RSA2048  = 0x20
+	IMAGE_TLV_ECDSA224 = 0x21
+	IMAGE_TLV_ECDSA256 = 0x22
+	IMAGE_TLV_ENC_RSA  = 0x30
+	IMAGE_TLV_ENC_KEK  = 0x31
+)
+
+var imageTlvTypeNameMap = map[uint8]string{
+	IMAGE_TLV_KEYHASH:  "KEYHASH",
+	IMAGE_TLV_SHA256:   "SHA256",
+	IMAGE_TLV_RSA2048:  "RSA2048",
+	IMAGE_TLV_ECDSA224: "ECDSA224",
+	IMAGE_TLV_ECDSA256: "ECDSA256",
+	IMAGE_TLV_ENC_RSA:  "ENC_RSA",
+	IMAGE_TLV_ENC_KEK:  "ENC_KEK",
+}
+
+type ImageVersion struct {
+	Major    uint8
+	Minor    uint8
+	Rev      uint16
+	BuildNum uint32
+}
+
+type ImageHdr struct {
+	Magic uint32
+	Pad1  uint32
+	HdrSz uint16
+	Pad2  uint16
+	ImgSz uint32
+	Flags uint32
+	Vers  ImageVersion
+	Pad3  uint32
+}
+
+type ImageTlvHdr struct {
+	Type uint8
+	Pad  uint8
+	Len  uint16
+}
+
+type ImageTlv struct {
+	Header ImageTlvHdr
+	Data   []byte
+}
+
+type ImageTrailer struct {
+	Magic     uint16
+	TlvTotLen uint16
+}
+
+type Image struct {
+	Header ImageHdr
+	Body   []byte
+	Tlvs   []ImageTlv
+}
+
+type ImageOffsets struct {
+	Header    int
+	Body      int
+	Trailer   int
+	Tlvs      []int
+	TotalSize int
+}
+
+func ImageTlvTypeName(tlvType uint8) string {
+	name, ok := imageTlvTypeNameMap[tlvType]
+	if !ok {
+		return "???"
+	}
+
+	return name
+}
+
+func ParseVersion(versStr string) (ImageVersion, error) {
+	var err error
+	var major uint64
+	var minor uint64
+	var rev uint64
+	var buildNum uint64
+	var ver ImageVersion
+
+	components := strings.Split(versStr, ".")
+	major, err = strconv.ParseUint(components[0], 10, 8)
+	if err != nil {
+		return ver, util.FmtNewtError("Invalid version string %s", versStr)
+	}
+	if len(components) > 1 {
+		minor, err = strconv.ParseUint(components[1], 10, 8)
+		if err != nil {
+			return ver, util.FmtNewtError("Invalid version string %s", versStr)
+		}
+	}
+	if len(components) > 2 {
+		rev, err = strconv.ParseUint(components[2], 10, 16)
+		if err != nil {
+			return ver, util.FmtNewtError("Invalid version string %s", versStr)
+		}
+	}
+	if len(components) > 3 {
+		buildNum, err = strconv.ParseUint(components[3], 10, 32)
+		if err != nil {
+			return ver, util.FmtNewtError("Invalid version string %s", versStr)
+		}
+	}
+
+	ver.Major = uint8(major)
+	ver.Minor = uint8(minor)
+	ver.Rev = uint16(rev)
+	ver.BuildNum = uint32(buildNum)
+	return ver, nil
+}
+
+func (ver ImageVersion) String() string {
+	return fmt.Sprintf("%d.%d.%d.%d",
+		ver.Major, ver.Minor, ver.Rev, ver.BuildNum)
+}
+
+func (h *ImageHdr) Map(offset int) map[string]interface{} {
+	return map[string]interface{}{
+		"Magic":  h.Magic,
+		"HdrSz":  h.HdrSz,
+		"ImgSz":  h.ImgSz,
+		"Flags":  h.Flags,
+		"Vers":   h.Vers.String(),
+		"offset": offset,
+	}
+}
+
+func rawBodyMap(offset int) map[string]interface{} {
+	return map[string]interface{}{
+		"offset": offset,
+	}
+}
+
+func (t *ImageTrailer) Map(offset int) map[string]interface{} {
+	return map[string]interface{}{
+		"Magic":     t.Magic,
+		"TlvTotLen": t.TlvTotLen,
+		"offset":    offset,
+	}
+}
+
+func (t *ImageTlv) Map(offset int) map[string]interface{} {
+	return map[string]interface{}{
+		"Type":    t.Header.Type,
+		"typestr": ImageTlvTypeName(t.Header.Type),
+		"Len":     t.Header.Len,
+		"offset":  offset,
+		"data":    hex.EncodeToString(t.Data),
+	}
+}
+
+func (img *Image) Map() (map[string]interface{}, error) {
+	offs, err := img.Offsets()
+	if err != nil {
+		return nil, err
+	}
+
+	m := map[string]interface{}{}
+	m["header"] = img.Header.Map(offs.Header)
+	m["body"] = rawBodyMap(offs.Body)
+	trailer := img.Trailer()
+	m["trailer"] = trailer.Map(offs.Trailer)
+
+	tlvMaps := []map[string]interface{}{}
+	for i, tlv := range img.Tlvs {
+		tlvMaps = append(tlvMaps, tlv.Map(offs.Tlvs[i]))
+	}
+	m["tlvs"] = tlvMaps
+
+	return m, nil
+}
+
+func (img *Image) Json() (string, error) {
+	m, err := img.Map()
+	if err != nil {
+		return "", err
+	}
+
+	b, err := json.MarshalIndent(m, "", "    ")
+	if err != nil {
+		return "", util.ChildNewtError(err)
+	}
+
+	return string(b), nil
+}
+
+func (tlv *ImageTlv) Write(w io.Writer) (int, error) {
+	totalSize := 0
+
+	err := binary.Write(w, binary.LittleEndian, &tlv.Header)
+	if err != nil {
+		return totalSize, util.ChildNewtError(err)
+	}
+	totalSize += IMAGE_TLV_SIZE
+
+	size, err := w.Write(tlv.Data)
+	if err != nil {
+		return totalSize, util.ChildNewtError(err)
+	}
+	totalSize += size
+
+	return totalSize, nil
+}
+
+func (i *Image) FindTlvs(tlvType uint8) []ImageTlv {
+	var tlvs []ImageTlv
+
+	for _, tlv := range i.Tlvs {
+		if tlv.Header.Type == tlvType {
+			tlvs = append(tlvs, tlv)
+		}
+	}
+
+	return tlvs
+}
+
+func (i *Image) FindUniqueTlv(tlvType uint8) (*ImageTlv, error) {
+	tlvs := i.FindTlvs(tlvType)
+	if len(tlvs) == 0 {
+		return nil, nil
+	}
+	if len(tlvs) > 1 {
+		return nil, util.FmtNewtError("Image contains %d TLVs with type %d",
+			len(tlvs), tlvType)
+	}
+
+	return &tlvs[0], nil
+}
+
+func (i *Image) RemoveTlvsIf(pred func(tlv ImageTlv) bool) int {
+	numRmed := 0
+	for idx := 0; idx < len(i.Tlvs); {
+		tlv := i.Tlvs[idx]
+		if pred(tlv) {
+			i.Tlvs = append(i.Tlvs[:idx], i.Tlvs[idx+1:]...)
+			numRmed++
+		} else {
+			idx++
+		}
+	}
+
+	return numRmed
+}
+
+func (img *Image) Trailer() ImageTrailer {
+	trailer := ImageTrailer{
+		Magic:     IMAGE_TRAILER_MAGIC,
+		TlvTotLen: IMAGE_TRAILER_SIZE,
+	}
+	for _, tlv := range img.Tlvs {
+		trailer.TlvTotLen += IMAGE_TLV_SIZE + tlv.Header.Len
+	}
+
+	return trailer
+}
+
+func (i *Image) Hash() ([]byte, error) {
+	tlv, err := i.FindUniqueTlv(IMAGE_TLV_SHA256)
+	if err != nil {
+		return nil, err
+	}
+
+	if tlv == nil {
+		return nil, util.FmtNewtError("Image does not contain hash TLV")
+	}
+
+	return tlv.Data, nil
+}
+
+func (i *Image) WritePlusOffsets(w io.Writer) (ImageOffsets, error) {
+	offs := ImageOffsets{}
+	offset := 0
+
+	offs.Header = offset
+
+	err := binary.Write(w, binary.LittleEndian, &i.Header)
+	if err != nil {
+		return offs, util.ChildNewtError(err)
+	}
+	offset += IMAGE_HEADER_SIZE
+
+	offs.Body = offset
+	size, err := w.Write(i.Body)
+	if err != nil {
+		return offs, util.ChildNewtError(err)
+	}
+	offset += size
+
+	trailer := i.Trailer()
+	offs.Trailer = offset
+	err = binary.Write(w, binary.LittleEndian, &trailer)
+	if err != nil {
+		return offs, util.ChildNewtError(err)
+	}
+	offset += IMAGE_TRAILER_SIZE
+
+	for _, tlv := range i.Tlvs {
+		offs.Tlvs = append(offs.Tlvs, offset)
+		size, err := tlv.Write(w)
+		if err != nil {
+			return offs, util.ChildNewtError(err)
+		}
+		offset += size
+	}
+
+	offs.TotalSize = offset
+
+	return offs, nil
+}
+
+func (i *Image) Offsets() (ImageOffsets, error) {
+	return i.WritePlusOffsets(ioutil.Discard)
+}
+
+func (i *Image) TotalSize() (int, error) {
+	offs, err := i.Offsets()
+	if err != nil {
+		return 0, err
+	}
+	return offs.TotalSize, nil
+}
+
+func (i *Image) Write(w io.Writer) (int, error) {
+	offs, err := i.WritePlusOffsets(w)
+	if err != nil {
+		return 0, err
+	}
+
+	return offs.TotalSize, nil
+}
+
+func (i *Image) WriteToFile(filename string) error {
+	f, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+	if err != nil {
+		return util.ChildNewtError(err)
+	}
+
+	if _, err := i.Write(f); err != nil {
+		return util.ChildNewtError(err)
+	}
+
+	return nil
+}
+
+func parseRawHeader(imgData []byte, offset int) (ImageHdr, int, error) {
+	var hdr ImageHdr
+
+	r := bytes.NewReader(imgData)
+	r.Seek(int64(offset), io.SeekStart)
+
+	if err := binary.Read(r, binary.LittleEndian, &hdr); err != nil {
+		return hdr, 0, util.FmtNewtError(
+			"Error reading image header: %s", err.Error())
+	}
+
+	if hdr.Magic != IMAGE_MAGIC {
+		return hdr, 0, util.FmtNewtError(
+			"Image magic incorrect; expected 0x%08x, got 0x%08x",
+			IMAGE_MAGIC, hdr.Magic)
+	}
+
+	remLen := len(imgData) - offset
+	if remLen < int(hdr.HdrSz) {
+		return hdr, 0, util.FmtNewtError(
+			"Image header incomplete; expected %d bytes, got %d bytes",
+			hdr.HdrSz, remLen)
+	}
+
+	return hdr, int(hdr.HdrSz), nil
+}
+
+func parseRawBody(imgData []byte, hdr ImageHdr,
+	offset int) ([]byte, int, error) {
+
+	imgSz := int(hdr.ImgSz)
+	remLen := len(imgData) - offset
+
+	if remLen < imgSz {
+		return nil, 0, util.FmtNewtError(
+			"Image body incomplete; expected %d bytes, got %d bytes",
+			imgSz, remLen)
+	}
+
+	return imgData[offset : offset+imgSz], imgSz, nil
+}
+
+func parseRawTrailer(imgData []byte, offset int) (ImageTrailer, int, error) {
+	var trailer ImageTrailer
+
+	r := bytes.NewReader(imgData)
+	r.Seek(int64(offset), io.SeekStart)
+
+	if err := binary.Read(r, binary.LittleEndian, &trailer); err != nil {
+		return trailer, 0, util.FmtNewtError(
+			"Image contains invalid trailer at offset %d: %s",
+			offset, err.Error())
+	}
+
+	return trailer, IMAGE_TRAILER_SIZE, nil
+}
+
+func parseRawTlv(imgData []byte, offset int) (ImageTlv, int, error) {
+	tlv := ImageTlv{}
+
+	r := bytes.NewReader(imgData)
+	r.Seek(int64(offset), io.SeekStart)
+
+	if err := binary.Read(r, binary.LittleEndian, &tlv.Header); err != nil {
+		return tlv, 0, util.FmtNewtError(
+			"Image contains invalid TLV at offset %d: %s", offset, err.Error())
+	}
+
+	tlv.Data = make([]byte, tlv.Header.Len)
+	if _, err := r.Read(tlv.Data); err != nil {
+		return tlv, 0, util.FmtNewtError(
+			"Image contains invalid TLV at offset %d: %s", offset, err.Error())
+	}
+
+	return tlv, IMAGE_TLV_SIZE + int(tlv.Header.Len), nil
+}
+
+func ParseImage(imgData []byte) (Image, error) {
+	img := Image{}
+	offset := 0
+
+	hdr, size, err := parseRawHeader(imgData, offset)
+	if err != nil {
+		return img, err
+	}
+	offset += size
+
+	body, size, err := parseRawBody(imgData, hdr, offset)
+	if err != nil {
+		return img, err
+	}
+	offset += size
+
+	trailer, size, err := parseRawTrailer(imgData, offset)
+	if err != nil {
+		return img, err
+	}
+	offset += size
+
+	var tlvs []ImageTlv
+	tlvLen := IMAGE_TRAILER_SIZE
+	for offset < len(imgData) {
+		tlv, size, err := parseRawTlv(imgData, offset)
+		if err != nil {
+			return img, err
+		}
+
+		tlvs = append(tlvs, tlv)
+		offset += size
+
+		tlvLen += IMAGE_TLV_SIZE + int(tlv.Header.Len)
+	}
+
+	if int(trailer.TlvTotLen) != tlvLen {
+		return img, util.FmtNewtError(
+			"invalid image: trailer indicates TLV-length=%d; actual=%d",
+			trailer.TlvTotLen, tlvLen)
+	}
+
+	img.Header = hdr
+	img.Body = body
+	img.Tlvs = tlvs
+
+	return img, nil
+}
+
+func ReadImage(filename string) (Image, error) {
+	ri := Image{}
+
+	imgData, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return ri, util.ChildNewtError(err)
+	}
+
+	return ParseImage(imgData)
+}
diff --git a/image/key.go b/image/key.go
new file mode 100644
index 0000000..9141f6e
--- /dev/null
+++ b/image/key.go
@@ -0,0 +1,294 @@
+/**
+ * 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 image
+
+import (
+	"crypto/aes"
+	"crypto/ecdsa"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha256"
+	"crypto/x509"
+	"encoding/asn1"
+	"encoding/base64"
+	"encoding/pem"
+	"io/ioutil"
+
+	keywrap "github.com/NickBall/go-aes-key-wrap"
+
+	"mynewt.apache.org/newt/util"
+)
+
+type ImageSigKey struct {
+	// Only one of these members is non-nil.
+	Rsa *rsa.PrivateKey
+	Ec  *ecdsa.PrivateKey
+}
+
+func ParsePrivateKey(keyBytes []byte) (interface{}, error) {
+	var privKey interface{}
+	var err error
+
+	block, data := pem.Decode(keyBytes)
+	if block != nil && block.Type == "EC PARAMETERS" {
+		/*
+		 * Openssl prepends an EC PARAMETERS block before the
+		 * key itself.  If we see this first, just skip it,
+		 * and go on to the data block.
+		 */
+		block, _ = pem.Decode(data)
+	}
+	if block != nil && block.Type == "RSA PRIVATE KEY" {
+		/*
+		 * ParsePKCS1PrivateKey returns an RSA private key from its ASN.1
+		 * PKCS#1 DER encoded form.
+		 */
+		privKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
+		if err != nil {
+			return nil, util.FmtNewtError(
+				"Private key parsing failed: %s", err)
+		}
+	}
+	if block != nil && block.Type == "EC PRIVATE KEY" {
+		/*
+		 * ParseECPrivateKey returns a EC private key
+		 */
+		privKey, err = x509.ParseECPrivateKey(block.Bytes)
+		if err != nil {
+			return nil, util.FmtNewtError(
+				"Private key parsing failed: %s", err)
+		}
+	}
+	if block != nil && block.Type == "PRIVATE KEY" {
+		// This indicates a PKCS#8 unencrypted private key.
+		// The particular type of key will be indicated within
+		// the key itself.
+		privKey, err = x509.ParsePKCS8PrivateKey(block.Bytes)
+		if err != nil {
+			return nil, util.FmtNewtError(
+				"Private key parsing failed: %s", err)
+		}
+	}
+	if block != nil && block.Type == "ENCRYPTED PRIVATE KEY" {
+		// This indicates a PKCS#8 key wrapped with PKCS#5
+		// encryption.
+		privKey, err = parseEncryptedPrivateKey(block.Bytes)
+		if err != nil {
+			return nil, util.FmtNewtError("Unable to decode encrypted private key: %s", err)
+		}
+	}
+	if privKey == nil {
+		return nil, util.NewNewtError("Unknown private key format, EC/RSA private " +
+			"key in PEM format only.")
+	}
+
+	return privKey, nil
+}
+
+func ReadKey(filename string) (ImageSigKey, error) {
+	key := ImageSigKey{}
+
+	keyBytes, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return key, util.FmtNewtError("Error reading key file: %s", err)
+	}
+
+	privKey, err := ParsePrivateKey(keyBytes)
+	if err != nil {
+		return key, err
+	}
+
+	switch priv := privKey.(type) {
+	case *rsa.PrivateKey:
+		key.Rsa = priv
+	case *ecdsa.PrivateKey:
+		key.Ec = priv
+	default:
+		return key, util.NewNewtError("Unknown private key format")
+	}
+
+	return key, nil
+}
+
+func ReadKeys(filenames []string) ([]ImageSigKey, error) {
+	keys := make([]ImageSigKey, len(filenames))
+
+	for i, filename := range filenames {
+		key, err := ReadKey(filename)
+		if err != nil {
+			return nil, err
+		}
+
+		keys[i] = key
+	}
+
+	return keys, nil
+}
+
+func (key *ImageSigKey) assertValid() {
+	if key.Rsa == nil && key.Ec == nil {
+		panic("invalid key; neither RSA nor ECC")
+	}
+
+	if key.Rsa != nil && key.Ec != nil {
+		panic("invalid key; neither RSA nor ECC")
+	}
+}
+
+func (key *ImageSigKey) sigKeyHash() ([]uint8, error) {
+	key.assertValid()
+
+	if key.Rsa != nil {
+		pubkey, _ := asn1.Marshal(key.Rsa.PublicKey)
+		sum := sha256.Sum256(pubkey)
+		return sum[:4], nil
+	} else {
+		switch key.Ec.Curve.Params().Name {
+		case "P-224":
+			fallthrough
+		case "P-256":
+			pubkey, _ := x509.MarshalPKIXPublicKey(&key.Ec.PublicKey)
+			sum := sha256.Sum256(pubkey)
+			return sum[:4], nil
+		default:
+			return nil, util.NewNewtError("Unsupported ECC curve")
+		}
+	}
+}
+
+func (key *ImageSigKey) sigLen() uint16 {
+	key.assertValid()
+
+	if key.Rsa != nil {
+		return 256
+	} else {
+		switch key.Ec.Curve.Params().Name {
+		case "P-224":
+			return 68
+		case "P-256":
+			return 72
+		default:
+			return 0
+		}
+	}
+}
+
+func (key *ImageSigKey) sigTlvType() uint8 {
+	key.assertValid()
+
+	if key.Rsa != nil {
+		return IMAGE_TLV_RSA2048
+	} else {
+		switch key.Ec.Curve.Params().Name {
+		case "P-224":
+			return IMAGE_TLV_ECDSA224
+		case "P-256":
+			return IMAGE_TLV_ECDSA256
+		default:
+			return 0
+		}
+	}
+}
+
+func parseEncKeyPem(keyBytes []byte, plainSecret []byte) ([]byte, error) {
+	b, _ := pem.Decode(keyBytes)
+	if b == nil {
+		return nil, nil
+	}
+
+	if b.Type != "PUBLIC KEY" && b.Type != "RSA PUBLIC KEY" {
+		return nil, util.NewNewtError("Invalid PEM file")
+	}
+
+	pub, err := x509.ParsePKIXPublicKey(b.Bytes)
+	if err != nil {
+		return nil, util.FmtNewtError(
+			"Error parsing pubkey file: %s", err.Error())
+	}
+
+	var pubk *rsa.PublicKey
+	switch pub.(type) {
+	case *rsa.PublicKey:
+		pubk = pub.(*rsa.PublicKey)
+	default:
+		return nil, util.FmtNewtError(
+			"Error parsing pubkey file: %s", err.Error())
+	}
+
+	rng := rand.Reader
+	cipherSecret, err := rsa.EncryptOAEP(
+		sha256.New(), rng, pubk, plainSecret, nil)
+	if err != nil {
+		return nil, util.FmtNewtError(
+			"Error from encryption: %s\n", err.Error())
+	}
+
+	return cipherSecret, nil
+}
+
+func parseEncKeyBase64(keyBytes []byte, plainSecret []byte) ([]byte, error) {
+	kek, err := base64.StdEncoding.DecodeString(string(keyBytes))
+	if err != nil {
+		return nil, util.FmtNewtError(
+			"Error decoding kek: %s", err.Error())
+	}
+	if len(kek) != 16 {
+		return nil, util.FmtNewtError(
+			"Unexpected key size: %d != 16", len(kek))
+	}
+
+	cipher, err := aes.NewCipher(kek)
+	if err != nil {
+		return nil, util.FmtNewtError(
+			"Error creating keywrap cipher: %s", err.Error())
+	}
+
+	cipherSecret, err := keywrap.Wrap(cipher, plainSecret)
+	if err != nil {
+		return nil, util.FmtNewtError("Error key-wrapping: %s", err.Error())
+	}
+
+	return cipherSecret, nil
+}
+
+func ReadEncKey(filename string, plainSecret []byte) ([]byte, error) {
+	keyBytes, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return nil, util.FmtNewtError(
+			"Error reading pubkey file: %s", err.Error())
+	}
+
+	// Try reading as PEM (asymetric key).
+	cipherSecret, err := parseEncKeyPem(keyBytes, plainSecret)
+	if err != nil {
+		return nil, err
+	}
+	if cipherSecret != nil {
+		return cipherSecret, nil
+	}
+
+	// Not PEM; assume this is a base64 encoded symetric key
+	cipherSecret, err = parseEncKeyBase64(keyBytes, plainSecret)
+	if err != nil {
+		return nil, err
+	}
+
+	return cipherSecret, nil
+}
diff --git a/image/keys_test.go b/image/keys_test.go
new file mode 100644
index 0000000..577d9cd
--- /dev/null
+++ b/image/keys_test.go
@@ -0,0 +1,244 @@
+/**
+ * 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 image_test
+
+import (
+	"io/ioutil"
+	"os"
+	"path"
+	"testing"
+)
+
+func TestRSA(t *testing.T) {
+	signatureTest(t, rsaPkcs1Private)
+}
+
+func TestPlainRSAPKCS8(t *testing.T) {
+	signatureTest(t, rsaPkcs8Private)
+}
+
+func TestEcdsa(t *testing.T) {
+	signatureTest(t, ecdsaPrivate)
+}
+
+func TestPlainEcdsaPkcs8(t *testing.T) {
+	signatureTest(t, ecdsaPkcs8Private)
+}
+
+func TestEncryptedRSA(t *testing.T) {
+	image.KeyPassword = []byte("sample")
+	signatureTest(t, rsaEncryptedPrivate)
+	image.KeyPassword = []byte{}
+}
+
+func TestEncryptedEcdsa(t *testing.T) {
+	image.KeyPassword = []byte("sample")
+	signatureTest(t, ecdsaEncryptedPrivate)
+	image.KeyPassword = []byte{}
+}
+
+func signatureTest(t *testing.T, privateKey []byte) {
+	tmpdir, err := ioutil.TempDir("", "newttest")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(tmpdir)
+
+	// Create a source image.  Format doesn't really matter that
+	// much, since the header will be placed on it by the image
+	// tool.
+
+	simpleName := path.Join(tmpdir, "simple.bin")
+	hashedName := path.Join(tmpdir, "simple-hashed.bin")
+	signedName := path.Join(tmpdir, "simple-signed.bin")
+	keyName := path.Join(tmpdir, "private.pem")
+
+	tmp := make([]byte, 256)
+	for i := 0; i < len(tmp); i++ {
+		tmp[i] = byte(i & 0xFF)
+	}
+	err = ioutil.WriteFile(simpleName, tmp, 0644)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	img, err := image.NewImage(simpleName, hashedName)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	img.SetVersion("1.5")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	img.Generate(nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Now try with a signature.
+	err = ioutil.WriteFile(keyName, privateKey, 0644)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	img, err = image.NewImage(simpleName, signedName)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	err = img.SetSigningKey(keyName, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	err = img.SetVersion("1.6")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	err = img.Generate(nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+// An RSA private key in the old PKCS1 format.
+var rsaPkcs1Private = []byte(`-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA6q2Q/VoFf6U5xm35ynls+HDbHKwfIbBr27PtFJxlS9YT0xKJ
+bcZScPTVizTlft0wfp2TctX/vGd/Y/X3qo5ckRmz+lKUeHm46i4k6rtOBbhBz2id
+hwrO7/ylzwaf8lxn2dj/9ikoYQKFtBb/cKu8wyuvW3gs/ou51AVEF8aKTrl5Expy
+PrhSlh97er2zUmm8NAoo259I5yHK1SvR9kCw2gNXSDQLpFlK2WikdmEbIu0N+cvN
+WM4ONAhffkasznrEOoLPSI66RDrzYhi/Ks9t+N2buEOXao19fDRcSHgZLKT8e6W6
+uK7WxRiEzNbajzgDddbZFqWlcpE7sqPNHFBijwIDAQABAoIBAQDdXx7fLpTzROvM
+F5/C9GnrraGzWVYAlIgZ9o8Umzceo3GN8PV8fND1xq7Novc9he8h8QjPEbksg0Dz
+DWo0FBiTs3hIELAHOWNKXH7sggVmddp2iUvXwEVWsq/CK5CjsbExGXbSQR7a6+Mt
+72fEY+wq+0Fuel2PPETuEI2cE+gRuyspIcO7asmMvLRkxLi2EXU0s4JlqV9UfxKQ
+aqn0PHlRXa5SIzys3mVhXuoe45T50+VKX0DIfu/RuV8njNkkMx74DeEVvf5W4MJW
+vHrRBHoK6KoMrqiwafyPLW/Rh6fMYAdPrffMVuuThtG7Hp83VBVX1HxFhI4Jrf3S
+Hf63hmSZAoGBAO2R/vYBl57qgWEKiMQaU8rzctRbP0TtTsqNdISPMnHV1Tn/rNAU
+m0N7/6IBDb+IlndXDQwIsW/DTXhF2XJSu7n6GXua8B1LF+zuVWUsFfmE3+eLz7B8
+x8G/OkSnOTfRZCYWEoEvzhynn1dlADQ+x49I/XmKqccvAhY71glk6WULAoGBAPzi
+IYo9G+ktlNj9/3OciX7aTCiIIMDyPYtYS6wi59gwH9IswaicHYK4w2fDpTWKlzEE
+18dKF4puuI5GxnKCwHBiWxGhij063cZKKMqA64X41csK+mumux/PAb2gKbGSzzoF
+mSgkKXJ+sZ4ytlgsijEAHV85Sw7j+xy8A0qnCWMNAoGAeCDR7q1hcM8duucrvxWc
+90vg7bZyKLVimROsLneGR3+cAWbiiJlS5W3nFpE31XkItLHE/CfNKTl1i/KuAJwL
+JwBrMFBpSDa3k2v0rGL9fZ2N5rSQwapnC/ZZTWvNiAcOgB+7Ha4BqAWuke+VidWQ
+7Ug4O+Q882Y2xO1ezoNDbX8CgYBq228KyAm8PXuRObsw8iuTg9D8q5ETlwj0kcng
+IhvP2X4IxMrMYbOCompHtX9hIYADwaUgXCmYYHLyA+wlRSTmGFmdGKKefvppqLqV
+32YmhWBp3Oi2hoy5wzJcG4qis4OHZAg00xsEe464Z3tvxNpcHE1NCJuz3hglKzlE
+2VJ5HQKBgQDRisWDbdnOEp7LTXp3Aa33PF1Rx/pkFk4Wb+2Hk977O1OxsAin2cKM
+S5HCltHvON2sCmSQUIxMXXKaNPJiGL3UZJxWZDj38zSg0vO+msmemS1Yjt0xCpbO
+pkl0kvKb/NVlsY4w9kquvql+t9e1rUu9Ug28TKEsSjc9SFrcnVPoNA==
+-----END RSA PRIVATE KEY-----
+`)
+
+// An RSA private key in PKCS8 format, with no encryption.
+var rsaPkcs8Private = []byte(`-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC+FjuXqPSPucsQ
+adxY4nw+9kTgAdsXRIPxq4Q//wkfjEjYhDczN+/rafi0hApuRh7PN7VMGOsDGGR1
+edyertiLt3SfUHAZROIqZ0VAoKGtxgXmnC+s+mMujAv9Ssntbmbi5tNxDcltdWjA
+SdBn7tbIMVVofKaMMugyuXCglxebMm8yxtkSgUvE1E6zZERnteDJTPo8dBCiqkvU
+hf+vG9s1j9lNDMjrZ+d5CHIFmBxJ/WFa6m49lNBFb1Ba43bKdj6mkK05rZ4VWMXU
+evy3Z/UUgU4VPJpoB+GIKy82iOrtjiU7s/6aDkvZ2e+fgxKksN0pzFE9azeA73QS
+bamp28E/AgMBAAECggEBAJ78+4UDFOKt1JF66YkSjjcfRkZSZwyUCwP0oF3ik5/m
+dvtZws29KJevgAyEMDFBxv0srB/k65QgL84uSgATYB2kKRAjeE86VSyASeUfNXui
+GEdlNV8p4hEJo/GMP06uu7FmvU1e6a36uM20L3LuyoiQ8s29DJRQ8/ORNQmstlrg
+J32FZSjTF1mElGPSc1koxhWvl1hE7UGE9pxsSfdsvPNhCIWwAOnVnIv49xG8EWaK
+CkHhEVVdZW8IvO9GYR5U0BJcgzNmdNkS8HVQBIxZtboGAAuPI32EC7siDomKmCF6
+rEcs40f/J/RlK6lrTyKKfqWb4DPtRrOSh9cmjrFFZlECgYEA6mZIANLXJd7CINZ9
+fjotI+FxH8BDOZF7l8xTnOk1e3Me1ia7t2GMcIL+frfG/zMBiDFq0IQuUYScRK1v
+pAILjJKFiU6yY8vH6FZ3mXqiiag6RPa+q89DaUsO0uXRUjQvhtTd5Yy6r8Eac1ya
+y6XC5T5sCJ6HgaF3qlheap+5FkkCgYEAz5qSLShV5oekuj1R0fs+h/Yn7VW9Q0sj
+px8jOD4MWc8gPZ9gZe0UPTvofOLrM3eAetP4egSif99AE9iD8EbiBzAt16OX7EN8
+d7xNiIN922Ep3pubcD6f1vglaI7Thrca/p52g6kWPip6+PWFd1acU6u31Uj0Xvgz
+VFiafstF+0cCgYEAw2sOcJFXCZ2Tnyjzav85jwZu95ek9CPUNJQGyXSsQAWUGdok
++hf7q/mqDx9Maoqtpkv8z2bD7vZuCdvGjaee1U16wyS3GPhV69/ayjwxsi5slf5Y
+rIiZnPkUnMM5Jh2X2gMyFCSlp82ILdFwxIOn3tOR4gW411w0lfIilSYgevECgYA3
+JAgVZHREcdzH9seHrWLze+co+6/0cr26guO46YogRIp8s5tIF0tb5FCg8yijl+cR
+OMHzrs12h1aertCEfl9Ep4BVmUcd4uLpbqNtUfeY0FrtnIkRrCCKWYieF+mJC5No
+86/o0n1s752QCK51fxSwiJigVutJWkVP7uTCLr2cuwKBgQCJPWMcWmSuRlLOVWnO
+jPFoa02Bb83n8GrRpQkpkZZofHextwfo2dd1sZF72zghRsbdC6e0Zj1GrekJOYXO
+8AXmCpyKlXJU7iH5tPGSo68uFN05R6mINbTNmEIQBNTKv8UoKT+nHcTycFrVtarX
+A8EPW2xB86m+Bjq/GNyRgfbPMg==
+-----END PRIVATE KEY-----
+`)
+
+// An ECDSA key in the X.509 internal private key format.
+var ecdsaPrivate = []byte(`-----BEGIN EC PRIVATE KEY-----
+MGgCAQEEHF64kDx3pZyVvezbqYMIxlLbtuPQmI85k4GRy1mgBwYFK4EEACGhPAM6
+AASRtolOCTLQYkDefkIF02tUXR92MKHrbtH4WK/8bfTSFVkaygTPdJbpNthK2wae
+oX9ZeFHS1pcOfQ==
+-----END EC PRIVATE KEY-----
+`)
+
+// An ECDSA key in PKCS#8 format, no encryption.
+var ecdsaPkcs8Private = []byte(`-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgHKeDq4UU6M+c+pMm
+j0AQZlBs7f4r67668eDCUB8aDR2hRANCAATyZPzsx+xn9JtlxdspevTrYisiMTjl
+YuBJCrV1FZj2HkplEgO+ZIMuD7eRvyTEBS2bw6F1aCeKOMUmYVImAbpc
+-----END PRIVATE KEY-----
+`)
+
+// A password-protected RSA private key in PKCS#5/8 format.  The
+// password for this key is "sample".
+var rsaEncryptedPrivate = []byte(`-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIRMifqJThk8kCAggA
+MB0GCWCGSAFlAwQBKgQQTMUBoFpzjJ5UNRnCIeqf4QSCBNDkQvXnUNmss8erKiDo
+Uqs2tf9ZD8MjDThLBmF/gV1dg1q6aDY+3fI2E4yLXJb2PmKcUq82YZ0FDeoCvJRJ
+BCurzM9slur5akpNBTFoFwtFsdHz7nKNS4MHUul22rGBnVFUUNTySmpjl/m+dxWO
+fa6tWpGTAr7tsCy9gF5PxpSw7NR/NpIL0PmpydHWhTs1tl2csqBqK6Tp014Kefi/
+pmmeb2eRl5cmprxW32rW2QBMtv4z91SsbnlVdz4r8txTG+3S4td9v9jD5kqcIiC2
+KQHrbH9y7okUk/ISsp9ANKPJt10fbYDxORiMK57XssXy1enGjpkIIrUGz1TMydkD
+USfwqkmPuIrrzOXnbxU4ef2wC/pA/h9Smby3WWYo8725/1kZyIediNDcgi/Qgrs4
+1VQAYzsD6duwyUNSo+tgmYVFGvZhsottus3fMWe/Ay1biJ6z6Vk8gqKWI1VV/REJ
+zK/I9hgKGxj2N2Ff6E/YkcwQenHWj/iDWLjvokyOBnPFNqzzM2Qqo1XFpzj4EO5D
+0WD4EzZYvUhk3lZZNydvXiuy8RrCVLLJMS08XgOqQaiFqqxj2hjRwv3nBesk7iA8
+5Tv8GMa5QkNrISCnp4/uGBh+v/CjwVRqPTcK3/mctPN2nLhI6H4pF4Y6apXkz1TN
+NMQqxaxmVVg8fyLaS4/xfUr8LAmiEtOwvs0XOhcqCTvvlsO4N+yec4VD4gmsTDY9
+/2b/+YwSlGMpA+GQQbg0FraaF8NyJRG1mSER6WiUGGM1cuKK44nzBbykQbZwzDSA
+kkhjDaadkhv/NPKAUR3sNy2GXVaNL/ItCpQUHRKKcIPp0HhdXsl0YebuwRlHjw/6
+UOdzNYe23e40X/Xl3vmOKRbzhLP/qO2DV21o0wI4ujF8Xu5h1h8s49HPp58G1ldy
+/hJ6durYKX8T5khiR2iXYewoy0YObuccV//Ov1/ySOp/x0/QuCl/swvs8Jf7awnu
+rpRrHPArpCvMmXmt5Y+TFYXFjkJGwsxTew5TcwBebBlIET2XNbo2pbz4WqJ3eVlK
+CNZVDEZ8mMrGT00FBi759Vfw9rhrnqXnLlNtJZ5VCXFUw8Tos302sLaQWXzHYyf8
+4awM8G9PSu5Q9lFcN9od4H95YrAAv/l8F+pcGgEKD8ZuzsgFIalqgx5wzmUMDcPM
+NKV5u9mtHjI92ru6NB8rGesM6sy6kBGvpotsDWawpV2SoCrkbyEkk+kXaGS+fsG7
+D2H37GfktN8R5Ktc0Uf/JJiNfDzq8lk1J4r7LBQlWUbhKbfGMYxt+7Xo0GsqAsLp
+PKSUwx+hTZb3BmW6s4Q6vivI1MdQbWVT1zh41StvfRSNlo70iOFxOM0lU1jjY989
+UKo+gcolddvZbMNwip0ILPO3dsa+he1jJ/gbo9qBHLy7plfsBLLakZP1Nu6xdlqQ
+TSSobaE8uxUMZk+wMWClA9AOZ1TcUr2yRV5GVj/bxG9ab+H37vF9F8vFE+jjJ7yN
+6pjdohm4gXeSVx7ON4SeZLsVwNYkCVYS89E81qLx1jP9F57+6IUGDZN5EMC0aJLT
+ny75MCCLT00KD7BFsb0KDLXxp++eu/L2hinorT3p6dXp/9mUoxmy6wJqEyqCFniZ
+N2GZN7+LDTIbHUxCijVWamU2DQ==
+-----END ENCRYPTED PRIVATE KEY-----
+`)
+
+// A password-protected ECDSA private key in PKCS#5/8 format.  The
+// password for this key is "sample"
+var ecdsaEncryptedPrivate = []byte(`-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIHeMEkGCSqGSIb3DQEFDTA8MBsGCSqGSIb3DQEFDDAOBAjlKrDSKNg9QQICCAAw
+HQYJYIZIAWUDBAEqBBDliPNzQTNpdlppTcYpmuhWBIGQVhfWaVSzUvi/qIZLiZVn
+Nulfw5jDOlbn3UBX9kp/Z9Pro582Q0kjzLfm5UahvDINEJWxL4pc/28UnGQTBr0Q
+nSEg+RbqpuD099C38H0Gq/YkIM+RDG4aiQrkmzHXyVsHshIbG+z2LsLTIwmU69/Z
+v0nX6/hGErVR8YWcrOne086rCvfJVrxyO5+EUqrkLhEr
+-----END ENCRYPTED PRIVATE KEY-----
+`)
diff --git a/image/v1.go b/image/v1.go
new file mode 100644
index 0000000..bab86f6
--- /dev/null
+++ b/image/v1.go
@@ -0,0 +1,487 @@
+/**
+ * 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.
+ */
+
+// This file implements parsing and generation of version-1 images.  Much of
+// this code duplicates the v2 code.  The expectation is that this file will be
+// removed when version 1 is oficially retired (soon).
+
+package image
+
+import (
+	"bytes"
+	"crypto"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha256"
+	"encoding/binary"
+	"encoding/hex"
+	"io"
+	"io/ioutil"
+
+	"mynewt.apache.org/newt/util"
+)
+
+const IMAGEv1_MAGIC = 0x96f3b83c /* Image header magic */
+
+const (
+	IMAGEv1_F_PIC                      = 0x00000001
+	IMAGEv1_F_SHA256                   = 0x00000002 /* Image contains hash TLV */
+	IMAGEv1_F_PKCS15_RSA2048_SHA256    = 0x00000004 /* PKCS15 w/RSA2048 and SHA256 */
+	IMAGEv1_F_ECDSA224_SHA256          = 0x00000008 /* ECDSA224 over SHA256 */
+	IMAGEv1_F_NON_BOOTABLE             = 0x00000010 /* non bootable image */
+	IMAGEv1_F_ECDSA256_SHA256          = 0x00000020 /* ECDSA256 over SHA256 */
+	IMAGEv1_F_PKCS1_PSS_RSA2048_SHA256 = 0x00000040 /* RSA-PSS w/RSA2048 and SHA256 */
+)
+
+const (
+	IMAGEv1_TLV_SHA256   = 1
+	IMAGEv1_TLV_RSA2048  = 2
+	IMAGEv1_TLV_ECDSA224 = 3
+	IMAGEv1_TLV_ECDSA256 = 4
+)
+
+// Set this to enable RSA-PSS for RSA signatures, instead of PKCS#1
+// v1.5.  Eventually, this should be the default.
+var UseRsaPss = false
+
+type ImageHdrV1 struct {
+	Magic uint32
+	TlvSz uint16
+	KeyId uint8
+	Pad1  uint8
+	HdrSz uint16
+	Pad2  uint16
+	ImgSz uint32
+	Flags uint32
+	Vers  ImageVersion
+	Pad3  uint32
+}
+
+type ImageV1 struct {
+	Header ImageHdrV1
+	Body   []byte
+	Tlvs   []ImageTlv
+}
+
+func (img *ImageV1) FindTlvs(tlvType uint8) []ImageTlv {
+	var tlvs []ImageTlv
+
+	for _, tlv := range img.Tlvs {
+		if tlv.Header.Type == tlvType {
+			tlvs = append(tlvs, tlv)
+		}
+	}
+
+	return tlvs
+}
+
+func (img *ImageV1) Hash() ([]byte, error) {
+	tlvs := img.FindTlvs(IMAGEv1_TLV_SHA256)
+	if len(tlvs) == 0 {
+		return nil, util.FmtNewtError("Image does not contain hash TLV")
+	}
+	if len(tlvs) > 1 {
+		return nil, util.FmtNewtError("Image contains %d hash TLVs", len(tlvs))
+	}
+
+	return tlvs[0].Data, nil
+}
+
+func (img *ImageV1) WritePlusOffsets(w io.Writer) (ImageOffsets, error) {
+	offs := ImageOffsets{}
+	offset := 0
+
+	offs.Header = offset
+
+	err := binary.Write(w, binary.LittleEndian, &img.Header)
+	if err != nil {
+		return offs, util.ChildNewtError(err)
+	}
+	offset += IMAGE_HEADER_SIZE
+
+	offs.Body = offset
+	size, err := w.Write(img.Body)
+	if err != nil {
+		return offs, util.ChildNewtError(err)
+	}
+	offset += size
+
+	for _, tlv := range img.Tlvs {
+		offs.Tlvs = append(offs.Tlvs, offset)
+		size, err := tlv.Write(w)
+		if err != nil {
+			return offs, util.ChildNewtError(err)
+		}
+		offset += size
+	}
+
+	offs.TotalSize = offset
+
+	return offs, nil
+}
+
+func (img *ImageV1) Offsets() (ImageOffsets, error) {
+	return img.WritePlusOffsets(ioutil.Discard)
+}
+
+func (img *ImageV1) TotalSize() (int, error) {
+	offs, err := img.Offsets()
+	if err != nil {
+		return 0, err
+	}
+	return offs.TotalSize, nil
+}
+
+func (img *ImageV1) Write(w io.Writer) (int, error) {
+	offs, err := img.WritePlusOffsets(w)
+	if err != nil {
+		return 0, err
+	}
+
+	return offs.TotalSize, nil
+}
+
+func (key *ImageSigKey) sigHdrTypeV1() (uint32, error) {
+	key.assertValid()
+
+	if key.Rsa != nil {
+		if UseRsaPss {
+			return IMAGEv1_F_PKCS1_PSS_RSA2048_SHA256, nil
+		} else {
+			return IMAGEv1_F_PKCS15_RSA2048_SHA256, nil
+		}
+	} else {
+		switch key.Ec.Curve.Params().Name {
+		case "P-224":
+			return IMAGEv1_F_ECDSA224_SHA256, nil
+		case "P-256":
+			return IMAGEv1_F_ECDSA256_SHA256, nil
+		default:
+			return 0, util.FmtNewtError("Unsupported ECC curve")
+		}
+	}
+}
+
+func (key *ImageSigKey) sigTlvTypeV1() uint8 {
+	key.assertValid()
+
+	if key.Rsa != nil {
+		return IMAGEv1_TLV_RSA2048
+	} else {
+		switch key.Ec.Curve.Params().Name {
+		case "P-224":
+			return IMAGEv1_TLV_ECDSA224
+		case "P-256":
+			return IMAGEv1_TLV_ECDSA256
+		default:
+			return 0
+		}
+	}
+}
+
+func generateV1SigRsa(key *rsa.PrivateKey, hash []byte) ([]byte, error) {
+	var signature []byte
+	var err error
+
+	if UseRsaPss {
+		opts := rsa.PSSOptions{
+			SaltLength: rsa.PSSSaltLengthEqualsHash,
+		}
+		signature, err = rsa.SignPSS(
+			rand.Reader, key, crypto.SHA256, hash, &opts)
+	} else {
+		signature, err = rsa.SignPKCS1v15(
+			rand.Reader, key, crypto.SHA256, hash)
+	}
+	if err != nil {
+		return nil, util.FmtNewtError("Failed to compute signature: %s", err)
+	}
+
+	return signature, nil
+}
+
+func generateV1SigTlvRsa(key ImageSigKey, hash []byte) (ImageTlv, error) {
+	sig, err := generateV1SigRsa(key.Rsa, hash)
+	if err != nil {
+		return ImageTlv{}, err
+	}
+
+	return ImageTlv{
+		Header: ImageTlvHdr{
+			Type: key.sigTlvTypeV1(),
+			Pad:  0,
+			Len:  256, /* 2048 bits */
+		},
+		Data: sig,
+	}, nil
+}
+
+func generateV1SigTlvEc(key ImageSigKey, hash []byte) (ImageTlv, error) {
+	sig, err := generateSigEc(key.Ec, hash)
+	if err != nil {
+		return ImageTlv{}, err
+	}
+
+	sigLen := key.sigLen()
+	if len(sig) > int(sigLen) {
+		return ImageTlv{}, util.FmtNewtError("Something is really wrong\n")
+	}
+
+	b := &bytes.Buffer{}
+
+	if _, err := b.Write(sig); err != nil {
+		return ImageTlv{},
+			util.FmtNewtError("Failed to append sig: %s", err.Error())
+	}
+
+	pad := make([]byte, int(sigLen)-len(sig))
+	if _, err := b.Write(pad); err != nil {
+		return ImageTlv{}, util.FmtNewtError(
+			"Failed to serialize image trailer: %s", err.Error())
+	}
+
+	return ImageTlv{
+		Header: ImageTlvHdr{
+			Type: key.sigTlvTypeV1(),
+			Pad:  0,
+			Len:  sigLen + uint16(len(pad)),
+		},
+		Data: b.Bytes(),
+	}, nil
+}
+
+func generateV1SigTlv(key ImageSigKey, hash []byte) (ImageTlv, error) {
+	key.assertValid()
+
+	if key.Rsa != nil {
+		return generateV1SigTlvRsa(key, hash)
+	} else {
+		return generateV1SigTlvEc(key, hash)
+	}
+}
+
+func calcHashV1(initialHash []byte, hdr ImageHdrV1,
+	plainBody []byte) ([]byte, error) {
+
+	hash := sha256.New()
+
+	add := func(itf interface{}) error {
+		if err := binary.Write(hash, binary.LittleEndian, itf); err != nil {
+			return util.FmtNewtError("Failed to hash data: %s", err.Error())
+		}
+
+		return nil
+	}
+
+	if initialHash != nil {
+		if err := add(initialHash); err != nil {
+			return nil, err
+		}
+	}
+
+	if err := add(hdr); err != nil {
+		return nil, err
+	}
+
+	extra := hdr.HdrSz - IMAGE_HEADER_SIZE
+	if extra > 0 {
+		b := make([]byte, extra)
+		if err := add(b); err != nil {
+			return nil, err
+		}
+	}
+
+	if err := add(plainBody); err != nil {
+		return nil, err
+	}
+
+	return hash.Sum(nil), nil
+}
+
+func (ic *ImageCreator) CreateV1() (ImageV1, error) {
+	ri := ImageV1{}
+
+	if len(ic.SigKeys) > 1 {
+		return ri, util.FmtNewtError(
+			"V1 image format only allows one key, %d keys specified",
+			len(ic.SigKeys))
+	}
+
+	// First the header
+	hdr := ImageHdrV1{
+		Magic: IMAGEv1_MAGIC,
+		TlvSz: 0, // Filled in later.
+		KeyId: 0,
+		Pad1:  0,
+		HdrSz: IMAGE_HEADER_SIZE,
+		Pad2:  0,
+		ImgSz: uint32(len(ic.Body)),
+		Flags: IMAGEv1_F_SHA256,
+		Vers:  ic.Version,
+		Pad3:  0,
+	}
+
+	if !ic.Bootable {
+		hdr.Flags |= IMAGEv1_F_NON_BOOTABLE
+	}
+
+	if ic.HeaderSize != 0 {
+		/*
+		 * Pad the header out to the given size.  There will
+		 * just be zeros between the header and the start of
+		 * the image when it is padded.
+		 */
+		if ic.HeaderSize < IMAGE_HEADER_SIZE {
+			return ri, util.FmtNewtError("Image header must be at "+
+				"least %d bytes", IMAGE_HEADER_SIZE)
+		}
+
+		hdr.HdrSz = uint16(ic.HeaderSize)
+	}
+
+	if len(ic.SigKeys) > 0 {
+		keyFlag, err := ic.SigKeys[0].sigHdrTypeV1()
+		if err != nil {
+			return ri, err
+		}
+		hdr.Flags |= keyFlag
+		hdr.TlvSz = 4 + ic.SigKeys[0].sigLen()
+	}
+	hdr.TlvSz += 4 + 32
+
+	if hdr.HdrSz > IMAGE_HEADER_SIZE {
+		// Pad the header out to the given size.  There will
+		// just be zeros between the header and the start of
+		// the image when it is padded.
+		extra := ic.HeaderSize - IMAGE_HEADER_SIZE
+		if extra < 0 {
+			return ri, util.FmtNewtError("Image header must be at "+
+				"least %d bytes", IMAGE_HEADER_SIZE)
+		}
+
+		hdr.HdrSz = uint16(ic.HeaderSize)
+		for i := 0; i < extra; i++ {
+			ri.Body = append(ri.Body, 0)
+		}
+	}
+
+	hashBytes, err := calcHashV1(ic.InitialHash, hdr, ic.Body)
+	if err != nil {
+		return ri, err
+	}
+
+	util.StatusMessage(util.VERBOSITY_VERBOSE,
+		"Computed Hash for image as %s\n", hex.EncodeToString(hashBytes))
+
+	/*
+	 * Followed by data.
+	 */
+	dataBuf := make([]byte, 1024)
+	r := bytes.NewReader(ic.Body)
+	w := bytes.Buffer{}
+	for {
+		cnt, err := r.Read(dataBuf)
+		if err != nil && err != io.EOF {
+			return ri, util.FmtNewtError(
+				"Failed to read from image body: %s", err.Error())
+		}
+		if cnt == 0 {
+			break
+		}
+
+		if _, err = w.Write(dataBuf[0:cnt]); err != nil {
+			return ri, util.FmtNewtError(
+				"Failed to write to image body: %s", err.Error())
+		}
+	}
+	ri.Body = w.Bytes()
+
+	// Hash TLV.
+	tlv := ImageTlv{
+		Header: ImageTlvHdr{
+			Type: IMAGEv1_TLV_SHA256,
+			Pad:  0,
+			Len:  uint16(len(hashBytes)),
+		},
+		Data: hashBytes,
+	}
+	ri.Tlvs = append(ri.Tlvs, tlv)
+
+	if len(ic.SigKeys) > 0 {
+		tlv, err := generateV1SigTlv(ic.SigKeys[0], hashBytes)
+		if err != nil {
+			return ri, err
+		}
+		ri.Tlvs = append(ri.Tlvs, tlv)
+	}
+
+	offs, err := ri.Offsets()
+	if err != nil {
+		return ri, err
+	}
+	hdr.TlvSz = uint16(offs.TotalSize - offs.Tlvs[0])
+
+	ri.Header = hdr
+
+	return ri, nil
+}
+
+func GenerateV1Image(opts ImageCreateOpts) (ImageV1, error) {
+	ic := NewImageCreator()
+
+	srcBin, err := ioutil.ReadFile(opts.SrcBinFilename)
+	if err != nil {
+		return ImageV1{}, util.FmtNewtError(
+			"Can't read app binary: %s", err.Error())
+	}
+
+	ic.Body = srcBin
+	ic.Version = opts.Version
+	ic.SigKeys = opts.SigKeys
+
+	if opts.LoaderHash != nil {
+		ic.InitialHash = opts.LoaderHash
+		ic.Bootable = false
+	} else {
+		ic.Bootable = true
+	}
+
+	if opts.SrcEncKeyFilename != "" {
+		plainSecret := make([]byte, 16)
+		if _, err := rand.Read(plainSecret); err != nil {
+			return ImageV1{}, util.FmtNewtError(
+				"Random generation error: %s\n", err)
+		}
+
+		cipherSecret, err := ReadEncKey(opts.SrcEncKeyFilename, plainSecret)
+		if err != nil {
+			return ImageV1{}, err
+		}
+
+		ic.PlainSecret = plainSecret
+		ic.CipherSecret = cipherSecret
+	}
+
+	ri, err := ic.CreateV1()
+	if err != nil {
+		return ImageV1{}, err
+	}
+
+	return ri, nil
+}
diff --git a/manifest/manifest.go b/manifest/manifest.go
new file mode 100644
index 0000000..62d8c06
--- /dev/null
+++ b/manifest/manifest.go
@@ -0,0 +1,95 @@
+package manifest
+
+import (
+	"encoding/json"
+	"io"
+	"io/ioutil"
+
+	"mynewt.apache.org/newt/artifact/flash"
+	"mynewt.apache.org/newt/util"
+)
+
+/*
+ * Data that's going to go to build manifest file
+ */
+type ManifestSizeArea struct {
+	Name string `json:"name"`
+	Size uint32 `json:"size"`
+}
+
+type ManifestSizeSym struct {
+	Name  string              `json:"name"`
+	Areas []*ManifestSizeArea `json:"areas"`
+}
+
+type ManifestSizeFile struct {
+	Name string             `json:"name"`
+	Syms []*ManifestSizeSym `json:"sym"`
+}
+
+type ManifestSizePkg struct {
+	Name  string              `json:"name"`
+	Files []*ManifestSizeFile `json:"files"`
+}
+
+type ManifestPkg struct {
+	Name string `json:"name"`
+	Repo string `json:"repo"`
+}
+
+type ManifestRepo struct {
+	Name   string `json:"name"`
+	Commit string `json:"commit"`
+	Dirty  bool   `json:"dirty,omitempty"`
+	URL    string `json:"url,omitempty"`
+}
+
+type Manifest struct {
+	Name       string            `json:"name"`
+	Date       string            `json:"build_time"`
+	Version    string            `json:"build_version"`
+	BuildID    string            `json:"id"`
+	Image      string            `json:"image"`
+	ImageHash  string            `json:"image_hash"`
+	Loader     string            `json:"loader"`
+	LoaderHash string            `json:"loader_hash"`
+	Pkgs       []*ManifestPkg    `json:"pkgs"`
+	LoaderPkgs []*ManifestPkg    `json:"loader_pkgs,omitempty"`
+	TgtVars    []string          `json:"target"`
+	Repos      []*ManifestRepo   `json:"repos"`
+	FlashAreas []flash.FlashArea `json:"flash_map"`
+
+	PkgSizes       []*ManifestSizePkg `json:"pkgsz"`
+	LoaderPkgSizes []*ManifestSizePkg `json:"loader_pkgsz,omitempty"`
+}
+
+func ReadManifest(path string) (Manifest, error) {
+	m := Manifest{}
+
+	content, err := ioutil.ReadFile(path)
+	if err != nil {
+		return m, util.ChildNewtError(err)
+	}
+
+	if err := json.Unmarshal(content, &m); err != nil {
+		return m, util.FmtNewtError(
+			"Failure decoding manifest with path \"%s\": %s",
+			path, err.Error())
+	}
+
+	return m, nil
+}
+
+func (m *Manifest) Write(w io.Writer) (int, error) {
+	buffer, err := json.MarshalIndent(m, "", "  ")
+	if err != nil {
+		return 0, util.FmtNewtError("Cannot encode manifest: %s", err.Error())
+	}
+
+	cnt, err := w.Write(buffer)
+	if err != nil {
+		return 0, util.FmtNewtError("Cannot write manifest: %s", err.Error())
+	}
+
+	return cnt, nil
+}


[mynewt-artifact] 19/23: Add missing Apache licenses

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

commit 5b2c4f803ea2330ce8799c026fe4606a1ab679e6
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Fri Jun 21 11:53:27 2019 -0700

    Add missing Apache licenses
---
 LICENSE             | 311 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 errors/errors.go    |  23 +++-
 image/image_test.go |  19 ++++
 image/map.go        |  19 ++++
 image/parse.go      |  19 ++++
 image/verify.go     |  19 ++++
 mfg/mfg_test.go     |  19 ++++
 mfg/parse.go        |  19 ++++
 mfg/verify.go       |  19 ++++
 sec/sig.go          |  19 ++++
 10 files changed, 484 insertions(+), 2 deletions(-)

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c4c0480
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,311 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed 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.
+
+This product bundles pretty, which is available under the MIT license.  For
+details, see:
+     * newt/vendor/github.com/kr/pretty/License
+     * newtmgr/vendor/github.com/kr/pretty/License
+
+This product bundles kr/text, which is available under the MIT license.  For
+details, see:
+     * newt/vendor/github.com/kr/text/License
+     * newtmgr/vendor/github.com/kr/text/License
+
+This product bundles mapstructure, which is available under the MIT license.
+For details, see:
+    * newt/vendor/github.com/mitchellh/mapstructure/LICENSE
+    * newtmgr/vendor/github.com/mitchellh/mapstructure/LICENSE
+
+This product bundles logrus, which is available under the MIT license.  For
+details, see:
+    * newt/vendor/github.com/sirupsen/logrus/LICENSE
+    * newtmgr/vendor/github.com/sirupsen/logrus/LICENSE
+
+This product bundles Cast, which is available under the MIT license.  For
+details, see:
+    * newt/vendor/github.com/spf13/cast/LICENSE
+    * newtmgr/vendor/github.com/spf13/cast/LICENSE
+
+This product bundles jWalterWeatherman, which is available under the MIT
+license.  For details, see:
+    * newt/vendor/github.com/spf13/jwalterweatherman/LICENSE
+    * newtmgr/vendor/github.com/spf13/jwalterweatherman/LICENSE
+
+This product bundles pflag, which is available under the "3-clause BSD"
+license.  For details, see:
+    * newt/vendor/github.com/spf13/pflag/LICENSE
+    * newtmgr/vendor/github.com/spf13/pflag/LICENSE
+
+This product bundles the unix Go package, which is available under the
+"3-clause BSD" license.  For details, see:
+    * newt/vendor/golang.org/x/sys/LICENSE
+    * newtmgr/vendor/golang.org/x/sys/LICENSE
+
+This product bundles fsnotify.v1, which is available under the "3-clause BSD"
+license.  For details, see:
+    * newt/vendor/gopkg.in/fsnotify.v1/LICENSE
+    * newtmgr/vendor/gopkg.in/fsnotify.v1/LICENSE
+
+This product bundles yaml.v2's Go port of libyaml, which is available under the
+MIT license.  For details, see:
+    * newt/vendor/mynewt.apache.org/newt/yaml/apic.go
+    * newt/vendor/mynewt.apache.org/newt/yaml/emitterc.go
+    * newt/vendor/mynewt.apache.org/newt/yaml/parserc.go
+    * newt/vendor/mynewt.apache.org/newt/yaml/readerc.go
+    * newt/vendor/mynewt.apache.org/newt/yaml/scannerc.go
+    * newt/vendor/mynewt.apache.org/newt/yaml/writerc.go
+    * newt/vendor/mynewt.apache.org/newt/yaml/yamlh.go
+    * newt/vendor/mynewt.apache.org/newt/yaml/yamlprivateh.go
+    * newtmgr/vendor/mynewt.apache.org/newt/yaml/apic.go
+    * newtmgr/vendor/mynewt.apache.org/newt/yaml/emitterc.go
+    * newtmgr/vendor/mynewt.apache.org/newt/yaml/parserc.go
+    * newtmgr/vendor/mynewt.apache.org/newt/yaml/readerc.go
+    * newtmgr/vendor/mynewt.apache.org/newt/yaml/scannerc.go
+    * newtmgr/vendor/mynewt.apache.org/newt/yaml/writerc.go
+    * newtmgr/vendor/mynewt.apache.org/newt/yaml/yamlh.go
+    * newtmgr/vendor/mynewt.apache.org/newt/yaml/yamlprivateh.go
+    * yaml/apic.go
+    * yaml/emitterc.go
+    * yaml/parserc.go
+    * yaml/readerc.go
+    * yaml/scannerc.go
+    * yaml/writerc.go
+    * yaml/yamlh.go
+    * yaml/yamlprivateh.go
+
+This product bundles viper, which is available under the MIT license.  For
+details, see:
+    * newt/vendor/mynewt.apache.org/newt/viper/LICENSE
+    * newtmgr/vendor/mynewt.apache.org/newt/viper/LICENSE
+    * viper/LICENSE
+
+This product bundles go-crc16, which is available under the MIT license.  For
+details, see:
+    * newtmgr/vendor/github.com/joaojeronimo/go-crc16/README.md
+
+This product bundles GATT, which is available under the "3-clause BSD" license.
+For details, see:
+    * newtmgr/vendor/github.com/runtimeinc/gatt
+
+This product bundles xpc, which is available under the MIT license.  For
+details, see:
+    * newtmgr/vendor/github.com/runtimeinc/gatt/xpc/LICENSE
+
+This product bundles gioctl, which is available under the MIT license.  For
+details, see:
+    * newtmgr/vendor/github.com/runtimeinc/gatt/linux/gioctl/LICENSE.md
+
+This product bundles tarm/serial, which is available under the "3-clause BSD"
+license.  For details, see:
+    * newtmgr/vendor/github.com/tarm/serial/LICENSE
+
+This product bundles ugorji/go/codec, which is available under the MIT license.
+For details, see:
+    * newtmgr/vendor/github.com/ugorji/go/LICENSE
+
+This product bundles go-coap which is available under the MIT license.
+For details, see:
+    * newtmgr/vendor/github.com/dustin/go-coap/LICENSE
+
+This product bundles go-homedir which is available under the MIT license.
+For details, see:
+    * newtmgr/vendor/github.com/mitchellh/go-homedir/LICENSE
diff --git a/errors/errors.go b/errors/errors.go
index 4b76b41..34113a2 100644
--- a/errors/errors.go
+++ b/errors/errors.go
@@ -1,5 +1,24 @@
-// This is a thin wrapper over the `pkg/errors` package.  It decorates this
-// package with the following functionality:
+/**
+ * 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.
+ */
+
+// This is a thin wrapper over the `pkg/errors` package.  It decorates the
+// base package with the following functionality:
 //
 // 1. Wrap and Wrapf produce an error with exactly one stack trace.  If the
 // wrapped error already contains a stack trace, these functions just append
diff --git a/image/image_test.go b/image/image_test.go
index fb0d8df..83b4295 100644
--- a/image/image_test.go
+++ b/image/image_test.go
@@ -1,3 +1,22 @@
+/**
+ * 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 image
 
 import (
diff --git a/image/map.go b/image/map.go
index 76c6aa0..ac8acef 100644
--- a/image/map.go
+++ b/image/map.go
@@ -1,3 +1,22 @@
+/**
+ * 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 image
 
 import (
diff --git a/image/parse.go b/image/parse.go
index f1019a8..200d0f8 100644
--- a/image/parse.go
+++ b/image/parse.go
@@ -1,3 +1,22 @@
+/**
+ * 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 image
 
 import (
diff --git a/image/verify.go b/image/verify.go
index 91ddf28..0ac2b25 100644
--- a/image/verify.go
+++ b/image/verify.go
@@ -1,3 +1,22 @@
+/**
+ * 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 image
 
 import (
diff --git a/mfg/mfg_test.go b/mfg/mfg_test.go
index a2e503e..60f51fd 100644
--- a/mfg/mfg_test.go
+++ b/mfg/mfg_test.go
@@ -1,3 +1,22 @@
+/**
+ * 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 mfg
 
 import (
diff --git a/mfg/parse.go b/mfg/parse.go
index 1d4e4b7..78343e7 100644
--- a/mfg/parse.go
+++ b/mfg/parse.go
@@ -1,3 +1,22 @@
+/**
+ * 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 mfg
 
 import (
diff --git a/mfg/verify.go b/mfg/verify.go
index ffa2817..7fc9f22 100644
--- a/mfg/verify.go
+++ b/mfg/verify.go
@@ -1,3 +1,22 @@
+/**
+ * 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 mfg
 
 import (
diff --git a/sec/sig.go b/sec/sig.go
index d6b8ffe..26a57ac 100644
--- a/sec/sig.go
+++ b/sec/sig.go
@@ -1,3 +1,22 @@
+/**
+ * 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 sec
 
 import (


[mynewt-artifact] 03/23: Artifact library update

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

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

    Artifact library update
---
 flash/flash.go               |  10 +--
 image/create.go              | 147 ++++++++++++++++++++++---------------------
 image/image.go               |  52 +++++++++------
 image/key.go                 |  66 +++++++++++++++----
 image/v1.go                  |  16 +++--
 mfg/mfg.go                   |  74 +++++++++++++---------
 mfg/paths.go => misc/misc.go |  13 +---
 7 files changed, 228 insertions(+), 150 deletions(-)

diff --git a/flash/flash.go b/flash/flash.go
index c37d2dd..6b85814 100644
--- a/flash/flash.go
+++ b/flash/flash.go
@@ -38,11 +38,11 @@ var SYSTEM_AREA_NAME_ID_MAP = map[string]int{
 }
 
 type FlashArea struct {
-	Name   string
-	Id     int
-	Device int
-	Offset int
-	Size   int
+	Name   string `json:"name"`
+	Id     int    `json:"id"`
+	Device int    `json:"device"`
+	Offset int    `json:"offset"`
+	Size   int    `json:"size"`
 }
 
 type areaOffSorter struct {
diff --git a/image/create.go b/image/create.go
index c3e8820..76e701d 100644
--- a/image/create.go
+++ b/image/create.go
@@ -69,7 +69,7 @@ func NewImageCreator() ImageCreator {
 	}
 }
 
-func generateEncTlv(cipherSecret []byte) (ImageTlv, error) {
+func GenerateEncTlv(cipherSecret []byte) (ImageTlv, error) {
 	var encType uint8
 
 	if len(cipherSecret) == 256 {
@@ -90,7 +90,7 @@ func generateEncTlv(cipherSecret []byte) (ImageTlv, error) {
 	}, nil
 }
 
-func generateSigRsa(key ImageSigKey, hash []byte) ([]byte, error) {
+func GenerateSigRsa(key ImageSigKey, hash []byte) ([]byte, error) {
 	opts := rsa.PSSOptions{
 		SaltLength: rsa.PSSSaltLengthEqualsHash,
 	}
@@ -103,7 +103,7 @@ func generateSigRsa(key ImageSigKey, hash []byte) ([]byte, error) {
 	return signature, nil
 }
 
-func generateSigEc(key ImageSigKey, hash []byte) ([]byte, error) {
+func GenerateSigEc(key ImageSigKey, hash []byte) ([]byte, error) {
 	r, s, err := ecdsa.Sign(rand.Reader, key.Ec, hash)
 	if err != nil {
 		return nil, util.FmtNewtError("Failed to compute signature: %s", err)
@@ -130,13 +130,13 @@ func generateSigEc(key ImageSigKey, hash []byte) ([]byte, error) {
 	return signature, nil
 }
 
-func generateSig(key ImageSigKey, hash []byte) ([]byte, error) {
+func GenerateSig(key ImageSigKey, hash []byte) ([]byte, error) {
 	key.assertValid()
 
 	if key.Rsa != nil {
-		return generateSigRsa(key, hash)
+		return GenerateSigRsa(key, hash)
 	} else {
-		return generateSigEc(key, hash)
+		return GenerateSigEc(key, hash)
 	}
 }
 
@@ -167,7 +167,7 @@ func BuildSigTlvs(keys []ImageSigKey, hash []byte) ([]ImageTlv, error) {
 		tlvs = append(tlvs, tlv)
 
 		// Signature TLV.
-		sig, err := generateSig(key, hash)
+		sig, err := GenerateSig(key, hash)
 		if err != nil {
 			return nil, err
 		}
@@ -205,13 +205,18 @@ func GenerateImage(opts ImageCreateOpts) (Image, error) {
 	}
 
 	if opts.SrcEncKeyFilename != "" {
-		plainSecret := make([]byte, 16)
-		if _, err := rand.Read(plainSecret); err != nil {
+		plainSecret, err := GeneratePlainSecret()
+		if err != nil {
+			return Image{}, err
+		}
+
+		pubKeBytes, err := ioutil.ReadFile(opts.SrcEncKeyFilename)
+		if err != nil {
 			return Image{}, util.FmtNewtError(
-				"Random generation error: %s\n", err)
+				"Error reading pubkey file: %s", err.Error())
 		}
 
-		cipherSecret, err := ReadEncKey(opts.SrcEncKeyFilename, plainSecret)
+		cipherSecret, err := GenerateCipherSecret(pubKeBytes, plainSecret)
 		if err != nil {
 			return Image{}, err
 		}
@@ -228,7 +233,7 @@ func GenerateImage(opts ImageCreateOpts) (Image, error) {
 	return ri, nil
 }
 
-func calcHash(initialHash []byte, hdr ImageHdr,
+func calcHash(initialHash []byte, hdr ImageHdr, pad []byte,
 	plainBody []byte) ([]byte, error) {
 
 	hash := sha256.New()
@@ -255,6 +260,10 @@ func calcHash(initialHash []byte, hdr ImageHdr,
 		return nil, err
 	}
 
+	if err := add(pad); err != nil {
+		return nil, err
+	}
+
 	extra := hdr.HdrSz - IMAGE_HEADER_SIZE
 	if extra > 0 {
 		b := make([]byte, extra)
@@ -270,11 +279,43 @@ func calcHash(initialHash []byte, hdr ImageHdr,
 	return hash.Sum(nil), nil
 }
 
+func EncryptImageBody(imageBody []byte, secret []byte) ([]byte, error) {
+	block, err := aes.NewCipher(secret)
+	if err != nil {
+		return nil, util.NewNewtError("Failed to create block cipher")
+	}
+	nonce := make([]byte, 16)
+	stream := cipher.NewCTR(block, nonce)
+
+	dataBuf := make([]byte, 16)
+	encBuf := make([]byte, 16)
+	r := bytes.NewReader(imageBody)
+	w := bytes.Buffer{}
+	for {
+		cnt, err := r.Read(dataBuf)
+		if err != nil && err != io.EOF {
+			return nil, util.FmtNewtError(
+				"Failed to read from image body: %s", err.Error())
+		}
+		if cnt == 0 {
+			break
+		}
+
+		stream.XORKeyStream(encBuf, dataBuf[0:cnt])
+		if _, err = w.Write(encBuf[0:cnt]); err != nil {
+			return nil, util.FmtNewtError(
+				"Failed to write to image body: %s", err.Error())
+		}
+	}
+
+	return w.Bytes(), nil
+}
+
 func (ic *ImageCreator) Create() (Image, error) {
-	ri := Image{}
+	img := Image{}
 
 	// First the header
-	hdr := ImageHdr{
+	img.Header = ImageHdr{
 		Magic: IMAGE_MAGIC,
 		Pad1:  0,
 		HdrSz: IMAGE_HEADER_SIZE,
@@ -286,75 +327,41 @@ func (ic *ImageCreator) Create() (Image, error) {
 	}
 
 	if !ic.Bootable {
-		hdr.Flags |= IMAGE_F_NON_BOOTABLE
+		img.Header.Flags |= IMAGE_F_NON_BOOTABLE
 	}
 
 	if ic.CipherSecret != nil {
-		hdr.Flags |= IMAGE_F_ENCRYPTED
+		img.Header.Flags |= IMAGE_F_ENCRYPTED
 	}
 
 	if ic.HeaderSize != 0 {
-		// Pad the header out to the given size.  There will
-		// just be zeros between the header and the start of
-		// the image when it is padded.
+		// Pad the header out to the given size.  There will just be zeros
+		// between the header and the start of the image when it is padded.
 		extra := ic.HeaderSize - IMAGE_HEADER_SIZE
 		if extra < 0 {
-			return ri, util.FmtNewtError("Image header must be at "+
+			return img, util.FmtNewtError("Image header must be at "+
 				"least %d bytes", IMAGE_HEADER_SIZE)
 		}
 
-		hdr.HdrSz = uint16(ic.HeaderSize)
-		for i := 0; i < extra; i++ {
-			ri.Body = append(ri.Body, 0)
-		}
+		img.Header.HdrSz = uint16(ic.HeaderSize)
+		img.Pad = make([]byte, extra)
 	}
 
-	ri.Header = hdr
-
-	hashBytes, err := calcHash(ic.InitialHash, hdr, ic.Body)
+	hashBytes, err := calcHash(ic.InitialHash, img.Header, img.Pad, ic.Body)
 	if err != nil {
-		return ri, err
+		return img, err
 	}
 
-	var stream cipher.Stream
+	// Followed by data.
 	if ic.CipherSecret != nil {
-		block, err := aes.NewCipher(ic.PlainSecret)
+		encBody, err := EncryptImageBody(ic.Body, ic.PlainSecret)
 		if err != nil {
-			return ri, util.NewNewtError("Failed to create block cipher")
-		}
-		nonce := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
-		stream = cipher.NewCTR(block, nonce)
-	}
-
-	/*
-	 * Followed by data.
-	 */
-	dataBuf := make([]byte, 16)
-	encBuf := make([]byte, 16)
-	r := bytes.NewReader(ic.Body)
-	w := bytes.Buffer{}
-	for {
-		cnt, err := r.Read(dataBuf)
-		if err != nil && err != io.EOF {
-			return ri, util.FmtNewtError(
-				"Failed to read from image body: %s", err.Error())
-		}
-		if cnt == 0 {
-			break
-		}
-
-		if ic.CipherSecret == nil {
-			_, err = w.Write(dataBuf[0:cnt])
-		} else {
-			stream.XORKeyStream(encBuf, dataBuf[0:cnt])
-			_, err = w.Write(encBuf[0:cnt])
-		}
-		if err != nil {
-			return ri, util.FmtNewtError(
-				"Failed to write to image body: %s", err.Error())
+			return img, err
 		}
+		img.Body = append(img.Body, encBody...)
+	} else {
+		img.Body = append(img.Body, ic.Body...)
 	}
-	ri.Body = append(ri.Body, w.Bytes()...)
 
 	util.StatusMessage(util.VERBOSITY_VERBOSE,
 		"Computed Hash for image as %s\n", hex.EncodeToString(hashBytes))
@@ -368,21 +375,21 @@ func (ic *ImageCreator) Create() (Image, error) {
 		},
 		Data: hashBytes,
 	}
-	ri.Tlvs = append(ri.Tlvs, tlv)
+	img.Tlvs = append(img.Tlvs, tlv)
 
 	tlvs, err := BuildSigTlvs(ic.SigKeys, hashBytes)
 	if err != nil {
-		return ri, err
+		return img, err
 	}
-	ri.Tlvs = append(ri.Tlvs, tlvs...)
+	img.Tlvs = append(img.Tlvs, tlvs...)
 
 	if ic.CipherSecret != nil {
-		tlv, err := generateEncTlv(ic.CipherSecret)
+		tlv, err := GenerateEncTlv(ic.CipherSecret)
 		if err != nil {
-			return ri, err
+			return img, err
 		}
-		ri.Tlvs = append(ri.Tlvs, tlv)
+		img.Tlvs = append(img.Tlvs, tlv)
 	}
 
-	return ri, nil
+	return img, nil
 }
diff --git a/image/image.go b/image/image.go
index 1ed093f..705abe6 100644
--- a/image/image.go
+++ b/image/image.go
@@ -113,6 +113,7 @@ type ImageTrailer struct {
 
 type Image struct {
 	Header ImageHdr
+	Pad    []byte
 	Body   []byte
 	Tlvs   []ImageTlv
 }
@@ -186,36 +187,36 @@ func (ver ImageVersion) String() string {
 
 func (h *ImageHdr) Map(offset int) map[string]interface{} {
 	return map[string]interface{}{
-		"Magic":  h.Magic,
-		"HdrSz":  h.HdrSz,
-		"ImgSz":  h.ImgSz,
-		"Flags":  h.Flags,
-		"Vers":   h.Vers.String(),
-		"offset": offset,
+		"magic":   h.Magic,
+		"hdr_sz":  h.HdrSz,
+		"img_sz":  h.ImgSz,
+		"flags":   h.Flags,
+		"vers":    h.Vers.String(),
+		"_offset": offset,
 	}
 }
 
 func rawBodyMap(offset int) map[string]interface{} {
 	return map[string]interface{}{
-		"offset": offset,
+		"_offset": offset,
 	}
 }
 
 func (t *ImageTrailer) Map(offset int) map[string]interface{} {
 	return map[string]interface{}{
-		"Magic":     t.Magic,
-		"TlvTotLen": t.TlvTotLen,
-		"offset":    offset,
+		"magic":       t.Magic,
+		"tlv_tot_len": t.TlvTotLen,
+		"_offset":     offset,
 	}
 }
 
 func (t *ImageTlv) Map(offset int) map[string]interface{} {
 	return map[string]interface{}{
-		"Type":    t.Header.Type,
-		"typestr": ImageTlvTypeName(t.Header.Type),
-		"Len":     t.Header.Len,
-		"offset":  offset,
-		"data":    hex.EncodeToString(t.Data),
+		"type":     t.Header.Type,
+		"len":      t.Header.Len,
+		"data":     hex.EncodeToString(t.Data),
+		"_typestr": ImageTlvTypeName(t.Header.Type),
+		"_offset":  offset,
 	}
 }
 
@@ -297,19 +298,26 @@ func (i *Image) FindUniqueTlv(tlvType uint8) (*ImageTlv, error) {
 	return &tlvs[0], nil
 }
 
-func (i *Image) RemoveTlvsIf(pred func(tlv ImageTlv) bool) int {
-	numRmed := 0
+func (i *Image) RemoveTlvsIf(pred func(tlv ImageTlv) bool) []ImageTlv {
+	rmed := []ImageTlv{}
+
 	for idx := 0; idx < len(i.Tlvs); {
 		tlv := i.Tlvs[idx]
 		if pred(tlv) {
+			rmed = append(rmed, tlv)
 			i.Tlvs = append(i.Tlvs[:idx], i.Tlvs[idx+1:]...)
-			numRmed++
 		} else {
 			idx++
 		}
 	}
 
-	return numRmed
+	return rmed
+}
+
+func (i *Image) RemoveTlvsWithType(tlvType uint8) []ImageTlv {
+	return i.RemoveTlvsIf(func(tlv ImageTlv) bool {
+		return tlv.Header.Type == tlvType
+	})
 }
 
 func (img *Image) Trailer() ImageTrailer {
@@ -349,6 +357,12 @@ func (i *Image) WritePlusOffsets(w io.Writer) (ImageOffsets, error) {
 	}
 	offset += IMAGE_HEADER_SIZE
 
+	err = binary.Write(w, binary.LittleEndian, i.Pad)
+	if err != nil {
+		return offs, util.ChildNewtError(err)
+	}
+	offset += len(i.Pad)
+
 	offs.Body = offset
 	size, err := w.Write(i.Body)
 	if err != nil {
diff --git a/image/key.go b/image/key.go
index 8345cd9..a343e2d 100644
--- a/image/key.go
+++ b/image/key.go
@@ -21,6 +21,7 @@ package image
 
 import (
 	"crypto/aes"
+	"crypto/cipher"
 	"crypto/ecdsa"
 	"crypto/rand"
 	"crypto/rsa"
@@ -212,7 +213,7 @@ func (key *ImageSigKey) sigTlvType() uint8 {
 	}
 }
 
-func parseEncKeyPem(keyBytes []byte, plainSecret []byte) ([]byte, error) {
+func ParsePubKePem(keyBytes []byte) (*rsa.PublicKey, error) {
 	b, _ := pem.Decode(keyBytes)
 	if b == nil {
 		return nil, nil
@@ -237,6 +238,20 @@ func parseEncKeyPem(keyBytes []byte, plainSecret []byte) ([]byte, error) {
 			"Error parsing pubkey file: %s", err.Error())
 	}
 
+	return pubk, nil
+}
+
+func ParsePrivKeDer(keyBytes []byte) (*rsa.PrivateKey, error) {
+	privKey, err := x509.ParsePKCS1PrivateKey(keyBytes)
+	if err != nil {
+		return nil, util.FmtNewtError(
+			"Error parsing private key file: %s", err.Error())
+	}
+
+	return privKey, nil
+}
+
+func EncryptSecretRsa(pubk *rsa.PublicKey, plainSecret []byte) ([]byte, error) {
 	rng := rand.Reader
 	cipherSecret, err := rsa.EncryptOAEP(
 		sha256.New(), rng, pubk, plainSecret, nil)
@@ -248,7 +263,21 @@ func parseEncKeyPem(keyBytes []byte, plainSecret []byte) ([]byte, error) {
 	return cipherSecret, nil
 }
 
-func parseEncKeyBase64(keyBytes []byte, plainSecret []byte) ([]byte, error) {
+func DecryptSecretRsa(privk *rsa.PrivateKey,
+	cipherSecret []byte) ([]byte, error) {
+
+	rng := rand.Reader
+	plainSecret, err := rsa.DecryptOAEP(
+		sha256.New(), rng, privk, cipherSecret, nil)
+	if err != nil {
+		return nil, util.FmtNewtError(
+			"Error from encryption: %s\n", err.Error())
+	}
+
+	return plainSecret, nil
+}
+
+func ParseKeBase64(keyBytes []byte) (cipher.Block, error) {
 	kek, err := base64.StdEncoding.DecodeString(string(keyBytes))
 	if err != nil {
 		return nil, util.FmtNewtError(
@@ -265,7 +294,11 @@ func parseEncKeyBase64(keyBytes []byte, plainSecret []byte) ([]byte, error) {
 			"Error creating keywrap cipher: %s", err.Error())
 	}
 
-	cipherSecret, err := keywrap.Wrap(cipher, plainSecret)
+	return cipher, nil
+}
+
+func encryptSecretAes(c cipher.Block, plainSecret []byte) ([]byte, error) {
+	cipherSecret, err := keywrap.Wrap(c, plainSecret)
 	if err != nil {
 		return nil, util.FmtNewtError("Error key-wrapping: %s", err.Error())
 	}
@@ -273,27 +306,36 @@ func parseEncKeyBase64(keyBytes []byte, plainSecret []byte) ([]byte, error) {
 	return cipherSecret, nil
 }
 
-func ReadEncKey(filename string, plainSecret []byte) ([]byte, error) {
-	keyBytes, err := ioutil.ReadFile(filename)
-	if err != nil {
+func GeneratePlainSecret() ([]byte, error) {
+	plainSecret := make([]byte, 16)
+	if _, err := rand.Read(plainSecret); err != nil {
 		return nil, util.FmtNewtError(
-			"Error reading pubkey file: %s", err.Error())
+			"Random generation error: %s\n", err)
 	}
 
+	return plainSecret, nil
+}
+
+func GenerateCipherSecret(pubKeBytes []byte,
+	plainSecret []byte) ([]byte, error) {
+
 	// Try reading as PEM (asymetric key).
-	cipherSecret, err := parseEncKeyPem(keyBytes, plainSecret)
+	rsaPubKe, err := ParsePubKePem(pubKeBytes)
 	if err != nil {
 		return nil, err
 	}
-	if cipherSecret != nil {
-		return cipherSecret, nil
+	if rsaPubKe != nil {
+		return EncryptSecretRsa(rsaPubKe, plainSecret)
 	}
 
 	// Not PEM; assume this is a base64 encoded symetric key
-	cipherSecret, err = parseEncKeyBase64(keyBytes, plainSecret)
+	aesPubKe, err := ParseKeBase64(pubKeBytes)
 	if err != nil {
 		return nil, err
 	}
+	if aesPubKe != nil {
+		return encryptSecretAes(aesPubKe, plainSecret)
+	}
 
-	return cipherSecret, nil
+	return nil, util.FmtNewtError("Invalid image-crypt key")
 }
diff --git a/image/v1.go b/image/v1.go
index 5540d85..0dc10a5 100644
--- a/image/v1.go
+++ b/image/v1.go
@@ -233,7 +233,7 @@ func generateV1SigTlvRsa(key ImageSigKey, hash []byte) (ImageTlv, error) {
 }
 
 func generateV1SigTlvEc(key ImageSigKey, hash []byte) (ImageTlv, error) {
-	sig, err := generateSigEc(key, hash)
+	sig, err := GenerateSigEc(key, hash)
 	if err != nil {
 		return ImageTlv{}, err
 	}
@@ -463,13 +463,17 @@ func GenerateV1Image(opts ImageCreateOpts) (ImageV1, error) {
 	}
 
 	if opts.SrcEncKeyFilename != "" {
-		plainSecret := make([]byte, 16)
-		if _, err := rand.Read(plainSecret); err != nil {
-			return ImageV1{}, util.FmtNewtError(
-				"Random generation error: %s\n", err)
+		plainSecret, err := GeneratePlainSecret()
+		if err != nil {
+			return ImageV1{}, err
 		}
 
-		cipherSecret, err := ReadEncKey(opts.SrcEncKeyFilename, plainSecret)
+		pubKeBytes, err := ioutil.ReadFile(opts.SrcEncKeyFilename)
+		if err != nil {
+			return ImageV1{}, util.FmtNewtError(
+				"Error reading pubkey file: %s", err.Error())
+		}
+		cipherSecret, err := GenerateCipherSecret(pubKeBytes, plainSecret)
 		if err != nil {
 			return ImageV1{}, err
 		}
diff --git a/mfg/mfg.go b/mfg/mfg.go
index 8e999ad..3e29523 100644
--- a/mfg/mfg.go
+++ b/mfg/mfg.go
@@ -7,7 +7,7 @@ import (
 )
 
 const MFG_IMG_FILENAME = "mfgimg.bin"
-const MFG_MANIFEST_FILENAME = "manifest.json"
+const MANIFEST_FILENAME = "manifest.json"
 
 type Mfg struct {
 	Bin  []byte
@@ -53,27 +53,6 @@ func AddPadding(b []byte, eraseVal byte, padLen int) []byte {
 	return b
 }
 
-func (m *Mfg) bytesZeroedHash(eraseVal byte) ([]byte, error) {
-	binCopy := make([]byte, len(m.Bin))
-	copy(binCopy, m.Bin)
-
-	m.Meta.ClearHash()
-
-	metaBytes, err := m.Meta.Bytes()
-	if err != nil {
-		return nil, err
-	}
-
-	padLen := m.MetaOff + len(metaBytes) - len(binCopy)
-	if padLen > 0 {
-		binCopy = AddPadding(binCopy, eraseVal, padLen)
-	}
-
-	copy(binCopy[m.MetaOff:m.MetaOff+len(metaBytes)], metaBytes)
-
-	return binCopy, nil
-}
-
 // Calculates the SHA256 hash, using the full manufacturing image as input.
 // Hash-calculation algorithm is as follows:
 // 1. Zero out the 32 bytes that will contain the hash.
@@ -86,11 +65,16 @@ func CalcHash(bin []byte) []byte {
 	return hash[:]
 }
 
-func (m *Mfg) Bytes(eraseVal byte) ([]byte, error) {
+func (m *Mfg) RecalcHash(eraseVal byte) error {
+	if m.Meta == nil || m.Meta.Hash() == nil {
+		return nil
+	}
+
 	// First, write with zeroed hash.
-	bin, err := m.bytesZeroedHash(eraseVal)
+	m.Meta.ClearHash()
+	bin, err := m.Bytes(eraseVal)
 	if err != nil {
-		return nil, err
+		return err
 	}
 
 	// Calculate hash and fill TLV.
@@ -101,13 +85,47 @@ func (m *Mfg) Bytes(eraseVal byte) ([]byte, error) {
 
 		hashOff := m.MetaOff + m.Meta.HashOffset()
 		if hashOff+META_HASH_SZ > len(bin) {
-			return nil, util.FmtNewtError(
+			return util.FmtNewtError(
 				"unexpected error: hash extends beyond end " +
 					"of manufacturing image")
 		}
+	}
+
+	return nil
+}
 
-		copy(bin[hashOff:hashOff+META_HASH_SZ], tlv.Data)
+func (m *Mfg) Hash() ([]byte, error) {
+	var hashBytes []byte
+	if m.Meta != nil {
+		hashBytes = m.Meta.Hash()
+	}
+	if hashBytes == nil {
+		// No hash TLV; calculate hash manually.
+		bin, err := m.Bytes(0xff)
+		if err != nil {
+			return nil, err
+		}
+		hashBytes = CalcHash(bin)
 	}
 
-	return bin, nil
+	return hashBytes, nil
+}
+
+func (m *Mfg) Bytes(eraseVal byte) ([]byte, error) {
+	binCopy := make([]byte, len(m.Bin))
+	copy(binCopy, m.Bin)
+
+	metaBytes, err := m.Meta.Bytes()
+	if err != nil {
+		return nil, err
+	}
+
+	padLen := m.MetaOff + len(metaBytes) - len(binCopy)
+	if padLen > 0 {
+		binCopy = AddPadding(binCopy, eraseVal, padLen)
+	}
+
+	copy(binCopy[m.MetaOff:m.MetaOff+len(metaBytes)], metaBytes)
+
+	return binCopy, nil
 }
diff --git a/mfg/paths.go b/misc/misc.go
similarity index 70%
rename from mfg/paths.go
rename to misc/misc.go
index 483aca2..2f685e1 100644
--- a/mfg/paths.go
+++ b/misc/misc.go
@@ -17,19 +17,12 @@
  * under the License.
  */
 
-package mfg
+package misc
 
 import (
 	"fmt"
-	"path/filepath"
 )
 
-const MANIFEST_FILENAME = "manifest.json"
-const BOOT_DIR = "bootloader"
-const BOOT_MANIFEST_PATH = BOOT_DIR + "/manifest.json"
-const SECTION_BIN_DIR = "sections"
-
-func SectionBinPath(mfgPkgName string, sectionNum int) string {
-	return fmt.Sprintf("%s/%s-s%d.bin", SECTION_BIN_DIR,
-		filepath.Base(mfgPkgName), sectionNum)
+func HashString(hash []byte) string {
+	return fmt.Sprintf("%x", hash)
 }


[mynewt-artifact] 18/23: Some code reorginazation and comments

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

commit fb24b39ed90f333627f0ccf885e0d4ab7ee04bbe
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Thu Jun 20 17:45:46 2019 -0700

    Some code reorginazation and comments
---
 errors/errors.go         |  18 ++-
 flash/flash.go           |   6 +-
 image/image.go           | 304 ++++++-----------------------------------------
 image/image_test.go      |   4 +-
 image/map.go             |  81 +++++++++++++
 image/parse.go           | 200 +++++++++++++++++++++++++++++++
 manifest/manifest.go     |   4 +
 manifest/mfg_manifest.go |  23 +++-
 mfg/map_meta.go          |   4 +
 mfg/meta.go              | 114 ++++--------------
 mfg/mfg.go               |  65 ++++------
 mfg/mfg_test.go          |   4 +-
 mfg/parse.go             | 118 ++++++++++++++++++
 mfg/verify.go            |   2 +-
 misc/misc.go             |  28 -----
 sec/key.go               |  23 ++--
 sec/sig.go               |   2 +-
 17 files changed, 543 insertions(+), 457 deletions(-)

diff --git a/errors/errors.go b/errors/errors.go
index 8154fc5..4b76b41 100644
--- a/errors/errors.go
+++ b/errors/errors.go
@@ -1,5 +1,5 @@
-// This is a thin wrapper over the `pkg/errors` package.  It decorates the
-// base package with the following functionality:
+// This is a thin wrapper over the `pkg/errors` package.  It decorates this
+// package with the following functionality:
 //
 // 1. Wrap and Wrapf produce an error with exactly one stack trace.  If the
 // wrapped error already contains a stack trace, these functions just append
@@ -26,16 +26,21 @@ func Cause(err error) error {
 	return pkgerrors.Cause(err)
 }
 
-// Errorf formats an error
+// Errorf formats according to a format specifier and returns the string
+// as a value that satisfies error.
+// Errorf also records the stack trace at the point it was called.
 func Errorf(format string, args ...interface{}) error {
 	return pkgerrors.Errorf(format, args...)
 }
 
-// New creates a new error
+// New returns an error with the supplied message.
+// New also records the stack trace at the point it was called.
 func New(message string) error {
 	return pkgerrors.New(message)
 }
 
+// Wrap returns an error annotating err with a stack trace at the point Wrap is
+// called, and the supplied message.  If err is nil, Wrap returns nil.
 func Wrap(err error, message string) error {
 	if _, ok := err.(stackTracer); !ok {
 		return pkgerrors.Wrap(err, message)
@@ -45,10 +50,14 @@ func Wrap(err error, message string) error {
 	}
 }
 
+// Wrapf returns an error annotating err with a stack trace at the point Wrapf
+// is called, and the format specifier.  If err is nil, Wrapf returns nil.
 func Wrapf(err error, format string, args ...interface{}) error {
 	return Wrap(err, fmt.Sprintf(format, args...))
 }
 
+// WithStack annotates err with a stack trace at the point WithStack was called.
+// If err is nil, WithStack returns nil.
 func WithStack(err error) error {
 	if _, ok := err.(stackTracer); !ok {
 		return pkgerrors.WithStack(err)
@@ -57,6 +66,7 @@ func WithStack(err error) error {
 	}
 }
 
+// HasStackTrace tells you if the given error contains a stack trace.
 func HasStackTrace(err error) bool {
 	_, ok := err.(stackTracer)
 	return ok
diff --git a/flash/flash.go b/flash/flash.go
index 6b85814..5903fb2 100644
--- a/flash/flash.go
+++ b/flash/flash.go
@@ -99,6 +99,10 @@ func SortFlashAreasById(areas []FlashArea) []FlashArea {
 }
 
 func areasDistinct(a FlashArea, b FlashArea) bool {
+	if a.Device != b.Device {
+		return true
+	}
+
 	var lo FlashArea
 	var hi FlashArea
 
@@ -110,7 +114,7 @@ func areasDistinct(a FlashArea, b FlashArea) bool {
 		hi = a
 	}
 
-	return lo.Device != hi.Device || lo.Offset+lo.Size <= hi.Offset
+	return lo.Offset+lo.Size <= hi.Offset
 }
 
 // @return overlapping-areas, id-conflicts.
diff --git a/image/image.go b/image/image.go
index 5c489c5..7289f52 100644
--- a/image/image.go
+++ b/image/image.go
@@ -20,16 +20,11 @@
 package image
 
 import (
-	"bytes"
 	"encoding/binary"
-	"encoding/hex"
-	"encoding/json"
 	"fmt"
 	"io"
 	"io/ioutil"
 	"os"
-	"strconv"
-	"strings"
 
 	"github.com/apache/mynewt-artifact/errors"
 	"github.com/apache/mynewt-artifact/sec"
@@ -150,120 +145,11 @@ func ImageTlvTypeIsSig(tlvType uint8) bool {
 		tlvType == IMAGE_TLV_ECDSA256
 }
 
-func ParseVersion(versStr string) (ImageVersion, error) {
-	var err error
-	var major uint64
-	var minor uint64
-	var rev uint64
-	var buildNum uint64
-	var ver ImageVersion
-
-	components := strings.Split(versStr, ".")
-	major, err = strconv.ParseUint(components[0], 10, 8)
-	if err != nil {
-		return ver, errors.Errorf("invalid version string %s", versStr)
-	}
-	if len(components) > 1 {
-		minor, err = strconv.ParseUint(components[1], 10, 8)
-		if err != nil {
-			return ver, errors.Errorf("invalid version string %s", versStr)
-		}
-	}
-	if len(components) > 2 {
-		rev, err = strconv.ParseUint(components[2], 10, 16)
-		if err != nil {
-			return ver, errors.Errorf("invalid version string %s", versStr)
-		}
-	}
-	if len(components) > 3 {
-		buildNum, err = strconv.ParseUint(components[3], 10, 32)
-		if err != nil {
-			return ver, errors.Errorf("invalid version string %s", versStr)
-		}
-	}
-
-	ver.Major = uint8(major)
-	ver.Minor = uint8(minor)
-	ver.Rev = uint16(rev)
-	ver.BuildNum = uint32(buildNum)
-	return ver, nil
-}
-
 func (ver ImageVersion) String() string {
 	return fmt.Sprintf("%d.%d.%d.%d",
 		ver.Major, ver.Minor, ver.Rev, ver.BuildNum)
 }
 
-func (h *ImageHdr) Map(offset int) map[string]interface{} {
-	return map[string]interface{}{
-		"magic":   h.Magic,
-		"hdr_sz":  h.HdrSz,
-		"img_sz":  h.ImgSz,
-		"flags":   h.Flags,
-		"vers":    h.Vers.String(),
-		"_offset": offset,
-	}
-}
-
-func rawBodyMap(offset int) map[string]interface{} {
-	return map[string]interface{}{
-		"_offset": offset,
-	}
-}
-
-func (t *ImageTrailer) Map(offset int) map[string]interface{} {
-	return map[string]interface{}{
-		"magic":       t.Magic,
-		"tlv_tot_len": t.TlvTotLen,
-		"_offset":     offset,
-	}
-}
-
-func (t *ImageTlv) Map(offset int) map[string]interface{} {
-	return map[string]interface{}{
-		"type":     t.Header.Type,
-		"len":      t.Header.Len,
-		"data":     hex.EncodeToString(t.Data),
-		"_typestr": ImageTlvTypeName(t.Header.Type),
-		"_offset":  offset,
-	}
-}
-
-func (img *Image) Map() (map[string]interface{}, error) {
-	offs, err := img.Offsets()
-	if err != nil {
-		return nil, err
-	}
-
-	m := map[string]interface{}{}
-	m["header"] = img.Header.Map(offs.Header)
-	m["body"] = rawBodyMap(offs.Body)
-	trailer := img.Trailer()
-	m["trailer"] = trailer.Map(offs.Trailer)
-
-	tlvMaps := []map[string]interface{}{}
-	for i, tlv := range img.Tlvs {
-		tlvMaps = append(tlvMaps, tlv.Map(offs.Tlvs[i]))
-	}
-	m["tlvs"] = tlvMaps
-
-	return m, nil
-}
-
-func (img *Image) Json() (string, error) {
-	m, err := img.Map()
-	if err != nil {
-		return "", err
-	}
-
-	b, err := json.MarshalIndent(m, "", "    ")
-	if err != nil {
-		return "", errors.Wrapf(err, "failed to marshal image")
-	}
-
-	return string(b), nil
-}
-
 func (tlv *ImageTlv) Write(w io.Writer) (int, error) {
 	totalSize := 0
 
@@ -282,18 +168,34 @@ func (tlv *ImageTlv) Write(w io.Writer) (int, error) {
 	return totalSize, nil
 }
 
-func (i *Image) FindTlvs(tlvType uint8) []ImageTlv {
-	var tlvs []ImageTlv
+// FindTlvIndices searches an image for TLVs of the specified type and
+// returns their indices.
+func (i *Image) FindTlvIndices(tlvType uint8) []int {
+	var idxs []int
 
-	for _, tlv := range i.Tlvs {
+	for i, tlv := range i.Tlvs {
 		if tlv.Header.Type == tlvType {
-			tlvs = append(tlvs, tlv)
+			idxs = append(idxs, i)
 		}
 	}
 
+	return idxs
+}
+
+// FindTlvs retrieves all TLVs in an image's footer with the specified type.
+func (i *Image) FindTlvs(tlvType uint8) []*ImageTlv {
+	var tlvs []*ImageTlv
+
+	idxs := i.FindTlvIndices(tlvType)
+	for _, idx := range idxs {
+		tlvs = append(tlvs, &i.Tlvs[idx])
+	}
+
 	return tlvs
 }
 
+// FindUniqueTlv retrieves a TLV in an image's footer with the specified
+// type.  It returns an error if there is more than one TLV with this type.
 func (i *Image) FindUniqueTlv(tlvType uint8) (*ImageTlv, error) {
 	tlvs := i.FindTlvs(tlvType)
 	if len(tlvs) == 0 {
@@ -304,9 +206,11 @@ func (i *Image) FindUniqueTlv(tlvType uint8) (*ImageTlv, error) {
 			len(tlvs), tlvType)
 	}
 
-	return &tlvs[0], nil
+	return tlvs[0], nil
 }
 
+// RemoveTlvsIf removes all TLVs from an image that satisfy the supplied
+// predicate.  It returns a slice of the removed TLVs.
 func (i *Image) RemoveTlvsIf(pred func(tlv ImageTlv) bool) []ImageTlv {
 	rmed := []ImageTlv{}
 
@@ -323,12 +227,15 @@ func (i *Image) RemoveTlvsIf(pred func(tlv ImageTlv) bool) []ImageTlv {
 	return rmed
 }
 
+// RemoveTlvsWithType removes from an image all TLVs with the specified type.
+// It returns a slice of the removed TLVs.
 func (i *Image) RemoveTlvsWithType(tlvType uint8) []ImageTlv {
 	return i.RemoveTlvsIf(func(tlv ImageTlv) bool {
 		return tlv.Header.Type == tlvType
 	})
 }
 
+// ImageTrailer constructs an image trailer corresponding to the given image.
 func (img *Image) Trailer() ImageTrailer {
 	trailer := ImageTrailer{
 		Magic:     IMAGE_TRAILER_MAGIC,
@@ -341,6 +248,7 @@ func (img *Image) Trailer() ImageTrailer {
 	return trailer
 }
 
+// Hash retrieves the contents of an image's SHA256 TLV.
 func (i *Image) Hash() ([]byte, error) {
 	tlv, err := i.FindUniqueTlv(IMAGE_TLV_SHA256)
 	if err != nil {
@@ -355,10 +263,13 @@ func (i *Image) Hash() ([]byte, error) {
 	return tlv.Data, nil
 }
 
+// CalcHash calculates a SHA256 of the given image.
 func (i *Image) CalcHash() ([]byte, error) {
 	return calcHash(nil, i.Header, i.Pad, i.Body)
 }
 
+// WritePlusOffsets writes a binary image to the given writer.  It returns
+// the offsets of the image components that got written.
 func (i *Image) WritePlusOffsets(w io.Writer) (ImageOffsets, error) {
 	offs := ImageOffsets{}
 	offset := 0
@@ -406,10 +317,13 @@ func (i *Image) WritePlusOffsets(w io.Writer) (ImageOffsets, error) {
 	return offs, nil
 }
 
+// Offsets returns the offsets of each of an image's components if it were
+// serialized.
 func (i *Image) Offsets() (ImageOffsets, error) {
 	return i.WritePlusOffsets(ioutil.Discard)
 }
 
+// TotalSize returns the size of the image if it were serialized, in bytes.
 func (i *Image) TotalSize() (int, error) {
 	offs, err := i.Offsets()
 	if err != nil {
@@ -418,6 +332,7 @@ func (i *Image) TotalSize() (int, error) {
 	return offs.TotalSize, nil
 }
 
+// Write serializes and writes a Mynewt image.
 func (i *Image) Write(w io.Writer) (int, error) {
 	offs, err := i.WritePlusOffsets(w)
 	if err != nil {
@@ -427,6 +342,7 @@ func (i *Image) Write(w io.Writer) (int, error) {
 	return offs.TotalSize, nil
 }
 
+// WriteToFile writes a Mynewt image to a file.
 func (i *Image) WriteToFile(filename string) error {
 	f, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
 	if err != nil {
@@ -440,6 +356,8 @@ func (i *Image) WriteToFile(filename string) error {
 	return nil
 }
 
+// CollectSigs returns a slice of all signatures present in an image's
+// trailer.
 func (img *Image) CollectSigs() ([]sec.Sig, error) {
 	var sigs []sec.Sig
 
@@ -470,151 +388,3 @@ func (img *Image) CollectSigs() ([]sec.Sig, error) {
 
 	return sigs, nil
 }
-
-func parseRawHeader(imgData []byte, offset int) (ImageHdr, int, error) {
-	var hdr ImageHdr
-
-	r := bytes.NewReader(imgData)
-	r.Seek(int64(offset), io.SeekStart)
-
-	if err := binary.Read(r, binary.LittleEndian, &hdr); err != nil {
-		return hdr, 0, errors.Wrapf(err, "error reading image header")
-	}
-
-	if hdr.Magic != IMAGE_MAGIC {
-		return hdr, 0, errors.Errorf(
-			"image magic incorrect; expected 0x%08x, got 0x%08x",
-			uint32(IMAGE_MAGIC), hdr.Magic)
-	}
-
-	remLen := len(imgData) - offset
-	if remLen < int(hdr.HdrSz) {
-		return hdr, 0, errors.Errorf(
-			"image header incomplete; expected %d bytes, got %d bytes",
-			hdr.HdrSz, remLen)
-	}
-
-	return hdr, int(hdr.HdrSz), nil
-}
-
-func parseRawBody(imgData []byte, hdr ImageHdr,
-	offset int) ([]byte, int, error) {
-
-	imgSz := int(hdr.ImgSz)
-	remLen := len(imgData) - offset
-
-	if remLen < imgSz {
-		return nil, 0, errors.Errorf(
-			"image body incomplete; expected %d bytes, got %d bytes",
-			imgSz, remLen)
-	}
-
-	return imgData[offset : offset+imgSz], imgSz, nil
-}
-
-func parseRawTrailer(imgData []byte, offset int) (ImageTrailer, int, error) {
-	var trailer ImageTrailer
-
-	r := bytes.NewReader(imgData)
-	r.Seek(int64(offset), io.SeekStart)
-
-	if err := binary.Read(r, binary.LittleEndian, &trailer); err != nil {
-		return trailer, 0, errors.Wrapf(err,
-			"image contains invalid trailer at offset %d", offset)
-	}
-
-	return trailer, IMAGE_TRAILER_SIZE, nil
-}
-
-func parseRawTlv(imgData []byte, offset int) (ImageTlv, int, error) {
-	tlv := ImageTlv{}
-
-	r := bytes.NewReader(imgData)
-	r.Seek(int64(offset), io.SeekStart)
-
-	if err := binary.Read(r, binary.LittleEndian, &tlv.Header); err != nil {
-		return tlv, 0, errors.Wrapf(err,
-			"image contains invalid TLV at offset %d", offset)
-	}
-
-	tlv.Data = make([]byte, tlv.Header.Len)
-	if _, err := r.Read(tlv.Data); err != nil {
-		return tlv, 0, errors.Wrapf(err,
-			"image contains invalid TLV at offset %d", offset)
-	}
-
-	return tlv, IMAGE_TLV_SIZE + int(tlv.Header.Len), nil
-}
-
-func ParseImage(imgData []byte) (Image, error) {
-	img := Image{}
-	offset := 0
-
-	hdr, size, err := parseRawHeader(imgData, offset)
-	if err != nil {
-		return img, err
-	}
-	offset += size
-
-	body, size, err := parseRawBody(imgData, hdr, offset)
-	if err != nil {
-		return img, err
-	}
-	offset += size
-
-	trailer, size, err := parseRawTrailer(imgData, offset)
-	if err != nil {
-		return img, err
-	}
-	offset += size
-
-	totalLen := IMAGE_HEADER_SIZE + len(body) + int(trailer.TlvTotLen)
-	if len(imgData) < totalLen {
-		return img, errors.Errorf("image data truncated: have=%d want=%d",
-			len(imgData), totalLen)
-	}
-
-	// Trim excess data following image trailer.
-	imgData = imgData[:totalLen]
-
-	var tlvs []ImageTlv
-	tlvLen := IMAGE_TRAILER_SIZE
-	for offset < len(imgData) {
-		tlv, size, err := parseRawTlv(imgData, offset)
-		if err != nil {
-			return img, err
-		}
-
-		tlvs = append(tlvs, tlv)
-
-		offset += size
-		if offset > len(imgData) {
-			return img, errors.Errorf("TLVs extend beyond end of image")
-		}
-
-		tlvLen += size
-	}
-
-	if int(trailer.TlvTotLen) != tlvLen {
-		return img, errors.Errorf(
-			"invalid image: trailer indicates TLV-length=%d; actual=%d",
-			trailer.TlvTotLen, tlvLen)
-	}
-
-	img.Header = hdr
-	img.Body = body
-	img.Tlvs = tlvs
-
-	return img, nil
-}
-
-func ReadImage(filename string) (Image, error) {
-	ri := Image{}
-
-	imgData, err := ioutil.ReadFile(filename)
-	if err != nil {
-		return ri, errors.Wrapf(err, "failed to read image from file")
-	}
-
-	return ParseImage(imgData)
-}
diff --git a/image/image_test.go b/image/image_test.go
index 132e6fb..fb0d8df 100644
--- a/image/image_test.go
+++ b/image/image_test.go
@@ -78,7 +78,7 @@ func testOne(t *testing.T, e entry) {
 		}
 	}
 
-	err = img.VerifyIntegrity()
+	err = img.Verify()
 	if !e.integrity {
 		if err == nil {
 			fatalErr("integrity", "good", "bad", nil)
@@ -93,7 +93,7 @@ func testOne(t *testing.T, e entry) {
 
 	man := readManifest(e.basename)
 
-	err = img.ValidateManifest(man)
+	err = img.VerifyManifest(man)
 	if !e.man {
 		if err == nil {
 			fatalErr("manifest", "good", "bad", nil)
diff --git a/image/map.go b/image/map.go
new file mode 100644
index 0000000..76c6aa0
--- /dev/null
+++ b/image/map.go
@@ -0,0 +1,81 @@
+package image
+
+import (
+	"encoding/hex"
+	"encoding/json"
+
+	"github.com/apache/mynewt-artifact/errors"
+)
+
+func (h *ImageHdr) Map(offset int) map[string]interface{} {
+	return map[string]interface{}{
+		"_offset": offset,
+		"flags":   h.Flags,
+		"hdr_sz":  h.HdrSz,
+		"img_sz":  h.ImgSz,
+		"magic":   h.Magic,
+		"vers":    h.Vers.String(),
+	}
+}
+
+func rawBodyMap(offset int) map[string]interface{} {
+	return map[string]interface{}{
+		"_offset": offset,
+	}
+}
+
+func (t *ImageTrailer) Map(offset int) map[string]interface{} {
+	return map[string]interface{}{
+		"_offset":     offset,
+		"magic":       t.Magic,
+		"tlv_tot_len": t.TlvTotLen,
+	}
+}
+
+func (t *ImageTlv) Map(index int, offset int) map[string]interface{} {
+	return map[string]interface{}{
+		"_index":   index,
+		"_offset":  offset,
+		"_typestr": ImageTlvTypeName(t.Header.Type),
+		"data":     hex.EncodeToString(t.Data),
+		"len":      t.Header.Len,
+		"type":     t.Header.Type,
+	}
+}
+
+// Map produces a JSON-friendly map representation of an image.
+func (img *Image) Map() (map[string]interface{}, error) {
+	offs, err := img.Offsets()
+	if err != nil {
+		return nil, err
+	}
+
+	m := map[string]interface{}{}
+	m["header"] = img.Header.Map(offs.Header)
+	m["body"] = rawBodyMap(offs.Body)
+	trailer := img.Trailer()
+	m["trailer"] = trailer.Map(offs.Trailer)
+
+	tlvMaps := []map[string]interface{}{}
+	for i, tlv := range img.Tlvs {
+		tlvMaps = append(tlvMaps, tlv.Map(i, offs.Tlvs[i]))
+	}
+	m["tlvs"] = tlvMaps
+
+	return m, nil
+}
+
+// Json produces a JSON representation of an image.
+func (img *Image) Json() (string, error) {
+	m, err := img.Map()
+	if err != nil {
+		return "", err
+	}
+
+	b, err := json.MarshalIndent(m, "", "    ")
+	if err != nil {
+		return "", errors.Wrapf(err, "failed to marshal image")
+	}
+
+	return string(b), nil
+}
diff --git a/image/parse.go b/image/parse.go
new file mode 100644
index 0000000..f1019a8
--- /dev/null
+++ b/image/parse.go
@@ -0,0 +1,200 @@
+package image
+
+import (
+	"bytes"
+	"encoding/binary"
+	"io"
+	"io/ioutil"
+	"strconv"
+	"strings"
+
+	"github.com/apache/mynewt-artifact/errors"
+)
+
+// ParseVersion parses an image version string (e.g., "1.2.3.4")
+func ParseVersion(versStr string) (ImageVersion, error) {
+	var err error
+	var major uint64
+	var minor uint64
+	var rev uint64
+	var buildNum uint64
+	var ver ImageVersion
+
+	components := strings.SplitN(versStr, ".", 4)
+	major, err = strconv.ParseUint(components[0], 10, 8)
+	if err != nil {
+		return ver, errors.Errorf("invalid version string %s", versStr)
+	}
+	if len(components) > 1 {
+		minor, err = strconv.ParseUint(components[1], 10, 8)
+		if err != nil {
+			return ver, errors.Errorf("invalid version string %s", versStr)
+		}
+	}
+	if len(components) > 2 {
+		rev, err = strconv.ParseUint(components[2], 10, 16)
+		if err != nil {
+			return ver, errors.Errorf("invalid version string %s", versStr)
+		}
+	}
+	if len(components) > 3 {
+		buildNum, err = strconv.ParseUint(components[3], 10, 32)
+		if err != nil {
+			return ver, errors.Errorf("invalid version string %s", versStr)
+		}
+	}
+
+	ver.Major = uint8(major)
+	ver.Minor = uint8(minor)
+	ver.Rev = uint16(rev)
+	ver.BuildNum = uint32(buildNum)
+	return ver, nil
+}
+
+func parseRawHeader(imgData []byte, offset int) (ImageHdr, int, error) {
+	var hdr ImageHdr
+
+	r := bytes.NewReader(imgData)
+	r.Seek(int64(offset), io.SeekStart)
+
+	if err := binary.Read(r, binary.LittleEndian, &hdr); err != nil {
+		return hdr, 0, errors.Wrapf(err, "error reading image header")
+	}
+
+	if hdr.Magic != IMAGE_MAGIC {
+		return hdr, 0, errors.Errorf(
+			"image magic incorrect; expected 0x%08x, got 0x%08x",
+			uint32(IMAGE_MAGIC), hdr.Magic)
+	}
+
+	remLen := len(imgData) - offset
+	if remLen < int(hdr.HdrSz) {
+		return hdr, 0, errors.Errorf(
+			"image header incomplete; expected %d bytes, got %d bytes",
+			hdr.HdrSz, remLen)
+	}
+
+	return hdr, int(hdr.HdrSz), nil
+}
+
+func parseRawBody(imgData []byte, hdr ImageHdr,
+	offset int) ([]byte, int, error) {
+
+	imgSz := int(hdr.ImgSz)
+	remLen := len(imgData) - offset
+
+	if remLen < imgSz {
+		return nil, 0, errors.Errorf(
+			"image body incomplete; expected %d bytes, got %d bytes",
+			imgSz, remLen)
+	}
+
+	return imgData[offset : offset+imgSz], imgSz, nil
+}
+
+func parseRawTrailer(imgData []byte, offset int) (ImageTrailer, int, error) {
+	var trailer ImageTrailer
+
+	r := bytes.NewReader(imgData)
+	r.Seek(int64(offset), io.SeekStart)
+
+	if err := binary.Read(r, binary.LittleEndian, &trailer); err != nil {
+		return trailer, 0, errors.Wrapf(err,
+			"image contains invalid trailer at offset %d", offset)
+	}
+
+	return trailer, IMAGE_TRAILER_SIZE, nil
+}
+
+func parseRawTlv(imgData []byte, offset int) (ImageTlv, int, error) {
+	tlv := ImageTlv{}
+
+	r := bytes.NewReader(imgData)
+	r.Seek(int64(offset), io.SeekStart)
+
+	if err := binary.Read(r, binary.LittleEndian, &tlv.Header); err != nil {
+		return tlv, 0, errors.Wrapf(err,
+			"image contains invalid TLV at offset %d", offset)
+	}
+
+	tlv.Data = make([]byte, tlv.Header.Len)
+	if _, err := r.Read(tlv.Data); err != nil {
+		return tlv, 0, errors.Wrapf(err,
+			"image contains invalid TLV at offset %d", offset)
+	}
+
+	return tlv, IMAGE_TLV_SIZE + int(tlv.Header.Len), nil
+}
+
+func ParseImage(imgData []byte) (Image, error) {
+	img := Image{}
+	offset := 0
+
+	hdr, size, err := parseRawHeader(imgData, offset)
+	if err != nil {
+		return img, err
+	}
+	offset += size
+
+	body, size, err := parseRawBody(imgData, hdr, offset)
+	if err != nil {
+		return img, err
+	}
+	offset += size
+
+	trailer, size, err := parseRawTrailer(imgData, offset)
+	if err != nil {
+		return img, err
+	}
+	offset += size
+
+	totalLen := IMAGE_HEADER_SIZE + len(body) + int(trailer.TlvTotLen)
+	if len(imgData) < totalLen {
+		return img, errors.Errorf("image data truncated: have=%d want=%d",
+			len(imgData), totalLen)
+	}
+
+	// Trim excess data following image trailer.
+	imgData = imgData[:totalLen]
+
+	var tlvs []ImageTlv
+	tlvLen := IMAGE_TRAILER_SIZE
+	for offset < len(imgData) {
+		tlv, size, err := parseRawTlv(imgData, offset)
+		if err != nil {
+			return img, err
+		}
+
+		tlvs = append(tlvs, tlv)
+
+		offset += size
+		if offset > len(imgData) {
+			return img, errors.Errorf("TLVs extend beyond end of image")
+		}
+
+		tlvLen += size
+	}
+
+	if int(trailer.TlvTotLen) != tlvLen {
+		return img, errors.Errorf(
+			"invalid image: trailer indicates TLV-length=%d; actual=%d",
+			trailer.TlvTotLen, tlvLen)
+	}
+
+	img.Header = hdr
+	img.Body = body
+	img.Tlvs = tlvs
+
+	return img, nil
+}
+
+func ReadImage(filename string) (Image, error) {
+	ri := Image{}
+
+	imgData, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return ri, errors.Wrapf(err, "failed to read image from file")
+	}
+
+	return ParseImage(imgData)
+}
diff --git a/manifest/manifest.go b/manifest/manifest.go
index 3a7298a..7508bc0 100644
--- a/manifest/manifest.go
+++ b/manifest/manifest.go
@@ -82,6 +82,7 @@ type Manifest struct {
 	LoaderPkgSizes []*ManifestSizePkg `json:"loader_pkgsz,omitempty"`
 }
 
+// ReadManifest reads a JSON manifest from a file.
 func ReadManifest(path string) (Manifest, error) {
 	m := Manifest{}
 
@@ -98,6 +99,7 @@ func ReadManifest(path string) (Manifest, error) {
 	return m, nil
 }
 
+// Write serializes a manifest as JSON and writes it to the given writer.
 func (m *Manifest) Write(w io.Writer) (int, error) {
 	buffer, err := json.MarshalIndent(m, "", "  ")
 	if err != nil {
@@ -112,6 +114,8 @@ func (m *Manifest) Write(w io.Writer) (int, error) {
 	return cnt, nil
 }
 
+// FindTargetVar searches a manifest's target definition for a setting with
+// the specified key.  Examples of keys are: "app", "bsp", and "syscfg".
 func (m *Manifest) FindTargetVar(key string) string {
 	for _, tv := range m.TgtVars {
 		parts := strings.SplitN(tv, "=", 2)
diff --git a/manifest/mfg_manifest.go b/manifest/mfg_manifest.go
index 3ebd22a..f742e12 100644
--- a/manifest/mfg_manifest.go
+++ b/manifest/mfg_manifest.go
@@ -75,6 +75,8 @@ type MfgManifest struct {
 	Meta    *MfgManifestMeta    `json:"meta,omitempty"`
 }
 
+// ReadMfgManifest reads a JSON mfg manifest from a byte slice and produces an
+// MfgManifest object.
 func ParseMfgManifest(jsonText []byte) (MfgManifest, error) {
 	m := MfgManifest{
 		// Backwards compatibility: assume 0xff if unspecified.
@@ -88,6 +90,8 @@ func ParseMfgManifest(jsonText []byte) (MfgManifest, error) {
 	return m, nil
 }
 
+// ReadMfgManifest reads a JSON mfg manifest from a file and produces an
+// MfgManifest object.
 func ReadMfgManifest(path string) (MfgManifest, error) {
 	content, err := ioutil.ReadFile(path)
 	if err != nil {
@@ -103,6 +107,12 @@ func ReadMfgManifest(path string) (MfgManifest, error) {
 	return m, nil
 }
 
+// IsBoot indicates whether an mfg manifest target is a boot loader.
+func (mt *MfgManifestTarget) IsBoot() bool {
+	return mt.BinPath != ""
+}
+
+// MarshalJson produces a JSON representation of an mfg manifest.
 func (m *MfgManifest) MarshalJson() ([]byte, error) {
 	buffer, err := json.MarshalIndent(m, "", "  ")
 	if err != nil {
@@ -112,7 +122,9 @@ func (m *MfgManifest) MarshalJson() ([]byte, error) {
 	return buffer, nil
 }
 
-func (m *MfgManifest) FindFlashArea(device int, offset int) *flash.FlashArea {
+// FindFlashAreaDevOff searches an mfg manifest for a flash area with the
+// specified device and offset.
+func (m *MfgManifest) FindFlashAreaDevOff(device int, offset int) *flash.FlashArea {
 	for i, _ := range m.FlashAreas {
 		fa := &m.FlashAreas[i]
 		if fa.Device == device && fa.Offset == offset {
@@ -123,6 +135,8 @@ func (m *MfgManifest) FindFlashArea(device int, offset int) *flash.FlashArea {
 	return nil
 }
 
+// FindFlashAreaName searches an mfg manifest for a flash area with the
+// specified name.
 func (m *MfgManifest) FindFlashAreaName(name string) *flash.FlashArea {
 	for i, _ := range m.FlashAreas {
 		fa := &m.FlashAreas[i]
@@ -134,6 +148,7 @@ func (m *MfgManifest) FindFlashAreaName(name string) *flash.FlashArea {
 	return nil
 }
 
+// SecSig converts the provided mfg manifest signature into a sec.Sig object.
 func (ms *MfgManifestSig) SecSig() (sec.Sig, error) {
 	keyHash, err := hex.DecodeString(ms.Key)
 	if err != nil {
@@ -153,6 +168,8 @@ func (ms *MfgManifestSig) SecSig() (sec.Sig, error) {
 	}, nil
 }
 
+// SecSigs converts all the signutures in the provided mfg manifest into
+// sec.Sig objects.
 func (m *MfgManifest) SecSigs() ([]sec.Sig, error) {
 	var sigs []sec.Sig
 	for _, ms := range m.Signatures {
@@ -166,7 +183,3 @@ func (m *MfgManifest) SecSigs() ([]sec.Sig, error) {
 
 	return sigs, nil
 }
-
-func (mt *MfgManifestTarget) IsBoot() bool {
-	return mt.BinPath != ""
-}
diff --git a/mfg/map_meta.go b/mfg/map_meta.go
index 19b1ee8..99714ff 100644
--- a/mfg/map_meta.go
+++ b/mfg/map_meta.go
@@ -86,6 +86,7 @@ func (b *MetaTlvBodyMmrRef) Map() map[string]interface{} {
 	}
 }
 
+// Map produces a JSON-friendly map representation of an MMR TLV.
 func (t *MetaTlv) Map(index int, offset int) map[string]interface{} {
 	hmap := map[string]interface{}{
 		"_type_name": MetaTlvTypeName(t.Header.Type),
@@ -110,6 +111,7 @@ func (t *MetaTlv) Map(index int, offset int) map[string]interface{} {
 	}
 }
 
+// Map produces a JSON-friendly map representation of an MMR footer.
 func (f *MetaFooter) Map(offset int) map[string]interface{} {
 	return map[string]interface{}{
 		"_offset": offset,
@@ -119,6 +121,7 @@ func (f *MetaFooter) Map(offset int) map[string]interface{} {
 	}
 }
 
+// Map produces a JSON-friendly map representation of an MMR.
 func (m *Meta) Map(endOffset int) map[string]interface{} {
 	offsets := m.Offsets()
 	startOffset := endOffset - int(m.Footer.Size)
@@ -140,6 +143,7 @@ func (m *Meta) Map(endOffset int) map[string]interface{} {
 	}
 }
 
+// Json produces a JSON representation of an MMR.
 func (m *Meta) Json(offset int) (string, error) {
 	mmap := m.Map(offset)
 
diff --git a/mfg/meta.go b/mfg/meta.go
index b4786bc..1e7029c 100644
--- a/mfg/meta.go
+++ b/mfg/meta.go
@@ -160,6 +160,8 @@ func (tlv *MetaTlv) Write(w io.Writer) (int, error) {
 	return sz, nil
 }
 
+// StructuredBody constructs the appropriate "body" object from a raw TLV
+// (e.g., MetaTlvBodyHash from a TLV with type=META_TLV_TYPE_HASH).
 func (tlv *MetaTlv) StructuredBody() (interface{}, error) {
 	r := bytes.NewReader(tlv.Data)
 
@@ -197,6 +199,8 @@ func (tlv *MetaTlv) StructuredBody() (interface{}, error) {
 	}
 }
 
+// WritePlusOffsets writes a binary MMR to the given writer.  It returns the
+// offsets of the mfgimage components that got written.
 func (meta *Meta) WritePlusOffsets(w io.Writer) (MetaOffsets, error) {
 	mo := MetaOffsets{}
 	sz := 0
@@ -221,11 +225,14 @@ func (meta *Meta) WritePlusOffsets(w io.Writer) (MetaOffsets, error) {
 	return mo, nil
 }
 
+// Offsets returns the offsets of each of an MMR's components if it were
+// serialized.
 func (meta *Meta) Offsets() MetaOffsets {
 	mo, _ := meta.WritePlusOffsets(ioutil.Discard)
 	return mo
 }
 
+// Write serializes and writes an MMR.
 func (meta *Meta) Write(w io.Writer) (int, error) {
 	mo, err := meta.WritePlusOffsets(w)
 	if err != nil {
@@ -235,10 +242,12 @@ func (meta *Meta) Write(w io.Writer) (int, error) {
 	return mo.TotalSize, nil
 }
 
+// Size calculates the total size of an MMR if it were serialied.
 func (meta *Meta) Size() int {
 	return meta.Offsets().TotalSize
 }
 
+// Bytes serializes an MMR to binary form.
 func (meta *Meta) Bytes() ([]byte, error) {
 	b := &bytes.Buffer{}
 
@@ -250,6 +259,8 @@ func (meta *Meta) Bytes() ([]byte, error) {
 	return b.Bytes(), nil
 }
 
+// FindTlvIndices searches an MMR for TLVs of the specified type and returns
+// their indices.
 func (meta *Meta) FindTlvIndices(typ uint8) []int {
 	indices := []int{}
 
@@ -262,6 +273,7 @@ func (meta *Meta) FindTlvIndices(typ uint8) []int {
 	return indices
 }
 
+// FindTlvIndices searches an MMR for all TLVs of the specified type.
 func (meta *Meta) FindTlvs(typ uint8) []*MetaTlv {
 	indices := meta.FindTlvIndices(typ)
 
@@ -273,15 +285,18 @@ func (meta *Meta) FindTlvs(typ uint8) []*MetaTlv {
 	return tlvs
 }
 
+// FindTlvIndices searches an MMR for the first TLV of the specified type.
 func (meta *Meta) FindFirstTlv(typ uint8) *MetaTlv {
-	indices := meta.FindTlvIndices(typ)
-	if len(indices) == 0 {
+	tlvs := meta.FindTlvs(typ)
+	if len(tlvs) == 0 {
 		return nil
 	}
 
-	return &meta.Tlvs[indices[0]]
+	return tlvs[0]
 }
 
+// HashOffset calculates the offset of the SHA256 TLV in an MMR if it were
+// serialized.
 func (meta *Meta) HashOffset() int {
 	mo := meta.Offsets()
 	indices := meta.FindTlvIndices(META_TLV_TYPE_HASH)
@@ -292,6 +307,7 @@ func (meta *Meta) HashOffset() int {
 	return META_TLV_HEADER_SZ + mo.Tlvs[indices[0]]
 }
 
+// ClearHash zeroes out an MMRs SHA256 TLV.
 func (meta *Meta) ClearHash() {
 	tlv := meta.FindFirstTlv(META_TLV_TYPE_HASH)
 	if tlv != nil {
@@ -299,6 +315,8 @@ func (meta *Meta) ClearHash() {
 	}
 }
 
+// Hash locates an MMR's SHA256 TLV and returns its value.  It returns nil if
+// the MMR doesn't have a SHA256 TLV.
 func (meta *Meta) Hash() []byte {
 	tlv := meta.FindFirstTlv(META_TLV_TYPE_HASH)
 	if tlv == nil {
@@ -307,15 +325,15 @@ func (meta *Meta) Hash() []byte {
 	return tlv.Data
 }
 
+// Clone performs a deep copy of an MMR.
 func (meta *Meta) Clone() Meta {
-	var tlvs []MetaTlv
-	for _, src := range meta.Tlvs {
-		dst := MetaTlv{
+	tlvs := make([]MetaTlv, len(meta.Tlvs))
+	for i, src := range meta.Tlvs {
+		tlvs[i] = MetaTlv{
 			Header: src.Header,
 			Data:   make([]byte, len(src.Data)),
 		}
-		copy(dst.Data, src.Data)
-		tlvs = append(tlvs, dst)
+		copy(tlvs[i].Data, src.Data)
 	}
 
 	return Meta{
@@ -323,83 +341,3 @@ func (meta *Meta) Clone() Meta {
 		Footer: meta.Footer,
 	}
 }
-
-func parseMetaTlv(bin []byte) (MetaTlv, int, error) {
-	r := bytes.NewReader(bin)
-
-	tlv := MetaTlv{}
-	if err := binary.Read(r, binary.LittleEndian, &tlv.Header); err != nil {
-		return tlv, 0, errors.Wrapf(err, "error reading TLV header")
-	}
-
-	data := make([]byte, tlv.Header.Size)
-	sz, err := r.Read(data)
-	if err != nil {
-		return tlv, 0, errors.Wrapf(err,
-			"error reading %d bytes of TLV data",
-			tlv.Header.Size)
-	}
-	if sz != len(data) {
-		return tlv, 0, errors.Errorf(
-			"error reading %d bytes of TLV data: incomplete read",
-			tlv.Header.Size)
-	}
-	tlv.Data = data
-
-	return tlv, META_TLV_HEADER_SZ + int(tlv.Header.Size), nil
-}
-
-func parseMetaFooter(bin []byte) (MetaFooter, int, error) {
-	r := bytes.NewReader(bin)
-
-	var ftr MetaFooter
-	if err := binary.Read(r, binary.LittleEndian, &ftr); err != nil {
-		return ftr, 0, errors.Wrapf(err,
-			"error reading meta footer")
-	}
-
-	if ftr.Magic != META_MAGIC {
-		return ftr, 0, errors.Errorf(
-			"meta footer contains invalid magic; exp:0x%08x, got:0x%08x",
-			META_MAGIC, ftr.Magic)
-	}
-
-	return ftr, META_FOOTER_SZ, nil
-}
-
-func ParseMeta(bin []byte) (Meta, int, error) {
-	if len(bin) < META_FOOTER_SZ {
-		return Meta{}, 0, errors.Errorf(
-			"binary too small to accommodate meta footer; "+
-				"bin-size=%d ftr-size=%d", len(bin), META_FOOTER_SZ)
-	}
-
-	ftr, _, err := parseMetaFooter(bin[len(bin)-META_FOOTER_SZ:])
-	if err != nil {
-		return Meta{}, 0, err
-	}
-
-	if int(ftr.Size) > len(bin) {
-		return Meta{}, 0, errors.Errorf(
-			"binary too small to accommodate meta region; "+
-				"bin-size=%d meta-size=%d", len(bin), ftr.Size)
-	}
-
-	ftrOff := len(bin) - META_FOOTER_SZ
-	off := len(bin) - int(ftr.Size)
-
-	tlvs := []MetaTlv{}
-	for off < ftrOff {
-		tlv, sz, err := parseMetaTlv(bin[off:])
-		if err != nil {
-			return Meta{}, 0, err
-		}
-		tlvs = append(tlvs, tlv)
-		off += sz
-	}
-
-	return Meta{
-		Tlvs:   tlvs,
-		Footer: ftr,
-	}, off, nil
-}
diff --git a/mfg/mfg.go b/mfg/mfg.go
index eea6abf..2667003 100644
--- a/mfg/mfg.go
+++ b/mfg/mfg.go
@@ -20,7 +20,6 @@
 package mfg
 
 import (
-	"bytes"
 	"crypto/sha256"
 
 	"github.com/apache/mynewt-artifact/errors"
@@ -39,33 +38,9 @@ type Mfg struct {
 	MetaOff int
 }
 
-func Parse(data []byte, metaEndOff int, eraseVal byte) (Mfg, error) {
-	m := Mfg{
-		Bin: data,
-	}
-
-	if metaEndOff >= 0 {
-		if metaEndOff > len(data) {
-			return m, errors.Errorf(
-				"MMR offset (%d) beyond end of mfgimage (%d)",
-				metaEndOff, len(data))
-		}
-
-		meta, _, err := ParseMeta(data[:metaEndOff])
-		if err != nil {
-			return m, err
-		}
-		m.Meta = &meta
-		m.MetaOff = metaEndOff - int(meta.Footer.Size)
-
-		for i := 0; i < int(meta.Footer.Size); i++ {
-			m.Bin[m.MetaOff+i] = eraseVal
-		}
-	}
-
-	return m, nil
-}
-
+// StripPadding produces a new byte slice by removing padding from an
+// existing byte slice.  Padding is defined as a sequence of trailing bytes,
+// all with the specified value.
 func StripPadding(b []byte, eraseVal byte) []byte {
 	var pad int
 	for pad = 0; pad < len(b); pad++ {
@@ -78,6 +53,8 @@ func StripPadding(b []byte, eraseVal byte) []byte {
 	return b[:len(b)-pad]
 }
 
+// AddPadding produces a new byte slice by adding extra bytes to the end of
+// an existing slice.
 func AddPadding(b []byte, eraseVal byte, padLen int) []byte {
 	for i := 0; i < padLen; i++ {
 		b = append(b, eraseVal)
@@ -114,8 +91,10 @@ func (m *Mfg) RecalcHash(eraseVal byte) ([]byte, error) {
 	return CalcHash(bin), nil
 }
 
+// RefillHash replace's the contents of an mfgimage's SHA256 TLV with a newly
+// calculated hash.
 func (m *Mfg) RefillHash(eraseVal byte) error {
-	if m.Meta == nil || m.Meta.Hash() == nil {
+	if m.Meta == nil {
 		return nil
 	}
 	tlv := m.Meta.FindFirstTlv(META_TLV_TYPE_HASH)
@@ -135,6 +114,9 @@ func (m *Mfg) RefillHash(eraseVal byte) error {
 	return nil
 }
 
+// Hash retrieves the SHA256 value associated with an mfgimage.  If the
+// mfgimage has an MMR with a SHA256 TLV, the TLV's value is returned.
+// Otherwise, the hash is calculated and returned.
 func (m *Mfg) Hash(eraseVal byte) ([]byte, error) {
 	var hashBytes []byte
 
@@ -154,21 +136,7 @@ func (m *Mfg) Hash(eraseVal byte) ([]byte, error) {
 	return hashBytes, nil
 }
 
-func (m *Mfg) HashIsValid(eraseVal byte) (bool, error) {
-	// If the mfg doesn't contain a hash TLV, then there is nothing to verify.
-	tlv := m.Meta.FindFirstTlv(META_TLV_TYPE_HASH)
-	if tlv == nil {
-		return true, nil
-	}
-
-	hash, err := m.RecalcHash(eraseVal)
-	if err != nil {
-		return false, err
-	}
-
-	return bytes.Equal(hash, tlv.Data), nil
-}
-
+// Bytes serializes an mfgimage into binary form.
 func (m *Mfg) Bytes(eraseVal byte) ([]byte, error) {
 	binCopy := make([]byte, len(m.Bin))
 	copy(binCopy, m.Bin)
@@ -188,6 +156,7 @@ func (m *Mfg) Bytes(eraseVal byte) ([]byte, error) {
 	return binCopy, nil
 }
 
+// Clone performs a deep copy of an mfgimage.
 func (m *Mfg) Clone() Mfg {
 	var meta *Meta
 	if m.Meta != nil {
@@ -205,7 +174,11 @@ func (m *Mfg) Clone() Mfg {
 	}
 }
 
-func (m *Mfg) ExtractFlashArea(area flash.FlashArea, eraseVal byte) ([]byte, error) {
+// ExtractFlashArea copies a portion out of a serialized mfgimage.  The
+// offset and size to copy indicated by the provided FlashArea.
+func (m *Mfg) ExtractFlashArea(area flash.FlashArea,
+	eraseVal byte) ([]byte, error) {
+
 	b, err := m.Bytes(eraseVal)
 	if err != nil {
 		return nil, err
@@ -228,6 +201,8 @@ func (m *Mfg) ExtractFlashArea(area flash.FlashArea, eraseVal byte) ([]byte, err
 	return b[area.Offset:end], nil
 }
 
+// Tlvs retrieves the slice of TLVs present in an mfgimage's MMR.  It returns
+// nil if the mfgimage has no MMR.
 func (m *Mfg) Tlvs() []MetaTlv {
 	if m.Meta == nil {
 		return nil
diff --git a/mfg/mfg_test.go b/mfg/mfg_test.go
index 5e7d8ff..a2e503e 100644
--- a/mfg/mfg_test.go
+++ b/mfg/mfg_test.go
@@ -86,7 +86,7 @@ func testOne(t *testing.T, e entry) {
 		}
 	}
 
-	err = m.VerifyIntegrity(man.EraseVal)
+	err = m.Verify(man.EraseVal)
 	if !e.integrity {
 		if err == nil {
 			fatalErr("integrity", "good", "bad", nil)
@@ -99,7 +99,7 @@ func testOne(t *testing.T, e entry) {
 		}
 	}
 
-	err = m.ValidateManifest(man)
+	err = m.VerifyManifest(man)
 	if !e.man {
 		if err == nil {
 			fatalErr("manifest", "good", "bad", nil)
diff --git a/mfg/parse.go b/mfg/parse.go
new file mode 100644
index 0000000..1d4e4b7
--- /dev/null
+++ b/mfg/parse.go
@@ -0,0 +1,118 @@
+package mfg
+
+import (
+	"bytes"
+	"encoding/binary"
+
+	"github.com/apache/mynewt-artifact/errors"
+)
+
+func parseMetaFooter(bin []byte) (MetaFooter, int, error) {
+	r := bytes.NewReader(bin)
+
+	var ftr MetaFooter
+	if err := binary.Read(r, binary.LittleEndian, &ftr); err != nil {
+		return ftr, 0, errors.Wrapf(err,
+			"error reading meta footer")
+	}
+
+	if ftr.Magic != META_MAGIC {
+		return ftr, 0, errors.Errorf(
+			"meta footer contains invalid magic; exp:0x%08x, got:0x%08x",
+			META_MAGIC, ftr.Magic)
+	}
+
+	return ftr, META_FOOTER_SZ, nil
+}
+
+func parseMetaTlv(bin []byte) (MetaTlv, int, error) {
+	r := bytes.NewReader(bin)
+
+	tlv := MetaTlv{}
+	if err := binary.Read(r, binary.LittleEndian, &tlv.Header); err != nil {
+		return tlv, 0, errors.Wrapf(err, "error reading TLV header")
+	}
+
+	data := make([]byte, tlv.Header.Size)
+	sz, err := r.Read(data)
+	if err != nil {
+		return tlv, 0, errors.Wrapf(err,
+			"error reading %d bytes of TLV data",
+			tlv.Header.Size)
+	}
+	if sz != len(data) {
+		return tlv, 0, errors.Errorf(
+			"error reading %d bytes of TLV data: incomplete read",
+			tlv.Header.Size)
+	}
+	tlv.Data = data
+
+	return tlv, META_TLV_HEADER_SZ + int(tlv.Header.Size), nil
+}
+
+func parseMeta(bin []byte) (Meta, error) {
+	if len(bin) < META_FOOTER_SZ {
+		return Meta{}, errors.Errorf(
+			"binary too small to accommodate meta footer; "+
+				"bin-size=%d ftr-size=%d", len(bin), META_FOOTER_SZ)
+	}
+
+	ftr, _, err := parseMetaFooter(bin[len(bin)-META_FOOTER_SZ:])
+	if err != nil {
+		return Meta{}, err
+	}
+
+	if int(ftr.Size) > len(bin) {
+		return Meta{}, errors.Errorf(
+			"binary too small to accommodate meta region; "+
+				"bin-size=%d meta-size=%d", len(bin), ftr.Size)
+	}
+
+	ftrOff := len(bin) - META_FOOTER_SZ
+	off := len(bin) - int(ftr.Size)
+
+	tlvs := []MetaTlv{}
+	for off < ftrOff {
+		tlv, sz, err := parseMetaTlv(bin[off:])
+		if err != nil {
+			return Meta{}, err
+		}
+		tlvs = append(tlvs, tlv)
+		off += sz
+	}
+
+	return Meta{
+		Tlvs:   tlvs,
+		Footer: ftr,
+	}, nil
+}
+
+// Parse parses a serialized mfgimage (e.g., "mfgimg.bin") and produces an
+// Mfg object.  metaEndOff is the offset immediately following the MMR, or -1
+// if there is no MMR.
+func Parse(data []byte, metaEndOff int, eraseVal byte) (Mfg, error) {
+	m := Mfg{
+		Bin: data,
+	}
+
+	if metaEndOff >= 0 {
+		if metaEndOff > len(data) {
+			return m, errors.Errorf(
+				"MMR offset (%d) beyond end of mfgimage (%d)",
+				metaEndOff, len(data))
+		}
+
+		meta, err := parseMeta(data[:metaEndOff])
+		if err != nil {
+			return m, err
+		}
+		m.Meta = &meta
+		m.MetaOff = metaEndOff - int(meta.Footer.Size)
+
+		for i := 0; i < int(meta.Footer.Size); i++ {
+			m.Bin[m.MetaOff+i] = eraseVal
+		}
+	}
+
+	return m, nil
+}
diff --git a/mfg/verify.go b/mfg/verify.go
index 17a5a76..ffa2817 100644
--- a/mfg/verify.go
+++ b/mfg/verify.go
@@ -105,7 +105,7 @@ func (m *Mfg) validateManMmrs(man manifest.MfgManifest) error {
 
 func (m *Mfg) validateManTargets(man manifest.MfgManifest) error {
 	for _, t := range man.Targets {
-		fa := man.FindFlashArea(man.Device, t.Offset)
+		fa := man.FindFlashAreaDevOff(man.Device, t.Offset)
 		if fa == nil {
 			return errors.Errorf(
 				"no flash area in mfgimage corresponding to target \"%s\"",
diff --git a/misc/misc.go b/misc/misc.go
deleted file mode 100644
index 2f685e1..0000000
--- a/misc/misc.go
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * 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 misc
-
-import (
-	"fmt"
-)
-
-func HashString(hash []byte) string {
-	return fmt.Sprintf("%x", hash)
-}
diff --git a/sec/key.go b/sec/key.go
index 473ef62..dabbd1f 100644
--- a/sec/key.go
+++ b/sec/key.go
@@ -168,7 +168,7 @@ func (key *SignKey) PubKey() PubSignKey {
 
 func (key *SignKey) PubBytes() ([]byte, error) {
 	pk := key.PubKey()
-	return pk.PubBytes()
+	return pk.Bytes()
 }
 
 func RawKeyHash(pubKeyBytes []byte) []byte {
@@ -256,18 +256,15 @@ func DecryptSecretRsa(privk *rsa.PrivateKey,
 func ParseKeBase64(keyBytes []byte) (cipher.Block, error) {
 	kek, err := base64.StdEncoding.DecodeString(string(keyBytes))
 	if err != nil {
-		return nil, errors.Wrapf(err,
-			"Error decoding kek: %s")
+		return nil, errors.Wrapf(err, "error decoding kek")
 	}
 	if len(kek) != 16 {
-		return nil, errors.Errorf(
-			"Unexpected key size: %d != 16", len(kek))
+		return nil, errors.Errorf("unexpected key size: %d != 16", len(kek))
 	}
 
 	cipher, err := aes.NewCipher(kek)
 	if err != nil {
-		return nil, errors.Wrapf(err,
-			"Error creating keywrap cipher")
+		return nil, errors.Wrapf(err, "error creating keywrap cipher")
 	}
 
 	return cipher, nil
@@ -276,7 +273,7 @@ func ParseKeBase64(keyBytes []byte) (cipher.Block, error) {
 func EncryptSecretAes(c cipher.Block, plainSecret []byte) ([]byte, error) {
 	cipherSecret, err := keywrap.Wrap(c, plainSecret)
 	if err != nil {
-		return nil, errors.Wrapf(err, "Error key-wrapping")
+		return nil, errors.Wrapf(err, "error key-wrapping")
 	}
 
 	return cipherSecret, nil
@@ -288,14 +285,14 @@ func (key *PubSignKey) AssertValid() {
 	}
 }
 
-func (key *PubSignKey) PubBytes() ([]byte, error) {
+func (key *PubSignKey) Bytes() ([]byte, error) {
 	key.AssertValid()
 
-	var pubBytes []byte
+	var b []byte
 
 	if key.Rsa != nil {
 		var err error
-		pubBytes, err = asn1.Marshal(*key.Rsa)
+		b, err = asn1.Marshal(*key.Rsa)
 		if err != nil {
 			return nil, err
 		}
@@ -304,11 +301,11 @@ func (key *PubSignKey) PubBytes() ([]byte, error) {
 		case "P-224":
 			fallthrough
 		case "P-256":
-			pubBytes, _ = x509.MarshalPKIXPublicKey(*key.Ec)
+			b, _ = x509.MarshalPKIXPublicKey(*key.Ec)
 		default:
 			return nil, errors.Errorf("unsupported ECC curve")
 		}
 	}
 
-	return pubBytes, nil
+	return b, nil
 }
diff --git a/sec/sig.go b/sec/sig.go
index 8aa0b32..d6b8ffe 100644
--- a/sec/sig.go
+++ b/sec/sig.go
@@ -14,7 +14,7 @@ type Sig struct {
 }
 
 func checkOneKeyOneSig(k PubSignKey, sig Sig, hash []byte) (bool, error) {
-	pubBytes, err := k.PubBytes()
+	pubBytes, err := k.Bytes()
 	if err != nil {
 		return false, errors.WithStack(err)
 	}


[mynewt-artifact] 14/23: Add "errors" 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-artifact.git

commit 697ec59beded3decd59cd08f3f3a46b7573f418c
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Thu Jun 20 14:05:35 2019 -0700

    Add "errors" package
    
    This is a thin wrapper over the `pkg/errors` package.  It decorates the
    base package with the following functionality:
    
    1. Wrap and Wrapf produce an error with exactly one stack trace.  If
    the wrapped error already contains a stack trace, these functions just
    append text to the message.
    
    2. WithStack produces an error with exactly one stack trace.  If the
    wrapped error already contains a stack trace, it is returned unmodified.
    
    All of artifact's public APIs return errors produced with this package.
    This allows us to remove the dependency on newt (`NewtError`) but still
    retain error stack traces.
---
 errors/errors.go | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/errors/errors.go b/errors/errors.go
new file mode 100644
index 0000000..8154fc5
--- /dev/null
+++ b/errors/errors.go
@@ -0,0 +1,63 @@
+// This is a thin wrapper over the `pkg/errors` package.  It decorates the
+// base package with the following functionality:
+//
+// 1. Wrap and Wrapf produce an error with exactly one stack trace.  If the
+// wrapped error already contains a stack trace, these functions just append
+// text to the message.
+//
+// 2. WithStack produces an error with exactly one stack trace.  If the
+// wrapped error already contains a stack trace, this function returns it
+// unmodified.
+
+package errors
+
+import (
+	"fmt"
+
+	pkgerrors "github.com/pkg/errors"
+)
+
+type stackTracer interface {
+	StackTrace() pkgerrors.StackTrace
+}
+
+// Cause retrieves the underlying cause of an error
+func Cause(err error) error {
+	return pkgerrors.Cause(err)
+}
+
+// Errorf formats an error
+func Errorf(format string, args ...interface{}) error {
+	return pkgerrors.Errorf(format, args...)
+}
+
+// New creates a new error
+func New(message string) error {
+	return pkgerrors.New(message)
+}
+
+func Wrap(err error, message string) error {
+	if _, ok := err.(stackTracer); !ok {
+		return pkgerrors.Wrap(err, message)
+	} else {
+		msg := err.Error() + ": " + message
+		return pkgerrors.WithMessage(err, msg)
+	}
+}
+
+func Wrapf(err error, format string, args ...interface{}) error {
+	return Wrap(err, fmt.Sprintf(format, args...))
+}
+
+func WithStack(err error) error {
+	if _, ok := err.(stackTracer); !ok {
+		return pkgerrors.WithStack(err)
+	} else {
+		return err
+	}
+}
+
+func HasStackTrace(err error) bool {
+	_, ok := err.(stackTracer)
+	return ok
+}


[mynewt-artifact] 23/23: Merge pull request #1 from ccollins476ad/verify

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

commit 441a611715c43c6995b119d674f72869af829601
Merge: b7033fa ced5960
Author: ccollins476ad <cc...@apache.org>
AuthorDate: Fri Jun 21 14:25:20 2019 -0700

    Merge pull request #1 from ccollins476ad/verify
    
    Add "verify" functionality

 .gitignore                                   |   23 +
 LICENSE                                      |  311 ++++
 NOTICE                                       |    8 +
 README.md                                    |   28 +
 errors/errors.go                             |   92 ++
 flash/flash.go                               |    6 +-
 go.mod                                       |   10 +
 go.sum                                       |   19 +
 image/create.go                              |   35 +-
 image/image.go                               |  347 +---
 image/image_test.go                          |  218 +++
 image/keys_test.go                           |   61 +-
 image/map.go                                 |  100 ++
 image/parse.go                               |  219 +++
 image/testdata/bad-hash.img                  |  Bin 0 -> 9412 bytes
 image/testdata/bad-signature.img             |  Bin 0 -> 9680 bytes
 image/testdata/bad-signature.json            | 2267 ++++++++++++++++++++++++++
 image/testdata/garbage.img                   |    1 +
 image/testdata/good-signed.img               |  Bin 0 -> 9680 bytes
 image/testdata/good-signed.json              | 2267 ++++++++++++++++++++++++++
 image/testdata/good-unsigned.img             |  Bin 0 -> 9412 bytes
 image/testdata/good-unsigned.json            | 2267 ++++++++++++++++++++++++++
 image/testdata/mismatch-hash.img             |  Bin 0 -> 9412 bytes
 image/testdata/mismatch-hash.json            | 2267 ++++++++++++++++++++++++++
 image/testdata/mismatch-version.img          |  Bin 0 -> 9412 bytes
 image/testdata/mismatch-version.json         | 2267 ++++++++++++++++++++++++++
 image/testdata/sign-key-pub.der              |  Bin 0 -> 270 bytes
 image/testdata/sign-key.pem                  |   27 +
 image/testdata/truncated.img                 |  Bin 0 -> 9000 bytes
 image/v1.go                                  |   57 +-
 image/verify.go                              |  106 ++
 manifest/manifest.go                         |   29 +-
 manifest/mfg_manifest.go                     |  108 +-
 mfg/map_meta.go                              |   19 +-
 mfg/meta.go                                  |  149 +-
 mfg/mfg.go                                   |  139 +-
 mfg/mfg_test.go                              |  248 +++
 mfg/parse.go                                 |  137 ++
 mfg/testdata/garbage.bin                     |    1 +
 mfg/testdata/garbage.json                    |   78 +
 mfg/testdata/hash1-fm1-ext0-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-ext0-tgts1-sign0.json |   78 +
 mfg/testdata/hash1-fm1-ext1-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-ext1-tgts1-sign0.json |   85 +
 mfg/testdata/hash1-fm1-ext1-tgts1-sign1.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-ext1-tgts1-sign1.json |   91 ++
 mfg/testdata/hash1-fm1-ext1-tgtsm-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-ext1-tgtsm-sign0.json |   92 ++
 mfg/testdata/hash1-fm1-extm-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fm1-extm-tgts1-sign0.json |   85 +
 mfg/testdata/hash1-fmm-ext1-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hash1-fmm-ext1-tgts1-sign0.json |   85 +
 mfg/testdata/hashm-fm1-ext0-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hashm-fm1-ext0-tgts1-sign0.json |   78 +
 mfg/testdata/hashx-fm1-ext0-tgts1-sign0.bin  |  Bin 0 -> 42448 bytes
 mfg/testdata/hashx-fm1-ext0-tgts1-sign0.json |   78 +
 mfg/testdata/sign-key-pub.der                |  Bin 0 -> 270 bytes
 mfg/testdata/sign-key.pem                    |   27 +
 mfg/testdata/unknown-tlv.bin                 |  Bin 0 -> 42448 bytes
 mfg/testdata/unknown-tlv.json                |   78 +
 mfg/verify.go                                |  221 +++
 misc/misc.go                                 |   28 -
 sec/encrypt.go                               |   10 +-
 sec/key.go                                   |  111 +-
 sec/sig.go                                   |   74 +
 65 files changed, 14526 insertions(+), 606 deletions(-)


[mynewt-artifact] 17/23: Turn `artifact` into a Go module

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

commit fea4ea7907034777a6cc9ccb0e2f0cfaccd69572
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Thu Jun 20 13:59:58 2019 -0700

    Turn `artifact` into a Go module
---
 go.mod | 10 ++++++++++
 go.sum | 19 +++++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..57b20e8
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,10 @@
+module github.com/apache/mynewt-artifact
+
+go 1.12
+
+require (
+	github.com/NickBall/go-aes-key-wrap v0.0.0-20170929221519-1c3aa3e4dfc5
+	github.com/pkg/errors v0.8.1
+	github.com/stretchr/testify v1.3.0 // indirect
+	golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..ec754ba
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,19 @@
+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/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+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-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-artifact] 22/23: Add README.md

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

commit ced5960b82111b3a06060a678079b30cf5b3ae85
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Fri Jun 21 14:03:19 2019 -0700

    Add README.md
---
 README.md | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4e64803
--- /dev/null
+++ b/README.md
@@ -0,0 +1,28 @@
+<!--
+#
+# 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.
+#
+-->
+
+# Mynewt Artifact
+
+Apache Mynewt Artifact is a Go library that parses, manipulates, validates, and outputs Mynewt artifacts.  Supported artifacts are:
+* Images
+* Image manifests
+* Manufacturing images (mfgimages)
+* Manufacturing manifests


[mynewt-artifact] 09/23: Enabling mfg bin to hex image convertion.

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

commit 846046a16d0c5c8d56e56179b90a2ed9fdf54c0b
Author: Miguel Azevedo <mi...@gmail.com>
AuthorDate: Sat Mar 23 22:51:30 2019 +0000

    Enabling mfg bin to hex image convertion.
---
 manifest/mfg_manifest.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/manifest/mfg_manifest.go b/manifest/mfg_manifest.go
index da2d5e9..1c61ccd 100644
--- a/manifest/mfg_manifest.go
+++ b/manifest/mfg_manifest.go
@@ -13,6 +13,7 @@ type MfgManifestTarget struct {
 	Offset       int    `json:"offset"`
 	BinPath      string `json:"bin_path,omitempty"`
 	ImagePath    string `json:"image_path,omitempty"`
+	HexPath      string `json:"hex_path,omitempty"`
 	ManifestPath string `json:"manifest_path"`
 }
 


[mynewt-artifact] 10/23: Update missing license headers and rat excludes

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

commit 0351ab5812615bcaf4efe0e16ca5112d9a2cdda7
Author: Szymon Janc <sz...@codecoup.pl>
AuthorDate: Tue Mar 26 21:40:12 2019 +0100

    Update missing license headers and rat excludes
---
 manifest/manifest.go     | 19 +++++++++++++++++++
 manifest/mfg_manifest.go | 19 +++++++++++++++++++
 mfg/mfg.go               | 19 +++++++++++++++++++
 sec/encrypt.go           | 19 +++++++++++++++++++
 4 files changed, 76 insertions(+)

diff --git a/manifest/manifest.go b/manifest/manifest.go
index 3a3376a..95c76c6 100644
--- a/manifest/manifest.go
+++ b/manifest/manifest.go
@@ -1,3 +1,22 @@
+/**
+ * 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 manifest
 
 import (
diff --git a/manifest/mfg_manifest.go b/manifest/mfg_manifest.go
index 1c61ccd..f2c506c 100644
--- a/manifest/mfg_manifest.go
+++ b/manifest/mfg_manifest.go
@@ -1,3 +1,22 @@
+/**
+ * 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 manifest
 
 import (
diff --git a/mfg/mfg.go b/mfg/mfg.go
index d3f5f93..2d5df83 100644
--- a/mfg/mfg.go
+++ b/mfg/mfg.go
@@ -1,3 +1,22 @@
+/**
+ * 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 mfg
 
 import (
diff --git a/sec/encrypt.go b/sec/encrypt.go
index bd99594..35ecdd7 100644
--- a/sec/encrypt.go
+++ b/sec/encrypt.go
@@ -1,3 +1,22 @@
+/**
+ * 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 sec
 
 import (


[mynewt-artifact] 08/23: Enable production of hex mfgimg files.

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

commit 8ba2a3d4a0ccd3c16fb4001ee157206bfdfb99f2
Author: Miguel Azevedo <mi...@gmail.com>
AuthorDate: Sat Mar 23 20:48:18 2019 +0000

    Enable production of hex mfgimg files.
---
 manifest/mfg_manifest.go | 1 +
 mfg/mfg.go               | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/manifest/mfg_manifest.go b/manifest/mfg_manifest.go
index b7f00e8..da2d5e9 100644
--- a/manifest/mfg_manifest.go
+++ b/manifest/mfg_manifest.go
@@ -44,6 +44,7 @@ type MfgManifest struct {
 	Version    string            `json:"version"`
 	Device     int               `json:"device"`
 	BinPath    string            `json:"bin_path"`
+	HexPath    string            `json:"hex_path"`
 	Bsp        string            `json:"bsp"`
 	Signatures []MfgManifestSig  `json:"signatures,omitempty"`
 	FlashAreas []flash.FlashArea `json:"flash_map"`
diff --git a/mfg/mfg.go b/mfg/mfg.go
index 3e29523..d3f5f93 100644
--- a/mfg/mfg.go
+++ b/mfg/mfg.go
@@ -6,7 +6,8 @@ import (
 	"mynewt.apache.org/newt/util"
 )
 
-const MFG_IMG_FILENAME = "mfgimg.bin"
+const MFG_BIN_IMG_FILENAME = "mfgimg.bin"
+const MFG_HEX_IMG_FILENAME = "mfgimg.hex"
 const MANIFEST_FILENAME = "manifest.json"
 
 type Mfg struct {


[mynewt-artifact] 02/23: Support for the 2.0 mfgimage format

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

commit 9dd213a93f66442563a7b113edcc935fd6c9b710
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Fri Dec 14 14:19:57 2018 -0800

    Support for the 2.0 mfgimage format
---
 image/create.go          | 101 +++++---------
 image/image.go           |   6 +
 image/key.go             |  19 ++-
 image/v1.go              |   2 +-
 manifest/manifest.go     |   3 +-
 manifest/mfg_manifest.go |  73 ++++++++++
 mfg/map_meta.go          | 153 ++++++++++++++++++++
 mfg/meta.go              | 352 +++++++++++++++++++++++++++++++++++++++++++++++
 mfg/mfg.go               | 113 +++++++++++++++
 mfg/paths.go             |  35 +++++
 10 files changed, 777 insertions(+), 80 deletions(-)

diff --git a/image/create.go b/image/create.go
index 5b28120..c3e8820 100644
--- a/image/create.go
+++ b/image/create.go
@@ -90,12 +90,12 @@ func generateEncTlv(cipherSecret []byte) (ImageTlv, error) {
 	}, nil
 }
 
-func generateSigRsa(key *rsa.PrivateKey, hash []byte) ([]byte, error) {
+func generateSigRsa(key ImageSigKey, hash []byte) ([]byte, error) {
 	opts := rsa.PSSOptions{
 		SaltLength: rsa.PSSSaltLengthEqualsHash,
 	}
 	signature, err := rsa.SignPSS(
-		rand.Reader, key, crypto.SHA256, hash, &opts)
+		rand.Reader, key.Rsa, crypto.SHA256, hash, &opts)
 	if err != nil {
 		return nil, util.FmtNewtError("Failed to compute signature: %s", err)
 	}
@@ -103,24 +103,8 @@ func generateSigRsa(key *rsa.PrivateKey, hash []byte) ([]byte, error) {
 	return signature, nil
 }
 
-func generateSigTlvRsa(key ImageSigKey, hash []byte) (ImageTlv, error) {
-	sig, err := generateSigRsa(key.Rsa, hash)
-	if err != nil {
-		return ImageTlv{}, err
-	}
-
-	return ImageTlv{
-		Header: ImageTlvHdr{
-			Type: key.sigTlvType(),
-			Pad:  0,
-			Len:  256, /* 2048 bits */
-		},
-		Data: sig,
-	}, nil
-}
-
-func generateSigEc(key *ecdsa.PrivateKey, hash []byte) ([]byte, error) {
-	r, s, err := ecdsa.Sign(rand.Reader, key, hash)
+func generateSigEc(key ImageSigKey, hash []byte) ([]byte, error) {
+	r, s, err := ecdsa.Sign(rand.Reader, key.Ec, hash)
 	if err != nil {
 		return nil, util.FmtNewtError("Failed to compute signature: %s", err)
 	}
@@ -135,88 +119,65 @@ func generateSigEc(key *ecdsa.PrivateKey, hash []byte) ([]byte, error) {
 		return nil, util.FmtNewtError("Failed to construct signature: %s", err)
 	}
 
-	return signature, nil
-}
-
-func generateSigTlvEc(key ImageSigKey, hash []byte) (ImageTlv, error) {
-	sig, err := generateSigEc(key.Ec, hash)
-	if err != nil {
-		return ImageTlv{}, err
-	}
-
 	sigLen := key.sigLen()
-	if len(sig) > int(sigLen) {
-		return ImageTlv{}, util.FmtNewtError("Something is really wrong\n")
-	}
-
-	b := &bytes.Buffer{}
-
-	if _, err := b.Write(sig); err != nil {
-		return ImageTlv{},
-			util.FmtNewtError("Failed to append sig: %s", err.Error())
+	if len(signature) > int(sigLen) {
+		return nil, util.FmtNewtError("Something is really wrong\n")
 	}
 
-	pad := make([]byte, int(sigLen)-len(sig))
-	if _, err := b.Write(pad); err != nil {
-		return ImageTlv{}, util.FmtNewtError(
-			"Failed to serialize image trailer: %s", err.Error())
-	}
+	pad := make([]byte, int(sigLen)-len(signature))
+	signature = append(signature, pad...)
 
-	return ImageTlv{
-		Header: ImageTlvHdr{
-			Type: key.sigTlvType(),
-			Pad:  0,
-			Len:  sigLen,
-		},
-		Data: b.Bytes(),
-	}, nil
+	return signature, nil
 }
 
-func generateSigTlv(key ImageSigKey, hash []byte) (ImageTlv, error) {
+func generateSig(key ImageSigKey, hash []byte) ([]byte, error) {
 	key.assertValid()
 
 	if key.Rsa != nil {
-		return generateSigTlvRsa(key, hash)
+		return generateSigRsa(key, hash)
 	} else {
-		return generateSigTlvEc(key, hash)
+		return generateSigEc(key, hash)
 	}
 }
 
-func generateKeyHashTlv(key ImageSigKey) (ImageTlv, error) {
-	key.assertValid()
-
-	keyHash, err := key.sigKeyHash()
-	if err != nil {
-		return ImageTlv{}, util.FmtNewtError(
-			"Failed to compute hash of the public key: %s", err.Error())
-	}
-
+func BuildKeyHashTlv(keyBytes []byte) ImageTlv {
+	data := RawKeyHash(keyBytes)
 	return ImageTlv{
 		Header: ImageTlvHdr{
 			Type: IMAGE_TLV_KEYHASH,
 			Pad:  0,
-			Len:  uint16(len(keyHash)),
+			Len:  uint16(len(data)),
 		},
-		Data: keyHash,
-	}, nil
+		Data: data,
+	}
 }
 
-func GenerateSigTlvs(keys []ImageSigKey, hash []byte) ([]ImageTlv, error) {
+func BuildSigTlvs(keys []ImageSigKey, hash []byte) ([]ImageTlv, error) {
 	var tlvs []ImageTlv
 
 	for _, key := range keys {
 		key.assertValid()
 
-		tlv, err := generateKeyHashTlv(key)
+		// Key hash TLV.
+		pubKey, err := key.PubBytes()
 		if err != nil {
 			return nil, err
 		}
+		tlv := BuildKeyHashTlv(pubKey)
 		tlvs = append(tlvs, tlv)
 
-		tlv, err = generateSigTlv(key, hash)
+		// Signature TLV.
+		sig, err := generateSig(key, hash)
 		if err != nil {
 			return nil, err
 		}
+		tlv = ImageTlv{
+			Header: ImageTlvHdr{
+				Type: key.sigTlvType(),
+				Len:  uint16(len(sig)),
+			},
+			Data: sig,
+		}
 		tlvs = append(tlvs, tlv)
 	}
 
@@ -409,7 +370,7 @@ func (ic *ImageCreator) Create() (Image, error) {
 	}
 	ri.Tlvs = append(ri.Tlvs, tlv)
 
-	tlvs, err := GenerateSigTlvs(ic.SigKeys, hashBytes)
+	tlvs, err := BuildSigTlvs(ic.SigKeys, hashBytes)
 	if err != nil {
 		return ri, err
 	}
diff --git a/image/image.go b/image/image.go
index 244cfed..1ed093f 100644
--- a/image/image.go
+++ b/image/image.go
@@ -134,6 +134,12 @@ func ImageTlvTypeName(tlvType uint8) string {
 	return name
 }
 
+func ImageTlvTypeIsSig(tlvType uint8) bool {
+	return tlvType == IMAGE_TLV_RSA2048 ||
+		tlvType == IMAGE_TLV_ECDSA224 ||
+		tlvType == IMAGE_TLV_ECDSA256
+}
+
 func ParseVersion(versStr string) (ImageVersion, error) {
 	var err error
 	var major uint64
diff --git a/image/key.go b/image/key.go
index 9141f6e..8345cd9 100644
--- a/image/key.go
+++ b/image/key.go
@@ -152,25 +152,30 @@ func (key *ImageSigKey) assertValid() {
 	}
 }
 
-func (key *ImageSigKey) sigKeyHash() ([]uint8, error) {
+func (key *ImageSigKey) PubBytes() ([]uint8, error) {
 	key.assertValid()
 
+	var pubkey []byte
+
 	if key.Rsa != nil {
-		pubkey, _ := asn1.Marshal(key.Rsa.PublicKey)
-		sum := sha256.Sum256(pubkey)
-		return sum[:4], nil
+		pubkey, _ = asn1.Marshal(key.Rsa.PublicKey)
 	} else {
 		switch key.Ec.Curve.Params().Name {
 		case "P-224":
 			fallthrough
 		case "P-256":
-			pubkey, _ := x509.MarshalPKIXPublicKey(&key.Ec.PublicKey)
-			sum := sha256.Sum256(pubkey)
-			return sum[:4], nil
+			pubkey, _ = x509.MarshalPKIXPublicKey(&key.Ec.PublicKey)
 		default:
 			return nil, util.NewNewtError("Unsupported ECC curve")
 		}
 	}
+
+	return pubkey, nil
+}
+
+func RawKeyHash(pubKeyBytes []byte) []byte {
+	sum := sha256.Sum256(pubKeyBytes)
+	return sum[:4]
 }
 
 func (key *ImageSigKey) sigLen() uint16 {
diff --git a/image/v1.go b/image/v1.go
index bab86f6..5540d85 100644
--- a/image/v1.go
+++ b/image/v1.go
@@ -233,7 +233,7 @@ func generateV1SigTlvRsa(key ImageSigKey, hash []byte) (ImageTlv, error) {
 }
 
 func generateV1SigTlvEc(key ImageSigKey, hash []byte) (ImageTlv, error) {
-	sig, err := generateSigEc(key.Ec, hash)
+	sig, err := generateSigEc(key, hash)
 	if err != nil {
 		return ImageTlv{}, err
 	}
diff --git a/manifest/manifest.go b/manifest/manifest.go
index 62d8c06..3a3376a 100644
--- a/manifest/manifest.go
+++ b/manifest/manifest.go
@@ -5,7 +5,6 @@ import (
 	"io"
 	"io/ioutil"
 
-	"mynewt.apache.org/newt/artifact/flash"
 	"mynewt.apache.org/newt/util"
 )
 
@@ -57,7 +56,7 @@ type Manifest struct {
 	LoaderPkgs []*ManifestPkg    `json:"loader_pkgs,omitempty"`
 	TgtVars    []string          `json:"target"`
 	Repos      []*ManifestRepo   `json:"repos"`
-	FlashAreas []flash.FlashArea `json:"flash_map"`
+	Syscfg     map[string]string `json:"syscfg"`
 
 	PkgSizes       []*ManifestSizePkg `json:"pkgsz"`
 	LoaderPkgSizes []*ManifestSizePkg `json:"loader_pkgsz,omitempty"`
diff --git a/manifest/mfg_manifest.go b/manifest/mfg_manifest.go
new file mode 100644
index 0000000..25cf9b7
--- /dev/null
+++ b/manifest/mfg_manifest.go
@@ -0,0 +1,73 @@
+package manifest
+
+import (
+	"encoding/json"
+	"io/ioutil"
+
+	"mynewt.apache.org/newt/artifact/flash"
+	"mynewt.apache.org/newt/util"
+)
+
+type MfgManifestTarget struct {
+	Name         string `json:"name"`
+	Offset       int    `json:"offset"`
+	BinPath      string `json:"bin_path,omitempty"`
+	ImagePath    string `json:"image_path,omitempty"`
+	ManifestPath string `json:"manifest_path"`
+}
+
+type MfgManifestMetaMmr struct {
+	Area      string `json:"area"`
+	Device    int    `json:"_device"`
+	EndOffset int    `json:"_end_offset"`
+}
+
+type MfgManifestMeta struct {
+	EndOffset int                  `json:"end_offset"`
+	Size      int                  `json:"size"`
+	Hash      bool                 `json:"hash_present"`
+	FlashMap  bool                 `json:"flash_map_present"`
+	Mmrs      []MfgManifestMetaMmr `json:"mmrs,omitempty"`
+	// XXX: refhash
+}
+
+type MfgManifest struct {
+	Name       string            `json:"name"`
+	BuildTime  string            `json:"build_time"`
+	Format     int               `json:"format"`
+	MfgHash    string            `json:"mfg_hash"`
+	Version    string            `json:"version"`
+	Device     int               `json:"device"`
+	BinPath    string            `json:"bin_path"`
+	FlashAreas []flash.FlashArea `json:"flash_map"`
+
+	Targets []MfgManifestTarget `json:"targets"`
+	Meta    *MfgManifestMeta    `json:"meta,omitempty"`
+}
+
+func ReadMfgManifest(path string) (MfgManifest, error) {
+	m := MfgManifest{}
+
+	content, err := ioutil.ReadFile(path)
+	if err != nil {
+		return m, util.ChildNewtError(err)
+	}
+
+	if err := json.Unmarshal(content, &m); err != nil {
+		return m, util.FmtNewtError(
+			"Failure decoding mfg manifest with path \"%s\": %s",
+			path, err.Error())
+	}
+
+	return m, nil
+}
+
+func (m *MfgManifest) MarshalJson() ([]byte, error) {
+	buffer, err := json.MarshalIndent(m, "", "  ")
+	if err != nil {
+		return nil, util.FmtNewtError(
+			"Cannot encode mfg manifest: %s", err.Error())
+	}
+
+	return buffer, nil
+}
diff --git a/mfg/map_meta.go b/mfg/map_meta.go
new file mode 100644
index 0000000..b16c37e
--- /dev/null
+++ b/mfg/map_meta.go
@@ -0,0 +1,153 @@
+/**
+ * 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 mfg
+
+import (
+	"bytes"
+	"encoding/binary"
+	"encoding/hex"
+	"encoding/json"
+
+	"mynewt.apache.org/newt/util"
+)
+
+func (t *MetaTlv) bodyMap() (map[string]interface{}, error) {
+	r := bytes.NewReader(t.Data)
+
+	readBody := func(dst interface{}) error {
+		if err := binary.Read(r, binary.LittleEndian, dst); err != nil {
+			return util.FmtNewtError(
+				"Error parsing TLV data: %s", err.Error())
+		}
+		return nil
+	}
+
+	switch t.Header.Type {
+	case META_TLV_TYPE_HASH:
+		var body MetaTlvBodyHash
+		if err := readBody(&body); err != nil {
+			return nil, err
+		}
+		return body.Map(), nil
+
+	case META_TLV_TYPE_FLASH_AREA:
+		var body MetaTlvBodyFlashArea
+		if err := readBody(&body); err != nil {
+			return nil, err
+		}
+		return body.Map(), nil
+
+	case META_TLV_TYPE_MMR_REF:
+		var body MetaTlvBodyMmrRef
+		if err := readBody(&body); err != nil {
+			return nil, err
+		}
+		return body.Map(), nil
+
+	default:
+		return nil, util.FmtNewtError(
+			"Unknown meta TLV type: %d", t.Header.Type)
+	}
+}
+
+func (b *MetaTlvBodyFlashArea) Map() map[string]interface{} {
+	return map[string]interface{}{
+		"area":   b.Area,
+		"device": b.Device,
+		"offset": b.Offset,
+		"size":   b.Size,
+	}
+}
+
+func (b *MetaTlvBodyHash) Map() map[string]interface{} {
+	return map[string]interface{}{
+		"hash": hex.EncodeToString(b.Hash[:]),
+	}
+}
+
+func (b *MetaTlvBodyMmrRef) Map() map[string]interface{} {
+	return map[string]interface{}{
+		"area": b.Area,
+	}
+}
+
+func (t *MetaTlv) Map(offset int) map[string]interface{} {
+	hmap := map[string]interface{}{
+		"_type_name": MetaTlvTypeName(t.Header.Type),
+		"type":       t.Header.Type,
+		"size":       t.Header.Size,
+	}
+
+	var body interface{}
+
+	bmap, err := t.bodyMap()
+	if err != nil {
+		body = hex.EncodeToString(t.Data)
+	} else {
+		body = bmap
+	}
+
+	return map[string]interface{}{
+		"_offset": offset,
+		"header":  hmap,
+		"data":    body,
+	}
+}
+
+func (f *MetaFooter) Map(offset int) map[string]interface{} {
+	return map[string]interface{}{
+		"_offset": offset,
+		"size":    f.Size,
+		"magic":   f.Magic,
+		"version": f.Version,
+	}
+}
+
+func (m *Meta) Map(endOffset int) map[string]interface{} {
+	offsets := m.Offsets()
+	startOffset := endOffset - int(m.Footer.Size)
+
+	tlvs := []map[string]interface{}{}
+	for i, t := range m.Tlvs {
+		tlv := t.Map(startOffset + offsets.Tlvs[i])
+		tlvs = append(tlvs, tlv)
+	}
+
+	ftr := m.Footer.Map(startOffset + offsets.Footer)
+
+	return map[string]interface{}{
+		"_offset":     startOffset,
+		"_end_offset": endOffset,
+		"_size":       m.Footer.Size,
+		"tlvs":        tlvs,
+		"footer":      ftr,
+	}
+}
+
+func (m *Meta) Json(offset int) (string, error) {
+	mmap := m.Map(offset)
+
+	bin, err := json.MarshalIndent(mmap, "", "    ")
+	if err != nil {
+		return "", util.ChildNewtError(err)
+	}
+
+	return string(bin), nil
+}
diff --git a/mfg/meta.go b/mfg/meta.go
new file mode 100644
index 0000000..4521a89
--- /dev/null
+++ b/mfg/meta.go
@@ -0,0 +1,352 @@
+/**
+ * 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 mfg
+
+import (
+	"bytes"
+	"encoding/binary"
+	"io"
+	"io/ioutil"
+
+	"mynewt.apache.org/newt/util"
+)
+
+// The "manufacturing meta region" is located at the end of the boot loader
+// flash area.  This region has the following structure.
+//
+//  0                   1                   2                   3
+//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |Version (0x01) |                  0xff padding                 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |   TLV type    |   TLV size    | TLV data ("TLV size" bytes)   ~
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               ~
+// ~                                                               ~
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |   TLV type    |   TLV size    | TLV data ("TLV size" bytes)   ~
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               ~
+// ~                                                               ~
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |   Region size                 |         0xff padding          |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                       Magic (0x3bb2a269)                      |
+// +-+-+-+-+-+--+-+-+-+-end of boot loader area+-+-+-+-+-+-+-+-+-+-+
+//
+// The number of TLVs is variable; two are shown above for illustrative
+// purposes.
+//
+// Fields:
+// <Header>
+// 1. Version: Manufacturing meta version number; always 0x01.
+//
+// <TLVs>
+// 2. TLV type: Indicates the type of data to follow.
+// 3. TLV size: The number of bytes of data to follow.
+// 4. TLV data: TLV-size bytes of data.
+//
+// <Footer>
+// 5. Region size: The size, in bytes, of the entire manufacturing meta region;
+//    includes header, TLVs, and footer.
+// 6. Magic: indicates the presence of the manufacturing meta region.
+
+const META_MAGIC = 0x3bb2a269
+const META_VERSION = 2
+const META_TLV_TYPE_HASH = 0x01
+const META_TLV_TYPE_FLASH_AREA = 0x02
+const META_TLV_TYPE_MMR_REF = 0x04
+
+const META_HASH_SZ = 32
+const META_FOOTER_SZ = 8
+const META_TLV_HEADER_SZ = 2
+const META_TLV_HASH_SZ = META_HASH_SZ
+const META_TLV_FLASH_AREA_SZ = 10
+const META_TLV_MMR_REF_SZ = 1
+
+type MetaFooter struct {
+	Size    uint16 // Includes header, TLVs, and footer.
+	Version uint8
+	Pad8    uint8  // 0xff
+	Magic   uint32 // META_MAGIC
+}
+
+type MetaTlvHeader struct {
+	Type uint8 // Indicates the type of data to follow.
+	Size uint8 // The number of bytes of data to follow.
+}
+
+type MetaTlvBodyFlashArea struct {
+	Area   uint8  // Unique value identifying this flash area.
+	Device uint8  // Indicates host flash device (aka section number).
+	Offset uint32 // The byte offset within the flash device.
+	Size   uint32 // Size, in bytes, of entire flash area.
+}
+
+type MetaTlvBodyHash struct {
+	Hash [META_HASH_SZ]byte
+}
+
+type MetaTlvBodyMmrRef struct {
+	Area uint8
+}
+
+type MetaTlv struct {
+	Header MetaTlvHeader
+	Data   []byte
+}
+
+type Meta struct {
+	Tlvs   []MetaTlv
+	Footer MetaFooter
+}
+
+type MetaOffsets struct {
+	Tlvs      []int
+	Footer    int
+	TotalSize int
+}
+
+var metaTlvTypeNameMap = map[uint8]string{
+	META_TLV_TYPE_HASH:       "hash",
+	META_TLV_TYPE_FLASH_AREA: "flash_area",
+	META_TLV_TYPE_MMR_REF:    "mmr_ref",
+}
+
+func MetaTlvTypeName(typ uint8) string {
+	name := metaTlvTypeNameMap[typ]
+	if name == "" {
+		name = "???"
+	}
+	return name
+}
+
+func writeElem(elem interface{}, w io.Writer) error {
+	/* XXX: Assume target platform uses little endian. */
+	if err := binary.Write(w, binary.LittleEndian, elem); err != nil {
+		return util.ChildNewtError(err)
+	}
+	return nil
+}
+
+func (tlv *MetaTlv) Write(w io.Writer) (int, error) {
+	sz := 0
+
+	if err := writeElem(tlv.Header, w); err != nil {
+		return sz, err
+	}
+	sz += META_TLV_HEADER_SZ
+
+	if err := writeElem(tlv.Data, w); err != nil {
+		return sz, err
+	}
+	sz += len(tlv.Data)
+
+	return sz, nil
+}
+
+func (meta *Meta) WritePlusOffsets(w io.Writer) (MetaOffsets, error) {
+	mo := MetaOffsets{}
+	sz := 0
+
+	for _, tlv := range meta.Tlvs {
+		tlvSz, err := tlv.Write(w)
+		if err != nil {
+			return mo, err
+		}
+		mo.Tlvs = append(mo.Tlvs, sz)
+		sz += tlvSz
+	}
+
+	if err := writeElem(meta.Footer, w); err != nil {
+		return mo, err
+	}
+	mo.Footer = sz
+	sz += META_FOOTER_SZ
+
+	mo.TotalSize = sz
+
+	return mo, nil
+}
+
+func (meta *Meta) Offsets() MetaOffsets {
+	mo, _ := meta.WritePlusOffsets(ioutil.Discard)
+	return mo
+}
+
+func (meta *Meta) Write(w io.Writer) (int, error) {
+	mo, err := meta.WritePlusOffsets(w)
+	if err != nil {
+		return 0, err
+	}
+
+	return mo.TotalSize, nil
+}
+
+func (meta *Meta) Size() int {
+	return meta.Offsets().TotalSize
+}
+
+func (meta *Meta) Bytes() ([]byte, error) {
+	b := &bytes.Buffer{}
+
+	_, err := meta.Write(b)
+	if err != nil {
+		return nil, err
+	}
+
+	return b.Bytes(), nil
+}
+
+func (meta *Meta) FindTlvIndices(typ uint8) []int {
+	indices := []int{}
+
+	for i, tlv := range meta.Tlvs {
+		if tlv.Header.Type == typ {
+			indices = append(indices, i)
+		}
+	}
+
+	return indices
+}
+
+func (meta *Meta) FindTlvs(typ uint8) []*MetaTlv {
+	indices := meta.FindTlvIndices(typ)
+
+	tlvs := []*MetaTlv{}
+	for _, index := range indices {
+		tlvs = append(tlvs, &meta.Tlvs[index])
+	}
+
+	return tlvs
+}
+
+func (meta *Meta) FindFirstTlv(typ uint8) *MetaTlv {
+	indices := meta.FindTlvIndices(typ)
+	if len(indices) == 0 {
+		return nil
+	}
+
+	return &meta.Tlvs[indices[0]]
+}
+
+func (meta *Meta) HashOffset() int {
+	mo := meta.Offsets()
+	indices := meta.FindTlvIndices(META_TLV_TYPE_HASH)
+	if len(indices) == 0 {
+		return -1
+	}
+
+	return META_TLV_HEADER_SZ + mo.Tlvs[indices[0]]
+}
+
+func (meta *Meta) ClearHash() {
+	tlv := meta.FindFirstTlv(META_TLV_TYPE_HASH)
+	if tlv != nil {
+		tlv.Data = make([]byte, META_HASH_SZ)
+	}
+}
+
+func (meta *Meta) Hash() []byte {
+	tlv := meta.FindFirstTlv(META_TLV_TYPE_HASH)
+	if tlv == nil {
+		return nil
+	}
+	return tlv.Data
+}
+
+func parseMetaTlv(bin []byte) (MetaTlv, int, error) {
+	r := bytes.NewReader(bin)
+
+	tlv := MetaTlv{}
+	if err := binary.Read(r, binary.LittleEndian, &tlv.Header); err != nil {
+		return tlv, 0, util.FmtNewtError(
+			"Error reading TLV header: %s", err.Error())
+	}
+
+	data := make([]byte, tlv.Header.Size)
+	sz, err := r.Read(data)
+	if err != nil {
+		return tlv, 0, util.FmtNewtError(
+			"Error reading %d bytes of TLV data: %s",
+			tlv.Header.Size, err.Error())
+	}
+	if sz != len(data) {
+		return tlv, 0, util.FmtNewtError(
+			"Error reading %d bytes of TLV data: incomplete read",
+			tlv.Header.Size)
+	}
+	tlv.Data = data
+
+	return tlv, META_TLV_HEADER_SZ + int(tlv.Header.Size), nil
+}
+
+func parseMetaFooter(bin []byte) (MetaFooter, int, error) {
+	r := bytes.NewReader(bin)
+
+	var ftr MetaFooter
+	if err := binary.Read(r, binary.LittleEndian, &ftr); err != nil {
+		return ftr, 0, util.FmtNewtError(
+			"Error reading meta footer: %s", err.Error())
+	}
+
+	if ftr.Magic != META_MAGIC {
+		return ftr, 0, util.FmtNewtError(
+			"Meta footer contains invalid magic; exp:0x%08x, got:0x%08x",
+			META_MAGIC, ftr.Magic)
+	}
+
+	return ftr, META_FOOTER_SZ, nil
+}
+
+func ParseMeta(bin []byte) (Meta, int, error) {
+	if len(bin) < META_FOOTER_SZ {
+		return Meta{}, 0, util.FmtNewtError(
+			"Binary too small to accommodate meta footer; "+
+				"bin-size=%d ftr-size=%d", len(bin), META_FOOTER_SZ)
+	}
+
+	ftr, _, err := parseMetaFooter(bin[len(bin)-META_FOOTER_SZ:])
+	if err != nil {
+		return Meta{}, 0, err
+	}
+
+	if int(ftr.Size) > len(bin) {
+		return Meta{}, 0, util.FmtNewtError(
+			"Binary too small to accommodate meta region; "+
+				"bin-size=%d meta-size=%d", len(bin), ftr.Size)
+	}
+
+	ftrOff := len(bin) - META_FOOTER_SZ
+	off := len(bin) - int(ftr.Size)
+
+	tlvs := []MetaTlv{}
+	for off < ftrOff {
+		tlv, sz, err := parseMetaTlv(bin[off:])
+		if err != nil {
+			return Meta{}, 0, err
+		}
+		tlvs = append(tlvs, tlv)
+		off += sz
+	}
+
+	return Meta{
+		Tlvs:   tlvs,
+		Footer: ftr,
+	}, off, nil
+}
diff --git a/mfg/mfg.go b/mfg/mfg.go
new file mode 100644
index 0000000..8e999ad
--- /dev/null
+++ b/mfg/mfg.go
@@ -0,0 +1,113 @@
+package mfg
+
+import (
+	"crypto/sha256"
+
+	"mynewt.apache.org/newt/util"
+)
+
+const MFG_IMG_FILENAME = "mfgimg.bin"
+const MFG_MANIFEST_FILENAME = "manifest.json"
+
+type Mfg struct {
+	Bin  []byte
+	Meta *Meta
+
+	// Unused if Meta==nil.
+	MetaOff int
+}
+
+func Parse(data []byte, metaEndOff int, eraseVal byte) (Mfg, error) {
+	m := Mfg{
+		Bin: data,
+	}
+
+	if metaEndOff >= 0 {
+		meta, _, err := ParseMeta(data[:metaEndOff])
+		if err != nil {
+			return m, err
+		}
+		m.Meta = &meta
+		m.MetaOff = metaEndOff - int(meta.Footer.Size)
+	}
+
+	return m, nil
+}
+
+func StripPadding(b []byte, eraseVal byte) []byte {
+	var pad int
+	for pad = 0; pad < len(b); pad++ {
+		off := len(b) - pad - 1
+		if b[off] != eraseVal {
+			break
+		}
+	}
+
+	return b[:len(b)-pad]
+}
+
+func AddPadding(b []byte, eraseVal byte, padLen int) []byte {
+	for i := 0; i < padLen; i++ {
+		b = append(b, eraseVal)
+	}
+	return b
+}
+
+func (m *Mfg) bytesZeroedHash(eraseVal byte) ([]byte, error) {
+	binCopy := make([]byte, len(m.Bin))
+	copy(binCopy, m.Bin)
+
+	m.Meta.ClearHash()
+
+	metaBytes, err := m.Meta.Bytes()
+	if err != nil {
+		return nil, err
+	}
+
+	padLen := m.MetaOff + len(metaBytes) - len(binCopy)
+	if padLen > 0 {
+		binCopy = AddPadding(binCopy, eraseVal, padLen)
+	}
+
+	copy(binCopy[m.MetaOff:m.MetaOff+len(metaBytes)], metaBytes)
+
+	return binCopy, nil
+}
+
+// Calculates the SHA256 hash, using the full manufacturing image as input.
+// Hash-calculation algorithm is as follows:
+// 1. Zero out the 32 bytes that will contain the hash.
+// 2. Apply SHA256 to the result.
+//
+// This function assumes that the 32 bytes of hash data have already been
+// zeroed.
+func CalcHash(bin []byte) []byte {
+	hash := sha256.Sum256(bin)
+	return hash[:]
+}
+
+func (m *Mfg) Bytes(eraseVal byte) ([]byte, error) {
+	// First, write with zeroed hash.
+	bin, err := m.bytesZeroedHash(eraseVal)
+	if err != nil {
+		return nil, err
+	}
+
+	// Calculate hash and fill TLV.
+	tlv := m.Meta.FindFirstTlv(META_TLV_TYPE_HASH)
+	if tlv != nil {
+		hashData := CalcHash(bin)
+		copy(tlv.Data, hashData)
+
+		hashOff := m.MetaOff + m.Meta.HashOffset()
+		if hashOff+META_HASH_SZ > len(bin) {
+			return nil, util.FmtNewtError(
+				"unexpected error: hash extends beyond end " +
+					"of manufacturing image")
+		}
+
+		copy(bin[hashOff:hashOff+META_HASH_SZ], tlv.Data)
+	}
+
+	return bin, nil
+}
diff --git a/mfg/paths.go b/mfg/paths.go
new file mode 100644
index 0000000..483aca2
--- /dev/null
+++ b/mfg/paths.go
@@ -0,0 +1,35 @@
+/**
+ * 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 mfg
+
+import (
+	"fmt"
+	"path/filepath"
+)
+
+const MANIFEST_FILENAME = "manifest.json"
+const BOOT_DIR = "bootloader"
+const BOOT_MANIFEST_PATH = BOOT_DIR + "/manifest.json"
+const SECTION_BIN_DIR = "sections"
+
+func SectionBinPath(mfgPkgName string, sectionNum int) string {
+	return fmt.Sprintf("%s/%s-s%d.bin", SECTION_BIN_DIR,
+		filepath.Base(mfgPkgName), sectionNum)
+}


[mynewt-artifact] 21/23: Add NOTICE file

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

commit b9800595bc6593a50f005e70f3a5fbb6db22151a
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Fri Jun 21 12:18:15 2019 -0700

    Add NOTICE file
---
 NOTICE | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..410dec9
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,8 @@
+Apache Mynewt
+Copyright 2015-2019 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+Portions of this software were developed at
+Runtime Inc, copyright 2015.


[mynewt-artifact] 04/23: Include BSP name in mfg manifest

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

commit 8a3bd09fcd14c25f6f41ffbbb7f3b67fbfbb5a48
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Thu Dec 20 13:49:01 2018 -0800

    Include BSP name in mfg manifest
---
 manifest/mfg_manifest.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/manifest/mfg_manifest.go b/manifest/mfg_manifest.go
index 25cf9b7..bb27adc 100644
--- a/manifest/mfg_manifest.go
+++ b/manifest/mfg_manifest.go
@@ -39,6 +39,7 @@ type MfgManifest struct {
 	Version    string            `json:"version"`
 	Device     int               `json:"device"`
 	BinPath    string            `json:"bin_path"`
+	Bsp        string            `json:"bsp"`
 	FlashAreas []flash.FlashArea `json:"flash_map"`
 
 	Targets []MfgManifestTarget `json:"targets"`