All of lore.kernel.org
 help / color / mirror / Atom feed
From: Bin Meng <bmeng.cn@gmail.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v3 5/6] binman: add ROM image signing for Bay Trail SoC
Date: Mon, 4 Dec 2017 14:30:03 +0800	[thread overview]
Message-ID: <CAEUhbmUJ7CNqPdZqxgxjMrBMYj__w6qPy9G-EnH_LQg7B0Scdg@mail.gmail.com> (raw)
In-Reply-To: <20171117011605.5916-1-agust@denx.de>

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

  parent reply	other threads:[~2017-12-04  6:30 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
2017-12-04 13:33   ` Anatolij Gustschin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAEUhbmUJ7CNqPdZqxgxjMrBMYj__w6qPy9G-EnH_LQg7B0Scdg@mail.gmail.com \
    --to=bmeng.cn@gmail.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.