#!/usr/bin/env python3 # Utility to create image blobs for Corvette Secure Boot import argparse import sys from Crypto.Cipher import AES import array import hashlib import hmac import os import binascii import importlib from am_defines import * #from keys_info import keyTblAes, keyTblHmac, minAesKeyIdx, maxAesKeyIdx, minHmacKeyIdx, maxHmacKeyIdx, INFO_KEY #****************************************************************************** # # Generate the image blob as per command line parameters # #****************************************************************************** def process(loadaddress, appFile, magicNum, crcI, crcB, authI, authB, protection, authKeyIdx, output, encKeyIdx, version, erasePrev, child0, child1, authalgo, encalgo, keyFile): app_binarray = bytearray() # Open the file, and read it into an array of integers. with appFile as f_app: app_binarray.extend(f_app.read()) f_app.close() filenames = keyFile.split('.') keys = importlib.import_module(filenames[0]) encVal = 0 if (encalgo != 0): encVal = 1 if ((encKeyIdx < keys.minAesKeyIdx) or (encKeyIdx > keys.maxAesKeyIdx)): am_print("Invalid encKey Idx ", encKeyIdx, level=AM_PRINT_LEVEL_ERROR); return if (encalgo == 2): if (encKeyIdx & 0x1): am_print("Invalid encKey Idx ", encKeyIdx, level=AM_PRINT_LEVEL_ERROR); return keySize = 32 else: keySize = 16 if (authalgo != 0): if ((authKeyIdx < keys.minHmacKeyIdx) or (authKeyIdx > keys.maxHmacKeyIdx) or (authKeyIdx & 0x1)): am_print("Invalid authKey Idx ", authKeyIdx, level=AM_PRINT_LEVEL_ERROR); return if (magicNum == AM_IMAGE_MAGIC_MAIN): hdr_length = AM_IMAGEHDR_SIZE_MAIN; #fixed header length elif ((magicNum == AM_IMAGE_MAGIC_CHILD) or (magicNum == AM_IMAGE_MAGIC_CUSTPATCH) or (magicNum == AM_IMAGE_MAGIC_NONSECURE) or (magicNum == AM_IMAGE_MAGIC_INFO0)): hdr_length = AM_IMAGEHDR_SIZE_AUX; #fixed header length else: am_print("magic number", hex(magicNum), " not supported", level=AM_PRINT_LEVEL_ERROR) return am_print("Header Size = ", hex(hdr_length)) #generate mutable byte array for the header hdr_binarray = bytearray([0x00]*hdr_length); orig_app_length = (len(app_binarray)) am_print("original app_size ",hex(orig_app_length), "(",orig_app_length,")") am_print("load_address ",hex(loadaddress), "(",loadaddress,")") if (loadaddress & 0x3): am_print("load address needs to be word aligned", level=AM_PRINT_LEVEL_ERROR) return if (loadaddress & (FLASH_PAGE_SIZE - 1)): am_print("WARNING!!! - load address is not page aligned", level=AM_PRINT_LEVEL_ERROR) if (magicNum == AM_IMAGE_MAGIC_INFO0): if (orig_app_length & 0x3): am_print("INFO0 blob length needs to be multiple of 4", level=AM_PRINT_LEVEL_ERROR) return if ((loadaddress + orig_app_length) > INFO_SIZE_BYTES): am_print("INFO0 Offset and length exceed size", level=AM_PRINT_LEVEL_ERROR) return if (encVal == 1): block_size = AM_SECBOOT_AESCBC_BLOCK_SIZE_BYTES app_binarray = pad_to_block_size(app_binarray, block_size, 1) else: # Add Padding app_binarray = pad_to_block_size(app_binarray, 4, 0) app_length = (len(app_binarray)) am_print("app_size ",hex(app_length), "(",app_length,")") # Create Image blobs # w0 blobLen = hdr_length + app_length w0 = (magicNum << 24) | ((encVal & 0x1) << 23) | blobLen am_print("w0 =", hex(w0)) fill_word(hdr_binarray, 0, w0) # w2 securityVal = ((authI << 1) | crcI) << 4 | (authB << 1) | crcB am_print("Security Value ", hex(securityVal)) w2 = ((securityVal << 24) & 0xff000000) | ((authalgo) & 0xf) | ((authKeyIdx << 4) & 0xf0) | ((encalgo << 8) & 0xf00) | ((encKeyIdx << 12) & 0xf000) fill_word(hdr_binarray, 8, w2) am_print("w2 = ",hex(w2)) if (magicNum == AM_IMAGE_MAGIC_INFO0): # Insert the INFO0 size and offset addrWord = ((orig_app_length>>2) << 16) | ((loadaddress>>2) & 0xFFFF) versionKeyWord = keys.INFO_KEY else: # Insert the application binary load address. addrWord = loadaddress | (protection & 0x3) # Initialize versionKeyWord versionKeyWord = (version & 0x7FFF) | ((erasePrev & 0x1) << 15) am_print("addrWord = ",hex(addrWord)) fill_word(hdr_binarray, AM_IMAGEHDR_OFFSET_ADDR, addrWord) am_print("versionKeyWord = ",hex(versionKeyWord)) fill_word(hdr_binarray, AM_IMAGEHDR_OFFSET_VERKEY, versionKeyWord) # Initialize child (Child Ptr/ Feature key) am_print("child0/feature = ",hex(child0)) fill_word(hdr_binarray, AM_IMAGEHDR_OFFSET_CHILDPTR, child0) am_print("child1 = ",hex(child1)) fill_word(hdr_binarray, AM_IMAGEHDR_OFFSET_CHILDPTR + 4, child1) authKeyIdx = authKeyIdx - keys.minHmacKeyIdx if (authB != 0): # Authentication needed am_print("Boot Authentication Enabled") # am_print("Key used for HMAC") # am_print([hex(keys.keyTblHmac[authKeyIdx*AM_SECBOOT_KEYIDX_BYTES + n]) for n in range (0, AM_HMAC_SIG_SIZE)]) # Initialize the clear image HMAC sigClr = compute_hmac(keys.keyTblHmac[authKeyIdx*AM_SECBOOT_KEYIDX_BYTES:(authKeyIdx*AM_SECBOOT_KEYIDX_BYTES+AM_HMAC_SIG_SIZE)], (hdr_binarray[AM_IMAGEHDR_START_HMAC:hdr_length] + app_binarray)) am_print("HMAC Clear") am_print([hex(n) for n in sigClr]) # Fill up the HMAC for x in range(0, AM_HMAC_SIG_SIZE): hdr_binarray[AM_IMAGEHDR_OFFSET_SIGCLR + x] = sigClr[x] # All the header fields part of the encryption are now final if (encVal == 1): am_print("Encryption Enabled") encKeyIdx = encKeyIdx - keys.minAesKeyIdx ivValAes = os.urandom(AM_SECBOOT_AESCBC_BLOCK_SIZE_BYTES) am_print("Initialization Vector") am_print([hex(ivValAes[n]) for n in range (0, AM_SECBOOT_AESCBC_BLOCK_SIZE_BYTES)]) keyAes = os.urandom(keySize) am_print("AES Key used for encryption") am_print([hex(keyAes[n]) for n in range (0, keySize)]) # Encrypted Part am_print("Encrypting blob of size " , (hdr_length - AM_IMAGEHDR_START_ENCRYPT + app_length)) enc_binarray = encrypt_app_aes((hdr_binarray[AM_IMAGEHDR_START_ENCRYPT:hdr_length] + app_binarray), keyAes, ivValAes) # am_print("Key used for encrypting AES Key") # am_print([hex(keys.keyTblAes[encKeyIdx*keySize + n]) for n in range (0, keySize)]) # Encrypted Key enc_key = encrypt_app_aes(keyAes, keys.keyTblAes[encKeyIdx*keySize:encKeyIdx*keySize + keySize], ivVal0) am_print("Encrypted Key") am_print([hex(enc_key[n]) for n in range (0, keySize)]) # Fill up the IV for x in range(0, AM_SECBOOT_AESCBC_BLOCK_SIZE_BYTES): hdr_binarray[AM_IMAGEHDR_OFFSET_IV + x] = ivValAes[x] # Fill up the Encrypted Key for x in range(0, keySize): hdr_binarray[AM_IMAGEHDR_OFFSET_KEK + x] = enc_key[x] else: enc_binarray = hdr_binarray[AM_IMAGEHDR_START_ENCRYPT:hdr_length] + app_binarray if (authI != 0): # Install Authentication needed am_print("Install Authentication Enabled") # am_print("Key used for HMAC") # am_print([hex(keys.keyTblHmac[authKeyIdx*AM_SECBOOT_KEYIDX_BYTES + n]) for n in range (0, AM_HMAC_SIG_SIZE)]) # Initialize the top level HMAC sig = compute_hmac(keys.keyTblHmac[authKeyIdx*AM_SECBOOT_KEYIDX_BYTES:(authKeyIdx*AM_SECBOOT_KEYIDX_BYTES+AM_HMAC_SIG_SIZE)], (hdr_binarray[AM_IMAGEHDR_START_HMAC_INST:AM_IMAGEHDR_START_ENCRYPT] + enc_binarray)) am_print("Generated Signature") am_print([hex(n) for n in sig]) # Fill up the HMAC for x in range(0, AM_HMAC_SIG_SIZE): hdr_binarray[AM_IMAGEHDR_OFFSET_SIG + x] = sig[x] # compute the CRC for the blob - this is done on a clear image crc = crc32(hdr_binarray[AM_IMAGEHDR_START_CRC:hdr_length] + app_binarray) am_print("crc = ",hex(crc)); w1 = crc fill_word(hdr_binarray, AM_IMAGEHDR_OFFSET_CRC, w1) # now output all three binary arrays in the proper order output = output + '.bin' am_print("Writing to file ", output) with open(output, mode = 'wb') as out: out.write(hdr_binarray[0:AM_IMAGEHDR_START_ENCRYPT]) out.write(enc_binarray) def parse_arguments(): parser = argparse.ArgumentParser(description = 'Generate Corvette Image Blob') parser.add_argument('--bin', dest='appFile', type=argparse.FileType('rb'), help='binary file (blah.bin)') parser.add_argument('--load-address', dest='loadaddress', type=auto_int, default=hex(AM_SECBOOT_DEFAULT_NONSECURE_MAIN), help='Load address of the binary.') parser.add_argument('--magic-num', dest='magic_num', default=hex(AM_IMAGE_MAGIC_NONSECURE), type=lambda x: x.lower(), # type = str.lower, choices = [ hex(AM_IMAGE_MAGIC_MAIN), hex(AM_IMAGE_MAGIC_CHILD), hex(AM_IMAGE_MAGIC_CUSTPATCH), hex(AM_IMAGE_MAGIC_NONSECURE), hex(AM_IMAGE_MAGIC_INFO0) ], help = 'Magic Num (' + str(hex(AM_IMAGE_MAGIC_MAIN)) + ': Main, ' + str(hex(AM_IMAGE_MAGIC_CHILD)) + ': Child, ' + str(hex(AM_IMAGE_MAGIC_CUSTPATCH)) + ': CustOTA, ' + str(hex(AM_IMAGE_MAGIC_NONSECURE)) + ': NonSecure, ' + str(hex(AM_IMAGE_MAGIC_INFO0)) + ': Info0) ' '- default[Main]' ) parser.add_argument('-o', dest = 'output', default='outimage', help = 'Output filename (without the extension)') parser.add_argument('--authkey', dest = 'authkey', type=auto_int, default=(AM_SECBOOT_MIN_KEYIDX_INFO0), choices = range(AM_SECBOOT_MIN_KEYIDX_INFO0, AM_SECBOOT_MAX_KEYIDX_INFO0 + 1), help = 'Authentication Key Idx? (' + str(AM_SECBOOT_MIN_KEYIDX_INFO0) + ' to ' + str(AM_SECBOOT_MAX_KEYIDX_INFO0) + ')') parser.add_argument('--kek', dest = 'kek', type=auto_int, default=(AM_SECBOOT_MIN_KEYIDX_INFO0), choices = range(AM_SECBOOT_MIN_KEYIDX_INFO0, AM_SECBOOT_MAX_KEYIDX_INFO0 + 1), help = 'KEK Index? (' + str(AM_SECBOOT_MIN_KEYIDX_INFO0) + ' to ' + str(AM_SECBOOT_MAX_KEYIDX_INFO0) + ')') parser.add_argument('--authalgo', dest = 'authalgo', type=auto_int, default=0, choices=range(0, AM_SECBOOT_AUTH_ALGO_MAX+1), help = helpAuthAlgo) parser.add_argument('--encalgo', dest = 'encalgo', type=auto_int, default=0, choices = range(0, AM_SECBOOT_ENC_ALGO_MAX+1), help = helpEncAlgo) parser.add_argument('--child0', dest = 'child0', type=auto_int, default=hex(0xFFFFFFFF), help = 'child (blobPtr#0 for Main / feature key for AM3P)') parser.add_argument('--child1', dest = 'child1', type=auto_int, default=hex(0xFFFFFFFF), help = 'child (blobPtr#1 for Main)') parser.add_argument('--version', dest = 'version', type=auto_int, default=0, help = 'version (15 bit)') parser.add_argument('--crcI', dest = 'crcI', type=auto_int, default=1, choices=[0,1], help = 'Install CRC check enabled (Default = Y)?') parser.add_argument('--crcB', dest = 'crcB', type=auto_int, default=0, choices=[0,1], help = 'Boot CRC check enabled (Default = N)?') parser.add_argument('--authI', dest = 'authI', type=auto_int, default=0, choices=[0,1], help = 'Install Authentication check enabled (Default = N)?') parser.add_argument('--authB', dest = 'authB', type=auto_int, default=0, choices=[0,1], help = 'Boot Authentication check enabled (Default = N)?') parser.add_argument('--erasePrev', dest = 'erasePrev', type=auto_int, default=0, choices=[0,1], help = 'erasePrev (Valid only for main)') parser.add_argument('-p', dest = 'protection', type=auto_int, default=0, choices = [0x0, 0x1, 0x2, 0x3], help = 'protection info 2 bit C W') parser.add_argument('-k', type=str, dest='keyFile', nargs='?', default='keys_info.py', help='key file in specified format [default = keys_info.py]') parser.add_argument('--loglevel', dest='loglevel', type=auto_int, default=AM_PRINT_LEVEL_INFO, choices = range(AM_PRINT_LEVEL_MIN, AM_PRINT_LEVEL_MAX+1), help=helpPrintLevel) args = parser.parse_args() args.magic_num = int(args.magic_num, 16) return args #****************************************************************************** # # Main function. # #****************************************************************************** def main(): # Read the arguments. args = parse_arguments() am_set_print_level(args.loglevel) process(args.loadaddress, args.appFile, args.magic_num, args.crcI, args.crcB, args.authI, args.authB, args.protection, args.authkey, args.output, args.kek, args.version, args.erasePrev, args.child0, args.child1, args.authalgo, args.encalgo, args.keyFile) if __name__ == '__main__': main()