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

[GitHub] ccollins476ad closed pull request #243: Artifact library

ccollins476ad closed pull request #243: Artifact library
URL: https://github.com/apache/mynewt-newt/pull/243
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/artifact/flash/flash.go b/artifact/flash/flash.go
new file mode 100644
index 00000000..c37d2ddc
--- /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 00000000..5b281202
--- /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/newt/image/encrypted.go b/artifact/image/encrypted.go
similarity index 100%
rename from newt/image/encrypted.go
rename to artifact/image/encrypted.go
diff --git a/artifact/image/image.go b/artifact/image/image.go
new file mode 100644
index 00000000..244cfed9
--- /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 00000000..9141f6e1
--- /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/newt/image/keys_test.go b/artifact/image/keys_test.go
similarity index 99%
rename from newt/image/keys_test.go
rename to artifact/image/keys_test.go
index 86124031..577d9cdd 100644
--- a/newt/image/keys_test.go
+++ b/artifact/image/keys_test.go
@@ -24,8 +24,6 @@ import (
 	"os"
 	"path"
 	"testing"
-
-	"mynewt.apache.org/newt/newt/image"
 )
 
 func TestRSA(t *testing.T) {
diff --git a/artifact/image/v1.go b/artifact/image/v1.go
new file mode 100644
index 00000000..bab86f6e
--- /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 00000000..62d8c06b
--- /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
+}
diff --git a/newt/builder/build.go b/newt/builder/build.go
index 9db22135..2604db04 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(
@@ -793,41 +792,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 113cbfde..260ef537 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 becba56a..17995fec 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 4753433c..8da6f1a8 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 0673fefd..d62f5b28 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 f5fa8e64..c3045f4e 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"
@@ -109,6 +105,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) {
 
@@ -264,7 +264,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
@@ -480,11 +483,6 @@ func (t *TargetBuilder) Build() error {
 		return err
 	}
 
-	/* Create manifest. */
-	if err := t.createManifest(); err != nil {
-		return err
-	}
-
 	return nil
 }
 
