All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH v3 5/6] binman: add ROM image signing for Bay Trail SoC
@ 2017-11-17  1:16 Anatolij Gustschin
  2017-11-20 15:40 ` Simon Glass
  2017-12-04  6:30 ` Bin Meng
  0 siblings, 2 replies; 5+ messages in thread
From: Anatolij Gustschin @ 2017-11-17  1:16 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Anatolij Gustschin <agust@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
+		sign;
+#ifdef CONFIG_SYS_SOC
+		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 <mv@denx.de>
+# Adapted for binman integration: Anatolij Gustschin <agust@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'
+
+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
+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 = []
+
+
+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):
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [U-Boot] [PATCH v3 5/6] binman: add ROM image signing for Bay Trail SoC
  2017-11-17  1:16 [U-Boot] [PATCH v3 5/6] binman: add ROM image signing for Bay Trail SoC Anatolij Gustschin
@ 2017-11-20 15:40 ` Simon Glass
  2017-11-28 15:27   ` Anatolij Gustschin
  2017-12-04  6:30 ` Bin Meng
  1 sibling, 1 reply; 5+ messages in thread
From: Simon Glass @ 2017-11-20 15:40 UTC (permalink / raw)
  To: u-boot

Hi Anatolij,

On 16 November 2017 at 18:16, Anatolij Gustschin <agust@denx.de> wrote:
> Generate u-boot-verified.rom image containing Secure Boot Manifest
> when secure boot option is enabled.
>
> Signed-off-by: Anatolij Gustschin <agust@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
>

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

> 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
> +               sign;
> +#ifdef CONFIG_SYS_SOC
> +               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 <mv@denx.de>
> +# Adapted for binman integration: Anatolij Gustschin <agust@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"

Please use ' throughout if you can

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

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

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

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

Should probably use

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

so that the file gets closed here.

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

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

> +    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):
> --
> 2.11.0
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [U-Boot] [PATCH v3 5/6] binman: add ROM image signing for Bay Trail SoC
  2017-11-20 15:40 ` Simon Glass
@ 2017-11-28 15:27   ` Anatolij Gustschin
  0 siblings, 0 replies; 5+ messages in thread
From: Anatolij Gustschin @ 2017-11-28 15:27 UTC (permalink / raw)
  To: u-boot

Hi Simon,

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

> Hi Anatolij,
> 
> On 16 November 2017 at 18:16, Anatolij Gustschin <agust@denx.de> wrote:
> > Generate u-boot-verified.rom image containing Secure Boot Manifest
> > when secure boot option is enabled.
> >
> > Signed-off-by: Anatolij Gustschin <agust@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
> >  
> 
> 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.

...
> > +FSP_FILE_NAME = "fsp-sb.bin"  
> 
> Please use ' throughout if you can

OK.

...
> > +SIGNED_MANIFEST_FILE_NAME = 'signed_manifest.bin'
> > +UNSIGNED_MANIFEST_FILE_NAME = 'un'+SIGNED_MANIFEST_FILE_NAME  
> 
> space around +

OK.

...
> > +
> > +oem_data_hash_files = []  
> 
> comment?

OK.

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

...
> > +# 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.

OK, will fix.

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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [U-Boot] [PATCH v3 5/6] binman: add ROM image signing for Bay Trail SoC
  2017-11-17  1:16 [U-Boot] [PATCH v3 5/6] binman: add ROM image signing for Bay Trail SoC Anatolij Gustschin
  2017-11-20 15:40 ` Simon Glass
@ 2017-12-04  6:30 ` Bin Meng
  2017-12-04 13:33   ` Anatolij Gustschin
  1 sibling, 1 reply; 5+ messages in thread
From: Bin Meng @ 2017-12-04  6:30 UTC (permalink / raw)
  To: u-boot

Hi Anatolij,

On Fri, Nov 17, 2017 at 9:16 AM, Anatolij Gustschin <agust@denx.de> wrote:
> Generate u-boot-verified.rom image containing Secure Boot Manifest
> when secure boot option is enabled.
>
> Signed-off-by: Anatolij Gustschin <agust@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 <mv@denx.de>
> +# Adapted for binman integration: Anatolij Gustschin <agust@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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [U-Boot] [PATCH v3 5/6] binman: add ROM image signing for Bay Trail SoC
  2017-12-04  6:30 ` Bin Meng
@ 2017-12-04 13:33   ` Anatolij Gustschin
  0 siblings, 0 replies; 5+ messages in thread
From: Anatolij Gustschin @ 2017-12-04 13:33 UTC (permalink / raw)
  To: u-boot

Hi Bin,

On Mon, 4 Dec 2017 14:30:03 +0800
Bin Meng bmeng.cn at gmail.com wrote:
...
> > 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.

OK, will fix it.
 
> > +               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.

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

...
> > +# 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.

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

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

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2017-12-04 13:33 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-11-17  1:16 [U-Boot] [PATCH v3 5/6] binman: add ROM image signing for Bay Trail SoC Anatolij Gustschin
2017-11-20 15:40 ` Simon Glass
2017-11-28 15:27   ` Anatolij Gustschin
2017-12-04  6:30 ` Bin Meng
2017-12-04 13:33   ` Anatolij Gustschin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.