Remove old binary signing scripts of TF-M 1.1

pull/14333/head
Vikas Katariya 2021-01-15 15:49:59 +00:00 committed by Lingkai Dong
parent 8648e4f4d6
commit 308ffe9e89
5 changed files with 0 additions and 567 deletions

View File

@ -1,18 +0,0 @@
# Copyright 2017 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.
# This file is intentionally empty.
#
# The __init__.py files are required to make Python treat the directories as
# containing packages.

View File

@ -1,79 +0,0 @@
# Copyright (c) 2019, Arm Limited.
#
# SPDX-License-Identifier: Apache-2.0
#
# 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.
import os
import sys
import cbor
# SW component IDs
SW_COMPONENT_RANGE = 0
SW_COMPONENT_TYPE = SW_COMPONENT_RANGE + 1
MEASUREMENT_VALUE = SW_COMPONENT_RANGE + 2
SW_COMPONENT_VERSION = SW_COMPONENT_RANGE + 4
SIGNER_ID = SW_COMPONENT_RANGE + 5
MEASUREMENT_DESCRIPTION = SW_COMPONENT_RANGE + 6
def create_sw_component_data(sw_type, sw_version, sw_measurement_type,
sw_measurement_value, sw_signer_id):
# List of SW component claims (key ID + value)
key_value_list = [
SW_COMPONENT_TYPE, sw_type,
SW_COMPONENT_VERSION, sw_version,
SIGNER_ID, sw_signer_id,
MEASUREMENT_DESCRIPTION, sw_measurement_type,
MEASUREMENT_VALUE, sw_measurement_value
]
# The measurement value should be the last item (key + value) in the list
# to make it easier to modify its value later in the bootloader.
# A dictionary would be the best suited data structure to store these
# key-value pairs (claims), however dictionaries are not sorted, but for
# example the lists do keep to order of items which we care about now.
# An ordered dictionary could be used instead, but it would be converted
# to a dict before the encoding and this conversion may not keep the order
# of the items.
if (len(key_value_list) % 2) != 0:
print('Error: The length of the sw component claim list must '
'be even (key + value).', file=sys.stderr)
sys.exit(1)
else:
claim_number = (int)(len(key_value_list) / 2)
# The output of this function must be a CBOR encoded map (dictionary) of
# the SW component claims. The CBOR representation of an array and a map
# (dictionary) is quite similar. To convert the encoded list to a map, it
# is enough to modify the first byte (CBOR data item header) of the
# data. This applies up to 23 items (11 claims in this case) - until the 5
# lower bits of the item header are used as an item count specifier.
if claim_number > 11:
print('Error: There are more than 11 claims in the '
'list of sw component claims.', file=sys.stderr)
sys.exit(1)
record_array = bytearray(cbor.dumps(key_value_list))
# Modify the CBOR data item header (from array to map)
# 7..5 bits : Major type
# Array - 0x80
# Map - 0xA0
# 4..0 bits : Number of items
record_array[0] = 0xA0 + claim_number
return bytes(record_array)

View File