@@ -498,11 +496,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()
@@ -627,137 +625,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 {
@@ -811,7 +678,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()
@@ -822,119 +689,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
diff --git a/newt/cli/image_cmds.go b/newt/cli/image_cmds.go
index 73459ed4..cf742ac1 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 1e35d4ee..513e2c4a 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 d3f3ed90..56098ff0 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,37 @@ 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)
+			}
+			if err := b.Build(); err != nil {
+				NewtUsage(nil, 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/flash/flash.go b/newt/flashmap/flashmap.go
similarity index 67%
rename from newt/flash/flash.go
rename to newt/flashmap/flashmap.go
index 97114c51..68fa699f 100644
--- a/newt/flash/flash.go
+++ b/newt/flashmap/flashmap.go
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package flash
+package flashmap
 
 import (
 	"bytes"
@@ -32,24 +32,11 @@ import (
 	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 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 = `/**
@@ -59,24 +46,16 @@ const C_VAR_COMMENT = `/**
  */
 `
 
-type FlashArea struct {
-	Name   string
-	Id     int
-	Device int
-	Offset int
-	Size   int
-}
-
 type FlashMap struct {
-	Areas       map[string]FlashArea
-	Overlaps    [][]FlashArea
-	IdConflicts [][]FlashArea
+	Areas       map[string]flash.FlashArea
+	Overlaps    [][]flash.FlashArea
+	IdConflicts [][]flash.FlashArea
 }
 
 func newFlashMap() FlashMap {
 	return FlashMap{
-		Areas:    map[string]FlashArea{},
-		Overlaps: [][]FlashArea{},
+		Areas:    map[string]flash.FlashArea{},
+		Overlaps: [][]flash.FlashArea{},
 	}
 }
 
@@ -104,9 +83,9 @@ func parseSize(val string) (int, error) {
 }
 
 func parseFlashArea(
-	name string, ymlFields map[string]interface{}) (FlashArea, error) {
+	name string, ymlFields map[string]interface{}) (flash.FlashArea, error) {
 
-	area := FlashArea{
+	area := flash.FlashArea{
 		Name: name,
 	}
 
@@ -116,7 +95,7 @@ func parseFlashArea(
 	sizePresent := false
 
 	var isSystem bool
-	area.Id, isSystem = SYSTEM_AREA_NAME_ID_MAP[name]
+	area.Id, isSystem = flash.SYSTEM_AREA_NAME_ID_MAP[name]
 
 	var err error
 
@@ -132,7 +111,7 @@ func parseFlashArea(
 			if err != nil {
 				return area, flashAreaErr(name, "invalid user id: %s", v)
 			}
-			area.Id = userId + AREA_USER_ID_MIN
+			area.Id = userId + flash.AREA_USER_ID_MIN
 			idPresent = true
 
 		case "device":
@@ -179,8 +158,8 @@ func parseFlashArea(
 	return area, nil
 }
 
-func (flashMap FlashMap) unSortedAreas() []FlashArea {
-	areas := make([]FlashArea, 0, len(flashMap.Areas))
+func (flashMap FlashMap) unSortedAreas() []flash.FlashArea {
+	areas := make([]flash.FlashArea, 0, len(flashMap.Areas))
 	for _, area := range flashMap.Areas {
 		areas = append(areas, area)
 	}
@@ -188,21 +167,9 @@ func (flashMap FlashMap) unSortedAreas() []FlashArea {
 	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) SortedAreas() []flash.FlashArea {
+	areas := flashMap.unSortedAreas()
+	return flash.SortFlashAreasById(areas)
 }
 
 func (flashMap FlashMap) DeviceIds() []int {
@@ -221,9 +188,9 @@ func (flashMap FlashMap) DeviceIds() []int {
 	return devices
 }
 
-func areasDistinct(a FlashArea, b FlashArea) bool {
-	var lo FlashArea
-	var hi FlashArea
+func areasDistinct(a flash.FlashArea, b flash.FlashArea) bool {
+	var lo flash.FlashArea
+	var hi flash.FlashArea
 
 	if a.Offset < b.Offset {
 		lo = a
@@ -237,50 +204,12 @@ func areasDistinct(a FlashArea, b FlashArea) bool {
 }
 
 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})
-			}
-		}
-	}
+	flashMap.Overlaps, flashMap.IdConflicts =
+		flash.DetectErrors(flashMap.unSortedAreas())
 }
 
 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
+	return flash.ErrorText(flashMap.Overlaps, flashMap.IdConflicts)
 }
 
 func Read(ymlFlashMap map[string]interface{}) (FlashMap, error) {
@@ -312,16 +241,16 @@ func Read(ymlFlashMap map[string]interface{}) (FlashMap, error) {
 	return flashMap, nil
 }
 
-func (flashMap FlashMap) varDecl() string {
+func flashMapVarDecl(fm FlashMap) string {
 	return fmt.Sprintf("const struct flash_area %s[%d]", C_VAR_NAME,
-		len(flashMap.Areas))
+		len(fm.Areas))
 }
 
-func (area FlashArea) writeHeader(w io.Writer) {
+func writeFlashAreaHeader(w io.Writer, area flash.FlashArea) {
 	fmt.Fprintf(w, "#define %-40s %d\n", area.Name, area.Id)
 }
 
-func (flashMap FlashMap) writeHeader(w io.Writer) {
+func writeFlashMapHeader(w io.Writer, fm FlashMap) {
 	fmt.Fprintf(w, newtutil.GeneratedPreamble())
 
 	fmt.Fprintf(w, "#ifndef H_MYNEWT_SYSFLASH_\n")
@@ -330,11 +259,11 @@ func (flashMap FlashMap) writeHeader(w io.Writer) {
 	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, "extern %s;\n", flashMapVarDecl(fm))
 	fmt.Fprintf(w, "\n")
 
-	for _, area := range flashMap.SortedAreas() {
-		area.writeHeader(w)
+	for _, area := range fm.SortedAreas() {
+		writeFlashAreaHeader(w, area)
 	}
 
 	fmt.Fprintf(w, "\n#endif\n")
@@ -348,7 +277,7 @@ func sizeComment(size int) string {
 	return fmt.Sprintf(" /* %d kB */", size/1024)
 }
 
-func (area FlashArea) writeSrc(w io.Writer) {
+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)
@@ -359,23 +288,23 @@ func (area FlashArea) writeSrc(w io.Writer) {
 	fmt.Fprintf(w, "    },\n")
 }
 
-func (flashMap FlashMap) writeSrc(w io.Writer) {
+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 = {", flashMap.varDecl())
+	fmt.Fprintf(w, "%s = {", flashMapVarDecl(fm))
 
-	for _, area := range flashMap.SortedAreas() {
+	for _, area := range fm.SortedAreas() {
 		fmt.Fprintf(w, "\n")
-		area.writeSrc(w)
+		writeFlashAreaSrc(w, area)
 	}
 
 	fmt.Fprintf(w, "};\n")
 }
 
-func (flashMap FlashMap) ensureWrittenGen(path string, contents []byte) error {
+func ensureFlashMapWrittenGen(path string, contents []byte) error {
 	writeReqd, err := util.FileContentsChanged(path, contents)
 	if err != nil {
 		return err
@@ -398,12 +327,15 @@ func (flashMap FlashMap) ensureWrittenGen(path string, contents []byte) error {
 	return nil
 }
 
-func (flashMap FlashMap) EnsureWritten(
-	srcDir string, includeDir string, targetName string) error {
+func EnsureFlashMapWritten(
+	fm FlashMap,
+	srcDir string,
+	includeDir string,
+	targetName string) error {
 
 	buf := bytes.Buffer{}
-	flashMap.writeSrc(&buf)
-	if err := flashMap.ensureWrittenGen(
+	writeFlashMapSrc(&buf, fm)
+	if err := ensureFlashMapWrittenGen(
 		fmt.Sprintf("%s/%s-sysflash.c", srcDir, targetName),
 		buf.Bytes()); err != nil {
 
@@ -411,8 +343,8 @@ func (flashMap FlashMap) EnsureWritten(
 	}
 
 	buf = bytes.Buffer{}
-	flashMap.writeHeader(&buf)
-	if err := flashMap.ensureWrittenGen(
+	writeFlashMapHeader(&buf, fm)
+	if err := ensureFlashMapWrittenGen(
 		includeDir+"/"+HEADER_PATH, buf.Bytes()); err != nil {
 		return err
 	}
diff --git a/newt/image/image.go b/newt/image/image.go
deleted file mode 100644
index 278804e0..00000000
--- 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/imgprod/imgprod.go b/newt/imgprod/imgprod.go
new file mode 100644
index 00000000..a858c494
--- /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 00000000..37067d44
--- /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
+}
diff --git a/newt/manifest/manifest.go b/newt/manifest/manifest.go
new file mode 100644
index 00000000..7c9eaff4
--- /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
+}
diff --git a/newt/mfg/create.go b/newt/mfg/create.go
index 2ee9f070..4930329e 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 87aabe0f..5e6e5adb 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 51d6e6d9..10c6c8e1 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 14065931..81196ff5 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 46e6294f..2cf914c1 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{},
@@ -801,7 +801,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 40f68b30..8d4bbc25 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{}
@@ -1076,7 +1076,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()
@@ -1085,7 +1085,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 {


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services