mirror of https://github.com/ARMmbed/mbed-os.git
Removed scripts for provisiongs from repository, made fixes per PR comments, adjusted Readme.md
parent
44401b5917
commit
515555e7ba
|
@ -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+
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
packet
|
||||
packet
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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()
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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()
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
|
@ -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()
|
|
@ -90,7 +90,7 @@
|
|||
"id": 4,
|
||||
"monotonic": 0,
|
||||
"smif_id": 1,
|
||||
"upgrade": true,
|
||||
"upgrade": false,
|
||||
"encrypt": false,
|
||||
"encrypt_key_id": 1,
|
||||
"upgrade_auth": [
|
File diff suppressed because it is too large
Load Diff
|
@ -1 +0,0 @@
|
|||
eyJhbGciOiJFUzI1NiJ9.eyJjeV9wdWJfa2V5Ijp7ImNydiI6IlAtMjU2Iiwia2lkIjoiMyIsImt0eSI6IkVDIiwidXNlIjoic2lnIiwieCI6IlNiOGxUcHlfcGQzTnJVVGtoSXpnMmp6TTM3dU5xTml1dDhXQy1RdjNYTVEiLCJ5IjoiQ3R3Q2k0YXJYc2pFRDVUVm1yX3ZQbFAya2UxMzNLSzdsUDdTel9JWmlERSJ9LCJleHAiOjE1Nzc3NDMyMDAsImlhdCI6MTU2MzI4NTQ1NCwiaW1hZ2VfYWRkcmVzcyI6MjY5Mjg3NDI0LCJpbWFnZV9maWxlIjoiQ3lwcmVzc0Jvb3Rsb2FkZXJfQ00wcC5oZXgiLCJpbWFnZV9oYXNoIjpbNjksOTYsNTEsNCwxMzksMTcwLDE4MSwxNDMsMTU5LDE2MywyMDgsMCwzNiwyNDYsNTcsMTY1LDI1NCwxOTgsMTI0LDY5LDczLDAsMTAxLDIxNywxMjUsMTE4LDQ3LDIwLDQzLDYzLDIxMyw3NV0sImltYWdlX2lkIjowLCJpbWFnZV92ZXJzaW9uIjoiMS4wLjAuMTI1IiwicG9saWN5X3RlbXBsYXRlIjoiIn0.Y_P-BfopTCNRBi5ZhwjGsCkq3qeFoGAW30Oy-Tn4JK-BMxcyXyIoABcB06Oyg6PwJjcPprTNCuAV_Pxirn3r0A
|
File diff suppressed because it is too large
Load Diff
|
@ -1 +0,0 @@
|
|||
eyJhbGciOiJFUzI1NiJ9.eyJjeV9wdWJfa2V5Ijp7ImNydiI6IlAtMjU2Iiwia2lkIjoiMyIsImt0eSI6IkVDIiwidXNlIjoic2lnIiwieCI6IlNiOGxUcHlfcGQzTnJVVGtoSXpnMmp6TTM3dU5xTml1dDhXQy1RdjNYTVEiLCJ5IjoiQ3R3Q2k0YXJYc2pFRDVUVm1yX3ZQbFAya2UxMzNLSzdsUDdTel9JWmlERSJ9LCJleHAiOjE1Nzc3NDMyMDAsImlhdCI6MTU2MzI4NjA3NiwiaW1hZ2VfYWRkcmVzcyI6MjY5Mjg3NDI0LCJpbWFnZV9maWxlIjoiQ3lwcmVzc0Jvb3Rsb2FkZXJfQ00wcC5oZXgiLCJpbWFnZV9oYXNoIjpbMTY3LDExNSwxMTUsNjIsNiwyMjEsMTIsMjM0LDE0MiwzNyw0NiwyNTQsMjM5LDI0OCw2MCwxNTcsOTksNCwxOTcsMTc1LDIxNiw4MCwxMDIsNDYsMTY5LDE5NiwxNzgsNTMsOTQsNDksMjMxLDIwMl0sImltYWdlX2lkIjowLCJpbWFnZV92ZXJzaW9uIjoiMS4wLjAuMTI1IiwicG9saWN5X3RlbXBsYXRlIjoiIn0.kC_8cSqHKBHl1770umc1em1ZhuwjSHKxY1TigIBjcPVd_Gnr2X4LcMGIB6biwVWWYmtONF7k87XXfpM4NqByAg
|
|
@ -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.
|
|
@ -1 +0,0 @@
|
|||
eyJhbGciOiJFUzI1NiJ9.eyJhdXRoIjp7fSwiY3lfcHViX2tleSI6eyJjcnYiOiJQLTI1NiIsImtpZCI6IjMiLCJrdHkiOiJFQyIsInVzZSI6InNpZyIsIngiOiJTYjhsVHB5X3BkM05yVVRraEl6ZzJqek0zN3VOcU5pdXQ4V0MtUXYzWE1RIiwieSI6IkN0d0NpNGFyWHNqRUQ1VFZtcl92UGxQMmtlMTMzS0s3bFA3U3pfSVppREUifSwiZXhwIjoxNTc3NzQzMjAwLCJoc21fcHViX2tleSI6eyJjcnYiOiJQLTI1NiIsImtpZCI6IjQiLCJrdHkiOiJFQyIsInVzZSI6InNpZyIsIngiOiJzSk1zTi0ySm8yN2tjNTF3Vks3eEoyZlA5QkRrekFjMmZaRVpNbG9oSFhBIiwieSI6Ik1XbHV6bVhnWE92ZFFRRFlYM3l5MVRrOVFvSEwtOURaaHN3WnBZMFhlNVUifSwiaWF0IjoxNTUzNjg1NTczLCJ0eXBlIjoiQ1lfQVVUSF9IU00ifQ.oXeFYugOceM2XvnoWTEju8ByztA4OvFKrQxndIOts_nlgmRti2ddoHRGR86GMNCDfcHr54mXH33GkQ8D96DoGw
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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.
|
|
@ -1,5 +0,0 @@
|
|||
The following are licensed under the Apache 2.0 license:
|
||||
|
||||
mcuboot
|
||||
Mbed Crypto
|
||||
|
|
@ -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.
|
||||
*/
|
||||
|
|
@ -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.
|
||||
*/
|
|
@ -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
|
||||
|
|
@ -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"]
|
||||
|
|
@ -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()
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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"
|
||||
|
|
@ -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()
|
|
@ -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()
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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"""
|
||||
|
|
Loading…
Reference in New Issue