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

[mynewt-newt] 12/17: Artifact library update

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

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

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

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

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