Hi Anatolij, On Fri, Nov 17, 2017 at 9:16 AM, Anatolij Gustschin <ag...@denx.de> wrote: > Generate u-boot-verified.rom image containing Secure Boot Manifest > when secure boot option is enabled. > > Signed-off-by: Anatolij Gustschin <ag...@denx.de> > --- > NOTE: This patch applies on top of binman changes in binman-working > branch in git://git.denx.de/u-boot-dm.git > > Changes in v3: > - New patch. Moved signing script functionality (secure_boot_helper.py > in first series) to binman. The signing is enabled automatically > via u-boot.dtsi when secure boot option is enabled > - Clean up all temporary files generated by signing script > > arch/x86/dts/u-boot.dtsi | 7 + > tools/binman/signing/baytrail.py | 313 > +++++++++++++++++++++++++++++++++++++++ > tools/binman/signing/signer.py | 3 + > 3 files changed, 323 insertions(+) > create mode 100644 tools/binman/signing/baytrail.py > > diff --git a/arch/x86/dts/u-boot.dtsi b/arch/x86/dts/u-boot.dtsi > index 7e37d4f394..98e2309108 100644 > --- a/arch/x86/dts/u-boot.dtsi > +++ b/arch/x86/dts/u-boot.dtsi > @@ -15,6 +15,13 @@ > sort-by-pos; > pad-byte = <0xff>; > size = <CONFIG_ROM_SIZE>; > +#ifdef CONFIG_BAYTRAIL_SECURE_BOOT
This needs to be a generic macro like CONFIG_SECURE_BOOT as this affects all x86 rom images. > + sign; > +#ifdef CONFIG_SYS_SOC I believe CONFIG_SYS_SOC is defined by every board, so no need to do #ifdef here. > + socname = CONFIG_SYS_SOC; > +#endif > +#endif > + > #ifdef CONFIG_HAVE_INTEL_ME > intel-descriptor { > filename = CONFIG_FLASH_DESCRIPTOR_FILE; > diff --git a/tools/binman/signing/baytrail.py > b/tools/binman/signing/baytrail.py > new file mode 100644 > index 0000000000..3bfbbedb5d > --- /dev/null > +++ b/tools/binman/signing/baytrail.py > @@ -0,0 +1,313 @@ > +# Copyright (c) 2017 DENX Software Engineering > +# Written by Markus Valentin <m...@denx.de> > +# Adapted for binman integration: Anatolij Gustschin <ag...@denx.de> > +# > +# SPDX-License-Identifier: GPL-2.0+ > +# > +# Functions for signing the binman output image for Bay Trail SoC > +# > + > +import binascii > +import logging, sys > +import os > + > +from hashlib import sha256 > +from os.path import basename, isfile, splitext > +from os.path import join as pjoin > +from struct import pack > + > +import OpenSSL > +from OpenSSL import crypto > +from cryptography import x509 > +from cryptography.hazmat.backends import default_backend > + > +FSP_FILE_NAME = "fsp-sb.bin" > +FSP_STAGE2_FILE_NAME = "fsp_stage2.bin" > +U_BOOT_ROM_FILE_NAME = 'u-boot.rom' > +OUTPUT_FILE_NAME = 'u-boot-verified.rom' > +U_BOOT_TO_SIGN_FILE_NAME = 'u-boot-to-sign.bin' > +IBB_FILE_NAME = 'ibb.bin' > +FPF_CONFIG_FILE_NAME = 'fpf_config.txt' > +SIGNED_MANIFEST_FILE_NAME = 'signed_manifest.bin' > +UNSIGNED_MANIFEST_FILE_NAME = 'un'+SIGNED_MANIFEST_FILE_NAME > +OEM_FILE_NAME = 'oemdata.bin' > + > +OEM_PRIV_KEY_FILE_NAME = 'oemkey.pem' > +OEM_PUB_KEY_FILE_NAME = 'pub_oemkey.pem' > +OEM_PUBKEY_BIN_FILE_NAME = 'pub_oemkey.bin' > +OEM_PUBKEY_AND_SIG_FILE_NAME = 'oem_pub_sig.bin' This deserves a comment block on how there files are generated on the host. > + > +FIT_PUB_KEY_FILE_NAME = "dev.crt" > + > +# FSP Stage2 size is 0x1f400. For debug FSP it is 0x2f400, > +# you must change it here wenn building with debug FSP image! typo: wenn -> when > +FSP_STAGE_2_SIZE = 0x1f400 > +FSP_UPD_SIZE = 0xc00 > +IBB_SIZE = 0x1fc00 > +MANIFEST_SIZE = 0x400 > +OEM_BLOCK_MAX_SIZE = 0x190 > +U_BOOT_ROM_SIZE = 0x800000 Can this file size be determined from the CONFIG_ROM_SIZE? > +ROMFILE_SYS_TEXT_BASE = 0x00700000 and calculate this by ourselves? > + > +MANIFEST_IDENTIFIER = b'$VBM' > +VERSION = 1 > +SECURE_VERSION_NUMBER = 2 > +OEM_DATA_PREAMBLE = '01000200' > + > +oem_data_hash_files = [] > + > + > +def append_binary_files(first_file, second_file, new_file): > + with open(new_file, 'wb') as f: > + f.write(bytearray(open(first_file, 'rb').read())) > + f.write(bytearray(open(second_file, 'rb').read())) > + > + > +# This function creates the OEM-Data block which must be inserted > +# into the Bay Trail Secure Boot Manifest. > +def assemble_oem_data(file_path): > + file_size = 0 > + with open(file_path, 'wb') as f: > + f.write(binascii.unhexlify(OEM_DATA_PREAMBLE)) > + file_size += 4 > + for hash_file in oem_data_hash_files: > + f.write(open(hash_file, 'rb').read()) > + file_size += 32 > + pad_file_with_zeros(f, OEM_BLOCK_MAX_SIZE-file_size) > + > + > +# This function creates the final U-Boot ROM image from > +# the original u-boot.rom and the signed Initial Boot Block > +# which contains the Secure Boot Manifest > +def assemble_secure_boot_image(u_boot_rom, signed_ibb): > + data = bytearray(open(u_boot_rom, 'rb').read()) > + ibb = bytearray(open(signed_ibb, 'rb').read()) > + data[-(MANIFEST_SIZE+IBB_SIZE):] = ibb > + open(OUTPUT_FILE_NAME, 'wb').write(data) > + > + > +# Constructs a complete Secure Boot Manifest which is just missing > +# the OEM publickey and the manifest signature > +def create_unsigned_secure_boot_manifest(unsigned_manifest, > + oem_file='oemdata.bin', > + ibb='ibb.bin'): > + with open(unsigned_manifest, 'wb') as f: > + f.write(MANIFEST_IDENTIFIER) > + f.write(pack('i', VERSION)) > + f.write(pack('i', MANIFEST_SIZE)) > + f.write(pack('i', SECURE_VERSION_NUMBER)) > + pad_file_with_zeros(f, 4) > + hash_function = sha256() > + hash_function.update(bytearray(open(ibb, 'rb').read())) > + f.write(hash_function.digest()[::-1]) > + pad_file_with_zeros(f, 36) > + f.write(bytearray(open(oem_file, 'rb').read())) > + pad_file_with_zeros(f, 20) > + > + > +# Fetch part of a binary from from_byte to to_byte and write > +# this part to a secondary file > +def extract_binary_part(binary_to_extract_from, to_file, from_byte, to_byte): > + data = open(binary_to_extract_from, 'rb').read() > + open(to_file, 'wb').write(data[from_byte:to_byte]) > + > + > +# Calculate a SHA256 checksum over a file and write a file with it to a > +# file next to the original file. If requested, change endianness > +# (sometimes needed because the TXE engine wants another byteorder) > +def sha256_to_file(binary_dir, file_to_hash, change_endianness=False): > + # We collect the hashes in a list (in the correct order) to be able > + # to put them later to the OEM data section > + if not oem_data_hash_files.__contains__(hashfile_path(binary_dir, > + file_to_hash)): > + oem_data_hash_files.append(hashfile_path(binary_dir, file_to_hash)) > + with open(file_to_hash, 'rb') as f: > + hash_function = sha256() > + hash_function.update(f.read()) > + # write as little to file > + if change_endianness: > + open(hashfile_path(binary_dir, file_to_hash), > + 'wb').write(hash_function.digest()) > + else: > + open(hashfile_path(binary_dir, file_to_hash), > + 'wb').write(hash_function.digest()[::-1]) > + > + > +# Create hash filename using the file_to_hash name > +def hashfile_path(binary_dir, file_to_hash): > + hash_file_name = splitext( > + basename(file_to_hash))[0].__add__('.sha256') > + return pjoin(binary_dir, hash_file_name) > + > + > +# Pad the given files with a given byte number of zeros. > +# byte_count value must be divisible by 4 > +def pad_file_with_zeros(file_handle, byte_count): > + if byte_count % 4 != 0: > + raise ValueError("Given byte_count must be divisible by 4 ...") > + pad_count = 0 > + while pad_count < byte_count: > + file_handle.write(pack('i', 0)) > + pad_count += 4 > + > + > +# Extract the modulus of a public key. The TXE engine gets the public key > +# split in modulus and exponent, for this reason we need to extract it. > +def get_modulus_from_pubkey(public_key_path): > + public_key = open(public_key_path, 'rb').read() > + cert = x509.load_pem_x509_certificate(public_key, default_backend()) > + return ("%X" % (cert.public_key().public_numbers().n)) > + > + > +# Save a given modulus and exponent to a file as binary for use within > +# the manifest > +def save_binary_public_key(pub_key_file_path, modulus, exponent=0x10001): > + with open(pub_key_file_path, 'wb') as f: > + f.write(binascii.unhexlify(modulus)[::-1]) > + f.write(pack('i', exponent)) > + > + > +# Replace the public key hash in the fuse configuration text file > +# and set the lock bit > +def replace_oem_pubkey_hash(pubkey_hash, fpf_config_path, lock_fuses): > + data = binascii.hexlify(pubkey_hash) > + fpf_config_file = open(fpf_config_path, 'r').readlines() > + > + new_line_hash = "FUSE_FILE_OEM_KEY_HASH_1:{:s}:{}\n"\ > + .format(data.upper().decode('ascii'), > + str(lock_fuses).upper()) > + new_line_sb_enabled = "FUSE_FILE_SECURE_BOOT_EN:01:{}\n"\ > + .format(str(lock_fuses).upper()) > + > + with open(fpf_config_path, 'w') as f: > + for line in fpf_config_file: > + if line.startswith('FUSE_FILE_OEM_KEY_HASH_1'): > + line = new_line_hash > + if line.startswith('FUSE_FILE_SECURE_BOOT_EN'): > + line = new_line_sb_enabled > + f.write(line) > + > + > +# For the TXE engine one needs to change the endianness > +def reverse_endianness(file_to_reverse): > + data = open(file_to_reverse, 'rb').read() > + open(file_to_reverse, 'wb').write(data[::-1]) > + > + > +# Sign the given file with the private_key using OpenSSL > +# and write it to the signature_file > +def sign_file(unsigned_file, private_key, signature_file): > + key = open(private_key, 'r').read() > + pkey_obj = crypto.load_privatekey(crypto.FILETYPE_PEM, key) > + data = open(unsigned_file, 'rb').read() > + signature = OpenSSL.crypto.sign(pkey_obj, data, "sha256") > + open(signature_file, 'wb').write(signature) > + > + > +# Perform IBB signing and generate Secure Boot Manifest for > +# Bay Trail SoC, then create final ROM image containing the > +# manifest and write it to u-boot-verified.rom file. > +def baytrail_sign(u_boot_rom, keydir, indir, outdir): > + FORMAT = ' BINMAN %(message)s' > + logging.basicConfig(stream=sys.stderr, level=logging.INFO, format=FORMAT) > + logging.info("Signing image %s" % u_boot_rom) > + for dir in indir: > + fsp = pjoin(dir, FSP_FILE_NAME) > + if not isfile(fsp): > + continue > + > + if not isfile(fsp): > + raise ValueError("Can't find FSP file %s" % fsp) > + > + # Assemble file paths > + fit_public_key = pjoin(keydir, FIT_PUB_KEY_FILE_NAME) > + fit_public_key_modulus = pjoin(keydir, FIT_PUB_KEY_FILE_NAME+".mod") > + fsp_stage2 = pjoin(outdir, FSP_STAGE2_FILE_NAME) > + u_boot_to_sign = pjoin(outdir, U_BOOT_TO_SIGN_FILE_NAME) > + ibb = pjoin(outdir, IBB_FILE_NAME) > + signed_ibb = pjoin(outdir, "signed_" + IBB_FILE_NAME) > + > + signed_manifest = pjoin(outdir, SIGNED_MANIFEST_FILE_NAME) > + unsigned_manifest = pjoin(outdir, UNSIGNED_MANIFEST_FILE_NAME) > + manifest_signature = splitext(unsigned_manifest)[0] + ".signature" > + > + oem_file = pjoin(outdir, OEM_FILE_NAME) > + oem_private_key = pjoin(keydir, OEM_PRIV_KEY_FILE_NAME) > + oem_public_key = pjoin(keydir, OEM_PUB_KEY_FILE_NAME) > + oem_pubkey_binary = pjoin(keydir, OEM_PUBKEY_BIN_FILE_NAME) > + oem_pubkey_and_sig = pjoin(keydir, OEM_PUBKEY_AND_SIG_FILE_NAME) > + > + # Check for all needed files to be available > + for f in [fsp, u_boot_rom, fit_public_key, oem_private_key]: > + if not isfile(f): > + raise ValueError("File %s not found..." % f) > + > + # Get everything from ROM file except IBB + Manfifest + UPD + FSP Stage2 > + # (127KiB + 1KiB + 3KiB + 125Kib), then write it to a separated file and > + # calculate the hash. FPS Stage2 is verified in FSP, so skip it here > + extract_binary_part(u_boot_rom, u_boot_to_sign, ROMFILE_SYS_TEXT_BASE, > + (U_BOOT_ROM_SIZE - (IBB_SIZE + MANIFEST_SIZE + > + FSP_UPD_SIZE + > FSP_STAGE_2_SIZE))) > + sha256_to_file(outdir, u_boot_to_sign, True) > + > + # Extract Stage2 of the FSP and calculate its hash > + extract_binary_part(fsp, fsp_stage2, 0, FSP_STAGE_2_SIZE) > + sha256_to_file(outdir, fsp_stage2) > + > + with open(fit_public_key_modulus, 'wb') as f: > + f.write(binascii.unhexlify(get_modulus_from_pubkey(fit_public_key))) > + sha256_to_file(outdir, fit_public_key_modulus, True) > + > + # Assemble oemdata > + logging.debug("Assembling OEM data from %d hashes: %s" % > + (oem_data_hash_files.__len__(), oem_data_hash_files)) > + assemble_oem_data(oem_file) > + > + logging.debug("Extracting last 127K from %s as %s" % > + (u_boot_rom, ibb)) > + extract_binary_part(u_boot_rom, ibb, > + (U_BOOT_ROM_SIZE-IBB_SIZE), U_BOOT_ROM_SIZE) > + > + logging.debug("Creating Secure Boot Manifest") > + create_unsigned_secure_boot_manifest(unsigned_manifest, oem_file, ibb) > + > + logging.debug("Signing manifest with OpenSSL and private key %s" % > + (oem_private_key)) > + sign_file(unsigned_manifest, oem_private_key, manifest_signature) > + > + logging.debug("Append public key and signature to unsigned manifest") > + oem_pub_key_modulus = get_modulus_from_pubkey(oem_public_key) > + save_binary_public_key(oem_pubkey_binary, oem_pub_key_modulus) > + > + reverse_endianness(manifest_signature) > + append_binary_files(oem_pubkey_binary, manifest_signature, > + oem_pubkey_and_sig) > + > + append_binary_files(unsigned_manifest, oem_pubkey_and_sig, > + signed_manifest) > + > + if isfile(FPF_CONFIG_FILE_NAME): > + hash_function = sha256() > + hash_function.update(bytearray(open(oem_pubkey_binary, 'rb').read())) > + replace_oem_pubkey_hash(hash_function.digest()[::-1], > + FPF_CONFIG_FILE_NAME, False) > + > + logging.debug("Append manifest with signature to IBB") > + append_binary_files(signed_manifest, ibb, signed_ibb) > + > + logging.debug("Assemble %s from %s and %s" % > + (OUTPUT_FILE_NAME, u_boot_rom, signed_manifest)) > + assemble_secure_boot_image(u_boot_rom, signed_ibb) > + > + # Cleanup temporary files > + os.remove(fsp_stage2) > + os.remove(ibb) > + os.remove(signed_ibb) > + os.remove(signed_manifest) > + os.remove(manifest_signature) > + os.remove(oem_file) > + os.remove(u_boot_to_sign) > + os.remove(unsigned_manifest) > + os.remove(FIT_PUB_KEY_FILE_NAME + ".sha256") > + os.remove(splitext(fsp_stage2)[0] + ".sha256") > + os.remove(splitext(u_boot_to_sign)[0] + ".sha256") > diff --git a/tools/binman/signing/signer.py b/tools/binman/signing/signer.py > index 4ec43d424f..e9ce97f559 100644 > --- a/tools/binman/signing/signer.py > +++ b/tools/binman/signing/signer.py > @@ -6,10 +6,13 @@ > # Class for signing the output image of binman > # > > +from baytrail import baytrail_sign > + > # Dictionary with SoC names and corresponding signing functions. > # Image signing support for not yet supported SoCs can be added > # here > soc_sign_dict = { > + 'baytrail': baytrail_sign, > } > > class ImageSigner(object): > -- Regards, Bin _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot