imgtool: Added support for providing the signature by 3rd party

The sign command was extended so it now allow to provide the signature
as base64 formatted RAW file using --fix-sig along with the relevant
public key --fix-sig-pubkey.

This patch is added for support the case where the party which produces
the image dose not have access to the signing image key but must request
third party for the signature.

Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no>
This commit is contained in:
Andrzej Puzdrowski 2022-03-15 15:41:14 +01:00 committed by David Brown
parent ee1b7b9d6e
commit 160303c202
2 changed files with 48 additions and 4 deletions

View File

@ -305,7 +305,7 @@ class Image():
return cipherkey, ciphermac, pubk return cipherkey, ciphermac, pubk
def create(self, key, public_key_format, enckey, dependencies=None, def create(self, key, public_key_format, enckey, dependencies=None,
sw_type=None, custom_tlvs=None, encrypt_keylen=128, clear=False): sw_type=None, custom_tlvs=None, encrypt_keylen=128, clear=False, fixed_sig=None, pub_key=None):
self.enckey = enckey self.enckey = enckey
# Calculate the hash of the public key # Calculate the hash of the public key
@ -314,6 +314,11 @@ class Image():
sha = hashlib.sha256() sha = hashlib.sha256()
sha.update(pub) sha.update(pub)
pubbytes = sha.digest() pubbytes = sha.digest()
elif pub_key is not None:
pub = pub_key.get_public_bytes()
sha = hashlib.sha256()
sha.update(pub)
pubbytes = sha.digest()
else: else:
pubbytes = bytes(hashlib.sha256().digest_size) pubbytes = bytes(hashlib.sha256().digest_size)
@ -428,7 +433,7 @@ class Image():
tlv.add('SHA256', digest) tlv.add('SHA256', digest)
if key is not None: if key is not None and fixed_sig is None:
if public_key_format == 'hash': if public_key_format == 'hash':
tlv.add('KEYHASH', pubbytes) tlv.add('KEYHASH', pubbytes)
else: else:
@ -442,6 +447,14 @@ class Image():
else: else:
sig = key.sign_digest(digest) sig = key.sign_digest(digest)
tlv.add(key.sig_tlv(), sig) tlv.add(key.sig_tlv(), sig)
elif fixed_sig is not None and key is None:
if public_key_format == 'hash':
tlv.add('KEYHASH', pubbytes)
else:
tlv.add('PUBKEY', pub)
tlv.add(pub_key.sig_tlv(), fixed_sig['value'])
else:
raise click.UsageError("Can not sign using key and provide fixed-signature at the same time")
# At this point the image was hashed + signed, we can remove the # At this point the image was hashed + signed, we can remove the
# protected TLVs from the payload (will be re-added later) # protected TLVs from the payload (will be re-added later)
@ -486,6 +499,9 @@ class Image():
self.check_trailer() self.check_trailer()
def get_signature(self):
return self.signature
def add_header(self, enckey, protected_tlv_size, aes_length=128): def add_header(self, enckey, protected_tlv_size, aes_length=128):
"""Install the image header.""" """Install the image header."""

View File

@ -22,6 +22,7 @@ import click
import getpass import getpass
import imgtool.keys as keys import imgtool.keys as keys
import sys import sys
import base64
from imgtool import image, imgtool_version from imgtool import image, imgtool_version
from imgtool.version import decode_version from imgtool.version import decode_version
from .keys import ( from .keys import (
@ -68,6 +69,10 @@ keygens = {
'x25519': gen_x25519, 'x25519': gen_x25519,
} }
def load_signature(sigfile):
with open(sigfile, 'rb') as f:
signature = base64.b64decode(f.read())
return signature
def load_key(keyfile): def load_key(keyfile):
# TODO: better handling of invalid pass-phrase # TODO: better handling of invalid pass-phrase
@ -303,6 +308,11 @@ class BasedIntParamType(click.ParamType):
default='hash', help='In what format to add the public key to ' default='hash', help='In what format to add the public key to '
'the image manifest: full key or hash of the key.') 'the image manifest: full key or hash of the key.')
@click.option('-k', '--key', metavar='filename') @click.option('-k', '--key', metavar='filename')
@click.option('--fix-sig', metavar='filename',
help='fixed signature for the image. It will be used instead of'
'the signature calculated using the public key')
@click.option('--fix-sig-pubkey', metavar='filename',
help='public key relevant to fixed signature')
@click.command(help='''Create a signed or unsigned image\n @click.command(help='''Create a signed or unsigned image\n
INFILE and OUTFILE are parsed as Intel HEX if the params have INFILE and OUTFILE are parsed as Intel HEX if the params have
.hex extension, otherwise binary format is used''') .hex extension, otherwise binary format is used''')
@ -310,7 +320,8 @@ def sign(key, public_key_format, align, version, pad_sig, header_size,
pad_header, slot_size, pad, confirm, max_sectors, overwrite_only, pad_header, slot_size, pad, confirm, max_sectors, overwrite_only,
endian, encrypt_keylen, encrypt, infile, outfile, dependencies, endian, encrypt_keylen, encrypt, infile, outfile, dependencies,
load_addr, hex_addr, erased_val, save_enctlv, security_counter, load_addr, hex_addr, erased_val, save_enctlv, security_counter,
boot_record, custom_tlv, rom_fixed, max_align, clear): boot_record, custom_tlv, rom_fixed, max_align, clear, fix_sig,
fix_sig_pubkey):
if confirm: if confirm:
# Confirmed but non-padded images don't make much sense, because # Confirmed but non-padded images don't make much sense, because
@ -356,8 +367,25 @@ def sign(key, public_key_format, align, version, pad_sig, header_size,
else: else:
custom_tlvs[tag] = value.encode('utf-8') custom_tlvs[tag] = value.encode('utf-8')
# Allow signature calculated externally.
raw_signature = load_signature(fix_sig) if fix_sig else None
baked_signature = None
pub_key = None
if raw_signature is not None:
if fix_sig_pubkey is None:
raise click.UsageError(
'public key of the fixed signature is not specified')
pub_key = load_key(fix_sig_pubkey)
baked_signature = {
'value' : raw_signature
}
img.create(key, public_key_format, enckey, dependencies, boot_record, img.create(key, public_key_format, enckey, dependencies, boot_record,
custom_tlvs, int(encrypt_keylen), clear) custom_tlvs, int(encrypt_keylen), clear, baked_signature, pub_key)
img.save(outfile, hex_addr) img.save(outfile, hex_addr)