@ -1,267 +0,0 @@
# Copyright 2017 Linaro Limited
# Copyright (c) 2018-2019, Arm 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.
"""
Image signing and management.
"""
from . import version as versmod
from . import boot_record as br
import hashlib
import struct
IMAGE_MAGIC = 0x96f3b83d
IMAGE_HEADER_SIZE = 32
TLV_HEADER_SIZE = 4
PAYLOAD_DIGEST_SIZE = 32 # SHA256 hash
KEYHASH_SIZE = 32
DEP_IMAGES_KEY = "images"
DEP_VERSIONS_KEY = "versions"
# Image header flags.
IMAGE_F = {
'PIC': 0x0000001,
'NON_BOOTABLE': 0x0000010,
'RAM_LOAD': 0x0000020, }
TLV_VALUES = {
'KEYHASH': 0x01,
'KEY' : 0x02,
'SHA256' : 0x10,
'RSA2048': 0x20,
'RSA3072': 0x23,
'DEPENDENCY': 0x40,
'SEC_CNT': 0x50,
'BOOT_RECORD': 0x60, }
TLV_INFO_SIZE = 4
TLV_INFO_MAGIC = 0x6907
TLV_PROT_INFO_MAGIC = 0x6908
# Sizes of the image trailer, depending on flash write size.
trailer_sizes = {
write_size: 128 * 3 * write_size + 8 * 2 + 16
for write_size in [1, 2, 4, 8]
}
boot_magic = bytearray([
0x77, 0xc2, 0x95, 0xf3,
0x60, 0xd2, 0xef, 0x7f,
0x35, 0x52, 0x50, 0x0f,
0x2c, 0xb6, 0x79, 0x80, ])
class TLV():
def __init__(self, magic=TLV_INFO_MAGIC):
self.magic = magic
self.buf = bytearray()
def __len__(self):
return TLV_INFO_SIZE + len(self.buf)
def add(self, kind, payload):
"""
Add a TLV record. Kind should be a string found in TLV_VALUES above.
"""
buf = struct.pack('<BBH', TLV_VALUES[kind], 0, len(payload))
self.buf += buf
self.buf += payload
def get(self):
if len(self.buf) == 0:
return bytes()
header = struct.pack('<HH', self.magic, len(self))
return header + bytes(self.buf)
class Image():
@classmethod
def load(cls, path, included_header=False, **kwargs):
"""Load an image from a given file"""
with open(path, 'rb') as f:
payload = f.read()
obj = cls(**kwargs)
obj.payload = payload
# Add the image header if needed.
if not included_header and obj.header_size > 0:
obj.payload = (b'\000' * obj.header_size) + obj.payload
obj.check()
return obj
def __init__(self, version, header_size=IMAGE_HEADER_SIZE, security_cnt=0,
pad=0):
self.version = version
self.header_size = header_size or IMAGE_HEADER_SIZE
self.security_cnt = security_cnt
self.pad = pad
def __repr__(self):
return "<Image version={}, header_size={}, security_counter={}, \
pad={}, payloadlen=0x{:x}>".format(
self.version,
self.header_size,
self.security_cnt,
self.pad,
len(self.payload))
def save(self, path):
with open(path, 'wb') as f:
f.write(self.payload)
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 and v != b'\000' for v in self.payload[0:self.header_size]):
raise Exception("Padding requested, but image does not start with zeros")
def sign(self, sw_type, key, ramLoadAddress, dependencies=None):
image_version = (str(self.version.major) + '.'
+ str(self.version.minor) + '.'
+ str(self.version.revision))
# Calculate the hash of the public key
if key is not None:
pub = key.get_public_bytes()
sha = hashlib.sha256()
sha.update(pub)
pubbytes = sha.digest()
else:
pubbytes = bytes(KEYHASH_SIZE)
# The image hash is computed over the image header, the image itself
# and the protected TLV area. However, the boot record TLV (which is
# part of the protected area) should contain this hash before it is
# even calculated. For this reason the script fills this field with
# zeros and the bootloader will insert the right value later.
image_hash = bytes(PAYLOAD_DIGEST_SIZE)
# Create CBOR encoded boot record
boot_record = br.create_sw_component_data(sw_type, image_version,
"SHA256", image_hash,
pubbytes)
# Mandatory protected TLV area: TLV info header
# + security counter TLV
# + boot record TLV
# Size of the security counter TLV: header ('BBH') + payload ('I')
# = 8 Bytes
protected_tlv_size = TLV_INFO_SIZE + 8 + TLV_HEADER_SIZE \
+ len(boot_record)
if dependencies is None:
dependencies_num = 0
else:
# Size of a dependency TLV:
# header ('BBH') + payload('IBBHI') = 16 Bytes
dependencies_num = len(dependencies[DEP_IMAGES_KEY])
protected_tlv_size += (dependencies_num * 16)
# At this point the image is already on the payload, this adds
# the header to the payload as well
self.add_header(key, protected_tlv_size, ramLoadAddress)
prot_tlv = TLV(TLV_PROT_INFO_MAGIC)
# Protected TLVs must be added first, because they are also included
# in the hash calculation
payload = struct.pack('I', self.security_cnt)
prot_tlv.add('SEC_CNT', payload)
prot_tlv.add('BOOT_RECORD', boot_record)
if dependencies_num != 0:
for i in range(dependencies_num):
payload = struct.pack(
'<'+'B3x'+'BBHI',
int(dependencies[DEP_IMAGES_KEY][i]),
dependencies[DEP_VERSIONS_KEY][i].major,
dependencies[DEP_VERSIONS_KEY][i].minor,
dependencies[DEP_VERSIONS_KEY][i].revision,
dependencies[DEP_VERSIONS_KEY][i].build
)
prot_tlv.add('DEPENDENCY', payload)
self.payload += prot_tlv.get()
sha = hashlib.sha256()
sha.update(self.payload)
image_hash = sha.digest()
tlv = TLV()
tlv.add('SHA256', image_hash)
if key is not None:
if key.get_public_key_format() == 'hash':
tlv.add('KEYHASH', pubbytes)
else:
tlv.add('KEY', pub)
sig = key.sign(self.payload)
tlv.add(key.sig_tlv(), sig)
self.payload += tlv.get()
def add_header(self, key, protected_tlv_size, ramLoadAddress):
"""Install the image header.
The key is needed to know the type of signature, and
approximate the size of the signature."""
flags = 0
if ramLoadAddress is not None:
# add the load address flag to the header to indicate that an SRAM
# load address macro has been defined
flags |= IMAGE_F["RAM_LOAD"]
fmt = ('<' +
# type ImageHdr struct {
'I' + # Magic uint32
'I' + # LoadAddr uint32
'H' + # HdrSz uint16
'H' + # PTLVSz uint16
'I' + # ImgSz uint32
'I' + # Flags uint32
'BBHI' + # Vers ImageVersion
'I' # Pad1 uint32
) # }
assert struct.calcsize(fmt) == IMAGE_HEADER_SIZE
header = struct.pack(fmt,
IMAGE_MAGIC,
0 if (ramLoadAddress is None) else ramLoadAddress, # LoadAddr
self.header_size,
protected_tlv_size, # TLV info header + Protected TLVs
len(self.payload) - self.header_size, # ImageSz
flags,
self.version.major,
self.version.minor or 0,
self.version.revision or 0,
self.version.build or 0,
0) # Pad1
self.payload = bytearray(self.payload)
self.payload[:len(header)] = header
def pad_to(self, size, align):
"""Pad the image to the given size, with the given flash alignment."""
tsize = trailer_sizes[align]
padding = 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, size)
raise Exception(msg)
pbytes = b'\xff' * padding
pbytes += b'\xff' * (tsize - len(boot_magic))
pbytes += boot_magic
self.payload += pbytes

