Skip to contentSkip to conversation

binman: add ROM image signing for Bay Trail SoC

PATCH New Anatolij Gustschin 3 participants 5 messages Nov 17, 2017 – Dec 4, 2017 x86

lore
Share & tools
lore
#0 lore raw

PATCH v3 5/6 binman: add ROM image signing for Bay Trail SoC

Generate u-boot-verified.rom image containing Secure Boot Manifest when secure boot option is enabled.

---
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
arch/x86/dts/u-boot.dtsi +7 -0
index 7e37d4f394..98e2309108 100644
@@ -15,6 +15,13 @@
 		sort-by-pos;
 		pad-byte = <0xff>;
 		size = <CONFIG_ROM_SIZE>;
+#ifdef CONFIG_BAYTRAIL_SECURE_BOOT
+		sign;
+#ifdef CONFIG_SYS_SOC
+		socname = CONFIG_SYS_SOC;
+#endif
+#endif
+
 #ifdef CONFIG_HAVE_INTEL_ME
 		intel-descriptor {
 			filename = CONFIG_FLASH_DESCRIPTOR_FILE;
tools/binman/signing/baytrail.py new file +313 -0

313 lines not on this page · raw message

tools/binman/signing/signer.py +3 -0
index 4ec43d424f..e9ce97f559 100644
@@ -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):
Signature · 1 line
2.11.0
#1 lore raw

PATCH v3 5/6 binman: add ROM image signing for Bay Trail SoC

Hi Anatolij,

Quoted 14 lines · Anatolij Gustschin

Generate u-boot-verified.rom image containing Secure Boot Manifest when secure boot option is enabled.

---
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

Quoted 6 lines
 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

This is a really nice use of binman, integrating various things to make it work. It makes me wish we had this for FIT verified boot, since at present you need manual steps.

To finish this, please add a test and info in the binman README about the signing feature (x86-specific stuff can stay where it is).

Quoted 38 lines
arch/x86/dts/u-boot.dtsi +7 -0
index 7e37d4f394..98e2309108 100644
@@ -15,6 +15,13 @@
                sort-by-pos;
                pad-byte = <0xff>;
                size = <CONFIG_ROM_SIZE>;
+#ifdef CONFIG_BAYTRAIL_SECURE_BOOT
+               sign;
+#ifdef CONFIG_SYS_SOC
+               socname = CONFIG_SYS_SOC;
+#endif
+#endif
+
 #ifdef CONFIG_HAVE_INTEL_ME
                intel-descriptor {
                        filename = CONFIG_FLASH_DESCRIPTOR_FILE;
tools/binman/signing/baytrail.py new file +14 -0
new file mode 100644
index 0000000000..3bfbbedb5d
@@ -0,0 +1,313 @@
+# Copyright (c) 2017 DENX Software Engineering
+# Written by Markus Valentin <[email protected]>
+# Adapted for binman integration: Anatolij Gustschin <[email protected]>
+#
+# 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

tools/binman/signing/baytrail.py line 24

Quoted 10 lines
tools/binman/signing/baytrail.py new file +10 -0
+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"

Please use ' throughout if you can

tools/binman/signing/baytrail.py line 32

Quoted 8 lines
tools/binman/signing/baytrail.py new file +8 -0
+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

space around +

Quoted 15 lines
tools/binman/signing/baytrail.py new file +15 -0
+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'
+
+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!
+FSP_STAGE_2_SIZE = 0x1f400
+FSP_UPD_SIZE = 0xc00
+IBB_SIZE = 0x1fc00
+MANIFEST_SIZE = 0x400

tools/binman/signing/baytrail.py line 57

Quoted 10 lines
tools/binman/signing/baytrail.py new file +10 -0
+OEM_BLOCK_MAX_SIZE = 0x190
+U_BOOT_ROM_SIZE = 0x800000
+ROMFILE_SYS_TEXT_BASE = 0x00700000
+
+MANIFEST_IDENTIFIER = b'$VBM'
+VERSION = 1
+SECURE_VERSION_NUMBER = 2
+OEM_DATA_PREAMBLE = '01000200'
+
+oem_data_hash_files = []

comment?

tools/binman/signing/baytrail.py line 60

Quoted 3 lines
tools/binman/signing/baytrail.py new file +3 -0
+
+
+def append_binary_files(first_file, second_file, new_file):

function comment. Please fix globally. There is a standard format for these, describing args and return value.

Quoted 16 lines
tools/binman/signing/baytrail.py new file +16 -0
+    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)

