diff --git a/CommonUtility.py b/CommonUtility.py
new file mode 100644
index 0000000..d1cce6a
--- /dev/null
+++ b/CommonUtility.py
@@ -0,0 +1,456 @@
+#!/usr/bin/env python
+## @ CommonUtility.py
+# Common utility script
+#
+# Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+##
+# Import Modules
+#
+import os
+import sys
+import re
+import shutil
+import subprocess
+import struct
+import hashlib
+import string
+from ctypes import *
+from functools import reduce
+from importlib.machinery import SourceFileLoader
+from SingleSign import *
+
+
+# Key types defined should match with cryptolib.h
+PUB_KEY_TYPE = {
+ # key_type : key_val
+ "RSA" : 1,
+ "ECC" : 2,
+ "DSA" : 3,
+ }
+
+# Signing type schemes defined should match with cryptolib.h
+SIGN_TYPE_SCHEME = {
+ # sign_type : key_val
+ "RSA_PKCS1" : 1,
+ "RSA_PSS" : 2,
+ "ECC" : 3,
+ "DSA" : 4,
+ }
+
+# Hash values defined should match with cryptolib.h
+HASH_TYPE_VALUE = {
+ # Hash_string : Hash_Value
+ "SHA2_256" : 1,
+ "SHA2_384" : 2,
+ "SHA2_512" : 3,
+ "SM3_256" : 4,
+ }
+
+# Hash values defined should match with cryptolib.h
+HASH_VAL_STRING = dict(map(reversed, HASH_TYPE_VALUE.items()))
+
+AUTH_TYPE_HASH_VALUE = {
+ # Auth_type : Hash_type
+ "SHA2_256" : 1,
+ "SHA2_384" : 2,
+ "SHA2_512" : 3,
+ "SM3_256" : 4,
+ "RSA2048SHA256" : 1,
+ "RSA3072SHA384" : 2,
+ }
+
+HASH_DIGEST_SIZE = {
+ # Hash_string : Hash_Size
+ "SHA2_256" : 32,
+ "SHA2_384" : 48,
+ "SHA2_512" : 64,
+ "SM3_256" : 32,
+ }
+
+class PUB_KEY_HDR (Structure):
+ _pack_ = 1
+ _fields_ = [
+ ('Identifier', ARRAY(c_char, 4)), #signature ('P', 'U', 'B', 'K')
+ ('KeySize', c_uint16), #Length of Public Key
+ ('KeyType', c_uint8), #RSA or ECC
+ ('Reserved', ARRAY(c_uint8, 1)),
+ ('KeyData', ARRAY(c_uint8, 0)), #Pubic key data with KeySize bytes for RSA_KEY() format
+ ]
+
+ def __init__(self):
+ self.Identifier = b'PUBK'
+
+class SIGNATURE_HDR (Structure):
+ _pack_ = 1
+ _fields_ = [
+ ('Identifier', ARRAY(c_char, 4)), #signature Identifier('S', 'I', 'G', 'N')
+ ('SigSize', c_uint16), #Length of signature 2K and 3K in bytes
+ ('SigType', c_uint8), #PKCSv1.5 or RSA-PSS or ECC
+ ('HashAlg', c_uint8), #Hash Alg for signingh SHA256, 384
+ ('Signature', ARRAY(c_uint8, 0)), #Signature length defined by SigSize bytes
+ ]
+
+ def __init__(self):
+ self.Identifier = b'SIGN'
+
+class LZ_HEADER(Structure):
+ _pack_ = 1
+ _fields_ = [
+ ('signature', ARRAY(c_char, 4)),
+ ('compressed_len', c_uint32),
+ ('length', c_uint32),
+ ('version', c_uint16),
+ ('svn', c_uint8),
+ ('attribute', c_uint8)
+ ]
+ _compress_alg = {
+ b'LZDM' : 'Dummy',
+ b'LZ4 ' : 'Lz4',
+ b'LZMA' : 'Lzma',
+ }
+
+def print_bytes (data, indent=0, offset=0, show_ascii = False):
+ bytes_per_line = 16
+ printable = ' ' + string.ascii_letters + string.digits + string.punctuation
+ str_fmt = '{:s}{:04x}: {:%ds} {:s}' % (bytes_per_line * 3)
+ bytes_per_line
+ data_array = bytearray(data)
+ for idx in range(0, len(data_array), bytes_per_line):
+ hex_str = ' '.join('%02X' % val for val in data_array[idx:idx + bytes_per_line])
+ asc_str = ''.join('%c' % (val if (chr(val) in printable) else '.')
+ for val in data_array[idx:idx + bytes_per_line])
+ print (str_fmt.format(indent * ' ', offset + idx, hex_str, ' ' + asc_str if show_ascii else ''))
+
+def get_bits_from_bytes (bytes, start, length):
+ if length == 0:
+ return 0
+ byte_start = (start) // 8
+ byte_end = (start + length - 1) // 8
+ bit_start = start & 7
+ mask = (1 << length) - 1
+ val = bytes_to_value (bytes[byte_start:byte_end + 1])
+ val = (val >> bit_start) & mask
+ return val
+
+def set_bits_to_bytes (bytes, start, length, bvalue):
+ if length == 0:
+ return
+ byte_start = (start) // 8
+ byte_end = (start + length - 1) // 8
+ bit_start = start & 7
+ mask = (1 << length) - 1
+ val = bytes_to_value (bytes[byte_start:byte_end + 1])
+ val &= ~(mask << bit_start)
+ val |= ((bvalue & mask) << bit_start)
+ bytes[byte_start:byte_end+1] = value_to_bytearray (val, byte_end + 1 - byte_start)
+
+def value_to_bytes (value, length):
+ return value.to_bytes(length, 'little')
+
+def bytes_to_value (bytes):
+ return int.from_bytes (bytes, 'little')
+
+def value_to_bytearray (value, length):
+ return bytearray(value_to_bytes(value, length))
+
+def value_to_bytearray (value, length):
+ return bytearray(value_to_bytes(value, length))
+
+def get_aligned_value (value, alignment = 4):
+ if alignment != (1 << (alignment.bit_length() - 1)):
+ raise Exception ('Alignment (0x%x) should to be power of 2 !' % alignment)
+ value = (value + (alignment - 1)) & ~(alignment - 1)
+ return value
+
+def get_padding_length (data_len, alignment = 4):
+ new_data_len = get_aligned_value (data_len, alignment)
+ return new_data_len - data_len
+
+def get_file_data (file, mode = 'rb'):
+ return open(file, mode).read()
+
+def gen_file_from_object (file, object):
+ open (file, 'wb').write(object)
+
+def gen_file_with_size (file, size):
+ open (file, 'wb').write(b'\xFF' * size);
+
+def check_files_exist (base_name_list, dir = '', ext = ''):
+ for each in base_name_list:
+ if not os.path.exists (os.path.join (dir, each + ext)):
+ return False
+ return True
+
+def load_source (name, filepath):
+ mod = SourceFileLoader (name, filepath).load_module()
+ return mod
+
+def get_openssl_path ():
+ if os.name == 'nt':
+ if 'OPENSSL_PATH' not in os.environ:
+ openssl_dir = "C:\\Openssl\\bin\\"
+ if os.path.exists (openssl_dir):
+ os.environ['OPENSSL_PATH'] = openssl_dir
+ else:
+ os.environ['OPENSSL_PATH'] = "C:\\Openssl\\"
+ if 'OPENSSL_CONF' not in os.environ:
+ openssl_cfg = "C:\\Openssl\\openssl.cfg"
+ if os.path.exists(openssl_cfg):
+ os.environ['OPENSSL_CONF'] = openssl_cfg
+ openssl = os.path.join(os.environ.get ('OPENSSL_PATH', ''), 'openssl.exe')
+ else:
+ # Get openssl path for Linux cases
+ openssl = shutil.which('openssl')
+
+ return openssl
+
+def run_process (arg_list, print_cmd = False, capture_out = False):
+ sys.stdout.flush()
+ if os.name == 'nt' and os.path.splitext(arg_list[0])[1] == '' and \
+ os.path.exists (arg_list[0] + '.exe'):
+ arg_list[0] += '.exe'
+ if print_cmd:
+ print (' '.join(arg_list))
+
+ exc = None
+ result = 0
+ output = ''
+ try:
+ if capture_out:
+ output = subprocess.check_output(arg_list).decode()
+ else:
+ result = subprocess.call (arg_list)
+ except Exception as ex:
+ result = 1
+ exc = ex
+
+ if result:
+ if not print_cmd:
+ print ('Error in running process:\n %s' % ' '.join(arg_list))
+ if exc is None:
+ sys.exit(1)
+ else:
+ raise exc
+
+ return output
+
+# Adjust hash type algorithm based on Public key file
+def adjust_hash_type (pub_key_file):
+ key_type = get_key_type (pub_key_file)
+ if key_type == 'RSA2048':
+ hash_type = 'SHA2_256'
+ elif key_type == 'RSA3072':
+ hash_type = 'SHA2_384'
+ else:
+ hash_type = None
+
+ return hash_type
+
+def rsa_sign_file (priv_key, pub_key, hash_type, sign_scheme, in_file, out_file, inc_dat = False, inc_key = False):
+
+ bins = bytearray()
+ if inc_dat:
+ bins.extend(get_file_data(in_file))
+
+ single_sign_file(priv_key, hash_type, sign_scheme, in_file, out_file)
+
+ out_data = get_file_data(out_file)
+
+ sign = SIGNATURE_HDR()
+ sign.SigSize = len(out_data)
+ sign.SigType = SIGN_TYPE_SCHEME[sign_scheme]
+ sign.HashAlg = HASH_TYPE_VALUE[hash_type]
+
+ bins.extend(bytearray(sign) + out_data)
+ if inc_key:
+ key = gen_pub_key (priv_key, pub_key)
+ bins.extend(key)
+
+ if len(bins) != len(out_data):
+ gen_file_from_object (out_file, bins)
+
+def get_key_type (in_key):
+
+ # Check in_key is file or key Id
+ if not os.path.exists(in_key):
+ key = bytearray(gen_pub_key (in_key))
+ else:
+ # Check for public key in binary format.
+ key = bytearray(get_file_data(in_key))
+
+ pub_key_hdr = PUB_KEY_HDR.from_buffer(key)
+ if pub_key_hdr.Identifier != b'PUBK':
+ pub_key = gen_pub_key (in_key)
+ pub_key_hdr = PUB_KEY_HDR.from_buffer(pub_key)
+
+ key_type = next((key for key, value in PUB_KEY_TYPE.items() if value == pub_key_hdr.KeyType))
+ return '%s%d' % (key_type, (pub_key_hdr.KeySize - 4) * 8)
+
+
+def get_auth_hash_type (key_type, sign_scheme):
+ if key_type == "RSA2048" and sign_scheme == "RSA_PKCS1":
+ hash_type = 'SHA2_256'
+ auth_type = 'RSA2048_PKCS1_SHA2_256'
+ elif key_type == "RSA3072" and sign_scheme == "RSA_PKCS1":
+ hash_type = 'SHA2_384'
+ auth_type = 'RSA3072_PKCS1_SHA2_384'
+ elif key_type == "RSA2048" and sign_scheme == "RSA_PSS":
+ hash_type = 'SHA2_256'
+ auth_type = 'RSA2048_PSS_SHA2_256'
+ elif key_type == "RSA3072" and sign_scheme == "RSA_PSS":
+ hash_type = 'SHA2_384'
+ auth_type = 'RSA3072_PSS_SHA2_384'
+ else:
+ hash_type = ''
+ auth_type = ''
+ return auth_type, hash_type
+
+def gen_pub_key (in_key, pub_key = None):
+
+ keydata = single_sign_gen_pub_key (in_key, pub_key)
+
+ publickey = PUB_KEY_HDR()
+ publickey.KeySize = len(keydata)
+ publickey.KeyType = PUB_KEY_TYPE['RSA']
+
+ key = bytearray(publickey) + keydata
+
+ if pub_key:
+ gen_file_from_object (pub_key, key)
+
+ return key
+
+def decompress (in_file, out_file, tool_dir = ''):
+ if not os.path.isfile(in_file):
+ raise Exception ("Invalid input file '%s' !" % in_file)
+
+ # Remove the Lz Header
+ fi = open(in_file,'rb')
+ di = bytearray(fi.read())
+ fi.close()
+
+ lz_hdr = LZ_HEADER.from_buffer (di)
+ offset = sizeof (lz_hdr)
+ if lz_hdr.signature == b"LZDM" or lz_hdr.compressed_len == 0:
+ fo = open(out_file,'wb')
+ fo.write(di[offset:offset + lz_hdr.compressed_len])
+ fo.close()
+ return
+
+ temp = os.path.splitext(out_file)[0] + '.tmp'
+ if lz_hdr.signature == b"LZMA":
+ alg = "Lzma"
+ elif lz_hdr.signature == b"LZ4 ":
+ alg = "Lz4"
+ else:
+ raise Exception ("Unsupported compression '%s' !" % lz_hdr.signature)
+
+ fo = open(temp, 'wb')
+ fo.write(di[offset:offset + lz_hdr.compressed_len])
+ fo.close()
+
+ compress_tool = "%sCompress" % alg
+ if alg == "Lz4":
+ try:
+ cmdline = [
+ os.path.join (tool_dir, compress_tool),
+ "-d",
+ "-o", out_file,
+ temp]
+ run_process (cmdline, False, True)
+ except:
+ print("Could not find/use CompressLz4 tool, trying with python lz4...")
+ try:
+ import lz4.block
+ if lz4.VERSION != '3.1.1':
+ print("Recommended lz4 module version is '3.1.1', '%s' is currently installed." % lz4.VERSION)
+ except ImportError:
+ print("Could not import lz4, use 'python -m pip install lz4==3.1.1' to install it.")
+ exit(1)
+ decompress_data = lz4.block.decompress(get_file_data(temp))
+ with open(out_file, "wb") as lz4bin:
+ lz4bin.write(decompress_data)
+ else:
+ cmdline = [
+ os.path.join (tool_dir, compress_tool),
+ "-d",
+ "-o", out_file,
+ temp]
+ run_process (cmdline, False, True)
+ os.remove(temp)
+
+def compress (in_file, alg, svn=0, out_path = '', tool_dir = ''):
+ if not os.path.isfile(in_file):
+ raise Exception ("Invalid input file '%s' !" % in_file)
+
+ basename, ext = os.path.splitext(os.path.basename (in_file))
+ if out_path:
+ if os.path.isdir (out_path):
+ out_file = os.path.join(out_path, basename + '.lz')
+ else:
+ out_file = os.path.join(out_path)
+ else:
+ out_file = os.path.splitext(in_file)[0] + '.lz'
+
+ if alg == "Lzma":
+ sig = "LZMA"
+ elif alg == "Tiano":
+ sig = "LZUF"
+ elif alg == "Lz4":
+ sig = "LZ4 "
+ elif alg == "Dummy":
+ sig = "LZDM"
+ else:
+ raise Exception ("Unsupported compression '%s' !" % alg)
+
+ in_len = os.path.getsize(in_file)
+ if in_len > 0:
+ compress_tool = "%sCompress" % alg
+ if sig == "LZDM":
+ shutil.copy(in_file, out_file)
+ compress_data = get_file_data(out_file)
+ elif sig == "LZ4 ":
+ try:
+ cmdline = [
+ os.path.join (tool_dir, compress_tool),
+ "-e",
+ "-o", out_file,
+ in_file]
+ run_process (cmdline, False, True)
+ compress_data = get_file_data(out_file)
+ except:
+ print("Could not find/use CompressLz4 tool, trying with python lz4...")
+ try:
+ import lz4.block
+ if lz4.VERSION != '3.1.1':
+ print("Recommended lz4 module version is '3.1.1', '%s' is currently installed." % lz4.VERSION)
+ except ImportError:
+ print("Could not import lz4, use 'python -m pip install lz4==3.1.1' to install it.")
+ exit(1)
+ compress_data = lz4.block.compress(get_file_data(in_file), mode='high_compression')
+ elif sig == "LZMA":
+ cmdline = [
+ os.path.join (tool_dir, compress_tool),
+ "-e",
+ "-o", out_file,
+ in_file]
+ run_process (cmdline, False, True)
+ compress_data = get_file_data(out_file)
+ else:
+ compress_data = bytearray()
+
+ lz_hdr = LZ_HEADER ()
+ lz_hdr.signature = sig.encode()
+ lz_hdr.svn = svn
+ lz_hdr.compressed_len = len(compress_data)
+ lz_hdr.length = os.path.getsize(in_file)
+ data = bytearray ()
+ data.extend (lz_hdr)
+ data.extend (compress_data)
+ gen_file_from_object (out_file, data)
+
+ return out_file
diff --git a/GenContainer.py b/GenContainer.py
new file mode 100755
index 0000000..98c9f1a
--- /dev/null
+++ b/GenContainer.py
@@ -0,0 +1,884 @@
+#!/usr/bin/env python
+## @ GenContainer.py
+# Tools to operate on a container image
+#
+# Copyright (c) 2019 - 2022, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+import sys
+import argparse
+import re
+sys.dont_write_bytecode = True
+from ctypes import *
+from CommonUtility import *
+
+
+
+class COMPONENT_ENTRY (Structure):
+ _pack_ = 1
+ _fields_ = [
+ ('name', ARRAY(c_char, 4)), # SBL pod entry name
+ ('offset', c_uint32), # Component offset in byte from the payload (data) ('size', c_uint32), # Region/Component size in byte
+ ('size', c_uint32), # Region/Component size in byte
+ ('attribute', c_uint8), # Attribute: BIT7 Reserved component entry
+ ('alignment', c_uint8), # This image need to be loaded to memory in (1 << Alignment) address
+ ('auth_type', c_uint8), # Refer AUTH_TYPE_VALUE: 0 - "NONE"; 1- "SHA2_256"; 2- "SHA2_384"; 3- "RSA2048_PKCS1_SHA2_256"; 4 - RSA3072_PKCS1_SHA2_384;
+ # 5 - RSA2048_PSS_SHA2_256; 6 - RSA3072_PSS_SHA2_384
+ ('hash_size', c_uint8) # Hash data size, it could be image hash or public key hash
+ ]
+
+ _attr = {
+ 'RESERVED' : 0x80
+ }
+
+ def __new__(cls, buf = None):
+ if buf is None:
+ return Structure.__new__(cls)
+ else:
+ return cls.from_buffer_copy(buf)
+
+ def __init__(self, buf = None):
+ if buf is None:
+ self.hash_data = bytearray()
+ else:
+ off = sizeof(COMPONENT_ENTRY)
+ self.hash_data = bytearray(buf[off : off + self.hash_size])
+ self.data = bytearray()
+ self.auth_data = bytearray()
+
+
+class CONTAINER_HDR (Structure):
+ _pack_ = 1
+ _fields_ = [
+ ('signature', ARRAY(c_char, 4)), # Identifies structure
+ ('version', c_uint8), # Header version
+ ('svn', c_uint8), # Security version number
+ ('data_offset', c_uint16), # Offset of payload (data) from header in byte
+ ('data_size', c_uint32), # Size of payload (data) in byte
+ ('auth_type', c_uint8), # Refer AUTH_TYPE_VALUE: 0 - "NONE"; 1- "SHA2_256"; 2- "SHA2_384"; 3- "RSA2048_PKCS1_SHA2_256"; 4 - RSA3072_PKCS1_SHA2_384;
+ # 5 - RSA2048_PSS_SHA2_256; 6 - RSA3072_PSS_SHA2_384
+ ('image_type', c_uint8), # 0: Normal
+ ('flags', c_uint8), # BIT0: monolithic signing
+ ('entry_count', c_uint8), # Number of entry in the header
+ ]
+
+ _flags = {
+ 'MONO_SIGNING' : 0x01
+ }
+
+ _image_type = {
+ 'NORMAL' : 0x00, # Used for boot images in FV, regular ELF, PE32, etc. formats
+ 'CLASSIC' : 0xF3, # Used for booting Linux with bzImage, cmdline, initrd, etc.
+ 'MULTIBOOT' : 0xF4, # Multiboot compliant ELF images
+ }
+
+ def __new__(cls, buf = None):
+ if buf is None:
+ return Structure.__new__(cls)
+ else:
+ return cls.from_buffer_copy(buf)
+
+ def __init__(self, buf = None):
+ self.priv_key = ''
+ self.alignment = 0x1000
+ self.auth_data = bytearray()
+ self.comp_entry = []
+
+ if buf is not None:
+ # construct CONTAINER_HDR from existing buffer
+ offset = sizeof(self)
+ alignment = None
+ for i in range(self.entry_count):
+ component = COMPONENT_ENTRY(buf[offset:])
+ if alignment is None:
+ alignment = 1 << component.alignment
+ offset += (sizeof(component) + component.hash_size)
+ comp_offset = component.offset + self.data_offset
+ lz_hdr = LZ_HEADER.from_buffer(bytearray(buf[comp_offset:comp_offset + sizeof(LZ_HEADER)]))
+ auth_offset = comp_offset + lz_hdr.compressed_len + sizeof(lz_hdr)
+ component.data = bytearray (buf[comp_offset:auth_offset])
+ auth_offset = get_aligned_value (auth_offset, 4)
+ auth_size = CONTAINER.get_auth_size (component.auth_type, True)
+ component.auth_data = bytearray (buf[auth_offset:auth_offset + auth_size])
+ self.comp_entry.append (component)
+ auth_size = CONTAINER.get_auth_size (self.auth_type, True)
+ auth_offset = get_aligned_value (offset, 4)
+ self.auth_data = bytearray (buf[auth_offset:auth_offset + auth_size])
+ if alignment is not None:
+ self.alignment = alignment
+
+class CONTAINER ():
+ _struct_display_indent = 18
+ _auth_type_value = {
+ "NONE" : 0,
+ "SHA2_256" : 1,
+ "SHA2_384" : 2,
+ "RSA2048_PKCS1_SHA2_256" : 3,
+ "RSA3072_PKCS1_SHA2_384" : 4,
+ "RSA2048_PSS_SHA2_256" : 5,
+ "RSA3072_PSS_SHA2_384" : 6,
+ }
+
+ _auth_to_hashalg_str = {
+ "NONE" : "NONE",
+ "SHA2_256" : "SHA2_256",
+ "SHA2_384" : "SHA2_384",
+ "RSA2048_PKCS1_SHA2_256" : "SHA2_256",
+ "RSA3072_PKCS1_SHA2_384" : "SHA2_384",
+ "RSA2048_PSS_SHA2_256" : "SHA2_256",
+ "RSA3072_PSS_SHA2_384" : "SHA2_384",
+ }
+
+
+ _auth_to_signscheme_str = {
+ "NONE" : "",
+ "SHA2_256" : "",
+ "SHA2_384" : "",
+ "RSA2048_PKCS1_SHA2_256" : "RSA_PKCS1",
+ "RSA3072_PKCS1_SHA2_384" : "RSA_PKCS1",
+ "RSA2048_PSS_SHA2_256" : "RSA_PSS",
+ "RSA3072_PSS_SHA2_384" : "RSA_PSS",
+ }
+
+ def __init__(self, buf = None):
+ self.out_dir = '.'
+ self.input_dir = '.'
+ self.key_dir = '.'
+ self.tool_dir = '.'
+ if buf is None:
+ self.header = CONTAINER_HDR ()
+ else:
+ self.header = CONTAINER_HDR (buf)
+ # Check if image type is valid
+ image_type_str = CONTAINER.get_image_type_str(self.header.image_type)
+
+ def init_header (self, signature, alignment, image_type = 'NORMAL'):
+ self.header.signature = signature
+ self.header.version = 1
+ self.header.alignment = alignment
+ self.header.flags = 0
+ if image_type not in CONTAINER_HDR._image_type.keys():
+ raise Exception ("Invalid image type '%s' specified !" % image_type)
+ self.header.image_type = CONTAINER_HDR._image_type[image_type]
+
+ @staticmethod
+ def get_image_type_str (image_type_val):
+ try:
+ image_type_str = next((key for key, value in CONTAINER_HDR._image_type.items() if value == image_type_val))
+ except StopIteration:
+ raise Exception ("Unknown image type value 0x%x in container header !" % image_type_val)
+ return image_type_str
+
+ @staticmethod
+ def get_auth_type_val (auth_type_str):
+ return CONTAINER._auth_type_value[auth_type_str]
+
+ @staticmethod
+ def get_auth_type_str (auth_type_val):
+ try:
+ auth_type_str = next(k for k, v in CONTAINER._auth_type_value.items() if v == auth_type_val)
+ except StopIteration:
+ raise Exception ("Unknown auth type value 0x%x !" % auth_type_val)
+ return auth_type_str
+
+ @staticmethod
+ def get_auth_size (auth_type, signed = False):
+ # calculate the length for the required authentication info
+ if type(auth_type) is type(1):
+ auth_type_str = CONTAINER.get_auth_type_str (auth_type)
+ else:
+ auth_type_str = auth_type
+ if auth_type_str == 'NONE':
+ auth_len = 0
+ elif auth_type_str.startswith ('RSA'):
+ auth_len = int(auth_type_str[3:7]) >> 3
+ if signed:
+ auth_len = auth_len * 2 + sizeof(PUB_KEY_HDR) + sizeof(SIGNATURE_HDR) + 4
+ elif auth_type_str.startswith ('SHA2_'):
+ auth_len = int(auth_type_str[5:]) >> 3
+ if signed:
+ auth_len = 0
+ else:
+ raise Exception ("Unsupported authentication type '%s' !" % auth_type)
+ return auth_len
+
+ @staticmethod
+ def decode_field (name, val):
+ # decode auth type into readable string
+ extra = ''
+ if name in ['CONTAINER_HDR.auth_type', 'COMPONENT_ENTRY.auth_type']:
+ auth_type = next(k for k, v in CONTAINER._auth_type_value.items() if v == val)
+ extra = '%d : %s' % (val, auth_type)
+ return extra
+
+ @staticmethod
+ def hex_str (data, name = ''):
+ # convert bytearray to hex string
+ dlen = len(data)
+ if dlen == 0:
+ hex_str = ''
+ else:
+ if dlen <= 16:
+ hex_str = ' '.join(['%02x' % x for x in data])
+ else:
+ hex_str = ' '.join(['%02x' % x for x in data[:8]]) + \
+ ' .... ' + ' '.join(['%02x' % x for x in data[-8:]])
+ hex_str = ' %s %s [%s]' % (name, ' ' * (CONTAINER._struct_display_indent - len(name) + 1), hex_str)
+ if len(data) > 0:
+ hex_str = hex_str + ' (len=0x%x)' % len(data)
+ return hex_str
+
+ @staticmethod
+ def output_struct (obj, indent = 0, plen = 0):
+ # print out a struct info
+ body = '' if indent else (' ' * indent + '<%s>:\n') % obj.__class__.__name__
+ if plen == 0:
+ plen = sizeof(obj)
+ pstr = (' ' * (indent + 1) + '{0:<%d} = {1}\n') % CONTAINER._struct_display_indent
+ for field in obj._fields_:
+ key = field[0]
+ val = getattr(obj, key)
+ rep = ''
+ if type(val) is str:
+ rep = "0x%X ('%s')" % (bytes_to_value(bytearray(val)), val)
+ elif type(val) in [int]:
+ rep = CONTAINER.decode_field ('%s.%s' % (obj.__class__.__name__, key), val)
+ if not rep:
+ rep = '0x%X' % (val)
+ else:
+ rep = str(val)
+ plen -= sizeof(field[1])
+ body += pstr.format(key, rep)
+ if plen <= 0:
+ break
+ return body.strip()
+
+ @staticmethod
+ def get_pub_key_hash (key, hash_type):
+ # calculate publish key hash
+ dh = bytearray (key)[sizeof(PUB_KEY_HDR):]
+ if hash_type == 'SHA2_256':
+ return bytearray(hashlib.sha256(dh).digest())
+ elif hash_type == 'SHA2_384':
+ return bytearray(hashlib.sha384(dh).digest())
+ else:
+ raise Exception ("Unsupported hash type in get_pub_key_hash!")
+
+ @staticmethod
+ def calculate_auth_data (file, auth_type, priv_key, out_dir):
+ # calculate auth info for a given file
+ hash_data = bytearray()
+ auth_data = bytearray()
+ basename = os.path.basename (file)
+ if auth_type in ['NONE']:
+ pass
+ elif auth_type in ["SHA2_256"]:
+ data = get_file_data (file)
+ hash_data.extend (hashlib.sha256(data).digest())
+ elif auth_type in ["SHA2_384"]:
+ data = get_file_data (file)
+ hash_data.extend (hashlib.sha384(data).digest())
+ elif auth_type in ['RSA2048_PKCS1_SHA2_256', 'RSA3072_PKCS1_SHA2_384', 'RSA2048_PSS_SHA2_256', 'RSA3072_PSS_SHA2_384' ]:
+ auth_type = adjust_auth_type (auth_type, priv_key)
+ pub_key = os.path.join(out_dir, basename + '.pub')
+ di = gen_pub_key (priv_key, pub_key)
+ key_hash = CONTAINER.get_pub_key_hash (di, CONTAINER._auth_to_hashalg_str[auth_type])
+ hash_data.extend (key_hash)
+ out_file = os.path.join(out_dir, basename + '.sig')
+ rsa_sign_file (priv_key, pub_key, CONTAINER._auth_to_hashalg_str[auth_type], CONTAINER._auth_to_signscheme_str[auth_type], file, out_file, False, True)
+ auth_data.extend (get_file_data(out_file))
+ else:
+ raise Exception ("Unsupport AuthType '%s' !" % auth_type)
+ return hash_data, auth_data
+
+
+ def set_dir_path(self, out_dir, inp_dir, key_dir, tool_dir):
+ self.out_dir = out_dir
+ self.inp_dir = inp_dir
+ self.key_dir = key_dir
+ self.tool_dir = tool_dir
+
+ def set_header_flags (self, flags, overwrite = False):
+ if overwrite:
+ self.header.flags = flags
+ else:
+ self.header.flags |= flags
+
+ def set_header_svn_info (self, svn):
+ self.header.svn = svn
+
+ def set_header_auth_info (self, auth_type_str = None, priv_key = None):
+ if priv_key is not None:
+ self.header.priv_key = priv_key
+
+ if auth_type_str is not None:
+ self.header.auth_type = CONTAINER.get_auth_type_val (auth_type_str)
+ auth_size = CONTAINER.get_auth_size (self.header.auth_type, True)
+ self.header.auth_data = b'\xff' * auth_size
+
+ def get_header_size (self):
+ length = sizeof (self.header)
+ for comp in self.header.comp_entry:
+ length += comp.hash_size
+ length += sizeof(COMPONENT_ENTRY) * len(self.header.comp_entry)
+ length += len(self.header.auth_data)
+ return length
+
+ def get_auth_data (self, comp_file, auth_type_str):
+ # calculate auth info for a give component file with specified auth type
+ auth_size = CONTAINER.get_auth_size (auth_type_str, True)
+ file_data = bytearray(get_file_data (comp_file))
+ auth_data = None
+ hash_data = bytearray()
+
+ if len(file_data) < sizeof (LZ_HEADER):
+ return file_data, hash_data, auth_data
+
+ lz_header = LZ_HEADER.from_buffer(file_data)
+ data = bytearray()
+ if lz_header.signature in LZ_HEADER._compress_alg:
+ offset = sizeof(lz_header) + get_aligned_value (lz_header.compressed_len)
+ if len(file_data) == auth_size + offset:
+ auth_data = file_data[offset:offset+auth_size]
+ data = file_data[:sizeof(lz_header) + lz_header.compressed_len]
+ if auth_type_str in ["SHA2_256"]:
+ hash_data.extend (hashlib.sha256(data).digest())
+ if auth_type_str in ["SHA2_384"]:
+ hash_data.extend (hashlib.sha384(data).digest())
+ elif auth_type_str in ['RSA2048', 'RSA3072']:
+ offset += ((CONTAINER.get_auth_size (auth_type_str)))
+ key_hash = self.get_pub_key_hash (file_data[offset:])
+ hash_data.extend (key_hash)
+ else:
+ raise Exception ("Unsupport AuthType '%s' !" % auth_type)
+ return data, hash_data, auth_data
+
+ def adjust_header (self):
+ # finalize the container
+ header = self.header
+ header.entry_count = len(header.comp_entry)
+ alignment = header.alignment - 1
+ header.data_offset = (self.get_header_size() + alignment) & ~alignment
+ if header.entry_count > 0:
+ length = header.comp_entry[-1].offset + header.comp_entry[-1].size
+ header.data_size = (length + alignment) & ~alignment
+ else:
+ header.data_size = 0
+ auth_type = self.get_auth_type_str (header.auth_type)
+ basename = header.signature.decode()
+ hdr_file = os.path.join(self.out_dir, basename + '.hdr')
+ hdr_data = bytearray (header)
+ for component in header.comp_entry:
+ hdr_data.extend (component)
+ hdr_data.extend (component.hash_data)
+ gen_file_from_object (hdr_file, hdr_data)
+ hash_data, auth_data = CONTAINER.calculate_auth_data (hdr_file, auth_type, header.priv_key, self.out_dir)
+ if len(auth_data) != len(header.auth_data):
+ print (len(auth_data) , len(header.auth_data))
+ raise Exception ("Unexpected authentication data length for container header !")
+ header.auth_data = auth_data
+
+ def get_data (self):
+ # Prepare data buffer
+ header = self.header
+ data = bytearray(header)
+ for component in header.comp_entry:
+ data.extend (component)
+ data.extend (component.hash_data)
+ padding = b'\xff' * get_padding_length (len(data))
+ data.extend(padding + header.auth_data)
+ for component in header.comp_entry:
+ offset = component.offset + header.data_offset
+ data.extend (b'\xff' * (offset - len(data)))
+ comp_data = bytearray(component.data)
+ padding = b'\xff' * get_padding_length (len(comp_data))
+ comp_data.extend (padding + component.auth_data)
+ if len(comp_data) > component.size:
+ raise Exception ("Component '%s' needs space 0x%X, but region size is 0x%X !" % (component.name.decode(), len(comp_data), component.size))
+ data.extend (comp_data)
+ offset = header.data_offset + header.data_size
+ data.extend (b'\xff' * (offset - len(data)))
+ return data
+
+ def locate_component (self, comp_name):
+ component = None
+ for each in self.header.comp_entry:
+ if each.name.decode() == comp_name.upper():
+ component = each
+ break;
+ return component
+
+ def dump (self):
+ print ('%s' % self.output_struct (self.header))
+ print (self.hex_str (self.header.auth_data, 'auth_data'))
+ for component in self.header.comp_entry:
+ print ('%s' % self.output_struct (component))
+ print (self.hex_str (component.hash_data, 'hash_data'))
+ print (self.hex_str (component.auth_data, 'auth_data'))
+ print (self.hex_str (component.data, 'data') + ' %s' % str(component.data[:4].decode()))
+
+ def create (self, layout):
+
+ # for monolithic signing, need to add a reserved _SG_ entry to hold the auth info
+ mono_sig = '_SG_'
+ is_mono_signing = True if layout[-1][0] == mono_sig else False
+
+ # get the first entry in layout as CONTAINER_HDR
+ container_sig, container_file, image_type, auth_type, key_file, alignment, region_size, svn = layout[0]
+
+ if alignment == 0:
+ alignment = 0x1000
+
+ if auth_type == '':
+ auth_type = 'NONE'
+
+ if image_type == '':
+ image_type = 'NORMAL'
+
+ if container_file == '':
+ container_file = container_sig + '.bin'
+ key_path = os.path.join(self.key_dir, key_file)
+ if os.path.isfile (key_path):
+ auth_type = adjust_auth_type (auth_type, key_path)
+
+ # build header
+ self.init_header (container_sig.encode(), alignment, image_type)
+ self.set_header_auth_info (auth_type, key_path)
+ self.set_header_svn_info (svn)
+
+ name_set = set()
+ is_last_entry = False
+ for name, file, compress_alg, auth_type, key_file, alignment, region_size, svn in layout[1:]:
+ if is_last_entry:
+ raise Exception ("'%s' must be the last entry in layout for monolithic signing!" % mono_sig)
+ if compress_alg == '':
+ compress_alg = 'Dummy'
+ if auth_type == '':
+ auth_type = 'NONE'
+
+ # build a component entry
+ component = COMPONENT_ENTRY ()
+ component.name = name.encode()
+ if alignment == 0:
+ component.alignment = self.header.alignment.bit_length() - 1
+ else:
+ component.alignment = alignment.bit_length() - 1
+ component.attribute = 0
+ component.auth_type = self.get_auth_type_val (auth_type)
+ key_file = os.path.join (self.key_dir, key_file)
+ if file:
+ if os.path.isabs(file):
+ in_file = file
+ else:
+ for tst in [self.inp_dir, self.out_dir]:
+ in_file = os.path.join(tst, file)
+ if os.path.isfile(in_file):
+ break
+ if not os.path.isfile(in_file):
+ raise Exception ("Component file path '%s' is invalid !" % file)
+ else:
+ in_file = os.path.join(self.out_dir, component.name.decode() + '.bin')
+ gen_file_with_size (in_file, 0)
+ if component.name == mono_sig.encode():
+ component.attribute = COMPONENT_ENTRY._attr['RESERVED']
+ compress_alg = 'Dummy'
+ is_last_entry = True
+
+ # compress the component
+ lz_file = compress (in_file, compress_alg, svn, self.out_dir, self.tool_dir)
+ component.data = bytearray(get_file_data (lz_file))
+
+ # calculate the component auth info
+ component.hash_data, component.auth_data = CONTAINER.calculate_auth_data (lz_file, auth_type, key_file, self.out_dir)
+ component.hash_size = len(component.hash_data)
+ if region_size == 0:
+ # arrange the region size automatically
+ region_size = len(component.data)
+ region_size = get_aligned_value (region_size, 4) + len(component.auth_data)
+ if is_mono_signing:
+ region_size = get_aligned_value (region_size, self.header.alignment)
+ else:
+ region_size = get_aligned_value (region_size, (1 << component.alignment))
+ component.size = region_size
+ name_set.add (component.name)
+ self.header.comp_entry.append (component)
+
+ if len(name_set) != len(self.header.comp_entry):
+ raise Exception ("Found duplicated component names in a container !")
+
+ # calculate the component offset based on alignment requirement
+ base_offset = None
+ offset = self.get_header_size ()
+ for component in self.header.comp_entry:
+ alignment = (1 << component.alignment) - 1
+ next_offset = (offset + alignment) & ~alignment
+ if is_mono_signing and (next_offset - offset >= sizeof(LZ_HEADER)):
+ offset = next_offset - sizeof(LZ_HEADER)
+ else:
+ offset = next_offset
+ if base_offset is None:
+ base_offset = offset
+ component.offset = offset - base_offset
+ offset += component.size
+
+ if is_mono_signing:
+ # for monolithic signing, set proper flags and update header
+ self.set_header_flags (CONTAINER_HDR._flags['MONO_SIGNING'])
+ self.adjust_header ()
+
+ # update auth info for last _SG_ entry
+ data = self.get_data ()[self.header.data_offset:]
+ pods_comp = self.header.comp_entry[-1]
+ pods_data = data[:pods_comp.offset]
+ gen_file_from_object (in_file, pods_data)
+ pods_comp.hash_data, pods_comp.auth_data = CONTAINER.calculate_auth_data (in_file, auth_type, key_file, self.out_dir)
+
+ self.adjust_header ()
+ data = self.get_data ()
+
+ out_file = os.path.join(self.out_dir, container_file)
+ gen_file_from_object (out_file, data)
+
+ return out_file
+
+ def replace (self, comp_name, comp_file, comp_alg, key_file, svn, new_name):
+ if self.header.flags & CONTAINER_HDR._flags['MONO_SIGNING']:
+ raise Exception ("Counld not replace component for monolithically signed container!")
+
+ component = self.locate_component (comp_name)
+ if not component:
+ raise Exception ("Counld not locate component '%s' in container !" % comp_name)
+ if comp_alg == '':
+ # reuse the original compression alg
+ lz_header = LZ_HEADER.from_buffer(component.data)
+ comp_alg = LZ_HEADER._compress_alg[lz_header.signature]
+ else:
+ comp_alg = comp_alg[0].upper() + comp_alg[1:]
+
+ # verify the new component hash does match the hash stored in the container header
+ auth_type_str = self.get_auth_type_str (component.auth_type)
+ data, hash_data, auth_data = self.get_auth_data (comp_file, auth_type_str)
+ if auth_data is None:
+ lz_file = compress (comp_file, comp_alg, svn, self.out_dir, self.tool_dir)
+ if auth_type_str.startswith ('RSA') and key_file == '':
+ raise Exception ("Signing key needs to be specified !")
+ hash_data, auth_data = CONTAINER.calculate_auth_data (lz_file, auth_type_str, key_file, self.out_dir)
+ data = get_file_data (lz_file)
+ component.data = bytearray(data)
+ component.auth_data = bytearray(auth_data)
+ if component.hash_data != bytearray(hash_data):
+ raise Exception ('Compoent hash does not match the one stored in container header !')
+
+ # create the final output file
+ data = self.get_data ()
+ if new_name == '':
+ new_name = self.header.signature + '.bin'
+ out_file = os.path.join(self.out_dir, new_name)
+ gen_file_from_object (out_file, data)
+
+ return out_file
+
+ def extract (self, name = '', file_path = ''):
+ if name == '':
+ # extract all components inside a container
+ # so creat a layout file first
+ if file_path == '':
+ file_name = self.header.signature + '.bin'
+ else:
+ file_name = os.path.splitext(os.path.basename (file_path))[0] + '.bin'
+
+ # create header entry
+ auth_type_str = self.get_auth_type_str (self.header.auth_type)
+ match = re.match('RSA(\d+)_', auth_type_str)
+ if match:
+ key_file = 'KEY_ID_CONTAINER_RSA%s' % match.group(1)
+ else:
+ key_file = ''
+ alignment = self.header.alignment
+ image_type_str = CONTAINER.get_image_type_str(self.header.image_type)
+ header = ['%s' % self.header.signature.decode(), file_name, image_type_str, auth_type_str, key_file]
+ layout = [(' Name', ' ImageFile', ' CompAlg', ' AuthType', ' KeyFile', ' Alignment', ' Size', 'Svn')]
+ layout.append(tuple(["'%s'" % x for x in header] + ['0x%x' % alignment, '0', '0x%x' % self.header.svn]))
+ # create component entry
+ for component in self.header.comp_entry:
+ auth_type_str = self.get_auth_type_str (component.auth_type)
+ match = re.match('RSA(\d+)_', auth_type_str)
+ if match:
+ key_file = 'KEY_ID_CONTAINER_COMP_RSA%s' % match.group(1)
+ else:
+ key_file = ''
+ lz_header = LZ_HEADER.from_buffer(component.data)
+ alg = LZ_HEADER._compress_alg[lz_header.signature]
+ svn = lz_header.svn
+ if component.attribute & COMPONENT_ENTRY._attr['RESERVED']:
+ comp_file = ''
+ else:
+ comp_file = component.name.decode() + '.bin'
+ comp = [component.name.decode(), comp_file, alg, auth_type_str, key_file]
+ layout.append(tuple(["'%s'" % x for x in comp] + ['0x%x' % (1 << component.alignment), '0x%x' % component.size, '0x%x' % svn]))
+
+ # write layout file
+ layout_file = os.path.join(self.out_dir, self.header.signature.decode() + '.txt')
+ fo = open (layout_file, 'w')
+ fo.write ('# Container Layout File\n#\n')
+ for idx, each in enumerate(layout):
+ line = ' %-6s, %-16s, %-10s, %-24s, %-32s, %-10s, %-10s, %-10s' % each
+ if idx == 0:
+ line = '# %s\n' % line
+ else:
+ line = ' (%s),\n' % line
+ fo.write (line)
+ if idx == 0:
+ line = '# %s\n' % ('=' * 136)
+ fo.write (line)
+ fo.close()
+
+ for component in self.header.comp_entry:
+ if component.attribute & COMPONENT_ENTRY._attr['RESERVED']:
+ continue
+ # creat individual component region and image binary
+ if (component.name.decode() == name) or (name == ''):
+ basename = os.path.join(self.out_dir, '%s' % component.name.decode())
+ sig_file = basename + '.rgn'
+ sig_data = component.data + b'\xff' * get_padding_length (len(component.data)) + component.auth_data
+ gen_file_from_object (sig_file, sig_data)
+
+ bin_file = basename + '.bin'
+ lz_header = LZ_HEADER.from_buffer(component.data)
+ signature = lz_header.signature
+ if signature in [b'LZDM']:
+ offset = sizeof(lz_header)
+ data = component.data[offset : offset + lz_header.compressed_len]
+ gen_file_from_object (bin_file, data)
+ elif signature in [b'LZMA', b'LZ4 ']:
+ decompress (sig_file, bin_file, self.tool_dir)
+ else:
+ raise Exception ("Unknown LZ format!")
+
+def gen_container_bin (container_list, out_dir, inp_dir, key_dir = '.', tool_dir = ''):
+ for each in container_list:
+ container = CONTAINER ()
+ container.set_dir_path (out_dir, inp_dir, key_dir, tool_dir)
+ out_file = container.create (each)
+ print ("Container '%s' was created successfully at: \n %s" % (container.header.signature.decode(), out_file))
+
+def adjust_auth_type (auth_type_str, key_path):
+ if os.path.exists(key_path):
+ sign_key_type = get_key_type(key_path)
+ if auth_type_str != '':
+ sign_scheme = CONTAINER._auth_to_signscheme_str[auth_type_str]
+ else:
+ # Set to default signing scheme if auth type is generated.
+ sign_scheme = 'RSA_PSS'
+ auth_type, hash_type = get_auth_hash_type (sign_key_type, sign_scheme)
+ if auth_type_str and (auth_type != auth_type_str):
+ print ("Override auth type to '%s' in order to match the private key type !" % auth_type)
+ auth_type_str = auth_type
+
+ return auth_type_str
+
+def gen_layout (comp_list, img_type, auth_type_str, svn, out_file, key_dir, key_file):
+ hash_type = CONTAINER._auth_to_hashalg_str[auth_type_str] if auth_type_str else ''
+ auth_type = auth_type_str
+ key_path = os.path.join(key_dir, key_file)
+ auth_type = adjust_auth_type (auth_type, key_path)
+ if auth_type == '':
+ raise Exception ("'auth' parameter is expected !")
+
+ # prepare the layout from individual components from '-cl'
+ if img_type not in CONTAINER_HDR._image_type.keys():
+ raise Exception ("Invalid Container Type '%s' !" % img_type)
+ layout = "('BOOT', '%s', '%s', '%s' , '%s', 0x10, 0, %s),\n" % (out_file, img_type, auth_type, key_file, svn)
+ end_layout = "('_SG_', '', 'Dummy', '%s', '', 0, 0, %s)," % (hash_type, svn)
+ for idx, each in enumerate(comp_list):
+ parts = each.split(':')
+ comp_name = parts[0]
+ if len(comp_name) != 4:
+ raise Exception ("Invalid component string format '%s' !" % each)
+
+ if (len(parts)) > 2:
+ comp_file = ':'.join(parts[1:2])
+ com_svn = ':'.join(parts[2:])
+ else:
+ comp_file = ':'.join(parts[1:])
+ com_svn = 0 # set to default svn
+
+ if comp_name == 'INRD':
+ align = 0x1000
+ else:
+ align = 0
+ layout += "('%s', '%s', 'Dummy', 'NONE', '', %s, 0, %s),\n" % (comp_name, comp_file, align, com_svn)
+ layout += end_layout
+ return layout
+
+def create_container (args):
+ layout = ""
+ # if '-l', get the layout content directly
+ # if '-cl' prepare the layout
+
+ #extract key dir and file
+ key_path = os.path.abspath(args.key_path)
+ if os.path.isdir(key_path):
+ key_dir = key_path
+ key_file = ''
+ else:
+ key_dir = os.path.dirname(key_path)
+ key_file = os.path.basename(key_path)
+
+ #extract out dir and file
+ out_path = os.path.abspath(args.out_path)
+ if os.path.isdir(out_path):
+ out_dir = out_path
+ out_file = ''
+ else:
+ out_dir = os.path.dirname(out_path)
+ out_file = os.path.basename(out_path)
+
+ if args.layout:
+ # Using layout file
+ layout = get_file_data(args.layout, 'r')
+ else:
+ # Using component list
+ if not key_file:
+ raise Exception ("key_path expects a key file path !")
+ layout = gen_layout (args.comp_list, args.img_type, args.auth, args.svn, out_file, key_dir, key_file)
+ container_list = eval ('[[%s]]' % layout.replace('\\', '/'))
+
+ comp_dir = os.path.abspath(args.comp_dir)
+ if not os.path.isdir(comp_dir):
+ raise Exception ("'comp_dir' expects a directory path !")
+ tool_dir = os.path.abspath(args.tool_dir)
+ if not os.path.isdir(tool_dir):
+ raise Exception ("'tool_dir' expects a directory path !")
+
+ if out_file:
+ # override the output file name
+ hdr_entry = list (container_list[0][0])
+ hdr_entry[1] = out_file
+ container_list[0][0] = tuple(hdr_entry)
+
+ if args.layout and args.auth:
+ # override auth
+ hdr_entry = list (container_list[0][0])
+ hdr_entry[3] = args.auth
+ container_list[0][0] = tuple(hdr_entry)
+
+ gen_container_bin (container_list, out_dir, comp_dir, key_dir, tool_dir)
+
+def extract_container (args):
+ tool_dir = args.tool_dir if args.tool_dir else '.'
+ data = get_file_data (args.image)
+ container = CONTAINER (data)
+ container.set_dir_path (args.out_dir, '.', '.', tool_dir)
+ container.extract (args.comp_name, args.image)
+ print ("Components were extraced successfully at:\n %s" % args.out_dir)
+
+def replace_component (args):
+ tool_dir = args.tool_dir if args.tool_dir else '.'
+ data = get_file_data (args.image)
+ container = CONTAINER (data)
+ out_path = os.path.abspath(args.out_image)
+ out_dir = os.path.dirname(out_path)
+ out_file = os.path.basename(out_path)
+ container.set_dir_path (out_dir, '.', '.', tool_dir)
+ file = container.replace (args.comp_name, args.comp_file, args.compress, args.key_file, args.svn, out_file)
+ print ("Component '%s' was replaced successfully at:\n %s" % (args.comp_name, file))
+
+def sign_component (args):
+ compress_alg = args.compress
+ compress_alg = compress_alg[0].upper() + compress_alg[1:]
+
+ #extract out dir and file
+ sign_file = os.path.abspath(args.out_file)
+ out_dir = os.path.dirname(sign_file)
+
+ lz_file = compress (args.comp_file, compress_alg, args.svn, out_dir, args.tool_dir)
+ data = bytearray(get_file_data (lz_file))
+ hash_data, auth_data = CONTAINER.calculate_auth_data (lz_file, args.auth, args.key_file, out_dir)
+
+ data.extend (b'\xff' * get_padding_length(len(data)))
+ data.extend (auth_data)
+ gen_file_from_object (sign_file, data)
+ print ("Component file was signed successfully at:\n %s" % sign_file)
+
+def display_container (args):
+ data = get_file_data (args.image)
+ container = CONTAINER (data)
+ container.dump ()
+
+def main():
+ parser = argparse.ArgumentParser()
+ sub_parser = parser.add_subparsers(help='command')
+
+ # Command for display
+ cmd_display = sub_parser.add_parser('view', help='display a container image')
+ cmd_display.add_argument('-i', dest='image', type=str, required=True, help='Container input image')
+ cmd_display.set_defaults(func=display_container)
+
+ # Command for create
+ cmd_display = sub_parser.add_parser('create', help='create a container image')
+ group = cmd_display.add_mutually_exclusive_group (required=True)
+ # '-l' or '-cl', one of them is mandatory
+ group.add_argument('-l', dest='layout', type=str, help='Container layout input file if no -cl')
+ group.add_argument('-cl', dest='comp_list',nargs='+', help='List of each component files, following XXXX:FileName format')
+ cmd_display.add_argument('-t', dest='img_type', type=str, default='CLASSIC', help='Container Image Type : [NORMAL, CLASSIC, MULTIBOOT]')
+ cmd_display.add_argument('-o', dest='out_path', type=str, default='.', help='Container output directory/file')
+ cmd_display.add_argument('-k', dest='key_path', type=str, default='', help='Input key directory/file. Use key directoy path when container layout -l option is used \
+ Use Key Id or key file path when component files with -cl option is specified')
+ cmd_display.add_argument('-a', dest='auth', choices=['SHA2_256', 'SHA2_384', 'RSA2048_PKCS1_SHA2_256',
+ 'RSA3072_PKCS1_SHA2_384', 'RSA2048_PSS_SHA2_256', 'RSA3072_PSS_SHA2_384', 'NONE'], default='', help='authentication algorithm')
+ cmd_display.add_argument('-cd', dest='comp_dir', type=str, default='', help='Componet image input directory')
+ cmd_display.add_argument('-td', dest='tool_dir', type=str, default='', help='Compression tool directory')
+ cmd_display.add_argument('-s', dest='svn', type=int, default=0, help='Security version number for Container header')
+ cmd_display.set_defaults(func=create_container)
+
+ # Command for extract
+ cmd_display = sub_parser.add_parser('extract', help='extract a component image')
+ cmd_display.add_argument('-i', dest='image', type=str, required=True, help='Container input image path')
+ cmd_display.add_argument('-n', dest='comp_name', type=str, default='', help='Component name to extract')
+ cmd_display.add_argument('-od', dest='out_dir', type=str, default='.', help='Output directory')
+ cmd_display.add_argument('-td', dest='tool_dir', type=str, default='', help='Compression tool directory')
+ cmd_display.set_defaults(func=extract_container)
+
+ # Command for replace
+ cmd_display = sub_parser.add_parser('replace', help='replace a component image')
+ cmd_display.add_argument('-i', dest='image', type=str, required=True, help='Container input image path')
+ cmd_display.add_argument('-o', dest='out_image', type=str, default='', help='Container new output image path')
+ cmd_display.add_argument('-n', dest='comp_name', type=str, required=True, help='Component name to replace')
+ cmd_display.add_argument('-f', dest='comp_file', type=str, required=True, help='Component input file path')
+ cmd_display.add_argument('-c', dest='compress', choices=['lz4', 'lzma', 'dummy'], default='dummy', help='compression algorithm')
+ cmd_display.add_argument('-k', dest='key_file', type=str, default='', help='Key Id or Private key file path to sign component')
+ cmd_display.add_argument('-td', dest='tool_dir', type=str, default='', help='Compression tool directory')
+ cmd_display.add_argument('-s', dest='svn', type=int, default=0, help='Security version number for Component')
+ cmd_display.set_defaults(func=replace_component)
+
+ # Command for sign
+ cmd_display = sub_parser.add_parser('sign', help='compress and sign a component image')
+ cmd_display.add_argument('-f', dest='comp_file', type=str, required=True, help='Component input file path')
+ cmd_display.add_argument('-o', dest='out_file', type=str, default='', help='Signed output image path')
+ cmd_display.add_argument('-c', dest='compress', choices=['lz4', 'lzma', 'dummy'], default='dummy', help='compression algorithm')
+ cmd_display.add_argument('-a', dest='auth', choices=['SHA2_256', 'SHA2_384', 'RSA2048_PKCS1_SHA2_256',
+ 'RSA3072_PKCS1_SHA2_384', 'RSA2048_PSS_SHA2_256', 'RSA3072_PSS_SHA2_384', 'NONE'], default='NONE', help='authentication algorithm')
+ cmd_display.add_argument('-k', dest='key_file', type=str, default='', help='Key Id or Private key file path to sign component')
+ cmd_display.add_argument('-td', dest='tool_dir', type=str, default='', help='Compression tool directory')
+ cmd_display.add_argument('-s', dest='svn', type=int, default=0, help='Security version number for Component')
+ cmd_display.set_defaults(func=sign_component)
+
+ # Parse arguments and run sub-command
+ args = parser.parse_args()
+ try:
+ func = args.func
+ except AttributeError:
+ parser.error("too few arguments")
+
+ # Additional check
+ if args.func == sign_component:
+ if args.auth.startswith('RSA') and args.key_file == '':
+ parser.error("the following arguments are required: -k")
+
+ func(args)
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/SingleSign.py b/SingleSign.py
new file mode 100644
index 0000000..5baacbd
--- /dev/null
+++ b/SingleSign.py
@@ -0,0 +1,301 @@
+#!/usr/bin/env python
+## @ SingleSign.py
+# Single signing script
+#
+# Copyright (c) 2020, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+##
+# Import Modules
+#
+import os
+import sys
+import re
+import shutil
+import subprocess
+import struct
+import hashlib
+import string
+
+SIGNING_KEY = {
+ # Key Id | Key File Name start |
+ # =================================================================
+ # KEY_ID_MASTER is used for signing Slimboot Key Hash Manifest container (KEYH Component)
+ "KEY_ID_MASTER_RSA2048" : "MasterTestKey_Priv_RSA2048.pem",
+ "KEY_ID_MASTER_RSA3072" : "MasterTestKey_Priv_RSA3072.pem",
+
+ # KEY_ID_CFGDATA is used for signing external Config data blob)
+ "KEY_ID_CFGDATA_RSA2048" : "ConfigTestKey_Priv_RSA2048.pem",
+ "KEY_ID_CFGDATA_RSA3072" : "ConfigTestKey_Priv_RSA3072.pem",
+
+ # KEY_ID_FIRMWAREUPDATE is used for signing capsule firmware update image)
+ "KEY_ID_FIRMWAREUPDATE_RSA2048" : "FirmwareUpdateTestKey_Priv_RSA2048.pem",
+ "KEY_ID_FIRMWAREUPDATE_RSA3072" : "FirmwareUpdateTestKey_Priv_RSA3072.pem",
+
+ # KEY_ID_CONTAINER is used for signing container header with mono signature
+ "KEY_ID_CONTAINER_RSA2048" : "ContainerTestKey_Priv_RSA2048.pem",
+ "KEY_ID_CONTAINER_RSA3072" : "ContainerTestKey_Priv_RSA3072.pem",
+
+ # CONTAINER_COMP1_KEY_ID is used for signing container components
+ "KEY_ID_CONTAINER_COMP_RSA2048" : "ContainerCompTestKey_Priv_RSA2048.pem",
+ "KEY_ID_CONTAINER_COMP_RSA3072" : "ContainerCompTestKey_Priv_RSA3072.pem",
+
+ # KEY_ID_OS1_PUBLIC, KEY_ID_OS2_PUBLIC is used for referencing Boot OS public keys
+ "KEY_ID_OS1_PUBLIC_RSA2048" : "OS1_TestKey_Pub_RSA2048.pem",
+ "KEY_ID_OS1_PUBLIC_RSA3072" : "OS1_TestKey_Pub_RSA3072.pem",
+
+ "KEY_ID_OS2_PUBLIC_RSA2048" : "OS2_TestKey_Pub_RSA2048.pem",
+ "KEY_ID_OS2_PUBLIC_RSA3072" : "OS2_TestKey_Pub_RSA3072.pem",
+
+ }
+
+MESSAGE_SBL_KEY_DIR = (
+ "!!! PRE-REQUISITE: Path to SBL_KEY_DIR has to be set with SBL KEYS DIRECTORY !!! \n"
+ "!!! Generate keys using GenerateKeys.py available in BootloaderCorePkg/Tools directory !!! \n"
+ "!!! Run $python BootloaderCorePkg/Tools/GenerateKeys.py -k $PATH_TO_SBL_KEY_DIR !!!\n"
+ "!!! Set SBL_KEY_DIR environ with path to SBL KEYS DIR !!!\n"
+ "!!! Windows $set SBL_KEY_DIR=$PATH_TO_SBL_KEY_DIR !!!\n"
+ "!!! Linux $export SBL_KEY_DIR=$PATH_TO_SBL_KEY_DIR !!!\n"
+ )
+
+def get_openssl_path ():
+ if os.name == 'nt':
+ if 'OPENSSL_PATH' not in os.environ:
+ openssl_dir = "C:\\Openssl\\bin\\"
+ if os.path.exists (openssl_dir):
+ os.environ['OPENSSL_PATH'] = openssl_dir
+ else:
+ os.environ['OPENSSL_PATH'] = "C:\\Openssl\\"
+ if 'OPENSSL_CONF' not in os.environ:
+ openssl_cfg = "C:\\Openssl\\openssl.cfg"
+ if os.path.exists(openssl_cfg):
+ os.environ['OPENSSL_CONF'] = openssl_cfg
+ openssl = os.path.join(os.environ.get ('OPENSSL_PATH', ''), 'openssl.exe')
+ else:
+ # Get openssl path for Linux cases
+ openssl = shutil.which('openssl')
+
+ return openssl
+
+def run_process (arg_list, print_cmd = False, capture_out = False):
+ sys.stdout.flush()
+ if print_cmd:
+ print (' '.join(arg_list))
+
+ exc = None
+ result = 0
+ output = ''
+ try:
+ if capture_out:
+ output = subprocess.check_output(arg_list).decode()
+ else:
+ result = subprocess.call (arg_list)
+ except Exception as ex:
+ result = 1
+ exc = ex
+
+ if result:
+ if not print_cmd:
+ print ('Error in running process:\n %s' % ' '.join(arg_list))
+ if exc is None:
+ sys.exit(1)
+ else:
+ raise exc
+
+ return output
+
+def check_file_pem_format (priv_key):
+ # Check for file .pem format
+ key_name = os.path.basename(priv_key)
+ if os.path.splitext(key_name)[1] == ".pem":
+ return True
+ else:
+ return False
+
+def get_key_id (priv_key):
+ # Extract base name if path is provided.
+ key_name = os.path.basename(priv_key)
+ # Check for KEY_ID in key naming.
+ if key_name.startswith('KEY_ID'):
+ return key_name
+ else:
+ return None
+
+def get_sbl_key_dir ():
+ # Check Key store setting SBL_KEY_DIR path
+ if 'SBL_KEY_DIR' not in os.environ:
+ raise Exception ("ERROR: SBL_KEY_DIR is not defined. Set SBL_KEY_DIR with SBL Keys directory!!\n"
+ + MESSAGE_SBL_KEY_DIR)
+
+ sbl_key_dir = os.environ.get('SBL_KEY_DIR')
+ if not os.path.exists(sbl_key_dir):
+ raise Exception (("ERROR:SBL_KEY_DIR set %s is not valid. Set the correct SBL_KEY_DIR path !!\n"
+ + MESSAGE_SBL_KEY_DIR) % sbl_key_dir)
+ else:
+ return sbl_key_dir
+
+def get_key_from_store (in_key):
+
+ #Check in_key is path to key
+ if os.path.exists(in_key):
+ return in_key
+
+ # Get Slimboot key dir path
+ sbl_key_dir = get_sbl_key_dir()
+
+ # Extract if in_key is key_id
+ priv_key = get_key_id (in_key)
+ if priv_key is not None:
+ if (priv_key in SIGNING_KEY):
+ # Generate key file name from key id
+ priv_key_file = SIGNING_KEY[priv_key]
+ else:
+ raise Exception('KEY_ID %s is not found in supported KEY IDs!!' % priv_key)
+ elif check_file_pem_format(in_key) == True:
+ # check if file name is provided in pem format
+ priv_key_file = in_key
+ else:
+ priv_key_file = None
+ raise Exception('key provided %s is not valid!' % in_key)
+
+ # Create a file path
+ # Join Key Dir and priv_key_file
+ try:
+ priv_key = os.path.join (sbl_key_dir, priv_key_file)
+ except:
+ raise Exception('priv_key is not found %s!' % priv_key)
+
+ # Check for priv_key construted based on KEY ID exists in specified path
+ if not os.path.isfile(priv_key):
+ raise Exception (("!!! ERROR: Key file corresponding to '%s' do not exist in Sbl key directory at '%s' !!! \n" + MESSAGE_SBL_KEY_DIR) % (in_key, sbl_key_dir))
+
+ return priv_key
+
+#
+# Sign an file using openssl
+#
+# priv_key [Input] Key Id or Path to Private key
+# hash_type [Input] Signing hash
+# sign_scheme[Input] Sign/padding scheme
+# in_file [Input] Input file to be signed
+# out_file [Input/Output] Signed data file
+#
+
+def single_sign_file (priv_key, hash_type, sign_scheme, in_file, out_file):
+
+ _hash_type_string = {
+ "SHA2_256" : 'sha256',
+ "SHA2_384" : 'sha384',
+ "SHA2_512" : 'sha512',
+ }
+
+ _hash_digest_Size = {
+ # Hash_string : Hash_Size
+ "SHA2_256" : 32,
+ "SHA2_384" : 48,
+ "SHA2_512" : 64,
+ "SM3_256" : 32,
+ }
+
+ _sign_scheme_string = {
+ "RSA_PKCS1" : 'pkcs1',
+ "RSA_PSS" : 'pss',
+ }
+
+ priv_key = get_key_from_store(priv_key)
+
+ # Temporary files to store hash generated
+ hash_file_tmp = out_file+'.hash.tmp'
+ hash_file = out_file+'.hash'
+
+ # Generate hash using openssl dgst in hex format
+ cmdargs = [get_openssl_path(), 'dgst', '-'+'%s' % _hash_type_string[hash_type], '-out', '%s' % hash_file_tmp, '%s' % in_file]
+ run_process (cmdargs)
+
+ # Extract hash form dgst command output and convert to ascii
+ with open(hash_file_tmp, 'r') as fin:
+ hashdata = fin.read()
+ fin.close()
+
+ try:
+ hashdata = hashdata.rsplit('=', 1)[1].strip()
+ except:
+ raise Exception('Hash Data not found for signing!')
+
+ if len(hashdata) != (_hash_digest_Size[hash_type] * 2):
+ raise Exception('Hash Data size do match with for hash type!')
+
+ hashdata_bytes = bytearray.fromhex(hashdata)
+ open (hash_file, 'wb').write(hashdata_bytes)
+
+ print ("Key used for Singing %s !!" % priv_key)
+
+ # sign using Openssl pkeyutl
+ cmdargs = [get_openssl_path(), 'pkeyutl', '-sign', '-in', '%s' % hash_file, '-inkey', '%s' % priv_key,
+ '-out', '%s' % out_file, '-pkeyopt', 'digest:%s' % _hash_type_string[hash_type],
+ '-pkeyopt', 'rsa_padding_mode:%s' % _sign_scheme_string[sign_scheme]]
+
+ run_process (cmdargs)
+
+ return
+
+#
+# Extract public key using openssl
+#
+# in_key [Input] Private key or public key in pem format
+# pub_key_file [Input/Output] Public Key to a file
+#
+# return keydata (mod, exp) in bin format
+#
+
+def single_sign_gen_pub_key (in_key, pub_key_file = None):
+
+ in_key = get_key_from_store(in_key)
+
+ # Expect key to be in PEM format
+ is_prv_key = False
+ cmdline = [get_openssl_path(), 'rsa', '-pubout', '-text', '-noout', '-in', '%s' % in_key]
+ # Check if it is public key or private key
+ text = open(in_key, 'r').read()
+ if '-BEGIN RSA PRIVATE KEY-' in text or '-BEGIN PRIVATE KEY-' in text:
+ is_prv_key = True
+ elif '-BEGIN PUBLIC KEY-' in text:
+ cmdline.extend (['-pubin'])
+ else:
+ raise Exception('Unknown key format "%s" !' % in_key)
+
+ if pub_key_file:
+ cmdline.extend (['-out', '%s' % pub_key_file])
+ capture = False
+ else:
+ capture = True
+
+ output = run_process (cmdline, capture_out = capture)
+ if not capture:
+ output = text = open(pub_key_file, 'r').read()
+ data = output.replace('\r', '')
+ data = data.replace('\n', '')
+ data = data.replace(' ', '')
+
+ # Extract the modulus
+ if is_prv_key:
+ match = re.search('modulus(.*)publicExponent:\s+(\d+)\s+', data)
+ else:
+ match = re.search('Modulus(?:.*?):(.*)Exponent:\s+(\d+)\s+', data)
+ if not match:
+ raise Exception('Public key not found!')
+ modulus = match.group(1).replace(':', '')
+ exponent = int(match.group(2))
+
+ mod = bytearray.fromhex(modulus)
+ # Remove the '00' from the front if the MSB is 1
+ if mod[0] == 0 and (mod[1] & 0x80):
+ mod = mod[1:]
+ exp = bytearray.fromhex('{:08x}'.format(exponent))
+
+ keydata = mod + exp
+
+ return keydata
+
diff --git a/core/abl_executable.mk b/core/abl_executable.mk
new file mode 100755
index 0000000..3fa5f97
--- /dev/null
+++ b/core/abl_executable.mk
@@ -0,0 +1,55 @@
+ifeq ($(strip $(LOCAL_MODULE_CLASS)),)
+LOCAL_MODULE_CLASS := ABL
+endif
+
+ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)
+LOCAL_MODULE_SUFFIX := .abl
+endif
+
+ifeq ($(strip $(LOCAL_MODULE_PATH)),)
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/abl
+endif
+
+LOCAL_CC := $(IAFW_CC)
+LOCAL_CLANG := true
+LOCAL_SANITIZE := never
+LOCAL_NO_DEFAULT_COMPILER_FLAGS := true
+LOCAL_CFLAGS += $(TARGET_IAFW_GLOBAL_CFLAGS)
+LOCAL_ASFLAGS += $(TARGET_IAFW_ASFLAGS)
+LOCAL_LDFLAGS := $(TARGET_IAFW_GLOBAL_LDFLAGS) -static \
+ -T $(TARGET_ABL_LDS) $(LOCAL_LDFLAGS)
+# If kernel enforce superpages the .text section gets aligned at
+# offset 0x200000 which break multiboot compliance.
+LOCAL_LDFLAGS += -z max-page-size=0x1000
+LOCAL_ABL_LDFALGS := $(LOCAL_LDFLAGS)
+LOCAL_OBJCOPY_FLAGS := $(TARGET_IAFW_GLOBAL_OBJCOPY_FLAGS) $(LOCAL_OBJCOPY_FLAGS)
+
+skip_build_from_source :=
+ifdef LOCAL_PREBUILT_MODULE_FILE
+ifeq (,$(call if-build-from-source,$(LOCAL_MODULE),$(LOCAL_PATH)))
+include $(BUILD_SYSTEM)/prebuilt_internal.mk
+skip_build_from_source := true
+endif
+endif
+
+ifndef skip_build_from_source
+
+ifdef LOCAL_IS_HOST_MODULE
+$(error This file should not be used to build host binaries. Included by (or near) $(lastword $(filter-out config/%,$(MAKEFILE_LIST))))
+endif
+
+WITHOUT_LIBCOMPILER_RT := true
+include $(BUILD_SYSTEM)/binary.mk
+WITHOUT_LIBCOMPILER_RT :=
+
+LIBPAYLOAD_CRT0_LIB := $(call intermediates-dir-for,STATIC_LIBRARIES,$(LIBPAYLOAD_CRT0))/$(LIBPAYLOAD_CRT0).a
+all_objects += $(LIBPAYLOAD_CRT0_LIB)
+
+$(LOCAL_BUILT_MODULE): PRIVATE_OBJCOPY_FLAGS := $(LOCAL_OBJCOPY_FLAGS)
+
+#$(LOCAL_BUILT_MODULE): $(all_objects) $(all_libraries) $(ABLIMAGE) $(ABLSIGN)
+$(LOCAL_BUILT_MODULE): $(all_objects) $(all_libraries)
+ $(call transform-o-to-sbl-executable,$(LOCAL_ABL_LDFALGS))
+
+endif # skip_build_from_source
+
diff --git a/core/definitions.mk b/core/definitions.mk
index 5a4a9f8..c31fe1d 100755
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -5,6 +5,7 @@ IAFW_BUILD_SYSTEM := $(INTEL_PATH_BUILD)/core
BUILD_EFI_STATIC_LIBRARY := $(IAFW_BUILD_SYSTEM)/iafw_static_library.mk
BUILD_IAFW_STATIC_LIBRARY := $(IAFW_BUILD_SYSTEM)/iafw_static_library.mk
BUILD_EFI_EXECUTABLE := $(IAFW_BUILD_SYSTEM)/efi_executable.mk
+BUILD_ABL_EXECUTABLE := $(IAFW_BUILD_SYSTEM)/abl_executable.mk
# Override default definition
CLEAR_VARS := $(IAFW_BUILD_SYSTEM)/clear_vars.mk
@@ -15,19 +16,36 @@ GENERATE_VERITY_KEY := $(HOST_OUT_EXECUTABLES)/generate_verity_key$(HOST_EXECUTA
OPENSSL := openssl
SBSIGN := sbsign
MKDOSFS := mkdosfs
-MKEXT2IMG := $(HOST_OUT_EXECUTABLES)/mkext2img
-DUMPEXT2IMG := $(HOST_OUT_EXECUTABLES)/dumpext2img
+#MKEXT2IMG := $(HOST_OUT_EXECUTABLES)/mkext2img
+#DUMPEXT2IMG := $(HOST_OUT_EXECUTABLES)/dumpext2img
MCOPY := mcopy
SESL := sign-efi-sig-list$(HOST_EXECUTABLE_SUFFIX)
CTESL := cert-to-efi-sig-list$(HOST_EXECUTABLE_SUFFIX)
IASL := $(INTEL_PATH_BUILD)/acpi-tools/linux64/bin/iasl
+# Generation
+KF4ABL_SYMBOLS_ZIP := $(PRODUCT_OUT)/kf4abl_symbols.zip
+FB4ABL_SYMBOLS_ZIP := $(PRODUCT_OUT)/fb4abl_symbols.zip
+
# Extra host tools we need built to use our *_from_target_files
# or sign_target_files_* scripts
INTEL_OTATOOLS := \
$(GENERATE_VERITY_KEY) \
$(AVBTOOL)
+ifeq ($(KERNELFLINGER_SUPPORT_NON_EFI_BOOT),true)
+# NON UEFI platform
+INTEL_OTATOOLS += \
+ # $(MKEXT2IMG) \
+ # $(DUMPEXT2IMG) \
+ $(FASTBOOT) \
+ $(IASL)
+endif
+
+ifeq ($(BOARD_USE_ABL),true)
+INTEL_OTATOOLS += abl_toolchain
+endif
+
otatools: $(INTEL_OTATOOLS)
# FIXME: may be unsafe to omit -no-sse
@@ -73,6 +91,7 @@ GNU_EFI_CRT0 := crt0-efi-$(TARGET_IAFW_ARCH_NAME)
LIBPAYLOAD_CRT0 := crt0-libpayload-$(TARGET_IAFW_ARCH_NAME)
TARGET_EFI_LDS := $(IAFW_BUILD_SYSTEM)/elf_$(TARGET_IAFW_ARCH_NAME)_efi.lds
+TARGET_ABL_LDS := $(IAFW_BUILD_SYSTEM)/elf_$(TARGET_IAFW_ARCH_NAME)_abl.lds
TARGET_IAFW_GLOBAL_OBJCOPY_FLAGS := \
-j .text -j .sdata -j .data \
-j .dynamic -j .dynsym -j .rel \
@@ -125,6 +144,32 @@ $(hide) $(IAFW_OBJCOPY) $(PRIVATE_OBJCOPY_FLAGS) \
$(hide) $(SBSIGN) --key $1 --cert $2 --output $@ $(@:.efi=.efiunsigned)
endef
+define transform-o-to-sbl-executable
+@echo "target ABL Executable: $(PRIVATE_MODULE) ($@)"
+$(hide) mkdir -p $(dir $@)
+$(hide) $(IAFW_LD) $1 \
+ --defsym=CONFIG_LP_BASE_ADDRESS=$(LIBPAYLOAD_BASE_ADDRESS) \
+ --defsym=CONFIG_LP_HEAP_SIZE=$(LIBPAYLOAD_HEAP_SIZE) \
+ --defsym=CONFIG_LP_STACK_SIZE=$(LIBPAYLOAD_STACK_SIZE) \
+ --whole-archive $(call module-built-files,$(LIBPAYLOAD_CRT0)) --no-whole-archive \
+ $(PRIVATE_ALL_OBJECTS) --start-group $(PRIVATE_ALL_STATIC_LIBRARIES) --end-group $(IAFW_LIBCLANG) \
+ -Map $(@:.abl=.map) -o $(@:.abl=.sym.elf)
+$(hide)$(IAFW_STRIP) --strip-all $(@:.abl=.sym.elf) -o $(@:.abl=.elf)
+
+$(hide) cp $(@:.abl=.elf) $@
+
+
+python3 $(INTEL_PATH_BUILD)/GenContainer.py create -t NORMAL -cl ELF1:$@ -k $(INTEL_PATH_BUILD)/testkeys/OS1_TestKey_Priv_RSA2048.pem -o $(PRODUCT_OUT)/sbl_os
+
+
+$(hide) if [ "$(PRIVATE_MODULE:debug=)" = fb4abl-user ]; then \
+ zip -juy $(FB4ABL_SYMBOLS_ZIP) $(@:.abl=.map) $(@:.abl=.sym.elf); \
+ zip -juy $(FB4ABL_SYMBOLS_ZIP) $@; \
+elif [ "$(PRIVATE_MODULE:debug=)" = kf4abl-user ]; then \
+ zip -juy $(KF4ABL_SYMBOLS_ZIP) $(@:.abl=.map) $(@:.abl=.sym.elf); \
+fi
+endef
+
# Hook up the prebuilts generation mechanism
include $(INTEL_PATH_COMMON)/external/external.mk
diff --git a/core/elf_ia32_abl.lds b/core/elf_ia32_abl.lds
new file mode 100644
index 0000000..bcb2165
--- /dev/null
+++ b/core/elf_ia32_abl.lds
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2008 Advanced Micro Devices, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+OUTPUT_FORMAT(elf32-i386)
+OUTPUT_ARCH(i386)
+
+ENTRY(_entry)
+
+SECTIONS
+{
+ . = CONFIG_LP_BASE_ADDRESS;
+
+ . = ALIGN(16);
+ _start = .;
+
+ .text : {
+ *(.text._entry)
+ *(.text)
+ *(.text.*)
+ }
+
+ .rodata : {
+ *(.rodata)
+ *(.rodata.*)
+ }
+
+ .data : {
+ *(.data)
+ *(.data.*)
+ }
+
+ _edata = .;
+
+ .bss : {
+ *(.sbss)
+ *(.sbss.*)
+ *(.bss)
+ *(.bss.*)
+ *(COMMON)
+
+ /* Stack and heap */
+
+ . = ALIGN(16);
+ _heap = .;
+ . += CONFIG_LP_HEAP_SIZE;
+ . = ALIGN(16);
+ _eheap = .;
+
+ _estack = .;
+ . += CONFIG_LP_STACK_SIZE;
+ . = ALIGN(16);
+ _stack = .;
+ }
+
+ _end = .;
+
+ /DISCARD/ : {
+ *(.comment)
+ *(.note*)
+ }
+}
diff --git a/core/elf_x86_64_abl.lds b/core/elf_x86_64_abl.lds
new file mode 100644
index 0000000..1eb9e25
--- /dev/null
+++ b/core/elf_x86_64_abl.lds
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2008 Advanced Micro Devices, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
+OUTPUT_ARCH(i386:x86-64)
+
+ENTRY(_entry)
+
+SECTIONS
+{
+ . = CONFIG_LP_BASE_ADDRESS;
+
+ . = ALIGN(16);
+ _start = .;
+
+ .text : {
+ *(.text._entry)
+ *(.text)
+ *(.text.*)
+ }
+
+ .rodata : {
+ *(.rodata)
+ *(.rodata.*)
+ }
+
+ .data : {
+ *(.data)
+ *(.data.*)
+ }
+
+ _edata = .;
+
+ .bss : {
+ *(.sbss)
+ *(.sbss.*)
+ *(.bss)
+ *(.bss.*)
+ *(COMMON)
+
+ /* Stack and heap */
+
+ . = ALIGN(16);
+ _heap = .;
+ . += CONFIG_LP_HEAP_SIZE;
+ . = ALIGN(16);
+ _eheap = .;
+
+ _estack = .;
+ . += CONFIG_LP_STACK_SIZE;
+ . = ALIGN(32);
+ _stack = .;
+ }
+
+ _end = .;
+
+ /DISCARD/ : {
+ *(.comment)
+ *(.note*)
+ }
+}
diff --git a/testkeys/OS1_TestKey_Priv_RSA2048.pem b/testkeys/OS1_TestKey_Priv_RSA2048.pem
new file mode 100644
index 0000000..3aab5f1
--- /dev/null
+++ b/testkeys/OS1_TestKey_Priv_RSA2048.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpgIBAAKCAQEAzLiDFGCKzj/TfsxpIxtQ1f2YI/5/7tmyPj23Zy/7bbnqrsfR
+CQyBbDElte1R9cCAYhK9aN5JTuparnsh3eFIhq9DeuyFtQtjo54t3AQ6cUVx+wyP
+8D8gFjC0TlIhin0xW/xwmMptXdxjxv44mEfI1ioIvGHSMNtic0jA9WRimlL+qIJ/
+P4fWisU24x5yoPDjGM5C3h5LWvcqGi1HGtyH0I6l74NcPahJl/dVV0di1s6eURXT
+gCSwJx2zL/H4Xf/qy5uh3fGKod4ay8MwaHOlVHc2CxnkoBjoj9mEUhNasVOiOmjY
+W7yxi9WDdSlDoZDrCyiGHtKmLnTBuTLJlU6pcQIDAQABAoIBAQCEm/CsvmyrdUS2
+mgpwpz0RoJdwmWadfX6sOqYWvUoMpaWTWfPZ/LPJNXzL/9Jbcjq3TJRM3dB2we/D
+nhct9sRYGieH9LYXtCzyy3/BSSviO629hUGnyfwq45moMiNv5fCXOUpmbpmxdxVa
+zUozuiwqLkhCXsscwr9fFf2H92K3u2md8xSeqEqgcz5eDWRryAB12jndQICJvpvb
+mS9yvhfZnZS3s66Pqfcf4MEUeKkxLgs7F0gTPxKSD5wwurjWd9p48b10YphH9B3S
+vjKIvrbwd4rRQdDIeHspJ+evhhXU/MIjsx3DwFJgCOOIghwqriEAd/BNhurS0qXS
+YrlhhekBAoGBAPfNk7E3lkRPC7laz24Co9effrPOSHfdAO6fRjLqu/5Cg6MSxT6K
+d/JC6hlv8kuFRr2hVC8GzaopmNLzgTq5/CEFDADdnirJ9kJDj+toicQL3+ntN2sT
+usk1I2Ym+RnuJFw5himq4/t7MP1BOlfRd6MhEuupWSvFcvn1IUu9qv7JAoGBANN+
+HBCq+XN/iuUgOfgj+p7kdz18j2GHeIa5tVirQeGsBGl3gLzVH9l0sxxeoEqdDzIv
+j3Nxstero1PfMZsRTfSomd2zsRshYH17FAAjue28MJGbBcCGEHpg5GCmeP5Wemqw
+dRDreRTPwroOLGcFILZTiYzSTFhQ8vdHW4upkmFpAoGBAOMPUPx6lcsjrvd5eUYx
+TxXdhIiXRRnnvNnpwbYYFohFRD9hjLDgykWHAOCsDhUbGxXZRL0Oe9cm5GyvSUOZ
+dah/6l7EsxA8HNTc+XvYPxwPUW4Uucn4uxdxBG2u2mPjLRgAcamud9ZQLDRy11Xu
+jrRAMwkTFK0h8FePQKIZOhzZAoGBAJqxY1XNuSgB4lY1fDFEH8yYz57pvPZZYI8q
+xrpUiZvHdnKuNjvop6Mm+NuHWzc4KiVLJGIMiAO4R37RgjcqMRsDN4gyJxjxEOK3
+Y6m2eAir3dqiyVJIvUTGGkWXfmiKWIAxEW+OzMGsNYJhW5j+5y51a+bU64Ktswmr
+6An/vlIBAoGBAO44DdQRrqmndVjXu/+yMLYmRxG7CCzUyxgxUeaR2fco1Qss/2Me
+fgk3T85tnFM4rq3gvSL4oyjUgMZJ8vtnWl+6IppSru9zzmEZ+aqT+GsHWsK6Tkcv
+SvZswG++MVPfOLAveappYkaSLhbbBi3ARfM7G90RG0SK3JLQNXVAX2Q1
+-----END RSA PRIVATE KEY-----