View File

@ -1,137 +0,0 @@
#!/usr/bin/env python3
# Copyright (c) 2017,2019 Linaro Limited.
# Copyright (c) 2017-2019, Arm 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.
"""
Cryptographic key management for imgtool.
"""
from __future__ import print_function
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.hashes import SHA256
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.asymmetric.padding import PSS, PKCS1v15
from cryptography.hazmat.primitives.asymmetric.padding import MGF1
import hashlib
from pyasn1.type import namedtype, univ
from pyasn1.codec.der.encoder import encode
# Sizes that bootutil will recognize
RSA_KEY_SIZES = [2048, 3072]
# Public exponent
PUBLIC_EXPONENT = 65537
# By default, we use RSA-PSS (PKCS 2.1). That can be overridden on
# the command line to support the older (less secure) PKCS1.5
sign_rsa_pss = True
AUTOGEN_MESSAGE = "/* Autogenerated by imgtool.py, do not edit. */"
class RSAUsageError(Exception):
pass
class RSAutil():
def __init__(self, key, public_key_format='hash'):
"""Construct an RSA key with the given key data"""
self.key = key
self.public_key_format = public_key_format
def key_size(self):
return self.key.key_size
def get_public_key_format(self):
return self.public_key_format
@staticmethod
def generate(key_size=2048):
if key_size not in RSA_KEY_SIZES:
raise RSAUsageError("Key size {} is not supported by MCUboot"
.format(key_size))
return RSAutil(rsa.generate_private_key(
public_exponent=PUBLIC_EXPONENT,
key_size=key_size,
backend=default_backend()))
def export_private(self, path):
with open(path, 'wb') as f:
f.write(self.key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()))
def get_public_bytes(self):
return self.key.public_key().public_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PublicFormat.PKCS1)
def emit_c(self):
print(AUTOGEN_MESSAGE)
print("const unsigned char rsa_pub_key[] = {", end='')
encoded = self.get_public_bytes()
for count, b in enumerate(encoded):
if count % 8 == 0:
print("\n\t", end='')
else:
print(" ", end='')
print("0x{:02x},".format(b), end='')
print("\n};")
print("const unsigned int rsa_pub_key_len = {};".format(len(encoded)))
def sig_type(self):
"""Return the type of this signature (as a string)"""
if sign_rsa_pss:
return "PKCS1_PSS_RSA{}_SHA256".format(self.key_size())
else:
return "PKCS15_RSA{}_SHA256".format(self.key_size())
def sig_len(self):
return 256 if self.key_size() == 2048 else 384
def sig_tlv(self):
return "RSA2048" if self.key_size() == 2048 else "RSA3072"
def sign(self, payload):
if sign_rsa_pss:
signature = self.key.sign(
data=payload,
padding=PSS(
mgf=MGF1(SHA256()),
salt_length=32
),
algorithm=SHA256()
)
else:
signature = self.key.sign(
data=payload,
padding=PKCS1v15(),
algorithm=SHA256()
)
assert len(signature) == self.sig_len()
return signature
def load(path, public_key_format='hash'):
with open(path, 'rb') as f:
pem = f.read()
try:
key = serialization.load_pem_private_key(
pem,
password=None,
backend=default_backend()
)
return RSAutil(key, public_key_format)
except ValueError:
raise Exception("Unsupported RSA key file")

