blob: 5d660d1e30cf9db428d62689dcd7252fa822db29 [file] [log] [blame]
/**
* 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/hex"
"github.com/apache/mynewt-artifact/errors"
"github.com/apache/mynewt-artifact/flash"
"github.com/apache/mynewt-artifact/manifest"
"github.com/apache/mynewt-artifact/sec"
)
func (m *Mfg) validateManFlashMap(man manifest.MfgManifest) error {
idAreaMap := map[int]flash.FlashArea{}
for _, area := range man.FlashAreas {
if _, dup := idAreaMap[area.Id]; dup {
return errors.Errorf(
"mfg manifest contains duplicate flash area: %d", area.Id)
}
idAreaMap[area.Id] = area
}
seen := map[int]struct{}{}
mmrHasFlash := man.Meta != nil && man.Meta.FlashMap
for _, t := range m.Tlvs() {
if t.Header.Type == META_TLV_TYPE_FLASH_AREA {
if !mmrHasFlash {
return errors.Errorf(
"mmr contains flash map; manifest indicates otherwise")
}
body, err := t.StructuredBody()
if err != nil {
return err
}
flashBody := body.(*MetaTlvBodyFlashArea)
if _, ok := idAreaMap[int(flashBody.Area)]; !ok {
return errors.Errorf(
"flash area %d missing from mfg manifest", flashBody.Area)
}
seen[int(flashBody.Area)] = struct{}{}
}
}
if mmrHasFlash {
for _, area := range man.FlashAreas {
if _, ok := seen[area.Id]; !ok {
return errors.Errorf("flash area %d missing from mmr", area.Id)
}
}
}
return nil
}
func (m *Mfg) validateManMmrs(man manifest.MfgManifest) error {
areaMap := map[int]struct{}{}
if man.Meta != nil {
for _, mmr := range man.Meta.Mmrs {
fa := man.FindFlashAreaName(mmr.Area)
if fa == nil {
return errors.Errorf(
"flash area %s missing from mfg manifest", mmr.Area)
}
if _, dup := areaMap[fa.Id]; dup {
return errors.Errorf(
"mfg manifest contains duplicate mmr ref: %s", mmr.Area)
}
areaMap[fa.Id] = struct{}{}
}
}
seen := map[int]struct{}{}
for _, t := range m.Tlvs() {
if t.Header.Type == META_TLV_TYPE_MMR_REF {
body, err := t.StructuredBody()
if err != nil {
return err
}
mmrBody := body.(*MetaTlvBodyMmrRef)
if _, ok := areaMap[int(mmrBody.Area)]; !ok {
return errors.Errorf(
"mmr ref %d missing from mfg manifest", mmrBody.Area)
}
seen[int(mmrBody.Area)] = struct{}{}
}
}
for area, _ := range areaMap {
if _, ok := seen[area]; !ok {
return errors.Errorf("mmr ref %d missing from mmr", area)
}
}
return nil
}
// VerifyStructure checks an mfgimage's structure and internal consistency. It
// returns an error if the mfgimage is incorrect.
func (m *Mfg) VerifyStructure(eraseVal byte) error {
for _, t := range m.Tlvs() {
// Verify that TLV has a valid `type` field.
body, err := t.StructuredBody()
if err != nil {
return err
}
// Verify contents of hash TLV.
switch t.Header.Type {
case META_TLV_TYPE_HASH:
hashBody := body.(*MetaTlvBodyHash)
hash, err := m.RecalcHash(eraseVal)
if err != nil {
return err
}
if !bytes.Equal(hash, hashBody.Hash[:]) {
return errors.Errorf(
"mmr contains incorrect hash: have=%s want=%s",
hex.EncodeToString(hashBody.Hash[:]),
hex.EncodeToString(hash))
}
}
}
return nil
}
// VerifyManifest compares an mfgimage's structure to its manifest. It returns
// an error if the mfgimage doesn't match the manifest.
func (m *Mfg) VerifyManifest(man manifest.MfgManifest) error {
if man.Format != 2 {
return errors.Errorf(
"only mfgimage format 2 supported (have=%d)", man.Format)
}
mfgHash, err := m.Hash(man.EraseVal)
if err != nil {
return err
}
hashStr := hex.EncodeToString(mfgHash)
if hashStr != man.MfgHash {
return errors.Errorf(
"manifest mfg hash different from mmr: man=%s mfg=%s",
man.MfgHash, hashStr)
}
if err := m.validateManFlashMap(man); err != nil {
return err
}
if err := m.validateManMmrs(man); err != nil {
return err
}
// Make sure each target is fully present.
for _, t := range man.Targets {
if man.FindFlashAreaDevOff(man.Device, t.Offset) == nil {
return errors.Errorf(
"no flash area in mfgimage corresponding to target \"%s\"",
t.Name)
}
}
return nil
}
// VerifySigs checks an mfgimage's signatures against the provided set of keys.
// It succeeds if the mfgimage has no signatures or if any signature can be
// verified. An error is returned if there is at least one signature and they
// all fail the check.
func VerifySigs(man manifest.MfgManifest, keys []sec.PubSignKey) (int, error) {
sigs, err := man.SecSigs()
if err != nil {
return -1, err
}
hash, err := hex.DecodeString(man.MfgHash)
if err != nil {
return -1, errors.Wrapf(err,
"mfg manifest contains invalid hash: %s", man.MfgHash)
}
for keyIdx, k := range keys {
sigIdx, err := sec.VerifySigs(k, sigs, hash)
if err != nil {
return -1, errors.Wrapf(err, "failed to verify mfgimg signatures")
}
if sigIdx != -1 {
return keyIdx, nil
}
}
return -1, errors.Errorf("mfg signatures do not match provided keys")
}