initial commit

This commit is contained in:
2022-10-23 23:45:43 -07:00
commit e190fa5193
6450 changed files with 8626944 additions and 0 deletions
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
@@ -0,0 +1,161 @@
on:
push:
name: generate executables
jobs:
windows:
name: windows
runs-on: windows-latest
steps:
- name: repo
uses: actions/checkout@v2.3.1
with:
path: asb
fetch-depth: 0
- name: python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: git
run: |
git config --global user.email "apollo3-uploader-builder@sparkfun.com"
git config --global user.name "apollo3-uploader-builder"
git config --global pull.ff only
cd asb
git status
git pull
cd ${GITHUB_WORKSPACE}
- name: install
run: |
python --version
python -m pip install --upgrade pip setuptools wheel
pip --version
pip install -r asb/requirements.txt
- name: build
run: |
cd asb
pyinstaller --onefile asb.py
cd ${GITHUB_WORKSPACE}
- name: copy
run: |
Remove-Item -Recurse -Force asb\dist\windows
mkdir asb\dist\windows
Move-Item -Path asb\dist\asb.exe -Destination asb\dist\windows\asb.exe
- name: commit
run: |
cd asb
git add dist\windows\*
git commit -m "generated windows executable"
git push
cd ${GITHUB_WORKSPACE}
linux:
name: linux
needs: windows
runs-on: ubuntu-latest
steps:
- name: repo
uses: actions/checkout@v2.3.1
with:
path: asb
fetch-depth: 0
- name: python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: git
run: |
git config --global user.email "apollo3-uploader-builder@sparkfun.com"
git config --global user.name "apollo3-uploader-builder"
git config --global pull.ff only
cd asb
git pull
cd ${GITHUB_WORKSPACE}
- name: install
run: |
python --version
python -m pip install --upgrade pip setuptools wheel
pip --version
pip install -r asb/requirements.txt
- name: build
run: |
cd asb
pyinstaller --onefile asb.py
cd ${GITHUB_WORKSPACE}
- name: copy
run: |
rm -rf ./asb/dist/linux
mkdir -p ./asb/dist/linux
mv ./asb/dist/asb ./asb/dist/linux/asb
- name: commit
run: |
cd ./asb
git add ./dist/linux/*
git commit -m "generated linux executable"
git push
cd ${GITHUB_WORKSPACE}
macosx:
name: macosx
needs: linux
runs-on: macos-latest
steps:
- name: repo
uses: actions/checkout@v2.3.1
with:
path: asb
fetch-depth: 0
- name: python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: git
run: |
git config --global user.email "apollo3-uploader-builder@sparkfun.com"
git config --global user.name "apollo3-uploader-builder"
git config --global pull.ff only
cd asb
git pull
cd ${GITHUB_WORKSPACE}
- name: install
run: |
python --version
python -m pip install --upgrade pip setuptools wheel
pip --version
pip install -r asb/requirements.txt
- name: build
run: |
cd asb
pyinstaller --onefile asb.py
cd ${GITHUB_WORKSPACE}
- name: copy
run: |
rm -rf ./asb/dist/macosx
mkdir -p ./asb/dist/macosx
mv ./asb/dist/asb ./asb/dist/macosx/asb
- name: commit
run: |
cd ./asb
git add ./dist/macosx/*
git commit -m "generated macosx executable"
git push
cd ${GITHUB_WORKSPACE}
@@ -0,0 +1,127 @@
# macosx
.DS_Store
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
@@ -0,0 +1,3 @@
![generate executables](https://github.com/sparkfun/Apollo3_Uploader_ASB/workflows/generate%20executables/badge.svg)
# Apollo3 Uploader - Ambiq Secure Bootloader (SVL)
@@ -0,0 +1,527 @@
#!/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)
INFO0_SIGNATURE0_O = 0x00000000
INFO0_SIGNATURE1_O = 0x00000004
INFO0_SIGNATURE2_O = 0x00000008
INFO0_SIGNATURE3_O = 0x0000000c
INFO0_SECURITY_O = 0x00000010
INFO0_CUSTOMER_TRIM_O = 0x00000014
INFO0_CUSTOMER_TRIM2_O = 0x00000018
INFO0_SECURITY_OVR_O = 0x00000020
INFO0_SECURITY_WIRED_CFG_O = 0x00000024
INFO0_SECURITY_WIRED_IFC_CFG0_O = 0x00000028
INFO0_SECURITY_WIRED_IFC_CFG1_O = 0x0000002C
INFO0_SECURITY_WIRED_IFC_CFG2_O = 0x00000030
INFO0_SECURITY_WIRED_IFC_CFG3_O = 0x00000034
INFO0_SECURITY_WIRED_IFC_CFG4_O = 0x00000038
INFO0_SECURITY_WIRED_IFC_CFG5_O = 0x0000003C
INFO0_SECURITY_VERSION_O = 0x00000040
INFO0_SECURITY_SRAM_RESV_O = 0x00000050
AM_REG_INFO0_SECURITY_SRAM_RESV_SRAM_RESV_M = 0x0000FFFF
INFO0_WRITE_PROTECT_L_O = 0x000001f8
INFO0_WRITE_PROTECT_H_O = 0x000001fc
INFO0_COPY_PROTECT_L_O = 0x00000200
INFO0_COPY_PROTECT_H_O = 0x00000204
INFO0_WRITE_PROTECT_SBL_L_O = 0x000009f8
INFO0_WRITE_PROTECT_SBL_H_O = 0x000009fc
INFO0_COPY_PROTECT_SBL_L_O = 0x00000A00
INFO0_COPY_PROTECT_SBL_H_O = 0x00000A04
INFO0_MAIN_PTR1_O = 0x00000C00
INFO0_MAIN_PTR2_O = 0x00000C04
INFO0_KREVTRACK_O = 0x00000C08
INFO0_AREVTRACK_O = 0x00000C0C
INFO0_MAIN_CNT0_O = 0x00000FF8
INFO0_MAIN_CNT1_O = 0x00000FFC
INFO0_CUST_KEK_W0_O = 0x00001800
INFO0_CUST_KEK_W1_O = 0x00001804
INFO0_CUST_KEK_W2_O = 0x00001808
INFO0_CUST_KEK_W3_O = 0x0000180c
INFO0_CUST_KEK_W4_O = 0x00001810
INFO0_CUST_KEK_W5_O = 0x00001814
INFO0_CUST_KEK_W6_O = 0x00001818
INFO0_CUST_KEK_W7_O = 0x0000181c
INFO0_CUST_KEK_W8_O = 0x00001820
INFO0_CUST_KEK_W9_O = 0x00001824
INFO0_CUST_KEK_W10_O = 0x00001828
INFO0_CUST_KEK_W11_O = 0x0000182c
INFO0_CUST_KEK_W12_O = 0x00001830
INFO0_CUST_KEK_W13_O = 0x00001834
INFO0_CUST_KEK_W14_O = 0x00001838
INFO0_CUST_KEK_W15_O = 0x0000183c
INFO0_CUST_KEK_W16_O = 0x00001840
INFO0_CUST_KEK_W17_O = 0x00001844
INFO0_CUST_KEK_W18_O = 0x00001848
INFO0_CUST_KEK_W19_O = 0x0000184c
INFO0_CUST_KEK_W20_O = 0x00001850
INFO0_CUST_KEK_W21_O = 0x00001854
INFO0_CUST_KEK_W22_O = 0x00001858
INFO0_CUST_KEK_W23_O = 0x0000185c
INFO0_CUST_KEK_W24_O = 0x00001860
INFO0_CUST_KEK_W25_O = 0x00001864
INFO0_CUST_KEK_W26_O = 0x00001868
INFO0_CUST_KEK_W27_O = 0x0000186c
INFO0_CUST_KEK_W28_O = 0x00001870
INFO0_CUST_KEK_W29_O = 0x00001874
INFO0_CUST_KEK_W30_O = 0x00001878
INFO0_CUST_KEK_W31_O = 0x0000187c
INFO0_CUST_AUTH_W0_O = 0x00001880
INFO0_CUST_AUTH_W1_O = 0x00001884
INFO0_CUST_AUTH_W2_O = 0x00001888
INFO0_CUST_AUTH_W3_O = 0x0000188c
INFO0_CUST_AUTH_W4_O = 0x00001890
INFO0_CUST_AUTH_W5_O = 0x00001894
INFO0_CUST_AUTH_W6_O = 0x00001898
INFO0_CUST_AUTH_W7_O = 0x0000189c
INFO0_CUST_AUTH_W8_O = 0x000018a0
INFO0_CUST_AUTH_W9_O = 0x000018a4
INFO0_CUST_AUTH_W10_O = 0x000018a8
INFO0_CUST_AUTH_W11_O = 0x000018ac
INFO0_CUST_AUTH_W12_O = 0x000018b0
INFO0_CUST_AUTH_W13_O = 0x000018b4
INFO0_CUST_AUTH_W14_O = 0x000018b8
INFO0_CUST_AUTH_W15_O = 0x000018bc
INFO0_CUST_AUTH_W16_O = 0x000018c0
INFO0_CUST_AUTH_W17_O = 0x000018c4
INFO0_CUST_AUTH_W18_O = 0x000018c8
INFO0_CUST_AUTH_W19_O = 0x000018cc
INFO0_CUST_AUTH_W20_O = 0x000018d0
INFO0_CUST_AUTH_W21_O = 0x000018d4
INFO0_CUST_AUTH_W22_O = 0x000018d8
INFO0_CUST_AUTH_W23_O = 0x000018dc
INFO0_CUST_AUTH_W24_O = 0x000018e0
INFO0_CUST_AUTH_W25_O = 0x000018e4
INFO0_CUST_AUTH_W26_O = 0x000018e8
INFO0_CUST_AUTH_W27_O = 0x000018ec
INFO0_CUST_AUTH_W28_O = 0x000018f0
INFO0_CUST_AUTH_W29_O = 0x000018f4
INFO0_CUST_AUTH_W30_O = 0x000018f8
INFO0_CUST_AUTH_W31_O = 0x000018fc
INFO0_CUST_PUBKEY_W0_O = 0x00001900
INFO0_CUST_PUBKEY_W1_O = 0x00001904
INFO0_CUST_PUBKEY_W2_O = 0x00001908
INFO0_CUST_PUBKEY_W3_O = 0x0000190c
INFO0_CUST_PUBKEY_W4_O = 0x00001910
INFO0_CUST_PUBKEY_W5_O = 0x00001914
INFO0_CUST_PUBKEY_W6_O = 0x00001918
INFO0_CUST_PUBKEY_W7_O = 0x0000191c
INFO0_CUST_PUBKEY_W8_O = 0x00001920
INFO0_CUST_PUBKEY_W9_O = 0x00001924
INFO0_CUST_PUBKEY_W10_O = 0x00001928
INFO0_CUST_PUBKEY_W11_O = 0x0000192c
INFO0_CUST_PUBKEY_W12_O = 0x00001930
INFO0_CUST_PUBKEY_W13_O = 0x00001934
INFO0_CUST_PUBKEY_W14_O = 0x00001938
INFO0_CUST_PUBKEY_W15_O = 0x0000193c
INFO0_CUST_PUBKEY_W16_O = 0x00001940
INFO0_CUST_PUBKEY_W17_O = 0x00001944
INFO0_CUST_PUBKEY_W18_O = 0x00001948
INFO0_CUST_PUBKEY_W19_O = 0x0000194c
INFO0_CUST_PUBKEY_W20_O = 0x00001950
INFO0_CUST_PUBKEY_W21_O = 0x00001954
INFO0_CUST_PUBKEY_W22_O = 0x00001958
INFO0_CUST_PUBKEY_W23_O = 0x0000195c
INFO0_CUST_PUBKEY_W24_O = 0x00001960
INFO0_CUST_PUBKEY_W25_O = 0x00001964
INFO0_CUST_PUBKEY_W26_O = 0x00001968
INFO0_CUST_PUBKEY_W27_O = 0x0000196c
INFO0_CUST_PUBKEY_W28_O = 0x00001970
INFO0_CUST_PUBKEY_W29_O = 0x00001974
INFO0_CUST_PUBKEY_W30_O = 0x00001978
INFO0_CUST_PUBKEY_W31_O = 0x0000197c
INFO0_CUST_PUBKEY_W32_O = 0x00001980
INFO0_CUST_PUBKEY_W33_O = 0x00001984
INFO0_CUST_PUBKEY_W34_O = 0x00001988
INFO0_CUST_PUBKEY_W35_O = 0x0000198c
INFO0_CUST_PUBKEY_W36_O = 0x00001990
INFO0_CUST_PUBKEY_W37_O = 0x00001994
INFO0_CUST_PUBKEY_W38_O = 0x00001998
INFO0_CUST_PUBKEY_W39_O = 0x0000199c
INFO0_CUST_PUBKEY_W40_O = 0x000019a0
INFO0_CUST_PUBKEY_W41_O = 0x000019a4
INFO0_CUST_PUBKEY_W42_O = 0x000019a8
INFO0_CUST_PUBKEY_W43_O = 0x000019ac
INFO0_CUST_PUBKEY_W44_O = 0x000019b0
INFO0_CUST_PUBKEY_W45_O = 0x000019b4
INFO0_CUST_PUBKEY_W46_O = 0x000019b8
INFO0_CUST_PUBKEY_W47_O = 0x000019bc
INFO0_CUST_PUBKEY_W48_O = 0x000019c0
INFO0_CUST_PUBKEY_W49_O = 0x000019c4
INFO0_CUST_PUBKEY_W50_O = 0x000019c8
INFO0_CUST_PUBKEY_W51_O = 0x000019cc
INFO0_CUST_PUBKEY_W52_O = 0x000019d0
INFO0_CUST_PUBKEY_W53_O = 0x000019d4
INFO0_CUST_PUBKEY_W54_O = 0x000019d8
INFO0_CUST_PUBKEY_W55_O = 0x000019dc
INFO0_CUST_PUBKEY_W56_O = 0x000019e0
INFO0_CUST_PUBKEY_W57_O = 0x000019e4
INFO0_CUST_PUBKEY_W58_O = 0x000019e8
INFO0_CUST_PUBKEY_W59_O = 0x000019ec
INFO0_CUST_PUBKEY_W60_O = 0x000019f0
INFO0_CUST_PUBKEY_W61_O = 0x000019f4
INFO0_CUST_PUBKEY_W62_O = 0x000019f8
INFO0_CUST_PUBKEY_W63_O = 0x000019fc
INFO0_CUSTOMER_KEY0_O = 0x00001a00
INFO0_CUSTOMER_KEY1_O = 0x00001a04
INFO0_CUSTOMER_KEY2_O = 0x00001a08
INFO0_CUSTOMER_KEY3_O = 0x00001a0c
INFO0_CUST_PUBHASH_W0_O = 0x00001a10
INFO0_CUST_PUBHASH_W1_O = 0x00001a14
INFO0_CUST_PUBHASH_W2_O = 0x00001a18
INFO0_CUST_PUBHASH_W3_O = 0x00001a1c
#******************************************************************************
#
# 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)
@@ -0,0 +1,899 @@
#!/usr/bin/env python3
# Combination of the three steps to take an 'application.bin' file and run it on a SparkFun Artemis module
# Information:
# This script performs the three main tasks:
# 1. Convert 'application.bin' to an OTA update blob
# 2. Convert the OTA blob into a wired update blob
# 3. Push the wired update blob into the Artemis module
import argparse
import sys
from Crypto.Cipher import AES
import array
import hashlib
import hmac
import os
import binascii
import serial
import serial.tools.list_ports as list_ports
import time
# from sf_am_defines import *
from sys import exit
from am_defines import *
from keys_info import keyTblAes, keyTblHmac, minAesKeyIdx, maxAesKeyIdx, minHmacKeyIdx, maxHmacKeyIdx, INFO_KEY, FLASH_KEY
#******************************************************************************
#
# Global Variables
#
#******************************************************************************
loadTries = 0 #If we fail, try again. Tracks the number of tries we've attempted
loadSuccess = False
blob2wiredfile = ''
uploadbinfile = ''
#******************************************************************************
#
# Generate the image blob as per command line parameters
#
#******************************************************************************
def bin2blob_process(loadaddress, appFile, magicNum, crcI, crcB, authI, authB, protection, authKeyIdx, output, encKeyIdx, version, erasePrev, child0, child1, authalgo, encalgo):
global blob2wiredfile
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()
encVal = 0
if (encalgo != 0):
encVal = 1
if ((encKeyIdx < minAesKeyIdx) or (encKeyIdx > 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 < minHmacKeyIdx) or (authKeyIdx > 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 (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 = 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 - minHmacKeyIdx
if (authB != 0): # Authentication needed
am_print("Boot Authentication Enabled")
# am_print("Key used for HMAC")
# am_print([hex(keyTblHmac[authKeyIdx*AM_SECBOOT_KEYIDX_BYTES + n]) for n in range (0, AM_HMAC_SIG_SIZE)])
# Initialize the clear image HMAC
sigClr = compute_hmac(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 - 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(keyTblAes[encKeyIdx*keySize + n]) for n in range (0, keySize)])
# Encrypted Key
enc_key = encrypt_app_aes(keyAes, 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(keyTblHmac[authKeyIdx*AM_SECBOOT_KEYIDX_BYTES + n]) for n in range (0, AM_HMAC_SIG_SIZE)])
# Initialize the top level HMAC
sig = compute_hmac(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 + '_OTA_blob.bin'
blob2wiredfile = output # save the output of bin2blob for use by blob2wired
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)
#******************************************************************************
#
# Generate the image blob as per command line parameters
#
#******************************************************************************
def blob2wired_process(appFile, imagetype, loadaddress, authalgo, encalgo, authKeyIdx, encKeyIdx, optionsVal, maxSize, output):
global uploadbinfile
app_binarray = bytearray()
# Open the file, and read it into an array of integers.
print('testing: ' + appFile )
with open(appFile,'rb') 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
if (encalgo != 0):
if ((encKeyIdx < minAesKeyIdx) or (encKeyIdx > 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 < minHmacKeyIdx) or (authKeyIdx > 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 + '_Wired_OTA_blob.bin'
uploadbinfile = output; # save the name of the output from blob2wired
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 = INFO_KEY
# word offset
fill_word(hdr_binarray, AM_WU_IMAGEHDR_OFFSET_ADDR, loadaddress>>2)
else:
key = FLASH_KEY
# load address
fill_word(hdr_binarray, AM_WU_IMAGEHDR_OFFSET_ADDR, loadaddress)
# 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 - 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(keyTblAes[keyIdx*AM_SECBOOT_KEYIDX_BYTES + n]) for n in range (0, keySize)])
# Encrypted Key
enc_key = encrypt_app_aes(keyAes, 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 - minHmacKeyIdx
# am_print("Key used for HMAC")
# am_print([hex(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(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
#******************************************************************************
#
# Main function
#
#******************************************************************************
def upload(args, verboseprint):
global loadTries
global loadSuccess
# Open a serial port, and communicate with Device
#
# Max flashing time depends on the amount of SRAM available.
# For very large images, the flashing happens page by page.
# However if the image can fit in the free SRAM, it could take a long time
# for the whole image to be flashed at the end.
# The largest image which can be stored depends on the max SRAM.
# Assuming worst case ~100 ms/page of flashing time, and allowing for the
# image to be close to occupying full SRAM (256K) which is 128 pages.
connection_timeout = 5
print('Connecting over serial port {}...'.format(args.port), flush=True)
#Check to see if the com port is available
try:
with serial.Serial(args.port, args.baud, timeout=connection_timeout) as ser:
pass
except:
#Show a list of com ports and recommend one
print("Detected Serial Ports:")
devices = list_ports.comports()
port = None
for dev in devices:
print(dev.description)
# The SparkFun BlackBoard has CH340 in the description
if 'CH340' in dev.description:
print("The port you selected was not found. But we did detect a CH340 on " + dev.device + " so you might try again on that port.")
break
elif 'FTDI' in dev.description:
print("The port you selected was not found. But we did detect an FTDI on " + dev.device + " so you might try again on that port.")
break
elif 'USB Serial Device' in dev.description:
print("The port you selected was not found. But we did detect a USB Serial Device on " + dev.device + " so you might try again on that port.")
break
else:
print("Com Port not found - Did you select the right one?")
exit()
#Begin talking over com port
#The auto-bootload sequence is good but not fullproof. The bootloader
#fails to correctly catch the BOOT signal about 1 out of ten times.
#Auto-retry this number of times before we give up.
while loadTries < 3:
loadSuccess = False
with serial.Serial(args.port, args.baud, timeout=connection_timeout) as ser:
#DTR is driven low when serial port open. DTR has now pulled RST low.
time.sleep(0.005) #3ms and 10ms work well. Not 50, and not 0.
#Setting RTS/DTR high causes the bootload pin to go high, then fall across 100ms
ser.setDTR(0) #Set DTR high
ser.setRTS(0) #Set RTS high - support the CH340E
#Give bootloader a chance to run and check bootload pin before communication begins. But must initiate com before bootloader timeout of 250ms.
time.sleep(0.100) #100ms works well
ser.reset_input_buffer() # reset the input bufer to discard any UART traffic that the device may have generated
connect_device(ser, args, verboseprint)
if(loadSuccess == True):
print("Tries =", loadTries)
print('Upload complete!')
exit()
else:
print("Fail")
loadTries = loadTries + 1
print("Tries =", loadTries)
print("Upload failed")
exit()
#******************************************************************************
#
# Communicate with Device
#
# Given a serial port, connects to the target device using the
# UART.
#
#******************************************************************************
def connect_device(ser, args, verboseprint):
global loadSuccess
# Send Hello
#generate mutable byte array for the header
hello = bytearray([0x00]*4)
fill_word(hello, 0, ((8 << 16) | AM_SECBOOT_WIRED_MSGTYPE_HELLO))
verboseprint('Sending Hello.')
response = send_command(hello, 88, ser, verboseprint)
#Check if response failed
if response == False:
verboseprint("Failed to respond")
return
verboseprint("Received response for Hello")
word = word_from_bytes(response, 4)
if ((word & 0xFFFF) == AM_SECBOOT_WIRED_MSGTYPE_STATUS):
# Received Status
print("Bootloader connected")
verboseprint("Received Status")
verboseprint("length = ", hex((word >> 16)))
verboseprint("version = ", hex(word_from_bytes(response, 8)))
verboseprint("Max Storage = ", hex(word_from_bytes(response, 12)))
verboseprint("Status = ", hex(word_from_bytes(response, 16)))
verboseprint("State = ", hex(word_from_bytes(response, 20)))
verboseprint("AMInfo = ")
for x in range(24, 88, 4):
verboseprint(hex(word_from_bytes(response, x)))
abort = args.abort
if (abort != -1):
# Send OTA Desc
verboseprint('Sending Abort command.')
abortMsg = bytearray([0x00]*8);
fill_word(abortMsg, 0, ((12 << 16) | AM_SECBOOT_WIRED_MSGTYPE_ABORT))
fill_word(abortMsg, 4, abort)
if send_ackd_command(abortMsg, ser, verboseprint) == False:
verboseprint("Failed to ack command")
return
otadescaddr = args.otadesc
if (otadescaddr != 0xFFFFFFFF):
# Send OTA Desc
verboseprint('Sending OTA Descriptor = ', hex(otadescaddr))
otaDesc = bytearray([0x00]*8);
fill_word(otaDesc, 0, ((12 << 16) | AM_SECBOOT_WIRED_MSGTYPE_OTADESC))
fill_word(otaDesc, 4, otadescaddr)
if send_ackd_command(otaDesc, ser, verboseprint) == False:
verboseprint("Failed to ack command")
return
imageType = args.imagetype
if (uploadbinfile != ''):
# Read the binary file from the command line.
with open(uploadbinfile, mode='rb') as binfile:
application = binfile.read()
# Gather the important binary metadata.
totalLen = len(application)
# Send Update command
verboseprint('Sending Update Command.')
# It is assumed that maxSize is 256b multiple
maxImageSize = args.split
if ((maxImageSize & (FLASH_PAGE_SIZE - 1)) != 0):
verboseprint ("split needs to be multiple of flash page size")
return
# Each Block of image consists of AM_WU_IMAGEHDR_SIZE Bytes Image header and the Image blob
maxUpdateSize = AM_WU_IMAGEHDR_SIZE + maxImageSize
numUpdates = (totalLen + maxUpdateSize - 1) // maxUpdateSize # Integer division
verboseprint("number of updates needed = ", numUpdates)
end = totalLen
for numUpdates in range(numUpdates, 0 , -1):
start = (numUpdates-1)*maxUpdateSize
crc = crc32(application[start:end])
applen = end - start
verboseprint("Sending block of size ", str(hex(applen)), " from ", str(hex(start)), " to ", str(hex(end)))
end = end - applen
update = bytearray([0x00]*16);
fill_word(update, 0, ((20 << 16) | AM_SECBOOT_WIRED_MSGTYPE_UPDATE))
fill_word(update, 4, applen)
fill_word(update, 8, crc)
# Size = 0 => We're not piggybacking any data to IMAGE command
fill_word(update, 12, 0)
if send_ackd_command(update, ser, verboseprint) == False:
verboseprint("Failed to ack command")
return
# Loop over the bytes in the image, and send them to the target.
resp = 0
# Max chunk size is AM_MAX_UART_MSG_SIZE adjusted for the header for Data message
maxChunkSize = AM_MAX_UART_MSG_SIZE - 12
for x in range(0, applen, maxChunkSize):
# Split the application into chunks of maxChunkSize bytes.
# This is the max chunk size supported by the UART bootloader
if ((x + maxChunkSize) > applen):
chunk = application[start+x:start+applen]
# print(str(hex(start+x)), " to ", str(hex(applen)))
else:
chunk = application[start+x:start+x+maxChunkSize]
# print(str(hex(start+x)), " to ", str(hex(start + x + maxChunkSize)))
chunklen = len(chunk)
# Build a data packet with a "data command" a "length" and the actual
# payload bytes, and send it to the target.
dataMsg = bytearray([0x00]*8);
fill_word(dataMsg, 0, (((chunklen + 12) << 16) | AM_SECBOOT_WIRED_MSGTYPE_DATA))
# seqNo
fill_word(dataMsg, 4, x)
verboseprint("Sending Data Packet of length ", chunklen)
if send_ackd_command(dataMsg + chunk, ser, verboseprint) == False:
verboseprint("Failed to ack command")
return
if (args.raw != ''):
# Read the binary file from the command line.
with open(args.raw, mode='rb') as rawfile:
blob = rawfile.read()
# Send Raw command
verboseprint('Sending Raw Command.')
ser.write(blob)
if (args.reset != 0):
# Send reset
verboseprint('Sending Reset Command.')
resetmsg = bytearray([0x00]*8);
fill_word(resetmsg, 0, ((12 << 16) | AM_SECBOOT_WIRED_MSGTYPE_RESET))
# options
fill_word(resetmsg, 4, args.reset)
if send_ackd_command(resetmsg, ser, verboseprint) == False:
verboseprint("Failed to ack command")
return
#Success! We're all done
loadSuccess = True
else:
# Received Wrong message
verboseprint("Received Unknown Message")
word = word_from_bytes(response, 4)
verboseprint("msgType = ", hex(word & 0xFFFF))
verboseprint("Length = ", hex(word >> 16))
verboseprint([hex(n) for n in response])
#print("!!!Wired Upgrade Unsuccessful!!!....Terminating the script")
#exit()
#******************************************************************************
#
# Send ACK'd command
#
# Sends a command, and waits for an ACK.
#
#******************************************************************************
def send_ackd_command(command, ser, verboseprint):
response = send_command(command, 20, ser, verboseprint)
#Check if response failed
if response == False:
verboseprint("Response not valid")
return False #Return error
word = word_from_bytes(response, 4)
if ((word & 0xFFFF) == AM_SECBOOT_WIRED_MSGTYPE_ACK):
# Received ACK
if (word_from_bytes(response, 12) != AM_SECBOOT_WIRED_ACK_STATUS_SUCCESS):
verboseprint("Received NACK")
verboseprint("msgType = ", hex(word_from_bytes(response, 8)))
verboseprint("error = ", hex(word_from_bytes(response, 12)))
verboseprint("seqNo = ", hex(word_from_bytes(response, 16)))
#print("!!!Wired Upgrade Unsuccessful!!!....Terminating the script")
verboseprint("Upload failed: No ack to command")
return False #Return error
return response
#******************************************************************************
#
# Send command
#
# Sends a command, and waits for the response.
#
#******************************************************************************
def send_command(params, response_len, ser, verboseprint):
# Compute crc
crc = crc32(params)
# print([hex(n) for n in int_to_bytes(crc)])
# print([hex(n) for n in params])
# send crc first
ser.write(int_to_bytes(crc))
# Next, send the parameters.
ser.write(params)
response = ''
response = ser.read(response_len)
# Make sure we got the number of bytes we asked for.
if len(response) != response_len:
verboseprint('No response for command 0x{:08X}'.format(word_from_bytes(params, 0) & 0xFFFF))
n = len(response)
if (n != 0):
verboseprint("received bytes ", len(response))
verboseprint([hex(n) for n in response])
return False
return response
#******************************************************************************
#
# Send a command that uses an array of bytes as its parameters.
#
#******************************************************************************
def send_bytewise_command(command, params, response_len, ser):
# Send the command first.
ser.write(int_to_bytes(command))
# Next, send the parameters.
ser.write(params)
response = ''
response = ser.read(response_len)
# Make sure we got the number of bytes we asked for.
if len(response) != response_len:
print("Upload failed: No reponse to command")
verboseprint('No response for command 0x{:08X}'.format(command))
exit()
return response
#******************************************************************************
#
# Errors
#
#******************************************************************************
class BootError(Exception):
pass
class NoAckError(BootError):
pass
def parse_arguments():
parser = argparse.ArgumentParser(description =
'Combination script to upload application binaries to Artemis module. Includes:\n\t\'- bin2blob: create OTA blob from binary image\'\n\t\'- blob2wired: create wired update image from OTA blob\'\n\t\'- upload: send wired update image to Apollo3 Artemis module via serial port\'\n\nThere are many command-line arguments. They have been labeled by which steps they apply to\n')
parser.add_argument('-a', dest = 'abort', default=-1, type=int, choices = [0,1,-1],
help = 'upload: Should it send abort command? (0 = abort, 1 = abort and quit, -1 = no abort) (default is -1)')
parser.add_argument('--authalgo', dest = 'authalgo', type=auto_int, default=0, choices=range(0, AM_SECBOOT_AUTH_ALGO_MAX+1),
help = 'bin2blob, blob2wired: ' + str(helpAuthAlgo))
parser.add_argument('--authI', dest = 'authI', type=auto_int, default=0, choices=[0,1],
help = 'bin2blob: Install Authentication check enabled (Default = N)?')
parser.add_argument('--authB', dest = 'authB', type=auto_int, default=0, choices=[0,1],
help = 'bin2blob: Boot Authentication check enabled (Default = N)?')
parser.add_argument('--authkey', dest = 'authkey', type=auto_int, default=(minHmacKeyIdx), choices = range(minHmacKeyIdx, maxHmacKeyIdx + 1),
help = 'bin2blob, blob2wired: Authentication Key Idx? (' + str(minHmacKeyIdx) + ' to ' + str(maxHmacKeyIdx) + ')')
parser.add_argument('-b', dest='baud', default=115200, type=int,
help = 'upload: Baud Rate (default is 115200)')
parser.add_argument('--bin', dest='appFile', type=argparse.FileType('rb'),
help='bin2blob: binary file (blah.bin)')
parser.add_argument('-clean', dest='clean', default=0, type=int,
help = 'All: whether or not to remove intermediate files')
parser.add_argument('--child0', dest = 'child0', type=auto_int, default=hex(0xFFFFFFFF),
help = 'bin2blob: child (blobPtr#0 for Main / feature key for AM3P)')
parser.add_argument('--child1', dest = 'child1', type=auto_int, default=hex(0xFFFFFFFF),
help = 'bin2blob: child (blobPtr#1 for Main)')
parser.add_argument('--crcI', dest = 'crcI', type=auto_int, default=1, choices=[0,1],
help = 'bin2blob: Install CRC check enabled (Default = Y)?')
parser.add_argument('--crcB', dest = 'crcB', type=auto_int, default=0, choices=[0,1],
help = 'bin2blob: Boot CRC check enabled (Default = N)?')
parser.add_argument('--encalgo', dest = 'encalgo', type=auto_int, default=0, choices = range(0, AM_SECBOOT_ENC_ALGO_MAX+1),
help = 'bin2blob, blob2wired: ' + str(helpEncAlgo))
parser.add_argument('--erasePrev', dest = 'erasePrev', type=auto_int, default=0, choices=[0,1],
help = 'bin2blob: erasePrev (Valid only for main)')
# parser.add_argument('-f', dest='binfile', default='',
# help = 'upload: Binary file to program into the target device')
parser.add_argument('-i', dest = 'imagetype', default=AM_SECBOOT_WIRED_IMAGETYPE_INVALID, type=auto_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),
(AM_SECBOOT_WIRED_IMAGETYPE_INVALID)
],
help = 'blob2wired, upload: 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) '
+ str(AM_SECBOOT_WIRED_IMAGETYPE_INVALID) + ': Invalid) '
'- default[Invalid]')
parser.add_argument('--kek', dest = 'kek', type=auto_int, default=(minAesKeyIdx), choices = range(minAesKeyIdx, maxAesKeyIdx+1),
help = 'KEK index? (' + str(minAesKeyIdx) + ' to ' + str(maxAesKeyIdx) + ')')
parser.add_argument('--load-address-wired', dest='loadaddress_blob', type=auto_int, default=hex(0x60000),
help='blob2wired: 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('--load-address-blob', dest='loadaddress_image', type=auto_int, default=hex(AM_SECBOOT_DEFAULT_NONSECURE_MAIN),
help='bin2blob: Load address of the binary.')
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='bin2blob, blob2wired: ' + str(helpPrintLevel))
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 = 'bin2blob: 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='wuimage',
help = 'all: Output filename (without the extension) [also used for intermediate filenames]')
parser.add_argument('-ota', dest = 'otadesc', type=auto_int, default=0xFE000,
help = 'upload: OTA Descriptor Page address (hex) - (Default is 0xFE000 - at the end of main flash) - enter 0xFFFFFFFF to instruct SBL to skip OTA')
parser.add_argument('--options', dest = 'options', type=auto_int, default=0x1,
help = 'blob2wired: 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('-p', dest = 'protection', type=auto_int, default=0, choices = [0x0, 0x1, 0x2, 0x3],
help = 'bin2blob: protection info 2 bit C W')
parser.add_argument('-port', dest = 'port', help = 'upload: Serial COMx Port')
parser.add_argument('-r', dest = 'reset', default=1, type=auto_int, choices = [0,1,2],
help = 'upload: Should it send reset command after image download? (0 = no reset, 1 = POI, 2 = POR) (default is 1)')
parser.add_argument('--raw', dest='raw', default='',
help = 'upload: Binary file for raw message')
parser.add_argument('--split', dest='split', type=auto_int, default=hex(MAX_DOWNLOAD_SIZE),
help='blob2wired, upload: Specify the max block size if the image will be downloaded in pieces')
parser.add_argument('--version', dest = 'version', type=auto_int, default=0,
help = 'bin2blob: version (15 bit)')
parser.add_argument("-v", "--verbose", default=0, help="All: Enable verbose output",
action="store_true")
args = parser.parse_args()
args.magic_num = int(args.magic_num, 16)
return args
#******************************************************************************
#
# Main function.
#
#******************************************************************************
# example calling:
# python artemis_bin_to_board.py --bin application.bin --load-address-blob 0x20000 --magic-num 0xCB -o application --version 0x0 --load-address-wired 0xC000 -i 6 --options 0x1 -b 921600 -port COM4 -r 1 -v
def main():
# Read the arguments.
args = parse_arguments()
am_set_print_level(args.loglevel)
global blob2wiredfile
bin2blob_process(args.loadaddress_blob, 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)
blob2wired_process( blob2wiredfile, args.imagetype, args.loadaddress_image, args.authalgo, args.encalgo, args.authkey, args.kek, args.options, args.split, args.output)
# todo: link the bin2blob step with the blob2wired step by input/output files
#Create print function for verbose output if caller deems it: https://stackoverflow.com/questions/5980042/how-to-implement-the-verbose-or-v-option-into-a-script
if args.verbose:
def verboseprint(*args):
# Print each argument separately so caller doesn't need to
# stuff everything to be printed into a single string
for arg in args:
print(arg, end=''),
print()
else:
verboseprint = lambda *a: None # do-nothing function
upload(args, verboseprint)
if(args.clean == 1):
print('Cleaning up intermediate files') # todo: why isnt this showing w/ -clean option?
if __name__ == '__main__':
main()
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,46 @@
#!/usr/bin/env python3
from am_defines import *
minAesKeyIdx = 8
maxAesKeyIdx = 15
minHmacKeyIdx = 8
maxHmacKeyIdx = 15
###### Following are just dummy keys - Should be substituted with real keys #######
keyTblAes = [
# Info0 Keys - Starting at index 8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
]
keyTblHmac = [
# Info0 Keys - Starting at index 8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
]
custKey = [
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
]
# These are dummy values. Contact AMBIQ to get the real Recovery Key
recoveryKey = [
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
]
###################################################################################
wrapKey = custKey
minWrapMode = AM_SECBOOT_KEYWRAP_NONE
INFO_KEY = 0xd894e09e
FLASH_KEY = 0x12344321
@@ -0,0 +1,4 @@
###### Requirements ######`
pyinstaller == 3.6
pycryptodome
pyserial