blob: 90fee88b97fec16f2464127a570186f1d8f1b13a [file] [log] [blame]
# Copyright 2019 Linaro Limited
#
# Licensed 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.
"""
SUIT Image signing and management.
"""
from . import image
import cbor2
import hashlib
import os.path
import struct
IMAGE_MAGIC = 0x96f3b83e
IMAGE_HEADER_SIZE = 32
BIN_EXT = "bin"
INTEL_HEX_EXT = "hex"
DEFAULT_MAX_SECTORS = 128
MANIFEST_INFO_SIZE = 4
MANIFEST_INFO_MAGIC = 0x6917
STRUCT_ENDIAN_DICT = {
'little': '<',
'big:': '>'
}
class Suit():
def __init__(self, endian):
self.payload = None
self.sequence = 0
self.endian = endian
def add_payload(self, payload):
self.payload = payload
def set_sequence(self, sequence):
self.sequence = sequence
def generate(self, key):
manifest = self._gen_manifest(self.payload)
sig = self._gen_sig(manifest, key)
body = cbor2.dumps({1: sig, 2: manifest})
return (struct.pack(STRUCT_ENDIAN_DICT[self.endian] +
"HH", MANIFEST_INFO_MAGIC, MANIFEST_INFO_SIZE + len(body)) +
body)
def _get_digest(self, payload, prot):
"""Make a COSE Digest of the payload, with the given
CBOR-encoded protected data. This is defined by SUIT."""
block = cbor2.dumps(["Digest", prot, payload])
sha = hashlib.sha256()
sha.update(block)
digest = sha.digest()
return [prot, {}, None, digest]
def _gen_manifest(self, payload):
"""Generate the actual SUIT manifest itself."""
prot = cbor2.dumps({1: 41})
digest = self._get_digest(payload, prot)
return cbor2.dumps({
1: 1,
2: self.sequence,
3: [ { 1: [b"0"], 2: len(payload), 3: digest } ] })
def _gen_sig(self, body, key):
"""Generate a COSE signature wrapper for the given payload,
signed with the given key. This doesn't return CBOR, but a
Python data structure intended to be included within CBOR."""
body_prot = cbor2.dumps({3: 0})
sig_prot = cbor2.dumps({1: -37}) # TODO: Hardcoded, -37 is RS256.
# The block we actually sign.
sig_block = cbor2.dumps(["Signature", body_prot, sig_prot, b"", body])
sig = key.sign(bytes(sig_block))
return cbor2.CBORTag(98, [body_prot, {}, None, [
[sig_prot, {4: b"key-id-here"}, sig]]])
class Image(image.Image):
def __init__(self, **kwargs):
image.Image.__init__(self, **kwargs)
def check(self):
"""Perform some sanity checking of the image."""
# If there is a header requested, make sure that the image
# starts with all zeros.
if self.header_size > 0:
if any(v != 0 for v in self.payload[0:self.header_size]):
raise Exception("Padding requested, but image does not start with zeros")
if self.slot_size > 0:
tsize = self._trailer_size(self.align, self.max_sectors,
self.overwrite_only)
padding = self.slot_size - (len(self.payload) + tsize)
if padding < 0:
msg = "Image size (0x{:x}) + trailer (0x{:x}) exceeds requested size 0x{:x}".format(
len(self.payload), tsize, self.slot_size)
raise Exception(msg)
def create(self, key, enckey):
if enckey is not None:
raise Exception("SUIT support does not yet support encryption")
self.add_header(None)
s = Suit(self.endian)
s.add_payload(self.payload)
# TODO: Where does this sequence number come from?
s.set_sequence(self.version.build)
self.payload += s.generate(key)
def _image_magic(self):
return IMAGE_MAGIC