From f689c05db78b001f31d447b404154c70d818b5cc Mon Sep 17 00:00:00 2001 From: Roman Okhrimenko Date: Wed, 10 Jun 2020 22:32:55 +0300 Subject: [PATCH] Modify post build scripts to support only cysecuretools signing --- tools/targets/PSOC6.py | 408 ++++++++------------------------------ tools/targets/__init__.py | 21 +- 2 files changed, 97 insertions(+), 332 deletions(-) diff --git a/tools/targets/PSOC6.py b/tools/targets/PSOC6.py index b906937ccc..ff6cadb660 100644 --- a/tools/targets/PSOC6.py +++ b/tools/targets/PSOC6.py @@ -145,344 +145,112 @@ def find_cm0_image(toolchain, resources, elf, hexf, hex_filename): return m0hexf -# 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 - - if fw_spe is None: - img_id = fw_nspe["id"] - - # check single stage scheme - if not (fw_cyb["launch"] == img_id): - # may be PSA NSPE part - if not fw_cyb["launch"] == SPE_IMAGE_ID: - toolchain.notify.debug("[PSOC6.sign_image] WARNING: ID of build image " + str(img_id) + - " does not correspond launch ID in CyBootloader - " + str(fw_cyb["launch"])) - else: - toolchain.notify.info("[PSOC6.sign_image] INFO: NSPE image ID is " + str(img_id) + - ". It will be launched by SPE part.") - - # check slots addresses and sizes if upgrade is set to True - for slot in fw_nspe["resources"]: - if slot["type"] == "BOOT": - slot0 = slot - - if fw_nspe["upgrade"] is True: - slot1 = slot - if slot["type"] == "UPGRADE": - 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 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 - if not (int(target_data["overrides"]["secure-rom-start"], 16) - MCUBOOT_HEADER_SIZE) ==\ - int(fw_spe["resources"][0]["address"]): - toolchain.notify.debug("[PSOC6.sign_image] WARNING: SPE start address " - "does not correspond BOOT slot start address defined in policy. " - "Check if MCUboot header offset 0x400 is included in SPE flash start") - - if not (int(target_data["overrides"]["non-secure-rom-start"], 16) - MCUBOOT_HEADER_SIZE) ==\ - int(fw_nspe["resources"][0]["address"]): - toolchain.notify.debug("[PSOC6.sign_image] WARNING: NSPE start address " - "does not correspond BOOT slot start address defined in policy. " - "Check if MCUboot header offset 0x400 is included in NSPE flash start") - - if (int(target_data["overrides"]["secure-rom-size"], 16) + MCUBOOT_HEADER_SIZE) >\ - int(fw_spe["resources"][0]["size"]): - toolchain.notify.debug("[PSOC6.sign_image] WARNING: SPE flash size " - "does not fit in BOOT slot size defined in policy.") - - if (int(target_data["overrides"]["non-secure-rom-size"], 16) + MCUBOOT_HEADER_SIZE) >\ - int(fw_nspe["resources"][0]["size"]): - toolchain.notify.debug("[PSOC6.sign_image] WARNING: NSPE flash size " - "does not fit in BOOT slot size defined in policy.") - - img_id = fw_spe["id"] - # check dual stage scheme - if img_id != 1: - toolchain.notify.debug("[PSOC6.sign_image] ERROR: Image ID of SPE image" - " is not equal to 1!") - 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 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 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"] is True: - if slot["type"] == "UPGRADE": - slot1 = slot - 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.") - if slot0 is None: - toolchain.notify.debug("[PSOC6.sign_image] WARNING: BOOT section not found in policy resources") - 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 - if slot1["address"] >= SMIF_MEM_MAP_START: - toolchain.notify.info("[PSOC6.sign_image] INFO: UPGRADE slot will be resided in external flash") - - 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) - else: - return (slot0, None, img_id) - - -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 - """ - from pathlib import Path - - targets_json = Path("targets/targets.json") - cy_targets = Path("targets/TARGET_Cypress/TARGET_PSOC6/") - sb_params_file_name = Path("secure_image_parameters.json") - root_dir = Path(os.getcwd()) - - mbed_os_targets = root_dir / targets_json - - if not os.path.isfile(str(mbed_os_targets)): - # try location for tests - mbed_os_targets = root_dir / 'mbed-os' / targets_json - 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 AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.") - - with open(str(mbed_os_targets)) as j: - json_str = j.read() - all_targets = json.loads(json_str) - j.close() - - processing_target = all_targets[target] - sb_params_file_path = root_dir / cy_targets / Path("TARGET_" + str(target)) / sb_params_file_name - - if os.path.isfile(str(sb_params_file_path)): - with open(str(sb_params_file_path)) as f: - json_str = f.read() - sb_config = json.loads(json_str) - f.close() - else: - toolchain.notify.debug("[PSOC6.sign_image] ERROR: secure_image_parametest.json not found!") - raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.") - - sdk_path = root_dir / sb_config["sdk_path"] - - priv_key_path = sdk_path / Path(sb_config["priv_key_file"]) - - 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 AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.") - - if "_PSA" in target: - # assume dual stage bootloading scheme - with open(sdk_path / Path(sb_config["policy_file"])) as p: - policy_str = p.read() - policy_file = json.loads(policy_str) - p.close() - - firmware_list = policy_file["boot_upgrade"]["firmware"] - - # collect firmware descriptions from policy for corresponding images - firmware_cyb_cm0p = firmware_list[0] - - if "_M0_" in target: - firmware_spe_cm0p = firmware_list[1] - firmware_nspe_cm4 = firmware_list[2] - - slots = check_slots_integrity(toolchain, fw_cyb=firmware_cyb_cm0p, fw_spe=firmware_spe_cm0p, - fw_nspe=firmware_nspe_cm4, target_data=processing_target) - else: - firmware_nspe_cm4 = firmware_list[2] - slots = check_slots_integrity(toolchain, fw_cyb=firmware_cyb_cm0p, fw_nspe=firmware_nspe_cm4, - target_data=processing_target) - else: - # consider single stage bootloading scheme - with open(sdk_path / Path(sb_config["policy_file"])) as p: - policy_str = p.read() - policy_file = json.loads(policy_str) - p.close() - - firmware_list = policy_file["boot_upgrade"]["firmware"] - firmware_cyb_cm0p = firmware_list[0] - firmware_nspe_cm4 = firmware_list[1] - slots = check_slots_integrity(toolchain, fw_cyb=firmware_cyb_cm0p, - fw_nspe=firmware_nspe_cm4, target_data=processing_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: - 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 - if slots[1].get("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 AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.") - - 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.") - - 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 - - -def sign_image(toolchain, binf): +def sign_image(toolchain, resourses, elf, binf, m0hex): """ Adds signature to a binary file being built, - prepares some intermediate binary artifacts. + using cysecuretools python package. :param toolchain: Toolchain object of current build session :param binf: Binary file created for target """ - from pathlib import PurePath - target_sig_data = None - # reserve name for separate NSPE image - out_cm4_hex = binf[:-4] + "_cm4.hex" + if m0hex != '': + m0hex_build = os.path.join(toolchain.build_dir, toolchain.target.hex_filename) + copy2(m0hex, m0hex_build) + m0hex = m0hex_build - # preserve original hex file from mbed-os build - mbed_hex = binf[:-4] + "_unsigned.hex" - copy2(binf, mbed_hex) + # Mapping from mbed target to cysecuretools target + TARGET_MAPPING = { + "CY8CKIT_064B0S2_4343W": "cy8ckit-064b0s2-4343w", + "CY8CPROTO_064B0S1_BLE": "cy8cproto-064b0s1-ble", + "CY8CPROTO_064S1_SB" : "cy8cproto-064s1-sb", + "CY8CPROTO_064B0S3" : "cy8cproto-064b0s3" + } - # find target name and type before processing - for part in PurePath(binf).parts: - if "CY" in part: - target_sig_data = process_target(toolchain=toolchain, target=part) + try: + secure_target = TARGET_MAPPING[toolchain.target.name] + except KeyError: + raise ConfigException("[PSOC6.sign_image] Target " + toolchain.target.name + " is not supported in cysecuretools.") - if target_sig_data is None: - toolchain.notify.debug("[PSOC6.sign_image] ERROR: Target not found!") - raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.") + from pathlib import Path, PurePath - for slot in target_sig_data: - # first check if image for slot under processing should be encrypted - 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) + mbed_os_root = Path(os.getcwd()) - # catch standard process pipes outputs - stderr = process.communicate()[1] - stdout = process.communicate()[0] - rc = process.wait() - toolchain.notify.info(stdout.decode("utf-8")) + policy_path = Path(toolchain.target.policy_file) + if policy_path.is_absolute(): + policy_file = policy_path + else: + policy_path = mbed_os_root / policy_path - 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 + if os.path.isfile(str(policy_path)): + policy_file = policy_path else: - if slot["slot_data"]["type"] == "UPGRADE": - out_hex_name = binf[:-4] + "_upgrade.hex" - else: - out_hex_name = binf - - out_bin_name = out_hex_name[:-4] + "_signed.bin" + policy_file = Path(find_policy(toolchain, resourses)) + + toolchain.notify.info("[PSOC6.sign_image] Using policy file: " + str(policy_file)) - # call imgtool for signature - args = [sys.executable, str(slot["sdk_path"] / "imgtool/imgtool.py"), - "sign", "--key", str(slot["sdk_path"] / slot["key_file"]), - "--header-size", str(hex(MCUBOOT_HEADER_SIZE)), "--pad-header", "--align", "8", - "--version", str(slot["img_data"]["VERSION"]), "--image-id", - str(slot["id"]), "--rollback_counter", str(slot["img_data"]["ROLLBACK_COUNTER"]), - "--slot-size", str(hex(slot["slot_data"]["size"])), "--overwrite-only", - mbed_hex, out_hex_name] - if slot["slot_data"]["type"] != "BOOT": - args.append("--pad") - process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + # Append cysecuretools path to sys.path and import cysecuretools. This will + # prioritize system installations of cysecuretools over the included + # cysecuretools. + #sb_tools_path = mbed_os_root / Path("targets/TARGET_Cypress/TARGET_PSOC6/") + #sys.path.append(str(sb_tools_path)) + import cysecuretools - # catch stderr outputs - stderr = process.communicate()[1] - rc = process.wait() + tools = cysecuretools.CySecureTools(secure_target, str(policy_file)) - 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 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!") - # preserve signed binary file - hex2bin(out_hex_name, out_bin_name) + if str(toolchain.target.boot_scheme) == 'single_image': + toolchain.notify.info("[PSOC6.sign_image] single image signing") + sign_application(toolchain, tools, binf, image_id=toolchain.target.cm0_img_id) - # preserve separate hex for cm4 - # 16 is image ID for NSPE image - if slot["id"] == NSPE_IMAGE_ID: - copy2(out_hex_name, out_cm4_hex) + elif str(toolchain.target.boot_scheme) == 'multi_image': + sign_application(toolchain, tools, m0hex, image_id=toolchain.target.cm0_img_id) + sign_application(toolchain, tools, binf, image_id=toolchain.target.cm4_img_id) + + complete(toolchain, elf, hexf0=binf, hexf1=m0hex) + + else: + raise ConfigException("[PSOC6.sign_image] Boot scheme " + str(toolchain.target.boot_scheme) + "is not supported. Supported boot schemes are 'single_image' and 'multi_image' ") + + +def sign_application(toolchain, tools, binary, image_id): + """ + Helper function for adding signature to binary + :param tools: CySecureTools object + :param binary: Path to binary file to add signature + :param image_id: ID of image slot in which binary will be flashed + """ + + # Get address and size of image slot from policy for passed image_id + # UPGRADE image will be generated automatically by cysecuretools + address, size = tools.flash_map(image_id=image_id, image_type="BOOT") + + tools.sign_image(binary, image_id) + toolchain.notify.debug("[PSOC6.sign_image] Slot start address and size for image ID " \ + + str(image_id) + " is " + hex(address) + ", " + hex(size)) + + +def find_policy(toolchain, resources): + """ + Locate path to policy file, defined in targets.json + :param toolchain: toolchain object from mbed build system + :param resources: resources object from mbed build system + """ + policy_filename = toolchain.target.policy_file + + if policy_filename is None: + return None + # Locate user-specified image + from tools.resources import FileType + json_files = resources.get_file_paths(FileType.JSON) + policy = next((f for f in json_files if os.path.basename(f) == policy_filename), None) + + if policy: + toolchain.notify.info("Policy file found: %s." % policy) + else: + toolchain.notify.info("Policy file %s not found. Aborting." % policy_filename) + raise ConfigException("Required policy file not found.") + + return policy - # produce hex file for slot1 - if slot["slot_data"]["type"] == "UPGRADE": - bin2hex(out_bin_name, out_hex_name, offset=int(slot["slot_data"]["address"])) - toolchain.notify.info("Image UPGRADE: " + out_hex_name + "\n") def complete(toolchain, elf0, hexf0, hexf1=None): diff --git a/tools/targets/__init__.py b/tools/targets/__init__.py index 9bb5467f6b..79d9819ade 100644 --- a/tools/targets/__init__.py +++ b/tools/targets/__init__.py @@ -669,20 +669,17 @@ class PSOC6Code(object): def sign_image(t_self, resources, elf, binf): """ Calls sign_image function to add signature to Secure Boot binary file. + This function is used with Cypress kits, that support cysecuretools signing. """ - version = sys.version_info + 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, resources, elf, binf, m0hexf) - # check python version before calling post build as is supports only python3+ - if((version[0] < 3) is True): - t_self.notify.info("[PSOC6.sing_image] Be careful - produced HEX file was not signed and thus " - "is not compatible with Cypress Secure Boot target. " - "You are using Python " + str(sys.version[:5]) + - " which is not supported by CySecureTools. " - "Consider installing Python 3.4+ and rebuild target. " - "For more information refver to User Guide https://www.cypress.com/secureboot-sdk-user-guide") - else: - from tools.targets.PSOC6 import sign_image as psoc6_sign_image - psoc6_sign_image(t_self, binf) class ArmMuscaA1Code(object): """Musca-A1 Hooks"""