blob: ec98b6757100029a37708164ef3fc62caaa35336 [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 image
import (
"bytes"
"encoding/hex"
"github.com/apache/mynewt-artifact/errors"
"github.com/apache/mynewt-artifact/manifest"
"github.com/apache/mynewt-artifact/sec"
)
func (img *Image) verifyHashDecrypted() error {
// Verify the hash.
haveHash, err := img.Hash()
if err != nil {
return err
}
wantHash, err := img.CalcHash(nil)
if err != nil {
return err
}
if !bytes.Equal(haveHash, wantHash) {
return errors.Errorf(
"image contains incorrect hash: have=%x want=%x",
haveHash, wantHash)
}
return nil
}
func (img *Image) verifyEncState() ([]byte, error) {
secret, err := img.CollectSecret()
if err != nil {
return nil, err
}
if img.Header.Flags&IMAGE_F_ENCRYPTED == 0 {
if secret != nil {
return nil, errors.Errorf(
"encrypted flag set in image header, but no encryption TLV")
}
return nil, nil
} else {
if secret == nil {
return nil, errors.Errorf(
"encryption TLV, but encrypted flag unset in image header")
}
return secret, nil
}
}
// VerifyStructure checks an image's structure for internal consistency. It
// returns an error if the image is incorrect.
func (img *Image) VerifyStructure() error {
// Verify that each TLV has a valid "type" field.
for _, t := range img.Tlvs {
if !ImageTlvTypeIsValid(t.Header.Type) {
return errors.Errorf(
"image contains TLV with invalid `type` field: %d",
t.Header.Type)
}
}
if _, err := img.verifyEncState(); err != nil {
return err
}
return nil
}
// VerifyHash calculates an image's hash and compares it to the image's SHA256
// TLV. If the image is encrypted, this function temporarily decrypts it
// before calculating the hash. The returned int is the index of the key that
// was used to decrypt the image, or -1 if none. An error is returned if the
// hash is incorrect.
func (img *Image) VerifyHash(privEncKeys []sec.PrivEncKey) (int, error) {
secret, err := img.verifyEncState()
if err != nil {
return -1, err
}
if secret == nil {
// Image not encrypted.
if err := img.verifyHashDecrypted(); err != nil {
return -1, err
}
return -1, nil
}
// Image is encrypted.
if len(privEncKeys) == 0 {
return -1, errors.Errorf(
"attempt to verify hash of encrypted image: no keys provided")
}
// We don't know which key the image is encrypted with. For each key,
// decrypt and then check the hash.
var hashErr error
for i, key := range privEncKeys {
dec, err := Decrypt(*img, key)
if err != nil {
hashErr = err
} else {
hashErr = dec.verifyHashDecrypted()
if hashErr == nil {
return i, nil
}
}
}
return -1, hashErr
}
// VerifySigs checks an image's attached signatures against the provided set of
// keys. It succeeds if the image has no signatures or if any signature can be
// verified. The returned int is the index of the key that was used to verify
// a signature, or -1 if none. An error is returned if there is at least one
// signature and they all fail the check.
func (img *Image) VerifySigs(keys []sec.PubSignKey) (int, error) {
sigs, err := img.CollectSigs()
if err != nil {
return -1, err
}
if len(sigs) == 0 {
return -1, nil
}
hash, err := img.Hash()
if err != nil {
return -1, err
}
for keyIdx, k := range keys {
sigIdx, err := sec.VerifySigs(k, sigs, hash)
if err != nil {
return -1, err
}
if sigIdx != -1 {
return keyIdx, nil
}
}
return -1, errors.Errorf("image signatures do not match provided keys")
}
// VerifyManifest compares an image's structure to its manifest. It returns
// an error if the image doesn't match the manifest.
func (img *Image) VerifyManifest(man manifest.Manifest) error {
ver, err := ParseVersion(man.Version)
if err != nil {
return errors.Wrapf(err, "manifest contains invalid `version` field")
}
if ver.Major != img.Header.Vers.Major ||
ver.Minor != img.Header.Vers.Minor ||
ver.Rev != img.Header.Vers.Rev ||
ver.BuildNum != img.Header.Vers.BuildNum {
return errors.Errorf(
"manifest version different from image header: man=%s img=%s",
ver.String(), img.Header.Vers.String())
}
var imgHash string
if hash, err := img.Hash(); err == nil {
imgHash = hex.EncodeToString(hash)
}
// A manifest contains two image hashes: `id` and `image_hash`. Check
// both.
checkHash := func(manHash string) error {
if imgHash != manHash {
return errors.Errorf(
"manifest image hash different from image TLV: man=%s img=%s",
manHash, imgHash)
}
return nil
}
if err := checkHash(man.BuildID); err != nil {
return err
}
if err := checkHash(man.ImageHash); err != nil {
return err
}
return nil
}