Removed scripts for provisiongs from repository, made fixes per PR comments, adjusted Readme.md

pull/11018/head
Roman Okhrimenko 2019-08-24 01:27:54 +03:00
parent 44401b5917
commit 515555e7ba
51 changed files with 116 additions and 10855 deletions

View File

@ -56,4 +56,3 @@ The Python modules used by Mbed tools are used under the following licenses:
- [psutil](https://pypi.org/project/psutil/) - BSD
- [click](https://pypi.org/project/click/) - BSD-3-Clause
- [cryptography](https://pypi.org/project/cryptography/) - BSD, Apache-2.0
- [jwcrypto](https://pypi.org/project/jwcrypto/) - LGPLv3+

View File

@ -28,5 +28,4 @@ wmi==1.4.9;platform_system=='Windows'
psutil==5.6.2
cryptography>=2.4.x,<2.5
Click>=7.0,<7.1
jwcrypto>=0.6,<0.7
pathlib>=1.0.1

View File

@ -13,5 +13,5 @@
"priv_key_file": "keys/USERAPP_CM4_KEY_PRIV.pem",
"aes_key_file": "keys/image-aes-128.key",
"dev_pub_key_file": "keys/dev_pub_key.pem",
"policy_file": "prepare/policy_single_stage_CM4.json"
"policy_file": "policy/policy_single_stage_CM4.json"
}

View File

@ -1 +1 @@
packet
packet

View File

@ -1,77 +1,9 @@
#### Version of Python required is 3.7+
This directory contains tools and scripts for generating keys, preparing provisioning packets and execution of provisioning.
These files are relevant to CY8CPROTO_064_SB or CY8CPROTO_064_SB_M0_PSA, CY8CPROTO_064_SB_PSA targets.
This directory contains scripts for adding signatures .
These files are relevant to CY8CPROTO_064_SB target.
**_NOTE_:** Detailed description about Secure Boot tools availabe on this link https://www.cypress.com/secureboot-sdk-user-guide
# DEVICE PROVISIONING
## 1. Generate new keys by executing the following commands from ./keys:
*Create keys for image signing:*
python keygen.py -k 8 --jwk USERAPP_CM4_KEY.json --pem-priv USERAPP_CM4_KEY_PRIV.pem
*Create key for image encryption:*
python keygen.py --aes aes.key
**_NOTE_:** DO NOT COMMIT any new keys to repository.
## 2. Create provisioning packets:
Use *provisioning_packet.py* from ./prepare folder.
Options:
--oem <filename> OEM key file.
--hsm <filename> HSM key file.
--cyboot <filename> Cypress Bootloader image certificate.
--cyauth <filename> Provisioning authorization certificate.
--policy <filename> Policy file.
--out <directory_path> Output directory.
--ckey <filename> Customer key that will be used for image signing. Use the option multiple times to specify multiple keys.
--devcert <filename> Chain of trust certificate. Use the option multiple times to specify multiple certificates.
* To create packet for CY8CPROTO_064_SB target using single-stage policy (CM4 only):
python provisioning_packet.py --policy policy_single_stage_CM4.json --out ../packet --cyboot ../prebuild/CyBootloader_Release/CypressBootloader_CM0p.jwt --ckey ../keys/USERAPP_CM4_KEY.json --devcert example_cert.pem
* To use external memory (via SMIF) as staging(upgrade) area (slot_1) of NSPE (CM4) image use policy file with corresponding name:
python provisioning_packet.py --policy policy_single_stage_CM4_smif.json --out ../packet --cyboot ../prebuild/CyBootloader_Release/CypressBootloader_CM0p.jwt --ckey ../keys/USERAPP_CM4_KEY.json --devcert example_cert.pem
The certificate in above examples is signed with OEM key from ./prebuild folder.
Prebuild folder contains CyBootloader_WithLogs and CyBootloader_Release with corresponding *.hex and *.jwt files.
* WithLogs prints execution results to terminal.
* Release does not print to terminal and boots up silently.
**_NOTE:_** CypressBootloader_CM0p.jwt and CypressBootloader_CM0p.hex must be used in pair from the same directory in provisioning packet generation (.packets/prov_cmd.jwt) and provisioning procedure itself.
## 3. Run entrance exam
python entrance_exam_runner.py
## 4. Perform provisioning:
**_ATTENTION:_** Proceed to **UPGRADE IMAGES** section first if UPGRADE image is needed.
Execute *provision_device_runner.py*.
The script will run with the default arguments if no arguments specified.
Default arguments can be overridden with a custom:
--prov-jwt <filename> Path to provisioning JWT file (packet which contains all data necessary for provisioning, including policy, authorization packets and keys)
--hex <filename> Path to Cypress Bootloader HEX binary file
--pubkey-json <filename> File where to save public key in JSON format
--pubkey-pem <filename> File where to save public key in PEM format
*Example:*
python provision_device_runner.py --prov-jwt packet/prov_cmd.jwt --hex prebuild/CyBootloader_Release/CypressBootloader_CM0p.hex --pubkey-json keys/dev_pub_key.json --pubkey-pem keys/dev_pub_key.pem
**_NOTE:_** PSoC6 supply voltage of 2.5V is required to perform provisioning.
**_NOTE_:** Before starting work with Cypress Secure Boot enabled target please read User Guide https://www.cypress.com/secureboot-sdk-user-guide
## UPGRADE IMAGES
@ -87,9 +19,9 @@ The upgrade images types are determined by the following policy setting (firmwar
- **_"encrypt_key_id":_** 1, - should remain unchanged, means that Device Key will be used in ECDH/HKDF protocol
Requirements:
- Policy with **_smif.json** from prepare/ folder should be used.
- Policy with **_smif.json** from policy/ folder should be used.
For encrypted image:
- aes.key generated, as described in **DEVICE_PROVISIONING - 1**
- aes.key generated, as described in user guide
- dev_pub_key.pem must be placed in keys/ folder (this key is generated in provisioning procedure)
- secure_image_parameters.json file in the target directory must contain valid keys' paths
@ -110,7 +42,7 @@ Encrypted UPGRADE image:
"encrypt": true,
"encrypt_key_id": 1,
Modified policy file should be used for provisioning the device, as described in paragraph 4.
Modified policy file should be used for provisioning the device, as described in User Guide.
Now mbed-os application or test can be built as described in section **TESTS**. Images for UPGRADE are generated at build time, according to policy.
@ -140,11 +72,3 @@ The generic HEX file (for example one that is produced by mbed-os build system)
Run commands:
mbed test --compile -m CY8CPROTO_064_SB -t GCC_ARM -n tests-mbed* -v
# TROUBLESHOOTING:
1. In case of messages like "unable to find device" execute "mbedls -m 1907:CY8CPROTO_064_SB", then check with "mbedls" if device is detected as CY8CPROTO_064_SB with code 1907.
2. Keys, from ./keys folder is used for signing images by default, these keys should be used for provisioning.
3. Consider using CyBootloader from CyBootloader_WithLogs folder. It produces logs, which are useful to understand whether CyBootloader works correctly.
4. When running application with SMIF and _smif.json policy the field "smif_id" should be set to 1 for CY8CPROTO_064_SB.
5. Low frequency quartz (32768 Hz) oscillator have to be soldered on CY8CPROTO_064_SB (not present in stock version of board).

View File

@ -1,45 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
from execute.enums import DebugCore
from execute.helper import get_target_name
from execute.programmer.programmer import ProgrammingTool
from execute.entrance_exam import entrance_exam
TOOL_NAME = 'pyocd' # Programming/debugging tool used for communication with device
ACCESS_PORT = DebugCore.debug_sys_ap # Access port used for entrance exam
def main():
"""
Provides high level support for entrance exam procedure.
"""
target = get_target_name(TOOL_NAME, ACCESS_PORT)
if not target:
print('Invalid access port.')
sys.exit(1)
status = False
tool = ProgrammingTool.create(TOOL_NAME)
if tool.connect(target):
status = entrance_exam(tool)
if not status:
sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -1,68 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from time import sleep
from execute.helper import check_mode
from execute.enums import ProtectionState
from execute.gen_data_from_json import ENTRANCE_EXAM_FW_STATUS_REG, ENTRANCE_EXAM_FW_STATUS_MASK, \
ENTRANCE_EXAM_FW_STATUS_VAL, ENTRANCE_EXAM_REGION_HASH_ADDR, ENTRANCE_EXAM_REGION_HASH_SIZE, \
ENTRANCE_EXAM_REGION_HASH_MODE, ENTRANCE_EXAM_REGION_HASH_EXPECTED_VAL
from execute.sys_call import region_hash
def entrance_exam(tool):
"""
Checks device life-cycle, Flashboot firmware and Flash state.
:param tool: Programming/debugging tool used for communication with device.
:return: True if entrance exam passed, otherwise False.
"""
# Check the device life-cycle stage
print('Check device protection state:')
if not check_mode(tool, ProtectionState.secure):
return False
# Check if any firmware is launched by FlashBoot and running on the device
print(os.linesep + 'Read Flashboot firmware status:')
fb_firmware_status = tool.read32(ENTRANCE_EXAM_FW_STATUS_REG)
print(f'FB Firmware status = {hex(fb_firmware_status)}')
print(f'Received FB_FW_STATUS = {hex(fb_firmware_status & ENTRANCE_EXAM_FW_STATUS_MASK)}')
print(f'Expected FB_FW_STATUS = {hex(ENTRANCE_EXAM_FW_STATUS_VAL)}')
is_exam_pass = (fb_firmware_status & ENTRANCE_EXAM_FW_STATUS_MASK) == ENTRANCE_EXAM_FW_STATUS_VAL
if is_exam_pass:
print('PASS: FB Firmware status is as expected')
else:
print('FAIL: FB Firmware status is not as expected')
if fb_firmware_status == 0xA1000100:
print('Test firmware exists and running on CM4 core. Device is in SECURE UNCLAIMED mode')
elif fb_firmware_status == 0xA1000101:
print('Secure firmware exists and running on CM0p core. Device is in SECURE CLAIMED mode')
return False
# Check flash for malicious firmware
print(os.linesep + 'Check if Main Flash of the device is empty:')
if region_hash(tool, ENTRANCE_EXAM_REGION_HASH_ADDR, ENTRANCE_EXAM_REGION_HASH_SIZE,
ENTRANCE_EXAM_REGION_HASH_MODE, ENTRANCE_EXAM_REGION_HASH_EXPECTED_VAL):
print('PASS: Flash value is as expected')
print()
print('*****************************************')
print(' ENTRANCE EXAM TEST PASSED ')
print('*****************************************')
tool.reset()
sleep(0.2)
else:
print('FAIL: Flash value is not as expected')
return False
return True

View File

@ -1,30 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from enum import IntEnum, Enum
class DebugCore(Enum):
"""
Provides set of access ports for programming/debugging.
"""
debug_cm0_ap, debug_cm4_ap, debug_sys_ap = range(3)
class ProtectionState(IntEnum):
"""
Provides set of device life-cycle stages.
"""
unknown, virgin, normal, secure, dead = range(5)

View File

@ -1,38 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#################################################################################
# ENTRANCE EXAM REGISTER AND VARIABLE CONSTANTS
#################################################################################
ENTRANCE_EXAM_EFUSE_ASSET_HASH_BASE_REG = 0x402C0840
ENTRANCE_EXAM_ASSET_HASH_LEN = 16
ENTRANCE_EXAM_ASSET_HASH_ZEROS = 72
ENTRANCE_EXAM_ASSET_HASH_EXPECTED_STR = "0xD9 0x1B 0x11 0xAA 0x5D 0x75 0x68 0x6A " \
"0x12 0x10 0xE0 0x88 0x38 0xE1 0x2B 0x79"
ENTRANCE_EXAM_LIFECYCLE_STAGE_REG = 0x402C082b
ENTRANCE_EXAM_LIFECYCLE_STAGE_EXPECTED_VAL = 0x01
ENTRANCE_EXAM_FW_STATUS_REG = 0x08044800
ENTRANCE_EXAM_FW_STATUS_VAL = 0xF0000000
ENTRANCE_EXAM_FW_STATUS_MASK = 0xF0800000
ENTRANCE_EXAM_SRAM_ADDR = 0x0802c000
ENTRANCE_EXAM_SRAM_SIZE = 0x00004000
ENTRANCE_EXAM_REGION_HASH_ADDR = 0x10000000
ENTRANCE_EXAM_REGION_HASH_SIZE = 0x000e0000
ENTRANCE_EXAM_REGION_HASH_MODE = 255
ENTRANCE_EXAM_REGION_HASH_EXPECTED_VAL = 0x00

View File

@ -1,59 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from execute.enums import DebugCore, ProtectionState
from execute.p6_reg import CYREG_CPUSS_PROTECTION, CYREG_IPC2_STRUCT_DATA, CYREG_EFUSE_SECURE_HASH
def check_mode(tool, expected_mode: ProtectionState):
"""
Checks device protection state and compares with the expected.
:return: The device protection state.
"""
mode_name = expected_mode.name.upper()
if tool.read32(CYREG_CPUSS_PROTECTION) != int(expected_mode):
print(f'FAIL: Device is not in {mode_name} mode, error code: {hex(tool.read32(CYREG_IPC2_STRUCT_DATA))}')
print('Read Secure Hash from eFUSEs:') # 00 expected on virgin device
got_factory_hash = ''
i = 0
while i < 24:
hash_byte_val = hex(tool.read8(CYREG_EFUSE_SECURE_HASH + i))
got_factory_hash += hash_byte_val + ' '
i += 1
print(f"Received SECURE_HASH: '{got_factory_hash}'")
return False
print(f'PASS: Device is in {mode_name} mode')
return True
def get_target_name(tool_name, access_port):
"""
Gets name of the target based on programming tool and access port.
:param tool_name: The name of programming/debugging tool.
:param access_port: The access port used for communication.
:return: The target name.
"""
if tool_name == 'pyocd':
if access_port == DebugCore.debug_cm0_ap:
target = 'cy8c64xx_cm0'
elif access_port == DebugCore.debug_cm4_ap:
target = 'cy8c64xx_cm4'
elif access_port == DebugCore.debug_sys_ap:
target = 'cy8c64xx_cm4_full'
else:
raise ValueError(f"Unhandled access port value: '{access_port}'")
else:
raise ValueError(f"Unhandled programming tool: '{tool_name}'")
return target

View File

@ -1,17 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
FLASH_ADDRESS = 0x10000000
FLASH_SIZE = 0x000e0000

View File

@ -1,95 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#################################################################################
# PSOC6 BLE REGISTER ADDRESSES
#################################################################################
CYREG_IPC2_STRUCT_ACQUIRE = 0x40230040 # 0x40220040 2M
CYREG_IPC2_STRUCT_NOTIFY = 0x40230048
CYREG_IPC2_STRUCT_DATA = 0x4023004C
CYREG_IPC2_STRUCT_DATA1 = 0x40230050 # for 2M only
CYREG_IPC2_STRUCT_LOCK_STATUS = 0x40230050 # 0x4023005C
CYREG_CPUSS_PROTECTION = 0x40210500 # 0x4023005C 2M
CYREG_EFUSE_FACTORY_HASH = 0x402c082c
CYREG_EFUSE_LIFECYCLE_STAGE = 0x402c082b
CYREG_EFUSE_SECURE_HASH = 0x402c0814
CYREG_EFUSE_LAST_BYTE = 0x402c0879
NVSTORE_AREA_1_ADDRESS = 0x100FB600
NVSTORE_OEM_ADDRESS = 0x100FFA00
NVSTORE_DEV_KEY_ADDRESS = 0x100FFC00
NVSTORE_UDS_ADDRESS = 0x100FFE00
#################################################################################
# PSOC6 BLE SFLASH ADDRESSES
#################################################################################
SFLASH_TOC1_ADDR = 0x16007800
SFLASH_TOC1_LEN = 0x1C
SFLASH_TOC2_ADDR = 0x16007c00
SFLASH_TOC2_LEN = 0x30
TOC1_ROW_IDX = 60
TOC1_DUPL_ROW_IDX = 61
TOC2_ROW_IDX = 62
TOC2_DUPL_ROW_IDX = 63
TOC1_BYTE_SIZE = 0x1C # Stored as 1st word of TOC1
MAGIC_NUMBER1 = 0x01211219 # Stored as 2nd word of TOC1
NUM_OBJECTS_TOC1 = 0x4 # Stored as 3rd word of TOC1
SFLASH_TRIM_START_ADDR = 0x16000200 # Stored as 4th word of TOC1
SFLASH_HV_PARAM_TABLE_ADDR = 0x16000400
SRAM_HV_PARAM_TABLE_ADDR = 0x08001000 # Only for *C PSoC6ABLE2 silicon
SFLASH_UNIQUEID_START_ADDR = 0x16000600 # Stored as 5th word of TOC1
FLASHBOOT_START_ADDR = 0x16002000 # Stored as 6th word of TOC1
SFLASH_SYSCALL_TABLE_PTR_ADDR = 0x16000004 # Stored as 7th word of TOC1
SFLASH_SYSCALL_TABLE_PTR = 0x16004100
SFLASH_SKIP_HASH = 0x16000008
SFLASH_FLL_CONTROL = 0x16000008
SFLASH_NORMAL_ACCESS_CTL = 0x16000008
MAGIC_NUMBER2 = 0x01211220
SFLASH_FLL_EN_IDX = 0x2
#################################################################################
# CONSTANTS AND DEFINITIONS
#################################################################################
# any random SRAM address. Fixing it to 0x2000 for all sram_scratch purposes.
# SRAM_SCRATCH = 0x08046000
# SRAM_SCRATCH_SIZE = 0x00000800
FLASH_START_ADDR = 0x10000000
FLASH_SHA256_STR_ADDR = 0x10000100
FLASH_SHA256_STR_LEN = 12
FLASH_SHA256_DIG_ADDR = 0x10000110
FLASH_SIZE = 0x200000 # 2 MB for PSoC 6A-2M
SFLASH_START_ADDR = 0x16000000
SFLASH_SIZE = 0x8000
GENERAL_TRIM_TABLE_HASH_ADDR = 0x16000200
TRIM_START_ADDR = 0x16000203
TRIM_TABLE_LEN_ADDR = 0x16000200
WFLASH_START_ADDR = 0x14000000
WFLASH_SIZE = 0x8000
SRAM_START_ADDR = 0x08000000
SRAM_SIZE = 0x48000
# SRAM_PUB_ADDR = 0x08046800 # 2M: 0x080ff400
ADDR_FMPARAMSTRUCT = 0x08000800
FLASH_ROW_SIZE = 512
# Number of trials
TRIAL_MAX = 300
SYSTEM_RESET = 0x0
ONLY_CM4_RESET = 0x1
INVALIDRESETTYPE = 0x3
R_ACCESS = 0x01
W_ACCESS = 0x02
X_ACCESS = 0x04
NIL_ACCESS = 0x00

View File

@ -1,18 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from . import base
from . import programmer
from . import pyocd_wrapper

View File

@ -1,179 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from enum import Enum
from abc import ABCMeta, abstractmethod
class ResetType(Enum):
SW = 1,
HW = 2
class Interface(Enum):
SWD = 1
JTAG = 2
class ProgrammerBase(metaclass=ABCMeta):
def __init__(self):
pass
@abstractmethod
def connect(self, target_name=None, interface=None):
"""
Connects to target.
:param target_name: The target name.
:param interface: Debug interface.
:return: True if connected successfully, otherwise False.
"""
raise NotImplementedError()
@abstractmethod
def disconnect(self):
"""
Disconnects from target.
"""
raise NotImplementedError()
@abstractmethod
def set_frequency(self, value_khz):
"""
Sets probe frequency.
:param value_khz: Frequency in kHz.
"""
raise NotImplementedError()
@abstractmethod
def halt(self):
"""
Halts the target.
"""
raise NotImplementedError()
@abstractmethod
def resume(self):
"""
Resumes the execution
"""
raise NotImplementedError()
@abstractmethod
def reset(self, reset_type=ResetType.SW):
"""
Resets the target.
:param reset_type: The reset type.
"""
raise NotImplementedError()
@abstractmethod
def reset_and_halt(self, reset_type=ResetType.SW):
"""
Resets the target and halts the CPU immediately after reset.
:param reset_type: The reset type.
"""
raise NotImplementedError()
@abstractmethod
def read8(self, address):
"""
Reads 8-bit value from specified memory location.
:param address: The memory address to read.
:return: The read value.
"""
raise NotImplementedError()
@abstractmethod
def read16(self, address):
"""
Reads 16-bit value from specified memory location.
:param address: The memory address to read.
:return: The read value.
"""
raise NotImplementedError()
@abstractmethod
def read32(self, address):
"""
Reads 32-bit value from specified memory location.
:param address: The memory address to read.
:return: The read value.
"""
raise NotImplementedError()
@abstractmethod
def write8(self, address, value):
"""
Writes 8-bit value by specified memory location.
:param address: The memory address to write.
:param value: The 8-bit value to write.
"""
raise NotImplementedError()
@abstractmethod
def write16(self, address, value):
"""
Writes 16-bit value by specified memory location.
:param address: The memory address to write.
:param value: The 16-bit value to write.
"""
raise NotImplementedError()
@abstractmethod
def write32(self, address, value):
"""
Writes 32-bit value by specified memory location.
:param address: The memory address to write.
:param value: The 32-bit value to write.
"""
raise NotImplementedError()
@abstractmethod
def read_reg(self, reg_name):
"""
Gets value of a core register.
:param reg_name: Core register name.
:return: The register value.
"""
raise NotImplementedError()
@abstractmethod
def write_reg(self, reg_name, value):
"""
Sets value of a core register.
:param reg_name: Core register name.
:param value: The value to set.
"""
raise NotImplementedError()
@abstractmethod
def erase(self, address, size):
"""
Erases entire device flash or specified sectors.
:param address: The memory location.
:param size: The memory size.
"""
raise NotImplementedError()
@abstractmethod
def program(self, filename, file_format=None, address=None):
"""
Programs a file into flash.
:param filename: Path to a file.
:param file_format: File format. Default is to use the file's extension.
:param address: Base address used for the address where to flash a binary.
:return: True if programmed successfully, otherwise False.
"""
raise NotImplementedError()

View File

@ -1,23 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from pyocd.core.exceptions import TransferFaultError
class ExtendedTransferFaultError(TransferFaultError):
def __str__(self):
desc = super().__str__()
desc += ' If address points to a register it should be aligned with the register size.'
return desc

View File

@ -1,29 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from execute.programmer.pyocd_wrapper import Pyocd
tools = {
'pyocd': Pyocd
}
class ProgrammingTool:
@staticmethod
def create(name):
tool_type = tools[name]
tool = tool_type()
return tool

View File

@ -1,272 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from execute.programmer.base import ProgrammerBase, Interface, ResetType
from pyocd.core.helpers import ConnectHelper
from pyocd.core import exceptions
from pyocd.flash import loader
from pyocd.flash.loader import FlashEraser
from pyocd import coresight
from execute.programmer.exceptions import ExtendedTransferFaultError
class Pyocd(ProgrammerBase):
def __init__(self):
super(Pyocd, self).__init__()
self.session = None
self.board = None
self.target = None
self.probe = None
def connect(self, target_name=None, interface=None, probe_id=None):
"""
Connects to target using default debug interface.
:param target_name: The target name.
:param interface: Debug interface.
:param probe_id: Probe serial number.
:return: True if connected successfully, otherwise False.
"""
if interface:
raise NotImplementedError
else:
if target_name:
options = {
'target_override': target_name
}
else:
options = {}
self.session = ConnectHelper.session_with_chosen_probe(blocking=True, options=options, board_id=probe_id, unique_id=probe_id)
if self.session is None:
return False
self.board = self.session.board
try:
self.session.open()
except exceptions.TransferFaultError as e:
if not self.board.target.is_locked():
print(f"Transfer fault while initializing board: {e}")
return False
except Exception as e:
print(f"Exception while initializing board: {e}")
return False
self.target = self.board.target
self.probe = self.session.probe
# Write infinite loop into RAM and start core execution
self.halt()
# B662 - CPSIE I - Enable IRQ by clearing PRIMASK
# E7FE - B - Jump to address (argument is an offset)
self.write32(0x08000000, 0xE7FEB662)
self.write_reg('pc', 0x08000000)
self.write_reg('sp', 0x08001000)
self.write_reg('xpsr', 0x01000000)
self.resume()
return True
def disconnect(self):
"""
Closes active connection.
"""
if self.session is None:
raise ValueError('Debug session is not initialized.')
self.session.close()
def set_frequency(self, value_khz):
"""
Sets probe frequency.
:param value_khz: Frequency in kHz.
"""
if self.probe is None:
raise ValueError('Debug probe is not initialized.')
self.probe.set_clock(value_khz * 1000)
def halt(self):
"""
Halts the target.
"""
if self.session is None:
raise ValueError('Debug session is not initialized.')
self.target.halt()
def resume(self):
"""
Resumes the execution
"""
if self.target is None:
raise ValueError('Target is not initialized.')
self.target.resume()
def reset(self, reset_type=ResetType.SW):
"""
Resets the target.
:param reset_type: The reset type.
"""
if self.target is None:
raise ValueError('Target is not initialized.')
self.target.reset(reset_type=reset_type)
def reset_and_halt(self, reset_type=ResetType.SW):
"""
Resets the target and halts the CPU immediately after reset.
:param reset_type: The reset type.
"""
if self.target is None:
raise ValueError('Target is not initialized.')
self.target.reset_and_halt(reset_type=reset_type)
def read8(self, address):
"""
Reads 8-bit value from specified memory location.
:param address: The memory address to read.
:return: The read value.
"""
if self.target is None:
raise ValueError('Target is not initialized.')
try:
data = self.target.read_memory(address, transfer_size=8)
except exceptions.TransferFaultError as e:
raise ExtendedTransferFaultError(e.fault_address, e.fault_length)
return data
def read16(self, address):
"""
Reads 16-bit value from specified memory location.
:param address: The memory address to read.
:return: The read value.
"""
if self.target is None:
raise ValueError('Target is not initialized.')
if (address & 0x01) == 0:
try:
data = self.target.read_memory(address, transfer_size=16)
except exceptions.TransferFaultError as e:
raise ExtendedTransferFaultError(e.fault_address, e.fault_length)
return data
else:
raise ValueError('Address not aligned.')
def read32(self, address):
"""
Reads 32-bit value from specified memory location.
:param address: The memory address to read.
:return: The read value.
"""
if self.target is None:
raise ValueError('Target is not initialized.')
if (address & 0x03) == 0:
try:
data = self.target.read_memory(address, transfer_size=32)
except exceptions.TransferFaultError as e:
raise ExtendedTransferFaultError(e.fault_address, e.fault_length)
return data
else:
raise ValueError('Address not aligned.')
def write8(self, address, value):
"""
Writes 8-bit value by specified memory location.
:param address: The memory address to write.
:param value: The 8-bit value to write.
"""
if self.target is None:
raise ValueError('Target is not initialized.')
try:
data = self.target.write_memory(address, value, transfer_size=8)
except exceptions.TransferFaultError as e:
raise ExtendedTransferFaultError(e.fault_address, e.fault_length)
return data
def write16(self, address, value):
"""
Writes 16-bit value by specified memory location.
:param address: The memory address to write.
:param value: The 16-bit value to write.
"""
if self.target is None:
raise ValueError('Target is not initialized.')
try:
data = self.target.write_memory(address, value, transfer_size=16)
except exceptions.TransferFaultError as e:
raise ExtendedTransferFaultError(e.fault_address, e.fault_length)
return data
def write32(self, address, value):
"""
Writes 32-bit value by specified memory location.
:param address: The memory address to write.
:param value: The 32-bit value to write.
"""
if self.target is None:
raise ValueError('Target is not initialized.')
try:
data = self.target.write_memory(address, value, transfer_size=32)
except exceptions.TransferFaultError as e:
raise ExtendedTransferFaultError(e.fault_address, e.fault_length)
return data
def read_reg(self, reg_name):
"""
Gets value of a core register.
:param reg_name: Core register name.
:return: The register value.
"""
reg = reg_name.lower()
if reg in coresight.cortex_m.CORE_REGISTER:
value = self.target.read_core_register(reg)
return value
else:
raise ValueError(f'Unknown core register {reg}.')
def write_reg(self, reg_name, value):
"""
Sets value of a core register.
:param reg_name: Core register name.
:param value: The value to set.
:return: The register value.
"""
reg = reg_name.lower()
if reg in coresight.cortex_m.CORE_REGISTER:
self.target.write_core_register(reg, value)
else:
raise ValueError(f'Unknown core register {reg}.')
def erase(self, address, size):
"""
Erases entire device flash or specified sectors.
:param address: The memory location.
:param size: The memory size.
"""
region = self.session.target.memory_map.get_region_for_address(address)
if not region:
raise ValueError('Address 0x%08x is not within a memory region.' % address)
if not region.is_flash:
raise ValueError('Address 0x%08x is not in flash.' % address)
eraser = FlashEraser(self.session, FlashEraser.Mode.SECTOR)
address_range = f"{hex(address)}-{hex(address + size)}"
eraser.erase([address_range])
def program(self, filename, file_format=None, address=None):
"""
Programs a file into flash.
:param filename: Path to a file.
:param file_format: File format. Default is to use the file's extension.
:param address: Base address used for the address where to flash a binary.
:return: True if programmed successfully, otherwise False.
"""
if self.session is None:
raise ValueError('Debug session is not initialized.')
programmer = loader.FileProgrammer(self.session, chip_erase='sector')
programmer.program(filename, base_address=address, file_format=file_format)

View File

@ -1,105 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from time import sleep
from execute.helper import check_mode
from execute.enums import ProtectionState
from execute.sys_call import get_prov_details, provision_keys_and_policies
from execute.p6_memory_map import FLASH_ADDRESS, FLASH_SIZE
from execute.gen_data_from_json import ENTRANCE_EXAM_FW_STATUS_REG, ENTRANCE_EXAM_FW_STATUS_MASK, \
ENTRANCE_EXAM_FW_STATUS_VAL
from execute.p6_reg import CYREG_CPUSS_PROTECTION, NVSTORE_AREA_1_ADDRESS
def provision_execution(tool, pub_key_json, prov_cmd_jwt, cy_bootloader_hex):
"""
Programs Cypress Bootloader and calls system calls for device provisioning.
:param tool: Programming/debugging tool used for communication with device.
:param pub_key_json: File where to save public key in JSON format.
:param prov_cmd_jwt: Path to provisioning JWT file (packet which contains
all data necessary for provisioning, including policy, authorization
packets and keys).
:param cy_bootloader_hex: Path to Cypress Bootloader program file.
:return: True if provisioning passed, otherwise False.
"""
tool.set_frequency(200)
print("CPUSS.PROTECTION state: '0': UNKNOWN. '1': VIRGIN. '2': NORMAL. '3': SECURE. '4': DEAD.")
print(hex(CYREG_CPUSS_PROTECTION), hex(tool.read32(CYREG_CPUSS_PROTECTION)))
reset_device(tool)
result, key = get_prov_details(tool, 1)
print('Device public key has been read successfully.' if result else 'FAIL: Cannot read device public key.')
print(key)
with open(os.path.join(pub_key_json), 'w') as json_file:
json_file.write(key)
# Check the device life-cycle stage
print('Check device protection state')
if not check_mode(tool, ProtectionState.secure):
return False
print(os.linesep + 'Erase main flash and TOC3:')
print('erasing...')
tool.erase(FLASH_ADDRESS, FLASH_SIZE)
reset_device(tool)
print(os.linesep + 'Read FB Firmware status:')
fb_firmware_status = tool.read32(ENTRANCE_EXAM_FW_STATUS_REG)
print(f'FB Firmware status = {hex(fb_firmware_status)}')
# Print Expected and received LIFECYCLE_STAGE values
print(f'Received FB_FW_STATUS = {hex(fb_firmware_status & ENTRANCE_EXAM_FW_STATUS_MASK)}')
print(f'Expected FB_FW_STATUS = {hex(ENTRANCE_EXAM_FW_STATUS_VAL)}')
# Verify if received value is the same as expected
is_exam_pass = (fb_firmware_status & ENTRANCE_EXAM_FW_STATUS_MASK) == ENTRANCE_EXAM_FW_STATUS_VAL
print('PASS: FB Firmware status is as expected' if is_exam_pass else 'FAIL: FB Firmware status is not as expected')
if is_exam_pass:
print(os.linesep + 'PROGRAMMING APP HEX:')
tool.program(cy_bootloader_hex)
reset_device(tool)
if is_exam_pass:
print(os.linesep + 'Run provisioning syscall')
blow_secure_fuse = 1 # indicates whether to convert device to SECURE CLAIMED mode
is_exam_pass = provision_keys_and_policies(tool, blow_secure_fuse, os.path.join(prov_cmd_jwt))
print(hex(NVSTORE_AREA_1_ADDRESS) + ': ', sep=' ', end='', flush=True)
if is_exam_pass:
i = 0
while i < 8 * 4: # output 8 words
print(hex(tool.read32(NVSTORE_AREA_1_ADDRESS + i)) + ' ', sep=' ', end='', flush=True)
i += 4
print(os.linesep)
else:
print('FAIL: Unexpected ProvisionKeysAndPolicies syscall response')
if is_exam_pass:
print('*****************************************')
print(" PROVISIONING PASSED ")
print("*****************************************")
reset_device(tool)
return is_exam_pass
def reset_device(tool):
"""
Resets device and waits for device initialization.
:param tool: Programming/debugging tool used for communication with device.
"""
tool.reset()
sleep(0.2)

View File

@ -1,198 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from execute.gen_data_from_json import ENTRANCE_EXAM_SRAM_ADDR, ENTRANCE_EXAM_SRAM_SIZE
from execute.p6_reg import CYREG_IPC2_STRUCT_ACQUIRE, CYREG_IPC2_STRUCT_DATA, CYREG_IPC2_STRUCT_NOTIFY, \
CYREG_IPC2_STRUCT_LOCK_STATUS
PROVISION_KEYS_AND_POLICIES_OPCODE = 0x33 # ProvisionKeysAndPolicies API opcode
GET_PROV_DETAILS_OPCODE = 0x37 # GetProvDetails() API opcode
REGION_HASH_OPCODE = 0x31 # RegionHash() API opcode
def region_hash(tool, address, length, mode, exp_value):
"""
Procedure calls RegionHash syscall over IPC and read response.
:param tool: Programming/debugging tool used for communication with device.
:param address: Region hash address.
:param length: Region hash size.
:param mode: Region hash mode.
:param exp_value: Region hash expected value.
:return: True if syscall executed successfully, otherwise False.
"""
# Acquire IPC structure
tool.write32(CYREG_IPC2_STRUCT_ACQUIRE, 0x80000000)
ipc_acquire = 0
while (ipc_acquire & 0x80000000) == 0:
ipc_acquire = tool.read32(CYREG_IPC2_STRUCT_ACQUIRE)
# Set RAM address and Opcode
op_code = (REGION_HASH_OPCODE << 24) + (exp_value << 16) + (mode << 8) + 0
tool.write32(CYREG_IPC2_STRUCT_DATA, ENTRANCE_EXAM_SRAM_ADDR)
tool.write32(ENTRANCE_EXAM_SRAM_ADDR, op_code)
scratch_addr = ENTRANCE_EXAM_SRAM_ADDR + 0x08
tool.write32(ENTRANCE_EXAM_SRAM_ADDR + 0x04, scratch_addr)
tool.write32(ENTRANCE_EXAM_SRAM_ADDR + 0x08, length)
tool.write32(ENTRANCE_EXAM_SRAM_ADDR + 0x0C, address)
# IPC_STRUCT[ipc_id].IPC_NOTIFY -
tool.write32(CYREG_IPC2_STRUCT_NOTIFY, 0x00000001)
# Wait on response
response = 0x80000000
while (response & 0x80000000) != 0:
response = tool.read32(CYREG_IPC2_STRUCT_LOCK_STATUS)
response = tool.read32(ENTRANCE_EXAM_SRAM_ADDR)
if (response & 0xFF000000) == 0xa0000000:
print('Region compare complete')
return True
else:
print('Region compare error response:')
print(hex(CYREG_IPC2_STRUCT_DATA), hex(tool.read32(CYREG_IPC2_STRUCT_DATA)))
print(hex(ENTRANCE_EXAM_SRAM_ADDR), hex(tool.read32(ENTRANCE_EXAM_SRAM_ADDR)))
print(hex(ENTRANCE_EXAM_SRAM_ADDR + 0x04), hex(tool.read32(ENTRANCE_EXAM_SRAM_ADDR + 0x04)))
print(hex(ENTRANCE_EXAM_SRAM_ADDR + 0x08), hex(tool.read32(ENTRANCE_EXAM_SRAM_ADDR + 0x08)))
print(hex(ENTRANCE_EXAM_SRAM_ADDR + 0x0C), hex(tool.read32(ENTRANCE_EXAM_SRAM_ADDR + 0x0C)))
return False
def get_prov_details(tool, key_id):
"""
Calls GetProvDetails syscall over IPC.
:param tool: Programming/debugging tool used for communication with device.
:param key_id: Public key ID.
:return: True if get provision details successfully, otherwise False.
"""
# Acquire IPC structure
tool.write32(CYREG_IPC2_STRUCT_ACQUIRE, 0x80000000)
print(hex(CYREG_IPC2_STRUCT_ACQUIRE), hex(tool.read32(CYREG_IPC2_STRUCT_ACQUIRE)))
ipc_acquire = 0
while (ipc_acquire & 0x80000000) == 0:
ipc_acquire = tool.read32(CYREG_IPC2_STRUCT_ACQUIRE)
# Set RAM address and Opcode
op_code = GET_PROV_DETAILS_OPCODE << 24
tool.write32(CYREG_IPC2_STRUCT_DATA, ENTRANCE_EXAM_SRAM_ADDR) # IPC_STRUCT.DATA
tool.write32(ENTRANCE_EXAM_SRAM_ADDR, op_code) # SRAM_SCRATCH
scratch_addr = ENTRANCE_EXAM_SRAM_ADDR + 0x08
tool.write32(ENTRANCE_EXAM_SRAM_ADDR + 0x04, scratch_addr)
tool.write32(ENTRANCE_EXAM_SRAM_ADDR + 0x08, key_id)
tool.write32(ENTRANCE_EXAM_SRAM_ADDR + 0x0C, 0x0)
# IPC_STRUCT[ipc_id].IPC_NOTIFY -
tool.write32(CYREG_IPC2_STRUCT_NOTIFY, 0x00000001)
# Wait on response
response = 0x80000000
while (response & 0x80000000) != 0:
response = tool.read32(CYREG_IPC2_STRUCT_LOCK_STATUS)
response = tool.read32(ENTRANCE_EXAM_SRAM_ADDR)
print(hex(CYREG_IPC2_STRUCT_DATA), hex(tool.read32(CYREG_IPC2_STRUCT_DATA)))
print(hex(ENTRANCE_EXAM_SRAM_ADDR), hex(tool.read32(ENTRANCE_EXAM_SRAM_ADDR))) # Expected MSB=0xA0
print(hex(ENTRANCE_EXAM_SRAM_ADDR + 0x04), hex(tool.read32(ENTRANCE_EXAM_SRAM_ADDR + 0x04)))
print(hex(ENTRANCE_EXAM_SRAM_ADDR + 0x08), hex(tool.read32(ENTRANCE_EXAM_SRAM_ADDR + 0x08)))
is_exam_pass = (response & 0xFF000000) == 0xa0000000
if is_exam_pass:
scratch_addr = tool.read32(ENTRANCE_EXAM_SRAM_ADDR + 0x04)
read_hash_size = tool.read32(scratch_addr + 0x00)
read_hash_addr = tool.read32(scratch_addr + 0x04)
i = 0
response = ''
while i < read_hash_size:
# Save data in string format
hash_byte_chr = chr(tool.read8(read_hash_addr + i))
response += hash_byte_chr
i += 1
response = response.strip()
else:
print(hex(CYREG_IPC2_STRUCT_DATA), tool.read32(CYREG_IPC2_STRUCT_DATA))
print(hex(ENTRANCE_EXAM_SRAM_ADDR), tool.read32(ENTRANCE_EXAM_SRAM_ADDR))
response = None
return is_exam_pass, response
def provision_keys_and_policies(tool, blow_secure_efuse, filename):
"""
Calls ProvisionKeysAndPolicies syscall over IPC.
:param tool: Programming/debugging tool used for communication with device.
:param blow_secure_efuse: Indicates whether to convert device to SECURE CLAIMED mode.
:param filename: Path to provisioning JWT file (packet which contains
all data necessary for provisioning, including policy, authorization
packets and keys).
:return: True if sending provision keys and policies passed, otherwise False
"""
file_size = os.path.getsize(filename)
if file_size > ENTRANCE_EXAM_SRAM_SIZE:
print('JWT packet too long')
return False
print('UDS eFuses will be blown' if blow_secure_efuse == 1 else 'UDS eFuses will NOT be blown')
print(f'JWT packet size: {file_size}')
with open(filename, 'r+') as jwt_file:
jwt_file.seek(0)
content = jwt_file.read()
jwt_chars = list(content)
# Acquires IPC structure.
tool.write32(CYREG_IPC2_STRUCT_ACQUIRE, 0x80000000)
print(hex(CYREG_IPC2_STRUCT_ACQUIRE), hex(tool.read32(CYREG_IPC2_STRUCT_ACQUIRE)))
ipc_acquire = 0
while (ipc_acquire & 0x80000000) == 0:
ipc_acquire = tool.read32(CYREG_IPC2_STRUCT_ACQUIRE)
# Set RAM address and Opcode
tool.write32(CYREG_IPC2_STRUCT_DATA, ENTRANCE_EXAM_SRAM_ADDR)
tool.write32(ENTRANCE_EXAM_SRAM_ADDR, (PROVISION_KEYS_AND_POLICIES_OPCODE << 24) + (blow_secure_efuse << 16))
scratch_addr = ENTRANCE_EXAM_SRAM_ADDR + 0x08
tool.write32(ENTRANCE_EXAM_SRAM_ADDR + 0x04, scratch_addr)
tool.write32(ENTRANCE_EXAM_SRAM_ADDR + 0x08, file_size + 0x04)
scratch_addr = ENTRANCE_EXAM_SRAM_ADDR + 0x0C
for char in jwt_chars:
tool.write8(scratch_addr, ord(char))
scratch_addr += 1
# IPC_STRUCT[ipc_id].IPC_NOTIFY -
tool.write32(CYREG_IPC2_STRUCT_NOTIFY, 0x00000001)
print(hex(CYREG_IPC2_STRUCT_NOTIFY), hex(tool.read32(CYREG_IPC2_STRUCT_NOTIFY)))
# Wait on response
response = 0x80000000
while (response & 0x80000000) != 0:
response = tool.read32(CYREG_IPC2_STRUCT_LOCK_STATUS)
# Read response for test
print(hex(CYREG_IPC2_STRUCT_DATA), hex(tool.read32(CYREG_IPC2_STRUCT_DATA)))
print(hex(ENTRANCE_EXAM_SRAM_ADDR) + ': ', sep=' ', end='', flush=True)
i = 0
while i < 4 * 4: # output 4 words
print(hex(tool.read32(ENTRANCE_EXAM_SRAM_ADDR)) + ' ', sep=' ', end='', flush=True)
i += 4
print(os.linesep)
response = tool.read32(ENTRANCE_EXAM_SRAM_ADDR)
result = (response & 0xFF000000) == 0xa0000000
print('ProvisionKeysAndPolicies', 'complete' if result else f'error response: {hex(response)}')
return result

View File

@ -1,73 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import click
import json
from jwcrypto import jwk
CUSTOMER_KEY_ID_MIN = 6
CUSTOMER_KEY_ID_MAX = 10
kid_help = 'Key ID to define key slot number in the key storage. Key ID must be in range {}.'
@click.command()
@click.option('-k', '--kid', 'kId',
type=click.IntRange(CUSTOMER_KEY_ID_MIN, CUSTOMER_KEY_ID_MAX),
default=CUSTOMER_KEY_ID_MIN,
help=kid_help.format(str(list(range(CUSTOMER_KEY_ID_MIN, CUSTOMER_KEY_ID_MAX+1)))))
@click.option('--jwk', 'jwKey',
type=click.File('w'),
default='key.json',
help='Name of the key in JWK format to create.')
@click.option('--pem-priv', 'pemPriv',
type=click.File('wb'),
default=None,
help='Name of the private key in PEM format to create. If it is not given PEM file will not be created.')
@click.option('--pem-pub', 'pemPub',
type=click.File('wb'),
default=None,
help='Name of the public key in PEM format to create. If it is not given PEM file will not be created.')
@click.option('--aes', 'aes',
type=click.File('w'),
default=None,
help='Name of the AES-128 key to create. If it is given only AES key wiil be created and JWK will not.')
def main(kId, jwKey, pemPriv, pemPub, aes):
if aes == None:
key = jwk.JWK.generate(kty='EC', crv='P-256', use='sig')
keyJson = json.loads(key.export(private_key=True))
keyJson['kid'] = str(kId)
keyStr = json.dumps(keyJson, indent=4)
jwKey.write(keyStr)
if pemPriv != None:
pemPriv.write(key.export_to_pem(private_key=True, password=None))
if pemPub != None:
pemPub.write(key.export_to_pem(private_key=False, password=None))
print(keyStr)
else:
key = os.urandom(16)
iv = os.urandom(16)
file = key.hex() + '\n' + iv.hex()
aes.write(file)
print('AES-128 KEY: ', key.hex(), sep='\t')
print('AES-128 IV:', iv.hex(), sep='\t')
if __name__ == "__main__":
main()

View File

@ -90,7 +90,7 @@
"id": 4,
"monotonic": 0,
"smif_id": 1,
"upgrade": true,
"upgrade": false,
"encrypt": false,
"encrypt_key_id": 1,
"upgrade_auth": [

View File

@ -1 +0,0 @@
eyJhbGciOiJFUzI1NiJ9.eyJjeV9wdWJfa2V5Ijp7ImNydiI6IlAtMjU2Iiwia2lkIjoiMyIsImt0eSI6IkVDIiwidXNlIjoic2lnIiwieCI6IlNiOGxUcHlfcGQzTnJVVGtoSXpnMmp6TTM3dU5xTml1dDhXQy1RdjNYTVEiLCJ5IjoiQ3R3Q2k0YXJYc2pFRDVUVm1yX3ZQbFAya2UxMzNLSzdsUDdTel9JWmlERSJ9LCJleHAiOjE1Nzc3NDMyMDAsImlhdCI6MTU2MzI4NTQ1NCwiaW1hZ2VfYWRkcmVzcyI6MjY5Mjg3NDI0LCJpbWFnZV9maWxlIjoiQ3lwcmVzc0Jvb3Rsb2FkZXJfQ00wcC5oZXgiLCJpbWFnZV9oYXNoIjpbNjksOTYsNTEsNCwxMzksMTcwLDE4MSwxNDMsMTU5LDE2MywyMDgsMCwzNiwyNDYsNTcsMTY1LDI1NCwxOTgsMTI0LDY5LDczLDAsMTAxLDIxNywxMjUsMTE4LDQ3LDIwLDQzLDYzLDIxMyw3NV0sImltYWdlX2lkIjowLCJpbWFnZV92ZXJzaW9uIjoiMS4wLjAuMTI1IiwicG9saWN5X3RlbXBsYXRlIjoiIn0.Y_P-BfopTCNRBi5ZhwjGsCkq3qeFoGAW30Oy-Tn4JK-BMxcyXyIoABcB06Oyg6PwJjcPprTNCuAV_Pxirn3r0A

View File

@ -1 +0,0 @@
eyJhbGciOiJFUzI1NiJ9.eyJjeV9wdWJfa2V5Ijp7ImNydiI6IlAtMjU2Iiwia2lkIjoiMyIsImt0eSI6IkVDIiwidXNlIjoic2lnIiwieCI6IlNiOGxUcHlfcGQzTnJVVGtoSXpnMmp6TTM3dU5xTml1dDhXQy1RdjNYTVEiLCJ5IjoiQ3R3Q2k0YXJYc2pFRDVUVm1yX3ZQbFAya2UxMzNLSzdsUDdTel9JWmlERSJ9LCJleHAiOjE1Nzc3NDMyMDAsImlhdCI6MTU2MzI4NjA3NiwiaW1hZ2VfYWRkcmVzcyI6MjY5Mjg3NDI0LCJpbWFnZV9maWxlIjoiQ3lwcmVzc0Jvb3Rsb2FkZXJfQ00wcC5oZXgiLCJpbWFnZV9oYXNoIjpbMTY3LDExNSwxMTUsNjIsNiwyMjEsMTIsMjM0LDE0MiwzNyw0NiwyNTQsMjM5LDI0OCw2MCwxNTcsOTksNCwxOTcsMTc1LDIxNiw4MCwxMDIsNDYsMTY5LDE5NiwxNzgsNTMsOTQsNDksMjMxLDIwMl0sImltYWdlX2lkIjowLCJpbWFnZV92ZXJzaW9uIjoiMS4wLjAuMTI1IiwicG9saWN5X3RlbXBsYXRlIjoiIn0.kC_8cSqHKBHl1770umc1em1ZhuwjSHKxY1TigIBjcPVd_Gnr2X4LcMGIB6biwVWWYmtONF7k87XXfpM4NqByAg

View File

@ -1,51 +0,0 @@
Copyright (c) 2018-2019 Cypress Semiconductor Corporation
Permissive Binary License
Version 1.0, September 2015
Redistribution. Redistribution and use in binary form, without
modification, are permitted provided that the following conditions are
met:
1) Redistributions must reproduce the above copyright notice and the
following disclaimer in the documentation and/or other materials
provided with the distribution.
2) Unless to the extent explicitly permitted by law, no reverse
engineering, decompilation, or disassembly of this software is
permitted.
3) Redistribution as part of a software development kit must include the
accompanying file named "DEPENDENCIES" and any dependencies listed in
that file.
4) Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
Limited patent license. The copyright holders (and contributors) grant a
worldwide, non-exclusive, no-charge, royalty-free patent license to
make, have made, use, offer to sell, sell, import, and otherwise
transfer this software, where such license applies only to those patent
claims licensable by the copyright holders (and contributors) that are
necessarily infringed by this software. This patent license shall not
apply to any combinations that include this software. No hardware is
licensed hereunder.
If you institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the software
itself infringes your patent(s), then your rights granted under this
license shall terminate as of the date such litigation is filed.
DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS." ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1 +0,0 @@
eyJhbGciOiJFUzI1NiJ9.eyJhdXRoIjp7fSwiY3lfcHViX2tleSI6eyJjcnYiOiJQLTI1NiIsImtpZCI6IjMiLCJrdHkiOiJFQyIsInVzZSI6InNpZyIsIngiOiJTYjhsVHB5X3BkM05yVVRraEl6ZzJqek0zN3VOcU5pdXQ4V0MtUXYzWE1RIiwieSI6IkN0d0NpNGFyWHNqRUQ1VFZtcl92UGxQMmtlMTMzS0s3bFA3U3pfSVppREUifSwiZXhwIjoxNTc3NzQzMjAwLCJoc21fcHViX2tleSI6eyJjcnYiOiJQLTI1NiIsImtpZCI6IjQiLCJrdHkiOiJFQyIsInVzZSI6InNpZyIsIngiOiJzSk1zTi0ySm8yN2tjNTF3Vks3eEoyZlA5QkRrekFjMmZaRVpNbG9oSFhBIiwieSI6Ik1XbHV6bVhnWE92ZFFRRFlYM3l5MVRrOVFvSEwtOURaaHN3WnBZMFhlNVUifSwiaWF0IjoxNTUzNjg1NTczLCJ0eXBlIjoiQ1lfQVVUSF9IU00ifQ.oXeFYugOceM2XvnoWTEju8ByztA4OvFKrQxndIOts_nlgmRti2ddoHRGR86GMNCDfcHr54mXH33GkQ8D96DoGw

View File

@ -1,18 +0,0 @@
{
"hsm_priv_key": {
"crv": "P-256",
"d": "d-r9nPjLka7g5BHiT7OexYV7na-ofuNfjPPN4XvP_yQ",
"kty": "EC",
"use": "sig",
"x": "sJMsN-2Jo27kc51wVK7xJ2fP9BDkzAc2fZEZMlohHXA",
"y": "MWluzmXgXOvdQQDYX3yy1Tk9QoHL-9DZhswZpY0Xe5U"
},
"hsm_pub_key": {
"crv": "P-256",
"kty": "EC",
"use": "sig",
"kid": "4",
"x": "sJMsN-2Jo27kc51wVK7xJ2fP9BDkzAc2fZEZMlohHXA",
"y": "MWluzmXgXOvdQQDYX3yy1Tk9QoHL-9DZhswZpY0Xe5U"
}
}

View File

@ -1,18 +0,0 @@
{
"oem_priv_key": {
"crv": "P-256",
"d": "JVozA1oRvg-zSotMUbrGebV3oBhBaF1mqUyEn_Fdcqc",
"kty": "EC",
"use": "sig",
"x": "vfb7_jewTxpFVINcXdrZQJBArC5igrN0BLc783FigrM",
"y": "9rBBUKXzpj1A5K7fxPtEaJdsfo7Jj_wsF7LTZLc-sPM"
},
"oem_pub_key": {
"crv": "P-256",
"kty": "EC",
"use": "sig",
"kid": "5",
"x": "vfb7_jewTxpFVINcXdrZQJBArC5igrN0BLc783FigrM",
"y": "9rBBUKXzpj1A5K7fxPtEaJdsfo7Jj_wsF7LTZLc-sPM"
}
}

View File

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,5 +0,0 @@
The following are licensed under the Apache 2.0 license:
mcuboot
Mbed Crypto

View File

@ -1,23 +0,0 @@
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

View File

@ -1,46 +0,0 @@
/*
* $ Copyright Cypress Semiconductor $
*/
/* $OpenBSD: base64.c,v 1.3 1997/11/08 20:46:55 deraadt Exp $ */
/*
* Copyright (c) 1996 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/*
* Portions Copyright (c) 1995 by International Business Machines, Inc.
*
* International Business Machines, Inc. (hereinafter called IBM) grants
* permission under its copyrights to use, copy, modify, and distribute this
* Software with or without fee, provided that the above copyright notice and
* all paragraphs of this notice appear in all copies, and that the name of IBM
* not be used in connection with the marketing of any product incorporating
* the Software or modifications thereof, without specific, written prior
* permission.
*
* To the extent it has a right to do so, IBM grants an immunity from suit
* under its patents, if any, for the use, sale or manufacture of products to
* the extent that such products are used for performing Domain Name System
* dynamic updates in TCP/IP networks by means of the Software. No immunity is
* granted for any product per se or for any other function of any product.
*
* THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
* DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
* IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
*/

View File

@ -1,116 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from jwcrypto import jwk, jws, jwt
from jwcrypto.common import base64url_decode
from jwcrypto.common import json_decode
from datetime import datetime
import json
import copy
class crypto :
def create_jwk() :
key = jwk.JWK.generate(kty='EC', crv='P-256', use='sig')
pub_key= json.loads( key.export(private_key=False) )
priv_key= json.loads( key.export(private_key=True) )
print( "create_jwk()= " + json.dumps( pub_key, indent=4 ) )
return priv_key,pub_key
def create_jwt( payload , key ) :
token= jwt.JWT( header={"alg": "ES256"},claims=payload )
token.make_signed_token( jwk.JWK( **key ) )
txt= token.serialize(compact=True)
print( "create_jwt()= " + json.dumps( crypto.readable_jwt(txt) ,indent=4 ))
return txt
def readable_jwt( txt ) :
"""
Convert a JWT token in base64url into a readable dictionary object
with decoded payload and header for printing and logging
"""
lst= txt.split('.')
readable= {}
readable["protected"]= json_decode(base64url_decode(lst[0]))
readable["payload"]= json_decode(base64url_decode(lst[1]))
readable["signature"]= lst[2]
# create readable timestamps for exp/iat claims
payload= readable["payload"]
if "iat" in payload :
t= payload["iat"]
if isinstance(t,int) :
t= datetime.fromtimestamp(t).isoformat(' ')
payload["iat"]= t
if "exp" in payload :
t= payload["exp"]
if isinstance(t,int) :
t= datetime.fromtimestamp(t).isoformat(' ')
payload["exp"]= t
print(json.dumps(readable,indent=4,sort_keys=False))
return readable
def dump_jwt( txt,file_name ) :
"""
Dumps a JWT dictionary object into a text file
"""
with open(file_name,"w") as f :
f.write(txt)
f.close()
def read_jwt( file_name ) :
"""
Reads a JWT dictionary object from a text file
"""
with open(file_name,"r") as f :
txt = f.read()
f.close()
return txt
def jwt_payload( txt ) :
"""
Returns the payload of a JWT without validating it's signature
Sometimes used for tokens that contain a public key in its payload, where the signature proves possesion of the corresponding private key
In that case, the payload is needed to obtain the public key with which to then validate the JWT
"""
# split the token
lst= txt.split('.')
payload= lst[1]
obj= json_decode(base64url_decode(payload))
return obj
def validate_jwt( txt , key ) :
"""
Validates a signed JWT
"""
try :
jwt.JWT(key=jwk.JWK(**key),jwt=txt)
print( " JWT signature is valid" )
return True
except :
print( " JWT signature is not valid" )
return False
def create_x509_cert( pub_key , priv_key , prod_id , die_id=None , dev_id=None ) :
"""
TODO: create a X.509 certificate here certifying pub_key, signed with private_key
"""
cert= "CertificateToBeDone(die_id={},dev_id={},prod_id={})".format(die_id,dev_id,prod_id)
return cert

View File

@ -1,50 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from cyprov_entity import Entity
from cyprov_crypto import crypto
# Customer Entity
class CustomerEntity(Entity):
def __init__(self, state_name, audit_name) :
Entity.__init__(self, state_name, audit_name)
if "custom_priv_key" not in self.state:
d = dict()
d["custom_priv_key"] = self.state
self.state = d
def create_entity(self, kid) :
"""
Creates the Customer entity.
Creates the Customer main key-pair and returns nothing.
"""
customer_priv_key, customer_pub_key = crypto.create_jwk()
customer_priv_key["kid"] = str(kid)
customer_pub_key["kid"] = str(kid)
self.state["custom_priv_key"] = customer_priv_key
self.state["custom_pub_key"] = customer_pub_key
self.state_loaded = True
def get_pub_key(self):
if "custom_pub_key" not in self.state:
key = dict(self.state["custom_priv_key"])
del key["d"]
else:
key = self.state["custom_pub_key"]
return key
def get_priv_key(self):
return self.state["custom_priv_key"]

View File

@ -1,46 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
# Cypress Entity
class Entity :
def __init__( self , state_name , audit_name ) :
self.state_name= state_name
self.state_loaded= False
self.audit_name= audit_name
Entity.load_state(self)
def load_state( self ) :
try :
with open(self.state_name,"r+") as f :
self.state= json.loads(f.read())
f.close()
self.state_loaded= True
except FileNotFoundError:
self.state= {}
self.state_loaded= False
def save_state( self ) :
if not self.state_loaded :
raise Exception( "Internal error - state not loaded" )
with open(self.state_name,"w") as f :
f.write( json.dumps( self.state,indent=4 ) )
f.close()
def append_audit_record( self,record ) :
with open(self.audit_name,"a") as f :
f.write( json.dumps(record,indent=4) + "\n" )
f.close()

View File

@ -1,443 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from cyprov_types import types
from cyprov_entity import Entity
from cyprov_crypto import crypto
from datetime import datetime
from datetime import timedelta
# HSM Entity
class HsmEntity(Entity):
def __init__(self, state_name, audit_name):
Entity.__init__(self, state_name, audit_name)
def create_entity(self):
"""
Creates the HSM entity.
Creates the hsm_priv_key,hsm_pub_key key-pair.
"""
hsm_priv_key, hsm_pub_key = crypto.create_jwk()
self.state["hsm_priv_key"] = hsm_priv_key
self.state["hsm_pub_key"] = hsm_pub_key
def request_disti_authorization(self):
"""
HSM creates a request for authorization to Distributor
It includes just the HSM public key and a one week expiration limit
We use a JWT to prove knowledge of the private key and convey an expiration limit
"""
hsm_priv_key = self.state["hsm_priv_key"]
hsm_pub_key = self.state["hsm_pub_key"]
# create the request
exp = datetime.now() + timedelta(7)
payload = {}
payload["type"] = types.HSM_REQ_DISTI_AUTH
payload["hsm_pub_key"] = hsm_pub_key
payload["iat"] = int(datetime.now().timestamp())
payload["exp"] = int(exp.timestamp())
auth_req = crypto.create_jwt(payload, hsm_priv_key)
# Create audit record
record = {}
record["type"] = types.HSM_REQ_DISTI_AUTH
record["iat"] = datetime.now().isoformat(' ')
record["auth_req"] = crypto.readable_jwt(auth_req)
self.append_audit_record(record)
return auth_req
def install_disti_auth(self, disti_auth):
"""
Install distributor authorization.
Returns nothingraise Exception(
"""
hsm_pub_key = self.state["hsm_pub_key"]
# Validate the distributor authorization
disti_auth_payload = crypto.jwt_payload(disti_auth)
disti_pub_key = disti_auth_payload["disti_pub_key"]
if not crypto.validate_jwt(disti_auth, disti_pub_key):
raise Exception("Invalid signature for distributor authorization")
if disti_auth_payload["type"] != types.DISTI_AUTH_HSM:
raise Exception("Invalid type for distributor authorization")
if disti_auth_payload["hsm_pub_key"] != hsm_pub_key:
raise Exception("Distributor authorization is not for this HSM")
if datetime.fromtimestamp(disti_auth_payload["exp"]) < datetime.now():
raise Exception("Distributor authorization expired")
# install distributor key and authorization
self.state["disti_pub_key"] = disti_pub_key
self.state["disti_auth"] = disti_auth
self.state["disti_auth_readable"] = crypto.readable_jwt(disti_auth)
# create audit record
record = {}
record["type"] = types.DISTI_AUTH_HSM
record["iat"] = datetime.now().isoformat(' ')
record["disti_auth"] = crypto.readable_jwt(disti_auth)
self.append_audit_record(record)
def request_cy_authorization(self, cy_pub_key=None):
"""
HSM creates a request for authorization to CY
It contains the HSM public key, the distributor authorization and a 1 week expiration
The JWT proves knowledge of the private key and the distributor authorization is used by CY to identify the request
"""
hsm_priv_key = self.state["hsm_priv_key"]
hsm_pub_key = self.state["hsm_pub_key"]
disti_auth = self.state["disti_auth"]
# create the authorization request
exp = datetime.now() + timedelta(7)
payload = {}
payload["type"] = types.HSM_REQ_CY_AUTH
payload["hsm_pub_key"] = hsm_pub_key
payload["disti_auth"] = disti_auth
payload["iat"] = int(datetime.now().timestamp())
payload["exp"] = int(exp.timestamp())
if cy_pub_key != None:
payload["cy_pub_key"] = cy_pub_key
auth_req = crypto.create_jwt(payload, hsm_priv_key)
# create audit record
cy_auth_readable = crypto.readable_jwt(auth_req)
cy_auth_readable["payload"]["disti_auth"] = crypto.readable_jwt(cy_auth_readable["payload"]["disti_auth"])
record = {}
record["type"] = types.HSM_REQ_CY_AUTH
record["iat"] = datetime.now().isoformat(' ')
record["cy_auth_req"] = cy_auth_readable
self.append_audit_record(record)
# install the cy_pub_key if one is given (allows for explicit binding to Cypress or some other supplier)
self.state["cy_pub_key"] = cy_pub_key
return auth_req
def install_cy_auth(self, cy_auth, cy_pub_key=None):
"""
Install Cypress authorization.
First checks the signature on the token and then just stores it
The expiration date on the token is enforced by the HSM only (since device has not time).
The authorization privilages (which may contain serial number and/or wounding limitations) are enforced by the device.
"""
hsm_pub_key = self.state["hsm_pub_key"]
# validate the authorization
cy_auth_payload = crypto.jwt_payload(cy_auth)
if cy_pub_key == None:
cy_pub_key = cy_auth_payload["cy_pub_key"]
elif cy_auth_payload["cy_pub_key"] != cy_pub_key:
raise Exception("Cypress authorization is not from Cypress")
if not crypto.validate_jwt(cy_auth, cy_pub_key):
raise Exception("Invalid signature for CY HSM authorization")
if cy_auth_payload["type"] != types.CY_AUTH_HSM:
raise Exception("Invalid type for CY HSM authoriation")
if datetime.fromtimestamp(cy_auth_payload["exp"]) < datetime.now():
raise Exception("CY HSM authorization is expired")
if cy_auth_payload["cy_pub_key"] != cy_pub_key:
raise Exception("Authorization appears to be not from Cypress")
# install the authorization
self.state["cy_pub_key"] = cy_pub_key
self.state["cy_auth"] = cy_auth
self.state["cy_auth_readable"] = crypto.readable_jwt(cy_auth)
self.state["products"] = {}
# create audit record
record = {}
record["type"] = types.CY_AUTH_HSM
record["iat"] = datetime.now().isoformat(' ')
record["cy_auth"] = crypto.readable_jwt(cy_auth)
self.append_audit_record(record)
def create_signing_key(self, prod_id):
"""
Creates a signing-key package that an OEM uses to create its chain of trust
One or more OEM projects may share the same signing key
An HSM can maintain multiple signing keys for multiple OEMs, identified by a string "prod_id"
HSM operator must make sure that "prod_id" is unique across all of its customers, e.g. by including the customer name or ID
"""
cy_auth = self.state["cy_auth"]
disti_auth = self.state["disti_auth"]
hsm_priv_key = self.state["hsm_priv_key"]
# create the signing key
signing_priv_key, signing_pub_key = crypto.create_jwk()
# create the token
exp = datetime.now() + timedelta(7)
payload = {}
payload["type"] = types.HSM_SIGNING_KEY_PKG
payload["iat"] = int(datetime.now().timestamp())
payload["exp"] = int(exp.timestamp())
payload["prod_id"] = prod_id
payload["signing_pub_key"] = signing_pub_key
payload["cy_auth"] = cy_auth
payload["disti_auth"] = disti_auth
signing_pkg = crypto.create_jwt(payload, hsm_priv_key)
# store signing key for later use
prod = {}
prod["iat"] = int(datetime.now().timestamp())
prod["signing_priv_key"] = signing_priv_key
prod["signing_pub_key"] = signing_pub_key
self.state["products"][prod_id] = prod
# create audit record
signing_pkg_readable = crypto.readable_jwt(signing_pkg)
signing_pkg_readable["payload"]["cy_auth"] = crypto.readable_jwt(signing_pkg_readable["payload"]["cy_auth"])
signing_pkg_readable["payload"]["disti_auth"] = crypto.readable_jwt(
signing_pkg_readable["payload"]["disti_auth"])
record = {}
record["type"] = types.HSM_SIGNING_KEY_PKG
record["iat"] = datetime.now().isoformat(' ')
record["signing_pkg"] = signing_pkg_readable
self.append_audit_record(record)
return signing_pkg
def install_rot_authorization(self, rot_auth_pkg):
"""
The HSM checks the response package and the rot_auth token itself and simply stores it for later use
"""
hsm_pub_key = self.state["hsm_pub_key"]
rot_auth_pkg_payload = crypto.jwt_payload(rot_auth_pkg)
prod_id = rot_auth_pkg_payload["prod_id"]
rot_auth = rot_auth_pkg_payload["rot_auth"]
chain_of_trust = rot_auth_pkg_payload["chain_of_trust"]
# validate the RoT authorization
rot_auth_payload = crypto.jwt_payload(rot_auth)
oem_pub_key = rot_auth_payload["oem_pub_key"]
if not crypto.validate_jwt(rot_auth, oem_pub_key):
raise Exception("Invalid signature on OEM root-of-trust authorization")
if rot_auth_payload["type"] != types.OEM_ROT_AUTH:
raise Exception("Invalid type for OEM root-of-trust authorization")
if rot_auth_payload["hsm_pub_key"] != hsm_pub_key:
raise Exception("Invalid HSM public key in OEM root-of-trust authorization")
if rot_auth_payload["prod_id"] != prod_id:
raise Exception("Invalid prod_id in OEM root-of-trust authorization")
# validate the package itself
if not crypto.validate_jwt(rot_auth_pkg, oem_pub_key):
raise Exception("Invalid signature on OEM root-of-trust authorization package")
if rot_auth_pkg_payload["type"] != types.OEM_ROT_AUTH_PKG:
raise Exception("Invalid type for OEM root-of-trust authorization package")
if datetime.fromtimestamp(rot_auth_pkg_payload["exp"]) < datetime.now():
raise Exception("OEM root-of-trust authorization package is expired")
# store the result for later usage
prod = self.state["products"][prod_id]
prod["oem_pub_key"] = oem_pub_key
prod["rot_auth"] = rot_auth
prod["rot_auth_readable"] = crypto.readable_jwt(rot_auth)
prod["chain_of_trust"] = chain_of_trust
# create audit record
rot_auth_pkg_readable = crypto.readable_jwt(rot_auth_pkg)
rot_auth_pkg_readable["payload"]["rot_auth"] = crypto.readable_jwt(rot_auth_pkg_readable["payload"]["rot_auth"])
record = {}
record["type"] = types.OEM_ROT_AUTH_PKG
record["iat"] = datetime.now().isoformat(' ')
record["rot_auth_pkg"] = rot_auth_pkg_readable
self.append_audit_record(record)
def create_rot_command(self, prod_id):
"""
The cy_auth token means Cypress authorized the HSM for provisioning
The rot_auth token means that the OEM authorized the HSM to provision RoT on its behalf
Together, they form the RoT command to the device
"""
hsm_priv_key = self.state["hsm_priv_key"]
cy_auth = self.state["cy_auth"]
rot_auth = self.state["products"][prod_id]["rot_auth"]
# check CY authorization has not expired
cy_auth_payload = crypto.jwt_payload(cy_auth)
if datetime.fromtimestamp(cy_auth_payload["exp"]) < datetime.now():
raise Exception("Cypress authorization for HSM expired")
# create the RoT command
payload = {}
payload["type"] = types.HSM_ROT_CMD
payload["prod_id"] = prod_id
payload["cy_auth"] = cy_auth
payload["rot_auth"] = rot_auth
rot_cmd = crypto.create_jwt(payload, hsm_priv_key)
# create audit record
rot_cmd_readable = crypto.readable_jwt(rot_cmd)
rot_cmd_readable["payload"]["cy_auth"] = crypto.readable_jwt(rot_cmd_readable["payload"]["cy_auth"])
rot_cmd_readable["payload"]["rot_auth"] = crypto.readable_jwt(rot_cmd_readable["payload"]["rot_auth"])
record = {}
record["type"] = types.HSM_ROT_CMD
record["iat"] = datetime.now().isoformat()
record["rot_cmd"] = rot_cmd_readable
self.append_audit_record(record)
return rot_cmd
def accept_provision_authorization(self, prov_auth):
"""
Accepts a provisioning request from an OEM, that is authorized by the distributor
prov_req= jwt( payload={ OEM_PROV_REQ , blob } , key=oem_priv_key )
prov_auth= jwt( payload={ DISTI_PROV_AUTH , prov_req , cnt , exp } , key=disti_priv_key )
When successful 'installs' the provisioning request in the HSM as the active request for given prod_id
"""
disti_pub_key = self.state["disti_pub_key"]
# validate the Disti's command
prov_auth_payload = crypto.jwt_payload(prov_auth)
if not crypto.validate_jwt(prov_auth, disti_pub_key):
raise Exception("Invalid signature for provisioning authorization")
if prov_auth_payload["type"] != types.DISTI_PROV_AUTH:
raise Exception("Invalid type for provisioning authorization")
prov_req = prov_auth_payload["prov_req"]
prov_req_cnt = prov_auth_payload["cnt"]
prov_req_exp = datetime.fromtimestamp(prov_auth_payload["exp"])
# validate the OEM's provisioning request
prov_req_payload = crypto.jwt_payload(prov_req)
prod_id = prov_req_payload["prod_id"]
oem_pub_key = self.state["products"][prod_id]["oem_pub_key"]
if not crypto.validate_jwt(prov_req, oem_pub_key):
raise Exception("Invalid signature for provisioning request")
# install the provisioning request for execution
prod = self.state["products"][prod_id]
prod["prov_req"] = prov_req
prod["prov_req_readable"] = crypto.readable_jwt(prov_req)
prod["prov_req_cnt"] = prov_req_cnt
prod["prov_req_exp"] = int(prov_req_exp.timestamp())
# create audit record
record = {}
record["type"] = types.DISTI_PROV_AUTH
record["iat"] = datetime.now().isoformat(' ')
record["prod_id"] = prod_id
record["prov_req"] = crypto.readable_jwt(prov_req)
self.append_audit_record(record)
def create_provision_cmd(self, prod_id, dev_rsp, image_jwt_file):
"""
Creates and returns a provisioning command prov_cmd that can be send to a device.
prov_req= jwt( payload={ OEM_PROV_REQ , blob } , key=oem_priv_key )
chain_of_trust= [ x509_certs ]
hsm_auth= jwt( payload={ CY_HSM_AUTH , hsm_pub_key , auth } , key=cy_priv_key )
prov_cmd= jwt( payload={ HSM_PROV_CMD , prov_req , chain_of_trust , hsm_auth } , key=hsm_priv_key )
Requires the device response to the RoT command (which contains the device identity and keys).
dev_rsp= jwt( payload= { DEV_ROT_RSP , prod_id,die_id,dev_id, dev_pub_key } , key=dev_priv_key )
There must be an active provisioning request in the HSM for the given product ID.
"""
prod = self.state["products"][prod_id]
oem_pub_key = prod["oem_pub_key"]
signing_priv_key = prod["signing_priv_key"]
chain_of_trust = prod["chain_of_trust"]
prov_req = prod["prov_req"]
prov_req_cnt = prod["prov_req_cnt"]
prov_req_exp = datetime.fromtimestamp(prod["prov_req_exp"])
hsm_priv_key = self.state["hsm_priv_key"]
cy_auth = self.state["cy_auth"]
image_cert = crypto.read_jwt(image_jwt_file)
# validate the OEM's provisioning request
if prov_req_exp < datetime.now():
raise Exception("Provisioning request expired")
if prov_req_cnt <= 0:
raise Exception("Provisioning request count exceeded")
prov_req_cnt -= 1;
# validate the device response
dev_rsp_payload = crypto.jwt_payload(dev_rsp)
dev_pub_key = dev_rsp_payload["dev_pub_key"]
die_id = dev_rsp_payload["die_id"]
dev_id = dev_rsp_payload["dev_id"]
if dev_rsp_payload["prod_id"] != prod_id:
raise Exception("Product ID in provision request does not match device (" + prod_id + ")")
if not crypto.validate_jwt(dev_rsp, dev_pub_key):
raise Exception("Invalid signature on device response to RoT command")
# create the device chain of trust (X509)
dev_cert = crypto.create_x509_cert(prod_id=prod_id, die_id=die_id, dev_id=dev_id, pub_key=dev_pub_key,
priv_key=signing_priv_key)
chain_of_trust = chain_of_trust + [dev_cert]
# create the provisioning command
payload = {}
payload["type"] = types.HSM_PROV_CMD
payload["prov_req"] = prov_req
payload["image_cert"] = image_cert
payload["chain_of_trust"] = chain_of_trust
payload["cy_auth"] = cy_auth
prov_cmd = crypto.create_jwt(payload, hsm_priv_key)
# create audit record
prov_cmd_readable = crypto.readable_jwt(prov_cmd)
prov_cmd_readable["payload"]["cy_auth"] = crypto.readable_jwt(prov_cmd_readable["payload"]["cy_auth"])
prov_cmd_readable["payload"]["prov_req"] = crypto.readable_jwt(prov_cmd_readable["payload"]["prov_req"])
prov_cmd_readable["payload"]["image_cert"] = crypto.readable_jwt(prov_cmd_readable["payload"]["image_cert"])
record = {}
record["type"] = types.HSM_PROV_CMD
record["iat"] = datetime.now().isoformat(' ')
record["prod_id"] = prod_id
record["dev_rsp"] = crypto.readable_jwt(dev_rsp)
record["prov_cmd"] = prov_cmd_readable
self.append_audit_record(record)
self.state["products"][prod_id]["prov_req_cnt"] = prov_req_cnt
return prov_cmd
def pack_rot_command(self, prod_id, cy_auth_file, rot_auth):
payload = {}
payload['type'] = types.HSM_ROT_CMD
payload['prod_id'] = prod_id
payload['cy_auth'] = crypto.read_jwt(cy_auth_file)
payload['rot_auth'] = rot_auth
hsm_priv_key = self.state["hsm_priv_key"]
rot_cmd = crypto.create_jwt(payload, hsm_priv_key)
return rot_cmd
def pack_provision_cmd(self, **kwargs):
payload = {}
print(kwargs)
if kwargs is not None:
for k, v in kwargs.items():
if type(v) is tuple:
sequence = []
for cert_file in v:
with open(cert_file) as cert:
sequence.append(cert.read())
payload[k] = sequence
else:
if os.path.isfile(v):
payload[k] = crypto.read_jwt(v)
else:
payload[k] = v
payload['type'] = types.HSM_PROV_CMD
hsm_priv_key = self.state['hsm_priv_key']
prov_cmd = crypto.create_jwt(payload, hsm_priv_key)
return prov_cmd

View File

@ -1,143 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from cyprov_entity import Entity
from cyprov_crypto import crypto
from cyprov_types import types
from datetime import datetime
from datetime import timedelta
# Oem (Customr) Entity
class OemEntity(Entity) :
def __init__( self , state_name , audit_name ) :
Entity.__init__(self,state_name,audit_name)
def create_entity( self , chain_of_trust=[] ) :
"""
Creates the Oem entity.
Creates the Oem main key-pair and returns nothing.
"""
oem_priv_key,oem_pub_key= crypto.create_jwk()
self.state["oem_priv_key"]= oem_priv_key
self.state["oem_pub_key"]= oem_pub_key
self.state["oem_chain_of_trust"]= chain_of_trust
def create_rot_authorization( self , signing_pkg ) :
"""
OEM creates a root-of-trust authorization package based on the signing key package from the HSM
It's a JWT with two main parts:
- a rot_auth authorization token that is sent to the device
- a chain of X509 certificates that establishses the OEM's trust in the HSM signing key
Note that this chain is not used inside the device and used only by 3rd parties
"""
oem_pub_key= self.state["oem_pub_key"]
oem_priv_key= self.state["oem_priv_key"]
oem_chain_of_trust= self.state["oem_chain_of_trust"]
# get HSM public key and check CY authorization
signing_pkg_payload= crypto.jwt_payload(signing_pkg)
cy_auth= signing_pkg_payload["cy_auth"]
cy_auth_payload= crypto.jwt_payload(cy_auth)
cy_pub_key= cy_auth_payload["cy_pub_key"]
hsm_pub_key= cy_auth_payload["hsm_pub_key"]
if not crypto.validate_jwt( cy_auth , cy_pub_key ) :
raise Exception( "Invalid signature on Cypress HSM authorization in request" )
if cy_auth_payload["type"] != types.CY_AUTH_HSM :
raise Exception( "Invalid type for Cypress HSM authorization in request" )
if datetime.fromtimestamp(cy_auth_payload["exp"]) < datetime.now() :
raise Exception( "Cypress HSM authorization expired" )
# validate HSM request itself
signing_pub_key= signing_pkg_payload["signing_pub_key"]
prod_id= signing_pkg_payload["prod_id"]
if not crypto.validate_jwt( signing_pkg , hsm_pub_key ) :
raise Exception( "Invalid signature on HSM signing key package" )
if signing_pkg_payload["type"] != types.HSM_SIGNING_KEY_PKG :
raise Exception( "Invalid type on HSM signing key package" )
if datetime.fromtimestamp(signing_pkg_payload["exp"]) < datetime.now() :
raise Exception( "HSM signing key package expired" )
# create the RoT transfer authorization (that will go to the device)
payload= {}
payload["type"]= types.OEM_ROT_AUTH
payload["oem_pub_key"]= oem_pub_key
payload["hsm_pub_key"]= hsm_pub_key
payload["prod_id"]= prod_id
payload["iat"]= int(datetime.now().timestamp())
rot_auth= crypto.create_jwt( payload , oem_priv_key )
# create the chain of trust
cert= crypto.create_x509_cert( signing_pub_key , oem_priv_key , prod_id )
chain_of_trust= oem_chain_of_trust + [ cert ]
# create the response
exp= datetime.now() + timedelta(7)
payload= {}
payload["type"]= types.OEM_ROT_AUTH_PKG
payload["iat"]= int(datetime.now().timestamp())
payload["exp"]= int(exp.timestamp())
payload["prod_id"]= prod_id
payload["rot_auth"]= rot_auth
payload["chain_of_trust"]= chain_of_trust
rot_auth_pkg= crypto.create_jwt( payload , oem_priv_key )
# create audit record
signing_pkg_readable= crypto.readable_jwt(signing_pkg)
signing_pkg_readable["payload"]["cy_auth"]= crypto.readable_jwt(signing_pkg_readable["payload"]["cy_auth"])
signing_pkg_readable["payload"]["disti_auth"]= crypto.readable_jwt(signing_pkg_readable["payload"]["disti_auth"])
rot_auth_pkg_readable= crypto.readable_jwt(rot_auth_pkg)
rot_auth_pkg_readable["payload"]["rot_auth"]= crypto.readable_jwt(rot_auth_pkg_readable["payload"]["rot_auth"])
record= {}
record["type"]= types.OEM_ROT_AUTH_PKG
record["iat"]= datetime.now().isoformat(' ')
record["signing_pkg"]= signing_pkg_readable
record["rot_auth_pkg"]= rot_auth_pkg_readable
self.append_audit_record(record)
return rot_auth_pkg
def create_provision_request( self , blob ) :
"""
The OEM can create a request for provisioning by signing a keys & policies blob with its private key
Note that blob must contain at least the prod_id field
"""
# create the request
oem_priv_key= self.state["oem_priv_key"]
prov_req= crypto.create_jwt( blob , oem_priv_key )
# create audit record
record= {}
record["type"]= types.OEM_PROV_REQ
record["iat"]= datetime.now().isoformat(' ')
record["prod_id"]= blob["prod_id"]
record["prov_req"]= crypto.readable_jwt(prov_req)
self.append_audit_record(record)
return prov_req
def pack_rot_auth( self, prod_id, hsm_pub_key ):
oem_pub_key = self.state["oem_pub_key"]
oem_priv_key = self.state["oem_priv_key"]
payload= {}
payload["type"] = types.OEM_ROT_AUTH
payload["oem_pub_key"] = oem_pub_key
payload["hsm_pub_key"] = hsm_pub_key
payload["prod_id"] = prod_id
payload["iat"] = int(datetime.now().timestamp())
rot_auth = crypto.create_jwt( payload , oem_priv_key )
return rot_auth

View File

@ -1,79 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from jwcrypto import jwk
import json
def pretty_search(dict_or_list, key_to_search, search_for_first_only=False):
"""
Give it a dict or a list of dicts and a dict key (to get values of),
it will search through it and all containing dicts and arrays
for all values of dict key you gave, and will return you set of them
unless you wont specify search_for_first_only=True
:param dict_or_list:
:param key_to_search:
:param search_for_first_only:
:return:
"""
search_result = set()
if isinstance(dict_or_list, dict):
for key in dict_or_list:
key_value = dict_or_list[key]
if key == key_to_search:
if search_for_first_only:
return key_value
else:
search_result.add(key_value)
if isinstance(key_value, dict) or isinstance(key_value, list) or isinstance(key_value, set):
_search_result = pretty_search(key_value, key_to_search, search_for_first_only)
if _search_result and search_for_first_only:
return _search_result
elif _search_result:
for result in _search_result:
search_result.add(result)
elif isinstance(dict_or_list, list) or isinstance(dict_or_list, set):
for element in dict_or_list:
if isinstance(element, list) or isinstance(element, set) or isinstance(element, dict):
_search_result = pretty_search(element, key_to_search, search_result)
if _search_result and search_for_first_only:
return _search_result
elif _search_result:
for result in _search_result:
search_result.add(result)
return search_result if search_result else None
class PemKey:
def __init__(self, jwk_file = None, item = None):
if jwk_file != None:
with open(jwk_file) as f:
jwk_str = f.read()
self.jwk = json.loads(jwk_str)
if item != None:
self.jwk = pretty_search(self.jwk, item, search_for_first_only=True)
def save(self, file = None, private_key=False):
key = jwk.JWK(**self.jwk)
pem_str = key.export_to_pem(private_key, password=None)
if file != None:
with open(file, 'wb') as f:
f.write(pem_str)
else:
print(pem_str)
def load(self, jwk):
self.jwk = jwk

View File

@ -1,29 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class types :
HSM_REQ_DISTI_AUTH= "HSM_REQ_DISTI_AUTH"
DISTI_AUTH_HSM= "DISTI_AUTH_HSM"
HSM_REQ_CY_AUTH= "HSM_REQ_CY_AUTH"
CY_AUTH_HSM= "CY_AUTH_HSM"
HSM_SIGNING_KEY_PKG= "HSM_SIGNING_KEY_PKG"
OEM_ROT_AUTH= "OEM_ROT_AUTH"
OEM_ROT_AUTH_PKG= "OEM_ROT_AUTH_PKG"
OEM_PROV_REQ= "OEM_PROV_REQ"
DISTI_PROV_AUTH= "DISTI_PROV_AUTH"
CY_DEV_ID= "CY_DEV_ID"
HSM_ROT_CMD= "HSM_ROT_CMD"
HSM_PROV_CMD= "HSM_PROV_CMD"

View File

@ -1,133 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
import json
import click
# Default input values and pathes
PROD_NAME = 'my_thing'
CYPROV_LIB_PATH = './provisioning_lib'
# Default output values and pathes
OEM_AUDIT_NAME = 'oem_log.json'
HSM_AUDIT_NAME = 'hsm_log.json'
PROV_REQ_JWT_FILE = 'prov_req.jwt'
ROT_AUTH_JWT_FILE = 'rot_auth.jwt'
PROV_JWT_FILE = 'prov_cmd.jwt'
CUSTOMER_KEY_N = 5
sys.path.insert(0, CYPROV_LIB_PATH)
from cyprov_hsm import HsmEntity
from cyprov_oem import OemEntity
from cyprov_customer import CustomerEntity
from cyprov_crypto import crypto
def process_customer_keys(paths):
customer_key_n = len(paths)
customer = []
if customer_key_n > 0:
customer_key_id = 6
for i in range(customer_key_n):
customer_log = os.path.basename(paths[i])
customer_log = os.path.splitext(customer_log)[0] + '_log.json'
customer.append(CustomerEntity(paths[i], customer_log))
if not customer[i].state_loaded:
customer[i].create_entity(customer_key_id + i)
customer[i].save_state()
return customer
@click.command()
@click.option('--oem', 'oem_state_path',
default='../prebuild/oem_state.json',
help='OEM key file.')
@click.option('--hsm', 'hsm_state_path',
default='../prebuild/hsm_state.json',
help='HSM key file.')
@click.option('--cyboot', 'image_cert',
default='../prebuild/CypressBootloader_CM0p.jwt',
help='Cypress Bootloader image certificate.')
@click.option('--cyauth', 'cy_auth_path',
default='../prebuild/cy_auth.jwt',
help='Provisioning authorization certificate.')
@click.option('--policy', 'policy_path',
default='policy_single_stage_CM4.json',
help='Policy file.')
@click.option('--out', 'output_path',
default='../packet',
help='Output directory.')
@click.option('--ckey', 'cust_key_path',
default=None,
multiple=True,
help='Customer key that will be used for image signing. Use the option multiple times '
'to specify multiple keys.')
@click.option('--devcert', 'dev_cert',
default=[],
multiple=True,
help='Chain of trust certificate. Use the option multiple times to specify multiple certificates.')
def main(oem_state_path, hsm_state_path, image_cert, cy_auth_path,
policy_path, output_path, cust_key_path, dev_cert):
if len(cust_key_path) > CUSTOMER_KEY_N:
raise Exception('Maximum number of customer keys must be {}!'.format(CUSTOMER_KEY_N))
prod_id = PROD_NAME
oem_audit_path = os.path.join(output_path, OEM_AUDIT_NAME)
hsm_audit_path = os.path.join(output_path, HSM_AUDIT_NAME)
prov_req_jwt_path = os.path.join(output_path, PROV_REQ_JWT_FILE)
rot_auth_jwt_path = os.path.join(output_path, ROT_AUTH_JWT_FILE)
prov_jwt_path = os.path.join(output_path, PROV_JWT_FILE)
if not os.path.exists(output_path):
os.makedirs(output_path)
oem = OemEntity(oem_state_path, oem_audit_path)
hsm = HsmEntity(hsm_state_path, hsm_audit_path)
with open(policy_path) as f:
json_str = f.read()
blob = json.loads(json_str)
blob['prod_id'] = prod_id
customer = process_customer_keys(cust_key_path)
if len(customer) > 0:
blob['custom_pub_key'] = [key.get_pub_key() for key in customer]
prov_req = oem.create_provision_request(blob)
crypto.dump_jwt(prov_req, prov_req_jwt_path)
rot_auth_pkg = oem.pack_rot_auth(prod_id, hsm.state["hsm_pub_key"])
crypto.dump_jwt(rot_auth_pkg, rot_auth_jwt_path)
prov_cmd = hsm.pack_provision_cmd(
cy_auth=cy_auth_path,
image_cert=image_cert,
prov_req=prov_req,
rot_auth=rot_auth_pkg,
chain_of_trust=dev_cert)
crypto.dump_jwt(prov_cmd, prov_jwt_path)
print('#' * 80)
print('Provisioning packet is created')
print('#' * 80)
if __name__ == "__main__":
main()

View File

@ -1,80 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
import click
from execute.helper import get_target_name
from execute.enums import DebugCore
from execute.provision_device import provision_execution
from execute.programmer.programmer import ProgrammingTool
from prepare.provisioning_lib.cyprov_pem import PemKey
TOOL_NAME = 'pyocd' # Programming/debugging tool used for communication with device
ACCESS_PORT = DebugCore.debug_sys_ap # Access port used for device provisioning
@click.command()
@click.option('--prov-jwt', 'prov_cmd_jwt',
default='packet/prov_cmd.jwt',
type=click.STRING,
help='Path to provisioning JWT file (packet which contains all data necessary '
'for provisioning, including policy, authorization packets and keys)')
@click.option('--hex', 'cy_bootloader_hex',
default='prebuild/CyBootloader_Release/CypressBootloader_CM0p.hex',
type=click.STRING,
help='Path to Cypress Bootloader HEX binary file')
@click.option('--pubkey-json', 'pub_key_json',
default='keys/dev_pub_key.json',
type=click.STRING,
help='File where to save public key in JSON format')
@click.option('--pubkey-pem', 'pub_key_pem',
default='keys/dev_pub_key.pem',
type=click.STRING,
help='File where to save public key in PEM format')
def main(prov_cmd_jwt, cy_bootloader_hex, pub_key_json, pub_key_pem):
"""
Parses command line arguments and provides high level support for
provisioning device with the specified programming tool.
:param prov_cmd_jwt: Path to provisioning JWT file (packet which contains
all data necessary for provisioning, including policy, authorization
packets and keys).
:param cy_bootloader_hex: Path to Cypress Bootloader program file.
:param pub_key_json: File where to save public key in JSON format.
:param pub_key_pem: File where to save public key in PEM format.
"""
# Verify arguments
target = get_target_name(TOOL_NAME, ACCESS_PORT)
if not target:
print('Invalid access port.')
sys.exit(1)
test_status = False
tool = ProgrammingTool.create(TOOL_NAME)
if tool.connect(target):
test_status = provision_execution(tool, pub_key_json, prov_cmd_jwt, cy_bootloader_hex)
if test_status:
# Read device response file and take device key from it
if os.path.exists(pub_key_json):
pem = PemKey(pub_key_json)
pem.save(pub_key_pem, private_key=False)
else:
sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -1,7 +0,0 @@
# To install next python modules, please execute command:
# python -m pip install -r requirements.txt
cryptography>=2.4.2
Click>=7.0
intelhex>=2.2.1
jwcrypto>=0.6.0

View File

@ -1,30 +0,0 @@
:020000041000EA
:400000000001000831010010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075
:400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
:400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
:4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
:40010000526F736574746120636F646500000000764FAF5C61AC315F1497F9DFA542713965B785E5CC2F707D6468D7D1124CDFCF13B500240C4800F01DF821000094012395
:4001400006220A4800F062F80123009406221900064800F05BF801340222044B0448DC60DA6000F099F8F7E7280300108000324020BCBE00014B18607047C0463C100008CC
:4001800070B50D00044C01001822200000F094F8A56101BEFEE7C0460010000870B506000C001500072903D914491548FFF7E8FF1F232A009A4303D012491148FFF7E0FF8A
:4001C000114B1B689A695B69B01AC0090001C0181F231D40032C07D8E400A340A54002689A431543056070BD043CE400A340A54042689A4315434560F5E7C04642020000CD
:4002000033040010430200003C100008F7B51D0008AB1B7807000E0014000193072903D9D1211B48FFF7ACFF0F2322009A4303D0D2211748FFF7A4FF012D03D9D3211448C7
:40024000FFF79EFF1F23019A9A4303D0D4211048FFF796FF012211001540B140B5400F203B6804408B431D433D600500B1008D408C40084B31001B68380063331A78BA18AE
:400280001368AB431C431460019AFFF787FFF7BD100400103C1000080230800803D001300238FCD1C046C0467047EFF3108072B6704780F310887047000052E3011041E2BB
:4002C00000C0A0E11EFF2F0110402DE90C30A0E101E0F1E500005EE3014042E201E0C3E40400001A02C08CE00C0053E10500000A01E0C3E4FBFFFFEA000054E303C0A0E1E7
:400300000420A0E1F0FFFF1A1040BDE81EFF2FE10DC0A0E1F8DF2DE904B04CE20DC0A0E1F8DF2DE904B04CE200002140000025400000014000003440000024400000314064
:400340000000324000001F4101010101010101010110101080550501053B04101C0101000FC000000000100001010101011D3A577896000820000010000F002000023F06D7
:40038000080E00080009000A000B24282C303400100000009000000088000000080000008000000004F0000000F000002005A000D00100018001A0011000000004000000C4
:4003C000400000004400000048000000800000000001000010020000880200000004000010040000200400004004000048040000800400008404000090040000C0070000E1
:40040000C4070000C8070000CC070000004000002E2F68616C2F70646C2F647269766572732F736F757263652F63795F6770696F2E63002E2F68616C2F70646C2F64726907
:40044000766572732F696E636C7564652F63795F6770696F2E68000000000000000000000000000000000000000000000000000000000000000000000000000000000000FA
:40048000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C
:4004C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FC
:4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000BB
:40054000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007B
:40058000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003B
:4005C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FB
:0200000490303A
:02000000FF3BC4
:0200000490501A
:0C00000000000000000000000000FF3BBA
:00000001FF

View File

@ -1,301 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
import os.path
import time
import unittest
from intelhex import IntelHex
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(os.path.dirname('../execute'))
from test_utility import *
from execute.programmer.programmer import ProgrammingTool
from execute.programmer.exceptions import ExtendedTransferFaultError
TOOL = 'pyocd'
TARGET = 'cy8c64xx_cm4'
PROBE_ID = '19071301d90e140c00d90e1400000000000000002e127069' # DAP-Link
#PROBE_ID = '1A06199701047400' # CMSIS-DAP
# PSoC6 BLE Memory Regions
RAM_ADDR = 0x08000000
MAIN_ADDR = 0x10000000
WORK_ADDR = 0x14000000
SFLASH_ADDR = 0x16000000
# PSoC6 Register Addresses
CYREG_IPC2_STRUCT_ACQUIRE = 0x40230040
CYREG_IPC2_STRUCT_DATA = 0x4023004c
CYREG_IPC2_STRUCT_NOTIFY = 0x40230048
CYREG_GPIO_PORT_11_OUT = 0x40320580
CYREG_CM4_POWER_CTRL = 0x40210080
ENTRANCE_EXAM_SRAM_ADDR = 0x0802E000
# Debug halting control and status register
CYREG_DHCSR = 0xE000EDF0
S_HALT = (1 << 17)
S_LOCKUP = (1 << 19)
class TestReadWrite(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.tool = ProgrammingTool.create(TOOL)
cls.tool.connect(TARGET, probe_id=PROBE_ID)
create_test_bin()
print('[INFO] Program Main region with test data')
cls.tool.program(TEST_BIN, address=MAIN_ADDR)
print('[INFO] Program Work region with test data')
cls.tool.program(TEST_BIN, address=WORK_ADDR)
@classmethod
def tearDownClass(cls):
cls.tool.disconnect()
def test_read8_ram(self):
data = self.tool.read8(RAM_ADDR)
self.assertGreaterEqual(data, 0)
def test_read8_flash_main_region(self):
data = self.tool.read8(MAIN_ADDR)
self.assertEqual(data, 0x41)
def test_read8_flash_work_region(self):
data = self.tool.read8(WORK_ADDR)
self.assertEqual(data, 0x41)
def test_read8_sflash(self):
data = self.tool.read8(SFLASH_ADDR)
self.assertEqual(data, 0x00)
def test_read8_register32_negative(self):
with self.assertRaises(ExtendedTransferFaultError) as context:
self.tool.read8(CYREG_IPC2_STRUCT_DATA)
self.assertTrue('If address points to a register it should be aligned with the register size'
in str(context.exception))
def test_read16_ram(self):
data = self.tool.read16(RAM_ADDR)
self.assertGreater(data, 0)
def test_read16_flash_main_region(self):
data = self.tool.read16(MAIN_ADDR)
self.assertEqual(data, 0x3141)
def test_read16_flash_work_region(self):
data = self.tool.read16(WORK_ADDR)
self.assertEqual(data, 0x3141)
def test_read16_register32_negative(self):
with self.assertRaises(ExtendedTransferFaultError) as context:
self.tool.read16(CYREG_IPC2_STRUCT_DATA)
self.assertTrue('If address points to a register it should be aligned with the register size'
in str(context.exception))
def test_read32_ram(self):
data = self.tool.read32(RAM_ADDR)
self.assertGreater(data, 0x00)
def test_read32_flash_main_region(self):
data = self.tool.read32(MAIN_ADDR)
self.assertEqual(data, 0x32413141)
def test_read32_not_exists_flash_region(self):
with self.assertRaises(ExtendedTransferFaultError):
self.tool.read32(0xFF00FF00)
def test_read32_flash_work_region(self):
data = self.tool.read32(WORK_ADDR)
self.assertEqual(data, 0x32413141)
def test_read32_register32(self):
data = self.tool.read32(CYREG_IPC2_STRUCT_DATA)
self.assertGreaterEqual(data, 0x00000000)
def test_write8_ram(self):
self.tool.write8(RAM_ADDR, 0xCE)
data = self.tool.read8(RAM_ADDR)
self.assertEqual(0xCE, data)
def test_write16_ram(self):
self.tool.write16(RAM_ADDR, 0xAC21)
data = self.tool.read16(RAM_ADDR)
self.assertEqual(0xAC21, data)
def test_write32_ram(self):
self.tool.write32(RAM_ADDR, 0xDEADBEAF)
data = self.tool.read32(RAM_ADDR)
self.assertEqual(data, 0xDEADBEAF)
def test_write32_CYREG_IPC2_STRUCT_ACQUIRE(self):
self.tool.write32(CYREG_IPC2_STRUCT_ACQUIRE, 0x80000000)
data = self.tool.read32(CYREG_IPC2_STRUCT_ACQUIRE)
byte = decomposite32(data)[3]
self.assertEqual(byte, 0x80)
def test_write32_CYREG_IPC2_STRUCT_DATA(self):
self.tool.write32(CYREG_IPC2_STRUCT_DATA, 0xAFECAB91)
data = self.tool.read32(CYREG_IPC2_STRUCT_DATA)
self.assertEqual(data, 0xAFECAB91)
def test_write32_CYREG_IPC2_STRUCT_NOTIFY(self):
self.tool.write32(CYREG_IPC2_STRUCT_NOTIFY, 0x00000001)
data = self.tool.read32(CYREG_IPC2_STRUCT_NOTIFY)
byte = decomposite32(data)[3]
self.assertEqual(byte, 0x00)
def test_write32_ENTRANCE_EXAM_SRAM_ADDR(self):
self.tool.write32(ENTRANCE_EXAM_SRAM_ADDR, 0xD1A2B3C4)
data = self.tool.read32(ENTRANCE_EXAM_SRAM_ADDR)
self.assertEqual(data, 0xD1A2B3C4)
def test_write32_ENTRANCE_EXAM_SRAM_ADDR_offset4(self):
self.tool.write32(ENTRANCE_EXAM_SRAM_ADDR + 0x04, 0xABCABCFA)
data = self.tool.read32(ENTRANCE_EXAM_SRAM_ADDR + 0x04)
self.assertEqual(data, 0xABCABCFA)
def test_write32_ENTRANCE_EXAM_SRAM_ADDR_offset8(self):
self.tool.write32(ENTRANCE_EXAM_SRAM_ADDR + 0x08, 0x12890735)
data = self.tool.read32(ENTRANCE_EXAM_SRAM_ADDR + 0x08)
self.assertEqual(data, 0x12890735)
def tet_read_write_core_regs(self):
value1 = self.tool.read_reg('r1')
self.tool.write_reg('r1', value1 + 10)
value2 = self.tool.read_reg('r1')
self.assertEqual(value1 + 10, value2)
def test_read_strange_reg_negative(self):
with self.assertRaises(ValueError):
self.tool.read_reg('r200')
class TestProgramming(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.tool = ProgrammingTool.create(TOOL)
cls.tool.connect(TARGET, probe_id=PROBE_ID)
create_test_bin()
@classmethod
def tearDownClass(cls):
cls.tool.disconnect()
def test_erase(self):
self.tool.erase(MAIN_ADDR, 0x200)
match = False
i = 0
while i < os.path.getsize(TEST_BIN):
data = self.tool.read8(MAIN_ADDR + i)
match = data == 0x00
if not match:
break
i += 1
self.assertTrue(match)
def test_program_bin(self):
self.tool.erase(MAIN_ADDR, 0x200)
self.tool.program(TEST_BIN, address=MAIN_ADDR)
with open(TEST_BIN, 'rb') as file_obj:
exp_data = file_obj.read()
match = False
i = 0
while i < len(exp_data):
data = self.tool.read8(MAIN_ADDR + i)
match = data == exp_data[i]
if not match:
break
i += 1
self.assertTrue(match)
def test_program_hex(self):
intel_hex = IntelHex()
intel_hex.loadhex(BLINKY_SMALL_HEX)
arr = intel_hex.gets(MAIN_ADDR, 0x400)
self.tool.erase(MAIN_ADDR, 0x400)
self.tool.program(BLINKY_SMALL_HEX)
match = False
i = 0
while i < len(arr):
data = self.tool.read8(MAIN_ADDR + i)
match = data == arr[i]
if not match:
break
i += 1
self.assertTrue(match)
class TestControlAPIs(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.tool = ProgrammingTool.create(TOOL)
cls.tool.connect(TARGET, probe_id=PROBE_ID)
@classmethod
def tearDownClass(cls):
cls.tool.disconnect()
def test_reset(self):
self.tool.write32(CYREG_GPIO_PORT_11_OUT, 0xDEADBEAF)
self.tool.reset()
self.assertEqual(self.tool.read32(CYREG_GPIO_PORT_11_OUT), 0x00)
def test_halt_resume(self):
self.tool.reset()
if self.tool.read32(CYREG_CM4_POWER_CTRL) & 3 != 3:
self.tool.write32(CYREG_CM4_POWER_CTRL, 0x05fa0003) # CM4 is sleeping, trying to wake it up
dhcsr = self.tool.read32(CYREG_DHCSR)
if dhcsr & S_LOCKUP:
self.tool.halt()
dhcsr = self.tool.read32(CYREG_DHCSR)
self.tool.write_reg('xpsr', 0x01000000) # set thumb bit
else:
self.tool.halt()
dhcsr = self.tool.read32(CYREG_DHCSR)
self.assertTrue(dhcsr & S_HALT)
self.tool.resume()
dhcsr = self.tool.read32(CYREG_DHCSR)
self.assertFalse(dhcsr & S_HALT)
#def test_reset_and_halt(self):
# self.tool.write32(CYREG_GPIO_PORT_11_OUT, 0xDEADBEAF)
# self.tool.reset_and_halt()
# self.assertEqual(self.tool.read32(CYREG_GPIO_PORT_11_OUT), 0x00)
# dhcsr = self.tool.read32(CYREG_DHCSR)
# self.assertTrue(dhcsr & S_HALT)
@unittest.skip("HW frequency cannot be changed.")
def test_set_frequency(self):
self.tool.erase(MAIN_ADDR, 0xC000)
self.tool.set_frequency(100)
time_before_program = time.time()
self.tool.program(BLINKY_LARGE_HEX)
time1 = time.time() - time_before_program
self.tool.set_frequency(1200)
self.tool.erase(MAIN_ADDR, 0xC000)
time_before_program = time.time()
self.tool.program(BLINKY_LARGE_HEX)
time2 = time.time() - time_before_program
self.assertGreater(time1, time2)
def test_connection(self):
self.tool.disconnect()
self.assertFalse(self.tool.session.is_open)
self.tool.connect(TARGET, probe_id=PROBE_ID)
self.assertTrue(self.tool.session.is_open)
if __name__ == '__main__':
ret = not unittest.main().wasSuccessful()
sys.exit(ret)

View File

@ -1,39 +0,0 @@
# Copyright 2019 Cypress Semiconductor Corporation
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
TEST_BIN = "test_data/test.bin"
BLINKY_SMALL_HEX = "test_data/blinky_small.hex"
BLINKY_LARGE_HEX = "test_data/blinky_large.hex"
def create_test_bin():
with open(TEST_BIN, 'wb') as file:
file.write(b'A1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FF'
b'A1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FF'
b'A1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FF'
b'A1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FF'
b'A1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FF'
b'A1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FFA1A2A3B1B2B3111213FF'
b'A1A2A3B1B2B3111213FFA1A2A3B1B2B')
def decomposite32(value):
"""
Decomposites 32-bit value into byte array.
:param value: 32-bit value.
:return: Array of bytes.
"""
byte = value.to_bytes(4, 'little')
return byte

View File

@ -55,6 +55,10 @@ SPE_IMAGE_ID = 1
NSPE_IMAGE_ID = 16
SMIF_MEM_MAP_START = 0x18000000
class AddSignatureError(Exception):
""" A simple class that represents all the exceptions associated with
adding signature to Secure Boot image
"""
# Patch Cypress hex file:
# - update checksum
@ -150,6 +154,17 @@ def find_cm0_image(toolchain, resources, elf, hexf, hex_filename):
# check if policy parameters are consistent
def check_slots_integrity(toolchain, fw_cyb, target_data, fw_spe=None, fw_nspe=None):
"""
Function checks consistency of parameters presented in
policy file used for build of Secure Boot enabled target.
:param toolchain: Toolchain object of current build session
:param fw_cyb: CyBootloader firmware description from policy
:param target_data: Object contains description of
processing target from target.json
:param fw_spe: CM0p firmware descpription object from policy
:param fw_nspe: CM4 firmware descpription object from policy
:return: List of slots and image id corresponding to them
"""
slot0 = None
slot1 = None
@ -171,24 +186,23 @@ def check_slots_integrity(toolchain, fw_cyb, target_data, fw_spe=None, fw_nspe=N
if slot["type"] == "BOOT":
slot0 = slot
if fw_nspe["upgrade"] and True:
if fw_nspe["upgrade"] is True:
slot1 = slot
if slot["type"] == "UPGRADE":
try:
if fw_nspe["encrypt"] and True:
# mark slot1 image as one, that should be encrypted
slot1.update({'encrypt': True})
toolchain.notify.info("[PSOC6.sign_image] INFO: Image for UPGRADE NSPE will"
" be ENCRYPTED per policy settings.")
except KeyError:
None
if fw_nspe.get("encrypt") is True:
# mark slot1 image as one, that should be encrypted
slot1.update({'encrypt': True})
toolchain.notify.info("[PSOC6.sign_image] INFO: Image for UPGRADE NSPE will"
" be ENCRYPTED per policy settings.")
else:
pass
else:
toolchain.notify.info("[PSOC6.sign_image] INFO: Image for UPGRADE will not"
" be built per policy settings.")
break
if slot0 is None:
toolchain.notify.debug("[PSOC6.sign_image] WARNING: BOOT section not found in policy resources")
raise Exception("imgtool finished execution with errors!")
raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.")
else:
# check if PSA targets flash map correspond to slots addresses and sizes in policy
@ -219,40 +233,38 @@ def check_slots_integrity(toolchain, fw_cyb, target_data, fw_spe=None, fw_nspe=N
if img_id != 1:
toolchain.notify.debug("[PSOC6.sign_image] ERROR: Image ID of SPE image"
" is not equal to 1!")
raise Exception("imgtool finished execution with errors!")
raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.")
if not (fw_cyb["launch"] == img_id):
toolchain.notify.debug("[PSOC6.sign_image] ERROR: ID of build image"
" does not correspond launch ID in CyBootloader!")
raise Exception("imgtool finished execution with errors!")
raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.")
if not (fw_spe["launch"] == fw_nspe["id"]):
toolchain.notify.debug("[PSOC6.sign_image] ERROR: ID of NSPE image"
" does not correspond launch ID in SPE part!")
raise Exception("imgtool finished execution with errors!")
raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.")
# check slots addresses and sizes if upgrade is set to True
for slot in fw_spe["resources"]:
if slot["type"] == "BOOT":
slot0 = slot
if fw_spe["upgrade"] and True:
if fw_spe["upgrade"] is True:
if slot["type"] == "UPGRADE":
slot1 = slot
try:
if fw_spe["encrypt"] and True:
# mark slot1 image as one, that should be encrypted
slot1.update({'encrypt': True})
toolchain.notify.info("[PSOC6.sign_image] INFO: Image for UPGRADE SPE will"
" be ENCRYPTED per policy settings.")
except KeyError:
None
if fw_spe.get("encrypt") is True:
# mark slot1 image as one, that should be encrypted
slot1.update({'encrypt': True})
toolchain.notify.info("[PSOC6.sign_image] INFO: Image for UPGRADE SPE will"
" be ENCRYPTED per policy settings.")
else:
pass
else:
toolchain.notify.info("[PSOC6.sign_image] INFO: Image for UPGRADE will not"
" be produced per policy settings.")
" be produced per policy settings.")
if slot0 is None:
toolchain.notify.debug("[PSOC6.sign_image] WARNING: BOOT section not found in policy resources")
raise Exception("imgtool finished execution with errors!")
raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.")
if slot1 is not None:
# bigger or equal to 0x18000000 in hex is a start of SMIF memory
@ -262,14 +274,18 @@ def check_slots_integrity(toolchain, fw_cyb, target_data, fw_spe=None, fw_nspe=N
if slot0["size"] != slot1["size"]:
toolchain.notify.debug("[PSOC6.sign_image] WARNING: BOOT and UPGRADE slots sizes are not equal")
return [slot0, slot1, img_id]
return (slot0, slot1, img_id)
else:
return [slot0, None, img_id]
return (slot0, None, img_id)
# Resolve Secure Boot policy sections considering target
def process_target(toolchain, target):
"""
Gathers and process information about target being built
:param toolchain: Toolchain object of current build session
:param target: Name of target being built
:return: List with all data needed for adding signature
"""
targets_json = Path("targets/targets.json")
cy_targets = Path("targets/TARGET_Cypress/TARGET_PSOC6/")
sb_params_file_name = Path("secure_image_parameters.json")
@ -283,7 +299,7 @@ def process_target(toolchain, target):
root_dir = root_dir / 'mbed-os'
if not os.path.isfile(str(mbed_os_targets)):
toolchain.notify.debug("[PSOC6.sign_image] ERROR: targets.json not found!")
raise Exception("imgtool finished execution with errors!")
raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.")
with open(str(mbed_os_targets)) as j:
json_str = j.read()
@ -300,7 +316,7 @@ def process_target(toolchain, target):
f.close()
else:
toolchain.notify.debug("[PSOC6.sign_image] ERROR: secure_image_parametest.json not found!")
raise Exception("imgtool finished execution with errors!")
raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.")
sdk_path = root_dir / sb_config["sdk_path"]
@ -308,7 +324,7 @@ def process_target(toolchain, target):
if not os.path.isfile(str(priv_key_path)):
toolchain.notify.debug("[PSOC6.sign_image] ERROR: Private key file not found in " + str(priv_key_path))
raise Exception("imgtool finished execution with errors!")
raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.")
if "_PSA" in target:
# assume dual stage bootloading scheme
@ -347,35 +363,38 @@ def process_target(toolchain, target):
target_sig_data = [{"img_data": sb_config["boot0"], "slot_data": slots[0],
"key_file": sb_config["priv_key_file"], "sdk_path": sdk_path, "id": slots[2]}]
if slots[1] is not None:
if slots[1] is not None:
target_sig_data.append({"img_data": sb_config["boot1"], "slot_data": slots[1],
"key_file": sb_config["priv_key_file"], "sdk_path": sdk_path, "id": slots[2]})
# check if slot1 image sould be encrypted
try:
if slots[1]["encrypt"] is True:
dev_pub_key = sdk_path / Path(sb_config["dev_pub_key_file"])
if not os.path.isfile(str(dev_pub_key)):
toolchain.notify.debug("[PSOC6.sign_image] ERROR: Device public key file not found in " + str(dev_pub_key))
raise Exception("imgtool finished execution with errors!")
if slots[1].get("encrypt") is True:
aes_key_file = sdk_path / Path(sb_config["aes_key_file"])
if not os.path.isfile(str(aes_key_file)):
toolchain.notify.debug("[PSOC6.sign_image] ERROR: AES-128 key file not found in " + str(aes_key_file))
raise Exception("imgtool finished execution with errors!")
dev_pub_key = sdk_path / Path(sb_config["dev_pub_key_file"])
if not os.path.isfile(str(dev_pub_key)):
toolchain.notify.debug("[PSOC6.sign_image] ERROR: Device public key file not found in " + str(dev_pub_key))
raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.")
target_sig_data[1].update({"aes_key": sb_config["aes_key_file"], "dev_pub_key": sb_config["dev_pub_key_file"]})
aes_key_file = sdk_path / Path(sb_config["aes_key_file"])
if not os.path.isfile(str(aes_key_file)):
toolchain.notify.debug("[PSOC6.sign_image] ERROR: AES-128 key file not found in " + str(aes_key_file))
raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.")
except KeyError:
target_sig_data[1].update({"aes_key": sb_config["aes_key_file"], "dev_pub_key": sb_config["dev_pub_key_file"]})
else:
toolchain.notify.info("[PSOC6.sign_image] INFO: Image for slot UPGRADE would not be encrypted per policy settings")
return target_sig_data
# Sign binary image using imgtool
def sign_image(toolchain, elf0, binf, hexf1=None):
def sign_image(toolchain, binf):
"""
Adds signature to a binary file being built,
prepares some intermediate binary artifacts.
:param toolchain: Toolchain object of current build session
:param binf: Binary file created for target
"""
target_sig_data = None
# reserve name for separate NSPE image
out_cm4_hex = binf[:-4] + "_cm4.hex"
@ -391,41 +410,40 @@ def sign_image(toolchain, elf0, binf, hexf1=None):
if target_sig_data is None:
toolchain.notify.debug("[PSOC6.sign_image] ERROR: Target not found!")
raise Exception("imgtool finished execution with errors!")
raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.")
for slot in target_sig_data:
# first check if image for slot under processing should be encrypted
try:
if slot["slot_data"]["encrypt"] is True:
# call encrypt_img to perform encryption
args = [sys.executable, str(slot["sdk_path"] / "encrypted_image_runner.py"),
"--sdk-path", str(slot["sdk_path"]), "--hex-file", os.getcwd() + '/' + mbed_hex,
"--key-priv", str(slot["sdk_path"] / slot["key_file"]),
"--key-pub", str(slot["sdk_path"] / slot["dev_pub_key"]),
"--key-aes", str(slot["sdk_path"] / slot["aes_key"]),
"--ver", str(slot["img_data"]["VERSION"]), "--img-id", str(slot["id"]),
"--rlb-count", str(slot["img_data"]["ROLLBACK_COUNTER"]),
"--slot-size", str(hex(slot["slot_data"]["size"])),
"--img-offset", str(slot["slot_data"]["address"])]
if slot["slot_data"]["type"] != "BOOT":
args.append("--pad")
process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if slot["slot_data"].get("encrypt") is True:
# call encrypt_img to perform encryption
args = [sys.executable, str(slot["sdk_path"] / "encrypted_image_runner.py"),
"--sdk-path", str(slot["sdk_path"]), "--hex-file", os.getcwd() + '/' + mbed_hex,
"--key-priv", str(slot["sdk_path"] / slot["key_file"]),
"--key-pub", str(slot["sdk_path"] / slot["dev_pub_key"]),
"--key-aes", str(slot["sdk_path"] / slot["aes_key"]),
"--ver", str(slot["img_data"]["VERSION"]), "--img-id", str(slot["id"]),
"--rlb-count", str(slot["img_data"]["ROLLBACK_COUNTER"]),
"--slot-size", str(hex(slot["slot_data"]["size"])),
"--img-offset", str(slot["slot_data"]["address"])]
if slot["slot_data"]["type"] != "BOOT":
args.append("--pad")
process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# catch standard process pipes outputs
stderr = process.communicate()[1]
stdout = process.communicate()[0]
rc = process.wait()
print(stdout.decode("utf-8"))
# catch standard process pipes outputs
stderr = process.communicate()[1]
stdout = process.communicate()[0]
rc = process.wait()
toolchain.notify.info(stdout.decode("utf-8"))
if rc != 0:
toolchain.notify.debug("[PSOC6.sign_image] ERROR: Encryption script ended with error!")
toolchain.notify.debug("[PSOC6.sign_image] Message from encryption script: " + stderr.decode("utf-8"))
raise Exception("imgtool finished execution with errors!")
else:
toolchain.notify.info("[PSOC6.sign_image] SUCCESS: Image for slot " +
slot["slot_data"]["type"] + " is signed and encrypted with no errors!")
if rc != 0:
toolchain.notify.debug("[PSOC6.sign_image] ERROR: Encryption script ended with error!")
toolchain.notify.debug("[PSOC6.sign_image] Message from encryption script: " + stderr.decode("utf-8"))
raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.")
else:
toolchain.notify.info("[PSOC6.sign_image] SUCCESS: Image for slot " +
slot["slot_data"]["type"] + " is signed and encrypted with no errors!")
# all non ecrypted images take this path
except KeyError:
else:
if slot["slot_data"]["type"] == "UPGRADE":
out_hex_name = binf[:-4] + "_upgrade.hex"
out_bin_name = out_hex_name[:-4] + "_signed.bin"
@ -452,7 +470,7 @@ def sign_image(toolchain, elf0, binf, hexf1=None):
if rc != 0:
toolchain.notify.debug("[PSOC6.sign_image] ERROR: Signature is not added!")
toolchain.notify.debug("[PSOC6.sign_image] Message from imgtool: " + stderr.decode("utf-8"))
raise Exception("imgtool finished execution with errors!")
raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.")
else:
toolchain.notify.info("[PSOC6.sign_image] SUCCESS: Image for slot " +
slot["slot_data"]["type"] + " is signed with no errors!")
@ -467,7 +485,11 @@ def sign_image(toolchain, elf0, binf, hexf1=None):
# produce hex file for slot1
if slot["slot_data"]["type"] == "UPGRADE":
bin2hex(out_bin_name, out_hex_name, offset=int(slot["slot_data"]["address"]))
print("Image UPGRADE: " + out_hex_name + "\n")
toolchain.notify.info("Image UPGRADE: " + out_hex_name + "\n")
def complete(toolchain, elf0, hexf0, hexf1=None):
"""
Merge CM4 and CM0 images to a single binary
"""
complete_func(toolchain.notify.debug, elf0, hexf0, hexf1)

View File

@ -677,15 +677,11 @@ class PSOC6Code:
@staticmethod
def sign_image(t_self, resources, elf, binf):
"""
Calls sign_image function to add signature to Secure Boot binary file.
"""
from tools.targets.PSOC6 import sign_image as psoc6_sign_image
if hasattr(t_self.target, "hex_filename"):
hex_filename = t_self.target.hex_filename
# Completing main image involves merging M0 image.
from tools.targets.PSOC6 import find_cm0_image
m0hexf = find_cm0_image(t_self, resources, elf, binf, hex_filename)
psoc6_sign_image(t_self, elf, binf, m0hexf)
else:
psoc6_sign_image(t_self, elf, binf)
psoc6_sign_image(t_self, binf)
class ArmMuscaA1Code:
"""Musca-A1 Hooks"""