Modify post build scripts to support only cysecuretools signing

pull/13122/head
Roman Okhrimenko 2020-06-10 22:32:55 +03:00
parent b59a80d6ca
commit f689c05db7
2 changed files with 97 additions and 332 deletions

View File

@ -145,344 +145,112 @@ def find_cm0_image(toolchain, resources, elf, hexf, hex_filename):
return m0hexf return m0hexf
# check if policy parameters are consistent def sign_image(toolchain, resourses, elf, binf, m0hex):
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):
""" """
Adds signature to a binary file being built, 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 toolchain: Toolchain object of current build session
:param binf: Binary file created for target :param binf: Binary file created for target
""" """
from pathlib import PurePath
target_sig_data = None if m0hex != '':
# reserve name for separate NSPE image m0hex_build = os.path.join(toolchain.build_dir, toolchain.target.hex_filename)
out_cm4_hex = binf[:-4] + "_cm4.hex" copy2(m0hex, m0hex_build)
m0hex = m0hex_build
# preserve original hex file from mbed-os build # Mapping from mbed target to cysecuretools target
mbed_hex = binf[:-4] + "_unsigned.hex" TARGET_MAPPING = {
copy2(binf, mbed_hex) "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 try:
for part in PurePath(binf).parts: secure_target = TARGET_MAPPING[toolchain.target.name]
if "CY" in part: except KeyError:
target_sig_data = process_target(toolchain=toolchain, target=part) raise ConfigException("[PSOC6.sign_image] Target " + toolchain.target.name + " is not supported in cysecuretools.")
if target_sig_data is None: from pathlib import Path, PurePath
toolchain.notify.debug("[PSOC6.sign_image] ERROR: Target not found!")
raise AddSignatureError("PSOC6.sign_image finished execution with errors! Signature is not added.")
for slot in target_sig_data: mbed_os_root = Path(os.getcwd())
# 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)
# catch standard process pipes outputs policy_path = Path(toolchain.target.policy_file)
stderr = process.communicate()[1] if policy_path.is_absolute():
stdout = process.communicate()[0] policy_file = policy_path
rc = process.wait() else:
toolchain.notify.info(stdout.decode("utf-8")) policy_path = mbed_os_root / policy_path
if rc != 0: if os.path.isfile(str(policy_path)):
toolchain.notify.debug("[PSOC6.sign_image] ERROR: Encryption script ended with error!") policy_file = policy_path
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
else: else:
if slot["slot_data"]["type"] == "UPGRADE": policy_file = Path(find_policy(toolchain, resourses))
out_hex_name = binf[:-4] + "_upgrade.hex"
else: toolchain.notify.info("[PSOC6.sign_image] Using policy file: " + str(policy_file))
out_hex_name = binf
out_bin_name = out_hex_name[:-4] + "_signed.bin"
# call imgtool for signature # Append cysecuretools path to sys.path and import cysecuretools. This will
args = [sys.executable, str(slot["sdk_path"] / "imgtool/imgtool.py"), # prioritize system installations of cysecuretools over the included
"sign", "--key", str(slot["sdk_path"] / slot["key_file"]), # cysecuretools.
"--header-size", str(hex(MCUBOOT_HEADER_SIZE)), "--pad-header", "--align", "8", #sb_tools_path = mbed_os_root / Path("targets/TARGET_Cypress/TARGET_PSOC6/")
"--version", str(slot["img_data"]["VERSION"]), "--image-id", #sys.path.append(str(sb_tools_path))
str(slot["id"]), "--rollback_counter", str(slot["img_data"]["ROLLBACK_COUNTER"]), import cysecuretools
"--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)
# catch stderr outputs tools = cysecuretools.CySecureTools(secure_target, str(policy_file))
stderr = process.communicate()[1]
rc = process.wait()
if rc != 0: if str(toolchain.target.boot_scheme) == 'single_image':
toolchain.notify.debug("[PSOC6.sign_image] ERROR: Signature is not added!") toolchain.notify.info("[PSOC6.sign_image] single image signing")
toolchain.notify.debug("[PSOC6.sign_image] Message from imgtool: " + stderr.decode("utf-8")) sign_application(toolchain, tools, binf, image_id=toolchain.target.cm0_img_id)
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)
# preserve separate hex for cm4 elif str(toolchain.target.boot_scheme) == 'multi_image':
# 16 is image ID for NSPE image sign_application(toolchain, tools, m0hex, image_id=toolchain.target.cm0_img_id)
if slot["id"] == NSPE_IMAGE_ID: sign_application(toolchain, tools, binf, image_id=toolchain.target.cm4_img_id)
copy2(out_hex_name, out_cm4_hex)
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): def complete(toolchain, elf0, hexf0, hexf1=None):

View File

@ -669,20 +669,17 @@ class PSOC6Code(object):
def sign_image(t_self, resources, elf, binf): def sign_image(t_self, resources, elf, binf):
""" """
Calls sign_image function to add signature to Secure Boot binary file. 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): class ArmMuscaA1Code(object):
"""Musca-A1 Hooks""" """Musca-A1 Hooks"""