All of lore.kernel.org
 help / color / mirror / Atom feed
From: Anatolij Gustschin <agust@denx.de>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH 4/5] tools: add secure_boot_helper.py
Date: Thu, 11 May 2017 17:14:55 +0200	[thread overview]
Message-ID: <1494515696-2087-5-git-send-email-agust@denx.de> (raw)
In-Reply-To: <1494515696-2087-1-git-send-email-agust@denx.de>

From: Markus Valentin <mv@denx.de>

This script should be used for simple creation of secure bootable
images for baytrail platforms

Signed-off-by: Markus Valentin <mv@denx.de>
---
 tools/secure_boot_helper.py | 313 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 313 insertions(+)
 create mode 100644 tools/secure_boot_helper.py

diff --git a/tools/secure_boot_helper.py b/tools/secure_boot_helper.py
new file mode 100644
index 0000000..884786e
--- /dev/null
+++ b/tools/secure_boot_helper.py
@@ -0,0 +1,313 @@
+#!/usr/bin/env python3
+"""
+ Copyright (C) 2017 Markus Valentin <mv@denx.de>
+
+ SPDX-License-Identifier:     GPL-2.0+
+ """
+
+
+import argparse
+import binascii
+
+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'
+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_STAGE_2_SIZE = 0x1f400
+IBB_SIZE = 0x1fc00
+MANIFEST_SIZE = 0x400
+OEM_BLOCK_MAX_SIZE = 0x190
+U_BOOT_ROM_SIZE = 0x800000
+ROMFILE_SYS_TEXT_BASE = 0x00700000
+U_BOOT_TO_SIGN_OFFSET = 0x2CA0
+
+
+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 a oemdata data block which gets constructed
+# as stated in section 3.2 of the document "Secure-Boot for Intel
+# Bay Trail based platfomrs with U-Boot"
+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-verified.rom 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("u-boot-verified.rom", 'wb').write(data)
+
+
+# when calling this function it constructs a complete secure-boot manifest
+# which is just missing oem-publickey and the manifest-signature (see
+# section 3.1)
+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 a subpart of a binary from byte 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 given change endianness (sometimes needed
+# because the txe engine wants a other byteorder)
+def sha256_to_file(binary_dir, file_to_hash, change_endianess=False):
+    # we collect the hashes in a list(in the correct order) to be able
+    # to put them later to the oem 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_endianess:
+            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 hashfile name 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 must be dividable by 4
+def pad_file_with_zeros(file_handle, byte_count):
+    if byte_count % 4 != 0:
+        print("Given  byte count is not 4-divideable exiting")
+        exit()
+    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 publickey
+# 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)
+
+    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:
+        f.write(new_line_sb_enabled)
+        f.write(new_line_hash)
+
+
+# for the txe engine one needs to change the endianness
+def reverse_endianess(file_to_reverse):
+    data = open(file_to_reverse, 'rb').read()
+    open(file_to_reverse, 'wb').write(data[::-1])
+
+
+# sign the given file with the given private key and
+# write it to the signature_file using openssl
+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)
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="This script assembles a " +
+                                     "verified boot enabled u-boot using " +
+                                     "openssl")
+    parser.add_argument("-c", "--fpf-config", default="./fpf_config.txt",
+                        help="Path to the fpf-config file defaults to" +
+                        " ./fpf_config.txt",
+                        required=True)
+    parser.add_argument("-I", "--board-dir", help="set directory to get the " +
+                        " fsp and other board related files from.")
+    parser.add_argument("-b", "--binary_dir", default=".",
+                        help="directory to fetch binaries from and " +
+                        "save created binaries to.")
+    parser.add_argument("-k", "--key-dir", default="./mykeys",
+                        help="directory to fetch keys from")
+    parser.add_argument('--lock-fuses', action='store_true',
+                        help="Set this flag to configure fuses to " +
+                        "lock the values")
+
+    args = parser.parse_args()
+
+    # assemble correct paths
+    fsp = pjoin(args.board_dir, FSP_FILE_NAME)
+    fsp_stage2 = pjoin(args.board_dir, FSP_STAGE2_FILE_NAME)
+
+    fit_public_key = pjoin(args.key_dir, FIT_PUB_KEY_FILE_NAME)
+    fit_public_key_modulus = pjoin(args.key_dir, FIT_PUB_KEY_FILE_NAME+".mod")
+
+    u_boot_rom = pjoin(args.binary_dir, U_BOOT_ROM_FILE_NAME)
+    u_boot_to_sign = pjoin(args.binary_dir, U_BOOT_TO_SIGN_FILE_NAME)
+
+    ibb = pjoin(args.binary_dir, IBB_FILE_NAME)
+
+    signed_ibb = pjoin(args.binary_dir, "signed_"+IBB_FILE_NAME)
+    unsigned_manifest = pjoin(args.binary_dir, UNSIGNED_MANIFEST_FILE_NAME)
+    signed_manifest = pjoin(args.binary_dir, SIGNED_MANIFEST_FILE_NAME)
+
+    manifest_signature = pjoin(args.binary_dir, splitext(
+                               basename(UNSIGNED_MANIFEST_FILE_NAME))[0].
+                               __add__(".signature"))
+
+    oem_file = pjoin(args.binary_dir, OEM_FILE_NAME)
+    oem_private_key = pjoin(args.key_dir, OEM_PRIV_KEY_FILE_NAME)
+    oem_public_key = pjoin(args.key_dir, OEM_PUB_KEY_FILE_NAME)
+    oem_pubkey_binary = pjoin(args.key_dir, OEM_PUBKEY_BIN_FILE_NAME)
+    oem_pubkey_and_sig = pjoin(args.key_dir,
+                               OEM_PUBKEY_AND_SIG_FILE_NAME)
+
+    # check for needed files to be available
+    for f in [fsp, u_boot_rom, fit_public_key, oem_private_key]:
+        if not isfile(f):
+            print("%s not found ... exiting" % (f))
+            exit()
+
+    # get everything from rom-file execept IBB+Manfifest(128k) and write it to
+    # seperated file and calculate hash
+    extract_binary_part(u_boot_rom, u_boot_to_sign,
+                        (ROMFILE_SYS_TEXT_BASE+U_BOOT_TO_SIGN_OFFSET),
+                        (U_BOOT_ROM_SIZE-(IBB_SIZE+MANIFEST_SIZE)))
+    sha256_to_file(args.binary_dir, u_boot_to_sign, True)
+
+    # extract stage2 of the fsp and calculate a hash about
+    # the file
+    extract_binary_part(fsp, fsp_stage2, 0, FSP_STAGE_2_SIZE)
+    sha256_to_file(args.binary_dir, 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(args.binary_dir, fit_public_key_modulus, True)
+
+    # assemble oemdata
+    print("Assembling oem data from %d hashes: \n %s" %
+          (oem_data_hash_files.__len__(), oem_data_hash_files))
+    assemble_oem_data(oem_file)
+
+    print("Extracting last 127K:\n 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)
+
+    print("Creating Secure Boot Manifest")
+    create_unsigned_secure_boot_manifest(unsigned_manifest,
+                                         oem_file,
+                                         ibb)
+
+    print("Signing manifest with openssl and private key %s"
+          % (oem_private_key))
+    sign_file(unsigned_manifest, oem_private_key, manifest_signature)
+
+    print("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_endianess(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)
+
+    hash_function = sha256()
+    hash_function.update(bytearray(open(oem_pubkey_binary, 'rb').read()))
+    replace_oem_pubkey_hash(hash_function.digest()[::-1], args.fpf_config,
+                            args.lock_fuses)
+
+    print("Append manifest with signature to ibb")
+    append_binary_files(signed_manifest, ibb, signed_ibb)
+
+    print("Assemble u-boot-verified.rom from:\n %s and %s"
+          % (u_boot_rom, signed_manifest))
+    assemble_secure_boot_image(u_boot_rom, signed_ibb)
-- 
2.7.4

  parent reply	other threads:[~2017-05-11 15:14 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-05-11 15:14 [U-Boot] [PATCH 0/5] Introduce secure boot for Baytrail Anatolij Gustschin
2017-05-11 15:14 ` [U-Boot] [PATCH 1/5] x86: congatec: add secureboot enabled defconfig for conga-qeval20-qa3-e3845 Anatolij Gustschin
2017-05-15  3:03   ` Simon Glass
2017-05-11 15:14 ` [U-Boot] [PATCH 2/5] x86: baytrail: Add fsp-header verification for secure boot fsp Anatolij Gustschin
2017-05-15  3:03   ` Simon Glass
2017-05-15  7:20     ` Anatolij Gustschin
2017-05-11 15:14 ` [U-Boot] [PATCH 3/5] x86: baytrail: secureboot: Add functions for verification of u-boot Anatolij Gustschin
2017-05-12  8:25   ` Lothar Waßmann
2017-05-12  8:56     ` Anatolij Gustschin
2017-05-15  3:03   ` Simon Glass
2017-05-15  7:29     ` Anatolij Gustschin
2017-05-11 15:14 ` Anatolij Gustschin [this message]
2017-05-15  3:03   ` [U-Boot] [PATCH 4/5] tools: add secure_boot_helper.py Simon Glass
2017-05-11 15:14 ` [U-Boot] [PATCH 5/5] doc: x86: Add section about secure boot on Baytrail Anatolij Gustschin
2017-05-15  3:03   ` Simon Glass

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=1494515696-2087-5-git-send-email-agust@denx.de \
    --to=agust@denx.de \
    --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.