View File

@ -1,66 +0,0 @@
# Copyright 2017 Linaro Limited
# Copyright (c) 2018, Arm 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.
"""
Semi Semantic Versioning
Implements a subset of semantic versioning that is supportable by the image header.
"""
import argparse
from collections import namedtuple
import re
SemiSemVersion = namedtuple('SemiSemVersion', ['major', 'minor', 'revision', 'build'])
def increment_build_num(lastVer):
newVer = SemiSemVersion(lastVer.major, lastVer.minor, lastVer.revision, lastVer.build + 1)
return newVer
# -1 if a is older than b; 0 if they're the same version; 1 if a is newer than b
def compare(a, b):
if (a.major > b.major): return 1
elif (a.major < b.major): return -1
else:
if (a.minor > b.minor): return 1
elif (a.minor < b.minor): return -1
else:
if (a.revision > b.revision): return 1
elif (a.revision < b.revision): return -1
else:
if (a.build > b.build): return 1
elif (a.build < b.build): return -1
else: return 0
version_re = re.compile(r"""^([1-9]\d*|0)(\.([1-9]\d*|0)(\.([1-9]\d*|0)(\+([1-9]\d*|0))?)?)?$""")
def decode_version(text):
"""Decode the version string, which should be of the form maj.min.rev+build"""
m = version_re.match(text)
if m:
result = SemiSemVersion(
int(m.group(1)) if m.group(1) else 0,
int(m.group(3)) if m.group(3) else 0,
int(m.group(5)) if m.group(5) else 0,
int(m.group(7)) if m.group(7) else 0)
return result
else:
msg = "Invalid version number, should be maj.min.rev+build with later parts optional"
raise argparse.ArgumentTypeError(msg)
if __name__ == '__main__':
print(decode_version("1.2"))
print(decode_version("1.0"))
print(decode_version("0.0.2+75"))
print(decode_version("0.0.0+00"))