358 lines
13 KiB
Python
358 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
# Utility functioins
|
|
|
|
import sys
|
|
from Crypto.Cipher import AES
|
|
from Crypto.PublicKey import RSA
|
|
from Crypto.Signature import PKCS1_v1_5
|
|
from Crypto.Hash import SHA256
|
|
import array
|
|
import hashlib
|
|
import hmac
|
|
import os
|
|
import binascii
|
|
|
|
|
|
ivVal0 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
|
|
FLASH_PAGE_SIZE = 0x2000 # 8K
|
|
MAX_DOWNLOAD_SIZE = 0x48000 # 288K
|
|
AM_SECBOOT_DEFAULT_NONSECURE_MAIN = 0xC000
|
|
|
|
AM_SECBOOT_AESCBC_BLOCK_SIZE_WORDS = 4
|
|
AM_SECBOOT_AESCBC_BLOCK_SIZE_BYTES = 4*AM_SECBOOT_AESCBC_BLOCK_SIZE_WORDS
|
|
|
|
AM_SECBOOT_MIN_KEYIDX_INFO0 = 8 ## KeyIdx 8 - 15
|
|
AM_SECBOOT_MAX_KEYIDX_INFO0 = 15
|
|
AM_SECBOOT_MIN_KEYIDX_INFO1 = 0 ## KeyIdx 0 - 7
|
|
AM_SECBOOT_MAX_KEYIDX_INFO1 = 7
|
|
AM_SECBOOT_KEYIDX_BYTES = 16
|
|
|
|
# Encryption Algorithm
|
|
AM_SECBOOT_ENC_ALGO_NONE = 0
|
|
AM_SECBOOT_ENC_ALGO_AES128 = 1
|
|
AM_SECBOOT_ENC_ALGO_MAX = AM_SECBOOT_ENC_ALGO_AES128
|
|
# String constants
|
|
helpEncAlgo = 'Encryption Algo? (0(default) = none, 1 = AES128)'
|
|
|
|
# Authentication Algorithm
|
|
AM_SECBOOT_AUTH_ALGO_NONE = 0
|
|
AM_SECBOOT_AUTH_ALGO_SHA256HMAC = 1
|
|
AM_SECBOOT_AUTH_ALGO_MAX = AM_SECBOOT_AUTH_ALGO_SHA256HMAC
|
|
# String constants
|
|
helpAuthAlgo = 'Authentication Algo? (0(default) = none, 1 = SHA256)'
|
|
|
|
|
|
FLASH_INVALID = 0xFFFFFFFF
|
|
|
|
# KeyWrap Mode
|
|
AM_SECBOOT_KEYWRAP_NONE = 0
|
|
AM_SECBOOT_KEYWRAP_XOR = 1
|
|
AM_SECBOOT_KEYWRAP_AES128 = 2
|
|
AM_SECBOOT_KEYWRAP_MAX = AM_SECBOOT_KEYWRAP_AES128
|
|
|
|
#******************************************************************************
|
|
#
|
|
# Magic Numbers
|
|
#
|
|
#******************************************************************************
|
|
AM_IMAGE_MAGIC_MAIN = 0xC0
|
|
AM_IMAGE_MAGIC_CHILD = 0xCC
|
|
AM_IMAGE_MAGIC_NONSECURE = 0xCB
|
|
AM_IMAGE_MAGIC_INFO0 = 0xCF
|
|
|
|
# Dummy for creating images for customer - not understood by SBL
|
|
# This could be any value from the definition:
|
|
# #define AM_IMAGE_MAGIC_CUST(x) ((((x) & 0xF0) == 0xC0) && ((x) != 0xC0) && ((x) != 0xCC) && ((x) != 0xCB) && ((x) != 0xCF))
|
|
AM_IMAGE_MAGIC_CUSTPATCH = 0xC1
|
|
|
|
#******************************************************************************
|
|
#
|
|
# Image Types
|
|
#
|
|
#******************************************************************************
|
|
AM_SECBOOT_WIRED_IMAGETYPE_SBL = 0
|
|
AM_SECBOOT_WIRED_IMAGETYPE_AM3P = 1
|
|
AM_SECBOOT_WIRED_IMAGETYPE_PATCH = 2
|
|
AM_SECBOOT_WIRED_IMAGETYPE_MAIN = 3
|
|
AM_SECBOOT_WIRED_IMAGETYPE_CHILD = 4
|
|
AM_SECBOOT_WIRED_IMAGETYPE_CUSTPATCH = 5
|
|
AM_SECBOOT_WIRED_IMAGETYPE_NONSECURE = 6
|
|
AM_SECBOOT_WIRED_IMAGETYPE_INFO0 = 7
|
|
AM_SECBOOT_WIRED_IMAGETYPE_INFO0_NOOTA = 32
|
|
AM_SECBOOT_WIRED_IMAGETYPE_INVALID = 0xFF
|
|
|
|
|
|
#******************************************************************************
|
|
#
|
|
# Wired Message Types
|
|
#
|
|
#******************************************************************************
|
|
AM_SECBOOT_WIRED_MSGTYPE_HELLO = 0
|
|
AM_SECBOOT_WIRED_MSGTYPE_STATUS = 1
|
|
AM_SECBOOT_WIRED_MSGTYPE_OTADESC = 2
|
|
AM_SECBOOT_WIRED_MSGTYPE_UPDATE = 3
|
|
AM_SECBOOT_WIRED_MSGTYPE_ABORT = 4
|
|
AM_SECBOOT_WIRED_MSGTYPE_RECOVER = 5
|
|
AM_SECBOOT_WIRED_MSGTYPE_RESET = 6
|
|
AM_SECBOOT_WIRED_MSGTYPE_ACK = 7
|
|
AM_SECBOOT_WIRED_MSGTYPE_DATA = 8
|
|
|
|
|
|
#******************************************************************************
|
|
#
|
|
# Wired Message ACK Status
|
|
#
|
|
#******************************************************************************
|
|
AM_SECBOOT_WIRED_ACK_STATUS_SUCCESS = 0
|
|
AM_SECBOOT_WIRED_ACK_STATUS_FAILURE = 1
|
|
AM_SECBOOT_WIRED_ACK_STATUS_INVALID_INFO0 = 2
|
|
AM_SECBOOT_WIRED_ACK_STATUS_CRC = 3
|
|
AM_SECBOOT_WIRED_ACK_STATUS_SEC = 4
|
|
AM_SECBOOT_WIRED_ACK_STATUS_MSG_TOO_BIG = 5
|
|
AM_SECBOOT_WIRED_ACK_STATUS_UNKNOWN_MSGTYPE = 6
|
|
AM_SECBOOT_WIRED_ACK_STATUS_INVALID_ADDR = 7
|
|
AM_SECBOOT_WIRED_ACK_STATUS_INVALID_OPERATION = 8
|
|
AM_SECBOOT_WIRED_ACK_STATUS_INVALID_PARAM = 9
|
|
AM_SECBOOT_WIRED_ACK_STATUS_SEQ = 10
|
|
AM_SECBOOT_WIRED_ACK_STATUS_TOO_MUCH_DATA = 11
|
|
|
|
#******************************************************************************
|
|
#
|
|
# Definitions related to Image Headers
|
|
#
|
|
#******************************************************************************
|
|
AM_HMAC_SIG_SIZE = 32
|
|
AM_KEK_SIZE = 16
|
|
AM_CRC_SIZE = 4
|
|
|
|
AM_MAX_UART_MSG_SIZE = 8192 # 8K buffer in SBL
|
|
|
|
# Wiredupdate Image Header
|
|
AM_WU_IMAGEHDR_OFFSET_SIG = 16
|
|
AM_WU_IMAGEHDR_OFFSET_IV = 48
|
|
AM_WU_IMAGEHDR_OFFSET_KEK = 64
|
|
AM_WU_IMAGEHDR_OFFSET_IMAGETYPE = (AM_WU_IMAGEHDR_OFFSET_KEK + AM_KEK_SIZE)
|
|
AM_WU_IMAGEHDR_OFFSET_OPTIONS = (AM_WU_IMAGEHDR_OFFSET_IMAGETYPE + 1)
|
|
AM_WU_IMAGEHDR_OFFSET_KEY = (AM_WU_IMAGEHDR_OFFSET_IMAGETYPE + 4)
|
|
AM_WU_IMAGEHDR_OFFSET_ADDR = (AM_WU_IMAGEHDR_OFFSET_KEY + 4)
|
|
AM_WU_IMAGEHDR_OFFSET_SIZE = (AM_WU_IMAGEHDR_OFFSET_ADDR + 4)
|
|
|
|
AM_WU_IMAGEHDR_START_HMAC = (AM_WU_IMAGEHDR_OFFSET_SIG + AM_HMAC_SIG_SIZE)
|
|
AM_WU_IMAGEHDR_START_ENCRYPT = (AM_WU_IMAGEHDR_OFFSET_KEK + AM_KEK_SIZE)
|
|
AM_WU_IMAGEHDR_SIZE = (AM_WU_IMAGEHDR_OFFSET_KEK + AM_KEK_SIZE + 16)
|
|
|
|
|
|
# Image Header
|
|
AM_IMAGEHDR_SIZE_MAIN = 256
|
|
AM_IMAGEHDR_SIZE_AUX = (112 + AM_KEK_SIZE)
|
|
|
|
AM_IMAGEHDR_OFFSET_CRC = 4
|
|
AM_IMAGEHDR_OFFSET_SIG = 16
|
|
AM_IMAGEHDR_OFFSET_IV = 48
|
|
AM_IMAGEHDR_OFFSET_KEK = 64
|
|
AM_IMAGEHDR_OFFSET_SIGCLR = (AM_IMAGEHDR_OFFSET_KEK + AM_KEK_SIZE)
|
|
AM_IMAGEHDR_START_CRC = (AM_IMAGEHDR_OFFSET_CRC + AM_CRC_SIZE)
|
|
AM_IMAGEHDR_START_HMAC_INST = (AM_IMAGEHDR_OFFSET_SIG + AM_HMAC_SIG_SIZE)
|
|
AM_IMAGEHDR_START_ENCRYPT = (AM_IMAGEHDR_OFFSET_KEK + AM_KEK_SIZE)
|
|
AM_IMAGEHDR_START_HMAC = (AM_IMAGEHDR_OFFSET_SIGCLR + AM_HMAC_SIG_SIZE)
|
|
AM_IMAGEHDR_OFFSET_ADDR = AM_IMAGEHDR_START_HMAC
|
|
AM_IMAGEHDR_OFFSET_VERKEY = (AM_IMAGEHDR_OFFSET_ADDR + 4)
|
|
AM_IMAGEHDR_OFFSET_CHILDPTR = (AM_IMAGEHDR_OFFSET_VERKEY + 4)
|
|
|
|
# Recover message
|
|
AM_WU_RECOVERY_HDR_SIZE = 44
|
|
AM_WU_RECOVERY_HDR_OFFSET_CUSTID = 8
|
|
AM_WU_RECOVERY_HDR_OFFSET_RECKEY = (AM_WU_RECOVERY_HDR_OFFSET_CUSTID + 4)
|
|
AM_WU_RECOVERY_HDR_OFFSET_NONCE = (AM_WU_RECOVERY_HDR_OFFSET_RECKEY + 16)
|
|
AM_WU_RECOVERY_HDR_OFFSET_RECBLOB = (AM_WU_RECOVERY_HDR_OFFSET_NONCE + 16)
|
|
|
|
|
|
#******************************************************************************
|
|
#
|
|
# INFOSPACE related definitions
|
|
#
|
|
#******************************************************************************
|
|
AM_SECBOOT_INFO0_SIGN_PROGRAMMED0 = 0x48EAAD88
|
|
AM_SECBOOT_INFO0_SIGN_PROGRAMMED1 = 0xC9705737
|
|
AM_SECBOOT_INFO0_SIGN_PROGRAMMED2 = 0x0A6B8458
|
|
AM_SECBOOT_INFO0_SIGN_PROGRAMMED3 = 0xE41A9D74
|
|
|
|
AM_SECBOOT_INFO0_SIGN_UINIT0 = 0x5B75A5FA
|
|
AM_SECBOOT_INFO0_SIGN_UINIT1 = 0x7B9C8674
|
|
AM_SECBOOT_INFO0_SIGN_UINIT2 = 0x869A96FE
|
|
AM_SECBOOT_INFO0_SIGN_UINIT3 = 0xAEC90860
|
|
|
|
INFO_SIZE_BYTES = (8 * 1024)
|
|
INFO_MAX_AUTH_KEY_WORDS = 32
|
|
INFO_MAX_ENC_KEY_WORDS = 32
|
|
|
|
INFO_MAX_AUTH_KEYS = (INFO_MAX_AUTH_KEY_WORDS*4//AM_SECBOOT_KEYIDX_BYTES)
|
|
INFO_MAX_ENC_KEYS = (INFO_MAX_ENC_KEY_WORDS*4//AM_SECBOOT_KEYIDX_BYTES)
|
|
|
|
|
|
#******************************************************************************
|
|
#
|
|
# CRC using ethernet poly, as used by Corvette hardware for validation
|
|
#
|
|
#******************************************************************************
|
|
def crc32(L):
|
|
return (binascii.crc32(L) & 0xFFFFFFFF)
|
|
|
|
#******************************************************************************
|
|
#
|
|
# Pad the text to the block_size. bZeroPad determines how to handle text which
|
|
# is already multiple of block_size
|
|
#
|
|
#******************************************************************************
|
|
def pad_to_block_size(text, block_size, bZeroPad):
|
|
text_length = len(text)
|
|
amount_to_pad = block_size - (text_length % block_size)
|
|
if (amount_to_pad == block_size):
|
|
if (bZeroPad == 0):
|
|
amount_to_pad = 0
|
|
for i in range(0, amount_to_pad, 1):
|
|
text += bytes(chr(amount_to_pad), 'ascii')
|
|
return text
|
|
|
|
|
|
#******************************************************************************
|
|
#
|
|
# AES CBC encryption
|
|
#
|
|
#******************************************************************************
|
|
def encrypt_app_aes(cleartext, encKey, iv):
|
|
key = array.array('B', encKey).tostring()
|
|
ivVal = array.array('B', iv).tostring()
|
|
plaintext = array.array('B', cleartext).tostring()
|
|
|
|
encryption_suite = AES.new(key, AES.MODE_CBC, ivVal)
|
|
cipher_text = encryption_suite.encrypt(plaintext)
|
|
|
|
return cipher_text
|
|
|
|
#******************************************************************************
|
|
#
|
|
# AES 128 CBC encryption
|
|
#
|
|
#******************************************************************************
|
|
def encrypt_app_aes128(cleartext, encKey, iv):
|
|
key = array.array('B', encKey).tostring()
|
|
ivVal = array.array('B', iv).tostring()
|
|
plaintext = array.array('B', cleartext).tostring()
|
|
|
|
encryption_suite = AES.new(key, AES.MODE_CBC, ivVal)
|
|
cipher_text = encryption_suite.encrypt(plaintext)
|
|
|
|
return cipher_text
|
|
|
|
#******************************************************************************
|
|
#
|
|
# SHA256 HMAC
|
|
#
|
|
#******************************************************************************
|
|
def compute_hmac(key, data):
|
|
sig = hmac.new(array.array('B', key).tostring(), array.array('B', data).tostring(), hashlib.sha256).digest()
|
|
return sig
|
|
|
|
#******************************************************************************
|
|
#
|
|
# RSA PKCS1_v1_5 sign
|
|
#
|
|
#******************************************************************************
|
|
def compute_rsa_sign(prvKeyFile, data):
|
|
key = open(prvKeyFile, "r").read()
|
|
rsakey = RSA.importKey(key)
|
|
signer = PKCS1_v1_5.new(rsakey)
|
|
digest = SHA256.new()
|
|
digest.update(bytes(data))
|
|
sign = signer.sign(digest)
|
|
return sign
|
|
|
|
#******************************************************************************
|
|
#
|
|
# RSA PKCS1_v1_5 sign verification
|
|
#
|
|
#******************************************************************************
|
|
def verify_rsa_sign(pubKeyFile, data, sign):
|
|
key = open(pubKeyFile, "r").read()
|
|
rsakey = RSA.importKey(key)
|
|
#print(hex(rsakey.n))
|
|
verifier = PKCS1_v1_5.new(rsakey)
|
|
digest = SHA256.new()
|
|
digest.update(bytes(data))
|
|
return verifier.verify(digest, sign)
|
|
|
|
#******************************************************************************
|
|
#
|
|
# Fill one word in bytearray
|
|
#
|
|
#******************************************************************************
|
|
def fill_word(barray, offset, w):
|
|
barray[offset + 0] = (w >> 0) & 0x000000ff;
|
|
barray[offset + 1] = (w >> 8) & 0x000000ff;
|
|
barray[offset + 2] = (w >> 16) & 0x000000ff;
|
|
barray[offset + 3] = (w >> 24) & 0x000000ff;
|
|
|
|
|
|
#******************************************************************************
|
|
#
|
|
# Turn a 32-bit number into a series of bytes for transmission.
|
|
#
|
|
# This command will split a 32-bit integer into an array of bytes, ordered
|
|
# LSB-first for transmission over the UART.
|
|
#
|
|
#******************************************************************************
|
|
def int_to_bytes(n):
|
|
A = [n & 0xFF,
|
|
(n >> 8) & 0xFF,
|
|
(n >> 16) & 0xFF,
|
|
(n >> 24) & 0xFF]
|
|
|
|
return A
|
|
|
|
#******************************************************************************
|
|
#
|
|
# Extract a word from a byte array
|
|
#
|
|
#******************************************************************************
|
|
def word_from_bytes(B, n):
|
|
return (B[n] + (B[n + 1] << 8) + (B[n + 2] << 16) + (B[n + 3] << 24))
|
|
|
|
|
|
#******************************************************************************
|
|
#
|
|
# automatically figure out the integer format (base 10 or 16)
|
|
#
|
|
#******************************************************************************
|
|
def auto_int(x):
|
|
return int(x, 0)
|
|
|
|
#******************************************************************************
|
|
#
|
|
# User controllable Prints control
|
|
#
|
|
#******************************************************************************
|
|
# Defined print levels
|
|
AM_PRINT_LEVEL_MIN = 0
|
|
AM_PRINT_LEVEL_NONE = AM_PRINT_LEVEL_MIN
|
|
AM_PRINT_LEVEL_ERROR = 1
|
|
AM_PRINT_LEVEL_INFO = 2
|
|
AM_PRINT_LEVEL_VERBOSE = 4
|
|
AM_PRINT_LEVEL_DEBUG = 5
|
|
AM_PRINT_LEVEL_MAX = AM_PRINT_LEVEL_DEBUG
|
|
|
|
# Global variable to control the prints
|
|
AM_PRINT_VERBOSITY = AM_PRINT_LEVEL_INFO
|
|
|
|
helpPrintLevel = 'Set Log Level (0: None), (1: Error), (2: INFO), (4: Verbose), (5: Debug) [Default = Info]'
|
|
|
|
def am_set_print_level(level):
|
|
global AM_PRINT_VERBOSITY
|
|
AM_PRINT_VERBOSITY = level
|
|
|
|
def am_print(*args, level=AM_PRINT_LEVEL_INFO, **kwargs):
|
|
global AM_PRINT_VERBOSITY
|
|
if (AM_PRINT_VERBOSITY >= level):
|
|
print(*args, **kwargs)
|