tools/binman/signing/baytrail.py line 86

Quoted 10 lines
tools/binman/signing/baytrail.py new file +10 -0
+
+
+# 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)

Should probably use

with open(OUTPUT_FILE_NAME, 'wb') as fd:
fd.write(data)

so that the file gets closed here.

Quoted 206 lines
tools/binman/signing/baytrail.py new file +206 -0
+
+
+# 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],

tools/binman/signing/baytrail.py line 302

Quoted 10 lines
tools/binman/signing/baytrail.py new file +10 -0
+                                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

Instead of this, can you create a tmpdir and remove the whole directory?

Quoted 30 lines
tools/binman/signing/baytrail.py new file +11 -0
+    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")
tools/binman/signing/signer.py +3 -1
index 4ec43d424f..e9ce97f559 100644
@@ -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):
--
Quoted 2 lines

2.11.0

Regards,
Simon

#2 lore raw

PATCH v3 5/6 binman: add ROM image signing for Bay Trail SoC

Hi Simon,

Quoted 24 lines · On Mon, 20 Nov 2017 08:40:32 -0700 Simon Glass sjg at chromium.org wrote:

Hi Anatolij,

Quoted 14 lines · Anatolij Gustschin

Generate u-boot-verified.rom image containing Secure Boot Manifest when secure boot option is enabled.

---
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

Quoted 6 lines
 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
Quoted 6 lines

This is a really nice use of binman, integrating various things to make it work. It makes me wish we had this for FIT verified boot, since at present you need manual steps.

To finish this, please add a test and info in the binman README about the signing feature (x86-specific stuff can stay where it is).

OK.

...

Quoted 3 lines
Quoted 1 line

+FSP_FILE_NAME = "fsp-sb.bin"

Please use ' throughout if you can

OK.

...

Quoted 4 lines

tools/binman/signing/baytrail.py line 32

Quoted 2 lines
tools/binman/signing/baytrail.py new file +2 -0
+SIGNED_MANIFEST_FILE_NAME = 'signed_manifest.bin'
+UNSIGNED_MANIFEST_FILE_NAME = 'un'+SIGNED_MANIFEST_FILE_NAME  

space around +

OK.

...

Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+oem_data_hash_files = []  

comment?

OK.

...

Quoted 5 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+def append_binary_files(first_file, second_file, new_file):  

function comment. Please fix globally. There is a standard format for these, describing args and return value.

OK, done in v4.

...

Quoted 11 lines

tools/binman/signing/baytrail.py line 86

Quoted 8 lines
tools/binman/signing/baytrail.py new file +8 -0
+# 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)  

Should probably use

Quoted 4 lines

with open(OUTPUT_FILE_NAME, 'wb') as fd:
fd.write(data)

so that the file gets closed here.

OK, will fix.

...

Quoted 5 lines

tools/binman/signing/baytrail.py line 302

Quoted 3 lines
tools/binman/signing/baytrail.py new file +3 -0
+    assemble_secure_boot_image(u_boot_rom, signed_ibb)
+
+    # Cleanup temporary files  

Instead of this, can you create a tmpdir and remove the whole directory?

OK, will rework for v4.

Thanks,
Anatolij

#3 lore raw

PATCH v3 5/6 binman: add ROM image signing for Bay Trail SoC

In reply to Anatolij Gustschin

Hi Anatolij,

Quoted 25 lines · Anatolij Gustschin

Generate u-boot-verified.rom image containing Secure Boot Manifest when secure boot option is enabled.

---
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
arch/x86/dts/u-boot.dtsi +0 -0
index 7e37d4f394..98e2309108 100644

arch/x86/dts/u-boot.dtsi line 18

