You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2019/01/04 18:21:05 UTC

[mynewt-newt] branch master updated (d9fea77 -> d18c971)

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


    from d9fea77  Merge pull request #251 from michal-narajowski/cmake-link-fix
     new 1e5f83d  "artifact" library
     new cf572b6  Remove the `flash` and `image` packages
     new 111666c  builder - reduce scope
     new 3e5cfc6  Use "artifact" library
     new 3fd4404  Add `flashmap` package
     new 7cdcc26  Add `imgprod` package - image production
     new 8e570b8  Add `manifest` package - manifest generation
     new 4f67405  Larva tool
     new 42266eb  Rename mimg.go to larva.go
     new 22fba5e  Larva: Add `mfg bootkey` command
     new b7f0717  Support for the 2.0 mfgimage format
     new b7d290f  Artifact library update
     new 22ef74c  newt - Use latest artifact library
     new a6095d7  larva: Add `image {de,en}crypt` commands
     new 987f960  Include BSP name in mfg manifest
     new dc5ebdf  Add readme for newt's mfg support.
     new d18c971  Fix int overflow on 32-bit platforms.

The 17 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:
 artifact/flash/flash.go                       |  160 +++
 artifact/image/create.go                      |  395 +++++++
 {newt => artifact}/image/encrypted.go         |    0
 artifact/image/image.go                       |  564 ++++++++++
 artifact/image/key.go                         |  341 ++++++
 {newt => artifact}/image/keys_test.go         |    2 -
 artifact/image/v1.go                          |  491 +++++++++
 artifact/manifest/manifest.go                 |   94 ++
 artifact/manifest/mfg_manifest.go             |   74 ++
 artifact/mfg/map_meta.go                      |  153 +++
 artifact/mfg/meta.go                          |  352 ++++++
 artifact/mfg/mfg.go                           |  131 +++
 newt/mfg/read.go => artifact/misc/misc.go     |   20 +-
 larva/cli/image_cmds.go                       |  596 ++++++++++
 larva/cli/mfg_cmds.go                         |  418 +++++++
 larva/cli/util.go                             |  100 ++
 larva/larva.go                                |  100 ++
 larva/lvimg/lvimg.go                          |  188 ++++
 larva/lvmfg/lvmfg.go                          |  203 ++++
 newt/builder/build.go                         |   38 +-
 newt/builder/buildutil.go                     |    2 +-
 newt/builder/paths.go                         |    4 +
 newt/builder/size.go                          |   55 +-
 newt/builder/size_report.go                   |    1 -
 newt/builder/targetbuild.go                   |  273 +----
 newt/cli/build_cmds.go                        |   11 +
 newt/cli/image_cmds.go                        |  133 +--
 newt/cli/mfg_cmds.go                          |   58 +-
 newt/cli/run_cmds.go                          |   38 +-
 newt/{flash/flash.go => flashmap/flashmap.go} |  173 +--
 newt/image/image.go                           | 1463 -------------------------
 newt/imgprod/imgprod.go                       |  284 +++++
 newt/imgprod/v1.go                            |  217 ++++
 newt/manifest/manifest.go                     |  334 ++++++
 newt/mfg/README.md                            |   85 ++
 newt/mfg/build.go                             |  564 ++++++++++
 newt/mfg/create.go                            |  535 ---------
 newt/mfg/decode.go                            |  310 ++++++
 newt/mfg/emit.go                              |  343 ++++++
 newt/mfg/load.go                              |  317 ------
 newt/mfg/meta.go                              |  247 -----
 newt/mfg/mfg.go                               |   98 --
 newt/mfg/misc.go                              |   92 ++
 newt/mfg/part.go                              |   96 ++
 newt/mfg/paths.go                             |  154 +--
 newt/pkg/bsp_package.go                       |    6 +-
 newt/resolve/resolve.go                       |    8 +-
 newt/syscfg/syscfg.go                         |    8 +-
 util/util.go                                  |    8 +
 49 files changed, 6896 insertions(+), 3441 deletions(-)
 create mode 100644 artifact/flash/flash.go
 create mode 100644 artifact/image/create.go
 rename {newt => artifact}/image/encrypted.go (100%)
 create mode 100644 artifact/image/image.go
 create mode 100644 artifact/image/key.go
 rename {newt => artifact}/image/keys_test.go (99%)
 create mode 100644 artifact/image/v1.go
 create mode 100644 artifact/manifest/manifest.go
 create mode 100644 artifact/manifest/mfg_manifest.go
 create mode 100644 artifact/mfg/map_meta.go
 create mode 100644 artifact/mfg/meta.go
 create mode 100644 artifact/mfg/mfg.go
 rename newt/mfg/read.go => artifact/misc/misc.go (63%)
 create mode 100644 larva/cli/image_cmds.go
 create mode 100644 larva/cli/mfg_cmds.go
 create mode 100644 larva/cli/util.go
 create mode 100644 larva/larva.go
 create mode 100644 larva/lvimg/lvimg.go
 create mode 100644 larva/lvmfg/lvmfg.go
 rename newt/{flash/flash.go => flashmap/flashmap.go} (64%)
 delete mode 100644 newt/image/image.go
 create mode 100644 newt/imgprod/imgprod.go
 create mode 100644 newt/imgprod/v1.go
 create mode 100644 newt/manifest/manifest.go
 create mode 100644 newt/mfg/README.md
 create mode 100644 newt/mfg/build.go
 delete mode 100644 newt/mfg/create.go
 create mode 100644 newt/mfg/decode.go
 create mode 100644 newt/mfg/emit.go
 delete mode 100644 newt/mfg/load.go
 delete mode 100644 newt/mfg/meta.go
 delete mode 100644 newt/mfg/mfg.go
 create mode 100644 newt/mfg/misc.go
 create mode 100644 newt/mfg/part.go


[mynewt-newt] 03/17: builder - reduce scope

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

commit 111666cfc2819d1df71b147377ef44f34a424665
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Tue Nov 20 17:49:16 2018 -0800

    builder - reduce scope
    
    It is difficult to reuse parts of the builder package in other parts of
    newt (or other tools) because they only work in the context of a longer
    sequence of operations.
    
    This commit removes some functionality from the `builder` package.  The
    following guidelines are observed:
    
    `builder` is only used for operations that directly involve the
    Builder's package list or TargetBuilder's dependency graphs.  The
    builder package no longer knows about higher-level concepts like images
    and manifests.
    
    The `builder` package also doesn't read or write files to disk, except
    indirectly via the `toolchain` package.  Data is passed into and out of
    `builder` using data structures and byte slices, not filenames.
---
 newt/builder/build.go       |  38 +-----
 newt/builder/buildutil.go   |   2 +-
 newt/builder/paths.go       |   4 +
 newt/builder/size.go        |  55 +--------
 newt/builder/size_report.go |   1 -
 newt/builder/targetbuild.go | 274 +++-----------------------------------------
 6 files changed, 21 insertions(+), 353 deletions(-)

diff --git a/newt/builder/build.go b/newt/builder/build.go
index ecedfe8..e1fe17c 100644
--- a/newt/builder/build.go
+++ b/newt/builder/build.go
@@ -29,7 +29,6 @@ import (
 
 	log "github.com/Sirupsen/logrus"
 
-	"mynewt.apache.org/newt/newt/image"
 	"mynewt.apache.org/newt/newt/interfaces"
 	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/newt/pkg"
@@ -92,7 +91,7 @@ func NewBuilder(
 	for api, rpkg := range apiMap {
 		bpkg := b.PkgMap[rpkg]
 		if bpkg == nil {
-			for _, rpkg := range b.sortedRpkgs() {
+			for _, rpkg := range b.SortedRpkgs() {
 				log.Debugf("    * %s", rpkg.Lpkg.Name())
 			}
 			return nil, util.FmtNewtError(
@@ -798,41 +797,6 @@ func (b *Builder) buildRomElf(common *symbol.SymbolMap) error {
 	return nil
 }
 
-func (b *Builder) CreateImage(version string,
-	keystrs []string, keyId uint8,
-	loaderImg *image.Image) (*image.Image, error) {
-
-	img, err := image.NewImage(b.AppBinPath(), b.AppImgPath())
-	if err != nil {
-		return nil, err
-	}
-
-	err = img.SetVersion(version)
-	if err != nil {
-		return nil, err
-	}
-
-	if len(keystrs) == 1 {
-		if err := img.SetKeyV1(keystrs[0], keyId); err != nil {
-			return nil, err
-		}
-	} else {
-		if err := img.SetKeys(keystrs); err != nil {
-			return nil, err
-		}
-	}
-
-	img.HeaderSize = uint(b.targetBuilder.target.HeaderSize)
-	if err := img.Generate(loaderImg); err != nil {
-		return nil, err
-	}
-
-	util.StatusMessage(util.VERBOSITY_DEFAULT,
-		"App image succesfully generated: %s\n", img.TargetImg)
-
-	return img, nil
-}
-
 // Deletes files that should never be reused for a subsequent build.  This
 // list includes:
 //     <app>.img
diff --git a/newt/builder/buildutil.go b/newt/builder/buildutil.go
index 113cbfd..260ef53 100644
--- a/newt/builder/buildutil.go
+++ b/newt/builder/buildutil.go
@@ -83,7 +83,7 @@ func (b *Builder) sortedBuildPackages() []*BuildPackage {
 	return sorter.bpkgs
 }
 
-func (b *Builder) sortedRpkgs() []*resolve.ResolvePackage {
+func (b *Builder) SortedRpkgs() []*resolve.ResolvePackage {
 	bpkgs := b.sortedBuildPackages()
 
 	rpkgs := make([]*resolve.ResolvePackage, len(bpkgs), len(bpkgs))
diff --git a/newt/builder/paths.go b/newt/builder/paths.go
index becba56..17995fe 100644
--- a/newt/builder/paths.go
+++ b/newt/builder/paths.go
@@ -166,6 +166,10 @@ func (b *Builder) AppHexPath() string {
 		".hex"
 }
 
+func (b *Builder) AppMapPath() string {
+	return b.AppElfPath() + ".map"
+}
+
 func (b *Builder) AppBinPath() string {
 	return b.AppElfPath() + ".bin"
 }
diff --git a/newt/builder/size.go b/newt/builder/size.go
index 4753433..8da6f1a 100644
--- a/newt/builder/size.go
+++ b/newt/builder/size.go
@@ -28,9 +28,8 @@ import (
 	"strconv"
 	"strings"
 
-	"mynewt.apache.org/newt/newt/image"
-	"mynewt.apache.org/newt/util"
 	"mynewt.apache.org/newt/newt/interfaces"
+	"mynewt.apache.org/newt/util"
 )
 
 /*
@@ -420,58 +419,6 @@ func (b *Builder) FindPkgNameByArName(arName string) string {
 	return filepath.Base(arName)
 }
 
-func (b *Builder) PkgSizes() (*image.ImageManifestSizeCollector, error) {
-	if b.appPkg == nil {
-		return nil, util.NewNewtError("app package not specified for this target")
-	}
-
-	if b.targetBuilder.bspPkg.Arch == "sim" {
-		return nil, util.NewNewtError("'newt size' not supported for sim targets")
-	}
-	mapFile := b.AppElfPath() + ".map"
-
-	libs, err := ParseMapFileSizes(mapFile)
-	if err != nil {
-		return nil, err
-	}
-
-	/*
-	 * Order libraries by name.
-	 */
-	pkgSizes := make(PkgSizeArray, len(libs))
-	i := 0
-	for _, es := range libs {
-		pkgSizes[i] = es
-		i++
-	}
-	sort.Sort(pkgSizes)
-
-	c := image.NewImageManifestSizeCollector()
-	for _, es := range pkgSizes {
-		p := c.AddPkg(b.FindPkgNameByArName(es.Name))
-
-		/*
-		 * Order symbols by name.
-		 */
-		symbols := make(SymbolDataArray, len(es.Syms))
-		i := 0
-		for _, sym := range es.Syms {
-			symbols[i] = sym
-			i++
-		}
-		sort.Sort(symbols)
-		for _, sym := range symbols {
-			for area, areaSz := range sym.Sizes {
-				if areaSz != 0 {
-					p.AddSymbol(sym.ObjName, sym.Name, area, areaSz)
-				}
-			}
-		}
-	}
-
-	return c, nil
-}
-
 func (b *Builder) Size() error {
 	if b.appPkg == nil {
 		return util.NewNewtError("app package not specified for this target")
diff --git a/newt/builder/size_report.go b/newt/builder/size_report.go
index 0673fef..d62f5b2 100644
--- a/newt/builder/size_report.go
+++ b/newt/builder/size_report.go
@@ -76,7 +76,6 @@ func runAddr2lineCommand(elfFilePath string, address string) ([]byte, error) {
 	return cmdOut, err
 }
 
-
 func loadSymbolsAndPaths(elfFilePath, pathToStrip string) (map[string]string,
 	error) {
 	symbolsPath := make(map[string]string)
diff --git a/newt/builder/targetbuild.go b/newt/builder/targetbuild.go
index 3f10dbb..23c361a 100644
--- a/newt/builder/targetbuild.go
+++ b/newt/builder/targetbuild.go
@@ -24,22 +24,18 @@ import (
 	"crypto/ecdsa"
 	"crypto/rsa"
 	"crypto/x509"
-	"encoding/json"
 	"encoding/pem"
 	"fmt"
 	"io/ioutil"
 	"os"
-	"path/filepath"
-	"sort"
 	"strings"
-	"time"
 
 	log "github.com/Sirupsen/logrus"
 
-	"mynewt.apache.org/newt/newt/flash"
-	"mynewt.apache.org/newt/newt/image"
+	"mynewt.apache.org/newt/artifact/flash"
+	"mynewt.apache.org/newt/artifact/image"
+	"mynewt.apache.org/newt/newt/flashmap"
 	"mynewt.apache.org/newt/newt/interfaces"
-	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/project"
 	"mynewt.apache.org/newt/newt/resolve"
@@ -106,6 +102,10 @@ func NewTargetBuilder(target *target.Target) (*TargetBuilder, error) {
 	return NewTargetTester(target, nil)
 }
 
+func (t *TargetBuilder) BspPkg() *pkg.BspPackage {
+	return t.bspPkg
+}
+
 func (t *TargetBuilder) NewCompiler(dstDir string, buildProfile string) (
 	*toolchain.Compiler, error) {
 
@@ -288,7 +288,10 @@ func (t *TargetBuilder) validateAndWriteCfg() error {
 	}
 
 	// Generate flash map.
-	if err := t.bspPkg.FlashMap.EnsureWritten(srcDir, incDir,
+	if err := flashmap.EnsureFlashMapWritten(
+		t.bspPkg.FlashMap,
+		srcDir,
+		incDir,
 		pkg.ShortName(t.target.Package())); err != nil {
 
 		return err
@@ -504,11 +507,6 @@ func (t *TargetBuilder) Build() error {
 		return err
 	}
 
-	/* Create manifest. */
-	if err := t.createManifest(); err != nil {
-		return err
-	}
-
 	return nil
 }
 
@@ -522,11 +520,11 @@ func (t *TargetBuilder) RelinkLoader() (error, map[string]bool,
 
 	/* fetch symbols from the elf and from the libraries themselves */
 	log.Debugf("Loader packages:")
-	for _, rpkg := range t.LoaderBuilder.sortedRpkgs() {
+	for _, rpkg := range t.LoaderBuilder.SortedRpkgs() {
 		log.Debugf("    * %s", rpkg.Lpkg.Name())
 	}
 	log.Debugf("App packages:")
-	for _, rpkg := range t.AppBuilder.sortedRpkgs() {
+	for _, rpkg := range t.AppBuilder.SortedRpkgs() {
 		log.Debugf("    * %s", rpkg.Lpkg.Name())
 	}
 	err, appLibSym := t.AppBuilder.ExtractSymbolInfo()
@@ -651,137 +649,6 @@ func (t *TargetBuilder) InjectSetting(key string, value string) {
 	t.injectedSettings[key] = value
 }
 
-func readManifest(path string) (*image.ImageManifest, error) {
-	content, err := ioutil.ReadFile(path)
-	if err != nil {
-		return nil, util.ChildNewtError(err)
-	}
-
-	manifest := &image.ImageManifest{}
-	if err := json.Unmarshal(content, &manifest); err != nil {
-		return nil, util.FmtNewtError(
-			"Failure decoding manifest with path \"%s\": %s", err.Error())
-	}
-
-	return manifest, nil
-}
-
-func (t *TargetBuilder) createManifest() error {
-	manifest := &image.ImageManifest{
-		Date: time.Now().Format(time.RFC3339),
-		Name: t.GetTarget().FullName(),
-	}
-
-	rm := image.NewRepoManager()
-	for _, rpkg := range t.AppBuilder.sortedRpkgs() {
-		manifest.Pkgs = append(manifest.Pkgs,
-			rm.GetImageManifestPkg(rpkg.Lpkg))
-	}
-
-	if t.LoaderBuilder != nil {
-		for _, rpkg := range t.LoaderBuilder.sortedRpkgs() {
-			manifest.LoaderPkgs = append(manifest.LoaderPkgs,
-				rm.GetImageManifestPkg(rpkg.Lpkg))
-		}
-	}
-
-	manifest.Repos = rm.AllRepos()
-
-	vars := t.GetTarget().TargetY.AllSettingsAsStrings()
-	keys := make([]string, 0, len(vars))
-	for k := range vars {
-		keys = append(keys, k)
-	}
-	sort.Strings(keys)
-	for _, k := range keys {
-		manifest.TgtVars = append(manifest.TgtVars, k+"="+vars[k])
-	}
-	syscfgKV := t.GetTarget().Package().SyscfgY.GetValStringMapString(
-		"syscfg.vals", nil)
-	if len(syscfgKV) > 0 {
-		tgtSyscfg := fmt.Sprintf("target.syscfg=%s",
-			syscfg.KeyValueToStr(syscfgKV))
-		manifest.TgtVars = append(manifest.TgtVars, tgtSyscfg)
-	}
-
-	c, err := t.AppBuilder.PkgSizes()
-	if err == nil {
-		manifest.PkgSizes = c.Pkgs
-	}
-	if t.LoaderBuilder != nil {
-		c, err = t.LoaderBuilder.PkgSizes()
-		if err == nil {
-			manifest.LoaderPkgSizes = c.Pkgs
-		}
-	}
-	file, err := os.Create(t.AppBuilder.ManifestPath())
-	if err != nil {
-		return util.FmtNewtError("Cannot create manifest file %s: %s",
-			t.AppBuilder.ManifestPath(), err.Error())
-	}
-	defer file.Close()
-
-	buffer, err := json.MarshalIndent(manifest, "", "  ")
-	if err != nil {
-		return util.FmtNewtError("Cannot encode manifest: %s", err.Error())
-	}
-	_, err = file.Write(buffer)
-	if err != nil {
-		return util.FmtNewtError("Cannot write manifest file: %s",
-			err.Error())
-	}
-
-	return nil
-}
-
-// Reads an existing manifest file and augments it with image fields:
-//     * Image version
-//     * App image path
-//     * App image hash
-//     * Loader image path
-//     * Loader image hash
-//     * Build ID
-func (t *TargetBuilder) augmentManifest(
-	appImg *image.Image,
-	loaderImg *image.Image,
-	buildId []byte) error {
-
-	manifest, err := readManifest(t.AppBuilder.ManifestPath())
-	if err != nil {
-		return err
-	}
-
-	manifest.Version = appImg.Version.String()
-	manifest.ImageHash = fmt.Sprintf("%x", appImg.Hash)
-	manifest.Image = filepath.Base(appImg.TargetImg)
-
-	if loaderImg != nil {
-		manifest.Loader = filepath.Base(loaderImg.TargetImg)
-		manifest.LoaderHash = fmt.Sprintf("%x", loaderImg.Hash)
-	}
-
-	manifest.BuildID = fmt.Sprintf("%x", buildId)
-
-	file, err := os.Create(t.AppBuilder.ManifestPath())
-	if err != nil {
-		return util.FmtNewtError("Cannot create manifest file %s: %s",
-			t.AppBuilder.ManifestPath(), err.Error())
-	}
-	defer file.Close()
-
-	buffer, err := json.MarshalIndent(manifest, "", "  ")
-	if err != nil {
-		return util.FmtNewtError("Cannot encode manifest: %s", err.Error())
-	}
-	_, err = file.Write(buffer)
-	if err != nil {
-		return util.FmtNewtError("Cannot write manifest file: %s",
-			err.Error())
-	}
-
-	return nil
-}
-
 // Calculates the size of a single boot trailer.  This is the amount of flash
 // that must be reserved at the end of each image slot.
 func (t *TargetBuilder) bootTrailerSize() int {
@@ -835,7 +702,7 @@ func (t *TargetBuilder) bootTrailerSize() int {
 
 // Calculates the size of the largest image that can be written to each image
 // slot.
-func (t *TargetBuilder) maxImgSizes() []int {
+func (t *TargetBuilder) MaxImgSizes() []int {
 	sz0 := t.bspPkg.FlashMap.Areas[flash.FLASH_AREA_NAME_IMAGE_0].Size
 	sz1 := t.bspPkg.FlashMap.Areas[flash.FLASH_AREA_NAME_IMAGE_1].Size
 	trailerSz := t.bootTrailerSize()
@@ -846,119 +713,6 @@ func (t *TargetBuilder) maxImgSizes() []int {
 	}
 }
 
-// Verifies that each already-built image leaves enough room for a boot trailer
-// a the end of its slot.
-func (t *TargetBuilder) verifyImgSizes(li *image.Image, ai *image.Image) error {
-	maxSizes := t.maxImgSizes()
-
-	errLines := []string{}
-	if li != nil {
-		if overflow := int(li.TotalSize) - maxSizes[0]; overflow > 0 {
-			errLines = append(errLines,
-				fmt.Sprintf("loader overflows slot-0 by %d bytes "+
-					"(image=%d max=%d)",
-					overflow, li.TotalSize, maxSizes[0]))
-		}
-		if overflow := int(ai.TotalSize) - maxSizes[1]; overflow > 0 {
-			errLines = append(errLines,
-				fmt.Sprintf("app overflows slot-1 by %d bytes "+
-					"(image=%d max=%d)",
-					overflow, ai.TotalSize, maxSizes[1]))
-
-		}
-	} else {
-		if overflow := int(ai.TotalSize) - maxSizes[0]; overflow > 0 {
-			errLines = append(errLines,
-				fmt.Sprintf("app overflows slot-0 by %d bytes "+
-					"(image=%d max=%d)",
-					overflow, ai.TotalSize, maxSizes[0]))
-		}
-	}
-
-	if len(errLines) > 0 {
-		if !newtutil.NewtForce {
-			return util.NewNewtError(strings.Join(errLines, "; "))
-		} else {
-			for _, e := range errLines {
-				util.StatusMessage(util.VERBOSITY_QUIET,
-					"* Warning: %s (ignoring due to force flag)\n", e)
-			}
-		}
-	}
-
-	return nil
-}
-
-// @return                      app-image, loader-image, error
-func (t *TargetBuilder) CreateImages(version string,
-	keystrs []string, keyId uint8) (*image.Image, *image.Image, error) {
-
-	if err := t.Build(); err != nil {
-		return nil, nil, err
-	}
-
-	var err error
-	var appImg *image.Image
-	var loaderImg *image.Image
-
-	c, err := t.NewCompiler("", "")
-	if err != nil {
-		return nil, nil, err
-	}
-
-	if t.LoaderBuilder != nil {
-		loaderImg, err = t.LoaderBuilder.CreateImage(version, keystrs, keyId,
-			nil)
-		if err != nil {
-			return nil, nil, err
-		}
-		tgtArea := t.bspPkg.FlashMap.Areas[flash.FLASH_AREA_NAME_IMAGE_0]
-		log.Debugf("Convert %s -> %s at offset 0x%x",
-			t.LoaderBuilder.AppImgPath(),
-			t.LoaderBuilder.AppHexPath(),
-			tgtArea.Offset)
-		err = c.ConvertBinToHex(t.LoaderBuilder.AppImgPath(),
-			t.LoaderBuilder.AppHexPath(), tgtArea.Offset)
-		if err != nil {
-			log.Errorf("Can't convert to hexfile %s\n", err.Error())
-		}
-	}
-
-	appImg, err = t.AppBuilder.CreateImage(version, keystrs, keyId, loaderImg)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	flashTargetArea := ""
-	if t.LoaderBuilder == nil {
-		flashTargetArea = flash.FLASH_AREA_NAME_IMAGE_0
-	} else {
-		flashTargetArea = flash.FLASH_AREA_NAME_IMAGE_1
-	}
-	tgtArea := t.bspPkg.FlashMap.Areas[flashTargetArea]
-	if tgtArea.Name != "" {
-		log.Debugf("Convert %s -> %s at offset 0x%x",
-			t.AppBuilder.AppImgPath(),
-			t.AppBuilder.AppHexPath(),
-			tgtArea.Offset)
-		err = c.ConvertBinToHex(t.AppBuilder.AppImgPath(),
-			t.AppBuilder.AppHexPath(), tgtArea.Offset)
-		if err != nil {
-			log.Errorf("Can't convert to hexfile %s\n", err.Error())
-		}
-	}
-	buildId := image.CreateBuildId(appImg, loaderImg)
-	if err := t.augmentManifest(appImg, loaderImg, buildId); err != nil {
-		return nil, nil, err
-	}
-
-	if err := t.verifyImgSizes(loaderImg, appImg); err != nil {
-		return nil, nil, err
-	}
-
-	return appImg, loaderImg, nil
-}
-
 func (t *TargetBuilder) CreateDepGraph() (DepGraph, error) {
 	if err := t.ensureResolved(); err != nil {
 		return nil, err


[mynewt-newt] 17/17: 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-newt.git

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

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

diff --git a/artifact/image/image.go b/artifact/image/image.go
index 705abe6..6bd0050 100644
--- a/artifact/image/image.go
+++ b/artifact/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-newt] 09/17: Rename mimg.go to larva.go

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

commit 42266eb84c315662d3bbc033bdfc3c7d2aa2230f
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Tue Nov 27 18:47:30 2018 -0800

    Rename mimg.go to larva.go
---
 larva/{mimg.go => larva.go} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/larva/mimg.go b/larva/larva.go
similarity index 100%
rename from larva/mimg.go
rename to larva/larva.go


[mynewt-newt] 06/17: Add `imgprod` package - image production

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

commit 7cdcc26257367cd40590e59f3440e3058f82261f
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Tue Nov 20 17:51:53 2018 -0800

    Add `imgprod` package - image production
---
 newt/imgprod/imgprod.go | 286 ++++++++++++++++++++++++++++++++++++++++++++++++
 newt/imgprod/v1.go      | 218 ++++++++++++++++++++++++++++++++++++
 2 files changed, 504 insertions(+)

diff --git a/newt/imgprod/imgprod.go b/newt/imgprod/imgprod.go
new file mode 100644
index 0000000..a858c49
--- /dev/null
+++ b/newt/imgprod/imgprod.go
@@ -0,0 +1,286 @@
+/**
+ * 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.
+ */
+
+// imgprod - Image production.
+
+package imgprod
+
+import (
+	"fmt"
+	"os"
+	"strings"
+
+	"mynewt.apache.org/newt/artifact/image"
+	"mynewt.apache.org/newt/newt/builder"
+	"mynewt.apache.org/newt/newt/manifest"
+	"mynewt.apache.org/newt/newt/newtutil"
+	"mynewt.apache.org/newt/util"
+)
+
+type ImageProdOpts struct {
+	LoaderSrcFilename string
+	LoaderDstFilename string
+	AppSrcFilename    string
+	AppDstFilename    string
+	EncKeyFilename    string
+	Version           image.ImageVersion
+	SigKeys           []image.ImageSigKey
+}
+
+type ProducedImage struct {
+	Filename string
+	Image    image.Image
+	Hash     []byte
+	FileSize int
+}
+
+type ProducedImageSet struct {
+	Loader *ProducedImage
+	App    ProducedImage
+}
+
+func produceLoader(opts ImageProdOpts) (ProducedImage, error) {
+	pi := ProducedImage{}
+
+	igo := image.ImageCreateOpts{
+		SrcBinFilename:    opts.LoaderSrcFilename,
+		SrcEncKeyFilename: opts.EncKeyFilename,
+		Version:           opts.Version,
+		SigKeys:           opts.SigKeys,
+	}
+
+	ri, err := image.GenerateImage(igo)
+	if err != nil {
+		return pi, err
+	}
+
+	hash, err := ri.Hash()
+	if err != nil {
+		return pi, err
+	}
+
+	fileSize, err := ri.TotalSize()
+	if err != nil {
+		return pi, err
+	}
+
+	imgFile, err := os.OpenFile(opts.LoaderDstFilename,
+		os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+	if err != nil {
+		return pi, util.FmtNewtError(
+			"Can't open target image %s: %s",
+			opts.LoaderDstFilename, err.Error())
+	}
+	defer imgFile.Close()
+
+	if _, err := ri.Write(imgFile); err != nil {
+		return pi, err
+	}
+
+	util.StatusMessage(util.VERBOSITY_DEFAULT,
+		"Loader image successfully generated: %s\n", opts.LoaderDstFilename)
+
+	pi.Filename = opts.LoaderDstFilename
+	pi.Image = ri
+	pi.Hash = hash
+	pi.FileSize = fileSize
+
+	return pi, nil
+}
+
+func produceApp(opts ImageProdOpts, loaderHash []byte) (ProducedImage, error) {
+	pi := ProducedImage{}
+
+	igo := image.ImageCreateOpts{
+		SrcBinFilename:    opts.AppSrcFilename,
+		SrcEncKeyFilename: opts.EncKeyFilename,
+		Version:           opts.Version,
+		SigKeys:           opts.SigKeys,
+		LoaderHash:        loaderHash,
+	}
+
+	ri, err := image.GenerateImage(igo)
+	if err != nil {
+		return pi, err
+	}
+
+	hash, err := ri.Hash()
+	if err != nil {
+		return pi, err
+	}
+
+	fileSize, err := ri.TotalSize()
+	if err != nil {
+		return pi, err
+	}
+
+	imgFile, err := os.OpenFile(opts.AppDstFilename,
+		os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+	if err != nil {
+		return pi, util.FmtNewtError(
+			"Can't open target image %s: %s", opts.AppDstFilename, err.Error())
+	}
+	defer imgFile.Close()
+
+	if _, err := ri.Write(imgFile); err != nil {
+		return pi, err
+	}
+
+	util.StatusMessage(util.VERBOSITY_DEFAULT,
+		"App image successfully generated: %s\n", opts.AppDstFilename)
+
+	pi.Filename = opts.AppDstFilename
+	pi.Image = ri
+	pi.Hash = hash
+	pi.FileSize = fileSize
+
+	return pi, nil
+}
+
+// Verifies that each already-built image leaves enough room for a boot trailer
+// a the end of its slot.
+func verifyImgSizes(pset ProducedImageSet, maxSizes []int) error {
+	errLines := []string{}
+	slot := 0
+
+	if pset.Loader != nil {
+		if overflow := int(pset.Loader.FileSize) - maxSizes[0]; overflow > 0 {
+			errLines = append(errLines,
+				fmt.Sprintf("loader overflows slot-0 by %d bytes "+
+					"(image=%d max=%d)",
+					overflow, pset.Loader.FileSize, maxSizes[0]))
+		}
+		slot++
+	}
+
+	if overflow := int(pset.App.FileSize) - maxSizes[slot]; overflow > 0 {
+		errLines = append(errLines,
+			fmt.Sprintf("app overflows slot-%d by %d bytes "+
+				"(image=%d max=%d)",
+				slot, overflow, pset.App.FileSize, maxSizes[slot]))
+
+	}
+
+	if len(errLines) > 0 {
+		if !newtutil.NewtForce {
+			return util.NewNewtError(strings.Join(errLines, "; "))
+		} else {
+			for _, e := range errLines {
+				util.StatusMessage(util.VERBOSITY_QUIET,
+					"* Warning: %s (ignoring due to force flag)\n", e)
+			}
+		}
+	}
+
+	return nil
+}
+
+func ProduceImages(opts ImageProdOpts) (ProducedImageSet, error) {
+	pset := ProducedImageSet{}
+
+	var loaderHash []byte
+	if opts.LoaderSrcFilename != "" {
+		pi, err := produceLoader(opts)
+		if err != nil {
+			return pset, err
+		}
+		loaderHash = pi.Hash
+
+		pset.Loader = &pi
+	}
+
+	pi, err := produceApp(opts, loaderHash)
+	if err != nil {
+		return pset, err
+	}
+	pset.App = pi
+
+	return pset, nil
+}
+
+func ProduceManifest(opts manifest.ManifestCreateOpts) error {
+	m, err := manifest.CreateManifest(opts)
+	if err != nil {
+		return err
+	}
+
+	file, err := os.Create(opts.TgtBldr.AppBuilder.ManifestPath())
+	if err != nil {
+		return util.FmtNewtError("Cannot create manifest file %s: %s",
+			opts.TgtBldr.AppBuilder.ManifestPath(), err.Error())
+	}
+	defer file.Close()
+
+	if _, err := m.Write(file); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func OptsFromTgtBldr(b *builder.TargetBuilder, ver image.ImageVersion,
+	sigKeys []image.ImageSigKey, encKeyFilename string) ImageProdOpts {
+
+	opts := ImageProdOpts{
+		AppSrcFilename: b.AppBuilder.AppBinPath(),
+		AppDstFilename: b.AppBuilder.AppImgPath(),
+		EncKeyFilename: encKeyFilename,
+		Version:        ver,
+		SigKeys:        sigKeys,
+	}
+
+	if b.LoaderBuilder != nil {
+		opts.LoaderSrcFilename = b.LoaderBuilder.AppBinPath()
+		opts.LoaderDstFilename = b.LoaderBuilder.AppImgPath()
+	}
+
+	return opts
+}
+
+func ProduceAll(t *builder.TargetBuilder, ver image.ImageVersion,
+	sigKeys []image.ImageSigKey, encKeyFilename string) error {
+
+	popts := OptsFromTgtBldr(t, ver, sigKeys, encKeyFilename)
+	pset, err := ProduceImages(popts)
+	if err != nil {
+		return err
+	}
+
+	mopts := manifest.ManifestCreateOpts{
+		TgtBldr:    t,
+		AppHash:    pset.App.Hash,
+		Version:    ver,
+		BuildID:    fmt.Sprintf("%x", pset.App.Hash),
+		FlashAreas: t.BspPkg().FlashMap.SortedAreas(),
+	}
+
+	if pset.Loader != nil {
+		mopts.LoaderHash = pset.Loader.Hash
+	}
+
+	if err := ProduceManifest(mopts); err != nil {
+		return err
+	}
+
+	if err := verifyImgSizes(pset, mopts.TgtBldr.MaxImgSizes()); err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/newt/imgprod/v1.go b/newt/imgprod/v1.go
new file mode 100644
index 0000000..37067d4
--- /dev/null
+++ b/newt/imgprod/v1.go
@@ -0,0 +1,218 @@
+package imgprod
+
+import (
+	"fmt"
+	"os"
+	"strings"
+
+	"mynewt.apache.org/newt/artifact/image"
+	"mynewt.apache.org/newt/newt/builder"
+	"mynewt.apache.org/newt/newt/manifest"
+	"mynewt.apache.org/newt/newt/newtutil"
+	"mynewt.apache.org/newt/util"
+)
+
+type ProducedImageV1 struct {
+	Filename string
+	Image    image.ImageV1
+	Hash     []byte
+	FileSize int
+}
+
+type ProducedImageSetV1 struct {
+	Loader *ProducedImageV1
+	App    ProducedImageV1
+}
+
+func produceLoaderV1(opts ImageProdOpts) (ProducedImageV1, error) {
+	pi := ProducedImageV1{}
+
+	igo := image.ImageCreateOpts{
+		SrcBinFilename:    opts.LoaderSrcFilename,
+		SrcEncKeyFilename: opts.EncKeyFilename,
+		Version:           opts.Version,
+		SigKeys:           opts.SigKeys,
+	}
+
+	img, err := image.GenerateV1Image(igo)
+	if err != nil {
+		return pi, err
+	}
+
+	hash, err := img.Hash()
+	if err != nil {
+		return pi, err
+	}
+
+	fileSize, err := img.TotalSize()
+	if err != nil {
+		return pi, err
+	}
+
+	imgFile, err := os.OpenFile(opts.LoaderDstFilename,
+		os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+	if err != nil {
+		return pi, util.FmtNewtError(
+			"Can't open target image %s: %s",
+			opts.LoaderDstFilename, err.Error())
+	}
+	defer imgFile.Close()
+
+	if _, err := img.Write(imgFile); err != nil {
+		return pi, err
+	}
+
+	util.StatusMessage(util.VERBOSITY_DEFAULT,
+		"V1 loader image successfully generated: %s\n", opts.LoaderDstFilename)
+
+	pi.Filename = opts.LoaderDstFilename
+	pi.Image = img
+	pi.Hash = hash
+	pi.FileSize = fileSize
+
+	return pi, nil
+}
+
+func produceAppV1(opts ImageProdOpts,
+	loaderHash []byte) (ProducedImageV1, error) {
+
+	pi := ProducedImageV1{}
+
+	igo := image.ImageCreateOpts{
+		SrcBinFilename:    opts.AppSrcFilename,
+		SrcEncKeyFilename: opts.EncKeyFilename,
+		Version:           opts.Version,
+		SigKeys:           opts.SigKeys,
+		LoaderHash:        loaderHash,
+	}
+
+	img, err := image.GenerateV1Image(igo)
+	if err != nil {
+		return pi, err
+	}
+
+	hash, err := img.Hash()
+	if err != nil {
+		return pi, err
+	}
+
+	fileSize, err := img.TotalSize()
+	if err != nil {
+		return pi, err
+	}
+
+	imgFile, err := os.OpenFile(opts.AppDstFilename,
+		os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+	if err != nil {
+		return pi, util.FmtNewtError(
+			"Can't open target image %s: %s", opts.AppDstFilename, err.Error())
+	}
+	defer imgFile.Close()
+
+	if _, err := img.Write(imgFile); err != nil {
+		return pi, err
+	}
+
+	util.StatusMessage(util.VERBOSITY_DEFAULT,
+		"App image successfully generated: %s\n", opts.AppDstFilename)
+
+	pi.Filename = opts.AppDstFilename
+	pi.Image = img
+	pi.Hash = hash
+	pi.FileSize = fileSize
+
+	return pi, nil
+}
+
+// Verifies that each already-built image leaves enough room for a boot trailer
+// a the end of its slot.
+func verifyImgSizesV1(pset ProducedImageSetV1, maxSizes []int) error {
+	errLines := []string{}
+	slot := 0
+
+	if pset.Loader != nil {
+		if overflow := int(pset.Loader.FileSize) - maxSizes[0]; overflow > 0 {
+			errLines = append(errLines,
+				fmt.Sprintf("loader overflows slot-0 by %d bytes "+
+					"(image=%d max=%d)",
+					overflow, pset.Loader.FileSize, maxSizes[0]))
+		}
+		slot++
+	}
+
+	if overflow := int(pset.App.FileSize) - maxSizes[slot]; overflow > 0 {
+		errLines = append(errLines,
+			fmt.Sprintf("app overflows slot-%d by %d bytes "+
+				"(image=%d max=%d)",
+				slot, overflow, pset.App.FileSize, maxSizes[slot]))
+
+	}
+
+	if len(errLines) > 0 {
+		if !newtutil.NewtForce {
+			return util.NewNewtError(strings.Join(errLines, "; "))
+		} else {
+			for _, e := range errLines {
+				util.StatusMessage(util.VERBOSITY_QUIET,
+					"* Warning: %s (ignoring due to force flag)\n", e)
+			}
+		}
+	}
+
+	return nil
+}
+
+func ProduceImagesV1(opts ImageProdOpts) (ProducedImageSetV1, error) {
+	pset := ProducedImageSetV1{}
+
+	var loaderHash []byte
+	if opts.LoaderSrcFilename != "" {
+		pi, err := produceLoaderV1(opts)
+		if err != nil {
+			return pset, err
+		}
+		loaderHash = pi.Hash
+
+		pset.Loader = &pi
+	}
+
+	pi, err := produceAppV1(opts, loaderHash)
+	if err != nil {
+		return pset, err
+	}
+	pset.App = pi
+
+	return pset, nil
+}
+
+func ProduceAllV1(t *builder.TargetBuilder, ver image.ImageVersion,
+	sigKeys []image.ImageSigKey, encKeyFilename string) error {
+
+	popts := OptsFromTgtBldr(t, ver, sigKeys, encKeyFilename)
+	pset, err := ProduceImagesV1(popts)
+	if err != nil {
+		return err
+	}
+
+	mopts := manifest.ManifestCreateOpts{
+		TgtBldr:    t,
+		AppHash:    pset.App.Hash,
+		Version:    ver,
+		BuildID:    fmt.Sprintf("%x", pset.App.Hash),
+		FlashAreas: t.BspPkg().FlashMap.SortedAreas(),
+	}
+
+	if pset.Loader != nil {
+		mopts.LoaderHash = pset.Loader.Hash
+	}
+
+	if err := ProduceManifest(mopts); err != nil {
+		return err
+	}
+
+	if err := verifyImgSizesV1(pset, mopts.TgtBldr.MaxImgSizes()); err != nil {
+		return err
+	}
+
+	return nil
+}


[mynewt-newt] 05/17: Add `flashmap` 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-newt.git

commit 3fd440474030cbd633d57e3649279939da4fdc01
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Tue Nov 20 17:51:46 2018 -0800

    Add `flashmap` package
    
    This is a thin package implemented on top of the artifact library's
    `flash` pacakge.
---
 newt/flashmap/flashmap.go | 353 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 353 insertions(+)

diff --git a/newt/flashmap/flashmap.go b/newt/flashmap/flashmap.go
new file mode 100644
index 0000000..68fa699
--- /dev/null
+++ b/newt/flashmap/flashmap.go
@@ -0,0 +1,353 @@
+/**
+ * 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 flashmap
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/spf13/cast"
+
+	"mynewt.apache.org/newt/artifact/flash"
+	"mynewt.apache.org/newt/newt/newtutil"
+	"mynewt.apache.org/newt/util"
+)
+
+const HEADER_PATH = "sysflash/sysflash.h"
+const C_VAR_NAME = "sysflash_map_dflt"
+const C_VAR_COMMENT = `/**
+ * This flash map definition is used for two purposes:
+ * 1. To locate the meta area, which contains the true flash map definition.
+ * 2. As a fallback in case the meta area cannot be read from flash.
+ */
+`
+
+type FlashMap struct {
+	Areas       map[string]flash.FlashArea
+	Overlaps    [][]flash.FlashArea
+	IdConflicts [][]flash.FlashArea
+}
+
+func newFlashMap() FlashMap {
+	return FlashMap{
+		Areas:    map[string]flash.FlashArea{},
+		Overlaps: [][]flash.FlashArea{},
+	}
+}
+
+func flashAreaErr(areaName string, format string, args ...interface{}) error {
+	return util.NewNewtError(
+		"failure while parsing flash area \"" + areaName + "\": " +
+			fmt.Sprintf(format, args...))
+}
+
+func parseSize(val string) (int, error) {
+	lower := strings.ToLower(val)
+
+	multiplier := 1
+	if strings.HasSuffix(lower, "kb") {
+		multiplier = 1024
+		lower = strings.TrimSuffix(lower, "kb")
+	}
+
+	num, err := util.AtoiNoOct(lower)
+	if err != nil {
+		return 0, err
+	}
+
+	return num * multiplier, nil
+}
+
+func parseFlashArea(
+	name string, ymlFields map[string]interface{}) (flash.FlashArea, error) {
+
+	area := flash.FlashArea{
+		Name: name,
+	}
+
+	idPresent := false
+	devicePresent := false
+	offsetPresent := false
+	sizePresent := false
+
+	var isSystem bool
+	area.Id, isSystem = flash.SYSTEM_AREA_NAME_ID_MAP[name]
+
+	var err error
+
+	fields := cast.ToStringMapString(ymlFields)
+	for k, v := range fields {
+		switch k {
+		case "user_id":
+			if isSystem {
+				return area, flashAreaErr(name,
+					"system areas cannot specify a user ID")
+			}
+			userId, err := util.AtoiNoOct(v)
+			if err != nil {
+				return area, flashAreaErr(name, "invalid user id: %s", v)
+			}
+			area.Id = userId + flash.AREA_USER_ID_MIN
+			idPresent = true
+
+		case "device":
+			area.Device, err = util.AtoiNoOct(v)
+			if err != nil {
+				return area, flashAreaErr(name, "invalid device: %s", v)
+			}
+			devicePresent = true
+
+		case "offset":
+			area.Offset, err = util.AtoiNoOct(v)
+			if err != nil {
+				return area, flashAreaErr(name, "invalid offset: %s", v)
+			}
+			offsetPresent = true
+
+		case "size":
+			area.Size, err = parseSize(v)
+			if err != nil {
+				return area, flashAreaErr(name, err.Error())
+			}
+			sizePresent = true
+
+		default:
+			util.StatusMessage(util.VERBOSITY_QUIET,
+				"Warning: flash area \"%s\" contains unrecognized field: %s",
+				name, k)
+		}
+	}
+
+	if !isSystem && !idPresent {
+		return area, flashAreaErr(name, "required field \"user_id\" missing")
+	}
+	if !devicePresent {
+		return area, flashAreaErr(name, "required field \"device\" missing")
+	}
+	if !offsetPresent {
+		return area, flashAreaErr(name, "required field \"offset\" missing")
+	}
+	if !sizePresent {
+		return area, flashAreaErr(name, "required field \"size\" missing")
+	}
+
+	return area, nil
+}
+
+func (flashMap FlashMap) unSortedAreas() []flash.FlashArea {
+	areas := make([]flash.FlashArea, 0, len(flashMap.Areas))
+	for _, area := range flashMap.Areas {
+		areas = append(areas, area)
+	}
+
+	return areas
+}
+
+func (flashMap FlashMap) SortedAreas() []flash.FlashArea {
+	areas := flashMap.unSortedAreas()
+	return flash.SortFlashAreasById(areas)
+}
+
+func (flashMap FlashMap) DeviceIds() []int {
+	deviceMap := map[int]struct{}{}
+
+	for _, area := range flashMap.Areas {
+		deviceMap[area.Device] = struct{}{}
+	}
+
+	devices := make([]int, 0, len(deviceMap))
+	for device, _ := range deviceMap {
+		devices = append(devices, device)
+	}
+	sort.Ints(devices)
+
+	return devices
+}
+
+func areasDistinct(a flash.FlashArea, b flash.FlashArea) bool {
+	var lo flash.FlashArea
+	var hi flash.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
+}
+
+func (flashMap *FlashMap) detectOverlaps() {
+	flashMap.Overlaps, flashMap.IdConflicts =
+		flash.DetectErrors(flashMap.unSortedAreas())
+}
+
+func (flashMap FlashMap) ErrorText() string {
+	return flash.ErrorText(flashMap.Overlaps, flashMap.IdConflicts)
+}
+
+func Read(ymlFlashMap map[string]interface{}) (FlashMap, error) {
+	flashMap := newFlashMap()
+
+	ymlAreas := ymlFlashMap["areas"]
+	if ymlAreas == nil {
+		return flashMap, util.NewNewtError(
+			"\"areas\" mapping missing from flash map definition")
+	}
+
+	areaMap := cast.ToStringMap(ymlAreas)
+	for k, v := range areaMap {
+		if _, ok := flashMap.Areas[k]; ok {
+			return flashMap, flashAreaErr(k, "name conflict")
+		}
+
+		ymlArea := cast.ToStringMap(v)
+		area, err := parseFlashArea(k, ymlArea)
+		if err != nil {
+			return flashMap, flashAreaErr(k, err.Error())
+		}
+
+		flashMap.Areas[k] = area
+	}
+
+	flashMap.detectOverlaps()
+
+	return flashMap, nil
+}
+
+func flashMapVarDecl(fm FlashMap) string {
+	return fmt.Sprintf("const struct flash_area %s[%d]", C_VAR_NAME,
+		len(fm.Areas))
+}
+
+func writeFlashAreaHeader(w io.Writer, area flash.FlashArea) {
+	fmt.Fprintf(w, "#define %-40s %d\n", area.Name, area.Id)
+}
+
+func writeFlashMapHeader(w io.Writer, fm FlashMap) {
+	fmt.Fprintf(w, newtutil.GeneratedPreamble())
+
+	fmt.Fprintf(w, "#ifndef H_MYNEWT_SYSFLASH_\n")
+	fmt.Fprintf(w, "#define H_MYNEWT_SYSFLASH_\n")
+	fmt.Fprintf(w, "\n")
+	fmt.Fprintf(w, "#include \"flash_map/flash_map.h\"\n")
+	fmt.Fprintf(w, "\n")
+	fmt.Fprintf(w, "%s", C_VAR_COMMENT)
+	fmt.Fprintf(w, "extern %s;\n", flashMapVarDecl(fm))
+	fmt.Fprintf(w, "\n")
+
+	for _, area := range fm.SortedAreas() {
+		writeFlashAreaHeader(w, area)
+	}
+
+	fmt.Fprintf(w, "\n#endif\n")
+}
+
+func sizeComment(size int) string {
+	if size%1024 != 0 {
+		return ""
+	}
+
+	return fmt.Sprintf(" /* %d kB */", size/1024)
+}
+
+func writeFlashAreaSrc(w io.Writer, area flash.FlashArea) {
+	fmt.Fprintf(w, "    /* %s */\n", area.Name)
+	fmt.Fprintf(w, "    {\n")
+	fmt.Fprintf(w, "        .fa_id = %d,\n", area.Id)
+	fmt.Fprintf(w, "        .fa_device_id = %d,\n", area.Device)
+	fmt.Fprintf(w, "        .fa_off = 0x%08x,\n", area.Offset)
+	fmt.Fprintf(w, "        .fa_size = %d,%s\n", area.Size,
+		sizeComment(area.Size))
+	fmt.Fprintf(w, "    },\n")
+}
+
+func writeFlashMapSrc(w io.Writer, fm FlashMap) {
+	fmt.Fprintf(w, newtutil.GeneratedPreamble())
+
+	fmt.Fprintf(w, "#include \"%s\"\n", HEADER_PATH)
+	fmt.Fprintf(w, "\n")
+	fmt.Fprintf(w, "%s", C_VAR_COMMENT)
+	fmt.Fprintf(w, "%s = {", flashMapVarDecl(fm))
+
+	for _, area := range fm.SortedAreas() {
+		fmt.Fprintf(w, "\n")
+		writeFlashAreaSrc(w, area)
+	}
+
+	fmt.Fprintf(w, "};\n")
+}
+
+func ensureFlashMapWrittenGen(path string, contents []byte) error {
+	writeReqd, err := util.FileContentsChanged(path, contents)
+	if err != nil {
+		return err
+	}
+	if !writeReqd {
+		log.Debugf("flash map unchanged; not writing file (%s).", path)
+		return nil
+	}
+
+	log.Debugf("flash map changed; writing file (%s).", path)
+
+	if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
+		return util.NewNewtError(err.Error())
+	}
+
+	if err := ioutil.WriteFile(path, contents, 0644); err != nil {
+		return util.NewNewtError(err.Error())
+	}
+
+	return nil
+}
+
+func EnsureFlashMapWritten(
+	fm FlashMap,
+	srcDir string,
+	includeDir string,
+	targetName string) error {
+
+	buf := bytes.Buffer{}
+	writeFlashMapSrc(&buf, fm)
+	if err := ensureFlashMapWrittenGen(
+		fmt.Sprintf("%s/%s-sysflash.c", srcDir, targetName),
+		buf.Bytes()); err != nil {
+
+		return err
+	}
+
+	buf = bytes.Buffer{}
+	writeFlashMapHeader(&buf, fm)
+	if err := ensureFlashMapWrittenGen(
+		includeDir+"/"+HEADER_PATH, buf.Bytes()); err != nil {
+		return err
+	}
+
+	return nil
+}


[mynewt-newt] 07/17: Add `manifest` package - manifest generation

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

commit 8e570b87e88c3d4888ebf69b9b6ff6da2cf354b7
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Tue Nov 20 17:52:09 2018 -0800

    Add `manifest` package - manifest generation
---
 newt/manifest/manifest.go | 305 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 305 insertions(+)

diff --git a/newt/manifest/manifest.go b/newt/manifest/manifest.go
new file mode 100644
index 0000000..7c9eaff
--- /dev/null
+++ b/newt/manifest/manifest.go
@@ -0,0 +1,305 @@
+/**
+ * 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.
+ */
+
+// imgprod - Manifest generation.
+
+package manifest
+
+import (
+	"fmt"
+	"os"
+	"sort"
+	"strings"
+	"time"
+
+	log "github.com/Sirupsen/logrus"
+
+	"mynewt.apache.org/newt/artifact/flash"
+	"mynewt.apache.org/newt/artifact/image"
+	"mynewt.apache.org/newt/artifact/manifest"
+	"mynewt.apache.org/newt/newt/builder"
+	"mynewt.apache.org/newt/newt/pkg"
+	"mynewt.apache.org/newt/newt/syscfg"
+	"mynewt.apache.org/newt/util"
+)
+
+type ManifestSizeCollector struct {
+	Pkgs []*manifest.ManifestSizePkg
+}
+
+type ManifestCreateOpts struct {
+	TgtBldr    *builder.TargetBuilder
+	LoaderHash []byte
+	AppHash    []byte
+	Version    image.ImageVersion
+	BuildID    string
+	FlashAreas []flash.FlashArea
+}
+
+type RepoManager struct {
+	repos map[string]manifest.ManifestRepo
+}
+
+func NewRepoManager() *RepoManager {
+	return &RepoManager{
+		repos: make(map[string]manifest.ManifestRepo),
+	}
+}
+
+func (r *RepoManager) AllRepos() []*manifest.ManifestRepo {
+	keys := make([]string, 0, len(r.repos))
+	for k := range r.repos {
+		keys = append(keys, k)
+	}
+
+	sort.Strings(keys)
+
+	repos := make([]*manifest.ManifestRepo, 0, len(keys))
+	for _, key := range keys {
+		r := r.repos[key]
+		repos = append(repos, &r)
+	}
+
+	return repos
+}
+
+func (c *ManifestSizeCollector) AddPkg(pkg string) *manifest.ManifestSizePkg {
+	p := &manifest.ManifestSizePkg{
+		Name: pkg,
+	}
+	c.Pkgs = append(c.Pkgs, p)
+
+	return p
+}
+
+func AddSymbol(p *manifest.ManifestSizePkg, file string, sym string, area string,
+	symSz uint32) {
+
+	f := addFile(p, file)
+	s := addSym(f, sym)
+	addArea(s, area, symSz)
+}
+
+func addFile(p *manifest.ManifestSizePkg, file string) *manifest.ManifestSizeFile {
+	for _, f := range p.Files {
+		if f.Name == file {
+			return f
+		}
+	}
+	f := &manifest.ManifestSizeFile{
+		Name: file,
+	}
+	p.Files = append(p.Files, f)
+
+	return f
+}
+
+func addSym(f *manifest.ManifestSizeFile, sym string) *manifest.ManifestSizeSym {
+	s := &manifest.ManifestSizeSym{
+		Name: sym,
+	}
+	f.Syms = append(f.Syms, s)
+
+	return s
+}
+
+func addArea(s *manifest.ManifestSizeSym, area string, areaSz uint32) {
+	a := &manifest.ManifestSizeArea{
+		Name: area,
+		Size: areaSz,
+	}
+	s.Areas = append(s.Areas, a)
+}
+
+func (r *RepoManager) GetManifestPkg(
+	lpkg *pkg.LocalPackage) *manifest.ManifestPkg {
+
+	ip := &manifest.ManifestPkg{
+		Name: lpkg.FullName(),
+	}
+
+	var path string
+	if lpkg.Repo().IsLocal() {
+		ip.Repo = lpkg.Repo().Name()
+		path = lpkg.BasePath()
+	} else {
+		ip.Repo = lpkg.Repo().Name()
+		path = lpkg.BasePath()
+	}
+
+	if _, present := r.repos[ip.Repo]; present {
+		return ip
+	}
+
+	repo := manifest.ManifestRepo{
+		Name: ip.Repo,
+	}
+
+	// Make sure we restore the current working dir to whatever it was when
+	// this function was called
+	cwd, err := os.Getwd()
+	if err != nil {
+		log.Debugf("Unable to determine current working directory: %v", err)
+		return ip
+	}
+	defer os.Chdir(cwd)
+
+	if err := os.Chdir(path); err != nil {
+		return ip
+	}
+
+	var res []byte
+
+	res, err = util.ShellCommand([]string{
+		"git",
+		"rev-parse",
+		"HEAD",
+	}, nil)
+	if err != nil {
+		log.Debugf("Unable to determine commit hash for %s: %v", path, err)
+		repo.Commit = "UNKNOWN"
+	} else {
+		repo.Commit = strings.TrimSpace(string(res))
+		res, err = util.ShellCommand([]string{
+			"git",
+			"status",
+			"--porcelain",
+		}, nil)
+		if err != nil {
+			log.Debugf("Unable to determine dirty state for %s: %v", path, err)
+		} else {
+			if len(res) > 0 {
+				repo.Dirty = true
+			}
+		}
+		res, err = util.ShellCommand([]string{
+			"git",
+			"config",
+			"--get",
+			"remote.origin.url",
+		}, nil)
+		if err != nil {
+			log.Debugf("Unable to determine URL for %s: %v", path, err)
+		} else {
+			repo.URL = strings.TrimSpace(string(res))
+		}
+	}
+	r.repos[ip.Repo] = repo
+
+	return ip
+}
+
+func ManifestPkgSizes(b *builder.Builder) (ManifestSizeCollector, error) {
+	msc := ManifestSizeCollector{}
+
+	libs, err := builder.ParseMapFileSizes(b.AppMapPath())
+	if err != nil {
+		return msc, err
+	}
+
+	// Order libraries by name.
+	pkgSizes := make(builder.PkgSizeArray, len(libs))
+	i := 0
+	for _, es := range libs {
+		pkgSizes[i] = es
+		i++
+	}
+	sort.Sort(pkgSizes)
+
+	for _, es := range pkgSizes {
+		p := msc.AddPkg(b.FindPkgNameByArName(es.Name))
+
+		// Order symbols by name.
+		symbols := make(builder.SymbolDataArray, len(es.Syms))
+		i := 0
+		for _, sym := range es.Syms {
+			symbols[i] = sym
+			i++
+		}
+		sort.Sort(symbols)
+		for _, sym := range symbols {
+			for area, areaSz := range sym.Sizes {
+				if areaSz != 0 {
+					AddSymbol(p, sym.ObjName, sym.Name, area, areaSz)
+				}
+			}
+		}
+	}
+
+	return msc, nil
+}
+
+func CreateManifest(opts ManifestCreateOpts) (manifest.Manifest, error) {
+	t := opts.TgtBldr
+
+	m := manifest.Manifest{
+		Name:       t.GetTarget().FullName(),
+		Date:       time.Now().Format(time.RFC3339),
+		Version:    opts.Version.String(),
+		BuildID:    opts.BuildID,
+		Image:      t.AppBuilder.AppImgPath(),
+		ImageHash:  fmt.Sprintf("%x", opts.AppHash),
+		FlashAreas: opts.FlashAreas,
+	}
+
+	rm := NewRepoManager()
+	for _, rpkg := range t.AppBuilder.SortedRpkgs() {
+		m.Pkgs = append(m.Pkgs, rm.GetManifestPkg(rpkg.Lpkg))
+	}
+
+	m.Repos = rm.AllRepos()
+
+	vars := t.GetTarget().TargetY.AllSettingsAsStrings()
+	keys := make([]string, 0, len(vars))
+	for k := range vars {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+	for _, k := range keys {
+		m.TgtVars = append(m.TgtVars, k+"="+vars[k])
+	}
+	syscfgKV := t.GetTarget().Package().SyscfgY.GetValStringMapString(
+		"syscfg.vals", nil)
+	if len(syscfgKV) > 0 {
+		tgtSyscfg := fmt.Sprintf("target.syscfg=%s",
+			syscfg.KeyValueToStr(syscfgKV))
+		m.TgtVars = append(m.TgtVars, tgtSyscfg)
+	}
+
+	c, err := ManifestPkgSizes(t.AppBuilder)
+	if err == nil {
+		m.PkgSizes = c.Pkgs
+	}
+
+	if t.LoaderBuilder != nil {
+		m.Loader = t.LoaderBuilder.AppImgPath()
+		m.LoaderHash = fmt.Sprintf("%x", opts.LoaderHash)
+
+		for _, rpkg := range t.LoaderBuilder.SortedRpkgs() {
+			m.LoaderPkgs = append(m.LoaderPkgs, rm.GetManifestPkg(rpkg.Lpkg))
+		}
+
+		c, err = ManifestPkgSizes(t.LoaderBuilder)
+		if err == nil {
+			m.LoaderPkgSizes = c.Pkgs
+		}
+	}
+
+	return m, nil
+}


[mynewt-newt] 11/17: 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-newt.git

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

    Support for the 2.0 mfgimage format
---
 artifact/image/create.go                  | 101 ++----
 artifact/image/image.go                   |   6 +
 artifact/image/key.go                     |  19 +-
 artifact/image/v1.go                      |   2 +-
 artifact/manifest/manifest.go             |   3 +-
 artifact/manifest/mfg_manifest.go         |  73 ++++
 artifact/mfg/map_meta.go                  | 153 ++++++++
 artifact/mfg/meta.go                      | 352 +++++++++++++++++++
 artifact/mfg/mfg.go                       | 113 ++++++
 newt/mfg/read.go => artifact/mfg/paths.go |  23 +-
 larva/cli/image_cmds.go                   | 341 ++++++++++++++++--
 larva/cli/mfg_cmds.go                     | 189 +++++++---
 larva/lvimg/lvimg.go                      | 116 ++++++
 larva/{mfg/mfg.go => lvmfg/lvmfg.go}      | 111 +++---
 newt/builder/targetbuild.go               |   1 +
 newt/cli/build_cmds.go                    |  11 +
 newt/cli/mfg_cmds.go                      |  56 ++-
 newt/flashmap/flashmap.go                 |  17 -
 newt/imgprod/imgprod.go                   |  14 +-
 newt/imgprod/v1.go                        |   9 +-
 newt/manifest/manifest.go                 |  47 ++-
 newt/mfg/build.go                         | 564 ++++++++++++++++++++++++++++++
 newt/mfg/create.go                        | 535 ----------------------------
 newt/mfg/decode.go                        | 310 ++++++++++++++++
 newt/mfg/emit.go                          | 342 ++++++++++++++++++
 newt/mfg/load.go                          | 317 -----------------
 newt/mfg/meta.go                          | 248 -------------
 newt/mfg/mfg.go                           |  98 ------
 newt/mfg/misc.go                          |  92 +++++
 newt/mfg/part.go                          |  96 +++++
 newt/mfg/paths.go                         | 154 +-------
 31 files changed, 2905 insertions(+), 1608 deletions(-)

diff --git a/artifact/image/create.go b/artifact/image/create.go
index 5b28120..c3e8820 100644
--- a/artifact/image/create.go
+++ b/artifact/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/artifact/image/image.go b/artifact/image/image.go
index 244cfed..1ed093f 100644
--- a/artifact/image/image.go
+++ b/artifact/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/artifact/image/key.go b/artifact/image/key.go
index 9141f6e..8345cd9 100644
--- a/artifact/image/key.go
+++ b/artifact/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/artifact/image/v1.go b/artifact/image/v1.go
index bab86f6..5540d85 100644
--- a/artifact/image/v1.go
+++ b/artifact/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/artifact/manifest/manifest.go b/artifact/manifest/manifest.go
index 62d8c06..3a3376a 100644
--- a/artifact/manifest/manifest.go
+++ b/artifact/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/artifact/manifest/mfg_manifest.go b/artifact/manifest/mfg_manifest.go
new file mode 100644
index 0000000..25cf9b7
--- /dev/null
+++ b/artifact/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/artifact/mfg/map_meta.go b/artifact/mfg/map_meta.go
new file mode 100644
index 0000000..b16c37e
--- /dev/null
+++ b/artifact/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/artifact/mfg/meta.go b/artifact/mfg/meta.go
new file mode 100644
index 0000000..4521a89
--- /dev/null
+++ b/artifact/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/artifact/mfg/mfg.go b/artifact/mfg/mfg.go
new file mode 100644
index 0000000..8e999ad
--- /dev/null
+++ b/artifact/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/newt/mfg/read.go b/artifact/mfg/paths.go
similarity index 64%
rename from newt/mfg/read.go
rename to artifact/mfg/paths.go
index 04520db..483aca2 100644
--- a/newt/mfg/read.go
+++ b/artifact/mfg/paths.go
@@ -20,21 +20,16 @@
 package mfg
 
 import (
-	"strings"
-
-	"mynewt.apache.org/newt/newt/builder"
+	"fmt"
+	"path/filepath"
 )
 
-// @return						mfg-image-path, error
-func (mi *MfgImage) Upload() (string, error) {
-	// For now, we always upload section 0 only.
-	section0Path := MfgSectionBinPath(mi.basePkg.Name(), 0)
-	baseName := strings.TrimSuffix(section0Path, ".bin")
-
-	envSettings := map[string]string{"MFG_IMAGE": "1"}
-	if err := builder.Load(baseName, mi.bsp, envSettings); err != nil {
-		return "", err
-	}
+const MANIFEST_FILENAME = "manifest.json"
+const BOOT_DIR = "bootloader"
+const BOOT_MANIFEST_PATH = BOOT_DIR + "/manifest.json"
+const SECTION_BIN_DIR = "sections"
 
-	return section0Path, nil
+func SectionBinPath(mfgPkgName string, sectionNum int) string {
+	return fmt.Sprintf("%s/%s-s%d.bin", SECTION_BIN_DIR,
+		filepath.Base(mfgPkgName), sectionNum)
 }
diff --git a/larva/cli/image_cmds.go b/larva/cli/image_cmds.go
index ce1093e..cdcac33 100644
--- a/larva/cli/image_cmds.go
+++ b/larva/cli/image_cmds.go
@@ -20,16 +20,26 @@
 package cli
 
 import (
-	"encoding/hex"
+	"encoding/binary"
 	"fmt"
+	"io/ioutil"
+	"os"
+	"sort"
 
 	log "github.com/Sirupsen/logrus"
 	"github.com/spf13/cobra"
 
 	"mynewt.apache.org/newt/artifact/image"
+	"mynewt.apache.org/newt/larva/lvimg"
 	"mynewt.apache.org/newt/util"
 )
 
+func tlvStr(tlv image.ImageTlv) string {
+	return fmt.Sprintf("%s,0x%02x",
+		image.ImageTlvTypeName(tlv.Header.Type),
+		tlv.Header.Type)
+}
+
 func readImage(filename string) (image.Image, error) {
 	img, err := image.ReadImage(filename)
 	if err != nil {
@@ -41,35 +51,39 @@ func readImage(filename string) (image.Image, error) {
 }
 
 func writeImage(img image.Image, filename string) error {
+	if err := lvimg.VerifyImage(img); err != nil {
+		return err
+	}
+
 	if err := img.WriteToFile(filename); err != nil {
 		return err
 	}
 
-	log.Debugf("Wrote image %s", filename)
+	util.StatusMessage(util.VERBOSITY_DEFAULT, "Wrote image %s\n", filename)
 	return nil
 }
 
-func reportDupSigs(img image.Image) {
-	m := map[string]struct{}{}
-	dups := map[string]struct{}{}
-
-	for _, tlv := range img.Tlvs {
-		if tlv.Header.Type == image.IMAGE_TLV_KEYHASH {
-			h := hex.EncodeToString(tlv.Data)
-			if _, ok := m[h]; ok {
-				dups[h] = struct{}{}
-			} else {
-				m[h] = struct{}{}
-			}
-		}
+func parseTlvArgs(typeArg string, filenameArg string) (image.ImageTlv, error) {
+	tlvType, err := util.AtoiNoOct(typeArg)
+	if err != nil || tlvType < 0 {
+		return image.ImageTlv{}, util.FmtNewtError(
+			"Invalid TLV type integer: %s", typeArg)
 	}
 
-	if len(dups) > 0 {
-		fmt.Printf("Warning: duplicate signatures detected:\n")
-		for d, _ := range dups {
-			fmt.Printf("    %s\n", d)
-		}
+	data, err := ioutil.ReadFile(filenameArg)
+	if err != nil {
+		return image.ImageTlv{}, util.FmtNewtError(
+			"Error reading TLV data file: %s", err.Error())
 	}
+
+	return image.ImageTlv{
+		Header: image.ImageTlvHdr{
+			Type: uint8(tlvType),
+			Pad:  0,
+			Len:  uint16(len(data)),
+		},
+		Data: data,
+	}, nil
 }
 
 func runShowCmd(cmd *cobra.Command, args []string) {
@@ -89,6 +103,32 @@ func runShowCmd(cmd *cobra.Command, args []string) {
 	fmt.Printf("%s\n", s)
 }
 
+func runBriefCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 1 {
+		LarvaUsage(cmd, nil)
+	}
+
+	img, err := readImage(args[0])
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	offsets, err := img.Offsets()
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	fmt.Printf("%8d| Header\n", offsets.Header)
+	fmt.Printf("%8d| Body\n", offsets.Body)
+	fmt.Printf("%8d| Trailer\n", offsets.Trailer)
+	for i, tlv := range img.Tlvs {
+		fmt.Printf("%8d| TLV%d: type=%s(%d)\n",
+			offsets.Tlvs[i], i, image.ImageTlvTypeName(tlv.Header.Type),
+			tlv.Header.Type)
+	}
+	fmt.Printf("Total=%d\n", offsets.TotalSize)
+}
+
 func runSignCmd(cmd *cobra.Command, args []string) {
 	if len(args) < 2 {
 		LarvaUsage(cmd, nil)
@@ -116,14 +156,106 @@ func runSignCmd(cmd *cobra.Command, args []string) {
 			"Failed to read hash from specified image: %s", err.Error()))
 	}
 
-	tlvs, err := image.GenerateSigTlvs(keys, hash)
+	tlvs, err := image.BuildSigTlvs(keys, hash)
 	if err != nil {
 		LarvaUsage(nil, err)
 	}
 
 	img.Tlvs = append(img.Tlvs, tlvs...)
 
-	reportDupSigs(img)
+	if err := writeImage(img, outFilename); err != nil {
+		LarvaUsage(nil, err)
+	}
+}
+
+func runAddTlvsCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 3 {
+		LarvaUsage(cmd, nil)
+	}
+
+	inFilename := args[0]
+	outFilename, err := CalcOutFilename(inFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	img, err := readImage(inFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	tlvArgs := args[1:]
+	if len(tlvArgs)%2 != 0 {
+		LarvaUsage(cmd, util.FmtNewtError(
+			"Invalid argument count; each TLV requires two arguments"))
+	}
+
+	tlvs := []image.ImageTlv{}
+	for i := 0; i < len(tlvArgs); i += 2 {
+		tlv, err := parseTlvArgs(tlvArgs[i], tlvArgs[i+1])
+		if err != nil {
+			LarvaUsage(cmd, err)
+		}
+
+		tlvs = append(tlvs, tlv)
+	}
+
+	img.Tlvs = append(img.Tlvs, tlvs...)
+
+	if err := writeImage(img, outFilename); err != nil {
+		LarvaUsage(nil, err)
+	}
+}
+
+func runRmtlvsCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 2 {
+		LarvaUsage(cmd, nil)
+	}
+
+	inFilename := args[0]
+	outFilename, err := CalcOutFilename(inFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	img, err := readImage(inFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	tlvIndices := []int{}
+	idxMap := map[int]struct{}{}
+	for _, arg := range args[1:] {
+		idx, err := util.AtoiNoOct(arg)
+		if err != nil {
+			LarvaUsage(cmd, util.FmtNewtError("Invalid TLV index: %s", arg))
+		}
+
+		if idx < 0 || idx >= len(img.Tlvs) {
+			LarvaUsage(nil, util.FmtNewtError(
+				"TLV index %s out of range; "+
+					"must be in range [0, %d] for this image",
+				arg, len(img.Tlvs)-1))
+		}
+
+		if _, ok := idxMap[idx]; ok {
+			LarvaUsage(nil, util.FmtNewtError(
+				"TLV index %d specified more than once", idx))
+		}
+		idxMap[idx] = struct{}{}
+
+		tlvIndices = append(tlvIndices, idx)
+	}
+
+	// Remove TLVs in reverse order to preserve index mapping.
+	sort.Sort(sort.Reverse(sort.IntSlice(tlvIndices)))
+	for _, idx := range tlvIndices {
+		tlv := img.Tlvs[idx]
+		util.StatusMessage(util.VERBOSITY_DEFAULT,
+			"Removing TLV%d: %s\n", idx, tlvStr(tlv))
+
+		img.Tlvs = append(img.Tlvs[0:idx], img.Tlvs[idx+1:]...)
+	}
 
 	if err := writeImage(img, outFilename); err != nil {
 		LarvaUsage(nil, err)
@@ -160,6 +292,113 @@ func runRmsigsCmd(cmd *cobra.Command, args []string) {
 	}
 }
 
+func runHashableCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 1 {
+		LarvaUsage(cmd, nil)
+	}
+
+	if OptOutFilename == "" {
+		LarvaUsage(cmd, util.FmtNewtError("--outfile (-o) option required"))
+	}
+
+	inFilename := args[0]
+	outFilename := OptOutFilename
+
+	img, err := readImage(inFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	f, err := os.Create(outFilename)
+	if err != nil {
+		LarvaUsage(nil, util.ChildNewtError(err))
+	}
+	defer f.Close()
+
+	if err := binary.Write(f, binary.LittleEndian, &img.Header); err != nil {
+		LarvaUsage(nil, util.FmtNewtError(
+			"Error writing image header: %s", err.Error()))
+	}
+	_, err = f.Write(img.Body)
+	if err != nil {
+		LarvaUsage(nil, util.FmtNewtError(
+			"Error writing image body: %s", err.Error()))
+	}
+
+	util.StatusMessage(util.VERBOSITY_DEFAULT,
+		"Wrote hashable content to %s\n", outFilename)
+}
+
+func runAddsigCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 4 {
+		LarvaUsage(cmd, nil)
+	}
+
+	imgFilename := args[0]
+	keyFilename := args[1]
+	sigFilename := args[2]
+
+	sigType, err := util.AtoiNoOct(args[3])
+	if err != nil || sigType < 0 || sigType > 255 ||
+		!image.ImageTlvTypeIsSig(uint8(sigType)) {
+
+		LarvaUsage(cmd, util.FmtNewtError(
+			"Invalid signature type: %s", args[3]))
+	}
+
+	outFilename, err := CalcOutFilename(imgFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	img, err := readImage(imgFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	keyData, err := ioutil.ReadFile(keyFilename)
+	if err != nil {
+		LarvaUsage(cmd, util.FmtNewtError(
+			"Error reading key file: %s", err.Error()))
+	}
+
+	sigData, err := ioutil.ReadFile(sigFilename)
+	if err != nil {
+		LarvaUsage(cmd, util.FmtNewtError(
+			"Error reading signature file: %s", err.Error()))
+	}
+
+	// ECDSA256 signatures need to be padded out to >=72 bytes.
+	if sigType == image.IMAGE_TLV_ECDSA256 {
+		sigData, err = lvimg.PadEcdsa256Sig(sigData)
+		if err != nil {
+			LarvaUsage(nil, err)
+		}
+	}
+
+	// Build and append key hash TLV.
+	keyHashTlv := image.BuildKeyHashTlv(keyData)
+	util.StatusMessage(util.VERBOSITY_DEFAULT, "Adding TLV%d (%s)\n",
+		len(img.Tlvs), tlvStr(keyHashTlv))
+	img.Tlvs = append(img.Tlvs, keyHashTlv)
+
+	// Build and append signature TLV.
+	sigTlv := image.ImageTlv{
+		Header: image.ImageTlvHdr{
+			Type: uint8(sigType),
+			Len:  uint16(len(sigData)),
+		},
+		Data: sigData,
+	}
+	util.StatusMessage(util.VERBOSITY_DEFAULT, "Adding TLV%d (%s)\n",
+		len(img.Tlvs), tlvStr(sigTlv))
+	img.Tlvs = append(img.Tlvs, sigTlv)
+
+	if err := writeImage(img, outFilename); err != nil {
+		LarvaUsage(nil, err)
+	}
+}
+
 func AddImageCommands(cmd *cobra.Command) {
 	imageCmd := &cobra.Command{
 		Use:   "image",
@@ -177,6 +416,13 @@ func AddImageCommands(cmd *cobra.Command) {
 	}
 	imageCmd.AddCommand(showCmd)
 
+	briefCmd := &cobra.Command{
+		Use:   "brief <img-file>",
+		Short: "Displays brief text description of a Mynewt image file",
+		Run:   runBriefCmd,
+	}
+	imageCmd.AddCommand(briefCmd)
+
 	signCmd := &cobra.Command{
 		Use:   "sign <img-file> <priv-key-pem> [priv-key-pem...]",
 		Short: "Appends signatures to a Mynewt image file",
@@ -190,6 +436,33 @@ func AddImageCommands(cmd *cobra.Command) {
 
 	imageCmd.AddCommand(signCmd)
 
+	addtlvsCmd := &cobra.Command{
+		Use: "addtlvs <img-file> <tlv-type> <data-filename> " +
+			"[tlv-type] [data-filename] [...]",
+		Short: "Adds the specified TLVs to a Mynewt image file",
+		Run:   runAddTlvsCmd,
+	}
+
+	addtlvsCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o", "",
+		"File to write to")
+	addtlvsCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+		"Replace input file")
+
+	imageCmd.AddCommand(addtlvsCmd)
+
+	rmtlvsCmd := &cobra.Command{
+		Use:   "rmtlvs <img-file> <tlv-index> [tlv-index] [...]",
+		Short: "Removes the specified TLVs from a Mynewt image file",
+		Run:   runRmtlvsCmd,
+	}
+
+	rmtlvsCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o", "",
+		"File to write to")
+	rmtlvsCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+		"Replace input file")
+
+	imageCmd.AddCommand(rmtlvsCmd)
+
 	rmsigsCmd := &cobra.Command{
 		Use:   "rmsigs",
 		Short: "Removes all signatures from a Mynewt image file",
@@ -202,4 +475,28 @@ func AddImageCommands(cmd *cobra.Command) {
 		"Replace input file")
 
 	imageCmd.AddCommand(rmsigsCmd)
+
+	hashableCmd := &cobra.Command{
+		Use:   "hashable <img-file>",
+		Short: "Removes all signatures from a Mynewt image file",
+		Run:   runHashableCmd,
+	}
+
+	hashableCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o",
+		"", "File to write to")
+
+	imageCmd.AddCommand(hashableCmd)
+
+	addsigCmd := &cobra.Command{
+		Use:   "addsig <image> <pub-key-der> <sig-der> <sig-tlv-type>",
+		Short: "Adds a signature to a Mynewt image file",
+		Run:   runAddsigCmd,
+	}
+
+	addsigCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o",
+		"", "File to write to")
+	addsigCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+		"Replace input file")
+
+	imageCmd.AddCommand(addsigCmd)
 }
diff --git a/larva/cli/mfg_cmds.go b/larva/cli/mfg_cmds.go
index ad201cc..1a90b12 100644
--- a/larva/cli/mfg_cmds.go
+++ b/larva/cli/mfg_cmds.go
@@ -29,36 +29,39 @@ import (
 
 	"mynewt.apache.org/newt/artifact/flash"
 	"mynewt.apache.org/newt/artifact/manifest"
-	"mynewt.apache.org/newt/larva/mfg"
+	"mynewt.apache.org/newt/artifact/mfg"
+	"mynewt.apache.org/newt/larva/lvmfg"
 	"mynewt.apache.org/newt/util"
 )
 
-var optDeviceNum int
-
-func readManifest(filename string) (manifest.Manifest, error) {
-	man, err := manifest.ReadManifest(filename)
+func readMfgBin(filename string) ([]byte, error) {
+	bin, err := ioutil.ReadFile(filename)
 	if err != nil {
-		return man, err
+		return nil, util.FmtNewtError(
+			"Failed to read manufacturing image: %s", err.Error())
 	}
 
-	log.Debugf("Successfully read manifest %s", filename)
-	return man, nil
+	return bin, nil
 }
 
-func readFlashAreas(manifestFilename string) ([]flash.FlashArea, error) {
-	man, err := readManifest(manifestFilename)
-	if err != nil {
-		return nil, err
-	}
+func readManifest(mfgDir string) (manifest.MfgManifest, error) {
+	return manifest.ReadMfgManifest(mfgDir + "/" + mfg.MANIFEST_FILENAME)
+}
 
-	areas := flash.SortFlashAreasByDevOff(man.FlashAreas)
+func extractFlashAreas(mman manifest.MfgManifest) ([]flash.FlashArea, error) {
+	areas := flash.SortFlashAreasByDevOff(mman.FlashAreas)
+
+	if len(areas) == 0 {
+		LarvaUsage(nil, util.FmtNewtError(
+			"Boot loader manifest does not contain flash map"))
+	}
 
 	overlaps, conflicts := flash.DetectErrors(areas)
 	if len(overlaps) > 0 || len(conflicts) > 0 {
 		return nil, util.NewNewtError(flash.ErrorText(overlaps, conflicts))
 	}
 
-	if err := mfg.VerifyAreas(areas, optDeviceNum); err != nil {
+	if err := lvmfg.VerifyAreas(areas); err != nil {
 		return nil, err
 	}
 
@@ -66,8 +69,10 @@ func readFlashAreas(manifestFilename string) ([]flash.FlashArea, error) {
 	return areas, nil
 }
 
-func createMfgMap(binDir string, areas []flash.FlashArea) (mfg.MfgMap, error) {
-	mm := mfg.MfgMap{}
+func createNameBlobMap(binDir string,
+	areas []flash.FlashArea) (lvmfg.NameBlobMap, error) {
+
+	mm := lvmfg.NameBlobMap{}
 
 	for _, area := range areas {
 		filename := fmt.Sprintf("%s/%s.bin", binDir, area.Name)
@@ -84,27 +89,68 @@ func createMfgMap(binDir string, areas []flash.FlashArea) (mfg.MfgMap, error) {
 	return mm, nil
 }
 
-func runSplitCmd(cmd *cobra.Command, args []string) {
-	if len(args) < 3 {
+func runMfgShowCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 2 {
 		LarvaUsage(cmd, nil)
 	}
+	inFilename := args[0]
 
-	imgFilename := args[0]
-	manFilename := args[1]
-	outDir := args[2]
-
-	mfgBin, err := ioutil.ReadFile(imgFilename)
+	metaEndOff, err := util.AtoiNoOct(args[1])
 	if err != nil {
 		LarvaUsage(cmd, util.FmtNewtError(
-			"Failed to read manufacturing image: %s", err.Error()))
+			"invalid meta offset \"%s\"", args[1]))
+	}
+
+	bin, err := readMfgBin(inFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	m, err := mfg.Parse(bin, metaEndOff, 0xff)
+	if err != nil {
+		LarvaUsage(nil, err)
 	}
 
-	areas, err := readFlashAreas(manFilename)
+	if m.Meta == nil {
+		util.StatusMessage(util.VERBOSITY_DEFAULT,
+			"Manufacturing image %s does not contain an MMR\n", inFilename)
+	} else {
+		s, err := m.Meta.Json(metaEndOff)
+		if err != nil {
+			LarvaUsage(nil, err)
+		}
+		util.StatusMessage(util.VERBOSITY_DEFAULT,
+			"Manufacturing image %s contains an MMR with "+
+				"the following properties:\n%s\n", inFilename, s)
+	}
+}
+
+func runSplitCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 2 {
+		LarvaUsage(cmd, nil)
+	}
+
+	mfgDir := args[0]
+	outDir := args[1]
+
+	mm, err := readManifest(mfgDir)
 	if err != nil {
 		LarvaUsage(cmd, err)
 	}
 
-	mm, err := mfg.Split(mfgBin, optDeviceNum, areas)
+	areas, err := extractFlashAreas(mm)
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	binPath := fmt.Sprintf("%s/%s", mfgDir, mm.BinPath)
+	bin, err := ioutil.ReadFile(binPath)
+	if err != nil {
+		LarvaUsage(cmd, util.FmtNewtError(
+			"Failed to read \"%s\": %s", binPath, err.Error()))
+	}
+
+	nbmap, err := lvmfg.Split(bin, mm.Device, areas, 0xff)
 	if err != nil {
 		LarvaUsage(nil, err)
 	}
@@ -113,39 +159,88 @@ func runSplitCmd(cmd *cobra.Command, args []string) {
 		LarvaUsage(nil, util.ChildNewtError(err))
 	}
 
-	for name, data := range mm {
+	for name, data := range nbmap {
 		filename := fmt.Sprintf("%s/%s.bin", outDir, name)
-		if err := ioutil.WriteFile(filename, data, os.ModePerm); err != nil {
+		if err := ioutil.WriteFile(filename, data,
+			os.ModePerm); err != nil {
+
 			LarvaUsage(nil, util.ChildNewtError(err))
 		}
 	}
+
+	mfgDstDir := fmt.Sprintf("%s/mfg", outDir)
+	util.StatusMessage(util.VERBOSITY_DEFAULT,
+		"Copying source mfg directory to %s\n", mfgDstDir)
+	if err := util.CopyDir(mfgDir, mfgDstDir); err != nil {
+		LarvaUsage(nil, err)
+	}
 }
 
 func runJoinCmd(cmd *cobra.Command, args []string) {
-	if len(args) < 3 {
+	if len(args) < 2 {
 		LarvaUsage(cmd, nil)
 	}
 
-	binDir := args[0]
-	manFilename := args[1]
-	outFilename := args[2]
+	splitDir := args[0]
+	outDir := args[1]
 
-	areas, err := readFlashAreas(manFilename)
+	if util.NodeExist(outDir) {
+		LarvaUsage(nil, util.FmtNewtError(
+			"Destination \"%s\" already exists", outDir))
+	}
+
+	mm, err := readManifest(splitDir + "/mfg")
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+	areas, err := extractFlashAreas(mm)
 	if err != nil {
 		LarvaUsage(cmd, err)
 	}
 
-	mm, err := createMfgMap(binDir, areas)
+	nbmap, err := createNameBlobMap(splitDir, areas)
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	bin, err := lvmfg.Join(nbmap, 0xff, areas)
 	if err != nil {
 		LarvaUsage(nil, err)
 	}
 
-	mfgBin, err := mfg.Join(mm, 0xff, areas)
+	m, err := mfg.Parse(bin, mm.Meta.EndOffset, 0xff)
 	if err != nil {
 		LarvaUsage(nil, err)
 	}
 
-	if err := ioutil.WriteFile(outFilename, mfgBin, os.ModePerm); err != nil {
+	infos, err := ioutil.ReadDir(splitDir + "/mfg")
+	if err != nil {
+		LarvaUsage(nil, util.FmtNewtError(
+			"Error reading source mfg directory: %s", err.Error()))
+	}
+	for _, info := range infos {
+		if info.Name() != mfg.MFG_IMG_FILENAME {
+			src := splitDir + "/mfg/" + info.Name()
+			dst := outDir + "/" + info.Name()
+			if info.IsDir() {
+				err = util.CopyDir(src, dst)
+			} else {
+				err = util.CopyFile(src, dst)
+			}
+			if err != nil {
+				LarvaUsage(nil, err)
+			}
+		}
+	}
+
+	finalBin, err := m.Bytes(0xff)
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	if err := ioutil.WriteFile(outDir+"/"+mfg.MFG_IMG_FILENAME, finalBin,
+		os.ModePerm); err != nil {
+
 		LarvaUsage(nil, util.ChildNewtError(err))
 	}
 }
@@ -182,7 +277,7 @@ func runBootKeyCmd(cmd *cobra.Command, args []string) {
 			"Failed to read new key der: %s", err.Error()))
 	}
 
-	if err := mfg.ReplaceBootKey(sec0, okey, nkey); err != nil {
+	if err := lvmfg.ReplaceBootKey(sec0, okey, nkey); err != nil {
 		LarvaUsage(nil, err)
 	}
 
@@ -201,26 +296,28 @@ func AddMfgCommands(cmd *cobra.Command) {
 	}
 	cmd.AddCommand(mfgCmd)
 
+	showCmd := &cobra.Command{
+		Use:   "show <mfgimg.bin> <meta-end-offset>",
+		Short: "Displays JSON describing a manufacturing image",
+		Run:   runMfgShowCmd,
+	}
+
+	mfgCmd.AddCommand(showCmd)
+
 	splitCmd := &cobra.Command{
-		Use:   "split <mfg-image> <manifest> <out-dir>",
+		Use:   "split <mfg-image-dir> <out-dir>",
 		Short: "Splits a Mynewt mfg section into several files",
 		Run:   runSplitCmd,
 	}
 
-	splitCmd.PersistentFlags().IntVarP(&optDeviceNum, "device", "d", 0,
-		"Flash device number")
-
 	mfgCmd.AddCommand(splitCmd)
 
 	joinCmd := &cobra.Command{
-		Use:   "join <bin-dir> <manifest> <out-mfg-image>",
+		Use:   "join <split-dir> <out-dir>",
 		Short: "Joins a split mfg section into a single file",
 		Run:   runJoinCmd,
 	}
 
-	joinCmd.PersistentFlags().IntVarP(&optDeviceNum, "device", "d", 0,
-		"Flash device number")
-
 	mfgCmd.AddCommand(joinCmd)
 
 	bootKeyCmd := &cobra.Command{
diff --git a/larva/lvimg/lvimg.go b/larva/lvimg/lvimg.go
new file mode 100644
index 0000000..60d5ef4
--- /dev/null
+++ b/larva/lvimg/lvimg.go
@@ -0,0 +1,116 @@
+/**
+ * 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 lvimg
+
+import (
+	"encoding/hex"
+	"fmt"
+	"strings"
+
+	"mynewt.apache.org/newt/artifact/image"
+	"mynewt.apache.org/newt/util"
+)
+
+func GetDupSigs(img image.Image) []string {
+	m := map[string]struct{}{}
+	var dups []string
+
+	for _, tlv := range img.Tlvs {
+		if tlv.Header.Type == image.IMAGE_TLV_KEYHASH {
+			h := hex.EncodeToString(tlv.Data)
+			if _, ok := m[h]; ok {
+				dups = append(dups, h)
+			} else {
+				m[h] = struct{}{}
+			}
+		}
+	}
+
+	return dups
+}
+
+func DetectInvalidSigTlvs(img image.Image) error {
+	var errStrs []string
+	addErr := func(format string, args ...interface{}) {
+		s := fmt.Sprintf(format, args...)
+		errStrs = append(errStrs, s)
+	}
+
+	prevIsHash := false
+	for i, tlv := range img.Tlvs {
+		curIsHash := tlv.Header.Type == image.IMAGE_TLV_KEYHASH
+		curIsSig := image.ImageTlvTypeIsSig(tlv.Header.Type)
+		isLast := i == len(img.Tlvs)-1
+
+		if prevIsHash && !curIsSig {
+			prevTlv := img.Tlvs[i-1]
+			addErr("TLV%d (%s) not immediately followed by signature TLV",
+				i-1, image.ImageTlvTypeName(prevTlv.Header.Type))
+		} else if curIsHash && isLast {
+			addErr("TLV%d (%s) not immediately followed by signature TLV",
+				i, image.ImageTlvTypeName(tlv.Header.Type))
+		} else if !prevIsHash && curIsSig {
+			addErr("TLV%d (%s) not immediately preceded by key hash TLV",
+				i, image.ImageTlvTypeName(tlv.Header.Type))
+		}
+
+		prevIsHash = curIsHash
+	}
+
+	if len(errStrs) > 0 {
+		return util.FmtNewtError("%s", strings.Join(errStrs, "\n"))
+	}
+
+	return nil
+}
+
+func VerifyImage(img image.Image) error {
+	if len(img.Tlvs) == 0 || img.Tlvs[0].Header.Type != image.IMAGE_TLV_SHA256 {
+		return util.FmtNewtError("First TLV must be SHA256")
+	}
+
+	if err := DetectInvalidSigTlvs(img); err != nil {
+		return err
+	}
+
+	if dups := GetDupSigs(img); len(dups) > 0 {
+		s := "Duplicate signatures detected:"
+		for _, d := range dups {
+			s += fmt.Sprintf("\n    %s", d)
+		}
+
+		return util.FmtNewtError("%s", s)
+	}
+
+	return nil
+}
+
+func PadEcdsa256Sig(sig []byte) ([]byte, error) {
+	if len(sig) < 70 {
+		return nil, util.FmtNewtError(
+			"Invalid ECDSA256 signature; length (%d) less than 70", len(sig))
+	}
+
+	if len(sig) < 72 {
+		sig = append(sig, []byte{0x00, 0x00}...)
+	}
+
+	return sig, nil
+}
diff --git a/larva/mfg/mfg.go b/larva/lvmfg/lvmfg.go
similarity index 64%
rename from larva/mfg/mfg.go
rename to larva/lvmfg/lvmfg.go
index f98ab06..dbc0168 100644
--- a/larva/mfg/mfg.go
+++ b/larva/lvmfg/lvmfg.go
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package mfg
+package lvmfg
 
 import (
 	"bytes"
@@ -26,10 +26,17 @@ import (
 	"strings"
 
 	"mynewt.apache.org/newt/artifact/flash"
+	"mynewt.apache.org/newt/artifact/mfg"
 	"mynewt.apache.org/newt/util"
 )
 
-type MfgMap map[string][]byte
+type NameBlobMap map[string][]byte
+
+func (to NameBlobMap) Union(from NameBlobMap) {
+	for k, v := range from {
+		to[k] = v
+	}
+}
 
 func errInvalidArea(areaName string, format string,
 	args ...interface{}) error {
@@ -52,24 +59,27 @@ func verifyArea(area flash.FlashArea, minOffset int) error {
 }
 
 // `areas` must be sorted by device ID, then by offset.
-func VerifyAreas(areas []flash.FlashArea, deviceNum int) error {
+func VerifyAreas(areas []flash.FlashArea) error {
+	prevDevice := -1
 	off := 0
 	for _, area := range areas {
-		if area.Device == deviceNum {
-			if err := verifyArea(area, off); err != nil {
-				return err
-			}
-			off += area.Size
+		if area.Device != prevDevice {
+			off = 0
+		}
+
+		if err := verifyArea(area, off); err != nil {
+			return err
 		}
+		off += area.Size
 	}
 
 	return nil
 }
 
 func Split(mfgBin []byte, deviceNum int,
-	areas []flash.FlashArea) (MfgMap, error) {
+	areas []flash.FlashArea, eraseVal byte) (NameBlobMap, error) {
 
-	mm := MfgMap{}
+	mm := NameBlobMap{}
 
 	for _, area := range areas {
 		if _, ok := mm[area.Name]; ok {
@@ -77,16 +87,18 @@ func Split(mfgBin []byte, deviceNum int,
 				"two or more flash areas with same name: \"%s\"", area.Name)
 		}
 
-		if area.Device == deviceNum && area.Offset < len(mfgBin) {
-			end := area.Offset + area.Size
-			if end > len(mfgBin) {
-				return nil, util.FmtNewtError(
-					"area \"%s\" (offset=%d size=%d) "+
-						"extends beyond end of manufacturing image",
-					area.Name, area.Offset, area.Size)
+		if area.Device == deviceNum {
+			var areaBin []byte
+			if area.Offset < len(mfgBin) {
+				end := area.Offset + area.Size
+				overflow := end - len(mfgBin)
+				if overflow > 0 {
+					end -= overflow
+				}
+				areaBin = mfgBin[area.Offset:end]
 			}
 
-			mm[area.Name] = mfgBin[area.Offset:end]
+			mm[area.Name] = StripPadding(areaBin, eraseVal)
 		}
 	}
 
@@ -94,19 +106,8 @@ func Split(mfgBin []byte, deviceNum int,
 }
 
 // `areas` must be sorted by device ID, then by offset.
-func Join(mm MfgMap, eraseVal byte, areas []flash.FlashArea) ([]byte, error) {
-	// Ensure all areas in the mfg map belong to the same flash device.
-	device := -1
-	for _, area := range areas {
-		if _, ok := mm[area.Name]; ok {
-			if device == -1 {
-				device = area.Device
-			} else if device != area.Device {
-				return nil, util.FmtNewtError(
-					"multiple flash devices: %d != %d", device, area.Device)
-			}
-		}
-	}
+func Join(mm NameBlobMap, eraseVal byte,
+	areas []flash.FlashArea) ([]byte, error) {
 
 	// Keep track of which areas we haven't seen yet.
 	unseen := map[string]struct{}{}
@@ -115,24 +116,33 @@ func Join(mm MfgMap, eraseVal byte, areas []flash.FlashArea) ([]byte, error) {
 	}
 
 	joined := []byte{}
-
-	off := 0
 	for _, area := range areas {
 		bin := mm[area.Name]
-		if bin == nil {
-			break
-		}
-		delete(unseen, area.Name)
 
-		padSize := area.Offset - off
-		for i := 0; i < padSize; i++ {
-			joined = append(joined, 0xff)
-		}
+		// Only include this area if it belongs to the mfg image we are
+		// joining.
+		if bin != nil {
+			delete(unseen, area.Name)
 
-		joined = append(joined, bin...)
+			// Pad remainder of previous area in this section.
+			padSize := area.Offset - len(joined)
+			if padSize > 0 {
+				joined = mfg.AddPadding(joined, eraseVal, padSize)
+			}
+
+			// Append data to joined binary.
+			binstr := ""
+			if len(bin) >= 4 {
+				binstr = fmt.Sprintf("%x", bin[:4])
+			}
+			util.StatusMessage(util.VERBOSITY_DEFAULT,
+				"inserting %s (%x) at offset %d (0x%x)\n",
+				area.Name, binstr, len(joined), len(joined))
+			joined = append(joined, bin...)
+		}
 	}
 
-	// Ensure we processed every area in the mfg map.
+	// Ensure we processed every area in the map.
 	if len(unseen) > 0 {
 		names := []string{}
 		for name, _ := range unseen {
@@ -144,6 +154,9 @@ func Join(mm MfgMap, eraseVal byte, areas []flash.FlashArea) ([]byte, error) {
 			"unprocessed flash areas: %s", strings.Join(names, ", "))
 	}
 
+	// Strip padding from the end of the joined bianry.
+	joined = StripPadding(joined, eraseVal)
+
 	return joined, nil
 }
 
@@ -176,3 +189,15 @@ func ReplaceBootKey(sec0 []byte, okey []byte, nkey []byte) error {
 
 	return 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]
+}
diff --git a/newt/builder/targetbuild.go b/newt/builder/targetbuild.go
index 23c361a..94fe8f9 100644
--- a/newt/builder/targetbuild.go
+++ b/newt/builder/targetbuild.go
@@ -28,6 +28,7 @@ import (
 	"fmt"
 	"io/ioutil"
 	"os"
+	"path/filepath"
 	"strings"
 
 	log "github.com/Sirupsen/logrus"
diff --git a/newt/cli/build_cmds.go b/newt/cli/build_cmds.go
index d986510..d7cb8fc 100644
--- a/newt/cli/build_cmds.go
+++ b/newt/cli/build_cmds.go
@@ -27,6 +27,8 @@ import (
 
 	"github.com/spf13/cobra"
 	"mynewt.apache.org/newt/newt/builder"
+	"mynewt.apache.org/newt/newt/imgprod"
+	"mynewt.apache.org/newt/newt/manifest"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/project"
 	"mynewt.apache.org/newt/newt/target"
@@ -160,6 +162,15 @@ func buildRunCmd(cmd *cobra.Command, args []string, printShellCmds bool, execute
 			NewtUsage(nil, err)
 		}
 
+		// Produce bare "imageless" manifest.
+		mopts, err := manifest.OptsForNonImage(b)
+		if err != nil {
+			NewtUsage(nil, err)
+		}
+		if err := imgprod.ProduceManifest(mopts); err != nil {
+			NewtUsage(nil, err)
+		}
+
 		util.StatusMessage(util.VERBOSITY_DEFAULT,
 			"Target successfully built: %s\n", t.Name())
 	}
diff --git a/newt/cli/mfg_cmds.go b/newt/cli/mfg_cmds.go
index 513e2c4..78d5eb9 100644
--- a/newt/cli/mfg_cmds.go
+++ b/newt/cli/mfg_cmds.go
@@ -20,6 +20,8 @@
 package cli
 
 import (
+	"fmt"
+
 	"github.com/spf13/cobra"
 
 	"mynewt.apache.org/newt/artifact/image"
@@ -50,31 +52,34 @@ func ResolveMfgPkg(pkgName string) (*pkg.LocalPackage, error) {
 	return lpkg, nil
 }
 
-func mfgCreate(mi *mfg.MfgImage) {
-	pathStr := ""
-	for _, path := range mi.FromPaths() {
-		pathStr += "    * " + path + "\n"
+func mfgCreate(me mfg.MfgEmitter) {
+	srcPaths, dstPaths, err := me.Emit()
+	if err != nil {
+		NewtUsage(nil, err)
 	}
 
-	util.StatusMessage(util.VERBOSITY_DEFAULT,
-		"Creating a manufacturing image from the following files:\n%s\n",
-		pathStr)
+	srcStr := ""
+	dstStr := ""
 
-	outputPaths, err := mi.CreateMfgImage()
-	if err != nil {
-		NewtUsage(nil, err)
+	for _, p := range srcPaths {
+		srcStr += fmt.Sprintf("    %s\n", p)
 	}
 
-	pathStr = ""
-	for _, path := range outputPaths {
-		pathStr += "    * " + path + "\n"
+	for _, p := range dstPaths {
+		dstStr += fmt.Sprintf("    %s\n", p)
 	}
+
 	util.StatusMessage(util.VERBOSITY_DEFAULT,
-		"Generated the following files:\n%s", pathStr)
+		"Creating a manufacturing image from the following files:\n%s\n",
+		srcStr)
+
+	util.StatusMessage(util.VERBOSITY_DEFAULT,
+		"Generated the following files:\n%s\n",
+		dstStr)
 }
 
-func mfgLoad(mi *mfg.MfgImage) {
-	binPath, err := mi.Upload()
+func mfgLoad(basePkg *pkg.LocalPackage) {
+	binPath, err := mfg.Upload(basePkg)
 	if err != nil {
 		NewtUsage(nil, err)
 	}
@@ -101,13 +106,12 @@ func mfgCreateRunCmd(cmd *cobra.Command, args []string) {
 		NewtUsage(cmd, err)
 	}
 
-	mi, err := mfg.Load(lpkg)
+	me, err := mfg.LoadMfgEmitter(lpkg, ver)
 	if err != nil {
 		NewtUsage(nil, err)
 	}
 
-	mi.SetVersion(ver)
-	mfgCreate(mi)
+	mfgCreate(me)
 }
 
 func mfgLoadRunCmd(cmd *cobra.Command, args []string) {
@@ -121,12 +125,7 @@ func mfgLoadRunCmd(cmd *cobra.Command, args []string) {
 		NewtUsage(cmd, err)
 	}
 
-	mi, err := mfg.Load(lpkg)
-	if err != nil {
-		NewtUsage(nil, err)
-	}
-
-	mfgLoad(mi)
+	mfgLoad(lpkg)
 }
 
 func mfgDeployRunCmd(cmd *cobra.Command, args []string) {
@@ -149,15 +148,14 @@ func mfgDeployRunCmd(cmd *cobra.Command, args []string) {
 		}
 	}
 
-	mi, err := mfg.Load(lpkg)
+	me, err := mfg.LoadMfgEmitter(lpkg, ver)
 	if err != nil {
 		NewtUsage(nil, err)
 	}
 
-	mi.SetVersion(ver)
-	mfgCreate(mi)
+	mfgCreate(me)
 
-	mfgLoad(mi)
+	mfgLoad(lpkg)
 }
 
 func AddMfgCommands(cmd *cobra.Command) {
diff --git a/newt/flashmap/flashmap.go b/newt/flashmap/flashmap.go
index 68fa699..d044f42 100644
--- a/newt/flashmap/flashmap.go
+++ b/newt/flashmap/flashmap.go
@@ -26,7 +26,6 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"sort"
 	"strings"
 
 	log "github.com/Sirupsen/logrus"
@@ -172,22 +171,6 @@ func (flashMap FlashMap) SortedAreas() []flash.FlashArea {
 	return flash.SortFlashAreasById(areas)
 }
 
-func (flashMap FlashMap) DeviceIds() []int {
-	deviceMap := map[int]struct{}{}
-
-	for _, area := range flashMap.Areas {
-		deviceMap[area.Device] = struct{}{}
-	}
-
-	devices := make([]int, 0, len(deviceMap))
-	for device, _ := range deviceMap {
-		devices = append(devices, device)
-	}
-	sort.Ints(devices)
-
-	return devices
-}
-
 func areasDistinct(a flash.FlashArea, b flash.FlashArea) bool {
 	var lo flash.FlashArea
 	var hi flash.FlashArea
diff --git a/newt/imgprod/imgprod.go b/newt/imgprod/imgprod.go
index a858c49..dc72eca 100644
--- a/newt/imgprod/imgprod.go
+++ b/newt/imgprod/imgprod.go
@@ -262,16 +262,14 @@ func ProduceAll(t *builder.TargetBuilder, ver image.ImageVersion,
 		return err
 	}
 
-	mopts := manifest.ManifestCreateOpts{
-		TgtBldr:    t,
-		AppHash:    pset.App.Hash,
-		Version:    ver,
-		BuildID:    fmt.Sprintf("%x", pset.App.Hash),
-		FlashAreas: t.BspPkg().FlashMap.SortedAreas(),
+	var loaderHash []byte
+	if pset.Loader != nil {
+		loaderHash = pset.Loader.Hash
 	}
 
-	if pset.Loader != nil {
-		mopts.LoaderHash = pset.Loader.Hash
+	mopts, err := manifest.OptsForImage(t, ver, pset.App.Hash, loaderHash)
+	if err != nil {
+		return err
 	}
 
 	if err := ProduceManifest(mopts); err != nil {
diff --git a/newt/imgprod/v1.go b/newt/imgprod/v1.go
index 37067d4..05f0a33 100644
--- a/newt/imgprod/v1.go
+++ b/newt/imgprod/v1.go
@@ -195,11 +195,10 @@ func ProduceAllV1(t *builder.TargetBuilder, ver image.ImageVersion,
 	}
 
 	mopts := manifest.ManifestCreateOpts{
-		TgtBldr:    t,
-		AppHash:    pset.App.Hash,
-		Version:    ver,
-		BuildID:    fmt.Sprintf("%x", pset.App.Hash),
-		FlashAreas: t.BspPkg().FlashMap.SortedAreas(),
+		TgtBldr: t,
+		AppHash: pset.App.Hash,
+		Version: ver,
+		BuildID: fmt.Sprintf("%x", pset.App.Hash),
 	}
 
 	if pset.Loader != nil {
diff --git a/newt/manifest/manifest.go b/newt/manifest/manifest.go
index 7c9eaff..c2b5243 100644
--- a/newt/manifest/manifest.go
+++ b/newt/manifest/manifest.go
@@ -30,7 +30,6 @@ import (
 
 	log "github.com/Sirupsen/logrus"
 
-	"mynewt.apache.org/newt/artifact/flash"
 	"mynewt.apache.org/newt/artifact/image"
 	"mynewt.apache.org/newt/artifact/manifest"
 	"mynewt.apache.org/newt/newt/builder"
@@ -49,7 +48,7 @@ type ManifestCreateOpts struct {
 	AppHash    []byte
 	Version    image.ImageVersion
 	BuildID    string
-	FlashAreas []flash.FlashArea
+	Syscfg     map[string]string
 }
 
 type RepoManager struct {
@@ -245,17 +244,47 @@ func ManifestPkgSizes(b *builder.Builder) (ManifestSizeCollector, error) {
 	return msc, nil
 }
 
+func OptsForNonImage(t *builder.TargetBuilder) (ManifestCreateOpts, error) {
+	res, err := t.Resolve()
+	if err != nil {
+		return ManifestCreateOpts{}, err
+	}
+
+	return ManifestCreateOpts{
+		TgtBldr: t,
+		Syscfg:  res.Cfg.SettingValues(),
+	}, nil
+}
+
+func OptsForImage(t *builder.TargetBuilder, ver image.ImageVersion,
+	appHash []byte, loaderHash []byte) (ManifestCreateOpts, error) {
+
+	res, err := t.Resolve()
+	if err != nil {
+		return ManifestCreateOpts{}, err
+	}
+
+	return ManifestCreateOpts{
+		TgtBldr:    t,
+		AppHash:    appHash,
+		LoaderHash: loaderHash,
+		Version:    ver,
+		BuildID:    fmt.Sprintf("%x", appHash),
+		Syscfg:     res.Cfg.SettingValues(),
+	}, nil
+}
+
 func CreateManifest(opts ManifestCreateOpts) (manifest.Manifest, error) {
 	t := opts.TgtBldr
 
 	m := manifest.Manifest{
-		Name:       t.GetTarget().FullName(),
-		Date:       time.Now().Format(time.RFC3339),
-		Version:    opts.Version.String(),
-		BuildID:    opts.BuildID,
-		Image:      t.AppBuilder.AppImgPath(),
-		ImageHash:  fmt.Sprintf("%x", opts.AppHash),
-		FlashAreas: opts.FlashAreas,
+		Name:      t.GetTarget().FullName(),
+		Date:      time.Now().Format(time.RFC3339),
+		Version:   opts.Version.String(),
+		BuildID:   opts.BuildID,
+		Image:     t.AppBuilder.AppImgPath(),
+		ImageHash: fmt.Sprintf("%x", opts.AppHash),
+		Syscfg:    opts.Syscfg,
 	}
 
 	rm := NewRepoManager()
diff --git a/newt/mfg/build.go b/newt/mfg/build.go
new file mode 100644
index 0000000..a3f7291
--- /dev/null
+++ b/newt/mfg/build.go
@@ -0,0 +1,564 @@
+/**
+ * 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"
+	"fmt"
+	"io/ioutil"
+	"path/filepath"
+	"sort"
+	"strings"
+
+	"mynewt.apache.org/newt/artifact/flash"
+	"mynewt.apache.org/newt/artifact/image"
+	"mynewt.apache.org/newt/artifact/manifest"
+	"mynewt.apache.org/newt/artifact/mfg"
+	"mynewt.apache.org/newt/newt/builder"
+	"mynewt.apache.org/newt/newt/flashmap"
+	"mynewt.apache.org/newt/newt/parse"
+	"mynewt.apache.org/newt/newt/pkg"
+	"mynewt.apache.org/newt/newt/project"
+	"mynewt.apache.org/newt/newt/target"
+	"mynewt.apache.org/newt/util"
+)
+
+type MfgBuildTarget struct {
+	Target  *target.Target
+	Area    flash.FlashArea
+	Offset  int
+	IsBoot  bool
+	BinPath string
+}
+
+type MfgBuildRaw struct {
+	Filename string
+	Offset   int
+	Area     flash.FlashArea
+}
+
+type MfgBuildMetaMmr struct {
+	Area flash.FlashArea
+}
+
+type MfgBuildMeta struct {
+	Area     flash.FlashArea
+	Hash     bool
+	FlashMap bool
+	Mmrs     []MfgBuildMetaMmr
+}
+
+// Can be used to construct an Mfg object.
+type MfgBuilder struct {
+	BasePkg *pkg.LocalPackage
+	Bsp     *pkg.BspPackage
+	Targets []MfgBuildTarget
+	Raws    []MfgBuildRaw
+	Meta    *MfgBuildMeta
+}
+
+// Searches the provided flash map for the named area.
+func lookUpArea(fm flashmap.FlashMap, name string) (flash.FlashArea, error) {
+	area, ok := fm.Areas[name]
+	if !ok {
+		return flash.FlashArea{}, util.FmtNewtError(
+			"reference to undefined flash area \"%s\"", name)
+	}
+
+	return area, nil
+}
+
+// Searches the project for the target corresponding to the specified decoded
+// entry (read from `mfg.yml`).
+func lookUpTarget(dt DecodedTarget) (*target.Target, error) {
+	t := target.GetTargets()[dt.Name]
+	if t == nil {
+		return nil, util.FmtNewtError(
+			"target entry references undefined target \"%s\"", dt.Name)
+	}
+
+	return t, nil
+}
+
+func normalizeOffset(offset int, length int,
+	area flash.FlashArea) (int, error) {
+
+	areaEnd := area.Offset + area.Size
+	if offset == OFFSET_END {
+		if length > area.Size {
+			return 0, util.FmtNewtError(
+				"segment is too large to fit in flash area \"%s\"; "+
+					"segment=%d, area=%d", area.Name, length, area.Size)
+		}
+		return areaEnd - length, nil
+	}
+
+	if offset+length > area.Size {
+		return 0, util.FmtNewtError(
+			"segment extends beyond end of flash area \"%s\"; "+
+				"offset=%d len=%d area_len=%d",
+			area.Name, offset, length, area.Size)
+	}
+
+	return area.Offset + offset, nil
+}
+
+func calcBsp(dm DecodedMfg,
+	basePkg *pkg.LocalPackage) (*pkg.BspPackage, error) {
+
+	var bspLpkg *pkg.LocalPackage
+	bspMap := map[*pkg.LocalPackage]struct{}{}
+	for _, dt := range dm.Targets {
+		t, err := lookUpTarget(dt)
+		if err != nil {
+			return nil, err
+		}
+
+		bspLpkg = t.Bsp()
+		bspMap[bspLpkg] = struct{}{}
+	}
+
+	if dm.Bsp != "" {
+		var err error
+		bspLpkg, err = project.GetProject().ResolvePackage(
+			basePkg.Repo(), dm.Bsp)
+		if err != nil {
+			return nil, util.FmtNewtError(
+				"failed to resolve BSP package: %s", err.Error())
+		}
+		bspMap[bspLpkg] = struct{}{}
+	}
+
+	if len(bspMap) == 0 {
+		return nil, util.FmtNewtError("at least one target required")
+	}
+
+	if len(bspMap) > 1 {
+		return nil, util.FmtNewtError("multiple BSPs detected")
+	}
+
+	bsp, err := pkg.NewBspPackage(bspLpkg)
+	if err != nil {
+		return nil, util.FmtNewtError(err.Error())
+	}
+
+	return bsp, nil
+}
+
+func (raw *MfgBuildRaw) ToPart(entryIdx int) (Part, error) {
+	data, err := ioutil.ReadFile(raw.Filename)
+	if err != nil {
+		return Part{}, util.ChildNewtError(err)
+	}
+
+	off, err := normalizeOffset(raw.Offset, len(data), raw.Area)
+	if err != nil {
+		return Part{}, err
+	}
+
+	return Part{
+		Name:   fmt.Sprintf("raw-%d (%s)", entryIdx, raw.Filename),
+		Offset: off,
+		Data:   data,
+	}, nil
+}
+
+func (mt *MfgBuildTarget) ToPart() (Part, error) {
+	data, err := ioutil.ReadFile(mt.BinPath)
+	if err != nil {
+		return Part{}, util.ChildNewtError(err)
+	}
+
+	off, err := normalizeOffset(mt.Offset, len(data), mt.Area)
+	if err != nil {
+		return Part{}, err
+	}
+
+	return Part{
+		Name:   fmt.Sprintf("%s (%s)", mt.Area.Name, filepath.Base(mt.BinPath)),
+		Offset: off,
+		Data:   data,
+	}, nil
+}
+
+func newMfgBuildTarget(dt DecodedTarget,
+	fm flashmap.FlashMap) (MfgBuildTarget, error) {
+
+	t, err := lookUpTarget(dt)
+	if err != nil {
+		return MfgBuildTarget{}, err
+	}
+
+	area, err := lookUpArea(fm, dt.Area)
+	if err != nil {
+		return MfgBuildTarget{}, err
+	}
+
+	mpath := builder.ManifestPath(dt.Name, builder.BUILD_NAME_APP,
+		t.App().Name())
+	man, err := manifest.ReadManifest(mpath)
+	if err != nil {
+		return MfgBuildTarget{}, util.FmtNewtError("%s", err.Error())
+	}
+
+	isBoot := parse.ValueIsTrue(man.Syscfg["BOOT_LOADER"])
+
+	return MfgBuildTarget{
+		Target:  t,
+		Area:    area,
+		Offset:  dt.Offset,
+		IsBoot:  isBoot,
+		BinPath: targetSrcBinPath(t, isBoot),
+	}, nil
+}
+
+func newMfgBuildRaw(dr DecodedRaw,
+	fm flashmap.FlashMap, basePath string) (MfgBuildRaw, error) {
+
+	filename := dr.Filename
+	if !strings.HasPrefix(filename, "/") {
+		filename = basePath + "/" + filename
+	}
+
+	area, err := lookUpArea(fm, dr.Area)
+	if err != nil {
+		return MfgBuildRaw{}, err
+	}
+
+	return MfgBuildRaw{
+		Filename: filename,
+		Offset:   dr.Offset,
+		Area:     area,
+	}, nil
+}
+
+func newMfgBuildMeta(dm DecodedMeta,
+	fm flashmap.FlashMap) (MfgBuildMeta, error) {
+
+	area, ok := fm.Areas[dm.Area]
+	if !ok {
+		return MfgBuildMeta{}, util.FmtNewtError(
+			"meta region specifies unrecognized flash area: \"%s\"", dm.Area)
+	}
+
+	var mmrs []MfgBuildMetaMmr
+	for _, dmmr := range dm.Mmrs {
+		area, err := lookUpArea(fm, dmmr.Area)
+		if err != nil {
+			return MfgBuildMeta{}, err
+		}
+		mmr := MfgBuildMetaMmr{
+			Area: area,
+		}
+		mmrs = append(mmrs, mmr)
+	}
+
+	return MfgBuildMeta{
+		Area:     area,
+		Hash:     dm.Hash,
+		FlashMap: dm.FlashMap,
+		Mmrs:     mmrs,
+	}, nil
+}
+
+func (mb *MfgBuilder) parts() ([]Part, error) {
+	parts := []Part{}
+
+	// Create parts from the raw entries.
+	for i, raw := range mb.Raws {
+		part, err := raw.ToPart(i)
+		if err != nil {
+			return nil, err
+		}
+		parts = append(parts, part)
+	}
+
+	// Create parts from the target entries.
+	for _, t := range mb.Targets {
+		part, err := t.ToPart()
+		if err != nil {
+			return nil, err
+		}
+		parts = append(parts, part)
+	}
+
+	// Sort by offset.
+	return SortParts(parts), nil
+}
+
+func (mb *MfgBuilder) detectOverlaps() error {
+	type overlap struct {
+		p1 Part
+		p2 Part
+	}
+
+	overlaps := []overlap{}
+
+	parts, err := mb.parts()
+	if err != nil {
+		return err
+	}
+
+	for i, p1 := range parts[:len(parts)-1] {
+		p1end := p1.Offset + len(p1.Data)
+		for _, p2 := range parts[i+1:] {
+			// Parts are sorted by offset, so only one comparison is
+			// necessary to detect overlap.
+			if p2.Offset < p1end {
+				overlaps = append(overlaps, overlap{
+					p1: p1,
+					p2: p2,
+				})
+			}
+		}
+	}
+
+	if len(overlaps) > 0 {
+		str := "flash overlaps detected:"
+		for _, overlap := range overlaps {
+
+			p1end := overlap.p1.Offset + len(overlap.p1.Data)
+			p2end := overlap.p2.Offset + len(overlap.p2.Data)
+			str += fmt.Sprintf("\n    * [%s] (%d - %d) <=> [%s] (%d - %d)",
+				overlap.p1.Name, overlap.p1.Offset, p1end,
+				overlap.p2.Name, overlap.p2.Offset, p2end)
+		}
+
+		return util.NewNewtError(str)
+	}
+
+	return nil
+}
+
+// Determines which flash device the manufacturing image is intended for.  It
+// is an error if the mfg definition specifies 0 or >1 devices.
+func (mb *MfgBuilder) calcDevice() (int, error) {
+	deviceMap := map[int]struct{}{}
+	for _, t := range mb.Targets {
+		deviceMap[t.Area.Device] = struct{}{}
+	}
+	for _, r := range mb.Raws {
+		deviceMap[r.Area.Device] = struct{}{}
+	}
+
+	devices := make([]int, 0, len(deviceMap))
+	for d, _ := range deviceMap {
+		devices = append(devices, d)
+	}
+	sort.Ints(devices)
+
+	if len(devices) == 0 {
+		return 0, util.FmtNewtError(
+			"manufacturing image definition does not indicate flash device")
+	}
+
+	if len(devices) > 1 {
+		return 0, util.FmtNewtError(
+			"multiple flash devices in use by single manufacturing image: %v",
+			devices)
+	}
+
+	return devices[0], nil
+}
+
+func newMfgBuilder(basePkg *pkg.LocalPackage, dm DecodedMfg,
+	ver image.ImageVersion) (MfgBuilder, error) {
+
+	mb := MfgBuilder{
+		BasePkg: basePkg,
+	}
+
+	bsp, err := calcBsp(dm, basePkg)
+	if err != nil {
+		return mb, err
+	}
+	mb.Bsp = bsp
+
+	for _, dt := range dm.Targets {
+		mbt, err := newMfgBuildTarget(dt, bsp.FlashMap)
+		if err != nil {
+			return mb, err
+		}
+		mb.Targets = append(mb.Targets, mbt)
+	}
+
+	for _, dr := range dm.Raws {
+		mbr, err := newMfgBuildRaw(dr, bsp.FlashMap, basePkg.BasePath())
+		if err != nil {
+			return mb, err
+		}
+		mb.Raws = append(mb.Raws, mbr)
+	}
+
+	if dm.Meta != nil {
+		meta, err := newMfgBuildMeta(*dm.Meta, mb.Bsp.FlashMap)
+		if err != nil {
+			return mb, err
+		}
+		mb.Meta = &meta
+	}
+
+	if _, err := mb.calcDevice(); err != nil {
+		return mb, err
+	}
+
+	if err := mb.detectOverlaps(); err != nil {
+		return mb, err
+	}
+
+	return mb, nil
+}
+
+// Creates a zeroed-out hash MMR TLV.  The hash's original value must be zero
+// for the actual hash to be calculated later.  After the actual value is
+// calculated, it replaces the zeros in the TLV.
+func newZeroHashTlv() mfg.MetaTlv {
+	return mfg.MetaTlv{
+		Header: mfg.MetaTlvHeader{
+			Type: mfg.META_TLV_TYPE_HASH,
+			Size: mfg.META_TLV_HASH_SZ,
+		},
+		Data: make([]byte, mfg.META_HASH_SZ),
+	}
+}
+
+// Creates a flash area MMR TLV.
+func newFlashAreaTlv(area flash.FlashArea) (mfg.MetaTlv, error) {
+	tlv := mfg.MetaTlv{
+		Header: mfg.MetaTlvHeader{
+			Type: mfg.META_TLV_TYPE_FLASH_AREA,
+			Size: mfg.META_TLV_FLASH_AREA_SZ,
+		},
+	}
+
+	body := mfg.MetaTlvBodyFlashArea{
+		Area:   uint8(area.Id),
+		Device: uint8(area.Device),
+		Offset: uint32(area.Offset),
+		Size:   uint32(area.Size),
+	}
+
+	b := &bytes.Buffer{}
+	if err := binary.Write(b, binary.LittleEndian, body); err != nil {
+		return tlv, util.ChildNewtError(err)
+	}
+
+	tlv.Data = b.Bytes()
+
+	return tlv, nil
+}
+
+// Creates an MMR ref TLV.
+func newMmrRefTlv(area flash.FlashArea) (mfg.MetaTlv, error) {
+	tlv := mfg.MetaTlv{
+		Header: mfg.MetaTlvHeader{
+			Type: mfg.META_TLV_TYPE_MMR_REF,
+			Size: mfg.META_TLV_MMR_REF_SZ,
+		},
+	}
+
+	body := mfg.MetaTlvBodyMmrRef{
+		Area: uint8(area.Id),
+	}
+
+	b := &bytes.Buffer{}
+	if err := binary.Write(b, binary.LittleEndian, body); err != nil {
+		return tlv, util.ChildNewtError(err)
+	}
+
+	tlv.Data = b.Bytes()
+
+	return tlv, nil
+}
+
+// Builds a manufacturing meta region.
+func (mb *MfgBuilder) buildMeta() (mfg.Meta, error) {
+	meta := mfg.Meta{
+		Footer: mfg.MetaFooter{
+			Size:    0, // Filled in later.
+			Version: mfg.META_VERSION,
+			Pad8:    0xff,
+			Magic:   mfg.META_MAGIC,
+		},
+	}
+
+	// Hash TLV.
+	if mb.Meta.Hash {
+		meta.Tlvs = append(meta.Tlvs, newZeroHashTlv())
+	}
+
+	// Flash map TLVs.
+	if mb.Meta.FlashMap {
+		for _, area := range mb.Bsp.FlashMap.SortedAreas() {
+			tlv, err := newFlashAreaTlv(area)
+			if err != nil {
+				return meta, err
+			}
+
+			meta.Tlvs = append(meta.Tlvs, tlv)
+		}
+	}
+
+	// MMR ref TLVs.
+	for _, mmr := range mb.Meta.Mmrs {
+		tlv, err := newMmrRefTlv(mmr.Area)
+		if err != nil {
+			return meta, err
+		}
+
+		meta.Tlvs = append(meta.Tlvs, tlv)
+	}
+
+	// Fill in region size in footer now that we know the value.
+	meta.Footer.Size = uint16(meta.Size())
+
+	return meta, nil
+}
+
+// Builds a manufacturing image.
+func (mb *MfgBuilder) Build() (mfg.Mfg, error) {
+	parts, err := mb.parts()
+	if err != nil {
+		return mfg.Mfg{}, err
+	}
+
+	bin, err := PartsBytes(parts)
+	if err != nil {
+		return mfg.Mfg{}, err
+	}
+
+	var metaOff int
+	var metap *mfg.Meta
+	if mb.Meta != nil {
+		meta, err := mb.buildMeta()
+		if err != nil {
+			return mfg.Mfg{}, err
+		}
+		metap = &meta
+		metaOff = mb.Meta.Area.Offset + mb.Meta.Area.Size - meta.Size()
+	}
+
+	return mfg.Mfg{
+		Bin:     bin,
+		Meta:    metap,
+		MetaOff: metaOff,
+	}, nil
+}
diff --git a/newt/mfg/create.go b/newt/mfg/create.go
deleted file mode 100644
index 4930329..0000000
--- a/newt/mfg/create.go
+++ /dev/null
@@ -1,535 +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 mfg
-
-import (
-	"encoding/json"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"sort"
-	"time"
-
-	"mynewt.apache.org/newt/artifact/flash"
-	"mynewt.apache.org/newt/newt/builder"
-	"mynewt.apache.org/newt/newt/pkg"
-	"mynewt.apache.org/newt/newt/target"
-	"mynewt.apache.org/newt/util"
-)
-
-type mfgManifest struct {
-	BuildTime   string `json:"build_time"`
-	MfgHash     string `json:"mfg_hash"`
-	Version     string `json:"version"`
-	MetaSection int    `json:"meta_section"`
-	MetaOffset  int    `json:"meta_offset"`
-}
-
-type mfgSection struct {
-	offset int
-	blob   []byte
-}
-
-type createState struct {
-	// {0:[section0], 1:[section1], ...}
-	dsMap      map[int]mfgSection
-	metaOffset int
-	hashOffset int
-	hash       []byte
-}
-
-func insertPartIntoBlob(section mfgSection, part mfgPart) {
-	partEnd := part.offset + len(part.data)
-
-	if len(section.blob) < partEnd {
-		panic("internal error; mfg blob too small")
-	}
-
-	copy(section.blob[part.offset:partEnd], part.data)
-}
-
-func (mi *MfgImage) partFromImage(
-	imgPath string, flashAreaName string) (mfgPart, error) {
-
-	part := mfgPart{
-		// Boot loader and images always go in device 0.
-		device: 0,
-	}
-
-	area, ok := mi.bsp.FlashMap.Areas[flashAreaName]
-	if !ok {
-		return part, util.FmtNewtError(
-			"Image at \"%s\" requires undefined flash area \"%s\"",
-			imgPath, flashAreaName)
-	}
-
-	part.name = fmt.Sprintf("%s (%s)", flashAreaName, filepath.Base(imgPath))
-	part.offset = area.Offset
-
-	var err error
-
-	part.data, err = ioutil.ReadFile(imgPath)
-	if err != nil {
-		return part, util.ChildNewtError(err)
-	}
-
-	overflow := len(part.data) - area.Size
-	if overflow > 0 {
-		return part, util.FmtNewtError(
-			"Image \"%s\" is too large to fit in flash area \"%s\"; "+
-				"image-size=%d flash-area-size=%d overflow=%d",
-			imgPath, flashAreaName, len(part.data), area.Size, overflow)
-	}
-
-	// If an image slot is used, the entire flash area is unwritable.  This
-	// restriction comes from the boot loader's need to write status at the end
-	// of an area.  Pad out part with unwriten flash (0xff).  This probably
-	// isn't terribly efficient...
-	for i := 0; i < -overflow; i++ {
-		part.data = append(part.data, 0xff)
-	}
-
-	return part, nil
-}
-
-func partFromRawEntry(entry MfgRawEntry, entryIdx int) mfgPart {
-	return mfgPart{
-		name:   fmt.Sprintf("entry-%d (%s)", entryIdx, entry.filename),
-		offset: entry.offset,
-		data:   entry.data,
-	}
-}
-
-func (mi *MfgImage) targetParts() ([]mfgPart, error) {
-	parts := []mfgPart{}
-
-	bootPath := mi.dstBootBinPath()
-	if bootPath != "" {
-		bootPart, err := mi.partFromImage(
-			bootPath, flash.FLASH_AREA_NAME_BOOTLOADER)
-		if err != nil {
-			return nil, err
-		}
-
-		parts = append(parts, bootPart)
-	}
-
-	for i := 0; i < 2; i++ {
-		imgPath := mi.dstImgPath(i)
-		if imgPath != "" {
-			areaName, err := areaNameFromImgIdx(i)
-			if err != nil {
-				return nil, err
-			}
-
-			part, err := mi.partFromImage(imgPath, areaName)
-			if err != nil {
-				return nil, err
-			}
-			parts = append(parts, part)
-		}
-	}
-
-	return parts, nil
-}
-
-func sectionSize(parts []mfgPart) (int, int) {
-	greatest := 0
-	lowest := 0
-	if len(parts) > 0 {
-		lowest = parts[0].offset
-	}
-	for _, part := range parts {
-		lowest = util.IntMin(lowest, part.offset)
-	}
-	for _, part := range parts {
-		end := part.offset + len(part.data)
-		greatest = util.IntMax(greatest, end)
-	}
-
-	return lowest, greatest
-}
-
-func sectionFromParts(parts []mfgPart) mfgSection {
-	offset, sectionSize := sectionSize(parts)
-	blob := make([]byte, sectionSize)
-
-	section := mfgSection{
-		offset: offset,
-		blob:   blob,
-	}
-
-	// Initialize section 0's data as unwritten flash (0xff).
-	for i, _ := range blob {
-		blob[i] = 0xff
-	}
-
-	for _, part := range parts {
-		insertPartIntoBlob(section, part)
-	}
-
-	return section
-}
-
-func (mi *MfgImage) devicePartMap() (map[int][]mfgPart, error) {
-	dpMap := map[int][]mfgPart{}
-
-	// Create parts from the raw entries.
-	for i, entry := range mi.rawEntries {
-		part := partFromRawEntry(entry, i)
-		dpMap[entry.device] = append(dpMap[entry.device], part)
-	}
-
-	// Insert the boot loader and image parts into section 0.
-	targetParts, err := mi.targetParts()
-	if err != nil {
-		return nil, err
-	}
-	dpMap[0] = append(dpMap[0], targetParts...)
-
-	// Sort each part slice by offset.
-	for device, _ := range dpMap {
-		sortParts(dpMap[device])
-	}
-
-	return dpMap, nil
-}
-
-func (mi *MfgImage) deviceSectionMap() (map[int]mfgSection, error) {
-	dpMap, err := mi.devicePartMap()
-	if err != nil {
-		return nil, err
-	}
-
-	// Convert each part slice into a section.
-	dsMap := map[int]mfgSection{}
-	for device, parts := range dpMap {
-		dsMap[device] = sectionFromParts(parts)
-	}
-
-	return dsMap, nil
-}
-
-func (mi *MfgImage) createSections() (createState, error) {
-	cs := createState{}
-
-	var err error
-
-	if err := mi.detectOverlaps(); err != nil {
-		return cs, err
-	}
-
-	cs.dsMap, err = mi.deviceSectionMap()
-	if err != nil {
-		return cs, err
-	}
-
-	if len(cs.dsMap) < 1 {
-		panic("Invalid state; no section 0")
-	}
-
-	cs.metaOffset, cs.hashOffset, err = insertMeta(cs.dsMap[0].blob,
-		mi.bsp.FlashMap)
-	if err != nil {
-		return cs, err
-	}
-
-	// Calculate manufacturing hash.
-	devices := make([]int, 0, len(cs.dsMap))
-	for device, _ := range cs.dsMap {
-		devices = append(devices, device)
-	}
-	sort.Ints(devices)
-
-	sections := make([][]byte, len(devices))
-	for i, device := range devices {
-		sections[i] = cs.dsMap[device].blob
-	}
-	cs.hash = calcMetaHash(sections)
-	copy(cs.dsMap[0].blob[cs.hashOffset:cs.hashOffset+META_HASH_SZ], cs.hash)
-
-	return cs, nil
-}
-
-func areaNameFromImgIdx(imgIdx int) (string, error) {
-	switch imgIdx {
-	case 0:
-		return flash.FLASH_AREA_NAME_IMAGE_0, nil
-	case 1:
-		return flash.FLASH_AREA_NAME_IMAGE_1, nil
-	default:
-		return "", util.FmtNewtError("invalid image index: %d", imgIdx)
-	}
-}
-
-func bootLoaderFromPaths(t *target.Target) []string {
-	return []string{
-		/* boot.elf */
-		builder.AppElfPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
-
-		/* boot.elf.bin */
-		builder.AppBinPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
-
-		/* manifest.json */
-		builder.ManifestPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
-	}
-}
-
-func loaderFromPaths(t *target.Target) []string {
-	if t.LoaderName == "" {
-		return nil
-	}
-
-	return []string{
-		/* <loader>.elf */
-		builder.AppElfPath(t.Name(), builder.BUILD_NAME_LOADER,
-			t.Loader().Name()),
-
-		/* <app>.img */
-		builder.AppImgPath(t.Name(), builder.BUILD_NAME_LOADER,
-			t.Loader().Name()),
-	}
-}
-
-func appFromPaths(t *target.Target) []string {
-	return []string{
-		/* <app>.elf */
-		builder.AppElfPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
-
-		/* <app>.img */
-		builder.AppImgPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
-
-		/* manifest.json */
-		builder.ManifestPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
-	}
-}
-
-func imageFromPaths(t *target.Target) []string {
-	paths := loaderFromPaths(t)
-	paths = append(paths, appFromPaths(t)...)
-	return paths
-}
-
-func (mi *MfgImage) copyBinFile(srcPath string, dstDir string) error {
-	dstPath := dstDir + "/" + filepath.Base(srcPath)
-
-	util.StatusMessage(util.VERBOSITY_VERBOSE, "copying file %s --> %s\n",
-		srcPath, dstPath)
-
-	if err := util.CopyFile(srcPath, dstPath); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (mi *MfgImage) copyBinFiles() error {
-	dstPath := MfgBinDir(mi.basePkg.Name())
-	if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil {
-		return util.ChildNewtError(err)
-	}
-
-	bootPaths := bootLoaderFromPaths(mi.boot)
-	for _, path := range bootPaths {
-		dstDir := MfgBootDir(mi.basePkg.Name())
-		if err := mi.copyBinFile(path, dstDir); err != nil {
-			return err
-		}
-	}
-
-	for i, imgTarget := range mi.images {
-		imgPaths := imageFromPaths(imgTarget)
-		dstDir := MfgImageBinDir(mi.basePkg.Name(), i)
-		for _, path := range imgPaths {
-			if err := mi.copyBinFile(path, dstDir); err != nil {
-				return err
-			}
-		}
-	}
-
-	return nil
-}
-
-func (mi *MfgImage) dstBootBinPath() string {
-	if mi.boot == nil {
-		return ""
-	}
-
-	return fmt.Sprintf("%s/%s.elf.bin",
-		MfgBootDir(mi.basePkg.Name()),
-		pkg.ShortName(mi.boot.App()))
-}
-
-func (mi *MfgImage) dstImgPath(slotIdx int) string {
-	var pack *pkg.LocalPackage
-	var imgIdx int
-
-	if len(mi.images) >= 1 {
-		switch slotIdx {
-		case 0:
-			if mi.images[0].LoaderName != "" {
-				pack = mi.images[0].Loader()
-			} else {
-				pack = mi.images[0].App()
-			}
-			imgIdx = 0
-
-		case 1:
-			if mi.images[0].LoaderName != "" {
-				pack = mi.images[0].App()
-				imgIdx = 0
-			} else {
-				if len(mi.images) >= 2 {
-					pack = mi.images[1].App()
-				}
-				imgIdx = 1
-			}
-
-		default:
-			panic(fmt.Sprintf("invalid image index: %d", imgIdx))
-		}
-	}
-
-	if pack == nil {
-		return ""
-	}
-
-	return fmt.Sprintf("%s/%s.img",
-		MfgImageBinDir(mi.basePkg.Name(), imgIdx), pkg.ShortName(pack))
-}
-
-// Returns a slice containing the path of each file required to build the
-// manufacturing image.
-func (mi *MfgImage) FromPaths() []string {
-	paths := []string{}
-
-	if mi.boot != nil {
-		paths = append(paths, bootLoaderFromPaths(mi.boot)...)
-	}
-	if len(mi.images) >= 1 {
-		paths = append(paths, imageFromPaths(mi.images[0])...)
-	}
-	if len(mi.images) >= 2 {
-		paths = append(paths, imageFromPaths(mi.images[1])...)
-	}
-
-	for _, raw := range mi.rawEntries {
-		paths = append(paths, raw.filename)
-	}
-
-	return paths
-}
-
-func (mi *MfgImage) build() (createState, error) {
-	if err := mi.copyBinFiles(); err != nil {
-		return createState{}, err
-	}
-
-	cs, err := mi.createSections()
-	if err != nil {
-		return cs, err
-	}
-
-	return cs, nil
-}
-
-func (mi *MfgImage) createManifest(cs createState) ([]byte, error) {
-	manifest := mfgManifest{
-		BuildTime:   time.Now().Format(time.RFC3339),
-		Version:     mi.version.String(),
-		MfgHash:     fmt.Sprintf("%x", cs.hash),
-		MetaSection: 0,
-		MetaOffset:  cs.metaOffset,
-	}
-	buffer, err := json.MarshalIndent(manifest, "", "  ")
-	if err != nil {
-		return nil, util.FmtNewtError("Failed to encode mfg manifest: %s",
-			err.Error())
-	}
-
-	return buffer, nil
-}
-
-func appendNonEmptyStr(dst []string, src string) []string {
-	if src != "" {
-		dst = append(dst, src)
-	}
-
-	return dst
-}
-
-func (mi *MfgImage) ToPaths() []string {
-	paths := []string{}
-
-	paths = appendNonEmptyStr(paths, mi.BootBinPath())
-	paths = appendNonEmptyStr(paths, mi.BootElfPath())
-	paths = appendNonEmptyStr(paths, mi.BootManifestPath())
-
-	for i := 0; i < len(mi.images); i++ {
-		paths = appendNonEmptyStr(paths, mi.LoaderImgPath(i))
-		paths = appendNonEmptyStr(paths, mi.LoaderElfPath(i))
-		paths = appendNonEmptyStr(paths, mi.AppImgPath(i))
-		paths = appendNonEmptyStr(paths, mi.AppElfPath(i))
-		paths = appendNonEmptyStr(paths, mi.ImageManifestPath(i))
-	}
-
-	paths = append(paths, mi.SectionBinPaths()...)
-	paths = append(paths, mi.SectionHexPaths()...)
-	paths = append(paths, mi.ManifestPath())
-
-	return paths
-}
-
-// @return                      [paths-of-artifacts], error
-func (mi *MfgImage) CreateMfgImage() ([]string, error) {
-	cs, err := mi.build()
-	if err != nil {
-		return nil, err
-	}
-
-	sectionDir := MfgSectionBinDir(mi.basePkg.Name())
-	if err := os.MkdirAll(sectionDir, 0755); err != nil {
-		return nil, util.ChildNewtError(err)
-	}
-
-	for device, section := range cs.dsMap {
-		sectionPath := MfgSectionBinPath(mi.basePkg.Name(), device)
-		err := ioutil.WriteFile(sectionPath, section.blob[section.offset:], 0644)
-		if err != nil {
-			return nil, util.ChildNewtError(err)
-		}
-		hexPath := MfgSectionHexPath(mi.basePkg.Name(), device)
-		mi.compiler.ConvertBinToHex(sectionPath, hexPath, section.offset)
-	}
-
-	manifest, err := mi.createManifest(cs)
-	if err != nil {
-		return nil, err
-	}
-
-	manifestPath := mi.ManifestPath()
-	if err := ioutil.WriteFile(manifestPath, manifest, 0644); err != nil {
-		return nil, util.FmtNewtError("Failed to write mfg manifest file: %s",
-			err.Error())
-	}
-
-	return mi.ToPaths(), nil
-}
diff --git a/newt/mfg/decode.go b/newt/mfg/decode.go
new file mode 100644
index 0000000..802e88d
--- /dev/null
+++ b/newt/mfg/decode.go
@@ -0,0 +1,310 @@
+/**
+ * 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 contains functionality for loading mfg definitions from `mfg.yml`
+// files.
+
+package mfg
+
+import (
+	"github.com/spf13/cast"
+
+	"mynewt.apache.org/newt/newt/ycfg"
+	"mynewt.apache.org/newt/util"
+)
+
+// Indicates that an element is located at the end of a flash area.
+const OFFSET_END = -1
+
+type DecodedTarget struct {
+	Name   string
+	Area   string
+	Offset int
+}
+
+type DecodedRaw struct {
+	Filename string
+	Area     string
+	Offset   int
+}
+
+type DecodedMmrRef struct {
+	Area string
+}
+
+type DecodedMeta struct {
+	Area     string
+	Hash     bool
+	FlashMap bool
+	Mmrs     []DecodedMmrRef
+}
+
+type DecodedMfg struct {
+	Targets []DecodedTarget
+	Raws    []DecodedRaw
+	Meta    *DecodedMeta
+
+	// Only required if no targets present.
+	Bsp string
+}
+
+func decodeOffsetStr(offsetStr string) (int, error) {
+	if offsetStr == "end" {
+		return OFFSET_END, nil
+	}
+
+	offsetInt, err := cast.ToIntE(offsetStr)
+	if err != nil {
+		return 0, util.FmtNewtError("invalid offset value: \"%s\"", offsetStr)
+	}
+
+	return offsetInt, nil
+}
+
+func decodeBool(kv map[string]interface{}, key string) (*bool, error) {
+	var bp *bool
+
+	val := kv[key]
+	if val != nil {
+		b, err := cast.ToBoolE(val)
+		if err != nil {
+			return nil, util.FmtNewtError(
+				"invalid `%s` value \"%v\"; "+
+					"value must be either \"true\" or \"false\"", key, val)
+		}
+
+		bp = &b
+	}
+
+	return bp, nil
+}
+
+func decodeBoolDflt(kv map[string]interface{}, key string,
+	dflt bool) (bool, error) {
+
+	bp, err := decodeBool(kv, key)
+	if err != nil {
+		return false, err
+	}
+
+	if bp == nil {
+		return dflt, nil
+	} else {
+		return *bp, nil
+	}
+}
+
+func decodeTarget(yamlTarget interface{}) (DecodedTarget, error) {
+	dt := DecodedTarget{}
+
+	kv, err := cast.ToStringMapE(yamlTarget)
+	if err != nil {
+		return dt, util.FmtNewtError(
+			"mfg contains invalid `mfg.targets` map: %s", err.Error())
+	}
+
+	nameVal := kv["name"]
+	if nameVal == nil {
+		return dt, util.FmtNewtError(
+			"mfg target entry missing required field \"name\"")
+	}
+	dt.Name = cast.ToString(nameVal)
+
+	areaVal := kv["area"]
+	if areaVal == nil {
+		return dt, util.FmtNewtError(
+			"target entry \"%s\" missing required field \"area\"", dt.Name)
+	}
+	dt.Area = cast.ToString(areaVal)
+
+	offsetVal := kv["offset"]
+	if offsetVal == nil {
+		return dt, util.FmtNewtError(
+			"target entry \"%s\" missing required field \"offset\"", dt.Name)
+	}
+	offsetStr := cast.ToString(offsetVal)
+	offsetInt, err := decodeOffsetStr(offsetStr)
+	if err != nil {
+		return dt, util.FmtNewtError(
+			"in target entry \"%s\": %s", dt.Name, err.Error())
+	}
+	dt.Offset = offsetInt
+
+	return dt, nil
+}
+
+func decodeRaw(yamlRaw interface{}, entryIdx int) (DecodedRaw, error) {
+	dr := DecodedRaw{}
+
+	kv, err := cast.ToStringMapE(yamlRaw)
+	if err != nil {
+		return dr, util.FmtNewtError(
+			"mfg contains invalid `mfg.raw` map: %s", err.Error())
+	}
+
+	areaVal := kv["area"]
+	if areaVal == nil {
+		return dr, util.FmtNewtError(
+			"raw entry missing required field \"area\"")
+	}
+	dr.Area = cast.ToString(areaVal)
+
+	offsetVal := kv["offset"]
+	if offsetVal == nil {
+		return dr, util.FmtNewtError(
+			"mfg raw entry missing required field \"offset\"")
+	}
+	offsetStr := cast.ToString(offsetVal)
+	offsetInt, err := decodeOffsetStr(offsetStr)
+	if err != nil {
+		return dr, util.FmtNewtError(
+			"in raw entry %d: %s", entryIdx, err.Error())
+	}
+	dr.Offset = offsetInt
+
+	filenameVal := kv["name"]
+	if filenameVal == nil {
+		return dr, util.FmtNewtError(
+			"mfg raw entry missing required field \"filename\"")
+	}
+	dr.Filename = cast.ToString(filenameVal)
+
+	return dr, nil
+}
+
+func decodeMmr(yamlMmr interface{}) (DecodedMmrRef, error) {
+	dm := DecodedMmrRef{}
+
+	kv, err := cast.ToStringMapE(yamlMmr)
+	if err != nil {
+		return dm, util.FmtNewtError(
+			"mfg meta contains invalid `mmrs` sequence: %s", err.Error())
+	}
+
+	areaVal := kv["area"]
+	if areaVal == nil {
+		return dm, util.FmtNewtError(
+			"mmr entry missing required field \"area\"")
+	}
+	dm.Area = cast.ToString(areaVal)
+
+	return dm, nil
+}
+
+func decodeMmrs(yamlMmrs interface{}) ([]DecodedMmrRef, error) {
+	yamlSlice, err := cast.ToSliceE(yamlMmrs)
+	if err != nil {
+		return nil, util.FmtNewtError(
+			"mfg meta contains invalid `mmrs` sequence: %s", err.Error())
+	}
+
+	mmrs := []DecodedMmrRef{}
+	for _, yamlMmr := range yamlSlice {
+		mmr, err := decodeMmr(yamlMmr)
+		if err != nil {
+			return nil, err
+		}
+		mmrs = append(mmrs, mmr)
+	}
+
+	return mmrs, nil
+}
+
+func decodeMeta(
+	kv map[string]interface{}) (DecodedMeta, error) {
+
+	dm := DecodedMeta{}
+
+	areaVal := kv["area"]
+	if areaVal == nil {
+		return dm, util.FmtNewtError(
+			"meta map missing required field \"area\"")
+	}
+	dm.Area = cast.ToString(areaVal)
+
+	hash, err := decodeBoolDflt(kv, "hash", false)
+	if err != nil {
+		return dm, err
+	}
+	dm.Hash = hash
+
+	fm, err := decodeBoolDflt(kv, "flash_map", false)
+	if err != nil {
+		return dm, err
+	}
+	dm.FlashMap = fm
+
+	yamlMmrs := kv["mmrs"]
+	if yamlMmrs != nil {
+		mmrs, err := decodeMmrs(yamlMmrs)
+		if err != nil {
+			return dm, err
+		}
+		dm.Mmrs = mmrs
+	}
+
+	return dm, nil
+}
+
+func decodeMfg(yc ycfg.YCfg) (DecodedMfg, error) {
+	dm := DecodedMfg{}
+
+	yamlTargets := yc.GetValSlice("mfg.targets", nil)
+	if yamlTargets != nil {
+		for _, yamlTarget := range yamlTargets {
+			t, err := decodeTarget(yamlTarget)
+			if err != nil {
+				return dm, err
+			}
+
+			dm.Targets = append(dm.Targets, t)
+		}
+	}
+
+	dm.Bsp = yc.GetValString("mfg.bsp", nil)
+
+	if len(dm.Targets) == 0 && dm.Bsp == "" {
+		return dm, util.FmtNewtError(
+			"\"mfg.bsp\" field required for mfg images without any targets")
+	}
+
+	itf := yc.GetValSlice("mfg.raw", nil)
+	slice := cast.ToSlice(itf)
+	if slice != nil {
+		for i, yamlRaw := range slice {
+			raw, err := decodeRaw(yamlRaw, i)
+			if err != nil {
+				return dm, err
+			}
+
+			dm.Raws = append(dm.Raws, raw)
+		}
+	}
+
+	yamlMeta := yc.GetValStringMap("mfg.meta", nil)
+	if yamlMeta != nil {
+		meta, err := decodeMeta(yamlMeta)
+		if err != nil {
+			return dm, err
+		}
+		dm.Meta = &meta
+	}
+
+	return dm, nil
+}
diff --git a/newt/mfg/emit.go b/newt/mfg/emit.go
new file mode 100644
index 0000000..b977fe7
--- /dev/null
+++ b/newt/mfg/emit.go
@@ -0,0 +1,342 @@
+/**
+ * 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"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+
+	"mynewt.apache.org/newt/artifact/flash"
+	"mynewt.apache.org/newt/artifact/image"
+	"mynewt.apache.org/newt/artifact/manifest"
+	"mynewt.apache.org/newt/artifact/mfg"
+	"mynewt.apache.org/newt/newt/builder"
+	"mynewt.apache.org/newt/newt/flashmap"
+	"mynewt.apache.org/newt/newt/target"
+	"mynewt.apache.org/newt/util"
+)
+
+// Current manufacturing image binary format version.
+const MANIFEST_FORMAT = 2
+
+// Represents a file copy operation.
+type CpEntry struct {
+	From string
+	To   string
+}
+
+type MfgEmitTarget struct {
+	Name         string
+	Offset       int
+	IsBoot       bool
+	BinPath      string
+	ElfPath      string
+	ManifestPath string
+}
+
+type MfgEmitRaw struct {
+	Filename string
+	Offset   int
+}
+
+type MfgEmitMetaMmr struct {
+	Area flash.FlashArea
+}
+
+type MfgEmitMeta struct {
+	Offset   int
+	Hash     bool
+	FlashMap bool
+	Mmrs     []MfgEmitMetaMmr
+}
+
+type MfgEmitter struct {
+	Name    string
+	Ver     image.ImageVersion
+	Targets []MfgEmitTarget
+	Raws    []MfgEmitRaw
+	Meta    *MfgEmitMeta
+
+	Mfg      mfg.Mfg
+	Device   int
+	FlashMap flashmap.FlashMap
+}
+
+// Calculates the source path of a target's binary.  Boot loader targets use
+// `.bin` files; image targets use `.img`.
+func targetSrcBinPath(t *target.Target, isBoot bool) string {
+	if isBoot {
+		return builder.AppBinPath(t.Name(), builder.BUILD_NAME_APP,
+			t.App().Name())
+	} else {
+		return builder.AppImgPath(t.Name(), builder.BUILD_NAME_APP,
+			t.App().Name())
+	}
+}
+
+// Calculates the source path of a target's `.elf` file.
+func targetSrcElfPath(t *target.Target) string {
+	return builder.AppElfPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name())
+}
+
+// Calculates the source path of a target's manifest file.
+func targetSrcManifestPath(t *target.Target) string {
+	return builder.ManifestPath(t.Name(), builder.BUILD_NAME_APP,
+		t.App().Name())
+}
+
+func newMfgEmitTarget(bt MfgBuildTarget) (MfgEmitTarget, error) {
+	return MfgEmitTarget{
+		Name:    bt.Target.FullName(),
+		Offset:  bt.Area.Offset + bt.Offset,
+		IsBoot:  bt.IsBoot,
+		BinPath: targetSrcBinPath(bt.Target, bt.IsBoot),
+		ElfPath: targetSrcElfPath(bt.Target),
+		ManifestPath: builder.ManifestPath(bt.Target.Name(),
+			builder.BUILD_NAME_APP, bt.Target.App().Name()),
+	}, nil
+}
+
+func newMfgEmitRaw(br MfgBuildRaw) MfgEmitRaw {
+	return MfgEmitRaw{
+		Filename: br.Filename,
+		Offset:   br.Area.Offset + br.Offset,
+	}
+}
+
+func newMfgEmitMeta(bm MfgBuildMeta, metaOff int) MfgEmitMeta {
+	mmrs := []MfgEmitMetaMmr{}
+	for _, bmmr := range bm.Mmrs {
+		mmr := MfgEmitMetaMmr{
+			Area: bmmr.Area,
+		}
+		mmrs = append(mmrs, mmr)
+	}
+
+	return MfgEmitMeta{
+		Offset:   bm.Area.Offset + metaOff,
+		Hash:     bm.Hash,
+		FlashMap: bm.FlashMap,
+		Mmrs:     mmrs,
+	}
+}
+
+// NewMfgEmitter creates an mfg emitter from an mfg builder.
+func NewMfgEmitter(mb MfgBuilder, name string, ver image.ImageVersion,
+	device int) (MfgEmitter, error) {
+
+	me := MfgEmitter{
+		Name:     name,
+		Ver:      ver,
+		Device:   device,
+		FlashMap: mb.Bsp.FlashMap,
+	}
+
+	m, err := mb.Build()
+	if err != nil {
+		return me, err
+	}
+	me.Mfg = m
+
+	for _, bt := range mb.Targets {
+		et, err := newMfgEmitTarget(bt)
+		if err != nil {
+			return me, err
+		}
+
+		me.Targets = append(me.Targets, et)
+	}
+
+	for _, br := range mb.Raws {
+		et := newMfgEmitRaw(br)
+		me.Raws = append(me.Raws, et)
+	}
+
+	if mb.Meta != nil {
+		mm := newMfgEmitMeta(*mb.Meta, me.Mfg.MetaOff)
+		me.Meta = &mm
+	}
+
+	return me, nil
+}
+
+// Calculates the necessary file copy operations for emitting an mfg image.
+func (me *MfgEmitter) calcCpEntries() []CpEntry {
+	entries := []CpEntry{}
+	for i, mt := range me.Targets {
+		var binTo string
+		if mt.IsBoot {
+			binTo = MfgTargetBinPath(i)
+		} else {
+			binTo = MfgTargetImgPath(i)
+		}
+
+		entry := CpEntry{
+			From: mt.BinPath,
+			To:   MfgBinDir(me.Name) + "/" + binTo,
+		}
+		entries = append(entries, entry)
+
+		entry = CpEntry{
+			From: mt.ElfPath,
+			To: MfgBinDir(me.Name) + "/" +
+				MfgTargetElfPath(i),
+		}
+		entries = append(entries, entry)
+
+		entry = CpEntry{
+			From: mt.ManifestPath,
+			To: MfgBinDir(me.Name) + "/" +
+				MfgTargetManifestPath(i),
+		}
+		entries = append(entries, entry)
+	}
+
+	return entries
+}
+
+func copyBinFiles(entries []CpEntry) error {
+	for _, entry := range entries {
+		if err := os.MkdirAll(filepath.Dir(entry.To), 0755); err != nil {
+			return util.ChildNewtError(err)
+		}
+
+		util.StatusMessage(util.VERBOSITY_VERBOSE, "copying file %s --> %s\n",
+			entry.From, entry.To)
+
+		if err := util.CopyFile(entry.From, entry.To); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// emitManifest generates an mfg manifest.
+func (me *MfgEmitter) emitManifest() ([]byte, error) {
+	hashBytes := me.Mfg.Meta.Hash()
+	if hashBytes == nil {
+		// No hash TLV; calculate hash manually.
+		bin, err := me.Mfg.Bytes(0xff)
+		if err != nil {
+			return nil, err
+		}
+		hashBytes = mfg.CalcHash(bin)
+	}
+	hashStr := fmt.Sprintf("%x", hashBytes)
+
+	mm := manifest.MfgManifest{
+		Name:       me.Name,
+		BuildTime:  time.Now().Format(time.RFC3339),
+		Format:     MANIFEST_FORMAT,
+		MfgHash:    hashStr,
+		Version:    me.Ver.String(),
+		Device:     me.Device,
+		BinPath:    mfg.MFG_IMG_FILENAME,
+		FlashAreas: me.FlashMap.SortedAreas(),
+	}
+
+	for i, t := range me.Targets {
+		mmt := manifest.MfgManifestTarget{
+			Name:         t.Name,
+			ManifestPath: MfgTargetManifestPath(i),
+			Offset:       t.Offset,
+		}
+
+		if t.IsBoot {
+			mmt.BinPath = MfgTargetBinPath(i)
+		} else {
+			mmt.ImagePath = MfgTargetImgPath(i)
+		}
+
+		mm.Targets = append(mm.Targets, mmt)
+	}
+
+	if me.Meta != nil {
+		mmm := manifest.MfgManifestMeta{
+			EndOffset: me.Mfg.MetaOff + int(me.Mfg.Meta.Footer.Size),
+			Size:      int(me.Mfg.Meta.Footer.Size),
+		}
+
+		mmm.Hash = me.Meta.Hash
+		mmm.FlashMap = me.Meta.FlashMap
+
+		for _, mmr := range me.Meta.Mmrs {
+			mmm.Mmrs = append(mmm.Mmrs, manifest.MfgManifestMetaMmr{
+				Area:      mmr.Area.Name,
+				Device:    mmr.Area.Device,
+				EndOffset: mmr.Area.Offset + mmr.Area.Size,
+			})
+		}
+
+		mm.Meta = &mmm
+	}
+
+	return mm.MarshalJson()
+}
+
+// @return                      [source-paths], [dest-paths], error
+func (me *MfgEmitter) Emit() ([]string, []string, error) {
+	mbin, err := me.Mfg.Bytes(0xff)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	cpEntries := me.calcCpEntries()
+	if err := copyBinFiles(cpEntries); err != nil {
+		return nil, nil, err
+	}
+
+	// Write mfgimg.bin
+	binPath := MfgBinPath(me.Name)
+	if err := os.MkdirAll(filepath.Dir(binPath), 0755); err != nil {
+		return nil, nil, util.ChildNewtError(err)
+	}
+	if err := ioutil.WriteFile(binPath, mbin, 0644); err != nil {
+		return nil, nil, err
+	}
+
+	// Write manifest.
+	manifest, err := me.emitManifest()
+	if err != nil {
+		return nil, nil, err
+	}
+
+	manifestPath := MfgManifestPath(me.Name)
+	if err := ioutil.WriteFile(manifestPath, manifest, 0644); err != nil {
+		return nil, nil, util.FmtNewtError(
+			"Failed to write mfg manifest file: %s", err.Error())
+	}
+
+	srcPaths := []string{}
+	dstPaths := []string{
+		binPath,
+		manifestPath,
+	}
+	for _, entry := range cpEntries {
+		srcPaths = append(srcPaths, entry.From)
+		dstPaths = append(dstPaths, entry.To)
+	}
+
+	return srcPaths, dstPaths, nil
+}
diff --git a/newt/mfg/load.go b/newt/mfg/load.go
deleted file mode 100644
index e17635b..0000000
--- a/newt/mfg/load.go
+++ /dev/null
@@ -1,317 +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 mfg
-
-import (
-	"fmt"
-	"io/ioutil"
-	"sort"
-	"strconv"
-	"strings"
-
-	"github.com/spf13/cast"
-
-	"mynewt.apache.org/newt/newt/newtutil"
-	"mynewt.apache.org/newt/newt/pkg"
-	"mynewt.apache.org/newt/newt/project"
-	"mynewt.apache.org/newt/newt/target"
-	"mynewt.apache.org/newt/newt/toolchain"
-	"mynewt.apache.org/newt/util"
-)
-
-const MFG_YAML_FILENAME string = "mfg.yml"
-
-type partSorter struct {
-	parts []mfgPart
-}
-
-func (s partSorter) Len() int {
-	return len(s.parts)
-}
-func (s partSorter) Swap(i, j int) {
-	s.parts[i], s.parts[j] = s.parts[j], s.parts[i]
-}
-func (s partSorter) Less(i, j int) bool {
-	return s.parts[i].offset < s.parts[j].offset
-}
-
-func sortParts(parts []mfgPart) []mfgPart {
-	sorter := partSorter{
-		parts: parts,
-	}
-
-	sort.Sort(sorter)
-	return sorter.parts
-}
-
-func (mi *MfgImage) loadError(
-	msg string, args ...interface{}) *util.NewtError {
-
-	return util.FmtNewtError("Error in %s mfg: %s", mi.basePkg.Name(),
-		fmt.Sprintf(msg, args...))
-
-}
-
-func (mi *MfgImage) loadTarget(targetName string) (
-	*target.Target, error) {
-
-	tgt := target.GetTargets()[targetName]
-	if tgt == nil {
-		return nil, mi.loadError("cannot resolve referenced target \"%s\"",
-			targetName)
-	}
-
-	return tgt, nil
-}
-
-func (mi *MfgImage) loadRawEntry(
-	entryIdx int, rawEntry map[string]string) (MfgRawEntry, error) {
-
-	raw := MfgRawEntry{}
-
-	var err error
-
-	deviceStr := rawEntry["device"]
-	if deviceStr == "" {
-		return raw, mi.loadError(
-			"raw entry %d missing required \"device\" field", entryIdx)
-	}
-
-	raw.device, err = util.AtoiNoOct(deviceStr)
-	if err != nil {
-		return raw, mi.loadError(
-			"raw entry %d contains invalid offset: %s", entryIdx, deviceStr)
-	}
-
-	offsetStr := rawEntry["offset"]
-	if offsetStr == "" {
-		return raw, mi.loadError(
-			"raw entry %d missing required \"offset\" field", entryIdx)
-	}
-
-	raw.offset, err = util.AtoiNoOct(offsetStr)
-	if err != nil {
-		return raw, mi.loadError(
-			"raw entry %d contains invalid offset: %s", entryIdx, offsetStr)
-	}
-
-	raw.filename = rawEntry["file"]
-	if raw.filename == "" {
-		return raw, mi.loadError(
-			"raw entry %d missing required \"file\" field", entryIdx)
-	}
-
-	if !strings.HasPrefix(raw.filename, "/") {
-		raw.filename = mi.basePkg.BasePath() + "/" + raw.filename
-	}
-
-	raw.data, err = ioutil.ReadFile(raw.filename)
-	if err != nil {
-		return raw, mi.loadError(
-			"error loading file for raw entry %d; filename=%s: %s",
-			entryIdx, raw.filename, err.Error())
-	}
-
-	return raw, nil
-}
-
-func (mi *MfgImage) detectInvalidDevices() error {
-	sectionIds := mi.sectionIds()
-	deviceIds := mi.bsp.FlashMap.DeviceIds()
-
-	deviceMap := map[int]struct{}{}
-	for _, device := range deviceIds {
-		deviceMap[device] = struct{}{}
-	}
-
-	invalidIds := []int{}
-	for _, sectionId := range sectionIds {
-		if _, ok := deviceMap[sectionId]; !ok {
-			invalidIds = append(invalidIds, sectionId)
-		}
-	}
-
-	if len(invalidIds) == 0 {
-		return nil
-	}
-
-	listStr := ""
-	for i, id := range invalidIds {
-		if i != 0 {
-			listStr += ", "
-		}
-		listStr += strconv.Itoa(id)
-	}
-
-	return util.FmtNewtError(
-		"image specifies flash devices that are not present in the BSP's "+
-			"flash map: %s", listStr)
-}
-
-func (mi *MfgImage) detectOverlaps() error {
-	type overlap struct {
-		part0 mfgPart
-		part1 mfgPart
-	}
-
-	overlaps := []overlap{}
-
-	dpMap, err := mi.devicePartMap()
-	if err != nil {
-		return err
-	}
-
-	// Iterate flash devices in order.
-	devices := make([]int, 0, len(dpMap))
-	for device, _ := range dpMap {
-		devices = append(devices, device)
-	}
-	sort.Ints(devices)
-
-	for _, device := range devices {
-		parts := dpMap[device]
-		for i, part0 := range parts[:len(parts)-1] {
-			part0End := part0.offset + len(part0.data)
-			for _, part1 := range parts[i+1:] {
-				// Parts are sorted by offset, so only one comparison is
-				// necessary to detect overlap.
-				if part1.offset < part0End {
-					overlaps = append(overlaps, overlap{
-						part0: part0,
-						part1: part1,
-					})
-				}
-			}
-		}
-	}
-
-	if len(overlaps) > 0 {
-		str := "flash overlaps detected:"
-		for _, overlap := range overlaps {
-
-			part0End := overlap.part0.offset + len(overlap.part0.data)
-			part1End := overlap.part1.offset + len(overlap.part1.data)
-			str += fmt.Sprintf("\n    * s%d [%s] (%d - %d) <=> [%s] (%d - %d)",
-				overlap.part0.device,
-				overlap.part0.name, overlap.part0.offset, part0End,
-				overlap.part1.name, overlap.part1.offset, part1End)
-		}
-
-		return util.NewNewtError(str)
-	}
-
-	return nil
-}
-
-func Load(basePkg *pkg.LocalPackage) (*MfgImage, error) {
-	v, err := newtutil.ReadConfig(basePkg.BasePath(),
-		strings.TrimSuffix(MFG_YAML_FILENAME, ".yml"))
-	if err != nil {
-		return nil, err
-	}
-
-	mi := &MfgImage{
-		basePkg: basePkg,
-	}
-
-	bootName := v.GetValString("mfg.bootloader", nil)
-	if bootName == "" {
-		return nil, mi.loadError("mfg.bootloader field required")
-	}
-	mi.boot, err = mi.loadTarget(bootName)
-	if err != nil {
-		return nil, err
-	}
-
-	imgNames := v.GetValStringSlice("mfg.images", nil)
-	if imgNames != nil {
-		for _, imgName := range imgNames {
-			imgTarget, err := mi.loadTarget(imgName)
-			if err != nil {
-				return nil, err
-			}
-
-			mi.images = append(mi.images, imgTarget)
-		}
-	}
-
-	if len(mi.images) > 2 {
-		return nil, mi.loadError("too many images (%d); maximum is 2",
-			len(mi.images))
-	}
-
-	itf := v.GetFirstVal("mfg.raw", nil)
-	slice := cast.ToSlice(itf)
-	if slice != nil {
-		for i, entryItf := range slice {
-			yamlEntry := cast.ToStringMapString(entryItf)
-			entry, err := mi.loadRawEntry(i, yamlEntry)
-			if err != nil {
-				return nil, err
-			}
-
-			mi.rawEntries = append(mi.rawEntries, entry)
-		}
-	}
-
-	proj := project.GetProject()
-
-	bspLpkg, err := proj.ResolvePackage(mi.basePkg.Repo(),
-		mi.boot.BspName)
-	if err != nil {
-		return nil, mi.loadError(
-			"could not resolve boot loader BSP package: %s",
-			mi.boot.BspName)
-	}
-	mi.bsp, err = pkg.NewBspPackage(bspLpkg)
-	if err != nil {
-		return nil, mi.loadError(err.Error())
-	}
-
-	compilerPkg, err := proj.ResolvePackage(mi.bsp.Repo(), mi.bsp.CompilerName)
-	if err != nil {
-		return nil, mi.loadError(err.Error())
-	}
-	mi.compiler, err = toolchain.NewCompiler(compilerPkg.BasePath(), "",
-		target.DEFAULT_BUILD_PROFILE)
-	if err != nil {
-		return nil, mi.loadError(err.Error())
-	}
-
-	for _, imgTarget := range mi.images {
-		if len(mi.images) > 1 && imgTarget.LoaderName != "" {
-			return nil, mi.loadError("only one image allowed in "+
-				"split image mode (%s is a split build)", imgTarget.Name())
-		}
-
-		if imgTarget.Bsp() != mi.bsp.LocalPackage {
-			return nil, mi.loadError(
-				"image target \"%s\" specified conflicting BSP; "+
-					"boot loader uses %s, image uses %s",
-				imgTarget.Name(), mi.bsp.Name(), imgTarget.BspName)
-		}
-	}
-
-	if err := mi.detectInvalidDevices(); err != nil {
-		return nil, err
-	}
-
-	return mi, nil
-}
diff --git a/newt/mfg/meta.go b/newt/mfg/meta.go
deleted file mode 100644
index 5e6e5ad..0000000
--- a/newt/mfg/meta.go
+++ /dev/null
@@ -1,248 +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 mfg
-
-import (
-	"bytes"
-	"crypto/sha256"
-	"encoding/binary"
-
-	"mynewt.apache.org/newt/artifact/flash"
-	"mynewt.apache.org/newt/newt/flashmap"
-	"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 = 1
-const META_TLV_CODE_HASH = 0x01
-const META_TLV_CODE_FLASH_AREA = 0x02
-
-const META_HASH_SZ = 32
-const META_FOOTER_SZ = 8
-const META_TLV_HASH_SZ = META_HASH_SZ
-const META_TLV_FLASH_AREA_SZ = 12
-
-type metaHeader struct {
-	version uint8  // 1
-	pad8    uint8  // 0xff
-	pad16   uint16 // 0xffff
-}
-
-type metaFooter struct {
-	size  uint16 // Includes header, TLVs, and footer.
-	pad16 uint16 // 0xffff
-	magic uint32 // META_MAGIC
-}
-
-type metaTlvHeader struct {
-	typ  uint8 // Indicates the type of data to follow.
-	size uint8 // The number of bytes of data to follow.
-}
-
-type metaTlvFlashArea struct {
-	header   metaTlvHeader
-	areaId   uint8  // Unique value identifying this flash area.
-	deviceId uint8  // Indicates host flash device (aka section number).
-	pad16    uint16 // 0xffff
-	offset   uint32 // The byte offset within the flash device.
-	size     uint32 // Size, in bytes, of entire flash area.
-}
-
-type metaTlvHash struct {
-	header metaTlvHeader
-	hash   [META_HASH_SZ]byte
-}
-
-func writeElem(elem interface{}, buf *bytes.Buffer) error {
-	/* XXX: Assume target platform uses little endian. */
-	if err := binary.Write(buf, binary.LittleEndian, elem); err != nil {
-		return util.ChildNewtError(err)
-	}
-	return nil
-}
-
-func writeHeader(buf *bytes.Buffer) error {
-	hdr := metaHeader{
-		version: META_VERSION,
-		pad8:    0xff,
-		pad16:   0xffff,
-	}
-	return writeElem(hdr, buf)
-}
-
-func writeFooter(buf *bytes.Buffer) error {
-	ftr := metaFooter{
-		size:  uint16(buf.Len() + META_FOOTER_SZ),
-		pad16: 0xffff,
-		magic: META_MAGIC,
-	}
-	return writeElem(ftr, buf)
-}
-
-func writeTlvHeader(typ uint8, size uint8, buf *bytes.Buffer) error {
-	tlvHdr := metaTlvHeader{
-		typ:  typ,
-		size: size,
-	}
-	return writeElem(tlvHdr, buf)
-}
-
-// Writes a single entry of the flash map TLV.
-func writeFlashMapEntry(area flash.FlashArea, buf *bytes.Buffer) error {
-	tlv := metaTlvFlashArea{
-		header: metaTlvHeader{
-			typ:  META_TLV_CODE_FLASH_AREA,
-			size: META_TLV_FLASH_AREA_SZ,
-		},
-		areaId:   uint8(area.Id),
-		deviceId: uint8(area.Device),
-		pad16:    0xffff,
-		offset:   uint32(area.Offset),
-		size:     uint32(area.Size),
-	}
-	return writeElem(tlv, buf)
-}
-
-// Writes a zeroed-out hash TLV.  The hash's original value must be zero for
-// the actual hash to be calculated later.  After the actual value is
-// calculated, it replaces the zeros in the TLV.
-func writeZeroHash(buf *bytes.Buffer) error {
-	tlv := metaTlvHash{
-		header: metaTlvHeader{
-			typ:  META_TLV_CODE_HASH,
-			size: META_TLV_HASH_SZ,
-		},
-		hash: [META_HASH_SZ]byte{},
-	}
-	return writeElem(tlv, buf)
-}
-
-// @return						meta-offset, hash-offset, error
-func insertMeta(section0Data []byte, flashMap flashmap.FlashMap) (
-	int, int, error) {
-
-	buf := &bytes.Buffer{}
-
-	if err := writeHeader(buf); err != nil {
-		return 0, 0, err
-	}
-
-	for _, area := range flashMap.SortedAreas() {
-		if err := writeFlashMapEntry(area, buf); err != nil {
-			return 0, 0, err
-		}
-	}
-
-	if err := writeZeroHash(buf); err != nil {
-		return 0, 0, err
-	}
-	hashSubOff := buf.Len() - META_HASH_SZ
-
-	if err := writeFooter(buf); err != nil {
-		return 0, 0, err
-	}
-
-	// The meta region gets placed at the very end of the boot loader slot.
-	bootArea, ok := flashMap.Areas[flash.FLASH_AREA_NAME_BOOTLOADER]
-	if !ok {
-		return 0, 0,
-			util.NewNewtError("Required boot loader flash area missing")
-	}
-
-	if bootArea.Size < buf.Len() {
-		return 0, 0, util.FmtNewtError(
-			"Boot loader flash area too small to accommodate meta region; "+
-				"boot=%d meta=%d", bootArea.Size, buf.Len())
-	}
-
-	metaOff := bootArea.Offset + bootArea.Size - buf.Len()
-	for i := metaOff; i < bootArea.Size; i++ {
-		if section0Data[i] != 0xff {
-			return 0, 0, util.FmtNewtError(
-				"Boot loader extends into meta region; "+
-					"meta region starts at offset %d", metaOff)
-		}
-	}
-
-	// Copy the meta region into the manufacturing image.  The meta hash is
-	// still zeroed.
-	copy(section0Data[metaOff:], buf.Bytes())
-
-	return metaOff, metaOff + hashSubOff, nil
-}
-
-// Calculates the SHA256 hash, using the full manufacturing image as input.
-// Hash-calculation algorithm is as follows:
-// 1. Concatenate sections in ascending order of index.
-// 2. Zero out the 32 bytes that will contain the hash.
-// 3. Apply SHA256 to the result.
-//
-// This function assumes that the 32 bytes of hash data have already been
-// zeroed.
-func calcMetaHash(sections [][]byte) []byte {
-	// Concatenate all sections.
-	blob := []byte{}
-	for _, section := range sections {
-		blob = append(blob, section...)
-	}
-
-	// Calculate hash.
-	hash := sha256.Sum256(blob)
-
-	return hash[:]
-}
diff --git a/newt/mfg/mfg.go b/newt/mfg/mfg.go
deleted file mode 100644
index 10c6c8e..0000000
--- a/newt/mfg/mfg.go
+++ /dev/null
@@ -1,98 +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 mfg
-
-import (
-	"sort"
-
-	"mynewt.apache.org/newt/artifact/image"
-	"mynewt.apache.org/newt/newt/pkg"
-	"mynewt.apache.org/newt/newt/target"
-	"mynewt.apache.org/newt/newt/toolchain"
-)
-
-type MfgRawEntry struct {
-	device   int
-	offset   int
-	filename string
-	data     []byte
-}
-
-// A chunk of data in the manufacturing image.  Can be a firmware image or a
-// raw entry (contents of a data file).
-type mfgPart struct {
-	device int
-	offset int
-	data   []byte
-	name   string
-}
-
-type MfgImage struct {
-	basePkg *pkg.LocalPackage
-
-	bsp      *pkg.BspPackage
-	compiler *toolchain.Compiler
-
-	boot       *target.Target
-	images     []*target.Target
-	rawEntries []MfgRawEntry
-
-	version image.ImageVersion
-}
-
-func (mi *MfgImage) SetVersion(ver image.ImageVersion) {
-	mi.version = ver
-}
-
-func (mi *MfgImage) imgApps(imageIdx int) (
-	app *pkg.LocalPackage, loader *pkg.LocalPackage) {
-
-	if imageIdx >= len(mi.images) {
-		return
-	}
-
-	t := mi.images[imageIdx]
-	app = t.App()
-	loader = t.Loader()
-	return
-}
-
-func (mi *MfgImage) sectionIds() []int {
-	idMap := map[int]struct{}{}
-
-	// The bootloader and images always go in section 0.
-	idMap[0] = struct{}{}
-
-	for _, entry := range mi.rawEntries {
-		idMap[entry.device] = struct{}{}
-	}
-
-	ids := make([]int, 0, len(idMap))
-	for id, _ := range idMap {
-		ids = append(ids, id)
-	}
-	sort.Ints(ids)
-
-	return ids
-}
-
-func (mi *MfgImage) NumImages() int {
-	return len(mi.images)
-}
diff --git a/newt/mfg/misc.go b/newt/mfg/misc.go
new file mode 100644
index 0000000..73d911d
--- /dev/null
+++ b/newt/mfg/misc.go
@@ -0,0 +1,92 @@
+/**
+ * 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 (
+	"strings"
+
+	"mynewt.apache.org/newt/artifact/image"
+	"mynewt.apache.org/newt/newt/builder"
+	"mynewt.apache.org/newt/newt/newtutil"
+	"mynewt.apache.org/newt/newt/pkg"
+)
+
+func loadDecodedMfg(basePath string) (DecodedMfg, error) {
+	yc, err := newtutil.ReadConfig(basePath,
+		strings.TrimSuffix(YAML_FILENAME, ".yml"))
+	if err != nil {
+		return DecodedMfg{}, err
+	}
+
+	dm, err := decodeMfg(yc)
+	if err != nil {
+		return DecodedMfg{}, err
+	}
+
+	return dm, nil
+}
+
+func LoadMfgEmitter(basePkg *pkg.LocalPackage,
+	ver image.ImageVersion) (MfgEmitter, error) {
+
+	dm, err := loadDecodedMfg(basePkg.BasePath())
+	if err != nil {
+		return MfgEmitter{}, err
+	}
+
+	mb, err := newMfgBuilder(basePkg, dm, ver)
+	if err != nil {
+		return MfgEmitter{}, err
+	}
+
+	device, err := mb.calcDevice()
+	if err != nil {
+		return MfgEmitter{}, err
+	}
+
+	me, err := NewMfgEmitter(mb, basePkg.Name(), ver, device)
+	if err != nil {
+		return MfgEmitter{}, err
+	}
+
+	return me, nil
+}
+
+func Upload(basePkg *pkg.LocalPackage) (string, error) {
+	dm, err := loadDecodedMfg(basePkg.BasePath())
+	if err != nil {
+		return "", err
+	}
+
+	mb, err := newMfgBuilder(basePkg, dm, image.ImageVersion{})
+	if err != nil {
+		return "", err
+	}
+
+	envSettings := map[string]string{"MFG_IMAGE": "1"}
+	binPath := MfgBinPath(basePkg.Name())
+	basePath := strings.TrimSuffix(binPath, ".bin")
+
+	if err := builder.Load(basePath, mb.Bsp, envSettings); err != nil {
+		return "", err
+	}
+
+	return binPath, nil
+}
diff --git a/newt/mfg/part.go b/newt/mfg/part.go
new file mode 100644
index 0000000..ca84d29
--- /dev/null
+++ b/newt/mfg/part.go
@@ -0,0 +1,96 @@
+/**
+ * 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"
+	"io"
+	"sort"
+
+	"mynewt.apache.org/newt/util"
+)
+
+// A chunk of data in the manufacturing image.  Can be a firmware image or a
+// raw entry (contents of a data file).
+type Part struct {
+	Name   string
+	Offset int
+	Data   []byte
+}
+
+type partSorter struct {
+	parts []Part
+}
+
+func (s partSorter) Len() int {
+	return len(s.parts)
+}
+func (s partSorter) Swap(i, j int) {
+	s.parts[i], s.parts[j] = s.parts[j], s.parts[i]
+}
+func (s partSorter) Less(i, j int) bool {
+	return s.parts[i].Offset < s.parts[j].Offset
+}
+
+func SortParts(parts []Part) []Part {
+	sorter := partSorter{
+		parts: parts,
+	}
+
+	sort.Sort(sorter)
+	return sorter.parts
+}
+
+func WriteParts(parts []Part, w io.Writer, eraseVal byte) (int, error) {
+	off := 0
+	for _, p := range parts {
+		if p.Offset < off {
+			return off, util.FmtNewtError(
+				"Invalid mfg parts: out of order")
+		}
+
+		// Pad the previous block up to the current offset.
+		for off < p.Offset {
+			if _, err := w.Write([]byte{eraseVal}); err != nil {
+				return off, util.ChildNewtError(err)
+			}
+			off++
+		}
+
+		// Write the current block's data.
+		size, err := w.Write(p.Data)
+		if err != nil {
+			return off, util.ChildNewtError(err)
+		}
+		off += size
+	}
+
+	// Note: the final block does not get padded.
+
+	return off, nil
+}
+
+func PartsBytes(parts []Part) ([]byte, error) {
+	b := &bytes.Buffer{}
+	if _, err := WriteParts(parts, b, 0xff); err != nil {
+		return nil, err
+	}
+	return b.Bytes(), nil
+}
diff --git a/newt/mfg/paths.go b/newt/mfg/paths.go
index 04cf448..3b3e1f6 100644
--- a/newt/mfg/paths.go
+++ b/newt/mfg/paths.go
@@ -21,162 +21,42 @@ package mfg
 
 import (
 	"fmt"
-	"path/filepath"
-	"strconv"
 
+	"mynewt.apache.org/newt/artifact/mfg"
 	"mynewt.apache.org/newt/newt/builder"
-	"mynewt.apache.org/newt/newt/pkg"
 )
 
+// Filename containing a manufacturing image definition.
+const YAML_FILENAME string = "mfg.yml"
+
 func MfgBinDir(mfgPkgName string) string {
 	return builder.BinRoot() + "/" + mfgPkgName
 }
 
-func MfgBootDir(mfgPkgName string) string {
-	return MfgBinDir(mfgPkgName) + "/bootloader"
-}
-
-func MfgBootBinPath(mfgPkgName string, appName string) string {
-	return MfgBootDir(mfgPkgName) + "/" + appName + ".elf.bin"
-}
-
-func MfgBootElfPath(mfgPkgName string, appName string) string {
-	return MfgBootDir(mfgPkgName) + "/" + appName + ".elf"
-}
-
-func MfgBootManifestPath(mfgPkgName string, appName string) string {
-	return MfgBootDir(mfgPkgName) + "/manifest.json"
-}
-
-// Image indices start at 0.
-func MfgImageBinDir(mfgPkgName string, imageIdx int) string {
-	return MfgBinDir(mfgPkgName) + "/image" + strconv.Itoa(imageIdx)
-}
-
-func MfgImageImgPath(mfgPkgName string, imageIdx int,
-	appName string) string {
-
-	return MfgImageBinDir(mfgPkgName, imageIdx) + "/" + appName + ".img"
-}
-
-func MfgImageElfPath(mfgPkgName string, imageIdx int,
-	appName string) string {
-
-	return MfgImageBinDir(mfgPkgName, imageIdx) + "/" + appName + ".elf"
-}
-
-func MfgImageManifestPath(mfgPkgName string, imageIdx int) string {
-	return MfgImageBinDir(mfgPkgName, imageIdx) + "/manifest.json"
-}
-
-func MfgSectionBinDir(mfgPkgName string) string {
-	return MfgBinDir(mfgPkgName) + "/sections"
-}
-
-func MfgSectionBinPath(mfgPkgName string, sectionNum int) string {
-	return fmt.Sprintf("%s/%s-s%d.bin", MfgSectionBinDir(mfgPkgName),
-		filepath.Base(mfgPkgName), sectionNum)
-}
-
-func MfgSectionHexPath(mfgPkgName string, sectionNum int) string {
-	return fmt.Sprintf("%s/%s-s%d.hex", MfgSectionBinDir(mfgPkgName),
-		filepath.Base(mfgPkgName), sectionNum)
+func MfgBinPath(mfgPkgName string) string {
+	return MfgBinDir(mfgPkgName) + "/" + mfg.MFG_IMG_FILENAME
 }
 
 func MfgManifestPath(mfgPkgName string) string {
-	return MfgBinDir(mfgPkgName) + "/manifest.json"
+	return MfgBinDir(mfgPkgName) + "/" + mfg.MFG_MANIFEST_FILENAME
 }
 
-func (mi *MfgImage) ManifestPath() string {
-	return MfgManifestPath(mi.basePkg.Name())
+func MfgTargetDir(targetNum int) string {
+	return fmt.Sprintf("targets/%d", targetNum)
 }
 
-func (mi *MfgImage) BootBinPath() string {
-	if mi.boot == nil {
-		return ""
-	}
-
-	return MfgBootBinPath(mi.basePkg.Name(),
-		pkg.ShortName(mi.boot.App()))
-}
-
-func (mi *MfgImage) BootElfPath() string {
-	if mi.boot == nil {
-		return ""
-	}
-
-	return MfgBootElfPath(mi.basePkg.Name(), pkg.ShortName(mi.boot.App()))
-}
-
-func (mi *MfgImage) BootManifestPath() string {
-	if mi.boot == nil {
-		return ""
-	}
-
-	return MfgBootManifestPath(mi.basePkg.Name(),
-		pkg.ShortName(mi.boot.App()))
-}
-
-func (mi *MfgImage) AppImgPath(imageIdx int) string {
-	app, _ := mi.imgApps(imageIdx)
-	if app == nil {
-		return ""
-	}
-
-	return MfgImageImgPath(mi.basePkg.Name(), imageIdx, pkg.ShortName(app))
-}
-
-func (mi *MfgImage) AppElfPath(imageIdx int) string {
-	app, _ := mi.imgApps(imageIdx)
-	if app == nil {
-		return ""
-	}
-
-	return MfgImageElfPath(mi.basePkg.Name(), imageIdx, pkg.ShortName(app))
+func MfgTargetBinPath(targetNum int) string {
+	return fmt.Sprintf("%s/binary.bin", MfgTargetDir(targetNum))
 }
 
-func (mi *MfgImage) LoaderImgPath(imageIdx int) string {
-	_, loader := mi.imgApps(imageIdx)
-	if loader == nil {
-		return ""
-	}
-
-	return MfgImageImgPath(mi.basePkg.Name(), imageIdx, pkg.ShortName(loader))
+func MfgTargetImgPath(targetNum int) string {
+	return fmt.Sprintf("%s/image.img", MfgTargetDir(targetNum))
 }
 
-func (mi *MfgImage) LoaderElfPath(imageIdx int) string {
-	_, loader := mi.imgApps(imageIdx)
-	if loader == nil {
-		return ""
-	}
-
-	return MfgImageElfPath(mi.basePkg.Name(), imageIdx, pkg.ShortName(loader))
+func MfgTargetElfPath(targetNum int) string {
+	return fmt.Sprintf("%s/elf.elf", MfgTargetDir(targetNum))
 }
 
-func (mi *MfgImage) ImageManifestPath(imageIdx int) string {
-	if imageIdx >= len(mi.images) {
-		return ""
-	}
-
-	return MfgImageManifestPath(mi.basePkg.Name(), imageIdx)
-}
-
-func (mi *MfgImage) SectionBinPaths() []string {
-	sectionIds := mi.sectionIds()
-
-	paths := make([]string, len(sectionIds))
-	for i, sectionId := range sectionIds {
-		paths[i] = MfgSectionBinPath(mi.basePkg.Name(), sectionId)
-	}
-	return paths
-}
-
-func (mi *MfgImage) SectionHexPaths() []string {
-	sectionIds := mi.sectionIds()
-
-	paths := make([]string, len(sectionIds))
-	for i, sectionId := range sectionIds {
-		paths[i] = MfgSectionHexPath(mi.basePkg.Name(), sectionId)
-	}
-	return paths
+func MfgTargetManifestPath(targetNum int) string {
+	return fmt.Sprintf("%s/manifest.json", MfgTargetDir(targetNum))
 }


[mynewt-newt] 16/17: Add readme for newt's mfg 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-newt.git

commit dc5ebdf40fa24ad96713c8e5a18b97299f4d000b
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Thu Dec 27 17:25:46 2018 -0800

    Add readme for newt's mfg support.
---
 newt/mfg/README.md | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)

diff --git a/newt/mfg/README.md b/newt/mfg/README.md
new file mode 100644
index 0000000..52dc5f0
--- /dev/null
+++ b/newt/mfg/README.md
@@ -0,0 +1,85 @@
+<!--
+#
+# 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.
+#
+-->
+
+# mfg
+
+### Definitions:
+
+| Term | Long Name | Meaning |
+| ---- | --------- | ------- |
+| Flashdev | Flash device | A single piece of flash hardware.  E.g., "internal flash", or "external SPI flash". |
+| Mfgimage | Manufacturing image | A file with the entire contents of a single flashdev. At manufacturing time, a separate mfgimage is typically written to each of the device's flashdevs. |
+| MMR | Manufacturing Meta Region | A chunk of read-only data included in an mfgimage. Contains identifying information for the mfgimage and other data that stays with the device until end of life. |
+
+### Design
+
+#### artifact/mfg
+
+The `artifact` library defines the `Mfg` type.  An `Mfg` is a barebones representation of an mfgimage.  It contains a raw binary of everything except the MMR, and a slice of MMR TLVs.  An `Mfg` is be converted to a flat byte slice with the following sequence:
+
+1. Calculate the SHA256 and add it to the MMR (`Mfg#CalcHash()`)
+2. Extract the byte-slice representation (`Mfg#Bytes()`)
+
+An `Mfg` can be parsed from a byte slice using the `Parse()` function.
+
+#### newt/mfg
+
+##### High level
+
+The newt tool creates mfgimages from:
+
+1. An mfg definition (including an `mfg.yml` file).
+2. Build artifacts for each target specified in the `mfg.yml` file.
+
+Therefore, mfgimage creation typically goes something like this:
+
+1. Build boot loader: `newt build <...>`
+2. Create [signed, encrypted] images: `newt create-image -2 <...> 1.2.3.4`
+3. Build mfgimage: `newt mfg create <...>`
+
+An mfgimage created by newt consists of:
+
+1. The binary flashdev contents.
+2. A `manifest.json` file describing the mfgimage.
+3. Build artifacts that were used as inputs.
+
+##### Details
+
+Newt performs a sequence of data structure transformations to produce the outputs listed above.  In the sequence depicted below, objects are enclosed in [brackets], steps are enclosed in (parentheses).
+
+```
+          (decode)          (build)          (emit)
+[MfgDecoder] --> [MfgBuilder] --> [MfgEmitter] --> [OUTPUT]
+```
+
+The steps are described below.
+
+###### 1. Decode
+
+Newt parses and verifies the `mfg.yml` file.
+
+###### 2. Build
+
+Newt uses the output of the decode step to determine which targets are included in the mfgimage.  It ensures the targets have been built and that they all share the same BSP.  Finally, it produces an `artifact/Mfg` object from the necessary binary files.
+
+###### 3. Emit
+
+Newt produces the manifest and writes all the mfgimage files to disk.


[mynewt-newt] 12/17: 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-newt.git

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

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

diff --git a/artifact/flash/flash.go b/artifact/flash/flash.go
index c37d2dd..6b85814 100644
--- a/artifact/flash/flash.go
+++ b/artifact/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/artifact/image/create.go b/artifact/image/create.go
index c3e8820..76e701d 100644
--- a/artifact/image/create.go
+++ b/artifact/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/artifact/image/image.go b/artifact/image/image.go
index 1ed093f..705abe6 100644
--- a/artifact/image/image.go
+++ b/artifact/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/artifact/image/key.go b/artifact/image/key.go
index 8345cd9..a343e2d 100644
--- a/artifact/image/key.go
+++ b/artifact/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/artifact/image/v1.go b/artifact/image/v1.go
index 5540d85..0dc10a5 100644
--- a/artifact/image/v1.go
+++ b/artifact/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/artifact/mfg/mfg.go b/artifact/mfg/mfg.go
index 8e999ad..3e29523 100644
--- a/artifact/mfg/mfg.go
+++ b/artifact/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/artifact/mfg/paths.go b/artifact/misc/misc.go
similarity index 70%
rename from artifact/mfg/paths.go
rename to artifact/misc/misc.go
index 483aca2..2f685e1 100644
--- a/artifact/mfg/paths.go
+++ b/artifact/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-newt] 15/17: 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-newt.git

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

    Include BSP name in mfg manifest
---
 artifact/manifest/mfg_manifest.go | 1 +
 newt/mfg/emit.go                  | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/artifact/manifest/mfg_manifest.go b/artifact/manifest/mfg_manifest.go
index 25cf9b7..bb27adc 100644
--- a/artifact/manifest/mfg_manifest.go
+++ b/artifact/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"`
diff --git a/newt/mfg/emit.go b/newt/mfg/emit.go
index 9eb8cf3..3974fda 100644
--- a/newt/mfg/emit.go
+++ b/newt/mfg/emit.go
@@ -80,6 +80,7 @@ type MfgEmitter struct {
 	Mfg      mfg.Mfg
 	Device   int
 	FlashMap flashmap.FlashMap
+	BspName  string
 }
 
 // Calculates the source path of a target's binary.  Boot loader targets use
@@ -150,6 +151,7 @@ func NewMfgEmitter(mb MfgBuilder, name string, ver image.ImageVersion,
 		Ver:      ver,
 		Device:   device,
 		FlashMap: mb.Bsp.FlashMap,
+		BspName:  mb.Bsp.FullName(),
 	}
 
 	m, err := mb.Build()
@@ -248,6 +250,7 @@ func (me *MfgEmitter) emitManifest() ([]byte, error) {
 		Device:     me.Device,
 		BinPath:    mfg.MFG_IMG_FILENAME,
 		FlashAreas: me.FlashMap.SortedAreas(),
+		Bsp:        me.BspName,
 	}
 
 	for i, t := range me.Targets {


[mynewt-newt] 04/17: Use "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-newt.git

commit 3e5cfc649ee4a07b2b2beeb315cd73835fa66850
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Tue Nov 20 17:49:41 2018 -0800

    Use "artifact" library
    
    Import and use the artifact libary.
---
 newt/cli/image_cmds.go  | 133 ++++++++++++++++--------------------------------
 newt/cli/mfg_cmds.go    |   2 +-
 newt/cli/run_cmds.go    |  38 ++++++++------
 newt/mfg/create.go      |   6 +--
 newt/mfg/meta.go        |   5 +-
 newt/mfg/mfg.go         |   2 +-
 newt/pkg/bsp_package.go |   6 +--
 newt/resolve/resolve.go |   8 +--
 newt/syscfg/syscfg.go   |   8 +--
 9 files changed, 86 insertions(+), 122 deletions(-)

diff --git a/newt/cli/image_cmds.go b/newt/cli/image_cmds.go
index 73459ed..cf742ac 100644
--- a/newt/cli/image_cmds.go
+++ b/newt/cli/image_cmds.go
@@ -23,38 +23,50 @@ import (
 	"strconv"
 
 	"github.com/spf13/cobra"
+
+	"mynewt.apache.org/newt/artifact/image"
 	"mynewt.apache.org/newt/newt/builder"
-	"mynewt.apache.org/newt/newt/image"
+	"mynewt.apache.org/newt/newt/imgprod"
 	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/util"
 )
 
 var useV1 bool
 var useV2 bool
+var encKeyFilename string
 
-func parseKeyArgs(args []string) ([]string, uint8, error) {
+// @return                      keys, key ID, error
+func parseKeyArgs(args []string) ([]image.ImageSigKey, uint8, error) {
 	if len(args) == 0 {
 		return nil, 0, nil
 	}
 
-	if len(args) == 1 {
-		return args, 0, nil
-	}
+	var keyId uint8
+	var keyFilenames []string
 
-	if image.UseV1 {
-		keyId64, err := strconv.ParseUint(args[1], 10, 8)
+	if len(args) == 1 {
+		keyFilenames = append(keyFilenames, args[0])
+	} else if useV1 {
+		keyIdUint, err := strconv.ParseUint(args[1], 10, 8)
 		if err != nil {
 			return nil, 0, util.NewNewtError("Key ID must be between 0-255")
 		}
-		return args[:1], uint8(keyId64), nil
+		keyId = uint8(keyIdUint)
+		keyFilenames = args[:1]
+	} else {
+		keyId = 0
+		keyFilenames = args
 	}
 
-	return args, 0, nil
+	keys, err := image.ReadKeys(keyFilenames)
+	if err != nil {
+		return nil, 0, err
+	}
+
+	return keys, keyId, nil
 }
 
 func createImageRunCmd(cmd *cobra.Command, args []string) {
-	var keyId uint8
-
 	if len(args) < 2 {
 		NewtUsage(cmd, util.NewNewtError("Must specify target and version"))
 	}
@@ -62,10 +74,9 @@ func createImageRunCmd(cmd *cobra.Command, args []string) {
 	if useV1 && useV2 {
 		NewtUsage(cmd, util.NewNewtError("Either -1, or -2, but not both"))
 	}
-	if useV2 {
-		image.UseV1 = false
-	} else {
-		image.UseV1 = true
+
+	if !useV2 {
+		useV1 = true
 	}
 
 	TryGetProject()
@@ -76,65 +87,30 @@ func createImageRunCmd(cmd *cobra.Command, args []string) {
 		NewtUsage(cmd, util.NewNewtError("Invalid target name: "+targetName))
 	}
 
-	version := args[1]
+	ver, err := image.ParseVersion(args[1])
+	if err != nil {
+		NewtUsage(cmd, err)
+	}
 
 	b, err := builder.NewTargetBuilder(t)
 	if err != nil {
 		NewtUsage(nil, err)
 	}
 
-	keystrs, keyId, err := parseKeyArgs(args[2:])
+	keys, _, err := parseKeyArgs(args[2:])
 	if err != nil {
 		NewtUsage(cmd, err)
 	}
 
-	if _, _, err := b.CreateImages(version, keystrs, keyId); err != nil {
+	if err := b.Build(); err != nil {
 		NewtUsage(nil, err)
 	}
-}
 
-func resignImageRunCmd(cmd *cobra.Command, args []string) {
-	var keyId uint8
-	var keystr string
-
-	if len(args) < 1 {
-		NewtUsage(cmd, util.NewNewtError("Must specify image to re-sign."))
-	}
-
-	if useV1 && useV2 {
-		NewtUsage(cmd, util.NewNewtError("Either -1, or -2, but not both"))
-	}
-	if useV2 {
-		image.UseV1 = false
+	if useV1 {
+		err = imgprod.ProduceAllV1(b, ver, keys, encKeyFilename)
 	} else {
-		image.UseV1 = true
-	}
-
-	imgName := args[0]
-	img, err := image.OldImage(imgName)
-	if err != nil {
-		NewtUsage(nil, err)
-		return
-	}
-
-	if len(args) > 1 {
-		if len(args) > 2 {
-			keyId64, err := strconv.ParseUint(args[2], 10, 8)
-			if err != nil {
-				NewtUsage(cmd,
-					util.NewNewtError("Key ID must be between 0-255"))
-			}
-			keyId = uint8(keyId64)
-		}
-		keystr = args[1]
-		err = img.SetKeyV1(keystr, keyId)
-		if err != nil {
-			NewtUsage(nil, err)
-			return
-		}
+		err = imgprod.ProduceAll(b, ver, keys, encKeyFilename)
 	}
-
-	err = img.ReSign()
 	if err != nil {
 		NewtUsage(nil, err)
 	}
@@ -185,44 +161,23 @@ func AddImageCommands(cmd *cobra.Command) {
 		"1", "1", false, "Use old image header format")
 	createImageCmd.PersistentFlags().BoolVarP(&useV2,
 		"2", "2", false, "Use new image header format")
-	createImageCmd.PersistentFlags().StringVarP(&image.PubKeyFile,
+	createImageCmd.PersistentFlags().StringVarP(&encKeyFilename,
 		"encrypt", "e", "", "Encrypt image using this public key")
 
 	cmd.AddCommand(createImageCmd)
 	AddTabCompleteFn(createImageCmd, targetList)
 
-	resignImageHelpText := "Sign/Re-sign an existing image file with the specified signing key.\nIf a signing key is not specified, the signing key in the current image\nis stripped.  "
-	resignImageHelpText += "A image header will be recreated!\n"
-	resignImageHelpText += "\nWarning: The image hash will change if you change key-id "
-	resignImageHelpText += "or the type of key used for signing.\n"
-	resignImageHelpText += "Default image format is version 1.\n"
-	resignImageHelpText += "RSA signature format by default for ver 1 image is PKCSv1.5\n"
-	resignImageHelpText += "RSA signature format for ver 2 image is RSA-PSS\n"
-
-	resignImageHelpEx := "  newt resign-image my_target1.img private.pem\n"
-	resignImageHelpEx += "  newt resign-image my_target1.img private.pem 5\n"
+	resignImageHelpText :=
+		"This command is obsolete; use the `larva` tool to resign images."
 
 	resignImageCmd := &cobra.Command{
-		Use:     "resign-image <image-file> [signing-key [key-id]]",
-		Short:   "Re-sign an image.",
-		Long:    resignImageHelpText,
-		Example: resignImageHelpEx,
-		Run:     resignImageRunCmd,
+		Use:   "resign-image",
+		Short: "Obsolete",
+		Long:  resignImageHelpText,
+		Run: func(cmd *cobra.Command, args []string) {
+			cmd.Help()
+		},
 	}
 
-	resignImageCmd.PersistentFlags().BoolVarP(&newtutil.NewtForce,
-		"force", "f", false,
-		"Ignore flash overflow errors during image creation")
-	resignImageCmd.PersistentFlags().BoolVar(&image.UseRsaPss,
-		"rsa-pss", false,
-		"Use RSA-PSS instead of PKCS#1 v1.5 for RSA sig. "+
-			"Meaningful for version 1 image format.")
-	resignImageCmd.PersistentFlags().BoolVarP(&useV1,
-		"1", "1", false, "Use old image header format")
-	resignImageCmd.PersistentFlags().BoolVarP(&useV2,
-		"2", "2", false, "Use new image header format")
-	resignImageCmd.PersistentFlags().StringVarP(&image.PubKeyFile,
-		"encrypt", "e", "", "Encrypt image using this public key")
-
 	cmd.AddCommand(resignImageCmd)
 }
diff --git a/newt/cli/mfg_cmds.go b/newt/cli/mfg_cmds.go
index 1e35d4e..513e2c4 100644
--- a/newt/cli/mfg_cmds.go
+++ b/newt/cli/mfg_cmds.go
@@ -22,7 +22,7 @@ package cli
 import (
 	"github.com/spf13/cobra"
 
-	"mynewt.apache.org/newt/newt/image"
+	"mynewt.apache.org/newt/artifact/image"
 	"mynewt.apache.org/newt/newt/mfg"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/util"
diff --git a/newt/cli/run_cmds.go b/newt/cli/run_cmds.go
index d3f3ed9..55b0b9b 100644
--- a/newt/cli/run_cmds.go
+++ b/newt/cli/run_cmds.go
@@ -23,7 +23,8 @@ import (
 	"fmt"
 	"github.com/spf13/cobra"
 
-	"mynewt.apache.org/newt/newt/image"
+	"mynewt.apache.org/newt/artifact/image"
+	"mynewt.apache.org/newt/newt/imgprod"
 	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/newt/parse"
 	"mynewt.apache.org/newt/util"
@@ -37,10 +38,8 @@ func runRunCmd(cmd *cobra.Command, args []string) {
 	if useV1 && useV2 {
 		NewtUsage(cmd, util.NewNewtError("Either -1, or -2, but not both"))
 	}
-	if useV2 {
-		image.UseV1 = false
-	} else {
-		image.UseV1 = true
+	if !useV2 {
+		useV1 = true
 	}
 
 	TryGetProject()
@@ -60,9 +59,9 @@ func runRunCmd(cmd *cobra.Command, args []string) {
 			NewtUsage(nil, err)
 		}
 	} else {
-		var version string = ""
+		var verStr string
 		if len(args) > 1 {
-			version = args[1]
+			verStr = args[1]
 		} else {
 			// If user did not provide version number and the target is not a
 			// bootloader and doesn't run in the simulator, then ask the user
@@ -78,29 +77,38 @@ func runRunCmd(cmd *cobra.Command, args []string) {
 			if !parse.ValueIsTrue(settings["BOOT_LOADER"]) &&
 				!parse.ValueIsTrue(settings["BSP_SIMULATED"]) {
 
-				version = "0"
+				verStr = "0"
 				fmt.Println("Enter image version(default 0):")
-				fmt.Scanf("%s\n", &version)
+				fmt.Scanf("%s\n", &verStr)
 			}
 		}
+
 		if err := b.Build(); err != nil {
 			NewtUsage(nil, err)
 		}
 
-		if len(version) > 0 {
-			var keystrs []string
-			var keyId uint8
+		if len(verStr) > 0 {
+			ver, err := image.ParseVersion(verStr)
+			if err != nil {
+				NewtUsage(cmd, err)
+			}
+
+			var keys []image.ImageSigKey
 
 			if len(args) > 2 {
-				keystrs, keyId, err = parseKeyArgs(args[2:])
+				keys, _, err = parseKeyArgs(args[2:])
 				if err != nil {
 					NewtUsage(cmd, err)
 				}
 			}
 
-			_, _, err = b.CreateImages(version, keystrs, keyId)
+			if useV1 {
+				err = imgprod.ProduceAllV1(b, ver, keys, "")
+			} else {
+				err = imgprod.ProduceAll(b, ver, keys, "")
+			}
 			if err != nil {
-				NewtUsage(cmd, err)
+				NewtUsage(nil, err)
 			}
 		}
 
diff --git a/newt/mfg/create.go b/newt/mfg/create.go
index 2ee9f07..4930329 100644
--- a/newt/mfg/create.go
+++ b/newt/mfg/create.go
@@ -28,8 +28,8 @@ import (
 	"sort"
 	"time"
 
+	"mynewt.apache.org/newt/artifact/flash"
 	"mynewt.apache.org/newt/newt/builder"
-	"mynewt.apache.org/newt/newt/flash"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/target"
 	"mynewt.apache.org/newt/util"
@@ -44,8 +44,8 @@ type mfgManifest struct {
 }
 
 type mfgSection struct {
-	offset     int
-	blob       []byte
+	offset int
+	blob   []byte
 }
 
 type createState struct {
diff --git a/newt/mfg/meta.go b/newt/mfg/meta.go
index 87aabe0..5e6e5ad 100644
--- a/newt/mfg/meta.go
+++ b/newt/mfg/meta.go
@@ -24,7 +24,8 @@ import (
 	"crypto/sha256"
 	"encoding/binary"
 
-	"mynewt.apache.org/newt/newt/flash"
+	"mynewt.apache.org/newt/artifact/flash"
+	"mynewt.apache.org/newt/newt/flashmap"
 	"mynewt.apache.org/newt/util"
 )
 
@@ -172,7 +173,7 @@ func writeZeroHash(buf *bytes.Buffer) error {
 }
 
 // @return						meta-offset, hash-offset, error
-func insertMeta(section0Data []byte, flashMap flash.FlashMap) (
+func insertMeta(section0Data []byte, flashMap flashmap.FlashMap) (
 	int, int, error) {
 
 	buf := &bytes.Buffer{}
diff --git a/newt/mfg/mfg.go b/newt/mfg/mfg.go
index 51d6e6d..10c6c8e 100644
--- a/newt/mfg/mfg.go
+++ b/newt/mfg/mfg.go
@@ -22,7 +22,7 @@ package mfg
 import (
 	"sort"
 
-	"mynewt.apache.org/newt/newt/image"
+	"mynewt.apache.org/newt/artifact/image"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/target"
 	"mynewt.apache.org/newt/newt/toolchain"
diff --git a/newt/pkg/bsp_package.go b/newt/pkg/bsp_package.go
index 1406593..81196ff 100644
--- a/newt/pkg/bsp_package.go
+++ b/newt/pkg/bsp_package.go
@@ -24,7 +24,7 @@ import (
 	"runtime"
 	"strings"
 
-	"mynewt.apache.org/newt/newt/flash"
+	"mynewt.apache.org/newt/newt/flashmap"
 	"mynewt.apache.org/newt/newt/interfaces"
 	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/newt/ycfg"
@@ -41,7 +41,7 @@ type BspPackage struct {
 	Part2LinkerScripts []string /* scripts to link app to second partition */
 	DownloadScript     string
 	DebugScript        string
-	FlashMap           flash.FlashMap
+	FlashMap           flashmap.FlashMap
 	BspV               ycfg.YCfg
 }
 
@@ -162,7 +162,7 @@ func (bsp *BspPackage) Reload(settings map[string]string) error {
 		return util.NewNewtError("BSP does not specify a flash map " +
 			"(bsp.flash_map)")
 	}
-	bsp.FlashMap, err = flash.Read(ymlFlashMap)
+	bsp.FlashMap, err = flashmap.Read(ymlFlashMap)
 	if err != nil {
 		return err
 	}
diff --git a/newt/resolve/resolve.go b/newt/resolve/resolve.go
index c9ccb42..45944c6 100644
--- a/newt/resolve/resolve.go
+++ b/newt/resolve/resolve.go
@@ -26,7 +26,7 @@ import (
 
 	log "github.com/Sirupsen/logrus"
 
-	"mynewt.apache.org/newt/newt/flash"
+	"mynewt.apache.org/newt/newt/flashmap"
 	"mynewt.apache.org/newt/newt/logcfg"
 	"mynewt.apache.org/newt/newt/parse"
 	"mynewt.apache.org/newt/newt/pkg"
@@ -61,7 +61,7 @@ type Resolver struct {
 	pkgMap           map[*pkg.LocalPackage]*ResolvePackage
 	seedPkgs         []*pkg.LocalPackage
 	injectedSettings map[string]string
-	flashMap         flash.FlashMap
+	flashMap         flashmap.FlashMap
 	cfg              syscfg.Cfg
 	lcfg             logcfg.LCfg
 	sysinitCfg       sysinit.SysinitCfg
@@ -131,7 +131,7 @@ type Resolution struct {
 func newResolver(
 	seedPkgs []*pkg.LocalPackage,
 	injectedSettings map[string]string,
-	flashMap flash.FlashMap) *Resolver {
+	flashMap flashmap.FlashMap) *Resolver {
 
 	r := &Resolver{
 		apis:             map[string]resolveApi{},
@@ -800,7 +800,7 @@ func ResolveFull(
 	loaderSeeds []*pkg.LocalPackage,
 	appSeeds []*pkg.LocalPackage,
 	injectedSettings map[string]string,
-	flashMap flash.FlashMap) (*Resolution, error) {
+	flashMap flashmap.FlashMap) (*Resolution, error) {
 
 	// First, calculate syscfg and determine which package provides each
 	// required API.  Syscfg and APIs are project-wide; that is, they are
diff --git a/newt/syscfg/syscfg.go b/newt/syscfg/syscfg.go
index 8a81a68..3010937 100644
--- a/newt/syscfg/syscfg.go
+++ b/newt/syscfg/syscfg.go
@@ -34,7 +34,7 @@ import (
 	log "github.com/Sirupsen/logrus"
 	"github.com/spf13/cast"
 
-	"mynewt.apache.org/newt/newt/flash"
+	"mynewt.apache.org/newt/newt/flashmap"
 	"mynewt.apache.org/newt/newt/interfaces"
 	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/newt/parse"
@@ -738,7 +738,7 @@ func (cfg *Cfg) detectPriorityViolations() {
 }
 
 // Detects all flash conflict errors in the syscfg and records them internally.
-func (cfg *Cfg) detectFlashConflicts(flashMap flash.FlashMap) {
+func (cfg *Cfg) detectFlashConflicts(flashMap flashmap.FlashMap) {
 	entries := cfg.settingsOfType(CFG_SETTING_TYPE_FLASH_OWNER)
 
 	areaEntryMap := map[string][]CfgEntry{}
@@ -1072,7 +1072,7 @@ func (cfg *Cfg) detectAmbiguities() {
 
 // Detects and records errors in the build's syscfg.  This should only be
 // called after APIs are resolved to avoid false positives.
-func (cfg *Cfg) DetectErrors(flashMap flash.FlashMap) {
+func (cfg *Cfg) DetectErrors(flashMap flashmap.FlashMap) {
 	cfg.detectAmbiguities()
 	cfg.detectViolations()
 	cfg.detectPriorityViolations()
@@ -1081,7 +1081,7 @@ func (cfg *Cfg) DetectErrors(flashMap flash.FlashMap) {
 
 func Read(lpkgs []*pkg.LocalPackage, apis []string,
 	injectedSettings map[string]string, settings map[string]string,
-	flashMap flash.FlashMap) (Cfg, error) {
+	flashMap flashmap.FlashMap) (Cfg, error) {
 
 	cfg := NewCfg()
 	for k, v := range injectedSettings {


[mynewt-newt] 08/17: Larva tool

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

commit 4f6740563035339a1029a49155624822e9fe62af
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Tue Nov 20 17:37:59 2018 -0800

    Larva tool
    
    This tool has the following functionality:
        * Parse a `.img` file and display it in JSON format.
        * Remove signatures from a `.img` file.
        * Add signatures to a `.img` file.
        * Split a manufacturing image into several files, one for each flash
          area.
        * Join a split manufacturing image.
---
 larva/cli/image_cmds.go | 205 ++++++++++++++++++++++++++++++++++++++++++++++++
 larva/cli/mfg_cmds.go   | 184 +++++++++++++++++++++++++++++++++++++++++++
 larva/cli/util.go       |  66 ++++++++++++++++
 larva/mfg/mfg.go        | 147 ++++++++++++++++++++++++++++++++++
 larva/mimg.go           |  84 ++++++++++++++++++++
 5 files changed, 686 insertions(+)

diff --git a/larva/cli/image_cmds.go b/larva/cli/image_cmds.go
new file mode 100644
index 0000000..ce1093e
--- /dev/null
+++ b/larva/cli/image_cmds.go
@@ -0,0 +1,205 @@
+/**
+ * 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 cli
+
+import (
+	"encoding/hex"
+	"fmt"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/spf13/cobra"
+
+	"mynewt.apache.org/newt/artifact/image"
+	"mynewt.apache.org/newt/util"
+)
+
+func readImage(filename string) (image.Image, error) {
+	img, err := image.ReadImage(filename)
+	if err != nil {
+		return img, err
+	}
+
+	log.Debugf("Successfully read image %s", filename)
+	return img, nil
+}
+
+func writeImage(img image.Image, filename string) error {
+	if err := img.WriteToFile(filename); err != nil {
+		return err
+	}
+
+	log.Debugf("Wrote image %s", filename)
+	return nil
+}
+
+func reportDupSigs(img image.Image) {
+	m := map[string]struct{}{}
+	dups := map[string]struct{}{}
+
+	for _, tlv := range img.Tlvs {
+		if tlv.Header.Type == image.IMAGE_TLV_KEYHASH {
+			h := hex.EncodeToString(tlv.Data)
+			if _, ok := m[h]; ok {
+				dups[h] = struct{}{}
+			} else {
+				m[h] = struct{}{}
+			}
+		}
+	}
+
+	if len(dups) > 0 {
+		fmt.Printf("Warning: duplicate signatures detected:\n")
+		for d, _ := range dups {
+			fmt.Printf("    %s\n", d)
+		}
+	}
+}
+
+func runShowCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 1 {
+		LarvaUsage(cmd, nil)
+	}
+
+	img, err := readImage(args[0])
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	s, err := img.Json()
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+	fmt.Printf("%s\n", s)
+}
+
+func runSignCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 2 {
+		LarvaUsage(cmd, nil)
+	}
+
+	inFilename := args[0]
+	outFilename, err := CalcOutFilename(inFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	img, err := readImage(inFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	keys, err := image.ReadKeys(args[1:])
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	hash, err := img.Hash()
+	if err != nil {
+		LarvaUsage(cmd, util.FmtNewtError(
+			"Failed to read hash from specified image: %s", err.Error()))
+	}
+
+	tlvs, err := image.GenerateSigTlvs(keys, hash)
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	img.Tlvs = append(img.Tlvs, tlvs...)
+
+	reportDupSigs(img)
+
+	if err := writeImage(img, outFilename); err != nil {
+		LarvaUsage(nil, err)
+	}
+}
+
+func runRmsigsCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 1 {
+		LarvaUsage(cmd, nil)
+	}
+
+	inFilename := args[0]
+	outFilename, err := CalcOutFilename(inFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	img, err := readImage(inFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	cnt := img.RemoveTlvsIf(func(tlv image.ImageTlv) bool {
+		return tlv.Header.Type == image.IMAGE_TLV_KEYHASH ||
+			tlv.Header.Type == image.IMAGE_TLV_RSA2048 ||
+			tlv.Header.Type == image.IMAGE_TLV_ECDSA224 ||
+			tlv.Header.Type == image.IMAGE_TLV_ECDSA256
+	})
+
+	log.Debugf("Removed %d existing signatures", cnt)
+
+	if err := writeImage(img, outFilename); err != nil {
+		LarvaUsage(nil, err)
+	}
+}
+
+func AddImageCommands(cmd *cobra.Command) {
+	imageCmd := &cobra.Command{
+		Use:   "image",
+		Short: "Shows and manipulates Mynewt image (.img) files",
+		Run: func(cmd *cobra.Command, args []string) {
+			cmd.Usage()
+		},
+	}
+	cmd.AddCommand(imageCmd)
+
+	showCmd := &cobra.Command{
+		Use:   "show <img-file>",
+		Short: "Displays JSON describing a Mynewt image file",
+		Run:   runShowCmd,
+	}
+	imageCmd.AddCommand(showCmd)
+
+	signCmd := &cobra.Command{
+		Use:   "sign <img-file> <priv-key-pem> [priv-key-pem...]",
+		Short: "Appends signatures to a Mynewt image file",
+		Run:   runSignCmd,
+	}
+
+	signCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o", "",
+		"File to write to")
+	signCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+		"Replace input file")
+
+	imageCmd.AddCommand(signCmd)
+
+	rmsigsCmd := &cobra.Command{
+		Use:   "rmsigs",
+		Short: "Removes all signatures from a Mynewt image file",
+		Run:   runRmsigsCmd,
+	}
+
+	rmsigsCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o", "",
+		"File to write to")
+	rmsigsCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+		"Replace input file")
+
+	imageCmd.AddCommand(rmsigsCmd)
+}
diff --git a/larva/cli/mfg_cmds.go b/larva/cli/mfg_cmds.go
new file mode 100644
index 0000000..3a9e6ac
--- /dev/null
+++ b/larva/cli/mfg_cmds.go
@@ -0,0 +1,184 @@
+/**
+ * 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 cli
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/spf13/cobra"
+
+	"mynewt.apache.org/newt/artifact/flash"
+	"mynewt.apache.org/newt/artifact/manifest"
+	"mynewt.apache.org/newt/larva/mfg"
+	"mynewt.apache.org/newt/util"
+)
+
+var optDeviceNum int
+
+func readManifest(filename string) (manifest.Manifest, error) {
+	man, err := manifest.ReadManifest(filename)
+	if err != nil {
+		return man, err
+	}
+
+	log.Debugf("Successfully read manifest %s", filename)
+	return man, nil
+}
+
+func readFlashAreas(manifestFilename string) ([]flash.FlashArea, error) {
+	man, err := readManifest(manifestFilename)
+	if err != nil {
+		return nil, err
+	}
+
+	areas := flash.SortFlashAreasByDevOff(man.FlashAreas)
+
+	overlaps, conflicts := flash.DetectErrors(areas)
+	if len(overlaps) > 0 || len(conflicts) > 0 {
+		return nil, util.NewNewtError(flash.ErrorText(overlaps, conflicts))
+	}
+
+	if err := mfg.VerifyAreas(areas, optDeviceNum); err != nil {
+		return nil, err
+	}
+
+	log.Debugf("Successfully read flash areas: %+v", areas)
+	return areas, nil
+}
+
+func createMfgMap(binDir string, areas []flash.FlashArea) (mfg.MfgMap, error) {
+	mm := mfg.MfgMap{}
+
+	for _, area := range areas {
+		filename := fmt.Sprintf("%s/%s.bin", binDir, area.Name)
+		bin, err := ioutil.ReadFile(filename)
+		if err != nil {
+			if !os.IsNotExist(err) {
+				return nil, util.ChildNewtError(err)
+			}
+		} else {
+			mm[area.Name] = bin
+		}
+	}
+
+	return mm, nil
+}
+
+func runSplitCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 3 {
+		LarvaUsage(cmd, nil)
+	}
+
+	imgFilename := args[0]
+	manFilename := args[1]
+	outDir := args[2]
+
+	mfgBin, err := ioutil.ReadFile(imgFilename)
+	if err != nil {
+		LarvaUsage(cmd, util.FmtNewtError(
+			"Failed to read manufacturing image: %s", err.Error()))
+	}
+
+	areas, err := readFlashAreas(manFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	mm, err := mfg.Split(mfgBin, optDeviceNum, areas)
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	if err := os.Mkdir(outDir, os.ModePerm); err != nil {
+		LarvaUsage(nil, util.ChildNewtError(err))
+	}
+
+	for name, data := range mm {
+		filename := fmt.Sprintf("%s/%s.bin", outDir, name)
+		if err := ioutil.WriteFile(filename, data, os.ModePerm); err != nil {
+			LarvaUsage(nil, util.ChildNewtError(err))
+		}
+	}
+}
+
+func runJoinCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 3 {
+		LarvaUsage(cmd, nil)
+	}
+
+	binDir := args[0]
+	manFilename := args[1]
+	outFilename := args[2]
+
+	areas, err := readFlashAreas(manFilename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	mm, err := createMfgMap(binDir, areas)
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	mfgBin, err := mfg.Join(mm, 0xff, areas)
+	if err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	if err := ioutil.WriteFile(outFilename, mfgBin, os.ModePerm); err != nil {
+		LarvaUsage(nil, util.ChildNewtError(err))
+	}
+}
+
+func AddMfgCommands(cmd *cobra.Command) {
+	mfgCmd := &cobra.Command{
+		Use:   "mfg",
+		Short: "Manipulates Mynewt manufacturing images",
+		Run: func(cmd *cobra.Command, args []string) {
+			cmd.Usage()
+		},
+	}
+	cmd.AddCommand(mfgCmd)
+
+	splitCmd := &cobra.Command{
+		Use:   "split <mfg-image> <manifest> <out-dir>",
+		Short: "Splits a Mynewt mfg section into several files",
+		Run:   runSplitCmd,
+	}
+
+	splitCmd.PersistentFlags().IntVarP(&optDeviceNum, "device", "d", 0,
+		"Flash device number")
+
+	mfgCmd.AddCommand(splitCmd)
+
+	joinCmd := &cobra.Command{
+		Use:   "join <bin-dir> <manifest> <out-mfg-image>",
+		Short: "Joins a split mfg section into a single file",
+		Run:   runJoinCmd,
+	}
+
+	joinCmd.PersistentFlags().IntVarP(&optDeviceNum, "device", "d", 0,
+		"Flash device number")
+
+	mfgCmd.AddCommand(joinCmd)
+}
diff --git a/larva/cli/util.go b/larva/cli/util.go
new file mode 100644
index 0000000..20c3d5e
--- /dev/null
+++ b/larva/cli/util.go
@@ -0,0 +1,66 @@
+/**
+ * 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 cli
+
+import (
+	"fmt"
+	"os"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/spf13/cobra"
+
+	"mynewt.apache.org/newt/util"
+)
+
+var OptOutFilename string
+var OptInPlace bool
+
+func LarvaUsage(cmd *cobra.Command, err error) {
+	if err != nil {
+		sErr := err.(*util.NewtError)
+		log.Debugf("%s", sErr.StackTrace)
+		fmt.Fprintf(os.Stderr, "Error: %s\n", sErr.Text)
+	}
+
+	if cmd != nil {
+		fmt.Printf("\n")
+		fmt.Printf("%s - ", cmd.Name())
+		cmd.Help()
+	}
+	os.Exit(1)
+}
+
+func CalcOutFilename(inFilename string) (string, error) {
+	if OptOutFilename != "" {
+		if OptInPlace {
+			return "", util.FmtNewtError(
+				"Only one of --outfile (-o) or --inplace (-i) options allowed")
+		}
+
+		return OptOutFilename, nil
+	}
+
+	if !OptInPlace {
+		return "", util.FmtNewtError(
+			"--outfile (-o) or --inplace (-i) option required")
+	}
+
+	return inFilename, nil
+}
diff --git a/larva/mfg/mfg.go b/larva/mfg/mfg.go
new file mode 100644
index 0000000..0cbbbb0
--- /dev/null
+++ b/larva/mfg/mfg.go
@@ -0,0 +1,147 @@
+/**
+ * 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"
+	"sort"
+	"strings"
+
+	"mynewt.apache.org/newt/artifact/flash"
+	"mynewt.apache.org/newt/util"
+)
+
+type MfgMap map[string][]byte
+
+func errInvalidArea(areaName string, format string,
+	args ...interface{}) error {
+
+	suffix := fmt.Sprintf(format, args...)
+	return util.FmtNewtError("Invalid flash area \"%s\": %s", areaName, suffix)
+}
+
+func verifyArea(area flash.FlashArea, minOffset int) error {
+	if area.Offset < minOffset {
+		return errInvalidArea(area.Name, "invalid offset %d; expected >= %d",
+			area.Offset, minOffset)
+	}
+
+	if area.Size < 0 {
+		return errInvalidArea(area.Name, "invalid size %d", area.Size)
+	}
+
+	return nil
+}
+
+// `areas` must be sorted by device ID, then by offset.
+func VerifyAreas(areas []flash.FlashArea, deviceNum int) error {
+	off := 0
+	for _, area := range areas {
+		if area.Device == deviceNum {
+			if err := verifyArea(area, off); err != nil {
+				return err
+			}
+			off += area.Size
+		}
+	}
+
+	return nil
+}
+
+func Split(mfgBin []byte, deviceNum int,
+	areas []flash.FlashArea) (MfgMap, error) {
+
+	mm := MfgMap{}
+
+	for _, area := range areas {
+		if _, ok := mm[area.Name]; ok {
+			return nil, util.FmtNewtError(
+				"two or more flash areas with same name: \"%s\"", area.Name)
+		}
+
+		if area.Device == deviceNum && area.Offset < len(mfgBin) {
+			end := area.Offset + area.Size
+			if end > len(mfgBin) {
+				return nil, util.FmtNewtError(
+					"area \"%s\" (offset=%d size=%d) "+
+						"extends beyond end of manufacturing image",
+					area.Name, area.Offset, area.Size)
+			}
+
+			mm[area.Name] = mfgBin[area.Offset:end]
+		}
+	}
+
+	return mm, nil
+}
+
+// `areas` must be sorted by device ID, then by offset.
+func Join(mm MfgMap, eraseVal byte, areas []flash.FlashArea) ([]byte, error) {
+	// Ensure all areas in the mfg map belong to the same flash device.
+	device := -1
+	for _, area := range areas {
+		if _, ok := mm[area.Name]; ok {
+			if device == -1 {
+				device = area.Device
+			} else if device != area.Device {
+				return nil, util.FmtNewtError(
+					"multiple flash devices: %d != %d", device, area.Device)
+			}
+		}
+	}
+
+	// Keep track of which areas we haven't seen yet.
+	unseen := map[string]struct{}{}
+	for name, _ := range mm {
+		unseen[name] = struct{}{}
+	}
+
+	joined := []byte{}
+
+	off := 0
+	for _, area := range areas {
+		bin := mm[area.Name]
+		if bin == nil {
+			break
+		}
+		delete(unseen, area.Name)
+
+		padSize := area.Offset - off
+		for i := 0; i < padSize; i++ {
+			joined = append(joined, 0xff)
+		}
+
+		joined = append(joined, bin...)
+	}
+
+	// Ensure we processed every area in the mfg map.
+	if len(unseen) > 0 {
+		names := []string{}
+		for name, _ := range unseen {
+			names = append(names, name)
+		}
+		sort.Strings(names)
+
+		return nil, util.FmtNewtError(
+			"unprocessed flash areas: %s", strings.Join(names, ", "))
+	}
+
+	return joined, nil
+}
diff --git a/larva/mimg.go b/larva/mimg.go
new file mode 100644
index 0000000..04cddec
--- /dev/null
+++ b/larva/mimg.go
@@ -0,0 +1,84 @@
+/**
+ * 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 main
+
+import (
+	"fmt"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/spf13/cobra"
+
+	"mynewt.apache.org/newt/larva/cli"
+	"mynewt.apache.org/newt/util"
+)
+
+var LarvaLogLevel log.Level
+var larvaVersion = "0.0.1"
+
+func main() {
+	larvaHelpText := ""
+	larvaHelpEx := ""
+
+	logLevelStr := ""
+	larvaCmd := &cobra.Command{
+		Use:     "larva",
+		Short:   "larva is a tool to help you compose and build your own OS",
+		Long:    larvaHelpText,
+		Example: larvaHelpEx,
+		PersistentPreRun: func(cmd *cobra.Command, args []string) {
+			logLevel, err := log.ParseLevel(logLevelStr)
+			if err != nil {
+				cli.LarvaUsage(nil, util.ChildNewtError(err))
+			}
+			LarvaLogLevel = logLevel
+
+			if err := util.Init(LarvaLogLevel, "",
+				util.VERBOSITY_DEFAULT); err != nil {
+
+				cli.LarvaUsage(nil, err)
+			}
+		},
+
+		Run: func(cmd *cobra.Command, args []string) {
+			cmd.Help()
+		},
+	}
+
+	larvaCmd.PersistentFlags().StringVarP(&logLevelStr, "loglevel", "l",
+		"WARN", "Log level")
+
+	versHelpText := `Display the larva version number`
+	versHelpEx := "  larva version"
+	versCmd := &cobra.Command{
+		Use:     "version",
+		Short:   "Display the larva version number",
+		Long:    versHelpText,
+		Example: versHelpEx,
+		Run: func(cmd *cobra.Command, args []string) {
+			fmt.Printf("%s\n", larvaVersion)
+		},
+	}
+	larvaCmd.AddCommand(versCmd)
+
+	cli.AddImageCommands(larvaCmd)
+	cli.AddMfgCommands(larvaCmd)
+
+	larvaCmd.Execute()
+}


[mynewt-newt] 13/17: newt - Use latest 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-newt.git

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

    newt - Use latest artifact library
---
 newt/mfg/emit.go  | 20 +++++++++-----------
 newt/mfg/paths.go |  2 +-
 util/util.go      |  8 ++++++++
 3 files changed, 18 insertions(+), 12 deletions(-)

diff --git a/newt/mfg/emit.go b/newt/mfg/emit.go
index b977fe7..9eb8cf3 100644
--- a/newt/mfg/emit.go
+++ b/newt/mfg/emit.go
@@ -20,7 +20,6 @@
 package mfg
 
 import (
-	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -30,6 +29,7 @@ import (
 	"mynewt.apache.org/newt/artifact/image"
 	"mynewt.apache.org/newt/artifact/manifest"
 	"mynewt.apache.org/newt/artifact/mfg"
+	"mynewt.apache.org/newt/artifact/misc"
 	"mynewt.apache.org/newt/newt/builder"
 	"mynewt.apache.org/newt/newt/flashmap"
 	"mynewt.apache.org/newt/newt/target"
@@ -234,22 +234,16 @@ func copyBinFiles(entries []CpEntry) error {
 
 // emitManifest generates an mfg manifest.
 func (me *MfgEmitter) emitManifest() ([]byte, error) {
-	hashBytes := me.Mfg.Meta.Hash()
-	if hashBytes == nil {
-		// No hash TLV; calculate hash manually.
-		bin, err := me.Mfg.Bytes(0xff)
-		if err != nil {
-			return nil, err
-		}
-		hashBytes = mfg.CalcHash(bin)
+	hashBytes, err := me.Mfg.Hash()
+	if err != nil {
+		return nil, err
 	}
-	hashStr := fmt.Sprintf("%x", hashBytes)
 
 	mm := manifest.MfgManifest{
 		Name:       me.Name,
 		BuildTime:  time.Now().Format(time.RFC3339),
 		Format:     MANIFEST_FORMAT,
-		MfgHash:    hashStr,
+		MfgHash:    misc.HashString(hashBytes),
 		Version:    me.Ver.String(),
 		Device:     me.Device,
 		BinPath:    mfg.MFG_IMG_FILENAME,
@@ -297,6 +291,10 @@ func (me *MfgEmitter) emitManifest() ([]byte, error) {
 
 // @return                      [source-paths], [dest-paths], error
 func (me *MfgEmitter) Emit() ([]string, []string, error) {
+	if err := me.Mfg.RecalcHash(0xff); err != nil {
+		return nil, nil, err
+	}
+
 	mbin, err := me.Mfg.Bytes(0xff)
 	if err != nil {
 		return nil, nil, err
diff --git a/newt/mfg/paths.go b/newt/mfg/paths.go
index 3b3e1f6..8c70b94 100644
--- a/newt/mfg/paths.go
+++ b/newt/mfg/paths.go
@@ -38,7 +38,7 @@ func MfgBinPath(mfgPkgName string) string {
 }
 
 func MfgManifestPath(mfgPkgName string) string {
-	return MfgBinDir(mfgPkgName) + "/" + mfg.MFG_MANIFEST_FILENAME
+	return MfgBinDir(mfgPkgName) + "/" + mfg.MANIFEST_FILENAME
 }
 
 func MfgTargetDir(targetNum int) string {
diff --git a/util/util.go b/util/util.go
index fa9098b..b1d7973 100644
--- a/util/util.go
+++ b/util/util.go
@@ -103,6 +103,14 @@ func ChildNewtError(parent error) *NewtError {
 	return newtErr
 }
 
+func FmtChildNewtError(parent error, format string,
+	args ...interface{}) *NewtError {
+
+	ne := ChildNewtError(parent)
+	ne.Text = fmt.Sprintf(format, args...)
+	return ne
+}
+
 // Print Silent, Quiet and Verbose aware status messages to stdout.
 func WriteMessage(f *os.File, level int, message string,
 	args ...interface{}) {


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

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

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

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

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


[mynewt-newt] 01/17: "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-newt.git

commit 1e5f83d231a283d5d50342b13d30f250e0ea55a2
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
---
 artifact/flash/flash.go       | 160 +++++++++++++
 artifact/image/create.go      | 427 +++++++++++++++++++++++++++++++++
 artifact/image/encrypted.go   | 196 +++++++++++++++
 artifact/image/image.go       | 544 ++++++++++++++++++++++++++++++++++++++++++
 artifact/image/key.go         | 294 +++++++++++++++++++++++
 artifact/image/keys_test.go   | 244 +++++++++++++++++++
 artifact/image/v1.go          | 487 +++++++++++++++++++++++++++++++++++++
 artifact/manifest/manifest.go |  95 ++++++++
 8 files changed, 2447 insertions(+)

diff --git a/artifact/flash/flash.go b/artifact/flash/flash.go
new file mode 100644
index 0000000..c37d2dd
--- /dev/null
+++ b/artifact/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/artifact/image/create.go b/artifact/image/create.go
new file mode 100644
index 0000000..5b28120
--- /dev/null
+++ b/artifact/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/artifact/image/encrypted.go b/artifact/image/encrypted.go
new file mode 100644
index 0000000..0547e2f
--- /dev/null
+++ b/artifact/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/artifact/image/image.go b/artifact/image/image.go
new file mode 100644
index 0000000..244cfed
--- /dev/null
+++ b/artifact/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/artifact/image/key.go b/artifact/image/key.go
new file mode 100644
index 0000000..9141f6e
--- /dev/null
+++ b/artifact/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/artifact/image/keys_test.go b/artifact/image/keys_test.go
new file mode 100644
index 0000000..577d9cd
--- /dev/null
+++ b/artifact/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/artifact/image/v1.go b/artifact/image/v1.go
new file mode 100644
index 0000000..bab86f6
--- /dev/null
+++ b/artifact/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/artifact/manifest/manifest.go b/artifact/manifest/manifest.go
new file mode 100644
index 0000000..62d8c06
--- /dev/null
+++ b/artifact/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-newt] 10/17: Larva: Add `mfg bootkey` command

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

commit 22fba5eadd358bc3e4dd4be30b2a8800391d1e14
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Wed Nov 28 10:02:25 2018 -0800

    Larva: Add `mfg bootkey` command
    
    This command replaces the boot loader key in a manufacturing image.
---
 larva/cli/mfg_cmds.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++
 larva/larva.go        | 24 +++++++++++++++++++----
 larva/mfg/mfg.go      | 31 +++++++++++++++++++++++++++++
 3 files changed, 105 insertions(+), 4 deletions(-)

diff --git a/larva/cli/mfg_cmds.go b/larva/cli/mfg_cmds.go
index 3a9e6ac..ad201cc 100644
--- a/larva/cli/mfg_cmds.go
+++ b/larva/cli/mfg_cmds.go
@@ -150,6 +150,47 @@ func runJoinCmd(cmd *cobra.Command, args []string) {
 	}
 }
 
+func runBootKeyCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 3 {
+		LarvaUsage(cmd, nil)
+	}
+
+	sec0Filename := args[0]
+	okeyFilename := args[1]
+	nkeyFilename := args[2]
+
+	outFilename, err := CalcOutFilename(sec0Filename)
+	if err != nil {
+		LarvaUsage(cmd, err)
+	}
+
+	sec0, err := ioutil.ReadFile(sec0Filename)
+	if err != nil {
+		LarvaUsage(cmd, util.FmtNewtError(
+			"Failed to read sec0 file: %s", err.Error()))
+	}
+
+	okey, err := ioutil.ReadFile(okeyFilename)
+	if err != nil {
+		LarvaUsage(cmd, util.FmtNewtError(
+			"Failed to read old key der: %s", err.Error()))
+	}
+
+	nkey, err := ioutil.ReadFile(nkeyFilename)
+	if err != nil {
+		LarvaUsage(cmd, util.FmtNewtError(
+			"Failed to read new key der: %s", err.Error()))
+	}
+
+	if err := mfg.ReplaceBootKey(sec0, okey, nkey); err != nil {
+		LarvaUsage(nil, err)
+	}
+
+	if err := ioutil.WriteFile(outFilename, sec0, os.ModePerm); err != nil {
+		LarvaUsage(nil, util.ChildNewtError(err))
+	}
+}
+
 func AddMfgCommands(cmd *cobra.Command) {
 	mfgCmd := &cobra.Command{
 		Use:   "mfg",
@@ -181,4 +222,17 @@ func AddMfgCommands(cmd *cobra.Command) {
 		"Flash device number")
 
 	mfgCmd.AddCommand(joinCmd)
+
+	bootKeyCmd := &cobra.Command{
+		Use:   "bootkey <sec0-bin> <cur-key-der> <new-key-der>",
+		Short: "Replaces the boot key in a manufacturing image",
+		Run:   runBootKeyCmd,
+	}
+
+	bootKeyCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o", "",
+		"File to write to")
+	bootKeyCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+		"Replace input file")
+
+	mfgCmd.AddCommand(bootKeyCmd)
 }
diff --git a/larva/larva.go b/larva/larva.go
index 04cddec..5d7a58c 100644
--- a/larva/larva.go
+++ b/larva/larva.go
@@ -30,7 +30,10 @@ import (
 )
 
 var LarvaLogLevel log.Level
-var larvaVersion = "0.0.1"
+var larvaSilent bool
+var larvaQuiet bool
+var larvaVerbose bool
+var larvaVersion = "0.0.2"
 
 func main() {
 	larvaHelpText := ""
@@ -43,15 +46,22 @@ func main() {
 		Long:    larvaHelpText,
 		Example: larvaHelpEx,
 		PersistentPreRun: func(cmd *cobra.Command, args []string) {
+			verbosity := util.VERBOSITY_DEFAULT
+			if larvaSilent {
+				verbosity = util.VERBOSITY_SILENT
+			} else if larvaQuiet {
+				verbosity = util.VERBOSITY_QUIET
+			} else if larvaVerbose {
+				verbosity = util.VERBOSITY_VERBOSE
+			}
+
 			logLevel, err := log.ParseLevel(logLevelStr)
 			if err != nil {
 				cli.LarvaUsage(nil, util.ChildNewtError(err))
 			}
 			LarvaLogLevel = logLevel
 
-			if err := util.Init(LarvaLogLevel, "",
-				util.VERBOSITY_DEFAULT); err != nil {
-
+			if err := util.Init(LarvaLogLevel, "", verbosity); err != nil {
 				cli.LarvaUsage(nil, err)
 			}
 		},
@@ -61,6 +71,12 @@ func main() {
 		},
 	}
 
+	larvaCmd.PersistentFlags().BoolVarP(&larvaVerbose, "verbose", "v", false,
+		"Enable verbose output when executing commands")
+	larvaCmd.PersistentFlags().BoolVarP(&larvaQuiet, "quiet", "q", false,
+		"Be quiet; only display error output")
+	larvaCmd.PersistentFlags().BoolVarP(&larvaSilent, "silent", "s", false,
+		"Be silent; don't output anything")
 	larvaCmd.PersistentFlags().StringVarP(&logLevelStr, "loglevel", "l",
 		"WARN", "Log level")
 
diff --git a/larva/mfg/mfg.go b/larva/mfg/mfg.go
index 0cbbbb0..f98ab06 100644
--- a/larva/mfg/mfg.go
+++ b/larva/mfg/mfg.go
@@ -20,6 +20,7 @@
 package mfg
 
 import (
+	"bytes"
 	"fmt"
 	"sort"
 	"strings"
@@ -145,3 +146,33 @@ func Join(mm MfgMap, eraseVal byte, areas []flash.FlashArea) ([]byte, error) {
 
 	return joined, nil
 }
+
+func ReplaceBootKey(sec0 []byte, okey []byte, nkey []byte) error {
+	if len(okey) != len(nkey) {
+		return util.FmtNewtError(
+			"key lengths differ (%d != %d)", len(okey), len(nkey))
+	}
+
+	if len(okey) > len(sec0) {
+		return util.FmtNewtError(
+			"key longer than flash section (%d > %d)", len(okey), len(sec0))
+	}
+
+	idx := bytes.Index(sec0, okey)
+	if idx == -1 {
+		return util.FmtNewtError("old key not present in flash section")
+	}
+
+	lastIdx := bytes.LastIndex(sec0, okey)
+	if idx != lastIdx {
+		return util.FmtNewtError(
+			"multiple instances of old key in flash section")
+	}
+
+	util.StatusMessage(util.VERBOSITY_VERBOSE,
+		"Replacing boot key at offset %d\n", idx)
+
+	copy(sec0[idx:idx+len(okey)], nkey)
+
+	return nil
+}


[mynewt-newt] 02/17: Remove the `flash` and `image` packages

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

commit cf572b687eabd989520219be59b2eb3a064e498b
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Tue Nov 20 17:50:16 2018 -0800

    Remove the `flash` and `image` packages
    
    This functionality is now implemented in the "artifact" library.
---
 newt/flash/flash.go     |  421 --------------
 newt/image/encrypted.go |  196 -------
 newt/image/image.go     | 1463 -----------------------------------------------
 newt/image/keys_test.go |  246 --------
 4 files changed, 2326 deletions(-)

diff --git a/newt/flash/flash.go b/newt/flash/flash.go
deleted file mode 100644
index 97114c5..0000000
--- a/newt/flash/flash.go
+++ /dev/null
@@ -1,421 +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 flash
-
-import (
-	"bytes"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"sort"
-	"strings"
-
-	log "github.com/Sirupsen/logrus"
-	"github.com/spf13/cast"
-
-	"mynewt.apache.org/newt/newt/newtutil"
-	"mynewt.apache.org/newt/util"
-)
-
-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"
-
-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,
-}
-
-const AREA_USER_ID_MIN = 16
-
-const HEADER_PATH = "sysflash/sysflash.h"
-const C_VAR_NAME = "sysflash_map_dflt"
-const C_VAR_COMMENT = `/**
- * This flash map definition is used for two purposes:
- * 1. To locate the meta area, which contains the true flash map definition.
- * 2. As a fallback in case the meta area cannot be read from flash.
- */
-`
-
-type FlashArea struct {
-	Name   string
-	Id     int
-	Device int
-	Offset int
-	Size   int
-}
-
-type FlashMap struct {
-	Areas       map[string]FlashArea
-	Overlaps    [][]FlashArea
-	IdConflicts [][]FlashArea
-}
-
-func newFlashMap() FlashMap {
-	return FlashMap{
-		Areas:    map[string]FlashArea{},
-		Overlaps: [][]FlashArea{},
-	}
-}
-
-func flashAreaErr(areaName string, format string, args ...interface{}) error {
-	return util.NewNewtError(
-		"failure while parsing flash area \"" + areaName + "\": " +
-			fmt.Sprintf(format, args...))
-}
-
-func parseSize(val string) (int, error) {
-	lower := strings.ToLower(val)
-
-	multiplier := 1
-	if strings.HasSuffix(lower, "kb") {
-		multiplier = 1024
-		lower = strings.TrimSuffix(lower, "kb")
-	}
-
-	num, err := util.AtoiNoOct(lower)
-	if err != nil {
-		return 0, err
-	}
-
-	return num * multiplier, nil
-}
-
-func parseFlashArea(
-	name string, ymlFields map[string]interface{}) (FlashArea, error) {
-
-	area := FlashArea{
-		Name: name,
-	}
-
-	idPresent := false
-	devicePresent := false
-	offsetPresent := false
-	sizePresent := false
-
-	var isSystem bool
-	area.Id, isSystem = SYSTEM_AREA_NAME_ID_MAP[name]
-
-	var err error
-
-	fields := cast.ToStringMapString(ymlFields)
-	for k, v := range fields {
-		switch k {
-		case "user_id":
-			if isSystem {
-				return area, flashAreaErr(name,
-					"system areas cannot specify a user ID")
-			}
-			userId, err := util.AtoiNoOct(v)
-			if err != nil {
-				return area, flashAreaErr(name, "invalid user id: %s", v)
-			}
-			area.Id = userId + AREA_USER_ID_MIN
-			idPresent = true
-
-		case "device":
-			area.Device, err = util.AtoiNoOct(v)
-			if err != nil {
-				return area, flashAreaErr(name, "invalid device: %s", v)
-			}
-			devicePresent = true
-
-		case "offset":
-			area.Offset, err = util.AtoiNoOct(v)
-			if err != nil {
-				return area, flashAreaErr(name, "invalid offset: %s", v)
-			}
-			offsetPresent = true
-
-		case "size":
-			area.Size, err = parseSize(v)
-			if err != nil {
-				return area, flashAreaErr(name, err.Error())
-			}
-			sizePresent = true
-
-		default:
-			util.StatusMessage(util.VERBOSITY_QUIET,
-				"Warning: flash area \"%s\" contains unrecognized field: %s",
-				name, k)
-		}
-	}
-
-	if !isSystem && !idPresent {
-		return area, flashAreaErr(name, "required field \"user_id\" missing")
-	}
-	if !devicePresent {
-		return area, flashAreaErr(name, "required field \"device\" missing")
-	}
-	if !offsetPresent {
-		return area, flashAreaErr(name, "required field \"offset\" missing")
-	}
-	if !sizePresent {
-		return area, flashAreaErr(name, "required field \"size\" missing")
-	}
-
-	return area, nil
-}
-
-func (flashMap FlashMap) unSortedAreas() []FlashArea {
-	areas := make([]FlashArea, 0, len(flashMap.Areas))
-	for _, area := range flashMap.Areas {
-		areas = append(areas, area)
-	}
-
-	return areas
-}
-
-func (flashMap FlashMap) SortedAreas() []FlashArea {
-	idMap := make(map[int]FlashArea, len(flashMap.Areas))
-	ids := make([]int, 0, len(flashMap.Areas))
-	for _, area := range flashMap.Areas {
-		idMap[area.Id] = area
-		ids = append(ids, area.Id)
-	}
-	sort.Ints(ids)
-
-	areas := make([]FlashArea, len(ids))
-	for i, id := range ids {
-		areas[i] = idMap[id]
-	}
-
-	return areas
-}
-
-func (flashMap FlashMap) DeviceIds() []int {
-	deviceMap := map[int]struct{}{}
-
-	for _, area := range flashMap.Areas {
-		deviceMap[area.Device] = struct{}{}
-	}
-
-	devices := make([]int, 0, len(deviceMap))
-	for device, _ := range deviceMap {
-		devices = append(devices, device)
-	}
-	sort.Ints(devices)
-
-	return devices
-}
-
-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
-}
-
-func (flashMap *FlashMap) detectOverlaps() {
-	flashMap.Overlaps = [][]FlashArea{}
-
-	// Convert the map to a slice.
-	areas := flashMap.unSortedAreas()
-
-	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) {
-				flashMap.Overlaps = append(
-					flashMap.Overlaps, []FlashArea{iarea, jarea})
-			}
-
-			if iarea.Id == jarea.Id {
-				flashMap.IdConflicts = append(
-					flashMap.IdConflicts, []FlashArea{iarea, jarea})
-			}
-		}
-	}
-}
-
-func (flashMap FlashMap) ErrorText() string {
-	str := ""
-
-	if len(flashMap.IdConflicts) > 0 {
-		str += "Conflicting flash area IDs detected:\n"
-
-		for _, pair := range flashMap.IdConflicts {
-			str += fmt.Sprintf("    (%d) %s =/= %s\n",
-				pair[0].Id-AREA_USER_ID_MIN, pair[0].Name, pair[1].Name)
-		}
-	}
-
-	if len(flashMap.Overlaps) > 0 {
-		str += "Overlapping flash areas detected:\n"
-
-		for _, pair := range flashMap.Overlaps {
-			str += fmt.Sprintf("    %s =/= %s\n", pair[0].Name, pair[1].Name)
-		}
-	}
-
-	return str
-}
-
-func Read(ymlFlashMap map[string]interface{}) (FlashMap, error) {
-	flashMap := newFlashMap()
-
-	ymlAreas := ymlFlashMap["areas"]
-	if ymlAreas == nil {
-		return flashMap, util.NewNewtError(
-			"\"areas\" mapping missing from flash map definition")
-	}
-
-	areaMap := cast.ToStringMap(ymlAreas)
-	for k, v := range areaMap {
-		if _, ok := flashMap.Areas[k]; ok {
-			return flashMap, flashAreaErr(k, "name conflict")
-		}
-
-		ymlArea := cast.ToStringMap(v)
-		area, err := parseFlashArea(k, ymlArea)
-		if err != nil {
-			return flashMap, flashAreaErr(k, err.Error())
-		}
-
-		flashMap.Areas[k] = area
-	}
-
-	flashMap.detectOverlaps()
-
-	return flashMap, nil
-}
-
-func (flashMap FlashMap) varDecl() string {
-	return fmt.Sprintf("const struct flash_area %s[%d]", C_VAR_NAME,
-		len(flashMap.Areas))
-}
-
-func (area FlashArea) writeHeader(w io.Writer) {
-	fmt.Fprintf(w, "#define %-40s %d\n", area.Name, area.Id)
-}
-
-func (flashMap FlashMap) writeHeader(w io.Writer) {
-	fmt.Fprintf(w, newtutil.GeneratedPreamble())
-
-	fmt.Fprintf(w, "#ifndef H_MYNEWT_SYSFLASH_\n")
-	fmt.Fprintf(w, "#define H_MYNEWT_SYSFLASH_\n")
-	fmt.Fprintf(w, "\n")
-	fmt.Fprintf(w, "#include \"flash_map/flash_map.h\"\n")
-	fmt.Fprintf(w, "\n")
-	fmt.Fprintf(w, "%s", C_VAR_COMMENT)
-	fmt.Fprintf(w, "extern %s;\n", flashMap.varDecl())
-	fmt.Fprintf(w, "\n")
-
-	for _, area := range flashMap.SortedAreas() {
-		area.writeHeader(w)
-	}
-
-	fmt.Fprintf(w, "\n#endif\n")
-}
-
-func sizeComment(size int) string {
-	if size%1024 != 0 {
-		return ""
-	}
-
-	return fmt.Sprintf(" /* %d kB */", size/1024)
-}
-
-func (area FlashArea) writeSrc(w io.Writer) {
-	fmt.Fprintf(w, "    /* %s */\n", area.Name)
-	fmt.Fprintf(w, "    {\n")
-	fmt.Fprintf(w, "        .fa_id = %d,\n", area.Id)
-	fmt.Fprintf(w, "        .fa_device_id = %d,\n", area.Device)
-	fmt.Fprintf(w, "        .fa_off = 0x%08x,\n", area.Offset)
-	fmt.Fprintf(w, "        .fa_size = %d,%s\n", area.Size,
-		sizeComment(area.Size))
-	fmt.Fprintf(w, "    },\n")
-}
-
-func (flashMap FlashMap) writeSrc(w io.Writer) {
-	fmt.Fprintf(w, newtutil.GeneratedPreamble())
-
-	fmt.Fprintf(w, "#include \"%s\"\n", HEADER_PATH)
-	fmt.Fprintf(w, "\n")
-	fmt.Fprintf(w, "%s", C_VAR_COMMENT)
-	fmt.Fprintf(w, "%s = {", flashMap.varDecl())
-
-	for _, area := range flashMap.SortedAreas() {
-		fmt.Fprintf(w, "\n")
-		area.writeSrc(w)
-	}
-
-	fmt.Fprintf(w, "};\n")
-}
-
-func (flashMap FlashMap) ensureWrittenGen(path string, contents []byte) error {
-	writeReqd, err := util.FileContentsChanged(path, contents)
-	if err != nil {
-		return err
-	}
-	if !writeReqd {
-		log.Debugf("flash map unchanged; not writing file (%s).", path)
-		return nil
-	}
-
-	log.Debugf("flash map changed; writing file (%s).", path)
-
-	if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
-		return util.NewNewtError(err.Error())
-	}
-
-	if err := ioutil.WriteFile(path, contents, 0644); err != nil {
-		return util.NewNewtError(err.Error())
-	}
-
-	return nil
-}
-
-func (flashMap FlashMap) EnsureWritten(
-	srcDir string, includeDir string, targetName string) error {
-
-	buf := bytes.Buffer{}
-	flashMap.writeSrc(&buf)
-	if err := flashMap.ensureWrittenGen(
-		fmt.Sprintf("%s/%s-sysflash.c", srcDir, targetName),
-		buf.Bytes()); err != nil {
-
-		return err
-	}
-
-	buf = bytes.Buffer{}
-	flashMap.writeHeader(&buf)
-	if err := flashMap.ensureWrittenGen(
-		includeDir+"/"+HEADER_PATH, buf.Bytes()); err != nil {
-		return err
-	}
-
-	return nil
-}
diff --git a/newt/image/encrypted.go b/newt/image/encrypted.go
deleted file mode 100644
index 0547e2f..0000000
--- a/newt/image/encrypted.go
+++ /dev/null
@@ -1,196 +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.
- */
-
-// 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/newt/image/image.go b/newt/image/image.go
deleted file mode 100644
index 278804e..0000000
--- a/newt/image/image.go
+++ /dev/null
@@ -1,1463 +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 image
-
-import (
-	"bytes"
-	"crypto"
-	"crypto/aes"
-	"crypto/cipher"
-	"crypto/ecdsa"
-	"crypto/rand"
-	"crypto/rsa"
-	"crypto/sha256"
-	"crypto/x509"
-	"encoding/asn1"
-	"encoding/base64"
-	"encoding/binary"
-	"encoding/hex"
-	"encoding/pem"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"math/big"
-	"os"
-	"sort"
-	"strconv"
-	"strings"
-
-	keywrap "github.com/NickBall/go-aes-key-wrap"
-	log "github.com/Sirupsen/logrus"
-
-	"mynewt.apache.org/newt/newt/pkg"
-	"mynewt.apache.org/newt/util"
-)
-
-// Set this to enable RSA-PSS for RSA signatures, instead of PKCS#1
-// v1.5.  Eventually, this should be the default.
-var UseRsaPss = false
-
-// Use old image format
-var UseV1 = false
-
-// Public key file to encrypt image
-var PubKeyFile = ""
-
-type ImageVersion struct {
-	Major    uint8
-	Minor    uint8
-	Rev      uint16
-	BuildNum uint32
-}
-
-type ImageKey struct {
-	// Only one of these members is non-nil.
-	Rsa *rsa.PrivateKey
-	Ec  *ecdsa.PrivateKey
-}
-
-type Image struct {
-	SourceBin  string
-	SourceImg  string
-	TargetImg  string
-	Version    ImageVersion
-	Keys       []ImageKey
-	KeyId      uint8
-	Hash       []byte
-	SrcSkip    uint // Number of bytes to skip from the source image.
-	HeaderSize uint // If non-zero pad out the header to this size.
-	TotalSize  uint // Total size, in bytes, of the generated .img file.
-}
-
-type ImageHdrV1 struct {
-	Magic uint32
-	TlvSz uint16
-	KeyId uint8
-	Pad1  uint8
-	HdrSz uint16
-	Pad2  uint16
-	ImgSz uint32
-	Flags uint32
-	Vers  ImageVersion
-	Pad3  uint32
-}
-
-type ImageHdr struct {
-	Magic uint32
-	Pad1  uint32
-	HdrSz uint16
-	Pad2  uint16
-	ImgSz uint32
-	Flags uint32
-	Vers  ImageVersion
-	Pad3  uint32
-}
-
-type ImageTlvInfo struct {
-	Magic     uint16
-	TlvTotLen uint16
-}
-
-type ImageTrailerTlv struct {
-	Type uint8
-	Pad  uint8
-	Len  uint16
-}
-
-const (
-	IMAGEv1_MAGIC       = 0x96f3b83c /* Image header magic */
-	IMAGE_MAGIC         = 0x96f3b83d /* Image header magic */
-	IMAGE_TRAILER_MAGIC = 0x6907     /* Image tlv info magic */
-)
-
-const (
-	IMAGE_HEADER_SIZE = 32
-)
-
-/*
- * Image header flags.
- */
-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 */
-
-	IMAGE_F_PIC          = 0x00000001
-	IMAGE_F_NON_BOOTABLE = 0x00000002 /* non bootable image */
-	IMAGE_F_ENCRYPTED    = 0x00000004 /* encrypted image */
-)
-
-/*
- * Image trailer TLV types.
- */
-const (
-	IMAGEv1_TLV_SHA256   = 1
-	IMAGEv1_TLV_RSA2048  = 2
-	IMAGEv1_TLV_ECDSA224 = 3
-	IMAGEv1_TLV_ECDSA256 = 4
-
-	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
-)
-
-/*
- * Data that's going to go to build manifest file
- */
-type ImageManifestSizeArea struct {
-	Name string `json:"name"`
-	Size uint32 `json:"size"`
-}
-
-type ImageManifestSizeSym struct {
-	Name  string                   `json:"name"`
-	Areas []*ImageManifestSizeArea `json:"areas"`
-}
-
-type ImageManifestSizeFile struct {
-	Name string                  `json:"name"`
-	Syms []*ImageManifestSizeSym `json:"sym"`
-}
-
-type ImageManifestSizePkg struct {
-	Name  string                   `json:"name"`
-	Files []*ImageManifestSizeFile `json:"files"`
-}
-
-type ImageManifestSizeCollector struct {
-	Pkgs []*ImageManifestSizePkg
-}
-
-type ImageManifest 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       []*ImageManifestPkg `json:"pkgs"`
-	LoaderPkgs []*ImageManifestPkg `json:"loader_pkgs,omitempty"`
-	TgtVars    []string            `json:"target"`
-	Repos      []ImageManifestRepo `json:"repos"`
-
-	PkgSizes       []*ImageManifestSizePkg `json:"pkgsz"`
-	LoaderPkgSizes []*ImageManifestSizePkg `json:"loader_pkgsz,omitempty"`
-}
-
-type ImageManifestPkg struct {
-	Name string `json:"name"`
-	Repo string `json:"repo"`
-}
-
-type ImageManifestRepo struct {
-	Name   string `json:"name"`
-	Commit string `json:"commit"`
-	Dirty  bool   `json:"dirty,omitempty"`
-	URL    string `json:"url,omitempty"`
-}
-
-type RepoManager struct {
-	repos map[string]ImageManifestRepo
-}
-
-type ECDSASig struct {
-	R *big.Int
-	S *big.Int
-}
-
-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 NewImage(srcBinPath string, dstImgPath string) (*Image, error) {
-	image := &Image{}
-
-	image.SourceBin = srcBinPath
-	image.TargetImg = dstImgPath
-	return image, nil
-}
-
-func OldImage(imgPath string) (*Image, error) {
-	image := &Image{}
-
-	image.SourceImg = imgPath
-	return image, nil
-}
-
-func (image *Image) SetVersion(versStr string) error {
-	ver, err := ParseVersion(versStr)
-	if err != nil {
-		return err
-	}
-
-	log.Debugf("Assigning version number %d.%d.%d.%d\n",
-		ver.Major, ver.Minor, ver.Rev, ver.BuildNum)
-
-	image.Version = ver
-
-	buf := new(bytes.Buffer)
-	err = binary.Write(buf, binary.LittleEndian, image.Version)
-	if err != nil {
-		fmt.Printf("Bombing out\n")
-		return nil
-	}
-
-	return nil
-}
-
-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) (ImageKey, error) {
-	key := ImageKey{}
-
-	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 (key *ImageKey) 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 (image *Image) SetKeys(filenames []string) error {
-	for _, filename := range filenames {
-		key, err := readKey(filename)
-		if err != nil {
-			return err
-		}
-
-		image.Keys = append(image.Keys, key)
-	}
-
-	return nil
-}
-
-func (image *Image) SetKeyV1(filename string, keyId uint8) error {
-	key, err := readKey(filename)
-	if err != nil {
-		return err
-	}
-
-	image.KeyId = keyId
-	image.Keys = []ImageKey{key}
-
-	return nil
-}
-
-func (key *ImageKey) 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 *ImageKey) 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 *ImageKey) 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 *ImageKey) sigTlvType() uint8 {
-	key.assertValid()
-
-	if UseV1 {
-		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
-			}
-		}
-	} else {
-		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 (image *Image) ReSign() error {
-	srcImg, err := os.Open(image.SourceImg)
-	if err != nil {
-		return util.FmtNewtError("Can't open image file %s: %s",
-			image.SourceImg, err.Error())
-	}
-
-	srcInfo, err := srcImg.Stat()
-	if err != nil {
-		return util.FmtNewtError("Can't stat image file %s: %s",
-			image.SourceImg, err.Error())
-	}
-
-	var hdr1 ImageHdrV1
-	var hdr2 ImageHdr
-	var hdrSz uint16
-	var imgSz uint32
-
-	err = binary.Read(srcImg, binary.LittleEndian, &hdr1)
-	if err == nil {
-		srcImg.Seek(0, 0)
-		err = binary.Read(srcImg, binary.LittleEndian, &hdr2)
-	}
-	if err != nil {
-		return util.FmtNewtError("Failing to access image %s: %s",
-			image.SourceImg, err.Error())
-	}
-	if hdr1.Magic == IMAGEv1_MAGIC {
-		if uint32(srcInfo.Size()) !=
-			uint32(hdr1.HdrSz)+hdr1.ImgSz+uint32(hdr1.TlvSz) {
-
-			return util.FmtNewtError("File %s is not an image\n",
-				image.SourceImg)
-		}
-		imgSz = hdr1.ImgSz
-		hdrSz = hdr1.HdrSz
-		image.Version = hdr1.Vers
-
-		log.Debugf("Resigning %s (ver %d.%d.%d.%d)", image.SourceImg,
-			hdr1.Vers.Major, hdr1.Vers.Minor, hdr1.Vers.Rev,
-			hdr1.Vers.BuildNum)
-	} else if hdr2.Magic == IMAGE_MAGIC {
-		if uint32(srcInfo.Size()) < uint32(hdr2.HdrSz)+hdr2.ImgSz {
-			return util.FmtNewtError("File %s is not an image\n",
-				image.SourceImg)
-		}
-		imgSz = hdr2.ImgSz
-		hdrSz = hdr2.HdrSz
-		image.Version = hdr2.Vers
-
-		log.Debugf("Resigning %s (ver %d.%d.%d.%d)", image.SourceImg,
-			hdr2.Vers.Major, hdr2.Vers.Minor, hdr2.Vers.Rev,
-			hdr2.Vers.BuildNum)
-	} else {
-		return util.FmtNewtError("File %s is not an image\n",
-			image.SourceImg)
-	}
-	srcImg.Seek(int64(hdrSz), 0)
-
-	tmpBin, err := ioutil.TempFile("", "")
-	if err != nil {
-		return util.FmtNewtError("Creating temp file failed: %s",
-			err.Error())
-	}
-	tmpBinName := tmpBin.Name()
-	defer os.Remove(tmpBinName)
-
-	log.Debugf("Extracting data from %s:%d-%d to %s\n",
-		image.SourceImg, int64(hdrSz), int64(hdrSz)+int64(imgSz), tmpBinName)
-	_, err = io.CopyN(tmpBin, srcImg, int64(imgSz))
-	srcImg.Close()
-	tmpBin.Close()
-	if err != nil {
-		return util.FmtNewtError("Cannot copy to tmpfile %s: %s",
-			tmpBin.Name(), err.Error())
-	}
-
-	image.SourceBin = tmpBinName
-	image.TargetImg = image.SourceImg
-	image.HeaderSize = uint(hdrSz)
-
-	return image.Generate(nil)
-}
-
-func generateSigRsa(key *rsa.PrivateKey, hash []byte) ([]byte, error) {
-	var signature []byte
-	var err error
-
-	if UseRsaPss || !UseV1 {
-		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 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 generateSigTlvRsa(key ImageKey, hash []byte) ([]byte, error) {
-	tlv := &ImageTrailerTlv{
-		Type: key.sigTlvType(),
-		Pad:  0,
-		Len:  256, /* 2048 bits */
-	}
-
-	sig, err := generateSigRsa(key.Rsa, hash)
-	if err != nil {
-		return nil, err
-	}
-
-	b := &bytes.Buffer{}
-
-	err = binary.Write(b, binary.LittleEndian, tlv)
-	if err != nil {
-		return nil, util.FmtNewtError(
-			"Failed to serialize image trailer: %s", err.Error())
-	}
-	_, err = b.Write(sig)
-	if err != nil {
-		return nil, util.FmtNewtError(
-			"Failed to append sig: %s", err.Error())
-	}
-
-	return b.Bytes(), nil
-}
-
-func generateSigTlvEc(key ImageKey, hash []byte) ([]byte, error) {
-	sig, err := generateSigEc(key.Ec, hash)
-	if err != nil {
-		return nil, err
-	}
-
-	sigLen := key.sigLen()
-	if len(sig) > int(sigLen) {
-		return nil, util.FmtNewtError("Something is really wrong\n")
-	}
-
-	tlv := &ImageTrailerTlv{
-		Type: key.sigTlvType(),
-		Pad:  0,
-		Len:  sigLen,
-	}
-
-	b := &bytes.Buffer{}
-
-	if err := binary.Write(b, binary.LittleEndian, tlv); err != nil {
-		return nil, util.FmtNewtError(
-			"Failed to serialize image trailer: %s", err.Error())
-	}
-
-	if _, err := b.Write(sig); err != nil {
-		return nil, util.FmtNewtError("Failed to append sig: %s", err.Error())
-	}
-
-	pad := make([]byte, int(sigLen)-len(sig))
-	if _, err := b.Write(pad); err != nil {
-		return nil, util.FmtNewtError(
-			"Failed to serialize image trailer: %s", err.Error())
-	}
-
-	return b.Bytes(), nil
-}
-
-func generateSigTlv(key ImageKey, hash []byte) ([]byte, error) {
-	key.assertValid()
-
-	if key.Rsa != nil {
-		return generateSigTlvRsa(key, hash)
-	} else {
-		return generateSigTlvEc(key, hash)
-	}
-}
-
-func generateKeyHashTlv(key ImageKey) ([]byte, error) {
-	key.assertValid()
-
-	keyHash, err := key.sigKeyHash()
-	if err != nil {
-		return nil, util.FmtNewtError(
-			"Failed to compute hash of the public key: %s", err.Error())
-	}
-
-	tlv := &ImageTrailerTlv{
-		Type: IMAGE_TLV_KEYHASH,
-		Pad:  0,
-		Len:  uint16(len(keyHash)),
-	}
-
-	b := &bytes.Buffer{}
-
-	if err := binary.Write(b, binary.LittleEndian, tlv); err != nil {
-		return nil, util.FmtNewtError(
-			"Failed to serial image trailer: %s", err.Error())
-	}
-
-	if _, err := b.Write(keyHash); err != nil {
-		return nil, util.FmtNewtError(
-			"Failed to append key hash: %s", err.Error())
-	}
-
-	return b.Bytes(), nil
-}
-
-func (image *Image) generateV1(loader *Image) error {
-	binFile, err := os.Open(image.SourceBin)
-	if err != nil {
-		return util.FmtNewtError("Can't open app binary: %s",
-			err.Error())
-	}
-	defer binFile.Close()
-
-	binInfo, err := binFile.Stat()
-	if err != nil {
-		return util.FmtNewtError("Can't stat app binary %s: %s",
-			image.SourceBin, err.Error())
-	}
-
-	imgFile, err := os.OpenFile(image.TargetImg,
-		os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
-	if err != nil {
-		return util.FmtNewtError("Can't open target image %s: %s",
-			image.TargetImg, err.Error())
-	}
-	defer imgFile.Close()
-
-	/*
-	 * Compute hash while updating the file.
-	 */
-	hash := sha256.New()
-
-	if loader != nil {
-		err = binary.Write(hash, binary.LittleEndian, loader.Hash)
-		if err != nil {
-			return util.FmtNewtError("Failed to seed hash: %s", err.Error())
-		}
-	}
-
-	/*
-	 * First the header
-	 */
-	hdr := &ImageHdrV1{
-		Magic: IMAGEv1_MAGIC,
-		TlvSz: 0,
-		KeyId: 0,
-		Pad1:  0,
-		HdrSz: IMAGE_HEADER_SIZE,
-		Pad2:  0,
-		ImgSz: uint32(binInfo.Size()) - uint32(image.SrcSkip),
-		Flags: 0,
-		Vers:  image.Version,
-		Pad3:  0,
-	}
-
-	if len(image.Keys) > 0 {
-		hdr.Flags, err = image.Keys[0].sigHdrTypeV1()
-		if err != nil {
-			return err
-		}
-
-		hdr.TlvSz = 4 + image.Keys[0].sigLen()
-		hdr.KeyId = image.KeyId
-	}
-
-	hdr.TlvSz += 4 + 32
-	hdr.Flags |= IMAGEv1_F_SHA256
-
-	if loader != nil {
-		hdr.Flags |= IMAGEv1_F_NON_BOOTABLE
-	}
-
-	if image.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 image.HeaderSize < IMAGE_HEADER_SIZE {
-			return util.FmtNewtError(
-				"Image header must be at least %d bytes", IMAGE_HEADER_SIZE)
-		}
-
-		hdr.HdrSz = uint16(image.HeaderSize)
-	}
-
-	err = binary.Write(imgFile, binary.LittleEndian, hdr)
-	if err != nil {
-		return util.FmtNewtError("Failed to serialize image hdr: %s",
-			err.Error())
-	}
-	err = binary.Write(hash, binary.LittleEndian, hdr)
-	if err != nil {
-		return util.FmtNewtError("Failed to hash data: %s", err.Error())
-	}
-
-	if image.HeaderSize > IMAGE_HEADER_SIZE {
-		/*
-		 * Pad the image (and hash) with zero bytes to fill
-		 * out the buffer.
-		 */
-		buf := make([]byte, image.HeaderSize-IMAGE_HEADER_SIZE)
-
-		_, err = imgFile.Write(buf)
-		if err != nil {
-			return util.FmtNewtError(
-				"Failed to write padding: %s", err.Error())
-		}
-
-		_, err = hash.Write(buf)
-		if err != nil {
-			return util.FmtNewtError("Failed to hash padding: %s", err.Error())
-		}
-	}
-
-	/*
-	 * Skip requested initial part of image.
-	 */
-	if image.SrcSkip > 0 {
-		buf := make([]byte, image.SrcSkip)
-		_, err = binFile.Read(buf)
-		if err != nil {
-			return util.FmtNewtError(
-				"Failed to read from %s: %s", image.SourceBin, err.Error())
-		}
-
-		nonZero := false
-		for _, b := range buf {
-			if b != 0 {
-				nonZero = true
-				break
-			}
-		}
-		if nonZero {
-			log.Warnf("Skip requested of image %s, but image not preceeded "+
-				"by %d bytes of all zeros",
-				image.SourceBin, image.SrcSkip)
-		}
-	}
-
-	/*
-	 * Followed by data.
-	 */
-	dataBuf := make([]byte, 1024)
-	for {
-		cnt, err := binFile.Read(dataBuf)
-		if err != nil && err != io.EOF {
-			return util.FmtNewtError(
-				"Failed to read from %s: %s", image.SourceBin, err.Error())
-		}
-		if cnt == 0 {
-			break
-		}
-		_, err = imgFile.Write(dataBuf[0:cnt])
-		if err != nil {
-			return util.FmtNewtError(
-				"Failed to write to %s: %s", image.TargetImg, err.Error())
-		}
-		_, err = hash.Write(dataBuf[0:cnt])
-		if err != nil {
-			return util.FmtNewtError(
-				"Failed to hash data: %s", err.Error())
-		}
-	}
-
-	image.Hash = hash.Sum(nil)
-
-	/*
-	 * Trailer with hash of the data
-	 */
-	tlv := &ImageTrailerTlv{
-		Type: IMAGEv1_TLV_SHA256,
-		Pad:  0,
-		Len:  uint16(len(image.Hash)),
-	}
-	err = binary.Write(imgFile, binary.LittleEndian, tlv)
-	if err != nil {
-		return util.FmtNewtError(
-			"Failed to serialize image trailer: %s", err.Error())
-	}
-	_, err = imgFile.Write(image.Hash)
-	if err != nil {
-		return util.FmtNewtError(
-			"Failed to append hash: %s", err.Error())
-	}
-
-	if len(image.Keys) > 0 {
-		tlvBytes, err := generateSigTlv(image.Keys[0], image.Hash)
-		if err != nil {
-			return err
-		}
-		if _, err := imgFile.Write(tlvBytes); err != nil {
-			return util.FmtNewtError(
-				"Failed to append sig TLV: %s", err.Error())
-		}
-	}
-
-	util.StatusMessage(util.VERBOSITY_VERBOSE,
-		"Computed Hash for image %s as %s \n",
-		image.TargetImg, hex.EncodeToString(image.Hash))
-
-	// XXX: Replace "1" with io.SeekCurrent when go 1.7 becomes mainstream.
-	sz, err := imgFile.Seek(0, 1)
-	if err != nil {
-		return util.FmtNewtError("Failed to calculate file size of generated "+
-			"image %s: %s", image.TargetImg, err.Error())
-	}
-	image.TotalSize = uint(sz)
-
-	return nil
-}
-
-func (image *Image) generateV2(loader *Image) error {
-	binFile, err := os.Open(image.SourceBin)
-	if err != nil {
-		return util.FmtNewtError("Can't open app binary: %s", err.Error())
-	}
-	defer binFile.Close()
-
-	binInfo, err := binFile.Stat()
-	if err != nil {
-		return util.FmtNewtError("Can't stat app binary %s: %s",
-			image.SourceBin, err.Error())
-	}
-
-	imgFile, err := os.OpenFile(image.TargetImg,
-		os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
-	if err != nil {
-		return util.FmtNewtError("Can't open target image %s: %s",
-			image.TargetImg, err.Error())
-	}
-	defer imgFile.Close()
-
-	plainSecret := make([]byte, 16)
-	var cipherSecret []byte
-	var _type uint8
-	if PubKeyFile != "" {
-		_, err = rand.Read(plainSecret)
-		if err != nil {
-			return util.FmtNewtError("Random generation error: %s\n", err)
-		}
-
-		keyBytes, err := ioutil.ReadFile(PubKeyFile)
-		if err != nil {
-			return util.FmtNewtError("Error reading pubkey file: %s", err)
-		}
-
-		// Try reading as PEM (asymetric key), if it fails, assume this is a
-		// base64 encoded symetric key
-		b, _ := pem.Decode(keyBytes)
-		if b == nil {
-			kek, err := base64.StdEncoding.DecodeString(string(keyBytes))
-			if err != nil {
-				return util.FmtNewtError("Error decoding kek: %s", err)
-			} else if len(kek) != 16 {
-				return util.FmtNewtError("Unexpected key size: %d != 16", len(kek))
-			}
-
-			cipher, err := aes.NewCipher(kek)
-			if err != nil {
-				return util.FmtNewtError("Error creating keywrap cipher: %s", err)
-			}
-
-			cipherSecret, err = keywrap.Wrap(cipher, plainSecret)
-			if err != nil {
-				return util.FmtNewtError("Error key-wrapping: %s", err)
-			}
-		} else if b.Type != "PUBLIC KEY" && b.Type != "RSA PUBLIC KEY" {
-			return util.NewNewtError("Invalid PEM file")
-		} else {
-			pub, err := x509.ParsePKIXPublicKey(b.Bytes)
-			if err != nil {
-				return util.FmtNewtError("Error parsing pubkey file: %s", err)
-			}
-
-			var pubk *rsa.PublicKey
-			switch pub.(type) {
-			case *rsa.PublicKey:
-				pubk = pub.(*rsa.PublicKey)
-			default:
-				return util.FmtNewtError("Error parsing pubkey file: %s", err)
-			}
-
-			rng := rand.Reader
-			cipherSecret, err = rsa.EncryptOAEP(sha256.New(), rng, pubk, plainSecret, nil)
-			if err != nil {
-				return util.FmtNewtError("Error from encryption: %s\n", err)
-			}
-		}
-	}
-
-	/*
-	 * Compute hash while updating the file.
-	 */
-	hash := sha256.New()
-
-	if loader != nil {
-		err = binary.Write(hash, binary.LittleEndian, loader.Hash)
-		if err != nil {
-			return util.FmtNewtError("Failed to seed hash: %s",
-				err.Error())
-		}
-	}
-
-	/*
-	 * First the header
-	 */
-	hdr := &ImageHdr{
-		Magic: IMAGE_MAGIC,
-		Pad1:  0,
-		HdrSz: IMAGE_HEADER_SIZE,
-		Pad2:  0,
-		ImgSz: uint32(binInfo.Size()) - uint32(image.SrcSkip),
-		Flags: 0,
-		Vers:  image.Version,
-		Pad3:  0,
-	}
-
-	if loader != nil {
-		hdr.Flags |= IMAGE_F_NON_BOOTABLE
-	}
-	if cipherSecret != nil {
-		hdr.Flags |= IMAGE_F_ENCRYPTED
-	}
-
-	if image.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 image.HeaderSize < IMAGE_HEADER_SIZE {
-			return util.FmtNewtError("Image header must be at "+
-				"least %d bytes", IMAGE_HEADER_SIZE)
-		}
-
-		hdr.HdrSz = uint16(image.HeaderSize)
-	}
-
-	err = binary.Write(imgFile, binary.LittleEndian, hdr)
-	if err != nil {
-		return util.FmtNewtError("Failed to serialize image hdr: %s",
-			err.Error())
-	}
-	err = binary.Write(hash, binary.LittleEndian, hdr)
-	if err != nil {
-		return util.FmtNewtError("Failed to hash data: %s",
-			err.Error())
-	}
-
-	if image.HeaderSize > IMAGE_HEADER_SIZE {
-		/*
-		 * Pad the image (and hash) with zero bytes to fill
-		 * out the buffer.
-		 */
-		buf := make([]byte, image.HeaderSize-IMAGE_HEADER_SIZE)
-
-		_, err = imgFile.Write(buf)
-		if err != nil {
-			return util.FmtNewtError("Failed to write padding: %s",
-				err.Error())
-		}
-
-		_, err = hash.Write(buf)
-		if err != nil {
-			return util.FmtNewtError("Failed to hash padding: %s",
-				err.Error())
-		}
-	}
-
-	/*
-	 * Skip requested initial part of image.
-	 */
-	if image.SrcSkip > 0 {
-		buf := make([]byte, image.SrcSkip)
-		_, err = binFile.Read(buf)
-		if err != nil {
-			return util.FmtNewtError("Failed to read from %s: %s",
-				image.SourceBin, err.Error())
-		}
-
-		nonZero := false
-		for _, b := range buf {
-			if b != 0 {
-				nonZero = true
-				break
-			}
-		}
-		if nonZero {
-			log.Warnf("Skip requested of image %s, but image not preceeded by %d bytes of all zeros",
-				image.SourceBin, image.SrcSkip)
-		}
-	}
-
-	var stream cipher.Stream
-	if cipherSecret != nil {
-		block, err := aes.NewCipher(plainSecret)
-		if err != nil {
-			return 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)
-	for {
-		cnt, err := binFile.Read(dataBuf)
-		if err != nil && err != io.EOF {
-			return util.FmtNewtError("Failed to read from %s: %s",
-				image.SourceBin, err.Error())
-		}
-		if cnt == 0 {
-			break
-		}
-		_, err = hash.Write(dataBuf[0:cnt])
-		if err != nil {
-			return util.FmtNewtError("Failed to hash data: %s",
-				err.Error())
-		}
-		if cipherSecret == nil {
-			_, err = imgFile.Write(dataBuf[0:cnt])
-		} else {
-			stream.XORKeyStream(encBuf, dataBuf[0:cnt])
-			_, err = imgFile.Write(encBuf[0:cnt])
-		}
-		if err != nil {
-			return util.FmtNewtError("Failed to write to %s: %s",
-				image.TargetImg, err.Error())
-		}
-	}
-
-	image.Hash = hash.Sum(nil)
-
-	/*
-	 * Write TLV info.
-	 */
-	tlvInfo := &ImageTlvInfo{
-		Magic:     IMAGE_TRAILER_MAGIC,
-		TlvTotLen: 0,
-	}
-	tlvInfoOff, err := imgFile.Seek(0, 1)
-	if err != nil {
-		return util.FmtNewtError("Failed to calculate file size of generated "+
-			"image %s: %s", image.TargetImg, err.Error())
-	}
-	err = binary.Write(imgFile, binary.LittleEndian, tlvInfo)
-	if err != nil {
-		return util.FmtNewtError("Failed to serialize image hdr: %s",
-			err.Error())
-	}
-
-	/*
-	 * Trailer with hash of the data
-	 */
-	tlv := &ImageTrailerTlv{
-		Type: IMAGE_TLV_SHA256,
-		Pad:  0,
-		Len:  uint16(len(image.Hash)),
-	}
-	err = binary.Write(imgFile, binary.LittleEndian, tlv)
-	if err != nil {
-		return util.FmtNewtError("Failed to serialize image "+
-			"trailer: %s", err.Error())
-	}
-	_, err = imgFile.Write(image.Hash)
-	if err != nil {
-		return util.FmtNewtError("Failed to append hash: %s",
-			err.Error())
-	}
-
-	for _, key := range image.Keys {
-		key.assertValid()
-
-		tlvBytes, err := generateKeyHashTlv(key)
-		if err != nil {
-			return err
-		}
-
-		if _, err = imgFile.Write(tlvBytes); err != nil {
-			return util.FmtNewtError(
-				"Failed to append key hash: %s", err.Error())
-		}
-
-		tlvBytes, err = generateSigTlv(key, image.Hash)
-		if err != nil {
-			return err
-		}
-
-		if _, err = imgFile.Write(tlvBytes); err != nil {
-			return util.FmtNewtError(
-				"Failed to append signature: %s", err.Error())
-		}
-	}
-
-	if cipherSecret != nil {
-		if len(cipherSecret) == 256 {
-			_type = IMAGE_TLV_ENC_RSA
-		} else if len(cipherSecret) == 24 {
-			_type = IMAGE_TLV_ENC_KEK
-		} else {
-			return util.FmtNewtError("Invalid enc TLV size ")
-		}
-		tlv := &ImageTrailerTlv{
-			Type: _type,
-			Pad:  0,
-			Len:  uint16(len(cipherSecret)),
-		}
-		if err := binary.Write(imgFile, binary.LittleEndian, tlv); err != nil {
-			return util.FmtNewtError(
-				"Failed to serialize cipher secret TLV: %s", err.Error())
-		}
-		if _, err := imgFile.Write(cipherSecret); err != nil {
-			return util.FmtNewtError("Failed to append encrypted key: %s",
-				err.Error())
-		}
-	}
-
-	util.StatusMessage(util.VERBOSITY_VERBOSE,
-		"Computed Hash for image %s as %s \n",
-		image.TargetImg, hex.EncodeToString(image.Hash))
-
-	// XXX: Replace "1" with io.SeekCurrent when go 1.7 becomes mainstream.
-	sz, err := imgFile.Seek(0, 1)
-	if err != nil {
-		return util.FmtNewtError("Failed to calculate file size of generated "+
-			"image %s: %s", image.TargetImg, err.Error())
-	}
-	image.TotalSize = uint(sz)
-
-	tlvInfo.TlvTotLen = uint16(sz - tlvInfoOff)
-
-	/*
-	 * Go back and write tlv info total length
-	 */
-	_, err = imgFile.Seek(tlvInfoOff, 0)
-	if err != nil {
-		return util.FmtNewtError("Failed to move to tlvInfo offset %d "+
-			"image: %s", int(tlvInfoOff), err.Error())
-	}
-	err = binary.Write(imgFile, binary.LittleEndian, tlvInfo)
-	if err != nil {
-		return util.FmtNewtError("Failed to serialize image hdr: %s",
-			err.Error())
-	}
-
-	return nil
-}
-
-func (image *Image) Generate(loader *Image) error {
-	if UseV1 {
-		return image.generateV1(loader)
-	} else {
-		return image.generateV2(loader)
-	}
-}
-
-func CreateBuildId(app *Image, loader *Image) []byte {
-	return app.Hash
-}
-
-func NewRepoManager() *RepoManager {
-	return &RepoManager{
-		repos: make(map[string]ImageManifestRepo),
-	}
-}
-
-func (r *RepoManager) GetImageManifestPkg(
-	lpkg *pkg.LocalPackage) *ImageManifestPkg {
-
-	ip := &ImageManifestPkg{
-		Name: lpkg.Name(),
-	}
-
-	var path string
-	if lpkg.Repo().IsLocal() {
-		ip.Repo = lpkg.Repo().Name()
-		path = lpkg.BasePath()
-	} else {
-		ip.Repo = lpkg.Repo().Name()
-		path = lpkg.BasePath()
-	}
-
-	if _, present := r.repos[ip.Repo]; present {
-		return ip
-	}
-
-	repo := ImageManifestRepo{
-		Name: ip.Repo,
-	}
-
-	// Make sure we restore the current working dir to whatever it was when
-	// this function was called
-	cwd, err := os.Getwd()
-	if err != nil {
-		log.Debugf("Unable to determine current working directory: %v", err)
-		return ip
-	}
-	defer os.Chdir(cwd)
-
-	if err := os.Chdir(path); err != nil {
-		return ip
-	}
-
-	var res []byte
-
-	res, err = util.ShellCommand([]string{
-		"git",
-		"rev-parse",
-		"HEAD",
-	}, nil)
-	if err != nil {
-		log.Debugf("Unable to determine commit hash for %s: %v", path, err)
-		repo.Commit = "UNKNOWN"
-	} else {
-		repo.Commit = strings.TrimSpace(string(res))
-		res, err = util.ShellCommand([]string{
-			"git",
-			"status",
-			"--porcelain",
-		}, nil)
-		if err != nil {
-			log.Debugf("Unable to determine dirty state for %s: %v", path, err)
-		} else {
-			if len(res) > 0 {
-				repo.Dirty = true
-			}
-		}
-		res, err = util.ShellCommand([]string{
-			"git",
-			"config",
-			"--get",
-			"remote.origin.url",
-		}, nil)
-		if err != nil {
-			log.Debugf("Unable to determine URL for %s: %v", path, err)
-		} else {
-			repo.URL = strings.TrimSpace(string(res))
-		}
-	}
-	r.repos[ip.Repo] = repo
-
-	return ip
-}
-
-func (r *RepoManager) AllRepos() []ImageManifestRepo {
-	keys := make([]string, 0, len(r.repos))
-	for k := range r.repos {
-		keys = append(keys, k)
-	}
-
-	sort.Strings(keys)
-
-	repos := make([]ImageManifestRepo, 0, len(keys))
-	for _, key := range keys {
-		repos = append(repos, r.repos[key])
-	}
-
-	return repos
-}
-
-func NewImageManifestSizeCollector() *ImageManifestSizeCollector {
-	return &ImageManifestSizeCollector{}
-}
-
-func (c *ImageManifestSizeCollector) AddPkg(pkg string) *ImageManifestSizePkg {
-	p := &ImageManifestSizePkg{
-		Name: pkg,
-	}
-	c.Pkgs = append(c.Pkgs, p)
-
-	return p
-}
-
-func (c *ImageManifestSizePkg) AddSymbol(file string, sym string, area string,
-	symSz uint32) {
-	f := c.addFile(file)
-	s := f.addSym(sym)
-	s.addArea(area, symSz)
-}
-
-func (p *ImageManifestSizePkg) addFile(file string) *ImageManifestSizeFile {
-	for _, f := range p.Files {
-		if f.Name == file {
-			return f
-		}
-	}
-	f := &ImageManifestSizeFile{
-		Name: file,
-	}
-	p.Files = append(p.Files, f)
-
-	return f
-}
-
-func (f *ImageManifestSizeFile) addSym(sym string) *ImageManifestSizeSym {
-	s := &ImageManifestSizeSym{
-		Name: sym,
-	}
-	f.Syms = append(f.Syms, s)
-
-	return s
-}
-
-func (s *ImageManifestSizeSym) addArea(area string, areaSz uint32) {
-	a := &ImageManifestSizeArea{
-		Name: area,
-		Size: areaSz,
-	}
-	s.Areas = append(s.Areas, a)
-}
diff --git a/newt/image/keys_test.go b/newt/image/keys_test.go
deleted file mode 100644
index 8612403..0000000
--- a/newt/image/keys_test.go
+++ /dev/null
@@ -1,246 +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 image_test
-
-import (
-	"io/ioutil"
-	"os"
-	"path"
-	"testing"
-
-	"mynewt.apache.org/newt/newt/image"
-)
-
-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-----
-`)