#!/usr/bin/env python3 # Utility to generate image blobs for Corvette Bootloader assisted Wired updates 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, FLASH_KEY #****************************************************************************** # # Generate the image blob as per command line parameters # #****************************************************************************** def process(appFile, imagetype, loadaddress, authalgo, encalgo, authKeyIdx, encKeyIdx, optionsVal, maxSize, output, 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() # Make sure it is page multiple if ((maxSize & (FLASH_PAGE_SIZE - 1)) != 0): am_print ("split needs to be multiple of flash page size", level=AM_PRINT_LEVEL_ERROR) return filenames = keyFile.split('.') keys = importlib.import_module(filenames[0]) if (encalgo != 0): 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 hdr_length = AM_WU_IMAGEHDR_SIZE; #fixed header length am_print("Header Size = ", hex(hdr_length)) orig_app_length = (len(app_binarray)) if (encalgo != 0): block_size = keySize 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,")") if (app_length + hdr_length > maxSize): am_print("Image size bigger than max - Creating Split image") start = 0 # now output all three binary arrays in the proper order output = output + '.bin' out = open(output, mode = 'wb') while (start < app_length): #generate mutable byte array for the header hdr_binarray = bytearray([0x00]*hdr_length); if (app_length - start > maxSize): end = start + maxSize else: end = app_length if (imagetype == AM_SECBOOT_WIRED_IMAGETYPE_INFO0_NOOTA): key = keys.INFO_KEY # word offset fill_word(hdr_binarray, AM_WU_IMAGEHDR_OFFSET_ADDR, loadaddress>>2) else: key = keys.FLASH_KEY # load address fill_word(hdr_binarray, AM_WU_IMAGEHDR_OFFSET_ADDR, loadaddress) if (loadaddress & (FLASH_PAGE_SIZE - 1)): am_print("WARNING!!! - load address is not page aligned", level=AM_PRINT_LEVEL_ERROR) # Create imageType & options hdr_binarray[AM_WU_IMAGEHDR_OFFSET_IMAGETYPE] = imagetype # Set the options only for the first block if (start == 0): hdr_binarray[AM_WU_IMAGEHDR_OFFSET_OPTIONS] = optionsVal else: hdr_binarray[AM_WU_IMAGEHDR_OFFSET_OPTIONS] = 0 # Create Info0 Update Blob for wired update fill_word(hdr_binarray, AM_WU_IMAGEHDR_OFFSET_KEY, key) # update size fill_word(hdr_binarray, AM_WU_IMAGEHDR_OFFSET_SIZE, end-start) w0 = ((authalgo & 0xf) | ((authKeyIdx << 8) & 0xf00) | ((encalgo << 16) & 0xf0000) | ((encKeyIdx << 24) & 0x0f000000)) fill_word(hdr_binarray, 0, w0) if (encalgo != 0): keyIdx = encKeyIdx - keys.minAesKeyIdx ivValAes = os.urandom(AM_SECBOOT_AESCBC_BLOCK_SIZE_BYTES) am_print("Initialization Vector") am_print([hex(n) for n in ivValAes]) keyAes = os.urandom(keySize) am_print("AES Key used for encryption") am_print([hex(keyAes[n]) for n in range (0, keySize)]) # Encrypted Part - after security header enc_binarray = encrypt_app_aes((hdr_binarray[AM_WU_IMAGEHDR_START_ENCRYPT:hdr_length] + app_binarray[start:end]), keyAes, ivValAes) # am_print("Key used for encrypting AES Key") # am_print([hex(keys.keyTblAes[keyIdx*AM_SECBOOT_KEYIDX_BYTES + n]) for n in range (0, keySize)]) # Encrypted Key enc_key = encrypt_app_aes(keyAes, keys.keyTblAes[keyIdx*AM_SECBOOT_KEYIDX_BYTES:(keyIdx*AM_SECBOOT_KEYIDX_BYTES + 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_WU_IMAGEHDR_OFFSET_IV + x] = ivValAes[x] # Fill up the Encrypted Key for x in range(0, keySize): hdr_binarray[AM_WU_IMAGEHDR_OFFSET_KEK + x] = enc_key[x] else: enc_binarray = hdr_binarray[AM_WU_IMAGEHDR_START_ENCRYPT:hdr_length] + app_binarray[start:end] if (authalgo != 0): # Authentication needed keyIdx = authKeyIdx - keys.minHmacKeyIdx # am_print("Key used for HMAC") # am_print([hex(keys.keyTblHmac[keyIdx*AM_SECBOOT_KEYIDX_BYTES + n]) for n in range (0, AM_HMAC_SIG_SIZE)]) # Initialize the HMAC - Sign is computed on image following the signature sig = compute_hmac(keys.keyTblHmac[keyIdx*AM_SECBOOT_KEYIDX_BYTES:(keyIdx*AM_SECBOOT_KEYIDX_BYTES+AM_HMAC_SIG_SIZE)], hdr_binarray[AM_WU_IMAGEHDR_START_HMAC:AM_WU_IMAGEHDR_START_ENCRYPT] + enc_binarray) am_print("HMAC") am_print([hex(n) for n in sig]) # Fill up the HMAC for x in range(0, AM_HMAC_SIG_SIZE): hdr_binarray[AM_WU_IMAGEHDR_OFFSET_SIG + x] = sig[x] am_print("Writing to file ", output) am_print("Image from ", str(hex(start)), " to ", str(hex(end)), " will be loaded at", str(hex(loadaddress))) out.write(hdr_binarray[0:AM_WU_IMAGEHDR_START_ENCRYPT]) out.write(enc_binarray) # Reset start for next chunk start = end loadaddress = loadaddress + maxSize def parse_arguments(): parser = argparse.ArgumentParser(description = 'Generate Corvette Wired Update Blob') parser.add_argument('--load-address', dest='loadaddress', type=auto_int, default=hex(0x60000), help='Load address of the binary - Where in flash the blob will be stored (could be different than install address of binary within).') parser.add_argument('--bin', dest='appFile', type=argparse.FileType('rb'), help='binary file (blah.bin)') parser.add_argument('-i', dest = 'imagetype', default=AM_SECBOOT_WIRED_IMAGETYPE_MAIN, type=int, choices = [ (AM_SECBOOT_WIRED_IMAGETYPE_SBL), (AM_SECBOOT_WIRED_IMAGETYPE_AM3P), (AM_SECBOOT_WIRED_IMAGETYPE_PATCH), (AM_SECBOOT_WIRED_IMAGETYPE_MAIN), (AM_SECBOOT_WIRED_IMAGETYPE_CHILD), (AM_SECBOOT_WIRED_IMAGETYPE_CUSTPATCH), (AM_SECBOOT_WIRED_IMAGETYPE_NONSECURE), (AM_SECBOOT_WIRED_IMAGETYPE_INFO0), (AM_SECBOOT_WIRED_IMAGETYPE_INFO0_NOOTA) ], help = 'ImageType (' + str(AM_SECBOOT_WIRED_IMAGETYPE_SBL) + ': SBL, ' + str(AM_SECBOOT_WIRED_IMAGETYPE_AM3P) + ': AM3P, ' + str(AM_SECBOOT_WIRED_IMAGETYPE_PATCH) + ': Patch, ' + str(AM_SECBOOT_WIRED_IMAGETYPE_MAIN) + ': Main, ' + str(AM_SECBOOT_WIRED_IMAGETYPE_CHILD) + ': Child, ' + str(AM_SECBOOT_WIRED_IMAGETYPE_CUSTPATCH) + ': CustOTA, ' + str(AM_SECBOOT_WIRED_IMAGETYPE_NONSECURE) + ': NonSecure, ' + str(AM_SECBOOT_WIRED_IMAGETYPE_INFO0) + ': Info0, ' + str(AM_SECBOOT_WIRED_IMAGETYPE_INFO0_NOOTA) + ': Info0-NoOTA) ' '- default[Main]') parser.add_argument('--options', dest = 'options', type=auto_int, default=0x1, help = 'Options (16b hex value) - bit0 instructs to perform OTA of the image after wired download (set to 0 if only downloading & skipping OTA flow)') parser.add_argument('-o', dest = 'output', default='wuimage', 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('--split', dest='split', type=auto_int, default=hex(MAX_DOWNLOAD_SIZE), help='Specify the max block size if the image will be downloaded in pieces') 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() return args #****************************************************************************** # # Main function. # #****************************************************************************** def main(): # Read the arguments. args = parse_arguments() am_set_print_level(args.loglevel) process(args.appFile, args.imagetype, args.loadaddress, args.authalgo, args.encalgo, args.authkey, args.kek, args.options, args.split, args.output, args.keyFile) if __name__ == '__main__': main()