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