Quoted 4 lines
arch/x86/dts/u-boot.dtsi +1 -0
@@ -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.

arch/x86/dts/u-boot.dtsi line 20

Quoted 2 lines
arch/x86/dts/u-boot.dtsi +2 -0
+               sign;
+#ifdef CONFIG_SYS_SOC

I believe CONFIG_SYS_SOC is defined by every board, so no need to do #ifdef here.

Quoted 41 lines
arch/x86/dts/u-boot.dtsi +4 -0
+               socname = CONFIG_SYS_SOC;
+#endif
+#endif
+
 #ifdef CONFIG_HAVE_INTEL_ME
                intel-descriptor {
                        filename = CONFIG_FLASH_DESCRIPTOR_FILE;
tools/binman/signing/baytrail.py new file +28 -0
new file mode 100644
index 0000000000..3bfbbedb5d
@@ -0,0 +1,313 @@
+# Copyright (c) 2017 DENX Software Engineering
+# Written by Markus Valentin <[email protected]>
+# Adapted for binman integration: Anatolij Gustschin <[email protected]>
+#
+# 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'

tools/binman/signing/baytrail.py line 38

Quoted 10 lines
tools/binman/signing/baytrail.py new file +10 -0
+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.

tools/binman/signing/baytrail.py line 43

Quoted 5 lines
tools/binman/signing/baytrail.py new file +5 -0
+
+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

tools/binman/signing/baytrail.py line 49

Quoted 6 lines
tools/binman/signing/baytrail.py new file +6 -0
+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?

tools/binman/signing/baytrail.py line 50

Quoted 1 line
tools/binman/signing/baytrail.py new file +1 -0
+ROMFILE_SYS_TEXT_BASE = 0x00700000

and calculate this by ourselves?

Quoted 272 lines
tools/binman/signing/baytrail.py new file +263 -0
+
+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")
tools/binman/signing/signer.py +1 -0
index 4ec43d424f..e9ce97f559 100644
@@ -6,10 +6,13 @@
 # Class for signing the output image of binman
 #
 
+from baytrail import baytrail_sign

tools/binman/signing/signer.py

Quoted 10 lines
tools/binman/signing/signer.py +2 -1
+
 # 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

#4 lore raw

PATCH v3 5/6 binman: add ROM image signing for Bay Trail SoC

Hi Bin,

On Mon, 4 Dec 2017 14:30:03 +0800
Bin Meng bmeng.cn at gmail.com wrote:
...

Quoted 4 lines
Quoted 4 lines
arch/x86/dts/u-boot.dtsi +0 -0
index 7e37d4f394..98e2309108 100644
Quoted 8 lines

arch/x86/dts/u-boot.dtsi line 18

Quoted 5 lines
arch/x86/dts/u-boot.dtsi +1 -0
@@ -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.

OK, will fix it.

Quoted 5 lines

arch/x86/dts/u-boot.dtsi line 20

Quoted 2 lines
arch/x86/dts/u-boot.dtsi +2 -0
+               sign;
+#ifdef CONFIG_SYS_SOC  

I believe CONFIG_SYS_SOC is defined by every board, so no need to do #ifdef here.

OK, I can drop it.

...

Quoted 6 lines

arch/x86/dts/u-boot.dtsi line 24

Quoted 4 lines
arch/x86/dts/u-boot.dtsi +4 -0
+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.

OK.

...

Quoted 4 lines

arch/x86/dts/u-boot.dtsi line 26

Quoted 2 lines
arch/x86/dts/u-boot.dtsi +2 -0
+# 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

OK, thanks.

Quoted 9 lines

arch/x86/dts/u-boot.dtsi

Quoted 6 lines
arch/x86/dts/u-boot.dtsi +6 -0
+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?

Quoted 3 lines
Quoted 1 line

+ROMFILE_SYS_TEXT_BASE = 0x00700000

and calculate this by ourselves?

I cannot invest more time on this, I'm having many issues to add coverage tests for this file, so I'll give it up. We are far beyond the planned efforts for this topic and I cannot spend time to polish this further, sorry.

Thanks,
Anatolij