| /** |
| * 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") |
| } |