From f18a90049f2df1197f7126b0aacd543ade66eb9c Mon Sep 17 00:00:00 2001 From: Maria Wisniewska Date: Tue, 7 Sep 2021 15:38:49 +0200 Subject: [PATCH] Release 1.5.0 --- docs/_static/images/spsdk-applications.png | Bin 42466 -> 52490 bytes .../images/spsdk-architecture-apps.png | Bin 47883 -> 71147 bytes docs/_static/images/spsdk-architecture.png | Bin 166681 -> 151982 bytes docs/_static/images/spsdk-help.png | Bin 167700 -> 186042 bytes docs/api/api.rst | 6 +- docs/api/mboot_interfaces.rst | 8 + docs/api/pfr.rst | 14 +- docs/api/sbfile.rst | 8 + docs/api/utils.rst | 18 +- docs/apps/blhost.rst | 8 +- docs/apps/nxpcertgen.rst | 25 + docs/apps/nxpdevhsm.rst | 64 ++ docs/apps/nxpkeygen.rst | 4 + docs/conf.py | 1 - docs/index.rst | 14 +- docs/release_notes.rst | 202 +++++ docs/requirements.txt | 2 +- docs/usage/applications.rst | 16 +- examples/crypto/certificates_validation.py | 27 +- examples/dat/dck_rsa_2048.yml | 17 +- examples/dat/hsm/sasp.py | 5 + examples/mboot.py | 9 +- examples/sbfile.py | 19 +- pyproject.toml | 1 - release_notes.txt | 108 ++- requirements.txt | 4 +- setup.py | 4 +- spsdk/__init__.py | 2 +- spsdk/__version__.py | 2 +- spsdk/apps/README.md | 6 +- spsdk/apps/blhost.py | 470 +++++++++++- spsdk/apps/blhost_helper.py | 97 ++- spsdk/apps/elftosb.py | 424 +++++++---- spsdk/apps/elftosb_utils/sb_21_helper.py | 30 +- spsdk/apps/elftosb_utils/sb_31_helper.py | 424 +++++++++-- spsdk/apps/elftosb_utils/sly_bd_lexer.py | 6 +- spsdk/apps/elftosb_utils/sly_bd_parser.py | 66 +- spsdk/apps/nxpcertgen.py | 143 +++- spsdk/apps/nxpdebugmbox.py | 31 +- spsdk/apps/nxpdevhsm.py | 688 ++++++++++++++++++ spsdk/apps/nxpdevscan.py | 3 + spsdk/apps/nxpkeygen.py | 181 ++--- spsdk/apps/pfr.py | 42 +- spsdk/apps/pfrc.py | 4 +- spsdk/apps/sdphost.py | 23 +- spsdk/apps/sdpshost.py | 2 +- spsdk/apps/shadowregs.py | 19 +- spsdk/apps/spsdk_apps.py | 5 +- spsdk/apps/utils.py | 88 ++- spsdk/crypto/__init__.py | 9 +- spsdk/crypto/certificate_management.py | 25 +- spsdk/crypto/keys_management.py | 41 +- spsdk/crypto/loaders.py | 62 +- spsdk/crypto/signature_provider.py | 12 + spsdk/dat/dac_packet.py | 2 +- spsdk/dat/dar_packet.py | 30 +- spsdk/dat/debug_credential.py | 73 +- spsdk/dat/debug_mailbox.py | 66 +- spsdk/dat/dm_commands.py | 22 +- spsdk/data/nxpcertgen/certgen_config.yml | 52 ++ spsdk/data/nxpkeygen/template_config.yml | 22 +- spsdk/data/pfr/cfpa/database.json | 9 +- spsdk/data/pfr/cfpa/lpc553x_0a.xml | 8 +- spsdk/data/pfr/cfpa/lpc55s3x_0a.xml | 8 +- spsdk/data/pfr/cmpa/database.json | 3 + spsdk/data/pfr/cmpa/lpc553x_0a.xml | 26 +- spsdk/data/pfr/cmpa/lpc55s3x_0a.xml | 26 +- spsdk/debuggers/debug_probe.py | 16 +- spsdk/debuggers/debug_probe_jlink.py | 94 +-- spsdk/debuggers/debug_probe_pemicro.py | 72 +- spsdk/debuggers/debug_probe_pyocd.py | 132 ++-- spsdk/debuggers/utils.py | 52 +- spsdk/exceptions.py | 12 + spsdk/image/bee.py | 101 +-- spsdk/image/commands.py | 190 +++-- spsdk/image/hab_audit_log.py | 62 +- spsdk/image/header.py | 18 +- spsdk/image/images.py | 261 ++++--- spsdk/image/keystore.py | 31 +- spsdk/image/mbimg.py | 163 +++-- spsdk/image/misc.py | 13 +- spsdk/image/secret.py | 44 +- spsdk/image/segments.py | 121 +-- spsdk/image/trustzone.py | 43 +- spsdk/mboot/__init__.py | 14 +- spsdk/mboot/commands.py | 70 +- spsdk/mboot/exceptions.py | 4 +- spsdk/mboot/interfaces/__init__.py | 7 +- spsdk/mboot/interfaces/uart.py | 4 +- spsdk/mboot/interfaces/usb.py | 41 +- spsdk/mboot/interfaces/usbsio.py | 299 ++++++++ spsdk/mboot/mcuboot.py | 303 +++++++- spsdk/mboot/memories.py | 6 + spsdk/mboot/properties.py | 7 +- spsdk/pfr/pfr.py | 65 +- spsdk/pfr/processor.py | 6 +- spsdk/pfr/translator.py | 16 +- spsdk/sbfile/commands.py | 127 +++- spsdk/sbfile/headers.py | 23 +- spsdk/sbfile/images.py | 173 +++-- spsdk/sbfile/misc.py | 33 +- spsdk/sbfile/sb1/commands.py | 21 +- spsdk/sbfile/sb1/headers.py | 31 +- spsdk/sbfile/sb1/images.py | 27 +- spsdk/sbfile/sb1/sections.py | 15 +- spsdk/sbfile/sb31/commands.py | 59 +- spsdk/sbfile/sb31/functions.py | 16 +- spsdk/sbfile/sb31/images.py | 27 +- spsdk/sbfile/sections.py | 35 +- spsdk/sdp/exceptions.py | 3 +- spsdk/sdp/interfaces/uart.py | 2 +- spsdk/sdp/interfaces/usb.py | 32 +- spsdk/sdp/sdp.py | 2 +- spsdk/sdp/sdps.py | 4 +- spsdk/shadowregs/shadowregs.py | 17 +- spsdk/utils/crypto/backend_internal.py | 56 +- spsdk/utils/crypto/backend_openssl.py | 14 +- spsdk/utils/crypto/cert_blocks.py | 82 ++- spsdk/utils/crypto/certificate.py | 2 +- spsdk/utils/crypto/common.py | 18 +- spsdk/utils/crypto/otfad.py | 82 ++- spsdk/utils/devicedescription.py | 10 +- spsdk/utils/easy_enum.py | 10 +- spsdk/utils/easy_enum.pyi | 4 +- spsdk/utils/exceptions.py | 4 + spsdk/utils/misc.py | 142 +++- spsdk/utils/nxpdevscan.py | 29 +- spsdk/utils/reg_config.py | 10 +- spsdk/utils/registers.py | 25 +- spsdk/utils/serial_proxy.py | 2 +- spsdk/utils/usbfilter.py | 18 +- tests/apps/data/certgen_config.yaml | 7 + tests/apps/data/invalid_file.json | 16 + tests/apps/data/test_config.json | 16 + tests/apps/data/zeros.bin | Bin 0 -> 120 bytes tests/apps/test_help.py | 12 +- tests/apps/test_utils.py | 17 +- tests/conftest.py | 1 + tests/crypto/data/certgen_config.yaml | 25 + tests/crypto/test_certgen.py | 46 +- tests/dat/data/new.pub | 0 tests/dat/test_dar_packet.py | 31 +- tests/debuggers/debug_probe_virtual.py | 74 +- tests/debuggers/test_debug_probe.py | 1 + tests/debuggers/test_debug_probe_utils.py | 20 +- tests/debuggers/test_debug_virtual_probe.py | 42 +- .../data/sb3_test_384_384_unencrypted.json | 57 -- .../data/sb_sources/BD_files/real_example1.bd | 82 +++ .../data/sb_sources/BD_files/real_example2.bd | 94 +++ ...mpleExample.bd => simpleExample_no_sha.bd} | 0 .../sb_sources/BD_files/simpleExample_sha.bd | 82 +++ .../sb_sources/SB_files/legacy_elf2sb.bin | Bin 19552 -> 0 bytes .../SB_files/legacy_elftosb_no_sha.bin | Bin 0 -> 19552 bytes .../SB_files/legacy_elftosb_sha.bin | Bin 0 -> 19584 bytes .../SB_files/legacy_real_example1.sb | Bin 0 -> 19552 bytes .../SB_files/legacy_real_example2.sb | Bin 0 -> 344528 bytes .../input_images/rt500_quad_flash_fcb.bin | Bin 0 -> 512 bytes .../keys_and_certs/chain_cert_0_v3.der.crt | Bin 0 -> 1645 bytes .../chain_cert_1_pkey_rsa4096.pem | 51 ++ .../keys_and_certs/chain_cert_1_v3.der.crt | Bin 0 -> 1649 bytes .../keys_and_certs/root_cert_0_ca_v3.der.crt | Bin 0 -> 1645 bytes .../root_cert_0_pkey_rsa4096.pem | 51 ++ .../sb_sources/output_images/audioImage.bin | Bin 0 -> 96256 bytes .../output_images/bootloaderImage.bin | Bin 0 -> 16384 bytes .../data/sb_sources/output_images/tmdData.bin | Bin 0 -> 512 bytes .../sb_sources/output_images/tmdImage.bin | Bin 0 -> 229376 bytes .../workspace/cfgs/lpc55s1x/mb_ram_crc.json | 14 + .../workspace/cfgs/lpc55s1x/mb_xip_crc.json | 14 + .../cfgs/lpc55s1x/mb_xip_crc_hwk.json | 13 + .../cfgs/lpc55s1x/mb_xip_crc_hwk_tz.json | 14 + .../cfgs/lpc55s1x/mb_xip_crc_tz.json | 15 + .../lpc55s1x/mb_xip_crc_tz_no_preset.json | 15 + .../cfgs/lpc55s1x/mb_xip_signed.json | 20 + .../data/workspace/cfgs/lpc55s1x/tzm.json | 110 +++ .../cfgs/lpc55s3x}/mb_ram_crc.json | 2 +- .../cfgs/lpc55s3x}/mb_ram_crc_version.json | 5 +- .../cfgs/lpc55s3x/mb_ram_plain_tz.json | 18 + .../cfgs/lpc55s3x}/mb_xip_256_none.json | 5 +- .../cfgs/lpc55s3x}/mb_xip_384_256.json | 5 +- .../cfgs/lpc55s3x}/mb_xip_384_384.json | 5 +- .../cfgs/lpc55s3x}/mb_xip_crc.json | 2 +- .../cfgs/lpc55s3x/mb_xip_plain_tz.json | 18 + .../cfgs/lpc55s3x}/sb3_256_256.json | 21 +- .../cfgs/lpc55s3x}/sb3_256_256_notime.json | 20 +- .../cfgs/lpc55s3x}/sb3_256_none.json | 20 +- .../cfgs/lpc55s3x}/sb3_256_none_ernad.json | 13 +- .../cfgs/lpc55s3x}/sb3_384_256.json | 20 +- .../sb3_384_256_fixed_timestamp.json | 22 +- .../lpc55s3x}/sb3_384_256_unencrypted.json | 20 +- .../cfgs/lpc55s3x}/sb3_384_384.json | 12 +- .../cfgs/lpc55s3x}/sb3_384_none.json | 19 +- .../sb3_test_384_384_unencrypted.json | 168 +++++ .../data/workspace/cfgs/lpc55xx/README | 1 + .../workspace/cfgs/lpc55xx/mb_ram_crc.json | 14 + .../workspace/cfgs/lpc55xx/mb_xip_crc.json | 14 + .../cfgs/lpc55xx/mb_xip_crc_hwk.json | 13 + .../cfgs/lpc55xx/mb_xip_crc_hwk_tz.json | 14 + .../workspace/cfgs/lpc55xx/mb_xip_crc_tz.json | 15 + .../cfgs/lpc55xx/mb_xip_crc_tz_no_preset.json | 15 + .../workspace/cfgs/lpc55xx/mb_xip_signed.json | 20 + .../cfgs/lpc55xx/mb_xip_signed_chain.json | 22 + .../data/workspace/cfgs/lpc55xx/tzm.json | 123 ++++ .../data/workspace/cfgs/rt5xx/mb_ram_crc.json | 12 + .../workspace/cfgs/rt5xx/mb_ram_crc_hwk.json | 13 + .../cfgs/rt5xx/mb_ram_crc_hwk_tz.json | 14 + .../workspace/cfgs/rt5xx/mb_ram_crc_tz.json | 15 + .../cfgs/rt5xx/mb_ram_crc_tz_no_preset.json | 15 + .../cfgs/rt5xx/mb_ram_encrypted_ks.json | 23 + .../cfgs/rt5xx/mb_ram_signed_ks.json | 23 + .../cfgs/rt5xx/mb_ram_signed_no_ks.json | 22 + .../data/workspace/cfgs/rt5xx/mb_xip_crc.json | 14 + .../workspace/cfgs/rt5xx/mb_xip_crc_hwk.json | 13 + .../cfgs/rt5xx/mb_xip_crc_hwk_tz.json | 14 + .../workspace/cfgs/rt5xx/mb_xip_crc_tz.json | 15 + .../cfgs/rt5xx/mb_xip_crc_tz_no_preset.json | 15 + .../cfgs/rt5xx/mb_xip_signed_no_ks.json | 20 + .../data/workspace/cfgs/rt5xx/tzm.json | 292 ++++++++ .../lpc55s1x/mb_xip_crc_hwk_legacy.bin | Bin 0 -> 2160 bytes .../lpc55s1x/mb_xip_crc_hwk_legacy.hex | 135 ++++ .../lpc55s1x/mb_xip_crc_hwk_tz_legacy.bin | Bin 0 -> 2572 bytes .../lpc55s1x/mb_xip_crc_legacy.bin | Bin 0 -> 2160 bytes .../lpc55s1x/mb_xip_crc_tz_legacy.bin | Bin 0 -> 2572 bytes .../mb_xip_crc_tz_no_preset_legacy.bin | Bin 0 -> 2160 bytes .../output_images/lpc55s1x/mb_xip_signed.bin | Bin 0 -> 3700 bytes .../{ => lpc55s3x}/mb_ram_crc.bin | Bin .../{ => lpc55s3x}/mb_ram_crc_entz.bin | Bin .../{ => lpc55s3x}/mb_ram_crc_my.bin | Bin .../lpc55s3x/mb_ram_crc_version.bin | Bin 0 -> 2660 bytes .../{ => lpc55s3x}/mb_ram_crc_version_my.bin | Bin .../lpc55s3x/mb_ram_plain_tz.bin | Bin 0 -> 2660 bytes .../mb_ram_plain_tz_ref.bin} | Bin .../{ => lpc55s3x}/mb_xip_256_none.bin | Bin 3252 -> 2952 bytes .../{ => lpc55s3x}/mb_xip_384_256.bin | Bin 11076 -> 11076 bytes .../{ => lpc55s3x}/mb_xip_384_256_false.bin | Bin .../{ => lpc55s3x}/mb_xip_384_256_true.bin | Bin .../{ => lpc55s3x}/mb_xip_384_384 - Copy.bin | Bin .../{ => lpc55s3x}/mb_xip_384_384.bin | Bin 11140 -> 11508 bytes .../{ => lpc55s3x}/mb_xip_384_384.txt | 0 .../{ => lpc55s3x}/mb_xip_crc.bin | Bin .../{ => lpc55s3x}/mb_xip_crc_my.bin | Bin .../lpc55s3x/mb_xip_plain_tz.bin | Bin 0 -> 2660 bytes .../lpc55s3x/mb_xip_plain_tz_ref.bin | Bin 0 -> 2660 bytes .../{ => lpc55s3x}/normal_boot_sb3.sb3 | Bin .../{ => lpc55s3x}/normal_boot_sb3_ft.sb3 | Bin .../normal_boot_sb3_unencrypted.sb3 | Bin .../{ => lpc55s3x}/normal_boot_sign.bin | Bin .../{ => lpc55s3x}/normal_boot_signNoIsk.bin | Bin .../{ => lpc55s3x}/normal_boot_sign_crc.bin | Bin .../output_images/{ => lpc55s3x}/sb3Img.sb3 | Bin .../{ => lpc55s3x}/sb3Img_unecrypted.sb3 | Bin .../{ => lpc55s3x}/sb3_256_256.sb3 | Bin .../{ => lpc55s3x}/sb3_256_256_notime.sb3 | Bin .../{ => lpc55s3x}/sb3_256_none.sb3 | Bin .../{ => lpc55s3x}/sb3_256_none_ernad.sb3 | Bin .../{ => lpc55s3x}/sb3_384_256.sb3 | Bin .../sb3_384_256_fixed_timestamp.sb3 | Bin .../sb3_384_256_unencrypted.sb3 | Bin .../{ => lpc55s3x}/sb3_384_384.sb3 | Bin .../{ => lpc55s3x}/sb3_384_384.txt | 0 .../{ => lpc55s3x}/sb3_384_384_nxp.sb3 | Bin .../{ => lpc55s3x}/sb3_384_none.sb3 | Bin .../{ => lpc55s3x}/sb3_test_384_384.sb3 | Bin .../sb3_test_384_384_unencrypted.sb3 | Bin .../lpc55xx/mb_xip_crc_hwk_legacy.bin | Bin 0 -> 2160 bytes .../lpc55xx/mb_xip_crc_hwk_tz_legacy.bin | Bin 0 -> 2624 bytes .../lpc55xx/mb_xip_crc_legacy.bin | Bin 0 -> 2160 bytes .../lpc55xx/mb_xip_crc_tz_legacy.bin | Bin 0 -> 2624 bytes .../mb_xip_crc_tz_no_preset_legacy.bin | Bin 0 -> 2160 bytes .../output_images/lpc55xx/mb_xip_signed.bin | Bin 0 -> 3700 bytes .../lpc55xx/mb_xip_signed_chain.bin | Bin 0 -> 7792 bytes .../mb_ram_crc_version_dualboot.bin | Bin 0 -> 2660 bytes .../rt5xx/mb_ram_crc_hwk_legacy.bin | Bin 0 -> 2160 bytes .../rt5xx/mb_ram_crc_hwk_tz_legacy.bin | Bin 0 -> 3300 bytes .../output_images/rt5xx/mb_ram_crc_legacy.bin | Bin 0 -> 2160 bytes .../rt5xx/mb_ram_crc_tz_legacy.bin | Bin 0 -> 3300 bytes .../rt5xx/mb_ram_crc_tz_no_preset_legacy.bin | Bin 0 -> 2160 bytes .../rt5xx/mb_ram_encrypted_ks_legacy.bin | Bin 0 -> 5228 bytes .../rt5xx/mb_ram_signed_ks_legacy.bin | Bin 0 -> 5156 bytes .../rt5xx/mb_ram_signed_no_ks_legacy.bin | Bin 0 -> 3732 bytes .../rt5xx/mb_xip_crc_hwk_legacy.bin | Bin 0 -> 2160 bytes .../rt5xx/mb_xip_crc_hwk_tz_legacy.bin | Bin 0 -> 3300 bytes .../output_images/rt5xx/mb_xip_crc_legacy.bin | Bin 0 -> 2160 bytes .../rt5xx/mb_xip_crc_tz_legacy.bin | Bin 0 -> 3300 bytes .../rt5xx/mb_xip_crc_tz_no_preset_legacy.bin | Bin 0 -> 2160 bytes .../rt5xx/mb_xip_signed_legacy.bin | Bin 0 -> 3700 bytes .../trustzone/lpc55s1x/tzm_legacy.bin | Bin 0 -> 412 bytes .../trustzone/lpc55xx/tzm_legacy.bin | Bin 0 -> 464 bytes .../workspace/trustzone/rt5xx/tzm_legacy.bin | Bin 0 -> 1140 bytes tests/elftosb/test_efltosb_sb21.py | 49 +- tests/elftosb/test_elftosb_mbi.py | 300 +++++++- tests/elftosb/test_elftosb_sb31.py | 28 +- tests/elftosb/test_elftosb_trustzone.py | 2 +- tests/image/commands/test_auth_key_cmd.py | 2 +- tests/image/commands/test_base_cmd.py | 15 +- tests/image/commands/test_check_data_cmd.py | 15 + tests/image/commands/test_init_cmd.py | 15 +- tests/image/commands/test_install_key.py | 2 +- tests/image/commands/test_set_cmd.py | 15 + tests/image/commands/test_write_data_cmd.py | 24 +- tests/image/header/test_header.py | 9 +- tests/image/images/test_bootimage_rt10xx.py | 40 +- tests/image/images/test_bootimg2_api.py | 2 +- tests/image/images/test_bootimg_api.py | 115 +++ tests/image/images/test_hab_audit_log.py | 60 +- tests/image/mbi/test_mbi.py | 333 ++++++++- tests/image/misc/test_format_value.py | 9 +- tests/image/misc/test_parse_int.py | 4 +- tests/image/secret/test_sec_api.py | 45 +- tests/image/segments/data/bee-data.bin | Bin 0 -> 256 bytes tests/image/segments/test_app.py | 2 +- tests/image/segments/test_base.py | 10 +- tests/image/segments/test_bdt.py | 11 +- tests/image/segments/test_bee.py | 86 ++- tests/image/segments/test_csf.py | 13 +- tests/image/segments/test_csf_api.py | 3 +- tests/image/segments/test_dcd.py | 14 +- tests/image/segments/test_dcd_api.py | 4 +- tests/image/segments/test_fcb.py | 19 +- tests/image/segments/test_ivt.py | 31 +- tests/image/segments/test_key_store.py | 22 +- tests/image/trustzone/test_trustzone.py | 40 +- tests/mboot/blhost/test_blhost_cli.py | 118 +++ tests/mboot/blhost/test_blhost_utils.py | 58 ++ tests/mboot/conftest.py | 7 +- tests/mboot/device_config.py | 5 +- tests/mboot/test_commands.py | 22 +- tests/mboot/test_mboot_api.py | 20 +- tests/mboot/test_mboot_exceptions.py | 2 +- tests/mboot/test_mboot_memories.py | 5 +- tests/mboot/test_properties.py | 10 +- tests/mboot/virtual_device.py | 9 +- tests/mcu_examples/test_rt10xx.py | 48 +- tests/mcu_examples/test_rt5xx.py | 29 +- .../nxpkeygen/data/2048b-rsa-example-cert.der | Bin 0 -> 847 bytes tests/{dat => nxpkeygen}/data/dck.pub | 0 .../data/dck_rsa2048_rot_meta_cert.yml | 10 + .../data/elf2sb_config.json | 0 .../{dat => nxpkeygen}/data/k0_cert0_2048.pem | 0 tests/nxpkeygen/data/n4a_dck_secp384r1.cert | Bin 0 -> 520 bytes tests/nxpkeygen/data/new_dck_2048.pem | 28 + tests/nxpkeygen/data/new_dck_2048.pub | 9 + .../data/new_dck_rsa2048.cert | Bin tests/nxpkeygen/data/new_dck_rsa2048.yml | 10 + .../data/new_dck_rsa2048_invalid.yml | 10 + tests/nxpkeygen/data/new_dck_secp256.yml | 10 + tests/nxpkeygen/data/new_dck_secp256_N4A.yml | 10 + .../data/new_dck_secp256_N4A_not_empty.yml | 0 .../data/new_dck_secp256r1.cert | Bin tests/nxpkeygen/data/new_dck_secp256r1.pem | 5 + tests/nxpkeygen/data/new_dck_secp256r1.pub | 4 + tests/nxpkeygen/data/new_dck_secp384_N4A.yml | 10 + .../data/new_dck_secp384_N4A_not_empty.yml | 0 .../{dat => nxpkeygen}/data/new_dck_secp384r1 | 0 tests/nxpkeygen/data/new_dck_secp384r1.pem | 6 + tests/nxpkeygen/data/new_dck_secp384r1.pub | 5 + tests/nxpkeygen/data/new_rotk_2048.pem | 28 + tests/nxpkeygen/data/new_rotk_2048.pub | 9 + tests/nxpkeygen/data/new_rotk_secp256r1.pem | 5 + tests/nxpkeygen/data/new_rotk_secp256r1.pub | 4 + tests/nxpkeygen/data/new_rotk_secp384r1.pem | 6 + tests/nxpkeygen/data/new_rotk_secp384r1.pub | 5 + .../data/no_key_dck_rsa_2048.yml | 0 .../data/org_dck_rsa_2048.yml | 0 .../{dat => nxpkeygen}/data/p0_cert0_2048.pub | 0 .../{dat => nxpkeygen}/data/p1_cert0_2048.pub | 0 .../{dat => nxpkeygen}/data/p2_cert0_2048.pub | 0 .../{dat => nxpkeygen}/data/p3_cert0_2048.pub | 0 .../data/plugin_dck_rsa_2048.yml | 0 tests/nxpkeygen/data/sample.cert | Bin 0 -> 940 bytes tests/{dat => nxpkeygen}/data/signature.bin | 0 .../data/signature_bytes.bin | Bin .../data/signature_provider.py | 4 + tests/{dat => nxpkeygen}/test_debug_cred.py | 59 +- tests/{crypto => nxpkeygen}/test_key_gen.py | 53 +- tests/{dat => nxpkeygen}/test_nxpkeygen.py | 109 +-- tests/{dat => nxpkeygen}/test_plugin.py | 0 tests/pfr/data/cfpa_no_change.yml | 11 + tests/pfr/data/cmpa_96mhz_rotkh.yml | 24 +- tests/pfr/data/n4a_CFPA_basic.bin | Bin 0 -> 512 bytes tests/pfr/test_cli_utils.py | 13 +- tests/pfr/test_pfr.py | 68 +- tests/sbfile/sb1/test_commands.py | 7 +- tests/sbfile/sb1/test_headers.py | 15 +- tests/sbfile/sb1/test_sb1.py | 29 +- tests/sbfile/sb1/test_sections.py | 4 +- tests/sbfile/sb31/test_commands_api.py | 86 ++- tests/sbfile/sb31/test_functions.py | 15 +- tests/sbfile/sb31/test_imge_api.py | 12 +- tests/sbfile/test_backend_openssl.py | 5 + tests/sbfile/test_backend_python.py | 68 ++ tests/sbfile/test_commands_api.py | 178 ++++- tests/sbfile/test_headers_api.py | 14 +- tests/sbfile/test_misc.py | 19 +- tests/sbfile/test_sbfile_image.py | 89 ++- tests/sbfile/test_sections_api.py | 68 +- tests/sdp/sdphost/test_sdphost_cli.py | 4 +- tests/sdp/test_sdp_exceptions.py | 4 +- tests/sdp/test_sdps.py | 3 +- tests/shadowregs/test_shadowregs.py | 4 +- tests/utils/crypto/test_cert_blocks.py | 80 +- tests/utils/crypto/test_common.py | 38 +- tests/utils/crypto/test_otfad.py | 62 +- tests/utils/data/reg_config_invalid.json | 41 ++ tests/utils/test_debug_info.py | 9 + tests/utils/test_devicedescription.py | 6 +- tests/utils/test_easy_enum.py | 123 +++- tests/utils/test_misc.py | 73 +- tests/utils/test_nxpdevscan.py | 14 +- tests/utils/test_reg_config.py | 11 + tests/utils/test_registers.py | 28 +- tests/utils/test_usbfilter.py | 1 - tools/approved_packages.json | 8 +- tools/checker_dependencies.py | 71 +- tools/pyinstaller/elftosb_version_info.txt | 43 ++ .../{hook-prettytable.py => hook-lark.py} | 6 +- tools/pyinstaller/nxpcertgen_version_info.txt | 43 ++ tools/pyinstaller/nxpdevhsm_version_info.txt | 43 ++ tools/pyinstaller/shadowregs_version_info.txt | 43 ++ tools/sr_xls2xml.py | 2 +- 419 files changed, 10615 insertions(+), 2440 deletions(-) create mode 100644 docs/apps/nxpdevhsm.rst create mode 100644 docs/release_notes.rst create mode 100644 spsdk/apps/nxpdevhsm.py create mode 100644 spsdk/data/nxpcertgen/certgen_config.yml create mode 100644 spsdk/mboot/interfaces/usbsio.py create mode 100644 tests/apps/data/certgen_config.yaml create mode 100644 tests/apps/data/invalid_file.json create mode 100644 tests/apps/data/test_config.json create mode 100644 tests/apps/data/zeros.bin create mode 100644 tests/crypto/data/certgen_config.yaml create mode 100644 tests/dat/data/new.pub delete mode 100644 tests/elftosb/data/sb3_test_384_384_unencrypted.json create mode 100644 tests/elftosb/data/sb_sources/BD_files/real_example1.bd create mode 100644 tests/elftosb/data/sb_sources/BD_files/real_example2.bd rename tests/elftosb/data/sb_sources/BD_files/{simpleExample.bd => simpleExample_no_sha.bd} (100%) create mode 100644 tests/elftosb/data/sb_sources/BD_files/simpleExample_sha.bd delete mode 100644 tests/elftosb/data/sb_sources/SB_files/legacy_elf2sb.bin create mode 100644 tests/elftosb/data/sb_sources/SB_files/legacy_elftosb_no_sha.bin create mode 100644 tests/elftosb/data/sb_sources/SB_files/legacy_elftosb_sha.bin create mode 100644 tests/elftosb/data/sb_sources/SB_files/legacy_real_example1.sb create mode 100644 tests/elftosb/data/sb_sources/SB_files/legacy_real_example2.sb create mode 100644 tests/elftosb/data/sb_sources/input_images/rt500_quad_flash_fcb.bin create mode 100644 tests/elftosb/data/sb_sources/keys_and_certs/chain_cert_0_v3.der.crt create mode 100644 tests/elftosb/data/sb_sources/keys_and_certs/chain_cert_1_pkey_rsa4096.pem create mode 100644 tests/elftosb/data/sb_sources/keys_and_certs/chain_cert_1_v3.der.crt create mode 100644 tests/elftosb/data/sb_sources/keys_and_certs/root_cert_0_ca_v3.der.crt create mode 100644 tests/elftosb/data/sb_sources/keys_and_certs/root_cert_0_pkey_rsa4096.pem create mode 100644 tests/elftosb/data/sb_sources/output_images/audioImage.bin create mode 100644 tests/elftosb/data/sb_sources/output_images/bootloaderImage.bin create mode 100644 tests/elftosb/data/sb_sources/output_images/tmdData.bin create mode 100644 tests/elftosb/data/sb_sources/output_images/tmdImage.bin create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_ram_crc.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_hwk.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_hwk_tz.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_tz.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_tz_no_preset.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_signed.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s1x/tzm.json rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/mb_ram_crc.json (77%) rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/mb_ram_crc_version.json (67%) create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_ram_plain_tz.json rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/mb_xip_256_none.json (91%) rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/mb_xip_384_256.json (93%) rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/mb_xip_384_384.json (93%) rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/mb_xip_crc.json (77%) create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_plain_tz.json rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/sb3_256_256.json (82%) rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/sb3_256_256_notime.json (81%) rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/sb3_256_none.json (77%) rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/sb3_256_none_ernad.json (82%) rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/sb3_384_256.json (82%) rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/sb3_384_256_fixed_timestamp.json (79%) rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/sb3_384_256_unencrypted.json (82%) rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/sb3_384_384.json (88%) rename tests/elftosb/data/{ => workspace/cfgs/lpc55s3x}/sb3_384_none.json (77%) create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_test_384_384_unencrypted.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55xx/README create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55xx/mb_ram_crc.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_hwk.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_hwk_tz.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_tz.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_tz_no_preset.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_signed.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_signed_chain.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55xx/tzm.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_hwk.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_hwk_tz.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_tz.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_tz_no_preset.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_encrypted_ks.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_signed_ks.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_signed_no_ks.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_hwk.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_hwk_tz.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_tz.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_tz_no_preset.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_signed_no_ks.json create mode 100644 tests/elftosb/data/workspace/cfgs/rt5xx/tzm.json create mode 100644 tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_hwk_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_hwk_legacy.hex create mode 100644 tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_hwk_tz_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_tz_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_tz_no_preset_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_signed.bin rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/mb_ram_crc.bin (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/mb_ram_crc_entz.bin (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/mb_ram_crc_my.bin (100%) create mode 100644 tests/elftosb/data/workspace/output_images/lpc55s3x/mb_ram_crc_version.bin rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/mb_ram_crc_version_my.bin (100%) create mode 100644 tests/elftosb/data/workspace/output_images/lpc55s3x/mb_ram_plain_tz.bin rename tests/elftosb/data/workspace/output_images/{mb_ram_crc_version.bin => lpc55s3x/mb_ram_plain_tz_ref.bin} (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/mb_xip_256_none.bin (83%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/mb_xip_384_256.bin (98%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/mb_xip_384_256_false.bin (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/mb_xip_384_256_true.bin (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/mb_xip_384_384 - Copy.bin (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/mb_xip_384_384.bin (93%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/mb_xip_384_384.txt (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/mb_xip_crc.bin (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/mb_xip_crc_my.bin (100%) create mode 100644 tests/elftosb/data/workspace/output_images/lpc55s3x/mb_xip_plain_tz.bin create mode 100644 tests/elftosb/data/workspace/output_images/lpc55s3x/mb_xip_plain_tz_ref.bin rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/normal_boot_sb3.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/normal_boot_sb3_ft.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/normal_boot_sb3_unencrypted.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/normal_boot_sign.bin (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/normal_boot_signNoIsk.bin (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/normal_boot_sign_crc.bin (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3Img.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3Img_unecrypted.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3_256_256.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3_256_256_notime.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3_256_none.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3_256_none_ernad.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3_384_256.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3_384_256_fixed_timestamp.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3_384_256_unencrypted.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3_384_384.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3_384_384.txt (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3_384_384_nxp.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3_384_none.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3_test_384_384.sb3 (100%) rename tests/elftosb/data/workspace/output_images/{ => lpc55s3x}/sb3_test_384_384_unencrypted.sb3 (100%) create mode 100644 tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_crc_hwk_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_crc_hwk_tz_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_crc_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_crc_tz_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_crc_tz_no_preset_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_signed.bin create mode 100644 tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_signed_chain.bin create mode 100644 tests/elftosb/data/workspace/output_images/mb_ram_crc_version_dualboot.bin create mode 100644 tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_crc_hwk_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_crc_hwk_tz_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_crc_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_crc_tz_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_crc_tz_no_preset_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_encrypted_ks_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_signed_ks_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_signed_no_ks_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/rt5xx/mb_xip_crc_hwk_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/rt5xx/mb_xip_crc_hwk_tz_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/rt5xx/mb_xip_crc_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/rt5xx/mb_xip_crc_tz_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/rt5xx/mb_xip_crc_tz_no_preset_legacy.bin create mode 100644 tests/elftosb/data/workspace/output_images/rt5xx/mb_xip_signed_legacy.bin create mode 100644 tests/elftosb/data/workspace/trustzone/lpc55s1x/tzm_legacy.bin create mode 100644 tests/elftosb/data/workspace/trustzone/lpc55xx/tzm_legacy.bin create mode 100644 tests/elftosb/data/workspace/trustzone/rt5xx/tzm_legacy.bin create mode 100644 tests/image/segments/data/bee-data.bin create mode 100644 tests/nxpkeygen/data/2048b-rsa-example-cert.der rename tests/{dat => nxpkeygen}/data/dck.pub (100%) create mode 100644 tests/nxpkeygen/data/dck_rsa2048_rot_meta_cert.yml rename tests/{dat => nxpkeygen}/data/elf2sb_config.json (100%) rename tests/{dat => nxpkeygen}/data/k0_cert0_2048.pem (100%) create mode 100644 tests/nxpkeygen/data/n4a_dck_secp384r1.cert create mode 100644 tests/nxpkeygen/data/new_dck_2048.pem create mode 100644 tests/nxpkeygen/data/new_dck_2048.pub rename tests/{dat => nxpkeygen}/data/new_dck_rsa2048.cert (100%) create mode 100644 tests/nxpkeygen/data/new_dck_rsa2048.yml create mode 100644 tests/nxpkeygen/data/new_dck_rsa2048_invalid.yml create mode 100644 tests/nxpkeygen/data/new_dck_secp256.yml create mode 100644 tests/nxpkeygen/data/new_dck_secp256_N4A.yml rename tests/{dat => nxpkeygen}/data/new_dck_secp256_N4A_not_empty.yml (100%) rename tests/{dat => nxpkeygen}/data/new_dck_secp256r1.cert (100%) create mode 100644 tests/nxpkeygen/data/new_dck_secp256r1.pem create mode 100644 tests/nxpkeygen/data/new_dck_secp256r1.pub create mode 100644 tests/nxpkeygen/data/new_dck_secp384_N4A.yml rename tests/{dat => nxpkeygen}/data/new_dck_secp384_N4A_not_empty.yml (100%) rename tests/{dat => nxpkeygen}/data/new_dck_secp384r1 (100%) create mode 100644 tests/nxpkeygen/data/new_dck_secp384r1.pem create mode 100644 tests/nxpkeygen/data/new_dck_secp384r1.pub create mode 100644 tests/nxpkeygen/data/new_rotk_2048.pem create mode 100644 tests/nxpkeygen/data/new_rotk_2048.pub create mode 100644 tests/nxpkeygen/data/new_rotk_secp256r1.pem create mode 100644 tests/nxpkeygen/data/new_rotk_secp256r1.pub create mode 100644 tests/nxpkeygen/data/new_rotk_secp384r1.pem create mode 100644 tests/nxpkeygen/data/new_rotk_secp384r1.pub rename tests/{dat => nxpkeygen}/data/no_key_dck_rsa_2048.yml (100%) rename tests/{dat => nxpkeygen}/data/org_dck_rsa_2048.yml (100%) rename tests/{dat => nxpkeygen}/data/p0_cert0_2048.pub (100%) rename tests/{dat => nxpkeygen}/data/p1_cert0_2048.pub (100%) rename tests/{dat => nxpkeygen}/data/p2_cert0_2048.pub (100%) rename tests/{dat => nxpkeygen}/data/p3_cert0_2048.pub (100%) rename tests/{dat => nxpkeygen}/data/plugin_dck_rsa_2048.yml (100%) create mode 100644 tests/nxpkeygen/data/sample.cert rename tests/{dat => nxpkeygen}/data/signature.bin (100%) rename tests/{dat => nxpkeygen}/data/signature_bytes.bin (100%) rename tests/{dat => nxpkeygen}/data/signature_provider.py (85%) rename tests/{dat => nxpkeygen}/test_debug_cred.py (76%) rename tests/{crypto => nxpkeygen}/test_key_gen.py (66%) rename tests/{dat => nxpkeygen}/test_nxpkeygen.py (68%) rename tests/{dat => nxpkeygen}/test_plugin.py (100%) create mode 100644 tests/pfr/data/cfpa_no_change.yml create mode 100644 tests/pfr/data/n4a_CFPA_basic.bin create mode 100644 tests/utils/data/reg_config_invalid.json create mode 100644 tools/pyinstaller/elftosb_version_info.txt rename tools/pyinstaller/hooks/{hook-prettytable.py => hook-lark.py} (62%) create mode 100644 tools/pyinstaller/nxpcertgen_version_info.txt create mode 100644 tools/pyinstaller/nxpdevhsm_version_info.txt create mode 100644 tools/pyinstaller/shadowregs_version_info.txt diff --git a/docs/_static/images/spsdk-applications.png b/docs/_static/images/spsdk-applications.png index 7cfa7cb74865174b82e7a7f403d2c14cdfb3ee2a..f04c6d22ef093ed07ac9270bb882e52e766d1d2f 100644 GIT binary patch literal 52490 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 CLQoI@ literal 42466 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[l|yZZP+*iw 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 G|NjAEK*#F< diff --git a/docs/_static/images/spsdk-architecture.png b/docs/_static/images/spsdk-architecture.png index cbe0b36cedbbc0eb2944e859076e0425a1cc8ebc..a7220b087d652d8484ace16d90f58088c5928b62 100644 GIT binary patch literal 151982 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 O>XIGLdrJiLzgV_!L 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

gsTi)ntdf!+Cr zJJsGGaA7vL&FRbGGXhF_m3iR3Gyn`9rKw23fwXgXC_?<-X5xQh>Nz$Mc7(W>{h>Vi z{W=sKvysyf!;wReoxM+T0R8tZ3_ObQOARkR$jLb|I%wEtk|k^ zC*Ba-(HSH-IB-zBskBKp=Y3Fz{wF5ym-Mv^5#{0YN;T)c z(yz!%3=fo!try?1w}&1|q-WpeUEz&!AXT5=AW6-n94w;Bsxo3N&2{z69;;sT_8uK; z6}dUswyksSwVylZwwpoo=__Je#Bezsq$zYynq;qrA23Z*|_XRS`m1aOY<+vhp5O!(9$myz0kE^ zUKr3lv}P+O$c2+lPn4$ayTAFYYa$PF8rAbsQC7vd)2GjVy`NEqsJMVZFHuvWc)CBm zbhg#7vVYYmrZB$n&c_VF$DOYpDh`{OY7sP5u{*5QpSzS42Q3yFdxMvhM;m50Z&xL9 z-tdF}vC4qgWr1$jZGsm_8#l6UpF3>R;Cy}mBzAa5>U1WCgsb^s?n@mVi;nOfb5&_g z`7|4OzoEy4P+SQ%?x8^Q(fzb+j1@D#2rf&i&FWyTsiNxHZGywEt8F+E@rjh?I}sF3 zh3N&d8>Y~i+P#sYFN{RM`T?K^`UMs$!+97N$G0vrM-5b1vGws1+^@909b*%v?b&6U zDjTBKz&26II3O{)R0Nr3lZe?=8*f=8VpN|LDwR{_FmE&MxNk7o&tzQM%QPl8R%k9e z%g0sr3OAM~cP)=aIvIeR|6Jh*t-Y_ZO;dboHWogWYg}=yorEN+{VFuhz|fwDLb0TotP69E_OOTwSQFcLa)hL z$FEL+k7ze6wr}66xRVy};xI2g%UDuG(EC*SChI{)M&G


WN}5>4xXYNU}`Z%NJr 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 diff --git a/docs/_static/images/spsdk-help.png b/docs/_static/images/spsdk-help.png index 92206b261ea357f5071b16a6ef62cc76b8b1ab02..eb2f1fb61628469cd8b0a55596d01468fb49c45e 100644 GIT binary patch literal 186042 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

dtN7u8gB+z0yI{?4A2o|nCEYSJ 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%0A`vjGo#3ZDSxIcqDV`8A1c=Zssl?y$XrU0pP# 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 @@ -45,9 +43,6 @@ The BusPal acts as a bus translator running on selected platforms. BusPal assist blhost - LPCUSBSIO ================== -.. warning:: - LPCUSBSIO is not supported in SPSDK - LPCUSBSIO - LPC USB Serial I/O(LPCUSBSIO), a firmware built in LPC Link2. The LPCUSBSIO acts as a bus translator, and establishes connection with *blhost* over USB-HID, and the MCU bootloader device over I2C and SPI. ----------------------- @@ -434,3 +429,6 @@ After the reset the device boots from flash and user image is programmed success ..note:: Elf files are not supported yet. +.. click:: spsdk.apps.blhost:trust_provisioning + :prog: blhost trust-provisioning + :nested: full diff --git a/docs/apps/nxpcertgen.rst b/docs/apps/nxpcertgen.rst index 98a6e0b7..87d58a4a 100644 --- a/docs/apps/nxpcertgen.rst +++ b/docs/apps/nxpcertgen.rst @@ -7,3 +7,28 @@ This user’s guide describes how to use *nxpcertgen* application. .. click:: spsdk.apps.nxpcertgen:main :prog: nxpcertgen :nested: full + +------------------------- +nxpcertgen - Sub-commands +------------------------- + +*nxpcertgen* consist of a set of sub-commands followed by options and arguments. +The options and the sub-command are separated with a ‘--’. + +.. code:: bash + + nxpcertgen [options] -- [sub-command] + +The "help" guide of *nxpcertgen* lists all of the options and sub-commands supported by the *nxpcertgen* utility. + +.. code:: bash + + nxpcertgen --help + +.. click:: spsdk.apps.nxpcertgen:generate + :prog: nxpcertgen generate + :nested: full + +.. click:: spsdk.apps.nxpcertgen:get_cfg_template + :prog: nxpcertgen get-cfg-template + :nested: full diff --git a/docs/apps/nxpdevhsm.rst b/docs/apps/nxpdevhsm.rst new file mode 100644 index 00000000..18478d0f --- /dev/null +++ b/docs/apps/nxpdevhsm.rst @@ -0,0 +1,64 @@ +====================== +User Guide - nxpdevhsm +====================== + +This user’s guide describes how to interface with the *MCU bootloader* to provisioned chip using *nxpdevhsm* application. + +The *nxpdevhsm* application is a command-line utility used on the host computer to use device HSM process to get provisioning SB3. + +.. click:: spsdk.apps.nxpdevhsm:main + :prog: nxpdevhsm + :nested: none + +------------------------- +nxpdevhsm - Communication +------------------------- + +The *nxpdevhsm* application is using blhost application and all supported communication interfaces that blhost offers(UART, USB, LPCUSBSIO[IC, SPI]) + +nxpdevhsm - blhost - USB +======================== + +*blhost* could be connected to MCU Bootloader over USB HID. + +:ref:`USB device identification in SPSDK` + +nxpdevhsm - blhost - UART +========================= + +*blhost* could be connected to MCU bootloader over UART. + +:ref:`UART device identification in SPSDK` + +nxpdevhsm - blhost - LPCUSBSIO +============================== + +LPCUSBSIO - LPC USB Serial I/O(LPCUSBSIO), a firmware built in LPC Link2. The LPCUSBSIO acts as a bus translator, and establishes connection with *blhost* over USB-HID, and the MCU bootloader device over I2C and SPI. + +------------------------- +nxpdevhsm - blhost - Note +------------------------- + +For more information about supported communication interface check the blhost application documentation. + + +------------------------ +nxpdevhsm - Sub-commands +------------------------ + +*nxpdevhsm* consist of a set of sub-commands followed by options and arguments. +The options and the sub-command are separated with a ‘--’. + +.. code:: bash + + nxpdevhsm [options] -- [sub-command] + +The "help" guide of *nxpdevhsm* lists all of the options and sub-commands supported by the *nxpdevhsm* utility. + +.. code:: bash + + nxpdevhsm --help + +.. click:: spsdk.apps.nxpdevhsm:generate + :prog: nxpdevhsm generate + :nested: full diff --git a/docs/apps/nxpkeygen.rst b/docs/apps/nxpkeygen.rst index 2855c7b0..23e6bcf1 100644 --- a/docs/apps/nxpkeygen.rst +++ b/docs/apps/nxpkeygen.rst @@ -32,3 +32,7 @@ The "help" guide of *nxpkeygen* lists all of the options and sub-commands suppor .. click:: spsdk.apps.nxpkeygen:genkey :prog: nxpkeygen genkey :nested: full + +.. click:: spsdk.apps.nxpkeygen:get_cfg_template + :prog: nxpkeygen get-cfg-template + :nested: full diff --git a/docs/conf.py b/docs/conf.py index 6174a23c..ed7c5c68 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,7 +35,6 @@ ".rst": "restructuredtext", ".md": "markdown", } -autodoc_mock_imports = ["hidapi", "hid"] autoclass_content = "both" diff --git a/docs/index.rst b/docs/index.rst index f5736647..1ac3828f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -12,6 +12,7 @@ Secure Provisioning SDK (SPSDK) :maxdepth: 1 spsdk.rst + release_notes.rst .. toctree:: :caption: Usage @@ -25,17 +26,18 @@ Secure Provisioning SDK (SPSDK) :caption: Application User Guides :maxdepth: 1 - apps/nxpdevscan - apps/sdphost apps/blhost - apps/pfr - apps/pfrc apps/elftosb - apps/nxpkeygen apps/nxpcertgen apps/nxpdebugmbox - apps/shadowregs + apps/nxpdevhsm + apps/nxpdevscan + apps/nxpkeygen + apps/pfr + apps/pfrc + apps/sdphost apps/sdpshost + apps/shadowregs .. toctree:: :caption: API Development Guide diff --git a/docs/release_notes.rst b/docs/release_notes.rst new file mode 100644 index 00000000..5ad55055 --- /dev/null +++ b/docs/release_notes.rst @@ -0,0 +1,202 @@ +.. NXP location + +.. _LIBUSBSIO_link: https://www.nxp.com/design/software/development-software/library-for-windows-macos-and-ubuntu-linux:LIBUSBSIO?tid=vanLIBUSBSIO + +============= +Release Notes +============= + +---------------------- +1.5.0 (07-August-2021) +---------------------- + +**New features** + +* :ref:`nxpdevhsm` - new application added: + + * The nxpdevhsm is a tool to create initial provisioning SB3 file for LPC55S36 to provision device with SB KEK needed to validate in device all standard SB3 files. + +* `LIBUSBSIO `__ integration as a replacement for HID_API module: + + * blhost - extend blhost by LPCUSBSIO interface + +* :ref:`blhost` - following trust-provisioning sub-commands added: + + * :ref:`oem_get_cust_cert_dice_puk` - creates the initial trust provisioning keys + * :ref:`oem_gen_master_share` - creates shares for initial trust provisioning keys + * :ref:`oem_set_master_share` - takes the entropy seed and the Encrypted OEM Master Share + * :ref:`hsm_gen_key` - creates OEM common keys, including encryption keys and signing keys + * :ref:`hsm_store_key` - stores known keys, and generate the corresponding key blob + * :ref:`hsm_enc_blk` - encrypts the given SB3 data bloc + * :ref:`hsm_enc_sign` - signs the given data + +* :ref:`elftosb`: + + * support for :ref:`SB 2.1 generation using BD file` + * LPC55S3x - add support for unsigned/plain images + * SB2.1 - SHA256 digest of all sections included in signed SB2.1 header + * add supported families listing into elftosb + * implement chip family option as a click.Choice + * allow loading certificates for MBI in PEM format + +* :ref:`nxpcertgen`: + + * generate the template for yml configuration file containing the parameters for certificate + * improve yml template description for nxpcertgen + * add support for generating certificates in DER format + +* :ref:`nxpkeygen`: + + * moved option -p from general space to gendc subcommand. + * add new -k keygen subcommand option to specify key type to generate + +* :ref:`nxpdebugmbox`: + + * refactor DebugCredential base class so that it will be possible to pass certificates in yml config file + * check nxpdebugmbox on LPC55S3x + +* :ref:`pfr` - update CMPA/CFPA registers XML data for LPC55S3x with CRR update + +* SPSDK :ref:`Applications`: + + * spsdk applications show help message when no parameter on command line provided + * improved help messages + * support Ctrl+C in cmd applications + +* replace functional asserts with raising a SPSDK-based exception +* replace all general exception with SPSDK-based exceptions + +**Bugfixes** + +* :ref:`nxpkeygen` - regenerates a key without --force +* :ref:`elftosb` - unclear error message: No such file or directory: 'None' +* :ref:`pfr` - duplicated error message: The silicon revision is not specified +* :ref:`nxpdebugmbox` - fix Retry of AP register reads after Chip reset +* :ref:`nxpdebugmbox` - add timeout to never ending loops in spin_read/write methods in Debug mailbox +* :ref:`blhost` - flash-erase-region command doesn't accept the memory_id argument in hex form +* :ref:`elftosb` - using kdkAccessRigths = 0 in SB31 is throwing an error in KeyDerivator + +-------------------- +1.4.0 (25-June-2021) +-------------------- + +**New features** + +* version flag added for all command-line application +* support for Python 3.9 added +* :ref:`blhost` - following sub-commands added: + * list-memory + * flash-program-once + * set-property + * flash-erase-all-unsecure + * flash-security-disable + * flash-read-resource + * reliable-update + * fuse-program + * flash-image + * program-aeskey +* :ref:`blhost` - memoryId calmp-down for mapped external memories added +* :ref:`elftosb` - support for SB 2.1 added +* :ref:`elftosb` - basic support for BD configuration file added +* :ref:`nxpdebugmbox` - debug port enabled check added +* :ref:`nxpkeygen` - new sub-command added to nxpkeygen to create a template for configuration YML file for DC keys +* :ref:`nxpkeygen` - new sub-command added to create a template for configuration YML file for DC keys +* :ref:`pfr` - default JSON config file generation removed, but still accepted as an input. The preferred is the YML configuration format. +* docs - Read The Docs documentation improvements + +**Bugfixes** + +* wrong DCD size by BootImgRT.parse +* cmdKeyStoreBackupRestore wrong param description +* :ref:`blhost` - typo in McuBootConnectionError exception +* :ref:`blhost` - mcuBoot Uart doesn't close the device after failed ping command +* :ref:`blhost` - assertion error when connection lost during fuses readout +* :ref:`blhost` - sub-command flash-read-resource fails when the length is not aligned +* :ref:`pfr` - incorrect keys hash computation for LPC55S3x +* :ref:`pfr` - wrong LPC55S69 silicon revision +* :ref:`pfr` - parse does not show PRINCE IV fields +* :ref:`sdphost` - running spdhost --help fails +* :ref:`shadowregs` - bad DEV_TEST_BIT in shadow registers + +--------------------- +1.3.1 (29-March-2021) +--------------------- + +* :ref:`pfr` - configuration template supports YAML with description, backward compatibility with JSON ensured +* :ref:`pfr` - API change: "keys" parameter has been moved from __init__ to export +* :ref:`pfr` - sub-commands renamed: + * user-config -> get-cfg-template + * parse -> parse-binary + * generate -> generate-binary +* :ref:`blhost` - allow key names for key-provisioning commands +* :ref:`blhost` - support for RT1170, RT1160 +* :ref:`shadowregs` - shadow registers tool is now top-level module +* :ref:`blhost` - fix baud rate parameter +* :ref:`pfr` - fix in data for LPC55S6x, LPC55S1x, LPC55S0x +* :ref:`blhost` - communication stack breaks down on RT1170 after unsuccessful key-prov enroll command + +-------------------- +1.3.0 (5-March-2021) +-------------------- + +* support creation of SB version 3.1 +* :ref:`elftosb` application based on legacy elf2sb supporting SB 3.1 support +* :ref:`nxpdevscan` - application for connected USB, UART devices discovery +* :ref:`shadowregs` - application for shadow registers management using DebugProbe +* support USB path argument in blhost/sdphost (all supported OS) +* :ref:`nxpcertgen` CLI application (basicConstrains, self-signed) +* :ref:`blhost` - commands added: + * flash-erase-all + * call + * load-image + * execute + * key-provisioning + * receive-sb-file +* :ref:`blhost` - extend commands' options: + * configure-memory now allows usage of internal memory + * extend error code in the output + * add parameters lock/nolock into efuse-program-once command + * add key selector option to the generate-key-blob command + * add nolock/lock selector to efuse-program-once command + * add hexdata option to the write-memory command + +------------------------ +1.2.0 (11-December-2020) +------------------------ + +* support for LPC55S3x devices +* extend support for LPC55S1x, LPC55S0x +* :ref:`pfrc` - console script for searching for brick conditions in pfr settings +* custom HSM support +* sdpshost CLI utility using sdpshost communication protocol +* remote signing for Debug Credential +* added command read-register into sdphost CLI +* dynamic plugin support +* MCU Link Debugger support +* :ref:`pfr` - added CMAC-based seal +* :ref:`pfr` - load Root of Trust from elf2sb configuration file + +------------------------ +1.1.0 (4-September-2020) +------------------------ + +* support for i.MX RT1170 device +* support for elliptic-curve cryptography (ECC) +* support for SDPS protocol +* included Debug Authentication functionality +* included support for debuggers +* :ref:`nxpkeygen` - utility for generating debug credential files and corresponding keys + +-------------------- +1.0.0 (4-April-2020) +-------------------- + +* support for LPC55S69 and LPC55S16 devices +* support for i.MX RT105x and RT106x devices +* support for i.MX RT595S and RT685S devices +* connectivity to the target via UART, USB-HID. +* support for generating, saving, loading RSA keys with different sizes +* generation and management of certificate +* :ref:`blhost` - CLI utility for communication with boot loader on a target +* :ref:`sdphost` - CLI utility for communication with ROM on a target +* :ref:`pfr` - CLI utility for generating and parsing Protected Flash Regions - CMPA and CFPA regions diff --git a/docs/requirements.txt b/docs/requirements.txt index cb52a355..015f19ae 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,6 @@ # docs sphinx==3.5.4 -sphinx-rtd-theme==0.5.2 +sphinx-rtd-theme==0.5.2 sphinx-autodoc-annotation sphinx_autodoc_typehints recommonmark diff --git a/docs/usage/applications.rst b/docs/usage/applications.rst index f3d43baf..f0841640 100644 --- a/docs/usage/applications.rst +++ b/docs/usage/applications.rst @@ -45,6 +45,9 @@ SPSDK applications are used for various functions and not all applications are v :align: center :scale: 50 % +.. note:: Please note that elftosb features marked with light green are supported but not tested and should be used with caution. + + :ref:`blhost` ============= @@ -98,7 +101,7 @@ The *nxpcertgen* application allows the user to generate the self-signed x.509 c :ref:`nxpdebugmbox` =================== -The *nxpkeygen* application allows user to: +The *nxpdebugmbox* application allows user to: - perform the Debug Authentication - start/stop Debug Mailbox @@ -109,6 +112,15 @@ The *nxpkeygen* application allows user to: nxpdebugmbox --help +:ref:`nxpdevhsm` +================= + +The *nxpdevhsm* application allows user to generate provisioned SB file. + +.. code:: bash + + nxpdevhsm --help + :ref:`nxpdevscan` ================= @@ -205,5 +217,3 @@ It allows user to: .. code:: bash shadowregs --help - - diff --git a/examples/crypto/certificates_validation.py b/examples/crypto/certificates_validation.py index 3a41d30a..b45d56f1 100644 --- a/examples/crypto/certificates_validation.py +++ b/examples/crypto/certificates_validation.py @@ -18,14 +18,15 @@ import os from os import path +from spsdk import SPSDKError from spsdk.crypto import ( RSAPublicKey, - validate_certificate_chain, get_public_key_from_certificate, - validate_certificate, is_ca_flag_set, - load_public_key, load_certificate, + load_public_key, + validate_certificate, + validate_certificate_chain, ) @@ -43,12 +44,12 @@ def main() -> None: # Check if public key of certificate has proper format assert isinstance(pubkey_from_ca0_cert, RSAPublicKey) # Compare CA's public key from file and the one from certificate - assert ( - ca0_pubkey_rsa2048.public_numbers() == pubkey_from_ca0_cert.public_numbers() - ), "Keys are not the same (the one from disc and the one from cert)" + if ca0_pubkey_rsa2048.public_numbers() != pubkey_from_ca0_cert.public_numbers(): + raise SPSDKError("Keys are not the same (the one from disc and the one from cert)") # Load certificate, which is singed by CA crt = load_certificate(path.join(data_dir, "crt_pem.crt")) - assert validate_certificate(crt, ca0_cert) + if not validate_certificate(crt, ca0_cert): + raise SPSDKError("The certificate is not valid") print("The certificate was signed by the CA.") # Load chain of certificate chain = ["chain_crt2_pem.crt", "chain_crt_pem.crt", "ca_cert_pem.crt"] @@ -57,12 +58,16 @@ def main() -> None: ch3_crt = load_certificate(path.join(data_dir, "chain_crt_pem.crt")) ch3_ca = load_certificate(path.join(data_dir, "ca_cert_pem.crt")) # Validate the chain (if corresponding items in chain are singed by one another) - assert validate_certificate_chain(chain_cert) + if not validate_certificate_chain(chain_cert): + raise SPSDKError("The certificate chain is not valid") print("The chain of certificates is valid.") # Checks if CA flag is set correctly - assert not is_ca_flag_set(ch3_crt2) - assert is_ca_flag_set(ch3_crt) - assert is_ca_flag_set(ch3_ca) + if is_ca_flag_set(ch3_crt2): + raise SPSDKError("CA flag is set") + if not is_ca_flag_set(ch3_crt): + raise SPSDKError("CA flag is not set") + if not is_ca_flag_set(ch3_ca): + raise SPSDKError("CA flag is not set") if __name__ == "__main__": diff --git a/examples/dat/dck_rsa_2048.yml b/examples/dat/dck_rsa_2048.yml index 3a964e77..1d02f0a8 100644 --- a/examples/dat/dck_rsa_2048.yml +++ b/examples/dat/dck_rsa_2048.yml @@ -33,7 +33,6 @@ # ============================================ # ============================================ - # ============ SoC Class ============ # A unique identifier for a set of SoCs that require no SoC-specific differentiation in # their debug authentication. The main usage is to allow a different set of debug @@ -67,7 +66,7 @@ cc_vu: 0x5678 # credential beacon is associated with a DC and is therefore vendor/RoT-signed. An # authentication beacon is provided and signed by the debugger during the # authentication process. -cred_beacon: 0 +cc_beacon: 0 # ============ RoT meta-data ============ # The RoT meta-data required by the device to corroborate; the ROTID sent in the @@ -75,8 +74,8 @@ cred_beacon: 0 # device. This allows different RoT identification, management and revocation # solutions to be handled. rot_meta: - - ./p0_cert0_2048.pub - - ./p1_cert0_2048.pub + - ./p0_cert0_2048.pub + - ./p1_cert0_2048.pub # ============ RoT Identifier ============ # RoTID allows the debugger to infer which RoT public key(s) are acceptable to the @@ -93,9 +92,9 @@ dck: ./dck.pub # ================================================================================================== # Signature configuration area # ================================================================================================== -# There are two ways how sign the final DC data blob. +# There are two ways how sign the final DC data blob. # -# 1. In case that you is available private pair for rot_meta with index rot_id just use first simple style +# 1. In case that you is available private pair for rot_meta with index rot_id just use first simple style # to use it by rotk key. As a second way to do same is use sign_provider option with 'type=file'. # # 2. For case that Debug Credential files are generated in untrusted environment (without access to RoT private keys), @@ -105,14 +104,12 @@ dck: ./dck.pub # # Those options are exclusive, so only one option could be used to sign the DC. - # ============ Signature Provider ============ # To use signing provider examples -# +# # sign_provider: 'type=file;file_path=./hsm/k0_cert0_2048.pem' # sign_provider: : 'type=sasp;key_number=0' -sign_provider: 'type=sasp;key_number=0' - +sign_provider: "type=sasp;key_number=0" # ============ RoT signature private key ============ # Private key for for the RoT meta chosen by rot_id to sign the image. # rotk: rotk0.pem diff --git a/examples/dat/hsm/sasp.py b/examples/dat/hsm/sasp.py index 04a74065..7fee1311 100644 --- a/examples/dat/hsm/sasp.py +++ b/examples/dat/hsm/sasp.py @@ -44,3 +44,8 @@ def sign(self, data: bytes) -> bytes: signature = response.json()["signature"] data = base64.b64decode(signature) return data.zfill(256) + + @property + def signature_length(self) -> int: + """Return length of the signature.""" + return 256 diff --git a/examples/mboot.py b/examples/mboot.py index 529ac36c..c9da54f0 100644 --- a/examples/mboot.py +++ b/examples/mboot.py @@ -9,6 +9,7 @@ from typing import Optional +from spsdk import SPSDKError from spsdk.mboot import McuBoot, scan_usb # Uncomment for printing debug messages @@ -31,9 +32,13 @@ def mboot_properties(name: str = None) -> Optional[list]: def main() -> None: - """Main function.""" + """Main function. + + :raises SPSDKError: When reading properting ends with error + """ property_list = mboot_properties() - assert property_list, "Error reading properties!" + if not property_list: + raise SPSDKError("Error reading properties!") for prop in property_list: print(prop) diff --git a/examples/sbfile.py b/examples/sbfile.py index e2407a5c..47e6bab9 100644 --- a/examples/sbfile.py +++ b/examples/sbfile.py @@ -14,15 +14,10 @@ import os from binascii import unhexlify -from spsdk.sbfile.images import ( - BootImageV20, - BootImageV21, - SBV2xAdvancedParams, - BootSectionV2, -) +from spsdk import SPSDKError from spsdk.sbfile.commands import CmdErase, CmdLoad, CmdReset -from spsdk.utils.crypto import CertBlockV2, KeyBlob, Otfad, Certificate - +from spsdk.sbfile.images import BootImageV20, BootImageV21, BootSectionV2, SBV2xAdvancedParams +from spsdk.utils.crypto import CertBlockV2, Certificate, KeyBlob, Otfad THIS_DIR = os.path.dirname(os.path.abspath(__file__)) DATA_DIR = os.path.join(THIS_DIR, "data") @@ -65,7 +60,10 @@ def gen_boot_section() -> BootSectionV2: def gen_boot_section_otfad() -> BootSectionV2: - """Generate a Boot Section with content encrypted by OTFAD.""" + """Generate a Boot Section with content encrypted by OTFAD. + + :raises SPSDKError: When length of key blobs is not 256 + """ with open(f"{DATA_DIR}/boot_image.bin", "rb") as boot_image_file: boot_data = boot_image_file.read() @@ -84,7 +82,8 @@ def gen_boot_section_otfad() -> BootSectionV2: ) # zero_fill and crc should be used only for testing ! enc_image = otfad.encrypt_image(boot_data, 0x08001000, True) key_blobs = otfad.encrypt_key_blobs(kek=bytes.fromhex("50F66BB4F23B855DCD8FEFC0DA59E963")) - assert len(key_blobs) == 256 + if len(key_blobs) != 256: + raise SPSDKError("Length of key blobs is not 256") boot_section = BootSectionV2( 0, diff --git a/pyproject.toml b/pyproject.toml index d9e0cf12..8c9eff47 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,6 @@ line-length = 100 target-version = ['py36', 'py37', 'py38'] include = '\.pyi?$' force-exclude = ''' -spsdk/apps ''' [tool.isort] diff --git a/release_notes.txt b/release_notes.txt index d4613c21..bf30fcb6 100644 --- a/release_notes.txt +++ b/release_notes.txt @@ -6,10 +6,86 @@ production deployment. The library allows the user to connect and communicate wi configure the device, prepare, download and upload data, including security operations. It is delivered in the form of a python library and command-line applications. -Version: 1.4.0 +Version: 1.5.0 ============== -Date: 25-June-2021 +Date: 7-September-2021 + +New features + +- nxpdevhsm - new application added: + - The nxpdevhsm is a tool to create initial provisioning SB3 file for LPC55S36 to provision device with SB KEK needed to validate in device all standard SB3 files. +- LIBUSBSIO integration as a replacement for HID_API module: + - blhost - extend blhost by LPCUSBSIO interface +- blhost - following trust-provisioning sub-commands added: + - oem_get_cust_cert_dice_puk - creates the initial trust provisioning keys + - oem_gen_master_share - creates shares for initial trust provisioning keys + - oem_set_master_share - takes the entropy seed and the Encrypted OEM Master Share + - hsm_gen_key - creates OEM common keys, including encryption keys and signing keys + - hsm_store_key - stores known keys, and generate the corresponding key blob + - hsm_enc_blk - encrypts the given SB3 data bloc + - hsm_enc_sign - signs the given data +- elftosb: + - support for SB 2.1 generation using BD file + - LPC55S3x - add support for unsigned/plain images + - SB2.1 - SHA256 digest of all sections included in signed SB2.1 header + - add supported families listing into elftosb + - implement chip family option as a click.Choice + - allow loading certificates for MBI in PEM format +- nxpcertgen: + - generate the template for yml configuration file containing the parameters for certificate + - improve yml template description for nxpcertgen + - add support for generating certificates in DER format +- nxpkeygen: + - moved option -p from general space to gendc subcommand. + - add new -k keygen subcommand option to specify key type to generate +- nxpdebugmbox: + - refactor DebugCredential base class so that it will be possible to pass certificates in yml config file + - check nxpdebugmbox on LPC55S3x +- pfr - update CMPA/CFPA registers XML data for LPC55S3x with CRR update +- SPSDK Applications: + - spsdk applications show help message when no parameter on command line provided + - improved help messages + - support Ctrl+C in cmd applications +- replace functional asserts with raising a SPSDK-based exception +- replace all general exception with SPSDK-based exceptions + +Bugfixes + +- nxpkeygen - regenerates a key without –force +- elftosb - unclear error message: No such file or directory: ‘None’ +- pfr - duplicated error message: The silicon revision is not specified +- nxpdebugmbox - fix Retry of AP register reads after Chip reset +- nxpdebugmbox - add timeout to never ending loops in spin_read/write methods in Debug mailbox +- blhost - flash-erase-region command doesn’t accept the memory_id argument in hex form +- elftosb - using kdkAccessRigths = 0 in SB31 is throwing an error in KeyDerivator +Supported devices +================= + +- i.MX RT1170, RT1160 +- i.MX RT1064, RT1060, RT1050, RT1020, RT1010 +- i.MX RT685S, RT595S +- LPC55S6x, LPC55S3x, LPC55S2x, LPC55S1x, LPC55S0x + +Note: More details in devices.txt + +System Requirements +=================== + +- Windows 10, 64-bit +- Ubuntu 16.04 or above, 64-bit +- Mac OS 10.13 or above, x64, M1 + +Supported Environment +===================== + +Python 3.6+ interpreter, old version 2.x is not supported + + +Revision History +================ + +1.4.0 - version flag added for all command-line application - support for Python 3.9 added - [blhost] following sub-commands added: @@ -31,8 +107,6 @@ Date: 25-June-2021 - [nxpkeygen] new sub-command added to create a template for configuration YML file for DC keys - [pfr] default JSON config file generation removed, but still accepted as an input. The preferred is the YML configuration format. - [docs] Read The Docs documentation improvements - -Bugfixes: - wrong DCD size by BootImgRT.parse - cmdKeyStoreBackupRestore wrong param description - [blhost] typo in McuBootConnectionError exception @@ -45,32 +119,6 @@ Bugfixes: - [sdphost] running spdhost --help fails - [shadowreg] bad DEV_TEST_BIT in shadow registers -Supported devices -================= - -- i.MX RT1170, RT1160 -- i.MX RT1064, RT1060, RT1050, RT1020, RT1010 -- i.MX RT685S, RT595S -- LPC55S6x, LPC55S3x, LPC55S2x, LPC55S1x, LPC55S0x - -Note: More details in devices.txt - -System Requirements -=================== - -- Windows 10, 64-bit -- Ubuntu 16.04 or above, 64-bit -- Mac OS 10.13 or above, x64, M1 - -Supported Environment -===================== - -Python 3.6+ interpreter, old version 2.x is not supported - - -Revision History -================ - 1.3.1 - [PFR] configuration template supports YAML with description, backward compatibility with JSON ensured - [PFR] API change: "keys" parameter has been moved from __init__ to export diff --git a/requirements.txt b/requirements.txt index 654359d8..010b8d42 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,6 @@ cryptography>=3.3,<3.4.4 oscrypto~=1.2 pycryptodome>=3.9.3,<4 pyserial>=3.1,<4 -hidapi~=0.10 jinja2>=2.11,<3 ruamel.yaml>=0.16.8,<0.17 pylink-square>=0.8.2,<0.9 @@ -21,3 +20,6 @@ pyocd>=0.28.3,<0.29 munch<2.5.1 bincopy<17.10 sly==0.4 +libusbsio>=2.1.5 +# There's an issue with 0.3.0 on win +cmsis-pack-manager<0.3.0 diff --git a/setup.py b/setup.py index b89fca40..29eea964 100644 --- a/setup.py +++ b/setup.py @@ -11,9 +11,6 @@ with open("requirements.txt") as req_file: requirements = req_file.read().splitlines() - # avoid build errors on readthedocs (excluding hidapi, which depends on C module) - if os.getenv("READTHEDOCS"): - requirements = [x for x in requirements if "hidapi" not in x] with open("README.md", "r") as f: @@ -75,6 +72,7 @@ "nxpdebugmbox=spsdk.apps.nxpdebugmbox:safe_main", "nxpcertgen=spsdk.apps.nxpcertgen:safe_main", "nxpdevscan=spsdk.apps.nxpdevscan:safe_main", + "nxpdevhsm=spsdk.apps.nxpdevhsm:safe_main", "shadowregs=spsdk.apps.shadowregs:safe_main", ], }, diff --git a/spsdk/__init__.py b/spsdk/__init__.py index 1aa56b5e..928dd39f 100644 --- a/spsdk/__init__.py +++ b/spsdk/__init__.py @@ -20,7 +20,7 @@ import os from .__version__ import __version__ as version -from .exceptions import SPSDKError +from .exceptions import SPSDKError, SPSDKIOError, SPSDKTypeError, SPSDKValueError __author__ = "NXP" __contact__ = "michal.starecek@nxp.com" diff --git a/spsdk/__version__.py b/spsdk/__version__.py index d536781c..f7d94550 100644 --- a/spsdk/__version__.py +++ b/spsdk/__version__.py @@ -10,4 +10,4 @@ Having the version in a separate file makes it easier to share it with setup.py """ -__version__ = "1.4.0.post2" +__version__ = "1.5.0" diff --git a/spsdk/apps/README.md b/spsdk/apps/README.md index 8c2044d7..d945019d 100644 --- a/spsdk/apps/README.md +++ b/spsdk/apps/README.md @@ -5,7 +5,7 @@ After installing SPSDK, several applications are present directly on PATH as exe - [spsdk](spsdk_apps.py) - entry point for all available applications. - [blhost](blhost.py) - console script for MBoot module. - [elftosb](elftosb.py) - utility for generating TrustZone, MasterBootImage and SecureBinary images. -- [nxpcertgen](nxpcertgen.py) - utility for generating the self-signed x.509 certificate. +- [nxpcertgen](nxpcertgen.py) - utility for generating the self-signed x.509 certificate. - [nxpdebugmbox](nxpdebugmbox.py) - utility for performing the Debug Authentication. - [nxpdevscan](nxpdevscan.py) - utility for listing all connected NXP USB and UART devices. - [nxpkeygen](nxpkeygen.py) - utility for generating RSA/ECC key pairs and debug credential files based on YAML configuration file. @@ -14,10 +14,10 @@ After installing SPSDK, several applications are present directly on PATH as exe - [sdphost](sdphost.py) - console script for SDP module. - [sdpshost](sdpshost.py) - console script for SDPS module. - [shadowregs](shadowregs.py) - utility for Shadow Registers controlling. - + `` spsdk --help`` - lists all available commands. -`` spsdk --help`` - print help for given application. +`` spsdk --help`` - print help for given application. `` spsdk --help `` - print help for given command. diff --git a/spsdk/apps/blhost.py b/spsdk/apps/blhost.py index ba3d5eb4..c2ddd322 100644 --- a/spsdk/apps/blhost.py +++ b/spsdk/apps/blhost.py @@ -18,7 +18,15 @@ from spsdk import SPSDKError from spsdk import __version__ as spsdk_version -from spsdk.apps.blhost_helper import parse_image_file, parse_key_prov_key_type, parse_property_tag +from spsdk.apps.blhost_helper import ( + OemGenMasterShareHelp, + OemSetMasterShareHelp, + parse_image_file, + parse_key_prov_key_type, + parse_property_tag, + parse_trust_prov_key_type, + parse_trust_prov_oem_key_type, +) from spsdk.apps.utils import ( INT, catch_spsdk_error, @@ -30,10 +38,11 @@ from spsdk.mboot import GenerateKeyBlobSelect, McuBoot, StatusCode, parse_property_value -@click.group() +@click.group(no_args_is_help=True) @optgroup.group("Interface configuration", cls=MutuallyExclusiveOptionGroup) @optgroup.option( - "-p", "--port", + "-p", + "--port", metavar="COM[,speed]", help="""Serial port configuration. Use 'nxpdevscan' utility to list devices on serial port.""", ) @@ -48,25 +57,45 @@ Use 'nxpdevscan' utility to list connected device names. """, ) -@click.option("-j", - "--json", - "use_json", - is_flag=True, - help="Prints output in JSON format." +@optgroup.option( + "-l", + "--lpcusbsio", + metavar="spi|i2c", + help="""USB-SIO bridge interface. + Following interfaces are supported: + + spi[,port,pin,speed_kHz,polarity,phase] + - port ... bridge GPIO port used as SPI SSEL + - pin ... bridge GPIO pin used as SPI SSEL + default SSEL is set to 0.15 which works + for the LPCLink2 bridge. The MCULink OB + bridge ignores the SSEL value anyway. + - speed_kHz ... SPI clock in kHz (default 1000) + - polarity ... SPI CPOL option (default=1) + - phase ... SPI CPHA option (default=1) + + i2c[,address,speed_kHz] + - address ... I2C device address (default 0x10) + - speed_kHz ... I2C clock in kHz (default 100) +""", ) -@click.option("-v", +@click.option("-j", "--json", "use_json", is_flag=True, help="Prints output in JSON format.") +@click.option( + "-v", "--verbose", "log_level", flag_value=logging.INFO, help="Prints more detailed information.", ) -@click.option("-d", +@click.option( + "-d", "--debug", "log_level", flag_value=logging.DEBUG, help="Display more debugging info.", ) -@click.option("-t", +@click.option( + "-t", "--timeout", metavar="", help="""Sets timeout when waiting on data over a serial line. The default is 5000 milliseconds.""", @@ -79,6 +108,7 @@ def main( ctx: click.Context, port: str, usb: str, + lpcusbsio: str, use_json: bool, log_level: int, timeout: int, @@ -99,7 +129,9 @@ def main( # if --help is provided anywhere on commandline, skip interface lookup and display help message if "--help" not in click.get_os_args(): ctx.obj = { - "interface": get_interface(module="mboot", port=port, usb=usb, timeout=timeout), + "interface": get_interface( + module="mboot", port=port, usb=usb, timeout=timeout, lpcusbsio=lpcusbsio + ), "use_json": use_json, } return 0 @@ -203,14 +235,14 @@ def execute(ctx: click.Context, address: int, argument: int, stackpointer: int) STACKPOINTER - Stack pointer for the application """ with McuBoot(ctx.obj["interface"]) as mboot: - response = mboot.execute(address, argument, stackpointer) + mboot.execute(address, argument, stackpointer) display_output([], mboot.status_code, ctx.obj["use_json"]) @main.command() @click.argument("address", type=INT(), required=True) @click.argument("byte_count", type=INT(), required=True) -@click.argument("memory_id", type=int, required=False, default=0) +@click.argument("memory_id", type=INT(), required=False, default="0") @click.pass_context def flash_erase_region(ctx: click.Context, address: int, byte_count: int, memory_id: int) -> None: """Erases one or more sectors of the flash memory. @@ -229,7 +261,7 @@ def flash_erase_region(ctx: click.Context, address: int, byte_count: int, memory @main.command() -@click.argument("memory_id", type=int, required=False, default=0) +@click.argument("memory_id", type=INT(), required=False, default="0") @click.pass_context def flash_erase_all(ctx: click.Context, memory_id: int) -> None: """Performs an erase of the entire flash memory. @@ -257,7 +289,7 @@ def flash_erase_all_unsecure(ctx: click.Context) -> None: @main.command() @click.argument("image_file_path", metavar="FILE", type=str, required=True) @click.argument("erase", type=str, required=False, default="none") -@click.argument("memory_id", type=INT(), required=False, default='0') +@click.argument("memory_id", type=INT(), required=False, default="0") @click.pass_context def flash_image(ctx: click.Context, image_file_path: str, erase: str, memory_id: int) -> None: """Write the formatted image in to the memory specified by memoryID. @@ -274,7 +306,9 @@ def flash_image(ctx: click.Context, image_file_path: str, erase: str, memory_id: try: mem_id = int(erase, 0) except ValueError as e: - raise SPSDKError("The option for erasing was not declared properly. Choose from 'erase' or 'none'.") from e + raise SPSDKError( + "The option for erasing was not declared properly. Choose from 'erase' or 'none'." + ) from e if memory_id: mem_id = memory_id segments = parse_image_file(image_file_path) @@ -293,14 +327,15 @@ def flash_image(ctx: click.Context, image_file_path: str, erase: str, memory_id: @main.command() -@click.argument("index", type=int, required=True) +@click.argument("index", type=INT(), required=True) @click.argument("byte_count", type=click.Choice(["4", "8"]), required=True) @click.argument("data", type=INT(base=16), required=True) @click.argument( "endianess", metavar="[LSB|MSB]", type=click.Choice(["LSB", "MSB"]), - default="LSB", required=False + default="LSB", + required=False, ) @click.pass_context def flash_program_once( @@ -322,7 +357,7 @@ def flash_program_once( @main.command() -@click.argument("index", type=int, required=True) +@click.argument("index", type=INT(), required=True) @click.argument("byte_count", type=click.Choice(["4", "8"]), required=True, default=4) @click.pass_context def flash_read_once(ctx: click.Context, index: int, byte_count: str) -> None: @@ -363,13 +398,18 @@ def flash_security_disable(ctx: click.Context, key: str) -> None: @main.command() @click.argument("address", type=INT(), required=True) -@click.argument("length", type=int, required=True) +@click.argument("length", type=INT(), required=True) @click.argument("option", type=click.Choice(["0", "1"]), required=True) @click.argument("out_file", metavar="FILE", type=click.File("wb"), required=False) @click.option("-h", "--use-hexdump", is_flag=True, default=False, help="Use hexdump format") @click.pass_context def flash_read_resource( - ctx: click.Context, address: int, length: int, option: str, out_file: click.File, use_hexdump: bool + ctx: click.Context, + address: int, + length: int, + option: str, + out_file: click.File, + use_hexdump: bool, ) -> None: """Read resource of flash module. @@ -395,7 +435,7 @@ def flash_read_resource( [len(response) if response else 0], mboot.status_code, ctx.obj["use_json"], - f"Read {len(response) if response else 0} of {length} bytes." + f"Read {len(response) if response else 0} of {length} bytes.", ) @@ -431,7 +471,7 @@ def fill_memory( @main.command() @click.argument("address", type=INT(), required=True) @click.argument("data_source", metavar="FILE[,BYTE_COUNT] | {{HEX-DATA}}", type=str, required=True) -@click.argument("memory_id", type=int, required=False, default=0) +@click.argument("memory_id", type=INT(), required=False, default="0") @click.pass_context def fuse_program(ctx: click.Context, address: int, data_source: str, memory_id: int) -> None: """Program fuse. @@ -459,7 +499,7 @@ def fuse_program(ctx: click.Context, address: int, data_source: str, memory_id: @click.argument("address", type=INT(), required=True) @click.argument("byte_count", type=INT(), required=True) @click.argument("out_file", metavar="FILE", type=click.File("wb"), required=False) -@click.argument("memory_id", type=int, default=0, required=False) +@click.argument("memory_id", type=INT(), default="0", required=False) @click.option("-h", "--use-hexdump", is_flag=True, default=False, help="Use hexdump format") @click.pass_context def fuse_read( @@ -536,7 +576,7 @@ def load_image(ctx: click.Context, boot_file: click.File) -> None: @main.command() @click.argument("property_tag", type=str, required=True) -@click.argument("index", type=int, default=0) +@click.argument("index", type=INT(), default="0") @click.pass_context def get_property(ctx: click.Context, property_tag: str, index: int) -> None: """Queries various bootloader properties and settings. @@ -624,7 +664,7 @@ def set_property(ctx: click.Context, property_tag: str, value: int) -> None: @click.argument("address", type=INT(), required=True) @click.argument("byte_count", type=INT(), required=True) @click.argument("out_file", metavar="FILE", type=click.File("wb"), required=False) -@click.argument("memory_id", type=int, default=0, required=False) +@click.argument("memory_id", type=INT(), default="0", required=False) @click.option("-h", "--use-hexdump", is_flag=True, default=False, help="Use hexdump format") @click.pass_context def read_memory( @@ -682,8 +722,7 @@ def receive_sb_file(ctx: click.Context, sb_file: click.File) -> None: @main.command() @click.argument("address", type=INT(), required=True) @click.pass_context -def reliable_update( - ctx: click.Context, address: int) -> None: +def reliable_update(ctx: click.Context, address: int) -> None: """Reliable Update. \b @@ -709,7 +748,7 @@ def reset(ctx: click.Context) -> None: @main.command() @click.argument("address", type=INT(), required=True) @click.argument("data_source", metavar="FILE[,BYTE_COUNT] | {{HEX-DATA}}", type=str, required=True) -@click.argument("memory_id", type=int, required=False, default=0) +@click.argument("memory_id", type=INT(), required=False, default="0") @click.pass_context def write_memory(ctx: click.Context, address: int, data_source: str, memory_id: int) -> None: """Writes memory from a file or a hex-data. @@ -851,7 +890,7 @@ def set_user_key(ctx: click.Context, key_type: str, file_and_size: str) -> None: @key_provisioning.command(name="set_key") @click.argument("key_type", metavar="TYPE", type=str, required=True) -@click.argument("key_size", metavar="SIZE", type=int, required=True) +@click.argument("key_size", metavar="SIZE", type=INT(), required=True) @click.pass_context def set_key(ctx: click.Context, key_type: str, key_size: int) -> None: """Generates a size bytes of the key specified by the type. @@ -882,7 +921,7 @@ def set_key(ctx: click.Context, key_type: str, key_size: int) -> None: @key_provisioning.command(name="write_key_nonvolatile") -@click.argument("memory_id", metavar="memoryID", type=int, default=0) +@click.argument("memory_id", metavar="memoryID", type=INT(), default="0") @click.pass_context def write_key_nonvolatile(ctx: click.Context, memory_id: int) -> None: """Writes the key to nonvolatile memory. @@ -896,7 +935,7 @@ def write_key_nonvolatile(ctx: click.Context, memory_id: int) -> None: @key_provisioning.command(name="read_key_nonvolatile") -@click.argument("memory_id", metavar="memoryID", type=int, default=0) +@click.argument("memory_id", metavar="memoryID", type=INT(), default="0") @click.pass_context def read_key_nonvolatile(ctx: click.Context, memory_id: int) -> None: """Loads the key from nonvolatile memory to bootloader. @@ -956,6 +995,368 @@ def read_key_store(ctx: click.Context, key_store_file: click.File) -> None: key_store_file.write(response) # type: ignore +@main.group() +@click.pass_context +def trust_provisioning(ctx: click.Context) -> None: + """Group of sub-commands related to trust provisioning.""" + + +@trust_provisioning.command(name="hsm_store_key") +@click.argument("key_type", metavar="KEY_TYPE", type=str, required=True) +@click.argument("key_property", metavar="KEY_PROPERTY", type=INT(), required=True) +@click.argument("key_input_addr", metavar="KEY_INPUT_ADDR", type=INT(), required=True) +@click.argument("key_input_size", metavar="KEY_INPUT_SIZE", type=INT(), required=True) +@click.argument("key_blob_output_addr", metavar="KEY_BLOB_OUTPUT_ADDR", type=INT(), required=True) +@click.argument("key_blob_output_size", metavar="KEY_BLOB_OUTPUT_SIZE", type=INT(), required=True) +@click.pass_context +def hsm_store_key( + ctx: click.Context, + key_type: str, + key_property: int, + key_input_addr: int, + key_input_size: int, + key_blob_output_addr: int, + key_blob_output_size: int, +) -> None: + """Stores known keys, and generate the corresponding key blob. + + It wraps the known key, which is given by the customer, + using NXP_CUST_KEK_EXT_SK, and output the RFC3396 key blob. + + \b + KEY_TYPE - Type of key to generate (CKDFK, HKDFK, HMACK, CMACK, AESK, KUOK) + KEY_PROPERTY - Bit 0: Key Size, 0 for 128bit, 1 for 256bit. Bits 30-31: set key protection CSS mode + KEY_INPUT_ADDR - The input buffer address where the key locates at + KEY_INPUT_SIZE - The byte count of the key + KEY_BLOB_OUTPUT_ADDR - The output buffer address where ROM writes the key blob to + KEY_BLOB_OUTPUT_SIZE - The output buffer size in byte + """ + key_type_int = parse_trust_prov_key_type(key_type) + with McuBoot(ctx.obj["interface"]) as mboot: + response = mboot.tp_hsm_store_key( + key_type_int, + key_property, + key_input_addr, + key_input_size, + key_blob_output_addr, + key_blob_output_size, + ) + + extra_output = "" + if response: + key_header = response[0] + key_blob_size = response[1] + if mboot.status_code == StatusCode.SUCCESS: + extra_output = "Output data size/value(s) is(are):\n" + extra_output += ( + f"\tKey Header: {key_header} ({hex(key_header)})\n" + f"\tKey Blob size: {key_blob_size} ({hex(key_blob_size)})" + ) + display_output(response, mboot.status_code, ctx.obj["use_json"], extra_output) + + +@trust_provisioning.command(name="hsm_gen_key") +@click.argument("key_type", metavar="KEY_TYPE", type=str, required=True) +@click.argument("reserved", metavar="RESERVED", type=INT(), required=True) +@click.argument("key_blob_output_addr", metavar="KEY_BLOB_OUTPUT_ADDR", type=INT(), required=True) +@click.argument("key_blob_output_size", metavar="KEY_BLOB_OUTPUT_SIZE", type=INT(), required=True) +@click.argument("ecdsa_puk_output_addr", metavar="ECDSA_PUK_OUTPUT_ADDR", type=INT(), required=True) +@click.argument("ecdsa_puk_output_size", metavar="ECDSA_PUK_OUTPUT_SIZE", type=INT(), required=True) +@click.pass_context +def hsm_gen_key( + ctx: click.Context, + key_type: str, + reserved: int, + key_blob_output_addr: int, + key_blob_output_size: int, + ecdsa_puk_output_addr: int, + ecdsa_puk_output_size: int, +) -> None: + """Creates OEM common keys, including encryption keys and signing keys. + + It outputs the key blob, which is wrapped by NXP_CUST_KEK_IN_SK + and the public portion of the signing key. + + \b + KEY_TYPE - Type of key to generate (MFWISK, MFWENCK, GENSIGNK, GETCUSTMKSK) + RESERVED - Reserved must be 0 + KEY_BLOB_OUTPUT_ADDR - Output buffer address where ROM writes the key blob to + KEY_BLOB_OUTPUT_SIZE - Output buffer size in bytes + ECDSA_PUK_OUTPUT_ADDR - Output buffer address where ROM writes the public key to + ECDSA_PUK_OUTPUT_SIZE - Output buffer size in bytes + """ + key_type_int = parse_trust_prov_oem_key_type(key_type) + with McuBoot(ctx.obj["interface"]) as mboot: + response = mboot.tp_hsm_gen_key( + key_type_int, + reserved, + key_blob_output_addr, + key_blob_output_size, + ecdsa_puk_output_addr, + ecdsa_puk_output_size, + ) + + extra_output = "" + if response: + keyblob_size = response[0] + ecdsa_puk_size = response[1] + if mboot.status_code == StatusCode.SUCCESS: + extra_output = "Output data size/value(s) is(are):\n" + else: + extra_output = ( + "Output buffer(s) is(are) smaller than the minimum requested which is(are):\n" + ) + extra_output += ( + f"\tKey Blob size: {keyblob_size} ({hex(keyblob_size)})\n" + f"\tECDSA Puk size: {ecdsa_puk_size} ({hex(ecdsa_puk_size)})" + ) + display_output(response, mboot.status_code, ctx.obj["use_json"], extra_output) + + +@trust_provisioning.command(name="hsm_enc_blk") +@click.argument( + "mfg_cust_mk_sk_0_blob_input_addr", + metavar="MFG_CUST_MK_SK_0_BLOB_INPUT_ADDR", + type=INT(), + required=True, +) +@click.argument( + "mfg_cust_mk_sk_0_blob_input_size", + metavar="MFG_CUST_MK_SK_0_BLOB_INPUT_SIZE", + type=INT(), + required=True, +) +@click.argument("kek_id", metavar="KEK_ID", type=str, required=True) +@click.argument("sb3_header_input_addr", metavar="SB3_HEADER_INPUT_ADDR", type=INT(), required=True) +@click.argument("sb3_header_input_size", metavar="SB3_HEADER_INPUT_SIZE", type=INT(), required=True) +@click.argument("block_num", metavar="BLOCK_NUM", type=INT(), required=True) +@click.argument("block_data_addr", metavar="BLOCK_DATA_ADDR", type=INT(), required=True) +@click.argument("block_data_size", metavar="BLOCK_DATA_SIZE", type=INT(), required=True) +@click.pass_context +def hsm_enc_blk( + ctx: click.Context, + mfg_cust_mk_sk_0_blob_input_addr: int, + mfg_cust_mk_sk_0_blob_input_size: int, + kek_id: str, + sb3_header_input_addr: int, + sb3_header_input_size: int, + block_num: int, + block_data_addr: int, + block_data_size: int, +) -> None: + """Encrypts the given SB3 data block. + + \b + MFG_CUST_MK_SK_0_BLOB_INPUT_ADDR - The input buffer address where the CKDF Master Key Blob locates at + MFG_CUST_MK_SK_0_BLOB_INPUT_SIZE - The byte count of the CKDF Master Key Blob + KEK_ID - The CKDF Master Key Encryption Key ID + (0x10: NXP_CUST_KEK_INT_SK, 0x11: NXP_CUST_KEK_EXT_SK) + SB3_HEADER_INPUT_ADDR - The input buffer address where the SB3 Header(block0) locates at + SB3_HEADER_INPUT_SIZE - The byte count of the SB3 Header + BLOCK_NUM - The index of the block. Due to SB3 Header(block 0) is always unencrypted, + the index starts from block1 + BLOCK_DATA_ADDR - The buffer address where the SB3 data block locates at + BLOCK_DATA_SIZE - The byte count of the SB3 data block + """ + kek_id_int = parse_trust_prov_key_type(kek_id) + with McuBoot(ctx.obj["interface"]) as mboot: + mboot.tp_hsm_enc_blk( + mfg_cust_mk_sk_0_blob_input_addr, + mfg_cust_mk_sk_0_blob_input_size, + kek_id_int, + sb3_header_input_addr, + sb3_header_input_size, + block_num, + block_data_addr, + block_data_size, + ) + display_output([], mboot.status_code, ctx.obj["use_json"]) + + +@trust_provisioning.command(name="hsm_enc_sign") +@click.argument("key_blob_input_addr", metavar="KEY_BLOB_INPUT_ADDR", type=INT(), required=True) +@click.argument("key_blob_input_size", metavar="KEY_BLOB_INPUT_SIZE", type=INT(), required=True) +@click.argument("block_data_input_addr", metavar="BLOCK_DATA_INPUT_ADDR", type=INT(), required=True) +@click.argument("block_data_input_size", metavar="BLOCK_DATA_INPUT_SIZE", type=INT(), required=True) +@click.argument("signature_output_addr", metavar="SIGNATURE_OUTPUT_ADDR", type=INT(), required=True) +@click.argument("signature_output_size", metavar="SIGNATURE_OUTPUT_SIZE", type=INT(), required=True) +@click.pass_context +def hsm_enc_sign( + ctx: click.Context, + key_blob_input_addr: int, + key_blob_input_size: int, + block_data_input_addr: int, + block_data_input_size: int, + signature_output_addr: int, + signature_output_size: int, +) -> None: + """Signs the given data. + + It uses the private key in the given key blob, which is generated by HSM_GEN_KEY. + + \b + KEY_BLOB_INPUT_ADDR - The input buffer address where signing key blob locates at + KEY_BLOB_INPUT_SIZE - The byte count of the signing key blob + BLOCK_DATA_INPUT_ADDR - The input buffer address where the data locates at + BLOCK_DATA_INPUT_SIZE - The byte count of the data + SIGNATURE_OUTPUT_ADDR - The output buffer address where ROM writes the signature to + SIGNATURE_OUTPUT_SIZE - The output buffer size in byte + """ + with McuBoot(ctx.obj["interface"]) as mboot: + response = mboot.tp_hsm_enc_sign( + key_blob_input_addr, + key_blob_input_size, + block_data_input_addr, + block_data_input_size, + signature_output_addr, + signature_output_size, + ) + + extra_output = "" + if response: + output_signature_size = response + if mboot.status_code == StatusCode.SUCCESS: + extra_output = "Output data size/value(s) is(are):\n" + else: + extra_output = ( + "Output buffer(s) is(are) smaller than the minimum requested which is(are):\n" + ) + extra_output += ( + f"\tSignature size: {output_signature_size} ({hex(output_signature_size)})" + ) + display_output([response], mboot.status_code, ctx.obj["use_json"], extra_output) + + +@trust_provisioning.command(name="oem_gen_master_share", cls=OemGenMasterShareHelp) +@click.argument("oem_share_input_addr", type=INT(), required=True) +@click.argument("oem_share_input_size", type=INT(), required=True) +@click.argument("oem_enc_share_output_addr", type=INT(), required=True) +@click.argument("oem_enc_share_output_size", type=INT(), required=True) +@click.argument("oem_enc_master_share_output_addr", type=INT(), required=True) +@click.argument("oem_enc_master_share_output_size", type=INT(), required=True) +@click.argument("oem_cust_cert_puk_output_addr", type=INT(), required=True) +@click.argument("oem_cust_cert_puk_output_size", type=INT(), required=True) +@click.pass_context +def oem_gen_master_share( + ctx: click.Context, + oem_share_input_addr: int, + oem_share_input_size: int, + oem_enc_share_output_addr: int, + oem_enc_share_output_size: int, + oem_enc_master_share_output_addr: int, + oem_enc_master_share_output_size: int, + oem_cust_cert_puk_output_addr: int, + oem_cust_cert_puk_output_size: int, +) -> None: + """Creates shares for initial trust provisioning keys. + + \b + OEM_SHARE_INPUT_ADDRR - The input buffer address where the OEM Share(entropy seed) locates at + OEM_SHARE_INPUT_SIZE - The byte count of the OEM Share + OEM_ENC_SHARE_OUTPUT_ADDR - The output buffer address where ROM writes the Encrypted OEM Share to + OEM_ENC_SHARE_OUTPUT_SIZE - The output buffer size in byte + OEM_ENC_MASTER_SHARE_OUTPUT_ADDR - The output buffer address where ROM writes the Encrypted OEM Master Share to + OEM_ENC_MASTER_SHARE_OUTPUT_SIZE - The output buffer size in byte. + OEM_CUST_CERT_PUK_OUTPUT_ADDR - The output buffer address where ROM writes + the OEM Customer Certificate Public Key to + OEM_CUST_CERT_PUK_OUTPUT_SIZE - The output buffer size in byte + """ + with McuBoot(ctx.obj["interface"]) as mboot: + response = mboot.tp_oem_gen_master_share( + oem_share_input_addr, + oem_share_input_size, + oem_enc_share_output_addr, + oem_enc_share_output_size, + oem_enc_master_share_output_addr, + oem_enc_master_share_output_size, + oem_cust_cert_puk_output_addr, + oem_cust_cert_puk_output_size, + ) + extra_output = "" + if response: + oem_enc_share_size = response[0] + oem_enc_master_share_size = response[1] + oem_cust_cert_puk_size = response[2] + if mboot.status_code == StatusCode.SUCCESS: + extra_output = "Output data size/value(s) is(are):\n" + else: + extra_output = ( + "Output buffer(s) is(are) smaller than the minimum requested which is(are):\n" + ) + extra_output += ( + f"\tOEM Share size: {oem_enc_share_size} ({hex(oem_enc_share_size)})\n" + f"\tOEM Master Share size: {oem_enc_master_share_size} ({hex(oem_enc_master_share_size)})\n" + f"\tCust Cert Puk size: {oem_cust_cert_puk_size} ({hex(oem_cust_cert_puk_size)})" + ) + display_output(response, mboot.status_code, ctx.obj["use_json"], extra_output) + + +@trust_provisioning.command(name="oem_set_master_share", cls=OemSetMasterShareHelp) +@click.argument("oem_share_input_addr", type=INT(), required=True) +@click.argument("oem_share_input_size", type=INT(), required=True) +@click.argument("oem_enc_master_share_input_addr", type=INT(), required=True) +@click.argument("oem_enc_master_share_input_size", type=INT(), required=True) +@click.pass_context +def oem_set_master_share( + ctx: click.Context, + oem_share_input_addr: int, + oem_share_input_size: int, + oem_enc_master_share_input_addr: int, + oem_enc_master_share_input_size: int, +) -> None: + """Takes the entropy seed and the Encrypted OEM Master Share.""" + with McuBoot(ctx.obj["interface"]) as mboot: + mboot.tp_oem_set_master_share( + oem_share_input_addr, + oem_share_input_size, + oem_enc_master_share_input_addr, + oem_enc_master_share_input_size, + ) + display_output([], mboot.status_code, ctx.obj["use_json"]) + + +@trust_provisioning.command(name="oem_get_cust_cert_dice_puk") +@click.argument("oem_rkt_input_addr", type=INT(), required=True) +@click.argument("oem_rkth_input_size", type=INT(), required=True) +@click.argument("oem_cust_cert_dice_puk_output_addr", type=INT(), required=True) +@click.argument("oem_cust_cert_dice_puk_output_size", type=INT(), required=True) +@click.pass_context +def oem_get_cust_cert_dice_puk( + ctx: click.Context, + oem_rkt_input_addr: int, + oem_rkth_input_size: int, + oem_cust_cert_dice_puk_output_addr: int, + oem_cust_cert_dice_puk_output_size: int, +) -> None: + """Creates the initial trust provisioning keys. + + \b + OEM_RKT_INPUT_ADDR - The input buffer address where the OEM RKTH locates at + OEM_RKTH_INPUT_SIZE - The byte count of the OEM RKTH + OEM_CUST_CERT_DICE_PUK_OUTPUT_ADDR - The output buffer address where ROM writes the OEM Customer + Certificate Public Key for DICE to + OEM_CUST_CERT_DICE_PUK_OUTPUT_SIZE - The output buffer size in byte + """ + with McuBoot(ctx.obj["interface"]) as mboot: + response = mboot.tp_oem_get_cust_cert_dice_puk( + oem_rkt_input_addr, + oem_rkth_input_size, + oem_cust_cert_dice_puk_output_addr, + oem_cust_cert_dice_puk_output_size, + ) + extra_output = "" + if response: + output_size = response + if mboot.status_code == StatusCode.SUCCESS: + extra_output = "Output data size/value(s) is(are):\n" + else: + extra_output = ( + "Output buffer(s) is(are) smaller than the minimum requested which is(are):" + ) + extra_output += f"\tCust Cert Dice Puk size: {output_size} ({hex(output_size)})" + display_output([response], mboot.status_code, ctx.obj["use_json"], extra_output) + + def display_output( response: list = None, status_code: int = 0, @@ -988,7 +1389,8 @@ def display_output( else: print(f"Response status = {decode_status_code(status_code)}") if isinstance(response, list): - for i, word in enumerate(response): + filtered_response = filter(lambda x: x is not None, response) + for i, word in enumerate(filtered_response): print(f"Response word {i + 1} = {word} ({word:#x})") if extra_output: print(extra_output) diff --git a/spsdk/apps/blhost_helper.py b/spsdk/apps/blhost_helper.py index 7aa16454..a100ad50 100644 --- a/spsdk/apps/blhost_helper.py +++ b/spsdk/apps/blhost_helper.py @@ -8,12 +8,49 @@ """Helper module for blhost application.""" import math -from typing import List, NamedTuple +from typing import Any, List import bincopy +import click from spsdk import SPSDKError -from spsdk.mboot.commands import KeyProvUserKeyType +from spsdk.mboot.commands import ( + KeyProvUserKeyType, + TrustProvKeyType, + TrustProvOemKeyType, + TrustProvWrappingKeyType, +) + + +class OemGenMasterShareHelp(click.Command): + """Class for customized "usage" help line for oem_gen_master_share command.""" + + def format_usage(self, ctx: Any, formatter: Any) -> None: + """Customizes "usage" help line for oem_gen_master_share command.""" + click.echo("Usage: blhost trust-provisioning oem_gen_master_share [OPTIONS]") + indent = 7 * "\t" + click.echo(indent + "OEM_SHARE_INPUT_ADDR") + click.echo(indent + "OEM_SHARE_INPUT_SIZE") + click.echo(indent + "OEM_ENC_SHARE_OUTPUT_ADDR") + click.echo(indent + "OEM_ENC_SHARE_OUTPUT_SIZE") + click.echo(indent + "OEM_ENC_MASTER_SHARE_OUTPUT_ADDR") + click.echo(indent + "OEM_ENC_MASTER_SHARE_OUTPUT_SIZE") + click.echo(indent + "OEM_CUST_CERT_PUK_OUTPUT_ADDR") + click.echo(indent + "OEM_CUST_CERT_PUK_OUTPUT_SIZE") + + +class OemSetMasterShareHelp(click.Command): + """Class for customized "usage" help line for oem_set_master_share command.""" + + def format_usage(self, ctx: Any, formatter: Any) -> None: + """Customizes "usage" help line for oem_set_master_share command.""" + click.echo("Usage: blhost trust-provisioning oem_set_master_share [OPTIONS]") + indent = 7 * "\t" + click.echo(indent + "OEM_SHARE_INPUT_ADDR") + click.echo(indent + "OEM_SHARE_INPUT_SIZE") + click.echo(indent + "OEM_ENC_MASTER_SHARE_INPUT_ADDR") + click.echo(indent + "OEM_ENC_MASTER_SHARE_INPUT_SIZE") + PROPERTIES_NAMES = { "list-properties": 0, @@ -67,16 +104,50 @@ def parse_key_prov_key_type(key_type: str) -> int: :param key_type: Name or number of the Key type :return: key type number """ + return _parse_key_type(key_type, KeyProvUserKeyType, 0xFF) + + +def parse_trust_prov_oem_key_type(key_type: str) -> int: + """Convert the key type as name or stringified number into integer. + + :param key_type: Name or number of the Key type + :return: key type number + """ + return _parse_key_type(key_type, TrustProvOemKeyType) + + +def parse_trust_prov_key_type(key_type: str) -> int: + """Convert the key type as name or stringified number into integer. + + :param key_type: Name or number of the Key type + :return: key type number + """ + return _parse_key_type(key_type, TrustProvKeyType) + + +def parse_trust_prov_wrapping_key_type(key_type: str) -> int: + """Convert the key type as name or stringified number into integer. + + :param key_type: Name or number of the Key type + :return: key type number + """ + return _parse_key_type(key_type, TrustProvWrappingKeyType) + + +def _parse_key_type(user_input: str, collection: Any, default: int = None) -> int: try: - return int(key_type, 0) + return int(user_input, 0) except: - key_type = key_type.upper() - key_type_int = KeyProvUserKeyType.get(key_type, 0xFF) - assert isinstance(key_type_int, int) + key_type = user_input.upper() + key_type_int = collection.get(key_type, default) + if key_type_int is None: + raise SPSDKError(f"Unable to find '{user_input}' in '{collection.__name__}'") return key_type_int -class SegmentInfo(): + +class SegmentInfo: """SegmentInfo class containing: start, length and data of segment.""" + ALIGNMENT = 1024 def __init__(self, start: int, length: int, data_bin: bytes) -> None: @@ -108,14 +179,14 @@ def parse_image_file(file_path: str) -> List[SegmentInfo]: """Parse image. :param file_path: path, where the image is stored - :raises SPSDKError: when elf/axf files are used - :raises SPSDKError: when binary file is used - :raises SPSDKError: when unsupported file is used + :raises SPSDKError: When elf/axf files are used + :raises SPSDKError: When binary file is used + :raises SPSDKError: When unsupported file is used :return: SegmentInfo object """ with open(file_path, "rb") as f: data = f.read(4) - if data == b'\x7fELF': + if data == b"\x7fELF": raise SPSDKError("Elf file is not supported") try: binfile = bincopy.BinFile(file_path) @@ -124,6 +195,8 @@ def parse_image_file(file_path: str) -> List[SegmentInfo]: for segment in binfile.segments ] except UnicodeDecodeError as e: - raise SPSDKError('Error: please use write-memory command for binary file downloading.') from e + raise SPSDKError( + "Error: please use write-memory command for binary file downloading." + ) from e except Exception as e: raise SPSDKError("Error loading file") from e diff --git a/spsdk/apps/elftosb.py b/spsdk/apps/elftosb.py index 8efa8ef5..9451d2b4 100644 --- a/spsdk/apps/elftosb.py +++ b/spsdk/apps/elftosb.py @@ -9,7 +9,7 @@ import os import sys from datetime import datetime -from typing import List +from typing import List, Union import click import commentjson as json @@ -20,17 +20,26 @@ from spsdk import __version__ as spsdk_version from spsdk.apps.elftosb_utils import sb_31_helper as elftosb_helper from spsdk.apps.utils import catch_spsdk_error -from spsdk.crypto import SignatureProvider -from spsdk.image import MasterBootImageN4Analog, MasterBootImageType, TrustZone +from spsdk.crypto import SignatureProvider, load_certificate_as_bytes +from spsdk.exceptions import SPSDKError +from spsdk.image import MasterBootImage, MasterBootImageN4Analog, MasterBootImageType, TrustZone +from spsdk.image.keystore import KeySourceType, KeyStore from spsdk.sbfile.images import BootImageV21, BootSectionV2 from spsdk.sbfile.sb31.images import SecureBinary31Commands, SecureBinary31Header from spsdk.utils.crypto import CertBlockV2, CertBlockV31, Certificate from spsdk.utils.crypto.backend_internal import internal_backend from spsdk.utils.misc import load_binary, load_text, write_file -# TODO update supported families... -SUPPORTED_FAMILIES = ['lpc55s3x'] - +SUPPORTED_FAMILIES = [ + "lpc55xx", + "lpc55s0x", + "lpc55s1x", + "lpc55s2x", + "lpc55s6x", + "lpc55s3x", + "rt5xx", + "rt6xx", +] def generate_trustzone_binary(tzm_conf: click.File) -> None: @@ -43,11 +52,39 @@ def generate_trustzone_binary(tzm_conf: click.File) -> None: tz_data = trustzone.export() write_file(tz_data, config.output_file, mode="wb") + click.echo(f"Success. (Trustzone binary: {config.output_file} created.)") + -def _get_trustzone(config: elftosb_helper.MasterBootImageConfig) -> TrustZone: +def _get_trustzone( + config: Union[ + elftosb_helper.MasterBootImageConfigGroup3, elftosb_helper.MasterBootImageConfigGroup4 + ] +) -> TrustZone: """Create appropriate TrustZone instance.""" - if not config.trustzone_preset_file: - return TrustZone.disabled() + family = config.family.lower() + + if family in ["lpc55s3x"]: + if not config.trustzone_preset_file: + return TrustZone.enabled() + + if family in [ + "lpc55xx", + "lpc55s0x", + "lpc55s1x", + "lpc552x", + "lpc55s2x", + "lpc55s6x", + "rt5xx", + "rt6xx", + ]: + if config.enable_trustzone is False: # type: ignore + # we don't care about preset data file, just "disable" TZ-M + return TrustZone.disabled() + + if not config.trustzone_preset_file: + # TZ-M is left enabled, but no data provided + return TrustZone.enabled() + try: tz_config_data = json.loads(load_text(config.trustzone_preset_file)) tz_config = elftosb_helper.TrustZoneConfig(tz_config_data) @@ -58,73 +95,181 @@ def _get_trustzone(config: elftosb_helper.MasterBootImageConfig) -> TrustZone: ) except ValueError: tz_raw_data = load_binary(config.trustzone_preset_file) - return TrustZone.from_binary( - family=config.family, revision=config.revision, raw_data=tz_raw_data - ) + revision = getattr(config, "revision", None) + return TrustZone.from_binary(family=config.family, revision=revision, raw_data=tz_raw_data) -def _get_master_boot_image_type( - config: elftosb_helper.MasterBootImageConfig, -) -> MasterBootImageType: - """Get appropriate MasterBootImage type.""" - sb3_image_types = { - "crc-ram-False": MasterBootImageType.CRC_RAM_IMAGE, - "crc-xip-False": MasterBootImageType.CRC_XIP_IMAGE, - "signed-xip-False": MasterBootImageType.SIGNED_XIP_IMAGE, - "signed-xip-True": MasterBootImageType.SIGNED_XIP_NXP_IMAGE, - } - image_type = ( - f"{config.output_image_auth_type}-{config.output_image_exec_target}-{config.use_isk}" - ) - return sb3_image_types[image_type] +def _get_cert_block_v2(cert_config: elftosb_helper.CertificateBlockConfigGroup3) -> CertBlockV2: + cert_block = CertBlockV2(build_number=cert_config.image_build_number) + + # add whole certificate chain used for image signing + for cert_path in cert_config.root_certificates[cert_config.main_cert_chain_id]: + cert_data = load_certificate_as_bytes(str(cert_path)) + cert_block.add_certificate(cert_data) + # set root key hash of each root certificate + for cert_idx, cert_path in enumerate(cert_config.root_certificates): + cert_data = load_certificate_as_bytes(str(cert_path[0])) + cert_block.set_root_key_hash(cert_idx, Certificate(cert_data)) + + return cert_block -def _get_cert_block_v31(config: elftosb_helper.CertificateBlockConfig) -> CertBlockV31: - root_certs = [load_binary(cert_file) for cert_file in config.root_certs] # type: ignore +def _get_cert_block_v31(cert_config: elftosb_helper.CertificateBlockConfigGroup4) -> CertBlockV31: + root_certs = [load_binary(cert_file) for cert_file in cert_config.root_certs] # type: ignore user_data = None - if config.use_isk and config.isk_sign_data_path: - user_data = load_binary(config.isk_sign_data_path) + if cert_config.use_isk and cert_config.isk_sign_data_path: + user_data = load_binary(cert_config.isk_sign_data_path) isk_private_key = None - if config.use_isk: - assert config.main_root_private_key_file - isk_private_key = load_binary(config.main_root_private_key_file) + if cert_config.use_isk: + assert cert_config.main_root_private_key_file + isk_private_key = load_binary(cert_config.main_root_private_key_file) isk_cert = None - if config.use_isk: - assert config.isk_certificate - isk_cert = load_binary(config.isk_certificate) + if cert_config.use_isk: + assert cert_config.isk_certificate + isk_cert = load_binary(cert_config.isk_certificate) cert_block = CertBlockV31( root_certs=root_certs, - used_root_cert=config.main_root_cert_id, + used_root_cert=cert_config.main_root_cert_id, user_data=user_data, - constraints=config.isk_constraint, + constraints=cert_config.isk_constraint, isk_cert=isk_cert, - ca_flag=not config.use_isk, + ca_flag=not cert_config.use_isk, isk_private_key=isk_private_key, ) return cert_block def generate_master_boot_image(image_conf: click.File) -> None: - """Generate MasterBootImage from json configuration file.""" + """Generate MasterBootImage from json configuration file. + + :param image_conf: master boot image json configuration file. + :raises SPSDKError: Raised when signing private key path is not set + :raises SPSDKError: on unsupported family. + """ config_data = json.load(image_conf) - config = elftosb_helper.MasterBootImageConfig(config_data) + family = config_data.get("family") + + if family in [ + "lpc55xx", + "lpc55s0x", + "lpc55s1x", + "lpc552x", + "lpc55s2x", + "lpc55s6x", + "rt5xx", + "rt6xx", + ]: + mbi = __create_mbi_group3(config_data) + elif family in ["lpc55s3x"]: + mbi = __create_mbi_group4(config_data) + else: + raise SPSDKError(f"Generating Master Boot Image failed. Family {family} is not supported.") + + mbi_data = mbi.export() + + mbi_output_file_path = config_data["masterBootOutputFile"] + write_file(mbi_data, mbi_output_file_path, mode="wb") + + click.echo(f"Success. (Master Boot Image: {mbi_output_file_path} created.)") + + +def __create_mbi_group3(config_data: dict) -> MasterBootImage: + """Create master boot image for group 3 devices. + + Into group 3 belongs LPC55xx (LPC552x/S2x, LPC55S6x), LPC55S0x, LPC55S1x, RT5xx, RT6xx. + + :param config: master boot image configuration data. Consult the content with elftosb user guide. + :return: master boot image fro group 3 devices. + :raises SPSDKError: if keystore size is of invalid size. + :raises SPSDKError: if keystore file path is invalid. + """ + config = elftosb_helper.MasterBootImageConfigGroup3(config_data) + app = load_binary(config.input_image_file) load_addr = config.output_image_exec_address trustzone = _get_trustzone(config) - image_type = _get_master_boot_image_type(config) - dual_boot_version = config.dual_boot_version + image_type = config.image_type + enable_hw_user_mode_keys = config.enable_hw_user_mode_keys + + # Create certificate block, if image is signed + cert_block = None + private_key_pem_data = None + if MasterBootImageType.is_signed(image_type): + cert_block = _get_cert_block_v2(config.certificate_block_config) + private_key_pem_data = load_binary( + config.certificate_block_config.main_cert_private_key_file + ) + + key_store = None + hmac_key = None + key_source = KeySourceType.get(config.device_key_source) + if key_source in [KeySourceType.OTP, KeySourceType.KEYSTORE]: + with open(config.output_image_encryption_key_file, "r") as f: + hmac_key = f.read() + + if config.use_key_store and key_source == KeySourceType.KEYSTORE: + try: + if config.key_store_file.strip(): + key_store_data = load_binary(config.key_store_file) + if len(key_store_data) != KeyStore.KEY_STORE_SIZE: + raise SPSDKError( + f'Expected keystore size is "{KeyStore.KEY_STORE_SIZE}", got "{len(key_store_data)}".' + ) + else: + key_store_data = bytes(KeyStore.KEY_STORE_SIZE) + except OSError: + raise SPSDKError( + f'Invalid keystore file "{config.key_store_file}" in Master Boot Image configuration file.' + ) + + key_store = KeyStore(key_source=key_source, key_store=key_store_data) # type: ignore + + # app_table: Optional[MultipleImageTable] = None, + # ctr_init_vector: bytes = None, + mbi = MasterBootImage( + app=app, + load_addr=load_addr, + image_type=image_type, + trust_zone=trustzone, + app_table=None, + cert_block=cert_block, + priv_key_pem_data=private_key_pem_data, + hmac_key=hmac_key, + key_store=key_store, + enable_hw_user_mode_keys=enable_hw_user_mode_keys, + # ctr_init_vector=None, # commented out due to testing purposes + ) + return mbi + + +def __create_mbi_group4(config_data: dict) -> MasterBootImageN4Analog: + """Create master boot image for group 4 devices. + + Into group 4 devices belongs LPC55S3x. + + :param config: master boot image configuration data. Consult the content with elftosb user guide. + :return: master boot image group 4 devices. + :raises SPSDKError: signing private key file path not set in configuration file. + """ + config = elftosb_helper.MasterBootImageConfigGroup4(config_data) + + app = load_binary(config.input_image_file) + load_addr = config.output_image_exec_address + trustzone = _get_trustzone(config) + image_type = config.image_type firmware_version = config.firmware_version cert_block = None signature_provider = None if MasterBootImageType.is_signed(image_type): - cert_config = elftosb_helper.CertificateBlockConfig(config_data) - cert_block = _get_cert_block_v31(cert_config) - if cert_config.use_isk: - signing_private_key_path = cert_config.isk_private_key_file + cert_block = _get_cert_block_v31(config.certificate_block_config) + if config.certificate_block_config.use_isk: + signing_private_key_path = config.certificate_block_config.isk_private_key_file else: - signing_private_key_path = cert_config.main_root_private_key_file + signing_private_key_path = config.certificate_block_config.main_root_private_key_file + if not signing_private_key_path: + raise SPSDKError("Signing private key path is not set.") signature_provider = SignatureProvider.create( f"type=file;file_path={signing_private_key_path}" ) @@ -134,14 +279,12 @@ def generate_master_boot_image(image_conf: click.File) -> None: load_addr=load_addr, image_type=image_type, trust_zone=trustzone, - dual_boot_version=dual_boot_version, firmware_version=firmware_version, cert_block=cert_block, signature_provider=signature_provider, ) - mbi_data = mbi.export() - write_file(mbi_data, config.master_boot_output_file, mode="wb") + return mbi def generate_secure_binary_21( @@ -152,15 +295,15 @@ def generate_secure_binary_21( signing_certificate_file_paths: List[click.Path], root_key_certificate_paths: List[click.Path], hoh_out_path: click.Path, - external_files: List[click.Path] - ) -> None: + external_files: List[click.Path], +) -> None: """Generate SecureBinary image from BD command file. :param bd_file_path: path to BD file. :param output_file_path: output path to generated secure binary file. :param key_file_path: path to key file. :param private_key_file_path: path to private key file for signing. This key - relates to last certificate from signinng certifacate chain. + relates to last certificate from signing certificate chain. :param signing_certificate_file_paths: signing certificate chain. :param root_key_certificate_paths: paths to root key certificate(s) for verifying other certificates. Only 4 root key certificates are allowed, @@ -170,7 +313,7 @@ def generate_secure_binary_21( None, 'hash.bin' is created under working directory. :param external_files: external files referenced from BD file. - :raises SyntaxError: + :raises SPSDKError: If incorrect bf file is provided """ # Create lexer and parser, load the BD file content and parse it for # further execution - the parsed BD file is a dictionary in JSON format @@ -181,7 +324,7 @@ def generate_secure_binary_21( parsed_bd_file = parser.parse(text=bd_file_content, extern=external_files) if parsed_bd_file is None: - raise SyntaxError("error: invalid bd file, secure binary file generation terminated") + raise SPSDKError("Invalid bd file, secure binary file generation terminated") # The dictionary contains following content: # { @@ -198,13 +341,23 @@ def generate_secure_binary_21( # we need to encrypt and sign the image, let's check, whether we have # everything we need # It appears, that flags option in BD file are irrelevant for 2.1 secure - # binary images. - if private_key_file_path is None or \ - signing_certificate_file_paths is None or \ - root_key_certificate_paths is None: - click.echo("error: Signed image requires private key with -s option, \ -one or more certificate(s) using -S option and one or more root key \ -certificates using -R option") + # binary images regarding encryption/signing - SB 2.1 must be encrypted + # and signed. + # However, bit 15 represents, whether the final SB 2.1 must include a + # SHA-256 of the botable section. + flags = parsed_bd_file["options"].get( + "flags", BootImageV21.FLAGS_SHA_PRESENT_BIT | BootImageV21.FLAGS_ENCRYPTED_SIGNED_BIT + ) + if ( + private_key_file_path is None + or signing_certificate_file_paths is None + or root_key_certificate_paths is None + ): + click.echo( + "error: Signed image requires private key with -s option, " + "one or more certificate(s) using -S option and one or more root key " + "certificates using -R option" + ) sys.exit(1) # Versions and build number are up to the user. If he doesn't provide any, @@ -230,7 +383,7 @@ def generate_secure_binary_21( # be definitely reported to tell the user, what kind of key is being # used click.echo("warning: no KEK key provided, using a zero KEK key") - sb_kek = bytes.fromhex('0' * 64) + sb_kek = bytes.fromhex("0" * 64) else: with open(str(key_file_path)) as kek_key_file: # TODO maybe we should validate the key length and content, to make @@ -286,15 +439,17 @@ def generate_secure_binary_21( *sb_sections, product_version=product_version, component_version=component_version, - build_number=build_number) + build_number=build_number, + flags=flags, + ) # create certificate block cert_block = CertBlockV2(build_number=build_number) - for cert_file_path in signing_certificate_file_paths: - cert_data = load_binary(str(cert_file_path)) + for cert_path in signing_certificate_file_paths: + cert_data = load_certificate_as_bytes(str(cert_path)) cert_block.add_certificate(cert_data) for cert_idx, cert_path in enumerate(root_key_certificate_paths): - cert_data = load_binary(str(cert_path)) + cert_data = load_certificate_as_bytes(str(cert_path)) cert_block.set_root_key_hash(cert_idx, Certificate(cert_data)) # We have our secure binary, now we attach to it the certificate block and @@ -315,9 +470,15 @@ def generate_secure_binary_21( with open(str(output_file_path), "wb") as sb_file_output: sb_file_output.write(secure_binary.export()) + click.echo(f"Success. (Secure binary 2.1: {output_file_path} created.)") + -def generate_secure_binary(container_conf: click.File) -> None: - """Geneate SecureBinary image from json configuration file.""" +def generate_secure_binary_31(container_conf: click.File) -> None: + """Geneate SecureBinary image from json configuration file. + + :param container_conf: configuration file + :raises SPSDKError: Raised when there is no signing key + """ config_data = json.load(container_conf) config = elftosb_helper.SB31Config(config_data) timestamp = config.timestamp @@ -347,7 +508,7 @@ def generate_secure_binary(container_conf: click.File) -> None: commands_data = sb_cmd_block.export() # CERTIFICATE BLOCK - cert_block = _get_cert_block_v31(config) + cert_block = _get_cert_block_v31(config.certificate_block_config) data_cb = cert_block.export() # SB FILE HEADER @@ -370,6 +531,8 @@ def generate_secure_binary(container_conf: click.File) -> None: final_data += data_cb # SIGNATURE + if not config.main_signing_key: + raise SPSDKError("There is no signing key") assert isinstance(config.main_signing_key, str) private_key_data = load_binary(config.main_signing_key) data_to_sign = final_data @@ -380,107 +543,80 @@ def generate_secure_binary(container_conf: click.File) -> None: write_file(final_data, config.container_output, mode="wb") -handlers = { - "command": generate_secure_binary_21, - "image_conf": generate_master_boot_image, - "container_conf": generate_secure_binary, - "tzm_conf": generate_trustzone_binary -} + click.echo(f"Success. (Secure binary 3.1: {config.container_output} created.)") -@click.command() + +@click.command(no_args_is_help=True) # type: ignore @click.option( - '-f', '--chip-family', - default='lpc55s3x', - help="Select the chip family (default is lpc55s3x)" -) -@optgroup.group( - "Output file type generation selection.", - cls=RequiredMutuallyExclusiveOptionGroup + "-f", + "--chip-family", + default="lpc55s3x", + help="Select the chip family (default is lpc55s3x)", + type=click.Choice(SUPPORTED_FAMILIES, case_sensitive=False), ) +@optgroup.group("Output file type generation selection.", cls=RequiredMutuallyExclusiveOptionGroup) @optgroup.option( - '-c', - '--command', + "-c", + "--command", type=click.Path(exists=True), - help="BD configuration file to produce secure binary v2.x" + help="BD configuration file to produce secure binary v2.x", ) @optgroup.option( - '-J', - '--image-conf', - type=click.File('r'), - help="Json image configuration file to produce master boot image" + "-J", + "--image-conf", + type=click.File("r"), + help="Json image configuration file to produce master boot image", ) @optgroup.option( - '-j', - '--container-conf', - type=click.File('r'), - help="json container configuration file to produce secure binary v3.x" + "-j", + "--container-conf", + type=click.File("r"), + help="json container configuration file to produce secure binary v3.x", ) @optgroup.option( - '-T', - '--tzm-conf', - type=click.File('r'), - help="json trust zone configuration file to produce trust zone binary" -) -@optgroup.group( - "Command file options" + "-T", + "--tzm-conf", + type=click.File("r"), + help="json trust zone configuration file to produce trust zone binary", ) +@optgroup.group("Command file options") +@optgroup.option("-o", "--output", type=click.Path(), help="Output file path.") @optgroup.option( - '-o', - '--output', - type=click.Path(), - help="Output file path." + "-k", "--key", type=click.Path(exists=True), help="Add a key file and enable encryption." ) @optgroup.option( - '-k', - '--key', - type=click.Path(exists=True), - help="Add a key file and enable encryption." + "-s", "--pkey", type=click.Path(exists=True), help="Path to private key for signing." ) @optgroup.option( - '-s', - '--pkey', - type=click.Path(exists=True), - help="Path to private key for signing." -) -@optgroup.option( - '-S', - '--cert', + "-S", + "--cert", type=click.Path(exists=True), multiple=True, help="Path to certificate files for signing. The first certificate will be \ -the self signed root key certificate." +the self signed root key certificate.", ) @optgroup.option( - '-R', - '--root-key-cert', + "-R", + "--root-key-cert", type=click.Path(exists=True), multiple=True, help="Path to root key certificate file(s) for verifying other certificates. \ Only 4 root key certificates are allowed, others are ignored. \ One of the certificates must match the first certificate passed \ -with -S/--cert arg." +with -S/--cert arg.", ) @optgroup.option( - '-h', - '--hash-of-hashes', + "-h", + "--hash-of-hashes", type=click.Path(), help="Path to output hash of hashes of root keys. If argument is not \ -provided, then by default the tool creates hash.bin in the working directory." -) -@click.version_option( - spsdk_version, - '-v', - '--version' -) -@click.help_option( - '--help' +provided, then by default the tool creates hash.bin in the working directory.", ) -@click.argument( - 'external', - type=click.Path(), - nargs=-1 -) -def main(chip_family: str, +@click.version_option(spsdk_version, "-v", "--version") +@click.help_option("--help") +@click.argument("external", type=click.Path(), nargs=-1) +def main( + chip_family: str, command: click.Path, output: click.Path, key: click.Path, @@ -491,7 +627,8 @@ def main(chip_family: str, container_conf: click.File, tzm_conf: click.File, hash_of_hashes: click.Path, - external: List[click.Path]) -> None: + external: List[click.Path], +) -> None: """Tool for generating TrustZone, MasterBootImage and SecureBinary images.""" if command: if output is None: @@ -507,7 +644,7 @@ def main(chip_family: str, signing_certificate_file_paths=cert, root_key_certificate_paths=root_key_cert, hoh_out_path=hash_of_hashes, - external_files=external + external_files=external, ) if chip_family not in SUPPORTED_FAMILIES: @@ -518,11 +655,12 @@ def main(chip_family: str, generate_master_boot_image(image_conf) if container_conf: - generate_secure_binary(container_conf) + generate_secure_binary_31(container_conf) if tzm_conf: generate_trustzone_binary(tzm_conf) + @catch_spsdk_error def safe_main() -> None: """Call the main function.""" diff --git a/spsdk/apps/elftosb_utils/sb_21_helper.py b/spsdk/apps/elftosb_utils/sb_21_helper.py index cf007536..8d6a925d 100644 --- a/spsdk/apps/elftosb_utils/sb_21_helper.py +++ b/spsdk/apps/elftosb_utils/sb_21_helper.py @@ -11,10 +11,10 @@ from numbers import Number from typing import Callable, Dict, List, Optional -import spsdk import spsdk.sbfile.commands as commands import spsdk.utils.crypto as crypto import spsdk.utils.misc as misc +from spsdk import SPSDKError def get_command(cmd_name: str) -> Callable[[Dict], commands.CmdBaseClass]: @@ -50,7 +50,7 @@ def _load(cmd_args: dict) -> commands.CmdLoad: """Returns a CmdLoad object initialized based on cmd_args. :param cmd_args: dictionary holding path to file or values and address - :raises spsdk.SPSDKError: if dict doesn't contain 'file' or 'values' key + :raises SPSDKError: If dict doesn't contain 'file' or 'values' key :return: CmdLoad object """ address = cmd_args["address"] @@ -62,7 +62,7 @@ def _load(cmd_args: dict) -> commands.CmdLoad: values = [int(s, 16) for s in cmd_args["values"].split(",")] data = struct.pack(f"<{len(values)}L", *values) return commands.CmdLoad(address=address, data=data) - raise spsdk.SPSDKError(f"Unsupported LOAD command args: {cmd_args}") + raise SPSDKError(f"Unsupported LOAD command args: {cmd_args}") def _erase_cmd_handler(cmd_args: dict) -> commands.CmdErase: @@ -102,11 +102,11 @@ def _encrypt(cmd_args: dict) -> commands.CmdLoad: } :param cmd_args: dictionary holding list of keyblobs, keyblob ID and load dict - :raises SPSDKError: if keyblob to be used is not in the list or is invalid + :raises SPSDKError: If keyblob to be used is not in the list or is invalid :return: CmdLoad object """ - keyblobs = cmd_args.get("keyblobs", []) keyblob_id = cmd_args["keyblob_id"] + keyblobs = cmd_args.get("keyblobs", []) load_dict = cmd_args.get("load", {}) address = load_dict["address"] @@ -119,11 +119,11 @@ def _encrypt(cmd_args: dict) -> commands.CmdLoad: try: valid_keyblob = _validate_keyblob(keyblobs, keyblob_id) - except spsdk.SPSDKError: + except SPSDKError: raise if valid_keyblob is None: - raise spsdk.SPSDKError(f"Missing keyblob {keyblob_id} for encryption.") + raise SPSDKError(f"Missing keyblob {keyblob_id} for encryption.") start_addr = valid_keyblob["keyblob_content"][0]["start"] end_addr = valid_keyblob["keyblob_content"][0]["end"] @@ -150,7 +150,7 @@ def _keywrap(cmd_args: dict) -> commands.CmdLoad: } :param cmd_args: dictionary holding list of keyblobs, keyblob ID and load dict - :raises SPSDKError: if keyblob to be used is not in the list or is invalid + :raises SPSDKError: If keyblob to be used is not in the list or is invalid :return: CmdLoad object """ # iterate over keyblobs @@ -163,10 +163,10 @@ def _keywrap(cmd_args: dict) -> commands.CmdLoad: try: valid_keyblob = _validate_keyblob(keyblobs, keyblob_id) - except spsdk.SPSDKError: + except SPSDKError: raise if valid_keyblob is None: - raise spsdk.SPSDKError(f"Missing keyblob {keyblob_id} for given keywrap") + raise SPSDKError(f"Missing keyblob {keyblob_id} for given keywrap") start_addr = valid_keyblob["keyblob_content"][0]["start"] end_addr = valid_keyblob["keyblob_content"][0]["end"] @@ -225,19 +225,19 @@ def _validate_keyblob(keyblobs: List, keyblob_id: Number) -> Optional[Dict]: :param keyblobs: list of dicts defining keyblobs :param keyblob_id: id of keyblob we want to check - :raises SPSDKError: if the keyblob definition is empty - :raises SPSDKError: if the kyeblob definition is missing one key - :return: keyblob if exists and is valid, None otherwise + :raises SPSDKError: If the keyblob definition is empty + :raises SPSDKError: If the kyeblob definition is missing one key + :return: keyblob If exists and is valid, None otherwise """ for kb in keyblobs: if keyblob_id == kb["keyblob_id"]: kb_content = kb["keyblob_content"] if len(kb_content) == 0: - raise spsdk.SPSDKError(f"Keyblob {keyblob_id} definition is empty!") + raise SPSDKError(f"Keyblob {keyblob_id} definition is empty!") for key in ["start", "end", "key", "counter"]: if key not in kb_content[0]: - raise spsdk.SPSDKError(f"Keyblob {keyblob_id} is missing '{key}' definition!") + raise SPSDKError(f"Keyblob {keyblob_id} is missing '{key}' definition!") return kb else: diff --git a/spsdk/apps/elftosb_utils/sb_31_helper.py b/spsdk/apps/elftosb_utils/sb_31_helper.py index 321ac67f..4800219f 100644 --- a/spsdk/apps/elftosb_utils/sb_31_helper.py +++ b/spsdk/apps/elftosb_utils/sb_31_helper.py @@ -8,10 +8,13 @@ """Module for parsing original elf2sb configuration files.""" # pylint: disable=too-few-public-methods,too-many-instance-attributes +import abc +import re import struct -from typing import List +from typing import Iterable, List, Union from spsdk import SPSDKError +from spsdk.image.mbimg import MasterBootImageType from spsdk.sbfile.sb31 import commands from spsdk.utils.misc import load_binary @@ -22,11 +25,22 @@ class RootOfTrustInfo: def __init__(self, data: dict) -> None: """Create object out of data loaded from elf2sb configuration file.""" self.config_data = data - self.private_key = data["mainCertPrivateKeyFile"] + self.private_key = data.get("mainCertPrivateKeyFile") or data.get( + "mainRootCertPrivateKeyFile" + ) + if not self.private_key: + raise SPSDKError( + "Private key not specified (mainCertPrivateKeyFile or mainRootCertPrivateKeyFile)" + ) self.public_keys = [data.get(f"rootCertificate{idx}File") for idx in range(4)] # filter out None and empty values self.public_keys = list(filter(None, self.public_keys)) - self.public_key_index = self.config_data["mainCertChainId"] + # look for keyID; can't use "or" because 0 is a valid number although it's a "falsy" value + self.public_key_index = data.get("mainCertChainId") + if self.public_key_index is None: + self.public_key_index = data.get("mainRootCertId") + if self.public_key_index is None: + raise SPSDKError("Main Cert ID not specified (mainCertChainId or mainRootCertId)") class TrustZoneConfig: @@ -40,15 +54,63 @@ def __init__(self, config_data: dict) -> None: self.presets = config_data["trustZonePreset"] -class CertificateBlockConfig: - """Configuration object for Certificate block.""" +class CertificateBlockConfig(abc.ABC): + """Abstract class for certificate block configurations. - def __init__(self, config_data: dict) -> None: - """Initialize CertificateBlockConfig from json config data.""" - self.root_certificate_0_file = config_data.get("rootCertificate0File") - self.root_certificate_1_file = config_data.get("rootCertificate1File") - self.root_certificate_2_file = config_data.get("rootCertificate2File") - self.root_certificate_3_file = config_data.get("rootCertificate3File") + This class serves as an interface class for it's children. At the moment + there is no special functionality required, but this may change in the future. + """ + + pass + + +class CertificateBlockConfigGroup3(CertificateBlockConfig): + """Certificate block configuration options for LPC55xx (LPC552x, LPC55S2x & LPC55S6x), LPC55S1x, RT5xx & RT6xx.""" + + def __init__(self, config: dict) -> None: + """Initialize the options based on `config`. + + Consult the configuration data with master boot image configuration + options as defined in elftosb used guide. + + :param config: group 3 devices configuration options from json file. + """ + self.image_build_number = int(config.get("imageBuildNumber", 0), 0) + self.root_certificates: List[List[str]] = [[] for _ in range(4)] + # TODO we need to read the whole chain from the dict for a given + # selection based on mainCertPrivateKeyFile!!! + self.root_certificates[0].append(config.get("rootCertificate0File", None)) + self.root_certificates[1].append(config.get("rootCertificate1File", None)) + self.root_certificates[2].append(config.get("rootCertificate2File", None)) + self.root_certificates[3].append(config.get("rootCertificate3File", None)) + self.main_cert_chain_id = config.get("mainCertChainId", 0) + + # get all certificate chain related keys from config + pattern = f"chainCertificate{self.main_cert_chain_id}File[0-3]" + keys = [key for key in config.keys() if re.fullmatch(pattern, key)] + # just in case, sort the chain certificate keys in order + keys.sort() + for key in keys: + self.root_certificates[self.main_cert_chain_id].append(config[key]) + + self.main_cert_private_key_file = config.get("mainCertPrivateKeyFile", "") + + +class CertificateBlockConfigGroup4(CertificateBlockConfig): + """Certificate block configuration options for LPC55S3x.""" + + def __init__(self, config: dict) -> None: + """Initialize options based on `config`. + + Consult the configuration data with master boot image configuration + options as defined in elftosb used guide. + + :param config: group 4 devices configuration options from json file. + """ + self.root_certificate_0_file = config.get("rootCertificate0File") + self.root_certificate_1_file = config.get("rootCertificate1File") + self.root_certificate_2_file = config.get("rootCertificate2File") + self.root_certificate_3_file = config.get("rootCertificate3File") self.root_certs = [ self.root_certificate_0_file, self.root_certificate_1_file, @@ -56,52 +118,305 @@ def __init__(self, config_data: dict) -> None: self.root_certificate_3_file, ] self.root_certs = [item for item in self.root_certs if item] - self.root_certificate_curve = config_data.get("rootCertificateEllipticCurve") - self.main_root_cert_id = config_data.get("mainRootCertId", 0) - self.main_root_private_key_file = config_data.get("mainRootCertPrivateKeyFile") - self.use_isk = config_data.get("useIsk", False) - self.isk_certificate = config_data.get("signingCertificateFile") - self.isk_private_key_file = config_data.get("signingCertificatePrivateKeyFile") - self.isk_constraint = int(config_data.get("signingCertificateConstraint", "0"), 0) - self.isk_certificate_curve = config_data.get("iskCertificateEllipticCurve") - self.isk_sign_data_path = config_data.get("signCertData") - - self.main_signing_key = self.main_root_private_key_file - self.main_curve_name = self.root_certificate_curve - if self.use_isk: - self.main_signing_key = self.isk_private_key_file - self.main_curve_name = self.isk_certificate_curve - - -class MasterBootImageConfig(CertificateBlockConfig): - """Configuration object for MasterBootImage.""" + self.root_certificate_curve = config.get("rootCertificateEllipticCurve") + self.main_root_cert_id = config.get("mainRootCertId", 0) + self.main_root_private_key_file = config.get("mainRootCertPrivateKeyFile") + self.use_isk = config.get("useIsk", False) + self.isk_certificate = config.get("signingCertificateFile") + self.isk_private_key_file = config.get("signingCertificatePrivateKeyFile") + self.isk_constraint = int(config.get("signingCertificateConstraint", "0"), 0) + self.isk_certificate_curve = config.get("iskCertificateEllipticCurve") + self.isk_sign_data_path = config.get("signCertData") - def __init__(self, config_data: dict) -> None: - """Initialize MasterBootImageConfig from json config data.""" - super().__init__(config_data) - self.family = config_data["family"] - self.revision = config_data.get("revision") - self.input_image_file = config_data["inputImageFile"] - self.output_image_exec_address = int(config_data["outputImageExecutionAddress"], 0) - self.output_image_exec_target = config_data.get("outputImageExecutionTarget") - self.output_image_auth_type = config_data.get("outputImageAuthenticationType") - self.output_image_subtype = config_data.get("outputImageSubtype", "default") - self.trustzone_preset_file = config_data.get("trustZonePresetFile") - self.is_dual_boot = config_data.get("isDualBootImageVersion", False) - self.dual_boot_version = config_data.get("dualBootImageVersion") - if self.is_dual_boot: - assert self.dual_boot_version - self.dual_boot_version = int(self.dual_boot_version, 0) - self.firmware_version = int(config_data.get("firmwareVersion", "1"), 0) - self.master_boot_output_file = config_data["masterBootOutputFile"] +class MasterBootImageConfig(abc.ABC): + """Abstract class for master boot image configuration options. + + This class defines the interface required by every child. + """ + + @staticmethod + @abc.abstractmethod + def _validate(config: dict) -> None: + """Device group specific validation method. + + :param config: configuration to be validated. + :raises SPSDKError: If the validation method has not been re-implemented. + """ + raise SPSDKError("Configuration validation is not implemented.") + + def _validate_post_init(self) -> None: + """Device group specific validation post __init__.""" + + @staticmethod + def _validate_item( + item_name: str, item_value: Union[bool, int, str], valid_values: Iterable + ) -> None: + if item_value not in valid_values: + raise SPSDKError( + f"Invalid {item_name}. " + f"Got: {item_value}, expected one of {valid_values}" + ) + + @property + def image_type(self) -> MasterBootImageType: + """Image type. -class SB31Config(CertificateBlockConfig): + :raises SPSDKError: If the image_type method has not been re-implemented. + """ + raise SPSDKError("image_type not implemented!") + + +class MasterBootImageConfigGroup3(MasterBootImageConfig): + """Configuration object for MasterBootImage.""" + + VALID_EXEC_TARGETS = ["ram", "xip"] + VALID_AUTH_TYPES = ["crc", "signed", "encrypted"] + + def __init__(self, config: dict) -> None: + """Initializes the object. + + :param config: master boot image configuration data. Consult the content with elftosb user guide. + """ + self._validate(config) + self.family = config["family"] + self.input_image_file = config["inputImageFile"] + execution_address = int( + config.get("imageLinkAddress") or config["outputImageExecutionAddress"], 0 + ) + self.output_image_exec_address = execution_address + self.output_image_exec_target = config["outputImageExecutionTarget"].lower() + if "xip" in self.output_image_exec_target: + self.output_image_exec_target = "xip" + self.output_image_auth_type = config["outputImageAuthenticationType"].lower() + authentication_type = self.output_image_auth_type + if "signed" in self.output_image_auth_type: + authentication_type = "signed" + if "encrypted" in self.output_image_auth_type: + authentication_type = "encrypted" + self.output_image_auth_type = authentication_type + self.output_image_encryption_key_file = config.get("outputImageEncryptionKeyFile", None) + self.enable_trustzone = config.get("enableTrustZone", False) + self.trustzone_preset_file = config.get("trustZonePresetFile", "") + self.device_key_source = config.get("deviceKeySource", "") + self.use_key_store = config.get("useKeyStore", False) + self.key_store_file = config.get("keyStoreFile", "") + self.enable_hw_user_mode_keys = config.get("enableHwUserModeKeys", None) + self.master_boot_output_file = config["masterBootOutputFile"] + self.certificate_block_config = CertificateBlockConfigGroup3(config) + self._validate_post_init() + + def _validate_post_init(self) -> None: + self._validate_item( + "outputImageExecutionTarget", self.output_image_exec_target, self.VALID_EXEC_TARGETS + ) + self._validate_item( + "outputImageAuthenticationType", self.output_image_auth_type, self.VALID_AUTH_TYPES + ) + + @staticmethod + def _validate(config: dict) -> None: + """Validates the json configuration file for group 3 devices. + + :param config: configuration dictionary for group 3 devices. + :raises SPSDKError: if invalid MCU family identifier provided + :raises SPSDKError: if execution target is RAM, but should be XIP + :raises SPSDKError: if `deviceKeySource` option is set for devices other then RT5xx/RT6xx + :raises SPSDKError: if `useKeyStore` is set for devices other then RT5xx/RT6xx + :raises SPSDKError: if `enableHwUserModeKeys` set to true on lpc55xx, lpc552x, lpc55s2x, lpc55s6x + """ + family = config.get("family", "NA").lower() + families = [ + "lpc55xx", + "lpc55s0x", + "lpc55s1x", + "lpc552x", + "lpc55s2x", + "lpc55s6x", + "rt5xx", + "rt6xx", + ] + if family not in families: + raise SPSDKError( + f'Invalid family. Expected one of "{", ".join(families)}", got {family}' + ) + + output_image_exec_target = config["outputImageExecutionTarget"].lower() + if family in ["lpc55xx", "lpc55s0x", "lpc55s1x", "lpc552x", "lpc55s2x", "lpc55s6x"]: + if "ram" == output_image_exec_target: + raise SPSDKError( + desc=( + f'Unsupported value "{output_image_exec_target}" of ' + f'"outputImageExecutionTarget" for selected family "{family}".' + f'Expected values for "{family}" ["XIP"].' + ) + ) + + # It would be convenient to handle this in the Master Boot Image generation, + # however, this would lead to validating the json parameters at different places, + # which is a bit unconvenient. I would like to have the validation at one place. + if config.get("deviceKeySource", None): + raise SPSDKError( + '"deviceKeySource" option is allowed only for RT5xx & RT6xx devices.' + ) + if config.get("useKeyStore", None): + raise SPSDKError('"useKeyStore" option is allowed only for RT5xx & RT6xx devices.') + + enable_hw_user_mode_keys = config["enableHwUserModeKeys"] + if family in ["lpc55xx", "lpc552x", "lpc55s2x", "lpc55s6x"]: + if True == enable_hw_user_mode_keys: + raise SPSDKError( + desc=( + f'Unsupported value "{enable_hw_user_mode_keys}" of ' + f'"enableHwUserModeKeys" for selected family "{family}".' + f'Expected values for "{family}" ["False"].' + ) + ) + + # If the image is signed, we need to provide up to 4 Root of Turst + # certificates (certificate chains), where one out of these will be + # used for signing the image. The used certificate chain is selected + # with the "mainCertChainId" parameter. The rest of RoT certificates + # are used to compute a hash, which is stored in the device for + # future use. + # From this perspective we don't need to define all certificate chains + # just the one used for signing and it's corresponding private key. + # This is defined by the "mainCertPrivateKeyFile" parameter. + # If multiple certificate chains are used, only the signing certificate + # chain is read, the rest is not taken into consideration (except the + # root certificate for hash computation). + # It seems that multiple certificate chains are useless, but this + # provides convenience when switching to different signing chain, as + # it's sufficient to change the private key and set different number. + # The number of certificates in a chain is unlimited, but as the whole + # certificate chain is stored into memory, the memory limits the + # number of certificates. + # TODO validate configurations expecting signed image, that they provide + # certificates etc.??? + + @property + def image_type(self) -> MasterBootImageType: + """Image type. + + The image type is encoded by the way how images are authenticated together + with from where the image is executed. Not all configurations of + authentication and storage are however valid. + + :raises SPSDKError: invalid authentication & execution target. + + :return: image type. + """ + image_types = { + "plain-ram": MasterBootImageType.PLAIN_IMAGE, + "plain-xip": MasterBootImageType.PLAIN_IMAGE, + "crc-ram": MasterBootImageType.CRC_RAM_IMAGE, + "crc-xip": MasterBootImageType.CRC_XIP_IMAGE, + "signed-ram": MasterBootImageType.SIGNED_RAM_IMAGE, + "signed-xip": MasterBootImageType.SIGNED_XIP_IMAGE, + "encrypted-ram": MasterBootImageType.ENCRYPTED_RAM_IMAGE, + } + image_type = f"{self.output_image_auth_type}-{self.output_image_exec_target}" + + try: + retval = image_types[image_type.lower()] + except KeyError as e: + raise SPSDKError( + ( + f"Unsupported combination of inputs: \n" + f"outputImageAuthenticationType: {self.output_image_auth_type}, " + f"outputImageExecutionTarget: {self.output_image_exec_target}" + ) + ) from e + + return retval + + +class MasterBootImageConfigGroup4(MasterBootImageConfig): + """Configuration object for MasterBootImage.""" + + VALID_EXEC_TARGETS = ["ram", "xip"] + VALID_AUTH_TYPES = ["crc", "signed", "encrypted"] + + def __init__(self, config: dict) -> None: + """Initializes the object. + + :param config: master boot image configuration data. Consult the content with elftosb user guide. + """ + self._validate(config) + self.family = config["family"] + self.revision = config.get("revision") + self.input_image_file = config["inputImageFile"] + self.output_image_exec_address = int(config["outputImageExecutionAddress"], 0) + self.output_image_exec_target = config["outputImageExecutionTarget"].lower() + if "xip" in self.output_image_exec_target: + self.output_image_exec_target = "xip" + self.output_image_auth_type = config["outputImageAuthenticationType"].lower() + authentication_type = self.output_image_auth_type + if "signed" in self.output_image_auth_type: + authentication_type = "signed" + if "encrypted" in self.output_image_auth_type: + authentication_type = "encrypted" + self.output_image_auth_type = authentication_type + self.output_image_subtype = config.get("outputImageSubtype", "default") + self.trustzone_preset_file = config.get("trustZonePresetFile", "") + self.firmware_version = int(config.get("firmwareVersion", "0"), 0) + self.master_boot_output_file = config["masterBootOutputFile"] + self.certificate_block_config = CertificateBlockConfigGroup4(config) + self._validate_post_init() + + def _validate_post_init(self) -> None: + self._validate_item( + "outputImageExecutionTarget", self.output_image_exec_target, self.VALID_EXEC_TARGETS + ) + self._validate_item( + "outputImageAuthenticationType", self.output_image_auth_type, self.VALID_AUTH_TYPES + ) + + @staticmethod + def _validate(config: dict) -> None: + """Verifies the Master Boot Image parameters validity.""" + pass + + @property + def image_type(self) -> MasterBootImageType: + """Image type. + + The image type is encoded by the way how images are authenticated together + with from where the image is executed. Not all configurations of + authentication and storage are however valid. + + :raises SPSDKError: + + :return: image type. + """ + image_types = { + "plain-ram": MasterBootImageType.PLAIN_IMAGE, + "plain-xip": MasterBootImageType.PLAIN_IMAGE, + "crc-ram": MasterBootImageType.CRC_RAM_IMAGE, + "crc-xip": MasterBootImageType.CRC_XIP_IMAGE, + "signed-ram": MasterBootImageType.SIGNED_RAM_IMAGE, + "signed-xip": MasterBootImageType.SIGNED_XIP_IMAGE, + "encrypted-ram": MasterBootImageType.ENCRYPTED_RAM_IMAGE, + } + image_type = f"{self.output_image_auth_type}-{self.output_image_exec_target}" + + try: + retval = image_types[image_type] + except KeyError as e: + raise SPSDKError( + ( + f"Unsupported combination of inputs: \n" + f"outputImageAuthenticationType: {self.output_image_auth_type}, " + f"outputImageExecutionTarget: {self.output_image_exec_target}" + ) + ) from e + return retval + + +class SB31Config: """Configuration object for SecureBinary image.""" def __init__(self, config_data: dict) -> None: """Initialize SB31Config from json config data.""" - super().__init__(config_data) self.family = config_data["family"] self.revision = config_data.get("revision") self.container_keyblob_enc_key_path = config_data.get("containerKeyBlobEncryptionKey") @@ -116,6 +431,15 @@ def __init__(self, config_data: dict) -> None: self.is_encrypted = config_data.get("isEncrypted", True) self.timestamp = config_data.get("timestamp") + self.certificate_block_config = CertificateBlockConfigGroup4(config_data) + + self.main_signing_key = self.certificate_block_config.main_root_private_key_file + self.main_curve_name = self.certificate_block_config.root_certificate_curve + # if use_isk is set, we use for signing the ISK certificate instead of root + if self.certificate_block_config.use_isk: + self.main_signing_key = self.certificate_block_config.isk_private_key_file + self.main_curve_name = self.certificate_block_config.isk_certificate_curve + def _erase_cmd_handler(cmd_args: dict) -> commands.CmdErase: address = int(cmd_args["address"], 0) diff --git a/spsdk/apps/elftosb_utils/sly_bd_lexer.py b/spsdk/apps/elftosb_utils/sly_bd_lexer.py index f0f88ced..3c9464ab 100644 --- a/spsdk/apps/elftosb_utils/sly_bd_lexer.py +++ b/spsdk/apps/elftosb_utils/sly_bd_lexer.py @@ -82,8 +82,8 @@ def add_source(self, source: Variable) -> None: "ifr": "IFR", "enable": "ENABLE", "keywrap": "KEYWRAP", - "keystore_to_nv" : "KEYSTORE_TO_NV", - "keystore_from_nv" : "KEYSTORE_FROM_NV", + "keystore_to_nv": "KEYSTORE_TO_NV", + "keystore_from_nv": "KEYSTORE_FROM_NV", "all": "ALL", "no": "NO", "options": "OPTIONS", @@ -152,7 +152,7 @@ def add_source(self, source: Variable) -> None: "DOLLAR", ] + list(reserved.values()) - literals = {'@'} + literals = {"@"} # A regular expression rules with some action code # The order of these functions MATTER!!! Make sure you know what you are diff --git a/spsdk/apps/elftosb_utils/sly_bd_parser.py b/spsdk/apps/elftosb_utils/sly_bd_parser.py index 94d2ddca..077703e2 100644 --- a/spsdk/apps/elftosb_utils/sly_bd_parser.py +++ b/spsdk/apps/elftosb_utils/sly_bd_parser.py @@ -15,12 +15,12 @@ from sly.lex import Token from sly.yacc import YaccProduction -import spsdk import spsdk.apps.elftosb_utils.sly_bd_lexer as bd_lexer +from spsdk import SPSDKError # pylint: disable=too-many-public-methods,too-many-lines -# too-many-public-methods : every method in the parser represents a sytnax rule, +# too-many-public-methods : every method in the parser represents a syntax rule, # this is necessary and thus can't be omitted. From this perspective this check # is disabled. # too-many-lines : the class can't be shortened, as all the methods represent @@ -42,11 +42,11 @@ class BDParser(Parser): # Uncomment this line to output parser debug file # debugfile = "parser.out" - log = logging.getLogger("bd_parser") + log = logging.getLogger(__name__) log.setLevel(logging.ERROR) def __init__(self) -> None: - """Initiliazation method.""" + """Initialization method.""" super().__init__() self._variables: List[bd_lexer.Variable] = [] self._sources: List[bd_lexer.Variable] = [] @@ -249,7 +249,7 @@ def constants_block(self, token: YaccProduction) -> Dict: # type: ignore For now, we don't store the constants in the final bd file. :param token: object holding the content defined in decorator. - :return: dictionary holding the content of constatns block. + :return: dictionary holding the content of constants block. """ dictionary: Dict = {} return dictionary @@ -267,7 +267,7 @@ def constant_def(self, token: YaccProduction) -> Dict: # type: ignore """Parser rule. :param token: object holding the content defined in decorator. - :return: dictionary holding empty constatn definition. + :return: dictionary holding empty constant definition. """ return token.empty @@ -414,7 +414,7 @@ def keyblob_block(self, token: YaccProduction) -> Dict: # type: ignore # def keyblob_options_list(self, token): # # After discussion with Lukas Zajac we will ignore empty definitions in keyblob # # It's not clear, whether this has some effect on the final sb file or not. - # # C++ elftosb implemenation is able to parse the file even without empty + # # C++ elftosb implementation is able to parse the file even without empty # # parenthesis # return token.empty @@ -688,7 +688,7 @@ def load_stmt(self, token: YaccProduction) -> Dict: # type: ignore :param token: object holding the content defined in decorator. :return: dictionary hodling the content of a load statement. """ - if token.load_data.get("pattern"): + if token.load_data.get("pattern") is not None: cmd = "fill" else: cmd = "load" @@ -732,9 +732,9 @@ def load_data(self, token: YaccProduction) -> Dict: # type: ignore if token.SOURCE_NAME == source.name: return {"file": source.value} - # with current implemenation, this code won't be ever reached. In case + # with current implementation, this code won't be ever reached. In case # a not defined source file is used as `load_data`, the parser detects - # it as a diffeerent rule: + # it as a different rule: # # load_data ::= int_const_expr # @@ -845,7 +845,7 @@ def enable_stmt(self, token: YaccProduction) -> Dict: # type: ignore :param token: object holding the content defined in decorator. :return: dictionary holding the content of enable statement. """ - dictionary: Dict = {token.ENABLE : {}} + dictionary: Dict = {token.ENABLE: {}} dictionary[token.ENABLE].update(token.mem_opt) dictionary[token.ENABLE]["address"] = token.int_const_expr return dictionary @@ -1107,61 +1107,59 @@ def message_type(self, token: YaccProduction) -> Dict: # type: ignore dictionary: Dict = {} return dictionary - @_("KEYSTORE_TO_NV mem_opt address_or_range") # type: ignore - def keystore_stmt(self, token: YaccProduction) -> Dict: # type: ignore + @_("KEYSTORE_TO_NV mem_opt address_or_range") # type: ignore + def keystore_stmt(self, token: YaccProduction) -> Dict: # type: ignore """Parser rule. :param token: object holding the content defined in decorator. :return: dictionary holding the content keystore statmenet. """ - dictionary = {token.KEYSTORE_TO_NV : {}} + dictionary = {token.KEYSTORE_TO_NV: {}} dictionary[token.KEYSTORE_TO_NV].update(token.mem_opt) dictionary[token.KEYSTORE_TO_NV].update(token.address_or_range) return dictionary - @_("KEYSTORE_FROM_NV mem_opt address_or_range") # type: ignore - def keystore_stmt(self, token: YaccProduction) -> Dict: # type: ignore + @_("KEYSTORE_FROM_NV mem_opt address_or_range") # type: ignore + def keystore_stmt(self, token: YaccProduction) -> Dict: # type: ignore """Parser rule. :param token: object holding the content defined in decorator. - :return: dictionary holding the content keystore statmenet. + :return: dictionary holding the content keystore statement. """ - dictionary = {token.KEYSTORE_FROM_NV : {}} + dictionary = {token.KEYSTORE_FROM_NV: {}} dictionary[token.KEYSTORE_FROM_NV].update(token.mem_opt) dictionary[token.KEYSTORE_FROM_NV].update(token.address_or_range) return dictionary - @_("IDENT") # type: ignore - def mem_opt(self, token: YaccProduction) -> None: # type: ignore + @_("IDENT") # type: ignore + def mem_opt(self, token: YaccProduction) -> None: # type: ignore """Parser rule. Unsupported syntax right now. :param token: object holding the content defined in decorator. - :return: dictionary holding the content of identifier. """ # search in variables for token.IDENT variable and get it's value self.error(token, ": identifier as memory option is not supported.") - @_("'@' int_const_expr") # type: ignore - def mem_opt(self, token: YaccProduction) -> Dict: # type: ignore + @_("'@' int_const_expr") # type: ignore + def mem_opt(self, token: YaccProduction) -> Dict: # type: ignore """Parser rule. :param token: object holding the content defined in decorator. :type token: :return: dictionary holding the content of memory type. """ - dictionary = {"mem_opt" : token.int_const_expr} + dictionary = {"mem_opt": token.int_const_expr} return dictionary - @_("empty") # type: ignore - def mem_opt(self, token: YaccProduction) -> None: # type: ignore + @_("empty") # type: ignore + def mem_opt(self, token: YaccProduction) -> None: # type: ignore """Parser rule. Unsupported syntax right now. :param token: object holding the content defined in decorator. - :return: content of empty production - empty dict. """ self.error(token, ": empty memory option is not supported.") @@ -1382,6 +1380,12 @@ def expr(self, token: YaccProduction) -> Number: # type: ignore :param token: object holding the content defined in decorator. :return: number stored under identifier. """ + # we need to convert the IDENT into a value stored under that identifier + # search the variables and check, whether there is a name of IDENT + for var in self._variables: + if var.name == token.IDENT: + return var.value + return token.IDENT @_("symbol_ref") # type: ignore @@ -1502,7 +1506,7 @@ def error(self, token: YaccProduction, msg: str = "") -> YaccProduction: :param token: object holding the content defined in decorator. :param msg: error message to use. - :raises spsdk.SPSDKError: raises error with 'msg' message. + :raises SPSDKError: Raises error with 'msg' message. """ self._parse_error = True @@ -1511,12 +1515,12 @@ def error(self, token: YaccProduction, msg: str = "") -> YaccProduction: if lineno != -1: column = BDParser._find_column(self._input, token) error_line = BDParser._find_line(self._input, lineno - 1) - raise spsdk.SPSDKError( + raise SPSDKError( f"bdcompiler:{lineno}:{column}: error{msg}\n\n{error_line}\n" + (column - 1) * " " + "^\n" ) else: - raise spsdk.SPSDKError(f"bdcompiler: error{msg}\n") + raise SPSDKError(f"bdcompiler: error{msg}\n") - raise spsdk.SPSDKError("bdcompiler: unspecified error.") + raise SPSDKError("bdcompiler: unspecified error.") diff --git a/spsdk/apps/nxpcertgen.py b/spsdk/apps/nxpcertgen.py index e8fcf0be..a7eb2ee9 100644 --- a/spsdk/apps/nxpcertgen.py +++ b/spsdk/apps/nxpcertgen.py @@ -6,15 +6,23 @@ # SPDX-License-Identifier: BSD-3-Clause """NXP Certificate Generator.""" -import json import logging +import os import sys +from typing import BinaryIO, TextIO import click +from spsdk import SPSDK_DATA_FOLDER, SPSDKError from spsdk import __version__ as spsdk_version -from spsdk.apps.utils import catch_spsdk_error +from spsdk.apps.utils import ( + catch_spsdk_error, + check_destination_dir, + check_file_exists, + load_configuration, +) from spsdk.crypto import ( + Encoding, generate_certificate, load_private_key, load_public_key, @@ -22,6 +30,8 @@ x509, ) +NXPCERTGEN_DATA_FOLDER: str = os.path.join(SPSDK_DATA_FOLDER, "nxpcertgen") + logger = logging.getLogger(__name__) @@ -29,48 +39,93 @@ class CertificateParametersConfig: """Configuration object for creating the certificate.""" def __init__(self, config_data: dict) -> None: - """Initialize cert_config from json config data.""" - self.issuer_private_key = config_data["issuer_private_key"] - self.subject_public_key = config_data["subject_public_key"] - self.serial_number = config_data["serial_number"] - self.duration = config_data["duration"] - self.BasicConstrains_ca = config_data["extensions"]["BASIC_CONSTRAINTS"]["ca"] - self.BasicConstrains_path_length = config_data["extensions"]["BASIC_CONSTRAINTS"][ - "path_length" - ] - self.issuer_name = generate_name(config_data["issuer"]) - self.subject_name = generate_name(config_data["subject"]) + """Initialize cert_config from yml config data.""" + try: + self.issuer_private_key = config_data["issuer_private_key"] + self.subject_public_key = config_data["subject_public_key"] + self.serial_number = config_data["serial_number"] + self.duration = config_data["duration"] + self.BasicConstrains_ca = config_data["extensions"]["BASIC_CONSTRAINTS"]["ca"] + self.BasicConstrains_path_length = config_data["extensions"]["BASIC_CONSTRAINTS"][ + "path_length" + ] + self.issuer_name = generate_name(config_data["issuer"]) + self.subject_name = generate_name(config_data["subject"]) + except KeyError as e: + raise SPSDKError(f"Error found in configuration: {e} not found") def generate_name(config_data: dict) -> x509.Name: """Set the issuer/subject distinguished attribute's.""" - attributes = [ - x509.NameAttribute(getattr(x509.NameOID, key), value) for key, value in config_data.items() - ] + attributes = [] + for key, value in config_data.items(): + if not hasattr(x509.NameOID, key): + raise SPSDKError(f"Invalid NameOID: {key}") + attributes.append(x509.NameAttribute(getattr(x509.NameOID, key), str(value))) return x509.Name(attributes) -@click.command() +@click.group(no_args_is_help=True) # type: ignore +@click.option( + "-v", + "--verbose", + "log_level", + flag_value=logging.INFO, + help="Prints more detailed information.", +) +@click.option( + "-d", + "--debug", + "log_level", + flag_value=logging.DEBUG, + help="Display more debugging info.", +) +@click.version_option(spsdk_version, "--version") +def main(log_level: int) -> None: + """Utility for certificate generation.""" + logging.basicConfig(level=log_level or logging.WARNING) + + +@main.command() @click.option( "-j", - "--json-conf", + "-c", + "--config", type=click.File("r"), - help="Path to json configuration file containing the parameters for certificate.", + required=True, + help="Path to yaml/json configuration file containing the parameters for certificate.", ) @click.option( - "-c", - "--cert-path", - type=click.Path(), + "-o", + "--output", + type=click.File(mode="wb"), + required=True, help="Path where certificate will be stored.", ) -@click.version_option(spsdk_version, "--version") -def main(json_conf: click.File, cert_path: str) -> None: - """Utility for certificate generation.""" +@click.option( + "-e", + "--encoding", + required=False, + type=click.Choice(["PEM", "DER"], case_sensitive=False), + default="PEM", + help="Encoding type. Default is PEM", +) +@click.option( + "--force", + is_flag=True, + default=False, + help="Force overwriting of an existing file. Create destination folder, if doesn't exist already.", +) +def generate(config: TextIO, output: BinaryIO, encoding: str, force: bool) -> None: + """Generate certificate.""" logger.info("Generating Certificate...") - logger.info("Loading configuration from json file...") + logger.info("Loading configuration from yml file...") - json_content = json.load(json_conf) # type: ignore - cert_config = CertificateParametersConfig(json_content) + check_destination_dir(output.name, force) + check_file_exists(output.name, force) + + config_data = load_configuration(config.name) + cert_config = CertificateParametersConfig(config_data) priv_key = load_private_key(cert_config.issuer_private_key) pub_key = load_public_key(cert_config.subject_public_key) @@ -86,8 +141,38 @@ def main(json_conf: click.File, cert_path: str) -> None: path_length=cert_config.BasicConstrains_path_length, ) logger.info("Saving the generated certificate to the specified path...") - save_crypto_item(certificate, cert_path) + encoding_type = Encoding.PEM if encoding.lower() == "pem" else Encoding.DER + save_crypto_item(certificate, output.name, encoding_type=encoding_type) logger.info("Certificate generated successfully...") + click.echo(f"The certificate file has been created: {os.path.abspath(output.name)}") + + +@main.command() +@click.argument("output", metavar="PATH", type=click.Path()) +@click.option( + "-f", + "--force", + is_flag=True, + default=False, + help="Force overwriting of an existing file. Create destination folder, if doesn't exist already.", +) +def get_cfg_template(output: click.Path, force: bool) -> None: + """Generate the template of Certificate generation YML configuration file. + + \b + PATH - file name path to write template config file + """ + logger.info("Creating Certificate template...") + check_destination_dir(str(output), force) + check_file_exists(str(output), force) + + with open(os.path.join(NXPCERTGEN_DATA_FOLDER, "certgen_config.yml"), "r") as file: + template = file.read() + + with open(str(output), "w") as file: + file.write(template) + + click.echo(f"The configuration template file has been created: {os.path.abspath(str(output))}") @catch_spsdk_error diff --git a/spsdk/apps/nxpdebugmbox.py b/spsdk/apps/nxpdebugmbox.py index e7e348d4..1f340d9d 100644 --- a/spsdk/apps/nxpdebugmbox.py +++ b/spsdk/apps/nxpdebugmbox.py @@ -14,6 +14,7 @@ import click +from spsdk import SPSDKError from spsdk import __version__ as spsdk_version from spsdk.apps.utils import INT, catch_spsdk_error from spsdk.dat import ( @@ -24,9 +25,8 @@ ) from spsdk.dat.debug_mailbox import DebugMailbox from spsdk.debuggers.utils import DebugProbeUtils, test_ahb_access -from spsdk.exceptions import SPSDKError -logger = logging.getLogger("DebugMBox") +logger = logging.getLogger(__name__) # pylint: disable=protected-access LOG_LEVEL_NAMES = [name.lower() for name in logging._nameToLevel] @@ -38,13 +38,14 @@ def _open_debugmbox(pass_obj: Dict) -> DebugMailbox: :param pass_obj: Input dictionary with arguments. :return: Active DebugMailbox object. - :raise SPSDKError: Raised with any kind of problems with debug probe. + :raises SPSDKError: Raised with any kind of problems with debug probe. """ interface = pass_obj["interface"] serial_no = pass_obj["serial_no"] debug_probe_params = pass_obj["debug_probe_params"] timing = pass_obj["timing"] reset = pass_obj["reset"] + operation_timeout = pass_obj["operation_timeout"] debug_probes = DebugProbeUtils.get_connected_probes( interface=interface, hardware_id=serial_no, user_params=debug_probe_params @@ -53,10 +54,12 @@ def _open_debugmbox(pass_obj: Dict) -> DebugMailbox: debug_probe = selected_probe.get_probe(debug_probe_params) debug_probe.open() - return DebugMailbox(debug_probe=debug_probe, reset=reset, moredelay=timing) + return DebugMailbox( + debug_probe=debug_probe, reset=reset, moredelay=timing, op_timeout=operation_timeout + ) -@click.group() +@click.group(no_args_is_help=True) @click.option("-i", "--interface") @click.option( "-p", @@ -73,7 +76,7 @@ def _open_debugmbox(pass_obj: Dict) -> DebugMailbox: "--debug", "log_level", metavar="LEVEL", - default="debug", + default="info", help=f"Set the level of system logging output. " f'Available options are: {", ".join(LOG_LEVEL_NAMES)}', type=click.Choice(LOG_LEVEL_NAMES), @@ -87,6 +90,13 @@ def _open_debugmbox(pass_obj: Dict) -> DebugMailbox: multiple=True, help="This option could be used " "multiply to setup non-standard option for debug probe.", ) +@click.option( + "--operation-timeout", + type=int, + default=4000, + help="Special option to change the standard operation timeout used" + " for communication with debugmailbox. Default value is 4000ms.", +) @click.version_option(spsdk_version, "-v", "--version") @click.help_option("--help") @click.pass_context @@ -99,6 +109,7 @@ def main( serial_no: str, debug_probe_option: List[str], reset: bool, + operation_timeout: int, ) -> int: """NXP Debug Mailbox Tool.""" logging.basicConfig(level=log_level.upper()) @@ -119,6 +130,7 @@ def main( "debug_probe_params": probe_user_params, "timing": timing, "reset": reset, + "operation_timeout": operation_timeout, } return 0 @@ -138,7 +150,8 @@ def auth(pass_obj: dict, beacon: int, certificate: str, key: str, force: bool) - with open(certificate, "rb") as f: debug_cred_data = f.read() debug_cred = DebugCredential.parse(debug_cred_data) - dac_data = dm_commands.DebugAuthenticationStart(dm=mail_box).run() + dac_rsp_len = 26 if debug_cred.HASH_LENGTH == 32 else 30 + dac_data = dm_commands.DebugAuthenticationStart(dm=mail_box, resplen=dac_rsp_len).run() # convert List[int] to bytes dac_data_bytes = struct.pack(f"<{len(dac_data)}I", *dac_data) dac = DebugAuthenticationChallenge.parse(dac_data_bytes) @@ -166,7 +179,9 @@ def auth(pass_obj: dict, beacon: int, certificate: str, key: str, force: bool) - mail_box.debug_probe.open() # Do test of access to AHB bus ahb_access_granted = test_ahb_access(mail_box.debug_probe) - logger.info(f"Debug Authentication ends {'successfully' if ahb_access_granted else 'without AHB access'}.") + logger.info( + f"Debug Authentication ends {'successfully' if ahb_access_granted else 'without AHB access'}." + ) except Exception as e: logger.error(f"Start Debug Mailbox failed!\n{e}") diff --git a/spsdk/apps/nxpdevhsm.py b/spsdk/apps/nxpdevhsm.py new file mode 100644 index 00000000..6837859c --- /dev/null +++ b/spsdk/apps/nxpdevhsm.py @@ -0,0 +1,688 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright 2021 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Module is used to generate initialization SB file.""" +import json +import logging +import os +import sys +from typing import BinaryIO, Callable, Dict, List, Optional, TextIO, Tuple + +import click +from click_option_group import MutuallyExclusiveOptionGroup, optgroup + +from spsdk import SPSDKError, SPSDKValueError +from spsdk import __version__ as version +from spsdk.apps.elftosb_utils.sb_31_helper import SB31Config, get_cmd_from_json +from spsdk.apps.utils import catch_spsdk_error, format_raw_data, get_interface +from spsdk.mboot.commands import TrustProvKeyType, TrustProvOemKeyType +from spsdk.mboot.interfaces import Interface as mbootInterface +from spsdk.mboot.mcuboot import McuBoot +from spsdk.sbfile.sb31.commands import BaseCmd, CmdLoadKeyBlob +from spsdk.sbfile.sb31.images import SecureBinary31Commands, SecureBinary31Header +from spsdk.utils.crypto.cert_blocks import CertificateBlockHeader +from spsdk.utils.crypto.common import crypto_backend +from spsdk.utils.misc import value_to_int + +logger = logging.getLogger(__name__) +LOG_LEVEL_NAMES = [name.lower() for name in logging._nameToLevel] + + +class DeviceHsm: + """Class to handle device HSM provisioning procedure.""" + + DEVBUFF_BASE = 0x20008000 + DEVBUFF_MAX_SIZE = 0x8000 + DEVBUFF_SIZE = 0x100 + + DEVBUFF_BASE0 = DEVBUFF_BASE + DEVBUFF_BASE1 = DEVBUFF_BASE0 + DEVBUFF_SIZE + DEVBUFF_BASE2 = DEVBUFF_BASE1 + DEVBUFF_SIZE + DEVBUFF_BASE3 = DEVBUFF_BASE2 + DEVBUFF_SIZE + + DEVBUFF_GEN_MASTER_SHARE_INPUT_SIZE = 16 + DEVBUFF_GEN_MASTER_ENC_SHARE_OUTPUT_SIZE = 48 + DEVBUFF_GEN_MASTER_ENC_MASTER_SHARE_OUTPUT_SIZE = 64 + DEVBUFF_GEN_MASTER_CUST_CERT_PUK_OUTPUT_SIZE = 64 + DEVBUFF_HSM_GENKEY_KEYBLOB_SIZE = 48 + DEVBUFF_HSM_GENKEY_KEYBLOB_PUK_SIZE = 64 + DEVBUFF_USER_PCK_KEY_SIZE = 32 + DEVBUFF_WRAPPED_USER_PCK_KEY_SIZE = 48 + DEFBUFF_DATA_BLOCK_SIZE = 256 + DEFBUFF_SB3_SIGNATURE_SIZE = 64 + + def __init__( + self, + mboot: McuBoot, + user_pck: bytes, + oem_share_input: bytes, + info_print: Callable, + container_conf: TextIO = None, + workspace: str = None, + ) -> None: + """Initialization of device HSM class. Its design to create provisioned SB3 file. + + :param mboot: mBoot communication interface. + :param oem_share_input: OEM share input data. + :param user_pck: USER PCK key. + :param container_conf: Optional elftosb configuration file (to specify user list of SB commands). + :param workspace: Optional folder to store middle results. + :raises SPSDKError: In case of any vulnerability. + """ + self.mboot = mboot + self.user_pck = user_pck + self.oem_share_input = oem_share_input + self.info_print = info_print + self.workspace = workspace + # if workspace and os.path.isdir(workspace) and os.listdir(workspace): + # raise SPSDKError("The workspace directory is already exists!") + if self.workspace and not os.path.isdir(self.workspace): + os.mkdir(self.workspace) + + # store input of OEM_SHARE_INPUT to workspace in case that is generated randomly + self.store_temp_res("OEM_SHARE_INPUT.BIN", self.oem_share_input) + + # Default value that could be given from SB3 configuration container + self.timestamp = None + self.sb3_descr = "SB3 SB_KEK" + self.sb3_fw_ver = 0 + + # Check the configuration file and options to update by user config + self.container_conf = None + if container_conf: + config_data = json.load(container_conf) + self.container_conf = SB31Config(config_data) + if self.container_conf.firmware_version: + self.sb3_fw_ver = self.container_conf.firmware_version + + if self.container_conf.description: + self.sb3_descr = self.container_conf.description + + if self.container_conf.timestamp: + self.timestamp = value_to_int(str(self.container_conf.timestamp)) + + self.wrapped_user_pck = bytes() + self.final_sb = bytes() + + def create_sb3(self) -> None: + """Do device hsm process to create SB_KEK provisioning SB file.""" + # 1: Call GEN_OEM_MASTER_SHARE to generate encOemShare.bin (ENC_OEM_SHARE will be later put in place of ISK) + self.info_print(" 1: Generating OEM master share.") + oem_enc_share, _, _ = self.oem_generate_master_share(self.oem_share_input) + + # 2: Call hsm_gen_key to generate 48 bytes FW signing key + self.info_print(" 2: Generating 48 bytes FW signing keys.") + cust_fw_auth_prk, cust_fw_auth_puk = self.generate_key( + TrustProvOemKeyType.MFWISK, "CUST_FW_AUTH" + ) + + # 3: Call hsm_gen_key to generate 48 bytes FW encryption key + self.info_print(" 3: Generating 48 bytes FW encryption keys.") + cust_fw_enc_prk, _ = self.generate_key(TrustProvOemKeyType.MFWENCK, "CUST_FW_ENC_SK") + + # 4: Call hsm_store_key to generate user defined CUST_MK_SK (aka PCK). + # Will be stored into PFR using loadKeyBlob SB3 command. + # Use NXP_CUST_KEK_EXT_SK in SB json + self.info_print(" 4: Wrapping user PCK key.") + self.wrapped_user_pck = self.wrap_key(self.user_pck) + + # 5: Generate template sb3 fw, sb3ImageType=6 + self.info_print(" 5: Creating template un-encrypted SB3 header and data blobs.") + # 5.1: Generate SB3.1 header template + self.info_print(" 5.1: Creating template SB3 header.") + sb3_header = SecureBinary31Header( + firmware_version=self.sb3_fw_ver, + curve_name="secp256r1", + description=self.sb3_descr, + timestamp=self.timestamp, + flags=0x01, # Bit0: PROV_MFW: when set, the SB3 file encrypts provisioning firmware + ) + self.timestamp = sb3_header.timestamp + sb3_header_exported = sb3_header.export() + logger.debug( + f" 5.1: The template SB3 header: \n{sb3_header.info()} \n Length:{len(sb3_header_exported)}" + ) + + # 5.2: Create SB3 file un-encrypted data part + self.info_print(" 5.2: Creating un-encrypted SB3 data.") + sb3_data = SecureBinary31Commands( + curve_name="secp256r1", is_encrypted=False, timestamp=self.timestamp + ) + sb3_data.set_commands(self.get_cmd_from_config(self.container_conf)) + logger.debug(f" 5.2: Created un-encrypted SB3 data: \n{sb3_data.info()}") + # 5.3: Get SB3 file data part individual chunks + data_cmd_blocks = sb3_data.get_cmd_blocks_to_export() + + # 6: Call hsm_enc_blk to encrypt all the data chunks from step 6. Use FW encryption key from step 3. + self.info_print(" 6: Encrypting SB3 data on device") + sb3_enc_data = self.encrypt_data_blocks( + cust_fw_enc_prk, sb3_header_exported, data_cmd_blocks + ) + # 6.1: Add to encrypted data parts SHA256 hashes + self.info_print(" 6.1: Enriching encrypted SB3 data by mandatory hashes.") + enc_final_data = sb3_data.process_cmd_blocks_to_export(sb3_enc_data) + self.store_temp_res("Final_data.bin", enc_final_data, "to_merge") + + # 6.2: Create dummy certification part of SB3 manifest + self.info_print(" 6.2: Creating dummy certificate block.") + cb_header = CertificateBlockHeader() + cb_header.cert_block_size = ( + cb_header.SIZE + 68 + self.DEVBUFF_GEN_MASTER_ENC_SHARE_OUTPUT_SIZE + ) + logger.debug(f" 6.2: The dummy certificate block has been created:\n{cb_header.info()}.") + + # 6.3: Update the SB3 pre-prepared header by current data + self.info_print(" 6.3: Updating SB3 header by valid values.") + sb3_header.block_count = sb3_data.block_count + sb3_header.image_total_length += ( + len(sb3_data.final_hash) + cb_header.cert_block_size + self.DEFBUFF_SB3_SIGNATURE_SIZE + ) + logger.debug( + f" 6.3: The SB3 header has been updated by valid values:\n{sb3_header.info()}." + ) + + # 6.4: Compose manifest that will be signed + self.info_print(" 6.4: Preparing SB3 manifest to sign.") + manifest_to_sign = bytes() + manifest_to_sign += sb3_header.export() + manifest_to_sign += sb3_data.final_hash + manifest_to_sign += cb_header.export() + manifest_to_sign += ( + b"\x11\x00\x00\x80" # 0x80000011 Cert Flags: CA Flag, 1 certificate and NIST P-256 + ) + manifest_to_sign += cust_fw_auth_puk + manifest_to_sign += oem_enc_share + self.store_temp_res("manifest_to_sign.bin", manifest_to_sign, "to_merge") + logger.debug( + f" 6.4: The SB3 manifest data to sign:\n{format_raw_data(manifest_to_sign, use_hexdump=True)}." + ) + + # 7: Get sign of SB3 file manifest + self.info_print(" 7: Creating SB3 manifest signature on device.") + manifest_signature = self.sign_data_blob(manifest_to_sign, cust_fw_auth_prk) + logger.debug( + f" 7: The SB3 manifest signature data:\n{format_raw_data(manifest_signature, use_hexdump=True)}." + ) + + # 8: Merge all parts together + self.info_print(" 8: Composing final SB3 file.") + self.final_sb = bytes() + self.final_sb += manifest_to_sign + self.final_sb += manifest_signature + self.final_sb += enc_final_data + self.store_temp_res("Final_SB3.sb3", self.final_sb) + logger.debug( + f" 8: The final SB3 file data:\n{format_raw_data(self.final_sb, use_hexdump=True)}." + ) + + def export(self) -> bytes: + """Get the Final SB file. + + :return: Final SB file in bytes. + """ + return self.final_sb + + def oem_generate_master_share(self, oem_share_input: bytes) -> Tuple[bytes, bytes, bytes]: + """Generate on device Encrypted OEM master share outputs. + + :param oem_share_input: OEM input (randomize seed) + :raises SPSDKError: In case of any vulnerability. + :return: Tuple with OEM generate master share outputs. + """ + if not self.mboot.write_memory(self.DEVBUFF_BASE0, oem_share_input): + raise SPSDKError("Cannot write OEM SHARE INPUT into device.") + + oem_gen_master_share_res = self.mboot.tp_oem_gen_master_share( + self.DEVBUFF_BASE0, + self.DEVBUFF_GEN_MASTER_SHARE_INPUT_SIZE, + self.DEVBUFF_BASE1, + self.DEVBUFF_SIZE, + self.DEVBUFF_BASE2, + self.DEVBUFF_SIZE, + self.DEVBUFF_BASE3, + self.DEVBUFF_SIZE, + ) + + if not oem_gen_master_share_res: + raise SPSDKError( + "OEM generate master share command failed," + " device probably needs reset due to doubled call of this command." + ) + + if ( + oem_gen_master_share_res[0] != self.DEVBUFF_GEN_MASTER_ENC_SHARE_OUTPUT_SIZE + and oem_gen_master_share_res[1] != self.DEVBUFF_GEN_MASTER_ENC_MASTER_SHARE_OUTPUT_SIZE + and oem_gen_master_share_res[2] != self.DEVBUFF_GEN_MASTER_CUST_CERT_PUK_OUTPUT_SIZE + ): + raise SPSDKError("OEM generate master share command has invalid results.") + + oem_enc_share = self.mboot.read_memory( + self.DEVBUFF_BASE1, + self.DEVBUFF_GEN_MASTER_ENC_SHARE_OUTPUT_SIZE, + ) + if not oem_enc_share: + raise SPSDKError("Cannot read OEM ENCRYPTED SHARE OUTPUT from device.") + self.store_temp_res("ENC_OEM_SHARE.bin", oem_enc_share) + + oem_enc_master_share = self.mboot.read_memory( + self.DEVBUFF_BASE2, + self.DEVBUFF_GEN_MASTER_ENC_MASTER_SHARE_OUTPUT_SIZE, + ) + if not oem_enc_master_share: + raise SPSDKError("Cannot read OEM ENCRYPTED MASTER SHARE OUTPUT from device.") + self.store_temp_res("ENC_OEM_MASTER_SHARE.bin", oem_enc_master_share) + + oem_cert = self.mboot.read_memory( + self.DEVBUFF_BASE3, + self.DEVBUFF_GEN_MASTER_CUST_CERT_PUK_OUTPUT_SIZE, + ) + if not oem_cert: + raise SPSDKError("Cannot read OEM CERTIFICATE from device.") + self.store_temp_res("OEM_CERT.bin", oem_cert) + + return oem_enc_share, oem_enc_master_share, oem_cert + + def generate_key( + self, key_type: TrustProvOemKeyType, key_name: str = None + ) -> Tuple[bytes, bytes]: + """Generate on device key pairs of provided type. + + :param key_type: Type of generated key pairs. + :param key_name: optional name for storing temporary files. + :raises SPSDKError: In case of any vulnerability. + :return: Tuple with Private and Public key. + """ + hsm_gen_key_res = self.mboot.tp_hsm_gen_key( + key_type, + 0, + self.DEVBUFF_BASE0, + self.DEVBUFF_SIZE, + self.DEVBUFF_BASE1, + self.DEVBUFF_SIZE, + ) + + if not hsm_gen_key_res: + raise SPSDKError("HSM generate key command failed.") + + if ( + hsm_gen_key_res[0] != self.DEVBUFF_HSM_GENKEY_KEYBLOB_SIZE + and hsm_gen_key_res[1] != self.DEVBUFF_HSM_GENKEY_KEYBLOB_PUK_SIZE + ): + raise SPSDKError("OEM generate master share command has invalid results.") + + prk = self.mboot.read_memory( + self.DEVBUFF_BASE0, + self.DEVBUFF_HSM_GENKEY_KEYBLOB_SIZE, + ) + if not prk: + raise SPSDKError("Cannot read generated private key from device.") + + puk = self.mboot.read_memory( + self.DEVBUFF_BASE1, + self.DEVBUFF_HSM_GENKEY_KEYBLOB_PUK_SIZE, + ) + if not puk: + raise SPSDKError("Cannot read generated public key from device.") + + self.store_temp_res((key_name or str(TrustProvOemKeyType[str(key_type)])) + "_PRK.bin", prk) + self.store_temp_res((key_name or str(TrustProvOemKeyType[str(key_type)])) + "_PUK.bin", puk) + + return prk, puk + + def wrap_key(self, user_pck: bytes) -> bytes: + """Wrap the user PCK key. + + :param user_pck: User PCK key + :raises SPSDKError: In case of any vulnerability. + :return: Wrapped user PCK by RFC3396. + """ + if not self.mboot.write_memory(self.DEVBUFF_BASE0, user_pck): + raise SPSDKError("Cannot write USER_PCK into device.") + + hsm_store_key_res = self.mboot.tp_hsm_store_key( + TrustProvKeyType.CKDFK, + 0x01, + self.DEVBUFF_BASE0, + self.DEVBUFF_USER_PCK_KEY_SIZE, + self.DEVBUFF_BASE1, + self.DEVBUFF_SIZE, + ) + + if not hsm_store_key_res: + raise SPSDKError("HSM Store Key command failed.") + + if hsm_store_key_res[1] != self.DEVBUFF_WRAPPED_USER_PCK_KEY_SIZE: + raise SPSDKError("HSM Store Key command has invalid results.") + + wrapped_user_pck = self.mboot.read_memory( + self.DEVBUFF_BASE1, + self.DEVBUFF_WRAPPED_USER_PCK_KEY_SIZE, + ) + if not wrapped_user_pck: + raise SPSDKError("Cannot read WRAPPED USER PCK from device.") + + self.store_temp_res("CUST_MK_SK.bin", wrapped_user_pck) + + return wrapped_user_pck + + def sign_data_blob(self, data_to_sign: bytes, key: bytes) -> bytes: + """Get HSM encryption sign for data blob. + + :param data_to_sign: Input data to sign. + :param key: FW signing key (MFWISK). + :raises SPSDKError: In case of any vulnerability. + :return: Data blob signature (64 bytes). + """ + if not self.mboot.write_memory(self.DEVBUFF_BASE0, key): + raise SPSDKError("Cannot write signing key into device.") + if not self.mboot.write_memory(self.DEVBUFF_BASE1, data_to_sign): + raise SPSDKError("Cannot write Data to sign into device.") + hsm_gen_key_res = self.mboot.tp_hsm_enc_sign( + self.DEVBUFF_BASE0, + len(key), + self.DEVBUFF_BASE1, + len(data_to_sign), + self.DEVBUFF_BASE2, + self.DEFBUFF_SB3_SIGNATURE_SIZE, + ) + + if hsm_gen_key_res != self.DEFBUFF_SB3_SIGNATURE_SIZE: + raise SPSDKError("HSM signing command failed.") + + signature = self.mboot.read_memory( + self.DEVBUFF_BASE2, + self.DEFBUFF_SB3_SIGNATURE_SIZE, + ) + if not signature: + raise SPSDKError("Cannot read generated signature from device.") + + self.store_temp_res("SB3_sign.bin", signature, "to_merge") + + return signature + + def store_temp_res(self, file_name: str, data: bytes, group: str = None) -> None: + """Storing temporary files into workspace. + + :param file_name: Name of file to store the data. + :param data: Data to store. + :param group: Subfolder name, defaults to None + """ + if not self.workspace: + return + group_dir = os.path.join(self.workspace, group or "") + if not os.path.isdir(group_dir): + os.mkdir(group_dir) + + filename = os.path.join(self.workspace, group or "", file_name) + + with open(filename, "wb") as data_file: + data_file.write(data) + + def get_cmd_from_config(self, container: Optional[SB31Config]) -> List[BaseCmd]: + """Process command description into a command object. + + :return: Command object + :raises SPSDKError: Unknown command + """ + commands = [] + if container: + cfg_commands: List[Dict[str, str]] = container.commands + for cmd in cfg_commands: + cmd_cpy: dict = cmd.copy() + name, args = cmd_cpy.popitem() + if name == "loadKeyBlob" and value_to_int(str(args["offset"])) == 0x04: + logger.warning( + f"""The duplicated 'loadKeyBlob' on offset 0x04 from + configuration file is Ignored:\n {args}.""" + ) + cfg_commands.remove(cmd) + + commands = get_cmd_from_json(container) + + commands.insert( + 0, + CmdLoadKeyBlob( + offset=0x04, + data=self.wrapped_user_pck, + key_wrap_id=CmdLoadKeyBlob.KeyWraps["NXP_CUST_KEK_EXT_SK"], + ), + ) + + return commands + + def encrypt_data_blocks( + self, cust_fw_enc_key: bytes, sb3_header: bytes, data_cmd_blocks: List[bytes] + ) -> List[bytes]: + """Encrypt all data blocks on device. + + :param cust_fw_enc_key: Firmware encryption key. + :param sb3_header: Un Encrypted SB3 file header. + :param data_cmd_blocks: List of un-encrypted SB3 file command blocks. + :raises SPSDKError: In case of any vulnerability. + :return: List of encrypted command blocks on device. + """ + if not self.mboot.write_memory(self.DEVBUFF_BASE0, cust_fw_enc_key): + raise SPSDKError("Cannot write customer fw encryption key into device.") + self.store_temp_res("SB3_header.bin", sb3_header, "to_encrypt") + if not self.mboot.write_memory(self.DEVBUFF_BASE1, sb3_header): + raise SPSDKError("Cannot write SB3 header into device.") + + encrypted_blocks = [] + for data_cmd_block_ix, data_cmd_block in enumerate(data_cmd_blocks, start=1): + self.store_temp_res(f"SB3_block_{data_cmd_block_ix}.bin", data_cmd_block, "to_encrypt") + if not self.mboot.write_memory(self.DEVBUFF_BASE2, data_cmd_block): + raise SPSDKError(f"Cannot write SB3 data block{data_cmd_block_ix} into device.") + + if not self.mboot.tp_hsm_enc_blk( + self.DEVBUFF_BASE0, + len(cust_fw_enc_key), + 16, + self.DEVBUFF_BASE1, + len(sb3_header), + data_cmd_block_ix, + self.DEVBUFF_BASE2, + self.DEFBUFF_DATA_BLOCK_SIZE, + ): + raise SPSDKError( + f"Cannot run SB3 data block_{data_cmd_block_ix} HSM Encryption in device." + ) + + encrypted_block = self.mboot.read_memory( + self.DEVBUFF_BASE2, + self.DEFBUFF_DATA_BLOCK_SIZE, + ) + if not encrypted_block: + raise SPSDKError(f"Cannot read SB3 data block_{data_cmd_block_ix} from device.") + + self.store_temp_res(f"SB3_block_{data_cmd_block_ix}.bin", encrypted_block, "encrypted") + + encrypted_blocks.append(encrypted_block) + + return encrypted_blocks + + +def get_user_pck(key: BinaryIO) -> bytes: + """Get binary from text or binary file. + + :param key: Binary user PCK key file. + :return: Binary array loaded from file. + :raises SPSDKValueError: When invalid input value is recognized. + """ + user_pck = key.read() + + if len(user_pck) != 32: + raise SPSDKValueError( + f"Invalid length of USER PCK INPUT ({len(user_pck)} not equal to 32)." + ) + + return user_pck + + +def get_oem_share_input(binary: BinaryIO) -> bytes: + """Get binary from text or binary file. + + :param binary: Path to binary file. + :return: Binary array loaded from file. + :raises SPSDKValueError: When invalid input value is recognized. + """ + if binary: + oem_share_input = binary.read() + else: + oem_share_input = crypto_backend().random_bytes(16) + + if len(oem_share_input) != 16: + raise SPSDKValueError( + f"Invalid length of OEM SHARE INPUT ({len(oem_share_input)} not equal to 16)." + ) + + return oem_share_input + + +@click.group(no_args_is_help=True) +@click.option( + "-d", + "--debug", + "log_level", + metavar="LEVEL", + default="warning", + help=f"Set the level of system logging output. " + f'Available options are: {", ".join(LOG_LEVEL_NAMES)}', + type=click.Choice(LOG_LEVEL_NAMES), +) +@click.version_option(version, "--version") +def main(log_level: str) -> int: + """Nxpdevhsm application is designed to create SB3 provisioning file for initial provisioning of device by OEM.""" + logging.basicConfig(level=log_level.upper()) + return 0 + + +@main.command() +@optgroup.group("Interface configuration", cls=MutuallyExclusiveOptionGroup) +@optgroup.option( + "-p", + "--port", + metavar="COM[,speed]", + help="""Serial port configuration. Use 'nxpdevscan' utility to list devices on serial port.""", +) +@optgroup.option( + "-u", + "--usb", + metavar="VID,PID", + help="""USB device identifier. + Following formats are supported: , or , device/instance path, device name. + : hex or dec string; e.g. 0x0AB12, 43794. + : hex or dec string; e.g. 0x0AB12:0x123, 1:3451. + Use 'nxpdevscan' utility to list connected device names. +""", +) +@optgroup.option( + "-l", + "--lpcusbsio", + metavar="spi|i2c", + help="""USB-SIO bridge interface. + Following interfaces are supported: + + spi[,port,pin,speed_kHz,polarity,phase] + - port ... bridge GPIO port used as SPI SSEL + - pin ... bridge GPIO pin used as SPI SSEL + default SSEL is set to 0.15 which works + for the LPCLink2 bridge. The MCULink OB + bridge ignores the SSEL value anyway. + - speed_kHz ... SPI clock in kHz (default 1000) + - polarity ... SPI CPOL option (default=1) + - phase ... SPI CPHA option (default=1) + + i2c[,address,speed_kHz] + - address ... I2C device address (default 0x10) + - speed_kHz ... I2C clock in kHz (default 100) +""", +) +@click.option( + "-k", + "--key", + type=click.File(mode="rb"), + required=True, + help="User PCK secret file (32-bytes long binary file). PCK (provisioned by OEM, known by OEM) - Part Common Key." + " This is a 256-bit pre-shared AES key provisioned by OEM. PCK is used to derive FW image encryption keys.", +) +@click.option( + "-o", + "--oem-share-input", + type=click.File(mode="rb"), + help="OEM share input file to use as a seed to randomize the provisioning process (16-bytes long binary file).", +) +@click.argument("output-path", type=click.File(mode="wb")) +@click.option( + "-w", + "--workspace", + type=click.Path(), + required=False, + help="Workspace folder to store temporary files, that could be used for future review.", +) +@click.option( + "-j", + "--container-conf", + type=click.File("r"), + required=False, + help="""json container configuration file to produce secure binary v3.x. + In this configuration file is enough to provide just commands and description section.""", +) +@click.option( + "-t", + "--timeout", + metavar="", + help="""Sets timeout when waiting on data over a serial line. The default is 5000 milliseconds.""", + default=5000, +) +def generate( + port: str, + usb: str, + lpcusbsio: str, + oem_share_input: BinaryIO, + key: BinaryIO, + output_path: BinaryIO, + workspace: click.Path, + container_conf: TextIO, + timeout: int, +) -> None: + """Generate provisioned SB file. + + \b + PATH - output file path, where the final provisioned SB file will be stored. + """ + interface = get_interface( + module="mboot", port=port, usb=usb, lpcusbsio=lpcusbsio, timeout=timeout + ) + assert isinstance(interface, mbootInterface) + + oem_share_in = get_oem_share_input(oem_share_input) + user_pck = get_user_pck(key) + + with McuBoot(interface) as mboot: + devhsm = DeviceHsm( + mboot=mboot, + user_pck=user_pck, + oem_share_input=oem_share_in, + info_print=click.echo, + container_conf=container_conf, + workspace=str(workspace), + ) + + devhsm.create_sb3() + output_path.write(devhsm.export()) + + click.echo(f"Final SB3 file has been written: {os.path.abspath(output_path.name)}") + + +@catch_spsdk_error +def safe_main() -> None: + """Call the main function.""" + sys.exit(main()) # pragma: no cover # pylint: disable=no-value-for-parameter + + +if __name__ == "__main__": + safe_main() # pragma: no cover diff --git a/spsdk/apps/nxpdevscan.py b/spsdk/apps/nxpdevscan.py index 4571ee7a..f3170806 100644 --- a/spsdk/apps/nxpdevscan.py +++ b/spsdk/apps/nxpdevscan.py @@ -11,6 +11,7 @@ from typing import IO import click +import logging from spsdk import __version__ as spsdk_version from spsdk.apps.utils import catch_spsdk_error @@ -29,6 +30,8 @@ @click.version_option(spsdk_version, "--version") def main(extend_vids: str, out: IO[str]) -> None: """Utility listing all connected NXP USB and UART devices.""" + logging.basicConfig() + additional_vids = [int(vid, 16) for vid in extend_vids] nxp_devices = nxpdevscan.search_nxp_usb_devices(additional_vids) diff --git a/spsdk/apps/nxpkeygen.py b/spsdk/apps/nxpkeygen.py index 95c6694b..c6b02553 100644 --- a/spsdk/apps/nxpkeygen.py +++ b/spsdk/apps/nxpkeygen.py @@ -6,20 +6,21 @@ # SPDX-License-Identifier: BSD-3-Clause """Module is used to generate public/private key and generating debug credential file.""" -import json import logging import os import sys from typing import List, Tuple import click +import commentjson as json import yaml -from spsdk import SPSDK_DATA_FOLDER +from spsdk import SPSDK_DATA_FOLDER, SPSDKValueError from spsdk import __version__ as version from spsdk.apps.elftosb_utils.sb_31_helper import RootOfTrustInfo -from spsdk.apps.utils import catch_spsdk_error +from spsdk.apps.utils import catch_spsdk_error, check_destination_dir, check_file_exists from spsdk.crypto import ( + ec, generate_ecc_private_key, generate_ecc_public_key, generate_rsa_private_key, @@ -35,39 +36,7 @@ logger = logging.getLogger(__name__) LOG_LEVEL_NAMES = [name.lower() for name in logging._nameToLevel] - - -@click.group() -@click.option( - "-p", - "--protocol", - "protocol", - type=str, - metavar="VERSION", - default="1.0", - help="Set the protocol version. Default is 1.0 (RSA). ", -) -@click.option( - "-d", - "--debug", - "log_level", - metavar="LEVEL", - default="debug", - help=f"Set the level of system logging output. " - f'Available options are: {", ".join(LOG_LEVEL_NAMES)}', - type=click.Choice(LOG_LEVEL_NAMES), -) -@click.version_option(version, "--version") -@click.pass_context -def main(ctx: click.Context, protocol: str, log_level: str) -> int: - """NXP Key Generator Tool.""" - is_rsa, protocol_version = determine_protocol_version(protocol) - key_param = determine_key_parameters(is_rsa, protocol_version) - - ctx.obj = {"is_rsa": is_rsa, "key_param": key_param, "protocol_version": protocol} - - logging.basicConfig(level=log_level.upper()) - return 0 +SUPPORTED_PROTOCOLS = ["1.0", "1.1", "2.0", "2.1", "2.2"] def determine_key_parameters(is_rsa: bool, protocol_version: List[str]) -> object: @@ -78,7 +47,7 @@ def determine_key_parameters(is_rsa: bool, protocol_version: List[str]) -> objec :return: string with keys' parameter """ rsa_key_sizes = {"0": 2048, "1": 4096} - ecc_curves = {"0": "P-256", "1": "P-384", "2": "P-521"} + ecc_curves = {"0": "secp256r1", "1": "secp384r1", "2": "secp521r1"} key_param = rsa_key_sizes[protocol_version[1]] if is_rsa else ecc_curves[protocol_version[1]] return key_param @@ -88,86 +57,105 @@ def determine_protocol_version(protocol: str) -> Tuple[bool, List[str]]: :param protocol: one of the values: '1.0', '1.1', '2.0', '2.1', '2.2' :return: is_rsa (true/false), protocol_version + :raises SPSDKValueError: In case that protocol is using unsupported key type. """ - assert protocol in [ - "1.0", - "1.1", - "2.0", - "2.1", - "2.2", - ], "Unsupported protocol was given." + if not protocol in SUPPORTED_PROTOCOLS: + raise SPSDKValueError(f"Unsupported protocol '{protocol}' was given.") protocol_version = protocol.split(".") is_rsa = protocol_version[0] == "1" return is_rsa, protocol_version -def check_destination_dir(path: str, create_folder: bool = False) -> None: - """Checks path's destination dir, optionally create the destination folder. +def get_list_of_supported_keys() -> List[str]: + """Generate list with list of supported key types. - :param path: Path to file to create/consider - :param create_folder: Create destination folder + :return: List of supported key types. """ - dest_dir = os.path.dirname(path) - if not dest_dir: - return - if create_folder: - os.makedirs(dest_dir, exist_ok=True) - return - if not os.path.isdir(dest_dir): - click.echo(f"Can't create '{path}', folder '{dest_dir}' doesn't exit.") - sys.exit(1) - - -def check_file_exists(path: str, force_overwrite: bool = False) -> bool: # type: ignore - """Check if file exists, exits if file exists and overwriting is disabled. - - :param path: Path to a file - :param force_overwrite: allows file overwriting - :return: if file overwriting is allowed, it return True if file exists - """ - if force_overwrite: - return os.path.isfile(path) - if os.path.isfile(path) and not force_overwrite: - click.echo(f"File '{path}' already exists. Use --force to overwrite it.") - sys.exit(1) + ret = ["rsa2048", "rsa3072", "rsa4096"] + # pylint: disable=protected-access + ret.extend(ec._CURVE_TYPES.keys()) # type: ignore + + return ret + + +@click.group(no_args_is_help=True) +@click.option( + "-d", + "--debug", + "log_level", + metavar="LEVEL", + default="warning", + help=f"Set the level of system logging output. " + f'Available options are: {", ".join(LOG_LEVEL_NAMES)}', + type=click.Choice(LOG_LEVEL_NAMES), +) +@click.version_option(version, "--version") +def main(log_level: str) -> int: + """NXP Key Generator Tool.""" + logging.basicConfig(level=log_level.upper()) + return 0 @main.command() +@click.option( + "-k", + "--key-type", + type=click.Choice(get_list_of_supported_keys(), case_sensitive=False), + metavar="KEY-TYPE", + default="RSA2048", + help=f"""\b + Set of the supported key types. Default is RSA2048. + + Note: NXP DAT protocol is using encryption keys by this table: + + NXP Protocol Version Encryption Type + 1.0 RSA 2048 + 1.1 RSA 4096 + 2.0 SECP256R1 + 2.1 SECP384R1 + 2.2 SECP521R1 + + All possible options: + {", ".join(get_list_of_supported_keys())}. + """, +) @click.option( "--password", "password", metavar="PASSWORD", - help="Password with which the output file will be " - "encrypted. If not provided, the output will be " - "unencrypted.", + help="Password with which the output file will be encrypted. " + "If not provided, the output will be unencrypted.", ) @click.argument("path", type=click.Path(file_okay=True)) @click.option( "--force", is_flag=True, default=False, - help="Force overwriting of an existing file. Create destination folder, if doesn't exist already.", + help="Force overwriting of an existing file.", ) -@click.pass_context -def genkey(ctx: click.Context, path: str, password: str, force: bool) -> None: +def genkey(key_type: str, path: str, password: str, force: bool) -> None: """Generate key pair for RoT or DCK. \b - PATH - path where the key pairs will be stored + PATH - output file path, where the key pairs (private and public key) will be stored. + Each key will be stored in separate file (.pub and .pem). """ - is_rsa = ctx.obj["is_rsa"] - key_param = ctx.obj["key_param"] + key_param = key_type.lower().strip() + is_rsa = "rsa" in key_param + check_destination_dir(path, force) check_file_exists(path, force) + pub_key_path = os.path.splitext(path)[0] + ".pub" + check_file_exists(pub_key_path, force) if is_rsa: logger.info("Generating RSA private key...") - priv_key_rsa = generate_rsa_private_key(key_size=key_param) + priv_key_rsa = generate_rsa_private_key(key_size=int(key_param.replace("rsa", ""))) logger.info("Generating RSA corresponding public key...") pub_key_rsa = generate_rsa_public_key(priv_key_rsa) logger.info("Saving RSA key pair...") save_rsa_private_key(priv_key_rsa, path, password if password else None) - save_rsa_public_key(pub_key_rsa, os.path.splitext(path)[0] + ".pub") + save_rsa_public_key(pub_key_rsa, pub_key_path) else: logger.info("Generating ECC private key...") priv_key_ec = generate_ecc_private_key(curve_name=key_param) @@ -175,10 +163,27 @@ def genkey(ctx: click.Context, path: str, password: str, force: bool) -> None: pub_key_ec = generate_ecc_public_key(priv_key_ec) logger.info("Saving ECC key pair...") save_ecc_private_key(priv_key_ec, path, password if password else None) - save_ecc_public_key(pub_key_ec, os.path.splitext(path)[0] + ".pub") + save_ecc_public_key(pub_key_ec, pub_key_path) @main.command() +@click.option( + "-p", + "--protocol", + "protocol", + type=str, + metavar="VERSION", + default="1.0", + help="""\b + Set the protocol version. Default is 1.0 (RSA). + NXP Protocol Version Encryption Type + 1.0 RSA 2048 + 1.1 RSA 4096 + 2.0 NIST P-256 SECP256R1 + 2.1 NIST P-384 SECP384R1 + 2.2 NIST P-521 SECP521R1 + """, +) @click.option( "-c", "--config", @@ -206,9 +211,8 @@ def genkey(ctx: click.Context, path: str, password: str, force: bool) -> None: help="External python file containing a custom SignatureProvider implementation.", ) @click.argument("dc_file_path", metavar="PATH", type=click.Path(file_okay=True)) -@click.pass_context def gendc( - ctx: click.Context, + protocol: str, plugin: click.Path, dc_file_path: str, config: click.File, @@ -229,8 +233,7 @@ def gendc( mod = module_from_spec(spec) spec.loader.exec_module(mod) # type: ignore - is_rsa = ctx.obj["is_rsa"] - protocol = ctx.obj["protocol_version"] + is_rsa = determine_protocol_version(protocol) check_destination_dir(dc_file_path, force) check_file_exists(dc_file_path, force) @@ -254,6 +257,7 @@ def gendc( with open(dc_file_path, "wb") as f: f.write(data) + @main.command() @click.argument("output", metavar="PATH", type=click.Path()) @click.option( @@ -263,7 +267,7 @@ def gendc( default=False, help="Force overwriting of an existing file. Create destination folder, if doesn't exist already.", ) -def get_gendc_config(output: click.Path, force: bool) -> None: +def get_cfg_template(output: click.Path, force: bool) -> None: """Generate the template of Debug Credentials YML configuration file. \b @@ -280,6 +284,7 @@ def get_gendc_config(output: click.Path, force: bool) -> None: click.echo("The configuration template file has been created.") + @catch_spsdk_error def safe_main() -> None: """Call the main function.""" diff --git a/spsdk/apps/pfr.py b/spsdk/apps/pfr.py index c5cb4834..4f2457d8 100644 --- a/spsdk/apps/pfr.py +++ b/spsdk/apps/pfr.py @@ -8,16 +8,14 @@ """Console script for pfr.""" import io -import json import sys # no_type_check decorator is used to suppress mypy's confusion in Click and cryptography libraries -from typing import List, Optional, Tuple, Type, Union, no_type_check +from typing import Optional, Tuple, Type, Union, no_type_check import click +import commentjson as json from click_option_group import MutuallyExclusiveOptionGroup, optgroup -from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey -from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey from ruamel.yaml import YAML from spsdk import SPSDK_YML_INDENT @@ -25,7 +23,7 @@ from spsdk import pfr from spsdk.apps.elftosb_utils.sb_31_helper import RootOfTrustInfo from spsdk.apps.utils import catch_spsdk_error -from spsdk.crypto import load_certificate, load_private_key, load_public_key +from spsdk.crypto.loaders import extract_public_keys from spsdk.pfr.exceptions import SPSDKPfrConfigError PFRArea = Union[Type[pfr.CMPA], Type[pfr.CFPA]] @@ -46,31 +44,7 @@ def _get_pfr_class(area_name: str) -> PFRArea: return getattr(pfr, area_name.upper()) -@no_type_check -def _extract_public_key( - file_path: str, password: Optional[str] -) -> Union[RSAPublicKey, EllipticCurvePublicKey]: - cert_candidate = load_certificate(file_path) - if cert_candidate: - return cert_candidate.public_key() - private_candidate = load_private_key(file_path, password.encode() if password else None) - if private_candidate: - return private_candidate.public_key() - public_candidate = load_public_key(file_path) - if public_candidate: - return public_candidate - assert False, f"Unable to load secret file '{file_path}'." - - -@no_type_check -def _extract_public_keys( - secret_files: Tuple[str], password: Optional[str] -) -> Union[List[RSAPublicKey], List[EllipticCurvePublicKey]]: - """Extract RSAPublic key from a file that contains Certificate, Private Key o Public Key.""" - return [_extract_public_key(file_path=source, password=password) for source in secret_files] - - -@click.group() +@click.group(no_args_is_help=True) @click.version_option(spsdk_version, "-v", "--version") def main() -> int: """Utility for generating and parsing Protected Flash Region data (CMPA, CFPA).""" @@ -231,7 +205,9 @@ def generate_binary( pfr_config = pfr.PfrConfiguration(str(user_config_file)) invalid_reason = pfr_config.is_invalid() if invalid_reason: - raise SPSDKPfrConfigError(f"The configuration file is not valid. The reason is: {invalid_reason}") + raise SPSDKPfrConfigError( + f"The configuration file is not valid. The reason is: {invalid_reason}" + ) assert pfr_config.type root_of_trust = None keys = None @@ -242,8 +218,10 @@ def generate_binary( root_of_trust = secret_file area = pfr_config.type if area.lower() == "cmpa" and root_of_trust: - keys = _extract_public_keys(root_of_trust, password) + keys = extract_public_keys(root_of_trust, password) pfr_obj = _get_pfr_class(area)(device=pfr_config.device, revision=pfr_config.revision) + if not pfr_config.revision: + pfr_config.revision = pfr_obj.revision pfr_obj.set_config(pfr_config, raw=not calc_inverse) data = pfr_obj.export(add_seal=add_seal, keys=keys) diff --git a/spsdk/apps/pfrc.py b/spsdk/apps/pfrc.py index 469d3d0b..4aa3c8b1 100644 --- a/spsdk/apps/pfrc.py +++ b/spsdk/apps/pfrc.py @@ -6,12 +6,12 @@ # SPDX-License-Identifier: BSD-3-Clause """Console script for pfrc (Utility to search for brick-conditions in PFR settings).""" -import json import logging import os import sys import click +import commentjson as json from spsdk import __version__ as spsdk_version from spsdk.apps.utils import catch_spsdk_error @@ -21,7 +21,7 @@ RULES_FILE = os.path.join(PFR_DATA_FOLDER, "rules.json") -@click.command() +@click.command(no_args_is_help=True) # type: ignore @click.option( "-m", "--cmpa-config", diff --git a/spsdk/apps/sdphost.py b/spsdk/apps/sdphost.py index 859280c6..4dd35190 100644 --- a/spsdk/apps/sdphost.py +++ b/spsdk/apps/sdphost.py @@ -21,15 +21,17 @@ from spsdk.sdp.commands import ResponseValue -@click.group() +@click.group(no_args_is_help=True) @optgroup.group("Interface configuration", cls=MutuallyExclusiveOptionGroup) @optgroup.option( - "-p", "--port", + "-p", + "--port", metavar="COM[,speed]", help="Serial port.", ) @optgroup.option( - "-u", "--usb", + "-u", + "--usb", metavar="VID,PID", help="""USB device identifier. Following formats are supported: , or , device/instance path, device name. @@ -39,22 +41,29 @@ """, ) @click.option( - "-j", "--json", "use_json", + "-j", + "--json", + "use_json", is_flag=True, help="Use JSON output", ) @click.option( - "-v", "--verbose", "log_level", + "-v", + "--verbose", + "log_level", flag_value=logging.INFO, help="Displays more verbose output", ) @click.option( - "-d", "--debug", "log_level", + "-d", + "--debug", + "log_level", flag_value=logging.DEBUG, help="Display debugging info", ) @click.option( - "-t", "--timeout", + "-t", + "--timeout", metavar="", help="Set packet timeout in milliseconds", default=5000, diff --git a/spsdk/apps/sdpshost.py b/spsdk/apps/sdpshost.py index ba6cbc5e..768f8136 100644 --- a/spsdk/apps/sdpshost.py +++ b/spsdk/apps/sdpshost.py @@ -24,7 +24,7 @@ """ -@click.group() +@click.group(no_args_is_help=True) @optgroup.group("Interface configuration", cls=MutuallyExclusiveOptionGroup) @optgroup.option("-p", "--port", help="Serial port") @optgroup.option("-u", "--usb", help="USB device's PID:VID") diff --git a/spsdk/apps/shadowregs.py b/spsdk/apps/shadowregs.py index 3bdb20ee..c0a60b8e 100644 --- a/spsdk/apps/shadowregs.py +++ b/spsdk/apps/shadowregs.py @@ -7,23 +7,22 @@ """Main Debug Authentication Tool application.""" import logging -import sys import os - -from typing import List, Dict +import sys +from typing import Dict, List import click +from spsdk import SPSDK_DATA_FOLDER from spsdk import __version__ as spsdk_version -from spsdk.shadowregs import ShadowRegisters, enable_debug +from spsdk.apps.utils import catch_spsdk_error from spsdk.debuggers.utils import DebugProbeUtils from spsdk.exceptions import SPSDKError -from spsdk.utils.registers import Registers, RegsRegister +from spsdk.shadowregs import ShadowRegisters, enable_debug from spsdk.utils.reg_config import RegConfig -from spsdk.apps.utils import catch_spsdk_error -from spsdk import SPSDK_DATA_FOLDER +from spsdk.utils.registers import Registers, RegsRegister -logger = logging.getLogger("ShadowRegs") +logger = logging.getLogger(__name__) # pylint: disable=protected-access LOG_LEVEL_NAMES = [name.lower() for name in logging._nameToLevel] @@ -37,7 +36,7 @@ def _open_shadow_registers(pass_obj: Dict) -> ShadowRegisters: :param pass_obj: Input dictionary with arguments. :return: Active ShadowRegisters object. - :raise SPSDKError: Raised with any kind of problems with debug probe. + :raises SPSDKError: Raised with any kind of problems with debug probe. """ config_file = pass_obj["config_file"] interface = pass_obj["interface"] @@ -72,7 +71,7 @@ def _open_shadow_registers(pass_obj: Dict) -> ShadowRegisters: ) -@click.group() +@click.group(no_args_is_help=True) @click.option( "-i", "--interface", diff --git a/spsdk/apps/spsdk_apps.py b/spsdk/apps/spsdk_apps.py index 1084e5b8..fde74c03 100644 --- a/spsdk/apps/spsdk_apps.py +++ b/spsdk/apps/spsdk_apps.py @@ -16,10 +16,12 @@ import click from spsdk import __version__ as spsdk_version + from .blhost import main as blhost_main from .elftosb import main as elftosb_main from .nxpcertgen import main as nxpcertgen_main from .nxpdebugmbox import main as nxpdebugmbox_main +from .nxpdevhsm import main as nxpdevhsm_main from .nxpdevscan import main as nxpdevscan_main from .nxpkeygen import main as nxpkeygen_main from .pfr import main as pfr_main @@ -30,7 +32,7 @@ from .utils import catch_spsdk_error -@click.group() +@click.group(no_args_is_help=True) @click.version_option(spsdk_version, "--version") def main() -> int: """Main entry point for all SPSDK applications.""" @@ -42,6 +44,7 @@ def main() -> int: main.add_command(nxpcertgen_main, name="nxpcertgen") main.add_command(nxpdebugmbox_main, name="nxpdebugmbox") main.add_command(nxpdevscan_main, name="nxpdevscan") +main.add_command(nxpdevhsm_main, name="nxpdevhsm") main.add_command(nxpkeygen_main, name="nxpkeygen") main.add_command(pfr_main, name="pfr") main.add_command(pfrc_main, name="pfrc") diff --git a/spsdk/apps/utils.py b/spsdk/apps/utils.py index 4a41d683..b234be43 100644 --- a/spsdk/apps/utils.py +++ b/spsdk/apps/utils.py @@ -7,13 +7,17 @@ """Module for general utilities used by applications.""" +import logging +import os import re import sys from functools import wraps -from typing import Union, Any, Callable, Tuple +from typing import Any, Callable, Tuple, Union import click +import commentjson as json import hexdump +import yaml from spsdk import SPSDKError from spsdk.mboot import interfaces as MBootInterfaceModule @@ -21,6 +25,8 @@ from spsdk.sdp import interfaces as SDPInterfaceModule from spsdk.sdp.interfaces import Interface as SDPInterface +logger = logging.getLogger(__name__) + class INT(click.ParamType): """Type that allows integers in bin, hex, oct format including _ as a visual separator.""" @@ -60,7 +66,7 @@ def convert(self, value: str, param: click.Parameter = None, ctx: click.Context def get_interface( - module: str, port: str = None, usb: str = None, timeout: int = 5000 + module: str, port: str = None, usb: str = None, timeout: int = 5000, lpcusbsio: str = None ) -> Union[MBootInterface, SDPInterface]: """Get appropriate interface. @@ -70,16 +76,19 @@ def get_interface( :param port: name and speed of the serial port (format: name[,speed]), defaults to None :param usb: PID,VID of the USB interface, defaults to None :param timeout: timeout in milliseconds + :param lpcusbsio: LPCUSBSIO spi or i2c config string :return: Selected interface instance :rtype: Interface - :raises ValueError: only one of 'port' or 'usb' must be specified - :raises SPSDKError: when SPSDK-specific error occurs + :raises SPSDKError: Only one of 'port' or 'usb' must be specified + :raises SPSDKError: When SPSDK-specific error occurs """ # check that one and only one interface is defined - if port is None and usb is None: - raise ValueError("One of 'port' or 'uart' must be specified.") - if port is not None and usb is not None: - raise ValueError("Only one of 'port' or 'uart' must be specified.") + all_interfaces = (port, usb, lpcusbsio) + count_interfaces = sum(i is not None for i in all_interfaces) + if count_interfaces == 0: + raise SPSDKError("One of '--port', '--usb' or '--lpcusbsio' must be specified.") + if count_interfaces > 1: + raise SPSDKError("Only one of '--port', '--usb' or '--lpcusbsio' must be specified.") interface_module = {"mboot": MBootInterfaceModule, "sdp": SDPInterfaceModule}[module] devices = [] @@ -98,6 +107,10 @@ def get_interface( if len(devices) > 1: raise SPSDKError(f"More than one device '{format_vid_pid(vid_pid)}' found") devices[0].timeout = timeout + if lpcusbsio: + devices = interface_module.scan_usbsio(lpcusbsio) # type: ignore + if len(devices) != 1: + raise SPSDKError(f"Cannot initialize USBSIO device '{lpcusbsio}'.") return devices[0] @@ -140,9 +153,11 @@ def wrapper(*args: tuple, **kwargs: dict) -> Any: return retval except (AssertionError, SPSDKError) as spsdk_exc: click.echo(f"ERROR:{spsdk_exc}") + logger.debug(str(spsdk_exc), exc_info=True) sys.exit(2) - except Exception as base_exc: # pylint: disable=broad-except + except (Exception, KeyboardInterrupt) as base_exc: # pylint: disable=broad-except click.echo(f"GENERAL ERROR: {type(base_exc).__name__}: {base_exc}") + logger.debug(str(base_exc), exc_info=True) sys.exit(3) return wrapper @@ -186,3 +201,58 @@ def parse_hex_data(hex_data: str) -> bytes: if not result: raise SPSDKError("Incorrect hex-data: Unable to get any data") return bytes(byte_pieces) + + +def check_destination_dir(path: str, create_folder: bool = False) -> None: + """Checks path's destination dir, optionally create the destination folder. + + :param path: Path to file to create/consider + :param create_folder: Create destination folder + """ + dest_dir = os.path.dirname(path) + if not dest_dir: + return + if create_folder: + os.makedirs(dest_dir, exist_ok=True) + return + if not os.path.isdir(dest_dir): + click.echo(f"Can't create '{path}', folder '{dest_dir}' doesn't exit.") + sys.exit(1) + + +def check_file_exists(path: str, force_overwrite: bool = False) -> bool: # type: ignore + """Check if file exists, exits if file exists and overwriting is disabled. + + :param path: Path to a file + :param force_overwrite: allows file overwriting + :return: if file overwriting is allowed, it return True if file exists + """ + if force_overwrite: + return os.path.isfile(path) + if os.path.isfile(path) and not force_overwrite: + click.echo(f"File '{path}' already exists. Use --force to overwrite it.") + sys.exit(1) + + +def load_configuration(path: str) -> dict: + """Load configuration from yml/json file. + + :param path: Path to configuration file + :raises SPSDKError: When unsupported file is provided + :return: Content of configuration as dictionary + """ + content = None + try: + with open(path) as f: + content = json.load(f) + return content + except json.JSONLibraryException: + content = None + try: + with open(path) as f: + content = yaml.safe_load(f) + except yaml.YAMLError: + content = None + if content is None: + raise SPSDKError(f"Unable to load '{path}'.") + return content diff --git a/spsdk/crypto/__init__.py b/spsdk/crypto/__init__.py index 68d76d75..9890393d 100644 --- a/spsdk/crypto/__init__.py +++ b/spsdk/crypto/__init__.py @@ -36,6 +36,7 @@ - loading the private key from file - loading the x.509 certificate from file """ +import cryptography.hazmat.primitives.asymmetric.utils as utils_cryptography from cryptography import x509 from cryptography.exceptions import * from cryptography.hazmat.backends import * @@ -44,18 +45,15 @@ from cryptography.hazmat.primitives import * from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import * -from cryptography.hazmat.primitives.asymmetric import padding, rsa -from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.asymmetric import ec, padding, rsa from cryptography.hazmat.primitives.asymmetric.ec import * from cryptography.hazmat.primitives.asymmetric.rsa import * # type: ignore from cryptography.hazmat.primitives.serialization import * -import cryptography.hazmat.primitives.asymmetric.utils as utils_cryptography - from cryptography.x509 import * from cryptography.x509 import ( AuthorityInformationAccessOID, - CRLEntryExtensionOID, CertificatePoliciesOID, + CRLEntryExtensionOID, ExtendedKeyUsageOID, ExtensionOID, NameOID, @@ -69,6 +67,7 @@ _get_encoding_type, generic_load, load_certificate, + load_certificate_as_bytes, load_private_key, load_public_key, ) diff --git a/spsdk/crypto/certificate_management.py b/spsdk/crypto/certificate_management.py index d37392a8..9643590d 100644 --- a/spsdk/crypto/certificate_management.py +++ b/spsdk/crypto/certificate_management.py @@ -7,22 +7,23 @@ """Module for certificate management (generating certificate, validating certificate, chains).""" from datetime import datetime, timedelta -from typing import Union, List +from typing import List, Union +from spsdk import SPSDKError from spsdk.crypto import ( - x509, - InvalidSignature, + Certificate, + CertificateSigningRequest, + EllipticCurvePrivateKey, + EllipticCurvePublicKey, Encoding, - default_backend, - hashes, + ExtensionOID, + InvalidSignature, RSAPrivateKey, RSAPublicKey, - ExtensionOID, - Certificate, - CertificateSigningRequest, + default_backend, + hashes, padding, - EllipticCurvePublicKey, - EllipticCurvePrivateKey, + x509, ) @@ -85,8 +86,10 @@ def validate_certificate_chain(chain_list: list) -> list: :param chain_list: list of certificates in chain :return: list of boolean values, which corresponds to the certificate validation in chain + :raises SPSDKError: When chain has less than two certificates """ - assert len(chain_list) > 1, "The chain must have at least two certificates" + if len(chain_list) <= 1: + raise SPSDKError("The chain must have at least two certificates") result = [] for i in range(len(chain_list) - 1): result.append(validate_certificate(chain_list[i], chain_list[i + 1])) diff --git a/spsdk/crypto/keys_management.py b/spsdk/crypto/keys_management.py index 13ee7aee..7d3f00a7 100644 --- a/spsdk/crypto/keys_management.py +++ b/spsdk/crypto/keys_management.py @@ -7,16 +7,33 @@ """Module for key generation and saving keys to file (RSA and ECC).""" from spsdk.crypto import ( - default_backend, - RSAPublicKey, - serialization, - rsa, - RSAPrivateKeyWithSerialization, - Encoding, - EllipticCurvePublicKey, EllipticCurvePrivateKeyWithSerialization, + EllipticCurvePublicKey, + Encoding, + RSAPrivateKeyWithSerialization, + RSAPublicKey, + default_backend, ec, + rsa, + serialization, ) +from spsdk.exceptions import SPSDKValueError + + +def get_ec_curve_object(name: str) -> ec.EllipticCurve: + """Get the EC curve object by its name. + + :param name: Name of EC curve. + :return: EC curve object. + :raises SPSDKValueError: Invalid EC curve name. + """ + # pylint: disable=protected-access + for key_object in ec._CURVE_TYPES: # type: ignore + if key_object.lower() == name.lower(): + # pylint: disable=protected-access + return ec._CURVE_TYPES[key_object]() # type: ignore + + raise SPSDKValueError(f"The EC curve with name '{name}' is not supported.") def generate_rsa_private_key( @@ -87,18 +104,14 @@ def save_rsa_public_key( def generate_ecc_private_key( - curve_name: str = "P-256", + curve_name: str = "secp256r1", ) -> EllipticCurvePrivateKeyWithSerialization: """Generate ECC private key. - :param curve_name: name of curve; currently supported: P-256, P-384, P-521 + :param curve_name: name of curve :return: ECC private key """ - curve_obj = { - "P-256": ec.SECP256R1(), - "P-384": ec.SECP384R1(), - "P-521": ec.SECP521R1(), - }[curve_name] + curve_obj = get_ec_curve_object(curve_name) return ec.generate_private_key(curve_obj, default_backend()) # type: ignore diff --git a/spsdk/crypto/loaders.py b/spsdk/crypto/loaders.py index 886c8f53..9bfba365 100644 --- a/spsdk/crypto/loaders.py +++ b/spsdk/crypto/loaders.py @@ -6,27 +6,24 @@ # SPDX-License-Identifier: BSD-3-Clause """Loading methods for keys/certificates/CSR.""" -from typing import Callable, Any, Union +from typing import Any, Callable, Iterable, List, Optional +from cryptography.hazmat._types import _PRIVATE_KEY_TYPES as PrivateKey +from cryptography.hazmat._types import _PUBLIC_KEY_TYPES as PublicKey + +from spsdk import SPSDKError from spsdk.crypto import ( - default_backend, - RSAPrivateKeyWithSerialization, - RSAPublicKey, Certificate, - load_pem_x509_certificate, - load_der_x509_certificate, - load_pem_public_key, + Encoding, + default_backend, + load_der_private_key, load_der_public_key, + load_der_x509_certificate, load_pem_private_key, - load_der_private_key, - Encoding, - EllipticCurvePrivateKeyWithSerialization, - EllipticCurvePublicKey, + load_pem_public_key, + load_pem_x509_certificate, ) -PrivateKey = Union[RSAPrivateKeyWithSerialization, EllipticCurvePrivateKeyWithSerialization] -PublicKey = Union[RSAPublicKey, EllipticCurvePublicKey] - def load_private_key( file_path: str, password: bytes = None, encoding: Encoding = None @@ -82,7 +79,7 @@ def load_certificate(file_path: str, encoding: Encoding = None) -> Certificate: :param file_path: path to file, where certificate is stored :param encoding: type of encoding - :return: Certificate + :return: Certificate (from cryptography library) """ real_encoding = encoding or _get_encoding_type(file_path) @@ -100,6 +97,17 @@ def solve(certificate_data: bytes) -> Certificate: return generic_load(file_path, solve) +def load_certificate_as_bytes(file_path: str) -> bytes: + """Load certificate from file in PEM/DER format. + + Converts the certificate into DER format and serializes it into bytes. + + :param file_path: path to certificate file. + :return: certificate in der format serialized into bytes. + """ + return load_certificate(file_path).public_bytes(encoding=Encoding.DER) + + def generic_load(file_path: str, inner_fun: Callable) -> Any: """General loading of item. @@ -129,3 +137,27 @@ def _get_encoding_type(file: str) -> Encoding: else: encoding = Encoding.PEM return encoding + + +def extract_public_key(file_path: str, password: Optional[str]) -> PublicKey: + """Extract any kind of public key from a file that contains Certificate, Private Key or Public Key. + + :raises SPSDKError: Raised when file can not be loaded + :return: private key of any type + + """ + cert_candidate = load_certificate(file_path) + if cert_candidate: + return cert_candidate.public_key() + private_candidate = load_private_key(file_path, password.encode() if password else None) + if private_candidate: + return private_candidate.public_key() + public_candidate = load_public_key(file_path) + if public_candidate: + return public_candidate + raise SPSDKError(f"Unable to load secret file '{file_path}'.") + + +def extract_public_keys(secret_files: Iterable[str], password: Optional[str]) -> List[PublicKey]: + """Extract any kind of public key from files that contain Certificate, Private Key or Public Key.""" + return [extract_public_key(file_path=source, password=password) for source in secret_files] diff --git a/spsdk/crypto/signature_provider.py b/spsdk/crypto/signature_provider.py index 02ac7a45..d2e00daf 100644 --- a/spsdk/crypto/signature_provider.py +++ b/spsdk/crypto/signature_provider.py @@ -32,6 +32,10 @@ def sign(self, data: bytes) -> Optional[bytes]: def info(self) -> str: """Provide information about the Signature provide.""" + @abc.abstractproperty + def signature_length(self) -> int: + """Return length of the signature.""" + @staticmethod def _convert_params(params: str) -> Dict[str, str]: """Coverts creation params from string into dictionary. @@ -86,6 +90,14 @@ def __init__( hash_alg_name = hash_alg or f"sha{hash_size}" self.hash_alg = getattr(crypto.hashes, hash_alg_name.upper())() + @property + def signature_length(self) -> int: + """Return length of the signature.""" + sig_len = self.private_key.key_size // 8 + if isinstance(self.private_key, crypto.EllipticCurvePrivateKey): + sig_len *= 2 + return sig_len + def info(self) -> str: """Return basic into about the signature provider.""" msg = "Plain File Signature Provider\n" diff --git a/spsdk/dat/dac_packet.py b/spsdk/dat/dac_packet.py index 0fa6ce53..1320695c 100644 --- a/spsdk/dat/dac_packet.py +++ b/spsdk/dat/dac_packet.py @@ -7,7 +7,7 @@ """Module with Debug Authentication Challenge (DAC) Packet.""" -from struct import unpack_from, pack, calcsize +from struct import calcsize, pack, unpack_from class DebugAuthenticationChallenge: diff --git a/spsdk/dat/dar_packet.py b/spsdk/dat/dar_packet.py index 2992dba8..1775c16c 100644 --- a/spsdk/dat/dar_packet.py +++ b/spsdk/dat/dar_packet.py @@ -10,7 +10,7 @@ from struct import pack from typing import Type -from spsdk import crypto +from spsdk import SPSDKError, crypto from spsdk.crypto.signature_provider import PlainFileSP from spsdk.dat import DebugAuthenticationChallenge, DebugCredential from spsdk.dat.utils import ecc_public_numbers_to_bytes @@ -38,7 +38,6 @@ def __init__( self.auth_beacon = auth_beacon self.dac = dac self.dck_priv = path_dck_private - self.is_n4analog = self.debug_credential.socc == 0x04 self.sig_provider = PlainFileSP(path_dck_private) def info(self) -> str: @@ -55,9 +54,11 @@ def _get_data_for_signature(self) -> bytes: return data def _get_signature(self) -> bytes: - assert self.sig_provider, f"Signature provider is not set" + if not self.sig_provider: + raise SPSDKError("Signature provider is not set") signature = self.sig_provider.sign(self._get_data_for_signature()) - assert signature + if not signature: + raise SPSDKError("Signature is not present") return signature def export(self) -> bytes: @@ -70,11 +71,9 @@ def export(self) -> bytes: return data def _get_common_data(self) -> bytes: - """Collects dc, auth_beacon and in case of n4analog devices - UUID.""" + """Collects dc, auth_beacon.""" data = self.debug_credential.export() data += pack(" bytes: return signature -class DebugAuthenticateResponseN4A_256(DebugAuthenticateResponse): +class DebugAuthenticateResponseN4A(DebugAuthenticateResponse): """Class for N4A specific of DAR.""" + def _get_common_data(self) -> bytes: + """Collects dc, auth_beacon and UUID.""" + data = self.debug_credential.export() + data += pack(" bytes: """Sign the DAR data using SignatureProvider.""" signature = super()._get_signature() @@ -158,8 +168,8 @@ def _get_signature(self) -> bytes: return ecc_public_numbers_to_bytes(public_numbers=public_numbers, length=32) -class DebugAuthenticateResponseN4A_384(DebugAuthenticateResponse): - """Class for N4A specific of DAR.""" +class DebugAuthenticateResponseN4A_384(DebugAuthenticateResponseN4A): + """Class for N4A specific of DAR, 384 bits sized keys.""" def _get_signature(self) -> bytes: """Sign the DAR data using SignatureProvider.""" diff --git a/spsdk/dat/debug_credential.py b/spsdk/dat/debug_credential.py index 28dd4144..3bbee870 100644 --- a/spsdk/dat/debug_credential.py +++ b/spsdk/dat/debug_credential.py @@ -7,16 +7,13 @@ """Module with Debugcredential class.""" -from struct import pack, unpack_from, calcsize +from struct import calcsize, pack, unpack_from from typing import Any, List, Type -from spsdk import crypto +from spsdk import SPSDKError, crypto from spsdk.crypto import SignatureProvider -from spsdk.dat.utils import ( - ecc_public_numbers_to_bytes, - ecc_key_to_bytes, - rsa_key_to_bytes, -) +from spsdk.crypto.loaders import extract_public_key +from spsdk.dat.utils import ecc_key_to_bytes, ecc_public_numbers_to_bytes, rsa_key_to_bytes from spsdk.utils.crypto.backend_internal import internal_backend @@ -27,6 +24,7 @@ class DebugCredential: FORMAT = "INVALID_FORMAT" FORMAT_NO_SIG = "INVALID_FORMAT" VERSION = "0.0" + HASH_LENGTH = 32 def __init__( self, @@ -69,9 +67,11 @@ def export(self) -> bytes: """Export to binary form (serialization). :return: binary representation of the debug credential + :raises SPSDKError: When Debug Credential Signature is not set, call the .sign method first """ # make sure user called .sign before - assert self.signature, "Debug Credential Signature is not set, call the .sign method first" + if not self.signature: + raise SPSDKError("Debug Credential Signature is not set, call the .sign method first") data = pack( self.FORMAT, *[int(v) for v in self.VERSION.split(".")], @@ -102,9 +102,11 @@ def info(self) -> str: def sign(self) -> None: """Sign the DC data using SignatureProvider.""" - assert self.signature_provider, f"Debug Credential Signature provider is not set" + if not self.signature_provider: + raise SPSDKError("Debug Credential Signature provider is not set") signature = self.signature_provider.sign(self._get_data_to_sign()) - assert signature, f"Debug Credential Signature provider didn't return any signature" + if not signature: + raise SPSDKError("Debug Credential Signature provider didn't return any signature") self.signature = signature def _get_data_to_sign(self) -> bytes: @@ -195,8 +197,16 @@ def parse(cls, data: bytes, offset: int = 0) -> "DebugCredential": version = "{}.{}".format(*unpack_from("<2H", data, offset)) socc = unpack_from(" "DebugCredential": + """Returns instance of class from DAP authentication challenge data. + + :return: Instance of this class. + """ + _versionH, _versionL, *rest = unpack_from(cls.FORMAT, data, 0) + return cls(*rest) class DebugCredentialRSA(DebugCredential): @@ -216,7 +226,7 @@ def _get_rot_meta(used_root_cert: int, rot_pub_keys: List[str]) -> bytes: """ rot_meta = bytearray(128) for index, rot_key in enumerate(rot_pub_keys): - rot = crypto.load_public_key(file_path=rot_key) + rot = extract_public_key(file_path=rot_key, password=None) assert isinstance(rot, crypto.RSAPublicKey) data = rsa_key_to_bytes(key=rot, exp_length=3, modulus_length=None) result = internal_backend.hash(data) @@ -243,7 +253,7 @@ def _get_rot_pub(rot_pub_id: int, rot_pub_keys: List[str]) -> bytes: :return: binary representing the rotk public key """ pub_key_path = rot_pub_keys[rot_pub_id] - pub_key = crypto.load_public_key(pub_key_path) + pub_key = extract_public_key(file_path=pub_key_path, password=None) assert isinstance(pub_key, crypto.RSAPublicKey) return rsa_key_to_bytes(key=pub_key, exp_length=4) @@ -259,7 +269,8 @@ class DebugCredentialECC(DebugCredential): def sign(self) -> None: """Sign the DC data using SignatureProvider.""" super().sign() - assert self.signature, "Debug Credential Signature is not set in base class" + if not self.signature: + raise SPSDKError("Debug Credential Signature is not set in base class") r, s = crypto.utils_cryptography.decode_dss_signature(self.signature) public_numbers = crypto.EllipticCurvePublicNumbers(r, s, self.CURVE) self.signature = ecc_public_numbers_to_bytes( @@ -277,7 +288,7 @@ def _get_rot_meta(used_root_cert: int, rot_pub_keys: List[str]) -> bytes: """ rot_meta = bytearray(528) for index, rot_key in enumerate(rot_pub_keys): - rot = crypto.load_public_key(file_path=rot_key) + rot = extract_public_key(file_path=rot_key, password=None) assert isinstance(rot, crypto.EllipticCurvePublicKey) data = ecc_key_to_bytes(key=rot, length=66) rot_meta[index * 132 : (index + 1) * 132] = data @@ -306,9 +317,9 @@ def _get_rot_pub(rot_pub_id: int, rot_pub_keys: List[str]) -> bytes: :return: binary representation """ pub_key_path = rot_pub_keys[rot_pub_id] - pub_key = crypto.load_public_key(pub_key_path) + pub_key = extract_public_key(file_path=pub_key_path, password=None) assert isinstance(pub_key, crypto.EllipticCurvePublicKey) - curve_index = {256: 1, 386: 2, 521: 3}[pub_key.curve.key_size] + curve_index = {256: 1, 384: 2, 521: 3}[pub_key.curve.key_size] return pack("<2H", rot_pub_id, curve_index) @@ -385,7 +396,7 @@ def _get_rot_pub(rot_pub_id: int, rot_pub_keys: List[str]) -> bytes: :return: binary representing the rotk public key """ root_key = rot_pub_keys[rot_pub_id] - root_public_key = crypto.load_public_key(root_key) + root_public_key = extract_public_key(file_path=root_key, password=None) length = root_public_key.key_size // 8 assert isinstance(root_public_key, crypto.EllipticCurvePublicKey) data = ecc_key_to_bytes(root_public_key, length=length) @@ -402,13 +413,15 @@ def info(self) -> str: msg += f"CC_SOCC : {hex(self.cc_socu)}\n" msg += f"CC_VU : {hex(self.cc_vu)}\n" msg += f"BEACON : {self.cc_beacon}\n" - if self.rot_meta[3] == b"\x80": - msg += f"CA FLAG IS SET \n" ctrk_records_num = self.rot_meta[0] >> 4 if ctrk_records_num == 1: - msg += f"CRTK table not present \n" + msg += "CRTK table not present \n" else: msg += f"CRTK table has {ctrk_records_num} entries\n" + # Compute and show RKTH HASH + key_length = 256 if (len(self.rot_meta) - 4) == 32 else 384 + ctrk_hash = internal_backend.hash(data=self.rot_meta[4:], algorithm=f"sha{key_length}") + msg += f"CRTK Hash: {ctrk_hash.hex()}" return msg @property @@ -428,7 +441,7 @@ def create_ctrk_table(rot_pub_keys: List[str]) -> bytes: return bytes() ctrk_table = bytes() for pub_key_path in rot_pub_keys: - pub_key = crypto.load_public_key(pub_key_path) + pub_key = extract_public_key(file_path=pub_key_path, password=None) assert isinstance(pub_key, crypto.EllipticCurvePublicKey) key_length = pub_key.key_size data = ecc_key_to_bytes(key=pub_key, length=key_length // 8) @@ -492,6 +505,7 @@ def parse(cls, data: bytes, offset: int = 0) -> "DebugCredential": :param data: Raw data as bytes :param offset: Offset of input data :return: DebugCredential object + :raises SPSDKError: When flag is invalid """ format_head = "<2HL16s4L" ( @@ -504,7 +518,8 @@ def parse(cls, data: bytes, offset: int = 0) -> "DebugCredential": beacon, flags, ) = unpack_from(format_head, data) - assert flags & 0x8000_0000 + if not flags & 0x8000_0000: + raise SPSDKError("Invalid flag") records_num = (flags & 0xF0) >> 4 rot_meta_len = 4 ctrk_hash_table = bytes() @@ -532,6 +547,14 @@ def parse(cls, data: bytes, offset: int = 0) -> "DebugCredential": signature=signature, ) + @classmethod + def get_instance_from_challenge(cls, data: bytes) -> "DebugCredential": + """Returns instance of class from DAP authentication challenge data. + + :return: Instance of this class. + """ + return cls.parse(data, 0) + class DebugCredentialECC256N4Analog(N4AnalogMixin): """DebugCredential class for LPC55s3x for version 2.0 (p256).""" @@ -539,6 +562,7 @@ class DebugCredentialECC256N4Analog(N4AnalogMixin): HASH_LENGTH = 32 CORD_LENGTH = 32 KEY_LENGTH = 256 + VERSION = "2.0" class DebugCredentialECC384N4Analog(N4AnalogMixin): @@ -547,6 +571,7 @@ class DebugCredentialECC384N4Analog(N4AnalogMixin): HASH_LENGTH = 48 CORD_LENGTH = 48 KEY_LENGTH = 384 + VERSION = "2.1" _version_mapping = { diff --git a/spsdk/dat/debug_mailbox.py b/spsdk/dat/debug_mailbox.py index 3cfdfb05..95f15f81 100644 --- a/spsdk/dat/debug_mailbox.py +++ b/spsdk/dat/debug_mailbox.py @@ -10,8 +10,12 @@ from time import sleep from munch import munchify + +from spsdk import SPSDKError from spsdk.debuggers.debug_probe import DebugProbe -from spsdk.exceptions import SPSDKError +from spsdk.exceptions import SPSDKIOError +from spsdk.utils.exceptions import SPSDKTimeoutError +from spsdk.utils.misc import Timeout logger = logging.getLogger(__name__) @@ -23,8 +27,21 @@ class DebugMailboxError(RuntimeError): class DebugMailbox: """Class for DebugMailbox.""" - def __init__(self, debug_probe: DebugProbe, reset: bool = True, moredelay: float = 1.0) -> None: - """Initialize DebugMailbox object.""" + def __init__( + self, + debug_probe: DebugProbe, + reset: bool = True, + moredelay: float = 1.0, + op_timeout: int = 4000, + ) -> None: + """Initialize DebugMailbox object. + + :param debug_probe: Debug probe instantion. + :param reset: Do reset of debug mailbox during initialization, defaults to True. + :param moredelay: Time of extra delay after reset sequence, defaults to 1.0. + :param op_timeout: Atomic operation timeout, defaults to 4000. + :raises SPSDKIOError: Various kind of vulnerabilities during connection to debug mailbox. + """ # setup debug port / access point self.debug_probe = debug_probe @@ -32,6 +49,8 @@ def __init__(self, debug_probe: DebugProbe, reset: bool = True, moredelay: float self.moredelay = moredelay # setup registers and register bitfields self.registers = REGISTERS + # set internal operation timeout + self.op_timeout = op_timeout # Proceed with initiation (Resynchronization request) @@ -67,31 +86,48 @@ def __init__(self, debug_probe: DebugProbe, reset: bool = True, moredelay: float while ret is None or (ret & self.registers.CSW.bits.REQ_PENDING): try: ret = self.debug_probe.dbgmlbx_reg_read(addr=self.registers.CSW.address) - except SPSDKError as e: - retries -= 1 - if retries == 0: - retries = 20 - raise IOError("TransferTimeoutError limit exceeded!") - sleep(0.05) + except SPSDKError: + pass + retries -= 1 + if retries == 0: + raise SPSDKIOError("TransferTimeoutError limit exceeded!") + sleep(0.05) def close(self) -> None: """Close session.""" self.debug_probe.close() def spin_read(self, reg: int) -> int: - """Read.""" + """Do atomic read operation to debugmailbox. + + :param reg: Register address. + :return: Read value. + :raises SPSDKTimeoutError: When read operation exceed defined operation timeout. + """ ret = None + timeout = Timeout(self.op_timeout, units="ms") while ret is None: try: ret = self.debug_probe.dbgmlbx_reg_read(addr=reg) except SPSDKError as e: logger.error(str(e)) logger.error(f"read exception {reg:#08X}") + if timeout.overflow(): + raise SPSDKTimeoutError( + f"The Debug Mailbox read operation ends on timeout. ({str(e)})" + ) from e sleep(0.01) + return ret def spin_write(self, reg: int, value: int) -> None: - """Write.""" + """Do atomic write operation to debugmailbox. + + :param reg: Register address. + :param value: Value to write. + :raises SPSDKTimeoutError: When write operation exceed defined operation timeout. + """ + timeout = Timeout(self.op_timeout, units="ms") while True: try: self.debug_probe.dbgmlbx_reg_write(addr=reg, data=value) @@ -100,11 +136,17 @@ def spin_write(self, reg: int, value: int) -> None: ret = self.debug_probe.dbgmlbx_reg_read(addr=self.registers.CSW.address) if (ret & self.registers.CSW.bits.REQ_PENDING) == 0: break + if timeout.overflow(): + raise SPSDKTimeoutError("Mailbox command request pending timeout.") return except SPSDKError as e: logger.error(str(e)) logger.error(f"write exception addr={reg:#08X}, val={value:#08X}") + if timeout.overflow(): + raise SPSDKTimeoutError( + f"The Debug Mailbox write operation ends on timeout. ({str(e)})" + ) from e sleep(0.01) @@ -115,7 +157,7 @@ def spin_write(self, reg: int, value: int) -> None: "CSW": { "address": 0x00, "bits": { - # Debugger will set this bit to 1 to request a resynchronrisation + # Debugger will set this bit to 1 to request a resynchronization "RESYNCH_REQ": (1 << 0), # Request is pending from debugger (i.e unread value in REQUEST) "REQ_PENDING": (1 << 1), diff --git a/spsdk/dat/dm_commands.py b/spsdk/dat/dm_commands.py index b34cbf1b..01dfac82 100644 --- a/spsdk/dat/dm_commands.py +++ b/spsdk/dat/dm_commands.py @@ -9,7 +9,9 @@ import time from typing import Any, List +from spsdk import SPSDKError from spsdk.utils.misc import format_value + from .debug_mailbox import DebugMailbox, logger @@ -34,7 +36,7 @@ def __init__( def run(self, params: list = []) -> List[Any]: """Run DebugMailboxCommand.""" if len(params) != self.paramlen: - raise ValueError( + raise SPSDKError( "Provided parameters length is not equal to command parameters length!" ) @@ -49,9 +51,9 @@ def run(self, params: list = []) -> List[Any]: ret = self.dm.spin_read(self.dm.registers.RETURN.address) logger.debug(f"-> spin_read: {format_value(ret, 32)}") if (ret & 0xFFFF) != 0xA5A5: - raise IOError("Device did not send correct ACK answer!") + raise SPSDKError("Device did not send correct ACK answer!") if ((ret >> 16) & 0xFFFF) != (self.paramlen - i): - raise IOError("Device expects parameters of different length we can provide!") + raise SPSDKError("Device expects parameters of different length we can provide!") logger.debug(f"<- spin_write: {format_value(params[i], 32)}") self.dm.spin_write(self.dm.registers.REQUEST.address, params[i]) @@ -59,11 +61,12 @@ def run(self, params: list = []) -> List[Any]: logger.debug(f"-> spin_read: {format_value(ret, 32)}") resplen = (ret >> 16) & 0x7FFF status = ret & 0xFFFF + if resplen != self.resplen: # MSB is used to show it is the new protocol -> 0x7FFF - raise IOError("Device wants to send us different size than expected!") + raise SPSDKError("Device wants to send us different size than expected!") if status != 0: - raise IOError(f"Status code is not success: {ret & 0xFFFF} !") + raise SPSDKError(f"Status code is not success: {ret & 0xFFFF} !") # do not send ack, in case no data follows if resplen == 0: @@ -144,10 +147,13 @@ def __init__(self, dm: DebugMailbox) -> None: class DebugAuthenticationStart(DebugMailboxCommand): """Class for DebugAuthenticationStart.""" - def __init__(self, dm: DebugMailbox) -> None: + def __init__(self, dm: DebugMailbox, resplen: int = 26) -> None: """Initialize.""" - # 26 words == 104 bytes - super(DebugAuthenticationStart, self).__init__(dm, id=16, name="DBG_AUTH_START", resplen=26) + # 26 words == 104 bytes (SHA256 - 32 Bytes) + # 30 words == 120 bytes (SHA384 - 48 Bytes) + super(DebugAuthenticationStart, self).__init__( + dm, id=16, name="DBG_AUTH_START", resplen=resplen + ) class DebugAuthenticationResponse(DebugMailboxCommand): diff --git a/spsdk/data/nxpcertgen/certgen_config.yml b/spsdk/data/nxpcertgen/certgen_config.yml new file mode 100644 index 00000000..cf9bb113 --- /dev/null +++ b/spsdk/data/nxpcertgen/certgen_config.yml @@ -0,0 +1,52 @@ +# This is template for configuration file used for generating certificates + +# ============================================== +# Issuer identification fields +# ============================================== +# All available option can be found within class NameOID in +# cryptography/src/cryptography/x509/oid.py at https://github.com/pyca/cryptography + +issuer: + COMMON_NAME: NXP + COUNTRY_NAME: CZ + LOCALITY_NAME: Roznov pod Radhostem + STATE_OR_PROVINCE_NAME: Morava + STREET_ADDRESS: 1.maje 1009 + ORGANIZATION_NAME: SPSDK Team + +# ============================================== +# Subject identification fields +# ============================================== +# All available option can be found within class NameOID in +# cryptography/src/cryptography/x509/oid.py at https://github.com/pyca/cryptography +subject: + COMMON_NAME: NXP - SPSDK + COUNTRY_NAME: CZ + LOCALITY_NAME: Roznov pod Radhostem + STATE_OR_PROVINCE_NAME: Morava + STREET_ADDRESS: 1.maje 1009 + ORGANIZATION_NAME: SPSDK Team + POSTAL_CODE: 756 61 + +# ============================================== +# The certificate settings +# ============================================== + +# Path, where issuer private key is stored +issuer_private_key: issuer_key.pem +# Path, where subject public key is stored +subject_public_key: subject_key.pub +# Serial number of certificate +serial_number: 12346578 +# Validity duration in days +duration: 3650 + +# ============================================== +# Certificate basic extensions +# ============================================== +extensions: + BASIC_CONSTRAINTS: + # Delegate certificate as a signing authority to create an intermediate certificates. + ca: false # Valid values true|false + # Integer length of the path of certificate signature from a given certificate, back to the root certificate + path_length: 0 diff --git a/spsdk/data/nxpkeygen/template_config.yml b/spsdk/data/nxpkeygen/template_config.yml index b631179b..e14d3b49 100644 --- a/spsdk/data/nxpkeygen/template_config.yml +++ b/spsdk/data/nxpkeygen/template_config.yml @@ -33,7 +33,6 @@ # ============================================ # ============================================ - # ============ SoC Class ============ # A unique identifier for a set of SoCs that require no SoC-specific differentiation in # their debug authentication. The main usage is to allow a different set of debug @@ -41,7 +40,8 @@ # credentials. A class can contain just a single revision of a single SoC model, if the # granularity of debug control warrants it. # Examples list of possible settings: -# 0x0001: LPC550x, LPC55s0x, LPC551x, LPC55s1x, LPC552x, LPC55s2x, LPC55s3x, LPC55s6x +# 0x0001: LPC550x, LPC55s0x, LPC551x, LPC55s1x, LPC552x, LPC55s2x, LPC55s6x +# 0x0004: LPC55s3x socc: 0x0001 @@ -67,7 +67,7 @@ cc_vu: 0 # credential beacon is associated with a DC and is therefore vendor/RoT-signed. An # authentication beacon is provided and signed by the debugger during the # authentication process. -cred_beacon: 0 +cc_beacon: 0 # ============ RoT meta-data ============ # The RoT meta-data required by the device to corroborate; the ROTID sent in the @@ -75,10 +75,10 @@ cred_beacon: 0 # device. This allows different RoT identification, management and revocation # solutions to be handled. rot_meta: - - ./rotk0.pub - - ./rotk1.pub - - ./rotk2.pub - - ./rotk3.pub + - ./rotk0.pub + - ./rotk1.pub + - ./rotk2.pub + - ./rotk3.pub # ============ RoT Identifier ============ # RoTID allows the debugger to infer which RoT public key(s) are acceptable to the @@ -95,9 +95,9 @@ dck: dck.pub # ================================================================================================== # Signature configuration area # ================================================================================================== -# There are two ways how sign the final DC data blob. +# There are two ways how sign the final DC data blob. # -# 1. In case that you is available private pair for rot_meta with index rot_id just use first simple style +# 1. In case that you is available private pair for rot_meta with index rot_id just use first simple style # to use it by rotk key. As a second way to do same is use sign_provider option with 'type=file'. # # 2. For case that Debug Credential files are generated in untrusted environment (without access to RoT private keys), @@ -107,14 +107,12 @@ dck: dck.pub # # Those options are exclusive, so only one option could be used to sign the DC. - # ============ Signature Provider ============ # To use signing provider examples -# +# # sign_provider: 'type=file;file_path=./hsm/k0_cert0_2048.pem' # sign_provider: : 'type=sasp;key_number=0' # ============ RoT signature private key ============ # Private key for for the RoT meta chosen by rot_id to sign the image. rotk: rotk0.pem - diff --git a/spsdk/data/pfr/cfpa/database.json b/spsdk/data/pfr/cfpa/database.json index 348d2ab7..0b99e46a 100644 --- a/spsdk/data/pfr/cfpa/database.json +++ b/spsdk/data/pfr/cfpa/database.json @@ -59,7 +59,8 @@ "latest": "0a", "address": "0x3_DC00", "seal_start": "CFPA_CRC32", - "seal_count": 1 + "seal_count": 1, + "mandatory_computed_regs": 1 }, "lpc553x": { "revisions": { @@ -68,7 +69,8 @@ "latest": "0a", "address": "0x3_DC00", "seal_start": "CFPA_CRC32", - "seal_count": 1 + "seal_count": 1, + "mandatory_computed_regs": 1 } }, "computed_registers": [ @@ -92,6 +94,7 @@ ], "seal_start": "SHA256_DIGEST0", "seal_count": 8, + "mandatory_computed_regs": 0, "backward_compatibility": { "DCFG_CC_SOCU_PIN": { "bitfields": { @@ -120,4 +123,4 @@ } } } -} \ No newline at end of file +} diff --git a/spsdk/data/pfr/cfpa/lpc553x_0a.xml b/spsdk/data/pfr/cfpa/lpc553x_0a.xml index edf69076..23f30026 100644 --- a/spsdk/data/pfr/cfpa/lpc553x_0a.xml +++ b/spsdk/data/pfr/cfpa/lpc553x_0a.xml @@ -167,7 +167,7 @@ - + @@ -220,7 +220,7 @@ - + @@ -280,7 +280,7 @@ - + @@ -289,7 +289,7 @@ - + diff --git a/spsdk/data/pfr/cfpa/lpc55s3x_0a.xml b/spsdk/data/pfr/cfpa/lpc55s3x_0a.xml index edf69076..23f30026 100644 --- a/spsdk/data/pfr/cfpa/lpc55s3x_0a.xml +++ b/spsdk/data/pfr/cfpa/lpc55s3x_0a.xml @@ -167,7 +167,7 @@ - + @@ -220,7 +220,7 @@ - + @@ -280,7 +280,7 @@ - + @@ -289,7 +289,7 @@ - + diff --git a/spsdk/data/pfr/cmpa/database.json b/spsdk/data/pfr/cmpa/database.json index 6b2b03d3..611e881f 100644 --- a/spsdk/data/pfr/cmpa/database.json +++ b/spsdk/data/pfr/cmpa/database.json @@ -60,6 +60,7 @@ "address": "0x3_E200", "seal_start": "CMPA_CMAC0", "seal_count": 4, + "mandatory_computed_regs": 1, "grouped_registers": [ { "name": "ROTKH", @@ -77,6 +78,7 @@ "address": "0x3_E200", "seal_start": "CMPA_CMAC0", "seal_count": 4, + "mandatory_computed_regs": 1, "grouped_registers": [ { "name": "ROTKH", @@ -111,6 +113,7 @@ ], "seal_start": "SHA256_DIGEST0", "seal_count": 8, + "mandatory_computed_regs": 0, "backward_compatibility": { "CC_SOCU_PIN": { "bitfields": { diff --git a/spsdk/data/pfr/cmpa/lpc553x_0a.xml b/spsdk/data/pfr/cmpa/lpc553x_0a.xml index 2f4743b1..79e685dd 100644 --- a/spsdk/data/pfr/cmpa/lpc553x_0a.xml +++ b/spsdk/data/pfr/cmpa/lpc553x_0a.xml @@ -100,7 +100,7 @@ - + @@ -109,7 +109,7 @@ - + @@ -160,7 +160,7 @@ - + @@ -546,7 +546,7 @@ - + @@ -628,7 +628,7 @@ - + @@ -657,7 +657,7 @@ - + @@ -671,14 +671,14 @@ - + - + @@ -692,7 +692,7 @@ - + @@ -700,7 +700,7 @@ - + @@ -714,14 +714,14 @@ - + - + @@ -735,7 +735,7 @@ - + diff --git a/spsdk/data/pfr/cmpa/lpc55s3x_0a.xml b/spsdk/data/pfr/cmpa/lpc55s3x_0a.xml index 2f4743b1..79e685dd 100644 --- a/spsdk/data/pfr/cmpa/lpc55s3x_0a.xml +++ b/spsdk/data/pfr/cmpa/lpc55s3x_0a.xml @@ -100,7 +100,7 @@ - + @@ -109,7 +109,7 @@ - + @@ -160,7 +160,7 @@ - + @@ -546,7 +546,7 @@ - + @@ -628,7 +628,7 @@ - + @@ -657,7 +657,7 @@ - + @@ -671,14 +671,14 @@ - + - + @@ -692,7 +692,7 @@ - + @@ -700,7 +700,7 @@ - + @@ -714,14 +714,14 @@ - + - + @@ -735,7 +735,7 @@ - + diff --git a/spsdk/debuggers/debug_probe.py b/spsdk/debuggers/debug_probe.py index a96d7519..708cc39e 100644 --- a/spsdk/debuggers/debug_probe.py +++ b/spsdk/debuggers/debug_probe.py @@ -8,34 +8,34 @@ from typing import Dict -from spsdk.exceptions import SPSDKError +from spsdk import SPSDKError -class DebugProbeError(SPSDKError): +class SPSDKDebugProbeError(SPSDKError): """The general issue with debug probe exception for use with SPSDK.""" -class ProbeNotFoundError(DebugProbeError): +class SPSDKProbeNotFoundError(SPSDKDebugProbeError): """The Probe not found exception for use with SPSDK.""" -class DebugMailBoxAPNotFoundError(DebugProbeError): +class SPSDKDebugMailBoxAPNotFoundError(SPSDKDebugProbeError): """The target doesn't have debug mailbox access port exception for use with SPSDK.""" -class DebugProbeTransferError(DebugProbeError): +class SPSDKDebugProbeTransferError(SPSDKDebugProbeError): """The communication error exception for use with SPSDK.""" -class DebugProbeNotOpenError(DebugProbeError): +class SPSDKDebugProbeNotOpenError(SPSDKDebugProbeError): """The debug probe is not opened exception for use with SPSDK.""" -class DebugProbeMemoryInterfaceAPNotFoundError(DebugProbeError): +class SPSDKDebugProbeMemoryInterfaceAPNotFoundError(SPSDKDebugProbeError): """The target doesn't have memory interface access port exception for use with SPSDK.""" -class DebugProbeMemoryInterfaceNotEnabled(DebugProbeError): +class SPSDKDebugProbeMemoryInterfaceNotEnabled(SPSDKDebugProbeError): """The target doesn't have memory interface enabled exception for use with SPSDK.""" diff --git a/spsdk/debuggers/debug_probe_jlink.py b/spsdk/debuggers/debug_probe_jlink.py index a7342e02..906e10de 100644 --- a/spsdk/debuggers/debug_probe_jlink.py +++ b/spsdk/debuggers/debug_probe_jlink.py @@ -8,7 +8,7 @@ import logging from time import sleep -from typing import Dict +from typing import Dict, Type import pylink import pylink.protocols.swd as swd @@ -16,10 +16,10 @@ from .debug_probe import ( DebugProbe, - DebugProbeError, - DebugProbeMemoryInterfaceNotEnabled, - DebugProbeNotOpenError, - DebugProbeTransferError, + SPSDKDebugProbeError, + SPSDKDebugProbeMemoryInterfaceNotEnabled, + SPSDKDebugProbeNotOpenError, + SPSDKDebugProbeTransferError, ) logger = logging.getLogger(__name__) @@ -45,7 +45,7 @@ def get_jlink_lib(cls) -> pylink.JLink: """Get J-Link object. :return: The J-Link Object - :raises DebugProbeError: The J-Link object get function failed. + :raises SPSDKDebugProbeError: The J-Link object get function failed. """ try: return pylink.JLink( @@ -55,7 +55,7 @@ def get_jlink_lib(cls) -> pylink.JLink: warn=JLINK_LOGGER.warn, ) except TypeError: - raise DebugProbeError("Cannot open Jlink DLL") + raise SPSDKDebugProbeError("Cannot open Jlink DLL") def __init__(self, hardware_id: str, user_params: Dict = None) -> None: """The PyLink class initialization. @@ -113,14 +113,14 @@ def open(self) -> None: The function is used to initialize the connection to target and enable using debug probe for DAT purposes. - :raises DebugProbeError: The PyLink cannot establish communication with target + :raises SPSDKDebugProbeError: The PyLink cannot establish communication with target """ try: self.pylink = DebugProbePyLink.get_jlink_lib() if self.pylink is None: - raise DebugProbeError("Getting of J-Link library failed.") - except DebugProbeError as exc: - raise DebugProbeError(f"Getting of J-Link library failed({str(exc)}).") + raise SPSDKDebugProbeError("Getting of J-Link library failed.") + except SPSDKDebugProbeError as exc: + raise SPSDKDebugProbeError(f"Getting of J-Link library failed({str(exc)}).") from exc try: @@ -134,7 +134,7 @@ def open(self) -> None: # Select ISP - AP if self.dbgmlbx_ap_ix == -1: if debug_mbox_ap_ix == -1: - raise DebugProbeError("The Debug mailbox access port is not available!") + raise SPSDKDebugProbeError("The Debug mailbox access port is not available!") self.dbgmlbx_ap_ix = debug_mbox_ap_ix else: if debug_mbox_ap_ix != self.dbgmlbx_ap_ix: @@ -143,7 +143,9 @@ def open(self) -> None: ) except JLinkException as exc: - raise DebugProbeError(f"PyLink cannot establish communication with target({str(exc)}).") + raise SPSDKDebugProbeError( + f"PyLink cannot establish communication with target({str(exc)})." + ) from exc def enable_memory_interface(self) -> None: """Debug probe enabling memory interface. @@ -152,16 +154,18 @@ def enable_memory_interface(self) -> None: to support various DEBUG PROBES. The function is used to initialize the target memory interface and enable using memory access of target over debug probe. - :raises DebugProbeNotOpenError: The PyLink probe is NOT opened - :raises DebugProbeError: Error with connection to target. + :raises SPSDKDebugProbeNotOpenError: The PyLink probe is NOT opened + :raises SPSDKDebugProbeError: Error with connection to target. """ if self.pylink is None: - raise DebugProbeNotOpenError("The PyLink debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyLink debug probe is not opened yet") try: self.pylink.connect(chip_name="Cortex-M33") self.enabled_memory_interface = True except JLinkException as exc: - raise DebugProbeError(f"PyLink cannot establish connection with target({str(exc)}).") + raise SPSDKDebugProbeError( + f"PyLink cannot establish connection with target({str(exc)})." + ) from exc def close(self) -> None: """Close PyLink interface. @@ -199,14 +203,15 @@ def mem_reg_read(self, addr: int = 0) -> int: :param addr: the register address :return: The read value of addressed register (4 bytes) - :raises DebugProbeNotOpenError: The PyLink probe is NOT opened - :raises DebugProbeMemoryInterfaceNotEnabled: The PyLink is using just CoreSight access. + :raises SPSDKDebugProbeNotOpenError: The PyLink probe is NOT opened + :raises SPSDKDebugProbeMemoryInterfaceNotEnabled: The PyLink is using just CoreSight access. + :raises SPSDKDebugProbeTransferError: The IO operation failed """ if self.pylink is None: - raise DebugProbeNotOpenError("The PyLink debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyLink debug probe is not opened yet") if not self.enabled_memory_interface: - raise DebugProbeMemoryInterfaceNotEnabled( + raise SPSDKDebugProbeMemoryInterfaceNotEnabled( "Memory interface is not enabled over J-Link." ) @@ -214,8 +219,9 @@ def mem_reg_read(self, addr: int = 0) -> int: reg = [0] try: reg = self.pylink.memory_read32(addr=addr, num_words=1) - except JLinkException as exc: + except (JLinkException, ValueError, TypeError) as exc: logger.error(f"Failed read memory({str(exc)}).") + raise SPSDKDebugProbeTransferError(f"Failed read memory({str(exc)}).") from exc return reg[0] def mem_reg_write(self, addr: int = 0, data: int = 0) -> None: @@ -226,14 +232,15 @@ def mem_reg_write(self, addr: int = 0, data: int = 0) -> None: :param addr: the register address :param data: the data to be written into register - :raises DebugProbeNotOpenError: The PyLink probe is NOT opened - :raises DebugProbeMemoryInterfaceNotEnabled: The PyLink is using just CoreSight access. + :raises SPSDKDebugProbeNotOpenError: The PyLink probe is NOT opened + :raises SPSDKDebugProbeMemoryInterfaceNotEnabled: The PyLink is using just CoreSight access. + :raises SPSDKDebugProbeTransferError: The IO operation failed """ if self.pylink is None: - raise DebugProbeNotOpenError("The PyLink debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyLink debug probe is not opened yet") if not self.enabled_memory_interface: - raise DebugProbeMemoryInterfaceNotEnabled( + raise SPSDKDebugProbeMemoryInterfaceNotEnabled( "Memory interface is not enabled over J-Link." ) @@ -242,8 +249,9 @@ def mem_reg_write(self, addr: int = 0, data: int = 0) -> None: data_list = list() data_list.append(data) self.pylink.memory_write32(addr=addr, data=data_list) - except JLinkException as exc: + except (JLinkException, ValueError, TypeError) as exc: logger.error(f"Failed write memory({str(exc)}).") + raise SPSDKDebugProbeTransferError(f"Failed write memory({str(exc)}).") from exc def coresight_reg_read(self, access_port: bool = True, addr: int = 0) -> int: """Read coresight register over PyLink interface. @@ -253,12 +261,12 @@ def coresight_reg_read(self, access_port: bool = True, addr: int = 0) -> int: :param access_port: if True, the Access Port (AP) register will be read(default), otherwise the Debug Port :param addr: the register address :return: The read value of addressed register (4 bytes) - :raises DebugProbeTransferError: The IO operation failed - :raises DebugProbeNotOpenError: The PyLink probe is NOT opened + :raises SPSDKDebugProbeTransferError: The IO operation failed + :raises SPSDKDebugProbeNotOpenError: The PyLink probe is NOT opened """ if self.pylink is None: - raise DebugProbeNotOpenError("The PyLink debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyLink debug probe is not opened yet") try: if access_port: @@ -278,10 +286,10 @@ def coresight_reg_read(self, access_port: bool = True, addr: int = 0) -> int: return response.data return self.pylink.coresight_read(reg=addr // 4, ap=access_port) - except JLinkException as exc: + except (JLinkException, ValueError, TypeError) as exc: # In case of transaction error reconfigure and initialize the JLink self._reinit_jlink_target() - raise DebugProbeTransferError( + raise SPSDKDebugProbeTransferError( f"The Coresight read operation failed({str(exc)})." ) from exc @@ -293,11 +301,11 @@ def coresight_reg_write(self, access_port: bool = True, addr: int = 0, data: int :param access_port: if True, the Access Port (AP) register will be write(default), otherwise the Debug Port :param addr: the register address :param data: the data to be written into register - :raises DebugProbeTransferError: The IO operation failed - :raises DebugProbeNotOpenError: The PyLink probe is NOT opened + :raises SPSDKDebugProbeTransferError: The IO operation failed + :raises SPSDKDebugProbeNotOpenError: The PyLink probe is NOT opened """ if self.pylink is None: - raise DebugProbeNotOpenError("The PyLink debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyLink debug probe is not opened yet") try: if access_port: @@ -310,14 +318,14 @@ def coresight_reg_write(self, access_port: bool = True, addr: int = 0, data: int request = swd.WriteRequest(addr // 4, data=data, ap=access_port) response = request.send(self.pylink) if not response.ack(): - raise DebugProbeTransferError("No ack from JLink") + raise SPSDKDebugProbeTransferError("No ack from JLink") else: self.pylink.coresight_write(reg=addr // 4, data=data, ap=access_port) - except JLinkException as exc: + except (JLinkException, ValueError, TypeError) as exc: # In case of transaction error reconfigure and initialize the JLink self._reinit_jlink_target() - raise DebugProbeTransferError( + raise SPSDKDebugProbeTransferError( f"The Coresight write operation failed({str(exc)})." ) from exc @@ -326,10 +334,10 @@ def reset(self) -> None: It resets a target. - :raises DebugProbeNotOpenError: The PyLink probe is NOT opened + :raises SPSDKDebugProbeNotOpenError: The PyLink probe is NOT opened """ if self.pylink is None: - raise DebugProbeNotOpenError("The PyLink debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyLink debug probe is not opened yet") self.pylink.reset() @@ -355,13 +363,13 @@ def _get_dmbox_ap(self) -> int: This is helper function to find and return the debug mailbox access port index. :return: Debug MailBox Access Port Index if found, otherwise -1 - :raises DebugProbeNotOpenError: The Segger JLink probe is NOT opened + :raises SPSDKDebugProbeNotOpenError: The Segger JLink probe is NOT opened """ idr_expected = 0x002A0000 idr_address = 0xFC if self.pylink is None: - raise DebugProbeNotOpenError("The Segger debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The Segger debug probe is not opened yet") logger.debug("Looking for debug mailbox access port") for access_port_ix in range(256): @@ -381,6 +389,6 @@ def _get_dmbox_ap(self) -> int: logger.debug(f"The AP({access_port_ix}) is not available") except JLinkException: logger.debug(f"The AP({access_port_ix}) is not available") - except DebugProbeTransferError: + except SPSDKDebugProbeTransferError: logger.debug(f"The AP({access_port_ix}) is not available") return -1 diff --git a/spsdk/debuggers/debug_probe_pemicro.py b/spsdk/debuggers/debug_probe_pemicro.py index 799858df..04f86ca3 100644 --- a/spsdk/debuggers/debug_probe_pemicro.py +++ b/spsdk/debuggers/debug_probe_pemicro.py @@ -9,18 +9,17 @@ import logging from typing import Dict, Optional -from pypemicro import PyPemicro, PEMicroException, PEMicroInterfaces +from pypemicro import PEMicroException, PEMicroInterfaces, PyPemicro -from spsdk.exceptions import SPSDKError +from spsdk import SPSDKError from .debug_probe import ( DebugProbe, - DebugProbeTransferError, - DebugProbeNotOpenError, - DebugProbeError, + SPSDKDebugProbeError, + SPSDKDebugProbeNotOpenError, + SPSDKDebugProbeTransferError, ) - logger = logging.getLogger(__name__) logger.setLevel(logging.CRITICAL) PEMICRO_LOGGER = logger.getChild("PyPemicro") @@ -34,7 +33,7 @@ def get_pemicro_lib(cls) -> PyPemicro: """Get J-Link object. :return: The J-Link Object - :raises DebugProbeError: The J-Link object get function failed. + :raises SPSDKDebugProbeError: The J-Link object get function failed. """ return PyPemicro( log_info=PEMICRO_LOGGER.info, @@ -87,31 +86,31 @@ def open(self) -> None: The function is used to initialize the connection to target and enable using debug probe for DAT purposes. - :raises DebugProbeError: The Pemicro cannot establish communication with target + :raises SPSDKDebugProbeError: The Pemicro cannot establish communication with target """ try: self.pemicro = DebugProbePemicro.get_pemicro_lib() if self.pemicro is None: - raise DebugProbeError(f"Getting of J-Link library failed.") - except DebugProbeError as exc: - raise DebugProbeError(f"Getting of J-Link library failed({str(exc)}).") + raise SPSDKDebugProbeError(f"Getting of J-Link library failed.") + except SPSDKDebugProbeError as exc: + raise SPSDKDebugProbeError(f"Getting of J-Link library failed({str(exc)}).") try: self.pemicro.open(debug_hardware_name_ip_or_serialnum=self.hardware_id) self.pemicro.connect(PEMicroInterfaces.SWD) # type: ignore dbgmlbx_ap_ix = self._get_dmbox_ap() except PEMicroException as exc: - raise DebugProbeError( + raise SPSDKDebugProbeError( f"Pemicro cannot establish communication with target({str(exc)})." ) if self.dbgmlbx_ap_ix == -1: if dbgmlbx_ap_ix == -1: - raise DebugProbeError(f"The Debug mailbox access port is not available!") + raise SPSDKDebugProbeError("The Debug mailbox access port is not available!") self.dbgmlbx_ap_ix = dbgmlbx_ap_ix else: if dbgmlbx_ap_ix != self.dbgmlbx_ap_ix: logger.info( - f"The detected debug mailbox accessport index is different to specified." + "The detected debug mailbox accessport index is different to specified." ) def close(self) -> None: @@ -130,11 +129,11 @@ def mem_reg_read(self, addr: int = 0) -> int: :param addr: the register address :return: The read value of addressed register (4 bytes) - :raises DebugProbeNotOpenError: The Pemicro probe is NOT opened + :raises SPSDKDebugProbeNotOpenError: The Pemicro probe is NOT opened :raises SPSDKError: The Pemicro probe has failed during read operation """ if self.pemicro is None: - raise DebugProbeNotOpenError("The Pemicro debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The Pemicro debug probe is not opened yet") self.last_access_memory = True reg = 0 @@ -142,7 +141,7 @@ def mem_reg_read(self, addr: int = 0) -> int: reg = self.pemicro.read_32bit(addr) except PEMicroException as exc: logger.error(f"Failed read memory({str(exc)}).") - raise SPSDKError(str(exc)) + raise SPSDKError(str(exc)) from exc return reg def mem_reg_write(self, addr: int = 0, data: int = 0) -> None: @@ -153,18 +152,18 @@ def mem_reg_write(self, addr: int = 0, data: int = 0) -> None: :param addr: the register address :param data: the data to be written into register - :raises DebugProbeNotOpenError: The Pemicro probe is NOT opened + :raises SPSDKDebugProbeNotOpenError: The Pemicro probe is NOT opened :raises SPSDKError: The Pemicro probe has failed during write operation """ if self.pemicro is None: - raise DebugProbeNotOpenError("The Pemicro debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The Pemicro debug probe is not opened yet") self.last_access_memory = True try: self.pemicro.write_32bit(address=addr, data=data) except PEMicroException as exc: logger.error(f"Failed write memory({str(exc)}).") - raise SPSDKError(str(exc)) + raise SPSDKError(str(exc)) from exc def dbgmlbx_reg_read(self, addr: int = 0) -> int: """Read debug mailbox access port register. @@ -196,11 +195,11 @@ def coresight_reg_read(self, access_port: bool = True, addr: int = 0) -> int: :param access_port: if True, the Access Port (AP) register will be read(default), otherwise the Debug Port :param addr: the register address :return: The read value of addressed register (4 bytes) - :raises DebugProbeTransferError: The IO operation failed - :raises DebugProbeNotOpenError: The Pemicro probe is NOT opened + :raises SPSDKDebugProbeTransferError: The IO operation failed + :raises SPSDKDebugProbeNotOpenError: The Pemicro probe is NOT opened """ if self.pemicro is None: - raise DebugProbeNotOpenError("The Pemicro debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The Pemicro debug probe is not opened yet") try: if self.last_access_memory: @@ -213,7 +212,9 @@ def coresight_reg_read(self, access_port: bool = True, addr: int = 0) -> int: ret = self.pemicro.read_dp_register(addr=addr) return ret except PEMicroException as exc: - raise DebugProbeTransferError(f"The Coresight read operation failed({str(exc)}).") + raise SPSDKDebugProbeTransferError( + f"The Coresight read operation failed({str(exc)})." + ) from exc def coresight_reg_write(self, access_port: bool = True, addr: int = 0, data: int = 0) -> None: """Write coresight register over Pemicro interface. @@ -223,11 +224,11 @@ def coresight_reg_write(self, access_port: bool = True, addr: int = 0, data: int :param access_port: if True, the Access Port (AP) register will be write(default), otherwise the Debug Port :param addr: the register address :param data: the data to be written into register - :raises DebugProbeTransferError: The IO operation failed - :raises DebugProbeNotOpenError: The Pemicro probe is NOT opened + :raises SPSDKDebugProbeTransferError: The IO operation failed + :raises SPSDKDebugProbeNotOpenError: The Pemicro probe is NOT opened """ if self.pemicro is None: - raise DebugProbeNotOpenError("The Pemicro debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The Pemicro debug probe is not opened yet") try: if self.last_access_memory: @@ -240,17 +241,19 @@ def coresight_reg_write(self, access_port: bool = True, addr: int = 0, data: int self.pemicro.write_dp_register(addr=addr, value=data) except PEMicroException as exc: - raise DebugProbeTransferError(f"The Coresight write operation failed({str(exc)}).") + raise SPSDKDebugProbeTransferError( + f"The Coresight write operation failed({str(exc)})." + ) from exc def reset(self) -> None: """Reset a target. It resets a target. - :raises DebugProbeNotOpenError: The Pemicro debug probe is not opened yet + :raises SPSDKDebugProbeNotOpenError: The Pemicro debug probe is not opened yet """ if self.pemicro is None: - raise DebugProbeNotOpenError("The Pemicro debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The Pemicro debug probe is not opened yet") try: self.pemicro.reset_target() @@ -264,15 +267,15 @@ def _get_dmbox_ap(self) -> int: This is helper function to find and return the debug mailbox access port index. :return: Debug MailBox Access Port Index if found, otherwise -1 - :raises DebugProbeNotOpenError: The PEMicro probe is NOT opened + :raises SPSDKDebugProbeNotOpenError: The PEMicro probe is NOT opened """ idr_expected = 0x002A0000 idr_address = 0xFC if self.pemicro is None: - raise DebugProbeNotOpenError("The Pemicro debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The Pemicro debug probe is not opened yet") - logger.debug(f"Looking for debug mailbox access port") + logger.debug("Looking for debug mailbox access port") for access_port_ix in range(256): try: @@ -280,12 +283,11 @@ def _get_dmbox_ap(self) -> int: (access_port_ix << self.APSEL_SHIFT) & self.APSEL_APBANKSEL ) ret = self.pemicro.read_ap_register(apselect=access_port_ix, addr=address) - if ret == idr_expected: logger.debug(f"Found debug mailbox ix:{access_port_ix}") return access_port_ix if ret != 0: - logger.debug(f"Found general access port ix:{access_port_ix}") + logger.debug(f"Found general access port ix:{access_port_ix}, IDR:{ret}") else: logger.debug(f"The AP({access_port_ix}) is not available") except PEMicroException: diff --git a/spsdk/debuggers/debug_probe_pyocd.py b/spsdk/debuggers/debug_probe_pyocd.py index 204ad4c2..af6e6b75 100644 --- a/spsdk/debuggers/debug_probe_pyocd.py +++ b/spsdk/debuggers/debug_probe_pyocd.py @@ -7,35 +7,33 @@ """Module for DebugMailbox PyOCD Debug probes support.""" import logging -from typing import Dict, Any - +from typing import Any, Dict import pylink +import pyocd import six - from pylink.errors import JLinkException - -import pyocd -from pyocd.core.helpers import ConnectHelper from pyocd.core.exceptions import Error as PyOCDError -from pyocd.probe.jlink_probe import JLinkProbe +from pyocd.core.helpers import ConnectHelper from pyocd.coresight import dap from pyocd.coresight.ap import MEM_AP -from pyocd.utility.sequencer import CallSequence -from pyocd.coresight.discovery import ADIVersion, ADIv5Discovery, ADIv6Discovery +from pyocd.coresight.discovery import ADIv5Discovery, ADIv6Discovery, ADIVersion from pyocd.probe.debug_probe import DebugProbe as PyOCDDebugProbe +from pyocd.probe.jlink_probe import JLinkProbe +from pyocd.utility.sequencer import CallSequence + +from spsdk.exceptions import SPSDKError from .debug_probe import ( DebugProbe, - ProbeNotFoundError, - DebugMailBoxAPNotFoundError, - DebugProbeTransferError, - DebugProbeNotOpenError, - DebugProbeError, - DebugProbeMemoryInterfaceAPNotFoundError, + SPSDKDebugMailBoxAPNotFoundError, + SPSDKDebugProbeError, + SPSDKDebugProbeMemoryInterfaceAPNotFoundError, + SPSDKDebugProbeNotOpenError, + SPSDKDebugProbeTransferError, + SPSDKProbeNotFoundError, ) - logger = logging.getLogger(__name__) logger.setLevel(logging.CRITICAL) @@ -110,9 +108,9 @@ def open(self) -> None: The function is used to initialize the connection to target and enable using debug probe for DAT purposes. - :raises ProbeNotFoundError: The probe has not found - :raises DebugMailBoxAPNotFoundError: The debug mailbox access port NOT found - :raises DebugProbeError: The PyOCD cannot establish communication with target + :raises SPSDKProbeNotFoundError: The probe has not found + :raises SPSDKDebugMailBoxAPNotFoundError: The debug mailbox access port NOT found + :raises SPSDKDebugProbeError: The PyOCD cannot establish communication with target """ try: self.pyocd_session = ConnectHelper.session_with_chosen_probe( @@ -120,19 +118,19 @@ def open(self) -> None: ) if self.pyocd_session is None: - raise ProbeNotFoundError("No probe available!") + raise SPSDKProbeNotFoundError("No probe available!") self.pyocd_session.options.set("scan_all_aps", True) self.pyocd_session.delegate = self self.pyocd_session.open() logger.info(f"PyOCD connected via {self.pyocd_session.probe.product_name} probe.") - except PyOCDError: - raise DebugProbeError("PyOCD cannot establish communication with target.") + except PyOCDError as exc: + raise SPSDKDebugProbeError("PyOCD cannot establish communication with target.") from exc self.dbgmlbx_ap = self._get_dmbox_ap() self.mem_ap = self._get_mem_ap() if self.mem_ap is None: logger.warning("The memory interface not found - probably locked device") if self.dbgmlbx_ap is None: - raise DebugMailBoxAPNotFoundError("No debug mail box access point available!") + raise SPSDKDebugMailBoxAPNotFoundError("No debug mail box access point available!") def close(self) -> None: """Close PyOCD interface. @@ -150,16 +148,18 @@ def mem_reg_read(self, addr: int = 0) -> int: :param addr: the register address :return: The read value of addressed register (4 bytes) - :raises DebugProbeMemoryInterfaceAPNotFoundError: The device doesn't content memory interface + :raises SPSDKDebugProbeMemoryInterfaceAPNotFoundError: The device doesn't content memory interface + :raises SPSDKDebugProbeTransferError: Memory read operation failed. """ if self.mem_ap is None: - raise DebugProbeMemoryInterfaceAPNotFoundError + raise SPSDKDebugProbeMemoryInterfaceAPNotFoundError reg = 0 try: reg = self.mem_ap.read32(addr=addr) except PyOCDError as exc: logger.error(f"Failed read memory({str(exc)}).") + raise SPSDKDebugProbeTransferError("The memory read operation failed") from exc return reg def mem_reg_write(self, addr: int = 0, data: int = 0) -> None: @@ -170,15 +170,17 @@ def mem_reg_write(self, addr: int = 0, data: int = 0) -> None: :param addr: the register address :param data: the data to be written into register - :raises DebugProbeMemoryInterfaceAPNotFoundError: The device doesn't content memory interface + :raises SPSDKDebugProbeMemoryInterfaceAPNotFoundError: The device doesn't content memory interface + :raises SPSDKDebugProbeTransferError: Memory write operation failed. """ if self.mem_ap is None: - raise DebugProbeMemoryInterfaceAPNotFoundError + raise SPSDKDebugProbeMemoryInterfaceAPNotFoundError try: self.mem_ap.write32(addr=addr, value=data) except PyOCDError as exc: logger.error(f"Failed write memory({str(exc)}).") + raise SPSDKDebugProbeTransferError("The memory write operation failed") from exc def dbgmlbx_reg_read(self, addr: int = 0) -> int: """Read debug mailbox access port register. @@ -187,15 +189,15 @@ def dbgmlbx_reg_read(self, addr: int = 0) -> int: :param addr: the register address :return: The read value of addressed register (4 bytes) - :raises DebugMailBoxAPNotFoundError: The dbgmlbx_reg_read is NOT implemented - :raises DebugProbeTransferError: The dbgmlbx_reg_read ends with data transfer error + :raises SPSDKDebugMailBoxAPNotFoundError: The dbgmlbx_reg_read is NOT implemented + :raises SPSDKDebugProbeTransferError: The dbgmlbx_reg_read ends with data transfer error """ if self.dbgmlbx_ap is None: - raise DebugMailBoxAPNotFoundError("No debug mail box access point available!") + raise SPSDKDebugMailBoxAPNotFoundError("No debug mail box access point available!") try: return self.dbgmlbx_ap.read_reg(self.APADDR & addr) - except: - raise DebugProbeTransferError("The Coresight read operation failed") + except PyOCDError as exc: + raise SPSDKDebugProbeTransferError("The Coresight read operation failed") from exc def dbgmlbx_reg_write(self, addr: int = 0, data: int = 0) -> None: """Write debug mailbox access port register. @@ -204,25 +206,25 @@ def dbgmlbx_reg_write(self, addr: int = 0, data: int = 0) -> None: :param addr: the register address :param data: the data to be written into register - :raises DebugMailBoxAPNotFoundError: The dbgmlbx_reg_write is NOT implemented - :raises DebugProbeTransferError: The dbgmlbx_reg_write ends with data transfer error + :raises SPSDKDebugMailBoxAPNotFoundError: The dbgmlbx_reg_write is NOT implemented + :raises SPSDKDebugProbeTransferError: The dbgmlbx_reg_write ends with data transfer error """ if self.dbgmlbx_ap is None: - raise DebugMailBoxAPNotFoundError("No debug mail box access point available!") + raise SPSDKDebugMailBoxAPNotFoundError("No debug mail box access point available!") try: self.dbgmlbx_ap.write_reg(addr=self.APADDR & addr, data=data) - except: - raise DebugProbeTransferError("The Coresight write operation failed") + except PyOCDError as exc: + raise SPSDKDebugProbeTransferError("The Coresight write operation failed") from exc def reset(self) -> None: """Reset a target. It resets a target. - :raises DebugProbeNotOpenError: The PyOCD debug probe is not opened yet + :raises SPSDKDebugProbeNotOpenError: The PyOCD debug probe is not opened yet """ if self.pyocd_session is None: - raise DebugProbeNotOpenError("The PyOCD debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyOCD debug probe is not opened yet") self.pyocd_session.target.reset() def _get_ap_by_ix(self, index: int) -> Any: @@ -230,26 +232,26 @@ def _get_ap_by_ix(self, index: int) -> Any: :param index: Index of requested access port class. :return: Access port class, by its IX - :raises DebugProbeNotOpenError: The PyOCD probe is NOT opened - :raises DebugProbeError: There is not active access port for specified index. + :raises SPSDKDebugProbeNotOpenError: The PyOCD probe is NOT opened + SPSDK:raises SPSDKDebugProbeError: There is not active access port for specified index. """ if self.pyocd_session is None: - raise DebugProbeNotOpenError("The PyOCD debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyOCD debug probe is not opened yet") for access_port in self.pyocd_session.target.aps.values(): if access_port.address.apsel == index: return access_port - raise DebugProbeError(f"The access port {index} is not present.") + raise SPSDKDebugProbeError(f"The access port {index} is not present.") def _get_ap_by_addr(self, addr: int) -> Any: """Function returns the AP PyoCD object by address if exists. :param addr: The access port address. :return: The Access port object. - :raises DebugProbeNotOpenError: The PyOCD probe is NOT opened + :raises SPSDKDebugProbeNotOpenError: The PyOCD probe is NOT opened """ if self.pyocd_session is None: - raise DebugProbeNotOpenError("The PyOCD debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyOCD debug probe is not opened yet") ap_sel = (addr & self.APSEL) >> self.APSEL_SHIFT return self._get_ap_by_ix(ap_sel) @@ -262,19 +264,19 @@ def coresight_reg_read(self, access_port: bool = True, addr: int = 0) -> int: :param access_port: if True, the Access Port (AP) register will be read(default), otherwise the Debug Port :param addr: the register address :return: The read value of addressed register (4 bytes) - :raises DebugProbeTransferError: The IO operation failed - :raises DebugProbeNotOpenError: The PyOCD probe is NOT opened + :raises SPSDKDebugProbeTransferError: The IO operation failed + :raises SPSDKDebugProbeNotOpenError: The PyOCD probe is NOT opened """ if self.pyocd_session is None: - raise DebugProbeNotOpenError("The PyOCD debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyOCD debug probe is not opened yet") try: if access_port: access_p = self._get_ap_by_addr(addr) return access_p.read_reg(self.APADDR & addr) else: return self.pyocd_session.target.dp.read_dp(addr) - except: - raise DebugProbeTransferError("The Coresight read operation failed") + except PyOCDError as exc: + raise SPSDKDebugProbeTransferError("The Coresight read operation failed") from exc def coresight_reg_write(self, access_port: bool = True, addr: int = 0, data: int = 0) -> None: """Write coresight register over PyOCD interface. @@ -284,19 +286,19 @@ def coresight_reg_write(self, access_port: bool = True, addr: int = 0, data: int :param access_port: if True, the Access Port (AP) register will be write(default), otherwise the Debug Port :param addr: the register address :param data: the data to be written into register - :raises DebugProbeTransferError: The IO operation failed - :raises DebugProbeNotOpenError: The PyOCD probe is NOT opened + :raises SPSDKDebugProbeTransferError: The IO operation failed + :raises SPSDKDebugProbeNotOpenError: The PyOCD probe is NOT opened """ if self.pyocd_session is None: - raise DebugProbeNotOpenError("The PyOCD debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyOCD debug probe is not opened yet") try: if access_port: access_p = self._get_ap_by_addr(addr) access_p.write_reg(self.APADDR & addr, data) else: self.pyocd_session.target.dp.write_dp(addr, data) - except: - raise DebugProbeTransferError("The Coresight write operation failed") + except PyOCDError as exc: + raise SPSDKDebugProbeTransferError("The Coresight write operation failed") from exc def _get_dmbox_ap(self) -> Any: """Search for Debug Mailbox Access Point. @@ -304,12 +306,12 @@ def _get_dmbox_ap(self) -> Any: This is helper function to find and return the debug mailbox access port. :return: Debug MailBox Access Port - :raises DebugProbeNotOpenError: The PyOCD probe is NOT opened + :raises SPSDKDebugProbeNotOpenError: The PyOCD probe is NOT opened """ idr_expected = 0x002A0000 if self.pyocd_session is None: - raise DebugProbeNotOpenError("The PyOCD debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyOCD debug probe is not opened yet") for access_port in self.pyocd_session.target.aps.values(): if self.dbgmlbx_ap_ix >= 0: @@ -329,10 +331,10 @@ def _get_mem_ap(self) -> Any: This is helper function to find and return the memory interface access port. :return: Memory Interface Access Port - :raises DebugProbeNotOpenError: The PyOCD probe is NOT opened + :raises SPSDKDebugProbeNotOpenError: The PyOCD probe is NOT opened """ if self.pyocd_session is None: - raise DebugProbeNotOpenError("The PyOCD debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyOCD debug probe is not opened yet") for access_port in self.pyocd_session.target.aps.values(): if self.mem_ap_ix >= 0: @@ -380,10 +382,10 @@ def dp_init_sequence(self) -> CallSequence: do some additional unwanted actions that are not welcomed by DAT. :return: Debug Port initialization call sequence - :raises DebugProbeNotOpenError: The PyOCD probe is NOT opened + :raises SPSDKDebugProbeNotOpenError: The PyOCD probe is NOT opened """ if self.pyocd_session is None: - raise DebugProbeNotOpenError("The PyOCD debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyOCD debug probe is not opened yet") debug_probe = self.pyocd_session.target.dp probe = debug_probe.probe @@ -409,11 +411,11 @@ def dp_init_sequence(self) -> CallSequence: def _connect_jlink(self) -> None: """Custom J-Link connect function. - :raises ValueError: Unsupported communication protocol. - :raises DebugProbeNotOpenError: The PyOCD probe is NOT opened + :raises SPSDKError: Unsupported communication protocol. + :raises SPSDKDebugProbeNotOpenError: The PyOCD probe is NOT opened """ if self.pyocd_session is None: - raise DebugProbeNotOpenError("The PyOCD debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The PyOCD debug probe is not opened yet") # Attempt to connect. debug_probe = self.pyocd_session.target.dp @@ -428,7 +430,7 @@ def _connect_jlink(self) -> None: # Validate selected protocol. if protocol not in probe._supported_protocols: - raise ValueError("unsupported wire protocol %s" % protocol) + raise SPSDKError("unsupported wire protocol %s" % protocol) # Convert protocol to port enum. if protocol == PyOCDDebugProbe.Protocol.SWD: diff --git a/spsdk/debuggers/utils.py b/spsdk/debuggers/utils.py index 534b06db..54c6598a 100644 --- a/spsdk/debuggers/utils.py +++ b/spsdk/debuggers/utils.py @@ -12,7 +12,8 @@ import colorama import prettytable -from spsdk.debuggers.debug_probe import DebugProbe, DebugProbeError, ProbeNotFoundError +from spsdk import SPSDKError +from spsdk.debuggers.debug_probe import DebugProbe, SPSDKDebugProbeError, SPSDKProbeNotFoundError from spsdk.debuggers.debug_probe_jlink import DebugProbePyLink from spsdk.debuggers.debug_probe_pemicro import DebugProbePemicro @@ -66,36 +67,36 @@ def append(self, item: ProbeDescription) -> None: """Overriding build-in function by check the type. :param item: ProbeDestription item. - :raises TypeError: Invalid input types has been used. + :raises SPSDKError: Invalid input types has been used. """ if isinstance(item, ProbeDescription): super(DebugProbes, self).append(item) else: - raise TypeError("The list accepts only ProbeDescription object") + raise SPSDKError("The list accepts only ProbeDescription object") def insert(self, index: int, item: ProbeDescription) -> None: """Overriding build-in function by check the type. :param item: ProbeDestription item. :param index: Index in list to insert. - :raises TypeError: Invalid input types has been used. + :raises SPSDKError: Invalid input types has been used. """ if isinstance(item, ProbeDescription): super(DebugProbes, self).insert(index, item) else: - raise TypeError("The list accepts only ProbeDescription object") + raise SPSDKError("The list accepts only ProbeDescription object") def select_probe(self, silent: bool = False) -> ProbeDescription: """Perform Probe selection. :param silent: When it True, the functions select the probe if applicable without any prints to log :return: The record of selected DebugProbe - :raises ProbeNotFoundError: No probe has been founded + :raises SPSDKProbeNotFoundError: No probe has been founded """ if len(self) == 0: if not silent: print("There is no any debug probe connected in system!") - raise ProbeNotFoundError("There is no any debug probe connected in system!") + raise SPSDKProbeNotFoundError("There is no any debug probe connected in system!") if not silent or len(self) > 1: # pragma: no cover self.print() @@ -108,7 +109,7 @@ def select_probe(self, silent: bool = False) -> ProbeDescription: i_selected = int(input()) if i_selected > len(self) - 1: print("The chosen probe index is out of range") - raise ProbeNotFoundError("The chosen probe index is out of range") + raise SPSDKProbeNotFoundError("The chosen probe index is out of range") return self[i_selected] @@ -164,7 +165,7 @@ def get_connected_probes( if (interface is None) or (interface.lower() == probe_key): try: probes.extend(PROBES[probe_key].get_connected_probes(hardware_id, user_params)) - except DebugProbeError as exc: + except SPSDKDebugProbeError as exc: logger.warning(f"The {probe_key} debug probe support is not ready({str(exc)}).") return probes @@ -181,29 +182,30 @@ def test_ahb_access(probe: DebugProbe, ap_mem: int = 0) -> bool: logger.debug("step T.1: Activate the correct AP") probe.coresight_reg_write(access_port=False, addr=2 * 4, data=ap_mem) - logger.debug("step T.2: Set the AP access size and address mode") - probe.coresight_reg_write( - access_port=True, - addr=probe.get_coresight_ap_address(ap_mem, 0 * 4), - data=0x22000012, - ) - - logger.debug("step T.3: Set the initial AHB address to access") - probe.coresight_reg_write( - access_port=True, - addr=probe.get_coresight_ap_address(ap_mem, 1 * 4), - data=0x20000000, - ) - - logger.debug("step T.4: Access the memory system at that address") try: + logger.debug("step T.2: Set the AP access size and address mode") + probe.coresight_reg_write( + access_port=True, + addr=probe.get_coresight_ap_address(ap_mem, 0 * 4), + data=0x22000012, + ) + + logger.debug("step T.3: Set the initial AHB address to access") + probe.coresight_reg_write( + access_port=True, + addr=probe.get_coresight_ap_address(ap_mem, 1 * 4), + data=0x20000000, + ) + + logger.debug("step T.4: Access the memory system at that address") + value = probe.coresight_reg_read( access_port=True, addr=probe.get_coresight_ap_address(ap_mem, 3 * 4) ) logger.debug(f"Read value at 0x2000_0000 is {value:08X}") ahb_enabled = True - except DebugProbeError: + except SPSDKDebugProbeError: logger.debug("Chip has NOT enabled AHB access.") return ahb_enabled diff --git a/spsdk/exceptions.py b/spsdk/exceptions.py index 9abec294..a2eb5ea1 100644 --- a/spsdk/exceptions.py +++ b/spsdk/exceptions.py @@ -24,3 +24,15 @@ def __init__(self, desc: str = None) -> None: def __str__(self) -> str: return self.fmt.format(description=self.description) + + +class SPSDKValueError(SPSDKError, ValueError): + """SPSDK standard value error.""" + + +class SPSDKTypeError(SPSDKError, TypeError): + """SPSDK standard type error.""" + + +class SPSDKIOError(SPSDKError, IOError): + """SPSDK standard IO error.""" diff --git a/spsdk/image/bee.py b/spsdk/image/bee.py index cfdef2f6..3ae6e1bb 100644 --- a/spsdk/image/bee.py +++ b/spsdk/image/bee.py @@ -8,14 +8,15 @@ import logging -from struct import pack, unpack_from, calcsize +from struct import calcsize, pack, unpack_from from typing import Any, List, Optional, Sequence from Crypto.Cipher import AES -from spsdk.utils.crypto import crypto_backend, Counter +from spsdk import SPSDKError +from spsdk.utils.crypto import Counter, crypto_backend from spsdk.utils.easy_enum import Enum -from spsdk.utils.misc import extend_block, DebugInfo +from spsdk.utils.misc import DebugInfo, extend_block _LOGGER = logging.getLogger(__name__) @@ -78,10 +79,10 @@ def parse(cls, data: bytes, offset: int = 0) -> Any: :param data: binary data to be parsed :param offset: to start parsing the data :return: instance created from binary data; this method returns just `0` - :raise ValueError: if size of the data is not sufficient + :raises SPSDKError: If size of the data is not sufficient """ if len(data) - offset < cls._size(): - raise ValueError("Insufficient size of the data") + raise SPSDKError("Insufficient size of the data") return 0 @@ -118,11 +119,14 @@ def info(self) -> str: def validate(self) -> None: """Validates the configuration of the instance.""" - assert (self.start_addr & _ENCR_BLOCK_ADDR_MASK == 0) and ( - self.length & _ENCR_BLOCK_ADDR_MASK == 0 - ) - assert 0 <= self.protected_level <= 3 - assert 0 <= self.start_addr < self.end_addr <= 0xFFFFFFFF + if (self.start_addr & _ENCR_BLOCK_ADDR_MASK != 0) and ( + self.length & _ENCR_BLOCK_ADDR_MASK != 0 + ): + raise SPSDKError("Invalid configuration of the instance") + if self.protected_level < 0 or self.protected_level > 3: + raise SPSDKError("Invalid protected level") + if self.start_addr < 0 or self.end_addr > 0xFFFFFFFF or self.start_addr >= self.end_addr: + raise SPSDKError("Invalid start/end address") def export(self) -> bytes: """Exports the binary representation.""" @@ -142,14 +146,14 @@ def parse(cls, data: bytes, offset: int = 0) -> "BeeFacRegion": :param data: binary data to be parsed :param offset: to start parsing the data :return: instance created from binary data - :raise ValueError: if reserved area is non-zero + :raises SPSDKError: If reserved area is non-zero """ super().parse(data, offset) # check size of the data (start, end, protected_level, _reserved) = unpack_from( BeeFacRegion._struct_format(), data, offset ) if _reserved != b"\x00" * 20: - raise ValueError("Reserved area is non-zero") + raise SPSDKError("Reserved area is non-zero") return BeeFacRegion(start, end - start, protected_level) @@ -242,18 +246,19 @@ def info(self) -> str: return result def validate(self) -> None: - """Validates settings of the instance. - - :raises AssertionError: if settings invalid - """ - assert 0 <= self._start_addr <= 0xFFFFFFFF - assert self._start_addr <= self._end_addr <= 0xFFFFFFFF - assert ( - self.mode == BeeProtectRegionBlockAesMode.CTR - ), "only AES/CTR encryption mode supported now" # TODO - assert len(self.counter) == 16 - assert self.counter[-4:] == b"\x00\x00\x00\x00", "last four bytes must be zero" - assert 0 < self.fac_count <= self.FAC_REGIONS + """Validates settings of the instance.""" + if self._start_addr < 0 or self._start_addr > 0xFFFFFFFF: + raise SPSDKError("Invalid start address") + if self._start_addr > self._end_addr or self._end_addr > 0xFFFFFFFF: + raise SPSDKError("Invalid start/end address") + if self.mode != BeeProtectRegionBlockAesMode.CTR: + raise SPSDKError("Only AES/CTR encryption mode supported now") # TODO + if len(self.counter) != 16: + raise SPSDKError("Invalid conter") + if self.counter[-4:] != b"\x00\x00\x00\x00": + raise SPSDKError("last four bytes must be zero") + if self.fac_count <= 0 or self.fac_count > self.FAC_REGIONS: + raise SPSDKError("Invalid FAC regions") for fac in self.fac_regions: fac.validate() @@ -285,7 +290,7 @@ def parse(cls, data: bytes, offset: int = 0) -> "BeeProtectRegionBlock": :param data: binary data to be parsed :param offset: to start parsing the data :return: instance created from binary data - :raise ValueError: if format does not match + :raises SPSDKError: If format does not match """ super().parse(data, offset) # check size of the input data ( @@ -302,11 +307,11 @@ def parse(cls, data: bytes, offset: int = 0) -> "BeeProtectRegionBlock": ) = unpack_from(BeeProtectRegionBlock._struct_format(), data, offset) # if (tagl != BeeProtectRegionBlock.TAGL) or (tagh != BeeProtectRegionBlock.TAGH): - raise ValueError("Invalid tag or unsupported version") + raise SPSDKError("Invalid tag or unsupported version") if version != BeeProtectRegionBlock.VERSION: - raise ValueError("Unsupported version") + raise SPSDKError("Unsupported version") if _reserved_32 != b"\x00" * 32: - raise ValueError("Reserved area is non-zero") + raise SPSDKError("Reserved area is non-zero") # result = BeeProtectRegionBlock(mode, lock_options, counter[::-1]) result._start_addr = start_addr @@ -326,16 +331,22 @@ def encrypt_block(self, key: bytes, start_addr: int, data: bytes) -> bytes: :param start_addr: start address of the data :param data: binary block to be encrypted; the block size must be BEE_ENCR_BLOCK_SIZE :return: encrypted block if it is inside any FAC region; untouched block if it is not in any FAC region + :raises SPSDKError: When incorrect length of binary block + :raises SPSDKError: When encryption mode different from AES/CTR provided + :raises SPSDKError: When invalid length of key + :raises SPSDKError: When invalid range of region """ - assert len(data) == BEE_ENCR_BLOCK_SIZE + if len(data) != BEE_ENCR_BLOCK_SIZE: + raise SPSDKError("Incorrect length of binary block to be encrypted") if self._start_addr <= start_addr < self._end_addr: - assert ( - self.mode == BeeProtectRegionBlockAesMode.CTR - ), "only AES/CTR encryption mode supported now" - assert len(key) == 16 + if self.mode != BeeProtectRegionBlockAesMode.CTR: + raise SPSDKError("only AES/CTR encryption mode supported now") + if len(key) != 16: + raise SPSDKError("Invalid length of key") for fac in self.fac_regions: if fac.start_addr <= start_addr < fac.end_addr: - assert start_addr + len(data) <= fac.end_addr + if start_addr + len(data) > fac.end_addr: + raise SPSDKError("Invalid range of region") cntr_key = Counter( self.counter, ctr_value=start_addr >> 4, @@ -373,10 +384,13 @@ def info(self) -> str: def validate(self) -> None: """Validates settings of the instance. - :raises AssertionError: if settings invalid + :raises SPSDKError: If invalid length of kib key + :raises SPSDKError: If invalid length of kib iv """ - assert len(self.kib_key) == self._KEY_LEN - assert len(self.kib_iv) == self._KEY_LEN + if len(self.kib_key) != self._KEY_LEN: + raise SPSDKError("Invalid length of kib key") + if len(self.kib_iv) != self._KEY_LEN: + raise SPSDKError("Invalid length of kib iv") def export(self) -> bytes: """Exports binary representation of the region (serialization).""" @@ -408,8 +422,8 @@ class BeeRegionHeader(BeeBaseClass): @classmethod def _struct_format(cls) -> str: - """:raise AssertionError: it is not expected to called for the class.""" - raise AssertionError( + """:raises SPSDKError: It is not expected to called for the class.""" + raise SPSDKError( "This method is not expected to be used for this class, format depends on its fields" ) @@ -472,11 +486,12 @@ def update(self) -> None: def validate(self) -> None: """Validates settings of the instance. - :raises AssertionError: if settings invalid + :raises SPSDKError: If settings invalid """ self._kib.validate() self._prdb.validate() - assert len(self._sw_key) == 16 + if len(self._sw_key) != 16: + raise SPSDKError("Invalid settings") def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Serialization to binary representation. @@ -508,9 +523,11 @@ def parse(cls, data: bytes, offset: int = 0, sw_key: bytes = b"") -> "BeeRegionH :param offset: to start parsing the data :param sw_key: SW key used to decrypt the EKIB data (the key is marked as SW_GP2 on RT10xx) :return: instance created from binary data + :raises SPSDKError: If invalid sw key """ super().parse(data, offset) # check size of the input data - assert len(sw_key) == 16 + if len(sw_key) != 16: + raise SPSDKError("Invalid sw key") aes = AES.new(sw_key, AES.MODE_ECB) decr_data = aes.decrypt(data[offset : offset + BeeKIB._size()]) kib = BeeKIB.parse(decr_data) diff --git a/spsdk/image/commands.py b/spsdk/image/commands.py index 407677b9..5cde4aa8 100644 --- a/spsdk/image/commands.py +++ b/spsdk/image/commands.py @@ -11,22 +11,22 @@ from abc import ABC from datetime import datetime from struct import pack, unpack_from -from typing import Iterable, List, Optional, Tuple, Union, Any, Iterator, Mapping, Type +from typing import Any, Iterable, Iterator, List, Mapping, Optional, Tuple, Type, Union from asn1crypto import cms, util, x509 +from spsdk import SPSDKError from spsdk.crypto import Certificate, Encoding -from spsdk.utils.crypto import matches_key_and_cert, crypto_backend +from spsdk.utils.crypto import crypto_backend, matches_key_and_cert from spsdk.utils.easy_enum import Enum from spsdk.utils.misc import DebugInfo -from .header import CmdTag, CmdHeader, Header, SegTag -from .secret import BaseClass, CertificateImg, EnumAlgorithm, MAC, Signature, SrkTable - ######################################################################################################################## # Enums ######################################################################################################################## from .. import SPSDKError +from .header import CmdHeader, CmdTag, Header, SegTag +from .secret import MAC, BaseClass, CertificateImg, EnumAlgorithm, Signature, SrkTable class EnumWriteOps(Enum): @@ -166,9 +166,9 @@ def cmd_data_offset(self, value: int) -> None: """Setter. :param value: offset to set - :raises TypeError: if cmd-data not supported by the command + :raises SPSDKError: If cmd-data not supported by the command """ - raise TypeError("cmd-data not supported by the command") + raise SPSDKError("cmd-data not supported by the command") @property def cmd_data_reference(self) -> Optional[BaseClass]: @@ -187,18 +187,18 @@ def cmd_data_reference(self, value: BaseClass) -> None: Note: the method must be implemented in `self.has_cmd_data_reference` returns True :param value: to be set - :raise TypeError: if reference not supported by the command + :raises SPSDKError: If reference not supported by the command """ - raise TypeError("cmd-data not supported by the command") + raise SPSDKError("cmd-data not supported by the command") def parse_cmd_data(self, data: bytes, offset: int) -> Any: """Parse additional command data from binary data. :param data: to be parsed :param offset: start position in data to parse - :raises TypeError: if cmd_data is not supported by the command + :raises SPSDKError: If cmd_data is not supported by the command """ - raise TypeError("cmd-data not supported by the command") + raise SPSDKError("cmd-data not supported by the command") def __eq__(self, other: Any) -> bool: return isinstance(other, self.__class__) and vars(other) == vars(self) @@ -251,8 +251,10 @@ def num_bytes(self, value: int) -> None: """Setter. :param value: number of bytes being written by the command + :raises SPSDKError: When number of bytes is not 1, 2 nor 4 """ - assert value in (1, 2, 4) + if value not in (1, 2, 4): + raise SPSDKError("number of bytes is not 1, 2 nor 4") self._header.param &= ~0x7 self._header.param |= value @@ -263,7 +265,8 @@ def ops(self) -> int: @ops.setter def ops(self, value: int) -> None: - assert value in EnumWriteOps + if value not in EnumWriteOps: + raise SPSDKError self._header.param &= ~(0x3 << 3) self._header.param |= int(value) << 3 @@ -278,9 +281,13 @@ def __init__( :param numbytes: number of bytes. Must be value: 1, 2 or 4 :param ops: type of write operation :param data: list of tuples: address and value + :raises SPSDKError: When incorrect number of bytes + :raises SPSDKError: When incorrect type of operation """ - assert numbytes in (1, 2, 4) - assert ops in EnumWriteOps + if numbytes not in (1, 2, 4): + raise SPSDKError("Incorrect number of bytes") + if ops not in EnumWriteOps: + raise SPSDKError("Incorrect type of operation") super().__init__(CmdTag.WRT_DAT, ((int(ops) & 0x3) << 3) | (numbytes & 0x7)) self._data: List[List[int]] = [] if data is not None: @@ -317,14 +324,17 @@ def info(self) -> str: def append(self, address: int, value: int) -> None: """Append of Write data command.""" - assert 0 <= address <= 0xFFFFFFFF, "address out of range" - assert 0 <= value <= 0xFFFFFFFF, "value out of range" + if address < 0 or address > 0xFFFFFFFF: + raise SPSDKError("Address out of range") + if value < 0 or value > 0xFFFFFFFF: + raise SPSDKError("Value out of range") self._data.append([address, value]) self._header.length += 8 def pop(self, index: int) -> List[int]: """Pop of Write data command.""" - assert 0 <= index < len(self._data) + if index < 0 or index >= len(self._data): + raise SPSDKError("Length of data is incorrect") cmd = self._data.pop(index) self._header.length -= 8 return cmd @@ -373,7 +383,8 @@ def num_bytes(self) -> int: @num_bytes.setter def num_bytes(self, value: int) -> None: - assert value in (1, 2, 4) + if value not in (1, 2, 4): + raise SPSDKError("Incorrect number of bytes") self._header.param &= ~0x7 self._header.param |= int(value) @@ -384,7 +395,12 @@ def ops(self) -> int: @ops.setter def ops(self, value: int) -> None: - assert value in EnumCheckOps + """Operation of Check data command. + + :raises SPSDKError: If incorrect operation + """ + if value not in EnumCheckOps: + raise SPSDKError("Incorrect operation") self._header.param &= ~(0x3 << 3) self._header.param |= int(value) << 3 @@ -403,9 +419,13 @@ def __init__( :param address: list of tuples: address and value :param mask: mask value :param count: count value + :raises SPSDKError: If incorrect number of bytes + :raises SPSDKError: If incorrect operation """ - assert numbytes in (1, 2, 4) - assert ops in EnumCheckOps + if numbytes not in (1, 2, 4): + raise SPSDKError("Incorrect number of bytes") + if ops not in EnumCheckOps: + raise SPSDKError("Incorrect operation") super().__init__(CmdTag.CHK_DAT, ((int(ops) & 0x3) << 3) | (numbytes & 0x7)) self.address = address self.mask = mask @@ -511,7 +531,8 @@ def itm(self) -> int: @itm.setter def itm(self, value: EnumItm) -> None: - assert value in EnumItm + if value not in EnumItm: + raise SPSDKError("Incorrect item of set command") self._header.param = value @property @@ -521,7 +542,8 @@ def hash_algorithm(self) -> EnumAlgorithm: @hash_algorithm.setter def hash_algorithm(self, value: EnumAlgorithm) -> None: - assert value in EnumAlgorithm + if value not in EnumAlgorithm: + raise SPSDKError("Incorrect type of algorithm") self._hash_alg = value @property @@ -531,7 +553,8 @@ def engine(self) -> EnumEngine: @engine.setter def engine(self, value: EnumEngine) -> None: - assert value in EnumEngine + if value not in EnumEngine: + raise SPSDKError("Incorrect type of engine plugin") self._engine = value def __init__( @@ -542,7 +565,8 @@ def __init__( engine_cfg: int = 0, ): """Initialize the set command.""" - assert itm in EnumItm + if itm not in EnumItm: + raise SPSDKError("Incorrect engine configuration flag") super().__init__(CmdTag.SET, itm) self.hash_algorithm: EnumAlgorithm = hash_alg self.engine = engine @@ -609,12 +633,14 @@ def engine(self) -> int: @engine.setter def engine(self, value: EnumEngine) -> None: - assert value in EnumEngine + if value not in EnumEngine: + raise SPSDKError("Incorrect value of engine") self._header.param = value def __init__(self, engine: int = EnumEngine.ANY, data: List[int] = None) -> None: """Initialize the initialize command.""" - assert engine in EnumEngine + if engine not in EnumEngine: + raise SPSDKError("Incorrect value of engine") super().__init__(CmdTag.INIT, engine) self._data = data if data else [] @@ -646,15 +672,24 @@ def info(self) -> str: return msg def append(self, value: int) -> None: - """Appending of Initialize command.""" + """Appending of Initialize command. + + :raises SPSDKError: If value out of range + """ assert isinstance(value, int), "value must be INT type" - assert 0 <= value < 0xFFFFFFFF, "value out of range" + if value < 0 or value >= 0xFFFFFFFF: + raise SPSDKError("Value out of range") self._data.append(value) self._header.length += 4 def pop(self, index: int) -> int: - """Pop of Initialize command.""" - assert 0 <= index < len(self._data) + """Pop of Initialize command. + + :return: value from the index + :raises SPSDKError: If incorrect length of data + """ + if index < 0 or index >= len(self._data): + raise SPSDKError("Incorrect length of data") val = self._data.pop(index) self._header.length -= 4 return val @@ -682,12 +717,14 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdInitialize": :param data: being parsed :param offset: current position to readd from data :return: parse command + :raises SPSDKError: If incorrect length of data """ header = CmdHeader.parse(data, offset, CmdTag.INIT) obj = cls(EnumEngine.from_int(header.param)) index = header.size while index < header.length: - assert (offset + index) < len(data) + if (offset + index) >= len(data): + raise SPSDKError("Incorrect length of data") val = unpack_from(">L", data, offset + index) obj.append(val[0]) index += 4 @@ -991,7 +1028,12 @@ def flags(self) -> EnumInsKey: @flags.setter def flags(self, value: EnumInsKey) -> None: - assert value in EnumInsKey + """Flags. + + :raises SPSDKError: If incorrect flag" + """ + if value not in EnumInsKey: + raise SPSDKError("Incorrect flag") self._header.param = value @property @@ -1004,8 +1046,10 @@ def certificate_format(self, value: EnumCertFormat) -> None: """Setter. :param value: certificate format + :raises SPSDKError: If incorrect certificate format """ - assert value in EnumCertFormat + if value not in EnumCertFormat: + raise SPSDKError("Incorrect certificate format") self._cert_fmt = value @property @@ -1018,8 +1062,10 @@ def hash_algorithm(self, value: EnumAlgorithm) -> None: """Setter. :param value: hash algorithm + :raises SPSDKError: If incorrect hash algorithm """ - assert value in EnumAlgorithm + if value not in EnumAlgorithm: + raise SPSDKError("Incorrect hash algorithm") self._hash_alg = value @property @@ -1036,16 +1082,21 @@ def source_index(self, value: int) -> None: """Setter. :param value: source key (verification key, KEK) index + :raises SPSDKError: If incorrect keys + :raises SPSDKError: If incorrect keys """ if self._cert_fmt == EnumCertFormat.SRK: - assert value in ( + # RT10xx supports just 4 SRK keys; this might need update for other devices + if value not in ( 0, 1, 2, 3, - ) # RT10xx supports just 4 SRK keys; this might need update for other devices + ): + raise SPSDKError("Incorrect keys") else: - assert value in (0, 2, 3, 4, 5) + if value not in (0, 2, 3, 4, 5): + raise SPSDKError("Incorrect keys") self._src_index = value @property @@ -1058,8 +1109,10 @@ def target_index(self, value: int) -> None: """Setter. :param value: target key index + :raises SPSDKError: If incorrect key index """ - assert value in (0, 1, 2, 3, 4, 5) + if value not in (0, 1, 2, 3, 4, 5): + raise SPSDKError("Incorrect key index") self._tgt_index = value @property @@ -1081,7 +1134,8 @@ def needs_cmd_data_reference(self) -> bool: if ( self.flags == EnumInsKey.ABS ): # reference is an absolute address; instance not assigned; used for DEK key - assert self._certificate_ref is None + if self._certificate_ref is not None: + raise SPSDKError("Reference is not none") return False return True @@ -1101,7 +1155,6 @@ def cmd_data_reference(self, value: Union[CertificateImg, SrkTable]) -> None: By default, the command does not support cmd_data_reference :param value: to be set - :raise ValueError: if cmd reference not supported by the command """ assert isinstance(value, (CertificateImg, SrkTable)) self._certificate_ref = value @@ -1224,7 +1277,8 @@ def flags(self) -> int: @flags.setter def flags(self, value: int) -> None: - assert value in EnumAuthDat + if value not in EnumAuthDat: + raise SPSDKError("Incorrect flag") self._header.param = value @property @@ -1234,7 +1288,8 @@ def key_index(self) -> int: @key_index.setter def key_index(self, value: int) -> None: - assert value in (0, 1, 2, 3, 4, 5) + if value not in (0, 1, 2, 3, 4, 5): + raise SPSDKError("Incorrect key index") self._key_index = value @property @@ -1244,7 +1299,8 @@ def engine(self) -> EnumEngine: @engine.setter def engine(self, value: EnumEngine) -> None: - assert value in EnumEngine + if value not in EnumEngine: + raise SPSDKError("Incorrect engine") self._engine = value def __init__( @@ -1273,7 +1329,8 @@ def __init__( if certificate and private_key_pem_data: assert isinstance(certificate, Certificate) assert isinstance(private_key_pem_data, bytes) - assert matches_key_and_cert(private_key_pem_data, certificate) + if not matches_key_and_cert(private_key_pem_data, certificate): + raise SPSDKError("Given private key does not match the public certificate") @property def needs_cmd_data_reference(self) -> bool: @@ -1309,8 +1366,7 @@ def cmd_data_reference(self, value: SignatureOrMAC) -> None: By default, the command does not support cmd_data_reference :param value: to be set - :raise ValueError: if cmd reference not supported by the command - :raise ExpectedSignatureOrMACError: if unspported data object is provided + :raise ExpectedSignatureOrMACError: if unsupported data object is provided """ if self.sig_format == EnumCertFormat.AEAD: assert isinstance(value, MAC) @@ -1367,7 +1423,8 @@ def __getitem__(self, key: int) -> Tuple[int, int]: def __setitem__(self, key: int, value: Tuple[int, int]) -> None: assert isinstance(value, (list, tuple)) - assert len(value) == 2 + if len(value) != 2: + raise SPSDKError("Incorrect length") self._blocks[key] = value def __iter__(self) -> Iterator[Union[Tuple[Any, ...], List[Any]]]: @@ -1401,7 +1458,8 @@ def append(self, start_address: int, size: int) -> None: def pop(self, index: int) -> Tuple[int, int]: """Pop of Authenticate data command.""" - assert 0 <= index < len(self._blocks) + if index < 0 or index >= len(self._blocks): + raise SPSDKError("Incorrect length of blocks") value = self._blocks.pop(index) self._header.length -= 8 return value @@ -1417,9 +1475,14 @@ def _cms_signature(self, zulu: datetime, data: bytes) -> bytes: :param zulu: current UTC time+date :param data: to be signed :return: CMS signature (binary) + :raises SPSDKError: If certificate is not present + :raises SPSDKError: If private key is not present + :raises SPSDKError: If incorrect time-zone" """ - assert self.certificate is not None - assert self.private_key_pem_data is not None + if self.certificate is None: + raise SPSDKError("Certificate is not present") + if self.private_key_pem_data is None: + raise SPSDKError("Private key is not present") # signed data (main section) signed_data = cms.SignedData() @@ -1458,7 +1521,8 @@ def _cms_signature(self, zulu: datetime, data: bytes) -> bytes: ) ) # check time-zone is assigned (expected UTC+0) - assert zulu.tzinfo + if not zulu.tzinfo: + raise SPSDKError("Incorrect time-zone") signed_attrs.append( cms.CMSAttribute( { @@ -1504,14 +1568,17 @@ def update_signature( :param base_data_addr: base address of the generated data :raises ValueError: When certificate or private key are not assigned :raises ValueError: When signatures not assigned explicitly + :raises SPSDKError: If incorrect start address + :raises SPSDKError: If incorrect end address + :raises SPSDKError: If incorrect length :return: True if length of the signature was unchanged, as this may affect content of the CSF section (pointer to data); """ if not self.certificate or not self.private_key_pem_data: - raise ValueError("certificate or private key not assigned, cannot update signature") + raise SPSDKError("certificate or private key not assigned, cannot update signature") if self.signature is None: - raise ValueError( + raise SPSDKError( "signature must be assigned explicitly, so its version matches to CST version" ) @@ -1522,11 +1589,14 @@ def update_signature( for blk in self._blocks: start = blk[0] - base_data_addr end = blk[0] + blk[1] - base_data_addr - assert start >= 0 - assert end <= len(data) + if start < 0: + raise SPSDKError("Incorrect start address") + if end > len(data): + raise SPSDKError("Incorrect end address") sign_data += data[start:end] total_len += blk[1] - assert len(sign_data) == total_len + if len(sign_data) != total_len: + raise SPSDKError("Incorrect length") else: sign_data = data # if no blocks defined, sign complete data; used for CSF if isinstance(self.signature, Signature): @@ -1607,11 +1677,11 @@ def parse_command(data: bytes, offset: int = 0) -> CmdBase: :param data: binary data to be parsed :param offset: to start parsing :return: instance of the command - :raise ValueError: if the command is not valid + :raises SPSDKError: If the command is not valid """ try: cmdtag = CmdTag.from_int(data[offset]) except ValueError: - raise ValueError("Unknown command at position: " + hex(offset)) + raise SPSDKError("Unknown command at position: " + hex(offset)) cmd_class = _CMD_TO_CLASS[cmdtag] return cmd_class.parse(data, offset) diff --git a/spsdk/image/hab_audit_log.py b/spsdk/image/hab_audit_log.py index 18c93ce2..f7fe63c5 100644 --- a/spsdk/image/hab_audit_log.py +++ b/spsdk/image/hab_audit_log.py @@ -10,11 +10,13 @@ import os from enum import Enum as PyEnum from struct import unpack_from -from typing import List, Type, Optional +from typing import List, Optional, Type +from spsdk import SPSDKError from spsdk.mboot import McuBoot, PropertyTag from spsdk.utils.easy_enum import Enum from spsdk.utils.misc import load_binary + from .commands import parse_command from .header import CmdTag @@ -230,7 +232,7 @@ def get_hab_log_info(hab_log: Optional[bytes]) -> bool: return True -def get_hab_enum_descr(enum_cls: Type[Enum], value: int) -> str: +def get_hab_enum_description(enum_cls: Type[Enum], value: int) -> str: """Converts integer value into description of the enumeration value. If the value does not match any value from enumeration, it is reported as `Unknown value` @@ -252,31 +254,31 @@ def parse_hab_log(hab_sts: int, hab_cfg: int, hab_state: int, data: bytes) -> Li :param hab_state: HAB state; this is result of the HAB function `report_status` :param data: HAB log data in binary format; result of the HAB function `report_event` :return: list of lines to be displayed, that describes the HAB status and content of the LOG - :raise ValueError: If a record has invalid data length + :raises SPSDKError: If a record has invalid data length """ result = list() result.append("=" * 60) - result.append(f"HAB Status: {get_hab_enum_descr(HabStatus, hab_sts)}") - result.append(f"HAB Config: {get_hab_enum_descr(HabConfig, hab_cfg)}") - result.append(f"HAB State : {get_hab_enum_descr(HabState, hab_state)}") + result.append(f"HAB Status: {get_hab_enum_description(HabStatus, hab_sts)}") + result.append(f"HAB Config: {get_hab_enum_description(HabConfig, hab_cfg)}") + result.append(f"HAB State : {get_hab_enum_description(HabState, hab_state)}") offset = 0 while offset + 8 <= len(data): result.append("=" * 60) # parse header - (cmd, leng, ver) = unpack_from(">BHB", data, offset) - if (cmd != 0xDB) or ((ver < 0x40) or (ver > 0x43)): + (cmd, length, version) = unpack_from(">BHB", data, offset) + if (cmd != 0xDB) or ((version < 0x40) or (version > 0x43)): break - if (leng < 8) or (leng > 1024): - raise ValueError("invalid log length") + if (length < 8) or (length > 1024): + raise SPSDKError("invalid log length") # parse data (sts, rsn, ctx, eng) = unpack_from("4B", data, offset + 4) # print results - result.append(f"Status: {get_hab_enum_descr(HabStatus, sts)}") - result.append(f"Reason: {get_hab_enum_descr(HabReason, rsn)}") - result.append(f"Context: {get_hab_enum_descr(HabContext, ctx)}") - result.append(f"Engine: {get_hab_enum_descr(HabEngine, eng)}") - if leng > 8: - result.append(f"Data: {data[offset + 8:offset + leng].hex().upper()}") + result.append(f"Status: {get_hab_enum_description(HabStatus, sts)}") + result.append(f"Reason: {get_hab_enum_description(HabReason, rsn)}") + result.append(f"Context: {get_hab_enum_description(HabContext, ctx)}") + result.append(f"Engine: {get_hab_enum_description(HabEngine, eng)}") + if length > 8: + result.append(f"Data: {data[offset + 8:offset + length].hex().upper()}") try: cmd = parse_command(data, offset + 8) result.append(f"Cmd : {CmdTag.desc(cmd.tag)}") @@ -284,7 +286,7 @@ def parse_hab_log(hab_sts: int, hab_cfg: int, hab_state: int, data: bytes) -> Li except ValueError: pass - offset += leng + offset += length return result @@ -299,12 +301,20 @@ def hab_audit_xip_app(cpu_data: CpuData, mboot: McuBoot, read_log_only: bool) -> It is recommended to call the function firstly with parameter `True` and second time with parameter False to see the difference. :return: bytes contains result of the hab log, otherwise returns None when an error occurred + :raises SPSDKError: When flashloader is not running + :raises SPSDKError: When given cpu data were not provided + :raises SPSDKError: When there is invalid address + :raises SPSDKError: When Log address is in conflict with reserved regions + :raises SPSDKError: When write memory failed + :raises SPSDKError: When call failed """ # check if the flashloader is running (not None) - assert mboot, "Flashloader is not running" + if not mboot: + raise SPSDKError("Flashloader is not running") # get CPU data dir, hab_audit_base and hab_audit_start - assert cpu_data, "Can not read the log, because given cpu data were not provided." + if not cpu_data: + raise SPSDKError("Can not read the log, because given cpu data were not provided.") cpu_data_bin_dir = cpu_data.bin evk_exec_hab_audit_base = cpu_data.base_address evk_exec_hab_audit_start = cpu_data.start_address @@ -325,17 +335,19 @@ def hab_audit_xip_app(cpu_data: CpuData, mboot: McuBoot, read_log_only: bool) -> log_addr = evk_exec_hab_audit_base + exec_hab_audit_code.find( b"\xA5\x5A\x11\x22\x33\x44\x55\x66" ) - assert log_addr > evk_exec_hab_audit_base + if log_addr <= evk_exec_hab_audit_base: + raise SPSDKError("Invalid address") # check if the executable binary is in collision with reserved region reserved_regions = mboot.get_property(PropertyTag.RESERVED_REGIONS) # check conflict between hab log address and any of reserved regions # we need 2 values (min and max) - that is why %2 is used - assert check_reserved_regions( - log_addr, reserved_regions - ), f"Log address is in conflict with reserved regions" - assert mboot.write_memory(evk_exec_hab_audit_base, exec_hab_audit_code, 0) - assert mboot.call(evk_exec_hab_audit_start | 1, 0 if read_log_only else 1) + if not check_reserved_regions(log_addr, reserved_regions): + raise SPSDKError("Log address is in conflict with reserved regions") + if not mboot.write_memory(evk_exec_hab_audit_base, exec_hab_audit_code, 0): + raise SPSDKError("Write memory failed") + if not mboot.call(evk_exec_hab_audit_start | 1, 0 if read_log_only else 1): + raise SPSDKError("Call failed") log = mboot.read_memory(log_addr, 100, 0) return log diff --git a/spsdk/image/header.py b/spsdk/image/header.py index 3fc10794..47d7cc30 100644 --- a/spsdk/image/header.py +++ b/spsdk/image/header.py @@ -8,12 +8,12 @@ """Header.""" -from struct import pack, unpack_from, calcsize +from struct import calcsize, pack, unpack_from from typing import Optional +from spsdk import SPSDKError from spsdk.utils.easy_enum import Enum - ######################################################################################################################## # Enums ######################################################################################################################## @@ -87,11 +87,13 @@ def __init__(self, tag: int = 0, param: int = 0, length: Optional[int] = None) - :param tag: section tag :param param: TODO :param length: length of the segment or command; if not specified, size of the header is used + :raises SPSDKError: If invalid length """ self._tag = tag self.param: int = param self.length: int = self.SIZE if length is None else length - assert self.SIZE <= self.length < 65536 + if self.SIZE > self.length or self.length >= 65536: + raise SPSDKError("Invalid length") @property def tag(self) -> int: @@ -146,9 +148,11 @@ def __init__(self, tag: CmdTag, param: int = 0, length: Optional[int] = None) -> :param tag: command tag :param param: TODO :param length: of the command binary section, in bytes + :raises SPSDKError: If invalid command tag """ super().__init__(tag, param, length) - assert tag in CmdTag.tags() + if tag not in CmdTag.tags(): + raise SPSDKError("Invalid command tag") @property def tag(self) -> CmdTag: @@ -163,10 +167,12 @@ def parse(cls, data: bytes, offset: int = 0, required_tag: Optional[int] = None) :param offset: to start reading binary data :param required_tag: CmdTag, None if not required :return: parsed instance - :raise UnparsedException: if required header tag does not match + :raises UnparsedException: if required header tag does not match + :raises SPSDKError: If invalid tag """ if required_tag is not None: - assert required_tag in CmdTag.tags() + if required_tag not in CmdTag.tags(): + raise SPSDKError("Invalid tag") return super(CmdHeader, cls).parse(data, offset, required_tag) diff --git a/spsdk/image/images.py b/spsdk/image/images.py index 5774b8c9..b15275e0 100644 --- a/spsdk/image/images.py +++ b/spsdk/image/images.py @@ -9,43 +9,50 @@ """Image.""" from datetime import datetime, timezone -from io import BytesIO, BufferedReader, SEEK_END, SEEK_CUR +from io import SEEK_CUR, SEEK_END, BufferedReader, BytesIO from struct import unpack_from -from typing import Optional, Tuple, Union, Any, List +from typing import Any, List, Optional, Tuple, Union from cryptography import x509 from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers.aead import AESCCM from cryptography.hazmat.primitives.serialization import Encoding +from spsdk import SPSDKError from spsdk.utils.crypto import crypto_backend from spsdk.utils.easy_enum import Enum -from spsdk.utils.misc import DebugInfo -from spsdk.utils.misc import align, align_block, extend_block -from .commands import CmdAuthData, CmdInstallKey -from .commands import EnumInsKey, EnumCertFormat, EnumAlgorithm, EnumAuthDat, EnumEngine +from spsdk.utils.misc import DebugInfo, align, align_block, extend_block + +from .commands import ( + CmdAuthData, + CmdInstallKey, + EnumAlgorithm, + EnumAuthDat, + EnumCertFormat, + EnumEngine, + EnumInsKey, +) from .header import Header, Header2, UnparsedException -from .misc import read_raw_data, read_raw_segment, NotEnoughBytesException -from .secret import Signature, CertificateImg, MAC, SrkTable +from .misc import NotEnoughBytesException, read_raw_data, read_raw_segment +from .secret import MAC, CertificateImg, Signature, SrkTable from .segments import ( - SegTag, - SegIVT2, - SegBDT, - SegAPP, - SegDCD, - SegCSF, - SegIVT3a, - SegIVT3b, - SegBDS3a, - SegBDS3b, - SegBIC1, AbstractFCB, FlexSPIConfBlockFCB, PaddingFCB, + SegAPP, + SegBDS3a, + SegBDS3b, + SegBDT, SegBEE, + SegBIC1, + SegCSF, + SegDCD, + SegIVT2, + SegIVT3a, + SegIVT3b, + SegTag, ) - ######################################################################################################################## # i.MX Boot Image Classes ######################################################################################################################## @@ -166,10 +173,16 @@ def __init__( :param offset: The IVT offset; use IVT_OFFSET_NOR_FLASH for NOR-FLASH or IVT_OFFSET_OTHER :param version: The version of boot img format; default value should be used :param plugin: Do not use; see `self.plugin` property + :raises SPSDKError: If invalid IVT offset + :raises SPSDKError: If invalid version + :raises SPSDKError: If Plugin is not supported """ - assert offset in BootImgRT.IVT_OFFSETS - assert version in self.VERSIONS - assert plugin is False # not supported yet + if offset not in BootImgRT.IVT_OFFSETS: + raise SPSDKError("Invalid IVT offset") + if version not in self.VERSIONS: + raise SPSDKError("Invalid version") + if plugin is True: + raise SPSDKError("Plugin is not supported") # not supported yet super().__init__(address, offset) self._nonce: Optional[bytes] = None self._dek_key: Optional[bytes] = None @@ -197,8 +210,10 @@ def dek_key(self, value: bytes) -> None: """Setter. :param value: DEK key for encrypted images + :raises SPSDKError: If invalid length of DEK key """ - assert len(value) == MAC.AES128_BLK_LEN + if len(value) != MAC.AES128_BLK_LEN: + raise SPSDKError("Invalid length of DEK key") self._dek_key = value @property @@ -233,8 +248,10 @@ def ivt_offset(self, value: int) -> None: """Setter. :param value: new IVT offset + :raises SPSDKError: If invalid IVT offset """ - assert value in self.IVT_OFFSETS + if value not in self.IVT_OFFSETS: + raise SPSDKError("Invalid IVT offset") self.offset = value @property @@ -303,7 +320,6 @@ def set_flexspi_fcb(self, data: Union[bytes, FlexSPIConfBlockFCB]) -> None: """Set FlexSPI external FLASH configuration. :param data: FlexSPIConfBlockFCB or binary data representing - :raise ValueError: if data are not valid Flex SPI configuration block """ self.fcb = ( data if isinstance(data, FlexSPIConfBlockFCB) else FlexSPIConfBlockFCB.parse(data) @@ -399,6 +415,8 @@ def _update_csf(self, csf: SegCSF) -> None: """Update CSF segment. :param csf: CSF segment tu be updated + :raises SPSDKError: If nonce not present + :raises SPSDKError: If mac not present """ self.app.padding_len = align(self.app.size, 0x1000) - self.app.size csf.update(True) @@ -415,8 +433,10 @@ def _update_csf(self, csf: SegCSF) -> None: # self.bdt.app_length += self.DEK_SIZE # to include DEK # update encryption signature - assert self._nonce - assert self._mac + if not self._nonce: + raise SPSDKError("Nonce not present") + if not self._mac: + raise SPSDKError("Mac not present") for mac in csf.macs: mac.update_aead_encryption_params(self._nonce, self._mac) @@ -479,26 +499,31 @@ def add_image( :param nonce: initial vector for AEAD HAB encryption, if not specified random value is used; For non-encrypted image use `None` The parameter should be used only for testing to produce stable output - :raise ValueError: if any parameter is not valid + :raises ValueError: if any parameter is not valid + :raises SPSDKError: If invalid image type + :raises SPSDKError: If image was already added + :raises SPSDKError: If entry_addr not detected from image, must be specified explicitly + :raises SPSDKError: If hab is not encrypted + :raises SPSDKError: If nonce is not empty """ - assert img_type == EnumAppType.APP + if img_type != EnumAppType.APP: + raise SPSDKError("Invalid image type") if self.app.data: - raise ValueError("Image was already added") + raise SPSDKError("Image was already added") entry_addr = unpack_from(" 0 - ), "entry_addr not detected from image, must be specified explicitly" + if not entry_addr > 0: + raise SPSDKError("entry_addr not detected from image, must be specified explicitly") elif (address >= 0) and (address != entry_addr): - raise ValueError("entry_address does not match with the image") + raise SPSDKError("entry_address does not match with the image") self._ivt.app_address = entry_addr self.app.data = data if dek_key is not None: # encrypted? # initialize DEK key self._dek_key = bytes([0]) * MAC.AES128_BLK_LEN if len(dek_key) == 0 else dek_key if len(self._dek_key) != MAC.AES128_BLK_LEN: - raise ValueError(f"Invalid dek_key length, expected {MAC.AES128_BLK_LEN} bytes") + raise SPSDKError(f"Invalid dek_key length, expected {MAC.AES128_BLK_LEN} bytes") # initialize NONCE if nonce: self._nonce = nonce @@ -506,21 +531,27 @@ def add_image( if self._nonce is None: self._nonce = crypto_backend().random_bytes(nonce_len) elif len(self._nonce) != nonce_len: - raise ValueError(f"Invalid nonce length, expected {nonce_len} bytes") + raise SPSDKError(f"Invalid nonce length, expected {nonce_len} bytes") # encrypt APP - assert self.hab_encrypted + if not self.hab_encrypted: + raise SPSDKError("Hab is not encrypted") self.app.data = self._hab_encrypt_app_data(align_block(data, MAC.AES128_BLK_LEN)) else: - assert nonce is None + if nonce is not None: + raise SPSDKError("Nonce is not empty") def add_dcd_bin(self, data: bytes) -> None: """Add DCD binary data. :param data: DCD binary data to be added + :raises SPSDKError: If DCD is already present + :raises SPSDKError: If DCD is not enabled """ - assert self.dcd is None + if self.dcd is not None: + raise SPSDKError("DCD is already present") self.dcd = SegDCD.parse(data) - assert self.dcd # must be enabled to include DCD into export + if not self.dcd: + raise SPSDKError("DCD must be enabled to include DCD into export") def add_csf_standard_auth( self, @@ -543,9 +574,14 @@ def add_csf_standard_auth( :param csf_priv_key: CSF private key; decrypted binary data in PEM format :param img_cert: IMG certificate :param img_priv_key: IMG private key; decrypted binary data in PEM format + :raises SPSDKError: If invalid length of srk table + :raises SPSDKError: If invalid index of selected SRK key + :raises SPSDKError: If application data not present """ - assert 1 <= len(srk_table) <= 4 - assert 0 <= src_key_index < len(srk_table) + if not 1 <= len(srk_table) <= 4: + raise SPSDKError("Invalid length of srk table") + if not 0 <= src_key_index < len(srk_table): + raise SPSDKError("Invalid index of selected SRK key") csf = SegCSF(version=version, enabled=True) # install SRK cmd_ins = CmdInstallKey( @@ -594,7 +630,8 @@ def add_csf_standard_auth( self.dcd.size, ) app_data = self.app.data - assert app_data is not None + if app_data is None: + raise SPSDKError("Application data not present") cmd_auth.append(self.address + self.app_offset, align(len(app_data), 16)) cmd_auth.cmd_data_reference = Signature(version=version) csf.append_command(cmd_auth) @@ -629,14 +666,22 @@ def _hab_encrypt_app_data(self, app_data: bytes) -> bytes: :param app_data: application data to be encrypted :return: encrypted application data (using HAB encryption) + :raises SPSDKError: If nonce is not present + :raises SPSDKError: If invalid length of application data + :raises SPSDKError: If DEK key is not present + :raises SPSDKError: If invalid length of encrypted data """ - assert self._nonce is not None - assert len(app_data) & (MAC.AES128_BLK_LEN - 1) == 0 + if self._nonce is None: + raise SPSDKError("Nonce is not present") + if not len(app_data) & (MAC.AES128_BLK_LEN - 1) == 0: + raise SPSDKError("Invalid length of application data") dek = self.dek_key - assert dek is not None + if dek is None: + raise SPSDKError("DEK key is not present") aesccm = AESCCM(dek, tag_length=MAC.AES128_BLK_LEN) encr = aesccm.encrypt(self._nonce, app_data, b"") - assert len(encr) == len(app_data) + 16 + if len(encr) != len(app_data) + 16: + raise SPSDKError("Invalid length of encrypted data") self._mac = encr[-16:] return encr[:-16] @@ -645,16 +690,22 @@ def decrypted_app_data(self) -> bytes: """Return decrypted binary application data. Note: dek key, mac and nonce must be assigned for decryption + :raises SPSDKError: If application not present + :raises SPSDKError: If invalid length of application data + :raises SPSDKError: If Mac or nonce or dek not present """ app_data = self.app.data - assert app_data + if not app_data: + raise SPSDKError("Application not present") if not self.hab_encrypted: return app_data - assert len(app_data) & (MAC.AES128_BLK_LEN - 1) == 0 + if not len(app_data) & (MAC.AES128_BLK_LEN - 1) == 0: + raise SPSDKError("Invalid length of application data") mac = self._mac dek = self.dek_key - assert mac and self._nonce and dek + if not (mac and self._nonce and dek): + raise SPSDKError("Mac or nonce or dek not present") aesccm = AESCCM(dek, tag_length=MAC.AES128_BLK_LEN) res = aesccm.decrypt(self._nonce, app_data + mac, b"") return res @@ -680,9 +731,14 @@ def add_csf_encrypted( :param csf_priv_key: CSF private key; decrypted binary data in PEM format :param img_cert: IMG certificate :param img_priv_key: IMG private key; decrypted binary data in PEM format + :raises SPSDKError: If invalid length of srk table + :raises SPSDKError: If invalid index of srk table + :raises SPSDKError: If application data is not present """ - assert 1 <= len(srk_table) <= 4 - assert 0 <= src_key_index < len(srk_table) + if not 1 <= len(srk_table) <= 4: + raise SPSDKError("Invalid length of srk table") + if not 0 <= src_key_index < len(srk_table): + raise SPSDKError("Invalid index of srk table") csf = SegCSF(version=version, enabled=True) # install SRK cmd_ins = CmdInstallKey( @@ -726,7 +782,8 @@ def add_csf_encrypted( ) cmd_auth.append(self.address + self.ivt_offset, SegIVT2.SIZE + BootImgRT.BDT_SIZE) app_data = self.app.data - assert app_data is not None + if app_data is None: + raise SPSDKError("Application data is not present") cmd_auth.cmd_data_reference = Signature(version=version) csf.append_command(cmd_auth) # install DEK key @@ -741,7 +798,8 @@ def add_csf_encrypted( certificate=cert, private_key_pem_data=img_priv_key, ) - assert app_data is not None + if app_data is None: + raise SPSDKError("Application data is not present") cmd_auth.append(self.address + self.app_offset, align(len(app_data), 16)) cmd_auth.cmd_data_reference = MAC(version=version, nonce_len=0xD, mac_len=16) csf.append_command(cmd_auth) @@ -753,16 +811,18 @@ def _export_fcb_bee(self, dbg_info: DebugInfo) -> bytes: :param dbg_info: optional instance to provide info about exported data :return: binary FCB segment and BEE regions - :raise ValueError: if any BEE region is configured for images not located in the FLASH + :raises ValueError: if any BEE region is configured for images not located in the FLASH + :raises SPSDKError: If invalid length of data """ if not self.fcb.enabled: return b"" data = self.fcb.export(dbg_info=dbg_info) - assert len(data) == self.fcb.space + if len(data) != self.fcb.space: + raise SPSDKError("Invalid length of data") if self.ivt_offset == self.IVT_OFFSET_NOR_FLASH: data += self.bee.export(dbg_info=dbg_info) elif self.bee.space > 0: - raise ValueError("BEE can be configured only for XIP images located in FLASH") + raise SPSDKError("BEE can be configured only for XIP images located in FLASH") return data def _bee_encrypt_img_data(self, data: bytes) -> bytes: @@ -770,15 +830,15 @@ def _bee_encrypt_img_data(self, data: bytes) -> bytes: :param data: image data (including IVT offset) to be encrypted :return: the image with encrypted regions - :raise ValueError: if image configuration is invalid and BEE encryption cannot be applied + :raises SPSDKError: If image configuration is invalid and BEE encryption cannot be applied """ if not self.bee_encrypted: return data if self.ivt_offset != self.IVT_OFFSET_NOR_FLASH: - raise ValueError("BEE encryption is supported only for NOR FLASH") + raise SPSDKError("BEE encryption is supported only for NOR FLASH") if self.hab_encrypted: - raise ValueError("BEE encryption cannot be used for HAB encrypted images") + raise SPSDKError("BEE encryption cannot be used for HAB encrypted images") # encrypt return data[: self.ivt_offset] + self.bee.encrypt_data( @@ -795,14 +855,16 @@ def export( :param zulu: optional UTC datetime; should be used only if you need fixed datetime for the test Note: the parameter is applied to CSF only, so it is not used for unsigned images :param dbg_info: optional instance to provide info about exported data - :raises ValueError: If the image is not encrypted + :raises SPSDKError: If the image is not encrypted + :raises SPSDKError: If padding is present + :raises SPSDKError: If invalid alignment of application :return: bytes """ csf = self.enabled_csf if csf: csf.update_signatures(zulu, b"", 0) # dummy call to provide size of the CSF section elif self.dek_key is not None: - raise ValueError("CSF must be assigned for encrypted images") + raise SPSDKError("CSF must be assigned for encrypted images") self._update() dbg_info.append_section("RT10xxBootableImage") @@ -819,13 +881,15 @@ def export( dbg_info.append_binary_section("BDT", bdt_data) # DCD if (self.dcd is not None) and self.dcd.enabled: - assert self.dcd.padding_len == 0 # no padding + if self.dcd.padding_len != 0: + raise SPSDKError("Padding can not be present") dcd_data = self.dcd.export() data += dcd_data dbg_info.append_binary_section("DCD", dcd_data) # padding before APP app_alignment = self.app_offset if self.fcb.enabled else self.app_offset - self.ivt_offset - assert app_alignment >= len(data) + if not app_alignment >= len(data): + raise SPSDKError("Invalid alignment of application") data = extend_block(data, app_alignment) # APP app_data = self.app.export() @@ -849,7 +913,7 @@ def _find_ivt_pos( :param strm: of image data; start seeking from current position :param size: maximum length - :raise ValueError: Raised when IVT is not found + :raises SPSDKError: Raised when IVT is not found :return: tuple with: Header, start position, end position """ start_pos = strm.tell() @@ -871,7 +935,7 @@ def _find_ivt_pos( except UnparsedException: # ignore different header tags pass - raise ValueError("IVT not found") + raise SPSDKError("IVT not found") @classmethod def _find_fcb_pos( @@ -912,14 +976,14 @@ def parse( :param stream: The stream buffer or bytes array :param step: Image searching step (this parameter is not used for RT) :param size: parsing size; None to parse till the end of the stream - :raises TypeError: Raised when the value type is incorrect + :raises SPSDKError: Raised when the value type is incorrect :return: BootImgRT object """ if isinstance(stream, (bytes, bytearray)): stream = BytesIO(stream) if not isinstance(stream, (BufferedReader, BytesIO)): - raise TypeError(' Not correct value type: "{}" !'.format(type(stream))) + raise SPSDKError(' Not correct value type: "{}" !'.format(type(stream))) header, start_pos, end_pos = cls._find_ivt_pos(stream, size) @@ -1180,15 +1244,15 @@ def parse( :param stream: The stream buffer or bytes array :param step: Image searching step :param size: parsing size - :raises TypeError: raised when value type is incorrect - :raises Exception: raised when there is not an i.MX Boot Image + :raises SPSDKError: Raised when value type is incorrect + :raises SPSDKError: Raised when there is not an i.MX Boot Image :return: BootImg2 object """ if isinstance(stream, (bytes, bytearray)): stream = BytesIO(stream) if not isinstance(stream, (BufferedReader, BytesIO)): - raise TypeError(f"Not correct value type: {type(stream)} !") + raise SPSDKError(f"Not correct value type: {type(stream)} !") header = Header() start_index = stream.tell() @@ -1212,7 +1276,7 @@ def parse( start_index = stream.seek(step, SEEK_CUR) if not imx_image: - raise Exception("Not an i.MX Boot Image!") + raise SPSDKError("Not an i.MX Boot Image!") obj = BootImg2() if header.param: @@ -1466,15 +1530,15 @@ def parse( :param stream: The stream buffer or bytes array :param step: Image searching step :param size: parsing size - :raises TypeError: raised when the value type is incorrect - :raises Exception: raised when there is not an i.MX Boot Image + :raises SPSDKError: Raised when the value type is incorrect + :raises SPSDKError: Raised when there is not an i.MX Boot Image :return: BootImg2 object """ if isinstance(stream, (bytes, bytearray)): stream = BytesIO(stream) if not isinstance(stream, (BufferedReader, BytesIO)): - raise TypeError(f"Not correct value type: {type(stream)}!") + raise SPSDKError(f"Not correct value type: {type(stream)}!") header = Header() start_index = stream.tell() @@ -1498,7 +1562,7 @@ def parse( start_index = stream.seek(step, SEEK_CUR) if not imx_image: - raise Exception("Not an i.MX Boot Image!") + raise SPSDKError("Not an i.MX Boot Image!") obj = cls(version=header.param) img_size = last_index - start_index @@ -1637,7 +1701,7 @@ def __init__(self, address: int = 0, offset: int = 0x400, version: int = 0x43) - @staticmethod def _compute_padding(size: int, sector_size: int) -> int: - return ((size // sector_size + (size % sector_size > 0)) * sector_size) - size + return (size // sector_size + (size % sector_size > 0)) * sector_size - size def _update(self) -> None: # Set zero padding for IVT and BDT sections @@ -1848,15 +1912,15 @@ def parse( :param stream: The stream buffer or bytes array :param step: Image searching step :param size: parsing size - :raises TypeError: raised when the values type is incorrect - :raises Exception: raised when there is not an i.MX Boot Image + :raises SPSDKError: Raised when the values type is incorrect + :raises SPSDKError: Raised when there is not an i.MX Boot Image :return: BootImg3a object """ if isinstance(stream, (bytes, bytearray)): stream = BytesIO(stream) if not isinstance(stream, (BufferedReader, BytesIO)): - raise TypeError(f"Not correct value type: {type(stream)}!") + raise SPSDKError(f"Not correct value type: {type(stream)}!") header = Header() start_index = stream.tell() @@ -1880,7 +1944,7 @@ def parse( start_index = stream.seek(step, SEEK_CUR) if not imx_image: - raise Exception("Not an i.MX Boot Image!") + raise SPSDKError("Not an i.MX Boot Image!") obj = cls(version=header.param) # TODO: not used right now: img_size = last_index - start_index @@ -1968,7 +2032,8 @@ def ivt(self) -> List[SegIVT3b]: @ivt.setter def ivt(self, value: List) -> None: assert isinstance(value, list) - assert len(value) == self.COUNT_OF_CONTAINERS + if len(value) != self.COUNT_OF_CONTAINERS: + raise SPSDKError("Invalid value of IVT") assert isinstance(value[0], SegIVT3b) self._ivt = value @@ -1980,7 +2045,8 @@ def bdt(self) -> List[SegBDS3b]: @bdt.setter def bdt(self, value: List) -> None: assert isinstance(value, list) - assert len(value) == self.COUNT_OF_CONTAINERS + if len(value) != self.COUNT_OF_CONTAINERS: + raise SPSDKError("Invalid value of BDT") assert isinstance(value[0], SegBDS3b) self._bdt = value @@ -2256,15 +2322,15 @@ def parse( :param stream: The stream buffer or bytes array :param step: Image searching step :param size: parsing size - :raises TypeError: When the value is incorrect - :raises Exception: If there is not an i.MX Boot Image + :raises SPSDKError: When the value is incorrect + :raises SPSDKError: If there is not an i.MX Boot Image :return: BootImg3b object """ if isinstance(stream, (bytes, bytearray)): stream = BytesIO(stream) if not isinstance(stream, (BufferedReader, BytesIO)): - raise TypeError(f"Not correct value type: {type(stream)}!") + raise SPSDKError(f"Not correct value type: {type(stream)}!") header = Header() start_index = stream.tell() @@ -2288,7 +2354,7 @@ def parse( start_index = stream.seek(step, SEEK_CUR) if not imx_image: - raise Exception("Not an i.MX Boot Image!") + raise SPSDKError("Not an i.MX Boot Image!") obj = cls(version=header.param) # TODO: not used right now: img_size = last_index - start_index @@ -2391,14 +2457,14 @@ def parse( :param step: Image searching step :param size: parsing size :return: BootImg4 object - :raises TypeError: Raised when the value type is incorrect - :raises Exception: If there is not an i.MX Boot Image + :raises SPSDKError: Raised when the value type is incorrect + :raises SPSDKError: If there is not an i.MX Boot Image """ if isinstance(stream, (bytes, bytearray)): stream = BytesIO(stream) if not isinstance(stream, (BufferedReader, BytesIO)): - raise TypeError(' Not correct value type: "{}" !'.format(type(stream))) + raise SPSDKError(' Not correct value type: "{}" !'.format(type(stream))) start_index = stream.tell() last_index = stream.seek(0, SEEK_END) @@ -2417,7 +2483,7 @@ def parse( start_index = stream.seek(step, SEEK_CUR) if not imx_image: - raise Exception(" Not an i.MX Boot Image !") + raise SPSDKError(" Not an i.MX Boot Image !") # TODO: not used right now: img_size = last_index - start_index obj = cls() @@ -2517,7 +2583,8 @@ def export(self) -> bytes: def parse(cls, data: Union[str, bytes]) -> None: """Parse.""" assert isinstance(data, (bytes, str)) - assert len(data) > cls.IMAGE_MIN_SIZE + if not len(data) > cls.IMAGE_MIN_SIZE: + raise SPSDKError("Invalid length of data to be parsed") ######################################################################################################################## @@ -2533,17 +2600,17 @@ def parse( """Common parser for all versions of i.MX boot images. :param stream: stream buffer to image - :raises TypeError: raised when the format of string is incorrect :param step: Image searching step :param size: parsing size :return: the object of boot image - :raises Exception: when not i.MX Boot Image is passed + :raises SPSDKError: Raised when the format of string is incorrect + :raises SPSDKError: When not i.MX Boot Image is passed """ if isinstance(stream, (bytes, bytearray)): stream = BytesIO(stream) if not isinstance(stream, (BufferedReader, BytesIO)): - raise TypeError(' Not correct value type: "{}" !'.format(type(stream))) + raise SPSDKError('Not correct value type: "{}" !'.format(type(stream))) # calculate stream size start_index = stream.tell() @@ -2582,4 +2649,4 @@ def parse( start_index = stream.seek(step, SEEK_CUR) - raise Exception(" Not an i.MX Boot Image !") + raise SPSDKError(" Not an i.MX Boot Image !") diff --git a/spsdk/image/keystore.py b/spsdk/image/keystore.py index 5facfb5d..2b21d810 100644 --- a/spsdk/image/keystore.py +++ b/spsdk/image/keystore.py @@ -9,6 +9,7 @@ from Crypto.Cipher import AES +from spsdk import SPSDKError from spsdk.utils.easy_enum import Enum @@ -37,16 +38,16 @@ def __init__(self, key_source: KeySourceType, key_store: bytes = None) -> None: :param key_source: device key source :param key_store: initial content of the key store in the bootable image; None if empty - :raises ValueError: if invalid key-store size + :raises SPSDKError: If invalid key-store size + :raises SPSDKError: KeyStore can be initialized only if key_source == KEYSTORE """ if key_store: if len(key_store) != self.KEY_STORE_SIZE: - raise ValueError( + raise SPSDKError( f"Invalid key-store size, expected is {str(self.KEY_STORE_SIZE)} bytes" ) - assert ( - key_source == KeySourceType.KEYSTORE - ), "KeyStore can be initialized only if key_source == KEYSTORE" + if key_source != KeySourceType.KEYSTORE: + raise SPSDKError("KeyStore can be initialized only if key_source == KEYSTORE") self._key_source = key_source self._key_store = key_store @@ -69,8 +70,10 @@ def derive_hmac_key(hmac_key: bytes) -> bytes: :param hmac_key: either master-key (for key_source == OTP) or user key (for key_source == KEYSTORE) :return: key used for image header authentication in LoadToRam images + :raises SPSDKError: If invalid length of hmac key """ - assert len(hmac_key) == 32 + if len(hmac_key) != 32: + raise SPSDKError("Invalid length of hmac key") aes = AES.new(hmac_key, AES.MODE_ECB) return aes.encrypt(bytes([0] * 16)) @@ -80,8 +83,10 @@ def derive_enc_image_key(master_key: bytes) -> bytes: :param master_key: stored in OTP :return: key used to decrypt encrypted images during boot + :raises SPSDKError: If invalid length of master key """ - assert len(master_key) == 32 + if len(master_key) != 32: + raise SPSDKError("Invalid length of master key") aes = AES.new(master_key, AES.MODE_ECB) return aes.encrypt(bytes([1] + [0] * 15 + [2] + [0] * 15)) @@ -91,8 +96,10 @@ def derive_sb_kek_key(master_key: bytes) -> bytes: :param master_key: 32 bytes key, stored in OTP :return: encryption key to handle SB2 file (update capsule) + :raises SPSDKError: If invalid length of master key """ - assert len(master_key) == 32 + if len(master_key) != 32: + raise SPSDKError("Invalid length of master key") aes = AES.new(master_key, AES.MODE_ECB) return aes.encrypt(bytes([3] + [0] * 15 + [4] + [0] * 15)) @@ -103,8 +110,12 @@ def derive_otfad_kek_key(master_key: bytes, otfad_input: bytes) -> bytes: :param master_key: 32 bytes key, stored in OTP :param otfad_input: 16 bytes input, stored in OTP :return: OTFAD encryption key for FLASH encryption/decryption + :raises SPSDKError: If invalid length of master key + :raises SPSDKError: If invalid length of input """ - assert len(master_key) == 32 - assert len(otfad_input) == 16 + if len(master_key) != 32: + raise SPSDKError("Invalid length of master key") + if len(otfad_input) != 16: + raise SPSDKError("Invalid length of input") aes = AES.new(master_key, AES.MODE_ECB) return aes.encrypt(otfad_input) diff --git a/spsdk/image/mbimg.py b/spsdk/image/mbimg.py index 9521bb20..34e7c08b 100644 --- a/spsdk/image/mbimg.py +++ b/spsdk/image/mbimg.py @@ -10,14 +10,15 @@ import struct from typing import Any, List, Optional, Sequence, Union -from Crypto.Cipher import AES from crcmod.predefined import mkPredefinedCrcFun +from Crypto.Cipher import AES +from spsdk import SPSDKError from spsdk.crypto import SignatureProvider from spsdk.image.keystore import KeySourceType, KeyStore from spsdk.image.trustzone import TrustZone, TrustZoneType from spsdk.utils import misc -from spsdk.utils.crypto import crypto_backend, CertBlock, serialize_ecc_signature +from spsdk.utils.crypto import CertBlock, crypto_backend, serialize_ecc_signature from spsdk.utils.easy_enum import Enum @@ -100,9 +101,13 @@ def __init__(self, img: bytes, dst_addr: int, flags: int = LTI_LOAD): :param img: binary image data :param dst_addr: destination address :param flags: see LTI constants + :raises SPSDKError: If invalid destination address + :raises SPSDKError: Other section types (INIT) are not supported """ - assert 0 <= dst_addr <= 0xFFFFFFFF - assert flags == self.LTI_LOAD # for now, other section types (INIT) are not supported + if dst_addr < 0 or dst_addr > 0xFFFFFFFF: + raise SPSDKError("Invalid destination address") + if flags != self.LTI_LOAD: + raise SPSDKError("for now, other section types (INIT) are not supported") self._img = img self._src_addr = 0 self._dst_addr = dst_addr @@ -211,8 +216,10 @@ def export(self, start_addr: int) -> bytes: :param start_addr: start address where the images are exported; the value matches source address for the first image :return: images with relocation table + :raises SPSDKError: If there is no entry for export """ - assert self._entries, "There must be at least one entry for export" + if not self._entries: + raise SPSDKError("There must be at least one entry for export") src_addr = start_addr result = bytes() for entry in self.entries: @@ -335,14 +342,16 @@ def __init__( :param enable_hw_user_mode_keys: flag for controlling secure hardware key bus. If true, then it is possible to access keys on hardware secure bus from non-secure application, else non-secure application will read zeros. :param ctr_init_vector: optional initial vector for encryption counter; None to use random vector - :raises TypeError: if type is not binary data - :raises ValueError: if images are not loaded from RAM + :raises SPSDKError: If type is not binary data + :raises SPSDKError: If images are not loaded from RAM + :raises SPSDKError: If invalid address """ if not isinstance(app, (bytes, bytearray)): - raise TypeError("app must be binary data (bytes, bytearray)") + raise SPSDKError("app must be binary data (bytes, bytearray)") if app_table and not MasterBootImageType.is_copied_to_ram(image_type): - raise ValueError("app_table can be used only for images loaded to RAM") - assert load_addr >= 0 + raise SPSDKError("app_table can be used only for images loaded to RAM") + if load_addr < 0: + raise SPSDKError("Invalid address") self.load_addr = load_addr self.image_type = image_type alignment = MasterBootImage._IMAGE_ALIGNMENT @@ -372,32 +381,32 @@ def __init__( def _validate_new_instance(self) -> None: """Validate new instance. - :raise ValueError: if there are invalid or conflicting parameters + :raises SPSDKError: If there are invalid or conflicting parameters """ # table if self.app_table: if not self.app_table.entries: - raise ValueError("app_table is empty") + raise SPSDKError("app_table is empty") # image size if len(self.app) < self.HMAC_OFFSET: - raise ValueError("Image must be at least {} bytes".format(str(self.HMAC_OFFSET))) + raise SPSDKError("Image must be at least {} bytes".format(str(self.HMAC_OFFSET))) # security stuff if MasterBootImageType.is_signed(self.image_type): if not self.cert_block: - raise ValueError( + raise SPSDKError( "Certificate block must be specified for signed image (cert_block)" ) if not self._priv_key_pem_data: - raise ValueError("Private Key must be specified for signed image (priv_key_path)") + raise SPSDKError("Private Key must be specified for signed image (priv_key_path)") else: if self.cert_block: - raise ValueError( + raise SPSDKError( "Certificate block must be specified only for signed image (cert_block)" ) if self._priv_key_pem_data: - raise ValueError( + raise SPSDKError( "Private Key must be specified only for signed image (priv_key_path)" ) @@ -405,34 +414,36 @@ def _validate_new_instance(self) -> None: if not self.ctr_init_vector or ( len(self.ctr_init_vector) != self._CTR_INIT_VECTOR_SIZE ): - raise ValueError( + raise SPSDKError( f"Invalid length of CTR init vector, expected {str(self._CTR_INIT_VECTOR_SIZE)} bytes" ) # hmac if MasterBootImageType.has_hmac(self.image_type): if not self.hmac_key: - raise ValueError( + raise SPSDKError( "HMAC key must be specified for load-to-ram signed images (hmac_key)" ) else: if self.hmac_key: - raise ValueError( + raise SPSDKError( "HMAC user key cannot be applied into selected image (hmac_user_key)" ) if self.key_store: - raise ValueError("KeyStore cannot be applied into selected image (key_store)") + raise SPSDKError("KeyStore cannot be applied into selected image (key_store)") def _verify_private_key(self) -> None: """Verifies private key. - :raise ValueError: if any parameter not valid + :raises SPSDKError: If certification block is not present + :raises SPSDKError: If any parameter not valid """ if self._priv_key_pem_data: cert_blk = self.cert_block - assert cert_blk is not None + if cert_blk is None: + raise SPSDKError("Certification block is not present") if not cert_blk.verify_private_key(self._priv_key_pem_data): # type: ignore - raise ValueError( + raise SPSDKError( "Signature verification failed, private key does not match to certificate" ) @@ -494,13 +505,15 @@ def _certificate(self, encr_data: bytes) -> bytes: - for encrypted image: certificate with encrypted image header and CTR init vector - for signed image: certificate - for plain image: empty bytes + :raises SPSDKError: If initial vector for encryption counter is not present """ if not self.cert_block: return bytes() # for encrypted image create encrypted header located behind certificate if MasterBootImageType.is_encrypted(self.image_type): - assert self.ctr_init_vector + if not self.ctr_init_vector: + raise SPSDKError("Initial vector for encryption counter is not present") encr_header = encr_data[:56] + self.ctr_init_vector else: encr_header = bytes() @@ -512,44 +525,65 @@ def _hmac(self, data: bytes) -> bytes: :param data: to calculate hmac :return: calculated hmac; empty bytes if the block does not contain any HMAC + :raises SPSDKError: If invalid hmac key + :raises SPSDKError: If invalid length of key + :raises SPSDKError: If invalid length of calculated hmac + :raises SPSDKError: Key_store must be specified for encrypted image + :raises SPSDKError: If invalid hmac key + :raises SPSDKError: If invalid initialization vector + :raises SPSDKError: Unsupported key_source """ if not MasterBootImageType.has_hmac(self.image_type): return bytes() - assert self.hmac_key and len(self.hmac_key) == self._HMAC_KEY_LENGTH + if not (self.hmac_key and len(self.hmac_key) == self._HMAC_KEY_LENGTH): + raise SPSDKError("Invalid hmac key") key = KeyStore.derive_hmac_key(self.hmac_key) - assert len(key) == self._HMAC_DERIVED_KEY_LEN + if len(key) != self._HMAC_DERIVED_KEY_LEN: + raise SPSDKError("Invalid length of key") result = crypto_backend().hmac(key, data) - assert len(result) == self.HMAC_SIZE + if len(result) != self.HMAC_SIZE: + raise SPSDKError("Invalid length of calculated hmac") return result def _encrypt(self, data: bytes) -> bytes: if not MasterBootImageType.is_encrypted(self.image_type): return data - assert self.key_store, "key_store must be specified for encrypted image" - assert self.hmac_key and len(self.hmac_key) == self._HMAC_KEY_LENGTH - assert self.ctr_init_vector + if not self.key_store: + raise SPSDKError("key_store must be specified for encrypted image") + if not (self.hmac_key and len(self.hmac_key) == self._HMAC_KEY_LENGTH): + raise SPSDKError("Invalid hmac key") + if not self.ctr_init_vector: + raise SPSDKError("Invalid initialization vector") if self.key_store.key_source == KeySourceType.KEYSTORE: key = self.hmac_key # user_key, the key not derived elif self.key_store.key_source == KeySourceType.OTP: key = self.key_store.derive_enc_image_key(self.hmac_key) else: - assert False, "Unsupported key_source" + if True: + raise SPSDKError("Unsupported key_source") aes = AES.new(key, AES.MODE_CTR, initial_value=self.ctr_init_vector, nonce=bytes()) return aes.encrypt(data) def export(self) -> bytes: - """Master boot image (binary).""" + """Master boot image (binary). + + :raises SPSDKError: If private key not present + :raises SPSDKError: If wrong private key + :return: exported bytes + """ data = self._update_ivt(self.data) # signed or encrypted if MasterBootImageType.is_signed(self.image_type): - assert self._priv_key_pem_data + if not self._priv_key_pem_data: + raise SPSDKError("Private key not present") cb = self.cert_block - assert (cb is not None) and cb.verify_private_key(self._priv_key_pem_data) # type: ignore + if not ((cb is not None) and cb.verify_private_key(self._priv_key_pem_data)): # type: ignore + raise SPSDKError("Wrong private key") # encrypt encr_data = self._encrypt(data) encr_data = ( @@ -631,15 +665,14 @@ def export(self) -> bytes: class MasterBootImageN4Analog(MasterBootImage): """Master Boot Image layout specific for LPC55s3x.""" - # flag indication presence of dual boot version (Used by LPC55s3x) - _DUAL_BOOT_VERSION_FLAG = 0x400 + # flag indication presence of boot image version (Used by LPC55s3x) + _BOOT_IMAGE_VERSION_FLAG = 0x400 def __init__( self, app: bytes, load_addr: int, - dual_boot_version: int = None, - firmware_version: int = 1, + firmware_version: int, sign_hash_len: int = 0, signature_provider: SignatureProvider = None, **kwargs: Any, @@ -647,18 +680,19 @@ def __init__( """Initialize MBI for LPC55s3x. :param app: application binary - :param load_addr: Addres where to load application - :param dual_boot_version: Version of dualboot application, defaults to None + :param load_addr: Address where to load application :param firmware_version: Firmware version, defaults to None :param sign_hash_len: Length of hash used for singing, defaults to 0 :param signature_provider: Signature provider meant to sign the image :param kwargs: keyword arguments passed to MasterBootImage + :raises SPSDKError: If trustZone was not set in parent class """ super().__init__(app=app, load_addr=load_addr, **kwargs) - self.dual_boot_version = dual_boot_version + self.firmware_version = firmware_version self.manifest = None self.signature_provider = signature_provider - assert self.trust_zone, "TrustZone was not set in parent class!" + if not self.trust_zone: + raise SPSDKError("TrustZone was not set in parent class!") if MasterBootImageType.is_signed(self.image_type): self.manifest = MasterBootImageManifest( firmware_version, @@ -668,9 +702,9 @@ def __init__( def _calculate_flags(self) -> int: flags = super()._calculate_flags() - if self.dual_boot_version: - flags |= self._DUAL_BOOT_VERSION_FLAG - flags |= self.dual_boot_version << 16 + if self.firmware_version: + flags |= self._BOOT_IMAGE_VERSION_FLAG + flags |= self.firmware_version << 16 return flags @property @@ -682,21 +716,22 @@ def data(self) -> bytes: - image manifest for signed image types - optionally trust zone data Please mind the result does not contain: certification block, HMAC, keystore and signature + :raises SPSDKError: If certificate Block is not set + :raises SPSDKError: If masterBootImageManifest is not set + :raises SPSDKError: If signature provider is not set + :raises SPSDKError: If signature is not set """ # binary image data = self.app if MasterBootImageType.is_signed(self.image_type): - assert self.cert_block, "Certificate Block is not set!" + if not self.cert_block: + raise SPSDKError("Certificate Block is not set!") data += self.cert_block.export() - assert self.manifest, "MasterBootImageManifest is not set!" + if not self.manifest: + raise SPSDKError("MasterBootImageManifest is not set!") data += self.manifest.export() # trust zone data data += self.trust_zone.export() - if MasterBootImageType.is_signed(self.image_type): - assert self.signature_provider, "Signature provider is not set!" - signature = self.signature_provider.sign(data) - assert signature, "Signature is not set!" - data += serialize_ecc_signature(signature, 32) return data def _validate_new_instance(self) -> None: @@ -705,19 +740,33 @@ def _validate_new_instance(self) -> None: @property def total_len(self) -> int: - """Return the total (expected) length of the image.""" + """Return the total (expected) length of the image. + + :raises SPSDKError: If MasterBootImageManifest is not set + :raises SPSDKError: If certificate Block is not set + :return: total length of the image + """ image_length = len(self.app) image_length += len(self.trust_zone.export()) if MasterBootImageType.is_signed(self.image_type): - assert self.manifest, "MasterBootImageManifest is not set!" + if not self.manifest: + raise SPSDKError("MasterBootImageManifest is not set!") image_length += len(self.manifest.export()) - assert self.cert_block, "Certificate Block is not set!" + if not self.cert_block: + raise SPSDKError("Certificate Block is not set!") image_length += self.cert_block.expected_size # type: ignore # signature length - image_length += 64 + assert self.signature_provider, "Signature provider is not set!" + image_length += self.signature_provider.signature_length return image_length def export(self) -> bytes: """Master boot image (binary).""" data = self._update_ivt(self.data) + if MasterBootImageType.is_signed(self.image_type): + assert self.signature_provider, "Signature provider is not set!" + signature = self.signature_provider.sign(data) + assert signature, "Signature is not set!" + data += serialize_ecc_signature(signature, 32) + # data += signature return data diff --git a/spsdk/image/misc.py b/spsdk/image/misc.py index ce856240..dbd02629 100644 --- a/spsdk/image/misc.py +++ b/spsdk/image/misc.py @@ -12,9 +12,10 @@ from io import SEEK_CUR from typing import Union +from spsdk import SPSDKError from spsdk.utils.registers import value_to_int + from .header import Header -from .. import SPSDKError class RawDataException(SPSDKError): @@ -64,12 +65,12 @@ def read_raw_data( """Read raw data.""" if index is not None: if index < 0: - raise ValueError(" Index must be non-negative, found {}".format(index)) + raise SPSDKError(" Index must be non-negative, found {}".format(index)) if index != stream.tell(): stream.seek(index) if length < 0: - raise ValueError(" Length must be non-negative, found {}".format(length)) + raise SPSDKError(" Length must be non-negative, found {}".format(length)) try: data = stream.read(length) @@ -104,11 +105,11 @@ def parse_int(number: str) -> int: :param number: input string :return: corresponding integer value - :raise ValueError: if parameter is not valid number + :raises SPSDKError: If parameter is not valid number """ match = NUMBER_FORMAT.match(number) if match is None: - raise ValueError("invalid number") + raise SPSDKError("invalid number") base = {"0b": 2, "0x": 16, None: 10}[match.group("prefix")] return int(match.group("number"), base=base) @@ -128,7 +129,7 @@ def dict_diff(main: dict, mod: dict) -> dict: try: if value_to_int(main_value) != value_to_int(value): diff[key] = value - except TypeError: + except (SPSDKError, TypeError): # Not a number! if main_value != value: diff[key] = value diff --git a/spsdk/image/secret.py b/spsdk/image/secret.py index 9dcf50c9..be124c81 100644 --- a/spsdk/image/secret.py +++ b/spsdk/image/secret.py @@ -9,17 +9,18 @@ """Commands and responses used by SDP module.""" import math from hashlib import sha256 -from struct import pack, unpack_from, unpack -from typing import List, Optional, Union, Any, Iterator +from struct import pack, unpack, unpack_from +from typing import Any, Iterator, List, Optional, Union -from cryptography.x509 import Certificate, KeyUsage, ExtensionNotFound from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.x509 import Certificate, ExtensionNotFound, KeyUsage +from spsdk import SPSDKError from spsdk.utils.easy_enum import Enum from spsdk.utils.misc import DebugInfo -from .header import SegTag, Header -from .misc import modulus_fmt, hexdump_fmt -from .. import SPSDKError + +from .header import Header, SegTag +from .misc import hexdump_fmt, modulus_fmt class EnumAlgorithm(Enum): @@ -332,10 +333,10 @@ def size(self) -> int: def _validate_data(self) -> None: """Validates the data. - :raise ValueError: if data length does not match with parameters + :raises SPSDKError: If data length does not match with parameters """ if len(self.data) != self.nonce_len + self.mac_len: - raise ValueError( + raise SPSDKError( f"length of data ({len(self.data)}) does not match with " f"nonce_bytes({self.nonce_len})+mac_bytes({self.mac_len})" ) @@ -371,11 +372,17 @@ def update_aead_encryption_params(self, nonce: bytes, mac: bytes) -> None: :param nonce: initialization vector, length depends on image size, :param mac: message authentication code used to authenticate uncrypted data, 16 bytes + :raises SPSDKError: If incorrect length of mac + :raises SPSDKError: If incorrect length of nonce + :raises SPSDKError: If incorrect number of MAC bytes" """ - assert len(mac) == MAC.AES128_BLK_LEN - assert 11 <= len(nonce) <= 13 + if len(mac) != MAC.AES128_BLK_LEN: + raise SPSDKError("Incorrect length of mac") + if len(nonce) < 11 or len(nonce) > 13: + raise SPSDKError("Incorrect length of nonce") self.nonce_len = len(nonce) - assert self.mac_len == MAC.AES128_BLK_LEN + if self.mac_len != MAC.AES128_BLK_LEN: + raise SPSDKError("Incorrect number of MAC bytes") self.data = nonce + mac def __repr__(self) -> str: @@ -538,8 +545,10 @@ def __init__(self, algorithm: int, digest: bytes) -> None: :param algorithm: int: Hash algorithm, only SHA256 now :param digest: bytes: Hash digest value + :raises SPSDKError: If incorrect algorithm """ - assert algorithm == EnumAlgorithm.SHA256 + if algorithm != EnumAlgorithm.SHA256: + raise SPSDKError("Incorrect algorithm") self._header = Header(tag=EnumSRK.KEY_HASH, param=algorithm) self.digest = digest self._header.length += len(digest) @@ -612,7 +621,8 @@ def flag(self) -> int: @flag.setter def flag(self, value: int) -> None: - assert value in (0, 0x80) + if value not in (0, 0x80): + raise SPSDKError("Incorrect flag") self._flag = value @property @@ -786,10 +796,14 @@ def get_fuse(self, index: int) -> int: :param index: of the fuse, 0-7 :return: value of the specified fuse; the value is in format, that cane be used as parameter for SDP `efuse_read_once` or `efuse_write_once` + :raises SPSDKError: If incorrect index of the fuse + :raises SPSDKError: If incorrect length of SRK items """ - assert 0 <= index < 8 + if index < 0 or index >= 8: + raise SPSDKError("Incorrect index of the fuse") int_data = self.export_fuses()[index * 4 : (1 + index) * 4] - assert len(int_data) == 4 + if len(int_data) != 4: + raise SPSDKError("Incorrect length of SRK items") return unpack(" bytes: diff --git a/spsdk/image/segments.py b/spsdk/image/segments.py index a4350bb3..bc7b6f3f 100644 --- a/spsdk/image/segments.py +++ b/spsdk/image/segments.py @@ -10,29 +10,31 @@ import logging from abc import ABC from datetime import datetime -from struct import pack, unpack_from, calcsize -from typing import Dict, List, Optional, Iterator, Sequence, Tuple, Union +from struct import calcsize, pack, unpack_from +from typing import Dict, Iterator, List, Optional, Sequence, Tuple, Union -from spsdk.utils.misc import align, align_block, extend_block, DebugInfo -from .bee import BeeRegionHeader, BEE_ENCR_BLOCK_SIZE +from spsdk import SPSDKError +from spsdk.utils.misc import DebugInfo, align, align_block, extend_block + +from .bee import BEE_ENCR_BLOCK_SIZE, BeeRegionHeader from .commands import ( + CmdAuthData, CmdBase, - CmdWriteData, CmdCheckData, - CmdUnlock, CmdNop, - CmdAuthData, - EnumWriteOps, + CmdTag, + CmdUnlock, + CmdWriteData, EnumCheckOps, EnumEngine, - CmdTag, + EnumWriteOps, parse_command, ) -from .header import Header, Header2, SegTag, CorruptedException +from .header import CorruptedException, Header, Header2, SegTag from .misc import size_fmt -from .secret import BaseClass, MAC +from .secret import MAC, BaseClass -logger = logging.getLogger("IMAGE:SEGMENTS") +logger = logging.getLogger(__name__) ######################################################################################################################## @@ -54,7 +56,8 @@ def padding_len(self) -> int: @padding_len.setter def padding_len(self, value: int) -> None: """New length (in bytes) of padding applied at the end of exported data.""" - assert value >= 0 + if value < 0: + raise SPSDKError("Length of padding must be >= 0") self.padding = value @property @@ -198,10 +201,14 @@ def __init__(self, size: int, padding_value: int = 0, enabled: bool = True): :param size: of the exported padding :param padding_value: byte value used as padding; 0 by default :param enabled: whether enabled + :raises SPSDKError: If invalid size of the exported padding + :raises SPSDKError: If invalid padding """ super().__init__() - assert 0 <= size <= 0xFFFF - assert 0 <= padding_value <= 0xFF + if size < 0 or size > 0xFFFF: + raise SPSDKError("Invalid size of the exported padding") + if padding_value < 0 or padding_value > 0xFF: + raise SPSDKError("Invalid padding") self._size = size self._padding_byte = bytes([padding_value]) self.enabled = enabled @@ -443,10 +450,10 @@ def parse(cls, buffer: bytes) -> "FlexSPIConfBlockFCB": :param buffer: data to be parsed :return: instance of the class representing the data - :raise ValueError: if data are not valid Flex SPI configuration block + :raises SPSDKError: If data are not valid Flex SPI configuration block """ if buffer[:4] != FlexSPIConfBlockFCB.TAG: - raise ValueError("TAG does not match: " + buffer[:4].hex()) + raise SPSDKError("TAG does not match: " + buffer[:4].hex()) version = buffer[7:3:-1] if ( @@ -455,11 +462,11 @@ def parse(cls, buffer: bytes) -> "FlexSPIConfBlockFCB": or (version[2] not in range(0, 9)) or (version[3] not in range(0, 9)) ): - raise ValueError("Invalid version number format") + raise SPSDKError("Invalid version number format") result = FlexSPIConfBlockFCB() if len(buffer) < result.size: - raise ValueError("Insufficient data length") + raise SPSDKError("Insufficient data length") offset = len(result.export_header()) result.version = version @@ -575,14 +582,14 @@ def update(self) -> None: def validate(self) -> None: """Validates settings of the instance. - :raises ValueError: if number of FAC regions exceeds the limit + :raises SPSDKError: If number of FAC regions exceeds the limit """ total_facs = 0 for region in self._regions: region.validate() total_facs += len(region.fac_regions) if total_facs > self.max_facs: - raise ValueError( + raise SPSDKError( f"Totally {total_facs} FAC regions, but only {self.max_facs} supported" ) @@ -628,8 +635,10 @@ def encrypt_data(self, start_addr: int, data: bytes) -> bytes: :param start_addr: start address of the data; must be aligned to block size :param data: to be encrypted :return: encrypted data, aligned to block size; blocks outside any FAC region kept untouched + :raises SPSDKError: If invalid start address """ - assert align(start_addr, BEE_ENCR_BLOCK_SIZE) == start_addr + if align(start_addr, BEE_ENCR_BLOCK_SIZE) != start_addr: + raise SPSDKError("Invalid start address") orig_len = len(data) data = align_block(data, BEE_ENCR_BLOCK_SIZE) result = bytes() @@ -660,7 +669,8 @@ def version(self) -> int: @version.setter def version(self, value: int) -> None: """The version of IVT and Image format.""" - assert 0x40 <= value < 0x4F + if value < 0x40 or value >= 0x4F: + raise SPSDKError("Invalid version of IVT and image format") self._header.param = value @property @@ -708,22 +718,22 @@ def info(self) -> str: def validate(self) -> None: """Validate settings of the segment. - :raises ValueError: if there is configuration problem + :raises SPSDKError: If there is configuration problem """ if self.ivt_address == 0 or self.bdt_address == 0 or self.bdt_address < self.ivt_address: - raise ValueError("Not valid IVT/BDT address") + raise SPSDKError("Not valid IVT/BDT address") if self.dcd_address and self.dcd_address < self.ivt_address: - raise ValueError( + raise SPSDKError( "Not valid DCD address: 0x{:X} < 0x{:X}".format(self.dcd_address, self.ivt_address) ) # TODO: resolve commented code: if self.app_address and self.app_address < self.ivt_address: - # raise ValueError("Not valid APP address: 0x{:X} < 0x{:X}".format(self.app_address, self.ivt_address)) + # raise SPSDKError("Not valid APP address: 0x{:X} < 0x{:X}".format(self.app_address, self.ivt_address)) if self.csf_address and self.csf_address < self.ivt_address: - raise ValueError( + raise SPSDKError( "Not valid CSF address: 0x{:X} < 0x{:X}".format(self.csf_address, self.ivt_address) ) if self.padding > 0: - raise ValueError("IVT padding should be zero: {}".format(self.padding)) + raise SPSDKError("IVT padding should be zero: {}".format(self.padding)) def export(self) -> bytes: """Export to binary representation (serialization). @@ -785,7 +795,8 @@ def plugin(self) -> int: @plugin.setter def plugin(self, value: int) -> None: - assert value in (0, 1, 2), "Plugin value must be 0 .. 2" + if value not in (0, 1, 2): + raise SPSDKError("Plugin value must be 0 .. 2") self._plugin = value @property @@ -952,7 +963,8 @@ def __getitem__(self, key: int) -> CmdBase: return self._commands[key] def __setitem__(self, key: int, value: CmdBase) -> None: - assert value.tag in self._COMMANDS + if value.tag not in self._COMMANDS: + raise SPSDKError("Invalid command") self._commands[key] = value def __iter__(self) -> Iterator: @@ -968,13 +980,15 @@ def info(self) -> str: def append(self, cmd: CmdBase) -> None: """Appending of Device configuration data (DCD) segment.""" - assert isinstance(cmd, CmdBase) and (cmd.tag in self._COMMANDS) + if not (isinstance(cmd, CmdBase) and (cmd.tag in self._COMMANDS)): + raise SPSDKError("Invalid command") self._commands.append(cmd) self._header.length += cmd.size def pop(self, index: int) -> CmdBase: """Popping of Device configuration data (DCD) segment.""" - assert 0 <= index < len(self._commands) + if index < 0 or index >= len(self._commands): + raise SPSDKError("Can not pop item from dcd segment") cmd = self._commands.pop(index) self._header.length -= cmd.size return cmd @@ -1085,6 +1099,7 @@ def _parse_cmd(self, dcd_obj: SegDCD, cmd: List[str]) -> None: :param dcd_obj: result of the builder :param cmd: command with arguments :raises SyntaxError: command is corrupted + :raises SPSDKError: When command is unsupported """ # ---------------------------- # Parse command @@ -1112,7 +1127,8 @@ def _parse_cmd(self, dcd_obj: SegDCD, cmd: List[str]) -> None: args = [int(value, 0) for value in cmd[2:]] dcd_obj.append(CmdUnlock(engine, *args)) else: - assert False, "unknown command" + if True: + raise SPSDKError("unknown command") elif cmd_tuple[0] == "write": if len(cmd) < 4: @@ -1275,7 +1291,8 @@ def __getitem__(self, key: int) -> CmdBase: return self.commands[key] def __setitem__(self, key: int, value: CmdBase) -> None: - assert SegCSF._is_csf_command(value) + if not SegCSF._is_csf_command(value): + raise SPSDKError("Invalid command") self._commands[key] = value def __iter__(self) -> Iterator[CmdBase]: @@ -1302,8 +1319,10 @@ def append_command(self, cmd: CmdBase) -> None: """Append CSF command to the segment. :param cmd: to be added + :raises SPSDKError: If invalid command """ - assert SegCSF._is_csf_command(cmd) + if not SegCSF._is_csf_command(cmd): + raise SPSDKError("Invalid command") self._commands.append(cmd) self._header.length += cmd.size self.update(False) @@ -1356,6 +1375,8 @@ def update_signatures(self, zulu: datetime, data: bytes, base_data_addr: int) -> :param zulu: current UTC time+date :param data: currently generated binary data; empty to create "fake" signature to update size of the segment :param base_data_addr: base address of the generated data + :raises SPSDKError: If invalid length of data + :raises SPSDKError: If invalid length of data """ if self.no_signature_updates: return @@ -1364,10 +1385,12 @@ def update_signatures(self, zulu: datetime, data: bytes, base_data_addr: int) -> if isinstance(cmd, CmdAuthData): if len(cmd) > 0: # any blocks defined? => sign image data if not cmd.update_signature(zulu, data, base_data_addr): - assert len(data) == 0 + if len(data) != 0: + raise SPSDKError("Invalid length of data") else: # sign CSF section if not cmd.update_signature(zulu, self._export_base()): - assert len(data) == 0 + if len(data) != 0: + raise SPSDKError("Invalid length of data") def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Export segment as bytes array (serialization). @@ -1395,9 +1418,13 @@ def _parse_cmd_data(self, cmd: CmdBase, data: bytes) -> None: :param cmd: command with reference to a cmd-data :param data: binary data array to be parsed :return: parsed instance, either Certificate or Signature + :raises SPSDKError: If invalid cmd + :raises SPSDKError: If invalid cmd's data """ - assert cmd.needs_cmd_data_reference - assert self._cmd_data.get(cmd.cmd_data_offset) is None + if not cmd.needs_cmd_data_reference: + raise SPSDKError("Invalid cmd") + if self._cmd_data.get(cmd.cmd_data_offset) is not None: + raise SPSDKError("Invalid cmd's data") result = cmd.parse_cmd_data(data, cmd.cmd_data_offset) self._cmd_data[cmd.cmd_data_offset] = result @@ -1491,11 +1518,11 @@ def info(self) -> str: def validate(self) -> None: """Validation of IVT3a segment.""" if self.ivt_address == 0 or self.bdt_address == 0 or self.bdt_address < self.ivt_address: - raise ValueError("Not valid IVT/BDT address") + raise SPSDKError("Not valid IVT/BDT address") if self.dcd_address and self.dcd_address < self.ivt_address: - raise ValueError("Not valid DCD address: 0x{:X}".format(self.dcd_address)) + raise SPSDKError("Not valid DCD address: 0x{:X}".format(self.dcd_address)) if self.csf_address and self.csf_address < self.ivt_address: - raise ValueError("Not valid CSF address: 0x{:X}".format(self.csf_address)) + raise SPSDKError("Not valid CSF address: 0x{:X}".format(self.csf_address)) def export(self) -> bytes: """Export segment as bytes array. @@ -1600,13 +1627,13 @@ def info(self) -> str: def validate(self) -> None: """Validation of IVT3b segment.""" if self.ivt_address == 0 or self.bdt_address == 0 or self.bdt_address < self.ivt_address: - raise ValueError("Not valid IVT/BDT address") + raise SPSDKError("Not valid IVT/BDT address") if self.dcd_address and self.dcd_address < self.ivt_address: - raise ValueError("Not valid DCD address: 0x{:X}".format(self.dcd_address)) + raise SPSDKError("Not valid DCD address: 0x{:X}".format(self.dcd_address)) if self.csf_address and self.csf_address < self.ivt_address: - raise ValueError("Not valid CSF address: 0x{:X}".format(self.csf_address)) + raise SPSDKError("Not valid CSF address: 0x{:X}".format(self.csf_address)) if self.scd_address and self.scd_address < self.ivt_address: - raise ValueError("Not valid SCD address: 0x{:X}".format(self.scd_address)) + raise SPSDKError("Not valid SCD address: 0x{:X}".format(self.scd_address)) def export(self) -> bytes: """Export segment as bytes array. diff --git a/spsdk/image/trustzone.py b/spsdk/image/trustzone.py index 38a040dc..59eb91ef 100644 --- a/spsdk/image/trustzone.py +++ b/spsdk/image/trustzone.py @@ -10,13 +10,16 @@ import logging import os import struct - from typing import Optional + +from spsdk import SPSDKError from spsdk.utils.easy_enum import Enum from spsdk.utils.misc import format_value from .misc import parse_int +logger = logging.getLogger(__name__) + class TrustZoneType(Enum): """Enum defining various types of TrustZone types.""" @@ -80,30 +83,32 @@ def __init__( self.revision: Optional[str] = revision if self.type == TrustZoneType.DISABLED and customizations: - raise ValueError("TrustZone was disabled, can't add trust_zone_data") + raise SPSDKError("TrustZone was disabled, can't add trust_zone_data") # TODO: Should empty customs qualifies for CUSTOM TZ type??? if self.customs is not None: self.type = TrustZoneType.CUSTOM if self.type == TrustZoneType.CUSTOM: - assert self.family, "Need to provide 'family' parameter" + if not self.family: + raise SPSDKError("Need to provide 'family' parameter") self.family = self.family.lower() - assert self.family in self.get_families(), "Chip family '{}' is not supported\n".format( - self.family - ) + if self.family not in self.get_families(): + raise SPSDKError(f"Chip family '{self.family}' is not supported\n") self.revision = self.sanitize_revision(self.family, self.revision) - assert ( - self.revision in self.get_revisions() - ), "Revision '{}' is not supported on family '{}'\n".format(self.revision, self.family) + if self.revision not in self.get_revisions(): + raise SPSDKError( + f"Revision '{self.revision}' is not supported on family '{self.family}'\n" + ) self.presets: dict = self._load_presets() if raw_data: self.customs = self._parse_raw_data(raw_data) - assert self.customs is not None, "Need to provide 'customization' parameter" + if self.customs is None: + raise SPSDKError("Need to provide 'customization' parameter") if not TrustZone.validate_custom_data(self.presets, self.customs): - raise ValueError( + raise SPSDKError( "Invalid register found in customization data:\n" "%s" % [item for item in self.customs if item not in self.presets] ) @@ -149,7 +154,7 @@ def _load_presets(self) -> dict: def _parse_raw_data(self, raw_data: bytes) -> dict: """Parse raw data into 'customizations' format.""" if len(self.presets) != len(raw_data) // 4: - raise ValueError( + raise SPSDKError( f"Incorrect raw_data length\nExpected: {len(self.presets)}, Got: {len(raw_data) // 4}" ) @@ -163,12 +168,14 @@ def validate_custom_data(data: dict, customizations: dict) -> bool: return all(item in data for item in customizations) def _custom_export(self) -> bytes: - assert self.presets is not None - assert self.customs is not None - logging.info(f"{len(self.presets)} registers loaded from defaults") - logging.debug(self.presets) - logging.info(f"{len(self.customs)} modifications provided") - logging.debug(self.customs) + if self.presets is None: + raise SPSDKError("Preset data not present") + if self.customs is None: + raise SPSDKError("Data not present") + logger.info(f"{len(self.presets)} registers loaded from defaults") + logger.debug(self.presets) + logger.info(f"{len(self.customs)} modifications provided") + logger.debug(self.customs) data = self.presets data.update(self.customs) registers = [parse_int(item) for item in data.values()] diff --git a/spsdk/mboot/__init__.py b/spsdk/mboot/__init__.py index d1e2ffd7..6bd58b75 100644 --- a/spsdk/mboot/__init__.py +++ b/spsdk/mboot/__init__.py @@ -8,18 +8,18 @@ """Module implementing communication with the MCU Bootloader.""" -from .mcuboot import McuBoot -from .commands import CommandTag, KeyProvUserKeyType, GenerateKeyBlobSelect -from .memories import ExtMemPropTags, ExtMemId -from .properties import PropertyTag, PeripheryTag, Version, parse_property_value -from .interfaces import scan_usb +from .commands import CommandTag, GenerateKeyBlobSelect, KeyProvUserKeyType +from .error_codes import StatusCode from .exceptions import ( - McuBootError, McuBootCommandError, McuBootConnectionError, McuBootDataAbortError, + McuBootError, ) -from .error_codes import StatusCode +from .interfaces import scan_usb +from .mcuboot import McuBoot +from .memories import ExtMemId, ExtMemPropTags, MemId +from .properties import PeripheryTag, PropertyTag, Version, parse_property_value __all__ = [ # global methods diff --git a/spsdk/mboot/commands.py b/spsdk/mboot/commands.py index 840c8a91..95e6a74d 100644 --- a/spsdk/mboot/commands.py +++ b/spsdk/mboot/commands.py @@ -8,9 +8,10 @@ """Commands and responses used by MBOOT module.""" -from struct import pack, unpack_from +from struct import pack, unpack, unpack_from from typing import Any, Dict, Type +from spsdk import SPSDKError from spsdk.utils.easy_enum import Enum from .error_codes import StatusCode @@ -50,6 +51,7 @@ class CommandTag(Enum): GENERATE_KEY_BLOB = (0x13, "GenerateKeyBlob", "Generate Key Blob") FUSE_PROGRAM = (0x14, "ProgramFuse", "Program Fuse") KEY_PROVISIONING = (0x15, "KeyProvisioning", "Key Provisioning") + TRUST_PROVISIONING = (0x16, "TrustProvisioning", "Trust Provisioning") FUSE_READ = (0x17, "ReadFuse", "Read Fuse") # reserved commands @@ -76,6 +78,11 @@ class ResponseTag(Enum): "KeyProvisioningResponse", "Key Provisioning Response", ) + TRUST_PROVISIONING_RESPONSE = ( + 0xB6, + "TrustProvisioningResponse", + "Trust Provisioning Response", + ) class KeyProvOperation(Enum): @@ -121,6 +128,45 @@ class GenerateKeyBlobSelect(Enum): CMK = (3, "CMK", "CMK from SNVS") +class TrustProvOperation(Enum): + """Type of trust provisioning operation.""" + + OEM_GEN_MASTER_SHARE = (0, "OemGenMasterShare", "Enroll Operation") + OEM_SET_MASTER_SHARE = (1, "SetUserKey", "Set User Key Operation") + OEM_GET_CUST_CERT_DICE_PUK = (2, "SetIntrinsicKey", "Set Intrinsic Key Operation") + HSM_GEN_KEY = (3, "HsmGenKey", "HSM gen key") + HSM_STORE_KEY = (4, "HsmStoreKey", "HSM store key") + HSM_ENC_BLOCK = (5, "HsmEncBlock", "HSM Enc block") + HSM_ENC_SIGN = (6, "HsnEncSign", "HSM enc sign") + + +class TrustProvOemKeyType(Enum): + """Type of oem key type definition.""" + + MFWISK = (0xC3A5, "MFWISK", "ECDSA Manufactoring Firmware Signing Key") + MFWENCK = (0xA5C3, "MFWENCK", "CKDF Master Key for Manufactoring Firmware Encryption Key") + GENSIGNK = (0x5A3C, "GENSIGNK", "Generic ECDSA Signing Key") + GETCUSTMKSK = (0x3C5A, "GETCUSTMKSK", "CKDF Master Key for Production Firmware Encryption Key") + + +class TrustProvKeyType(Enum): + """Type of key type definition.""" + + CKDFK = (1, "CKDFK", "CKDF Master Key") + HKDFK = (2, "HKDFK", "HKDF Master Key") + HMACK = (3, "HMACK", "HMAC Key") + CMACK = (4, "CMACK", "CMAC Key") + AESK = (5, "AESK", "AES Key") + KUOK = (6, "KUOK", "Key Unwrap Only Key") + + +class TrustProvWrappingKeyType(Enum): + """Type of wrapping key type definition.""" + + INT_SK = (0x10, "INT_SK", "The wrapping key for wrapping of MFG_CUST_MK_SK0_BLOB") + EXT_SK = (0x11, "EXT_SK", "The wrapping key for wrapping of MFG_CUST_MK_SK0_BLOB") + + ######################################################################################################################## # McuBoot Command and Response packet classes ######################################################################################################################## @@ -190,7 +236,6 @@ def __init__(self, tag: int, flags: int, *args: int, data: bytes = None) -> None :param args: Arguments used by the command :param data: Additional data, defaults to None """ - assert len(args) < 8 self.header = CmdHeader(tag, flags, 0, len(args)) self.params = list(args) if data is not None: @@ -383,6 +428,26 @@ def info(self) -> str: return f"Tag={tag}, Status={status}, Length={self.length}" +class TrustProvisioningResponse(CmdResponse): + """McuBoot Trust Provisioning response format class.""" + + def __init__(self, header: CmdHeader, raw_data: bytes) -> None: + """Initialize the Trust-Provisioning response object. + + :param header: Header for the response + :param raw_data: Response data + """ + super().__init__(header, raw_data) + self.status, *values = unpack(f"<{self.header.params_count}I", raw_data) + self.values = list(values) + + def info(self) -> str: + """Get object info.""" + tag = ResponseTag.name(self.header.tag) + status = StatusCode.get(self.status, f"Unknown[0x{self.status:08X}]") + return f"Tag={tag}, Status={status}" + + class NoResponse(CmdResponse): """Special internal case when no response is provided by the target.""" @@ -411,6 +476,7 @@ def parse_cmd_response(data: bytes, offset: int = 0) -> CmdResponse: ResponseTag.FLASH_READ_ONCE: FlashReadOnceResponse, ResponseTag.KEY_BLOB_RESPONSE: ReadMemoryResponse, # not sure what format is returned, this work on RT1050 ResponseTag.KEY_PROVISIONING_RESPONSE: KeyProvisioningResponse, + ResponseTag.TRUST_PROVISIONING_RESPONSE: TrustProvisioningResponse, } header = CmdHeader.from_bytes(data, offset) if header.tag in known_response: diff --git a/spsdk/mboot/exceptions.py b/spsdk/mboot/exceptions.py index cb1d757a..dad56bcc 100644 --- a/spsdk/mboot/exceptions.py +++ b/spsdk/mboot/exceptions.py @@ -8,9 +8,9 @@ """Exceptions used in the MBoot module.""" -from .error_codes import StatusCode -from ..exceptions import SPSDKError +from spsdk import SPSDKError +from .error_codes import StatusCode ######################################################################################################################## # McuBoot Exceptions diff --git a/spsdk/mboot/interfaces/__init__.py b/spsdk/mboot/interfaces/__init__.py index ba519830..bf43884f 100644 --- a/spsdk/mboot/interfaces/__init__.py +++ b/spsdk/mboot/interfaces/__init__.py @@ -1,12 +1,13 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright (c) 2019-2020 NXP +# Copyright (c) 2019-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause """Module implementing the Mboot communication protocol.""" from .base import Interface -from .usb import scan_usb, RawHid -from .uart import scan_uart, Uart +from .uart import Uart, scan_uart +from .usb import RawHid, scan_usb +from .usbsio import UsbSioI2C, UsbSioSPI, scan_usbsio diff --git a/spsdk/mboot/interfaces/uart.py b/spsdk/mboot/interfaces/uart.py index 01e32500..d6a672a1 100644 --- a/spsdk/mboot/interfaces/uart.py +++ b/spsdk/mboot/interfaces/uart.py @@ -17,13 +17,13 @@ from serial import Serial from serial.tools.list_ports import comports -from spsdk.mboot.exceptions import McuBootConnectionError, McuBootDataAbortError from spsdk.mboot.commands import CmdPacket, CmdResponse, parse_cmd_response +from spsdk.mboot.exceptions import McuBootConnectionError, McuBootDataAbortError from spsdk.utils.easy_enum import Enum from .base import Interface -logger = logging.getLogger("MBOOT:UART") +logger = logging.getLogger(__name__) def scan_uart(port: str = None, baudrate: int = None, timeout: int = None) -> List[Interface]: diff --git a/spsdk/mboot/interfaces/usb.py b/spsdk/mboot/interfaces/usb.py index 951f5b24..84ae7130 100644 --- a/spsdk/mboot/interfaces/usb.py +++ b/spsdk/mboot/interfaces/usb.py @@ -8,12 +8,10 @@ """Module for serial communication with a target device using MBoot protocol.""" import logging -import platform -import time from struct import pack, unpack_from from typing import Sequence, Union -import hid +import libusbsio from spsdk.utils.usbfilter import NXPUSBDeviceFilter, USBDeviceFilter @@ -21,7 +19,7 @@ from ..exceptions import McuBootConnectionError, McuBootDataAbortError, McuBootError from .base import Interface -logger = logging.getLogger("MBOOT:USB") +logger = logging.getLogger(__name__) # import os # os.environ['PYUSB_DEBUG'] = 'debug' @@ -151,11 +149,11 @@ def open(self) -> None: try: if not self.device: raise McuBootConnectionError("No device available") - self.device.open_path(self.path) + self.device.Open(self.path) self._opened = True except Exception as error: raise McuBootConnectionError( - "Unable to open device VID={self.vid} PID={self.pid} SN='{self.serial_number}'" + f"Unable to open device '{self.path}' VID={self.vid} PID={self.pid} SN='{self.serial_number}'" ) from error def close(self) -> None: @@ -168,11 +166,11 @@ def close(self) -> None: try: if not self.device: raise McuBootConnectionError("No device available") - self.device.close() + self.device.Close() self._opened = False except Exception as error: raise McuBootConnectionError( - "Unable to close device VID={self.vid} PID={self.pid} SN='{self.serial_number}'" + f"Unable to close device '{self.path}' VID={self.vid} PID={self.pid} SN='{self.serial_number}'" ) from error def write(self, packet: Union[CmdPacket, bytes]) -> None: @@ -202,7 +200,7 @@ def write(self, packet: Union[CmdPacket, bytes]) -> None: # try to read a begging of the ABORT_FRAME if self.allow_abort and report_id == REPORT_ID["DATA_OUT"]: try: - abort_data = self.device.read(1024, 10) + (abort_data, result) = self.device.Read(1024, timeout_ms=10) logger.debug(f"Read {len(abort_data)} bytes of abort data") except Exception as e: raise McuBootConnectionError(str(e)) from e @@ -212,17 +210,7 @@ def write(self, packet: Union[CmdPacket, bytes]) -> None: try: raw_data = self._encode_report(report_id, data) - bytes_written = self.device.write(raw_data) - # TODO: failure to write data (without an exception) indicates the MCU is busy - # After some amount of NAK the HID gives up - # this is just na WORKAROUND to give MCU some breathing room - if bytes_written < 0: - time.sleep(2) - # NOTE: on Windows and Mac, the request for sending data is still active, - # even when the read methid returns -1 (potential issue in HID library?) - # On Linux we simply fire the write request again - if platform.system() == "Linux": - self.device.write(raw_data) + bytes_written = self.device.Write(raw_data, timeout_ms=self.timeout) except Exception as e: raise McuBootConnectionError(str(e)) from e @@ -241,16 +229,16 @@ def read(self) -> Union[CmdResponse, bytes]: if not self.device: raise McuBootConnectionError("Device not available") try: - raw_data = self.device.read(1024, self.timeout) + (raw_data, result) = self.device.Read(1024, timeout_ms=self.timeout) except Exception as e: raise McuBootConnectionError(str(e)) from e if not raw_data: - logger.error(self.device.error()) + logger.error(f"Cannot read from HID device, error={result}") raise TimeoutError() # NOTE: uncomment the following when using KBoot/Flashloader v2.1 and older # import platform # if platform.system() == "Linux": - # raw_data += self.device.read(1024, self.timeout) + # raw_data += self.device.Read(1024, timeout_ms=self.timeout) return self._decode_report(bytes(raw_data)) @staticmethod @@ -261,13 +249,16 @@ def enumerate(usb_device_filter: USBDeviceFilter) -> Sequence[Interface]: :return: List of interfaces found """ devices = [] - all_hid_devices = hid.enumerate() + + # use HID_API of LIBUSBSIO library to enumerate all USB HID devices + sio = libusbsio.usbsio() + all_hid_devices = sio.HIDAPI_Enumerate() # iterate on all devices found for dev in all_hid_devices: if usb_device_filter.compare(dev) is True: new_device = RawHid() - new_device.device = hid.device() + new_device.device = sio.HIDAPI_DeviceCreate() new_device.vid = dev["vendor_id"] new_device.pid = dev["product_id"] new_device.vendor_name = dev["manufacturer_string"] diff --git a/spsdk/mboot/interfaces/usbsio.py b/spsdk/mboot/interfaces/usbsio.py new file mode 100644 index 00000000..f5f4c8d8 --- /dev/null +++ b/spsdk/mboot/interfaces/usbsio.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright (c) 2019-2021 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Module for USB-SIO communication with a target device using MBoot protocol.""" + +import logging +from typing import Any, List, Optional, Union + +import libusbsio +from libusbsio.libusbsio import LIBUSBSIO + +from spsdk import SPSDKError +from spsdk.mboot.commands import CmdPacket, CmdResponse +from spsdk.mboot.exceptions import McuBootConnectionError, McuBootDataAbortError + +from .base import Interface +from .uart import Uart + +logger = logging.getLogger(__name__) + + +def scan_usbsio(config: str = None) -> List[Interface]: + """Scan connected USB-SIO bridge devices. + + :param config: configuration string identifying spi or i2c SIO interface + :return: list of matching RawHid devices + :raises SPSDKError: When libusbsio library error or if no bridge device found + """ + cfg = config.split(",") if config else [] + if len(cfg) <= 0 or not cfg[0] in ["spi", "i2c"]: + raise SPSDKError("lpcusbsio connection needs 'spi' or 'i2c' configuration") + + sio = None + + try: + # get the global singleton instance of LIBUSBSIO library + sio = libusbsio.usbsio(loglevel=logger.level) + + # it may already be open (?) + if not sio.IsOpen(): + if sio.GetNumPorts() < 1: + raise Exception("No libusbsio bridge device found") + # TODO: add support for multiple bridge devices, this would require to extend + # command line configuration string by VID/PID and SIO port index. This would + # break the legacy blhost command line compatibility. + # For now, just open the first bridge device available + if not sio.Open(0): + raise Exception("Cannot open libusbsio device") + logger.debug(f"USBSIO device open: {sio.GetVersion()}") + except libusbsio.LIBUSBSIO_Exception as e: + raise SPSDKError(f"Error in libusbsio interface: {e}") + except Exception as e: + raise SPSDKError(str(e)) + + device: Optional[UsbSio] = None + + if cfg[0] == "i2c": + if sio.GetNumI2CPorts() > 0: + device = UsbSioI2C(config=config) + elif cfg[0] == "spi": + if sio.GetNumSPIPorts() > 0: + device = UsbSioSPI(config=config) + + if not device: + raise SPSDKError(f"No {cfg[0]} interface available in libusbsio device") + return [device] + + +class UsbSio(Uart): + """USBSIO general interface, base class for SPI or I2C communication over LIBUSBSIO. + + This class inherits from Uart communication as the SPI/I2C protocol is the same. + The Uart's read and write methods are leveraged. The low-level _read and _write + methods are overridden. + """ + + @property + def is_opened(self) -> bool: + """Indicates whether interface is open.""" + return True if self.port else False + + def __init__(self, config: str = None) -> None: + """Initialize the Interface object.""" + # device is the LIBUSBSIO.PORT instance (LIBUSBSIO.SPI or LIBUSBSIO.I2C class) + self.port: Optional[LIBUSBSIO.PORT] = None + + super().__init__() + + # work with the global LIBUSBSIO instance + self.sio = libusbsio.usbsio() + if not self.sio.IsOpen(): + raise SPSDKError("The libusbsio device is not open") + + # store USBSIO configuration and version + self.config = config + self.version = self.sio.GetVersion() + + def info(self) -> str: + """Return string containing information about the interface.""" + return f"libusbsio interface '{self.config}'" + + +class UsbSioSPI(UsbSio): + """USBSIO SPI interface.""" + + def __init__( + self, + config: str = None, + port: int = 0, + ssel_port: int = 0, + ssel_pin: int = 15, + speed_khz: int = 1000, + cpol: int = 1, + cpha: int = 1, + ) -> None: + """Initialize the UsbSioSPI Interface object. + + :param config: configuration string passed from command line + :param port: default SPI port to be used, typically 0 as only one port is supported by LPCLink2/MCULink + :param ssel_port: bridge GPIO port used to drive SPI SSEL signal + :param ssel_pin: bridge GPIO pin used to drive SPI SSEL signal + :param speed_khz: SPI clock speed in kHz + :param cpol: SPI clock polarity mode + :param cpha: SPI clock phase mode + :raises SPSDKError: When port configuration cannot be parsed + """ + super().__init__(config=config) + + # default configuration taken from parameters (and their default values) + self.spi_port = port + self.spi_sselport = ssel_port + self.spi_sselpin = ssel_pin + self.spi_speed_khz = speed_khz + self.spi_cpol = cpol + self.spi_cpha = cpha + + # values can be also overridden by a configuration string + if config: + # config format: spi[,,,,,] + cfg = config.split(",") + try: + self.spi_sselport = int(cfg[1], 0) + self.spi_sselpin = int(cfg[2], 0) + self.spi_speed_khz = int(cfg[3], 0) + self.spi_cpol = int(cfg[4], 0) + self.spi_cpha = int(cfg[5], 0) + except IndexError: + pass + except Exception as e: + raise SPSDKError( + "Cannot parse lpcusbsio SPI parameters.\n" + "Expected: spi[,,,,,]\n" + f"Given: {config}" + ) + + def open(self) -> None: + """Open the interface.""" + self.port: LIBUSBSIO.SPI = self.sio.SPI_Open( + portNum=self.spi_port, + busSpeed=self.spi_speed_khz * 1000, + cpol=self.spi_cpol, + cpha=self.spi_cpha, + ) + if not self.port: + raise SPSDKError("Cannot open lpcusbsio SPI interface.\n") + + def close(self) -> None: + """Close the interface.""" + if self.port: + self.port.Close() + self.port = None + + def _read(self, length: int) -> bytes: + """Read 'length' amount for bytes from device. + + :param length: Number of bytes to read + :return: Data read from the device + :raises McuBootConnectionError: When reading data from device fails + :raises TimeoutError: When no data received + """ + try: + (data, result) = self.port.Transfer( + devSelectPort=self.spi_sselport, + devSelectPin=self.spi_sselpin, + txData=None, + size=length, + ) + except Exception as e: + raise McuBootConnectionError(str(e)) from e + if result < 0 or not data: + raise TimeoutError() + logger.debug(f"<{' '.join(f'{b:02x}' for b in data)}>") + return data + + def _write(self, data: bytes) -> None: + """Send data to device. + + :param data: Data to send + :raises McuBootConnectionError: When sending the data fails + :raises TimeoutError: When data could not be written + """ + logger.debug(f"[{' '.join(f'{b:02x}' for b in data)}]") + try: + (dummy, result) = self.port.Transfer( + devSelectPort=self.spi_sselport, devSelectPin=self.spi_sselpin, txData=data + ) + except Exception as e: + raise McuBootConnectionError(str(e)) from e + if result < 0: + raise TimeoutError() + + +class UsbSioI2C(UsbSio): + """USBSIO I2C interface.""" + + def __init__( + self, config: str = None, port: int = 0, address: int = 0x10, speed_khz: int = 100 + ) -> None: + """Initialize the UsbSioI2C Interface object. + + :param config: configuration string passed from command line + :param port: default I2C port to be used, typically 0 as only one port is supported by LPCLink2/MCULink + :param address: I2C target device address + :param speed_khz: I2C clock speed in kHz + :raises SPSDKError: When port configuration cannot be parsed + """ + super().__init__(config=config) + + # default configuration taken from parameters (and their default values) + self.i2c_port = port + self.i2c_address = address + self.i2c_speed_khz = speed_khz + + # values can be also overridden by a configuration string + if config: + # config format: i2c[,

,] + cfg = config.split(",") + try: + self.i2c_address = int(cfg[1], 0) + self.i2c_speed_khz = int(cfg[2], 0) + except IndexError: + pass + except Exception as e: + raise SPSDKError( + "Cannot parse lpcusbsio I2C parameters.\n" + "Expected: i2c[,
,]\n" + f"Given: {config}" + ) + + def open(self) -> None: + """Open the interface.""" + self.port: LIBUSBSIO.I2C = self.sio.I2C_Open( + clockRate=self.i2c_speed_khz * 1000, portNum=self.i2c_port + ) + if not self.port: + raise SPSDKError("Cannot open lpcusbsio I2C interface.\n") + + def close(self) -> None: + """Close the interface.""" + if self.port: + self.port.Close() + self.port = None + + def _read(self, length: int) -> bytes: + """Read 'length' amount for bytes from device. + + :param length: Number of bytes to read + :return: Data read from the device + :raises TimeoutError: Time-out + :raises McuBootConnectionError: When reading data from device fails + :raises TimeoutError: When no data received + """ + try: + (data, result) = self.port.DeviceRead(devAddr=self.i2c_address, rxSize=length) + except Exception as e: + raise McuBootConnectionError(str(e)) from e + if result < 0 or not data: + raise TimeoutError() + logger.debug(f"<{' '.join(f'{b:02x}' for b in data)}>") + return data + + def _write(self, data: bytes) -> None: + """Send data to device. + + :param data: Data to send + :raises McuBootConnectionError: When sending the data fails + :raises TimeoutError: When data NAKed or could not be written + """ + logger.debug(f"[{' '.join(f'{b:02x}' for b in data)}]") + try: + result = self.port.DeviceWrite(devAddr=self.i2c_address, txData=data) + except Exception as e: + raise McuBootConnectionError(str(e)) from e + if result < 0: + raise TimeoutError() diff --git a/spsdk/mboot/mcuboot.py b/spsdk/mboot/mcuboot.py index d36e4e06..b32a155c 100644 --- a/spsdk/mboot/mcuboot.py +++ b/spsdk/mboot/mcuboot.py @@ -22,6 +22,8 @@ KeyProvOperation, KeyProvUserKeyType, NoResponse, + TrustProvisioningResponse, + TrustProvOperation, ) from .error_codes import StatusCode from .exceptions import ( @@ -35,7 +37,7 @@ from .memories import ExtMemId, ExtMemRegion, FlashRegion, MemoryRegion, RamRegion from .properties import PropertyTag, Version, parse_property_value -logger = logging.getLogger("MBOOT") +logger = logging.getLogger(__name__) ######################################################################################################################## @@ -236,7 +238,9 @@ def open(self) -> None: def close(self) -> None: """Disconnect from the device.""" - self._device.close() + if self._device.is_opened: + logger.info(f"Closing: {self._device.info()}") + self._device.close() def get_property_list(self) -> list: """Get a list of available properties. @@ -335,6 +339,8 @@ def _get_ext_memories(self) -> List[ExtMemRegion]: """Get information about the external memories. :return: list of ExtMemRegion objects supported by the device + :raises SPSDKError: If no response to get property command + :raises SPSDKError: Other Error """ ext_mem_list: List[ExtMemRegion] = [] ext_mem_ids: Sequence[int] = ExtMemId.tags() @@ -347,7 +353,8 @@ def _get_ext_memories(self) -> List[ExtMemRegion]: self._status_code = StatusCode.SUCCESS return ext_mem_list - assert values + if not values: + raise SPSDKError("No response to get property command") if Version(values[0]) <= Version("2.0.0"): # old versions mboot support only Quad SPI memory @@ -373,7 +380,8 @@ def _get_ext_memories(self) -> List[ExtMemRegion]: if self._status_code == StatusCode.MEMORY_NOT_CONFIGURED: ext_mem_list.append(ExtMemRegion(mem_id=mem_id)) - assert self._status_code != StatusCode.SUCCESS # Other Error + if self._status_code == StatusCode.SUCCESS: + raise SPSDKError("Other Error") else: ext_mem_list.append(ExtMemRegion(mem_id=mem_id, raw_values=values)) @@ -490,10 +498,10 @@ def flash_security_disable(self, backdoor_key: bytes) -> bool: :param backdoor_key: The key value as array of 8 bytes :return: False in case of any problem; True otherwise - :raises ValueError: If the backdoor_key is not 8 bytes long + :raises McuBootError: If the backdoor_key is not 8 bytes long """ if len(backdoor_key) != 8: - raise ValueError("Backdoor key must by 8 bytes long") + raise McuBootError("Backdoor key must by 8 bytes long") logger.info(f"CMD: FlashSecurityDisable(backdoor_key={backdoor_key!r})") key_high = backdoor_key[0:4][::-1] key_low = backdoor_key[4:8][::-1] @@ -507,7 +515,9 @@ def get_property(self, prop_tag: PropertyTag, index: int = 0) -> Optional[List[i :param index: External memory ID or internal memory region index (depends on property type) :return: list integers representing the property; None in case no response from device """ - logger.info(f"CMD: GetProperty({PropertyTag.name(prop_tag, 'UNKNOWN')!r}, index={index!r})") + logger.info( + f"CMD: GetProperty({PropertyTag.name(prop_tag, 'UNKNOWN')!r}, index={index!r})" # pylint: disable=too-many-function-args + ) cmd_packet = CmdPacket(CommandTag.GET_PROPERTY, 0, prop_tag, index) cmd_response = self._process_cmd(cmd_packet) return cmd_response.values if cmd_response.status == StatusCode.SUCCESS else None @@ -572,21 +582,21 @@ def reset(self, timeout: int = 2000, reopen: bool = True) -> bool: :param timeout: The maximal waiting time in [ms] for reopen connection :param reopen: True for reopen connection after HW reset else False :return: False in case of any problem; True otherwise - :raises ValueError: if reopen is not supported + :raises McuBootError: if reopen is not supported :raises McuBootConnectionError: Failure to reopen the device """ logger.info("CMD: Reset MCU") cmd_packet = CmdPacket(CommandTag.RESET, 0) ret_val = False if self._process_cmd(cmd_packet).status == StatusCode.SUCCESS: - self._device.close() + self.close() ret_val = True if reopen: if not self.reopen: - raise ValueError("reopen is not supported") + raise McuBootError("reopen is not supported") time.sleep(timeout / 1000) try: - self._device.open() + self.open() except SPSDKError: ret_val = False if self._cmd_exception: @@ -630,8 +640,10 @@ def flash_read_once(self, index: int, count: int = 4) -> Optional[bytes]: :param index: Start index :param count: Count of bytes :return: Data read; None in case of an failure + :raises SPSDKError: When invalid count of bytes. Must be 4 or 8 """ - assert count in (4, 8) + if count not in (4, 8): + raise SPSDKError("Invalid count of bytes. Must be 4 or 8") logger.info(f"CMD: FlashReadOnce(index={index}, bytes={count})") cmd_packet = CmdPacket(CommandTag.FLASH_READ_ONCE, 0, index, count) cmd_response = self._process_cmd(cmd_packet) @@ -643,8 +655,10 @@ def flash_program_once(self, index: int, data: bytes) -> bool: :param index: Start index :param data: Input data aligned to 4 or 8 bytes :return: False in case of any problem; True otherwise + :raises SPSDKError: When invalid length of data. Must be aligned to 4 or 8 bytes """ - assert len(data) in (4, 8) + if len(data) not in (4, 8): + raise SPSDKError("Invalid length of data. Must be aligned to 4 or 8 bytes") logger.info(f"CMD: FlashProgramOnce(index={index!r}, data={data!r})") cmd_packet = CmdPacket(CommandTag.FLASH_PROGRAM_ONCE, 0, index, len(data), data=data) return self._process_cmd(cmd_packet).status == StatusCode.SUCCESS @@ -873,6 +887,269 @@ def fuse_read(self, address: int, length: int, mem_id: int = 0) -> Optional[byte return self._read_data(CommandTag.FUSE_READ, cmd_response.length) return None + def tp_hsm_gen_key( + self, + key_type: int, + reserved: int, + key_blob_output_addr: int, + key_blob_output_size: int, + ecdsa_puk_output_addr: int, + ecdsa_puk_output_size: int, + ) -> Optional[List[Any]]: + """Trust provisioning: OEM generate common keys. + + :param key_type: Key to generate (MFW_ISK, MFW_ENCK, GEN_SIGNK, GET_CUST_MK_SK) + :param reserved: Reserved, must be zero + :param key_blob_output_addr: The output buffer address where ROM writes the key blob to + :param key_blob_output_size: The output buffer size in byte + :param ecdsa_puk_output_addr: The output buffer address where ROM writes the public key to + :param ecdsa_puk_output_size: The output buffer size in byte + :return: Return byte count of the key blob + byte count of the public key from the device; + None in case of an failure + """ + logger.info("CMD: [TrustProvisioning] OEM generate common keys") + cmd_packet = CmdPacket( + CommandTag.TRUST_PROVISIONING, + 0, + TrustProvOperation.HSM_GEN_KEY, + key_type, + reserved, + key_blob_output_addr, + key_blob_output_size, + ecdsa_puk_output_addr, + ecdsa_puk_output_size, + ) + cmd_response = self._process_cmd(cmd_packet) + if isinstance(cmd_response, TrustProvisioningResponse): + return cmd_response.values + return None + + def tp_oem_gen_master_share( + self, + oem_share_input_addr: int, + oem_share_input_size: int, + oem_enc_share_output_addr: int, + oem_enc_share_output_size: int, + oem_enc_master_share_output_addr: int, + oem_enc_master_share_output_size: int, + oem_cust_cert_puk_output_addr: int, + oem_cust_cert_puk_output_size: int, + ) -> Optional[List[int]]: + """Takes the entropy seed provided by the OEM as input. + + :param oem_share_input_addr: The input buffer address + where the OEM Share(entropy seed) locates at + :param oem_share_input_size: The byte count of the OEM Share + :param oem_enc_share_output_addr: The output buffer address + where ROM writes the Encrypted OEM Share to + :param oem_enc_share_output_size: The output buffer size in byte + :param oem_enc_master_share_output_addr: The output buffer address + where ROM writes the Encrypted OEM Master Share to + :param oem_enc_master_share_output_size: The output buffer size in byte. + :param oem_cust_cert_puk_output_addr: The output buffer address where + ROM writes the OEM Customer Certificate Public Key to + :param oem_cust_cert_puk_output_size: The output buffer size in byte + :return: Sizes of two encrypted blobs(the Encrypted OEM Share and the Encrypted OEM Master Share) + and a public key(the OEM Customer Certificate Public Key). + """ + logger.info("CMD: [TrustProvisioning] OEM generate master share") + cmd_packet = CmdPacket( + CommandTag.TRUST_PROVISIONING, + 0, + TrustProvOperation.OEM_GEN_MASTER_SHARE, + oem_share_input_addr, + oem_share_input_size, + oem_enc_share_output_addr, + oem_enc_share_output_size, + oem_enc_master_share_output_addr, + oem_enc_master_share_output_size, + oem_cust_cert_puk_output_addr, + oem_cust_cert_puk_output_size, + ) + cmd_response = self._process_cmd(cmd_packet) + if isinstance(cmd_response, TrustProvisioningResponse): + return cmd_response.values + return None + + def tp_oem_set_master_share( + self, + oem_share_input_addr: int, + oem_share_input_size: int, + oem_enc_master_share_input_addr: int, + oem_enc_master_share_input_size: int, + ) -> bool: + """Takes the entropy seed and the Encrypted OEM Master Share. + + :param oem_share_input_addr: The input buffer address + where the OEM Share(entropy seed) locates at + :param oem_share_input_size: The byte count of the OEM Share + :param oem_enc_master_share_input_addr: The input buffer address + where the Encrypted OEM Master Share locates at + :param oem_enc_master_share_input_size: The byte count of the Encrypted OEM Master Share + :return: False in case of any problem; True otherwise + """ + logger.info( + "CMD: [TrustProvisioning] Takes the entropy seed and the Encrypted OEM Master Share." + ) + cmd_packet = CmdPacket( + CommandTag.TRUST_PROVISIONING, + 0, + TrustProvOperation.OEM_SET_MASTER_SHARE, + oem_share_input_addr, + oem_share_input_size, + oem_enc_master_share_input_addr, + oem_enc_master_share_input_size, + ) + return self._process_cmd(cmd_packet).status == StatusCode.SUCCESS + + def tp_oem_get_cust_cert_dice_puk( + self, + oem_rkt_input_addr: int, + oem_rkth_input_size: int, + oem_cust_cert_dice_puk_output_addr: int, + oem_cust_cert_dice_puk_output_size: int, + ) -> Optional[int]: + """Creates the initial trust provisioning keys. + + :param oem_rkt_input_addr: The input buffer address where the OEM RKTH locates at + :param oem_rkth_input_size: The byte count of the OEM RKTH + :param oem_cust_cert_dice_puk_output_addr: The output buffer address where ROM writes the OEM Customer + Certificate Public Key for DICE to + :param oem_cust_cert_dice_puk_output_size: The output buffer size in byte + :return: The byte count of the OEM Customer Certificate Public Key for DICE + """ + logger.info("CMD: [TrustProvisioning] Creates the initial trust provisioning keys") + cmd_packet = CmdPacket( + CommandTag.TRUST_PROVISIONING, + 0, + TrustProvOperation.OEM_GET_CUST_CERT_DICE_PUK, + oem_rkt_input_addr, + oem_rkth_input_size, + oem_cust_cert_dice_puk_output_addr, + oem_cust_cert_dice_puk_output_size, + ) + cmd_response = self._process_cmd(cmd_packet) + if isinstance(cmd_response, TrustProvisioningResponse): + return cmd_response.values[0] + return None + + def tp_hsm_store_key( + self, + key_type: int, + key_property: int, + key_input_addr: int, + key_input_size: int, + key_blob_output_addr: int, + key_blob_output_size: int, + ) -> Optional[List[Any]]: + """Trust provisioning: OEM generate common keys. + + :param key_type: Key to generate (CKDFK, HKDFK, HMACK, CMACK, AESK, KUOK) + :param key_property: Bit 0: Key Size, 0 for 128bit, 1 for 256bit. + Bits 30-31: set key protection CSS mode. + :param key_input_addr: The input buffer address where the key locates at + :param key_input_size: The byte count of the key + :param key_blob_output_addr: The output buffer address where ROM writes the key blob to + :param key_blob_output_size: The output buffer size in byte + :return: Return header of the key blob + byte count of the key blob + (header is not included) from the device; None in case of an failure + """ + logger.info("CMD: [TrustProvisioning] OEM generate common keys") + cmd_packet = CmdPacket( + CommandTag.TRUST_PROVISIONING, + 0, + TrustProvOperation.HSM_STORE_KEY, + key_type, + key_property, + key_input_addr, + key_input_size, + key_blob_output_addr, + key_blob_output_size, + ) + cmd_response = self._process_cmd(cmd_packet) + if isinstance(cmd_response, TrustProvisioningResponse): + return cmd_response.values + return None + + def tp_hsm_enc_blk( + self, + mfg_cust_mk_sk_0_blob_input_addr: int, + mfg_cust_mk_sk_0_blob_input_size: int, + kek_id: int, + sb3_header_input_addr: int, + sb3_header_input_size: int, + block_num: int, + block_data_addr: int, + block_data_size: int, + ) -> bool: + """Trust provisioning: Encrypt the given SB3 data block. + + :param mfg_cust_mk_sk_0_blob_input_addr: The input buffer address + where the CKDF Master Key Blob locates at + :param mfg_cust_mk_sk_0_blob_input_size: The byte count of the CKDF Master Key Blob + :param kek_id: The CKDF Master Key Encryption Key ID + (0x10: NXP_CUST_KEK_INT_SK, 0x11: NXP_CUST_KEK_EXT_SK) + :param sb3_header_input_addr: The input buffer address, + where the SB3 Header(block0) locates at + :param sb3_header_input_size: The byte count of the SB3 Header + :param block_num: The index of the block. Due to SB3 Header(block 0) is always unencrypted, + the index starts from block1 + :param block_data_addr: The buffer address where the SB3 data block locates at + :param block_data_size: The byte count of the SB3 data block + :return: False in case of any problem; True otherwise + """ + logger.info("CMD: [TrustProvisioning] Encrypt the given SB3 data block") + cmd_packet = CmdPacket( + CommandTag.TRUST_PROVISIONING, + 0, + TrustProvOperation.HSM_ENC_BLOCK, + mfg_cust_mk_sk_0_blob_input_addr, + mfg_cust_mk_sk_0_blob_input_size, + kek_id, + sb3_header_input_addr, + sb3_header_input_size, + block_num, + block_data_addr, + block_data_size, + ) + return self._process_cmd(cmd_packet).status == StatusCode.SUCCESS + + def tp_hsm_enc_sign( + self, + key_blob_input_addr: int, + key_blob_input_size: int, + block_data_input_addr: int, + block_data_input_size: int, + signature_output_addr: int, + signature_output_size: int, + ) -> Optional[int]: + """Signs the given data. + + :param key_blob_input_addr: The input buffer address where signing key blob locates at + :param key_blob_input_size: The byte count of the signing key blob + :param block_data_input_addr: The input buffer address where the data locates at + :param block_data_input_size: The byte count of the data + :param signature_output_addr: The output buffer address where ROM writes the signature to + :param signature_output_size: The output buffer size in byte + :return: Return signature size; None in case of an failure + """ + logger.info("CMD: [TrustProvisioning] HSM ENC SIGN") + cmd_packet = CmdPacket( + CommandTag.TRUST_PROVISIONING, + 0, + TrustProvOperation.HSM_ENC_SIGN, + key_blob_input_addr, + key_blob_input_size, + block_data_input_addr, + block_data_input_size, + signature_output_addr, + signature_output_size, + ) + cmd_response = self._process_cmd(cmd_packet) + if isinstance(cmd_response, TrustProvisioningResponse): + return cmd_response.values[0] + return None + def _clamp_down_memory_id(memory_id: int) -> int: if memory_id > 255 or memory_id == 0: diff --git a/spsdk/mboot/memories.py b/spsdk/mboot/memories.py index 2dbaf44b..4012538a 100644 --- a/spsdk/mboot/memories.py +++ b/spsdk/mboot/memories.py @@ -35,6 +35,12 @@ class ExtMemId(Enum): MMC_CARD = (289, "MMC", "MMC/eMMC Memory Card") +class MemId(ExtMemId): + """McuBoot Internal/External Memory Property Tags.""" + + INTERNAL_MEMORY = (0, "RAM/FLASH", "Internal RAM/FLASH (Used for the PRINCE configuration)") + + ######################################################################################################################## # McuBoot External Memory Property Tags ######################################################################################################################## diff --git a/spsdk/mboot/properties.py b/spsdk/mboot/properties.py index 6f15b0e2..14b90b81 100644 --- a/spsdk/mboot/properties.py +++ b/spsdk/mboot/properties.py @@ -11,11 +11,12 @@ from typing import Any, List, Optional, Tuple, Type, Union +from spsdk.mboot.exceptions import McuBootError from spsdk.utils.easy_enum import Enum from .commands import CommandTag from .error_codes import StatusCode -from .memories import ExtMemPropTags, MemoryRegion, RamRegion +from .memories import ExtMemPropTags, MemoryRegion ######################################################################################################################## @@ -49,7 +50,7 @@ class Version: def __init__(self, *args: Union[str, int], **kwargs: int): """Initialize the Version object. - :raises TypeError: Argument passed the not str not int + :raises McuBootError: Argument passed the not str not int """ self.mark = kwargs.get("mark", "K") self.major = kwargs.get("major", 0) @@ -61,7 +62,7 @@ def __init__(self, *args: Union[str, int], **kwargs: int): elif isinstance(args[0], str): self.from_str(args[0]) else: - raise TypeError("Value must be 'str' or 'int' type !") + raise McuBootError("Value must be 'str' or 'int' type !") def __eq__(self, obj: Any) -> bool: return isinstance(obj, Version) and vars(obj) == vars(self) diff --git a/spsdk/pfr/pfr.py b/spsdk/pfr/pfr.py index c7827fae..ccaa63d8 100644 --- a/spsdk/pfr/pfr.py +++ b/spsdk/pfr/pfr.py @@ -18,13 +18,14 @@ from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey from ruamel.yaml.comments import CommentedMap as CM +from spsdk import SPSDKError from spsdk import __author__ as spsdk_author from spsdk import __release__ as spsdk_release from spsdk import __version__ as spsdk_version from spsdk.utils.crypto.abstract import BackendClass from spsdk.utils.crypto.backend_openssl import openssl_backend from spsdk.utils.exceptions import SPSDKRegsErrorRegisterNotFound -from spsdk.utils.misc import change_endianism, format_value, value_to_int +from spsdk.utils.misc import change_endianism, value_to_int from spsdk.utils.reg_config import RegConfig from spsdk.utils.registers import Registers, RegsBitField, RegsEnum, RegsRegister @@ -214,9 +215,13 @@ def get_yaml_config(self, data: CM, indent: int = 0) -> CM: :param data: The registers settings data. :param indent: YAML start indent. :return: YAML PFR configuration in commented map(ordered dict). + :raises SPSDKError: When there is no device found + :raises SPSDKError: When there is no type found """ - assert self.device - assert self.type + if not self.device: + raise SPSDKError("Device not found") + if not self.type: + raise SPSDKError("Type not found") res_data = CM() res_data.yaml_set_start_comment( @@ -280,13 +285,19 @@ def __init__( :param device: device to use, list of supported devices is available via 'devices' method :param revision: silicon revision, if not specified, the latest is being used :param user_config: PfrConfiguration with user configuration to use with initialization + :raises SPSDKError: When no device is provided + :raises SPSDKError: When no device is not supported + :raises SPSDKError: When there is invalid revision """ self.bc_cfg = None - assert device or user_config + if not (device or user_config): + raise SPSDKError("No device provided") self.config = self._load_config() - self.device = device or (user_config.device if user_config else "") + # either 'device' or 'user_config' IS defined! Mypy doesn't understand the check above + self.device = device or user_config.device # type: ignore - assert self.device in self.config.get_devices(), f"Device '{self.device}' is not supported" + if self.device not in self.config.get_devices(): + raise SPSDKError(f"Device '{self.device}' is not supported") self.revision = revision or (user_config.revision if user_config else "latest") if not self.revision or self.revision == "latest": self.revision = self.config.get_latest_revision(self.device) @@ -294,9 +305,8 @@ def __init__( f"The silicon revision is not specified, the latest: '{self.revision}' has been used." ) - assert self.revision in self.config.get_revisions( - self.device - ), f"Invalid revision '{self.revision}' for '{self.device}'" + if self.revision not in self.config.get_revisions(self.device): + raise SPSDKError(f"Invalid revision '{self.revision}' for '{self.device}'") self.registers = Registers(self.device) self.registers.load_registers_from_xml( xml=self.config.get_data_file(self.device, self.revision), @@ -382,10 +392,14 @@ def set_config(self, config: PfrConfiguration, raw: bool = False) -> None: :param config: PFR configuration. :param raw: When set all (included computed fields) configuration will be applied. + :raises SPSDKError: When device is not provided. + :raises SPSDKError: When revision is not provided. :raises SPSDKPfrConfigError: Invalid config file. """ - assert self.device - assert self.revision + if not self.device: + raise SPSDKError("No device provided") + if not self.revision: + raise SPSDKError("No revision provided") if config.device != self.device: raise SPSDKPfrConfigError( @@ -417,8 +431,12 @@ def set_config(self, config: PfrConfiguration, raw: bool = False) -> None: self.registers.load_yml_config(config.settings, computed_regs, computed_fields) if not raw: - # Just update only configured registers - exclude_hooks = list(set(self.registers.get_reg_names()) - set(config.settings.keys())) + # # Just update only configured registers + exclude_hooks = [] + if not self.config.get_value("mandatory_computed_regs", self.device): + exclude_hooks.extend( + list(set(self.registers.get_reg_names()) - set(config.settings.keys())) + ) self.registers.run_hooks(exclude_hooks) def get_yaml_config( @@ -491,18 +509,22 @@ def _get_seal_start_address(self) -> int: """Function returns start of seal fields for the device. :return: Start of seals fields. + :raises SPSDKError: When 'seal_start_address' in database.json can not be found """ start = self.config.get_seal_start_address(self.device) - assert start, "Can't find 'seal_start_address' in database.json" + if not start: + raise SPSDKError("Can't find 'seal_start_address' in database.json") return self.registers.find_reg(start).offset def _get_seal_count(self) -> int: """Function returns seal count for the device. :return: Count of seals fields. + :raises SPSDKError: When 'seal_count' in database.json can not be found """ count = self.config.get_seal_count(self.device) - assert count, "Can't find 'seal_count' in database.json" + if not count: + raise SPSDKError("Can't find 'seal_count' in database.json") return value_to_int(count) def export(self, add_seal: bool = False, keys: List[RSAPublicKey] = None) -> bytes: @@ -512,6 +534,7 @@ def export(self, add_seal: bool = False, keys: List[RSAPublicKey] = None) -> byt :param keys: List of Keys to compute ROTKH field. :return: Binary block with PFR configuration(CMPA or CFPA). :raises SPSDKPfrRotkhIsNotPresent: This PFR block doesn't contains ROTKH field. + :raises SPSDKError: The size of data is {len(data)}, is not equal to {self.BINARY_SIZE}. """ if keys: try: @@ -546,9 +569,8 @@ def export(self, add_seal: bool = False, keys: List[RSAPublicKey] = None) -> byt seal_count = self._get_seal_count() data[seal_start : seal_start + seal_count * 4] = self.MARK * seal_count - assert ( - len(data) == self.BINARY_SIZE - ), f"The size of data is {len(data)}, is not equal to {self.BINARY_SIZE}" + if len(data) != self.BINARY_SIZE: + raise SPSDKError(f"The size of data is {len(data)}, is not equal to {self.BINARY_SIZE}") return bytes(data) def parse(self, data: bytes) -> None: @@ -558,7 +580,8 @@ def parse(self, data: bytes) -> None: """ for reg in self._get_registers(): value = bytearray(data[reg.offset : reg.offset + reg.width // 8]) - reg.set_value(change_endianism(value), raw=True) + # don't change endian if register is meant to be used in 'reverse' (array of bytes) + reg.set_value(value if reg.reverse else change_endianism(value), raw=True) def _bc_bitfields(self, reg: RegsRegister, bitfield: RegsBitField) -> List[str]: """Function returns list of backward compatibility names for bitfield. @@ -577,8 +600,10 @@ def _bc_enums(self, reg: RegsRegister, bitfield: RegsBitField, enum: RegsEnum) - :param bitfield: Current bitfield :param enum: Current enum :return: List of backward compatibility names + :raises SPSDKError: If register is not provided """ - assert self.bc_cfg + if not self.bc_cfg: + raise SPSDKError("No register provided") ret = [] bitfield_n = [bitfield.name] reg_n = [reg.name] diff --git a/spsdk/pfr/processor.py b/spsdk/pfr/processor.py index 1412b177..4e5a825c 100644 --- a/spsdk/pfr/processor.py +++ b/spsdk/pfr/processor.py @@ -16,6 +16,8 @@ from .translator import Translator +logger = logging.getLogger(__name__) + class MyTransformer(ast.NodeTransformer): """AST-based transformer for replacing string names with actual values.""" @@ -26,7 +28,7 @@ def __init__(self, translator: Translator) -> None: :param translator: Translator instance """ self.translator = translator - self.logger = logging.getLogger("transformer") + self.logger = logger.getChild("transformer") def visit_Attribute(self, node: ast.Attribute) -> ast.Constant: # pylint: disable=invalid-name """Translate Attribute Nodes.""" @@ -53,7 +55,7 @@ def __init__(self, translator: Translator) -> None: :param translator: Translator instance """ - self.logger = logging.getLogger("processor") + self.logger = logger.getChild("processor") self.transformer = MyTransformer(translator) def process(self, condition: str) -> Tuple[bool, str]: diff --git a/spsdk/pfr/translator.py b/spsdk/pfr/translator.py index d9998a9b..cb2dfe1c 100644 --- a/spsdk/pfr/translator.py +++ b/spsdk/pfr/translator.py @@ -8,8 +8,11 @@ """Translator is responsible for converting stringified keys into values.""" import logging + from .pfr import CFPA, CMPA, PfrConfiguration +logger = logging.getLogger(__name__) + class Translator: """Translates single strings (register/key names) into values.""" @@ -20,7 +23,6 @@ def __init__(self, cmpa: PfrConfiguration, cfpa: PfrConfiguration) -> None: :param cmpa: configuration data loaded from CMPA config file :param cfpa: configuration data loaded from CFPA config file """ - self.logger = logging.getLogger("translator") self.cmpa_cfg = cmpa self.cmpa_obj = CMPA(device=cmpa.device, revision=cmpa.revision, user_config=cmpa) self.cfpa_cfg = cfpa @@ -38,33 +40,33 @@ def translate(self, key: str) -> int: :return: Register's (key's) value """ area, value = key.split(".", maxsplit=1) - self.logger.debug(f"Area designator: {area}") - self.logger.debug(f"Register designator: {value}") + logger.debug(f"Area designator: {area}") + logger.debug(f"Register designator: {value}") return self.handlers[area](value) def _cmpa_translate(self, key: str) -> int: """Handler for CMPA data.""" - self.logger.debug(f"Extracting value from {key}") + logger.debug(f"Extracting value from {key}") splitted = key.split(".", maxsplit=1) register = self.cmpa_obj.registers.find_reg(splitted[0]) if len(splitted) == 2: value = register.find_bitfield(splitted[1]).get_value() else: value = register.get_int_value() - self.logger.debug(f"Extracted value {value:x}") + logger.debug(f"Extracted value {value:x}") return value def _cfpa_translate(self, key: str) -> int: """Handler for CFPA data.""" - self.logger.debug(f"Extracting value from {key}") + logger.debug(f"Extracting value from {key}") splitted = key.split(".", maxsplit=1) register = self.cfpa_obj.registers.find_reg(splitted[0]) if len(splitted) == 2: value = register.find_bitfield(splitted[1]).get_value() else: value = register.get_int_value() - self.logger.debug(f"Extracted value {value:x}") + logger.debug(f"Extracted value {value:x}") return value diff --git a/spsdk/sbfile/commands.py b/spsdk/sbfile/commands.py index 46496aff..3bdf0ded 100644 --- a/spsdk/sbfile/commands.py +++ b/spsdk/sbfile/commands.py @@ -13,7 +13,8 @@ from crcmod.predefined import mkPredefinedCrcFun -from spsdk.mboot import ExtMemId +from spsdk import SPSDKError +from spsdk.mboot import ExtMemId, MemId from spsdk.utils.crypto.abstract import BaseClass from spsdk.utils.crypto.common import swap16 from spsdk.utils.easy_enum import Enum @@ -71,7 +72,8 @@ def crc(self) -> int: def __init__(self, tag: int, flags: int = 0) -> None: """Initialize header.""" - assert tag in EnumCmdTag.tags() + if tag not in EnumCmdTag.tags(): + raise SPSDKError("Incorrect command tag") self.tag = tag self.flags = flags self.address = 0 @@ -108,7 +110,7 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdHeader": :param offset: The offset of input data :return: CMDHeader object :raise Exception: raised when size is incorrect - :raise ValueError: raised when CRC is incorrect + :raises SPSDKError: Raised when CRC is incorrect """ if calcsize(cls.FORMAT) > len(data) - offset: raise Exception() @@ -117,7 +119,7 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdHeader": cls.FORMAT, data, offset ) if crc != obj.crc: - raise ValueError("CRC does not match") + raise SPSDKError("CRC does not match") return obj @@ -176,9 +178,11 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdNop": :param data: Input data as bytes :param offset: The offset of input data :return: CMD Nop object + :raises SPSDKError: When there is incorrect header tag """ header = CmdHeader.parse(data, offset) - assert header.tag == EnumCmdTag.NOP + if header.tag != EnumCmdTag.NOP: + raise SPSDKError("Incorrect header tag") return cls() @@ -199,9 +203,11 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdTag": :param data: Input data as bytes :param offset: The offset of input data :return: parsed instance + :raises SPSDKError: When there is incorrect header tag """ header = CmdHeader.parse(data, offset) - assert header.tag == EnumCmdTag.TAG + if header.tag != EnumCmdTag.TAG: + raise SPSDKError("Incorrect header tag") result = cls() result._header = header return result @@ -220,8 +226,10 @@ def address(self, value: int) -> None: """Setter. :param value: address in target processor to load data + :raises SPSDKError: When there is incorrect address """ - assert 0x00000000 <= value <= 0xFFFFFFFF + if value < 0x00000000 or value > 0xFFFFFFFF: + raise SPSDKError("Incorrect address") self._header.address = value @property @@ -265,16 +273,18 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdLoad": :param data: Input data as bytes :param offset: The offset of input data :return: CMD Load object - :raise ValueError: raised when there is invalid CRC + :raises SPSDKError: Raised when there is invalid CRC + :raises SPSDKError: When there is incorrect header tag """ header = CmdHeader.parse(data, offset) - assert header.tag == EnumCmdTag.LOAD + if header.tag != EnumCmdTag.LOAD: + raise SPSDKError("Incorrect header tag") offset += CmdHeader.SIZE header_count = SecBootBlckSize.align(header.count) cmd_data = data[offset : offset + header_count] crc32_function = mkPredefinedCrcFun("crc-32-mpeg") if header.data != crc32_function(cmd_data, 0xFFFFFFFF): - raise ValueError("Invalid CRC in the command header") + raise SPSDKError("Invalid CRC in the command header") obj = CmdLoad(header.address, cmd_data) obj.header.data = header.data obj.header.flags = header.flags @@ -295,7 +305,8 @@ def address(self) -> int: @address.setter def address(self, value: int) -> None: """Set address for the command Fill.""" - assert 0x00000000 <= value <= 0xFFFFFFFF + if value < 0x00000000 or value > 0xFFFFFFFF: + raise SPSDKError("Incorrect address") self._header.address = value @property @@ -313,15 +324,27 @@ def __init__(self, address: int, pattern: int, length: Optional[int] = None) -> :param address: to write data :param pattern: data to be written :param length: length of data to be filled, defaults to 4 - :raise ValueError: raised when size is not aligned to 4 bytes + :raises SPSDKError: Raised when size is not aligned to 4 bytes """ super().__init__(EnumCmdTag.FILL) length = length or 4 if length % 4: - raise ValueError("Length of memory range to fill must be a multiple of 4") - pattern_bytes = pattern.to_bytes(math.ceil(pattern.bit_length() / 8), "big") + raise SPSDKError("Length of memory range to fill must be a multiple of 4") + # if the pattern is a zero, the length is considered also as zero and the + # conversion to bytes produces empty byte "array", which is wrong, as + # zero should be converted to zero byte. Thus in case the pattern_len + # evaluates to 0, we set it to 1. + pattern_len = pattern.bit_length() / 8 or 1 + # We can get a number of 3 bytes, so we consider this as a word and set + # the length to 4 bytes with the first byte being zero. + if 3 == math.ceil(pattern_len): + pattern_len = 4 + pattern_bytes = pattern.to_bytes(math.ceil(pattern_len), "big") + # The pattern length is computed above, but as we transform the number + # into bytes, compute the len again just in case - a bit paranoid + # approach chosen. if len(pattern_bytes) not in [1, 2, 4]: - raise ValueError("Pattern must be 1, 2 or 4 bytes long") + raise SPSDKError("Pattern must be 1, 2 or 4 bytes long") replicate = 4 // len(pattern_bytes) final_pattern = replicate * pattern_bytes self.address = address @@ -355,9 +378,11 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdFill": :param data: Input data as bytes :param offset: The offset of input data :return: Command Fill object + :raises SPSDKError: If incorrect header tag """ header = CmdHeader.parse(data, offset) - assert header.tag == EnumCmdTag.FILL + if header.tag != EnumCmdTag.FILL: + raise SPSDKError("Incorrect header tag") # The last 4 bytes of header are part of pattern value offset += CmdHeader.SIZE - 4 return cls(header.address, header.data, header.count) @@ -374,7 +399,8 @@ def address(self) -> int: @address.setter def address(self, value: int) -> None: """Set address of the command Jump.""" - assert 0x00000000 <= value <= 0xFFFFFFFF + if value < 0x00000000 or value > 0xFFFFFFFF: + raise SPSDKError("Incorrect address") self._header.address = value @property @@ -385,7 +411,8 @@ def argument(self) -> int: @argument.setter def argument(self, value: int) -> None: """Set command's argument.""" - assert 0x00 <= value <= 0xFF + if value < 0x00 or value > 0xFF: + raise SPSDKError("Incorrect argument") self._header.data = value @property @@ -426,9 +453,11 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdJump": :param data: Input data as bytes :param offset: The offset of input data :return: Command Jump object + :raises SPSDKError: If incorrect header tag """ header = CmdHeader.parse(data, offset) - assert header.tag == EnumCmdTag.JUMP + if header.tag != EnumCmdTag.JUMP: + raise SPSDKError("Incorrect header tag") return cls(header.address, header.data, header.count if header.flags else None) @@ -447,7 +476,8 @@ def address(self) -> int: @address.setter def address(self, value: int) -> None: """Set command's address.""" - assert 0x00000000 <= value <= 0xFFFFFFFF + if value < 0x00000000 or value > 0xFFFFFFFF: + raise SPSDKError("Incorrect address") self._header.address = value @property @@ -476,9 +506,11 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdCall": :param data: Input data as bytes :param offset: The offset of input data :return: Command Call object + :raises SPSDKError: If incorrect header tag """ header = CmdHeader.parse(data, offset) - assert header.tag == EnumCmdTag.CALL + if header.tag != EnumCmdTag.CALL: + raise SPSDKError("Incorrect header tag") return cls(header.address, header.data) @@ -493,7 +525,8 @@ def address(self) -> int: @address.setter def address(self, value: int) -> None: """Set command's address.""" - assert 0x00000000 <= value <= 0xFFFFFFFF + if value < 0x00000000 or value > 0xFFFFFFFF: + raise SPSDKError("Incorrect address") self._header.address = value @property @@ -535,9 +568,11 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdErase": :param data: Input data as bytes :param offset: The offset of input data :return: Command Erase object + :raises SPSDKError: If incorrect header tag """ header = CmdHeader.parse(data, offset) - assert header.tag == EnumCmdTag.ERASE + if header.tag != EnumCmdTag.ERASE: + raise SPSDKError("Invalid header tag") return cls(header.address, header.count, header.flags) @@ -555,9 +590,11 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdReset": :param data: Input data as bytes :param offset: The offset of input data :return: Cmd Reset object + :raises SPSDKError: If incorrect header tag """ header = CmdHeader.parse(data, offset) - assert header.tag == EnumCmdTag.RESET + if header.tag != EnumCmdTag.RESET: + raise SPSDKError("Invalid header tag") return cls() @@ -585,19 +622,19 @@ def size(self, value: int) -> None: self._header.count = value @property - def mem_type(self) -> ExtMemId: + def mem_type(self) -> MemId: """Return memory to be enabled.""" - return ExtMemId.from_int(swap16(self._header.flags)) + return MemId.from_int(swap16(self._header.flags)) @mem_type.setter - def mem_type(self, value: ExtMemId) -> None: + def mem_type(self, value: MemId) -> None: """Setter. :param value: memory to be enabled """ self._header.flags = swap16(value) - def __init__(self, address: int, size: int, mem_type: ExtMemId): + def __init__(self, address: int, size: int, mem_type: MemId): """Initialize CmdMemEnable. :param address: source address with configuration data for memory initialization @@ -619,10 +656,12 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdMemEnable": :param data: Input data as bytes :param offset: The offset of input data :return: Command Memory Enable object + :raises SPSDKError: If incorrect header tag """ header = CmdHeader.parse(data, offset) - assert header.tag == EnumCmdTag.MEM_ENABLE - return cls(header.address, header.count, ExtMemId.from_int(swap16(header.flags))) + if header.tag != EnumCmdTag.MEM_ENABLE: + raise SPSDKError("Invalid header tag") + return cls(header.address, header.count, MemId.from_int(swap16(header.flags))) class CmdProg(CmdBaseClass): @@ -639,9 +678,11 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdProg": :param data: Input data as bytes :param offset: The offset of input data :return: parsed command object + :raises SPSDKError: If incorrect header tag """ header = CmdHeader.parse(data, offset) - assert header.tag == EnumCmdTag.PROG + if header.tag != EnumCmdTag.PROG: + raise SPSDKError("Invalid header tag") return cls() @@ -664,9 +705,11 @@ def __init__(self, ver_type: VersionCheckType, version: int) -> None: :param ver_type: version check type, see `VersionCheckType` enum :param version: to be checked + :raises SPSDKError: If invalid version check type """ super().__init__(EnumCmdTag.FW_VERSION_CHECK) - assert ver_type in VersionCheckType.tags() + if ver_type not in VersionCheckType.tags(): + raise SPSDKError("Invalid version check type") self.header.address = ver_type self.header.count = version @@ -693,9 +736,11 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdVersionCheck": :param data: Input data as bytes :param offset: The offset of input data :return: parsed command object + :raises SPSDKError: If incorrect header tag """ header = CmdHeader.parse(data, offset) - assert header.tag == EnumCmdTag.FW_VERSION_CHECK + if header.tag != EnumCmdTag.FW_VERSION_CHECK: + raise SPSDKError("Invalid header tag") ver_type = VersionCheckType.from_int(header.address) version = header.count return CmdVersionCheck(ver_type, version) @@ -720,11 +765,15 @@ def __init__(self, address: int, controller_id: ExtMemId): :param address: where to backup key-store or source for restoring key-store :param controller_id: ID of the memory to backup key-store or source memory to load key-store back + :raises SPSDKError: If invalid address + :raises SPSDKError: If invalid id of memory """ super().__init__(self.cmd_id()) - assert 0 <= address <= 0xFFFFFFFF + if address < 0 or address > 0xFFFFFFFF: + raise SPSDKError("Invalid address") self.header.address = address - assert 0 <= controller_id <= 0xFF + if controller_id < 0 or controller_id > 0xFF: + raise SPSDKError("Invalid ID of memory") self.header.flags = (self.header.flags & ~self.ROM_MEM_DEVICE_ID_MASK) | ( (controller_id << self.ROM_MEM_DEVICE_ID_SHIFT) & self.ROM_MEM_DEVICE_ID_MASK ) @@ -749,9 +798,11 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdKeyStoreBackupRestore": :param data: Input data as bytes :param offset: The offset of input data :return: CmdKeyStoreBackupRestore object + :raises SPSDKError: When there is invalid header tag """ header = CmdHeader.parse(data, offset) - assert header.tag == cls.cmd_id() + if header.tag != cls.cmd_id(): + raise SPSDKError("Invalid header tag") address = header.address controller_id = (header.flags & cls.ROM_MEM_DEVICE_ID_MASK) >> cls.ROM_MEM_DEVICE_ID_SHIFT return cls(address, controller_id) # type: ignore @@ -801,9 +852,9 @@ def parse_command(data: bytes, offset: int = 0) -> CmdBaseClass: :param data: Input data as bytes :param offset: The offset of input data to start parsing :return: parsed command object - :raise ValueError: raised when there is unsupported command provided + :raises SPSDKError: Raised when there is unsupported command provided """ header_tag = data[offset + 1] if header_tag not in _CMD_CLASS: - raise ValueError(f"Unsupported command: {str(header_tag)}") + raise SPSDKError(f"Unsupported command: {str(header_tag)}") return _CMD_CLASS[header_tag].parse(data, offset) diff --git a/spsdk/sbfile/headers.py b/spsdk/sbfile/headers.py index 04980fdf..2858afa2 100644 --- a/spsdk/sbfile/headers.py +++ b/spsdk/sbfile/headers.py @@ -8,16 +8,13 @@ """Image header.""" from datetime import datetime -from struct import pack, unpack_from, calcsize +from struct import calcsize, pack, unpack_from from typing import Optional +from spsdk import SPSDKError from spsdk.utils.crypto.abstract import BaseClass -from spsdk.utils.crypto.common import ( - swap16, - unpack_timestamp, - pack_timestamp, - crypto_backend, -) +from spsdk.utils.crypto.common import crypto_backend, pack_timestamp, swap16, unpack_timestamp + from .misc import BcdVersion3 @@ -106,17 +103,20 @@ def export(self, padding: bytes = None) -> bytes: :param padding: header padding 8 bytes (for testing purposes); None to use random value :return: binary representation - :raise AttributeError: raised when format is incorrect + :raises SPSDKError: Raised when format is incorrect + :raises SPSDKError: Raised when length of padding is incorrect + :raises SPSDKError: Raised when length of header is incorrect """ if not isinstance(self.nonce, bytes) or len(self.nonce) != 16: - raise AttributeError() + raise SPSDKError("Format is incorrect") major_version, minor_version = [int(v) for v in self.version.split(".")] product_version_words = [swap16(v) for v in self.product_version.nums] component_version_words = [swap16(v) for v in self.product_version.nums] if padding is None: padding = crypto_backend().random_bytes(8) else: - assert len(padding) == 8 + if len(padding) != 8: + raise SPSDKError("Invalid length of padding") result = pack( self.FORMAT, @@ -156,7 +156,8 @@ def export(self, padding: bytes = None) -> bytes: # padding[4] padding[4:], ) - assert len(result) == self.SIZE + if len(result) != self.SIZE: + raise SPSDKError("Invalid length of header") return result # pylint: disable=too-many-locals diff --git a/spsdk/sbfile/images.py b/spsdk/sbfile/images.py index 50e8703a..9bb351ef 100644 --- a/spsdk/sbfile/images.py +++ b/spsdk/sbfile/images.py @@ -8,12 +8,15 @@ """Boot Image V2.0, V2.1.""" from datetime import datetime -from typing import Iterator, Optional, List +from typing import Iterator, List, Optional +from spsdk import SPSDKError from spsdk.utils.crypto import CertBlockV2, Counter, crypto_backend from spsdk.utils.crypto.abstract import BaseClass +from spsdk.utils.crypto.backend_internal import internal_backend from spsdk.utils.crypto.common import calc_cypher_block_count from spsdk.utils.misc import find_first + from .commands import CmdHeader from .headers import ImageHeaderV2 from .sections import BootSectionV2, CertSectionV2 @@ -47,6 +50,8 @@ def __init__( :param mac: MAC key :param nonce: nonce :param timestamp: fixed timestamp for the header; use None to use current date/time + :raises SPSDKError: Invalid dek or mac + :raises SPSDKError: Invalid length of nonce """ self._dek: bytes = dek if dek else crypto_backend().random_bytes(32) self._mac: bytes = mac if mac else crypto_backend().random_bytes(32) @@ -54,8 +59,10 @@ def __init__( if timestamp is None: timestamp = datetime.now() self._timestamp = datetime.fromtimestamp(int(timestamp.timestamp())) - assert len(self._dek) == 32 and len(self._mac) == 32 - assert len(self._nonce) == 16 + if len(self._dek) != 32 and len(self._mac) != 32: + raise SPSDKError("Invalid dek or mac") + if len(self._nonce) != 16: + raise SPSDKError("Invalid length of nonce") @property def dek(self) -> bytes: @@ -111,6 +118,7 @@ def __init__( :param build_number: The build number value (default: 0) :param advanced_params: Advanced parameters for encryption of the SB file, use for tests only :param sections: Boot sections + :raises SPSDKError: Invalid dek or mac """ self._kek = kek # Set Flags value @@ -120,7 +128,10 @@ def __init__( # Set private attributes self._dek: bytes = advanced_params.dek self._mac: bytes = advanced_params.mac - assert len(self._dek) == self.HEADER_MAC_SIZE and len(self._mac) == self.HEADER_MAC_SIZE + if ( + len(self._dek) != self.HEADER_MAC_SIZE and len(self._mac) != self.HEADER_MAC_SIZE + ): # pragma: no cover # condition checked in SBV2xAdvancedParams constructor + raise SPSDKError("Invalid dek or mac") self._header = ImageHeaderV2( version="2.0", product_version=product_version, @@ -195,9 +206,11 @@ def cert_block(self, value: Optional[CertBlockV2]) -> None: """Setter. :param value: block to be assigned; None to remove previously assigned block + :raises SPSDKError: When certificate block is used when SB file is not signed """ if value is not None: - assert self.signed, "Certificate block cannot be used unless SB file is signed" + if not self.signed: + raise SPSDKError("Certificate block cannot be used unless SB file is signed") self._cert_section = CertSectionV2(value) if value else None @property @@ -217,7 +230,8 @@ def raw_size_without_signature(self) -> int: if self.signed: size += self.DEK_MAC_SIZE cert_block = self.cert_block - assert cert_block + if not cert_block: + raise SPSDKError("Certification block not present") size += cert_block.raw_size # Boot Sections for boot_section in self._boot_sections: @@ -231,7 +245,8 @@ def raw_size(self) -> int: if self.signed: cert_block = self.cert_block - assert cert_block + if not cert_block: # pragma: no cover # already checked in raw_size_without_signature + raise SPSDKError("Certificate block not present") size += cert_block.signature_size return size @@ -299,14 +314,14 @@ def add_boot_section(self, section: BootSectionV2) -> None: """Add new Boot section into image. :param section: Boot section - :raise TypeError: raised when section is not instance of BootSectionV2 class - :raise ValueError: raise when boot section has duplicate UID + :raises SPSDKError: Raised when section is not instance of BootSectionV2 class + :raises SPSDKError: Raised when boot section has duplicate UID """ if not isinstance(section, BootSectionV2): - raise TypeError() + raise SPSDKError("Section is not instance of BootSectionV2 class") duplicate_uid = find_first(self._boot_sections, lambda bs: bs.uid == section.uid) if duplicate_uid is not None: - raise ValueError(f"Boot section with duplicate UID: {str(section.uid)}") + raise SPSDKError(f"Boot section with duplicate UID: {str(section.uid)}") self._boot_sections.append(section) def export(self, padding: Optional[bytes] = None) -> bytes: @@ -314,14 +329,19 @@ def export(self, padding: Optional[bytes] = None) -> bytes: :param padding: header padding (8 bytes) for testing purpose; None to use random values (recommended) :return: exported bytes - :raise ValueError: raised when there are no boot sections or is not signed or private keys are missing + :raises SPSDKError: Raised when there are no boot sections or is not signed or private keys are missing + :raises SPSDKError: Raised when there is invalid dek or mac + :raises SPSDKError: Raised when certificate data is not present + :raises SPSDKError: Raised when there is invalid certificate block + :raises SPSDKError: Raised when there is invalid length of exported data """ - assert len(self.dek) == 32 and len(self.mac) == 32 + if len(self.dek) != 32 or len(self.mac) != 32: + raise SPSDKError("Invalid dek or mac") # validate params if not self._boot_sections: - raise ValueError("No boot section") + raise SPSDKError("No boot section") if self.signed and (self._cert_section is None): - raise ValueError("Certificate section is required for signed images") + raise SPSDKError("Certificate section is required for signed images") # update internals self.update() # Add Image Header data @@ -333,7 +353,8 @@ def export(self, padding: Optional[bytes] = None) -> bytes: # Add Padding data += padding if padding else crypto_backend().random_bytes(8) # Add Certificates data - assert self._header.nonce + if not self._header.nonce: + raise SPSDKError("There is no nonce in the header") counter = Counter(self._header.nonce) counter.increment(calc_cypher_block_count(len(data))) if self._cert_section is not None: @@ -347,13 +368,16 @@ def export(self, padding: Optional[bytes] = None) -> bytes: if self.signed: private_key_pem_data = self.private_key_pem_data if private_key_pem_data is None: - raise ValueError("Private key not assigned, cannot sign the image") + raise SPSDKError("Private key not assigned, cannot sign the image") certificate_block = self.cert_block - assert (certificate_block is not None) and certificate_block.verify_private_key( - private_key_pem_data - ) + if not ( + (certificate_block is not None) + and certificate_block.verify_private_key(private_key_pem_data) + ): + raise SPSDKError("Invalid certificate block") data += crypto_backend().rsa_sign(private_key_pem_data, data) - assert len(data) == self.raw_size + if len(data) != self.raw_size: + raise SPSDKError("Invalid length of exported data") return data # pylint: disable=too-many-locals @@ -368,8 +392,11 @@ def parse(cls, data: bytes, offset: int = 0, kek: bytes = bytes()) -> "BootImage :raise Exception: raised when header is in wrong format :raise Exception: raised when there is invalid header version :raise Exception: raised when signature is incorrect + :raises SPSDKError: Raised when kek is empty + :raises Exception: raised when header's nonce is not present """ - assert kek, "kek cannot be empty" + if not kek: + raise SPSDKError("kek cannot be empty") index = offset header_raw_data = data[index : index + ImageHeaderV2.SIZE] index += ImageHeaderV2.SIZE @@ -389,7 +416,8 @@ def parse(cls, data: bytes, offset: int = 0, kek: bytes = bytes()) -> "BootImage raise Exception(f"Invalid Header Version: {header.version} instead 2.0") image_size = header.image_blocks * 16 # Initialize counter - assert header.nonce + if not header.nonce: + raise SPSDKError("Header's nonce not present") counter = Counter(header.nonce) counter.increment(calc_cypher_block_count(index - offset)) # ... @@ -432,6 +460,11 @@ class BootImageV21(BaseClass): # Image specific data HEADER_MAC_SIZE = 32 KEY_BLOB_SIZE = 80 + SHA_256_SIZE = 32 + + # defines + FLAGS_SHA_PRESENT_BIT = 0x8000 # image contains SHA-256 + FLAGS_ENCRYPTED_SIGNED_BIT = 0x0008 # image is signed and encrypted def __init__( self, @@ -441,6 +474,7 @@ def __init__( component_version: str = "1.0.0", build_number: int = 0, advanced_params: SBV2xAdvancedParams = SBV2xAdvancedParams(), + flags: int = FLAGS_SHA_PRESENT_BIT | FLAGS_ENCRYPTED_SIGNED_BIT, ) -> None: """Initialize Secure Boot Image V2.1. @@ -451,6 +485,7 @@ def __init__( :param build_number: The build number value (default: 0) :param advanced_params: optional advanced parameters for encryption; it is recommended to use default value + :param flags: see flags defined in class. :param sections: Boot sections """ self._kek = kek @@ -464,7 +499,7 @@ def __init__( product_version=product_version, component_version=component_version, build_number=build_number, - flags=0x08, + flags=flags, nonce=advanced_params.nonce, timestamp=advanced_params.timestamp, ) @@ -551,7 +586,8 @@ def raw_size(self) -> int: cert_blk = self.cert_block if cert_blk: size += cert_blk.raw_size - assert self.signed + if not self.signed: # pragma: no cover # SB2.1 is always signed + raise SPSDKError("Certificate block is not signed") size += cert_blk.signature_size # Boot Sections for boot_section in self._boot_sections: @@ -582,7 +618,8 @@ def update(self) -> None: cert_blk = self.cert_block if cert_blk is not None: data_size += cert_blk.raw_size - assert self.signed + if not self.signed: # pragma: no cover # SB2.1 is always signed + raise SPSDKError("Certificate block is not signed") data_size += cert_blk.signature_size self._header.first_boot_tag_block = calc_cypher_block_count(data_size) # ... @@ -621,10 +658,10 @@ def add_boot_section(self, section: BootSectionV2) -> None: """Add new Boot section into image. :param section: Boot section to be added - :raise TypeError: raised when section is not instance of BootSectionV2 class + :raises SPSDKError: Raised when section is not instance of BootSectionV2 class """ if not isinstance(section, BootSectionV2): - raise TypeError() + raise SPSDKError("Section is not instance of BootSectionV2 class") self._boot_sections.append(section) # pylint: disable=too-many-locals @@ -636,17 +673,20 @@ def export( :param padding: header padding (8 bytes) for testing purpose; None to use random values (recommended) :param dbg_info: optional list, where debug info is exported in text form :return: exported bytes - :raise ValueError: raised when there is no boot section to be added - :raise ValueError: raise when certificate is not assigned - :raise ValueError: raise when private key is not assigned + :raises SPSDKError: Raised when there is no boot section to be added + :raises SPSDKError: Raised when certificate is not assigned + :raises SPSDKError: Raised when private key is not assigned + :raises SPSDKError: Raised when private header's nonce is invalid + :raises SPSDKError: Raised when private key does not match certificate + :raises SPSDKError: Raised when there is no debug info """ # validate params if not self._boot_sections: - raise ValueError("At least one Boot Section must be added") + raise SPSDKError("At least one Boot Section must be added") if self.cert_block is None: - raise ValueError("Certificate is not assigned") + raise SPSDKError("Certificate is not assigned") if self.private_key_pem_data is None: - raise ValueError("Private key not assigned, cannot sign the image") + raise SPSDKError("Private key not assigned, cannot sign the image") # Update internals if dbg_info is not None: dbg_info.append("[sb_file]") @@ -662,7 +702,11 @@ def export( + self.cert_block.raw_size + self.cert_block.signature_size ) - assert self._header.nonce + if self.header.flags & self.FLAGS_SHA_PRESENT_BIT: + bs_offset += self.SHA_256_SIZE + + if not self._header.nonce: + raise SPSDKError("Invalid header's nonce") counter = Counter(self._header.nonce, calc_cypher_block_count(bs_offset)) for sect in self._boot_sections: bs_data += sect.export( @@ -693,16 +737,19 @@ def export( if dbg_info: dbg_info.append("[cert_block]") dbg_info.append(self.cert_block.export().hex()) + # Add SHA-256 of Bootable sections if requested + if self.header.flags & self.FLAGS_SHA_PRESENT_BIT: + signed_data += internal_backend.hash(bs_data) # Add Signature data - assert self.cert_block.verify_private_key( - self.private_key_pem_data - ) # verify private key matches certificate + if not self.cert_block.verify_private_key(self.private_key_pem_data): + raise SPSDKError("Private key does not match certificate") signature = crypto_backend().rsa_sign(self.private_key_pem_data, signed_data) if dbg_info: dbg_info.append("[signature]") dbg_info.append(signature.hex()) dbg_info.append("[boot_sections]") - assert bs_dbg_info + if not bs_dbg_info: + raise SPSDKError("No debug information") dbg_info.extend(bs_dbg_info) return signed_data + signature + bs_data @@ -720,12 +767,16 @@ def parse( :param data: Raw data of parsed image :param offset: The offset of input data :param kek: The Key for unwrapping DEK and MAC keys (required) - :param plain_sections: Sections are not encrypted; this is used only for debugging, not supported by ROM code + :param plain_sections: Sections are not encrypted; this is used only for debugging, + not supported by ROM code :return: BootImageV21 parsed object - :raise Exception: raised when header is in incorrect format - :raise Exception: raised when signature is incorrect + :raises Exception: raised when header is in incorrect format + :raises Exception: raised when signature is incorrect + :raises SPSDKError: Raised when kek is empty + :raises Exception: raised when header's nonce not present" """ - assert kek, "kek cannot be empty" + if not kek: + raise SPSDKError("kek cannot be empty") index = offset header_raw_data = data[index : index + ImageHeaderV2.SIZE] index += ImageHeaderV2.SIZE @@ -743,23 +794,51 @@ def parse( # Parse Certificate Block cert_block = CertBlockV2.parse(data, index) index += cert_block.raw_size + # Verify Signature - if not cert_block.verify_data( - data[index : index + cert_block.signature_size], data[offset:index] - ): + signature_index = index + # The image may containt SHA, in such a case the signature is placed + # after SHA. Thus we must shift the index by SHA size. + if header.flags & BootImageV21.FLAGS_SHA_PRESENT_BIT: + signature_index += BootImageV21.SHA_256_SIZE + result = cert_block.verify_data( + data[signature_index : signature_index + cert_block.signature_size], + data[offset:signature_index], + ) + + if not result: raise Exception() + # Check flags, if 0x8000 bit is set, the SB file contains SHA-256 between + # certificate and signature. + if header.flags & BootImageV21.FLAGS_SHA_PRESENT_BIT: + bootable_section_sha256 = data[index : index + BootImageV21.SHA_256_SIZE] + index += BootImageV21.SHA_256_SIZE index += cert_block.signature_size # Check first Boot Section HMAC # TODO: not implemented yet # hmac_data_calc = crypto_backend().hmac(mac, data[index + CmdHeader.SIZE: index + CmdHeader.SIZE + ((2) * 32)]) # if hmac_data != hmac_data_calc: # raise Exception() - assert header.nonce + if not header.nonce: + raise SPSDKError("Header's nonce not present") counter = Counter(header.nonce) counter.increment(calc_cypher_block_count(index - offset)) boot_section = BootSectionV2.parse( data, index, dek=dek, mac=mac, counter=counter, plain_sect=plain_sections ) + if header.flags & BootImageV21.FLAGS_SHA_PRESENT_BIT: + computed_bootable_section_sha256 = internal_backend.hash( + data[index:], algorithm="sha256" + ) + + if bootable_section_sha256 != computed_bootable_section_sha256: + raise SPSDKError( + desc=( + "Error: invalid Bootable section SHA." + f"Expected {bootable_section_sha256.decode('utf-8')}," + f"got {computed_bootable_section_sha256.decode('utf-8')}" + ) + ) adv_params = SBV2xAdvancedParams( dek=dek, mac=mac, nonce=header.nonce, timestamp=header.timestamp ) diff --git a/spsdk/sbfile/misc.py b/spsdk/sbfile/misc.py index b2ea94e1..c27a9dd2 100644 --- a/spsdk/sbfile/misc.py +++ b/spsdk/sbfile/misc.py @@ -9,6 +9,7 @@ from typing import Any, Sequence, Union +from spsdk import SPSDKError from spsdk.utils import misc @@ -45,10 +46,10 @@ def to_num_blocks(size: int) -> int: :param size: to be converted, the size must be aligned to block boundary :return: corresponding number of cipher blocks - :raise ValueError: is size not aligned to block boundary + :raises SPSDKError: Raised when size is not aligned to block boundary """ if not SecBootBlckSize.is_aligned(size): - raise ValueError( + raise SPSDKError( f"Invalid size {size}, expected number aligned to BLOCK size {SecBootBlckSize.BLOCK_SIZE}" ) return size // SecBootBlckSize.BLOCK_SIZE @@ -79,13 +80,13 @@ def _check_number(num: int) -> bool: :param num: to be checked :return: True if number format is valid - :raise ValueError: if number format is not valid + :raises SPSDKError: If number format is not valid """ - if not 0 <= num <= 0x9999: - raise ValueError("Invalid number range") + if num < 0 or num > 0x9999: + raise SPSDKError("Invalid number range") for index in range(4): if (num >> 4 * index) & 0xF > 0x9: - raise ValueError("Invalid number, contains digit > 9") + raise SPSDKError("Invalid number, contains digit > 9") return True @staticmethod @@ -94,9 +95,10 @@ def _num_from_str(text: str) -> int: :param text: given string to be converted to a version number :return: version number - :raise ValueError: if format is not valid + :raises SPSDKError: If format is not valid """ - assert 0 <= len(text) <= 4 + if len(text) < 0 or len(text) > 4: + raise SPSDKError("Invalid text length") result = int(text, 16) BcdVersion3._check_number(result) return result @@ -107,10 +109,11 @@ def from_str(text: str) -> "BcdVersion3": :param text: version in format #.#.#, where # is 1-4 decimal digits :return: BcdVersion3 instance - :raise: ValueError: if format is not valid + :raises SPSDKError: If format is not valid """ parts = text.split(".") - assert len(parts) == 3 + if len(parts) != 3: + raise SPSDKError("Invalid length") major = BcdVersion3._num_from_str(parts[0]) minor = BcdVersion3._num_from_str(parts[1]) service = BcdVersion3._num_from_str(parts[2]) @@ -121,14 +124,14 @@ def to_version(input_version: BcdVersion3Format) -> "BcdVersion3": """Convert different input formats into BcdVersion3 instance. :param input_version: either directly BcdVersion3 or string - :raise ValueError: raises when the format is unsupported + :raises SPSDKError: Raises when the format is unsupported :return: BcdVersion3 instance """ if isinstance(input_version, BcdVersion3): return input_version if isinstance(input_version, str): return BcdVersion3.from_str(input_version) - raise ValueError("unsupported format") + raise SPSDKError("unsupported format") def __init__(self, major: int = 1, minor: int = 0, service: int = 0): """Initialize BcdVersion3. @@ -136,14 +139,16 @@ def __init__(self, major: int = 1, minor: int = 0, service: int = 0): :param major: number in BCD format, 1-4 decimal digits :param minor: number in BCD format, 1-4 decimal digits :param service: number in BCD format, 1-4 decimal digits + :raises SPSDKError: Invalid version """ - assert all( + if not all( [ BcdVersion3._check_number(major), BcdVersion3._check_number(minor), BcdVersion3._check_number(service), ] - ) + ): + raise SPSDKError("Invalid version") self.major = major self.minor = minor self.service = service diff --git a/spsdk/sbfile/sb1/commands.py b/spsdk/sbfile/sb1/commands.py index f927a75c..f6a7f1f6 100644 --- a/spsdk/sbfile/sb1/commands.py +++ b/spsdk/sbfile/sb1/commands.py @@ -9,18 +9,21 @@ from typing import Mapping, Type -from ..commands import CmdBaseClass, EnumCmdTag +from spsdk import SPSDKError + from ..commands import ( - CmdNop, - CmdTag, - CmdLoad, - CmdFill, - CmdJump, + CmdBaseClass, CmdCall, CmdErase, - CmdReset, + CmdFill, + CmdJump, + CmdLoad, CmdMemEnable, + CmdNop, CmdProg, + CmdReset, + CmdTag, + EnumCmdTag, ) # mapping of V1.x command to the implementation class @@ -45,9 +48,9 @@ def parse_v1_command(data: bytes, offset: int = 0) -> CmdBaseClass: :param data: Input data as bytes :param offset: The offset of input data to start parsing :return: parsed command object - :raise ValueError: raised when there is unsupported command + :raises SPSDKError: Raised when there is unsupported command """ header_tag = EnumCmdTag.from_int(data[offset + 1]) if header_tag not in _CMDV1_TO_CLASS: - raise ValueError(f"Unsupported command: {EnumCmdTag.name(header_tag)}") + raise SPSDKError(f"Unsupported command: {EnumCmdTag.name(header_tag)}") return _CMDV1_TO_CLASS[header_tag].parse(data, offset) diff --git a/spsdk/sbfile/sb1/headers.py b/spsdk/sbfile/sb1/headers.py index fc1c1d8e..50c681df 100644 --- a/spsdk/sbfile/sb1/headers.py +++ b/spsdk/sbfile/sb1/headers.py @@ -11,17 +11,14 @@ from struct import calcsize, pack, unpack_from from typing import Optional +from spsdk import SPSDKError from spsdk.utils.crypto.abstract import BaseClass -from spsdk.utils.crypto.common import ( - swap16, - crypto_backend, - pack_timestamp, - unpack_timestamp, -) +from spsdk.utils.crypto.common import crypto_backend, pack_timestamp, swap16, unpack_timestamp from spsdk.utils.easy_enum import Enum from spsdk.utils.misc import DebugInfo + from ..commands import CmdHeader, CmdTag -from ..misc import BcdVersion3Format, SecBootBlckSize, BcdVersion3 +from ..misc import BcdVersion3, BcdVersion3Format, SecBootBlckSize class SecureBootFlagsV1(Enum): @@ -75,11 +72,13 @@ def __init__( The first 16 bytes (of 20 total) also act as the initialization vector for CBC-encrypted regions. :param timestamp: datetime of the file creation, use None for current date/time Fixed value should be used only for regression testing to generate same results + :raises SPSDKError: Invalid header version """ # SHA-1 digest of all fields of the header prior to this one. self.digest = digest # Major version of the boot image format, currently 1. Minor version of the boot image format, currently 1 or 2. - assert version in ("1.0", "1.1", "1.2") + if version not in ("1.0", "1.1", "1.2"): + raise SPSDKError("Invalid header version") self.version = version self.flags = flags # Size of the entire image in blocks. @@ -219,12 +218,12 @@ def parse(cls, data: bytes, offset: int = 0) -> "SecureBootHeaderV1": :param data: given binary data to be decoded :param offset: to start parsing binary data; 0 by default :return: the instance of secure boot header v1 - :raise ValueError: raised when there is insufficient size - :raise ValueError: raised when there is invalid signature - :raise ValueError: raised when there is unexpected signature + :raises SPSDKError: Raised when there is insufficient size + :raises SPSDKError: Raised when there is invalid signature + :raises SPSDKError: Raised when there is unexpected signature """ if SecureBootHeaderV1._SIZE > len(data) - offset: - raise ValueError("Insufficient size") + raise SPSDKError("Insufficient size") ( digest, @@ -262,12 +261,12 @@ def parse(cls, data: bytes, offset: int = 0) -> "SecureBootHeaderV1": # check header signature 1 if signature1 != SecureBootHeaderV1._SIGNATURE1: - raise ValueError("Invalid signature") + raise SPSDKError("Invalid signature") # check header signature 2 for version 1.1 and greater if (major_version > 1) or ((major_version == 1) and (minor_version >= 2)): if signature2 != SecureBootHeaderV1._SIGNATURE2: - raise ValueError("Unexpected signature") + raise SPSDKError("Unexpected signature") product_version = BcdVersion3(swap16(pv0), swap16(pv1), swap16(pv2)) component_version = BcdVersion3(swap16(cv0), swap16(cv1), swap16(cv2)) @@ -358,10 +357,10 @@ def parse(cls, data: bytes, offset: int = 0) -> "SectionHeaderItemV1": :param data: to be parsed :param offset: to start parsing the data :return: the new instance - :raise ValueError: if size is not sufficient + :raises SPSDKError: If size is not sufficient """ if cls.SIZE > len(data) - offset: - raise ValueError("Insufficient size") + raise SPSDKError("Insufficient size") (identifier, offset, length, flags) = unpack_from(cls.FORMAT, data, offset) return cls(identifier, offset, length, SecureBootFlagsV1.from_int(flags)) diff --git a/spsdk/sbfile/sb1/images.py b/spsdk/sbfile/sb1/images.py index 1f97a034..e4ba2222 100644 --- a/spsdk/sbfile/sb1/images.py +++ b/spsdk/sbfile/sb1/images.py @@ -8,14 +8,16 @@ """Secure Boot Image Class.""" from datetime import datetime -from typing import Optional, List, Sequence +from typing import List, Optional, Sequence +from spsdk import SPSDKError from spsdk.utils.crypto import crypto_backend from spsdk.utils.crypto.abstract import BaseClass from spsdk.utils.misc import DebugInfo, align -from .headers import SecureBootHeaderV1, SectionHeaderItemV1, BootSectionHeaderV1 + +from ..misc import BcdVersion3, BcdVersion3Format, SecBootBlckSize +from .headers import BootSectionHeaderV1, SectionHeaderItemV1, SecureBootHeaderV1 from .sections import BootSectionV1 -from ..misc import BcdVersion3Format, SecBootBlckSize, BcdVersion3 ######################################################################################################################## @@ -76,7 +78,8 @@ def first_boot_section_id(self) -> int: @first_boot_section_id.setter def first_boot_section_id(self, value: int) -> None: - assert value < len(self._sections) + if value > len(self._sections): + raise SPSDKError("Invalid length of section") self._header.first_boot_section_id = value @property @@ -121,10 +124,10 @@ def append(self, section: BootSectionV1) -> None: def validate(self) -> None: """Validate settings. - :raise ValueError: if the settings is not consistent + :raises SPSDKError: If the settings is not consistent """ if not self._sections: - raise ValueError("At least one section must be defined") + raise SPSDKError("At least one section must be defined") def update(self) -> None: """Update content.""" @@ -167,6 +170,8 @@ def export( :param auth_padding: optional padding used after authentication; recommended to use None to apply random value :param dbg_info: instance allowing to debug generated output :return: serialize the instance into binary data + :raises SPSDKError: Invalid section data + :raises SPSDKError: Invalid padding length """ self.update() self.validate() @@ -182,7 +187,8 @@ def export( dbg_info.append_section("Sections") for sect in self._sections: sect_data = sect.export(dbg_info) - assert len(sect_data) == sect.size + if len(sect_data) != sect.size: + raise SPSDKError("Invalid section data") data += sect_data # authentication: SHA1 auth_code = crypto_backend().hash(data, "sha1") @@ -192,7 +198,8 @@ def export( padding_len = align(len(auth_code), SecBootBlckSize.BLOCK_SIZE) - len(auth_code) if auth_padding is None: auth_padding = crypto_backend().random_bytes(padding_len) - assert padding_len == len(auth_padding) + if padding_len != len(auth_padding): + raise SPSDKError("Invalid padding length") data += auth_padding dbg_info.append_binary_section("padding", auth_padding) return data @@ -205,6 +212,7 @@ def parse(cls, data: bytes, offset: int = 0) -> "SecureBootV1": :param offset: to start parsing the data :return: converted instance :raise ValueError: raised when digest does not match + :raises SPSDKError: Raised when section is invalid """ obj = SecureBootV1() cur_pos = offset @@ -217,7 +225,8 @@ def parse(cls, data: bytes, offset: int = 0) -> "SecureBootV1": cur_pos += sect_header.size # sections new_pos = offset + obj._header.first_boot_tag_block * SecBootBlckSize.BLOCK_SIZE - assert new_pos >= cur_pos + if new_pos < cur_pos: + raise SPSDKError("Invalid section") cur_pos = new_pos for _ in range(obj._header.section_count): boot_sect = BootSectionV1.parse(data, cur_pos) diff --git a/spsdk/sbfile/sb1/sections.py b/spsdk/sbfile/sb1/sections.py index 8d5548c2..7541f710 100644 --- a/spsdk/sbfile/sb1/sections.py +++ b/spsdk/sbfile/sb1/sections.py @@ -11,21 +11,22 @@ from spsdk.utils.crypto.abstract import BaseClass from spsdk.utils.misc import DebugInfo -from .commands import parse_v1_command -from .headers import BootSectionHeaderV1, SecureBootFlagsV1 -from ..commands import CmdBaseClass + from ..commands import ( - CmdNop, + CmdBaseClass, + CmdCall, CmdErase, - CmdLoad, CmdFill, CmdJump, - CmdCall, - CmdReset, + CmdLoad, CmdMemEnable, + CmdNop, CmdProg, + CmdReset, ) from ..misc import SecBootBlckSize +from .commands import parse_v1_command +from .headers import BootSectionHeaderV1, SecureBootFlagsV1 class BootSectionV1(BaseClass): diff --git a/spsdk/sbfile/sb31/commands.py b/spsdk/sbfile/sb31/commands.py index c1170ba9..35c4d3a4 100644 --- a/spsdk/sbfile/sb31/commands.py +++ b/spsdk/sbfile/sb31/commands.py @@ -7,12 +7,13 @@ """Module for creation commands.""" from abc import abstractmethod -from struct import pack, unpack_from, calcsize -from typing import Mapping, Type, List, Tuple +from struct import calcsize, pack, unpack_from +from typing import Mapping, Tuple, Type +from spsdk import SPSDKError from spsdk.sbfile.sb31.constants import EnumCmdTag -from spsdk.utils.misc import align_block from spsdk.utils.easy_enum import Enum +from spsdk.utils.misc import align_block ######################################################################################################################## # Main Class @@ -65,7 +66,8 @@ def address(self) -> int: @address.setter def address(self, value: int) -> None: """Set address.""" - assert 0x00000000 <= value <= 0xFFFFFFFF + if value < 0x00000000 or value > 0xFFFFFFFF: + raise SPSDKError("Invalid address") self._address = value @property @@ -76,7 +78,8 @@ def length(self) -> int: @length.setter def length(self, value: int) -> None: """Set value.""" - assert 0x00000000 <= value <= 0xFFFFFFFF + if value < 0x00000000 or value > 0xFFFFFFFF: + raise SPSDKError("Invalid length") self._length = value def __init__(self, address: int, length: int, cmd_tag: int = EnumCmdTag.NONE) -> None: @@ -105,15 +108,15 @@ def header_parse(cls, cmd_tag: int, data: bytes, offset: int = 0) -> Tuple[int, :param data: Input data as bytes array :param offset: The offset of input data :param cmd_tag: Information about command tag - :raises ValueError: Raise if tag is not equal to required TAG - :raises ValueError: Raise if cmd is not equal EnumCmdTag + :raises SPSDKError: Raised if tag is not equal to required TAG + :raises SPSDKError: Raised if cmd is not equal EnumCmdTag :return: Tuple """ tag, address, length, cmd = unpack_from(cls.FORMAT, data, offset) if tag != cls.TAG: - raise ValueError("TAG is not valid.") + raise SPSDKError("TAG is not valid.") if cmd != cmd_tag: - raise ValueError("Values are not same.") + raise SPSDKError("Values are not same.") return address, length @@ -160,11 +163,12 @@ def _extract_data(cls, data: bytes, offset: int = 0) -> Tuple[int, int, bytes, i tag, address, length, cmd = unpack_from(cls.FORMAT, data) memory_id = 0 if tag != cls.TAG: - raise ValueError(f"Invalid TAG, expected: {cls.TAG}") + raise SPSDKError(f"Invalid TAG, expected: {cls.TAG}") offset += BaseCmd.SIZE if cls.HAS_MEMORY_ID_BLOCK: memory_id, pad0, pad1, pad2 = unpack_from("<4L", data, offset=offset) - assert pad0 == pad1 == pad2 == 0 + if not pad0 == pad1 == pad2 == 0: + raise SPSDKError("Invalid padding") offset += 16 load_data = data[offset : offset + length] return address, length, load_data, cmd, memory_id @@ -176,7 +180,7 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdLoadBase": :param data: Input data as bytes array :param offset: The offset of input data :return: CmdLoad - :raises ValueError: Invalid cmd_tag was found + :raises SPSDKError: Invalid cmd_tag was found """ address, _, data, cmd_tag, memory_id = cls._extract_data(data, offset) if cmd_tag not in [ @@ -187,7 +191,7 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdLoadBase": EnumCmdTag.PROGRAM_FUSES, EnumCmdTag.PROGRAM_IFR, ]: - raise ValueError(f"Invalid cmd_tag found: {cmd_tag}") + raise SPSDKError(f"Invalid cmd_tag found: {cmd_tag}") if cls == CmdLoadBase: return cls(cmd_tag=cmd_tag, address=address, data=data, memory_id=memory_id) # pylint: disable=no-value-for-parameter @@ -226,10 +230,12 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdErase": :param data: Input data as bytes array :param offset: The offset of input data :return: CmdErase + :raises SPSDKError: Invalid padding """ address, length = cls.header_parse(data=data, offset=offset, cmd_tag=EnumCmdTag.ERASE) memory_id, pad0, pad1, pad2 = unpack_from("<4L", data, offset=offset + 16) - assert pad0 == pad1 == pad2 == 0 + if not pad0 == pad1 == pad2 == 0: + raise SPSDKError("Invalid padding") return cls(address=address, length=length, memory_id=memory_id) @@ -318,11 +324,12 @@ def _extract_data(cls, data: bytes, offset: int = 0) -> Tuple[int, int, bytes, i length *= 4 memory_id = 0 if tag != cls.TAG: - raise ValueError(f"Invalid TAG, expected: {cls.TAG}") + raise SPSDKError(f"Invalid TAG, expected: {cls.TAG}") offset += BaseCmd.SIZE if cls.HAS_MEMORY_ID_BLOCK: memory_id, pad0, pad1, pad2 = unpack_from("<4L", data, offset=offset) - assert pad0 == pad1 == pad2 == 0 + if pad0 != pad1 != pad2 != 0: + raise SPSDKError("Invalid padding") offset += 16 load_data = data[offset : offset + length] return address, length, load_data, cmd, memory_id @@ -427,12 +434,14 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdCopy": :param data: Input data as bytes array :param offset: The offset of input data :return: CmdCopy + :raises SPSDKError: Invalid padding """ address, length = cls.header_parse(data=data, offset=offset, cmd_tag=EnumCmdTag.COPY) destination_address, memory_id_from, memory_id_to, pad0 = unpack_from( "<4L", data, offset=offset + 16 ) - assert pad0 == 0 + if pad0 != 0: + raise SPSDKError("Invalid padding") return cls( address=address, length=length, @@ -584,10 +593,12 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdFillMemory": :param data: Input data as bytes array :param offset: The offset of input data :return: CmdErase + :raises SPSDKError: Invalid padding """ address, length = cls.header_parse(data=data, offset=offset, cmd_tag=EnumCmdTag.FILL_MEMORY) pattern, pad0, pad1, pad2 = unpack_from("<4L", data, offset=offset + 16) - assert pad0 == pad1 == pad2 == 0 + if pad0 != pad1 != pad2 != 0: + raise SPSDKError("Invalid padding") return cls(address=address, length=length, pattern=pattern) @@ -668,11 +679,11 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdSectionHeader": :param data: Input data as bytes array :param offset: The offset of input data - :raises ValueError: Raise when FORMAT is bigger than length of the data without offset + :raises SPSDKError: Raised when FORMAT is bigger than length of the data without offset :return: CmdSectionHeader """ if calcsize(cls.FORMAT) > len(data) - offset: - raise ValueError("FORMAT is bigger than length of the data without offset!") + raise SPSDKError("FORMAT is bigger than length of the data without offset!") section_uid, section_type, length, _ = unpack_from(cls.FORMAT, data, offset) return cls(section_uid=section_uid, section_type=section_type, length=length) @@ -700,13 +711,15 @@ def parse_command(data: bytes, offset: int = 0) -> object: :param data: Input data as bytes array :param offset: The offset of input data - :raises ValueError: Raise when tag is not in cmd_class + :raises SPSDKError: Raised when tag is not in cmd_class + :raises SPSDKError: Raised when tag is invalid :return: object """ # verify that first 4 bytes of frame are 55aaaa55 tag = unpack_from(" None: """Set all SB3.1 commands at once.""" self.commands = commands.copy() - def export(self) -> bytes: + def get_cmd_blocks_to_export(self) -> List[bytes]: """Export commands as bytes.""" commands_bytes = b"".join([command.export() for command in self.commands]) section_header = CmdSectionHeader(length=len(commands_bytes)) @@ -199,6 +199,11 @@ def export(self) -> bytes: for i in range(0, len(total), self.DATA_CHUNK_LENGTH) ] data_blocks[-1] = align_block(data_blocks[-1], alignment=self.DATA_CHUNK_LENGTH) + + return data_blocks + + def process_cmd_blocks_to_export(self, data_blocks: List[bytes]) -> bytes: + """Process given data blocks for export.""" self.block_count = len(data_blocks) processed_blocks = [ @@ -208,10 +213,16 @@ def export(self) -> bytes: final_data = b"".join(reversed(processed_blocks)) return final_data + def export(self) -> bytes: + """Export commands as bytes.""" + data_blocks = self.get_cmd_blocks_to_export() + return self.process_cmd_blocks_to_export(data_blocks) + def _process_block(self, block_number: int, block_data: bytes) -> bytes: """Process single block.""" if self.is_encrypted: - assert self.key_derivator + if not self.key_derivator: + raise SPSDKError("No key derivator") block_key = self.key_derivator.get_block_key(block_number) encrypted_block = internal_backend.aes_cbc_encrypt(block_key, block_data) else: diff --git a/spsdk/sbfile/sections.py b/spsdk/sbfile/sections.py index 4eaf501b..a4210008 100644 --- a/spsdk/sbfile/sections.py +++ b/spsdk/sbfile/sections.py @@ -10,17 +10,12 @@ from struct import unpack_from from typing import Iterator, List, Optional +from spsdk import SPSDKError from spsdk.utils.crypto import CertBlockV2, Counter from spsdk.utils.crypto.abstract import BaseClass from spsdk.utils.crypto.common import calc_cypher_block_count, crypto_backend -from .commands import ( - CmdHeader, - CmdBaseClass, - EnumCmdTag, - EnumSectionFlag, - parse_command, -) +from .commands import CmdBaseClass, CmdHeader, EnumCmdTag, EnumSectionFlag, parse_command ######################################################################################################################## # Boot Image Sections @@ -330,6 +325,7 @@ def export( :param counter: The counter object (required) :return: exported bytes :raise Exception: raised when dek, mac, counter have invalid format + :raises SPSDKError: Raised size of exported bytes is invalid """ if not isinstance(dek, bytes): raise Exception() @@ -347,7 +343,8 @@ def export( hmac_data = crypto_backend().hmac(mac, header_encrypted) hmac_data += crypto_backend().hmac(mac, body_data) result = header_encrypted + hmac_data + body_data - assert len(result) == self.raw_size + if len(result) != self.raw_size: + raise SPSDKError("Invalid size") return result @classmethod @@ -367,16 +364,16 @@ def parse( :param mac: The MAC value in bytes (required) :param counter: The counter object (required) :return: parsed cert section v2 object - :raise AttributeError: raised when dek, mac, counter are not valid - :raise Exception: raised when there is invalid header HMAC, TAG, FLAGS, Mark - :raise Exception: raised when there is invalid certificate block HMAC + :raises SPSDKError: Raised when dek, mac, counter are not valid + :raises SPSDKError: Raised when there is invalid header HMAC, TAG, FLAGS, Mark + :raises SPSDKError: Raised when there is invalid certificate block HMAC """ if not isinstance(dek, bytes): - raise AttributeError() + raise SPSDKError("DEK value has invalid format") if not isinstance(mac, bytes): - raise AttributeError() + raise SPSDKError("MAC value has invalid format") if not isinstance(counter, Counter): - raise AttributeError() + raise SPSDKError("Counter value has invalid format") index = offset header_encrypted = data[index : index + CmdHeader.SIZE] index += CmdHeader.SIZE @@ -385,19 +382,19 @@ def parse( cert_block_hmac = data[index : index + cls.HMAC_SIZE] index += cls.HMAC_SIZE if header_hmac != crypto_backend().hmac(mac, header_encrypted): - raise Exception("Invalid Header HMAC") + raise SPSDKError("Invalid Header HMAC") header_encrypted = crypto_backend().aes_ctr_decrypt(dek, header_encrypted, counter.value) header = CmdHeader.parse(header_encrypted) if header.tag != EnumCmdTag.TAG: - raise Exception(f"Invalid Header TAG: 0x{header.tag:02X}") + raise SPSDKError(f"Invalid Header TAG: 0x{header.tag:02X}") if header.flags != (EnumSectionFlag.CLEARTEXT | EnumSectionFlag.LAST_SECT): - raise Exception(f"Invalid Header FLAGS: 0x{header.flags:02X}") + raise SPSDKError(f"Invalid Header FLAGS: 0x{header.flags:02X}") if header.address != cls.SECT_MARK: - raise Exception(f"Invalid Section Mark: 0x{header.address:08X}") + raise SPSDKError(f"Invalid Section Mark: 0x{header.address:08X}") # Parse Certificate Block cert_block = CertBlockV2.parse(data, index) if cert_block_hmac != crypto_backend().hmac(mac, data[index : index + cert_block.raw_size]): - raise Exception("Invalid Certificate Block HMAC") + raise SPSDKError("Invalid Certificate Block HMAC") index += cert_block.raw_size cert_section_obj = cls(cert_block) counter.increment(calc_cypher_block_count(index - offset)) diff --git a/spsdk/sdp/exceptions.py b/spsdk/sdp/exceptions.py index 1af72b3d..ee99252a 100644 --- a/spsdk/sdp/exceptions.py +++ b/spsdk/sdp/exceptions.py @@ -8,8 +8,9 @@ """Exceptions used in the SDP module.""" +from spsdk import SPSDKError + from .error_codes import StatusCode -from ..exceptions import SPSDKError ######################################################################################################################## diff --git a/spsdk/sdp/interfaces/uart.py b/spsdk/sdp/interfaces/uart.py index 4e3f58ba..927929e6 100644 --- a/spsdk/sdp/interfaces/uart.py +++ b/spsdk/sdp/interfaces/uart.py @@ -18,7 +18,7 @@ from .base import Interface -logger = logging.getLogger("SDP:UART") +logger = logging.getLogger(__name__) def scan_uart(port: str = None, baudrate: int = None, timeout: int = None) -> List[Interface]: diff --git a/spsdk/sdp/interfaces/usb.py b/spsdk/sdp/interfaces/usb.py index 9bfbf90a..900c8fea 100644 --- a/spsdk/sdp/interfaces/usb.py +++ b/spsdk/sdp/interfaces/usb.py @@ -11,15 +11,16 @@ import platform from typing import Sequence, Tuple, Union -import hid +import libusbsio +from spsdk import SPSDKError from spsdk.utils.usbfilter import NXPUSBDeviceFilter, USBDeviceFilter from ..commands import CmdPacket, CmdResponse -from ..exceptions import SdpConnectionError +from ..exceptions import SdpCommandError, SdpConnectionError, SdpError from .base import Interface -logger = logging.getLogger("SDP:USB") +logger = logging.getLogger(__name__) # import os # os.environ['PYUSB_DEBUG'] = 'debug' @@ -158,13 +159,11 @@ def open(self) -> None: try: if not self.device: raise SdpConnectionError("No device available") - self.device.open_path(self.path) - self.device.set_nonblocking(False) - # self.device.read(1021, 1000) + self.device.Open(self.path) self._opened = True except Exception as error: raise SdpConnectionError( - "Unable to open device VID={self.vid} PID={self.pid} SN='{self.serial_number}'" + f"Unable to open device '{self.path}' VID={self.vid} PID={self.pid} SN='{self.serial_number}'" ) from error def close(self) -> None: @@ -177,18 +176,18 @@ def close(self) -> None: if not self.device: raise SdpConnectionError("No device available") try: - self.device.close() + self.device.Close() self._opened = False except OSError as error: raise SdpConnectionError( - "Unable to close device VID={self.vid} PID={self.pid} SN='{self.serial_number}'" + f"Unable to close device '{self.path}' VID={self.vid} PID={self.pid} SN='{self.serial_number}'" ) from error def write(self, packet: Union[CmdPacket, bytes]) -> None: """Write data on the OUT endpoint associated to the HID interfaces. :param packet: Data to send - :raises ValueError: Raises an error if packet type is incorrect + :raises SdpError: Raises an error if packet type is incorrect :raises SdpConnectionError: Raises an error if device is openned for writing :raises SdpConnectionError: Raises if device is not available :raises SdpConnectionError: Raises if writing to device fails @@ -203,7 +202,7 @@ def write(self, packet: Union[CmdPacket, bytes]) -> None: report_id, report_size, hid_ep1 = HID_REPORT["DATA"] data = packet else: - raise ValueError("Packet has to be either 'CmdPacket' or 'bytes'") + raise SdpError("Packet has to be either 'CmdPacket' or 'bytes'") if not self.device: raise SdpConnectionError("No device available") @@ -211,7 +210,7 @@ def write(self, packet: Union[CmdPacket, bytes]) -> None: while data_index < len(data): try: raw_data, data_index = self._encode_report(report_id, report_size, data, data_index) - self.device.write(raw_data) + self.device.Write(raw_data, timeout_ms=self.timeout) except Exception as e: raise SdpConnectionError(str(e)) from e @@ -229,9 +228,7 @@ def read(self, length: int = None) -> CmdResponse: if not self.device: raise SdpConnectionError("No device available") try: - raw_data = self.device.read(1024, self.timeout) - if raw_data[0] == 0x04 and platform.system() == "Linux": - raw_data += self.device.read(1024, self.timeout) + (raw_data, result) = self.device.Read(1024, timeout_ms=self.timeout) return self._decode_report(bytes(raw_data)) except Exception as e: raise SdpConnectionError(str(e)) from e @@ -244,13 +241,14 @@ def enumerate(usb_device_filter: USBDeviceFilter) -> Sequence[Interface]: :return: List of interfaces found """ devices = [] - all_hid_devices = hid.enumerate() + sio = libusbsio.usbsio() + all_hid_devices = sio.HIDAPI_Enumerate() # iterate on all devices found for dev in all_hid_devices: if usb_device_filter.compare(dev) is True: new_device = RawHid() - new_device.device = hid.device() + new_device.device = sio.HIDAPI_DeviceCreate() new_device.vid = dev["vendor_id"] new_device.pid = dev["product_id"] new_device.vendor_name = dev["manufacturer_string"] diff --git a/spsdk/sdp/sdp.py b/spsdk/sdp/sdp.py index 71732bf0..82d147f2 100644 --- a/spsdk/sdp/sdp.py +++ b/spsdk/sdp/sdp.py @@ -16,7 +16,7 @@ from .exceptions import SdpCommandError, SdpConnectionError, SdpError from .interfaces import Interface -logger = logging.getLogger("SDP") +logger = logging.getLogger(__name__) ######################################################################################################################## diff --git a/spsdk/sdp/sdps.py b/spsdk/sdp/sdps.py index 9f3aab7e..e471f2e9 100644 --- a/spsdk/sdp/sdps.py +++ b/spsdk/sdp/sdps.py @@ -10,12 +10,12 @@ import logging from struct import pack, unpack -from typing import Tuple, Mapping +from typing import Mapping, Tuple from .exceptions import SdpConnectionError from .interfaces import Interface -logger = logging.getLogger("SDPS") +logger = logging.getLogger(__name__) ROM_INFO = { "MX8QXP": {"no_cmd": True, "hid_ep1": False, "hid_pack_size": 1024}, diff --git a/spsdk/shadowregs/shadowregs.py b/spsdk/shadowregs/shadowregs.py index 8220c5b6..c105bc56 100644 --- a/spsdk/shadowregs/shadowregs.py +++ b/spsdk/shadowregs/shadowregs.py @@ -12,12 +12,11 @@ from ruamel.yaml import YAML from ruamel.yaml.comments import CommentedMap as CM -from spsdk import SPSDK_YML_INDENT, __author__, __release__, __version__ +from spsdk import SPSDK_YML_INDENT, SPSDKError, __author__, __release__, __version__ from spsdk.dat.debug_mailbox import DebugMailbox from spsdk.dat.dm_commands import StartDebugSession -from spsdk.debuggers.debug_probe import DebugProbe, DebugProbeError +from spsdk.debuggers.debug_probe import DebugProbe, SPSDKDebugProbeError from spsdk.debuggers.utils import test_ahb_access -from spsdk.exceptions import SPSDKError from spsdk.utils.misc import change_endianism, reverse_bytes_in_longs, value_to_bytes from spsdk.utils.reg_config import RegConfig from spsdk.utils.registers import Registers, RegsRegister @@ -106,10 +105,10 @@ def set_register(self, reg_name: str, data: Any) -> None: param reg: The register name. param data: The new data to be stored to shadow register. - raises DebugProbeError: The debug probe is not specified. + raises SPSDKDebugProbeError: The debug probe is not specified. """ if self.probe is None: - raise DebugProbeError("There is no debug probe.") + raise SPSDKDebugProbeError("There is no debug probe.") try: reg = self.regs.find_reg(reg_name) @@ -147,10 +146,10 @@ def get_register(self, reg_name: str) -> bytes: param reg: The register name. return: The value of requested register in bytes - raises DebugProbeError: The debug probe is not specified. + raises SPSDKDebugProbeError: The debug probe is not specified. """ if self.probe is None: - raise DebugProbeError("There is no debug probe.") + raise SPSDKDebugProbeError("There is no debug probe.") array = bytearray() try: @@ -214,7 +213,7 @@ def load_yml_config(self, file_name: str, raw: bool = False) -> None: :param file_name: The file_name (without extension) of stored configuration. :param raw: Raw input of configuration (including computed fields and anti-pole registers) - :raise SPSDKError: When the configuration file not found. + :raises SPSDKError: When the configuration file not found. """ antipole_regs = None if raw else list(self.config.get_antipole_regs(self.device).values()) computed_fields = None if raw else self.config.get_computed_fields(self.device) @@ -376,7 +375,7 @@ def enable_debug(probe: DebugProbe, ap_mem: int = 0) -> bool: except AttributeError as exc: raise SPSDKError(f"Invalid input parameters({str(exc)})") from exc - except DebugProbeError as exc: + except SPSDKDebugProbeError as exc: raise SPSDKError(f"Can't unlock device ({str(exc)})") from exc return debug_enabled diff --git a/spsdk/utils/crypto/backend_internal.py b/spsdk/utils/crypto/backend_internal.py index 4b0d409c..8a4d7eae 100644 --- a/spsdk/utils/crypto/backend_internal.py +++ b/spsdk/utils/crypto/backend_internal.py @@ -11,11 +11,11 @@ from typing import Any, Union # Used security modules -from Crypto import Random, Hash +from Crypto import Hash, Random from Crypto.Cipher import AES -from Crypto.Hash import HMAC, CMAC -from Crypto.PublicKey import RSA, ECC -from Crypto.Signature import pkcs1_15, DSS +from Crypto.Hash import CMAC, HMAC +from Crypto.PublicKey import ECC, RSA +from Crypto.Signature import DSS, pkcs1_15 from spsdk import SPSDKError @@ -54,12 +54,15 @@ def _get_algorithm(name: str, data: bytes) -> Any: :param name: Name of the algorithm (class name), case insensitive :param data: parameter for the constructor of the algorithm class :return: instance of algorithm class - :raise ValueError: if the algorithm is not found + :raises SPSDKError: If the algorithm is not found """ # algo_cls = getattr(Hash, name.upper(), None) # hack: get class object by name - algo_cls = importlib.import_module(f"Crypto.Hash.{name.upper()}") + try: + algo_cls = importlib.import_module(f"Crypto.Hash.{name.upper()}") + except ModuleNotFoundError as e: + raise SPSDKError(f"No module named 'Crypto.Hash.{name.upper()}") if algo_cls is None: - raise ValueError(f"Unsupported algorithm: Hash.{name}".format(name=name.upper())) + raise SPSDKError(f"Unsupported algorithm: Hash.{name}".format(name=name.upper())) return algo_cls.new(data) # type: ignore # pylint: disable=not-callable def cmac(self, data: bytes, key: bytes) -> bytes: # pylint: disable=no-self-use @@ -79,7 +82,7 @@ def hash(self, data: bytes, algorithm: str = "sha256") -> bytes: :param data: Input data in bytes :param algorithm: Algorithm type for HASH function :return: Hash-ed bytes - :raise ValueError: if the algorithm is not found + :raises SPSDKError: If the algorithm is not found """ return self._get_algorithm(algorithm, data).digest() @@ -90,11 +93,11 @@ def hmac(self, key: bytes, data: bytes, algorithm: str = "sha256") -> bytes: :param data: Input data in bytes format :param algorithm: Algorithm type for HASH function (sha256, sha384, sha512, ...) :return: HMAC bytes - :raise ValueError: if the algorithm is not found + :raises SPSDKError: If the algorithm is not found """ cls = getattr(Hash, algorithm.upper(), None) if cls is None: - raise ValueError() + raise SPSDKError("The algorithm is not found") hmac_obj = HMAC.new(key, data, cls) return hmac_obj.digest() @@ -105,14 +108,14 @@ def aes_key_wrap(self, kek: bytes, key_to_wrap: bytes) -> bytes: :param kek: The key-encrypting key :param key_to_wrap: Plain data :return: Wrapped key - :raise ValueError: Invalid length of kek or key_to_wrap + :raises SPSDKError: Invalid length of kek or key_to_wrap """ if len(kek) not in (16, 24, 32): - raise ValueError("The wrapping key must be a valid AES key length") + raise SPSDKError("The wrapping key must be a valid AES key length") if len(key_to_wrap) < 16: - raise ValueError("The key to wrap must be at least 16 bytes") + raise SPSDKError("The key to wrap must be at least 16 bytes") if len(key_to_wrap) % 8 != 0: - raise ValueError("The key to wrap must be a multiple of 8 bytes") + raise SPSDKError("The key to wrap must be a multiple of 8 bytes") iv = 0xA6A6A6A6A6A6A6A6 n = len(key_to_wrap) // 8 r = [b""] + [key_to_wrap[i * 8 : i * 8 + 8] for i in range(0, n)] @@ -132,14 +135,14 @@ def aes_key_unwrap(self, kek: bytes, wrapped_key: bytes) -> bytes: :param kek: The key-encrypting key :param wrapped_key: Encrypted data :return: Un-wrapped key - :raise ValueError: Invalid length of kek or key_to_wrap + :raises SPSDKError: Invalid length of kek or key_to_wrap """ if len(kek) not in (16, 24, 32): - raise ValueError("The wrapping key must be a valid AES key length") + raise SPSDKError("The wrapping key must be a valid AES key length") if len(wrapped_key) < 24: - raise ValueError("Must be at least 24 bytes") + raise SPSDKError("Must be at least 24 bytes") if len(wrapped_key) % 8 != 0: - raise ValueError("The wrapped key must be a multiple of 8 bytes") + raise SPSDKError("The wrapped key must be a multiple of 8 bytes") # default iv iv = 0xA6A6A6A6A6A6A6A6 n = len(wrapped_key) // 8 - 1 @@ -153,7 +156,7 @@ def aes_key_unwrap(self, kek: bytes, wrapped_key: bytes) -> bytes: a = unpack_from(">Q", b[:8])[0] r[i] = b[8:] if a != iv: - raise ValueError(f"Integrity Check Failed: {a:016X} (expected {iv:016X})") + raise SPSDKError(f"Integrity Check Failed: {a:016X} (expected {iv:016X})") return b"".join(r[1:]) def aes_cbc_encrypt(self, key: bytes, plain_data: bytes, iv: bytes = None) -> bytes: @@ -182,13 +185,16 @@ def aes_ctr_encrypt(self, key: bytes, plain_data: bytes, nonce: bytes) -> bytes: :param plain_data: Input data :param nonce: Nonce data with counter value :return: Encrypted data - :raise ValueError: Invalid length of key or nonce + :raises SPSDKError: Invalid length of key + :raises SPSDKError: Invalid length of nonce + :raises SPSDKError: Invalid length of plain text """ if len(key) not in (16, 24, 32): - raise ValueError("The key must be a valid AES key length") + raise SPSDKError("The key must be a valid AES key length") if len(nonce) != 16: - raise ValueError("The nonce length is not valid") - assert len(plain_data) <= len(nonce) + raise SPSDKError("The nonce length is not valid") + if len(plain_data) > len(nonce): + raise SPSDKError("The length of plain text is large than the length of nonce") aes = AES.new(key, AES.MODE_ECB) ctr = aes.encrypt(nonce) return bytes([p ^ c for p, c in zip(plain_data, ctr)]) @@ -215,7 +221,7 @@ def rsa_sign( :param data: Input data :param algorithm: Used algorithm :return: Singed data - :raise ValueError: if the algorithm is not found + :raises SPSDKError: If the algorithm is not found """ if isinstance(private_key, bytes): private_key = RSA.import_key(private_key) @@ -239,7 +245,7 @@ def rsa_verify( :param data: Input data :param algorithm: Used algorithm :return: True if signature is valid, False otherwise - :raise ValueError: if the algorithm is not found + :raises SPSDKError: If the algorithm is not found """ public_key = self.rsa_public_key(pub_key_mod, pub_key_exp) assert isinstance(public_key, RSA.RsaKey) diff --git a/spsdk/utils/crypto/backend_openssl.py b/spsdk/utils/crypto/backend_openssl.py index 9480942d..0793e75a 100644 --- a/spsdk/utils/crypto/backend_openssl.py +++ b/spsdk/utils/crypto/backend_openssl.py @@ -16,7 +16,7 @@ from cryptography.exceptions import InvalidSignature from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, hmac, keywrap, serialization -from cryptography.hazmat.primitives.asymmetric import rsa, padding, ec, utils +from cryptography.hazmat.primitives.asymmetric import ec, padding, rsa, utils from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from spsdk import SPSDKError @@ -55,11 +55,11 @@ def _get_algorithm(name: str) -> Any: :param name: of the algorithm (class name), case insensitive :return: instance of algorithm class - :raise ValueError: if algorithm not found + :raises SPSDKError: If algorithm not found """ algo_cls = getattr(hashes, name.upper(), None) # hack: get class object by name if algo_cls is None: - raise ValueError(f"Unsupported algorithm: hashes.{name}".format(name=name.upper())) + raise SPSDKError(f"Unsupported algorithm: hashes.{name}".format(name=name.upper())) return algo_cls() # pylint: disable=not-callable @@ -69,7 +69,7 @@ def hash(self, data: bytes, algorithm: str = "sha256") -> bytes: :param data: Input data in bytes :param algorithm: Algorithm type for HASH function :return: Hash-ed bytes - :raise ValueError: if algorithm not found + :raises SPSDKError: If algorithm not found """ hash_obj = hashes.Hash(self._get_algorithm(algorithm), default_backend()) hash_obj.update(data) @@ -82,7 +82,7 @@ def hmac(self, key: bytes, data: bytes, algorithm: str = "sha256") -> bytes: :param data: Input data in bytes format :param algorithm: Algorithm type for HASH function (sha256, sha384, sha512, ...) :return: HMAC bytes - :raise ValueError: if algorithm not found + :raises SPSDKError: If algorithm not found """ hmac_obj = hmac.HMAC(key, self._get_algorithm(algorithm), default_backend()) hmac_obj.update(data) @@ -142,7 +142,7 @@ def rsa_sign( :param data: Input data :param algorithm: Used algorithm :return: Signed data - :raise ValueError: if algorithm not found + :raises SPSDKError: If algorithm not found """ if isinstance(private_key, bytes): private_key = serialization.load_pem_private_key(private_key, None, default_backend()) @@ -169,7 +169,7 @@ def rsa_verify( :param data: Input data :param algorithm: Used algorithm :return: True if signature is valid, False otherwise - :raise ValueError: if algorithm not found + :raises SPSDKError: If algorithm not found """ public_key = rsa.RSAPublicNumbers(pub_key_exp, pub_key_mod).public_key(default_backend()) assert isinstance(public_key, rsa.RSAPublicKey) diff --git a/spsdk/utils/crypto/cert_blocks.py b/spsdk/utils/crypto/cert_blocks.py index 479aa02d..8a37943b 100644 --- a/spsdk/utils/crypto/cert_blocks.py +++ b/spsdk/utils/crypto/cert_blocks.py @@ -8,19 +8,18 @@ """Module for handling Certificate block.""" import re -from spsdk.sbfile.sb31.commands import BaseCmd -from struct import pack, unpack_from, calcsize +from struct import calcsize, pack, unpack_from from typing import List, Optional, Sequence, Union from Crypto.PublicKey import ECC -from spsdk import crypto -from spsdk.crypto import utils_cryptography +from spsdk import SPSDKError from spsdk.utils import misc + from .abstract import BaseClass from .backend_internal import internal_backend from .certificate import Certificate -from .common import crypto_backend, serialize_ecc_signature, ecc_public_numbers_to_bytes +from .common import crypto_backend class CertBlock(BaseClass): @@ -43,8 +42,10 @@ def __init__(self, version: str = "1.0", flags: int = 0, build_number: int = 0) :param version: Version of the certificate in format n.n :param flags: Flags for the Certificate Header :param build_number: of the certificate + :raises SPSDKError: When there is invalid version """ - assert re.match(r"[0-9]+\.[0-9]+", version) # check format of the version: N.N + if not re.match(r"[0-9]+\.[0-9]+", version): # check format of the version: N.N + raise SPSDKError("Invalid version") self.version = version self.flags = flags self.build_number = build_number @@ -154,7 +155,8 @@ def rkht(self) -> bytes: data = bytes() for rkh in self._root_key_hashes: data += rkh - assert len(data) == self.RKH_SIZE * self.RKHT_SIZE + if len(data) != self.RKH_SIZE * self.RKHT_SIZE: + raise SPSDKError("Invalid length of data") return internal_backend.hash(data) @property @@ -206,8 +208,10 @@ def alignment(self, value: int) -> None: """Setter. :param value: new alignment + :raises SPSDKError: When there is invalid alignment """ - assert value > 0 + if value <= 0: + raise SPSDKError("Invalid alignment") self._alignment = value @property @@ -228,8 +232,10 @@ def image_length(self, value: int) -> None: """Setter. :param value: new image length + :raises SPSDKError: When there is invalid image length """ - assert value > 0 + if value <= 0: + raise SPSDKError("Invalid image length") self._header.image_length = value def __init__(self, version: str = "1.0", flags: int = 0, build_number: int = 0) -> None: @@ -258,12 +264,16 @@ def set_root_key_hash(self, index: int, key_hash: Union[bytes, bytearray, Certif :param index: The index of Root Key Hash in the table :param key_hash: The Root Key Hash value (32 bytes, SHA-256); or Certificate where the hash can be created from public key + :raises SPSDKError: When there is invalid index of root key hash in the table + :raises SPSDKError: When there is invalid length of key hash """ if isinstance(key_hash, Certificate): key_hash = key_hash.public_key_hash assert isinstance(key_hash, (bytes, bytearray)) - assert 0 <= index < self.RKHT_SIZE - assert len(key_hash) == self.RKH_SIZE + if index < 0 or index >= self.RKHT_SIZE: + raise SPSDKError("Invalid index of root key hash in the table") + if len(key_hash) != self.RKH_SIZE: + raise SPSDKError("Invalid length of key hash") self._root_key_hashes[index] = bytes(key_hash) def add_certificate(self, cert: Union[bytes, Certificate]) -> None: @@ -272,23 +282,23 @@ def add_certificate(self, cert: Union[bytes, Certificate]) -> None: First call adds root certificate. Additional calls add chain certificates. :param cert: The certificate itself in DER format - :raise ValueError: if certificate cannot be added + :raises SPSDKError: If certificate cannot be added """ if isinstance(cert, bytes): cert_obj = Certificate(cert) elif isinstance(cert, Certificate): cert_obj = cert else: - raise ValueError("Invalid parameter type (cert)") + raise SPSDKError("Invalid parameter type (cert)") if cert_obj.version != "v3": - raise ValueError("Expected certificate v3 but received: " + cert_obj.version) + raise SPSDKError("Expected certificate v3 but received: " + cert_obj.version) if self._cert: # chain certificate? last_cert = self._cert[-1] # verify that it is signed by parent key if not cert_obj.verify(last_cert.public_key_modulus, last_cert.public_key_exponent): - raise ValueError("Chain certificate cannot be verified using parent public key") + raise SPSDKError("Chain certificate cannot be verified using parent public key") else: # root certificate if cert_obj.self_signed == "no": - raise ValueError("Root certificate must be self-signed") + raise SPSDKError("Root certificate must be self-signed") self._cert.append(cert_obj) self._header.cert_count += 1 self._header.cert_table_length += cert_obj.raw_size + 4 @@ -340,16 +350,16 @@ def export(self) -> bytes: """Serialize Certificate Block V2 object.""" # At least one certificate must be used if not self._cert: - raise ValueError("At least one certificate must be used") + raise SPSDKError("At least one certificate must be used") # The hast of root key certificate must be in RKHT if self.rkh_index is None: - raise ValueError("The HASH of used Root Key must be in RKHT") + raise SPSDKError("The HASH of used Root Key must be in RKHT") # CA: Using a single certificate is allowed. In this case, the sole certificate must be self-signed and must not # be a CA. If multiple certificates are used, the root must be self-signed and all but the last must be CAs. if self._cert[-1].ca: - raise ValueError("The last chain certificate must not be CA") + raise SPSDKError("The last chain certificate must not be CA") if not all(cert.ca for cert in self._cert[:-1]): - raise ValueError("All certificates except the last chain certificate must be CA") + raise SPSDKError("All certificates except the last chain certificate must be CA") # Export data = self.header.export() for cert in self._cert: @@ -358,7 +368,8 @@ def export(self) -> bytes: for key in self._root_key_hashes: data += bytes(key) data = misc.align_block(data, self.alignment) - assert len(data) == self.raw_size + if len(data) != self.raw_size: + raise SPSDKError("Invalid length of data") return data @classmethod @@ -446,12 +457,12 @@ def parse(cls, data: bytes, offset: int = 0) -> "CertificateBlockHeader": :param data: Input data as bytes :param offset: The offset of input data (default: 0) - :raises ValueError: Raise when SIZE is bigger than length of the data without offset - :raises ValueError: Raise when magic is not equal MAGIC + :raises SPSDKError: Raised when SIZE is bigger than length of the data without offset + :raises SPSDKError: Raised when magic is not equal MAGIC :return: CertificateBlockHeader """ if cls.SIZE > len(data) - offset: - raise ValueError("SIZE is bigger than length of the data without offset") + raise SPSDKError("SIZE is bigger than length of the data without offset") ( magic, minor_format_version, @@ -460,7 +471,7 @@ def parse(cls, data: bytes, offset: int = 0) -> "CertificateBlockHeader": ) = unpack_from(cls.FORMAT, data, offset) if magic != cls.MAGIC: - raise ValueError("Magic is not same!") + raise SPSDKError("Magic is not same!") return cls(format_version=f"{major_format_version}.{minor_format_version}") @@ -581,12 +592,12 @@ def __init__( self.signature_offset = calcsize("<3L") + len(self.user_data) self.signature_offset += 2 * self.isk_cert.pointQ.size_in_bytes() self.expected_size = ( - 4 - + 4 - + 4 - + 2 * self.coordinate_length - + len(self.user_data) - + 2 * self.coordinate_length + 4 # signature offset + + 4 # constraints + + 4 # flags + + 2 * self.isk_cert.pointQ.size_in_bytes() # isk public key coordinates + + len(self.user_data) # user data + + 2 * self.isk_private_key.pointQ.size_in_bytes() # isk blob signature ) def info(self) -> str: @@ -614,7 +625,8 @@ def create_isk_signature(self, key_record_data: bytes) -> None: def export(self) -> bytes: """Export ISK certificate as bytes array.""" - assert self.signature, "Signature is not set." + if not self.signature: + raise SPSDKError("Signature is not set.") data = bytes() data += pack("<3L", self.signature_offset, self.constraints, self.flags) # data += pack("<2L", self.signature_offset) @@ -661,8 +673,10 @@ def __init__( ) self.isk_certificate = None if not ca_flag: - assert isk_private_key, "ISK private key is not set." - assert isk_cert, "ISK certificate is not set." + if not isk_private_key: + raise SPSDKError("ISK private key is not set.") + if not isk_cert: + raise SPSDKError("ISK certificate is not set.") self.isk_certificate = IskCertificate( constraints=constraints, isk_private_key=isk_private_key, diff --git a/spsdk/utils/crypto/certificate.py b/spsdk/utils/crypto/certificate.py index 67d33f8a..52f319e0 100644 --- a/spsdk/utils/crypto/certificate.py +++ b/spsdk/utils/crypto/certificate.py @@ -11,7 +11,7 @@ from typing import Dict, Set from asn1crypto import x509 -from oscrypto.asymmetric import rsa_pkcs1v15_verify, load_public_key +from oscrypto.asymmetric import load_public_key, rsa_pkcs1v15_verify from oscrypto.errors import SignatureError from .backend_internal import internal_backend diff --git a/spsdk/utils/crypto/common.py b/spsdk/utils/crypto/common.py index 41ca975c..464df7ae 100644 --- a/spsdk/utils/crypto/common.py +++ b/spsdk/utils/crypto/common.py @@ -14,6 +14,8 @@ from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey from cryptography.x509 import Certificate +from spsdk import SPSDKError + from .abstract import BackendClass from .backend_openssl import openssl_backend @@ -42,9 +44,11 @@ def __init__( :param nonce: last for bytes are used as initial value for counter :param ctr_value: counter initial value; it is added to counter value retrieved from nonce :param ctr_byteorder_encoding: way how the counter is encoded into output value: either 'little' or 'big' + :raises SPSDKError: When invalid byteorder is provided """ assert isinstance(nonce, bytes) and len(nonce) == 16 - assert ctr_byteorder_encoding in ["little", "big"] + if ctr_byteorder_encoding not in ["little", "big"]: + raise SPSDKError("Wrong byte order") self._nonce = nonce[:-4] self._ctr_byteorder_encoding = ctr_byteorder_encoding self._ctr = int.from_bytes(nonce[-4:], ctr_byteorder_encoding) @@ -75,8 +79,10 @@ def swap16(x: int) -> int: :param x: Original number :return: Number with swapped bytes + :raises SPSDKError: When inncorrect number to be swapped is provided """ - assert 0 <= x <= 0xFFFF + if x < 0 or x > 0xFFFF: + raise SPSDKError("Incorrect number to be swapped") return ((x << 8) & 0xFF00) | ((x >> 8) & 0x00FF) @@ -86,11 +92,13 @@ def pack_timestamp(value: datetime) -> int: :param value: datetime to be converted :return: number of milliseconds since 1.1.2000 00:00:00; 64-bit integer + :raises SPSDKError: When there is incorrect result of convertion """ assert isinstance(value, datetime) start = datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=timezone.utc).timestamp() result = int((value.timestamp() - start) * 1000000) - assert 0 <= result <= 0xFFFFFFFFFFFFFFFF + if result < 0 or result > 0xFFFFFFFFFFFFFFFF: + raise SPSDKError("Incorrect result of convertion") return result @@ -100,9 +108,11 @@ def unpack_timestamp(value: int) -> datetime: :param value: number of milliseconds since 1.1.2000 00:00:00; 64-bit integer :return: corresponding datetime + :raises SPSDKError: When there is incorrect result of convertion """ assert isinstance(value, int) - assert 0 <= value <= 0xFFFFFFFFFFFFFFFF + if value < 0 or value > 0xFFFFFFFFFFFFFFFF: + raise SPSDKError("Incorrect result of convertion") start = int(datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=timezone.utc).timestamp() * 1000000) return datetime.fromtimestamp((start + value) / 1000000) diff --git a/spsdk/utils/crypto/otfad.py b/spsdk/utils/crypto/otfad.py index 8ea63149..01125127 100644 --- a/spsdk/utils/crypto/otfad.py +++ b/spsdk/utils/crypto/otfad.py @@ -12,6 +12,8 @@ from Crypto.Cipher import AES +from spsdk import SPSDKError + from ..misc import align_block from . import Counter, crypto_backend @@ -35,8 +37,11 @@ class KeyBlob: } keyblob_t; """ + _START_ADDR_MASK = 0x400 - 1 # Region addresses are modulo 1024 - _ADDR_MASK = 0x400 - 1 + # The address ends with RO, ADE, VLD bits. From this perspective, only + # bits [9:3] must be set to 1. The rest is configurable. + _END_ADDR_MASK = 0x3F8 # Key flags mask: RO, ADE, VLD _KEY_FLAG_MASK = 0x07 @@ -85,23 +90,27 @@ def __init__( :param counter_iv: optional counter init value for AES; None to use random value :param zero_fill: optional value for zero_fill (for testing only); None to use random value (recommended) :param crc: optional value for unused CRC fill (for testing only); None to use random value (recommended) - :raises ValueError: Start or end address are not aligned + :raises SPSDKError: Start or end address are not aligned + :raises SPSDKError: When there is invalid key + :raises SPSDKError: When there is invalid start/end address + :raises SPSDKError: When key_flags exceeds mask """ if key is None: key = crypto_backend().random_bytes(self.KEY_SIZE) if counter_iv is None: counter_iv = crypto_backend().random_bytes(self.CTR_SIZE) - assert (len(key) == self.KEY_SIZE) and (len(counter_iv) == self.CTR_SIZE) - assert 0 <= start_addr < end_addr <= 0xFFFFFFFF - assert ( - key_flags & ~self._KEY_FLAG_MASK == 0 - ), f"key_flags exceeds mask {hex(self._KEY_FLAG_MASK)}" - if (start_addr & self._ADDR_MASK) != 0: - raise ValueError( - f"Start address must be aligned to {hex(self._ADDR_MASK + 1)} boundary" + if (len(key) != self.KEY_SIZE) and (len(counter_iv) != self.CTR_SIZE): + raise SPSDKError("Invalid key") + if start_addr < 0 or start_addr >= end_addr or end_addr > 0xFFFFFFFF: + raise SPSDKError("Invalid start/end address") + if key_flags & ~self._KEY_FLAG_MASK != 0: + raise SPSDKError(f"key_flags exceeds mask {hex(self._KEY_FLAG_MASK)}") + if (start_addr & self._START_ADDR_MASK) != 0: + raise SPSDKError( + f"Start address must be aligned to {hex(self._START_ADDR_MASK + 1)} boundary" ) - if (end_addr & self._ADDR_MASK) != self._ADDR_MASK: - raise ValueError(f"End address must be aligned to {hex(self._ADDR_MASK)} boundary") + if (end_addr & self._END_ADDR_MASK) != self._END_ADDR_MASK: + raise SPSDKError(f"End address must be aligned to {hex(self._END_ADDR_MASK)} boundary") self.key = key self.ctr_init_vector = counter_iv self.start_addr = start_addr @@ -123,6 +132,9 @@ def plain_data(self) -> bytes: """Plain data for selected key range. :return: key blob exported into binary form (serialization) + :raises SPSDKError: Invalid value of zero fill parameter + :raises SPSDKError: Invalid value crc + :raises SPSDKError: Invalid length binary data """ result = bytes() result += self.key @@ -132,19 +144,22 @@ def plain_data(self) -> bytes: result += pack(" bytes :param kek: key to encode; 16 bytes long :param iv: counter initialization vector; 8 bytes; optional, OTFAD uses empty init value :return: Serialized key blob - :raise ValueError: if any parameter is not valid + :raises SPSDKError: If any parameter is not valid + :raises SPSDKError: If length of kek is not valid + :raises SPSDKError: If length of data is not valid """ if isinstance(kek, str): kek = bytes.fromhex(kek) - assert len(kek) == 16 - assert len(iv) == self._EXPORT_CTR_IV_SIZE + if len(kek) != 16: + raise SPSDKError("Invalid length of kek") + if len(iv) != self._EXPORT_CTR_IV_SIZE: + raise SPSDKError("Invalid length of initialization vector") n = self._EXPORT_NBLOCKS_5 plaintext = self.plain_data() # input data to be encrypted - assert len(plaintext) >= n * 8 + if len(plaintext) < n * 8: + raise SPSDKError("Invalid length of data to be encrypted") # step 1: initialize the byte - sized data variables # set a = iv @@ -199,13 +219,18 @@ def export(self, kek: Union[bytes, str], iv: bytes = bytes([0xA6] * 8)) -> bytes ) # align to 64 bytes (0 padding) def _get_ctr_nonce(self) -> bytes: - """Get the counter initial value for image encryption.""" + """Get the counter initial value for image encryption. + + :return: counter bytes + :raises SPSDKError: If length of counter is not valid + """ # CTRn_x[127-0] = {CTR_W0_x[C0...C3], // 32 bits of pre-programmed CTR # CTR_W1_x[C4...C7], // another 32 bits of CTR # CTR_W0_x[C0...C3] ^ CTR_W1_x[C4...C7], // exclusive-OR of CTR values # systemAddress[31-4], 0000b // 0-modulo-16 system address */ - assert len(self.ctr_init_vector) == 8 + if len(self.ctr_init_vector) != 8: + raise SPSDKError("Invalid length of counter init") result = bytearray(16) result[:4] = self.ctr_init_vector[:4] @@ -242,15 +267,17 @@ def encrypt_image(self, base_address: int, data: bytes, byte_swap: bool) -> byte :param byte_swap: this probably depends on the flash device, how bytes are organized there True should be used for FLASH on EVK RT6xx; False for FLASH on EVK RT5xx :return: encrypted data - :raise ValueError: if start_addr or end_addr does not match with base_address (+ data length) + :raises SPSDKError: If start_addr or end_addr does not match with base_address (+ data length) + :raises SPSDKError: If start address is not valid """ - assert base_address % 16 == 0 # Start address has to be 16 byte aligned + if base_address % 16 != 0: + raise SPSDKError("Invalid start address") # Start address has to be 16 byte aligned data = align_block(data, self._IMAGE_ALIGNMENT) # align data length data_len = len(data) # check start and end addresses if not self.matches_range(base_address, base_address + data_len - 1): - raise ValueError( + raise SPSDKError( f"Image address range is not within key blob: {hex(self.start_addr)}-{hex(self.end_addr)}" ) @@ -279,7 +306,8 @@ def encrypt_image(self, base_address: int, data: bytes, byte_swap: bool) -> byte # update counter for encryption counter.increment(16) - assert len(result) == data_len + if len(result) != data_len: + raise SPSDKError("Invalid length of encrypted data") return bytes(result) @@ -308,14 +336,14 @@ def encrypt_image(self, image: bytes, base_addr: int, byte_swap: bool) -> bytes: :param byte_swap: this probably depends on the flash device, how bytes are organized there True should be used for FLASH on EVK RT6xx; False for FLASH on EVK RT5xx :return: encrypted image - :raise ValueError: if address range does not match to any key blob + :raises SPSDKError: If address range does not match to any key blob """ image_end = base_addr + len(image) - 1 for key_blob in self._key_blobs: if key_blob.matches_range(base_addr, image_end): return key_blob.encrypt_image(base_addr, image, byte_swap) - raise ValueError("The image address range does not match to key blob") + raise SPSDKError("The image address range does not match to key blob") def encrypt_key_blobs(self, kek: Union[bytes, str]) -> bytes: """Encrypt key blobs with specified key. diff --git a/spsdk/utils/devicedescription.py b/spsdk/utils/devicedescription.py index fcc92582..efe6ca20 100644 --- a/spsdk/utils/devicedescription.py +++ b/spsdk/utils/devicedescription.py @@ -148,17 +148,17 @@ class intention is to be just a simple container. But to help the class def convert_usb_path(hid_api_usb_path: bytes) -> str: - """Converts the HID API path into string, which can be observed from OS. + """Converts the Libusbsio/HID_API path into string, which can be observed from OS. DESIGN REMARK: this function is not part of the USBLogicalDevice, as the class intention is to be just a simple container. But to help the class to get the required inputs, this helper method has been provided. Additionally, - this method relies on the fact that the provided path comes from the HID API. + this method relies on the fact that the provided path comes from the Libusbsio/HID_API. This method will most probably fail or provide improper results in case path from different USB API is provided. - :hid_api_usb_path: USB device path from HID API - :return: HID API path converted for given platform + :hid_api_usb_path: USB device path from Libusbsio/HID_API + :return: Libusbsio/HID_API path converted for given platform """ if platform.system() == "Windows": device_manager_path = hid_api_usb_path.decode("utf-8").upper() @@ -170,7 +170,7 @@ class intention is to be just a simple container. But to help the class return device_manager_path if platform.system() == "Linux": - # we expect the path in form of #, HID API returns + # we expect the path in form of #, Libusbsio/HID_API returns # :: linux_path = hid_api_usb_path.decode("utf-8") linux_path_parts = linux_path.split(":") diff --git a/spsdk/utils/easy_enum.py b/spsdk/utils/easy_enum.py index a0748ef3..27da8e06 100644 --- a/spsdk/utils/easy_enum.py +++ b/spsdk/utils/easy_enum.py @@ -25,6 +25,8 @@ from typing import Sequence, Union +from spsdk import SPSDKError + EnumKeyType = Union[str, int] @@ -34,6 +36,10 @@ class MetaEnum(type): def __new__(mcs, name, bases, attrs): cls = super().__new__(mcs, name, bases, attrs) cls._items_ = list() + + for base in bases: + cls._items_ += base._items_ + for attr, value in attrs.items(): if attr in set(dir(type(name, (object,), {}))) or ( attr.startswith("_") and attr.endswith("_") @@ -155,10 +161,10 @@ def from_int(cls, value: int) -> "Enum": - formally converts value into enumeration (for type hints), even the returned value is same as input :param value: integer value to be converted :return: corresponding enumeration - :raises ValueError: if specified value does not match any enumeration value + :raises SPSDKError: If specified value does not match any enumeration value """ if value not in cls.tags(): - raise ValueError( + raise SPSDKError( f"the following integer value is not defined within the enumeration: {str(value)}" ) diff --git a/spsdk/utils/easy_enum.pyi b/spsdk/utils/easy_enum.pyi index cbddb5a8..86e559bd 100644 --- a/spsdk/utils/easy_enum.pyi +++ b/spsdk/utils/easy_enum.pyi @@ -1,8 +1,8 @@ -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # This is stub for Enum class implemented in easy_enum, to provide info about differences implemented in Meta class -from typing import Iterator, Optional, Union, Sequence, Tuple, Type, TypeVar from enum import IntEnum +from typing import Iterator, Optional, Sequence, Tuple, Type, TypeVar, Union # type that represents a key for enum: either integer or string (name) EnumKeyType = Union[str, int] diff --git a/spsdk/utils/exceptions.py b/spsdk/utils/exceptions.py index 4665e7dd..6b49e590 100644 --- a/spsdk/utils/exceptions.py +++ b/spsdk/utils/exceptions.py @@ -27,3 +27,7 @@ class SPSDKRegsErrorBitfieldNotFound(SPSDKRegsError): class SPSDKRegsErrorEnumNotFound(SPSDKRegsError): """Enum has not been found.""" + + +class SPSDKTimeoutError(SPSDKError): + """SPSDK Timeout.""" diff --git a/spsdk/utils/misc.py b/spsdk/utils/misc.py index 2ac326ff..1c0e7040 100644 --- a/spsdk/utils/misc.py +++ b/spsdk/utils/misc.py @@ -8,8 +8,13 @@ import contextlib import os import re -from typing import Callable, Iterable, Iterator, Optional, TypeVar, List, Union +import time from math import ceil +from typing import Callable, Iterable, Iterator, List, Optional, TypeVar, Union + +from spsdk import SPSDKError +from spsdk.exceptions import SPSDKValueError +from spsdk.utils.exceptions import SPSDKTimeoutError # for generics T = TypeVar("T") # pylint: disable=invalid-name @@ -21,8 +26,10 @@ def align(number: int, alignment: int = 4) -> int: :param number: input to be aligned :param alignment: the boundary to align; typical value is power of 2 :return: aligned number; result is always >= size (e.g. aligned up) + :raises SPSDKError: When there is wrong alignment """ - assert alignment > 0 and number >= 0 + if alignment <= 0 or number < 0: + raise SPSDKError("Wrong alignment") return (number + (alignment - 1)) // alignment * alignment @@ -33,10 +40,14 @@ def align_block(data: bytes, alignment: int = 4, padding: int = 0) -> bytes: :param alignment: boundary alignment (typically 2, 4, 16, 64 or 256 boundary) :param padding: byte to be added, use -1 to fill with random data :return: aligned block + :raises SPSDKError: When there is wrong alignment """ assert isinstance(data, bytes) - assert alignment > 0 - assert -1 <= padding <= 255 + + if alignment < 0: + raise SPSDKError("Wrong alignment") + if padding < -1 or padding > 255: + raise SPSDKError("Wrong padding") current_size = len(data) num_padding = align(current_size, alignment) - current_size if not num_padding: @@ -61,9 +72,11 @@ def extend_block(data: bytes, length: int, padding: int = 0) -> bytes: :param length: requested block length; the value must be >= current block length :param padding: 8-bit value value to be used as a padding :return: block extended with padding + :raises SPSDKError: When the length is incorrect """ current_len = len(data) - assert length >= current_len + if length < current_len: + raise SPSDKError("Incorrect length") num_padding = length - current_len if not num_padding: return data @@ -188,9 +201,11 @@ def append(self, line: str) -> None: """Appends the line to the log. :param line: text to be added + :raises SPSDKError: When there is nothing to append """ if self.enabled: - assert self._lines is not None + if self._lines is None: + raise SPSDKError("There is nothing to append") self._lines.append(line) def append_section(self, name: str) -> None: @@ -222,8 +237,10 @@ def append_binary_data(self, data_name: str, data: bytes) -> None: :param data_name: the name :param data: binary data (up to 8 bytes) + :raises SPSDKError: When the data has incorrect length """ - assert len(data) <= 16 + if len(data) > 16: + raise SPSDKError("Incorrect data length") self.append(data_name + "=" + data.hex()) @property @@ -279,7 +296,7 @@ def value_to_int(value: Union[bytes, bytearray, int, str], default: int = None) :param value: Input value. :param default: Default Value in case of invalid input. :return: Value in Integer. - :raise TypeError: Unsupported input type. + :raises SPSDKError: Unsupported input type. """ if isinstance(value, int): return value @@ -307,7 +324,7 @@ def value_to_int(value: Union[bytes, bytearray, int, str], default: int = None) if default is not None: return default - raise TypeError(f"Invalid input number type({type(value)}) with value ({value})") + raise SPSDKError(f"Invalid input number type({type(value)}) with value ({value})") def value_to_bytes(value: Union[bytes, bytearray, int, str], align_to_2n: bool = True) -> bytes: @@ -332,7 +349,7 @@ def value_to_bool(value: Union[bool, int, str]) -> bool: :param value: Input value. :return: Boolean value. - :raise TypeError: Unsupported input type. + :raises SPSDKError: Unsupported input type. """ if isinstance(value, bool): return value @@ -343,7 +360,7 @@ def value_to_bool(value: Union[bool, int, str]) -> bool: if isinstance(value, str): return value in ("True", "T", "1") - raise TypeError(f"Invalid input Boolean type({type(value)}) with value ({value})") + raise SPSDKError(f"Invalid input Boolean type({type(value)}) with value ({value})") def reverse_bytes_in_longs(arr: bytes) -> bytes: @@ -351,11 +368,11 @@ def reverse_bytes_in_longs(arr: bytes) -> bytes: :param arr: Input array. :return: New array with reversed bytes. - :raises ValueError: Raises when invalid value is in input. + :raises SPSDKError: Raises when invalid value is in input. """ arr_len = len(arr) if arr_len % 4 != 0: - raise ValueError("The input array is not in modulo 4!") + raise SPSDKError("The input array is not in modulo 4!") result = bytearray() @@ -371,7 +388,7 @@ def change_endianism(bin_data: bytes) -> bytes: :param bin_data: input binary array. :return: Converted array (practically little to big endianism). - :raises ValueError: Invalid value on input. + :raises SPSDKError: Invalid value on input. """ data = bytearray(bin_data) length = len(data) @@ -384,6 +401,101 @@ def change_endianism(bin_data: bytes) -> bytes: # The length of 24 bits is not supported yet if length == 3: - raise ValueError("Unsupported length (3) for change endianism.") + raise SPSDKError("Unsupported length (3) for change endianism.") return reverse_bytes_in_longs(data) + + +class Timeout: + """Simple timout handle class.""" + + UNITS = { + "s": 1000000, + "ms": 1000, + "us": 1, + } + + def __init__(self, timeout: int, units: str = "s") -> None: + """Simple timeout class constructor. + + :param timeout: Timeout value. + :param units: Timeout units (MUST be from the UNITS list) + :raises SPSDKValueError: Invalid input value. + """ + if units not in self.UNITS: + raise SPSDKValueError("Units are not in supported units.") + self.enabled = timeout != 0 + self.timeout_us = timeout * self.UNITS[units] + self.start_time_us = self._get_current_time_us() + self.end_time = self.start_time_us + self.timeout_us + self.units = units + + @staticmethod + def _get_current_time_us() -> int: + """Returns current system time in microseconds. + + :return: Current time in microseconds + """ + return ceil(time.time() * 1_000_000) + + def _convert_to_units(self, time_us: int) -> int: + """Converts time in us into used units. + + :param time_us: Time in micro seconds. + :return: Time in user units. + """ + return time_us // self.UNITS[self.units] + + def get_consumed_time(self) -> int: + """Returns consumed time since start of timeouted operation. + + :return: Consumed time in units as the class was constructed + """ + return self._convert_to_units(self._get_current_time_us() - self.start_time_us) + + def get_consumed_time_ms(self) -> int: + """Returns consumed time since start of timeouted operation in milliseconds. + + :return: Consumed time in milliseconds + """ + return (self._get_current_time_us() - self.start_time_us) // 1000 + + def get_rest_time(self, raise_exc: bool = False) -> int: + """Returns rest time to timeout overflow. + + :param raise_exc: If set, the function raise SPSDKTimeoutError in case of overflow. + :return: Rest time in units as the class was constructed + :raises SPSDKTimeoutError: In case of overflow + """ + if self.enabled and self._get_current_time_us() > self.end_time and raise_exc: + raise SPSDKTimeoutError("Timeout of operation.") + + return ( + self._convert_to_units(self.end_time - self._get_current_time_us()) + if self.enabled + else 0 + ) + + def get_rest_time_ms(self, raise_exc: bool = False) -> int: + """Returns rest time to timeout overflow. + + :param raise_exc: If set, the function raise SPSDKTimeoutError in case of overflow. + :return: Rest time in milliseconds + :raises SPSDKTimeoutError: In case of overflow + """ + if self.enabled and self._get_current_time_us() > self.end_time and raise_exc: + raise SPSDKTimeoutError("Timeout of operation.") + + return ((self.end_time - self._get_current_time_us()) // 1000) if self.enabled else 0 + + def overflow(self, raise_exc: bool = False) -> bool: + """Check the the timer has been overflowed. + + :param raise_exc: If set, the function raise SPSDKTimeoutError in case of overflow. + :return: True if timeout overflowed, False otherwise. + :raises SPSDKTimeoutError: In case of overflow + """ + overflow = self.enabled and self._get_current_time_us() > self.end_time + if overflow and raise_exc: + raise SPSDKTimeoutError("Timeout of operation.") + return overflow diff --git a/spsdk/utils/nxpdevscan.py b/spsdk/utils/nxpdevscan.py index e3d393bb..0c1b7693 100644 --- a/spsdk/utils/nxpdevscan.py +++ b/spsdk/utils/nxpdevscan.py @@ -9,14 +9,14 @@ import logging -from spsdk.sdp.exceptions import SdpConnectionError -from typing import Dict, Sequence, Type +from typing import Sequence -import hid +import libusbsio from serial.tools.list_ports import comports from spsdk.mboot.interfaces.uart import scan_uart as mb_scan_uart from spsdk.sdp import SDP +from spsdk.sdp.exceptions import SdpConnectionError from spsdk.sdp.interfaces.uart import Uart as SDP_Uart from .devicedescription import ( @@ -32,6 +32,8 @@ 0x15A2, ] +logger = logging.getLogger(__name__) + def search_nxp_usb_devices(extend_vid_list: list = None) -> Sequence[DeviceDescription]: """Searches all NXP USB devices based on their Vendor ID. @@ -39,7 +41,8 @@ def search_nxp_usb_devices(extend_vid_list: list = None) -> Sequence[DeviceDescr :extend_vid_list: list of VIDs, to extend the default NXP VID list (int) :return: list of dicts corresponding to NXP devices """ - all_usb_devices = hid.enumerate() + sio = libusbsio.usbsio() + all_usb_devices = sio.HIDAPI_Enumerate() nxp_usb_devices = [] search_vids = NXP_USB_DEVICE_VIDS @@ -49,13 +52,13 @@ def search_nxp_usb_devices(extend_vid_list: list = None) -> Sequence[DeviceDescr for usb_device in all_usb_devices: for nxp_vid in search_vids: - if nxp_vid == usb_device.get("vendor_id"): + if nxp_vid == usb_device["vendor_id"]: # We found our device, let's create container for it - vid = usb_device.get("vendor_id") - pid = usb_device.get("product_id") - path = convert_usb_path(usb_device.get("path")) - product_string = usb_device.get("product_string") - manufacturer_string = usb_device.get("manufacturer_string") + vid = usb_device["vendor_id"] + pid = usb_device["product_id"] + path = convert_usb_path(usb_device["path"]) + product_string = usb_device["product_string"] + manufacturer_string = usb_device["manufacturer_string"] name = ", ".join(get_usb_device_name(vid, pid, None)) usb_dev = USBDeviceDescription( vid, pid, path, product_string, manufacturer_string, name @@ -96,8 +99,10 @@ def search_nxp_uart_devices() -> Sequence[DeviceDescription]: uart_dev = UartDeviceDescription(name=port, dev_type="SDP device") retval.append(uart_dev) except SdpConnectionError as e: - logging.debug(f"Exception {type(e).__name__} occurred while reading status via SDP. \ -Arguments: {e.args}") + logger.debug( + f"Exception {type(e).__name__} occurred while reading status via SDP. \ +Arguments: {e.args}" + ) pass return retval diff --git a/spsdk/utils/reg_config.py b/spsdk/utils/reg_config.py index ac5c55ac..bc2162ab 100644 --- a/spsdk/utils/reg_config.py +++ b/spsdk/utils/reg_config.py @@ -11,6 +11,8 @@ import os from typing import Any, Dict, List, Optional +from spsdk import SPSDKError + logger = logging.getLogger(__name__) @@ -164,9 +166,11 @@ def get_seal_start_address(self, device: str = None) -> Optional[str]: :param device: The device name, if not specified, the general value is used. :return: The seal start register name. + :raises SPSDKError: When seal start address has invalid name """ val = self.get_value("seal_start", device) - assert val is None or isinstance(val, str) + if not (val is None or isinstance(val, str)): + raise SPSDKError("Invalid seal start address name") return val def get_seal_count(self, device: str = None) -> Optional[int]: @@ -174,9 +178,11 @@ def get_seal_count(self, device: str = None) -> Optional[int]: :param device: The device name, if not specified, the general value is used. :return: The seal count. + :raises SPSDKError: When there is invalid seal count """ val = self.get_value("seal_count", device) - assert val is None or isinstance(val, int) + if not (val is None or isinstance(val, int)): + raise SPSDKError("Invalid seal count") return val def get_value(self, key: str, device: str = None, default: Any = None) -> Any: diff --git a/spsdk/utils/registers.py b/spsdk/utils/registers.py index 1d9c3987..75337f22 100644 --- a/spsdk/utils/registers.py +++ b/spsdk/utils/registers.py @@ -15,7 +15,7 @@ from jinja2 import Environment, FileSystemLoader from ruamel.yaml.comments import CommentedMap as CM -from spsdk import SPSDK_YML_INDENT, utils +from spsdk import SPSDK_YML_INDENT, SPSDKError, utils from spsdk.utils.exceptions import ( SPSDKRegsError, SPSDKRegsErrorBitfieldNotFound, @@ -45,7 +45,7 @@ def __init__(self, name: str, value: Any, description: str, max_width: int = 0) self.name = name or "N/A" try: self.value = value_to_bytes(value) - except (TypeError, ValueError): + except (TypeError, ValueError, SPSDKError): self.value = b"" self.description = description or "N/A" self.max_width = max_width @@ -60,13 +60,13 @@ def from_xml_element(cls, xml_element: ET.Element, maxwidth: int = 0) -> "RegsEn :raises SPSDKRegsError: Error during enum XML parsing. """ name = xml_element.attrib["name"] if "name" in xml_element.attrib else "N/A" - if not "value" in xml_element.attrib: + if "value" not in xml_element.attrib: raise SPSDKRegsError(f"Missing Enum Value Key for {name}.") raw_val = xml_element.attrib["value"] try: value = value_to_bytes(raw_val) - except (TypeError, ValueError) as exc: + except (TypeError, ValueError, SPSDKError) as exc: raise SPSDKRegsError(f"Invalid Enum Value: {raw_val}") from exc descr = xml_element.attrib["description"] if "description" in xml_element.attrib else "N/A" @@ -221,11 +221,11 @@ def set_value(self, new_val: Any, raw: bool = False) -> None: :param new_val: New value of bitfield. :param raw: If set, no automatic modification of value is applied. - :raise ValueError: The input value is out of range. + :raises SPSDKError: The input value is out of range. """ new_val_int = value_to_int(new_val) if new_val_int > 1 << self.width: - raise ValueError("The input value is out of bitfield range") + raise SPSDKError("The input value is out of bitfield range") reg_val = int.from_bytes(self.parent.get_value(), "big") mask = ((1 << self.width) - 1) << self.offset reg_val = reg_val & ~mask @@ -540,10 +540,10 @@ def set_value(self, val: Any, raw: bool = False) -> None: :param val: The new value to set. :param raw: Do not use any modification hooks. + :raises SPSDKError: When invalid values is loaded into register """ try: value = value_to_bytes(val) - # TODO check if this works with shorter arrays for example with ROTKH value = value.rjust(self.width // 8, b"\x00") if not raw and len(self._set_value_hooks) > 0: for hook in self._set_value_hooks: @@ -556,8 +556,9 @@ def set_value(self, val: Any, raw: bool = False) -> None: for index, sub_reg in enumerate(self.sub_regs): sub_reg.set_value(value[index * 4 : index * 4 + 4]) - except TypeError: + except SPSDKError: logger.error(f"Loaded invalid value {str(val)}") + raise SPSDKError(f"Loaded invalid value {str(val)}") def reset_value(self, raw: bool = False) -> None: """Reset the value of register. @@ -733,11 +734,11 @@ def add_register(self, reg: RegsRegister) -> None: """Adds register into register list. :param reg: Register to add to the class. - :raise TypeError: Invalid type has been provided. + :raises SPSDKError: Invalid type has been provided. :raise SPSDKRegsError: Cannot add register with same name """ if not isinstance(reg, RegsRegister): - raise TypeError("The 'reg' has invalid type.") + raise SPSDKError("The 'reg' has invalid type.") if reg.name in self.get_reg_names(): raise SPSDKRegsError(f"Cannot add register with same name: {reg.name}.") @@ -748,10 +749,10 @@ def remove_register(self, reg: RegsRegister) -> None: """Remove register from register list by its instance reference. :reg: Instance of register that should be removed. - :raise TypeError: Invalid type has been provided. + :raises SPSDKError: Invalid type has been provided. """ if not isinstance(reg, RegsRegister): - raise TypeError("The 'reg' has invalid type.") + raise SPSDKError("The 'reg' has invalid type.") self._registers.remove(reg) diff --git a/spsdk/utils/serial_proxy.py b/spsdk/utils/serial_proxy.py index ddce0d99..6c7c9b90 100644 --- a/spsdk/utils/serial_proxy.py +++ b/spsdk/utils/serial_proxy.py @@ -12,7 +12,7 @@ # pylint: disable=unused-import # Type is necessary for Mypy from typing import Dict, Type -logger = logging.getLogger("SerialProxy") +logger = logging.getLogger(__name__) class SerialProxy: diff --git a/spsdk/utils/usbfilter.py b/spsdk/utils/usbfilter.py index c4ff7c0f..10735c55 100644 --- a/spsdk/utils/usbfilter.py +++ b/spsdk/utils/usbfilter.py @@ -14,9 +14,9 @@ class USBDeviceFilter: """Generic USB Device Filtering class. - Create a filtering instance. This instance holds the USB ID you are insterested + Create a filtering instance. This instance holds the USB ID you are interested in during USB HID device search and allows you to compare, whether - provided USB HID object is the one you are insterested in. + provided USB HID object is the one you are interested in. The allowed format of `usb_id` string is following: vid or pid - vendor ID or product ID. String holding hex or dec number. @@ -90,14 +90,14 @@ def compare(self, usb_device_object: Any) -> bool: The provided USB ID during initialization may be VID or PID, VID/PID pair, or a path. See private methods for details. - :param usb_device_object: hidapi USB HID device object + :param usb_device_object: Libusbsio/HID_API device object (dictionary) :return: True on match, False otherwise """ vendor_id = usb_device_object["vendor_id"] product_id = usb_device_object["product_id"] - # the hidapi holds the path as bytes, so we convert it to string + # the Libusbsio/HID_API holds the path as bytes, so we convert it to string usb_path = usb_device_object["path"].decode("utf-8") if platform.system() == "Windows": @@ -113,10 +113,12 @@ def compare(self, usb_device_object: Any) -> bool: if platform.system() == "Linux": # The user input is expected in form of #. So we - # convert the path returned by HID API into this form so we can - # compare it + # convert the path returned by Libusbsio/HID_API into this form so we can + # compare it. Alternatively, the input is the real device path, + # like '/dev/hidraw0' - in this case, just leave it as it is. nums = usb_path.split(":") - usb_path = str.format("{}#{}", int(nums[0], 16), int(nums[1], 16)) + if len(nums) >= 2: + usb_path = str.format("{}#{}", int(nums[0], 16), int(nums[1], 16)) # Determine, whether given device matches one of the expected criterion if self.usb_id is None: @@ -221,7 +223,7 @@ def compare(self, usb_device_object: Any) -> bool: Extends the comparison by USB names - dictionary of device name and corresponding VID/PID. - :param usb_device_object: hidapi USB HID device object + :param usb_device_object: lpcusbsio USB HID device object :return: True on match, False otherwise """ diff --git a/tests/apps/data/certgen_config.yaml b/tests/apps/data/certgen_config.yaml new file mode 100644 index 00000000..fa619afb --- /dev/null +++ b/tests/apps/data/certgen_config.yaml @@ -0,0 +1,7 @@ +issuer: + COMMON_NAME: ONE + +subject: + COMMON_NAME: TWO +# this is comment +duration: 1234 diff --git a/tests/apps/data/invalid_file.json b/tests/apps/data/invalid_file.json new file mode 100644 index 00000000..a1401bd0 --- /dev/null +++ b/tests/apps/data/invalid_file.json @@ -0,0 +1,16 @@ +{ + "issuer": { + // this should cause an exception + "COMMON_NAME": ONE, + }, + "subject": { + "COMMON_NAME": "TWO", + }, + "serial_number": 777, + "extensions": { + "BASIC_CONSTRAINTS": { + "ca": true, + "path_length": 5 + } + } +} diff --git a/tests/apps/data/test_config.json b/tests/apps/data/test_config.json new file mode 100644 index 00000000..dd2e38d6 --- /dev/null +++ b/tests/apps/data/test_config.json @@ -0,0 +1,16 @@ +{ + "issuer": { + // this should cause an exception + "COMMON_NAME": "ONE", + }, + "subject": { + "COMMON_NAME": "TWO", + }, + "serial_number": 777, + "extensions": { + "BASIC_CONSTRAINTS": { + "ca": true, + "path_length": 5 + } + } +} diff --git a/tests/apps/data/zeros.bin b/tests/apps/data/zeros.bin new file mode 100644 index 0000000000000000000000000000000000000000..5366ed7f382c9d8333e485ebbf8edf46756ca2c9 GIT binary patch literal 120 LcmZQz7#IKm0C)fa literal 0 HcmV?d00001 diff --git a/tests/apps/test_help.py b/tests/apps/test_help.py index b115f436..455e6911 100644 --- a/tests/apps/test_help.py +++ b/tests/apps/test_help.py @@ -12,18 +12,22 @@ from spsdk.apps import spsdk_apps -def run_help(command_group): +def run_help(command_group, help_option): runner = CliRunner() - result = runner.invoke(command_group, ["--help"]) + result = runner.invoke(command_group, ["--help"] if help_option else None) assert result.exit_code == 0 assert "Show this message and exit." in result.output def test_spsdk_apps_help(): - run_help(spsdk_apps.main) + run_help(spsdk_apps.main, help_option=True) + run_help(spsdk_apps.main, help_option=False) def test_spsdk_apps_subcommands_help(): + devscan = spsdk_apps.main.commands.pop("nxpdevscan") + run_help(devscan, help_option=True) for name, command in spsdk_apps.main.commands.items(): logging.debug(f"running help for {name}") - run_help(command) + run_help(command, help_option=True) + run_help(command, help_option=False) diff --git a/tests/apps/test_utils.py b/tests/apps/test_utils.py index bc32b1ec..d8d46a37 100644 --- a/tests/apps/test_utils.py +++ b/tests/apps/test_utils.py @@ -8,8 +8,9 @@ from spsdk import SPSDKError from spsdk.apps import utils -from spsdk.apps.utils import catch_spsdk_error +from spsdk.apps.utils import catch_spsdk_error, load_configuration from spsdk.mboot.exceptions import McuBootConnectionError +from spsdk.utils.misc import use_working_directory def test_split_string(): @@ -87,3 +88,17 @@ def test_catch_spsdk_error(): assert exc_3.value.code == 3 assert function_under_test(None) == 0 + + +@pytest.mark.parametrize("file_name", ["certgen_config.yaml", "test_config.json"]) +def test_load_configuration(data_dir, file_name): + with use_working_directory(data_dir): + result = load_configuration(file_name) + assert isinstance(result, dict) + + +@pytest.mark.parametrize("file_name", ["zeros.bin", "invalid_file.json"]) +def test_load_configuration_invalid_file(data_dir, file_name): + with use_working_directory(data_dir): + with pytest.raises(SPSDKError): + load_configuration(file_name) diff --git a/tests/conftest.py b/tests/conftest.py index b0e31ec5..88d052ac 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,6 +7,7 @@ import logging from os import path + import pytest diff --git a/tests/crypto/data/certgen_config.yaml b/tests/crypto/data/certgen_config.yaml new file mode 100644 index 00000000..0d615da6 --- /dev/null +++ b/tests/crypto/data/certgen_config.yaml @@ -0,0 +1,25 @@ +issuer: + COMMON_NAME: ONE + COUNTRY_NAME: PL + LOCALITY_NAME: KR + STATE_OR_PROVINCE_NAME: MLPK + STREET_ADDRESS: BOT + ORGANIZATION_NAME: PKO + +subject: + COMMON_NAME: TWO + COUNTRY_NAME: PL + LOCALITY_NAME: LIMA + STATE_OR_PROVINCE_NAME: MLPK + STREET_ADDRESS: STOW + ORGANIZATION_NAME: IBM + POSTAL_CODE: 31503 + +issuer_private_key: issuer_privatekey_rsa2048.pem +subject_public_key: subject_publickey_rsa2048.pem +serial_number: 777 +duration: 1234 +extensions: + BASIC_CONSTRAINTS: + ca: true + path_length: 5 diff --git a/tests/crypto/test_certgen.py b/tests/crypto/test_certgen.py index 42ddd722..182d44b8 100644 --- a/tests/crypto/test_certgen.py +++ b/tests/crypto/test_certgen.py @@ -11,34 +11,33 @@ from typing import List import pytest +from click.testing import CliRunner +from spsdk import SPSDKError +from spsdk.apps.nxpcertgen import main from spsdk.crypto import ( - generate_rsa_private_key, - generate_rsa_public_key, - save_rsa_private_key, - save_rsa_public_key, - validate_ca_flag_in_cert_chain, Certificate, Encoding, -) -from spsdk.crypto import ( + ExtensionOID, + NameOID, _get_encoding_type, - load_private_key, + generate_certificate, + generate_rsa_private_key, + generate_rsa_public_key, + is_ca_flag_set, load_certificate, + load_private_key, load_public_key, - ExtensionOID, - NameOID, + save_crypto_item, + save_rsa_private_key, + save_rsa_public_key, + validate_ca_flag_in_cert_chain, + validate_certificate, + validate_certificate_chain, ) -from spsdk.crypto import validate_certificate_chain, validate_certificate, is_ca_flag_set -from spsdk.crypto import generate_certificate, save_crypto_item - from spsdk.crypto.certificate_management import generate_name_struct from spsdk.utils.misc import use_working_directory -from click.testing import CliRunner - -from spsdk.apps.nxpcertgen import main - def get_certificate(data_dir, cert_file_name: str) -> Certificate: cert = load_certificate(path.join(data_dir, cert_file_name)) @@ -211,10 +210,14 @@ def test_certificate_generation(tmpdir): assert path.isfile(path.join(tmpdir, "srk1.pem")) -def test_certificate_generation_cli(tmpdir, data_dir): +@pytest.mark.parametrize("json, encoding", [(True, "PEM"), (False, "Der")]) +def test_certificate_generation_cli(tmpdir, data_dir, json, encoding): with use_working_directory(data_dir): cert_path = os.path.join(tmpdir, "cert.crt") - cmd = f'-j {os.path.join(data_dir, "certgen_config.json")} -c {cert_path}' + if json: + cmd = f'generate -j {os.path.join(data_dir, "certgen_config.json")} -o {cert_path}' + else: + cmd = f'generate -c {os.path.join(data_dir, "certgen_config.yaml")} -o {cert_path} -e {encoding}' runner = CliRunner() result = runner.invoke(main, cmd.split()) assert result.exit_code == 0 @@ -226,3 +229,8 @@ def test_certificate_generation_cli(tmpdir, data_dir): assert generated_cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME).pop(0).value == "TWO" assert generated_cert.extensions.get_extension_for_oid(ExtensionOID.BASIC_CONSTRAINTS).value.ca assert generated_cert.serial_number == 777 + + +def test_invalid_certificate_chain(): + with pytest.raises(SPSDKError): + validate_certificate_chain(chain_list=[]) diff --git a/tests/dat/data/new.pub b/tests/dat/data/new.pub new file mode 100644 index 00000000..e69de29b diff --git a/tests/dat/test_dar_packet.py b/tests/dat/test_dar_packet.py index 028fd726..e01c2ffb 100644 --- a/tests/dat/test_dar_packet.py +++ b/tests/dat/test_dar_packet.py @@ -8,11 +8,13 @@ """Tests with Debug Authentication Packet (DAR) Packet.""" import os + import pytest import yaml -from spsdk.dat.dar_packet import DebugAuthenticateResponse +from spsdk import SPSDKError from spsdk.dat import DebugAuthenticationChallenge as DAC +from spsdk.dat.dar_packet import DebugAuthenticateResponse from spsdk.dat.debug_credential import DebugCredential as DC from spsdk.utils.misc import load_binary, use_working_directory @@ -25,7 +27,7 @@ ], ) def test_dar_packet_rsa( - tmpdir, data_dir, yml_file_name, version, dck_key_file, expected_length, dac_bin_file + data_dir, yml_file_name, version, dck_key_file, expected_length, dac_bin_file ): with use_working_directory(data_dir): dac_bytes = load_binary(os.path.join(data_dir, dac_bin_file)) @@ -55,9 +57,7 @@ def test_dar_packet_rsa( ("new_dck_secp384_N4A.yml", "2.1", "new_dck_secp384r1.pem", 444), ], ) -def test_dar_packet_4_analog_256( - tmpdir, data_dir, yml_file_name, version, file_key, expected_length -): +def test_dar_packet_4_analog_256(data_dir, yml_file_name, version, file_key, expected_length): with use_working_directory(data_dir): dac_bytes = load_binary(os.path.join(data_dir, "sample_dac_analog.bin")) with open(os.path.join(data_dir, yml_file_name), "r") as f: @@ -76,3 +76,24 @@ def test_dar_packet_4_analog_256( assert len(dar_bytes) == expected_length assert isinstance(dar_bytes, bytes) assert "Authentication Beacon" in dar.info() + + +def test_dar_packet_no_signature_provider(data_dir): + with use_working_directory(data_dir): + version = "1.0" + dac_bin_file = "sample_dac.bin" + yml_file_name = "new_dck_rsa2048.yml" + dac_bytes = load_binary(os.path.join(data_dir, dac_bin_file)) + with open(os.path.join(data_dir, yml_file_name), "r") as f: + yaml_config = yaml.safe_load(f) + dc = DC.create_from_yaml_config(version=version, yaml_config=yaml_config) + dc.sign() + dar = DebugAuthenticateResponse( + debug_credential=dc, + auth_beacon=0, + dac=DAC.parse(dac_bytes), + path_dck_private=os.path.join(data_dir, "new_dck_2048.pem"), + ) + dar.sig_provider = None + with pytest.raises(SPSDKError, match="Signature provider is not set"): + dar.export() diff --git a/tests/debuggers/debug_probe_virtual.py b/tests/debuggers/debug_probe_virtual.py index de4e1ec1..90072a93 100644 --- a/tests/debuggers/debug_probe_virtual.py +++ b/tests/debuggers/debug_probe_virtual.py @@ -6,17 +6,17 @@ # SPDX-License-Identifier: BSD-3-Clause """Module for DebugMailbox Virtual Debug probes support used for product testing.""" -from json.decoder import JSONDecodeError -import logging import json -from typing import Dict, Any +import logging +from json.decoder import JSONDecodeError +from typing import Any, Dict from spsdk.debuggers.debug_probe import ( DebugProbe, - DebugProbeTransferError, - DebugProbeNotOpenError, - DebugProbeError, - DebugProbeMemoryInterfaceNotEnabled, + SPSDKDebugProbeError, + SPSDKDebugProbeMemoryInterfaceNotEnabled, + SPSDKDebugProbeNotOpenError, + SPSDKDebugProbeTransferError, ) logger = logging.getLogger(__name__) @@ -59,7 +59,7 @@ def __init__(self, hardware_id: str, user_params: Dict = None) -> None: if user_params is not None: if "exc" in user_params.keys(): - raise DebugProbeError("Forced exception from constructor.") + raise SPSDKDebugProbeError("Forced exception from constructor.") if "subs_ap" in user_params.keys(): self.set_coresight_ap_substitute_data( self._load_subs_from_param(user_params["subs_ap"]) @@ -84,7 +84,7 @@ def get_connected_probes(cls, hardware_id: str = None, user_params: Dict = None) hardware id is listed. :param user_params: The user params dictionary :return: probe_description - :raises DebugProbeError: In case of invoked test Exception. + :raises SPSDKDebugProbeError: In case of invoked test Exception. """ # pylint: disable=import-outside-toplevel from spsdk.debuggers.utils import DebugProbes, ProbeDescription @@ -92,7 +92,7 @@ def get_connected_probes(cls, hardware_id: str = None, user_params: Dict = None) probes = DebugProbes() if user_params is not None and "exc" in user_params.keys(): - raise DebugProbeError("Forced exception from discovery function.") + raise SPSDKDebugProbeError("Forced exception from discovery function.") # Find this 'probe' just in case of direct request (user must know the hardware id :-) ) if hardware_id == DebugProbeVirtual.UNIQUE_SERIAL: @@ -140,7 +140,7 @@ def _get_requested_value(self, values: Dict, subs_values: Dict, addr: Any) -> in :param subs_values: The dictionary with substituted values. :param addr: Address of value. :return: Value by address. - :raises DebugProbeError: General virtual probe error. + :raises SPSDKDebugProbeError: General virtual probe error. """ if subs_values and addr in subs_values.keys(): if len(subs_values[addr]) > 0: @@ -148,7 +148,7 @@ def _get_requested_value(self, values: Dict, subs_values: Dict, addr: Any) -> in if isinstance(svalue, int): return svalue if isinstance(svalue, str) and svalue == "Exception": - raise DebugProbeError("Simulated Debug probe exception") + raise SPSDKDebugProbeError("Simulated Debug probe exception") return int(values[addr]) if addr in values.keys() else 0 @@ -158,10 +158,10 @@ def dbgmlbx_reg_read(self, addr: int = 0) -> int: This is read debug mailbox register function for SPSDK library to support various DEBUG PROBES. :param addr: the register address :return: The read value of addressed register (4 bytes) - :raises DebugProbeNotOpenError: The virtual probe is not open + :raises SPSDKDebugProbeNotOpenError: The virtual probe is not open """ if not self.opened: - raise DebugProbeNotOpenError("The Virtual debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The Virtual debug probe is not opened yet") # Add ap selection to 2 as a standard index of debug mailbox return self.coresight_reg_read(access_port=True, addr=addr | 2 << self.APSEL_SHIFT) @@ -172,10 +172,10 @@ def dbgmlbx_reg_write(self, addr: int = 0, data: int = 0) -> None: This is write debug mailbox register function for SPSDK library to support various DEBUG PROBES. :param addr: the register address :param data: the data to be written into register - :raises DebugProbeNotOpenError: The virtual probe is not open + :raises SPSDKDebugProbeNotOpenError: The virtual probe is not open """ if not self.opened: - raise DebugProbeNotOpenError("The Virtual debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The Virtual debug probe is not opened yet") # Add ap selection to 2 as a standard index of debug mailbox self.coresight_reg_write(access_port=True, addr=addr | 2 << self.APSEL_SHIFT, data=data) @@ -187,15 +187,15 @@ def mem_reg_read(self, addr: int = 0) -> int: to support various DEBUG PROBES. :param addr: the register address :return: The read value of addressed register (4 bytes) - :raises DebugProbeNotOpenError: The Virtual probe is NOT opened - :raises DebugProbeMemoryInterfaceNotEnabled: The Virtual is using just CoreSight access. - :raises DebugProbeError: General virtual probe error. + :raises SPSDKDebugProbeNotOpenError: The Virtual probe is NOT opened + :raises SPSDKDebugProbeMemoryInterfaceNotEnabled: The Virtual is using just CoreSight access. + :raises SPSDKDebugProbeError: General virtual probe error. """ if not self.opened: - raise DebugProbeNotOpenError("The Virtual debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The Virtual debug probe is not opened yet") if not self.enabled_memory_interface: - raise DebugProbeMemoryInterfaceNotEnabled( + raise SPSDKDebugProbeMemoryInterfaceNotEnabled( "Memory interface is not enabled over Virtual." ) @@ -208,14 +208,14 @@ def mem_reg_write(self, addr: int = 0, data: int = 0) -> None: to support various DEBUG PROBES. :param addr: the register address :param data: the data to be written into register - :raises DebugProbeNotOpenError: The Virtual probe is NOT opened - :raises DebugProbeMemoryInterfaceNotEnabled: The Virtual is using just CoreSight access. + :raises SPSDKDebugProbeNotOpenError: The Virtual probe is NOT opened + :raises SPSDKDebugProbeMemoryInterfaceNotEnabled: The Virtual is using just CoreSight access. """ if not self.opened: - raise DebugProbeNotOpenError("The Virtual debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The Virtual debug probe is not opened yet") if not self.enabled_memory_interface: - raise DebugProbeMemoryInterfaceNotEnabled( + raise SPSDKDebugProbeMemoryInterfaceNotEnabled( "Memory interface is not enabled over Virtual." ) @@ -228,12 +228,12 @@ def coresight_reg_read(self, access_port: bool = True, addr: int = 0) -> int: :param access_port: if True, the Access Port (AP) register will be read(default), otherwise the Debug Port :param addr: the register address :return: The read value of addressed register (4 bytes) - :raises DebugProbeTransferError: The IO operation failed - :raises DebugProbeNotOpenError: The Virtual probe is NOT opened - :raises DebugProbeError: General virtual probe error. + :raises SPSDKDebugProbeTransferError: The IO operation failed + :raises SPSDKDebugProbeNotOpenError: The Virtual probe is NOT opened + :raises SPSDKDebugProbeError: General virtual probe error. """ if not self.opened: - raise DebugProbeNotOpenError("The Virtual debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The Virtual debug probe is not opened yet") # As first try to solve AP requests if access_port: return self._get_requested_value(self.coresight_ap, self.coresight_ap_substituted, addr) @@ -248,28 +248,28 @@ def coresight_reg_write(self, access_port: bool = True, addr: int = 0, data: int :param access_port: if True, the Access Port (AP) register will be write(default), otherwise the Debug Port :param addr: the register address :param data: the data to be written into register - :raises DebugProbeTransferError: The IO operation failed - :raises DebugProbeNotOpenError: The Virtual probe is NOT opened + :raises SPSDKDebugProbeTransferError: The IO operation failed + :raises SPSDKDebugProbeNotOpenError: The Virtual probe is NOT opened """ if not self.opened: - raise DebugProbeNotOpenError("The Virtual debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The Virtual debug probe is not opened yet") if access_port: self.coresight_ap[addr] = data else: if self.coresight_dp_write_exception: self.coresight_dp_write_exception = False - raise DebugProbeTransferError(f"The Coresight write operation failed.") + raise SPSDKDebugProbeTransferError(f"The Coresight write operation failed.") self.coresight_dp[addr] = data def reset(self) -> None: """Reset a target. It resets a target. - :raises DebugProbeNotOpenError: The Virtual probe is NOT opened + :raises SPSDKDebugProbeNotOpenError: The Virtual probe is NOT opened """ if not self.opened: - raise DebugProbeNotOpenError("The Virtual debug probe is not opened yet") + raise SPSDKDebugProbeNotOpenError("The Virtual debug probe is not opened yet") logger.debug("The Virtual probe did reset of virtual target.") @@ -325,7 +325,7 @@ def _load_subs_from_param(self, arg: str) -> Dict: :param arg: Input string arguments with substitute values. :return: List of values for the substituted values. - :raises DebugProbeError: The input string is not able do parse. + :raises SPSDKDebugProbeError: The input string is not able do parse. """ try: subs_data_raw = json.loads(arg) @@ -334,4 +334,4 @@ def _load_subs_from_param(self, arg: str) -> Dict: subs_data[int(key)] = subs_data_raw[key] return subs_data except (TypeError, JSONDecodeError) as exc: - raise DebugProbeError(f"Cannot parse substituted values: ({str(exc)})") + raise SPSDKDebugProbeError(f"Cannot parse substituted values: ({str(exc)})") diff --git a/tests/debuggers/test_debug_probe.py b/tests/debuggers/test_debug_probe.py index dade0030..eb5a111f 100644 --- a/tests/debuggers/test_debug_probe.py +++ b/tests/debuggers/test_debug_probe.py @@ -6,6 +6,7 @@ # SPDX-License-Identifier: BSD-3-Clause """ Tests for Debug Probe interface.""" import pytest + import spsdk import spsdk.debuggers.debug_probe as DP diff --git a/tests/debuggers/test_debug_probe_utils.py b/tests/debuggers/test_debug_probe_utils.py index 9a50dc53..a78c2380 100644 --- a/tests/debuggers/test_debug_probe_utils.py +++ b/tests/debuggers/test_debug_probe_utils.py @@ -7,9 +7,15 @@ """ Tests for Debug Probe utilities.""" import pytest -from tests.debuggers.debug_probe_virtual import DebugProbeVirtual -from spsdk.debuggers.utils import DebugProbeUtils, DebugProbes, ProbeDescription, ProbeNotFoundError import spsdk.debuggers.debug_probe as DP +from spsdk import SPSDKError +from spsdk.debuggers.utils import ( + DebugProbes, + DebugProbeUtils, + ProbeDescription, + SPSDKProbeNotFoundError, +) +from tests.debuggers.debug_probe_virtual import DebugProbeVirtual def test_debugprobes_append(): @@ -20,7 +26,7 @@ def test_debugprobes_append(): assert probe_list.pop() == probe_descr - with pytest.raises(TypeError): + with pytest.raises(SPSDKError): probe_list.append("Invalid Type") @@ -32,7 +38,7 @@ def test_debugprobes_insert(): assert probe_list.pop() == probe_descr - with pytest.raises(TypeError): + with pytest.raises(SPSDKError): probe_list.insert(0, "Invalid Type") @@ -55,7 +61,7 @@ def test_debugprobes_get_probe(): probe = probe_list.select_probe().get_probe() assert isinstance(probe, DebugProbeVirtual) - with pytest.raises(DP.DebugProbeError): + with pytest.raises(DP.SPSDKDebugProbeError): assert probe_list.select_probe().get_probe({"exc": None}) is None @@ -63,10 +69,10 @@ def test_debugprobes_select_probe(): """Test of Debug Probe Utilities - Select probe.""" probe_list = DebugProbes() - with pytest.raises(ProbeNotFoundError): + with pytest.raises(SPSDKProbeNotFoundError): probe_list.select_probe(silent=True) - with pytest.raises(ProbeNotFoundError): + with pytest.raises(SPSDKProbeNotFoundError): probe_list.select_probe(silent=False) probe_description = ProbeDescription( diff --git a/tests/debuggers/test_debug_virtual_probe.py b/tests/debuggers/test_debug_virtual_probe.py index 72642195..3b18ff07 100644 --- a/tests/debuggers/test_debug_virtual_probe.py +++ b/tests/debuggers/test_debug_virtual_probe.py @@ -8,10 +8,10 @@ import pytest from spsdk.debuggers.debug_probe import ( - DebugProbeError, - DebugProbeNotOpenError, - DebugProbeMemoryInterfaceNotEnabled, - DebugProbeTransferError, + SPSDKDebugProbeError, + SPSDKDebugProbeMemoryInterfaceNotEnabled, + SPSDKDebugProbeNotOpenError, + SPSDKDebugProbeTransferError, ) from tests.debuggers.debug_probe_virtual import DebugProbeVirtual @@ -32,9 +32,9 @@ def test_virtualprobe_basic(): def test_virtualprobe_dp(): """Test of virtual Debug Probe - Debug port access.""" virtual_probe = DebugProbeVirtual("ID", None) - with pytest.raises(DebugProbeNotOpenError): + with pytest.raises(SPSDKDebugProbeNotOpenError): virtual_probe.coresight_reg_read(False, 0) - with pytest.raises(DebugProbeNotOpenError): + with pytest.raises(SPSDKDebugProbeNotOpenError): virtual_probe.coresight_reg_write(False, 0, 0) virtual_probe.open() @@ -48,7 +48,7 @@ def test_virtualprobe_dp(): virtual_probe.set_coresight_dp_substitute_data({0: [2, 3, "Exception", "Invalid"]}) assert virtual_probe.coresight_reg_read(False, 0) == 2 assert virtual_probe.coresight_reg_read(False, 0) == 3 - with pytest.raises(DebugProbeError): + with pytest.raises(SPSDKDebugProbeError): assert virtual_probe.coresight_reg_read(False, 0) == 3 assert virtual_probe.coresight_reg_read(False, 0) == 1 @@ -56,17 +56,17 @@ def test_virtualprobe_dp(): virtual_probe.dp_write_cause_exception() - with pytest.raises(DebugProbeTransferError): + with pytest.raises(SPSDKDebugProbeTransferError): virtual_probe.coresight_reg_write(False, 0, 0) def test_virtualprobe_ap(): """Test of virtual Debug Probe - Access port control.""" virtual_probe = DebugProbeVirtual("ID", None) - with pytest.raises(DebugProbeNotOpenError): + with pytest.raises(SPSDKDebugProbeNotOpenError): virtual_probe.coresight_reg_read(True, 0) - with pytest.raises(DebugProbeNotOpenError): + with pytest.raises(SPSDKDebugProbeNotOpenError): virtual_probe.coresight_reg_write(True, 0, 0) virtual_probe.open() @@ -81,7 +81,7 @@ def test_virtualprobe_ap(): virtual_probe.set_coresight_ap_substitute_data({0: [2, 3, "Exception", "Invalid"]}) assert virtual_probe.coresight_reg_read(True, 0) == 2 assert virtual_probe.coresight_reg_read(True, 0) == 3 - with pytest.raises(DebugProbeError): + with pytest.raises(SPSDKDebugProbeError): assert virtual_probe.coresight_reg_read(True, 0) == 3 assert virtual_probe.coresight_reg_read(True, 0) == 1 assert virtual_probe.coresight_reg_read(True, 0) == 1 @@ -90,9 +90,9 @@ def test_virtualprobe_ap(): def test_virtualprobe_debugmbox(): """Test of virtual Debug Probe - Debug mailbox API.""" virtual_probe = DebugProbeVirtual("ID", None) - with pytest.raises(DebugProbeNotOpenError): + with pytest.raises(SPSDKDebugProbeNotOpenError): virtual_probe.dbgmlbx_reg_read(0) - with pytest.raises(DebugProbeNotOpenError): + with pytest.raises(SPSDKDebugProbeNotOpenError): virtual_probe.dbgmlbx_reg_write(0, 0) virtual_probe.open() @@ -113,16 +113,16 @@ def test_virtualprobe_debugmbox(): def test_virtualprobe_memory(): """Test of virtual Debug Probe - Memory access tests.""" virtual_probe = DebugProbeVirtual("ID", None) - with pytest.raises(DebugProbeNotOpenError): + with pytest.raises(SPSDKDebugProbeNotOpenError): virtual_probe.mem_reg_read(0) - with pytest.raises(DebugProbeNotOpenError): + with pytest.raises(SPSDKDebugProbeNotOpenError): virtual_probe.mem_reg_write(0, 0) virtual_probe.open() - with pytest.raises(DebugProbeMemoryInterfaceNotEnabled): + with pytest.raises(SPSDKDebugProbeMemoryInterfaceNotEnabled): virtual_probe.mem_reg_read(0) - with pytest.raises(DebugProbeMemoryInterfaceNotEnabled): + with pytest.raises(SPSDKDebugProbeMemoryInterfaceNotEnabled): virtual_probe.mem_reg_write(0, 0) virtual_probe.enable_memory_interface() @@ -137,7 +137,7 @@ def test_virtualprobe_memory(): virtual_probe.set_virtual_memory_substitute_data({0: [2, 3, "Exception", "Invalid"]}) assert virtual_probe.mem_reg_read(0) == 2 assert virtual_probe.mem_reg_read(0) == 3 - with pytest.raises(DebugProbeError): + with pytest.raises(SPSDKDebugProbeError): assert virtual_probe.mem_reg_read(0) == 3 assert virtual_probe.mem_reg_read(0) == 1 assert virtual_probe.mem_reg_read(0) == 1 @@ -146,7 +146,7 @@ def test_virtualprobe_memory(): def test_virtualprobe_reset(): """Test of virtual Debug Probe - Reset API.""" virtual_probe = DebugProbeVirtual("ID", None) - with pytest.raises(DebugProbeNotOpenError): + with pytest.raises(SPSDKDebugProbeNotOpenError): virtual_probe.reset() virtual_probe.open() virtual_probe.reset() @@ -154,7 +154,7 @@ def test_virtualprobe_reset(): def test_virtualprobe_init(): """Test of virtual Debug Probe - Initialization.""" - with pytest.raises(DebugProbeError): + with pytest.raises(SPSDKDebugProbeError): virtual_probe = DebugProbeVirtual("ID", {"exc": None}) virtual_probe = DebugProbeVirtual( @@ -169,5 +169,5 @@ def test_virtualprobe_init(): def test_virtualprobe_init_false(): """Test of virtual Debug Probe - Invalid Initialization.""" - with pytest.raises(DebugProbeError): + with pytest.raises(SPSDKDebugProbeError): DebugProbeVirtual("ID", {"subs_ap": '{"0":1,2]}'}) diff --git a/tests/elftosb/data/sb3_test_384_384_unencrypted.json b/tests/elftosb/data/sb3_test_384_384_unencrypted.json deleted file mode 100644 index ea7bf400..00000000 --- a/tests/elftosb/data/sb3_test_384_384_unencrypted.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "family": "lpc55s3x", - "containerKeyBlobEncryptionKey": ".\\workspace\\keys\\userkey.txt", - "isNxpContainer": false, - "description": "sb3_test_384_384_unencrypted.sb3", - "kdkKeyAccesRights": 3, - "containerConfigurationWord": "0x0", - "timestamp" : "0x123456789abcdef", - "firmwareVersion": "0x1", - "rootCertificate0File": ".\\workspace\\keys_certs\\ec_secp384r1_cert0.pem", - "rootCertificate1File": ".\\workspace\\keys_certs\\ec_secp384r1_cert1.pem", - "rootCertificate2File": ".\\workspace\\keys_certs\\ec_secp384r1_cert2.pem", - "rootCertificate3File": ".\\workspace\\keys_certs\\ec_secp384r1_cert3.pem", - "rootCertificateEllipticCurve": "secp384r1", - "mainRootCertId": 0, - "mainRootCertPrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp384r1_cert0.pem", - "useIsk": true, - "signingCertificateFile": ".\\workspace\\keys_certs\\ec_secp384r1_sign_cert.pem", - "signingCertificatePrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp384r1_sign_cert.pem", - "signingCertificateConstraint": "0x0000", - "iskCertificateEllipticCurve": "secp384r1", - "signCertData": ".\\workspace\\input_images\\testfffffff.bin", - - "testSb3Magic": "", - "testSb3ImageType": false, - "testSb3ImageTypeValue": 255, - "testCertBlockMagic": "", - "testCorruptRkhRecord": false, - "testCorruptRkhRecordId": 0, - "testCorruptIskSignature": false, - "isEncrypted": false, - - "commands": [ - {"erase": {"address": "0x2588", "size": "0xFFFF", "memoryId": "0xA"}}, - {"load": {"address": "0x1256", "file": ".\\workspace\\input_images\\test1.bin"}}, - {"load": {"address": "0x2588", "file": ".\\workspace\\input_images\\test2.bin", "authentication": "none", "memoryId": "0xA"}}, - {"load": {"address": "0x1256", "file": ".\\workspace\\input_images\\test1.bin", "authentication": "hashlocking"}}, - {"load": {"address": "0x2588", "file": ".\\workspace\\input_images\\test2.bin", "authentication": "cmac"}}, - {"loadKeyBlob": {"offset": "0x1256", "file": ".\\workspace\\input_images\\test1.bin", "wrappingKeyId": "NXP_CUST_KEK_INT_SK"}}, - {"loadKeyBlob": {"offset": "0x2588", "file": ".\\workspace\\input_images\\test2.bin", "wrappingKeyId": "NXP_CUST_KEK_EXT_SK"}}, - {"programFuses": {"address": "0x1384", "values": "0x138498, 0x0, 0x5, 0x1ab, 0x1ab, 0xffffffff, 0xfffffff1"}}, - {"programFuses": {"address": "0x2588", "values": "0x138498, 0x0, 0x5, 0x1ab, 0x1ab, 0xffffffff, 0xfffffff1"}}, - {"programIFR": {"address": "0x1384", "file": ".\\workspace\\input_ifr\\ifr1.bin"}}, - {"programIFR": {"address": "0x2588", "file": ".\\workspace\\input_ifr\\ifr2.bin"}}, - {"call": {"address": "0x1384"}}, - {"call": {"address": "0x2588"}}, - {"execute": {"address": "0x1384"}}, - {"configureMemory": {"memoryId": "0xA", "configAddress": "0x1842"}}, - {"load": {"address": "0x25", "values": "0x138498"}}, - {"load": {"address": "0x25", "values": "0x138498, 0x25, 0x4856974"}}, - {"fillMemory": {"address": "0x25", "pattern": "0xFFFFFFFF", "size": "0xFF"}}, - {"copy": {"addressFrom": "0xF", "memoryIdFrom": "0xFF", "addressTo": "0xFFF", "memoryIdTo": "0xFFFFF", "size": "0xA"}}, - {"checkFwVersion": {"counterId": "nonsecure", "value": "0x1"}}, - {"checkFwVersion": {"counterId": "secure", "value": "0x3"}} - ], - "containerOutputFile": ".\\workspace\\output_images\\sb3_test_384_384_unencrypted.sb3" -} \ No newline at end of file diff --git a/tests/elftosb/data/sb_sources/BD_files/real_example1.bd b/tests/elftosb/data/sb_sources/BD_files/real_example1.bd new file mode 100644 index 00000000..d0391fb4 --- /dev/null +++ b/tests/elftosb/data/sb_sources/BD_files/real_example1.bd @@ -0,0 +1,82 @@ +options { + flags = 0x8; // bd file format: 0x8 encrypted + signed (always 0x8) + buildNumber = 0x1; + productVersion = "1.00.00"; + componentVersion = "1.00.00"; + secureBinaryVersion = "2.1"; +} +sources { + myImage = "sb_sources/output_images/application_signed.bin"; // Put location of Signed or UnSigned image input + key_store = "sb_sources/key_store/key_store_rt5xx.bin"; + fcb_block = "sb_sources/input_images/rt500_quad_flash_fcb.bin"; +} +keyblob(0){ + ( + start = 0x08008000, + end = 0x087983ff, // Increased to 128kB = start + 0x20000, last bits bust be 0x3ff to have the AES enabled, bit ADE = 1 + + key = "000102030405060708090A0B0C0D0E0F", + counter = "0123456789ABCDEF", + noByteSwap = 1 // RT500: noByteSwap = 1, RT600 = 0 (default) + ) +} +keyblob(1){ + ( + start = 0x08798400, + end = 0x087987fd, // Just 1kB, with Descriptor bit ADE = 0 (bit1 = 0), Encryption Disabled + + key = "000102030405060708090A0B0C0D0E0F", // Random user defined key + counter = "0123456789ABCDEF", // AES seed (used internally) + noByteSwap = 1 + ) +} +keyblob(2){ + ( + start = 0x08798800, + end = 0x08798bfd, // Just 1kB, with Descriptor bit ADE = 0 (bit1 = 0), Encryption Disabled + key = "000102030405060708090A0B0C0D0E0F", + counter = "0123456789ABCDEF", + noByteSwap = 1 + ) +} +keyblob(3){ + ( + start = 0x08798c00, + end = 0x08798ffd, // Just 1kB, with Descriptor bit ADE = 0 (bit1 = 0), Encryption Disabled + key = "000102030405060708090A0B0C0D0E0F", + counter = "0123456789ABCDEF", + noByteSwap = 1 + ) +} +section (0) { + load 0xc0000001 > 0x10C000; // Memory config word for QSPI Flash (like IS25WPxxx): 0xc0000001, Octal SPI: 0xc0403006 + enable @0x9 0x10C000; + //erase 0x8008000..0x8040000; // Erase 256kB block at first avoid erasing the keystore location + //erase 0x8008000..0x8A00000; // Erase 256kB block at first avoid erasing the keystore location + erase 0x8008000..0x8300000; // Erase 256kB block at first avoid erasing the keystore location + + + + //load 0xf000000f > 0x10d000; + //enable @0x9 0x10d000; // Load new FCB by boot ROM code + + load fcb_block > 0x08000400; // Load FCB block manually (workaround) + load key_store > 0x08000800; // Key Store will be copied to external Flash, offset 0x800 + + encrypt(0){ + load myImage > 0x08008000; // 0x08001000 + } + + keywrap (0) { + load {{0102030405060708090a0b0c0d0e0f00}} > 0x08000000; // Equal to OTFAD_KEK, uses same for all now, used internally by device for keyblob encryption. + } + keywrap (1) { + load {{0102030405060708090a0b0c0d0e0f00}} > 0x08000040; + } + keywrap (2) { + load {{0102030405060708090a0b0c0d0e0f00}} > 0x08000080; + } + keywrap (3) { + load {{0102030405060708090a0b0c0d0e0f00}} > 0x080000C0; + } +} diff --git a/tests/elftosb/data/sb_sources/BD_files/real_example2.bd b/tests/elftosb/data/sb_sources/BD_files/real_example2.bd new file mode 100644 index 00000000..66248b4e --- /dev/null +++ b/tests/elftosb/data/sb_sources/BD_files/real_example2.bd @@ -0,0 +1,94 @@ +constants +{ + BOOTLOADER = 0x0; + BOOTLOADER_start = 0x0; + BOOTLOADER_size = 0x4000; + BOOTLOADER_end = 0x0 + 0x4000; + TMD = 0x4000; + TMD_start = 0x4000; + TMD_size = 0x38000; + TMD_end = 0x4000 + 0x38000; + IMAGE_STAGING_AREA = 0x3c000; + IMAGE_STAGING_AREA_start = 0x3c000; + IMAGE_STAGING_AREA_size = 0x3bc00; + IMAGE_STAGING_AREA_end = 0x3c000 + 0x3bc00; + AUDIO_ARRAY = 0x77c00; + AUDIO_ARRAY_start = 0x77c00; + AUDIO_ARRAY_size = 0x17800; + AUDIO_ARRAY_end = 0x77c00 + 0x17800; + PERSISTENCE = 0x8f400; + PERSISTENCE_start = 0x8f400; + PERSISTENCE_size = 0xdc00; + PERSISTENCE_end = 0x8f400 + 0xdc00; + SPARE_BLOCKS = 0x9d000; + SPARE_BLOCKS_start = 0x9d000; + SPARE_BLOCKS_size = 0x600; + SPARE_BLOCKS_end = 0x9d000 + 0x600; + FW_UPDATE_FIRST_BLK = 0x9d600; + FW_UPDATE_FIRST_BLK_start = 0x9d600; + FW_UPDATE_FIRST_BLK_size = 0x200; + FW_UPDATE_FIRST_BLK_end = 0x9d600 + 0x200; + FW_UPDATE_PARTIAL_BLK = 0x9d800; + FW_UPDATE_PARTIAL_BLK_start = 0x9d800; + FW_UPDATE_PARTIAL_BLK_size = 0x200; + FW_UPDATE_PARTIAL_BLK_end = 0x9d800 + 0x200; + TMD_DATA = 0x9da00; + TMD_DATA_start = 0x9da00; + TMD_DATA_size = 0x200; + TMD_DATA_end = 0x9da00 + 0x200; + MANUFACTURER_DATA = 0x9dc00; + MANUFACTURER_DATA_start = 0x9dc00; + MANUFACTURER_DATA_size = 0x200; + MANUFACTURER_DATA_end = 0x9dc00 + 0x200; + CFPA_SCRATCH = 0x9de00; + CFPA_SCRATCH_start = 0x9de00; + CFPA_SCRATCH_size = 0x200; + CFPA_SCRATCH_end = 0x9de00 + 0x200; + CFPA_PING = 0x9e000; + CFPA_PING_start = 0x9e000; + CFPA_PING_size = 0x200; + CFPA_PING_end = 0x9e000 + 0x200; + CFPA_PONG = 0x9e200; + CFPA_PONG_start = 0x9e200; + CFPA_PONG_size = 0x200; + CFPA_PONG_end = 0x9e200 + 0x200; + CMPA = 0x9e400; + CMPA_start = 0x9e400; + CMPA_size = 0x200; + CMPA_end = 0x9e400 + 0x200; + KEYSTORE = 0x9e600; + KEYSTORE_start = 0x9e600; + KEYSTORE_size = 0x600; + KEYSTORE_end = 0x9e600 + 0x600; +} + +options +{ + flags = 0x8; + buildNumber = 0x1; + productVersion = "1.00.00"; + componentVersion = "1.0.0"; + secureBinaryVersion = "2.1"; +} + +sources +{ + tmdData = extern(0); + bootloaderImage = extern(1); + tmdImage = extern(2); + audioImage = extern(3); +} + +section (0) +{ + load 0x50000000 > 0x20034000; + load 0x0 > 0x20034004; + load 0x3c000 > 0x20034008; + enable @0x0 0x20034000; + enable @0x9 0x20034000; + erase all; + load bootloaderImage > BOOTLOADER; + load tmdImage > TMD; + load audioImage > AUDIO_ARRAY; + load tmdData > TMD_DATA; +} diff --git a/tests/elftosb/data/sb_sources/BD_files/simpleExample.bd b/tests/elftosb/data/sb_sources/BD_files/simpleExample_no_sha.bd similarity index 100% rename from tests/elftosb/data/sb_sources/BD_files/simpleExample.bd rename to tests/elftosb/data/sb_sources/BD_files/simpleExample_no_sha.bd diff --git a/tests/elftosb/data/sb_sources/BD_files/simpleExample_sha.bd b/tests/elftosb/data/sb_sources/BD_files/simpleExample_sha.bd new file mode 100644 index 00000000..b3430d4e --- /dev/null +++ b/tests/elftosb/data/sb_sources/BD_files/simpleExample_sha.bd @@ -0,0 +1,82 @@ +# This BD configuration file serves as a minimal working example +# to test the python parser +options { + flags = 0x8008; // bd file format: 0x8 encrypted + signed (always 0x8), 0x8000 means include SHA in final image + buildNumber = 0x1; + productVersion = "1.00.00"; + componentVersion = "1.00.00"; + secureBinaryVersion = "2.1"; +} +sources { + myImage = "sb_sources/output_images/application_signed.bin"; // Put location of Signed or UnSigned image input + key_store = "sb_sources/key_store/key_store_rt5xx.bin"; + fcb_block = "sb_sources/input_images/rt500_oct_flash_fcb.bin"; +} +keyblob (0) { + ( + start = 0x08001000, + end = 0x082013ff, + key = "00112233445566778899001122334455", + counter = "1122334455667788", + byteSwap = false + ) +} +keyblob (1) { + ( + start = 0x08201400, + end = 0x082017FF, + key = "aabbccddeeffaabbccddeeffaabbccdd", + counter = "1122334455667788", + byteSwap = false + ) +} +keyblob (2) { + ( + start = 0x08201800, + end = 0x08201BFF, + key = "aabbccddeeffaabbccddeeffaabbcc11", + counter = "1122334455667788", + byteSwap = false + ) +} +keyblob (3) { + ( + start = 0x08201C00, + end = 0x08201FFF, + key = "aabbccddeeffaabbccddeeffaabbcc22", + counter = "1122334455667788", + byteSwap = false + ) +} +section (0) { + load 0xc0403006 > 0x10C000; // Memory config word for Octal Flash + enable @0x9 0x10C000; + erase 0x8000000..0x8300000; //0x8040000 Erase 3MB 0x300000 block at first , + + encrypt (0){ + load myImage > 0x08001000; + } + + keywrap (0) { + load {{00000000000000000000000000000000}} > 0x08000000; + } + + keywrap (1) { + load {{00000000000000000000000000000000}} > 0x08000100; + } + + keywrap (2) { + load {{00000000000000000000000000000000}} > 0x08000200; + } + + keywrap (3) { + load {{00000000000000000000000000000000}} > 0x08000300; + } + + //load 0xf000000f > 0x10d000; + //enable @0x9 0x10d000; // Load new FCB by boot ROM code + load fcb_block > 0x08000400; // Load FCB block manually (workaround) + + load key_store > 0x08000800; // Key Store will be copied to external Flash, offset 0x800 + +} diff --git a/tests/elftosb/data/sb_sources/SB_files/legacy_elf2sb.bin b/tests/elftosb/data/sb_sources/SB_files/legacy_elf2sb.bin deleted file mode 100644 index bec18f0d8b8059b9dcc59a622d779a91e50c5700..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19552 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 diff --git a/tests/elftosb/data/sb_sources/SB_files/legacy_elftosb_no_sha.bin b/tests/elftosb/data/sb_sources/SB_files/legacy_elftosb_no_sha.bin new file mode 100644 index 0000000000000000000000000000000000000000..b980935e6fd526fcffce7d06e01349ceb5b95967 GIT binary patch literal 19552 zcmd3MgLf~mvuADFwr%&;wr$(CZQH$dzvXRjZQHi(cJFWZz1{aG?3|O6`Q!wXOlER2 z$tzO7Rg|z55N<{mFl#}n6wp6XQI%5!0fqt^1_J`J|4+d`n*Em`fS`cDfq;QrEL?5> z%A-TfSb_lkga42I2mc>7Px<#Ii4>q7C^m!NQ;q#@6CwYI$4YMgHk|UAlK&}d}K1WjS3^$oj4gN}4 z*Wl)nUu@TT&_F;691bm*LTTFSzlAU{b9VjD*hK$T@E?8`{Qrn)`foml{}NL$m=;qo zsBj@D2rvi;6yE?4P-o}%IukquG)=f<_zOHRFf2F_Q&1!`EE6;Ys2UgqDu}53e*-8u z5NVbF29R(dD%#8lOmP3H1_K8!C#t5R!3@U)^B)Wg2dbio@`MTF{%Cm=1s_)#gpe}5w~1R zsn2Gk0yY=6H-?ri&?tamLrg21^tN{Zp=Ih*0_P5AY*J9Dr zEJ(ToV&_$xXS`PqcAjKA=e*H@hD>q_c`vO_*QF2yW?!H6w-xe$S^V_sW`XtrQ<&NM z)j%D9i*y8v>8EWbecJl+h(2Is`AFq6fN$(O70n$9)a>cfD*yx(7znr|XpkwWk1449 z-^Rcv1q;e&3d$)8iZ2a{`Tt0j!T--P{$mCBs9+#WKuoazVG+T=KtVtr|EHq=S}VaW}(>l13c7*6f5Tk9xZ`KApqhGJ7fg^IR;MhC~PGf;&w^O-F1P|h+lR5 z)Uu8p;rD@@+AL+UqO3t!el~iv1^qvzhHDEI(WB&wOp;d-Hd@Mx6!=c8*18iDeW9!o zn@JFL$cX%1jz_bR4&zD0)S+&zsS{hyxy-U4hW+zT_gVDp$y#;dhxwEG~#9ne*HzdYgt7<+VOb;lL@8e_j&^ zNa{D)1wZYUa^(%~&bSf13X;($s{7Akt-6^1Y)D`^Wn)3VQz$15j~2M({VF$6RDCI` zhndCXIx)Yk9_2S@l-fxNZ@;VRr9OIxIW$*A3v)Gl9m&jbm}3g10NI`0mBDRi( zF8#aa()MiamQo?4x;+hHDMIRaDT%E&0fLgxZFarC!7jfFx=s3AXZyX&(v%X zwDZssC4)2)qTk}`3R*wkvEh3o8}$-XzJ$$z?N=}n2F3Cs z>;dUfu(c&{D__)vC95=**@;YDAL6CiFZX+8M%omqdima|1QurRIBe0-Y&aWg$i{6V zetwp?Mh-~zlrz`YDxGPd5_1`Cd8Do$B$JdnkDvG}dVyq*`nE$=DXpe-!GN!0)_o+l$0Ht#R)ie@2k zqPR?>VTLeyuztcIX|`>2k52Uy@01fPd^W>&(v9_%Y~bQo$^q!Xw7pXo>jl@Sa;on0 zWTln3pORm(W`$Pz{%P2ao)}J7bPJo8w{=GtwlP-x$}2mKo^RNiHg3j_T%!TSS~=88 zYL~S9h_-NELwCKao_~6$d}UdEk}1e_vsXt=SzUs+Zea1<=MyFP4`gi^<-njQQ!|FN zD7hZMImqJwv<({HvuD>XqAnJqK4P<^Fgl`=K}og>6Nd)3S<<()K)6$vgZWk9GOUvU zNSl}|h36%VWvIP;1M7FqW9Ex$s?#hCEkW>?SKb;Y)glM~%0ES$f3_hx@p#mxDLn~6 zwf|uu!!#MdSBstzJ)G%@H0xO=si#Tx63@7})~f>bNj?~?!a(W~MpSTzm(ozt4zHF| zx;q1rO=%B5%DWoX+pBVac&zqxj+QYw9&flHxFChrF(BaSsN?YDYd^eX?8cd07D_Zf z3oJD?Es1l7c^i?9N%0GeefxT8Qt#?rgWP&P|Iez&AE!lG8%WhV?TEPef+sE}>~-Fv$KP)DFS-4bxqJ;*^mj4ivUJCzB?3-((qw}Cnd4Rz)U8a){9f}qMd3r0QJNRW zQ(Esfi7iFv2=Mra=_S%E>BxIao%y-A3<@|cph(=LJwq*Si`_Y(TPF!l^r~Onfh<$L ztJd@W4HUHCRKe#XYQRQDT&Bx{sIuBCb{QkhIkU|PGHx52J<8 zkELU_D3qQMSh`XPqMQY$CpwdZ95-r4Zr+exfunVYo$`IpLwQw*`4CTsnhybh=a(_D zx&8*0mN!xF^Q%|18s?Kwh%)>LL!dh$ZOanGy z0qB8Bhwp+}HYS5o?n2=>TdF0_9RM>HEeBB&3!TU7=6#7bwsX%i&Hyk%)Pl`cMx+bP z6-|pR#nY<$wtNOE_f0?4)P9gE&~7s^Le7mt>lC#0_V++?skh`AqC0C_(`70kJ)N@I zmaSM29=tg?pg(Wlo;8!gv*0<~%7^&1raMpb2nYR6@phkuL`wB{TO08v*{=PMQWb9P zmNJ}$#VcxNCcD{{beykfYdL6%;dVH9$#qdjBRx08MPicMSa?m{=*?QqcHuFyG?&sW z@Msc=Nhzx!dl%4*85i{4^yfAQ6 zo0tkyE5UNe_Y3ZE|CQ1wRK38HJwg&_T+MDX_OP52p3>g*9{p)smtZ^kUkeS7DT%ZS z^*>RZTX1mI#i~@}Le=PC9q)_w^`Xvv6<`9q$wW2nLKmU6aX+&z;fHfU{r1?m$0YZk ziF@a1U(svt_Hd&o%bWI93kN@wGDfdmNTT7-cBj3905x2`7OxZ zeVqeAgIAxpTIcN^}gcCU8Gh%8ECId-GSx_4+Tyx$weJrzg_!83eLqbU{SoopS9|E6Nf)tA3J`7Kwjg*LemU#BEAD- z%oQ3&fjyh4M|a1|moF3K#&8INsFC@6>@1%z zED%oD$`v7^~M^4if{w>clRu7W3w9+ zYNZ9Irx!WOS1l3%JTw@O?QT(p6L_Kvj@>!*Z#a{6*j*LhV=HZ&mmcvx+gI8LKmIz0 z44{k(qX!}BgofDR7FqP6DcnnL{}`&f13V{hP zaRnL(HvLPf!nyT9xug1J03bhC4Q~)rWmO=##|Rz)mgA ze+>IQwxOyeg3-!sTMJ7Esz|HPy%9R}NAymW$V{?u3>GR#jG(943qkbHtd1g+4sXip=6UNzLi0w3FJ25wYhQ`LVBa zD9za5f}b+;+A>$BSdp-Y)=w+nE*vce=JklZ>s=NY;2I6L$k1dZ?$%XbXm8%i+ej$C z%%bVzY|cpi_E=C13-Onp9;rS%T}9LmC2K#_4$^DdIt2;=0R~fiQi|=mJuNLFDS}XM zR9JtnS@E6F^7H}JQY6zi?LWPzBrADeSMEK6D9xaLi{Zm3kF!aqmnVEQARv&&1zkbc zr881d*|tJn`t-IbCA&y=A$iXVApz}l#v>}nSq$DH0CVRYF|KT(gHjtb?jWogmI>Hd zlSLmPJI&Msdbd}hjhcpEyMbr4q@;)^Tzp5SjJYW6dijonQ!z}zuyMc{Xzkq(`2%nn z%XO2kER`E$Fs9!GVqM1NaSLh=2vgn3+;T}7@UIz%q!iyE=4ooxFZjDVeHmtBS8R|& zG3ygSt*+=|P%&Tk%J|pSqudG{)PHuPiBzWhZ0AuGm{MeN=xh>Vr~-jUt(}?;xieQR z5nHT9Guq#d*dyq^WE+Vd4Z~oRMygJLbI4zQ%M0R22$Ki=@dQ zc2A}Dp!%+pff#3$rsy)QW5zhY#YIn?k;_ty(<$(WjT;=$VcBqX83Q$r zVh*SDD(%W=YNR&Ci)yP<=5*JSyr3HqkyEQ&Ivq5Y6WLmzsHq7#^HcB)uIAAgnvnW` z{b{i#8{`F#ZE`I?g&*$+4dk(6=XZF+or};+iOYmH5*oe{aF)+M{oYktejSQ(ktR5- zxWI+1_sqDtzz`M3^@6~UPK=(3Tyc0X1hhI+=|Pgp{7w6`V~16^h_w^0vr9@y zZUiyM<38Osrl_s*z(bo>cKAr_#&FXyS?Y0~<-6_j*ISMX7WaJKkHWTHEyGX@*c7kt z@x{;&e)bD(2$7B~qGHgKy$Bm+WTJ-KpG@a)D+~fLyK_xP+`!N3+kYvh>J;^1OceLP zQw4SFLA1-r^*}YVO9hCKH74HXX~cDSHe^l4SBR`<2D%v;pg$ECnLKDT7&M~zUi>b* zwTk-w_U9=acTIlrxdmS}w4(AnO8VQt6fkPX>fAG^+-1D{MjAB@^h8@v+5VT_V365Y zoshYFVVRZ!d(F7iiA*pXZE`wi9PB8fo<|{{N}Q`31sQ8RIei@Bq8<>@Gdv5Qgq$>6 zmj-()@|v%&?}^S9hsW*$i}TVdan(j@*$o3#0+q1`jiQ zG-?FmFkB#~6=4@Fna2|Xp4-L$A&9VqG`(gH2}4= zu-%x92O;B71w@HNvoPGXRiRW)y2Ypl-G-J~TK$}zlu1lfOi#^LZxdSbC4c9x1R?I& zUL6nD$ylS-e*~3aSiO-LZ2DsKMInt!Bd5^X;ZX4U>1ZEkj@O4Jf86?IEbNhJ1Ui_# zu95jxl}K9BSYU=@>dhMB`COp3BI^=_(=$L|N+6XV1Dwwh(&Ra&5KDVq`Fv_y9VG|b zxnfUvX41gCcd%QdMHXU>)dDL3MN^41-?;4wU2DdWOG@8+0bPiU7D+nO^BE7v>tux4Qa=(ls6)iH#iR6o#yS~k9jkP zkOp>Y-UGjVnCZZcaz^`Er&OlDVlDb~aQv9b`#52@ZyGN$H7cdizK{pJ@ zCI>7u^4@95fE`s%2ca=5uD&t{dzbd7(8_W>Rxu}dVGE4jv+@9|^VAvqO(2W>D_i<; z#Cd?BZ48$s>e9@yr%sQ5x-G#RI1&Ju%pCCWhXrKpr6}c|;MHuMux24?#-Swm@cLY9 zND06@!V$IFqYd75k}rSjX-LU)tf9=;B2)~Y7;%Li#)S&Fryvtj8o~?I!16!@a^eqR zdY4?V*btI(%3A53G$?m+;YdE56pxZ>Fy!MSF4uL%4UJG7*y8T8jtjOB|6YUO&;aKC zif$uLEp{EOcAPs5$FbClIn*D!*?Didh4y%nHXC%~KLpQsY@8E|-~sb~fzgsgv>iaI z-9@YB30rZMYy&#_JP_jj7=rM1qG*mmc<_n$bxr9->rPxpP{qAC7>qOwzE zhT?+e7?G%Q)pbBqmck=McI(u4XlfjZW+(n}UZo-eCc@aNLRT-oNO`8XhU>WBI3U%J z@!v*b0ny?TmE@u4H_c$~0oFv8pqZlGaLJfE$@EyTPaZ0}a}|acId|4pYwrsiCTgg$ zb#buisGuGr_8v)d?Ej|agn}FNVF4y91H;{?!(16bTvD_aGdZ=0dJc@(Ikbg>;T?(2 z0FMYWeg|5yVIl*qkmk`4EQ#E=xX0NJT86aNaaSp1f8boYJh4ySfL6R168vrMeklYY zLU!70i6GvGui~opyb^J@l9N_kjUe!crl{wW6QbpnoQc+Vamm%opi|tmx{n6F!Hi1+ zeTA7gK5Z-aeY(}af_HA62=(wD-xneY zhG6QhX~DW*JWH0@$Btf4Hx-3YilFRTz z?D^}plP;QS^y;et>BhyBE_q+>9&3SRKf2g(MIs(^KJ5}$TN=vz3IDARMck^u1q()* zs%e)dVO`RVrtCG&g0Ukx4ZbvttE!)agPNoHU-Z?2V=+&J>|XP)2pWgGeiE5>1V0$Q zd(HxSfhS$=jaIAUH{O8GX+T~CuWY(7uGw*b1Ug4;VOjkcgs#|N7oa=gX?}@OS zr8P_bE)-G#h(B4qldeO$)QNjJm(VB}>F$DM?#b=f_`RJ#&lmXS!Lt&#(GFmIhQ zhKJx#PiD5e5N1Jz4Apr<89Gd9R(f3lThtH@sJ7|nKPOAzOI7ORJSAFiQ5~FqJ=lX{ zP*d`}K>pcG@88?`Yw|NB@%K119a!$0E?aK%EPy=ZN!KLb_qKss5hPB_Z;(`lm%s;0 z)Vx_O0Q7HT3D7gPgK=)7^)k+f_qZ`Uv4q1A~WlU`u9F z+U{OCNm8^t%>qV9Gg`H%$BF6qV#XmPOp!C< zIkc#LdaA;mh$SLOWytjrQ;q>|!PP#jcz$0Ckpe{G;w&Pkf00QbKICImkZIWDLRrhL zO9Y^n6V4TfQ4`J9&NL78sn%hgyeoTa_;4UQ;(d&huK&fHsm zb1MEM9pk17nEn{V+?W&Zfm6;7^0Yaignd{WHg}QOen+kQM|MA+Xd5G9A^-V6Svs4P zkA*(k+Dlyw?g;x4>3+z2%~6GeOxhNcRD*w}pf* z0CCwwxM;o{W+~SrHu=kI7Bq--aSAGe1_Nu98l>?;_t4C;j;yk}@DTc)D)}4KHnhom zJArFem}q*2#K2$ZwBsip!A**?OR>r21c>#sIK+jzj<;J=Lxz1orZwkB(is}N1{i?= zLl<}0tzdC`DKx#wwX%4s{4W{RA$Bp@NrjvTWfOi5q7@xKqwbf_QO)me-B3uod{&KY zWcJd%dfu{l;-0?fN57#{)&Q4Cr7opyFx9%(bkJ%qzq^p)M#Q`@h-J4?jU%$6YMu$% zEeN+Ti<6sJ1Q+KmKuRYG{zU8Webvk&=TnQkxE$SWl4N)pm^SrvAZ~%5>b`HMO(A8X z7XeHEu%Sh$1%H++kwY`sD{r8X2ntt=fuM>ob%{d_il>K`X_tN$FBoN^7_*_~P#r9J zrH1y1n}Po$m6jI1yRDj4$3XH$0>{>_oXf(Fh{#zsKKm`HE(=IMTcPwwQYxX3p2Fx` zFM<1?U{%WV3Vw+jW^5j1%M#Pp^h1Vr0Lmm8mA;))OW^al_4|GF>Yw*Y47#Wjq65CD zvj_*XZ$in9jGN>Sq1-*T!cnJM%)K|nb}NXoqOl@~j!vkMsUmOIzpmQ?w;#{afjDmj ze-R0bu{YR3u&=D-VScnxWGF9QcsJ`xazmKPZ48At-T8_?j$t#QRx+NPg}cs z)^+szz8_lovL?5o0A{!aBI;slsB+gj>08@SYX%hU=MkofeZ8fV*V8SRmU3G)oIu2} zi}#!%u3F9Q-U3S7@!zdJ_?DH>;muesb3dpiqs!nE8Y^(ko`O+1puIM%w zN=>*e!mMi5;jivUl_q0U5M8i+iLNo%nTCZ)92!`g?1k zsZ(G_DP-P${J!V&RK#OyvU7-5?pCabio}&W*|k5@-sHMrRhg`D?mpB?D^myuh~u^D z{P`~QD&y06%U#*}!9bv=H;;&PG=@KBAF_=y;5rFmj4jTzVMbR3b>b+$p?C9bYSD~^ zK1SSD-l>lv?tCpxFmbcjK17FEK42EWfPTQ~=T-X_Mc-jDED}pcd+m8U=nNVu+THz2 zwk$&&%F05TbtfT+5a07A$UEJe3#eBF{y0O+p<3C<%jwU-f)kYwW>a8{G)^O?tb=cJ z3?N}t-Zmo&bKFQZG?lA!i&sN^i>Ot|XP7|qqAmFo9j=f7N2L7b6L1+a6DEKl4e-=_ zf!g%lgbjL2p!FqSxC+^(b$r-vD4GmA{p^1lk&)OEaj%prlT`U65<@E}G^B0>K}-*nNe7s}a$km>aiC$?6ghc>D}2Y# zj4>;ipiXo~dkWz4OAbaoiDACGM&JjOh#$fM&_V+Bo7Du1-@%<4K*p&dzt0yr8;hjv z2!)C_oziKncv4F@*W^*W4aEU1Z+v>vC40#(?`&yQ&rT9nKL&UQnNDr?JRdzG_qG)G9WxWjCj$69`i zCKb9@w^}b(_LIQFw7CQYybp13s(hTD7$aszmw9EVWM$PKwoU+XZ>frzda>VLT&<7; z1c#(8N+O=wakeAzq#+n&1#xqytp#=yL6&p|!KwDPcgNV9Aw=iFd&BgsgX_uM13n=u zOJ7uZ@DAgZjOK+a@GCU_FFP6)n!mupR{EwpV6`SCZ;z+I54Wua5)r7x^lH>&;{&Ta zktAE6;&`)Y-K=kOR!1I1He(EDH6g5*r}mxTE5!mq z2bUq!H^1l1dQsvj;%hz_}sRX_sP+;P$R-m>xl|K z_pD*4i98x#S^AWmvB?y)#+HEifd=`+=RWO|C=l4~`ln*Ie{#vx? zgTdLtQ2+S1aGAW4Ws6!46b!&k-g9H0MlJl^rSNR_@KHd^Jq@3ivt&NE0AY0e2G4z$ ztz^GQ1+55$;IlVXlc0_M)F(9SK_E1(`2ZEz6uzcwBy9UsVmGAn;95xcsO0q}mv*Dq z@6kk0Ngd$xXBEQ{MQco?po7z*g!X2Zn6HgUfUQf$n)iJclr|Z&EXVB+09X}mhl0r@ zvF4v;ogS~~%1qe$+*tM^b&sN*d`~$GCsju?%d$I2je4`ZEhw?!8ZS3oor&V*^ex*Y zIW%NcNU*-yr@Swe>(@n3HX-t*tSUc9*xTr~gY)O+$R!UA-6!bEv#h+~Mhr=2L z`+U&D9-U;<%hMR{C7$i4H!y`W>Zy*z?#DW09gpgp3C2~3XDJHZPOME%(KS$2SHDJ@ zN`3~|D^|qlh2F*d@(?^T(w%0S&~u#T(t#G_v4R`?a@<-Piv*Pq@xwq4qoOA#r}UM? zrE7x~Fm$Qnx?Z)IafYzYzaN#2g13@<{)S;~whc3RMQq-qu~;Hv-DRGU@Xboaoh(J)#}p z-`4miJ%xh<{*1g7%)EWNDywuNn9Y!DfAeP&(G0ztEl$|9rpl_E6g>zM%R`!GX>grH zK38(|A;troX$=W^`Hwg_Ej=#4k}E4+;wxI8QGd0sh~+{e`g+7pz#5;Yt6v^lan=ku zl;99JA-Mbspd(&wc|PYVaOq3v-y0+#5$TZNz^5o%6yX;~x4h>g)2@SYsK-|-$kH4K zI;f~)0{1-qg_&}xP;d0(7$h!aLNwfJDK_&rfBCtJ?dJ*3LyZ#TVLtju6l9MR&}K4$xa+4Qi; z=Q5|C@4V%eHYAotg~|ioEp>&lbj)E~m&}qN=-3puwjYMzOW4<`Tp7fJTEbew9QE;w zHEq1(lalgQksEbQt22rdO2bsP3q?S%h(G zOkb&=`ktUNXt~YMpwda5)L4)m3p<8uIMrqsG$mh~Sy8K^@E6pgd2Oj|Ag&bjI~f)j zdxyyQpBo@dG?g)L%yXln3h+u@H+5zAL|c;8wV%}cA43s8y;Qa-oW zFn=#ZuNO&6Oz6F)@^@A0RAV*nwci^N7qHz|=ZzbQCx;WZ)B))kF}4yCErF+LPGIGZ zqg{Eo_i_B;qD-y=W3%lsBA@ZITT|AI2(i_W1EyT zm{DTR2h7g5aN_}uK8|6Q%cS9qllm|rcRZ7KQjc{v7$ZE&p63qD+CDH-K)E5c@7o*d z+EVzsD|as`uI+-y5z!X$!~TJ{w{6~yg`U|EPTdpKxR0ro_tb&Hh8EYSf`$CP%v+=> z=mt^SqjPQ0Cw`BPSl99B=lv}4dcn(E#CV>fk09!9B_R!Hc?x>azmv%0Zi1#=N(i=1 zO5C(soO%LAahrx!EZ2tsd(qcV+cop7h}yLnU_u(R#A_6^&WFSECuPo9GngVm`$^4V z%(B;>91yBvfCl)o4)|mZOYtdg+umvn%mtuv^cPH^xz`QGOhNflJPP;|)dUmXyGcs3YNd(jeUn2tzwJ?^V#{PP z18m$5q-9X26byh$Kh=^f9BVfs$YOixoa+~63!*f{xdY4{KkRQKff2+vNwp3oLu80J zOSCO%v~WZa=OegFv_HBL+=CT!n4CW=37UL$4Z_2ZC^gvutY#K7aiBg^Si0^j5>fch zwYxOLHq8q{<8jCa#52TU<4h0`GJc|#98mo0DEsPM6#XyBQ$?FwE+tUMM3X|`MQ>BI zi)xVYGDXURuZxuPPY!LT`Jz!rN7}}DItYJCEnt(#n135eLz84=et`QS(~d~F zUuoYKbJILh^nz&oCR2#}h)yL21^ITov0uXJiNuKknenQSTwl|=IGfzDw zv>;srZ-~f1v&^<^1a0zxv|hxO?CLB?pF8uKe%;eTg)W}JTg-Od8H z=-(oZB4Wzx@|7D0OKv^&n;s$a$BjX+A>2dc7PO8h!0{7!{i7nlUweC zD^0?_=DXom+{fElW<&HD04KPNCBv@-JYEw}bxFlhhRC0>(lJk!*>GfCkyw_6m_BJv zjz(uJxnz(54C{VXif4;yn(?%7WhdSMOUh?jx%BVYv?*~F&<0$=2OMqd$28y>(NsA0 z#Ji0Dxi{40_ZMQEutd4%PQ35_9tTdao6-d;B;?Ll`i}kGQcynX7k#kdg7QFV6#X-Z z2_8!k((?CReS8c1mXqs`dX|$1pxtUl6zOt^?ON*f@q`L*i^ykaBp@BSG#aa5$yn{a zP!it!9K}tGIDmITLIXdbUrH*Q($w98_x!cqy~o=}Z|k<7q^T^NHf5IBZ?ukW@35rh zIoa(Q6&?mFP5r_`hj{O9%5>TcyNlxH$%P;=+y}lNx?7hwCubx_&(#Je3GdF3HZ%%A zN~Qk^2?zXXPgBY)6!6UCOUP2ZTpHsxFA#lthzcJW~=F&LKPN zHdsL?^b%0Lq9z(_?dH;U{N~pyWdN2p6F4aQ!G-Z5pY?d%8fIkVi)M^y59sB|bid5l z5c9wPR>GksH(>OM>iLR5kcD~(mJhp$Bp;XLwpgxXTW)hHPUr;qF=aF>2vqNF1jScI zRif#1=F3*B3P77EwR>D3O>?1EhH0GXeEC$9zx$1^>3e%|@HceZfAopCBY&vA;}s zNFIUo-j?%wuNI+x4i>TW1d?$Ji;*__bXDcqSZupu2*}L!c8eI+=C9mIASKqkLoUXIuYgU^8n{jYfqJ1!>F2HsXOnn_A z2)=v=1mMKW+jL)!!eL>+a~4H5Tlcl$q%yPl&xUn#7sLLZL%+u7YNij$L1ZEP7^n*n z-g&pp$mjmElWNIZRZ#+=hvo6=m#GOJRocEkd|~Go3p{;2t86SgD<_F#SK6foY;0Y5 ziBCf{ZGJSx{enVb_|yrtl*Tu;X39_ef9ul=1bP)0`B586c>@4GyaRl ziY9(-3q?Ui{+^#;`ICksV#FMwC(3~v>pNgtP3@OFN9K+AIZXpF(b2}d9#rdx_Qzd< zbflqqH%gfr>)(5e2wdoU9?6^z*FAE!j$#z^@Eh;rNhk+w!##Lt} zTE2~|cQll=^QaGdtlHrcQk)}qT&Z(eqxx|}sGOo_RarH|Cb&c(Ud~j>g)QRoQcWCw z6r$4wRir`EAf~#234;sJ5*tqJGNDgNK7}9d(g``9?Y@WzK5vZm4T;XI7LVX=OY9D3 zm^7$Y$(UWH#nkOGo8d(R3fT^y5gd)*J6bk?*yc9sHQvjef&k&8J0LQV?&Yg;J4Evu zD}BzG^)kK?V^lTQTlHS0INUxY#Z~Wq-lU?b;U20i$TKJ=G8Er|Z;?q06S>PH<2b)@ z`l4b2lL6jmRe&xQ-DxsbO|8zStn>OM;RP$S4Rxj2qqOAzxw7hM)9 zG8q+E{G;YUyCRl`f2r*t2ARCWI)?$B*|(;n&2E254(t16QYxAJu501_MNmw zs_jCOugl_%%h;VQ1P$w@ca6yV;hi61ukyJL8KM|5BWJ|ELKIf(N;7W{lz^s4ZS@%T zE0N7u#B&<9eoM020LYC@3tVdL-D~@p<`MZFd5(DY%45VwD7`_&FSgnxz~i*@E>qsF zTu;6BMW^GO?YZF$g`==zR;@$&q43$mXg~RDI-5$*aWc9r&u;Xz0rSl7#a%D$6AWfI z8(3yQVu>L#8%*_e#7WwfTn|i&jocU&qhbw=GTWu*ayv|j8FGz{a+~>Z>)KldATJBilKvn69g6=+5iDzT0$s=kF9g!;IddmQ@6-9S& zh4kTA0O$8_=IZyh3xRu9;OSG9iAbuf^^Q(9aw0t3P40XD~0l1!lcWw+b_uoQT zT!FQ$*;$s|qvCr!8yBLJ>bk?^OW7_=k@3AKuDb4j>-I<|7w)l?I#{K0Ec{T6e~?n_FHJE(_~A&nH#{2etaJ2k3KqQ3`|o& zimr}5UAcFaAIWo!6&}^EpbOY8@*&t{Djk|jo%rbYd8V-n2=P8*^ zBr67HZh{@Cr{fo8z!;Jws82paRwWz0pA?@}!;uZi$T(j1r^ zJ%O&Ix5?pheh+$J2?JrBg+=C#i?Z0TiM}{-+1xn#>dORH8dJJ&l`||$DmkYoA+#Z1 zK#p<_Q#Ph2bzKDE_-6IxaaD^Slf%qwMn))<`<2i@o2iEQP?wzN#8{Q^#qLvE)VMog zmf3~)`gE!3C-!&qFYF@Q*{^CySmqW0@7d3Sn$-&s-!FOfsd662p|0JWIL-!BTDm17 zyt~0;;AOT24`f8R(2RBYQYcW;SnAUV(Ao0Dy(zWMD@bke46uhxr{N<2(z?*WSyL5( z5??xF$0v3B6zqMeJ2Mwju>vbR2;ha|zEig85=Ui#IC&5m7t_h#{g0+BX^{$5{b=bQ z+;sD`{wqIRascWr3HZB(a&LFR(9P9f#1wGX%lE5ckB{&l$iA z{@R?kc6XIh?#3BymtXqd?V6%Hk@ps5F!D z_E^+buN*^FHaS84nMsB^@(X{}Gd`Ehs(#~kc`{-&BPK29>ul<=QOP+k!eX)el3F!@ z5A=uxq?58-)qD{ln`O1MxM0q~s3L;@Xde6v5dr4!6dgo8S!a}MxMW=nS7Y_uWu_2Y z4^tOXr=^^m62N7$y3*fHWIeXn<6TQ2!c~SQ5^-%i>wX|ph?B1eJ}TEq@UBdlyqzH3 z(WsQ1&!!U(H%ZH0;OEDkxp3-6i7^VoBqV(y2I@8SBt~AQ&v=3`bTbgIgazN8!IL#HhDO|Z8<;`)d zlWsw^EStjD`0QgfdYkN_km1h$^H}j0IyN|R8Y^D07mJI-@tVae9?jMX z+DiDrs$pjw!X#@5bj}7cbSGV)A~ab+38zit;@2&hlR7&*8e$hH;?<}-QU7_Q$PtR< z%M-yz`j+{hq)!l+y;*12;YNY0E{eMI#HZ$XF!c#!6H?1O1TuDwd&&_m{fyaw5hjAt z0sX8wXL=6jWuCC75Tfpj{M3>YHvT)8DY<4x>8I$Sa%h4ZN;%mm)i`Va>P}lsYTbf1 z!7&VTU$+ZDE+Q#iG-PUvYIV&dF2?U6s;S?hLC*q5cN&rpH#^L@b%Rze=yqh7xirqe zCk^H_A@XU*Ayj$1!HEF|#Lu?tI9eBb)jinA_+jfY6o+iHA>!$X`?JN8!K*8?_@wyY z#_75}vPW>{`w>Gs$?_0#kU6}&f0S;Vh9cTj?RK~Q2=0?vxDM|Wp#wvOG|Q2RlIGO5 z`eXj|o-iOJkvnLVlNyMsSM)7tDM}}cYi36<;#@!rU*zvT#@_;W?qZEQFDVhvE1MPf ziSIO!y|rS>227HUK6JuW`K70yJH~sUQq$u3GSf7 z;fu6|VWh;jKMAKq!a_%7^Ns34mh{VA;FS%}gE)}zWSIvGjd-H%iN#`~X+_DcQuFZo zTDd(Tcd4j(-lP#|={Gz*>hw`uP-xdS~6D)+m0vKh~qo`Yh&d}K->kk}-z{HDK* zmvRsJx$7?GhUfWxM`;W~g~xjlc;yalAY2}uR@6X-_9_Y*4lIOjVJ$H2g)((pZ;dJ7 zYOd821inkLVMBM7NTQ0L<6QFQMtH}noi`xXk&dP#mkg_|z4*nN^-9^r9wSkd&DOp-SYdBssQRJ7*#Nc#}jigflffC^Je-Q&ZXN!NPQsA zrQLjSuL&57(kd>uA5K%lQ);|&rBw2}K#-fqua}EO(OgTpHwe^`A;`_`wwOI150~9? zbrPKvrcFUh8aZH5c{!k#L{W=|lYd`%!H3&DJ+`^i7^cb)%aA6TQUjoww=CH!DY1ba z=3gX*D1BYhSN}d#r>#p3U_#Vm57`qYM%jT%ref)U+P%BY(Tj*5N^N3&B{QQsez%NN z4sE+D=&>uS>w`&WP^Z7eOVpOk6roK|(a;<>-Tt7G*1CD$<<2)-)nO&bHTPB#v zg)D2u+B5cW|K4Rvz%b{*O?_bbfqB56C)CDTZ7q0vaQl;f zt6gi)E>-9a@d4yOOeKTUlDo6-7Pv8Ng&u>Z8TT+2M&6)e<=Bxl;l@&5?%P4&_FSz6 zRTqj}|MI!G4`aR0g^!Op8+y|3rRATvE%Q4nmDdRAXk* z_+V@5QKxN%oW*;@coH4Fuhut+Kn8&7=+!ZRck~?a8B@xwv@{ajY8r$nBem@&{IhX){&B)?Bd-im8HM89SjIfIw$k+G`c#@*<*ThKirj z*Ot1CrY%Rgu09EeCR)k6K!)DwE$N8<8KIuMAJt>{!GX02+mmcFDf4n||*C2OD=2)0I7n~qL z_$ulQc9ADsR}M5KN=~jS4H2GBFZvtKbGersr?!k2;yY#4jss%;IKbKFOB({AZ&ic?P$v_E>@dEU(R$l2tnFyliSpY(I|$KSS7Y&%Q5>;9WT;OdGRqOPC|?_>s3*|=55+$48rHYWUm zTkSp3;2gfwJAg=JE>5u33{vV0$LNv|722nYMPO|xYsJf4BdEMEM#KQrwO<}Kd7b5m zxkjkA&dCW*@kJcU1aEyjr#ZjEoMGArYj0q1u!`aWwyn_8Dd?eEq3hZ+NWA^6ufWUu zSFi~t+FYFBQ$l0^(T@7dV2 zG2^NOpZQv+ML^G_MM@bFlSC8e3uhkZ#x5y~pV5m50VQTGNs;*9SgCmlmtG6xq(`#T z0mQ66oB?~Gw`XWZOt!y@lT28jn`NKJ!Sooe+t+ok#a_K4p*U;`!|lS{OkNwpz8ES; zc_n8NgT7!^uGPPxrFoObvNtxbJ%vzn#IH^Ft(rNI@l;$po$Z`9cyG+uQ>IMyhw#5N zmV$?I{H^&WnCmvOUD0QwhaBFoIi&2Bz~(%gFniv84vkH!5-$E%0G0}A^~&9Yd4ck} zTutKi_b5LWSx2WsH4l&Pm4=G{&u+1IB|$-BtotgpWuMC_4@w`eRpwQq>hA{-j2L$} z^T9Znm**6fP&vXj6Gm5({yBr%uj<n8Tp zf<)I6g%PCTlAQA~YcABw5FrDklU{VKCw|no_ZLVg(=C1(b|8(qx5t7CfFIFqVULWYc9=dS-Ewd;iImaUqlO3$p?GY?$-` z%rf7p%PNH42MFTXFB==W?@!;W>#*>w4$sTc==2)X{^&xQd`zjtxNUC;`CuIxI)4zf zPir1^cr=v5K6f3ypxTxJ!Vb7;7LeT^JgSWK3Wc{?uJHq$0b}7%j_=i^z44@%3x9eq zSt;^6tQymdHjd}INirJv+~|jk0tfkw)mQHnlRLVy*DA>{o289*TlbJURk&!O7lg0U zUd>Qrm0*T;&>!rksu4E$%1iRBRtzgX$%5nIiF9i)Wi-h;uXt|17hfV)!Oh~OV6b!q z0m z)|VkCyGkyUR!b5Kglpu(T&jRF4Sdtb4Rnt>%H!PfV2xwU>?Db02G$jLU*Oe+7_PUL z@#XI~{i6zgcArQ_`C@x#>M`_+G|N@Zbf;!Kl5`gnpSiv|ok_dC2UOo^G3w1jWN($I z&&0`BFDPT4^Tb9A*Dc%4U3z5U9W21Zy8>!MseR<4VZ-Jg^;Kj@6Q{-|>*uL-?nCIo zuX5&%K+sL8uIXr;Le(Uz}LAiLJ^6&c^WcYxy`4z=R}iy)u2f4wY<#LJw! zKSJ)UH6hbGeEL{iz`kv55F-D?o8cZ|k9Bj#Vr3+@I3qqw%esvwKwMJnkTIg#uI9y*ZoBNbF}yv6M=)EipVrS+Q@Fl)eX0K#00Wx_(B zz)T7>HYJJ$jK6AYYi7h&7C4`~vTJY|_(3TR>q~Th#lzw-B4~x_)VqMLf;ca`iN??_ zDq&=(oBKhe0xI#+b`XI!I~=CwYCn^|{HrPuq+x}1#}ET(E(UsVTMQgEf4q}9XOwD75hm zb{!gtep6a;82@h%_=59*Ts8*NQxdaEz5CmyaKr@!3NxRgc>`Z4a?#iAeBBLsRk6a zC+JyNHVPNG@k(LDN0u7dK4!)?7yHy*FG)lT+>{=~BNidq)L(8uR5BQk^db~Vv;nkd zZKxITuPHZ!YERqZij34ukB(-+c3xsPPhTjqjv7Zj)O=-Z#v7Td;+yhC11P+X3Hcz$ z5MYMVb7QpKf$`M!sYE6dMEB{KeAo9$FNEf$XA}>jy_zdY=xT=GL9UP7V{Rar>-72f zUX^g;P^?7DxKY2)y~+3G?i2!IauhLqYb*Cu`9LGa!s^L?ug;uMZ(g*@K81}+r~j+t zzZW*dS#RWB5HtJzwguPodJL1XoyPGhPmv94-6+#q=aNjAnKU=zj=CPaAMTtRKe)w5 z%S$h^XL5NHp}!?~4$p18Fnb*o-nGA}PvtoflB%ws32QK4?vOHo8ZyUhII&Z0aLYxD zNbR9G?;X<&jh!(=P8fkovU?Q&1<61waP`)7!~@fp+P=e zy{i7rS8;g^<#iKPaN=4gt1Ti7(Zud|AdPRMlDsC&)n=y$Mf(l{EW-(S$+IE_J$okD zUNQrsO?K7Q!Qy zT)a(xs?e$?5e{FANw67Cw>-dKrj@tlqyOF=~u9xH@wM(IyCfV z+Tck0Uwusu41JOpQS~?~k%zW?IZ5o}RiEkfh`iKvVNp8CeAwabZd5Xl&&;ka}`}+uH)Y@U+LFJ@2=D_@OfLkSzQnPiDFW`qouzy zBWW1CP@p6(1V^h){Dj9KY#^d8cr%L7$YC>f?F(>-OS!DWo>=2P2J$A-9NTTXEIwyW zI1covIs>Lrtq4(?lVcDopi+XKX;I%z)T9qH2XYY~<(MYKVqNvkUwayO?B|F+9&=tD zv~xfWEX+}^$e4K(BoyCaJ4m0=T?=6>wzXtRZWP2@XMSJP?_%;@9%L4=#UmOdWWjwe zlc9L3_B3mXv47I)urlM=h?0cw(Jo94 C6BwWX literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/sb_sources/SB_files/legacy_elftosb_sha.bin b/tests/elftosb/data/sb_sources/SB_files/legacy_elftosb_sha.bin new file mode 100644 index 0000000000000000000000000000000000000000..66fe88ca5105cd93a8ef60b75b8b24cec0c1d072 GIT binary patch literal 19584 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 literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/sb_sources/SB_files/legacy_real_example1.sb b/tests/elftosb/data/sb_sources/SB_files/legacy_real_example1.sb new file mode 100644 index 0000000000000000000000000000000000000000..0cbda568cf7df69bfd2ce0b2d7a3ed465b4dbcfc GIT binary patch literal 19552 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Ν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 Div|(> literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/sb_sources/SB_files/legacy_real_example2.sb b/tests/elftosb/data/sb_sources/SB_files/legacy_real_example2.sb new file mode 100644 index 0000000000000000000000000000000000000000..d244a79e5ba63b46186767038c73bd39ef1a7840 GIT binary patch literal 344528 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∓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 z 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�cNo^PV_9o=wb>|VzFtFvzlWD=2HC)y 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 SRmg}AyMvw~)2Wvqe@0x`Qe`Fp literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/sb_sources/input_images/rt500_quad_flash_fcb.bin b/tests/elftosb/data/sb_sources/input_images/rt500_quad_flash_fcb.bin new file mode 100644 index 0000000000000000000000000000000000000000..4b581fde7b85d27bca5747a3e37872e0b99dc0d7 GIT binary patch literal 512 zcmZ>Bc5`B2VGLsc0!C(L5JeIY0IFtWVMNl?fW#)ue9a=k#URIG#G(dK%gVx{f=?G4 x3qDx}1_c%g4*V)mWpALVX9DtB6j0S5^AZTykF1t1Y*5exBLNWu2o^E+0|0GN1=Rom literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/sb_sources/keys_and_certs/chain_cert_0_v3.der.crt b/tests/elftosb/data/sb_sources/keys_and_certs/chain_cert_0_v3.der.crt new file mode 100644 index 0000000000000000000000000000000000000000..74a4ab61b308162806be446e47236fee2c0f34b2 GIT binary patch literal 1645 zcmXqLV#_pWVhLQp%*4n9L*3>qgIavN~6F^96S2{SoI8HyW- zf;b#Je9l#=$r%bksRgA;Iho0ZvIf#133eWlp!}-5{4#}t{1kyTGbpqHGV zYal1iYh-9(VQgq%Zft659wp9e0^}N*LAis{%}q>7$U)1<%D~*j#Lr;R#Kgta#Kg$3 z^TL`XyNZ?v-TRVw=gpaK{QB=rzAk!f^7X@xx+!0ar+qfPusHO_cVGK7qfF-b1(|an z>g-}HX|9tuGHh>5?$*78P>w&oXbF8*!&_-=djwZ~s+Z&iqXC ze(bNq&i9mL1KrO~ihZ%=l9|gt{<<3v=Pp>RvT<$FgWNw%4#J1T8xFs2JaS=Oi`n)i zQxp{Ss}2Ni=9$O#NPcaUg?b%*Qa79L&F8Z*?^7>EKE1tT*DAAHe?MK^#l*3; z;Ke5Il<4_gQnI2C*tQ9^+u1(dB|C4=oZn45R=(Yy{Jb{(Zeu)x%l8p=RYO87Nra1W&BoX+21!c_~Wn5RqT;bU*)B>x36a0=E1)FvEw9LsSnX| zn^cO^l2NJRp|}v#=U4Gcx`+;0E#dSy-5vn7$yVc3?pQOzn&et1i`Y-EWz)>GI*- z5BA;;xpbrZpJa3F0xx!N=5^;MD79a7JOBI3f1CDhiOz>R56>u2P}1SZa}yT0Wc_fx zTV2ME`ZLSi$~PBiy0Nxx-EFpDe#5z!QZgAp zAB(nbad^wN`Jkkj;qtP|UpG&cudD5dXxz2vp{uL$p<1ZJs8XNOD zwch3Y`mxgc&qLw|GTB_@2iP!C94CXgA>28{WO|zpTFi_<+PVxn>Pi%EBlnE zchSpfCf~^0$f7pB~P*sX0mWd#8ND z8OHkQXEsb;thaVj@{icl1{Rz=qU$%x%=x@Cqv@mC;{!7$l{zh5pf$kJ;oceZ{^nEu^Tt=Ry*NwmJ$3){l~Z;v1aA(9*q+Y@z0mP+_}B&_AzaVFBOhgF7M;o^30B5`PxZq^^>)- zYuP_FnD&*IKjxdNxtTS9FF8IcHQHRv+$%a@()R;tb*1tbvl=cjI;_ykei;7j;jvbo zxG4(+IcD%Wr`4a0T<`cZ!~Bwun?lrl@l{Xe{QLcR_WSg?->Ocz4j+ McOw0s>fDR906hnr{{R30 literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/sb_sources/keys_and_certs/chain_cert_1_pkey_rsa4096.pem b/tests/elftosb/data/sb_sources/keys_and_certs/chain_cert_1_pkey_rsa4096.pem new file mode 100644 index 00000000..5739775c --- /dev/null +++ b/tests/elftosb/data/sb_sources/keys_and_certs/chain_cert_1_pkey_rsa4096.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/tests/elftosb/data/sb_sources/keys_and_certs/chain_cert_1_v3.der.crt b/tests/elftosb/data/sb_sources/keys_and_certs/chain_cert_1_v3.der.crt new file mode 100644 index 0000000000000000000000000000000000000000..d2a8f5fb077983c614a320449a2dffa640cd79ed GIT binary patch literal 1649 zcmXqLV#_sXVhLTq%*4pV#9?!ofnnFKd$$aD**LY@JlekVGBR?rG8i;YG~_nmWMd9x zVH0L@jxrQC5Cw5Kc=(*FQj;?jf>H}glX5bX4P_0aK@#jdB0>38dHH1u1^FooL5V3D z`NbuvxrRIjTp(#K9%jFY07EeY5fG1=hc_%gs{~|@Z)tI6j-iTy65M)DMlsPch^$^N zNY){*qChV>Ki5D`oY%$IcOPK8JL@x_!$hE zn7Ejlm>3!EhdMZjFA!4|-(t`mln}8m`s}ww2P>zZKT+p-px)wznXvJT@S=pT+ipr) zTJiokHveAdN^`gBJ&A^b^;)|b{W5x-yNf1ESyYK}ZCo>@F7w=HS+kB;Gq+EAbXF(M zX7%}nH_C5Lkyl^Ae);HD`*&MYWZv3xJWWp(O-W7NZS|LNV#$w}3wBN9>ND9e!TVBu zI`2|RzJhwSva@E<|Bdckf2Y1Ke$$(KZw-vD>MS#1J|UO;Ax$jIc}Ml5l1#SeYkJah z&s@Bi?B?tEd%^mi$qPdrnT@Xs9q;lNTzB-pq_TrSbT#vJi*VKA(zh zSEM^aS+~1Al;K0u!x6npt7} zQau~Ewk6D&)xA_fTP8tzfz>@u#h`QlU%5G^`p$Cl$kN|gb}vJ1O=GM{^`qy`<_2p* z+UGtxvX3YMo(S zseFC;p>Bx@rf(8%Z=R>OwoB;Mud>fp%iMEqP0d>N-qQN{_nFJuXAx~P)c0tqyCCc?%X3w}f7)Mv zgXPctgTlq`|0BXyI#qhiyq;bk%Ow&z_OyH71R+xp!fWd$pK@b8!DdC-NpSzKVIoi>CE?i^QW7%FY#V&y7OGCxlFn4>B2>` zY786_joe*AOvr`hlFdB63gt}Z%%cvJbuIef<#na7+g+FDw)qD-a8cp~ry=+YuG5Pp;+~I%F z^ncU3vhB0}d#zf;yX}F&s^#h?{Ac}2-~W%aOSo&aq$RsxPR+HV7ptRZeq#75_OQmr zx59BQ`}NRo3l7igKGwP5W7nxb9TU^s+CZ88+aofTWJw*0GdjpEz_z6&FuY{;CB2Uv zVs0j1Gy!9iX;- Pv8=fCduPMDwdP9zu05Fu literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/sb_sources/keys_and_certs/root_cert_0_ca_v3.der.crt b/tests/elftosb/data/sb_sources/keys_and_certs/root_cert_0_ca_v3.der.crt new file mode 100644 index 0000000000000000000000000000000000000000..737301b3baf5c9a99c4b8efd1c0bbb595643c25c GIT binary patch literal 1645 zcmXqLV#_pWVhLQp%*4pV#K>sC%f_kI=F#?@mywa1mBFBKq9L~dCmVAp3!5;LbCjXD zfhdT>!NccVm71KP5R_U_nv|27Y$$6W4U%Bz5edq#%F8cPD9BGy2ue)J$S*EQ%{AmP z-~vf=@i6;E1Q?1Lh=6#^JiKA~StTHId`pWna|~4sl;GBLGKz_oL1guEL9z~c6$N_9 z`MCyi;=D$N1{TJK2Bs$FrUp^sye2@dkr|XbDBaw|q=X!_jI0dIO-%d@22D&{OifIT z3~w%$ewQp(nfvf%jB%bPmpz{>!$mvRz^{{I{Q9?4%0z33E7nE)I%yRV|KN0f(sB3M zyf?NPpP3`hZ>C&eT~eX>we?dzV{BH5`_=~;t{Z31t7|*mxb#f>m7>~IN!C}zdjH?# zC|+sU+ui#`p#IKt&L8Kl|2w86|M$3E^C1r3);Cj67cn!1G#t=b(sg0qY4tTdA`c^{ zY~A`_?ZPbX4)w~@*noT z6z?>xxIpZg@R~In7bP%-$NC9IY25j|nM-kGKbtQTbeq`ByiMlk>71nX4` zzm&c!WVy9x)~>r=*=f`MrH6Dk-;)r3DtYd{nOMn#V4m3(96h>g1d^>XRz>Zcs&q=R z@b}W_6DPR0@A5HNSt#-F{@&ZGqF#x}I;Z{jy0mqUZTgv>BTe6mR?9V~Xg_$t`ylz2 zX~{vU9Y!-2ufOiH`Dw$HYk%%pzm{iWW@KPo+<4NU@t8s53Sj1tRc2{iV9+>sVdL~A zjZ+3=nr;G?7Ca!A3bU{pFf%g#H{b^G_*qz(nV7yHr*>dL0!;0U45#(3)zx$wvE2*Y zA}f5u#q34ul?Od1)4P^6H8nI&fA}%v-$!1P%}mD^m|dNErMvm$HpR+V&EWS>S(Uub zK9Abo5;Apu>#c|4VKIIu-Rd6NW%E#-ktx?w8*m|aRar>(eAvI?{ zF?UxKE}46$@lf8uotYQqHrXoie~(|zl(6=_S-YP3Rp-z z25}!>9FXw7s>KkTYP;j((-T?-7CelnHofdh5e?IIakNSF&@5bkRr-Tz+lQ$yJo|Iy z{H69Y`G(TEZzd;Zzg?1iRH6KvzEN%KwdyhC2Ow@~vD$Cbi@#f{SS7KAt zt{jZ1`tZWI_(Eh@#NVcZ{)LJk4(;ctyAiR6kC9jO#Q)o(9$lXp++KiKGF Oi&S;d`3ovHjsgHNOriAv literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/sb_sources/keys_and_certs/root_cert_0_pkey_rsa4096.pem b/tests/elftosb/data/sb_sources/keys_and_certs/root_cert_0_pkey_rsa4096.pem new file mode 100644 index 00000000..be4cda60 --- /dev/null +++ b/tests/elftosb/data/sb_sources/keys_and_certs/root_cert_0_pkey_rsa4096.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/tests/elftosb/data/sb_sources/output_images/audioImage.bin b/tests/elftosb/data/sb_sources/output_images/audioImage.bin new file mode 100644 index 0000000000000000000000000000000000000000..3e614e6ba85f7f68878a3dc30963c08fee6256a5 GIT binary patch literal 96256 zcmeIufdBvi0K=g9Q(xc+g-~I@fB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK afB^#r3>YwAz<>b*1`HT5V8DO@18-n>4*&rG literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/sb_sources/output_images/bootloaderImage.bin b/tests/elftosb/data/sb_sources/output_images/bootloaderImage.bin new file mode 100644 index 0000000000000000000000000000000000000000..294f4016d05bdd696670c4840f1f36a71f9239de GIT binary patch literal 16384 zcmeIuF#!Mo0K%a4Pi+hzh(KY$fB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM q7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjMyaxtA00031 literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/sb_sources/output_images/tmdData.bin b/tests/elftosb/data/sb_sources/output_images/tmdData.bin new file mode 100644 index 0000000000000000000000000000000000000000..a64a5a93fb4aef4d5f63d79cb2582731b9ac5063 GIT binary patch literal 512 NcmZQz7zHCa1ONg600961 literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/sb_sources/output_images/tmdImage.bin b/tests/elftosb/data/sb_sources/output_images/tmdImage.bin new file mode 100644 index 0000000000000000000000000000000000000000..dd733ef66940fe6b68fb3439064ef5c4907b2631 GIT binary patch literal 229376 zcmeIuF#!Mo0K%a4Pwi_6h(KY$fB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM j7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFz^EdfGq$4 literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_ram_crc.json b/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_ram_crc.json new file mode 100644 index 00000000..316b1dbf --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_ram_crc.json @@ -0,0 +1,14 @@ +{ + "description": "this file is not supported, as lpc55s1x doesn't allow booting from RAM!!!", + "family": "lpc55s1x", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "RAM", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": false, + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s1x\\mb_ram_crc_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc.json b/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc.json new file mode 100644 index 00000000..53e9fd9c --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc.json @@ -0,0 +1,14 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old new compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "lpc55s1x", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "Internal Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": false, + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s1x\\mb_xip_crc_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_hwk.json b/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_hwk.json new file mode 100644 index 00000000..c224cade --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_hwk.json @@ -0,0 +1,13 @@ +{ + "family": "lpc55s1x", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "Internal Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": false, + "useKeyStore": false, + "enableHwUserModeKeys": true, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s1x\\mb_xip_crc_hwk_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_hwk_tz.json b/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_hwk_tz.json new file mode 100644 index 00000000..f464f8c8 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_hwk_tz.json @@ -0,0 +1,14 @@ +{ + "family": "lpc55s1x", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "Internal Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": true, + "trustZonePresetFile": ".\\workspace\\trustzone\\lpc55s1x\\tzm_legacy.bin", + "useKeyStore": false, + "enableHwUserModeKeys": true, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s1x\\mb_xip_crc_hwk_tz_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_tz.json b/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_tz.json new file mode 100644 index 00000000..e8e116a6 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_tz.json @@ -0,0 +1,15 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old not compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "lpc55s1x", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "Internal Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": true, + "trustZonePresetFile": ".\\workspace\\trustzone\\lpc55s1x\\tzm_legacy.bin", + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s1x\\mb_xip_crc_tz_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_tz_no_preset.json b/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_tz_no_preset.json new file mode 100644 index 00000000..0121b906 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_crc_tz_no_preset.json @@ -0,0 +1,15 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old not compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "lpc55s1x", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "Internal Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": true, + "trustZonePresetFile": "", + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s1x\\mb_xip_crc_tz_no_preset_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_signed.json b/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_signed.json new file mode 100644 index 00000000..8a68cfc8 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s1x/mb_xip_signed.json @@ -0,0 +1,20 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old new compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "lpc55s1x", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "Internal Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "Signed", + "enableTrustZone": false, + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "rootCertificate0File": ".\\sb_sources\\keys_and_certs\\root_k0_signed_cert0_noca.der.cert", + "rootCertificate1File": ".\\sb_sources\\keys_and_certs\\root_k1_signed_cert0_noca.der.cert", + "rootCertificate2File": ".\\sb_sources\\keys_and_certs\\root_k2_signed_cert0_noca.der.cert", + "rootCertificate3File": ".\\sb_sources\\keys_and_certs\\root_k3_signed_cert0_noca.der.cert", + "mainCertChainId": 1, + "mainCertPrivateKeyFile": ".\\sb_sources\\keys_and_certs\\k1_cert0_2048.pem", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s1x\\mb_xip_signed.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s1x/tzm.json b/tests/elftosb/data/workspace/cfgs/lpc55s1x/tzm.json new file mode 100644 index 00000000..48380cc6 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s1x/tzm.json @@ -0,0 +1,110 @@ +{ + "family": "lpc55s1x", + "revision": "a0", + "tzpOutputFile": ".\\workspace\\trustzone\\lpc55s1x\\tzm_legacy.bin", + "trustZonePreset": { + "CM33 Secure vector table address (cm33_vtor_addr)": "0x0", + "CM33 Non-secure vector table address (cm33_vtor_ns_addr)": "0x0", + "CM33 Interrupt target non-secure register 0 (cm33_nvic_itns0)": "0x0", + "CM33 Interrupt target non-secure register 1 (cm33_nvic_itns1)": "0x0", + "MPU Control Register (cm33_mpu_ctr)": "0x0", + "MPU Memory Attribute Indirection Register 0 (cm33_mpu_mair0)": "0x0", + "MPU Memory Attribute Indirection Register 1 (cm33_mpu_mair1)": "0x0", + "MPU Region 0 Base Address Register (cm33_mpu_rbar0)": "0x0", + "MPU Region 0 Limit Address Register (cm33_mpu_rlar0)": "0x0", + "MPU Region 1 Base Address Register (cm33_mpu_rbar1)": "0x0", + "MPU Region 1 Limit Address Register (cm33_mpu_rlar1)": "0x0", + "MPU Region 2 Base Address Register (cm33_mpu_rbar2)": "0x0", + "MPU Region 2 Limit Address Register (cm33_mpu_rlar2)": "0x0", + "MPU Region 3 Base Address Register (cm33_mpu_rbar3)": "0x0", + "MPU Region 3 Limit Address Register (cm33_mpu_rlar3)": "0x0", + "MPU Region 4 Base Address Register (cm33_mpu_rbar4)": "0x0", + "MPU Region 4 Limit Address Register (cm33_mpu_rlar4)": "0x0", + "MPU Region 5 Base Address Register (cm33_mpu_rbar5)": "0x0", + "MPU Region 5 Limit Address Register (cm33_mpu_rlar5)": "0x0", + "MPU Region 6 Base Address Register (cm33_mpu_rbar6)": "0x0", + "MPU Region 6 Limit Address Register (cm33_mpu_rlar6)": "0x0", + "MPU Region 7 Base Address Register(cm33_mpu_rbar7)": "0x0", + "MPU Region 7 Limit Address Register (cm33_mpu_rlar7)": "0x0", + "Non-secure MPU Control Register.(cm33_mpu_ctrl_ns)": "0x0", + "Non-secure MPU Memory Attribute Indirection Register 0 (cm33_mpu_mair0_ns)": "0x0", + "Non-secure MPU Memory Attribute Indirection Register 1 (cm33_mpu_mair1_ns)": "0x0", + "Non-secure MPU Region 0 Base Address Register (cm33_mpu_rbar0_ns)": "0x0", + "Non-secure MPU Region 0 Limit Address Register (cm33_mpu_rlar0_ns)": "0x0", + "Non-secure MPU Region 1 Base Address Register (cm33_mpu_rbar1_ns)": "0x0", + "Non-secure MPU Region 1 Limit Address Register(cm33_mpu_rlar1_ns)": "0x0", + "Non-secure MPU Region 2 Base Address Register (cm33_mpu_rbar2_ns)": "0x0", + "Non-secure MPU Region 2 Limit Address Register(cm33_mpu_rlar2_ns)": "0x0", + "Non-secure MPU Region 3 Base Address Register (cm33_mpu_rbar3_ns)": "0x0", + "Non-secure MPU Region 3 Limit Address Register(cm33_mpu_rlar3_ns)": "0x0", + "Non-secure MPU Region 4 Base Address Register (cm33_mpu_rbar4_ns)": "0x0", + "Non-secure MPU Region 4 Limit Address Register(cm33_mpu_rlar4_ns)": "0x0", + "Non-secure MPU Region 5 Base Address Register (cm33_mpu_rbar5_ns)": "0x0", + "Non-secure MPU Region 5 Limit Address Register(cm33_mpu_rlar5_ns)": "0x0", + "Non-secure MPU Region 6 Base Address Register (cm33_mpu_rbar6_ns)": "0x0", + "Non-secure MPU Region 6 Limit Address Register(cm33_mpu_rlar6_ns)": "0x0", + "Non-secure MPU Region 7 Base Address Register (cm33_mpu_rbar7_ns)": "0x0", + "Non-secure MPU Region 7 Limit Address Register (cm33_mpu_rlar7_ns)": "0x0", + "SAU Control Register (cm33_sau_ctrl)": "0x0", + "SAU Region 0 Base Address Register (cm33_sau_rbar0)": "0x0", + "SAU Region 0 Limit Address Register (cm33_sau_rlar0)": "0x0", + "SAU Region 1 Base Address Register (cm33_sau_rbar1)": "0x0", + "SAU Region 1 Limit Address Register (cm33_sau_rlar1)": "0x0", + "SAU Region 2 Base Address Register (cm33_sau_rbar2)": "0x0", + "SAU Region 2 Limit Address Register (cm33_sau_rlar2)": "0x0", + "SAU Region 3 Base Address Register (cm33_sau_rbar3)": "0x0", + "SAU Region 3 Limit Address Register (cm33_sau_rlar3)": "0x0", + "SAU Region 4 Base Address Register (cm33_sau_rbar4)": "0x0", + "SAU Region 4 Limit Address Register (cm33_sau_rlar4)": "0x0", + "SAU Region 5 Base Address Register (cm33_sau_rbar5)": "0x0", + "SAU Region 5 Limit Address Register (cm33_sau_rlar5)": "0x0", + "SAU Region 6 Base Address Register (cm33_sau_rbar6)": "0x0", + "SAU Region 6 Limit Address Register (cm33_sau_rlar6)": "0x0", + "SAU Region 7 Base Address Register (cm33_sau_rbar7)": "0x0", + "SAU Region 7 Limit Address Register (cm33_sau_rlar7)": "0x0", + "FLASH/ROM Slave Rule Register 0 (flash_rom_slave_rule)": "0x0", + "FLASH Memory Rule Register 0 (flash_mem_rule0)": "0x0", + "FLASH Memory Rule Register 1 (flash_mem_rule1)": "0x0", + "FLASH Memory Rule Register 2 (flash_mem_rule2)": "0x0", + "ROM Memory Rule Register 0 (rom_mem_rule0)": "0x0", + "ROM Memory Rule Register 1 (rom_mem_rule1)": "0x0", + "ROM Memory Rule Register 2 (rom_mem_rule2)": "0x0", + "ROM Memory Rule Register 3 (rom_mem_rule3)": "0x0", + "RAMX Slave Rule Register (ramx_slave_rule)": "0x0", + "RAMX Memory Rule Register 0 (ramx_mem_rule)": "0x0", + "RAM0 Slave Rule Register (ram0_slave_rule)": "0x0", + "RAM0 Memory Rule Register 0 (ram0_mem_rule)": "0x0", + "RAM1 Slave Rule Register (ram1_slave_rule)": "0x0", + "RAM1 Memory Rule Register 0 (ram1_mem_rule)": "0x0", + "RAM2 Slave Rule Register (ram2_slave_rule)": "0x0", + "RAM2 Memory Rule Register 0 (ram2_mem_rule)": "0x0", + "USB_HS Slave Rule Register (usb_hs_slave_rule)": "0x0", + "USB_HS Memory Rule Register 0 (usb_hs_mem_rule)": "0x0", + "APB Bridge Group 1 Memory Rule Register (apb_grp_slave_rule)": "0x0", + "APB Bridge Group 0 Memory Rule Register 0 (apb_grp0_mem_rule0)": "0x0", + "APB Bridge Group 0 Memory Rule Register 1 (apb_grp0_mem_rule1)": "0x0", + "APB Bridge Group 0 Memory Rule Register 2 (apb_grp0_mem_rule2)": "0x0", + "APB Bridge Group 0 Memory Rule Register 3 (apb_grp0_mem_rule3)": "0x0", + "APB Bridge Group 1 Memory Rule Register 0 (apb_grp1_mem_rule0)": "0x0", + "APB Bridge Group 1 Memory Rule Register 1 (apb_grp1_mem_rule1)": "0x0", + "APB Bridge Group 1 Memory Rule Register 2 (apb_grp1_mem_rule2)": "0x0", + "APB Bridge Group 1 Memory Rule Register 3 (apb_grp1_mem_rule3)": "0x0", + "AHB Peripherals 0 Slave Rule Register 0 (ahb_periph0_slave_rule0)": "0x0", + "AHB Peripherals 0 Slave Rule Register 1 (ahb_periph0_slave_rule1)": "0x0", + "AHB Peripherals 1 Slave Rule Register 0 (ahb_periph1_slave_rule0)": "0x0", + "AHB Peripherals 1 Slave Rule Register 1 (ahb_periph1_slave_rule1)": "0x0", + "AHB Peripherals 2 Slave Rule Register 0 (ahb_periph2_slave_rule0)": "0x0", + "AHB Peripherals 2 Slave Rule Register 1 (ahb_periph2_slave_rule1)": "0x0", + "AHB Peripherals 2 Memory Rule Register 0 (ahb_periph2_mem_rule)": "0x0", + "Secure GPIO Register 0 (sec_gp_reg0)": "0x0", + "Secure GPIO Register 1 (sec_gp_reg1)": "0x0", + "Secure GPIO Register 2 (sec_gp_reg2)": "0x0", + "Secure GPIO Lock Register (sec_gp_reg_lock)": "0x0", + "Master Secure Level Register (master_sec_reg)": "0x80000000", + "Master Secure Level Anti-pole Register (master_sec_anti_pol_reg)": "0xBFFFFFFF", + "CM33 Lock Control Register (cm33_lock_reg)": "0x800002AA", + "Secure Control Duplicate Register (misc_ctrl_dp_reg)": "0x22222222", + "Secure Control Register (misc_ctrl_reg)": "0x22222222", + "Miscellaneous TZM settings (misc_tzm_settings)": "0x0" + } +} diff --git a/tests/elftosb/data/mb_ram_crc.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_ram_crc.json similarity index 77% rename from tests/elftosb/data/mb_ram_crc.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_ram_crc.json index fd2ac6b2..6216492c 100644 --- a/tests/elftosb/data/mb_ram_crc.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_ram_crc.json @@ -5,5 +5,5 @@ "outputImageExecutionAddress": "0x1000", "outputImageAuthenticationType": "crc", "trustZonePresetFile": ".\\workspace\\trustzone\\tztest.bin", - "masterBootOutputFile": ".\\workspace\\output_images\\mb_ram_crc.bin" + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s3x\\mb_ram_crc.bin" } diff --git a/tests/elftosb/data/mb_ram_crc_version.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_ram_crc_version.json similarity index 67% rename from tests/elftosb/data/mb_ram_crc_version.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_ram_crc_version.json index 6d7252c5..903f8957 100644 --- a/tests/elftosb/data/mb_ram_crc_version.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_ram_crc_version.json @@ -6,7 +6,6 @@ "outputImageAuthenticationType": "crc", "enableTrustZone": true, "trustZonePresetFile": ".\\workspace\\trustzone\\tztest.bin", - "isDualBootImageVersion": true, - "dualBootImageVersion": "0x1", - "masterBootOutputFile": ".\\workspace\\output_images\\mb_ram_crc_version.bin" + "firmwareVersion": "0x20", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s3x\\mb_ram_crc_version.bin" } diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_ram_plain_tz.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_ram_plain_tz.json new file mode 100644 index 00000000..5b3c9c2b --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_ram_plain_tz.json @@ -0,0 +1,18 @@ +{ + "description": "The legacy tool doesn't support generating plain images with trust zone. This is a new requirement.", + "description2": "The image generated with legacy tool is hacked to have the CRC @ 0x28 and image type @ 0x24 zeroed. In output_images/lpc55s3x there are two files, mb_xxx_plain_tz.bin and ref. The reference is the original file left for reference.", + "description3": "isDualBootImageVersion & dualBootImageVersion keys are legacy tool valid only.", + "family": "lpc55s3x", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "ram", + "outputImageExecutionAddress": "0x1000", + "authTypeDescription": "for legacy tool, this value must be CRC, for new tool plain", + "outputImageAuthenticationType": "plain", + "isDualBootImageVersion": true, + "dualBootImageVersionDescription": "dualBootImageVersion replaced by firmwareVersion", + "dualBootImageVersion": "0x1", + "enableTrustZone": true, + "trustZonePresetFile": ".\\workspace\\trustzone\\tztest.bin", + "firmwareVersion": "0x1", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s3x\\mb_ram_plain_tz.bin" +} diff --git a/tests/elftosb/data/mb_xip_256_none.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_256_none.json similarity index 91% rename from tests/elftosb/data/mb_xip_256_none.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_256_none.json index 5611cedb..19868267 100644 --- a/tests/elftosb/data/mb_xip_256_none.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_256_none.json @@ -15,8 +15,7 @@ "useIsk": false, "mainRootCertPrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp256r1_cert0.pem", "rootCertificateEllipticCurve": "secp256r1", - "masterBootOutputFile": ".\\workspace\\output_images\\mb_xip_256_none.bin", - + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s3x\\mb_xip_256_none.bin", "testCertBlockMagic": "", "testCorruptRkhRecord": false, "testCorruptRkhRecordId": 0, @@ -24,4 +23,4 @@ "testImageManifestMagic": "", "testImageType": false, "testImageTypeValue": 255 -} \ No newline at end of file +} diff --git a/tests/elftosb/data/mb_xip_384_256.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_256.json similarity index 93% rename from tests/elftosb/data/mb_xip_384_256.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_256.json index 718c4598..92b1b646 100644 --- a/tests/elftosb/data/mb_xip_384_256.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_256.json @@ -20,8 +20,7 @@ "signingCertificateConstraint": "0x25", "iskCertificateEllipticCurve": "secp256r1", "signCertData": "", - "masterBootOutputFile": ".\\workspace\\output_images\\mb_xip_384_256.bin", - + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s3x\\mb_xip_384_256.bin", "testCertBlockMagic": "", "testCorruptRkhRecord": false, "testCorruptRkhRecordId": 0, @@ -29,4 +28,4 @@ "testImageManifestMagic": "", "testImageType": false, "testImageTypeValue": 255 -} \ No newline at end of file +} diff --git a/tests/elftosb/data/mb_xip_384_384.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_384.json similarity index 93% rename from tests/elftosb/data/mb_xip_384_384.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_384.json index ca9b6220..b9a6c3ac 100644 --- a/tests/elftosb/data/mb_xip_384_384.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_384.json @@ -20,8 +20,7 @@ "signingCertificateConstraint": "0x25", "iskCertificateEllipticCurve": "secp384r1", "signCertData": ".\\workspace\\input_images\\testfffffff.bin", - "masterBootOutputFile": ".\\workspace\\output_images\\mb_xip_384_384.bin", - + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s3x\\mb_xip_384_384.bin", "testCertBlockMagic": "", "testCorruptRkhRecord": false, "testCorruptRkhRecordId": 0, @@ -29,4 +28,4 @@ "testImageManifestMagic": "", "testImageType": false, "testImageTypeValue": 255 -} \ No newline at end of file +} diff --git a/tests/elftosb/data/mb_xip_crc.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_crc.json similarity index 77% rename from tests/elftosb/data/mb_xip_crc.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_crc.json index 994b3b66..1e31696f 100644 --- a/tests/elftosb/data/mb_xip_crc.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_crc.json @@ -5,5 +5,5 @@ "outputImageExecutionAddress": "0x08001000", "outputImageAuthenticationType": "crc", "trustZonePresetFile": ".\\workspace\\trustzone\\tztest.bin", - "masterBootOutputFile": ".\\workspace\\output_images\\mb_xip_crc.bin" + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s3x\\mb_xip_crc.bin" } diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_plain_tz.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_plain_tz.json new file mode 100644 index 00000000..7c425213 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_plain_tz.json @@ -0,0 +1,18 @@ +{ + "description": "The legacy tool doesn't support generating plain images with trust zone. This is a new requirement.", + "description2": "The image generated with legacy tool is hacked to have the CRC @ 0x28 and image type @ 0x24 zeroed. In output_images/lpc55s3x there are two files, mb_xxx_plain_tz.bin and ref. The reference is the original file left for reference.", + "description3": "isDualBootImageVersion & dualBootImageVersion keys are legacy tool valid only.", + "family": "lpc55s3x", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "xip", + "outputImageExecutionAddress": "0x1000", + "authTypeDescription": "for legacy tool, this value must be CRC, for new tool plain", + "outputImageAuthenticationType": "plain", + "isDualBootImageVersion": true, + "dualBootImageVersionDescription": "dualBootImageVersion replaced by firmwareVersion", + "dualBootImageVersion": "0x1", + "enableTrustZone": true, + "trustZonePresetFile": ".\\workspace\\trustzone\\tztest.bin", + "firmwareVersion": "0x1", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s3x\\mb_xip_plain_tz.bin" +} diff --git a/tests/elftosb/data/sb3_256_256.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_256.json similarity index 82% rename from tests/elftosb/data/sb3_256_256.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_256.json index 6fb5a02e..40c90d4c 100644 --- a/tests/elftosb/data/sb3_256_256.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_256.json @@ -19,7 +19,6 @@ "signingCertificateConstraint": "0x0000", "iskCertificateEllipticCurve": "secp256r1", "signCertData": ".\\workspace\\input_images\\testfffffff.bin", - "testSb3Magic": "", "testSb3ImageType": false, "testSb3ImageTypeValue": 255, @@ -27,12 +26,20 @@ "testCorruptRkhRecord": false, "testCorruptRkhRecordId": 0, "testCorruptIskSignature": false, - "timestamp": "0x123456", - "commands": [ - {"erase": {"address": "0x0", "size": "0x10000"}}, - {"load": {"address": "0x0", "file": ".\\workspace\\output_images\\normal_boot_sign.bin"}} + { + "erase": { + "address": "0x0", + "size": "0x10000" + } + }, + { + "load": { + "address": "0x0", + "file": ".\\workspace\\output_images\\lpc55s3x\\normal_boot_sign.bin" + } + } ], - "containerOutputFile": ".\\workspace\\output_images\\sb3_256_256.sb3" -} \ No newline at end of file + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\sb3_256_256.sb3" +} diff --git a/tests/elftosb/data/sb3_256_256_notime.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_256_notime.json similarity index 81% rename from tests/elftosb/data/sb3_256_256_notime.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_256_notime.json index f923f408..0285e53e 100644 --- a/tests/elftosb/data/sb3_256_256_notime.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_256_notime.json @@ -19,7 +19,6 @@ "signingCertificateConstraint": "0x0000", "iskCertificateEllipticCurve": "secp256r1", "signCertData": ".\\workspace\\input_images\\testfffffff.bin", - "testSb3Magic": "", "testSb3ImageType": false, "testSb3ImageTypeValue": 255, @@ -27,10 +26,19 @@ "testCorruptRkhRecord": false, "testCorruptRkhRecordId": 0, "testCorruptIskSignature": false, - "commands": [ - {"erase": {"address": "0x0", "size": "0x10000"}}, - {"load": {"address": "0x0", "file": ".\\workspace\\output_images\\normal_boot_sign.bin"}} + { + "erase": { + "address": "0x0", + "size": "0x10000" + } + }, + { + "load": { + "address": "0x0", + "file": ".\\workspace\\output_images\\lpc55s3x\\normal_boot_sign.bin" + } + } ], - "containerOutputFile": ".\\workspace\\output_images\\sb3_256_256_notime.sb3" -} \ No newline at end of file + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\sb3_256_256_notime.sb3" +} diff --git a/tests/elftosb/data/sb3_256_none.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_none.json similarity index 77% rename from tests/elftosb/data/sb3_256_none.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_none.json index 7b29ab97..474df3c6 100644 --- a/tests/elftosb/data/sb3_256_none.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_none.json @@ -14,7 +14,6 @@ "mainRootCertId": 0, "mainRootCertPrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp256r1_cert0.pem", "useIsk": false, - "timestamp": "0x123456", "testSb3Magic": "", "testSb3ImageType": false, @@ -23,10 +22,19 @@ "testCorruptRkhRecord": false, "testCorruptRkhRecordId": 0, "testCorruptIskSignature": false, - "commands": [ - {"erase": {"address": "0x0", "size": "0x10000"}}, - {"load": {"address": "0x0", "file": ".\\workspace\\output_images\\normal_boot_sign.bin"}} + { + "erase": { + "address": "0x0", + "size": "0x10000" + } + }, + { + "load": { + "address": "0x0", + "file": ".\\workspace\\output_images\\lpc55s3x\\normal_boot_sign.bin" + } + } ], - "containerOutputFile": ".\\workspace\\output_images\\sb3_256_none.sb3" -} \ No newline at end of file + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\sb3_256_none.sb3" +} diff --git a/tests/elftosb/data/sb3_256_none_ernad.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_none_ernad.json similarity index 82% rename from tests/elftosb/data/sb3_256_none_ernad.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_none_ernad.json index 0a70348d..018bbdda 100644 --- a/tests/elftosb/data/sb3_256_none_ernad.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_none_ernad.json @@ -14,7 +14,6 @@ "mainRootCertId": 0, "mainRootCertPrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp256r1_cert0.pem", "useIsk": false, - "timestamp": "0x123456", "testSb3Magic": "", "testSb3ImageType": false, @@ -23,9 +22,13 @@ "testCorruptRkhRecord": false, "testCorruptRkhRecordId": 0, "testCorruptIskSignature": false, - "commands": [ - {"load": {"address": "0x0", "file": ".\\workspace\\input_images\\new_image_ernad.bin"}} + { + "load": { + "address": "0x0", + "file": ".\\workspace\\input_images\\new_image_ernad.bin" + } + } ], - "containerOutputFile": ".\\workspace\\output_images\\sb3_256_none_ernad.sb3" -} \ No newline at end of file + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\sb3_256_none_ernad.sb3" +} diff --git a/tests/elftosb/data/sb3_384_256.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_256.json similarity index 82% rename from tests/elftosb/data/sb3_384_256.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_256.json index f310519c..45a4ceb8 100644 --- a/tests/elftosb/data/sb3_384_256.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_256.json @@ -19,7 +19,6 @@ "signingCertificateConstraint": "0x0000", "iskCertificateEllipticCurve": "secp256r1", "signCertData": ".\\workspace\\input_images\\testfffffff.bin", - "timestamp": "0x123456", "testSb3Magic": "", "testSb3ImageType": false, @@ -28,10 +27,19 @@ "testCorruptRkhRecord": false, "testCorruptRkhRecordId": 0, "testCorruptIskSignature": false, - "commands": [ - {"erase": {"address": "0x0", "size": "0x10000"}}, - {"load": {"address": "0x0", "file": ".\\workspace\\output_images\\normal_boot_sign.bin"}} + { + "erase": { + "address": "0x0", + "size": "0x10000" + } + }, + { + "load": { + "address": "0x0", + "file": ".\\workspace\\output_images\\lpc55s3x\\normal_boot_sign.bin" + } + } ], - "containerOutputFile": ".\\workspace\\output_images\\sb3_384_256.sb3" -} \ No newline at end of file + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\sb3_384_256.sb3" +} diff --git a/tests/elftosb/data/sb3_384_256_fixed_timestamp.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_256_fixed_timestamp.json similarity index 79% rename from tests/elftosb/data/sb3_384_256_fixed_timestamp.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_256_fixed_timestamp.json index a7088a74..4693491f 100644 --- a/tests/elftosb/data/sb3_384_256_fixed_timestamp.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_256_fixed_timestamp.json @@ -3,7 +3,7 @@ "containerKeyBlobEncryptionKey": ".\\workspace\\keys\\userkey.txt", "isNxpContainer": false, "description": "sb3_384_256.sb3", - "timestamp" : "0x123456789abcdef", + "timestamp": "0x123456789abcdef", "kdkAccessRights": 3, "containerConfigurationWord": "0x0", "firmwareVersion": "0x1", @@ -20,7 +20,6 @@ "signingCertificateConstraint": "0x0000", "iskCertificateEllipticCurve": "secp256r1", "signCertData": ".\\workspace\\input_images\\testfffffff.bin", - "testSb3Magic": "", "testSb3ImageType": false, "testSb3ImageTypeValue": 255, @@ -28,10 +27,19 @@ "testCorruptRkhRecord": false, "testCorruptRkhRecordId": 0, "testCorruptIskSignature": false, - "commands": [ - {"erase": {"address": "0x0", "size": "0x10000"}}, - {"load": {"address": "0x0", "file": ".\\workspace\\output_images\\normal_boot_sign.bin"}} + { + "erase": { + "address": "0x0", + "size": "0x10000" + } + }, + { + "load": { + "address": "0x0", + "file": ".\\workspace\\output_images\\lpc55s3x\\normal_boot_sign.bin" + } + } ], - "containerOutputFile": ".\\workspace\\output_images\\sb3_384_256_fixed_timestamp.sb3" -} \ No newline at end of file + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\sb3_384_256_fixed_timestamp.sb3" +} diff --git a/tests/elftosb/data/sb3_384_256_unencrypted.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_256_unencrypted.json similarity index 82% rename from tests/elftosb/data/sb3_384_256_unencrypted.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_256_unencrypted.json index df106336..f0dd9b30 100644 --- a/tests/elftosb/data/sb3_384_256_unencrypted.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_256_unencrypted.json @@ -19,7 +19,6 @@ "signingCertificateConstraint": "0x0000", "iskCertificateEllipticCurve": "secp256r1", "signCertData": ".\\workspace\\input_images\\testfffffff.bin", - "timestamp": "0x123456", "testSb3Magic": "", "testSb3ImageType": false, @@ -29,10 +28,19 @@ "testCorruptRkhRecordId": 0, "testCorruptIskSignature": false, "isEncrypted": false, - "commands": [ - {"erase": {"address": "0x0", "size": "0x10000"}}, - {"load": {"address": "0x0", "file": ".\\workspace\\output_images\\normal_boot_sign.bin"}} + { + "erase": { + "address": "0x0", + "size": "0x10000" + } + }, + { + "load": { + "address": "0x0", + "file": ".\\workspace\\output_images\\lpc55s3x\\normal_boot_sign.bin" + } + } ], - "containerOutputFile": ".\\workspace\\output_images\\sb3_384_256_unencrypted.sb3" -} \ No newline at end of file + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\sb3_384_256_unencrypted.sb3" +} diff --git a/tests/elftosb/data/sb3_384_384.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_384.json similarity index 88% rename from tests/elftosb/data/sb3_384_384.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_384.json index 1ee02190..65071f64 100644 --- a/tests/elftosb/data/sb3_384_384.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_384.json @@ -26,9 +26,13 @@ "testCorruptRkhRecord": false, "testCorruptRkhRecordId": 0, "testCorruptIskSignature": false, - "commands": [ - {"erase": {"address": "0x0", "size": "0x10000"}} + { + "erase": { + "address": "0x0", + "size": "0x10000" + } + } ], - "containerOutputFile": ".\\workspace\\output_images\\sb3_384_384_nxp.sb3" -} \ No newline at end of file + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\sb3_384_384_nxp.sb3" +} diff --git a/tests/elftosb/data/sb3_384_none.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_none.json similarity index 77% rename from tests/elftosb/data/sb3_384_none.json rename to tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_none.json index d9fc618b..8d4b6f19 100644 --- a/tests/elftosb/data/sb3_384_none.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_none.json @@ -22,10 +22,19 @@ "testCorruptRkhRecord": false, "testCorruptRkhRecordId": 0, "testCorruptIskSignature": false, - "commands": [ - {"erase": {"address": "0x0", "size": "0x10000"}}, - {"load": {"address": "0x0", "file": ".\\workspace\\output_images\\normal_boot_sign.bin"}} + { + "erase": { + "address": "0x0", + "size": "0x10000" + } + }, + { + "load": { + "address": "0x0", + "file": ".\\workspace\\output_images\\lpc55s3x\\normal_boot_sign.bin" + } + } ], - "containerOutputFile": ".\\workspace\\output_images\\sb3_384_none.sb3" -} \ No newline at end of file + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\sb3_384_none.sb3" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_test_384_384_unencrypted.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_test_384_384_unencrypted.json new file mode 100644 index 00000000..78b84ed8 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_test_384_384_unencrypted.json @@ -0,0 +1,168 @@ +{ + "family": "lpc55s3x", + "containerKeyBlobEncryptionKey": ".\\workspace\\keys\\userkey.txt", + "isNxpContainer": false, + "description": "sb3_test_384_384_unencrypted.sb3", + "kdkKeyAccesRights": 3, + "containerConfigurationWord": "0x0", + "timestamp": "0x123456789abcdef", + "firmwareVersion": "0x1", + "rootCertificate0File": ".\\workspace\\keys_certs\\ec_secp384r1_cert0.pem", + "rootCertificate1File": ".\\workspace\\keys_certs\\ec_secp384r1_cert1.pem", + "rootCertificate2File": ".\\workspace\\keys_certs\\ec_secp384r1_cert2.pem", + "rootCertificate3File": ".\\workspace\\keys_certs\\ec_secp384r1_cert3.pem", + "rootCertificateEllipticCurve": "secp384r1", + "mainRootCertId": 0, + "mainRootCertPrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp384r1_cert0.pem", + "useIsk": true, + "signingCertificateFile": ".\\workspace\\keys_certs\\ec_secp384r1_sign_cert.pem", + "signingCertificatePrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp384r1_sign_cert.pem", + "signingCertificateConstraint": "0x0000", + "iskCertificateEllipticCurve": "secp384r1", + "signCertData": ".\\workspace\\input_images\\testfffffff.bin", + "testSb3Magic": "", + "testSb3ImageType": false, + "testSb3ImageTypeValue": 255, + "testCertBlockMagic": "", + "testCorruptRkhRecord": false, + "testCorruptRkhRecordId": 0, + "testCorruptIskSignature": false, + "isEncrypted": false, + "commands": [ + { + "erase": { + "address": "0x2588", + "size": "0xFFFF", + "memoryId": "0xA" + } + }, + { + "load": { + "address": "0x1256", + "file": ".\\workspace\\input_images\\test1.bin" + } + }, + { + "load": { + "address": "0x2588", + "file": ".\\workspace\\input_images\\test2.bin", + "authentication": "none", + "memoryId": "0xA" + } + }, + { + "load": { + "address": "0x1256", + "file": ".\\workspace\\input_images\\test1.bin", + "authentication": "hashlocking" + } + }, + { + "load": { + "address": "0x2588", + "file": ".\\workspace\\input_images\\test2.bin", + "authentication": "cmac" + } + }, + { + "loadKeyBlob": { + "offset": "0x1256", + "file": ".\\workspace\\input_images\\test1.bin", + "wrappingKeyId": "NXP_CUST_KEK_INT_SK" + } + }, + { + "loadKeyBlob": { + "offset": "0x2588", + "file": ".\\workspace\\input_images\\test2.bin", + "wrappingKeyId": "NXP_CUST_KEK_EXT_SK" + } + }, + { + "programFuses": { + "address": "0x1384", + "values": "0x138498, 0x0, 0x5, 0x1ab, 0x1ab, 0xffffffff, 0xfffffff1" + } + }, + { + "programFuses": { + "address": "0x2588", + "values": "0x138498, 0x0, 0x5, 0x1ab, 0x1ab, 0xffffffff, 0xfffffff1" + } + }, + { + "programIFR": { + "address": "0x1384", + "file": ".\\workspace\\input_ifr\\ifr1.bin" + } + }, + { + "programIFR": { + "address": "0x2588", + "file": ".\\workspace\\input_ifr\\ifr2.bin" + } + }, + { + "call": { + "address": "0x1384" + } + }, + { + "call": { + "address": "0x2588" + } + }, + { + "execute": { + "address": "0x1384" + } + }, + { + "configureMemory": { + "memoryId": "0xA", + "configAddress": "0x1842" + } + }, + { + "load": { + "address": "0x25", + "values": "0x138498" + } + }, + { + "load": { + "address": "0x25", + "values": "0x138498, 0x25, 0x4856974" + } + }, + { + "fillMemory": { + "address": "0x25", + "pattern": "0xFFFFFFFF", + "size": "0xFF" + } + }, + { + "copy": { + "addressFrom": "0xF", + "memoryIdFrom": "0xFF", + "addressTo": "0xFFF", + "memoryIdTo": "0xFFFFF", + "size": "0xA" + } + }, + { + "checkFwVersion": { + "counterId": "nonsecure", + "value": "0x1" + } + }, + { + "checkFwVersion": { + "counterId": "secure", + "value": "0x3" + } + } + ], + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\sb3_test_384_384_unencrypted.sb3" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55xx/README b/tests/elftosb/data/workspace/cfgs/lpc55xx/README new file mode 100644 index 00000000..19a74c87 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55xx/README @@ -0,0 +1 @@ +The LPC55xx is a legacy name for LPC552x/LPC55S2x & LPC55S6x. diff --git a/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_ram_crc.json b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_ram_crc.json new file mode 100644 index 00000000..6ba0330f --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_ram_crc.json @@ -0,0 +1,14 @@ +{ + "description": "this file is not supported, as lpc55xx doesn't allow booting from RAM!!!", + "family": "lpc55xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "RAM", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": false, + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55xx\\mb_ram_crc_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc.json b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc.json new file mode 100644 index 00000000..2f418c0e --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc.json @@ -0,0 +1,14 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old new compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "lpc55xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "Internal Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": false, + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55xx\\mb_xip_crc_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_hwk.json b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_hwk.json new file mode 100644 index 00000000..490bb347 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_hwk.json @@ -0,0 +1,13 @@ +{ + "family": "lpc55xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "Internal Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": false, + "useKeyStore": false, + "enableHwUserModeKeys": true, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55xx\\mb_xip_crc_hwk_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_hwk_tz.json b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_hwk_tz.json new file mode 100644 index 00000000..08fdaea6 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_hwk_tz.json @@ -0,0 +1,14 @@ +{ + "family": "lpc55xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "Internal Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": true, + "trustZonePresetFile": ".\\workspace\\trustzone\\lpc55xx\\tzm_legacy.bin", + "useKeyStore": false, + "enableHwUserModeKeys": true, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55xx\\mb_xip_crc_hwk_tz_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_tz.json b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_tz.json new file mode 100644 index 00000000..c2e14731 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_tz.json @@ -0,0 +1,15 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old not compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "lpc55xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "Internal Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": true, + "trustZonePresetFile": ".\\workspace\\trustzone\\lpc55xx\\tzm_legacy.bin", + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55xx\\mb_xip_crc_tz_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_tz_no_preset.json b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_tz_no_preset.json new file mode 100644 index 00000000..fdccead1 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_crc_tz_no_preset.json @@ -0,0 +1,15 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old not compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "lpc55xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "Internal Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": true, + "trustZonePresetFile": "", + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55xx\\mb_xip_crc_tz_no_preset_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_signed.json b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_signed.json new file mode 100644 index 00000000..a7611e14 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_signed.json @@ -0,0 +1,20 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old new compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "lpc55xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "Internal Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "Signed", + "enableTrustZone": false, + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "rootCertificate0File": ".\\sb_sources\\keys_and_certs\\root_k0_signed_cert0_noca.der.cert", + "rootCertificate1File": ".\\sb_sources\\keys_and_certs\\root_k1_signed_cert0_noca.der.cert", + "rootCertificate2File": ".\\sb_sources\\keys_and_certs\\root_k2_signed_cert0_noca.der.cert", + "rootCertificate3File": ".\\sb_sources\\keys_and_certs\\root_k3_signed_cert0_noca.der.cert", + "mainCertChainId": 1, + "mainCertPrivateKeyFile": ".\\sb_sources\\keys_and_certs\\k1_cert0_2048.pem", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55xx\\mb_xip_signed.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_signed_chain.json b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_signed_chain.json new file mode 100644 index 00000000..6b33a7e3 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55xx/mb_xip_signed_chain.json @@ -0,0 +1,22 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old new compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "lpc55xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "Internal Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "Signed", + "enableTrustZone": false, + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "rootCertificate0File": ".\\sb_sources\\keys_and_certs\\root_k0_signed_cert0_noca.der.cert", + "rootCertificate1File": ".\\sb_sources\\keys_and_certs\\root_cert_0_ca_v3.der.crt", + "chainCertificate1File0": ".\\sb_sources\\keys_and_certs\\chain_cert_0_v3.der.crt", + "chainCertificate1File1": ".\\sb_sources\\keys_and_certs\\chain_cert_1_v3.der.crt", + "rootCertificate2File": ".\\sb_sources\\keys_and_certs\\root_k2_signed_cert0_noca.der.cert", + "rootCertificate3File": ".\\sb_sources\\keys_and_certs\\root_k3_signed_cert0_noca.der.cert", + "mainCertChainId": 1, + "mainCertPrivateKeyFile": ".\\sb_sources\\keys_and_certs\\chain_cert_1_pkey_rsa4096.pem", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55xx\\mb_xip_signed_chain.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55xx/tzm.json b/tests/elftosb/data/workspace/cfgs/lpc55xx/tzm.json new file mode 100644 index 00000000..7e78203b --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55xx/tzm.json @@ -0,0 +1,123 @@ +{ + "family": "lpc55xx", + "revision": "a1", + "tzpOutputFile": ".\\workspace\\trustzone\\lpc55xx\\tzm_legacy.bin", + "trustZonePreset": { + "CM33 Secure vector table address (cm33_vtor_addr)": "0x0u", + "CM33 Non-secure vector table address (cm33_vtor_ns_addr)": "0x0u", + "CM33 Interrupt target non-secure register 0 (cm33_nvic_itns0)": "0x0u", + "CM33 Interrupt target non-secure register 1 (cm33_nvic_itns1)": "0x0u", + "MCM33 Secure vector table address (mcm33_vtor_addr)": "0x0u", + "MPU Control Register.(cm33_mpu_ctrl)": "0x0u", + "MPU Memory Attribute Indirection Register 0 (cm33_mpu_mair0)": "0x0u", + "MPU Memory Attribute Indirection Register 1 (cm33_mpu_mair1)": "0x0u", + "MPU Region 0 Base Address Register (cm33_mpu_rbar0)": "0x0u", + "MPU Region 0 Limit Address Register (cm33_mpu_rlar0)": "0x0u", + "MPU Region 1 Base Address Register (cm33_mpu_rbar1)": "0x0u", + "MPU Region 1 Limit Address Register (cm33_mpu_rlar1)": "0x0u", + "MPU Region 2 Base Address Register (cm33_mpu_rbar2)": "0x0u", + "MPU Region 2 Limit Address Register (cm33_mpu_rlar2)": "0x0u", + "MPU Region 3 Base Address Register (cm33_mpu_rbar3)": "0x0u", + "MPU Region 3 Limit Address Register (cm33_mpu_rlar3)": "0x0u", + "MPU Region 4 Base Address Register (cm33_mpu_rbar4)": "0x0u", + "MPU Region 4 Limit Address Register (cm33_mpu_rlar4)": "0x0u", + "MPU Region 5 Base Address Register (cm33_mpu_rbar5)": "0x0u", + "MPU Region 5 Limit Address Register (cm33_mpu_rlar5)": "0x0u", + "MPU Region 6 Base Address Register (cm33_mpu_rbar6)": "0x0u", + "MPU Region 6 Limit Address Register (cm33_mpu_rlar6)": "0x0u", + "MPU Region 7 Base Address Register (cm33_mpu_rbar7)": "0x0u", + "MPU Region 7 Limit Address Register (cm33_mpu_rlar7)": "0x0u", + "Non-secure MPU Control Register.(cm33_mpu_ctrl_ns)": "0x0u", + "Non-secure MPU Memory Attribute Indirection Register 0 (cm33_mpu_mair0_ns)": "0x0u", + "Non-secure MPU Memory Attribute Indirection Register 1 (cm33_mpu_mair1_ns)": "0x0u", + "Non-secure MPU Region 0 Base Address Register (cm33_mpu_rbar0_ns)": "0x0u", + "Non-secure MPU Region 0 Limit Address Register (cm33_mpu_rlar0_ns)": "0x0u", + "Non-secure MPU Region 1 Base Address Register (cm33_mpu_rbar1_ns)": "0x0u", + "Non-secure MPU Region 1 Limit Address Register (cm33_mpu_rlar1_ns)": "0x0u", + "Non-secure MPU Region 2 Base Address Register (cm33_mpu_rbar2_ns)": "0x0u", + "Non-secure MPU Region 2 Limit Address Register (cm33_mpu_rlar2_ns)": "0x0u", + "Non-secure MPU Region 3 Base Address Register (cm33_mpu_rbar3_ns)": "0x0u", + "Non-secure MPU Region 3 Limit Address Register (cm33_mpu_rlar3_ns)": "0x0u", + "Non-secure MPU Region 4 Base Address Register (cm33_mpu_rbar4_ns)": "0x0u", + "Non-secure MPU Region 4 Limit Address Register (cm33_mpu_rlar4_ns)": "0x0u", + "Non-secure MPU Region 5 Base Address Register (cm33_mpu_rbar5_ns)": "0x0u", + "Non-secure MPU Region 5 Limit Address Register (cm33_mpu_rlar5_ns)": "0x0u", + "Non-secure MPU Region 6 Base Address Register (cm33_mpu_rbar6_ns)": "0x0u", + "Non-secure MPU Region 6 Limit Address Register (cm33_mpu_rlar6_ns)": "0x0u", + "Non-secure MPU Region 7 Base Address Register (cm33_mpu_rbar7_ns)": "0x0u", + "Non-secure MPU Region 7 Limit Address Register (cm33_mpu_rlar7_ns)": "0x0u", + "SAU Control Register.(cm33_sau_ctrl)": "0x0u", + "SAU Region 0 Base Address Register (cm33_sau_rbar0)": "0x0u", + "SAU Region 0 Limit Address Register (cm33_sau_rlar0)": "0x0u", + "SAU Region 1 Base Address Register (cm33_sau_rbar1)": "0x0u", + "SAU Region 1 Limit Address Register (cm33_sau_rlar1)": "0x0u", + "SAU Region 2 Base Address Register (cm33_sau_rbar2)": "0x0u", + "SAU Region 2 Limit Address Register (cm33_sau_rlar2)": "0x0u", + "SAU Region 3 Base Address Register (cm33_sau_rbar3)": "0x0u", + "SAU Region 3 Limit Address Register (cm33_sau_rlar3)": "0x0u", + "SAU Region 4 Base Address Register (cm33_sau_rbar4)": "0x0u", + "SAU Region 4 Limit Address Register (cm33_sau_rlar4)": "0x0u", + "SAU Region 5 Base Address Register (cm33_sau_rbar5)": "0x0u", + "SAU Region 5 Limit Address Register (cm33_sau_rlar5)": "0x0u", + "SAU Region 6 Base Address Register (cm33_sau_rbar6)": "0x0u", + "SAU Region 6 Limit Address Register (cm33_sau_rlar6)": "0x0u", + "SAU Region 7 Base Address Register (cm33_sau_rbar7)": "0x0u", + "SAU Region 7 Limit Address Register (cm33_sau_rlar7)": "0x0u", + "FLASH/ROM Slave Rule Register 0 (flash_rom_slave_rule)": "0x0u", + "FLASH Memory Rule Register 0 (flash_mem_rule0)": "0x0u", + "FLASH Memory Rule Register 1 (flash_mem_rule1)": "0x0u", + "FLASH Memory Rule Register 2 (flash_mem_rule2)": "0x0u", + "ROM Memory Rule Register 0 (rom_mem_rule0)": "0x0u", + "ROM Memory Rule Register 1 (rom_mem_rule1)": "0x0u", + "ROM Memory Rule Register 2 (rom_mem_rule2)": "0x0u", + "ROM Memory Rule Register 3 (rom_mem_rule3)": "0x0u", + "RAMX Slave Rule Register (ramx_slave_rule)": "0x0u", + "RAMX Memory Rule Register 0 (ramx_mem_rule0)": "0x0u", + "RAM0 Slave Rule Register (ram0_slave_rule)": "0x0u", + "RAM0 Memory Rule Register 0 (ram0_mem_rule0)": "0x0u", + "RAM0 Memory Rule Register 1 (ram0_mem_rule1)": "0x0u", + "RAM1 Slave Rule Register (ram1_slave_rule)": "0x0u", + "RAM1 Memory Rule Register 0 (ram1_mem_rule0)": "0x0u", + "RAM1 Memory Rule Register 1 (ram1_mem_rule1)": "0x0u", + "RAM2 Slave Rule Register (ram2_slave_rule)": "0x0u", + "RAM2 Memory Rule Register 0 (ram2_mem_rule0)": "0x0u", + "RAM2 Memory Rule Register 1 (ram2_mem_rule1)": "0x0", + "RAM3 Slave Rule Register (ram3_slave_rule)": "0x0u", + "RAM3 Memory Rule Register 0 (ram3_mem_rule0)": "0x0u", + "RAM3 Memory Rule Register 1 (ram3_mem_rule1)": "0x0u", + "RAM4 Slave Rule Register (ram4_slave_rule)": "0x0u", + "RAM4 Memory Rule Register 0 (ram4_mem_rule0)": "0x0u", + "APB Bridge Group Slave Rule Register (apb_grp_slave_rule)": "0x0u", + "APB Bridge Group 0 Memory Rule Register 0 (apb_grp0_mem_rule0)": "0x0u", + "APB Bridge Group 0 Memory Rule Register 1 (apb_grp0_mem_rule1)": "0x0u", + "APB Bridge Group 0 Memory Rule Register 2 (apb_grp0_mem_rule2)": "0x0u", + "APB Bridge Group 0 Memory Rule Register 3 (apb_grp0_mem_rule3)": "0x0u", + "APB Bridge Group 1 Memory Rule Register 0 (apb_grp1_mem_rule0)": "0x0u", + "APB Bridge Group 1 Memory Rule Register 1 (apb_grp1_mem_rule1)": "0x0u", + "APB Bridge Group 1 Memory Rule Register 2 (apb_grp1_mem_rule2)": "0x0u", + "APB Bridge Group 1 Memory Rule Register 3 (apb_grp1_mem_rule3)": "0x0u", + "AHB Peripherals 0 Slave Rule Register 0 (ahb_periph0_slave_rule0)": "0x0u", + "AHB Peripherals 0 Slave Rule Register 1 (ahb_periph0_slave_rule1)": "0x0u", + "AHB Peripherals 1 Slave Rule Register 0 (ahb_periph1_slave_rule0)": "0x0u", + "AHB Peripherals 1 Slave Rule Register 1 (ahb_periph1_slave_rule1)": "0x0u", + "AHB Peripherals 2 Slave Rule Register 0 (ahb_periph2_slave_rule0)": "0x0u", + "AHB Peripherals 2 Slave Rule Register 1 (ahb_periph2_slave_rule1)": "0x0u", + "AHB Peripherals 2 Memory Rule Register 0 (ahb_periph2_mem_rule0)": "0x0u", + "HS USB Slave Rule Register 0 (usb_hs_slave_rule0)": "0x0u", + "HS USB Memory Rule Register 0 (usb_hs__mem_rule0)": "0x0u", + "Secure GPIO Register 0 (sec_gp_reg0)": "0xffffffffu", + "Secure GPIO Register 1 (sec_gp_reg1)": "0xffffffffu", + "Secure GPIO Register 2 (sec_gp_reg2)": "0xffffffffu", + "Secure GPIO Register 3 (sec_gp_reg3)": "0xffffffffu", + "Secure Interrupt Mask for CPU1 Register 0 (sec_int_reg0)": "0xffffffffu", + "Secure Interrupt Mask for CPU1 Register 1 (sec_int_reg1)": "0xffffffffu", + "Secure GPIO Lock Register (sec_gp_reg_lock)": "0x00000aaau", + "Master Secure Level Register (master_sec_reg)": "0x80000000u", + "Master Secure Level Anti-pole Register (master_sec_anti_pol_reg)": "0xbfffffffu", + "CM33 Lock Control Register (cm33_lock_reg)": "0x800002aau", + "MCM33 Lock Control Register (mcm33_lock_reg)": "0x8000000au", + "Secure Control Duplicate Register (misc_ctrl_dp_reg)": "0x0000aaaau", + "Secure Control Register (misc_ctrl_reg)": "0x0000aaaau", + "Miscellaneous TZM settings (misc_tzm_settings)": "0x0u" + } +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc.json b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc.json new file mode 100644 index 00000000..c52aee03 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc.json @@ -0,0 +1,12 @@ +{ + "family": "rt5xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "RAM", + "imageLinkAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": false, + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\rt5xx\\mb_ram_crc_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_hwk.json b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_hwk.json new file mode 100644 index 00000000..ad127675 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_hwk.json @@ -0,0 +1,13 @@ +{ + "family": "rt5xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "RAM", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": false, + "useKeyStore": false, + "enableHwUserModeKeys": true, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\rt5xx\\mb_ram_crc_hwk_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_hwk_tz.json b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_hwk_tz.json new file mode 100644 index 00000000..798b8523 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_hwk_tz.json @@ -0,0 +1,14 @@ +{ + "family": "rt5xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "RAM", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": true, + "trustZonePresetFile": ".\\workspace\\trustzone\\rt5xx\\tzm_legacy.bin", + "useKeyStore": false, + "enableHwUserModeKeys": true, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\rt5xx\\mb_ram_crc_hwk_tz_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_tz.json b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_tz.json new file mode 100644 index 00000000..8e8ddab0 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_tz.json @@ -0,0 +1,15 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old not compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "rt5xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "RAM", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": true, + "trustZonePresetFile": ".\\workspace\\trustzone\\rt5xx\\tzm_legacy.bin", + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\rt5xx\\mb_ram_crc_tz_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_tz_no_preset.json b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_tz_no_preset.json new file mode 100644 index 00000000..b7f4a27e --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_crc_tz_no_preset.json @@ -0,0 +1,15 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old not compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "rt5xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "RAM", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": true, + "trustZonePresetFile": "", + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\rt5xx\\mb_ram_crc_tz_no_preset_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_encrypted_ks.json b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_encrypted_ks.json new file mode 100644 index 00000000..56219e15 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_encrypted_ks.json @@ -0,0 +1,23 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old new compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "rt5xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "RAM", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "Encrypted + Signed", + "outputImageEncryptionKeyFile": ".\\workspace\\keys\\userkey.txt", + "enableTrustZone": false, + "deviceKeySource": "Keystore", + "useKeyStore": true, + "useKeyStoreFile": "", + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "rootCertificate0File": ".\\sb_sources\\keys_and_certs\\root_k0_signed_cert0_noca.der.cert", + "rootCertificate1File": ".\\sb_sources\\keys_and_certs\\root_k1_signed_cert0_noca.der.cert", + "rootCertificate2File": ".\\sb_sources\\keys_and_certs\\root_k2_signed_cert0_noca.der.cert", + "rootCertificate3File": ".\\sb_sources\\keys_and_certs\\root_k3_signed_cert0_noca.der.cert", + "mainCertChainId": 1, + "mainCertPrivateKeyFile": ".\\sb_sources\\keys_and_certs\\k1_cert0_2048.pem", + "masterBootOutputFile": ".\\workspace\\output_images\\rt5xx\\mb_ram_encrypted_ks_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_signed_ks.json b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_signed_ks.json new file mode 100644 index 00000000..465bb056 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_signed_ks.json @@ -0,0 +1,23 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old new compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "rt5xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "RAM", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "Signed", + "outputImageEncryptionKeyFile": ".\\workspace\\keys\\userkey.txt", + "enableTrustZone": false, + "deviceKeySource": "Keystore", + "useKeyStore": true, + "useKeyStoreFile": "", + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "rootCertificate0File": ".\\sb_sources\\keys_and_certs\\root_k0_signed_cert0_noca.der.cert", + "rootCertificate1File": ".\\sb_sources\\keys_and_certs\\root_k1_signed_cert0_noca.der.cert", + "rootCertificate2File": ".\\sb_sources\\keys_and_certs\\root_k2_signed_cert0_noca.der.cert", + "rootCertificate3File": ".\\sb_sources\\keys_and_certs\\root_k3_signed_cert0_noca.der.cert", + "mainCertChainId": 1, + "mainCertPrivateKeyFile": ".\\sb_sources\\keys_and_certs\\k1_cert0_2048.pem", + "masterBootOutputFile": ".\\workspace\\output_images\\rt5xx\\mb_ram_signed_ks_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_signed_no_ks.json b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_signed_no_ks.json new file mode 100644 index 00000000..2999a301 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_ram_signed_no_ks.json @@ -0,0 +1,22 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old new compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "rt5xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "RAM", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "Signed", + "outputImageEncryptionKeyFile": ".\\workspace\\keys\\userkey.txt", + "enableTrustZone": false, + "deviceKeySource": "OTP", + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "rootCertificate0File": ".\\sb_sources\\keys_and_certs\\root_k0_signed_cert0_noca.der.cert", + "rootCertificate1File": ".\\sb_sources\\keys_and_certs\\root_k1_signed_cert0_noca.der.cert", + "rootCertificate2File": ".\\sb_sources\\keys_and_certs\\root_k2_signed_cert0_noca.der.cert", + "rootCertificate3File": ".\\sb_sources\\keys_and_certs\\root_k3_signed_cert0_noca.der.cert", + "mainCertChainId": 1, + "mainCertPrivateKeyFile": ".\\sb_sources\\keys_and_certs\\k1_cert0_2048.pem", + "masterBootOutputFile": ".\\workspace\\output_images\\rt5xx\\mb_ram_signed_no_ks_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc.json b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc.json new file mode 100644 index 00000000..881f9e6b --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc.json @@ -0,0 +1,14 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old new compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "rt5xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "External Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": false, + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\rt5xx\\mb_xip_crc_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_hwk.json b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_hwk.json new file mode 100644 index 00000000..27730402 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_hwk.json @@ -0,0 +1,13 @@ +{ + "family": "rt5xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "External Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": false, + "useKeyStore": false, + "enableHwUserModeKeys": true, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\rt5xx\\mb_xip_crc_hwk_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_hwk_tz.json b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_hwk_tz.json new file mode 100644 index 00000000..c4c173ef --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_hwk_tz.json @@ -0,0 +1,14 @@ +{ + "family": "rt5xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "External Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": true, + "trustZonePresetFile": ".\\workspace\\trustzone\\rt5xx\\tzm_legacy.bin", + "useKeyStore": false, + "enableHwUserModeKeys": true, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\rt5xx\\mb_xip_crc_hwk_tz_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_tz.json b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_tz.json new file mode 100644 index 00000000..40980ffc --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_tz.json @@ -0,0 +1,15 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old not compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "rt5xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "External Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": true, + "trustZonePresetFile": ".\\workspace\\trustzone\\rt5xx\\tzm_legacy.bin", + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\rt5xx\\mb_xip_crc_tz_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_tz_no_preset.json b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_tz_no_preset.json new file mode 100644 index 00000000..dbeda320 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_crc_tz_no_preset.json @@ -0,0 +1,15 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old not compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "rt5xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "External Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": true, + "trustZonePresetFile": "", + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "masterBootOutputFile": ".\\workspace\\output_images\\rt5xx\\mb_xip_crc_tz_no_preset_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_signed_no_ks.json b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_signed_no_ks.json new file mode 100644 index 00000000..58767946 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/mb_xip_signed_no_ks.json @@ -0,0 +1,20 @@ +{ + "remark": "imageLinkAddress & outputImageExecutionAddress represent the same configuration, however, imageLinkAddress is old new compliant with legacy elftosb and outputImageExecutionAddress is new name in new elftosb.", + "family": "rt5xx", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "External Flash (XIP)", + "imageLinkAddress": "0x0", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "Signed", + "enableTrustZone": false, + "useKeyStore": false, + "enableHwUserModeKeys": false, + "imageBuildNumber": "0x0", + "rootCertificate0File": ".\\sb_sources\\keys_and_certs\\root_k0_signed_cert0_noca.der.cert", + "rootCertificate1File": ".\\sb_sources\\keys_and_certs\\root_k1_signed_cert0_noca.der.cert", + "rootCertificate2File": ".\\sb_sources\\keys_and_certs\\root_k2_signed_cert0_noca.der.cert", + "rootCertificate3File": ".\\sb_sources\\keys_and_certs\\root_k3_signed_cert0_noca.der.cert", + "mainCertChainId": 1, + "mainCertPrivateKeyFile": ".\\sb_sources\\keys_and_certs\\k1_cert0_2048.pem", + "masterBootOutputFile": ".\\workspace\\output_images\\rt5xx\\mb_xip_signed_legacy.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/rt5xx/tzm.json b/tests/elftosb/data/workspace/cfgs/rt5xx/tzm.json new file mode 100644 index 00000000..6b028bd1 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/rt5xx/tzm.json @@ -0,0 +1,292 @@ +{ + "family": "rt5xx", + "revision": "a0", + "tzpOutputFile": ".\\workspace\\trustzone\\rt5xx\\tzm_legacy.bin", + "trustZonePreset": { + "Secure vector table address (vtor_addr)": "0x0", + "Non-secure vector table address (vtor_ns_addr)": "0x0", + "Interrupt target non-secure register 0 (nvic_itns0)": "0x0", + "Interrupt target non-secure register 1 (nvic_itns1)": "0x0", + "MPU Control Register (mpu_ctrl)": "0x0", + "MPU Memory Attribute Indirection Register 0 (mpu_mair0)": "0x0", + "MPU Memory Attribute Indirection Register 1 (mpu_mair1)": "0x0", + "MPU Region 0 Base Address Register (mpu_rbar0)": "0x0", + "MPU Region 0 Limit Address Register (mpu_rlar0)": "0x0", + "MPU Region 1 Base Address Register (mpu_rbar1)": "0x0", + "MPU Region 1 Limit Address Register (mpu_rlar1)": "0x0", + "MPU Region 2 Base Address Register (mpu_rbar2)": "0x0", + "MPU Region 2 Limit Address Register (mpu_rlar2)": "0x0", + "MPU Region 3 Base Address Register (mpu_rbar3)": "0x0", + "MPU Region 3 Limit Address Register (mpu_rlar3)": "0x0", + "MPU Region 4 Base Address Register (mpu_rbar4)": "0x0", + "MPU Region 4 Limit Address Register (mpu_rlar4)": "0x0", + "MPU Region 5 Base Address Register (mpu_rbar5)": "0x0", + "MPU Region 5 Limit Address Register (mpu_rlar5)": "0x0", + "MPU Region 6 Base Address Register (mpu_rbar6)": "0x0", + "MPU Region 6 Limit Address Register (mpu_rlar6)": "0x0", + "MPU Region 7 Base Address Register (mpu_rbar7)": "0x0", + "MPU Region 7 Limit Address Register (mpu_rlar7)": "0x0", + "Non-secure MPU Control Register (mpu_ctrl_ns)": "0x0", + "Non-secure MPU Memory Attribute Indirection Register 0 (mpu_mair0_ns)": "0x0", + "Non-secure MPU Memory Attribute Indirection Register 1 (mpu_mair1_ns)": "0x0", + "Non-secure MPU Region 0 Base Address Register (mpu_rbar0_ns)": "0x0", + "Non-secure MPU Region 0 Limit Address Register (mpu_rlar0_ns)": "0x0", + "Non-secure MPU Region 1 Base Address Register (mpu_rbar1_ns)": "0x0", + "Non-secure MPU Region 1 Limit Address Register (mpu_rlar1_ns)": "0x0", + "Non-secure MPU Region 2 Base Address Register (mpu_rbar2_ns)": "0x0", + "Non-secure MPU Region 2 Limit Address Register (mpu_rlar2_ns)": "0x0", + "Non-secure MPU Region 3 Base Address Register (mpu_rbar3_ns)": "0x0", + "Non-secure MPU Region 3 Limit Address Register (mpu_rlar3_ns)": "0x0", + "Non-secure MPU Region 4 Base Address Register (mpu_rbar4_ns)": "0x0", + "Non-secure MPU Region 4 Limit Address Register (mpu_rlar4_ns)": "0x0", + "Non-secure MPU Region 5 Base Address Register (mpu_rbar5_ns)": "0x0", + "Non-secure MPU Region 5 Limit Address Register (mpu_rlar5_ns)": "0x0", + "Non-secure MPU Region 6 Base Address Register (mpu_rbar6_ns)": "0x0", + "Non-secure MPU Region 6 Limit Address Register (mpu_rlar6_ns)": "0x0", + "Non-secure MPU Region 7 Base Address Register (mpu_rbar7_ns)": "0x0", + "Non-secure MPU Region 7 Limit Address Register (mpu_rlar7_ns)": "0x0", + "SAU Control Register (sau_ctrl)": "0x0", + "SAU Region 0 Base Address Register (sau_rbar0)": "0x0", + "SAU Region 0 Limit Address Register (sau_rlar0)": "0x0", + "SAU Region 1 Base Address Register (sau_rbar1)": "0x0", + "SAU Region 1 Limit Address Register (sau_rlar1)": "0x0", + "SAU Region 2 Base Address Register (sau_rbar2)": "0x0", + "SAU Region 2 Limit Address Register (sau_rlar2)": "0x0", + "SAU Region 3 Base Address Register (sau_rbar3)": "0x0", + "SAU Region 3 Limit Address Register (sau_rlar3)": "0x0", + "SAU Region 4 Base Address Register (sau_rbar4)": "0x0", + "SAU Region 4 Limit Address Register (sau_rlar4)": "0x0", + "SAU Region 5 Base Address Register (sau_rbar5)": "0x0", + "SAU Region 5 Limit Address Register (sau_rlar5)": "0x0", + "SAU Region 6 Base Address Register (sau_rbar6)": "0x0", + "SAU Region 6 Limit Address Register (sau_rlar6)": "0x0", + "SAU Region 7 Base Address Register (sau_rbar7)": "0x0", + "SAU Region 7 Limit Address Register (sau_rlar7)": "0x0", + "ROM Slave Rule Register 0 (bootrom0_slave_rule0)": "0x0", + "ROM Memory Rule Register 0 (bootrom0_mem_rule0)": "0x0", + "ROM Memory Rule Register 1 (bootrom0_mem_rule1)": "0x0", + "ROM Memory Rule Register 2 (bootrom0_mem_rule2)": "0x0", + "ROM Memory Rule Register 3 (bootrom0_mem_rule3)": "0x0", + "Quad/Octal SPI Slave Rule Register 0 (qospi_slave_rule0)": "0x0", + "Quad/Octal SPI 0 Memory Rule Register 0 (qospi0_mem_rule0)": "0x0", + "Quad/Octal SPI 0 Memory Rule Register 1 (qospi0_mem_rule1)": "0x0", + "Quad/Octal SPI 0 Memory Rule Register 2 (qospi0_mem_rule2)": "0x0", + "Quad/Octal SPI 0 Memory Rule Register 3 (qospi0_mem_rule3)": "0x0", + "Quad/Octal SPI 1 Memory Rule Register 0 (qospi1_mem_rule0)": "0x0", + "Quad/Octal SPI 1 Memory Rule Register 1 (qospi1_mem_rule1)": "0x0", + "Quad/Octal SPI 1 Memory Rule Register 2 (qospi1_mem_rule2)": "0x0", + "Quad/Octal SPI 1 Memory Rule Register 3 (qospi1_mem_rule3)": "0x0", + "Quad/Octal SPI 2 Memory Rule Register 0 (qospi2_mem_rule0)": "0x0", + "Quad/Octal SPI 2 Memory Rule Register 1 (qospi2_mem_rule1)": "0x0", + "Quad/Octal SPI 2 Memory Rule Register 2 (qospi2_mem_rule2)": "0x0", + "Quad/Octal SPI 2 Memory Rule Register 3 (qospi2_mem_rule3)": "0x0", + "Quad/Octal SPI 3 Memory Rule Register 0 (qospi3_mem_rule0)": "0x0", + "Quad/Octal SPI 3 Memory Rule Register 1 (qospi3_mem_rule1)": "0x0", + "Quad/Octal SPI 3 Memory Rule Register 2 (qospi3_mem_rule2)": "0x0", + "Quad/Octal SPI 3 Memory Rule Register 3 (qospi3_mem_rule3)": "0x0", + "Quad/Octal SPI 4 Memory Rule Register 0 (qospi4_mem_rule0)": "0x0", + "Quad/Octal SPI 4 Memory Rule Register 1 (qospi4_mem_rule1)": "0x0", + "Quad/Octal SPI 4 Memory Rule Register 2 (qospi4_mem_rule2)": "0x0", + "Quad/Octal SPI 4 Memory Rule Register 3 (qospi4_mem_rule3)": "0x0", + "RAM0 Slave Rule Register (ram0_slave_rule)": "0x0", + "RAM00 Memory Rule Register 0 (ram00_mem_rule0)": "0x0", + "RAM00 Memory Rule Register 1 (ram00_mem_rule1)": "0x0", + "RAM00 Memory Rule Register 2 (ram00_mem_rule2)": "0x0", + "RAM00 Memory Rule Register 3 (ram00_mem_rule3)": "0x0", + "RAM01 Memory Rule Register 0 (ram01_mem_rule0)": "0x0", + "RAM01 Memory Rule Register 1 (ram01_mem_rule1)": "0x0", + "RAM01 Memory Rule Register 2 (ram01_mem_rule2)": "0x0", + "RAM01 Memory Rule Register 3 (ram01_mem_rule3)": "0x0", + "RAM1 Slave Rule Register (ram1_slave_rule)": "0x0", + "RAM10 Memory Rule Register 0 (ram10_mem_rule0)": "0x0", + "RAM10 Memory Rule Register 1 (ram10_mem_rule1)": "0x0", + "RAM10 Memory Rule Register 2 (ram10_mem_rule2)": "0x0", + "RAM10 Memory Rule Register 3 (ram10_mem_rule3)": "0x0", + "RAM11 Memory Rule Register 0 (ram11_mem_rule0)": "0x0", + "RAM11 Memory Rule Register 1 (ram11_mem_rule1)": "0x0", + "RAM11 Memory Rule Register 2 (ram11_mem_rule2)": "0x0", + "RAM11 Memory Rule Register 3 (ram11_mem_rule3)": "0x0", + "RAM2 Slave Rule Register (ram2_slave_rule)": "0x0", + "RAM20 Memory Rule Register 0 (ram20_mem_rule0)": "0x0", + "RAM20 Memory Rule Register 1 (ram20_mem_rule1)": "0x0", + "RAM20 Memory Rule Register 2 (ram20_mem_rule2)": "0x0", + "RAM20 Memory Rule Register 3 (ram20_mem_rule3)": "0x0", + "RAM21 Memory Rule Register 0 (ram21_mem_rule0)": "0x0", + "RAM21 Memory Rule Register 1 (ram21_mem_rule1)": "0x0", + "RAM21 Memory Rule Register 2 (ram21_mem_rule2)": "0x0", + "RAM21 Memory Rule Register 3 (ram21_mem_rule3)": "0x0", + "RAM22 Memory Rule Register 0 (ram22_mem_rule0)": "0x0", + "RAM22 Memory Rule Register 1 (ram22_mem_rule1)": "0x0", + "RAM22 Memory Rule Register 2 (ram22_mem_rule2)": "0x0", + "RAM22 Memory Rule Register 3 (ram22_mem_rule3)": "0x0", + "RAM23 Memory Rule Register 0 (ram23_mem_rule0)": "0x0", + "RAM23 Memory Rule Register 1 (ram23_mem_rule1)": "0x0", + "RAM23 Memory Rule Register 2 (ram23_mem_rule2)": "0x0", + "RAM23 Memory Rule Register 3 (ram23_mem_rule3)": "0x0", + "RAM3 Slave Rule Register (ram3_slave_rule)": "0x0", + "RAM30 Memory Rule Register 0 (ram30_mem_rule0)": "0x0", + "RAM30 Memory Rule Register 1 (ram30_mem_rule1)": "0x0", + "RAM30 Memory Rule Register 2 (ram30_mem_rule2)": "0x0", + "RAM30 Memory Rule Register 3 (ram30_mem_rule3)": "0x0", + "RAM31 Memory Rule Register 0 (ram31_mem_rule0)": "0x0", + "RAM31 Memory Rule Register 1 (ram31_mem_rule1)": "0x0", + "RAM31 Memory Rule Register 2 (ram31_mem_rule2)": "0x0", + "RAM31 Memory Rule Register 3 (ram31_mem_rule3)": "0x0", + "RAM32 Memory Rule Register 0 (ram32_mem_rule0)": "0x0", + "RAM32 Memory Rule Register 1 (ram32_mem_rule1)": "0x0", + "RAM32 Memory Rule Register 2 (ram32_mem_rule2)": "0x0", + "RAM32 Memory Rule Register 3 (ram32_mem_rule3)": "0x0", + "RAM33 Memory Rule Register 0 (ram33_mem_rule0)": "0x0", + "RAM33 Memory Rule Register 1 (ram33_mem_rule1)": "0x0", + "RAM33 Memory Rule Register 2 (ram33_mem_rule2)": "0x0", + "RAM33 Memory Rule Register 3 (ram33_mem_rule3)": "0x0", + "RAM4 Slave Rule Register (ram4_slave_rule)": "0x0", + "RAM40 Memory Rule Register 0 (ram40_mem_rule0)": "0x0", + "RAM40 Memory Rule Register 1 (ram40_mem_rule1)": "0x0", + "RAM40 Memory Rule Register 2 (ram40_mem_rule2)": "0x0", + "RAM40 Memory Rule Register 3 (ram40_mem_rule3)": "0x0", + "RAM41 Memory Rule Register 0 (ram41_mem_rule0)": "0x0", + "RAM41 Memory Rule Register 1 (ram41_mem_rule1)": "0x0", + "RAM41 Memory Rule Register 2 (ram41_mem_rule2)": "0x0", + "RAM41 Memory Rule Register 3 (ram41_mem_rule3)": "0x0", + "RAM42 Memory Rule Register 0 (ram42_mem_rule0)": "0x0", + "RAM42 Memory Rule Register 1 (ram42_mem_rule1)": "0x0", + "RAM42 Memory Rule Register 2 (ram42_mem_rule2)": "0x0", + "RAM42 Memory Rule Register 3 (ram42_mem_rule3)": "0x0", + "RAM43 Memory Rule Register 0 (ram43_mem_rule0)": "0x0", + "RAM43 Memory Rule Register 1 (ram43_mem_rule1)": "0x0", + "RAM43 Memory Rule Register 2 (ram43_mem_rule2)": "0x0", + "RAM43 Memory Rule Register 3 (ram43_mem_rule3)": "0x0", + "RAM5 Slave Rule Register (ram5_slave_rule)": "0x0", + "RAM50 Memory Rule Register 0 (ram50_mem_rule0)": "0x0", + "RAM50 Memory Rule Register 1 (ram50_mem_rule1)": "0x0", + "RAM50 Memory Rule Register 2 (ram50_mem_rule2)": "0x0", + "RAM50 Memory Rule Register 3 (ram50_mem_rule3)": "0x0", + "RAM51 Memory Rule Register 0 (ram51_mem_rule0)": "0x0", + "RAM51 Memory Rule Register 1 (ram51_mem_rule1)": "0x0", + "RAM51 Memory Rule Register 2 (ram51_mem_rule2)": "0x0", + "RAM51 Memory Rule Register 3 (ram51_mem_rule3)": "0x0", + "RAM52 Memory Rule Register 0 (ram52_mem_rule0)": "0x0", + "RAM52 Memory Rule Register 1 (ram52_mem_rule1)": "0x0", + "RAM52 Memory Rule Register 2 (ram52_mem_rule2)": "0x0", + "RAM52 Memory Rule Register 3 (ram52_mem_rule3)": "0x0", + "RAM53 Memory Rule Register 0 (ram53_mem_rule0)": "0x0", + "RAM53 Memory Rule Register 1 (ram53_mem_rule1)": "0x0", + "RAM53 Memory Rule Register 2 (ram53_mem_rule2)": "0x0", + "RAM53 Memory Rule Register 3 (ram53_mem_rule3)": "0x0", + "RAM6 Slave Rule Register (ram6_slave_rule)": "0x0", + "RAM60 Memory Rule Register 0 (ram60_mem_rule0)": "0x0", + "RAM60 Memory Rule Register 1 (ram60_mem_rule1)": "0x0", + "RAM60 Memory Rule Register 2 (ram60_mem_rule2)": "0x0", + "RAM60 Memory Rule Register 3 (ram60_mem_rule3)": "0x0", + "RAM61 Memory Rule Register 0 (ram61_mem_rule0)": "0x0", + "RAM61 Memory Rule Register 1 (ram61_mem_rule1)": "0x0", + "RAM61 Memory Rule Register 2 (ram61_mem_rule2)": "0x0", + "RAM61 Memory Rule Register 3 (ram61_mem_rule3)": "0x0", + "RAM62 Memory Rule Register 0 (ram62_mem_rule0)": "0x0", + "RAM62 Memory Rule Register 1 (ram62_mem_rule1)": "0x0", + "RAM62 Memory Rule Register 2 (ram62_mem_rule2)": "0x0", + "RAM62 Memory Rule Register 3 (ram62_mem_rule3)": "0x0", + "RAM63 Memory Rule Register 0 (ram63_mem_rule0)": "0x0", + "RAM63 Memory Rule Register 1 (ram63_mem_rule1)": "0x0", + "RAM63 Memory Rule Register 2 (ram63_mem_rule2)": "0x0", + "RAM63 Memory Rule Register 3 (ram63_mem_rule3)": "0x0", + "RAM7 Slave Rule Register (ram7_slave_rule)": "0x0", + "RAM70 Memory Rule Register 0 (ram70_mem_rule0)": "0x0", + "RAM70 Memory Rule Register 1 (ram70_mem_rule1)": "0x0", + "RAM70 Memory Rule Register 2 (ram70_mem_rule2)": "0x0", + "RAM70 Memory Rule Register 3 (ram70_mem_rule3)": "0x0", + "RAM71 Memory Rule Register 0 (ram71_mem_rule0)": "0x0", + "RAM71 Memory Rule Register 1 (ram71_mem_rule1)": "0x0", + "RAM71 Memory Rule Register 2 (ram71_mem_rule2)": "0x0", + "RAM71 Memory Rule Register 3 (ram71_mem_rule3)": "0x0", + "RAM72 Memory Rule Register 0 (ram72_mem_rule0)": "0x0", + "RAM72 Memory Rule Register 1 (ram72_mem_rule1)": "0x0", + "RAM72 Memory Rule Register 2 (ram72_mem_rule2)": "0x0", + "RAM72 Memory Rule Register 3 (ram72_mem_rule3)": "0x0", + "RAM73 Memory Rule Register 0 (ram73_mem_rule0)": "0x0", + "RAM73 Memory Rule Register 1 (ram73_mem_rule1)": "0x0", + "RAM73 Memory Rule Register 2 (ram73_mem_rule2)": "0x0", + "RAM73 Memory Rule Register 3 (ram73_mem_rule3)": "0x0", + "RAM8 Slave Rule Register (ram8_slave_rule)": "0x0", + "RAM80 Memory Rule Register 0 (ram80_mem_rule0)": "0x0", + "RAM80 Memory Rule Register 1 (ram80_mem_rule1)": "0x0", + "RAM80 Memory Rule Register 2 (ram80_mem_rule2)": "0x0", + "RAM80 Memory Rule Register 3 (ram80_mem_rule3)": "0x0", + "RAM81 Memory Rule Register 0 (ram81_mem_rule0)": "0x0", + "RAM81 Memory Rule Register 1 (ram81_mem_rule1)": "0x0", + "RAM81 Memory Rule Register 2 (ram81_mem_rule2)": "0x0", + "RAM81 Memory Rule Register 3 (ram81_mem_rule3)": "0x0", + "RAM81 Memory Rule Register 0 (ram82_mem_rule0)": "0x0", + "RAM81 Memory Rule Register 1 (ram82_mem_rule1)": "0x0", + "RAM81 Memory Rule Register 2 (ram82_mem_rule2)": "0x0", + "RAM81 Memory Rule Register 3 (ram82_mem_rule3)": "0x0", + "RAM81 Memory Rule Register 0 (ram83_mem_rule0)": "0x0", + "RAM81 Memory Rule Register 1 (ram83_mem_rule1)": "0x0", + "RAM81 Memory Rule Register 2 (ram83_mem_rule2)": "0x0", + "RAM81 Memory Rule Register 3 (ram83_mem_rule3)": "0x0", + "(ezh_ram_slave_rule)": "0x0", + "(ezh_ram0_slave_rule0)": "0x0", + "(ezh_ram0_slave_rule1)": "0x0", + "(ezh_ram0_slave_rule2)": "0x0", + "(ezh_ram0_slave_rule3)": "0x0", + "Quad/Octal SPI Slave Rule Register 0 (qospi1_slave_rule0)": "0x0", + "Quad/Octal SPI 0 Memory Rule Register 0 (qospi10_mem_rule0)": "0x0", + "Quad/Octal SPI 0 Memory Rule Register 1 (qospi10_mem_rule1)": "0x0", + "Quad/Octal SPI 0 Memory Rule Register 2 (qospi10_mem_rule2)": "0x0", + "Quad/Octal SPI 0 Memory Rule Register 3 (qospi10_mem_rule3)": "0x0", + "Quad/Octal SPI 1 Memory Rule Register 0 (qospi11_mem_rule0)": "0x0", + "Quad/Octal SPI 1 Memory Rule Register 1 (qospi11_mem_rule1)": "0x0", + "Quad/Octal SPI 1 Memory Rule Register 2 (qospi11_mem_rule2)": "0x0", + "Quad/Octal SPI 1 Memory Rule Register 3 (qospi11_mem_rule3)": "0x0", + "Quad/Octal SPI 2 Memory Rule Register 0 (qospi12_mem_rule0)": "0x0", + "Quad/Octal SPI 2 Memory Rule Register 1 (qospi12_mem_rule1)": "0x0", + "Quad/Octal SPI 2 Memory Rule Register 2 (qospi12_mem_rule2)": "0x0", + "Quad/Octal SPI 2 Memory Rule Register 3 (qospi12_mem_rule3)": "0x0", + "Quad/Octal SPI 3 Memory Rule Register 0 (qospi13_mem_rule0)": "0x0", + "Quad/Octal SPI 3 Memory Rule Register 1 (qospi13_mem_rule1)": "0x0", + "Quad/Octal SPI 3 Memory Rule Register 2 (qospi13_mem_rule2)": "0x0", + "Quad/Octal SPI 3 Memory Rule Register 3 (qospi13_mem_rule3)": "0x0", + "Quad/Octal SPI 4 Memory Rule Register 0 (qospi14_mem_rule0)": "0x0", + "Quad/Octal SPI 4 Memory Rule Register 1 (qospi14_mem_rule1)": "0x0", + "Quad/Octal SPI 4 Memory Rule Register 2 (qospi14_mem_rule2)": "0x0", + "Quad/Octal SPI 4 Memory Rule Register 3 (qospi14_mem_rule3)": "0x0", + "APB Bridge Slave Rule Register (apb_bridge_slave_rule0)": "0x0", + "APB Bridge Group 0 Memory Rule Register 0 (apb_grp0_mem_rule0)": "0x0", + "APB Bridge Group 0 Memory Rule Register 1 (apb_grp0_mem_rule1)": "0x0", + "APB Bridge Group 0 Memory Rule Register 2 (apb_grp0_mem_rule2)": "0x0", + "APB Bridge Group 0 Memory Rule Register 3 (apb_grp0_mem_rule3)": "0x0", + "APB Bridge Group 1 Memory Rule Register 0 (apb_grp1_mem_rule0)": "0x0", + "APB Bridge Group 1 Memory Rule Register 1 (apb_grp1_mem_rule1)": "0x0", + "APB Bridge Group 1 Memory Rule Register 2 (apb_grp1_mem_rule2)": "0x0", + "APB Bridge Group 1 Memory Rule Register 3 (apb_grp1_mem_rule3)": "0x0", + "AHB Peripherals 0 Slave Rule Register 0 (ahb_periph0_slave_rule0)": "0x0", + "AIPS bridge 0 Memory Rule Register 0 (aips_bridge0_mem_rule0)": "0x0", + "AIPS bridge 0 Memory Rule Register 1 (aips_bridge0_mem_rule1)": "0x0", + "AHB Peripherals 1 Slave Rule Register (ahb_periph1_slave_rule0)": "0x0", + "AHB Peripherals 1 Slave Rule Register (ahb_periph1_slave_rule1)": "0x0", + "AIPS Bridge Slave Rule Register (aips_bridge_slave_rule0)": "0x0", + "AIPS bridge 1 Memory Rule Register 0 (aips_bridge1_mem_rule0)": "0x0", + "AIPS bridge 1 Memory Rule Register 1 (aips_bridge1_mem_rule1)": "0x0", + "AHB Peripherals 2 Slave Rule Register 0 (ahb_periph2_slave_rule0)": "0x0", + "AHB Peripherals 2 Slave Rule Register 0 (security_ctrl_mem_rule0)": "0x0", + "AHB Peripherals 3 Slave Rule Register 0 (ahb_periph3_slave_rule0)": "0x0", + "AHB Peripherals 3 Slave Rule Register 0 (ahb_periph3_slave_rule1)": "0x0", + "Secure GPIO Register 0 (sec_gp_reg0)": "0xffffffff", + "Secure GPIO Register 1 (sec_gp_reg1)": "0xffffffff", + "Secure GPIO Register 2 (sec_gp_reg2)": "0xffffffff", + "Secure GPIO Register 3 (sec_gp_reg3)": "0xffffffff", + "Secure GPIO Register 4 (sec_gp_reg4)": "0xffffffff", + "Secure GPIO Register 5 (sec_gp_reg5)": "0xffffffff", + "Secure GPIO Register 6 (sec_gp_reg6)": "0xffffffff", + "Secure GPIO Register 7 (sec_gp_reg7)": "0xffffffff", + "Secure GPIO Register 8 for DSP (sec_gp_reg8)": "0xffffffff", + "Secure GPIO Lock Register (sec_gp_reg_lock)": "0xaaaa", + "Master Secure Level Register (master_sec_reg)": "0x80000000", + "Master Secure Level Anti-pole Register (master_sec_anti_pol_reg)": "0xbfffffff", + "M33 Lock Control Register (m33_lock_reg)": "0x800002aa", + "Secure Control Duplicate Register (misc_ctrl_dp_reg)": "0xaaaa", + "Secure Control Register (misc_ctrl_reg)": "0xaaaa", + "Miscellaneous TZM settings (misc_tzm_settings)": "0x0" + } +} diff --git a/tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_hwk_legacy.bin b/tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_hwk_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..6ff199c7afd8b301800501c478fea553d92f6a2b GIT binary patch literal 2160 zcmeIzUr19?90%~<(QV${vhMl=8Pm=M2Ui-jnWYAiX6aUhe?Uk`+H$K|ihqgKOBxat zQA8BuC7+ZM}Y8YN8bTWKO|7&HH^aqOu{s<98#=c zgFH~72;5K(mEeVXXaYa9LKhr^emDac;VKNmEw~2{U=*Ih&wb3NPb_f$d}=Oa&TFhc z4ihj5)4*_!RGt;M;E=k>2=;G90Xu1*6V_yhr?pz$VVh$1ihV}bxCj(5 zXZj6s(_JN27B$mSB5qt(Cwop#N=Su)g52bC^%~i}Qc9~Bq$d^4-jRru$sa{NIX{gN zFP?XH?XT?Xe`K2|*Fl_V*_YA*>JRrhME<1Jc2JfK=F7E zG54Ox9+&D?fdXsb2L9*og9?vf0J?`P9*dXx<>N?EmjM2z_pN?)~4_Hvp>EkN^Mx literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_hwk_legacy.hex b/tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_hwk_legacy.hex new file mode 100644 index 00000000..93b3b514 --- /dev/null +++ b/tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_hwk_legacy.hex @@ -0,0 +1,135 @@ +00000000: 0004 0030 5906 0010 5d08 0010 5f08 0010 ...0Y...]..._... +00000010: 6108 0010 6308 0010 6508 0010 6708 0010 a...c...e...g... +00000020: 7008 0000 0550 0000 81e8 7171 6908 0010 p....P....qqi... +00000030: 6b08 0010 0000 0000 6d08 0010 6f08 0010 k.......m...o... +00000040: 5908 0010 5908 0010 d106 0010 d906 0010 Y...Y........... +00000050: e106 0010 e906 0010 f106 0010 f906 0010 ................ +00000060: 0107 0010 0907 0010 1107 0010 1907 0010 ................ +00000070: 2107 0010 2907 0010 3107 0010 3907 0010 !...)...1...9... +00000080: 4107 0010 4907 0010 5107 0010 5907 0010 A...I...Q...Y... +00000090: 6107 0010 6907 0010 7107 0010 7907 0010 a...i...q...y... +000000a0: 8107 0010 8907 0010 9107 0010 9907 0010 ................ +000000b0: a107 0010 a907 0010 b107 0010 b907 0010 ................ +000000c0: c107 0010 c907 0010 5908 0010 5908 0010 ........Y...Y... +000000d0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000000e0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000000f0: 5908 0010 d107 0010 5908 0010 5908 0010 Y.......Y...Y... +00000100: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000110: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000120: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000130: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000140: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000150: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000160: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000170: 5908 0010 d907 0010 e107 0010 e907 0010 Y............... +00000180: f107 0010 f907 0010 0108 0010 0908 0010 ................ +00000190: 1108 0010 1908 0010 2108 0010 2908 0010 ........!...)... +000001a0: 3108 0010 3908 0010 4108 0010 4908 0010 1...9...A...I... +000001b0: 5108 0010 5908 0010 5908 0010 5908 0010 Q...Y...Y...Y... +000001c0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000001d0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000001e0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000001f0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000200: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000210: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000220: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000230: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000240: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000250: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000260: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000270: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000280: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000290: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000002a0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000002b0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000002c0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000002d0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000002e0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000002f0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000300: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000310: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000320: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000330: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000340: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000350: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000360: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000370: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000380: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000390: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000003a0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000003b0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000003c0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000003d0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000003e0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +000003f0: 5908 0010 5908 0010 5908 0010 5908 0010 Y...Y...Y...Y... +00000400: 8c49 0a68 520a 5202 c0b2 1043 0860 8a48 .I.hR.R....C.`.H +00000410: 0168 4902 fbd4 0168 4902 fcd5 7047 80b5 .hI....hI...pG.. +00000420: 8648 0168 490b 4903 51f0 0101 0160 8448 .H.hI.I.Q....`.H +00000430: 0168 51f4 4021 0160 7f48 0068 0002 01d4 .hQ.@!.`.H.h.... +00000440: 0120 09e0 5520 fff7 dbff aa20 fff7 d8ff . ..U ..... .... +00000450: 0a20 fff7 d5ff 0020 02bd 80b5 c0b2 fff7 . ..... ........ +00000460: cfff 01bd 1fb5 0021 0024 0a21 b0fb f1f2 .......!.$.!.... +00000470: 01fb 1201 6a46 1155 0a21 b0fb f1f0 641c ....jF.U.!....d. +00000480: 0028 f2d1 09e0 6f48 6946 2144 11f8 011c .(....oHiF!D.... +00000490: 405c c0b2 fff7 e1ff 641e 012c f3da 0020 @\......d..,... +000004a0: 04b0 10bd 38b5 0500 0c00 0020 0020 0020 ....8...... . . +000004b0: 092c 01d2 002c 00d1 0824 641e 0de0 0f21 .,...,...$d....! +000004c0: a000 11fa 00f0 2840 a100 c840 5d49 c0b2 ......(@...@]I.. +000004d0: 085c c0b2 fff7 c1ff 641e 002c efd5 0020 .\......d..,... +000004e0: 32bd 38b5 0500 0c00 0020 0020 0020 092c 2.8...... . . ., +000004f0: 01d2 002c 00d1 0824 641e 0de0 0f21 a000 ...,...$d....!.. +00000500: 11fa 00f0 2840 a100 c840 4f49 c0b2 085c ....(@...@OI...\ +00000510: c0b2 fff7 a2ff 641e 002c efd5 0020 32bd ......d..,... 2. +00000520: 38b5 0400 0d25 0020 2000 c0b2 fff7 95ff 8....%. ....... +00000530: e4b2 0a2c 03d1 2800 c0b2 fff7 8eff 0020 ...,..(........ +00000540: 32bd 10b5 0400 03e0 2078 fff7 e9ff 641c 2....... x....d. +00000550: 2078 0028 f8d1 0020 10bd 0000 0eb4 f0b5 x.(... ........ +00000560: 0600 05af 0020 0020 3078 0100 c9b2 2529 ..... . 0x....%) +00000570: 03d0 c0b2 fff7 71ff 47e0 761c 3478 2000 ......q.G.v.4x . +00000580: c0b2 5828 15d0 6328 1ed0 6428 06d0 6c28 ..X(..c(..d(..l( +00000590: 20d0 7328 13d0 7828 05d0 2fe0 3868 3f1d .s(..x(../.8h?. +000005a0: fff7 60ff 31e0 3868 3f1d 0821 fff7 7aff ..`.1.8h?..!..z. +000005b0: 2be0 3868 3f1d 0821 fff7 93ff 25e0 3868 +.8h?..!....%.8h +000005c0: 3f1d fff7 beff 20e0 3868 3f1d c0b2 fff7 ?..... .8h?..... +000005d0: a7ff 1ae0 761c 3078 c0b2 7828 0dd1 ff1d ....v.0x..x(.... +000005e0: 37f0 0707 d7e9 0045 0837 0821 2800 fff7 7......E.7.!(... +000005f0: 59ff 0821 2000 fff7 55ff 06e0 2520 fff7 Y..! ...U...% .. +00000600: 2cff 2000 c0b2 fff7 28ff 761c 3078 0028 ,. .....(.v.0x.( +00000610: aad1 bff3 4f8f bff3 5f8f f0bc 5df8 10fb ....O..._...]... +00000620: 80b5 fff7 fcfe 094a 0949 0a48 fff7 96ff .......J.I.H.... +00000630: fee7 0000 1cd0 0850 14d0 0850 10d0 0850 .......P...P...P +00000640: 18d0 0850 a406 0010 b406 0010 2106 0010 ...P........!... +00000650: c406 0010 8406 0010 72b6 0648 0649 0160 ........r..H.I.` +00000660: 0a68 82f3 0888 0548 80f3 0a88 62b6 0448 .h.....H....b..H +00000670: 0047 0000 08ed 00e0 0000 0010 0000 0030 .G.............0 +00000680: 2106 0010 2573 3a20 4865 6c6c 6f2c 2074 !...%s: Hello, t +00000690: 6573 7420 696d 6167 652c 2061 6464 723d est image, addr= +000006a0: 2578 0a00 3031 3233 3435 3637 3839 6162 %x..0123456789ab +000006b0: 6364 6566 3031 3233 3435 3637 3839 4142 cdef0123456789AB +000006c0: 4344 4546 4b34 4631 2052 4f4d 0000 0000 CDEFK4F1 ROM.... +000006d0: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +000006e0: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +000006f0: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000700: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000710: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000720: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000730: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000740: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000750: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000760: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000770: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000780: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000790: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +000007a0: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +000007b0: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +000007c0: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +000007d0: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +000007e0: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +000007f0: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000800: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000810: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000820: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000830: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000840: 0048 0047 5908 0010 0048 0047 5908 0010 .H.GY....H.GY... +00000850: 0048 0047 5908 0010 fff7 febf fee7 fee7 .H.GY........... +00000860: fee7 fee7 fee7 fee7 fee7 fee7 fee7 fee7 ................ diff --git a/tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_hwk_tz_legacy.bin b/tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_hwk_tz_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..7a376a91f8232e3f9fcf8be3f199b336ba663117 GIT binary patch literal 2572 zcmeIzUr19?90%~SM*emXg4GG++# z55owI!WUrJM;q8-DX35a9;k$B@PQv@GS}?b+5P(0J8;1#M;hb?(?DF=A_dNSS;S|W zcx9B|1`pQ75@(Dnb9@Eoq#$MFIV9>bM;&ERBkL}0ajI1-PK7jUszc+cE^T2ds##KN zG8K_c%Y_~go_tGEg??PD{?)!BoKqtn3!ZNoneT{Y$n1^|#G_hKS}ZTm78br9p&F@N zQZ*8KlaN%7x=~m&PCP4@_;F{NFrrB7N%!aKFZJhIE@dAzk1G?ggwVfD+P2wzvRCNW z#N-DeM@rhc2ozWXxAC3t1r?scX*k?x^;&%_AV*H+q_Y-pEGM7c(`WWl9g*~rGWdlU z{}Hzl^ppZxImnSVyJ#sA)cm=L1B$3(G}Q-`sj E@8QYZZvX%Q literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_legacy.bin b/tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..70e0b5a3bae6eca51b2ecfbec6cd1dae67a9f16c GIT binary patch literal 2160 zcmeIzUr19?90%~<(QV${vhMl=71Pdz23H!inWYBN%zwK@n1YaywB=T_3^OBEFIhxX zL=sWdTSXB0VhtArEfspmr?yD?_c9>}AEMcUYZ;!tcl{%R(5GJ9<#W#Yjr04RbI&fj zWFb$Bjg(f2lwA;nHVDIBI4BWWeMDFFdHwB(_XFW`0DUL4{E$M8S1<F z2P_8_O27-1Pz5?PLJI_;9lGEsoPe`%5w5`vxC8g#0Sv9&bZCUxtTi`x?!5l>44l{HNR!-@HxyH2X6LOOFF=bD8`rGT&8pkAa`gWRGVO(Opmp_bra(@^l zUOMCKhF{q?|H!sbp^G>tb6=Om+nDUmPieX60`9Xkp~NkepXO0vjEj(yM@gGZ5mypK z3kEWA8(I6!eS4yeMq;j#{G?f@OVp>2ILo4D)g9W>WSdr;3~9DxhsKlL+TvtXvnJQ1 zD=! z^gAMFQrf-*6j%ng@IOBQDm;c$aOl3xZ_`;oj-1R&XRQ97tbAtoeT$#!iKO?G#w)~p zk9f=+SN8_)4|jBQy7}R7_hH_CAhP~2$ltGR`7=jTPg|EPhNU?(h zmVyeU;Dsuv0Y5ZAGXx4h~ofDwKj3s-On^&;T=;Yj*7He*NnmxZsx~jdG)@KcQ@q0_S}!;x~=I zHp=ePF%~$DUc;Aal?6`tC_*OonsIx!i3|O`=t22b$S4N{aZ@Uu z>`w^&rC_NQNm6N*tll3V{mjM}u>G};ra~_la+3BrW6yT_qP6N)#~5=~?=hl=M5u(h z^Y4gT?klOvq?P6papSbR*(-8WMp_jrDbCJUZ;;~~<+SRX^t`&o-=B~Q@lO#??oR{6 z%cdP&KNWrJk7x@OyNGi#)^$~UNXTw;Ml19#;y%k#O435+ERP9&M1-6?M%q}0xRN58 z*PBb)$a>FMw+e(rWI$z@`_wh(VG$4CRIqP zMnZ2ElFCsx30u~MXXO?@?@SX$6lpz~zI^?qzI^MY+~bxpWjvk~`u7R@cDrBp3H_Rw z7$$P2rCp0afhBMo-}zop;TfEU!w+pfo1X>b$f>+^*6NGr<+E)MEk3Fvl0H%vzYya; z;xS@e-5aA^Lhu6kj=c7z_FV D5Ov%p literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_tz_no_preset_legacy.bin b/tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_crc_tz_no_preset_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..b9ec29912d0b2be21cee8c28fda6aad75ae3af11 GIT binary patch literal 2160 zcmeIzUr19?90%~<(QV${vhMl=8OzRv23H!inWYAimg!c6DF_Kkn{GMFF#iy%mnsC?KR`=-Z%vRsuCXz%Y!$7)%1oA;k(d z$O9D$zzwBP4qm8%dhkOFw8Kd_0~g^M+=4ss0G_}zcnSURYajFJ6APR_pPCDq^8xD* z!zhfwBru$#6>N|PDinYlN}(LQPy-8DYjN)4dHw4dxa^ffwQ_A%Uqsm@`7U`_$eT4a zV6@YN$E&)d=Z#;Mc(ZF#j4|IiL^a>aROUPFp%7)U0miM#Ixh4f@eu8MA)_4>#Is`E z(Y}b#-v}04A0?GG$m-*%v2ixFf*q-})n%eoz)sqD!kTRJv{b4Uwh3l0J7{DLi$Fee zrauxlJyv2RQ8O(i;>KllvbW@it z7tTAodM^9!AK50#br9!d?CXa36p@|T2`v*{!F?9Sl&FcalRPZ+Q4z57FliGB;!2EY z+0#_iLgq)tzFlEP{Sik&cHF4bE~=6z>_uUt>H%$ayh+QC2Q*8(RpaqaZDl;Hnd6(1 zC7~VbgzgjW%t%s&eoAbd8($ZW*%>bc&y0-9_r+RNc7^*QVJ$Zy))l96a^8*7ZmC#O zH4=KgkW`MoPFRu-{8vu#^UgG3WRdnT(VMQm+M7OfHTAqZ;uB(g zM_fjZqjMYg1Y29%oV+X8*~MFq`J01I?hgbywmZTyx!i@Di?(bnE-CdlHU)x*f5+uJ kcUA1J+*?%X=C#%PP>%>J7|;7+|9{Ux=rg@DBY$5%0E!saf&c&j literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_signed.bin b/tests/elftosb/data/workspace/output_images/lpc55s1x/mb_xip_signed.bin new file mode 100644 index 0000000000000000000000000000000000000000..bc1829c63373122aa5675383708f03f90f34d8b1 GIT binary patch literal 3700 zcmeIzdsGuw8UXM+Nq{805|R2snYf_gr85X9C>m5SfeP}76|F)b5s*g^SvIw`kcfcG zON!QAb+sZ3iZ2=kg)IVFx1ug2u2e+D(DkuYtGqNI%Ir6;+V0tN_UK>RKb$$gx%17r z-@P;U-kCuxL`0e*-fA}DMFAOz1qu)c(yb6;i4aO!2I- zfIct`Ff+vC06v%i1i%T9z#I4h2?zy|Kn4=PdhiKQgRP(h{1NO2hrrjM4m5)n@cKU9 zrqsV<{kLiJUFuwi{<}dB=mWz5gK^{lK9~RmzzLAR8~6bU2nFxb*ZaEf$LoKsfdUCP zK8zd2Xwvc)v7>)4#^NQ6k*oCad3)-TtePD9in)Xt+KXXWbT&kxgCYSIohe4~h=E}!w!IdBElBYpkY$QW#00AKnt>Xq9e zVZWYZP>K@*1dI7Yn4RcjI;)$KPr+OVIuXvQ<~7WzF+mmx;q=aN#jYS%q-Q(Gr&t=S z1z#clGt?^-?MD|xt0hfZ_RIWc$VaY^+Y#>m*4ZI%vdjJ>n}sYL5soAJzIIS|v|LxF zUidO9g6DK^;MK4Y(}1feV-F>_!&OK)q(?Zf0U@iCy)~xD^3n=48%H_>@ zDdqU8#u*BKc}BNfNlk>7300F;CA8F2lV|sunO*Ba{_NRofe=x~NQy1Mp_Edl21l4H zSL)wSCZy;rB&^am4uljp4n!6AezSUrHLdN!J@hGngMp6i8yP3n-^2d5~C#uv427a_=V8_5rWQf+~9+Y z6-h}-S3F&@AstUhmc=PtahY76=If~9B7$_E<>Bcydycm(CRVOk`*ZBKaM5D_fFO?m z5)TVm3hf~TJ<#j@efj?#2g*3wIC}rT_gF<*ItJ5(({uG|OSeRbz9wfO6b%RsE0WMK z{k~!{Fb0D??;Jw)^j~m2W$&QVEF66 z%z_cVlC&Z$=xSUGlckHK;UAF}1ecD@Ets%sNFHHDM@$Q|6jkbsSY}6$kqO zPfUo3Nm4}7i>5Vi`F@nUb6ko2J~LT$@fO5L3oklHMdSizK`}Ne^=6I7@c|LgH2U zYr0hpHuaYSiJ2g#2D64S$G8j)hM_aHyE1GaGjdM1YnN39_fN1Js+@OsHKLs=pQ3w4&AFAM1`yY!31`!aH!I!f9@Sdn$H*QgiX% zVMEF`(R>&F(20(8iMS;BE>ms0aE)k=z;Rj8m65^Uo=#yabRYAEN_^VXciK04nOzk< zTHaw(Jn?4HcNgNNKR$KAt@%fKOGk~NJ)!e^yI#cj*v=^IbSw3{#3TIIKOI5>zRUS#^8t(A7(bKt-7pNrVr4;(O^_|l{IA%Q9cTjs_%^@@2sbN-MJlxdg z|HX1{{f)Yo=2e=|S%!$h;LVlYC$g%AA%Ydbe;ugHb-C2vQ`Q+F-3H$o$x(*~zAm@I z4nCfA`=ZP(oF{v3lR;fQvfTf3-R7){>0j?VyFPoivrnXnxWB_2-+H{<=68y?%bfz> zq}9_$XK$6>KQQ-fdc?yuwsmVcb1F}gRi4MDw%yy3S~@+1FV{KL%BJ6)P)F?a?ho zd0VI^jj3{~m$hBx_IBBcQZ@s#{fu39R(j54DCLP~ZkuIfj(zT8>c*$@)27vSB^eG4 zk)ln$WhGw@%}cJny~WJZ{UP6&mHWWK^OE+Or@e*~;TCw$J^jhW88gKvf_5pDO~HpV ZteSu5E7O!oW4gi;GxkRn=)L6He*t*DB9Z_A literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/mb_ram_crc.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/mb_ram_crc.bin similarity index 100% rename from tests/elftosb/data/workspace/output_images/mb_ram_crc.bin rename to tests/elftosb/data/workspace/output_images/lpc55s3x/mb_ram_crc.bin diff --git a/tests/elftosb/data/workspace/output_images/mb_ram_crc_entz.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/mb_ram_crc_entz.bin similarity index 100% rename from tests/elftosb/data/workspace/output_images/mb_ram_crc_entz.bin rename to tests/elftosb/data/workspace/output_images/lpc55s3x/mb_ram_crc_entz.bin diff --git a/tests/elftosb/data/workspace/output_images/mb_ram_crc_my.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/mb_ram_crc_my.bin similarity index 100% rename from tests/elftosb/data/workspace/output_images/mb_ram_crc_my.bin rename to tests/elftosb/data/workspace/output_images/lpc55s3x/mb_ram_crc_my.bin diff --git a/tests/elftosb/data/workspace/output_images/lpc55s3x/mb_ram_crc_version.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/mb_ram_crc_version.bin new file mode 100644 index 0000000000000000000000000000000000000000..b4d2f981b5daabd1e0b482c76ae3d80292fc3377 GIT binary patch literal 2660 zcmeIzUr19?90%~A2Ul9OnWYAi_D8oOOi9Q{-1M$y8CFKEUb2X& zh$5n(w~8R@B^xaWS{n3F1hqxdzn2Mt_z+E3T+8tEz3U$lgg*6<96smIIKSUH_ngZe zS;^C2Bc)Lyr3r%20wLH6x=ds#Us02UMLIVV$6?VaXI0VPx3|xRqa1Cz49k>sV;Kx43(k90_e=Ir2Gv^i7 z?}GstgipY5j&^XsY*3*Hyig96;D=fm&sr06C(i3{@4!jF9Ilh=EImD^h~89QqJGRdD)n`Vp!j$l;#p+aSWgFXtAg*|87o~q}g@^8^r%6AdxWbW&ncpH`7IY})$I)&#fjw>-M<)nB-7z09g@(5|eN#aVJ zXv(d0%tpC4%zb+zj2=Z@ML7vG&n{7&+UqQdm{~V!GZQUZK|-bY9+z0a`7U zN~%V}Xb_Uh(bfxF%7wdfi*HY+2{Vec>&fm+&FSt;)9Lix#$jcoGbW5LqxLm+zw8sn zc`@2g)_mLQqiQ1Q9i@oO ze-ZMSF|LlK+!tzVYj^WKp^iPgbw_Y($jyVgzI&M~B9q5kyr^XHlG3vBU~`Ke+VX2$ yxnkw2)m3Xts=U0eW*zboVFmMjpJ@N>IS6C4d$j-0vSHJLZ}*hFtcmJ>IDxNxkl1Me literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/mb_ram_crc_version_my.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/mb_ram_crc_version_my.bin similarity index 100% rename from tests/elftosb/data/workspace/output_images/mb_ram_crc_version_my.bin rename to tests/elftosb/data/workspace/output_images/lpc55s3x/mb_ram_crc_version_my.bin diff --git a/tests/elftosb/data/workspace/output_images/lpc55s3x/mb_ram_plain_tz.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/mb_ram_plain_tz.bin new file mode 100644 index 0000000000000000000000000000000000000000..be5107deabb42b570b324f0715423ba390a03ed0 GIT binary patch literal 2660 zcmeIzUr19?90%~<(K+vKS$F+`ifQM9gDWlC%u<6$`=eVCrX*w}Zh2R;3@am6FIhxb zL=jQYTSXA{QVk~rEfspmr?ybadYKS}57BhN)eKMHyZ#YD=u;2L;dB0s^ZT81&$;Z9 zh1?BRQW_;vnjiqp5QP1p%S2SjP!Y9Yd=OCR5ZX3q_%4AQLof^@FbWgEa!Ij)9hQO$ zMc{#Qs01I>LIVV#1&+W8I1T6F3fzEOa1S2AW9Wl^__2@aw22wcpH9x1%o)P^!!QD) zFaZqbXahSe1r>_G1LaT&KB$G6tTj7#_PqY~4qWufp*p!Pt2d%-lKdCEEac0Y95CxI zgNLg-qUX$C=J>K}Q;aeHS&V8wRjAB=%1a^2VgroZlJ#5|Z^eDouZ4_yP!P|Gbwqn3 z!gwKAYFXB zuK5{#`?qKdD1aT!sG_NZa zwNlOlbKmYTqyC7qC_8TEIU=f)#~meMGwUvGalBb8i0hg)-m39dcS zj<~dA5h$<(?%;pE3siUpXW?kK)ob-JzZ^P~mO6919cj69Z@0xu)kM+GRVEZxNawxDr=;8rgKf2x-mdWiYUR|3P(XEXY2XRqGK&wzNcfo-eo`jz&%ndr$|k+XHCE-c C&nZ;^ diff --git a/tests/elftosb/data/workspace/output_images/mb_xip_384_256.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/mb_xip_384_256.bin similarity index 98% rename from tests/elftosb/data/workspace/output_images/mb_xip_384_256.bin rename to tests/elftosb/data/workspace/output_images/lpc55s3x/mb_xip_384_256.bin index 8539922df2dca804a8d1b5fa51ed1ec6bac86046..5e9a881d3aaf2b952e59affdaafee12b6446ff76 100644 GIT binary patch delta 78 zcmV-U0I~nXR>W42B?1Hiu_xUsKt-pcb< kurv2q)peF1kOyuX6Mfue-wwZ5(?%32$cYK0vV+$5K!zkGN&o-= delta 78 zcmV-U0I~nXR>W42B?15du_xUsKw8;*hw#bCxtBq*R^Pg8K>Y-}etARfp4b#0;uW~3 kzZeQ02mwp&(TagC8)>VKm`+J$Ca{3plPJ%3gb7z&+^dKrYybcN diff --git a/tests/elftosb/data/workspace/output_images/mb_xip_384_256_false.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/mb_xip_384_256_false.bin similarity index 100% rename from tests/elftosb/data/workspace/output_images/mb_xip_384_256_false.bin rename to tests/elftosb/data/workspace/output_images/lpc55s3x/mb_xip_384_256_false.bin diff --git a/tests/elftosb/data/workspace/output_images/mb_xip_384_256_true.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/mb_xip_384_256_true.bin similarity index 100% rename from tests/elftosb/data/workspace/output_images/mb_xip_384_256_true.bin rename to tests/elftosb/data/workspace/output_images/lpc55s3x/mb_xip_384_256_true.bin diff --git a/tests/elftosb/data/workspace/output_images/mb_xip_384_384 - Copy.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/mb_xip_384_384 - Copy.bin similarity index 100% rename from tests/elftosb/data/workspace/output_images/mb_xip_384_384 - Copy.bin rename to tests/elftosb/data/workspace/output_images/lpc55s3x/mb_xip_384_384 - Copy.bin diff --git a/tests/elftosb/data/workspace/output_images/mb_xip_384_384.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/mb_xip_384_384.bin similarity index 93% rename from tests/elftosb/data/workspace/output_images/mb_xip_384_384.bin rename to tests/elftosb/data/workspace/output_images/lpc55s3x/mb_xip_384_384.bin index ce424d4f9386764ba33d7003f6587b0c0d828587..282aa62a536df3a8e5c9ba73e36c4612d5b3b8c0 100644 GIT binary patch delta 612 zcmZn({}MStf&GgP0|N^S<3{zb8caFNo8>gs7dK&`PNPKd@i~ z+P{s@tCQ9Jf7Qc}m;XxyuZuPP+IDL3z670xiMKO+XMUaQE#UogyU&AIarZUJ+ZoDc znx+^WXgQvJ$Z_`#;k`c{3=U4-dUu|D+T}xTZv#~dCGTFd_~AcW(DXdZ115$2_D7{w zOcu}oojJ%@`^NQL$+qx|Ub`=SShMR|=e*9yx2Nyd9(HPIWRtj; zwDBiHzRis;L+;jZ&vON9rFT!(KlH3Z&ga0J^QuBOJWZpt>`|EnTLIVV#1&+W8I1T6F3fzEOa1S2AW9Wl^__2@aw22wcpH9x1%o)P^!!QD) zFaZqbXahSe1r>_G1LaT&KB$G6tTj7#_PqY~4qWufp*p!Pt2d%-lKdCEEac0Y95CxI zgNLg-qUX$C=J>K}Q;aeHS&V8wRjAB=%1a^2VgroZlJ#5|Z^eDouZ4_yP!P|Gbwqn3 z!gwKAYFXB zuK5{#`?qKdD1aT!sG_NZa zwNlOlbKmYTqyC7qC_8TEIU=f)#~meMGwUvGalBb8i0hg)-m39dcS zj<~dA5h$<(?%;pE3siUpXW?kK)ob-JzZ^P~mO6919cj69Z@0xu)kM+GRVEZxNawxDr=;8rgKf2x-mdWiYUR|DV%fRi;7W@&v(zBc{^(YODG3=#o8Hw{hM5tomna^yZ#YD=u;2L;dB0s^ZT81&$--@ zg*?qRQd%TZc0v%^AOyQXmx-(;jEahSA`y)D0t)Rz+X2nrrI6z_48kysz-M3uq}ag$ z3qXZZ@In>TfFBy58G;ajPB;w5;51x-E6@Ws;V#^VNAMJW>|-)*YKrqGlXEI_USs`1 z7={t}3=HRJ2L~(w6-vPiRZs(dXn?7#H9dFwy#DqMob}7$M!7MsKc=jc0%v?I?9Ur} zVbwt1NKTM`6liFBrF{o47FEid(2(2^sZ(AfA`# zj`zoe@l3G9;y9^PBCB`CMn1B!Ic!g@qbWDa=uXl;rtIksU!+!D?-*sy>K$g(pwNq% zJNt&X<&Ki5j9Y0Y5jTt7&7PB+Qc{UtT$rA%UM0sD%4pR+^rX7Q-yf55@sAKs?hgaR z%O)LN|1o#p(vP~;a>Y6RtuJL4-Ha8j7tjT5R z%J7;+!Uza&t|zI&I3kw(>|YeF?=v0>p6i*JZ-|A+?1}crqFP}}EUL&96uca!4N`@q zY9x$iA*mc~ldz>-xGT5#_GFqcqe#1z>dV%j@5}BypLx(Ss*H8Vg)u&1-)Q&CK4DxI z6GKGKq_m|76qpa!@tyAl6&}M0IB?JAv-w#-4j<1-r>ws2tbD5Vp2bIXMACao6Pf=a z^W@)2PL^L?Lg|Lr*lW1??j=+Cls`_gasl)J3y>VG(aud=t+dH?_b literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/normal_boot_sb3.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/normal_boot_sb3.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/normal_boot_sb3.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/normal_boot_sb3.sb3 diff --git a/tests/elftosb/data/workspace/output_images/normal_boot_sb3_ft.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/normal_boot_sb3_ft.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/normal_boot_sb3_ft.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/normal_boot_sb3_ft.sb3 diff --git a/tests/elftosb/data/workspace/output_images/normal_boot_sb3_unencrypted.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/normal_boot_sb3_unencrypted.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/normal_boot_sb3_unencrypted.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/normal_boot_sb3_unencrypted.sb3 diff --git a/tests/elftosb/data/workspace/output_images/normal_boot_sign.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/normal_boot_sign.bin similarity index 100% rename from tests/elftosb/data/workspace/output_images/normal_boot_sign.bin rename to tests/elftosb/data/workspace/output_images/lpc55s3x/normal_boot_sign.bin diff --git a/tests/elftosb/data/workspace/output_images/normal_boot_signNoIsk.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/normal_boot_signNoIsk.bin similarity index 100% rename from tests/elftosb/data/workspace/output_images/normal_boot_signNoIsk.bin rename to tests/elftosb/data/workspace/output_images/lpc55s3x/normal_boot_signNoIsk.bin diff --git a/tests/elftosb/data/workspace/output_images/normal_boot_sign_crc.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/normal_boot_sign_crc.bin similarity index 100% rename from tests/elftosb/data/workspace/output_images/normal_boot_sign_crc.bin rename to tests/elftosb/data/workspace/output_images/lpc55s3x/normal_boot_sign_crc.bin diff --git a/tests/elftosb/data/workspace/output_images/sb3Img.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3Img.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3Img.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3Img.sb3 diff --git a/tests/elftosb/data/workspace/output_images/sb3Img_unecrypted.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3Img_unecrypted.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3Img_unecrypted.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3Img_unecrypted.sb3 diff --git a/tests/elftosb/data/workspace/output_images/sb3_256_256.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_256_256.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3_256_256.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_256_256.sb3 diff --git a/tests/elftosb/data/workspace/output_images/sb3_256_256_notime.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_256_256_notime.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3_256_256_notime.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_256_256_notime.sb3 diff --git a/tests/elftosb/data/workspace/output_images/sb3_256_none.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_256_none.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3_256_none.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_256_none.sb3 diff --git a/tests/elftosb/data/workspace/output_images/sb3_256_none_ernad.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_256_none_ernad.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3_256_none_ernad.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_256_none_ernad.sb3 diff --git a/tests/elftosb/data/workspace/output_images/sb3_384_256.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_384_256.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3_384_256.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_384_256.sb3 diff --git a/tests/elftosb/data/workspace/output_images/sb3_384_256_fixed_timestamp.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_384_256_fixed_timestamp.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3_384_256_fixed_timestamp.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_384_256_fixed_timestamp.sb3 diff --git a/tests/elftosb/data/workspace/output_images/sb3_384_256_unencrypted.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_384_256_unencrypted.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3_384_256_unencrypted.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_384_256_unencrypted.sb3 diff --git a/tests/elftosb/data/workspace/output_images/sb3_384_384.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_384_384.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3_384_384.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_384_384.sb3 diff --git a/tests/elftosb/data/workspace/output_images/sb3_384_384.txt b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_384_384.txt similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3_384_384.txt rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_384_384.txt diff --git a/tests/elftosb/data/workspace/output_images/sb3_384_384_nxp.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_384_384_nxp.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3_384_384_nxp.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_384_384_nxp.sb3 diff --git a/tests/elftosb/data/workspace/output_images/sb3_384_none.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_384_none.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3_384_none.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_384_none.sb3 diff --git a/tests/elftosb/data/workspace/output_images/sb3_test_384_384.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_test_384_384.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3_test_384_384.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_test_384_384.sb3 diff --git a/tests/elftosb/data/workspace/output_images/sb3_test_384_384_unencrypted.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_test_384_384_unencrypted.sb3 similarity index 100% rename from tests/elftosb/data/workspace/output_images/sb3_test_384_384_unencrypted.sb3 rename to tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_test_384_384_unencrypted.sb3 diff --git a/tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_crc_hwk_legacy.bin b/tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_crc_hwk_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..70e0b5a3bae6eca51b2ecfbec6cd1dae67a9f16c GIT binary patch literal 2160 zcmeIzUr19?90%~<(QV${vhMl=71Pdz23H!inWYBN%zwK@n1YaywB=T_3^OBEFIhxX zL=sWdTSXB0VhtArEfspmr?yD?_c9>}AEMcUYZ;!tcl{%R(5GJ9<#W#Yjr04RbI&fj zWFb$Bjg(f2lwA;nHVDIBI4BWWeMDFFdHwB(_XFW`0DUL4{E$M8S1<F z2P_8_O27-1Pz5?PLJI_;9lGEsoPe`%5w5`vxC8g#0Sv9&bZCUxtTi`x?!5l>44l{HNR!-@HxyH2X6LOOFF=bD8`rGT&8pkAa`gWRGVO(Opmp_bra(@^l zUOMCKhF{q?|H!sbp^G>tb6=Om+nDUmPieX60`9Xkp~NkepXO0vjEj(yM@gGZ5mypK z3kEWA8(I6!eS4yeMq;j#{G?f@OVp>2ILo4D)g9W>WSdr;3~9DxhsKlL+TvtXvnJQ1 zD=! z^gAMFQrf-*6j%ng@IOBQDm;c$aOl3xZ_`;oj-1R&XRQ97tbAtoeT$#!iKO?G#w)~p zk9f=+SN8_)4|jBQy7}R7_hH_CAh*O>!iRbAB*@+ zV=s()ynj!9EP34cWQM<}F~=AS9K)#bqgQ2t!#;|TiM?RlmTl%je=FjsUkMp?zaVZ( z#gcsqp+6HWwKz#CEs@pRW5XkCY!2I9XKyZyav=w4BN^6=k%-=nSs2BBdnFRFvgWp%03XgGWgl%@9{o zM6>SZl2)?ZGS1r_W%M-REH6qMdG?C>>;Xq*)X2J3o1bpe%F-dtn(owix<{Lvj%t?l zvTSu^%_5-(gs0GxRG}XdOMmpw3+J~P4+SsujLbL0LS%MF`w~&DBqJ78<%)}64$=mx zN>VivdW(=$j<#7?vrgPAm-u>RnlPeBi)VWC4QG4v+t216w2mrcv82$)Cu|#Se%UAV zi(=wEks~c_nFk6ifb003zY8inhGVerp4Dgdvw$2qnwL&ke6hTIV#htRkLrn}AjqBD7^FM zxOVNj^&9FoRn~cUQ^RKDBf<*C^FH1F+kFuFMDN7=-;csQny&u49T0ygas2;7iQ((s PxQSvI#^P}zgHgT!9GUkV literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_crc_legacy.bin b/tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_crc_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..70e0b5a3bae6eca51b2ecfbec6cd1dae67a9f16c GIT binary patch literal 2160 zcmeIzUr19?90%~<(QV${vhMl=71Pdz23H!inWYBN%zwK@n1YaywB=T_3^OBEFIhxX zL=sWdTSXB0VhtArEfspmr?yD?_c9>}AEMcUYZ;!tcl{%R(5GJ9<#W#Yjr04RbI&fj zWFb$Bjg(f2lwA;nHVDIBI4BWWeMDFFdHwB(_XFW`0DUL4{E$M8S1<F z2P_8_O27-1Pz5?PLJI_;9lGEsoPe`%5w5`vxC8g#0Sv9&bZCUxtTi`x?!5l>44l{HNR!-@HxyH2X6LOOFF=bD8`rGT&8pkAa`gWRGVO(Opmp_bra(@^l zUOMCKhF{q?|H!sbp^G>tb6=Om+nDUmPieX60`9Xkp~NkepXO0vjEj(yM@gGZ5mypK z3kEWA8(I6!eS4yeMq;j#{G?f@OVp>2ILo4D)g9W>WSdr;3~9DxhsKlL+TvtXvnJQ1 zD=! z^gAMFQrf-*6j%ng@IOBQDm;c$aOl3xZ_`;oj-1R&XRQ97tbAtoeT$#!iKO?G#w)~p zk9f=+SN8_)4|jBQy7}R7_hH_CAh*O>!iRbAB*@+ zV=s()ynj!9EP34cWQM<}F~=AS9K)#bqgQ2t!#;|TiM?RlmTl%je=FjsUkMp?zaVZ( z#gcsqp+6HWwKz#CEs@pRW5XkCY!2I9XKyZyav=w4BN^6=k%-=nSs2BBdnFRFvgWp%03XgGWgl%@9{o zM6>SZl2)?ZGS1r_W%M-REH6qMdG?C>>;Xq*)X2J3o1bpe%F-dtn(owix<{Lvj%t?l zvTSu^%_5-(gs0GxRG}XdOMmpw3+J~P4+SsujLbL0LS%MF`w~&DBqJ78<%)}64$=mx zN>VivdW(=$j<#7?vrgPAm-u>RnlPeBi)VWC4QG4v+t216w2mrcv82$)Cu|#Se%UAV zi(=wEks~c_nFk6ifb003zY8inhGVerp4Dgdvw$2qnwL&ke6hTIV#htRkLrn}AjqBD7^FM zxOVNj^&9FoRn~cUQ^RKDBf<*C^FH1F+kFuFMDN7=-;csQny&u49T0ygas2;7iQ((s PxQSvI#^P}zgHgT!9GUkV literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_crc_tz_no_preset_legacy.bin b/tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_crc_tz_no_preset_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..b9ec29912d0b2be21cee8c28fda6aad75ae3af11 GIT binary patch literal 2160 zcmeIzUr19?90%~<(QV${vhMl=8OzRv23H!inWYAimg!c6DF_Kkn{GMFF#iy%mnsC?KR`=-Z%vRsuCXz%Y!$7)%1oA;k(d z$O9D$zzwBP4qm8%dhkOFw8Kd_0~g^M+=4ss0G_}zcnSURYajFJ6APR_pPCDq^8xD* z!zhfwBru$#6>N|PDinYlN}(LQPy-8DYjN)4dHw4dxa^ffwQ_A%Uqsm@`7U`_$eT4a zV6@YN$E&)d=Z#;Mc(ZF#j4|IiL^a>aROUPFp%7)U0miM#Ixh4f@eu8MA)_4>#Is`E z(Y}b#-v}04A0?GG$m-*%v2ixFf*q-})n%eoz)sqD!kTRJv{b4Uwh3l0J7{DLi$Fee zrauxlJyv2RQ8O(i;>KllvbW@it z7tTAodM^9!AK50#br9!d?CXa36p@|T2`v*{!F?9Sl&FcalRPZ+Q4z57FliGB;!2EY z+0#_iLgq)tzFlEP{Sik&cHF4bE~=6z>_uUt>H%$ayh+QC2Q*8(RpaqaZDl;Hnd6(1 zC7~VbgzgjW%t%s&eoAbd8($ZW*%>bc&y0-9_r+RNc7^*QVJ$Zy))l96a^8*7ZmC#O zH4=KgkW`MoPFRu-{8vu#^UgG3WRdnT(VMQm+M7OfHTAqZ;uB(g zM_fjZqjMYg1Y29%oV+X8*~MFq`J01I?hgbywmZTyx!i@Di?(bnE-CdlHU)x*f5+uJ kcUA1J+*?%X=C#%PP>%>J7|;7+|9{Ux=rg@DBY$5%0E!saf&c&j literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_signed.bin b/tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_signed.bin new file mode 100644 index 0000000000000000000000000000000000000000..bc1829c63373122aa5675383708f03f90f34d8b1 GIT binary patch literal 3700 zcmeIzdsGuw8UXM+Nq{805|R2snYf_gr85X9C>m5SfeP}76|F)b5s*g^SvIw`kcfcG zON!QAb+sZ3iZ2=kg)IVFx1ug2u2e+D(DkuYtGqNI%Ir6;+V0tN_UK>RKb$$gx%17r z-@P;U-kCuxL`0e*-fA}DMFAOz1qu)c(yb6;i4aO!2I- zfIct`Ff+vC06v%i1i%T9z#I4h2?zy|Kn4=PdhiKQgRP(h{1NO2hrrjM4m5)n@cKU9 zrqsV<{kLiJUFuwi{<}dB=mWz5gK^{lK9~RmzzLAR8~6bU2nFxb*ZaEf$LoKsfdUCP zK8zd2Xwvc)v7>)4#^NQ6k*oCad3)-TtePD9in)Xt+KXXWbT&kxgCYSIohe4~h=E}!w!IdBElBYpkY$QW#00AKnt>Xq9e zVZWYZP>K@*1dI7Yn4RcjI;)$KPr+OVIuXvQ<~7WzF+mmx;q=aN#jYS%q-Q(Gr&t=S z1z#clGt?^-?MD|xt0hfZ_RIWc$VaY^+Y#>m*4ZI%vdjJ>n}sYL5soAJzIIS|v|LxF zUidO9g6DK^;MK4Y(}1feV-F>_!&OK)q(?Zf0U@iCy)~xD^3n=48%H_>@ zDdqU8#u*BKc}BNfNlk>7300F;CA8F2lV|sunO*Ba{_NRofe=x~NQy1Mp_Edl21l4H zSL)wSCZy;rB&^am4uljp4n!6AezSUrHLdN!J@hGngMp6i8yP3n-^2d5~C#uv427a_=V8_5rWQf+~9+Y z6-h}-S3F&@AstUhmc=PtahY76=If~9B7$_E<>Bcydycm(CRVOk`*ZBKaM5D_fFO?m z5)TVm3hf~TJ<#j@efj?#2g*3wIC}rT_gF<*ItJ5(({uG|OSeRbz9wfO6b%RsE0WMK z{k~!{Fb0D??;Jw)^j~m2W$&QVEF66 z%z_cVlC&Z$=xSUGlckHK;UAF}1ecD@Ets%sNFHHDM@$Q|6jkbsSY}6$kqO zPfUo3Nm4}7i>5Vi`F@nUb6ko2J~LT$@fO5L3oklHMdSizK`}Ne^=6I7@c|LgH2U zYr0hpHuaYSiJ2g#2D64S$G8j)hM_aHyE1GaGjdM1YnN39_fN1Js+@OsHKLs=pQ3w4&AFAM1`yY!31`!aH!I!f9@Sdn$H*QgiX% zVMEF`(R>&F(20(8iMS;BE>ms0aE)k=z;Rj8m65^Uo=#yabRYAEN_^VXciK04nOzk< zTHaw(Jn?4HcNgNNKR$KAt@%fKOGk~NJ)!e^yI#cj*v=^IbSw3{#3TIIKOI5>zRUS#^8t(A7(bKt-7pNrVr4;(O^_|l{IA%Q9cTjs_%^@@2sbN-MJlxdg z|HX1{{f)Yo=2e=|S%!$h;LVlYC$g%AA%Ydbe;ugHb-C2vQ`Q+F-3H$o$x(*~zAm@I z4nCfA`=ZP(oF{v3lR;fQvfTf3-R7){>0j?VyFPoivrnXnxWB_2-+H{<=68y?%bfz> zq}9_$XK$6>KQQ-fdc?yuwsmVcb1F}gRi4MDw%yy3S~@+1FV{KL%BJ6)P)F?a?ho zd0VI^jj3{~m$hBx_IBBcQZ@s#{fu39R(j54DCLP~ZkuIfj(zT8>c*$@)27vSB^eG4 zk)ln$WhGw@%}cJny~WJZ{UP6&mHWWK^OE+Or@e*~;TCw$J^jhW88gKvf_5pDO~HpV ZteSu5E7O!oW4gi;GxkRn=)L6He*t*DB9Z_A literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_signed_chain.bin b/tests/elftosb/data/workspace/output_images/lpc55xx/mb_xip_signed_chain.bin new file mode 100644 index 0000000000000000000000000000000000000000..d6df6f3469b3f1c7ebc137480b4f8681a3a232e7 GIT binary patch literal 7792 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 literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_crc_hwk_legacy.bin b/tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_crc_hwk_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..864acfb029afb9960e3e3d0f796c239928b0fe45 GIT binary patch literal 2160 zcmeIzUr19?90%~=DA-JIoD!>c1&;WjDg`;o+PQ!V)0yp3m+=EB(7zW@a{MyHC`otXP&!*;F<_ux| zVHkl?m;i=zw1N%tK!rkZLm5TxGsf9tx3(yP@nJqm)*Di(ZsBdix_%CVv3=WekBESshe;bx5LaSE zi+WN~3uQkr_U#HYdKqyPX2p#x(yREGaGX?{5wS5B!cR lwr<~MH<1hhVfT507u)$JLp&Z;$19jkqCYa4yg}H@#{p%gL;FUwoaZYXtlTKd2O3_ASUJF2N6%s zk0Zn@@{Vr&6@B}UXfu^Mh;uTo>#BGklbuB=EjPM|`>aSPaWfUAd06PFf`m^nqGEdqjm8nQv=-;NT+pJ#M zBlK%xdW^`Ply)ux1(v`aeCPW>g=cUY4nMMZEMDf5L#MLR*8GGRG?6SLIv`yfXHKu SMsvv`nA#sTQN$R}oTKlOI0QZb literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_crc_legacy.bin b/tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_crc_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..3dedd8cbdf1737c486e485dc84661eadd28a88a3 GIT binary patch literal 2160 zcmeIzUr19?90%~OBNLs zkwg^qRuM$LRKo>9ONActsV$WLy-WzghiJCoT8gLdUH^z6^r;tj`J8ip+#dJ8OKi$d^-}W{mkyBdY)GQJMdQm%?OXql{Zqja=w&ML*hCLPk3#h@0ZQ zv7xBYp9>aW8zYt0$?E;-$xm#089P{GYs^OJpq;c&No%UZ+g77i*`}D?v)9NP7r{d2 z%)B9PzOTf~V!5=Kh#QyH$zG6?lG3_hVSZ|K(lbx9Zzw8d^M{d7&W~fn zi|3qO_bdC>AK7NgcM#`f?CYv{7nPkkNi7>)!hIIUm6(}wQamE`2@$mO2x(JE;!2!o z$v`@0q1=1MzFiSUBT+|DPQs{jMAW8^+Djrv)jitEM5|Vq2x^u@yT%jU+VVt1%S~)Z zm4&yh5xQTvvm;3r`f;)T*Z7KX{Fw1X@a)K_d`GNCWmjY<8qxBTVohl}FYo09RZFFk zs*%u}grsuxjlz<0;J0#$?{}sNBa5{DYV!!^wL>-95bRaNt15$pgV)*H%YFCYQT-W67q?rDf%TeXYUJ{@-!M k_R6a2nw=#zZr)J03-ySwg7LgB^#Au9gg!es`~L6i8^lxAO8@`> literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_crc_tz_legacy.bin b/tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_crc_tz_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..4a923ab1e6fc6e06d3b8823777d9e52868ba455a GIT binary patch literal 3300 zcmeIzZ%7ki90%~6=$Mg0j@K{-<1hhVfR&J9 z1sf~{6)M0DwNMXUXoU{&LpL0Rqi_Pw!bP|W*WotYhlel>Bk=P&^668voS#q5+01#3 z^~YcwCg2M&)X@qySPCjsfE#L|9=y;BvstS!w@|Nty#wdHa;QyiGY!R*2FZ8M!$Mxu z)C;5iHhQ2r5L%`nD%r!Z>$T&FVMaSw&a#9lCNO}BHQzZFrmuY`+}xAq+I+k;>r1O zlz3&{(JjBCZ~PH$rcwuSPR4az7Vl!RvnZwIMi+3Ol?f$orlK?t3w>M!>^w}`WQw?w zAeuLjiCd`nu5sP2Fr$%}qoOEjie&I#W{ea-23v z)sm`_&^v^ra`f%Ol6K%(ImM4V(}WR4S~NA7ZRsD(?(EMz>YP-jB5|RAo3?JYdS#E$ zuZZdQME0b#Z4oH21a9IxKL9E`fm3kkfyHC-GM^kenU&5Idm>r+%&rG!4>c1>A1IAq zi18nB88ME&b=(u|>FIUy!@<78ynDZYPteKzfxw~lj<8HFcjfA;HEXMDYW=&q0>RzC z$MqW<8aFjWx5q%3PM8Srd!T3tdv;2WFk=! zMMObw6+zTXHJlK%ROlg}+9K)S%Y;CDh^7m!rFi<@^^XWbpL&tY=bZB!=l473o?Y%G zGr1Zqq%=vSG=m>nAqZ`7R3b7FQE$nb$#%qt0U;eh-wBOBBv9imjKKs>!3%ZJ2!3dXV{j7Az`=9>ArqwncG9L2)?}xry;`lZO*4DN0V8Wn1PYil z{f@Z#p%N>LX46t4Zd_I;dr3}8NQHrd+~jigCfUAHN~^d<&nud|LlG&HKa6~Gei|iS zJn!tBLR>s3xc6?*9 zEVMme=sw}jj3ia)r^JR|)8-BNvT}b*Yan>=cU-w+ jXVtFiJtfs{USGQx^@y;7@w_kg|Mwh(J~ucw{`d6_x7gLM literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_encrypted_ks_legacy.bin b/tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_encrypted_ks_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..741e80c3d943a7631f788cc8dcb29ff6eebfb683 GIT binary patch literal 5228 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 literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_signed_ks_legacy.bin b/tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_signed_ks_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..5887753bfc1828995739b924bbf1030ec630b46b GIT binary patch literal 5156 zcmeI#dsGuw8UXM+NeD?G5JXxZh!YA+c&IZxL{Je#Aiq(;zC7K47)xSUp#9-km(&)ZTIXsd-SjN4<_d~bHAMX z-I;v%&LjaefW^`QXAJ{z5+E^@3`wC>$YCk~ND~0r3;@z#oB;v2Wx=By8qNNL#rK7` zDm1By*D;S&_9sQ3-)g2Yhbh~XwtcfR6=dJZZL$KB6_~8RWCecx3dF)^Hc1bk`8S{* zs23W5Mj^x$aF~!4G#%nX&JYgyKmm{diiBbzF_Z?agFc3o&}L{m^k?W0^cD0qR1Y;n zEzs-hc$=gC9s9q{nD5f(20VWc)C&zjqYwgX$b_t*=@1ukhH%IS3V;MqB=j!ln(RAS zum86ON(C%g6f26-q~QcI692?UWCF@qJGme4$POvg7L)Io38<0%2!bSTf>GqKKNm^d z$Oke&LD~_NX%L}=@g`9X_nia_?mGy8QqC4?n>2*6jX=)M)&eeYU~y~51_qI_DM)&- zmFQ*EFR=x@K|RwT=cfg8msyP0?YwG$rsZNxdz;T zp5Y*|V;d~F2Z7ad(9iQf1TOop5j1HSFY}vWK5*;n0I2(0XNSMZKKx(VG{ANQC<@5y z+C|*eu-vG6-pi;AeoptZ94!q{4XA=J_7W0XQ~`J+dVq4y0$_2fUrPrjC&=r@6bNY1 zI66~xa&=hFp9B1@(kVw9Nh3x`-GNp&q7K^E<0SBoRghF8~`WAqOhVCwmO=r3k^NWpa1TixKn6d;n!~$-+w~;kH9(Qyz`&79MF|_h3Djm+phY=ibBir zt+!jtX19FnJ19AQ*AB^fSTQp_<9UyC(F*_mq{iBAp+x-1ptwv6E<_Y-Qv+hxqHcR9E7jPXT%C3zKuRpVvA=RzsjBNL9Q{L?; zm~Yzd|8!-ib@_~2Wq-dc6Fq$9idtIL^jAz6BYPwL`nz8w`P$6g+BK&l;0mR5m(_;C zA`6_Y?#GmS z4=KxzD!2TGrc(9uok89Ov#e6xm8 zN|)4&t*O(lST0FiCVb4zPP{#u6S`%1sgJwZ>WAQ)7gxo%o-6&GF)aGz?>9E3RA1H0 z^G3H!oubWW1f24`b>gUc>dyHUEgZpsnVIg;ndnj0jonV~9XD1DRB0~9e`UJZKFVUp z;|*;=J65t9Zq~OnuTn*N7^1g^6{&kp7S{5@xiMjvhU)+5dS$Tpa96Zw8+^_PjyXJ9 z?0P$@s&D3<@5FP29PzkyKG9yYGU!uXQDLRi*9Xq8+vM%y8*9QJ?DRo5pEzp$ku>#c z7k6>y8m9^G&7ucK7M{`TbN)zn4+uROTH=GC+v(-=<_etwEh$#`N!7<} z+`i6?W~yF)^C99KBr7S17TrI#I!hWhPZYL;^}@d_Gax@oR)#pkt?>yX1mLoc*%(<$3qGu=Ud3=TSw2JAMLOo+;e` literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_signed_no_ks_legacy.bin b/tests/elftosb/data/workspace/output_images/rt5xx/mb_ram_signed_no_ks_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..f7926bdf30dca83cf8324f8da640f6e4e6f61221 GIT binary patch literal 3732 zcmeI!dsGuw8UXM+Nq{805)mJ~oLJEC(g`2}qJR%fP@Vw=EkYpSAwm$T&2o-PkY!O( zRD5l9wY~sZd^8wPDgs(us1y>F3it{w)}`9YLmLpw>^H93?%8wp=wI7EoH@Ul`{ms4 z-kE#v%mgtI5o3rru}s8?0}_x3WFQ&rF+&LRM<|ntP%6Y}0Kt|HXE~4zdiIL)tPXNs zO6f<2t9DM52R#ll9hz~9F7ddN^WA|Ms6)|0A6=ja^nn2|0x%=QVF50f40ym12!J;T z03r|$Vt@psg0hUEr_aC^!Ya02e_cXaR4o<6X|$_pJXeZN5*PF6h4p^nn2| z0x%du7T|)(fCn6b0C?WK06I&8WnW^(i@v|lM@uc8YiOD1NIi&O3atZ11y2fP^e^sJdE+eg=WkS)Q zQt(Z+r0wi}Bct1WXc2QZlgCG-K89rSa5zVihB^nBD;M(Lcg82FEX1$S)(?di*AK-N z_g{(~=8Wd5N&U+)RuD_X7LxjXxeJ~xTbGTerc07#F1SQ0 z&6?|=U?W1{=I$}ebGDbaBq32MOZqJin7?4*qQE5{fdV`tG#J`L2zsE_`-k)YcN|Fl zSpC?e|K1a2S=ks&6Hd+5>n+<9{)0eS%0MU{5P6IkB99*MIh}^lXiVR$2-Vi!?hA{&Fu7)oCw! zWWK@LdqEA+ZT8=W2gT-XYO!SXSPVVEBzr| zX*qwDzZcITY{!k!XP;ciV9GQfbB1^Mv@3f%ay*T0`TxAE)1uhw?v8&nr$qhui!*M{ zt?Vxu(}(wk&+EVcYl4sEjP2d7B>~rIMSHm$ayOeXW{LMsnew#$bnz;cy{k|?`nZ{q|%~Pl2rkgGM02 zm@1kx7<3x#+3zPBo*0;D4Ar#AjuWLhsk8QX<`yY_dU<>PHuuBJ%C_%W-C>{q<%*_p z51TFW92I-{l3&Ii3|TF7npbd$In~!KQ;?IkDej-A%Wu?%9jvf!X&Z1#QQfLsk@9Os zYs=-1``;ul7Ee02ozYXWBlkyT4KH6&HEYUSA@+s2PmuV)6F_qcoZMA`T~o+T|D z(SWhB=IDjU5q8)9=@ZWA%LmF;&8tos&7B%yw)@G3_C>pwv1{*KY-wDX7w)c$+#a&I ztfwmX96yv79dd2x;$NJv5B44Fj*Qv~?-|i)`^R&g??se9wSMrm#8u3ZytG(P-l|-- zXrpFxZt3(dj$B!rKikPC#y~jO>5XqWd&1&VS#oPPZ|<7d>0`6EL_In_=Sp_ulU0@% zlUQD5=LHqB&P;9p;m?^R(?hvZjeWIb`oqZ=iTz9bL>1V)zZZO)I5-E@%NLz2sD4hr zA1ymSIVV|1tC8OxrGFB^9{Wh#{-~(unuRA)6d$M|@#J{&LG2`BR5~1Dc-W7vCjCLYmfJS*H~?R-FcIH`9_~- z!Lj?VDjlQ!8WdZn+1-3lKs30DI}&Vej4p4Qvhp^!Iq8b;g|5>|VcOmBz*ClAli{|Z K-dw}B>;D3@n=iHi literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/rt5xx/mb_xip_crc_hwk_legacy.bin b/tests/elftosb/data/workspace/output_images/rt5xx/mb_xip_crc_hwk_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..6ff199c7afd8b301800501c478fea553d92f6a2b GIT binary patch literal 2160 zcmeIzUr19?90%~<(QV${vhMl=8Pm=M2Ui-jnWYAiX6aUhe?Uk`+H$K|ihqgKOBxat zQA8BuC7+ZM}Y8YN8bTWKO|7&HH^aqOu{s<98#=c zgFH~72;5K(mEeVXXaYa9LKhr^emDac;VKNmEw~2{U=*Ih&wb3NPb_f$d}=Oa&TFhc z4ihj5)4*_!RGt;M;E=k>2=;G90Xu1*6V_yhr?pz$VVh$1ihV}bxCj(5 zXZj6s(_JN27B$mSB5qt(Cwop#N=Su)g52bC^%~i}Qc9~Bq$d^4-jRru$sa{NIX{gN zFP?XH?XT?Xe`K2|*Fl_V*_YA*>JRrhME<1Jc2JfK=F7E zG54Ox9+&D?fdXsb2L9*og9?vf0J?`P9*dXx<>N?EmjM2z_pN?)~4_Hvp>EkN^Mx literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/rt5xx/mb_xip_crc_hwk_tz_legacy.bin b/tests/elftosb/data/workspace/output_images/rt5xx/mb_xip_crc_hwk_tz_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..08f854da41e8ae0a622955bd93e0847611c1a72d GIT binary patch literal 3300 zcmeIzO=uHA6bJA(X_9VMs~bN+C9ShyV2c5hT5T(ctsiZoNNu5D!D3<)O|_-gibO9) z5m6CEM8TViAm*Y5QxJ@;;6YC&V)5&ef*?JJsS&o?((%8sRz#5Wp|ZO?bFi8@fJp46vp5iuu@WN zV27okLM3>h4jRA*Ezk}D=z=~t3Mb$!T!L$G1Ma|mcnDA6IsCegLi+S9=NFQ5Hgn!$ z{Sg?2G57`yb+myUmVyeE;DI`503Wo#Y}P8yE!OK_@4$JV9B!3cO+#^IljJ|=WnrIb z;+4@(4j*iaCQch?=J-llvWzkRDU4da)~n2a+)H6Hu~&@SGHqPw??nvl8zG||7Q{`- zXksWX^cR99mnTT26|#D7V(bf>Sitr-+S~G@Y|ug4m$WU@>+Nb(H`~XVqkfkWH6nr) z%$0jb+F#D}=-DoJbk(FNRRRZ>ZqsU*WALLU`D2ak|8o+hp& ziRL}XCah$+Yh1TG!svP2Sy_@Y^7M(O%n?U*#K^i+Tb%0DDpEnsn(Eegs$W}}ifES9 zs!VNoL%GoX!jnIeRG}XeD`$=`3+MEV$AagNjLf&iGGul~hT;*eEG^1wvZbZ3M`?>x zBdHn*ybuO&C$6#nOYh=8J>5-50ZuI>wcWXhP_dQ?{)(pX?R- zRWbFE$dQtEECL0Vz%6{|AAkx^;S?MiuzIaN=9j}KbJ7`$H=2{r>=`h7sfkGXL>c@- zjQ@z+h;jC>gNN2TBQm)?RjaGltgWf73+(9(hW7p* zH*DOrc}wHA>P8Q5ZQhQ2L|DOi-;4eKt%J~~2B$v$z2~1%u_;!dSb<^%3ao%AV2j1_ Q$)cD#7&B4S7*C(0A86hKng9R* literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/rt5xx/mb_xip_crc_legacy.bin b/tests/elftosb/data/workspace/output_images/rt5xx/mb_xip_crc_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..70e0b5a3bae6eca51b2ecfbec6cd1dae67a9f16c GIT binary patch literal 2160 zcmeIzUr19?90%~<(QV${vhMl=71Pdz23H!inWYBN%zwK@n1YaywB=T_3^OBEFIhxX zL=sWdTSXB0VhtArEfspmr?yD?_c9>}AEMcUYZ;!tcl{%R(5GJ9<#W#Yjr04RbI&fj zWFb$Bjg(f2lwA;nHVDIBI4BWWeMDFFdHwB(_XFW`0DUL4{E$M8S1<F z2P_8_O27-1Pz5?PLJI_;9lGEsoPe`%5w5`vxC8g#0Sv9&bZCUxtTi`x?!5l>44l{HNR!-@HxyH2X6LOOFF=bD8`rGT&8pkAa`gWRGVO(Opmp_bra(@^l zUOMCKhF{q?|H!sbp^G>tb6=Om+nDUmPieX60`9Xkp~NkepXO0vjEj(yM@gGZ5mypK z3kEWA8(I6!eS4yeMq;j#{G?f@OVp>2ILo4D)g9W>WSdr;3~9DxhsKlL+TvtXvnJQ1 zD=! z^gAMFQrf-*6j%ng@IOBQDm;c$aOl3xZ_`;oj-1R&XRQ97tbAtoeT$#!iKO?G#w)~p zk9f=+SN8_)4|jBQy7}R7_hH_CAhvUgV5Owk zzz&N+g$nRME!2Y#nxPE>&SzNyECv-Szyq~V4?bvynXFZuTddc=-hm4~Iou+*m zs#BT&gqOl(V#AEvGOb+bZ$u33OCh5j5yVZ& zXksuf^k;%4mnBH0<+9p0IsTDN&SU!;?5+7xHs~PjW7?MK_I5U?o9q+JQMc2G8Wq8E z=E}V$Zthc(H3vuMQLeyu)HiYSG`X5uawp5ZqU=ZcHdxJ%Ev!OJh?uM z5U(say6IQ+%|D{eROTek$+)g7;%!`Zm87-&=pyd3GN~lYRFdHlp^u56gGWf4NE26* zM04(E6IQa^HLlwoVKfwXR+OZSJcmSM=BT48Vr1Q+ElhQ2<*A@%O?7EJ)uYW%MKnul zWu_*)eyPy?!jnIeRG}XiD}Ehc7S8Dzj|9&j8JTa3CCKcK48|i`Sz0Wu&X$(G7^BTn zwWMk!^fn=>9DS>>W}J9dF7e~eG+{)M7E2H0ntBIvyLz(^+b5LCXhP`UrfgemKG`eu zt77UMks~E-UjPa$f?N2`-vsC?KR`=-Z%vRsuCXz%Y!$7)%1oA;k(d z$O9D$zzwBP4qm8%dhkOFw8Kd_0~g^M+=4ss0G_}zcnSURYajFJ6APR_pPCDq^8xD* z!zhfwBru$#6>N|PDinYlN}(LQPy-8DYjN)4dHw4dxa^ffwQ_A%Uqsm@`7U`_$eT4a zV6@YN$E&)d=Z#;Mc(ZF#j4|IiL^a>aROUPFp%7)U0miM#Ixh4f@eu8MA)_4>#Is`E z(Y}b#-v}04A0?GG$m-*%v2ixFf*q-})n%eoz)sqD!kTRJv{b4Uwh3l0J7{DLi$Fee zrauxlJyv2RQ8O(i;>KllvbW@it z7tTAodM^9!AK50#br9!d?CXa36p@|T2`v*{!F?9Sl&FcalRPZ+Q4z57FliGB;!2EY z+0#_iLgq)tzFlEP{Sik&cHF4bE~=6z>_uUt>H%$ayh+QC2Q*8(RpaqaZDl;Hnd6(1 zC7~VbgzgjW%t%s&eoAbd8($ZW*%>bc&y0-9_r+RNc7^*QVJ$Zy))l96a^8*7ZmC#O zH4=KgkW`MoPFRu-{8vu#^UgG3WRdnT(VMQm+M7OfHTAqZ;uB(g zM_fjZqjMYg1Y29%oV+X8*~MFq`J01I?hgbywmZTyx!i@Di?(bnE-CdlHU)x*f5+uJ kcUA1J+*?%X=C#%PP>%>J7|;7+|9{Ux=rg@DBY$5%0E!saf&c&j literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/rt5xx/mb_xip_signed_legacy.bin b/tests/elftosb/data/workspace/output_images/rt5xx/mb_xip_signed_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..bc1829c63373122aa5675383708f03f90f34d8b1 GIT binary patch literal 3700 zcmeIzdsGuw8UXM+Nq{805|R2snYf_gr85X9C>m5SfeP}76|F)b5s*g^SvIw`kcfcG zON!QAb+sZ3iZ2=kg)IVFx1ug2u2e+D(DkuYtGqNI%Ir6;+V0tN_UK>RKb$$gx%17r z-@P;U-kCuxL`0e*-fA}DMFAOz1qu)c(yb6;i4aO!2I- zfIct`Ff+vC06v%i1i%T9z#I4h2?zy|Kn4=PdhiKQgRP(h{1NO2hrrjM4m5)n@cKU9 zrqsV<{kLiJUFuwi{<}dB=mWz5gK^{lK9~RmzzLAR8~6bU2nFxb*ZaEf$LoKsfdUCP zK8zd2Xwvc)v7>)4#^NQ6k*oCad3)-TtePD9in)Xt+KXXWbT&kxgCYSIohe4~h=E}!w!IdBElBYpkY$QW#00AKnt>Xq9e zVZWYZP>K@*1dI7Yn4RcjI;)$KPr+OVIuXvQ<~7WzF+mmx;q=aN#jYS%q-Q(Gr&t=S z1z#clGt?^-?MD|xt0hfZ_RIWc$VaY^+Y#>m*4ZI%vdjJ>n}sYL5soAJzIIS|v|LxF zUidO9g6DK^;MK4Y(}1feV-F>_!&OK)q(?Zf0U@iCy)~xD^3n=48%H_>@ zDdqU8#u*BKc}BNfNlk>7300F;CA8F2lV|sunO*Ba{_NRofe=x~NQy1Mp_Edl21l4H zSL)wSCZy;rB&^am4uljp4n!6AezSUrHLdN!J@hGngMp6i8yP3n-^2d5~C#uv427a_=V8_5rWQf+~9+Y z6-h}-S3F&@AstUhmc=PtahY76=If~9B7$_E<>Bcydycm(CRVOk`*ZBKaM5D_fFO?m z5)TVm3hf~TJ<#j@efj?#2g*3wIC}rT_gF<*ItJ5(({uG|OSeRbz9wfO6b%RsE0WMK z{k~!{Fb0D??;Jw)^j~m2W$&QVEF66 z%z_cVlC&Z$=xSUGlckHK;UAF}1ecD@Ets%sNFHHDM@$Q|6jkbsSY}6$kqO zPfUo3Nm4}7i>5Vi`F@nUb6ko2J~LT$@fO5L3oklHMdSizK`}Ne^=6I7@c|LgH2U zYr0hpHuaYSiJ2g#2D64S$G8j)hM_aHyE1GaGjdM1YnN39_fN1Js+@OsHKLs=pQ3w4&AFAM1`yY!31`!aH!I!f9@Sdn$H*QgiX% zVMEF`(R>&F(20(8iMS;BE>ms0aE)k=z;Rj8m65^Uo=#yabRYAEN_^VXciK04nOzk< zTHaw(Jn?4HcNgNNKR$KAt@%fKOGk~NJ)!e^yI#cj*v=^IbSw3{#3TIIKOI5>zRUS#^8t(A7(bKt-7pNrVr4;(O^_|l{IA%Q9cTjs_%^@@2sbN-MJlxdg z|HX1{{f)Yo=2e=|S%!$h;LVlYC$g%AA%Ydbe;ugHb-C2vQ`Q+F-3H$o$x(*~zAm@I z4nCfA`=ZP(oF{v3lR;fQvfTf3-R7){>0j?VyFPoivrnXnxWB_2-+H{<=68y?%bfz> zq}9_$XK$6>KQQ-fdc?yuwsmVcb1F}gRi4MDw%yy3S~@+1FV{KL%BJ6)P)F?a?ho zd0VI^jj3{~m$hBx_IBBcQZ@s#{fu39R(j54DCLP~ZkuIfj(zT8>c*$@)27vSB^eG4 zk)ln$WhGw@%}cJny~WJZ{UP6&mHWWK^OE+Or@e*~;TCw$J^jhW88gKvf_5pDO~HpV ZteSu5E7O!oW4gi;GxkRn=)L6He*t*DB9Z_A literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/trustzone/lpc55s1x/tzm_legacy.bin b/tests/elftosb/data/workspace/trustzone/lpc55s1x/tzm_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..fc23de795a9421588ba0a1f3f580aa607f35b617 GIT binary patch literal 412 ccmZQz7zKkM1RDPT|G$3~6GMX%6oA|W0BhI=c>n+a literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/trustzone/lpc55xx/tzm_legacy.bin b/tests/elftosb/data/workspace/trustzone/lpc55xx/tzm_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..68eea8f0155df5ecd2a471f49216e16473fc2179 GIT binary patch literal 464 ncmZQz7zKkd1peaytGK|fZ2(H|U&X}G0OU5TTE)Ns!XN+u*M=cC literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/trustzone/rt5xx/tzm_legacy.bin b/tests/elftosb/data/workspace/trustzone/rt5xx/tzm_legacy.bin new file mode 100644 index 0000000000000000000000000000000000000000..b035396ad5486e2c89c38688c4b4c5645222ed3d GIT binary patch literal 1140 pcmZQz7zLvtFd71*AwYZx{3imeS_L+*0jPBUDkg>o5FaYW001XREkOVP literal 0 HcmV?d00001 diff --git a/tests/elftosb/test_efltosb_sb21.py b/tests/elftosb/test_efltosb_sb21.py index e20bc103..17da0fc3 100644 --- a/tests/elftosb/test_efltosb_sb21.py +++ b/tests/elftosb/test_efltosb_sb21.py @@ -7,18 +7,51 @@ import os from binascii import unhexlify +from itertools import zip_longest +import pytest from click.testing import CliRunner import spsdk.apps.elftosb as elftosb +from spsdk import SPSDKError from spsdk.sbfile.images import BootImageV21 from spsdk.utils.misc import use_working_directory -def test_elftosb_sb21(data_dir, tmpdir): +@pytest.mark.parametrize( + "bd_file,legacy_sb,external", + [ + ( + "sb_sources/BD_files/real_example1.bd", + "sb_sources/SB_files/legacy_real_example1.sb", + [], + ), + ( + "sb_sources/BD_files/real_example2.bd", + "sb_sources/SB_files/legacy_real_example2.sb", + [ + "sb_sources/output_images/tmdData.bin", + "sb_sources/output_images/bootloaderImage.bin", + "sb_sources/output_images/tmdImage.bin", + "sb_sources/output_images/audioImage.bin", + ], + ), + ( + "sb_sources/BD_files/simpleExample_no_sha.bd", + "sb_sources/SB_files/legacy_elftosb_no_sha.bin", + [], + ), + ( + "sb_sources/BD_files/simpleExample_sha.bd", + "sb_sources/SB_files/legacy_elftosb_sha.bin", + [], + ), + ], +) +def test_elftosb_sb21(bd_file, legacy_sb, external, data_dir, tmpdir): runner = CliRunner() with use_working_directory(data_dir): - bd_file_path = os.path.join(data_dir, "sb_sources/BD_files/simpleExample.bd") + bd_file_path = os.path.join(data_dir, bd_file) out_file_path_new = os.path.join(tmpdir, "new_elf2sb.bin") kek_key_path = os.path.join(data_dir, "sb_sources/keys/SBkek_PUF.txt") priv_key_path = os.path.join(data_dir, "sb_sources/keys_and_certs/k0_cert0_2048.pem") @@ -39,7 +72,7 @@ def test_elftosb_sb21(data_dir, tmpdir): ) hash_of_hashes_output_path = os.path.join(tmpdir, "hash.bin") - out_file_path_legacy = os.path.join(data_dir, "sb_sources/SB_files/legacy_elf2sb.bin") + out_file_path_legacy = os.path.join(data_dir, legacy_sb) cmd = f"-c {bd_file_path} \ -o {out_file_path_new}\ @@ -51,6 +84,8 @@ def test_elftosb_sb21(data_dir, tmpdir): -R {root_key_certificate2_path}\ -R {root_key_certificate3_path}\ -h {hash_of_hashes_output_path}" + for entry in external: + cmd += " " + entry result = runner.invoke(elftosb.main, cmd.split()) assert result.exit_code == 0 assert os.path.isfile(out_file_path_new) @@ -98,4 +133,10 @@ def test_elftosb_sb21(data_dir, tmpdir): # -1 for indexing starting from 0, -1 for previously removed line => -2 del sb_old_lines[TIMESTAMP_LINE - 2] - assert sb_new_lines == sb_old_lines + for i in zip_longest(sb_new_lines, sb_old_lines, fillvalue=None): + assert i[0] == i[1] + + +def test_sb_21_invalid_parse(): + with pytest.raises(SPSDKError, match="kek cannot be empty"): + BootImageV21.parse(data=bytes(232), kek=None) diff --git a/tests/elftosb/test_elftosb_mbi.py b/tests/elftosb/test_elftosb_mbi.py index a52fbef7..22df2482 100644 --- a/tests/elftosb/test_elftosb_mbi.py +++ b/tests/elftosb/test_elftosb_mbi.py @@ -6,16 +6,29 @@ # SPDX-License-Identifier: BSD-3-Clause """Test Trustzone part of elftosb app.""" -import os import filecmp import json +import os +import unittest.mock as mock +import commentjson as json import pytest from click.testing import CliRunner +from Crypto.Cipher import AES +from spsdk import SPSDKError from spsdk.apps import elftosb +from spsdk.image import MasterBootImage, MasterBootImageN4Analog, MasterBootImageType +from spsdk.image.keystore import KeySourceType, KeyStore +from spsdk.utils.crypto.backend_internal import ECC, RSA, internal_backend from spsdk.utils.misc import use_working_directory -from spsdk.image import MasterBootImageN4Analog, MasterBootImageType + +devices = ( + "lpc55xx", + "lpc55s0x", + "lpc55s1x", + "lpc55s3x", +) def process_config_file(config_path: str, destination: str): @@ -33,44 +46,303 @@ def process_config_file(config_path: str, destination: str): return ref_binary, new_binary, new_config +def get_signing_key(config_file) -> ECC.EccKey: + with open(config_file) as f: + config_data = json.load(f) + private_key_file = ( + config_data["signingCertificatePrivateKeyFile"] + if config_data["useIsk"] + else config_data["mainRootCertPrivateKeyFile"] + ) + with open(private_key_file.replace("\\", "/"), "rb") as f: + siging_key = ECC.import_key(f.read()) + return siging_key + + @pytest.mark.parametrize( - "config_file", ["mb_ram_crc.json", "mb_ram_crc_version.json", "mb_xip_crc.json"] + "config_file,device,message", + [ + ( + "mb_ram_crc.json", + "lpc55xx", + ( + f'Unsupported value "RAM" of ' + f'"outputImageExecutionTarget" for selected family "lpc55xx".' + f'Expected values for "lpc55xx" ["XIP"].' + ), + ), + ("mb_xip_crc.json", "lpc55xx", ""), + ("mb_xip_crc_tz.json", "lpc55xx", ""), + ("mb_xip_crc_tz_no_preset.json", "lpc55xx", ""), + ( + "mb_xip_crc_hwk.json", + "lpc55xx", + ( + f'Unsupported value "True" of ' + f'"enableHwUserModeKeys" for selected family "lpc55xx".' + f'Expected values for "lpc55xx" ["False"].' + ), + ), + ( + "mb_xip_crc_hwk_tz.json", + "lpc55xx", + ( + f'Unsupported value "True" of ' + f'"enableHwUserModeKeys" for selected family "lpc55xx".' + f'Expected values for "lpc55xx" ["False"].' + ), + ), + ( + "mb_ram_crc.json", + "lpc55s1x", + ( + f'Unsupported value "RAM" of ' + f'"outputImageExecutionTarget" for selected family "lpc55s1x".' + f'Expected values for "lpc55s1x" ["XIP"].' + ), + ), + ("mb_ram_crc.json", "rt5xx", ""), + ("mb_ram_crc_tz.json", "rt5xx", ""), + ("mb_ram_crc_tz_no_preset.json", "rt5xx", ""), + ("mb_ram_crc_hwk.json", "rt5xx", ""), + ("mb_ram_crc_hwk_tz.json", "rt5xx", ""), + ("mb_xip_crc.json", "rt5xx", ""), + ("mb_xip_crc_tz.json", "rt5xx", ""), + ("mb_xip_crc_tz_no_preset.json", "rt5xx", ""), + ("mb_xip_crc_hwk.json", "rt5xx", ""), + ("mb_xip_crc_hwk_tz.json", "rt5xx", ""), + ("mb_xip_crc_tz.json", "lpc55s1x", ""), + ("mb_xip_crc_tz_no_preset.json", "lpc55s1x", ""), + ("mb_xip_crc_hwk.json", "lpc55s1x", ""), + ("mb_xip_crc_hwk_tz.json", "lpc55s1x", ""), + ("mb_ram_crc.json", "lpc55s3x", ""), + ("mb_ram_crc_version.json", "lpc55s3x", ""), + ("mb_xip_crc.json", "lpc55s3x", ""), + ], ) -def test_elftosb_mbi_basic(data_dir, tmpdir, config_file): +def test_elftosb_mbi_basic(data_dir, tmpdir, config_file, device, message): runner = CliRunner() with use_working_directory(data_dir): - config_file = f"{data_dir}/{config_file}" + config_file = f"{data_dir}/workspace/cfgs/{device}/{config_file}" + ref_binary, new_binary, new_config = process_config_file(config_file, tmpdir) + + cmd = f"--image-conf {new_config}" + result = runner.invoke(elftosb.main, cmd.split()) + if result.exit_code != 0: + assert isinstance(result.exception, SPSDKError) + assert result.exception.description.lower() == message.lower() + else: + assert os.path.isfile(new_binary) + assert filecmp.cmp(new_binary, ref_binary) + + +@pytest.mark.parametrize( + "config_file,device", + [ + ("mb_xip_256_none.json", "lpc55s3x"), + ("mb_xip_384_256.json", "lpc55s3x"), + ("mb_xip_384_384.json", "lpc55s3x"), + ], +) +def test_elftosb_mbi_signed(data_dir, tmpdir, config_file, device): + runner = CliRunner() + with use_working_directory(data_dir): + config_file = f"{data_dir}/workspace/cfgs/{device}/{config_file}" ref_binary, new_binary, new_config = process_config_file(config_file, tmpdir) cmd = f"--image-conf {new_config}" result = runner.invoke(elftosb.main, cmd.split()) - assert result.exit_code == 0 assert os.path.isfile(new_binary) - assert filecmp.cmp(new_binary, ref_binary) + # validate file lengths + with open(ref_binary, "rb") as f: + ref_data = f.read() + with open(new_binary, "rb") as f: + new_data = f.read() + assert len(ref_data) == len(new_data) + + # validate signatures + signing_key = get_signing_key(config_file=config_file) + signature_length = 2 * signing_key.pointQ.size_in_bytes() + assert internal_backend.ecc_verify( + signing_key, new_data[-signature_length:], new_data[:-signature_length] + ) + assert internal_backend.ecc_verify( + signing_key, ref_data[-signature_length:], ref_data[:-signature_length] + ) + # validate data before signature + assert ref_data[:-signature_length] == new_data[:-signature_length] + + +# skip_hmac_keystore +# 0 indicates no hmac and no keystore present in output image +# 1 indicates hmac present but no keystore +# 2 indicates both hmac/keystore present in output image @pytest.mark.parametrize( - "config_file", ["mb_xip_256_none.json", "mb_xip_384_256.json", "mb_xip_384_384.json"] + "config_file,device,skip_hmac_keystore", + [ + ("mb_xip_signed.json", "lpc55xx", 0), + ("mb_xip_signed.json", "lpc55s1x", 0), + ("mb_xip_signed_chain.json", "lpc55xx", 0), + ("mb_xip_signed_no_ks.json", "rt5xx", 0), + ("mb_ram_signed_no_ks.json", "rt5xx", 1), + ("mb_ram_signed_ks.json", "rt5xx", 2), + ], ) -def test_elftosb_mbi_signed(data_dir, tmpdir, config_file): +def test_elftosb_mbi_legacy_signed(data_dir, tmpdir, config_file, device, skip_hmac_keystore): runner = CliRunner() with use_working_directory(data_dir): - config_file = f"{data_dir}/{config_file}" + config_file = f"{data_dir}/workspace/cfgs/{device}/{config_file}" ref_binary, new_binary, new_config = process_config_file(config_file, tmpdir) cmd = f"--image-conf {new_config}" result = runner.invoke(elftosb.main, cmd.split()) assert os.path.isfile(new_binary) - # assert filecmp.cmp(new_binary, ref_binary) + # validate file lengths + with open(ref_binary, "rb") as f: + ref_data = f.read() + with open(new_binary, "rb") as f: + new_data = f.read() + assert len(ref_data) == len(new_data) + + # validate signatures + with open(new_config, "r") as f: + config_data = json.load(f) + + signing_certificate_file_path = config_data["mainCertPrivateKeyFile"] + with open(signing_certificate_file_path, "rb") as f: + cert = f.read() + signing_key = RSA.import_key(cert) + + modulus = signing_key.n + exponent = signing_key.e + signature_length = int(len(f"{modulus:x}") / 2) + hmac_start = 0 + hmac_end = 0 + # skip_hmac_keystore + # 0 no hmac/keystore + # 1 hmac present + # 2 hmac & keystore present + if skip_hmac_keystore: + hmac_start = MasterBootImage.HMAC_OFFSET + gap_len = MasterBootImage.HMAC_SIZE + gap_len += KeyStore.KEY_STORE_SIZE if skip_hmac_keystore == 2 else 0 + hmac_end = hmac_start + gap_len + + assert internal_backend.rsa_verify( + modulus, + exponent, + new_data[-signature_length:], + new_data[:hmac_start] + new_data[hmac_end:-signature_length], + ) + assert internal_backend.rsa_verify( + modulus, + exponent, + ref_data[-signature_length:], + ref_data[:hmac_start] + ref_data[hmac_end:-signature_length], + ) -def test_elftosb_mbi_lower(data_dir): + # validate data before signature + assert ref_data[:-signature_length] == new_data[:-signature_length] + + +@pytest.mark.parametrize( + "config_file,device,skip_hmac_keystore", + [("mb_ram_encrypted_ks.json", "rt5xx", 2)], +) +def test_elftosb_mbi_legacy_encrypted(data_dir, tmpdir, config_file, device, skip_hmac_keystore): + runner = CliRunner() + with use_working_directory(data_dir): + config_file = f"{data_dir}/workspace/cfgs/{device}/{config_file}" + ref_binary, new_binary, new_config = process_config_file(config_file, tmpdir) + + cmd = f"--image-conf {new_config}" + new_defaults = list(MasterBootImage.__init__.__defaults__) + # The last value in defaults corresponds to ctr_init_vector, which we need to mock + new_defaults[-1] = b"\xc3\xdf\x23\x16\xfd\x40\xb1\x55\x86\xcb\x5a\xe4\x94\x83\xae\xe2" + new_defaults = tuple(new_defaults) + with mock.patch.object(MasterBootImage.__init__, "__defaults__", new_defaults): + result = runner.invoke(elftosb.main, cmd.split()) + assert os.path.isfile(new_binary) + + # validate file lengths + with open(ref_binary, "rb") as f: + ref_data = f.read() + with open(new_binary, "rb") as f: + new_data = f.read() + assert len(ref_data) == len(new_data) + + # validate signatures + with open(new_config, "r") as f: + config_data = json.load(f) + + signing_certificate_file_path = config_data["mainCertPrivateKeyFile"] + with open(signing_certificate_file_path, "rb") as f: + cert = f.read() + signing_key = RSA.import_key(cert) + + modulus = signing_key.n + exponent = signing_key.e + signature_length = int(len(f"{modulus:x}") / 2) + hmac_start = 0 + hmac_end = 0 + # skip_hmac_keystore + # 0 no hmac/keystore + # 1 hmac present + # 2 hmac & keystore present + if skip_hmac_keystore: + hmac_start = MasterBootImage.HMAC_OFFSET + gap_len = MasterBootImage.HMAC_SIZE + gap_len += KeyStore.KEY_STORE_SIZE if skip_hmac_keystore == 2 else 0 + hmac_end = hmac_start + gap_len + + assert internal_backend.rsa_verify( + modulus, + exponent, + new_data[-signature_length:], + new_data[:hmac_start] + new_data[hmac_end:-signature_length], + ) + assert internal_backend.rsa_verify( + modulus, + exponent, + ref_data[-signature_length:], + ref_data[:hmac_start] + ref_data[hmac_end:-signature_length], + ) + + # validate data before signature + assert ref_data[:-signature_length] == new_data[:-signature_length] + + +def test_elftosb_mbi_lower(): mbi = MasterBootImageN4Analog( - app=bytes(100), load_addr=0, image_type=MasterBootImageType.PLAIN_IMAGE + app=bytes(100), load_addr=0, image_type=MasterBootImageType.PLAIN_IMAGE, firmware_version=0 ) assert mbi.data - mbi = MasterBootImageN4Analog(app=bytes(100), load_addr=0) + mbi = MasterBootImageN4Analog(app=bytes(100), load_addr=0, firmware_version=0) assert mbi.data assert mbi.info() assert mbi.export() + + +def test_mbi_n4a_invalid(): + mbi = MasterBootImageN4Analog( + app=bytes(100), + load_addr=0, + image_type=MasterBootImageType.SIGNED_XIP_IMAGE, + firmware_version=0, + ) + mbi.cert_block = None + with pytest.raises(SPSDKError, match="Certificate Block is not set!"): + mbi.data + mbi = MasterBootImageN4Analog( + app=bytes(100), + load_addr=0, + image_type=MasterBootImageType.SIGNED_XIP_IMAGE, + firmware_version=0, + ) + mbi.manifest = None + with pytest.raises(SPSDKError, match="MasterBootImageManifest is not set!"): + mbi.total_len diff --git a/tests/elftosb/test_elftosb_sb31.py b/tests/elftosb/test_elftosb_sb31.py index 5b72a80c..ad2b18ff 100644 --- a/tests/elftosb/test_elftosb_sb31.py +++ b/tests/elftosb/test_elftosb_sb31.py @@ -15,7 +15,6 @@ from click.testing import CliRunner from spsdk.apps import elftosb -from spsdk.apps.elftosb_utils import sb_31_helper from spsdk.utils.misc import use_working_directory @@ -37,23 +36,23 @@ def process_config_file( @pytest.mark.parametrize( - "config_file", + "config_file,device", [ - "sb3_256_256.json", - "sb3_256_none.json", - "sb3_256_none_ernad.json", - "sb3_384_256.json", - "sb3_384_256_fixed_timestamp.json", - "sb3_384_256_unencrypted.json", - "sb3_384_384.json", - "sb3_384_none.json", - "sb3_test_384_384_unencrypted.json", + ("sb3_256_256.json", "lpc55s3x"), + ("sb3_256_none.json", "lpc55s3x"), + ("sb3_256_none_ernad.json", "lpc55s3x"), + ("sb3_384_256.json", "lpc55s3x"), + ("sb3_384_256_fixed_timestamp.json", "lpc55s3x"), + ("sb3_384_256_unencrypted.json", "lpc55s3x"), + ("sb3_384_384.json", "lpc55s3x"), + ("sb3_384_none.json", "lpc55s3x"), + ("sb3_test_384_384_unencrypted.json", "lpc55s3x"), ], ) -def test_elftosb_sb31(data_dir, tmpdir, config_file): +def test_elftosb_sb31(data_dir, tmpdir, config_file, device): runner = CliRunner() with use_working_directory(data_dir): - config_file = f"{data_dir}/{config_file}" + config_file = f"{data_dir}/workspace/cfgs/{device}/{config_file}" ref_binary, new_binary, new_config = process_config_file( config_file, tmpdir, "containerOutputFile" ) @@ -67,9 +66,10 @@ def test_elftosb_sb31(data_dir, tmpdir, config_file): def test_elftosb_sb31_notime(data_dir, tmpdir): config_file = "sb3_256_256.json" + device = "lpc55s3x" runner = CliRunner() with use_working_directory(data_dir): - config_file = f"{data_dir}/{config_file}" + config_file = f"{data_dir}/workspace/cfgs/{device}/{config_file}" ref_binary, new_binary, new_config = process_config_file( config_file, tmpdir, "containerOutputFile" ) diff --git a/tests/elftosb/test_elftosb_trustzone.py b/tests/elftosb/test_elftosb_trustzone.py index d8d1f4d2..aa4683cc 100644 --- a/tests/elftosb/test_elftosb_trustzone.py +++ b/tests/elftosb/test_elftosb_trustzone.py @@ -6,8 +6,8 @@ # SPDX-License-Identifier: BSD-3-Clause """Test Trustzone part of elftosb app.""" -import os import filecmp +import os from click.testing import CliRunner diff --git a/tests/image/commands/test_auth_key_cmd.py b/tests/image/commands/test_auth_key_cmd.py index 9ef55c7c..1779bbbc 100644 --- a/tests/image/commands/test_auth_key_cmd.py +++ b/tests/image/commands/test_auth_key_cmd.py @@ -8,7 +8,7 @@ import pytest -from spsdk.image import CmdNop, CmdAuthData, EnumEngine, EnumAuthDat +from spsdk.image import CmdAuthData, CmdNop, EnumAuthDat, EnumEngine def test_auth_data_cmd_basic(): diff --git a/tests/image/commands/test_base_cmd.py b/tests/image/commands/test_base_cmd.py index 7f3865ed..d93a27ad 100644 --- a/tests/image/commands/test_base_cmd.py +++ b/tests/image/commands/test_base_cmd.py @@ -8,13 +8,24 @@ import pytest +from spsdk.exceptions import SPSDKError +from spsdk.image.commands import CmdBase, CmdTag -def test_base_command(): - from spsdk.image.commands import CmdBase, CmdTag +def test_base_command(): base = CmdBase(CmdTag.NOP, 0) assert base.info() assert base.export() assert len(base.export()) == base.size with pytest.raises(NotImplementedError): base.parse(b"") + + +def test_no_supported(): + base = CmdBase(CmdTag.NOP, 0) + with pytest.raises(SPSDKError): + base.cmd_data_reference = 0 + with pytest.raises(SPSDKError): + base.cmd_data_offset = 0 + with pytest.raises(SPSDKError): + base.parse_cmd_data(data=None, offset=None) diff --git a/tests/image/commands/test_check_data_cmd.py b/tests/image/commands/test_check_data_cmd.py index b6d4808f..4b3fb4ad 100644 --- a/tests/image/commands/test_check_data_cmd.py +++ b/tests/image/commands/test_check_data_cmd.py @@ -6,6 +6,9 @@ # # SPDX-License-Identifier: BSD-3-Clause +import pytest + +from spsdk import SPSDKError from spsdk.image import CmdCheckData, EnumCheckOps @@ -44,3 +47,15 @@ def test_checkdata_export_parse_without_count(): def test_checkdata_info(): cmd = CmdCheckData(count=1) assert "Count: " in cmd.info() + + +def test_checkdata_invalid(): + cmd = CmdCheckData() + with pytest.raises(SPSDKError): + cmd.num_bytes = 6 + with pytest.raises(SPSDKError): + cmd.ops = 99 + with pytest.raises(SPSDKError): + cmd = CmdCheckData(numbytes=8) + with pytest.raises(SPSDKError): + cmd = CmdCheckData(ops=80) diff --git a/tests/image/commands/test_init_cmd.py b/tests/image/commands/test_init_cmd.py index e538a493..a2b0f80f 100644 --- a/tests/image/commands/test_init_cmd.py +++ b/tests/image/commands/test_init_cmd.py @@ -8,7 +8,8 @@ import pytest -from spsdk.image import CmdNop, CmdInitialize, EnumEngine +from spsdk import SPSDKError +from spsdk.image import CmdInitialize, CmdNop, EnumEngine def test_init_cmd(): @@ -91,3 +92,15 @@ def test_init_cmd_equality(): assert cmd != nop assert cmd == cmd assert cmd != cmd_other + + +def test_init_cmd_invalid(): + cmd = CmdInitialize() + with pytest.raises(SPSDKError): + cmd.engine = 55 + with pytest.raises(SPSDKError): + CmdInitialize(engine=55) + with pytest.raises(SPSDKError): + cmd.append(value=0xFFFFFFFF) + with pytest.raises(SPSDKError): + cmd.pop(index=77) diff --git a/tests/image/commands/test_install_key.py b/tests/image/commands/test_install_key.py index fb3b29a3..b8108098 100644 --- a/tests/image/commands/test_install_key.py +++ b/tests/image/commands/test_install_key.py @@ -6,7 +6,7 @@ # # SPDX-License-Identifier: BSD-3-Clause -from spsdk.image import CmdNop, CmdInstallKey, EnumInsKey, EnumCertFormat, EnumAlgorithm +from spsdk.image import CmdInstallKey, CmdNop, EnumAlgorithm, EnumCertFormat, EnumInsKey def test_install_key_cmd_base(): diff --git a/tests/image/commands/test_set_cmd.py b/tests/image/commands/test_set_cmd.py index 9e1e5840..52ffe75e 100644 --- a/tests/image/commands/test_set_cmd.py +++ b/tests/image/commands/test_set_cmd.py @@ -6,6 +6,9 @@ # # SPDX-License-Identifier: BSD-3-Clause +import pytest + +from spsdk import SPSDKError from spsdk.image import CmdNop, CmdSet, EnumItm @@ -44,3 +47,15 @@ def test_set_cmd_export_parse(): data = cmd.export() assert len(data) == 8 assert cmd == CmdSet.parse(data) + + +def test_set_cmd_invalid(): + with pytest.raises(SPSDKError): + CmdSet(itm=8) + cmd = CmdSet() + with pytest.raises(SPSDKError): + cmd.hash_algorithm = 55 + with pytest.raises(SPSDKError): + cmd.engine = 0xFE + with pytest.raises(SPSDKError): + cmd.itm = 55 diff --git a/tests/image/commands/test_write_data_cmd.py b/tests/image/commands/test_write_data_cmd.py index 9904dcc5..5fd6a2f3 100644 --- a/tests/image/commands/test_write_data_cmd.py +++ b/tests/image/commands/test_write_data_cmd.py @@ -8,7 +8,8 @@ import pytest -from spsdk.image import CmdWriteData, CmdNop, EnumWriteOps +from spsdk import SPSDKError +from spsdk.image import CmdNop, CmdWriteData, EnumWriteOps @pytest.mark.parametrize( @@ -31,6 +32,27 @@ def test_write_value_cmd_basic(input_data): assert "CmdWriteData" in repr(cmd) +def test_invalid_cmd_write_data(): + cmd = CmdWriteData() + with pytest.raises(SPSDKError): + cmd.num_bytes = 16 + with pytest.raises(SPSDKError): + cmd.ops = 10 + with pytest.raises(SPSDKError): + cmd = CmdWriteData(numbytes=8) + with pytest.raises(SPSDKError): + cmd = CmdWriteData(ops=9) + cmd = CmdWriteData() + with pytest.raises(SPSDKError): + cmd.append(address=0xFFFFFFFFF, value=0) + with pytest.raises(SPSDKError): + cmd.append(address=0xFFFFFFFF, value=0xFFFFFFFFF) + cmd.append(5, 6) + cmd.append(7, 8) + with pytest.raises(SPSDKError): + cmd.pop(3) + + def test_write_value_cmd_get_set_iter(): cmd = CmdWriteData() cmd.append(9, 9) diff --git a/tests/image/header/test_header.py b/tests/image/header/test_header.py index 05f40e60..a7a21766 100644 --- a/tests/image/header/test_header.py +++ b/tests/image/header/test_header.py @@ -7,7 +7,8 @@ import pytest -from spsdk.image.header import Header, SegTag, Header2, CmdHeader +from spsdk import SPSDKError +from spsdk.image.header import CmdHeader, Header, Header2, SegTag def test_basic_header_without_length(): @@ -106,11 +107,11 @@ def test_comparision_header_without_length(): def test_parse_invalid_command(): # invalid required_tag: not CmdTag - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): CmdHeader.parse(b"", 0, required_tag=-1) # invalid zero length - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): CmdHeader.parse(b"\xFF\x00\x00\x00", 0, required_tag=None) # invalid tag: not CmdTag - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): CmdHeader.parse(b"\xFF\x04\x00\x00", 0, required_tag=None) diff --git a/tests/image/images/test_bootimage_rt10xx.py b/tests/image/images/test_bootimage_rt10xx.py index 39e32aa4..fd1e7b4d 100644 --- a/tests/image/images/test_bootimage_rt10xx.py +++ b/tests/image/images/test_bootimage_rt10xx.py @@ -5,8 +5,11 @@ # # SPDX-License-Identifier: BSD-3-Clause +from re import L + import pytest +from spsdk import SPSDKError from spsdk.image import BootImgRT from spsdk.utils.misc import DebugInfo @@ -26,7 +29,7 @@ def test_bootimage_rt10xx_basic(): def test_bootimage_rt10xx_missing_ivt(): # IVT header not found - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): BootImgRT.parse(b"00000000") @@ -47,8 +50,39 @@ def test_bootimage_rt10xx_add_encrypted_image(): assert len(img.dek_key) == 16 # test invalid dek key length img = BootImgRT(0x20000000) - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): img.add_image(test_app_data, address=0x20000000, dek_key=b"x") # test image already added - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): img.add_image(test_app_data, address=0x20000000, dek_key=b"0123456789123456") + + +def test_invalid_image(): + img = BootImgRT(address=0x60000000) + with pytest.raises(SPSDKError): + img.add_image(bytes([1]) * 1024, address=0x20000000, dek_key=b"") + with pytest.raises(SPSDKError): + img.add_image(bytes([0]) * 1024, address=0x20000000, dek_key=b"\x00\x00\x00") + + +def test_invalid_export(): + img = BootImgRT(address=0x60000000) + img.dek_key = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + with pytest.raises(SPSDKError): + img.export() + + +def test_invalid_parse(): + with pytest.raises(SPSDKError): + BootImgRT.parse(stream=5) + + +def test_bootimage_rt10xx_add_encrypted_image_invalid(): + img = BootImgRT(0x20000000) + test_app_data = bytes(1024) + with pytest.raises(SPSDKError, match="Invalid image type"): + img.add_image(test_app_data, address=0x20000000, img_type=7) + with pytest.raises( + SPSDKError, match="entry_addr not detected from image, must be specified explicitly" + ): + img.add_image(test_app_data, address=-1) diff --git a/tests/image/images/test_bootimg2_api.py b/tests/image/images/test_bootimg2_api.py index e72aba50..2609e444 100644 --- a/tests/image/images/test_bootimg2_api.py +++ b/tests/image/images/test_bootimg2_api.py @@ -7,7 +7,7 @@ # SPDX-License-Identifier: BSD-3-Clause import os -import pytest + from spsdk.image import BootImg2 diff --git a/tests/image/images/test_bootimg_api.py b/tests/image/images/test_bootimg_api.py index b5793aa0..bce417b0 100644 --- a/tests/image/images/test_bootimg_api.py +++ b/tests/image/images/test_bootimg_api.py @@ -10,8 +10,12 @@ import pytest +from spsdk import SPSDKError from spsdk.image import parse +from spsdk.image.commands import CmdWriteData, EnumWriteOps from spsdk.image.images import BootImgBase, BootImgRT +from spsdk.image.secret import SrkItem, SrkTable +from spsdk.image.segments import SegCSF, SegDCD @pytest.mark.skip @@ -42,3 +46,114 @@ def test_rt_image_dcd(data_dir): parsed_dcd = BootImgRT.parse(image_data).dcd.export() assert parsed_dcd == dcd_data + + +def test_rt_image_invalid(): + with pytest.raises(SPSDKError, match="Invalid IVT offset"): + BootImgRT(address=0x1, offset=16) + with pytest.raises(SPSDKError, match="Invalid version"): + BootImgRT(address=0x1, version=0x44) + with pytest.raises(SPSDKError, match="Plugin is not supported"): + BootImgRT(address=0x1, plugin=True) + bimg = BootImgRT(address=0x1) + with pytest.raises(SPSDKError, match="Invalid length of DEK key"): + bimg.dek_key = bytes(15) + with pytest.raises(SPSDKError, match="Invalid IVT offset"): + bimg.ivt_offset = 15 + bimg = BootImgRT(address=0x1) + bimg._dek_key = bytes(15) + bimg.hab_encrypted + csf = SegCSF(enabled=True) + csf.append_command(CmdWriteData(ops=EnumWriteOps.WRITE_VALUE, data=[(0x30340004, 0x4F400005)])) + with pytest.raises(SPSDKError, match="Nonce not present"): + bimg.csf = csf + bimg._nonce = bytes(15) + with pytest.raises(SPSDKError, match="Mac not present"): + bimg.csf = csf + + +def test_rt_image_invalid_add_dcd(): + bimg = BootImgRT(address=0x1) + dcd = SegDCD() + bimg._dcd = dcd + with pytest.raises(SPSDKError, match="DCD is already present"): + bimg.add_dcd_bin(data=bytes(10)) + bimg = BootImgRT(address=0x1) + dcd1 = SegDCD(enabled=True) + data = dcd1.export() + with pytest.raises(SPSDKError, match="DCD must be enabled to include DCD into export"): + bimg.add_dcd_bin(data=data) + + +def test_rt_image_invalid_add_csf(): + bimg = BootImgRT(address=0x1) + srk = SrkTable() + with pytest.raises(SPSDKError, match="Invalid length of srk table"): + bimg.add_csf_standard_auth( + version=1, + srk_table=srk, + src_key_index=0, + csf_cert=bytes(4), + csf_priv_key=bytes(4), + img_cert=bytes(4), + img_priv_key=bytes(4), + ) + item = SrkItem() + srk.append(item) + with pytest.raises(SPSDKError, match="Invalid index of selected SRK key"): + bimg.add_csf_standard_auth( + version=1, + srk_table=srk, + src_key_index=10, + csf_cert=bytes(4), + csf_priv_key=bytes(4), + img_cert=bytes(4), + img_priv_key=bytes(4), + ) + + +def test_rt_image_invalid_hab_encrypt_app_data(): + bimg = BootImgRT(address=0x1) + with pytest.raises(SPSDKError, match="Nonce is not present"): + bimg._hab_encrypt_app_data(app_data=bytes(16)) + bimg = BootImgRT(address=0x1) + bimg._nonce = bytes(16) + with pytest.raises(SPSDKError, match="Invalid length of application data"): + bimg._hab_encrypt_app_data(app_data=bytes(15)) + bimg = BootImgRT(address=0x1) + bimg._dek_key = None + bimg._nonce = bytes(16) + with pytest.raises(SPSDKError, match="DEK key is not present"): + bimg._hab_encrypt_app_data(app_data=bytes(16)) + + +def test_rt_image_invalid_decrypted_app_data(): + bimg = BootImgRT(address=0x1) + with pytest.raises(SPSDKError, match="Application not present"): + bimg.decrypted_app_data() + + +def test_rt_image_invalid_add_csf_encrypted(): + bimg = BootImgRT(address=0x1) + srk = SrkTable() + with pytest.raises(SPSDKError, match="Invalid length of srk table"): + bimg.add_csf_encrypted( + version=1, + srk_table=srk, + src_key_index=2, + csf_cert=bytes(16), + csf_priv_key=bytes(16), + img_cert=bytes(16), + img_priv_key=bytes(16), + ) + srk.append(SrkItem()) + with pytest.raises(SPSDKError, match="Invalid index of srk table"): + bimg.add_csf_encrypted( + version=1, + srk_table=srk, + src_key_index=10, + csf_cert=bytes(4), + csf_priv_key=bytes(4), + img_cert=bytes(4), + img_priv_key=bytes(4), + ) diff --git a/tests/image/images/test_hab_audit_log.py b/tests/image/images/test_hab_audit_log.py index b0bb13c3..94c57220 100644 --- a/tests/image/images/test_hab_audit_log.py +++ b/tests/image/images/test_hab_audit_log.py @@ -5,20 +5,24 @@ # # SPDX-License-Identifier: BSD-3-Clause +from unittest.mock import patch + +import pytest + +from spsdk import SPSDKError from spsdk.image.hab_audit_log import ( - hab_audit_xip_app, - parse_hab_log, CpuData, - get_hab_enum_descr, check_reserved_regions, + get_hab_enum_description, get_hab_log_info, + hab_audit_xip_app, + parse_hab_log, ) -from spsdk.utils.easy_enum import Enum +from spsdk.mboot import McuBoot # from spsdk.utils.serial_proxy import SerialProxy from spsdk.mboot.interfaces import Uart -from spsdk.mboot import McuBoot -from unittest.mock import patch +from spsdk.utils.easy_enum import Enum # responses from rt1020 for mcu emulating from spsdk.utils.serial_proxy import SimpleReadSerialProxy @@ -31,16 +35,16 @@ class TestEnum(Enum): def test_get_hab_enum_descr(): """Test `get_hab_enum_descr`""" # test known value - t = get_hab_enum_descr(TestEnum, 0xA5) + t = get_hab_enum_description(TestEnum, 0xA5) assert t == "Test descr (0xa5)" # test unknown value - t = get_hab_enum_descr(TestEnum, 0xFF) + t = get_hab_enum_description(TestEnum, 0xFF) assert t == "0xff = Unknown value" def test_parse_hab_log(): - """Test `parse_hab_log` function. """ + """Test `parse_hab_log` function.""" lines = parse_hab_log(0xF0, 0xCC, 0xAA, b"") assert lines lines = parse_hab_log(51, 240, 102, b"\xdb\x00\x08\x43\x33\x22\x0a\x00") @@ -49,10 +53,16 @@ def test_parse_hab_log(): assert lines +def test_parse_hab_log_invalid(): + """Test `parse_hab_log` function - raises exception""" + with pytest.raises(SPSDKError): + parse_hab_log(51, 240, 102, b"\xdb\x00\x01\x43\x33\x22\x0a\x00") + + def test_hab_audit_xip_app_simple(data_dir): - """Test `hab_audit_log` function. """ - import re + """Test `hab_audit_log` function.""" import os + import re captured_log = os.path.join(data_dir, "cpu_data", "rt1020", "hab_audit_log_data.txt") with open(captured_log) as f: @@ -74,8 +84,32 @@ def test_hab_audit_xip_app_simple(data_dir): assert log[:4] != b"\xFF" * 4 +def test_hab_audit_xip_app_invalid(data_dir): + with pytest.raises(SPSDKError, match="Flashloader is not running"): + hab_audit_xip_app(CpuData.MIMXRT1020, mboot=None, read_log_only=True) + import os + import re + + captured_log = os.path.join(data_dir, "cpu_data", "rt1020", "hab_audit_log_data.txt") + with open(captured_log) as f: + text_data = f.read() + bin_data = bytes() + matches = re.finditer(f"<(?P[ 0-9a-z]*)>", text_data, re.MULTILINE) + for match in matches: + found = match.group("data") + bin_data += bytes(int(value, 16) for value in found.split(" ")) + with patch( + "spsdk.mboot.interfaces.uart.Serial", SimpleReadSerialProxy.init_data_proxy(bin_data) + ): + with McuBoot(Uart(port="totally-legit-port")) as mboot: + with pytest.raises( + SPSDKError, match="Can not read the log, because given cpu data were not provided." + ): + hab_audit_xip_app(None, mboot, read_log_only=True) + + def test_get_hab_log_info(): - """Test `get_hab_log_info` function. """ + """Test `get_hab_log_info` function.""" # checks the situation when hab log is valid assert get_hab_log_info(b"\xAA\xBB\xCC\xDD") # checks the situation when hab log is empty @@ -85,7 +119,7 @@ def test_get_hab_log_info(): def test_check_reserved_regions(): - """Test `check_reserved_region` function. """ + """Test `check_reserved_region` function.""" # checks the situation when parameter with reserved regions is empty assert check_reserved_regions(0x20200000, None) # checks the situation when hab log address is not in conflict diff --git a/tests/image/mbi/test_mbi.py b/tests/image/mbi/test_mbi.py index 6d3736ae..79d4dded 100644 --- a/tests/image/mbi/test_mbi.py +++ b/tests/image/mbi/test_mbi.py @@ -11,13 +11,19 @@ import pytest -from spsdk.image import MasterBootImage, MasterBootImageType, MultipleImageTable, MultipleImageEntry -from spsdk.image import TrustZone +from spsdk import SPSDKError +from spsdk.image import ( + MasterBootImage, + MasterBootImageType, + MultipleImageEntry, + MultipleImageTable, + TrustZone, +) from spsdk.image.keystore import KeySourceType, KeyStore +from spsdk.image.mbimg import MasterBootImageN4Analog from spsdk.utils.crypto import CertBlockV2, Certificate from spsdk.utils.misc import load_binary - ################################################################# # To create data sets for Master Boot Image (MBI) # - check the tests\image\data\mbi for .cmd and .json files @@ -112,18 +118,21 @@ def test_invalid_master_boot_image_params(data_dir): with pytest.raises(TypeError): # noinspection PyTypeChecker MasterBootImage(app=5) + # The app MUST be bytes or bytearray + with pytest.raises(SPSDKError): + MasterBootImage(app=5, load_addr=0) # load_addr must not be negative - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): MasterBootImage( app=bytes(range(64)), load_addr=-1, image_type=MasterBootImageType.CRC_XIP_IMAGE ) # certificate block is supported for signed images - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): MasterBootImage( app=bytes(range(64)), load_addr=0, image_type=MasterBootImageType.SIGNED_RAM_IMAGE ) - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): MasterBootImage( app=bytes(range(64)), load_addr=0, image_type=MasterBootImageType.SIGNED_XIP_IMAGE ) @@ -131,7 +140,7 @@ def test_invalid_master_boot_image_params(data_dir): # hmac_key must be provided for load-to-ram image cert_block = certificate_block(data_dir, ["selfsign_2048_v3.der.crt"]) priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): MasterBootImage( app=bytes(range(64)), load_addr=0, @@ -139,7 +148,7 @@ def test_invalid_master_boot_image_params(data_dir): cert_block=cert_block, priv_key_pem_data=priv_key_pem_data, ) - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): MasterBootImage( app=bytes(range(64)), load_addr=0, @@ -148,7 +157,7 @@ def test_invalid_master_boot_image_params(data_dir): priv_key_pem_data=priv_key_pem_data, key_store=KeyStore(KeySourceType.OTP), ) - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): MasterBootImage( app=bytes(range(64)), load_addr=0, @@ -158,7 +167,7 @@ def test_invalid_master_boot_image_params(data_dir): key_store=KeyStore(KeySourceType.KEYSTORE), ) # hmac_key (or key_store) cannot be provided for XIP image - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): MasterBootImage( app=bytes(range(64)), load_addr=0, @@ -167,7 +176,7 @@ def test_invalid_master_boot_image_params(data_dir): priv_key_pem_data=priv_key_pem_data, hmac_key=bytes(range(32)), ) - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): MasterBootImage( app=bytes(range(64)), load_addr=0, @@ -178,7 +187,7 @@ def test_invalid_master_boot_image_params(data_dir): ) # app_table can be specified only for RAM images - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): app_table = MultipleImageTable() MasterBootImage( app=bytes(range(64)), @@ -187,7 +196,7 @@ def test_invalid_master_boot_image_params(data_dir): app_table=app_table, ) # app_table is empty - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): app_table = MultipleImageTable() MasterBootImage( app=bytes(range(64)), @@ -196,11 +205,89 @@ def test_invalid_master_boot_image_params(data_dir): app_table=app_table, ) + # unsigned image have certification block + with pytest.raises(SPSDKError): + # create certification block + cert_block = certificate_block(data_dir, ["selfsign_2048_v3.der.crt"]) + priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + MasterBootImage( + app=bytes(range(64)), + load_addr=0, + image_type=MasterBootImageType.CRC_RAM_IMAGE, + cert_block=cert_block, + priv_key_pem_data=priv_key_pem_data, + ) + + # unsigned image have private key + with pytest.raises(SPSDKError): + # create certification block + priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + mbi = MasterBootImage( + app=bytes(range(64)), + load_addr=0, + image_type=MasterBootImageType.CRC_RAM_IMAGE, + ) + mbi._priv_key_pem_data = priv_key_pem_data + mbi._validate_new_instance() + + # signed image without private key + with pytest.raises(SPSDKError): + # create certification block + cert_block = certificate_block(data_dir, ["selfsign_2048_v3.der.crt"]) + MasterBootImage( + app=bytes(range(64)), + load_addr=0, + image_type=MasterBootImageType.SIGNED_XIP_IMAGE, + cert_block=cert_block, + ) + + # Invalid ctr_init_vector size + with pytest.raises(SPSDKError): + with open(os.path.join(data_dir, "testfffffff.bin"), "rb") as f: + org_data = f.read() + user_key = "E39FD7AB61AE6DDDA37158A0FC3008C6D61100A03C7516EA1BE55A39F546BAD5" + ctr_init_vector = bytes.fromhex("11") + # create certification block + cert_block = certificate_block(data_dir, ["selfsign_2048_v3.der.crt"]) + priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + + mbi = MasterBootImage( + app=org_data, + image_type=MasterBootImageType.ENCRYPTED_RAM_IMAGE, + load_addr=0x12345678, + trust_zone=TrustZone.disabled(), + cert_block=cert_block, + priv_key_pem_data=priv_key_pem_data, + hmac_key=user_key, + ctr_init_vector=ctr_init_vector, + ) + # error if image len is not enough - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): MasterBootImage(app=bytes(range(32)), load_addr=0).export() +@pytest.mark.parametrize( + "fw_ver, expected_val", + [ + (0, b"\x05\x00\x00\x00"), + (1, b"\x05\x04\x01\x00"), + (0x10, b"\x05\x04\x10\x00"), + (0xFF, b"\x05\x04\xff\x00"), + ], +) +def test_N4A_image_version(fw_ver, expected_val): + """Test of generating of various image versions into binary MBI""" + mbi = MasterBootImageN4Analog( + app=bytes(range(256)), + load_addr=0, + image_type=MasterBootImageType.CRC_XIP_IMAGE, + firmware_version=fw_ver, + ) + data = mbi.export()[0x24:0x28] + assert data == expected_val + + def _compare_image(mbi: MasterBootImage, data_dir: str, expected_mbi_filename: str) -> bool: """Compare generated image with expected image @@ -499,14 +586,14 @@ def test_signed_xip_multiple_certificates_invalid_input(data_dir): "selfsign_3072_v3.der.crt", "selfsign_2048_v3.der.crt", ] - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): certificate_block(data_dir, der_file_names, 1) # public key in certificate and private key does not match der_file_names = ["selfsign_4096_v3.der.crt"] cert_block = certificate_block(data_dir, der_file_names, 0) priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): MasterBootImage( app=bytes(range(128)), load_addr=0, @@ -519,7 +606,7 @@ def test_signed_xip_multiple_certificates_invalid_input(data_dir): # chain of certificates does not match der_file_names = ["selfsign_4096_v3.der.crt"] chain_certificates = ["ch3_crt2_v3.der.crt"] - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): certificate_block(data_dir, der_file_names, 0, chain_certificates) @@ -688,7 +775,7 @@ def test_plain_xip_crc_custom_tz(data_dir, input_img, tz_config, family, expecte def test_base_info(data_dir): - """Basic test for MasterBootImage - information """ + """Basic test for MasterBootImage - information""" # plain image mbi = MasterBootImage(app=bytes(range(64)), load_addr=0, enable_hw_user_mode_keys=True) output = mbi.info() @@ -755,3 +842,213 @@ def test_multiple_images_with_relocation_table(data_dir): ) assert _compare_image(mbi, os.path.join(data_dir, "multicore"), "expected_output.bin") + + +def test_invalid_masterbootimage(data_dir): + cert_block = certificate_block(data_dir, ["selfsign_2048_v3.der.crt"], 0) + priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + with pytest.raises(SPSDKError): + MasterBootImage(app=5, load_addr=0) + with pytest.raises(SPSDKError): + MasterBootImage( + app=bytes(5), + load_addr=0, + app_table=MultipleImageTable(), + image_type=MasterBootImageType.CRC_XIP_IMAGE, + ) + with pytest.raises(SPSDKError): + MasterBootImage( + app=bytes(68), + load_addr=0, + image_type=MasterBootImageType.SIGNED_XIP_IMAGE, + cert_block=cert_block, + ) + with pytest.raises(SPSDKError): + MasterBootImage( + app=bytes(68), + load_addr=0, + image_type=MasterBootImageType.CRC_RAM_IMAGE, + cert_block=cert_block, + ) + with pytest.raises(SPSDKError): + MasterBootImage( + app=bytes(68), + load_addr=0, + image_type=MasterBootImageType.CRC_RAM_IMAGE, + priv_key_pem_data=priv_key_pem_data, + cert_block=cert_block, # check it + ) + with pytest.raises(SPSDKError): + MasterBootImage( + app=bytes(68), + load_addr=0, + image_type=MasterBootImageType.ENCRYPTED_RAM_IMAGE, + priv_key_pem_data=priv_key_pem_data, + ctr_init_vector=b"\x00\x01", + cert_block=cert_block, + ) + + +def test_multiple_image_entry_table_invalid(): + with pytest.raises(SPSDKError, match="Invalid destination address"): + MultipleImageEntry(img=bytes(), dst_addr=0xFFFFFFFFA) + with pytest.raises(SPSDKError): + MultipleImageEntry(img=bytes(), dst_addr=0xFFFFFFFF, flags=4) + + +def test_multiple_image_table_invalid(): + with pytest.raises(SPSDKError, match="There must be at least one entry for export"): + img_table = MultipleImageTable() + img_table._entries = None + img_table.export(start_addr=0xFFF) + + +def test_master_boot_image_no_cert_block(): + table = MultipleImageTable() + table.add_entry(MultipleImageEntry(bytes(64), 0x80000)) + with pytest.raises(SPSDKError, match="Certification block is not present"): + MasterBootImage( + app=bytes(64), + app_table=table, + load_addr=0, + image_type=MasterBootImageType.CRC_RAM_IMAGE, + priv_key_pem_data=bytes(5), + ) + + +def test_master_boot_image_no_init_vector(data_dir): + with open(os.path.join(data_dir, "testfffffff.bin"), "rb") as f: + org_data = f.read() + user_key = "E39FD7AB61AE6DDDA37158A0FC3008C6D61100A03C7516EA1BE55A39F546BAD5" + key_store = KeyStore(KeySourceType.KEYSTORE, None) + cert_block = certificate_block(data_dir, ["selfsign_2048_v3.der.crt"]) + priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + mbi = MasterBootImage( + app=org_data, + image_type=MasterBootImageType.ENCRYPTED_RAM_IMAGE, + load_addr=0x12345678, + trust_zone=TrustZone.disabled(), + cert_block=cert_block, + priv_key_pem_data=priv_key_pem_data, + hmac_key=user_key, + key_store=key_store, + ) + mbi.ctr_init_vector = None + with pytest.raises(SPSDKError, match="Initial vector for encryption counter is not present"): + mbi.export() + + +def test_master_boot_image_invalid_hmac(data_dir): + with open(os.path.join(data_dir, "testfffffff.bin"), "rb") as f: + org_data = f.read() + user_key = "E39FD7AB61AE6DDDA37158A0FC3008C6D61100A03C7516EA1BE55A39F546BAD5" + key_store = KeyStore(KeySourceType.KEYSTORE, None) + cert_block = certificate_block(data_dir, ["selfsign_2048_v3.der.crt"]) + priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + mbi = MasterBootImage( + app=org_data, + image_type=MasterBootImageType.ENCRYPTED_RAM_IMAGE, + load_addr=0x12345678, + trust_zone=TrustZone.disabled(), + cert_block=cert_block, + priv_key_pem_data=priv_key_pem_data, + hmac_key=user_key, + key_store=key_store, + ) + mbi.hmac_key = None + with pytest.raises(SPSDKError, match="Invalid hmac key"): + mbi._hmac(data=bytes(16)) + mbi = MasterBootImage( + app=org_data, + image_type=MasterBootImageType.ENCRYPTED_RAM_IMAGE, + load_addr=0x12345678, + trust_zone=TrustZone.disabled(), + cert_block=cert_block, + priv_key_pem_data=priv_key_pem_data, + hmac_key=user_key, + key_store=key_store, + ) + mbi._HMAC_DERIVED_KEY_LEN = 7 + with pytest.raises(SPSDKError, match="Invalid length of key"): + mbi._hmac(data=bytes(16)) + mbi._HMAC_DERIVED_KEY_LEN = 16 + mbi.HMAC_SIZE = 44 + with pytest.raises(SPSDKError, match="Invalid length of calculated hmac"): + mbi._hmac(data=bytes(16)) + mbi.HMAC_SIZE = 32 + + +def test_master_boot_image_invalid_encrypt(data_dir): + with open(os.path.join(data_dir, "testfffffff.bin"), "rb") as f: + org_data = f.read() + user_key = "E39FD7AB61AE6DDDA37158A0FC3008C6D61100A03C7516EA1BE55A39F546BAD5" + key_store = KeyStore(KeySourceType.KEYSTORE, None) + cert_block = certificate_block(data_dir, ["selfsign_2048_v3.der.crt"]) + priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + mbi = MasterBootImage( + app=org_data, + image_type=MasterBootImageType.ENCRYPTED_RAM_IMAGE, + load_addr=0x12345678, + trust_zone=TrustZone.disabled(), + cert_block=cert_block, + priv_key_pem_data=priv_key_pem_data, + hmac_key=user_key, + key_store=key_store, + ) + mbi.key_store = None + with pytest.raises(SPSDKError, match="key_store must be specified for encrypted image"): + mbi._encrypt(data=bytes(16)) + mbi = MasterBootImage( + app=org_data, + image_type=MasterBootImageType.ENCRYPTED_RAM_IMAGE, + load_addr=0x12345678, + trust_zone=TrustZone.disabled(), + cert_block=cert_block, + priv_key_pem_data=priv_key_pem_data, + hmac_key=user_key, + key_store=key_store, + ) + mbi.hmac_key = bytes(5) + with pytest.raises(SPSDKError, match="Invalid hmac key"): + mbi._encrypt(data=bytes(16)) + mbi = MasterBootImage( + app=org_data, + image_type=MasterBootImageType.ENCRYPTED_RAM_IMAGE, + load_addr=0x12345678, + trust_zone=TrustZone.disabled(), + cert_block=cert_block, + priv_key_pem_data=priv_key_pem_data, + hmac_key=user_key, + key_store=key_store, + ) + mbi.ctr_init_vector = None + with pytest.raises(SPSDKError, match="Invalid initialization vector"): + mbi._encrypt(data=bytes(16)) + + +def test_invalid_export_mbi(data_dir): + with open(os.path.join(data_dir, "testfffffff.bin"), "rb") as f: + org_data = f.read() + user_key = "E39FD7AB61AE6DDDA37158A0FC3008C6D61100A03C7516EA1BE55A39F546BAD5" + key_store_bin = None + key_store = KeyStore(KeySourceType.KEYSTORE, key_store_bin) + cert_block = certificate_block(data_dir, ["selfsign_2048_v3.der.crt"]) + priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + mbi = MasterBootImage( + app=org_data, + image_type=MasterBootImageType.ENCRYPTED_RAM_IMAGE, + load_addr=0x12345678, + trust_zone=TrustZone.disabled(), + cert_block=cert_block, + priv_key_pem_data=priv_key_pem_data, + hmac_key=user_key, + key_store=key_store, + ctr_init_vector=bytes(16), + ) + mbi._priv_key_pem_data = None + with pytest.raises(SPSDKError, match="Private key not present"): + mbi.export() + mbi._priv_key_pem_data = priv_key_pem_data + mbi.cert_block = None + with pytest.raises(SPSDKError, match="Wrong private key"): + mbi.export() diff --git a/tests/image/misc/test_format_value.py b/tests/image/misc/test_format_value.py index bbae9bbf..b227cf00 100644 --- a/tests/image/misc/test_format_value.py +++ b/tests/image/misc/test_format_value.py @@ -5,15 +5,18 @@ # # SPDX-License-Identifier: BSD-3-Clause import io + import pytest -from spsdk.image.misc import read_raw_data, NotEnoughBytesException + +from spsdk import SPSDKError +from spsdk.image.misc import NotEnoughBytesException, read_raw_data def test_read_raw_segment(): stream = io.BytesIO() - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): read_raw_data(stream, length=0, index=-1) - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): read_raw_data(stream, length=-1, index=1) with pytest.raises(NotEnoughBytesException): read_raw_data(stream, length=1, index=1) diff --git a/tests/image/misc/test_parse_int.py b/tests/image/misc/test_parse_int.py index 3149a11a..f09f31df 100644 --- a/tests/image/misc/test_parse_int.py +++ b/tests/image/misc/test_parse_int.py @@ -5,7 +5,9 @@ # # SPDX-License-Identifier: BSD-3-Clause +from spsdk.exceptions import SPSDKError import pytest + from spsdk.image.misc import parse_int, size_fmt @@ -38,7 +40,7 @@ def test_parse_int(test_input, expected): @pytest.mark.parametrize("test_input", ["", "x", ".0", "1whatever*", " 3 "]) def test_parse_int_invalid_input(test_input): """ Test invalid inputs for parse_int() """ - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): parse_int(test_input) diff --git a/tests/image/secret/test_sec_api.py b/tests/image/secret/test_sec_api.py index 08810867..b7e4a62e 100644 --- a/tests/image/secret/test_sec_api.py +++ b/tests/image/secret/test_sec_api.py @@ -7,18 +7,21 @@ # SPDX-License-Identifier: BSD-3-Clause import os + import pytest from cryptography import x509 from cryptography.hazmat.backends import default_backend -from spsdk.image import SrkTable, SrkItem, MAC, Signature, CertificateImg, SecretKeyBlob + +from spsdk import SPSDKError +from spsdk.crypto.loaders import load_certificate +from spsdk.image import MAC, CertificateImg, SecretKeyBlob, Signature, SrkItem, SrkTable from spsdk.image.secret import ( - NotImplementedSRKPublicKeyType, - NotImplementedSRKItem, - SrkItemRSA, NotImplementedSRKCertificate, + NotImplementedSRKItem, + NotImplementedSRKPublicKeyType, SrkItemHash, + SrkItemRSA, ) -from spsdk.crypto.loaders import load_certificate @pytest.fixture(scope="module", name="srk_pem") @@ -99,7 +102,7 @@ def test_srk_table_single_cert(srk_pem): # test get_fuse() returns valid value for fuse_index in range(8): assert srk_table.get_fuse(fuse_index) >= 0 - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): srk_table.get_fuse(8) # test info() returns non-empty text assert srk_table.info() # test export returns any result @@ -156,10 +159,21 @@ def test_mac_class(): assert mac.nonce == test_nonce assert mac.mac == test_mac - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): mac.data = test_mac +def test_mac_invalid(): + mac = MAC() + with pytest.raises(SPSDKError, match="Incorrect length of mac"): + mac.update_aead_encryption_params(mac=bytes(4), nonce=bytes(12)) + with pytest.raises(SPSDKError, match="Incorrect length of nonce"): + mac.update_aead_encryption_params(mac=bytes(16), nonce=bytes(4)) + mac = MAC(mac_len=15) + with pytest.raises(SPSDKError, match="Incorrect number of MAC bytes"): + mac.update_aead_encryption_params(mac=bytes(16), nonce=bytes(12)) + + def test_signature_class(): sig = Signature(version=0x40) @@ -228,3 +242,20 @@ def test_srkitemhash_parse_not_valid_header(): srkhash_out = srkhash.export() with pytest.raises(NotImplementedSRKItem): SrkItemHash.parse(srkhash_out) + + +def test_srkitemhash_invalid_algorithm(): + with pytest.raises(SPSDKError, match="Incorrect algorithm"): + SrkItemHash(algorithm=88, digest=bytes(16)) + + +def test_srktable_invalid_flag(): + srk = SrkItemRSA(modulus=bytes(2048), exponent=bytes(4)) + with pytest.raises(SPSDKError, match="Incorrect flag"): + srk.flag = 8 + + +def test_srk_table_invalid_fuse(srk_pem): + srk_table = SrkTable(version=0x40) + with pytest.raises(SPSDKError, match="Incorrect index of the fuse"): + srk_table.get_fuse(index=9) diff --git a/tests/image/segments/data/bee-data.bin b/tests/image/segments/data/bee-data.bin new file mode 100644 index 0000000000000000000000000000000000000000..33de38a6e854443e1358363da0b61b7beff481ac GIT binary patch literal 256 zcmWG>bdPuSa0y~yU<_knfP;^Wa3%wTq*vgas-<=js{;5Nk%aLvk+q|%695Vb;!&Ug UlviXz*M;DJ05U%^Blr}t02oOMg#Z8m literal 0 HcmV?d00001 diff --git a/tests/image/segments/test_app.py b/tests/image/segments/test_app.py index c76f6eca..bfb0d2dc 100644 --- a/tests/image/segments/test_app.py +++ b/tests/image/segments/test_app.py @@ -6,7 +6,7 @@ # # SPDX-License-Identifier: BSD-3-Clause -from spsdk.image import SegBDT, SegAPP +from spsdk.image import SegAPP, SegBDT def test_app_segment_api(): diff --git a/tests/image/segments/test_base.py b/tests/image/segments/test_base.py index eefd865d..4748dd9c 100644 --- a/tests/image/segments/test_base.py +++ b/tests/image/segments/test_base.py @@ -8,8 +8,8 @@ import pytest -from spsdk.image import SegIVT3b -from spsdk.image.segments import SegFCB, FlexSPIConfBlockFCB +from spsdk import SPSDKError +from spsdk.image.segments import BaseSegment def test_base_exceptions(): @@ -29,3 +29,9 @@ def test_base(): base = BaseSegment() assert base.size == 0 + + +def test_base_invalid_padding_length(): + base_seg = BaseSegment() + with pytest.raises(SPSDKError, match="Length of padding must be >= 0"): + base_seg.padding_len = -1 diff --git a/tests/image/segments/test_bdt.py b/tests/image/segments/test_bdt.py index 5425c280..16aeebd8 100644 --- a/tests/image/segments/test_bdt.py +++ b/tests/image/segments/test_bdt.py @@ -6,7 +6,10 @@ # # SPDX-License-Identifier: BSD-3-Clause -from spsdk.image import SegIVT2, SegBDT +import pytest + +from spsdk import SPSDKError +from spsdk.image import SegBDT, SegIVT2 def test_bdt_export_parse(): @@ -63,3 +66,9 @@ def test_bdt_info(): info_strings = ["Start", "App Length", "Plugin"] for info_string in info_strings: assert info_string in output, f"string {info_string} is not in the output: {output}" + + +def test_bdt_invalid_plugin(): + bdt = SegBDT() + with pytest.raises(SPSDKError, match="Plugin value must be 0 .. 2"): + bdt.plugin = 10 diff --git a/tests/image/segments/test_bee.py b/tests/image/segments/test_bee.py index af319c96..bcf2bb33 100644 --- a/tests/image/segments/test_bee.py +++ b/tests/image/segments/test_bee.py @@ -5,18 +5,21 @@ # # SPDX-License-Identifier: BSD-3-Clause -import pytest from typing import Optional +import pytest + +from spsdk import SPSDKError from spsdk.image.bee import ( BeeBaseClass, BeeFacRegion, - BeeProtectRegionBlock, BeeKIB, + BeeProtectRegionBlock, BeeRegionHeader, ) from spsdk.image.segments import SegBEE from spsdk.utils.crypto import crypto_backend +from spsdk.utils.misc import load_binary def verify_base_class_features(inst: BeeBaseClass, decrypt_key: Optional[bytes] = None) -> None: @@ -46,7 +49,7 @@ def verify_base_class_features(inst: BeeBaseClass, decrypt_key: Optional[bytes] assert inst2 is not None assert inst == inst2 # verify ValueError exception by parser if input data are too short - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): cls.parse(b"\x00", 0) @@ -59,13 +62,13 @@ def test_bee_fac_region() -> None: verify_base_class_features(fac) # test invalid params: # - address is not aligned - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError, match="Invalid configuration of the instance"): BeeFacRegion(1, 1, 3) # - length == 0 - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError, match="Invalid start/end address"): BeeFacRegion(0x2000, 0, 3) - # - invalid mode - with pytest.raises(AssertionError): + # # - invalid mode + with pytest.raises(SPSDKError, match="Invalid protected level"): BeeFacRegion(0x1000, 0x2000, -1) @@ -74,9 +77,9 @@ def test_bee_protect_region_block() -> None: prdb = BeeProtectRegionBlock() assert prdb.fac_count == 0 # verify assertion if FAC are not specified - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): prdb.validate() - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): prdb.export() # test with FAC regions prdb.add_fac(BeeFacRegion(0x00000000, 0x01000000, 0)) @@ -87,10 +90,43 @@ def test_bee_protect_region_block() -> None: assert prdb.fac_count == 4 verify_base_class_features(prdb) # parse invalid data - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): BeeProtectRegionBlock.parse(b"\x00" * 256) +def test_bee_invalid_validate(): + prdb = BeeProtectRegionBlock() + prdb._start_addr = 0xFFFFFFFFA + with pytest.raises(SPSDKError, match="Invalid start address"): + prdb.validate() + prdb = BeeProtectRegionBlock() + prdb._end_addr = 0xFFFFFFFFA + with pytest.raises(SPSDKError, match="Invalid start/end address"): + prdb.validate() + prdb = BeeProtectRegionBlock() + prdb.mode = 10 + with pytest.raises(SPSDKError, match="Only AES/CTR encryption mode supported now"): + prdb.validate() + prdb = BeeProtectRegionBlock() + prdb.counter = bytes(22) + with pytest.raises(SPSDKError, match="Invalid conter"): + prdb.validate() + prdb = BeeProtectRegionBlock() + prdb.counter = b"\x01\x00\x00\x00" * 4 + with pytest.raises(SPSDKError, match="last four bytes must be zero"): + prdb.validate() + prdb = BeeProtectRegionBlock() + prdb.counter = bytes(16) + with pytest.raises(SPSDKError, match="Invalid FAC regions"): + prdb.validate() + prdb = BeeProtectRegionBlock() + with pytest.raises(SPSDKError, match="Incorrect length of binary block to be encrypted"): + prdb.encrypt_block(key=bytes(16), start_addr=0x0, data=bytes(16)) + prdb = BeeProtectRegionBlock() + with pytest.raises(SPSDKError, match="Invalid length of key"): + prdb.encrypt_block(key=bytes(15), start_addr=0xAA, data=bytes(1024)) + + def test_bee_kib() -> None: """Test BeeKIB class""" kib = BeeKIB() @@ -153,5 +189,33 @@ def test_seg_bee() -> None: assert seg == parsed_seg # total number of FACs exceeded hdr.add_fac(BeeFacRegion(0xF0000000, 0x00010000, 3)) - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): seg.validate() + + +def test_invalid_bee_fac_region_parse(): + with pytest.raises(SPSDKError): + BeeFacRegion.parse(b"1" * 1024) + + +def test_invalid_bee_protected_block_parse(data_dir): + """Test for unsupported version""" + valid_data = bytearray(load_binary(f"{data_dir}/bee-data.bin")) + + invalid_version = valid_data + invalid_version[8:12] = bytes(0) * 4 + with pytest.raises(SPSDKError): + BeeProtectRegionBlock.parse(invalid_version + bytes(4)) + + """Test for reserved area""" + invalid_reserved = valid_data + invalid_version[8:12] = b"\x00\x00\x01\x56" + invalid_reserved[78] = 0xFF + with pytest.raises(SPSDKError): + BeeProtectRegionBlock.parse(invalid_reserved + bytes(4)) + + +def test_seg_bee_invalid_encrypt_data() -> None: + seg = SegBEE([]) + with pytest.raises(SPSDKError, match="Invalid start address"): + seg.encrypt_data(start_addr=0xFFFFFFFFFFFFFFFFFFFF, data=bytes(16)) diff --git a/tests/image/segments/test_csf.py b/tests/image/segments/test_csf.py index 58c4f50e..da5619c1 100644 --- a/tests/image/segments/test_csf.py +++ b/tests/image/segments/test_csf.py @@ -7,12 +7,13 @@ # SPDX-License-Identifier: BSD-3-Clause import os.path + import pytest -from spsdk.image import CmdCheckData, CmdWriteData, EnumWriteOps -from spsdk.image import SegCSF, EnumCheckOps -from spsdk.image.segments import SegDCD +from spsdk import SPSDKError +from spsdk.image import CmdCheckData, CmdWriteData, EnumCheckOps, EnumWriteOps, SegCSF from spsdk.image.secret import Signature +from spsdk.image.segments import SegDCD from spsdk.utils.misc import extend_block @@ -104,3 +105,9 @@ def test_SegCSF_export_parse(): # with padding obj.padding_len = 0x10 assert obj.export() == extend_block(data, obj.size + 0x10) + + +def test_SegCSF_invalid_append_command(): + obj = SegCSF(enabled=True) + with pytest.raises(SPSDKError, match="Invalid command"): + obj.append_command(cmd=6) diff --git a/tests/image/segments/test_csf_api.py b/tests/image/segments/test_csf_api.py index d453e38e..81d468f0 100644 --- a/tests/image/segments/test_csf_api.py +++ b/tests/image/segments/test_csf_api.py @@ -7,7 +7,8 @@ # SPDX-License-Identifier: BSD-3-Clause import pytest -from spsdk.image import SegCSF, CmdWriteData, CmdCheckData, EnumWriteOps, EnumCheckOps + +from spsdk.image import CmdCheckData, CmdWriteData, EnumCheckOps, EnumWriteOps, SegCSF @pytest.fixture(scope="module") diff --git a/tests/image/segments/test_dcd.py b/tests/image/segments/test_dcd.py index a462443c..75667416 100644 --- a/tests/image/segments/test_dcd.py +++ b/tests/image/segments/test_dcd.py @@ -8,9 +8,9 @@ import pytest -from spsdk.image import CmdCheckData, CmdNop -from spsdk.image import EnumCheckOps -from spsdk.image.segments import SegFCB, FlexSPIConfBlockFCB, SegDCD +from spsdk import SPSDKError +from spsdk.image import CmdCheckData, CmdNop, EnumCheckOps +from spsdk.image.segments import FlexSPIConfBlockFCB, SegDCD, SegFCB def test_segDCD(): @@ -69,3 +69,11 @@ def test_segDCD_eq(): assert dcd_seg != confFlexSpi assert dcd_seg != segfcb assert dcd_seg == dcd_seg + + +def test_segDCD_invalid_append_pop(): + dcd_seg = SegDCD() + with pytest.raises(SPSDKError, match="Invalid command"): + dcd_seg.append(cmd=5) + with pytest.raises(SPSDKError, match="Can not pop item from dcd segment"): + dcd_seg.pop(index=100) diff --git a/tests/image/segments/test_dcd_api.py b/tests/image/segments/test_dcd_api.py index 9c4c4c11..32dd6be1 100644 --- a/tests/image/segments/test_dcd_api.py +++ b/tests/image/segments/test_dcd_api.py @@ -7,8 +7,10 @@ # SPDX-License-Identifier: BSD-3-Clause import os + import pytest -from spsdk.image import SegDCD, CmdWriteData, CmdCheckData, CmdNop, EnumWriteOps, EnumCheckOps + +from spsdk.image import CmdCheckData, CmdNop, CmdWriteData, EnumCheckOps, EnumWriteOps, SegDCD @pytest.fixture(scope="module") diff --git a/tests/image/segments/test_fcb.py b/tests/image/segments/test_fcb.py index f03e05bb..746a9f84 100644 --- a/tests/image/segments/test_fcb.py +++ b/tests/image/segments/test_fcb.py @@ -6,9 +6,11 @@ # SPDX-License-Identifier: BSD-3-Clause import os + import pytest -from spsdk.image.segments import FlexSPIConfBlockFCB, SegFCB, SegIVT3b, PaddingFCB +from spsdk import SPSDKError +from spsdk.image.segments import FlexSPIConfBlockFCB, PaddingFCB, SegFCB, SegIVT3b from spsdk.utils.misc import load_binary from tests.misc import compare_bin_files @@ -63,17 +65,17 @@ def test_flexspi_conf_block_fcb(data_dir) -> None: assert fcb.size == 0 assert fcb.export() == b"" # invalid tag - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): FlexSPIConfBlockFCB.parse(b"\x00" * 512) # invalid version - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): FlexSPIConfBlockFCB.parse(FlexSPIConfBlockFCB.TAG + b"\x00" * 512) # insufficient length - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): FlexSPIConfBlockFCB.parse(FlexSPIConfBlockFCB.TAG + FlexSPIConfBlockFCB.VERSION[::-1]) -def test_padding_fcb(data_dir) -> None: +def test_padding_fcb() -> None: """See PaddingFCB class""" fcb = PaddingFCB(10, padding_value=0xA5) # enabled, no padding @@ -95,3 +97,10 @@ def test_padding_fcb(data_dir) -> None: assert fcb.space == 16 assert fcb.export() == b"\xA5" * 10 + b"\x00" * 6 assert fcb.info() + + +def test_padding_fcb_invalid() -> None: + with pytest.raises(SPSDKError, match="Invalid size of the exported padding"): + PaddingFCB(size=-1, padding_value=0xA5) + with pytest.raises(SPSDKError, match="Invalid padding"): + PaddingFCB(size=10, padding_value=-1) diff --git a/tests/image/segments/test_ivt.py b/tests/image/segments/test_ivt.py index 9b377c52..be67e590 100644 --- a/tests/image/segments/test_ivt.py +++ b/tests/image/segments/test_ivt.py @@ -8,7 +8,8 @@ import pytest -from spsdk.image import SegIVT2, SegAPP, SegIVT3a, SegIVT3b +from spsdk import SPSDKError +from spsdk.image import SegAPP, SegIVT2, SegIVT3a, SegIVT3b from spsdk.image.segments import _format_ivt_item @@ -21,7 +22,7 @@ def test_ivt2_segment_api(): assert ivt2.app_address == 0 assert ivt2.csf_address == 0 - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): _ = ivt2.export() # set correct values @@ -41,17 +42,17 @@ def test_ivt2_validate(): ivt2.ivt_address = 0x877FF42C ivt2.dcd_address = 0x877FF400 ivt2.bdt_address = 0x877FF42E - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): ivt2.validate() ivt2.csf_address = 0x877FF000 ivt2.dcd_address = 0x877FF500 - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): ivt2.validate() ivt2.padding = 1 ivt2.csf_address = 0x877FF600 - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): ivt2.validate() @@ -89,6 +90,12 @@ def test_ivt2_equality(): assert ivt2 != ivt2_other +def test_ivt2_invalid_version(): + ivt2 = SegIVT2(0x41) + with pytest.raises(SPSDKError, match="Invalid version of IVT and image format"): + ivt2.version = 0x39 + + def test_ivt3a_segment_api(): ivt3a = SegIVT3a(0) assert ivt3a.version == 0 @@ -97,7 +104,7 @@ def test_ivt3a_segment_api(): assert ivt3a.dcd_address == 0 assert ivt3a.csf_address == 0 - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): _ = ivt3a.export() # set correct values @@ -112,11 +119,11 @@ def test_ivt3a_validate(): ivt3a.ivt_address = 0x800480 ivt3a.dcd_address = 0x800400 ivt3a.bdt_address = 0x800980 - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): ivt3a.validate() ivt3a.dcd_address = 0x800500 ivt3a.csf_address = 0x800200 - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): ivt3a.validate() @@ -169,7 +176,7 @@ def test_ivt3b_segment_api(): assert ivt3b.scd_address == 0 assert ivt3b.csf_address == 0 - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): _ = ivt3b.export() # set correct values @@ -186,17 +193,17 @@ def test_ivt3b_validate(): ivt3b.dcd_address = 0x2000E000 ivt3b.bdt_address = 0x2000E690 ivt3b.dcd_address = 0x2000E659 - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): ivt3b.validate() ivt3b.dcd_address = 0x2000E665 ivt3b.csf_address = 0x2000E050 - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): ivt3b.validate() ivt3b.csf_address = 0x2000E669 ivt3b.scd_address = 0x2000E600 - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): ivt3b.validate() diff --git a/tests/image/segments/test_key_store.py b/tests/image/segments/test_key_store.py index 963fc53e..86817a2b 100644 --- a/tests/image/segments/test_key_store.py +++ b/tests/image/segments/test_key_store.py @@ -1,12 +1,13 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause import pytest +from spsdk import SPSDKError from spsdk.image.keystore import KeySourceType, KeyStore @@ -31,8 +32,23 @@ def test_key_store(): def test_invalid_key_store(): # invalid key store length - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): KeyStore(KeySourceType.KEYSTORE, bytes(range(10))) # key-store specified in OTP mode - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): KeyStore(KeySourceType.OTP, bytes(range(10))) + with pytest.raises( + SPSDKError, match="KeyStore can be initialized only if key_source == KEYSTORE" + ): + KeyStore(KeySourceType.OTP, bytes(1424)) + key_store = KeyStore(KeySourceType.KEYSTORE, bytes([0] * KeyStore.KEY_STORE_SIZE)) + with pytest.raises(SPSDKError, match="Invalid length of hmac key"): + key_store.derive_hmac_key(hmac_key=bytes(31)) + with pytest.raises(SPSDKError, match="Invalid length of master key"): + key_store.derive_enc_image_key(master_key=bytes(31)) + with pytest.raises(SPSDKError, match="Invalid length of master key"): + key_store.derive_sb_kek_key(master_key=bytes(31)) + with pytest.raises(SPSDKError, match="Invalid length of master key"): + key_store.derive_otfad_kek_key(master_key=bytes(31), otfad_input=bytes(16)) + with pytest.raises(SPSDKError, match="Invalid length of input"): + key_store.derive_otfad_kek_key(master_key=bytes(32), otfad_input=bytes(15)) diff --git a/tests/image/trustzone/test_trustzone.py b/tests/image/trustzone/test_trustzone.py index b135aed3..39f24acc 100644 --- a/tests/image/trustzone/test_trustzone.py +++ b/tests/image/trustzone/test_trustzone.py @@ -5,10 +5,12 @@ # # SPDX-License-Identifier: BSD-3-Clause -import os import json +import os + import pytest +from spsdk import SPSDKError from spsdk.image import TrustZone, TrustZoneType @@ -47,23 +49,26 @@ def test_tz_types(sample_tz_data): def test_errors(sample_tz_data): - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): TrustZone.custom(family="totaly_legit_family", customizations=sample_tz_data) # throw error when TZ is disabled, but tz data are present - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): TrustZone(tz_type=TrustZoneType.DISABLED, customizations=sample_tz_data) # throw error when TZ is set to CUSTOM but no data and no family are provided - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): TrustZone(tz_type=TrustZoneType.CUSTOM) # throw error when TZ is set to CUSTOM but no family is provided - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): TrustZone(tz_type=TrustZoneType.CUSTOM, customizations=sample_tz_data) # throw error when TZ is set to CUSTOM but no data are provided - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): TrustZone(tz_type=TrustZoneType.CUSTOM, family="lpc55xx") # throw error for invalid customization data - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): TrustZone(family="lpc55xx", customizations={"fake": "this is fake"}) + # throw error when TZ type is custom and family is not set + with pytest.raises(SPSDKError, match="Need to provide 'family' parameter"): + TrustZone(tz_type=TrustZoneType.CUSTOM, family=None) def test_simplified_export(): @@ -72,6 +77,17 @@ def test_simplified_export(): assert TrustZone.disabled().export() == b"" +def test_export_invalid(sample_tz_data): + tz = TrustZone(family="lpc55xx", tz_type=TrustZoneType.CUSTOM, customizations=sample_tz_data) + tz.presets = None + with pytest.raises(SPSDKError, match="Preset data not present"): + tz.export() + tz = TrustZone(family="lpc55xx", tz_type=TrustZoneType.CUSTOM, customizations=sample_tz_data) + tz.customs = None + with pytest.raises(SPSDKError, match="Data not present"): + tz.export() + + # in data dir, there are example json config files for elftosb and their associated binaries # to create new datasets: # - create config file (as per elftosb documentation) @@ -91,3 +107,13 @@ def test_binary(data_dir, family, json_config, binary): my_data = TrustZone(family=family, customizations=json_config_data["trustZonePreset"]).export() assert my_data == binary_data + + +def test_tz_incorrect_data(sample_tz_data): + with pytest.raises(SPSDKError): + TrustZone( + family="lpc55xx", + raw_data=bytes(4), + tz_type=TrustZoneType.CUSTOM, + customizations=sample_tz_data, + ) diff --git a/tests/mboot/blhost/test_blhost_cli.py b/tests/mboot/blhost/test_blhost_cli.py index 505d5c04..490247d0 100644 --- a/tests/mboot/blhost/test_blhost_cli.py +++ b/tests/mboot/blhost/test_blhost_cli.py @@ -25,6 +25,9 @@ # get-property b"\x5a\xa4\x0c\x00\x4b\x33\x07\x00\x00\x02\x01\x00\x00\x00\x00\x00\x00\x00": b"\x5a\xa1\x5a\xa4\x0c\x00\x65\x1c\xa7\x00\x00\x02\x00\x00\x00\x00\x00\x00\x03\x4b", + # get-property 0xA + b"\x5a\xa4\x0c\x00\xe4\xe5\x07\x00\x00\x02\x0a\x00\x00\x00\x00\x00\x00\x00": + b"\x5a\xa1\x5a\xa4\x0c\x00\x2d\xc6\xa7\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00", # set-property 10 1 b"\x5a\xa4\x0c\x00\x67\x8d\x0c\x00\x00\x02\x0a\x00\x00\x00\x01\x00\x00\x00": b"\x5a\xa1\x5a\xa4\x0c\x00\xe0\xf7\xa0\x00\x00\x02\x00\x00\x00\x00\x0c\x00\x00\x00", @@ -49,6 +52,12 @@ # flash-erase-all-unsecure b"\x5a\xa4\x04\x00\xf6\x61\x0d\x00\x00\x00": b"\x5a\xa1\x5a\xa4\x0c\x00\x52\xcb\xa0\x00\x00\x02\x10\x27\x00\x00\x0d\x00\x00\x00", + # flash-erase-all 0x0 + b"\x5a\xa4\x08\x00\x0c\x22\x01\x00\x00\x01\x00\x00\x00\x00": + b"\x5a\xa1\x5a\xa4\x0c\x00\x66\xce\xa0\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00", + # flash-erase-region 0x8000000 0x0 + b"\x5a\xa4\x10\x00\x41\xee\x02\x00\x00\x03\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00": + b"\x5a\xa1\x5a\xa4\x0c\x00\xba\x55\xa0\x00\x00\x02\x00\x00\x00\x00\x02\x00\x00\x00", # flash-read-resource 1 4 1 b"\x5a\xa4\x10\x00\x71\xde\x10\x00\x00\x03\x01\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00": (b"\x5a\xa1" @@ -69,6 +78,27 @@ # flash-image {os.path.join(data_dir, 'evkmimxrt685_led_blinky_ext_flash.srec')} erase 3 b"\x5a\xa4\x10\x00\x46\xc2\x02\x00\x00\x03\x00\x10\x00\x08\x00\x58\x00\x00\x00\x00\x00\x00": b"\x5a\xa1\x5a\xa4\x0c\x00\x89\x42\xa0\x00\x00\x02\xdd\x27\x00\x00\x02\x00\x00\x00", + # trust-provisioning hsm_gen_key MFWISK 0 0x20008000 48 0x20009000 64 + b"\x5a\xa4\x20\x00\x53\x2e\x16\x00\x00\x07\x03\x00\x00\x00\xa5\xc3\x00\x00\x00\x00\x00\x00\x00\x80\x00\x20\x30\x00\x00\x00\x00\x90\x00\x20\x40\x00\x00\x00": + b"\x5a\xa1\x5a\xa4\x10\x00\x43\x02\xb6\x00\x00\x03\x00\x00\x00\x00\x30\x00\x00\x00\x40\x00\x00\x00", + # trust-provisioning hsm_store_key 5 1 0x2000B000 32 0x2000C000 48 + b"\x5a\xa4\x20\x00\x08\xf7\x16\x00\x00\x07\x04\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\x00\x00\xb0\x00\x20\x20\x00\x00\x00\x00\xc0\x00\x20\x30\x00\x00\x00": + b"\x5a\xa1\x5a\xa4\x10\x00\x5f\xd2\xb6\x00\x00\x03\x00\x00\x00\x00\x61\x00\x10\x10\x30\x00\x00\x00", + # trust-provisioning hsm_enc_blk 0x2000A000 48 16 0x2000C000 60 1 0x2000D000 256 + b"\x5a\xa4\x28\x00\x8e\xae\x16\x00\x00\x09\x05\x00\x00\x00\x00\xa0\x00\x20\x30\x00\x00\x00\x10\x00\x00\x00\x00\xc0\x00\x20\x3c\x00\x00\x00\x01\x00\x00\x00\x00\xd0\x00\x20\x00\x01\x00\x00": + b"\x5a\xa1\x5a\xa4\x08\x00\x49\x5e\xb6\x00\x00\x01\x00\x00\x00\x00", + # trust-provisioning hsm_enc_sign 0x20008000 48 0x2000F000 220 0x20010000 64 + b"\x5a\xa4\x20\x00\xe7\x20\x16\x00\x00\x07\x06\x00\x00\x00\x00\x80\x00\x20\x30\x00\x00\x00\x00\xf0\x00\x20\xdc\x00\x00\x00\x00\x00\x01\x20\x40\x00\x00\x00": + b"\x5a\xa1\x5a\xa4\x0c\x00\x20\xea\xb6\x00\x00\x02\x00\x00\x00\x00\x40\x00\x00\x00", + # trust-provisioning oem_gen_master_share 0x20008000 0x10 0x20009000 0x1000 0x2000A000 0x1000 0x2000B000 0x1000 + b"\x5a\xa4\x28\x00\x34\x07\x16\x00\x00\x09\x00\x00\x00\x00\x00\x80\x00\x20\x10\x00\x00\x00\x00\x90\x00\x20\x00\x10\x00\x00\x00\xa0\x00\x20\x00\x10\x00\x00\x00\xb0\x00\x20\x00\x10\x00\x00": + b"\x5a\xa1\x5a\xa4\x14\x00\xe6\x12\xb6\x00\x00\x04\x00\x00\x00\x00\x30\x00\x00\x00\x40\x00\x00\x00\x40\x00\x00\x00", + # trust-provisioning oem_set_master_share 0x20008000 16 0x20009000 64 + b"\x5a\xa4\x18\x00\x02\xfe\x16\x00\x00\x05\x01\x00\x00\x00\x00\x80\x00\x20\x10\x00\x00\x00\x00\x90\x00\x20\x40\x00\x00\x00": + b"\x5a\xa1\x5a\xa4\x08\x00\x49\x5e\xb6\x00\x00\x01\x00\x00\x00\x00", + # trust-provisioning oem_get_cust_cert_dice_puk 0x30015000 0x20 0x30016000 0x40 + b"\x5a\xa4\x18\x00\x01\xca\x16\x00\x00\x05\x02\x00\x00\x00\x00\x50\x01\x30\x20\x00\x00\x00\x00\x60\x01\x30\x40\x00\x00\x00": + b"\x5a\xa1\x5a\xa4\x0c\x00\x20\xea\xb6\x00\x00\x02\x00\x00\x00\x00\x40\x00\x00\x00", } # fmt: on @@ -102,6 +132,14 @@ def test_get_property(caplog): assert "Current Version = K3.0.0" in result.output +def test_get_property_hex_input(caplog): + cmd = "-p super-com get-property 0xA" + result = run_blhost_proxy(caplog, cmd) + assert "Response status = 0 (0x0) Success." in result.output + assert "Response word 1 = 1 (0x1)" in result.output + assert "Verify Writes = ON" in result.output + + def test_set_property(caplog): cmd = "-p super-com set-property 10 1" result = run_blhost_proxy(caplog, cmd) @@ -113,6 +151,7 @@ def test_efuse_read_once(caplog): result = run_blhost_proxy(caplog, cmd) assert "Response word 1 = 4 (0x4)" in result.output assert "Response word 2 = 0 (0x0)" in result.output + assert "Response status = 0 (0x0) Success." in result.output def test_efuse_read_once_unknown_error(caplog): @@ -157,6 +196,18 @@ def test_flash_erase_all_unsecure(caplog): assert "Response status = 10000 (0x2710) Unknown Command." in result.output +def test_flash_erase_all(caplog): + cmd = "-p super-com flash-erase-all 0x0" + result = run_blhost_proxy(caplog, cmd) + assert "Response status = 0 (0x0) Success." in result.output + + +def test_flash_erase_region(caplog): + cmd = "-p super-com flash-erase-region 0x8000000 0x0" + result = run_blhost_proxy(caplog, cmd) + assert "Response status = 0 (0x0) Success." in result.output + + def run_flash_read_resource(caplog, cmd): result = run_blhost_proxy(caplog, cmd, ignore_ack=True) assert "Response status = 0 (0x0) Success." in result.output @@ -201,3 +252,70 @@ def test_flash_image_memory_not_configured(caplog, data_dir): cmd = f"-p super-com flash-image {os.path.join(data_dir, 'evkmimxrt685_led_blinky_ext_flash.srec')} erase 3" result = run_blhost_proxy(caplog, cmd, expect_exit_code=1) assert "Response status = 10205 (0x27dd) Memory Not Configured." in result.output + + +def test_tp_hsm_gen_key(caplog): + cmd = "-p super-com trust-provisioning hsm_gen_key MFWISK 0 0x20008000 48 0x20009000 64" + result = run_blhost_proxy(caplog, cmd) + assert "Response status = 0 (0x0) Success." in result.output + assert "Response word 1 = 48 (0x30)" in result.output + assert "Response word 2 = 64 (0x40)" in result.output + assert "Output data size/value(s) is(are):" in result.output + assert "Key Blob size: 48 (0x30)" in result.output + assert "ECDSA Puk size: 64 (0x40)" in result.output + + +def test_tp_store_key(caplog): + cmd = "-p super-com trust-provisioning hsm_store_key 5 1 0x2000B000 32 0x2000C000 48" + result = run_blhost_proxy(caplog, cmd) + assert "Response status = 0 (0x0) Success." in result.output + assert "Response word 1 = 269484129 (0x10100061)" in result.output + assert "Response word 2 = 48 (0x30)" in result.output + assert "Output data size/value(s) is(are):" in result.output + assert "Key Header: 269484129 (0x10100061)" in result.output + assert "Key Blob size: 48 (0x30)" in result.output + + +def test_tp_hsm_enc_blk(caplog): + cmd = "-p super-com trust-provisioning hsm_enc_blk 0x2000A000 48 16 0x2000C000 60 1 0x2000D000 256" + result = run_blhost_proxy(caplog, cmd) + assert "Response status = 0 (0x0) Success." in result.output + + +def test_tp_hsm_enc_sign(caplog): + cmd = "-p super-com trust-provisioning hsm_enc_sign 0x20008000 48 0x2000F000 220 0x20010000 64" + result = run_blhost_proxy(caplog, cmd) + assert "Response status = 0 (0x0) Success." in result.output + assert "Response word 1 = 64 (0x40)" in result.output + assert "Output data size/value(s) is(are):" in result.output + assert "Signature size: 64 (0x40)" in result.output + + +def test_tp_oem_gen_master_share(caplog): + cmd = "-p super-com trust-provisioning oem_gen_master_share 0x20008000 0x10 0x20009000 0x1000 0x2000A000 0x1000 0x2000B000 0x1000" + result = run_blhost_proxy(caplog, cmd) + assert "Response status = 0 (0x0) Success." in result.output + assert "Response word 1 = 48 (0x30)" in result.output + assert "Response word 2 = 64 (0x40)" in result.output + assert "Response word 3 = 64 (0x40)" in result.output + assert "Output data size/value(s) is(are):" in result.output + assert "OEM Share size: 48 (0x30)" in result.output + assert "OEM Master Share size: 64 (0x40)" in result.output + assert "Cust Cert Puk size: 64 (0x40)" in result.output + + +def test_tp_hsm_enc_blk(caplog): + cmd = "-p super-com trust-provisioning oem_set_master_share 0x20008000 16 0x20009000 64" + result = run_blhost_proxy(caplog, cmd) + assert "Response status = 0 (0x0) Success." in result.output + + +def test_oem_get_cust_cert_dice_puk(caplog): + cmd = ( + "-p super-com trust-provisioning oem_get_cust_cert_dice_puk 0x30015000 0x20 0x30016000 0x40" + ) + result = run_blhost_proxy(caplog, cmd) + assert "Response status = 0 (0x0) Success." in result.output + assert "Response word 1 = 64 (0x40)" in result.output + assert "Output data size/value(s) is(are):" in result.output + assert "Cust Cert Dice Puk size: 64 (0x40)" in result.output diff --git a/tests/mboot/blhost/test_blhost_utils.py b/tests/mboot/blhost/test_blhost_utils.py index f4758a8d..712a97ae 100644 --- a/tests/mboot/blhost/test_blhost_utils.py +++ b/tests/mboot/blhost/test_blhost_utils.py @@ -17,6 +17,9 @@ parse_image_file, parse_key_prov_key_type, parse_property_tag, + parse_trust_prov_key_type, + parse_trust_prov_oem_key_type, + parse_trust_prov_wrapping_key_type, ) @@ -154,3 +157,58 @@ def test_parse_image_file_aligned_sizes(path, aligned_sizes: List[Tuple[int, int for segment, expected in zip(result, aligned_sizes): assert segment.aligned_start == expected[0] assert segment.aligned_length == expected[1] + + +@pytest.mark.parametrize( + "input_value, expected_output", + [ + ("MFWISK", 50085), + ("0xC3A5", 50085), + ("0xc3a5", 50085), + ("MFWENCK", 42435), + ("0xA5C3", 42435), + ("GENSIGNK", 23100), + ("0x5A3C", 23100), + ("GETCUSTMKSK", 15450), + ("0x3C5A", 15450), + ], +) +def test_parse_tp_prov_oem_key_type(input_value, expected_output): + actual = parse_trust_prov_oem_key_type(input_value) + assert actual == expected_output + + +@pytest.mark.parametrize( + "input_value, expected_output", + [ + ("1", 1), + ("CKDFK", 1), + ("2", 2), + ("HKDFK", 2), + ("3", 3), + ("HMACK", 3), + ("4", 4), + ("CMACK", 4), + ("5", 5), + ("AESK", 5), + ("6", 6), + ("KUOK", 6), + ], +) +def test_parse_tp_prov_key_type(input_value, expected_output): + actual = parse_trust_prov_key_type(input_value) + assert actual == expected_output + + +@pytest.mark.parametrize( + "input_value, expected_output", + [ + ("0x10", 16), + ("INT_SK", 16), + ("0x11", 17), + ("EXT_SK", 17), + ], +) +def test_parse_tp_prov_wrapping_key_type(input_value, expected_output): + actual = parse_trust_prov_wrapping_key_type(input_value) + assert actual == expected_output diff --git a/tests/mboot/conftest.py b/tests/mboot/conftest.py index ec089a6b..5435e2d5 100644 --- a/tests/mboot/conftest.py +++ b/tests/mboot/conftest.py @@ -5,9 +5,12 @@ # # SPDX-License-Identifier: BSD-3-Clause -import pytest from os import path -from spsdk.mboot import scan_usb, McuBoot + +import pytest + +from spsdk.mboot import McuBoot, scan_usb + from .device_config import DevConfig from .virtual_device import VirtualDevice diff --git a/tests/mboot/device_config.py b/tests/mboot/device_config.py index f3d8513b..46c1bb16 100644 --- a/tests/mboot/device_config.py +++ b/tests/mboot/device_config.py @@ -6,8 +6,9 @@ # SPDX-License-Identifier: BSD-3-Clause import yaml -from voluptuous import Schema, Optional, Required, All, Any, ALLOW_EXTRA -from spsdk.mboot import CommandTag, PropertyTag, PeripheryTag, ExtMemId, Version +from voluptuous import ALLOW_EXTRA, All, Any, Optional, Required, Schema + +from spsdk.mboot import CommandTag, ExtMemId, PeripheryTag, PropertyTag, Version ######################################################################################################################## # Validator schema for configuration file diff --git a/tests/mboot/test_commands.py b/tests/mboot/test_commands.py index d3569cb9..765187c7 100644 --- a/tests/mboot/test_commands.py +++ b/tests/mboot/test_commands.py @@ -6,17 +6,20 @@ # SPDX-License-Identifier: BSD-3-Clause import pytest + +from spsdk import SPSDKError from spsdk.mboot.commands import ( - ResponseTag, - CommandTag, CmdHeader, CmdPacket, CmdResponse, + CommandTag, + FlashReadOnceResponse, + FlashReadResourceResponse, GenericResponse, GetPropertyResponse, ReadMemoryResponse, - FlashReadOnceResponse, - FlashReadResourceResponse, + ResponseTag, + TrustProvisioningResponse, parse_cmd_response, ) @@ -104,3 +107,14 @@ def test_flash_read_resource_response_class(): assert response.status == 0 assert response.length == 4 assert response.info() + + +def test_tp_hsm_gen_key_response_class(): + response = parse_cmd_response( + b"\xb6\x00\x00\x03\x00\x00\x00\x00\x30\x00\x00\x00\x40\x00\x00\x00" + ) + assert isinstance(response, TrustProvisioningResponse) + assert response.header == CmdHeader(tag=0xB6, flags=0x00, reserved=0, params_count=3) + assert response.status == 0 + assert response.values == [48, 64] + assert response.info() diff --git a/tests/mboot/test_mboot_api.py b/tests/mboot/test_mboot_api.py index 4c8ea239..5f0cc881 100644 --- a/tests/mboot/test_mboot_api.py +++ b/tests/mboot/test_mboot_api.py @@ -7,6 +7,7 @@ import pytest +from spsdk import SPSDKError from spsdk.mboot.error_codes import StatusCode from spsdk.mboot.exceptions import McuBootCommandError, McuBootConnectionError, McuBootError from spsdk.mboot.mcuboot import ( @@ -17,6 +18,7 @@ PropertyTag, StatusCode, ) +from tests.mcu_examples.test_rt5xx import write_shadow_regis def test_class(mcuboot, target, config): @@ -82,8 +84,8 @@ def test_cmd_fill_memory(mcuboot, target): def test_cmd_flash_security_disable(mcuboot, target): assert mcuboot.flash_security_disable(b"12345678") - with pytest.raises(ValueError): - mcuboot.flash_security_disable(b"123456789") + with pytest.raises(McuBootError, match="Backdoor key must by 8 bytes long"): + mcuboot.flash_security_disable(backdoor_key=b"123456789") def test_cmd_get_property(mcuboot, target, config): @@ -142,10 +144,10 @@ def test_cmd_reset_reopen(mcuboot, target): def test_cmd_reset_reopen_disabled(mcuboot, target): """Test reset command with reopen disabled""" mcuboot.reopen = False - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): mcuboot.reset(reopen=True) mcuboot.open() # ensure device is again opened for communication - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): mcuboot.reset() mcuboot.open() # ensure device is again opened for communication @@ -175,11 +177,21 @@ def test_cmd_flash_read_once(mcuboot, target): # assert isinstance(value, bytes) +def test_cmd_flash_read_once(mcuboot): + with pytest.raises(SPSDKError, match="Invalid count of bytes. Must be 4 or 8"): + mcuboot.flash_read_once(index=0, count=3) + + def test_cmd_flash_program_once(mcuboot, target): assert not mcuboot.flash_program_once(0, b"\x00\x00\x00\x00") assert mcuboot.status_code == StatusCode.UNKNOWN_COMMAND +def test_cmd_flash_program_once_invalid_data(mcuboot): + with pytest.raises(SPSDKError, match="Invalid length of data. Must be aligned to 4 or 8 bytes"): + mcuboot.flash_program_once(index=0, data=bytes(9)) + + def test_cmd_flash_read_resource(mcuboot, target): value = mcuboot.flash_read_resource(0, 100) assert mcuboot.status_code == StatusCode.UNKNOWN_COMMAND diff --git a/tests/mboot/test_mboot_exceptions.py b/tests/mboot/test_mboot_exceptions.py index c13e1650..0874a49f 100644 --- a/tests/mboot/test_mboot_exceptions.py +++ b/tests/mboot/test_mboot_exceptions.py @@ -6,7 +6,7 @@ # SPDX-License-Identifier: BSD-3-Clause from spsdk import SPSDKError -from spsdk.mboot import McuBootError, McuBootCommandError, McuBootConnectionError +from spsdk.mboot import McuBootCommandError, McuBootConnectionError, McuBootError def raise_and_catch(raising_exc, catching_exc) -> bool: diff --git a/tests/mboot/test_mboot_memories.py b/tests/mboot/test_mboot_memories.py index 76913183..6eaee013 100644 --- a/tests/mboot/test_mboot_memories.py +++ b/tests/mboot/test_mboot_memories.py @@ -4,10 +4,7 @@ # Copyright 2021 NXP # # SPDX-License-Identifier: BSD-3-Clause - -import pytest -from spsdk.image.misc import size_fmt -from spsdk.mboot.memories import FlashRegion, RamRegion, ExtMemRegion, MemoryRegion +from spsdk.mboot.memories import ExtMemRegion, FlashRegion, RamRegion def test_ram_region(): diff --git a/tests/mboot/test_properties.py b/tests/mboot/test_properties.py index 26402f3d..e0791a44 100644 --- a/tests/mboot/test_properties.py +++ b/tests/mboot/test_properties.py @@ -8,19 +8,13 @@ import pytest from spsdk.mboot.commands import CommandTag +from spsdk.mboot.exceptions import McuBootError from spsdk.mboot.properties import ( - AvailableCommandsValue, - AvailablePeripheralsValue, BoolValue, DeviceUidValue, EnumValue, - ExternalMemoryAttributesValue, - FlashReadMargin, IntValue, - IrqNotifierPinValue, - PfrKeystoreUpdateOpt, PropertyTag, - ReservedRegionsValue, Version, VersionValue, parse_property_value, @@ -44,7 +38,7 @@ def test_version_class(): assert version != Version(0x00000102) assert str(version) assert repr(version) - with pytest.raises(TypeError): + with pytest.raises(McuBootError): _ = Version(0.5) diff --git a/tests/mboot/virtual_device.py b/tests/mboot/virtual_device.py index d75b141d..9a931271 100644 --- a/tests/mboot/virtual_device.py +++ b/tests/mboot/virtual_device.py @@ -8,19 +8,18 @@ import logging from struct import pack -import pytest - from spsdk.mboot.commands import ( - CommandTag, CmdPacket, + CommandTag, + KeyProvOperation, ResponseTag, parse_cmd_response, - KeyProvOperation, ) from spsdk.mboot.error_codes import StatusCode +from spsdk.mboot.exceptions import McuBootDataAbortError from spsdk.mboot.interfaces import Interface from spsdk.mboot.memories import ExtMemId -from spsdk.mboot.exceptions import McuBootDataAbortError + ######################################################################################################################## # Helper functions diff --git a/tests/mcu_examples/test_rt10xx.py b/tests/mcu_examples/test_rt10xx.py index b52c0b3a..af4e0801 100644 --- a/tests/mcu_examples/test_rt10xx.py +++ b/tests/mcu_examples/test_rt10xx.py @@ -16,38 +16,48 @@ import pytest from cryptography import x509 from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives.serialization import PrivateFormat, NoEncryption -from cryptography.hazmat.primitives.serialization import load_pem_private_key -from spsdk.crypto import generate_certificate, save_crypto_item, Encoding -from spsdk.crypto import generate_rsa_private_key, generate_rsa_public_key, save_rsa_private_key +from cryptography.hazmat.primitives.serialization import ( + NoEncryption, + PrivateFormat, + load_pem_private_key, +) + +from spsdk.crypto import ( + Encoding, + generate_certificate, + generate_rsa_private_key, + generate_rsa_public_key, + save_crypto_item, + save_rsa_private_key, +) from spsdk.image import ( - BootImgRT, - SrkTable, - SrkItem, - PaddingFCB, - FlexSPIConfBlockFCB, MAC, - hab_audit_log, - BeeRegionHeader, + BeeFacRegion, BeeKIB, BeeProtectRegionBlock, - BeeFacRegion, + BeeRegionHeader, + BootImgRT, + FlexSPIConfBlockFCB, + PaddingFCB, + SrkItem, + SrkTable, + hab_audit_log, ) -from spsdk.mboot import McuBoot, ExtMemId, PropertyTag +from spsdk.mboot import ExtMemId, McuBoot, PropertyTag from spsdk.mboot import scan_usb as mboot_scan_usb from spsdk.sbfile.sb1 import ( - SecureBootV1, BootSectionV1, - SecureBootFlagsV1, - CmdFill, - CmdMemEnable, CmdErase, + CmdFill, CmdLoad, + CmdMemEnable, + SecureBootFlagsV1, + SecureBootV1, ) -from spsdk.sdp import SDP, ResponseValue, StatusCode, SdpCommandError +from spsdk.sdp import SDP, ResponseValue, SdpCommandError, StatusCode from spsdk.sdp import scan_usb as sdp_scan_usb from spsdk.utils.easy_enum import Enum -from spsdk.utils.misc import load_binary, DebugInfo, align, align_block +from spsdk.utils.misc import DebugInfo, align, align_block, load_binary from tests.misc import compare_bin_files, write_dbg_log # ############################## EXECUTION PARAMETERS ############################## diff --git a/tests/mcu_examples/test_rt5xx.py b/tests/mcu_examples/test_rt5xx.py index fbf75eab..21791acd 100644 --- a/tests/mcu_examples/test_rt5xx.py +++ b/tests/mcu_examples/test_rt5xx.py @@ -14,13 +14,11 @@ import pytest from bitstring import BitArray -from spsdk.image import KeySourceType, KeyStore -from spsdk.image import MasterBootImage, MasterBootImageType -from spsdk.image import TrustZone -from spsdk.mboot import McuBoot, scan_usb, PropertyTag, ExtMemId, KeyProvUserKeyType +from spsdk.image import KeySourceType, KeyStore, MasterBootImage, MasterBootImageType, TrustZone +from spsdk.mboot import ExtMemId, KeyProvUserKeyType, McuBoot, PropertyTag, scan_usb +from spsdk.sbfile.commands import CmdErase, CmdFill, CmdLoad, CmdMemEnable from spsdk.sbfile.images import BootImageV21, BootSectionV2, CertBlockV2, SBV2xAdvancedParams -from spsdk.sbfile.commands import CmdErase, CmdLoad, CmdFill, CmdMemEnable -from spsdk.utils.crypto import Otfad, KeyBlob, Certificate +from spsdk.utils.crypto import Certificate, KeyBlob, Otfad from spsdk.utils.misc import align_block, load_binary from tests.misc import compare_bin_files, write_dbg_log @@ -104,7 +102,7 @@ def write_sb(data_dir: str, sb_file_name: str, bin_data: bytes, key_store: KeySt ####################################################################################################################### -def write_shadow_regs(data_dir: str, writes: List[Tuple[int, int]]) -> None: +def write_shadow_regis(data_dir: str, writes: List[Tuple[int, int]]) -> None: """Write shadow registers: - prepares and burns into FLASH a binary application for initialization of shadow registers - the application is launched using "execute" command @@ -636,7 +634,7 @@ def test_sb_unsigned_keystore(data_dir: str, subdir: str, image_name: str) -> No :param image_name: file name of the unsigned image WITHOUT file extension """ if not TEST_IMG_CONTENT: - write_shadow_regs(data_dir, [(0x40130194, 0x00000080)]) # BOOT_CFG[5]: USE_PUF = 1 + write_shadow_regis(data_dir, [(0x40130194, 0x00000080)]) # BOOT_CFG[5]: USE_PUF = 1 with open(os.path.join(data_dir, KEYSTORE_SUBDIR, "SBkek_PUF.txt"), "r") as f: sbkek_str = f.readline() @@ -655,6 +653,7 @@ def test_sb_unsigned_keystore(data_dir: str, subdir: str, image_name: str) -> No build_number=1, # parameters fixed for test only (to have always same output), do not use in production advanced_params=adv_params, + flags=0x0008, ) # certificate + private key @@ -704,7 +703,7 @@ def test_sb_unsigned_otp(data_dir: str, subdir: str, image_name: str) -> None: :param data_dir: absolute path of the directory with data files for the test :param image_name: file name of the unsigned image WITHOUT file extension """ - write_shadow_regs( + write_shadow_regis( data_dir, [ (0x40130194, 0x00000000), # BOOT_CFG[5]: USE_OTP = 0 @@ -737,6 +736,7 @@ def test_sb_unsigned_otp(data_dir: str, subdir: str, image_name: str) -> None: build_number=1, # parameters fixed for test only (to have always same output), do not use in production advanced_params=adv_params, + flags=0x0008, ) # certificate + private key @@ -787,7 +787,7 @@ def test_sb_signed_encr_keystore(data_dir: str, subdir: str, image_name: str) -> :param image_name: file name of the signed or encrypted image WITHOUT file extension """ if not TEST_IMG_CONTENT: - write_shadow_regs( + write_shadow_regis( data_dir, [ (0x40130194, 0x00000080), # BOOT_CFG[5]: USE_PUF = 1 @@ -816,6 +816,7 @@ def test_sb_signed_encr_keystore(data_dir: str, subdir: str, image_name: str) -> build_number=1, # parameters fixed for test only (to have always same output), do not use in production advanced_params=adv_params, + flags=0x0008, ) # certificate + private key @@ -867,7 +868,7 @@ def test_sb_otfad_keystore(data_dir: str, subdir: str, image_name: str, secure: """ if not TEST_IMG_CONTENT: secure_boot_en = 0x900000 if secure else 0 # BOOT_CFG[0]: SECURE_BOOT_EN=? - write_shadow_regs( + write_shadow_regis( data_dir, [ (0x40130194, 0x00000080), # BOOT_CFG[5]: USE_PUF = 1 @@ -896,6 +897,7 @@ def test_sb_otfad_keystore(data_dir: str, subdir: str, image_name: str, secure: build_number=1, # parameters fixed for test only (to have always same output), do not use in production advanced_params=adv_params, + flags=0x0008, ) # certificate + private key @@ -977,7 +979,7 @@ def test_sb_otfad_otp(data_dir: str, subdir: str, image_name: str, secure: bool) """ if not TEST_IMG_CONTENT: secure_boot_en = 0x900000 if secure else 0 # BOOT_CFG[0]: SECURE_BOOT_EN=? - write_shadow_regs( + write_shadow_regis( data_dir, [ (0x401301A8, 0x00001000), # OTFAD CFG @@ -988,7 +990,7 @@ def test_sb_otfad_otp(data_dir: str, subdir: str, image_name: str, secure: bool) (0x401301BC, 0x78DDDDDD), ], ) - write_shadow_regs( + write_shadow_regis( data_dir, [ # MASTER KEY - 000102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff @@ -1027,6 +1029,7 @@ def test_sb_otfad_otp(data_dir: str, subdir: str, image_name: str, secure: bool) build_number=1, # parameters fixed for test only (to have always same output), do not use in production advanced_params=adv_params, + flags=0x0008, ) # certificate + private key diff --git a/tests/nxpkeygen/data/2048b-rsa-example-cert.der b/tests/nxpkeygen/data/2048b-rsa-example-cert.der new file mode 100644 index 0000000000000000000000000000000000000000..6e6cc218e43bfc6cd21ad0b21fe5ee94cc13d559 GIT binary patch literal 847 zcmXqLV)iy@VlrO9%*4pV#Kg>Lz{|#|)#lOmotKf3o0Y*J*pSD7i;X#yg_(!h-_O;M z+kg|qV-scy@PTpId6>L|40#Q>K{6aXEWSPg-Y{*PJj_o1Aut{n4|9OGzk!@MuaTjF zrGcTLk+G?vSrm|K3gnnWxdv%4^*~EQ!u?_PKrDsY!_LFv&ZY@-nlZPcZ#6KKn z4$xjtCtpK810IkQ1$kJF4NVP<4VoC0kp0WZ%D~*j$j<;2=VEGNWMo)!`h57>)9<+U zcC@SzvFeFQPhc6=z5sz}F0)Ik`${iwms(?giCg?M z6Ehs5PP&Sm->u6jxsY~yen&2;$ z1#-`_(w)o?ZgWaozsFB%N%XpaqsLw?+OxVhC&v5ZL7O9$t9(zUOY(nOC*ddCec%Ic zpGVKtJ$n0A0kV6-UOT9M$b4P>$};xxge591YJ7*; zWjr-m=42~tHrX1U{r~S?$o;)@{Zsp6T@ugf{adUPH6d~h#{`b+r;Sq$B^OuQyt%nA z>dxvX#)V2TP11$ literal 0 HcmV?d00001 diff --git a/tests/dat/data/dck.pub b/tests/nxpkeygen/data/dck.pub similarity index 100% rename from tests/dat/data/dck.pub rename to tests/nxpkeygen/data/dck.pub diff --git a/tests/nxpkeygen/data/dck_rsa2048_rot_meta_cert.yml b/tests/nxpkeygen/data/dck_rsa2048_rot_meta_cert.yml new file mode 100644 index 00000000..ee230d46 --- /dev/null +++ b/tests/nxpkeygen/data/dck_rsa2048_rot_meta_cert.yml @@ -0,0 +1,10 @@ +socc: 0x0001 +uuid: "E004090E6BDD2155BBCE9E0665805BE3" +cc_socu: 0x03FF +cc_vu: 0x5678 +cc_beacon: 0 +rot_meta: +- 2048b-rsa-example-cert.der +rot_id: 0 +rotk: new_rotk_2048.pem +dck: new_dck_2048.pub \ No newline at end of file diff --git a/tests/dat/data/elf2sb_config.json b/tests/nxpkeygen/data/elf2sb_config.json similarity index 100% rename from tests/dat/data/elf2sb_config.json rename to tests/nxpkeygen/data/elf2sb_config.json diff --git a/tests/dat/data/k0_cert0_2048.pem b/tests/nxpkeygen/data/k0_cert0_2048.pem similarity index 100% rename from tests/dat/data/k0_cert0_2048.pem rename to tests/nxpkeygen/data/k0_cert0_2048.pem diff --git a/tests/nxpkeygen/data/n4a_dck_secp384r1.cert b/tests/nxpkeygen/data/n4a_dck_secp384r1.cert new file mode 100644 index 0000000000000000000000000000000000000000..210f336c3aae0f9ca9e211fb21a63fb32c6e2b3d GIT binary patch literal 520 zcmZQ#U}Runzz-Z47#f@p?C!q5VEZM`m5ohuaR$88jkjg6J)Ktb+*x(m;(PPFY&T!L z>AKfc{qbeLge?YGuL8Z>56m3TxAe@R z)9;u(>wHpg`c2%r>-8NkrMj%kQU%Y{JaTp!1Wx|1Z7I(eth8<6yuTN_+UK3E`X_um zZei)%luXVgynp8DX9eUi<}A?&QPz5}u{u9M;hL#=O|o_Ql+=hP4?OoVeud5^Wc~uy?h~zr?kySz1+{ F006-$^LhXP literal 0 HcmV?d00001 diff --git a/tests/nxpkeygen/data/new_dck_2048.pem b/tests/nxpkeygen/data/new_dck_2048.pem new file mode 100644 index 00000000..1f2275b6 --- /dev/null +++ b/tests/nxpkeygen/data/new_dck_2048.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCveEXNgHpqeZl/ +xYDN4l6Re8nMH2ZGeuewrlKmSIDGu0s+Auuc6fZKeOktZuXS4rxDV1xZqBGoXcuE +5hPTGFKyE534KpKyiLHHOMBgvS+cbgTpwIfWQ0Tyh/NnwSY105votsfW4N4bumU/ +A5vtY0ClArqxBhExlG5ui/a67B4+RsOx5KhN0GAFR2PamMLTuYwZU76gDYtNFiLa +LzYVY0GIrAvUmCuxMZG2sa1oEEwafCF5KfTNt+HkIfBRtP/EBeLnOgEqQBToLAUD +evHibpQIrkvmvm8jzRkY+YFBCH/t3KJHeB5PTX+3PNdGaLWMsDQNpedRd5D5Fkoy +bFnr1Bk5AgMBAAECggEAIKzY8se2BBXYy75PmgdJbPYMQgKm3QEOHAyaP3TI5jkC +be3FSlcXEg8p/NZofbjTLgcan2d4wk2VFI5zqp+g5qBYjln/L1fdIygiSuFv+EVU +jOLZaMDmGfPPE8cYAnYBVniTBiSht7AiInXdl3sgEppLgaOLmBv35IYT0AMgLqBX +wxRx34cTgVCLKITxpiP6UC/pEORo6IWtxk9ozEdZxiZ8dLVv1hf6Soi1Is2+jVge +J6foJazhco94W8TUgBhYtdj6/j/9OqBbJgTqsvaThIHA3GOvbkTJEVGLBXkpmQNk +yMG84DsBEWFshFeiwb2VPsIwDslux2ZtuLhxqSHIwQKBgQDhYSHQhUGm3ltSJMmG +ZAr/b0YCdmd+F2vP9CuHrV1SzOgpm0lDufIkLD/jGGiJjgzaUzjphtwJQbPBfX+b +kMOoeiaKamuR6H5yisWddLHz7faMITMOOPYtHwHObNcAq0tQkEx1c82fam21PqmH +aR7jUsjlpa5OizVTQmUVQsHupQKBgQDHTz8WzFA7nn0M57tjeOCh46aryLSHnDqt +8A7xidWknLbSzUfDOXRH+1H/GU6fU6hYWpeE/hcE3XtLioNrUBrb5p8mt+FcQ0YT +49soINnw15DJoLKmml5HDiJmaQpVAluqO/E55CT6TZiNus0LNG0BQ07PI311bVmZ +ZjUzAcWwBQKBgQDIzDZJRoAggJkSDW5yyq01uNjCFuXL5GgMpXkm72FnBOKPNyfG +LGO8sKPW24c/joyK83yQKrPdl2jkqcBIRGjdsL6nsXrXSzFlMZgw2ag2MLvpSybm +s/84YhjE5hmEIhj+1SEZv6viLPtDcGmwGAsJ81bDq6IpnkNLQ6s1jv2jdQKBgQCf +1QE2+ynsZbDKxJsVrj4WFUX3xMBhb/vuuPCC6pIVrEdAzbaa7Fw/zV94P4c5ZhE+ +qYeQ82LqV+MkNTQmvtqLNmqy1gs3vgtYsAV0LMQZzAUkbGZYDLtGKCOWFO5gNb/k +yKUuU7MDt257Wi5MqgIwXRW6Ly/RF5we1EQdX3J1MQKBgHX+G1at8gntriZR5qKW +P4Mt/r54/8DU0445SjJDSYZ0N3zv2cs0wj1/93wvn7+oIlPYG57vod9broSSW1vL +yOgMrFiIOGSfR1OhxpGoig9Yf+S5o2jSRYPRzMCmPhGC6VS+Un/Ejak6ugZu2fDH +NhnGoWUjaT+4VIjfIgFD1PPq +-----END PRIVATE KEY----- diff --git a/tests/nxpkeygen/data/new_dck_2048.pub b/tests/nxpkeygen/data/new_dck_2048.pub new file mode 100644 index 00000000..95fcf18e --- /dev/null +++ b/tests/nxpkeygen/data/new_dck_2048.pub @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3hFzYB6anmZf8WAzeJe +kXvJzB9mRnrnsK5SpkiAxrtLPgLrnOn2SnjpLWbl0uK8Q1dcWagRqF3LhOYT0xhS +shOd+CqSsoixxzjAYL0vnG4E6cCH1kNE8ofzZ8EmNdOb6LbH1uDeG7plPwOb7WNA +pQK6sQYRMZRubov2uuwePkbDseSoTdBgBUdj2pjC07mMGVO+oA2LTRYi2i82FWNB +iKwL1JgrsTGRtrGtaBBMGnwheSn0zbfh5CHwUbT/xAXi5zoBKkAU6CwFA3rx4m6U +CK5L5r5vI80ZGPmBQQh/7dyiR3geT01/tzzXRmi1jLA0DaXnUXeQ+RZKMmxZ69QZ +OQIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/dat/data/new_dck_rsa2048.cert b/tests/nxpkeygen/data/new_dck_rsa2048.cert similarity index 100% rename from tests/dat/data/new_dck_rsa2048.cert rename to tests/nxpkeygen/data/new_dck_rsa2048.cert diff --git a/tests/nxpkeygen/data/new_dck_rsa2048.yml b/tests/nxpkeygen/data/new_dck_rsa2048.yml new file mode 100644 index 00000000..8402ef3f --- /dev/null +++ b/tests/nxpkeygen/data/new_dck_rsa2048.yml @@ -0,0 +1,10 @@ +socc: 0x0001 +uuid: "E004090E6BDD2155BBCE9E0665805BE3" +cc_socu: 0x03FF +cc_vu: 0x5678 +cc_beacon: 0 +rot_meta: +- new_rotk_2048.pub +rot_id: 0 +rotk: new_rotk_2048.pem +dck: new_dck_2048.pub \ No newline at end of file diff --git a/tests/nxpkeygen/data/new_dck_rsa2048_invalid.yml b/tests/nxpkeygen/data/new_dck_rsa2048_invalid.yml new file mode 100644 index 00000000..11292dac --- /dev/null +++ b/tests/nxpkeygen/data/new_dck_rsa2048_invalid.yml @@ -0,0 +1,10 @@ +socc: 0x0001 +uuid: "E004090E6BDD2155BBCE9E0665805BE3" +cc_socu: 0x03FF +cc_vu: 0x5678 +cc_beacon: 0 +rot_meta: +- new_rotk_2048.pub +rot_id: 0 +rotk: new_rotk_2048.pem +dck: new.pub diff --git a/tests/nxpkeygen/data/new_dck_secp256.yml b/tests/nxpkeygen/data/new_dck_secp256.yml new file mode 100644 index 00000000..57706c70 --- /dev/null +++ b/tests/nxpkeygen/data/new_dck_secp256.yml @@ -0,0 +1,10 @@ +socc: 0x0001 +uuid: "E004090E6BDD2155BBCE9E0665805BE3" +cc_socu: 0x03FF +cc_vu: 0x5678 +cc_beacon: 0 +rot_meta: +- new_rotk_secp256r1.pub +rot_id: 0 +rotk: new_rotk_secp256r1.pem +dck: new_dck_secp256r1.pub \ No newline at end of file diff --git a/tests/nxpkeygen/data/new_dck_secp256_N4A.yml b/tests/nxpkeygen/data/new_dck_secp256_N4A.yml new file mode 100644 index 00000000..09431db7 --- /dev/null +++ b/tests/nxpkeygen/data/new_dck_secp256_N4A.yml @@ -0,0 +1,10 @@ +socc: 0x0004 +uuid: "E004090E6BDD2155BBCE9E0665805BE3" +cc_socu: 0x03FF +cc_vu: 0x5678 +cc_beacon: 0 +rot_meta: +- new_rotk_secp256r1.pub +rot_id: 0 +rotk: new_rotk_secp256r1.pem +dck: new_dck_secp256r1.pub \ No newline at end of file diff --git a/tests/dat/data/new_dck_secp256_N4A_not_empty.yml b/tests/nxpkeygen/data/new_dck_secp256_N4A_not_empty.yml similarity index 100% rename from tests/dat/data/new_dck_secp256_N4A_not_empty.yml rename to tests/nxpkeygen/data/new_dck_secp256_N4A_not_empty.yml diff --git a/tests/dat/data/new_dck_secp256r1.cert b/tests/nxpkeygen/data/new_dck_secp256r1.cert similarity index 100% rename from tests/dat/data/new_dck_secp256r1.cert rename to tests/nxpkeygen/data/new_dck_secp256r1.cert diff --git a/tests/nxpkeygen/data/new_dck_secp256r1.pem b/tests/nxpkeygen/data/new_dck_secp256r1.pem new file mode 100644 index 00000000..ffd82fb7 --- /dev/null +++ b/tests/nxpkeygen/data/new_dck_secp256r1.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgvuCxv099CNuBTGyl +Mz90wlgai2IjnMfNWj4xO7TvNtChRANCAARO6b1gIIh2d+7rMk27wsuqfnbZM82W +uVz0TfdceL302SF2CvzkPNzVJVUTa7KrGaP64QKRN46AV8PATJbD1Qp+ +-----END PRIVATE KEY----- diff --git a/tests/nxpkeygen/data/new_dck_secp256r1.pub b/tests/nxpkeygen/data/new_dck_secp256r1.pub new file mode 100644 index 00000000..ceaa3c73 --- /dev/null +++ b/tests/nxpkeygen/data/new_dck_secp256r1.pub @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETum9YCCIdnfu6zJNu8LLqn522TPN +lrlc9E33XHi99Nkhdgr85Dzc1SVVE2uyqxmj+uECkTeOgFfDwEyWw9UKfg== +-----END PUBLIC KEY----- diff --git a/tests/nxpkeygen/data/new_dck_secp384_N4A.yml b/tests/nxpkeygen/data/new_dck_secp384_N4A.yml new file mode 100644 index 00000000..5ec2d1e5 --- /dev/null +++ b/tests/nxpkeygen/data/new_dck_secp384_N4A.yml @@ -0,0 +1,10 @@ +socc: 0x0004 +uuid: "E004090E6BDD2155BBCE9E0665805BE3" +cc_socu: 0x03FF +cc_vu: 0x5678 +cc_beacon: 0 +rot_meta: +- new_rotk_secp384r1.pub +rot_id: 0 +rotk: new_rotk_secp384r1.pem +dck: new_dck_secp384r1.pub \ No newline at end of file diff --git a/tests/dat/data/new_dck_secp384_N4A_not_empty.yml b/tests/nxpkeygen/data/new_dck_secp384_N4A_not_empty.yml similarity index 100% rename from tests/dat/data/new_dck_secp384_N4A_not_empty.yml rename to tests/nxpkeygen/data/new_dck_secp384_N4A_not_empty.yml diff --git a/tests/dat/data/new_dck_secp384r1 b/tests/nxpkeygen/data/new_dck_secp384r1 similarity index 100% rename from tests/dat/data/new_dck_secp384r1 rename to tests/nxpkeygen/data/new_dck_secp384r1 diff --git a/tests/nxpkeygen/data/new_dck_secp384r1.pem b/tests/nxpkeygen/data/new_dck_secp384r1.pem new file mode 100644 index 00000000..00b968e7 --- /dev/null +++ b/tests/nxpkeygen/data/new_dck_secp384r1.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAFjPnMc9ARWbzpPECM +CnaCshxdkGjbXWLyVMxiDmuZw6VV3ReHR2k1Sy23V9n4WbWhZANiAAQn2M55QAr4 +p3jtloRyi0RTFbcB4KKj6SsfKmYHlWeTET1wFg6ENocgcPXxdu3d/e8sqy1yqnCZ +oApzu7mcY6wjnvfkPyPiMSrL/focXTB4NEaSu/lrDvI7zZcrT3PrG+E= +-----END PRIVATE KEY----- diff --git a/tests/nxpkeygen/data/new_dck_secp384r1.pub b/tests/nxpkeygen/data/new_dck_secp384r1.pub new file mode 100644 index 00000000..d9be9820 --- /dev/null +++ b/tests/nxpkeygen/data/new_dck_secp384r1.pub @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAENKDAnzCY1LQC2wVuThmeKhq4qo0JBvTm +ELDXVWRh+q5jsXr2EStq6+UOYN4y4uqAa4+ep81rapSmbV5C5k70oDr+ngSRBPOR +Hb8LGIjeN9DX37aNaaxLsgKWe50wwBbw +-----END PUBLIC KEY----- diff --git a/tests/nxpkeygen/data/new_rotk_2048.pem b/tests/nxpkeygen/data/new_rotk_2048.pem new file mode 100644 index 00000000..58cd4abb --- /dev/null +++ b/tests/nxpkeygen/data/new_rotk_2048.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDPxPRrqFyiKTwK +SqVHmu79P930T8P+AOsrUFcLvr/wgawPfdQWIvHYWLEUZQQCi1JcXLBKt84+dAlB +UsQEmCu7BMJZZsiAUQT9/MXRd8wKail/ChEWTJjvKaaRY3M0QHhMaM/zBHdx1APZ +2gQlou6Ma6wZWcwNkI1ikT3dGNhP53gxWPJWOkv8LX/ZMMjM0AkocwDRWcm45HTQ +zIk4EyXQ5GhULdhcLI7g0U/1nwfP/c7Duos0oKkL+TKXHcIfgQNskddKvmCkdcyo +3jeaOs69gsvlo4X2bTtAeMhFhFl2e58YdqIir0PgdNrpfYVLR1T5yfZFR7mNdXdf +jkV8umWzAgMBAAECggEBAI/3xfYjf6zOLdBj0QtBDIFnlAEKlKCZOluVG/wDfTTG +ZdeGNUPxiuuC6ZFcPd8Vc0iU4YOkwcD83rzpAPK+Du9UaOdSFXT0ryTf1qNDA5vj +3EUvUI/HG+H9VnJAG0AFvFYV+qJGYg35/4EhVOZPdETRBzu5wW9UzuOSCO//m0Qh +vLeNsbiG8Jwj4eQ3FXrNtbeGXHIW3oO8aIJ4w1dwHC6sO1FkUFQMMXJrORr8ppLz +otZbjMgo/c9ymRw2aVhimMVMh3M3sGPZ1yI6NyAfMpWErHiHf7TYydtA6yi9GKMp +uBQrn0Y0sPMTROb0QHqh0Ce9RohgrlTQhBv0YurQpCECgYEA6txExjqHa+Rat+p+ +Sj22zGNzZ4YKg+EiKU5R2Mbu80liQeBzS5BKYjgjwcehyC60T0fmHnrps92WjExl +4KEwKeh59PymX0gWodOsR2WTwmviamRjv7VvVCi5C1/8mDwz0zFODmAeK0msbpK6 +2PJsrSoFGIsXw5hJLoeN1uLGOm8CgYEA4nhx201e5WW0h/uLc8rbuHHE7RtOdSig +Sh4yOo3ZeMbS7rOXAL3sM0vRI/vDMsd+0QhXpI8E6+NzzmnWuB9iaK5MQugndCNs +NNgebPqwMBPkJvuw4SFl0PjLw/e52eUZZQu8Hn6PEkbMzEARzR4CtaXjIwLpoyvn ++mSMJxSCuv0CgYEAlBUyX/KDdGnVMWVvH4xt98009K81bhXDnlu9i3tTKp64j2Tg +DWL7I5bWJNakkT2xftOMqudA96HdzLRXAB1h6uqkCu5KUIV5j6uRUqsfUVUIu6B0 +rGnPuja7BgsNj0dWPfE2kCXWn+CiEk399dBU6ZI3CFSbNT6N1gKuUjFGX40CgYEA +puRrw1j5HcQdjjhpRCOyV/I/VRxSbTRLK1OlYFE4ejDLMO1X0kwjzImKziGyb7Fk +W1byf2qGDe/IX8zPV1CJjOsleAjtSkDwdODvSxBS1ptR/whsCPw7tAwk3X2pkFGs +rTJAp5hMhwN12O8+LxJ2nLZq9FopIv4wLe36+rG6xWECgYAkyHfFku6uqcv3vIbR +b7e/QxrAWBfx+u0jjrL4f4ilgQCmOaq68FfAjjt/QKJYFhXjIQK8cIQvtU41MWSX +vuh24mxgAfI4w3ZDGf3d9BviOR/3Zw7XRfLwRJnT5sL+vQK73WXPmjwOhoCx1fnk +hqwJdJmtYZcoryM+ZxtTEyS51A== +-----END PRIVATE KEY----- diff --git a/tests/nxpkeygen/data/new_rotk_2048.pub b/tests/nxpkeygen/data/new_rotk_2048.pub new file mode 100644 index 00000000..0d0dadd0 --- /dev/null +++ b/tests/nxpkeygen/data/new_rotk_2048.pub @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz8T0a6hcoik8CkqlR5ru +/T/d9E/D/gDrK1BXC76/8IGsD33UFiLx2FixFGUEAotSXFywSrfOPnQJQVLEBJgr +uwTCWWbIgFEE/fzF0XfMCmopfwoRFkyY7ymmkWNzNEB4TGjP8wR3cdQD2doEJaLu +jGusGVnMDZCNYpE93RjYT+d4MVjyVjpL/C1/2TDIzNAJKHMA0VnJuOR00MyJOBMl +0ORoVC3YXCyO4NFP9Z8Hz/3Ow7qLNKCpC/kylx3CH4EDbJHXSr5gpHXMqN43mjrO +vYLL5aOF9m07QHjIRYRZdnufGHaiIq9D4HTa6X2FS0dU+cn2RUe5jXV3X45FfLpl +swIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/nxpkeygen/data/new_rotk_secp256r1.pem b/tests/nxpkeygen/data/new_rotk_secp256r1.pem new file mode 100644 index 00000000..336a178d --- /dev/null +++ b/tests/nxpkeygen/data/new_rotk_secp256r1.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgqg8gAXs4lB0+JNGu +5d/4vdM6aT+kQ0qkgk/ebMZY3UChRANCAAReWSAs1uF0sPK7l8rxD5m1aaOWrxId +I0RshCpP3j7HTheyYNF8qp+cT0iJOXxKHutKInu/2zuR1J4xe2lgVUKG +-----END PRIVATE KEY----- diff --git a/tests/nxpkeygen/data/new_rotk_secp256r1.pub b/tests/nxpkeygen/data/new_rotk_secp256r1.pub new file mode 100644 index 00000000..bf99930a --- /dev/null +++ b/tests/nxpkeygen/data/new_rotk_secp256r1.pub @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXlkgLNbhdLDyu5fK8Q+ZtWmjlq8S +HSNEbIQqT94+x04XsmDRfKqfnE9IiTl8Sh7rSiJ7v9s7kdSeMXtpYFVChg== +-----END PUBLIC KEY----- diff --git a/tests/nxpkeygen/data/new_rotk_secp384r1.pem b/tests/nxpkeygen/data/new_rotk_secp384r1.pem new file mode 100644 index 00000000..1f7de97e --- /dev/null +++ b/tests/nxpkeygen/data/new_rotk_secp384r1.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDD20oq8ZS48q8hAUGrs +jLGcnhBYvRf5s+tuIwtudXnhbAO+hvJ7WzkKiQLLPSReVjuhZANiAASs3o/X4/Bm +bQP+pdaGRiCOwAuK3k28TAoN8u4M1UPoq3ycsZg7IubH3KToEN21vazxKeUHhs9i +FWfzVp0oaZ0GOlxknOODKW1J/+bzHaik2AXicKk4ZOm+rrCXD7hmFMU= +-----END PRIVATE KEY----- diff --git a/tests/nxpkeygen/data/new_rotk_secp384r1.pub b/tests/nxpkeygen/data/new_rotk_secp384r1.pub new file mode 100644 index 00000000..d1155ed6 --- /dev/null +++ b/tests/nxpkeygen/data/new_rotk_secp384r1.pub @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAErN6P1+PwZm0D/qXWhkYgjsALit5NvEwK +DfLuDNVD6Kt8nLGYOyLmx9yk6BDdtb2s8SnlB4bPYhVn81adKGmdBjpcZJzjgylt +Sf/m8x2opNgF4nCpOGTpvq6wlw+4ZhTF +-----END PUBLIC KEY----- diff --git a/tests/dat/data/no_key_dck_rsa_2048.yml b/tests/nxpkeygen/data/no_key_dck_rsa_2048.yml similarity index 100% rename from tests/dat/data/no_key_dck_rsa_2048.yml rename to tests/nxpkeygen/data/no_key_dck_rsa_2048.yml diff --git a/tests/dat/data/org_dck_rsa_2048.yml b/tests/nxpkeygen/data/org_dck_rsa_2048.yml similarity index 100% rename from tests/dat/data/org_dck_rsa_2048.yml rename to tests/nxpkeygen/data/org_dck_rsa_2048.yml diff --git a/tests/dat/data/p0_cert0_2048.pub b/tests/nxpkeygen/data/p0_cert0_2048.pub similarity index 100% rename from tests/dat/data/p0_cert0_2048.pub rename to tests/nxpkeygen/data/p0_cert0_2048.pub diff --git a/tests/dat/data/p1_cert0_2048.pub b/tests/nxpkeygen/data/p1_cert0_2048.pub similarity index 100% rename from tests/dat/data/p1_cert0_2048.pub rename to tests/nxpkeygen/data/p1_cert0_2048.pub diff --git a/tests/dat/data/p2_cert0_2048.pub b/tests/nxpkeygen/data/p2_cert0_2048.pub similarity index 100% rename from tests/dat/data/p2_cert0_2048.pub rename to tests/nxpkeygen/data/p2_cert0_2048.pub diff --git a/tests/dat/data/p3_cert0_2048.pub b/tests/nxpkeygen/data/p3_cert0_2048.pub similarity index 100% rename from tests/dat/data/p3_cert0_2048.pub rename to tests/nxpkeygen/data/p3_cert0_2048.pub diff --git a/tests/dat/data/plugin_dck_rsa_2048.yml b/tests/nxpkeygen/data/plugin_dck_rsa_2048.yml similarity index 100% rename from tests/dat/data/plugin_dck_rsa_2048.yml rename to tests/nxpkeygen/data/plugin_dck_rsa_2048.yml diff --git a/tests/nxpkeygen/data/sample.cert b/tests/nxpkeygen/data/sample.cert new file mode 100644 index 0000000000000000000000000000000000000000..f656684895b2b55ec205bf7a4dee4b8f5d5dc792 GIT binary patch literal 940 zcmZQ%U|?Vb;s-38eA#yuLwBE>$Cla<{rGj(pMW&YwU%s0QjQ)te!IT2>E@$Zjt&lc zxf%54t&bN2YN07uU*USTp(?9#X8qBIvyb8?R-Zg0pXOHee8ak+WgZR3c6-|~y`J;( zn^(n4-L$8d9_?`skBM9%xFYs+%QNB25<#1U=l;-|w5em`af<^9d-dn!vAjIce$CnC zQ~T%igKDOiXTR8X{Mv(i(z{aanPSiIJ0P5U-~JDcYxrxgh$(%%5wTGum4&H0C?;lu*Y}?#`(`I<%L(6Z{A{2UG%OedyQn| z8QuxKNfT}FO5E^&USSyVDa^|Ik8b@iaB37YT#P)q<4MVdGo2Q~su!MQgy`Og z(dm0|(f{jw_Va(w9p2S#vS20mPowFwhvXZXb0%K*+Ly4T^vsHT=CiEM?QJ^!baCsq zTx*Al6Rs_hW!3W~$`&cDcYaWE>t$`Lw|mIXliyt3clMT+$M?C`>`L7X3J;y8w5ifT zD~&J6t+ew}lzW-bwNvMwxpR$&`(c|$7cYfrCH>CPaCcu}_H$S5jYa$RPIC2*$ckQ= zu{S>0R(y4(9QU05m|_K&+7AKGFY|e5h@U?c{pGfJz;5~Yb@AHk&#tPU$LTe9_V2w5 z3$zxT+41M~+Ge9qx5BR}-q1DbT{~fA8ES`d;Ji|cQ(qYkdpS(!e%*Mg str: def sign(self, data: bytes) -> bytes: return b"x" * self.param + + @property + def signature_length(self) -> int: + return self.param diff --git a/tests/dat/test_debug_cred.py b/tests/nxpkeygen/test_debug_cred.py similarity index 76% rename from tests/dat/test_debug_cred.py rename to tests/nxpkeygen/test_debug_cred.py index 2aa2570f..c42ad4c2 100644 --- a/tests/dat/test_debug_cred.py +++ b/tests/nxpkeygen/test_debug_cred.py @@ -10,8 +10,8 @@ import pytest import yaml -from spsdk import crypto -from spsdk.crypto import hashes, ec, InvalidSignature +from spsdk import SPSDKError +from spsdk.crypto import InvalidSignature, ec, hashes from spsdk.crypto.loaders import load_private_key from spsdk.dat import utils from spsdk.dat.debug_credential import DebugCredential @@ -153,6 +153,33 @@ def test_niobe4analog_export_parse(data_dir, yml_file_name, version): assert dc == dc_parsed +def test_niobe4analog_export_parse_invalid(data_dir): + with use_working_directory(data_dir): + with open("new_dck_secp256_N4A.yml", "r") as f: + yaml_config = yaml.safe_load(f) + dc = DebugCredential.create_from_yaml_config(version="2.0", yaml_config=yaml_config) + dc.sign() + with pytest.raises(SPSDKError, match="Invalid flag"): + dc.parse(bytes(232)) + + +@pytest.mark.parametrize( + "dc_file_name, class_name", + [ + ("new_dck_rsa2048.cert", "DebugCredentialRSA2048"), + ("new_dck_secp256r1.cert", "DebugCredentialECC256"), + ("n4a_dck_secp384r1.cert", "DebugCredentialECC384N4Analog"), + ], +) +def test_parse(data_dir, dc_file_name, class_name): + """Verifies the parse mechanisms on DC files.""" + with use_working_directory(data_dir): + with open(dc_file_name, "rb") as f: + dc_file = f.read() + dc = DebugCredential.parse(dc_file) + assert dc.__class__.__name__ == class_name + + @pytest.mark.parametrize( "yml_file_name, version, required_values", [ @@ -193,3 +220,31 @@ def test_debugcredential_info_N4A(data_dir, yml_file_name, version, required_val assert req_string in output, f"string {req_string} is not in the output: {output}" for req_value in req_values: assert req_value in output, f"string {req_value} is not in the output: {output}" + + +def test_debugcredential_invalid(data_dir): + """Evoke exceptions.""" + with use_working_directory(data_dir): + with open("new_dck_rsa2048.yml", "r") as f: + yaml_config = yaml.safe_load(f) + dc = DebugCredential.create_from_yaml_config(version="1.0", yaml_config=yaml_config) + with pytest.raises(SPSDKError, match="Debug Credential Signature is not set"): + dc.export() + with pytest.raises(SPSDKError, match="Debug Credential Signature provider is not set"): + dc.signature_provider = None + dc.sign() + + +def test_debugcredential_rot_meta_as_cert(data_dir): + """Verifies the info message for debug authentication.""" + with use_working_directory(data_dir): + with open("dck_rsa2048_rot_meta_cert.yml", "r") as f: + yaml_config = yaml.safe_load(f) + dc = DebugCredential.create_from_yaml_config(version="1.0", yaml_config=yaml_config) + dc.sign() + assert dc.VERSION == "1.0" + assert dc.cc_beacon == 0 + assert dc.cc_socu == 1023 + assert dc.cc_vu == 22136 + assert dc.socc == 1 + assert dc.uuid == b"\xe0\x04\t\x0ek\xdd!U\xbb\xce\x9e\x06e\x80[\xe3" diff --git a/tests/crypto/test_key_gen.py b/tests/nxpkeygen/test_key_gen.py similarity index 66% rename from tests/crypto/test_key_gen.py rename to tests/nxpkeygen/test_key_gen.py index 9fbeb20f..c9f32c4e 100644 --- a/tests/crypto/test_key_gen.py +++ b/tests/nxpkeygen/test_key_gen.py @@ -7,16 +7,20 @@ """ Tests for key management (generating public/private key) """ from os import path +from typing import ByteString +import pytest + +from spsdk import SPSDKValueError from spsdk.crypto.keys_management import ( + generate_ecc_private_key, + generate_ecc_public_key, generate_rsa_private_key, generate_rsa_public_key, - save_rsa_private_key, - save_rsa_public_key, - generate_ecc_public_key, - generate_ecc_private_key, save_ecc_private_key, save_ecc_public_key, + save_rsa_private_key, + save_rsa_public_key, ) @@ -56,10 +60,39 @@ def test_keys_generation_4096(tmpdir): assert path.isfile(path.join(tmpdir, "pub_4096.pem")) -def test_keys_generation_P256(tmpdir): - priv_key = generate_ecc_private_key(curve_name="P-256") +@pytest.mark.parametrize( + "ec_name", + [ + "secp192r1", + "secp224r1", + "secp256r1", + "secp384r1", + "secp521r1", + "secp256k1", + "sect163k1", + "sect233k1", + "sect283k1", + "sect409k1", + "sect571k1", + "sect163r2", + "sect233r1", + "sect283r1", + "sect409r1", + "sect571r1", + "brainpoolP256r1", + "brainpoolP384r1", + "brainpoolP512r1", + ], +) +def test_keys_generation_ec(tmpdir, ec_name): + priv_key = generate_ecc_private_key(curve_name=ec_name) pub_key = generate_ecc_public_key(priv_key) - save_ecc_private_key(priv_key, path.join(tmpdir, "priv_P256.pem")) - save_ecc_public_key(pub_key, path.join(tmpdir, "pub_P256.pem")) - assert path.isfile(path.join(tmpdir, "priv_P256.pem")) - assert path.isfile(path.join(tmpdir, "pub_P256.pem")) + save_ecc_private_key(priv_key, path.join(tmpdir, f"key_{ec_name}.pem")) + save_ecc_public_key(pub_key, path.join(tmpdir, f"key_{ec_name}.pub")) + assert path.isfile(path.join(tmpdir, f"key_{ec_name}.pem")) + assert path.isfile(path.join(tmpdir, f"key_{ec_name}.pub")) + + +def test_keys_generation_ec_invalid(tmpdir): + with pytest.raises(SPSDKValueError): + generate_ecc_private_key(curve_name="invalid") diff --git a/tests/dat/test_nxpkeygen.py b/tests/nxpkeygen/test_nxpkeygen.py similarity index 68% rename from tests/dat/test_nxpkeygen.py rename to tests/nxpkeygen/test_nxpkeygen.py index 0f8631e0..1da4cd9c 100644 --- a/tests/dat/test_nxpkeygen.py +++ b/tests/nxpkeygen/test_nxpkeygen.py @@ -11,15 +11,9 @@ import pytest from click.testing import CliRunner +from spsdk import SPSDKValueError from spsdk.apps.nxpkeygen import determine_key_parameters, determine_protocol_version, main -from spsdk.crypto import ( - EllipticCurvePrivateKey, - EllipticCurvePublicKey, - RSAPrivateKey, - RSAPublicKey, - load_private_key, - load_public_key, -) +from spsdk.crypto import RSAPrivateKey, RSAPublicKey, load_private_key, load_public_key from spsdk.utils.misc import use_working_directory @@ -31,10 +25,9 @@ def test_command_line_interface(): assert "main [OPTIONS] COMMAND [ARGS]" in result.output assert "NXP Key Generator Tool." in result.output - assert "-p, --protocol VERSION" in result.output assert "genkey Generate key pair for RoT or DCK." in result.output assert "gendc Generate debug certificate (DC)." in result.output - assert "get-gendc-config Generate the template of Debug Credentials YML..." in result.output + assert "get-cfg-template Generate the template of Debug Credentials YML..." in result.output def test_generate_rsa_key(tmpdir) -> None: @@ -56,38 +49,13 @@ def test_generate_rsa_key(tmpdir) -> None: assert priv_key_from_file.key_size == 2048 -@pytest.mark.parametrize( - "protocol_version, curve_name", - [("2.0", "secp256r1"), ("2.1", "secp384r1"), ("2.2", "secp521r1")], -) -def test_generate_ecc_key(tmpdir, protocol_version, curve_name) -> None: - """Test generate ecc key pair.""" - pem_path = os.path.join(tmpdir, f"{curve_name}_key_ecc.pem") - pub_path = os.path.join(tmpdir, f"{curve_name}_key_ecc.pub") - - cmd = f"-p {protocol_version} genkey {pem_path}" - runner = CliRunner() - result = runner.invoke(main, cmd.split()) - assert result.exit_code == 0 - assert os.path.isfile(pem_path) - assert os.path.isfile(pub_path) - - pub_key_from_file = load_public_key(pub_path) - assert isinstance(pub_key_from_file, EllipticCurvePublicKey) - assert pub_key_from_file.curve.name == curve_name - - priv_key_from_file = load_private_key(pem_path) - assert isinstance(priv_key_from_file, EllipticCurvePrivateKey) - assert pub_key_from_file.curve.name == curve_name - - def test_generate_invalid_key(tmpdir) -> None: """Test generate invalid key pair.""" - cmd = f'-p 3.0 genkey {os.path.join(tmpdir, "key_invalid.pem")}' + cmd = f'genkey -k invalid-key-type {os.path.join(tmpdir, "key_invalid.pem")}' runner = CliRunner() result = runner.invoke(main, cmd.split()) - assert result.exit_code == 1 + assert result.exit_code != 0 assert not os.path.isfile(os.path.join(tmpdir, "key_invalid.pem")) assert not os.path.isfile(os.path.join(tmpdir, "key_invalid.pub")) @@ -97,9 +65,9 @@ def test_generate_invalid_key(tmpdir) -> None: [ ("1.0", True, 2048), ("1.1", True, 4096), - ("2.0", False, "P-256"), - ("2.1", False, "P-384"), - ("2.2", False, "P-521"), + ("2.0", False, "secp256r1"), + ("2.1", False, "secp384r1"), + ("2.2", False, "secp521r1"), ], ) def test_determine_protocol_version(protocol_version, expect_result, expected_key_params): @@ -113,7 +81,7 @@ def test_determine_protocol_version(protocol_version, expect_result, expected_ke def test_generate_rsa_dc_file(tmpdir, data_dir): """Test generate dc file with rsa 2048 protocol.""" out_file = f"{tmpdir}/dc_2048.cert" - cmd = f"-p 1.0 gendc -c new_dck_rsa2048.yml {out_file}" + cmd = f"gendc -c new_dck_rsa2048.yml -p 1.0 {out_file}" with use_working_directory(data_dir): runner = CliRunner() result = runner.invoke(main, cmd.split()) @@ -124,7 +92,7 @@ def test_generate_rsa_dc_file(tmpdir, data_dir): def test_generate_ecc_dc_file(tmpdir, data_dir): """Test generate dc file with ecc protocol.""" out_file = f"{tmpdir}/dc_secp256r1.cert" - cmd = f"-p 2.0 gendc -c new_dck_secp256.yml {out_file}" + cmd = f"gendc -p 2.0 -c new_dck_secp256.yml {out_file}" with use_working_directory(data_dir): runner = CliRunner() result = runner.invoke(main, cmd.split()) @@ -135,7 +103,7 @@ def test_generate_ecc_dc_file(tmpdir, data_dir): def test_generate_dc_file_N4A_256(tmpdir, data_dir): """Test generate dc file with ecc protocol for N4A""" out_file = f"{tmpdir}/dc_secp256r1_N4A.cert" - cmd = f"-p 2.0 gendc -c new_dck_secp256_N4A.yml {out_file}" + cmd = f"gendc -p 2.0 -c new_dck_secp256_N4A.yml {out_file}" with use_working_directory(data_dir): runner = CliRunner() result = runner.invoke(main, cmd.split()) @@ -146,7 +114,7 @@ def test_generate_dc_file_N4A_256(tmpdir, data_dir): def test_generate_dc_file_N4A_384(tmpdir, data_dir): """Test generate dc file with ecc protocol for N4A""" out_file = f"{tmpdir}/dc_secp384r1_N4A.cert" - cmd = f"-p 2.1 gendc -c new_dck_secp384_N4A.yml {out_file}" + cmd = f"gendc -p 2.1 -c new_dck_secp384_N4A.yml {out_file}" with use_working_directory(data_dir): runner = CliRunner() result = runner.invoke(main, cmd.split()) @@ -158,9 +126,9 @@ def test_generate_rsa_with_elf2sb(tmpdir, data_dir): org_file = f"{tmpdir}/org.dc" new_file = f"{tmpdir}/new.dc" - cmd1 = f"-p 1.0 gendc -c org_dck_rsa_2048.yml {org_file}" + cmd1 = f"gendc -p 1.0 -c org_dck_rsa_2048.yml {org_file}" # keys were removed from yaml and suplied by elf2sb config - cmd2 = f"-p 1.0 gendc -c no_key_dck_rsa_2048.yml -e elf2sb_config.json {new_file}" + cmd2 = f"gendc -p 1.0 -c no_key_dck_rsa_2048.yml -e elf2sb_config.json {new_file}" with use_working_directory(data_dir): result = CliRunner().invoke(main, cmd1.split()) assert result.exit_code == 0, result.output @@ -171,21 +139,60 @@ def test_generate_rsa_with_elf2sb(tmpdir, data_dir): def test_force_actual_dir(tmpdir): with use_working_directory(tmpdir): - result = CliRunner().invoke(main, "-p 1.0 genkey key".split()) + result = CliRunner().invoke(main, "genkey -k rsa2048 key".split()) assert result.exit_code == 0 # attempt to rewrite the key should fail - result = CliRunner().invoke(main, "-p 1.0 genkey key".split()) + result = CliRunner().invoke(main, "genkey -k rsa2048 key".split()) assert result.exit_code == 1 # attempt to rewrite should pass due to --forces - result = CliRunner().invoke(main, "-p 1.0 genkey key --force".split()) + result = CliRunner().invoke(main, "genkey -k rsa2048 key --force".split()) assert result.exit_code == 0 def test_force_subdir(tmpdir): with use_working_directory(tmpdir): - result = CliRunner().invoke(main, "-p 1.0 genkey tmp/key".split()) + result = CliRunner().invoke(main, "genkey -k rsa2048 tmp/key".split()) # should fail due to non-existing subfolder assert result.exit_code == 1 - result = CliRunner().invoke(main, "-p 1.0 genkey tmp/key --force".split()) + result = CliRunner().invoke(main, "genkey -k rsa2048 tmp/key --force".split()) assert result.exit_code == 0 assert os.path.isfile("tmp/key") + + +@pytest.mark.parametrize( + "key, valid", + [ + ("secp192r1", True), + ("secp224r1", True), + ("secp256r1", True), + ("secp384r1", True), + ("secp521r1", True), + ("secp256k1", True), + ("sect163k1", True), + ("sect233k1", True), + ("sect283k1", True), + ("sect409k1", True), + ("sect571k1", True), + ("sect163r2", True), + ("sect233r1", True), + ("sect283r1", True), + ("sect409r1", True), + ("sect571r1", True), + ("brainpoolP256r1", True), + ("brainpoolP384r1", True), + ("brainpoolP512r1", True), + ("ras2048", False), + ("secp100", False), + ], +) +def test_key_types(tmpdir, key, valid): + with use_working_directory(tmpdir): + if valid: + result = CliRunner().invoke(main, f"genkey -k {key} my_key_{key}.pem") + + assert result.exit_code == 0 + assert os.path.isfile(f"my_key_{key}.pem") + assert os.path.isfile(f"my_key_{key}.pub") + else: + result = CliRunner().invoke(main, f"genkey -k {key} my_key_{key}.pem") + assert result.exit_code != 0 diff --git a/tests/dat/test_plugin.py b/tests/nxpkeygen/test_plugin.py similarity index 100% rename from tests/dat/test_plugin.py rename to tests/nxpkeygen/test_plugin.py diff --git a/tests/pfr/data/cfpa_no_change.yml b/tests/pfr/data/cfpa_no_change.yml new file mode 100644 index 00000000..507dbcb9 --- /dev/null +++ b/tests/pfr/data/cfpa_no_change.yml @@ -0,0 +1,11 @@ +# NXP lpc55s3x PFR CFPA configuration +description: # The PFR CFPA configuration description. + device: lpc55s3x # The NXP device name. + revision: 0a # The NXP device revision. + type: CFPA # The PFR type (CMPA, CFPA). + version: 1.3.5 # The SPSDK tool version. + author: NXP # The author of the configuration. + release: alpha # The SPSDK release. +settings: # The PFR CFPA registers configuration. + HEADER: # Header + value: '0x00000000' # The value width: 32b diff --git a/tests/pfr/data/cmpa_96mhz_rotkh.yml b/tests/pfr/data/cmpa_96mhz_rotkh.yml index 15da7a01..1d2ea896 100644 --- a/tests/pfr/data/cmpa_96mhz_rotkh.yml +++ b/tests/pfr/data/cmpa_96mhz_rotkh.yml @@ -1,21 +1,21 @@ # NXP lpc55s6x PFR CMPA configuration -description: # The PFR CMPA configuration description. - device: lpc55s6x # The NXP device name. +description: # The PFR CMPA configuration description. + device: lpc55s6x # The NXP device name. revision: 1b # The NXP device revision. type: CMPA # The PFR type (CMPA, CFPA). version: 1.3.5 # The SPSDK tool version. author: NXP # The author of the configuration. release: alpha # The SPSDK release. settings: # The PFR CMPA registers configuration. - BOOT_CFG: # BOOT_CFG. - bitfields: # The register bitfields - BOOT_SPEED: BOOT_CFG_BOOT_SPEED_FRO_96MHZ # Width: 2b[0-3], Description: Core clock: + BOOT_CFG: # BOOT_CFG. + bitfields: # The register bitfields + BOOT_SPEED: BOOT_CFG_BOOT_SPEED_FRO_96MHZ # Width: 2b[0-3], Description: Core clock: # - BOOT_CFG_BOOT_SPEED_SYSTEM_SPEED_CODE, (0): Defined by NMPA.SYSTEM_SPEED_CODE # - BOOT_CFG_BOOT_SPEED_FRO_96MHZ, (1): 96MHz FRO # - BOOT_CFG_BOOT_SPEED_FRO_48MHZ, (2): 48MHz FRO CC_SOCU_PIN: # CC_SOCU_PIN. - bitfields: # The register bitfields - NIDEN: CC_SOCU_PIN_NIDEN_DISABLE # Width: 1b[0-1], Description: Non Secure non-invasive debug enable + bitfields: # The register bitfields + NIDEN: CC_SOCU_PIN_NIDEN_DISABLE # Width: 1b[0-1], Description: Non Secure non-invasive debug enable # - CC_SOCU_PIN_NIDEN_ENABLE, (0): Use DAP to enable # - CC_SOCU_PIN_NIDEN_DISABLE, (1): Fixed state DBGEN: CC_SOCU_PIN_DBGEN_DISABLE # Width: 1b[0-1], Description: Non Secure debug enable @@ -43,8 +43,8 @@ settings: # The PFR CMPA registers configuration. # - CC_SOCU_PIN_MCM33_NIDEN_ENABLE, (0): Use DAP to enable # - CC_SOCU_PIN_MCM33_NIDEN_DISABLE, (1): Fixed state CC_SOCU_DFLT: # CC_SOCU_DFLT. - bitfields: # The register bitfields - NIDEN: CC_SOCU_DFLT_NIDEN_ENABLE # Width: 1b[0-1], Description: Non Secure non-invasive debug fixed state + bitfields: # The register bitfields + NIDEN: CC_SOCU_DFLT_NIDEN_ENABLE # Width: 1b[0-1], Description: Non Secure non-invasive debug fixed state # - CC_SOCU_DFLT_NIDEN_DISABLE, (0): Disable # - CC_SOCU_DFLT_NIDEN_ENABLE, (1): Enable DBGEN: CC_SOCU_DFLT_DBGEN_ENABLE # Width: 1b[0-1], Description: Non Secure debug fixed state @@ -63,8 +63,8 @@ settings: # The PFR CMPA registers configuration. # - CC_SOCU_DFLT_MCM33_NIDEN_DISABLE, (0): Disable # - CC_SOCU_DFLT_MCM33_NIDEN_ENABLE, (1): Enable SECURE_BOOT_CFG: # Secure boot configuration flags. - bitfields: # The register bitfields - SKIP_DICE: SECURE_BOOT_CFG_SKIP_DICE_DISABLE_0 # Width: 2b[0-3], Description: Skip DICE computation + bitfields: # The register bitfields + SKIP_DICE: SECURE_BOOT_CFG_SKIP_DICE_DISABLE_0 # Width: 2b[0-3], Description: Skip DICE computation # - SECURE_BOOT_CFG_SKIP_DICE_ENABLE, (0): Enable DICE # - SECURE_BOOT_CFG_SKIP_DICE_DISABLE_1, (1): Disable DICE # - SECURE_BOOT_CFG_SKIP_DICE_DISABLE_2, (2): Disable DICE @@ -75,4 +75,4 @@ settings: # The PFR CMPA registers configuration. # - SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_2, (2): Boot signed images. (internal flash, RSA signed) # - SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_3, (3): Boot signed images. (internal flash, RSA signed) ROTKH: # ROTKH field is compounded by 8 32-bit fields and contains Root key table hash - value: ea5c35049dd077472cad747638dd478e4e435ca047fc62562b0b3ab83c2ad250 # The value width: 256b + value: 04355cea4777d09d7674ad2c8e47dd38a05c434e5662fc47b83a0b2b50d22a3c # The value width: 256b diff --git a/tests/pfr/data/n4a_CFPA_basic.bin b/tests/pfr/data/n4a_CFPA_basic.bin new file mode 100644 index 0000000000000000000000000000000000000000..ef0ef69e66e00368480f1e361c23e1c02f5eba23 GIT binary patch literal 512 YcmZQzpauB None: CertSectionV2.parse(data, 0, dek, crypto_backend().random_bytes(32), Counter(nonce)) +def test_invalid_export_cert_section_v2(data_dir): + cs = CertSectionV2(_create_cert_block_v2(data_dir)) + dek = crypto_backend().random_bytes(16) + mac = crypto_backend().random_bytes(16) + nonce = crypto_backend().random_bytes(16) + cs.HMAC_SIZE = 137 + with pytest.raises(SPSDKError, match="Invalid size"): + cs.export(dek, mac, Counter(nonce)) + + def test_certificate_block_v2(data_dir): _create_cert_block_v2(data_dir) + + +def test_invalid_parse_cert_section_v2(data_dir): + with pytest.raises(SPSDKError): + CertSectionV2.parse(bytes(123), 0, dek="6") + with pytest.raises(SPSDKError): + CertSectionV2.parse(bytes(123), 0, mac="6") + with pytest.raises(SPSDKError): + CertSectionV2.parse(bytes(123), 0, counter="6") + + +def test_invalid_header_hmac(data_dir): + cs = CertSectionV2(_create_cert_block_v2(data_dir)) + dek = crypto_backend().random_bytes(32) + mac = crypto_backend().random_bytes(32) + nonce = crypto_backend().random_bytes(16) + valid_data = cs.export(dek, mac, Counter(nonce)) + invalid_data = valid_data + invalid_data = bytearray(invalid_data) + invalid_data[0:32] = bytearray(32) + with pytest.raises(SPSDKError, match="HMAC"): + CertSectionV2.parse(invalid_data, 0, dek, mac, Counter(nonce)) + + +def test_invalid_header_tag(data_dir): + cs = CertSectionV2(_create_cert_block_v2(data_dir)) + cs._header.tag += 1 + dek = crypto_backend().random_bytes(32) + mac = crypto_backend().random_bytes(32) + nonce = crypto_backend().random_bytes(16) + valid_data = cs.export(dek, mac, Counter(nonce)) + with pytest.raises(SPSDKError, match="TAG"): + CertSectionV2.parse(data=valid_data, mac=mac, dek=dek, counter=Counter(nonce)) + + +def test_invalid_header_flag(data_dir): + cs = CertSectionV2(_create_cert_block_v2(data_dir)) + cs._header.flags += 1 + dek = crypto_backend().random_bytes(32) + mac = crypto_backend().random_bytes(32) + nonce = crypto_backend().random_bytes(16) + valid_data = cs.export(dek, mac, Counter(nonce)) + with pytest.raises(SPSDKError, match="FLAGS"): + CertSectionV2.parse(data=valid_data, mac=mac, dek=dek, counter=Counter(nonce)) + + +def test_invalid_header_flag(data_dir): + cs = CertSectionV2(_create_cert_block_v2(data_dir)) + cs._header.address += 1 + dek = crypto_backend().random_bytes(32) + mac = crypto_backend().random_bytes(32) + nonce = crypto_backend().random_bytes(16) + valid_data = cs.export(dek, mac, Counter(nonce)) + with pytest.raises(SPSDKError, match="Mark"): + CertSectionV2.parse(data=valid_data, mac=mac, dek=dek, counter=Counter(nonce)) diff --git a/tests/sdp/sdphost/test_sdphost_cli.py b/tests/sdp/sdphost/test_sdphost_cli.py index 9da0217e..ee19b5c1 100644 --- a/tests/sdp/sdphost/test_sdphost_cli.py +++ b/tests/sdp/sdphost/test_sdphost_cli.py @@ -7,17 +7,15 @@ """Testing the SDPHost application.""" -import logging + from unittest.mock import patch -import click from click.testing import CliRunner import spsdk from spsdk.apps import sdphost from spsdk.utils.serial_proxy import SerialProxy - data_responses = { b"\x05\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00": b"\x56\x78\x78\x56\xf0\xf0\xf0\xf0" } diff --git a/tests/sdp/test_sdp_exceptions.py b/tests/sdp/test_sdp_exceptions.py index cfb250bf..ac5afb62 100644 --- a/tests/sdp/test_sdp_exceptions.py +++ b/tests/sdp/test_sdp_exceptions.py @@ -1,12 +1,12 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause from spsdk import SPSDKError -from spsdk.sdp import SdpError, SdpCommandError, SdpConnectionError +from spsdk.sdp import SdpCommandError, SdpConnectionError, SdpError def raise_and_catch(raising_exc, catching_exc) -> bool: diff --git a/tests/sdp/test_sdps.py b/tests/sdp/test_sdps.py index 224cbe26..500efdc1 100644 --- a/tests/sdp/test_sdps.py +++ b/tests/sdp/test_sdps.py @@ -6,10 +6,9 @@ # SPDX-License-Identifier: BSD-3-Clause +from spsdk.sdp import SdpConnectionError from spsdk.sdp.interfaces.base import Interface from spsdk.sdp.sdps import SDPS -from spsdk.sdp import SdpConnectionError - data = b"\xAD" * 100 cmd_pack = b"BLTC\x01\x00\x00\x00d\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" diff --git a/tests/shadowregs/test_shadowregs.py b/tests/shadowregs/test_shadowregs.py index 673d2951..7a8b1eec 100644 --- a/tests/shadowregs/test_shadowregs.py +++ b/tests/shadowregs/test_shadowregs.py @@ -108,10 +108,10 @@ def test_shadowreg_invalid_probe(data_dir): shadowregs = SR.ShadowRegisters(probe, config, TEST_DEV_NAME) - with pytest.raises(DP.DebugProbeError): + with pytest.raises(DP.SPSDKDebugProbeError): shadowregs.set_register("REG1", 0x12345678) - with pytest.raises(DP.DebugProbeError): + with pytest.raises(DP.SPSDKDebugProbeError): shadowregs.get_register("REG1") diff --git a/tests/utils/crypto/test_cert_blocks.py b/tests/utils/crypto/test_cert_blocks.py index 19cab0b8..90680fa1 100644 --- a/tests/utils/crypto/test_cert_blocks.py +++ b/tests/utils/crypto/test_cert_blocks.py @@ -9,8 +9,9 @@ import pytest -from spsdk.utils.crypto.cert_blocks import CertBlockHeader -from spsdk.utils.crypto import Certificate, CertBlockV2 +from spsdk import SPSDKError +from spsdk.utils.crypto import CertBlockV2, Certificate +from spsdk.utils.crypto.cert_blocks import CertBlockHeader, CertificateBlockHeader def test_cert_block_header(): @@ -29,6 +30,11 @@ def test_cert_block_header(): assert header == header_parsed +def test_cert_block_header_invalid(): + with pytest.raises(SPSDKError, match="Invalid version"): + CertBlockHeader(version="bbb") + + def test_cert_block_basic(): cb = CertBlockV2() # test default values @@ -42,10 +48,10 @@ def test_cert_block_basic(): assert cb.image_length == 1 assert cb.header.image_length == 1 # invalid root key index - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): cb.set_root_key_hash(4, bytes([0] * 32)) # invalid root key size - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): cb.set_root_key_hash(0, bytes()) @@ -71,25 +77,83 @@ def test_cert_block(data_dir): with open(os.path.join(data_dir, "ca0_v3.der.crt"), "rb") as f: ca0_cert_data = f.read() ca0_cert = Certificate(ca0_cert_data) - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): cb.add_certificate(ca0_cert) # test exception if no certificate specified cb = CertBlockV2() cb.set_root_key_hash(0, cert_obj.public_key_hash) - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): cb.export() # test exception last certificate is set as CA cb = CertBlockV2() cb.set_root_key_hash(0, ca0_cert.public_key_hash) cb.add_certificate(ca0_cert) - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): cb.export() # test exception if hash does not match any certificate cb = CertBlockV2() cb.set_root_key_hash(0, ca0_cert.public_key_hash) cb.add_certificate(cert_data) - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): + cb.export() + + +def test_add_invalid_cert_in_cert_block(data_dir): + cb = CertBlockV2() + with open(os.path.join(data_dir, "selfsign_2048_v3.der.crt"), "rb") as f: + cert_data = f.read() + with open(os.path.join(data_dir, "ca0_v3.der.crt"), "rb") as f: + ca0_cert_data = f.read() + with pytest.raises(SPSDKError): + cb.add_certificate(cert=5) + with pytest.raises( + SPSDKError, match="Chain certificate cannot be verified using parent public key" + ): + cb.add_certificate(cert=cert_data) + cb.add_certificate(cert=ca0_cert_data) + + +def test_cert_block_export_invalid(data_dir): + with open(os.path.join(data_dir, "selfsign_2048_v3.der.crt"), "rb") as f: + cert_data = f.read() + with open(os.path.join(data_dir, "ca0_v3.der.crt"), "rb") as f: + ca0_cert_data = f.read() + cert_obj = Certificate(cert_data) + cb = CertBlockV2() + cb.set_root_key_hash(0, cert_obj.public_key_hash) + cb.add_certificate(cert_data) + cb.add_certificate(cert_data) + assert cb.rkh_index == 0 + with pytest.raises( + SPSDKError, match="All certificates except the last chain certificate must be CA" + ): cb.export() + + +def test_invalid_cert_block_header(): + ch = CertificateBlockHeader() + ch.MAGIC = b"chdx" + data = ch.export() + with pytest.raises(SPSDKError, match="Magic is not same!"): + CertificateBlockHeader.parse(data=data) + with pytest.raises(SPSDKError, match="SIZE is bigger than length of the data without offset"): + CertificateBlockHeader.parse(data=bytes(8)) + + +def test_cert_block_invalid(): + cb = CertBlockV2() + cb.RKH_SIZE = 77777 + with pytest.raises(SPSDKError, match="Invalid length of data"): + cb.rkht + with pytest.raises(SPSDKError, match="Invalid image length"): + cb.image_length = -2 + with pytest.raises(SPSDKError, match="Invalid alignment"): + cb.alignment = -2 + cb = CertBlockV2() + with pytest.raises(SPSDKError, match="Invalid index of root key hash in the table"): + cb.set_root_key_hash(5, bytes(32)) + with pytest.raises(SPSDKError, match="Invalid length of key hash"): + cb.set_root_key_hash(3, bytes(5)) diff --git a/tests/utils/crypto/test_common.py b/tests/utils/crypto/test_common.py index 0515d3c4..8b8e9fd7 100644 --- a/tests/utils/crypto/test_common.py +++ b/tests/utils/crypto/test_common.py @@ -6,9 +6,21 @@ # SPDX-License-Identifier: BSD-3-Clause """ Test of common crypto utilities module.""" +import datetime +from datetime import datetime, timezone + +import pytest from cryptography.hazmat.primitives.asymmetric.ec import SECP256R1 + +from spsdk import SPSDKError from spsdk.utils.crypto import Counter -from spsdk.utils.crypto.common import ecc_public_numbers_to_bytes, EllipticCurvePublicNumbers +from spsdk.utils.crypto.common import ( + EllipticCurvePublicNumbers, + ecc_public_numbers_to_bytes, + pack_timestamp, + swap16, + unpack_timestamp, +) def test_counter(): @@ -38,6 +50,11 @@ def test_counter(): assert cntr.value == bytes([0] * 14 + [1, 4]) +def test_counter_invalid(): + with pytest.raises(SPSDKError, match="Wrong byte order"): + Counter(nonce=bytes(16), ctr_byteorder_encoding="BIG") + + def test_ecc_public_numbers_to_bytes(): """Test conversion ECC public numbers to bytes.""" ecc = EllipticCurvePublicNumbers(0x1234567890ABCDEF, 0xEFCDAB9078563412, SECP256R1()) @@ -49,3 +66,22 @@ def test_ecc_public_numbers_to_bytes(): ecc_public_numbers_to_bytes(ecc, 8) == b"\x12\x34\x56\x78\x90\xab\xcd\xef\xef\xcd\xab\x90\x78\x56\x34\x12" ) + + +def test_swap16_invalid(): + with pytest.raises(SPSDKError, match="Incorrect number to be swapped"): + swap16(0xFFFFA) + + +def test_pack_timestamp_invalid(): + with pytest.raises(SPSDKError, match="Incorrect result of convertion"): + pack_timestamp( + value=datetime(1000, 1, 1, 0, 0, 0, 0, tzinfo=timezone.utc) + ) + + +def test_unpack_timestamp_invalid(): + with pytest.raises(SPSDKError, match="Incorrect result of convertion"): + unpack_timestamp( + value=99999999999999999999999 + ) diff --git a/tests/utils/crypto/test_otfad.py b/tests/utils/crypto/test_otfad.py index 17a6484a..6e36dea3 100644 --- a/tests/utils/crypto/test_otfad.py +++ b/tests/utils/crypto/test_otfad.py @@ -9,11 +9,12 @@ import pytest +from spsdk import SPSDKError from spsdk.utils.crypto import KeyBlob, Otfad def test_otfad_keyblob(data_dir): - """ Test generation of key blob for OTFAD """ + """Test generation of key blob for OTFAD""" # generate key blob using random keys key_blob = KeyBlob(start_addr=0x08001000, end_addr=0x0800F3FF) gen_blob = key_blob.export(kek=bytes.fromhex("50F66BB4F23B855DCD8FEFC0DA59E963")) @@ -53,19 +54,21 @@ def test_otfad_keyblob(data_dir): assert gen_blob != keyblob_bin # start address not aligned - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): KeyBlob(start_addr=0x08001001, end_addr=0x0800F3FF, key=key, counter_iv=counter) # end address not aligned - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): KeyBlob(start_addr=0x08001000, end_addr=0x0800F000, key=key, counter_iv=counter) # address of the image is not within key blob key_blob = KeyBlob(start_addr=0x08001000, end_addr=0x0800F3FF, key=key, counter_iv=counter) - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): key_blob.encrypt_image(0x8000000, plain_image, True) - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): key_blob.encrypt_image(0x800F000, plain_image, True) + with pytest.raises(SPSDKError, match="Invalid start address"): + key_blob.encrypt_image(0x800F001, plain_image, True) def test_otfad(data_dir): @@ -80,7 +83,7 @@ def test_otfad(data_dir): image = f.read() # invalid address - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): key_blob.encrypt_image(0x0, image, True) encr_image = otfad.encrypt_image(image, 0x08001000, True) @@ -90,3 +93,50 @@ def test_otfad(data_dir): assert encr_image == otfad_image otfad.info() + + +def test_oftad_invalid(data_dir): + """Test OTFAD - image address range does not match to key blob""" + otfad = Otfad() + key = bytes.fromhex("B1A0C56AF31E98CD6936A79D9E6F829D") + counter = bytes.fromhex("5689fab8b4bfb264") + key_blob = KeyBlob(start_addr=0x08001000, end_addr=0x0800F3FF, key=key, counter_iv=counter) + otfad.add_key_blob(key_blob) + assert otfad[0] == key_blob + with open(os.path.join(data_dir, "boot_image.bin"), "rb") as f: + image = f.read() + with pytest.raises(SPSDKError): + otfad.encrypt_image(image, 0x0800FFF, True) + + +def test_keyblob_invalid(): + with pytest.raises(SPSDKError, match="Invalid start/end address"): + KeyBlob(start_addr=0x08001000, end_addr=0x08000000) + key = bytes.fromhex("B1") + counter_iv = bytes.fromhex("53") + with pytest.raises(SPSDKError, match="Invalid key"): + KeyBlob(key=key, counter_iv=counter_iv, start_addr=0x08001000, end_addr=0x0800F3FF) + with pytest.raises(SPSDKError, match="key_flags exceeds mask "): + KeyBlob(start_addr=0x08000000, end_addr=0x080003FF, key_flags=0x8) + counter = bytes.fromhex("5689fab8b4bfb264") + key = bytes.fromhex("B1A0C56AF31E98CD6936A79D9E6F829D") + key_blob = KeyBlob(start_addr=0x08001000, end_addr=0x0800F3FF, key=key, counter_iv=counter) + with pytest.raises(SPSDKError, match="Invalid length of kek"): + key_blob.export(kek=bytes(15)) + with pytest.raises(SPSDKError, match="Invalid value crc"): + key_blob = KeyBlob(start_addr=0x08001000, end_addr=0x080013FF, crc=bytes(5)) + key_blob.export(kek=bytes(16)) + with pytest.raises(SPSDKError, match="Invalid value"): + key_blob = KeyBlob(start_addr=0x08001000, end_addr=0x080013FF, zero_fill=bytes(5)) + key_blob.export(kek=bytes(16)) + with pytest.raises(SPSDKError, match="Invalid length of initialization vector"): + key_blob = KeyBlob(start_addr=0x08001000, end_addr=0x080013FF) + key_blob.export(kek=bytes(16), iv=bytes(32)) + with pytest.raises(SPSDKError, match="Invalid length of data to be encrypted"): + key_blob = KeyBlob(start_addr=0x08001000, end_addr=0x080013FF) + key_blob._EXPORT_NBLOCKS_5 = 90 + key_blob.export(kek=bytes(16)) + key_blob = KeyBlob(start_addr=0x08001000, end_addr=0x080013FF, counter_iv=bytes(8)) + key_blob.ctr_init_vector = bytes(99) + with pytest.raises(SPSDKError, match="Invalid length of counter init"): + key_blob._get_ctr_nonce() diff --git a/tests/utils/data/reg_config_invalid.json b/tests/utils/data/reg_config_invalid.json new file mode 100644 index 00000000..9a1cbbdf --- /dev/null +++ b/tests/utils/data/reg_config_invalid.json @@ -0,0 +1,41 @@ +{ + "devices": { + "test_device1": { + "revisions": { + "x0": "test_device1_x0.xml", + "x1": "test_device1_x1.xml" + }, + "latest": "x1", + "address": "0xA5A5_1234", + "inverted_regs": { + "INVERTED_REG": "INVERTED_REG_AP" + }, + "computed_registers": [ + "COMPUTED_REG" + ], + "computed_fields": { + "COMPUTED_REG": { + "TEST_FIELD1": "computed_reg_test_field1", + "TEST_FIELD2": "computed_reg_test_field2" + }, + "COMPUTED_REG2": { + "TEST_FIELD1": "computed_reg2_test_field1", + "TEST_FIELD2": "computed_reg2_test_field2" + } + }, + "ignored_registers": [ + "IGNORED_REG" + ], + "ignored_fields": [ + "FIELD" + ], + "grouped_registers": [ + { + "name": "DeviceTest" + } + ], + "seal_start": 5, + "seal_count": "4" + } + } +} diff --git a/tests/utils/test_debug_info.py b/tests/utils/test_debug_info.py index 82076d96..13aa8025 100644 --- a/tests/utils/test_debug_info.py +++ b/tests/utils/test_debug_info.py @@ -6,6 +6,9 @@ # SPDX-License-Identifier: BSD-3-Clause +import pytest + +from spsdk import SPSDKError from spsdk.utils.misc import DebugInfo @@ -45,3 +48,9 @@ def test_debug_info_disabled() -> None: _log_test_output(dbg_info) assert dbg_info.lines == [] assert dbg_info.info() == "" + + +def test_debug_info_invalid(): + dbg_info = DebugInfo() + with pytest.raises(SPSDKError, match="Incorrect data length"): + dbg_info.append_binary_data("data", bytes(20)) diff --git a/tests/utils/test_devicedescription.py b/tests/utils/test_devicedescription.py index 3cf8059e..1fd834d4 100644 --- a/tests/utils/test_devicedescription.py +++ b/tests/utils/test_devicedescription.py @@ -8,8 +8,8 @@ from unittest.mock import MagicMock, patch import pytest -import spsdk.utils.devicedescription as devicedescription +import spsdk.utils.devicedescription as devicedescription from spsdk.mboot.interfaces.usb import USB_DEVICES as MB_USB_DEVICES @@ -63,7 +63,7 @@ def test_repr(): ], ) def test_get_device_name(vid, pid, expected_result): - """Verify search works and returns appropripate name based on VID/PID""" + """Verify search works and returns appropriate name based on VID/PID""" assert devicedescription.get_usb_device_name(vid, pid) == expected_result @@ -76,7 +76,7 @@ def test_get_device_name(vid, pid, expected_result): ], ) def test_get_device_name(vid, pid, expected_result): - """Verify search works and returns appropripate name based on VID/PID""" + """Verify search works and returns appropriate name based on VID/PID""" assert devicedescription.get_usb_device_name(vid, pid, MB_USB_DEVICES) == expected_result diff --git a/tests/utils/test_easy_enum.py b/tests/utils/test_easy_enum.py index df4b15ce..099f2487 100644 --- a/tests/utils/test_easy_enum.py +++ b/tests/utils/test_easy_enum.py @@ -5,6 +5,7 @@ # # SPDX-License-Identifier: BSD-3-Clause +from spsdk.exceptions import SPSDKError import pytest from spsdk.utils.easy_enum import Enum @@ -15,9 +16,18 @@ class TestEnum(Enum): TWO = (2, "TheTwo", "Just two.") +class TestEnum2(TestEnum): + THREE = 3 + FOUR = (4, "TheFour", "Just four.") + + def test_simple() -> None: assert TestEnum.ONE == 1 assert TestEnum.TWO == 2 + assert TestEnum2.ONE == 1 + assert TestEnum2.TWO == 2 + assert TestEnum2.THREE == 3 + assert TestEnum2.FOUR == 4 def test_get() -> None: @@ -44,6 +54,35 @@ def test_get() -> None: with pytest.raises(KeyError): TestEnum["three"] + # value -> name + assert TestEnum2.get(1) == "ONE" + assert TestEnum2[2] == "TheTwo" + assert TestEnum2.get(TestEnum.ONE) == "ONE" + assert TestEnum2[TestEnum.TWO] == "TheTwo" + assert TestEnum2.get(3) == "THREE" + assert TestEnum2[4] == "TheFour" + assert TestEnum2.get(TestEnum2.THREE) == "THREE" + assert TestEnum2[TestEnum2.FOUR] == "TheFour" + # non-existing value + assert TestEnum2.get(5) is None + assert TestEnum.get(5, 5) == 5 + with pytest.raises(KeyError): + TestEnum[5] + # name -> value + assert TestEnum2.get("ONE") == 1 + assert TestEnum2.get("TheTwo") == 2 + assert TestEnum2.get("three", 3) == 3 + assert TestEnum2.get("FIVE") is None + with pytest.raises(KeyError): + TestEnum["FIVE"] + # case insensitive + assert TestEnum2.get("one") == 1 + assert TestEnum2["thetwo"] == 2 + assert TestEnum2["three"] == 3 + assert TestEnum2["thefour"] == 4 + with pytest.raises(KeyError): + TestEnum2["five"] + def test_desc() -> None: # desc for value @@ -58,6 +97,24 @@ def test_desc() -> None: # invalid key assert TestEnum.desc("three") == "" assert TestEnum.desc("three", "default") == "default" + # desc for value + assert TestEnum2.desc(1) == "" + assert TestEnum2.desc(TestEnum2.TWO) == "Just two." + assert TestEnum2.desc(3) == "" + assert TestEnum2.desc(TestEnum2.FOUR) == "Just four." + # desc for name + assert TestEnum2.desc("ONE") == "" + assert TestEnum2.desc("thetwo") == "Just two." + assert TestEnum2.desc("THREE") == "" + assert TestEnum2.desc("thefour") == "Just four." + # default + assert TestEnum2.desc("ONE", "default") == "" + assert TestEnum2.desc("thetwo", "default") == "Just two." + assert TestEnum2.desc("THREE", "default") == "" + assert TestEnum2.desc("thefour", "default") == "Just four." + # invalid key + assert TestEnum2.desc("five") == "" + assert TestEnum2.desc("five", "default") == "default" def test_name() -> None: @@ -71,6 +128,26 @@ def test_name() -> None: assert TestEnum.name(3, "three") == "three" with pytest.raises(KeyError): TestEnum[3] + # test TestEnum2 + assert TestEnum2.name(1) == "ONE" + assert TestEnum2.name(2) == "TheTwo" + assert TestEnum2.name(TestEnum2.ONE) == "ONE" + assert TestEnum2.name(TestEnum2.TWO) == "TheTwo" + assert TestEnum2.name(3) == "THREE" + assert TestEnum2.name(4) == "TheFour" + assert TestEnum2.name(TestEnum2.ONE) == "ONE" + assert TestEnum2.name(TestEnum2.TWO) == "TheTwo" + assert TestEnum2.name(TestEnum2.THREE) == "THREE" + assert TestEnum2.name(TestEnum2.FOUR) == "TheFour" + # default + assert TestEnum2.name(1, "?") == "ONE" + assert TestEnum2.name(TestEnum2.TWO, "?") == "TheTwo" + assert TestEnum2.name(3, "?") == "THREE" + assert TestEnum2.name(4, "?") == "TheFour" + assert TestEnum2.name(5, "five") == "five" + assert TestEnum2.name(TestEnum2.FOUR, "?") == "TheFour" + with pytest.raises(KeyError): + TestEnum2[5] def test_tags() -> None: @@ -85,15 +162,35 @@ def test_tags() -> None: assert val[0] == TestEnum.name(val[1]) assert val[2] == TestEnum.desc(val[1]) + tags = TestEnum2.tags() + assert tags is not None + assert len(tags) == 4 + assert tags[0] == 1 + assert tags[1] == 2 + assert tags[2] == 3 + assert tags[3] == 4 + # test iter even it is deprecated + for index, val in enumerate(TestEnum2): + assert index + 1 == val[1] + assert val[0] == TestEnum2.name(val[1]) + assert val[2] == TestEnum2.desc(val[1]) + def test_from_int() -> None: assert TestEnum.from_int(1) == TestEnum.ONE assert TestEnum.from_int(2) == TestEnum.TWO - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): TestEnum.from_int(3) - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): TestEnum.from_int(0) + assert TestEnum2.from_int(1) == TestEnum2.ONE + assert TestEnum2.from_int(2) == TestEnum2.TWO + assert TestEnum2.from_int(3) == TestEnum2.THREE + assert TestEnum2.from_int(4) == TestEnum2.FOUR + with pytest.raises(SPSDKError): + TestEnum.from_int(5) + def test_contains() -> None: # value @@ -110,3 +207,25 @@ def test_contains() -> None: # not contains assert "three" not in TestEnum assert 3 not in TestEnum + + # value + assert 1 in TestEnum2 + assert 2 in TestEnum2 + assert 3 in TestEnum2 + assert 4 in TestEnum2 + # enum + assert TestEnum2.ONE in TestEnum2 + assert TestEnum2.TWO in TestEnum2 + assert TestEnum2.THREE in TestEnum2 + assert TestEnum2.FOUR in TestEnum2 + # name + assert "ONE" in TestEnum2 + assert "TheTwo" in TestEnum2 + assert "THREE" in TestEnum2 + assert "TheFour" in TestEnum2 + # case sensitive + assert "one" not in TestEnum2 + assert "three" not in TestEnum2 + # not contains + assert "five" not in TestEnum2 + assert 5 not in TestEnum2 diff --git a/tests/utils/test_misc.py b/tests/utils/test_misc.py index a7a485b3..44bc2205 100644 --- a/tests/utils/test_misc.py +++ b/tests/utils/test_misc.py @@ -6,27 +6,31 @@ # SPDX-License-Identifier: BSD-3-Clause import filecmp import os - +import time from typing import Union import pytest +from spsdk import SPSDKError +from spsdk.exceptions import SPSDKValueError +from spsdk.utils.exceptions import SPSDKTimeoutError from spsdk.utils.misc import ( + Timeout, align, align_block, align_block_fill_random, + change_endianism, extend_block, find_first, + format_value, + get_bytes_cnt_of_int, load_binary, load_file, - write_file, - format_value, reverse_bytes_in_longs, - get_bytes_cnt_of_int, - change_endianism, - value_to_int, - value_to_bytes, value_to_bool, + value_to_bytes, + value_to_int, + write_file, ) @@ -114,13 +118,13 @@ def test_align_block_invalid_input(): """Test invalid inputs for misc.align_block()""" with pytest.raises(AssertionError): align_block(None) - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError, match="Wrong alignment"): align_block(b"", -1) - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError, match="Wrong alignment"): align_block(b"", 0) - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError, match="Wrong padding"): align_block(b"", 1, -2) - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError, match="Wrong padding"): align_block(b"", 1, 256) @@ -148,16 +152,16 @@ def test_add_padding(test_input: bytes, length: int, padding: int, expected: byt def test_add_padding_invalid_input(): """Test invalid inputs for misc.align_block()""" # negative length - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): extend_block(b"", -1) # length < current length - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): extend_block(b"\x00\x00", 1) # padding > 255 - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): extend_block(b"\x00\x00", 1, 256) # padding < 0 - with pytest.raises(AssertionError): + with pytest.raises(SPSDKError): extend_block(b"\x00\x00", 1, -1) @@ -230,7 +234,7 @@ def test_reg_long_reverse(): assert reverse_bytes_in_longs(test_val_ret) == test_val test_val1 = b"\x01\x02\x03\x04\x11\x12" - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): reverse_bytes_in_longs(test_val1) @@ -288,7 +292,7 @@ def test_change_endianism(value, res, exc): if not exc: assert res == change_endianism(value) else: - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): change_endianism(value) @@ -314,7 +318,7 @@ def test_value_to_int(value, res, exc): if not exc: assert res == value_to_int(value) else: - with pytest.raises(TypeError): + with pytest.raises(SPSDKError): value_to_int(value) @@ -340,7 +344,7 @@ def test_value_to_bytes(value, res, exc): if not exc: assert res == value_to_bytes(value) else: - with pytest.raises(TypeError): + with pytest.raises(SPSDKError): value_to_bytes(value) @@ -362,5 +366,34 @@ def test_value_to_bool(value, res, exc): if not exc: assert res == value_to_bool(value) else: - with pytest.raises(TypeError): + with pytest.raises(SPSDKError): value_to_bool(value) + + +def test_timeout_basic(): + """Basic test of timeout.""" + timeout = Timeout(100, "ms") + assert not timeout.overflow() + time.sleep(0.1) + with pytest.raises(SPSDKTimeoutError): + timeout.overflow(True) + + +def test_timeout_invalid_unit(): + """Test of timeout class - invalid unit.""" + with pytest.raises(SPSDKValueError): + Timeout(100, "day") + + +def test_timeout_get_time(): + """Basic test of timeout.""" + timeout = Timeout(50, "ms") + assert timeout.get_consumed_time() < timeout.get_rest_time() + assert timeout.get_consumed_time_ms() < timeout.get_rest_time_ms() + time.sleep(0.1) + assert timeout.get_rest_time() < 0 + assert timeout.get_rest_time_ms() < 0 + with pytest.raises(SPSDKTimeoutError): + timeout.get_rest_time(True) + with pytest.raises(SPSDKTimeoutError): + timeout.get_rest_time_ms(True) diff --git a/tests/utils/test_nxpdevscan.py b/tests/utils/test_nxpdevscan.py index 48273531..686ac151 100644 --- a/tests/utils/test_nxpdevscan.py +++ b/tests/utils/test_nxpdevscan.py @@ -8,14 +8,12 @@ from unittest.mock import MagicMock, patch import pytest -import spsdk.utils.nxpdevscan as nds -import spsdk.utils.devicedescription as devicedescription - -from spsdk.mboot.interfaces.uart import Uart as SDP_Uart - from serial import Serial from serial.tools.list_ports_common import ListPortInfo +import spsdk.utils.devicedescription as devicedescription +import spsdk.utils.nxpdevscan as nds + def test_usb_device_search(): """Test, that search method returns all NXP devices based on their VID. @@ -56,7 +54,7 @@ def test_usb_device_search(): devicedescription.USBDeviceDescription(0x15A2, 0, "", "", "", ""), ] - with patch("hid.enumerate", MagicMock(return_value=test_vector)): + with patch("libusbsio.LIBUSBSIO.HIDAPI_Enumerate", MagicMock(return_value=test_vector)): devices = nds.search_nxp_usb_devices() assert len(devices) == len(result) @@ -120,7 +118,7 @@ def test_usb_device_search_extended(): devicedescription.USBDeviceDescription(0x0002, 0, "", "", "", ""), devicedescription.USBDeviceDescription(0x15A2, 0, "", "", "", ""), ] - with patch("hid.enumerate", MagicMock(return_value=test_vector)): + with patch("libusbsio.LIBUSBSIO.HIDAPI_Enumerate", MagicMock(return_value=test_vector)): devices = nds.search_nxp_usb_devices([0x2]) assert len(devices) == len(result) @@ -182,7 +180,7 @@ def test_uart_device_search(): ], ) def test_get_device_name(vid, pid, expected_result): - """Verify search works and returns appropripate name based on VID/PID""" + """Verify search works and returns appropriate name based on VID/PID""" assert devicedescription.get_usb_device_name(vid, pid) == expected_result diff --git a/tests/utils/test_reg_config.py b/tests/utils/test_reg_config.py index 5e6858c4..90849f2c 100644 --- a/tests/utils/test_reg_config.py +++ b/tests/utils/test_reg_config.py @@ -7,6 +7,9 @@ """ Tests for registers utility.""" import os +import pytest + +from spsdk import SPSDKError from spsdk.utils.reg_config import RegConfig @@ -216,3 +219,11 @@ def test_reg_config_get_grouped_registers(data_dir): assert grouped_registers[0]["name"] == "DeviceTest" grouped_registers = reg_config.get_grouped_registers("test_device2") assert grouped_registers[0]["name"] == "Test" + + +def test_reg_invalid(data_dir): + reg_config = RegConfig(os.path.join(data_dir, "reg_config_invalid.json")) + with pytest.raises(SPSDKError, match="Invalid seal start address name"): + reg_config.get_seal_start_address("test_device1") + with pytest.raises(SPSDKError, match="Invalid seal count"): + reg_config.get_seal_count("test_device1") diff --git a/tests/utils/test_registers.py b/tests/utils/test_registers.py index 8ed7c4d9..2d0ed79b 100644 --- a/tests/utils/test_registers.py +++ b/tests/utils/test_registers.py @@ -12,6 +12,7 @@ import pytest from ruamel.yaml import YAML +from spsdk import SPSDKError from spsdk.utils.exceptions import ( SPSDKRegsError, SPSDKRegsErrorBitfieldNotFound, @@ -130,7 +131,7 @@ def test_basic_regs(tmpdir): # The Registers MUST return empty array assert regs.get_reg_names() == [] - with pytest.raises(TypeError): + with pytest.raises(SPSDKError): regs.remove_register("String") with pytest.raises(ValueError): @@ -147,7 +148,7 @@ def test_basic_regs(tmpdir): assert regt == reg1 - with pytest.raises(TypeError): + with pytest.raises(SPSDKError): regs.add_register("Invalid Parameter") regt.set_value(TEST_REG_VALUE) @@ -246,10 +247,12 @@ def test_register_invalid_val(): ) val = reg.get_value() - reg.set_value("Invalid") + with pytest.raises(SPSDKError): + reg.set_value("Invalid") assert reg.get_value() == val - reg.set_value([1, 2]) + with pytest.raises(SPSDKError): + reg.set_value([1, 2]) assert reg.get_value() == val @@ -304,12 +307,9 @@ def test_enum_bytes(): def test_enum_invalidval(): """Enum test with INVALID value.""" - try: - enum = RegsEnum(TEST_ENUM_NAME, "InvalidValue", TEST_ENUM_DESCR, TEST_ENUM_MAXWIDTH) - printed_str = str(enum) - assert "N/A" in printed_str - except TypeError: - assert 0 + enum = RegsEnum(TEST_ENUM_NAME, "InvalidValue", TEST_ENUM_DESCR, TEST_ENUM_MAXWIDTH) + printed_str = str(enum) + assert "N/A" in printed_str def test_bitfield(): @@ -488,7 +488,7 @@ def test_bitfield_value(): bitfield.set_value(TEST_BITFIELD_SAVEVAL) assert bitfield.get_value() == TEST_BITFIELD_SAVEVAL - with pytest.raises(ValueError): + with pytest.raises(SPSDKError): bitfield.set_value(TEST_BITFIELD_OUTOFRANGEVAL) @@ -595,7 +595,7 @@ def test_bitfield_enums_invalid_name(): parent_reg.add_bitfield(bitfield) bitfield.add_enum(RegsEnum(f"{TEST_ENUM_NAME}", 0, f"{TEST_ENUM_DESCR}", TEST_BITFIELD_WIDTH)) - with pytest.raises(SPSDKRegsErrorEnumNotFound): + with pytest.raises(SPSDKError): bitfield.set_enum_value(f"Invalid Enum name") @@ -629,11 +629,11 @@ def test_registers_corrupted_xml(data_dir): """Test registers XML support with invalid data.""" regs = Registers(TEST_DEVICE_NAME) - with pytest.raises(SPSDKRegsError): + with pytest.raises(SPSDKError): with use_working_directory(data_dir): regs.load_registers_from_xml("registers_corr.xml") - with pytest.raises(SPSDKRegsError): + with pytest.raises(SPSDKError): with use_working_directory(data_dir): regs.load_registers_from_xml("registers_corr2.xml") diff --git a/tests/utils/test_usbfilter.py b/tests/utils/test_usbfilter.py index a8d3bb47..cd5e03da 100644 --- a/tests/utils/test_usbfilter.py +++ b/tests/utils/test_usbfilter.py @@ -4,7 +4,6 @@ # Copyright 2021 NXP # # SPDX-License-Identifier: BSD-3-Clause -import re from unittest.mock import MagicMock, patch import pytest diff --git a/tools/approved_packages.json b/tools/approved_packages.json index 7b075aa4..50c1819a 100644 --- a/tools/approved_packages.json +++ b/tools/approved_packages.json @@ -143,10 +143,10 @@ "is_manual": false }, { - "name": "hidapi", - "license": "BSD", - "home_page": "https://github.com/trezor/cython-hidapi", - "is_manual": true + "name": "libusbsio", + "license": "BSD-3-Clause", + "home_page": "https://pypi.org/project/libusbsio/", + "is_manual": false }, { "name": "humanfriendly", diff --git a/tools/checker_dependencies.py b/tools/checker_dependencies.py index b644e85e..593db96b 100644 --- a/tools/checker_dependencies.py +++ b/tools/checker_dependencies.py @@ -9,17 +9,20 @@ import argparse import itertools import json +import math import os import sys from typing import Dict, Iterator, List, NamedTuple, Optional, Union, no_type_check -from pip._internal.commands.show import search_packages_info +from pip import __version__ as pip_version +from pip._internal.commands.show import _PackageInfo, search_packages_info APPROVED_LICENSES_FILE_NAME = "approved_packages.json" APPROVED_LICENSES_FILE = os.path.abspath( os.path.join(os.path.dirname(__file__), APPROVED_LICENSES_FILE_NAME) ) ROOT_PACKAGE = "spsdk" +MIN_PIP_VERSION = "21.2.0" class DependencyInfo(NamedTuple): @@ -28,7 +31,7 @@ class DependencyInfo(NamedTuple): name: str license: str home_page: str - is_manual: str + is_manual: bool def __str__(self) -> str: dep_info = f"{self.name:20} -> {self.license}" @@ -37,13 +40,13 @@ def __str__(self) -> str: return dep_info @staticmethod - def from_pgk_meta(pgk_meta_item: dict) -> "DependencyInfo": + def from_pgk_meta(pgk_meta_item: _PackageInfo) -> "DependencyInfo": """Extract data from package's meta info.""" return DependencyInfo( - name=pgk_meta_item["name"], - license=pgk_meta_item["license"], - home_page=pgk_meta_item["home-page"], - is_manual=pgk_meta_item["license"] == "UNKNOWN", + name=pgk_meta_item.name, + license=pgk_meta_item.license, + home_page=pgk_meta_item.homepage, + is_manual=pgk_meta_item.license == "UNKNOWN", ) @@ -98,8 +101,8 @@ def from_pip_meta(root_package: str = ROOT_PACKAGE) -> "DependenciesList": def _get_requires(module_names: List[str]) -> Iterator[List[str]]: """Get `requires` fields from given set of package names.""" for pkg_info in search_packages_info(module_names): - yield pkg_info["requires"] - yield from DependenciesList._get_requires(pkg_info["requires"]) + yield pkg_info.requires + yield from DependenciesList._get_requires(pkg_info.requires) def parse_inputs(input_args: List[str] = None) -> dict: @@ -118,7 +121,7 @@ def parse_inputs(input_args: List[str] = None) -> dict: # Mypy really doesn't like dictionary unpacking commands_parser = parser.add_subparsers(**commands_parser_opts) # type: ignore commands_parser.add_parser("print", help="Only print dependencies and their licenses") - commands_parser.add_parser("print_lic", help="Only print licenses of dependencies") + commands_parser.add_parser("print-lic", help="Only print licenses of dependencies") commands_parser.add_parser("check", help="Check whether all dependencies are approved") commands_parser.add_parser("init", help="Initialize the approved licenses list file") parser.add_argument( @@ -219,15 +222,61 @@ def init_approved_file(actual_list: DependenciesList) -> int: return 0 +def numberify_version(version: str, separator: str = ".") -> int: + """Turn version string into a number. + + Each group is weighted by a multiple of 1000 + + 1.2.3 -> 1 * 1_000_000 + 2 * 1_000 + 3 * 1 = 1_002_003 + 21.100.9 -> 21 * 1_000_000 + 100 * 1_000 + 9 * 1 = 21_100_009 + + :param version: Version string numbers separated by `separator` + :param separator: Separator used in the version string, defaults to "." + :return: Number representing the version + """ + sanitized_version = sanitize_version(version=version, separator=separator, valid_numbers=3) + return int( + sum( + int(number) * math.pow(10, 3 * order) + for order, number in enumerate(reversed(sanitized_version.split(separator))) + ) + ) + + +def sanitize_version(version: str, separator: str = ".", valid_numbers: int = 3) -> str: + """Sanitize version string. + + Append '.0' in case version string has fewer parts than `valid_numbers` + Remove right-most version parts after `valid_numbers` amount of parts + + 1.2 -> 1.2.0 + 1.2.3.4 -> 1.2.3 + + :param version: Original version string + :param separator: Separator used in the version string, defaults to "." + :param valid_numbers: Amount of numbers to sanitize, defaults to 3 + :return: Sanitized version string + """ + version_parts = version.split(separator) + version_parts += ["0"] * (valid_numbers - len(version_parts)) + return separator.join(version_parts[:valid_numbers]) + + def main() -> int: """Main function.""" + if numberify_version(pip_version) < numberify_version(MIN_PIP_VERSION): + print("Please install newer version of pip") + print(f"Minimum version required: {MIN_PIP_VERSION}, you have: {pip_version}") + print("To update pip run: 'python -m pip install --upgrade pip'") + return 1 + args = parse_inputs() actual_dep_list = DependenciesList.from_pip_meta(root_package=args["root_package"]) handlers = { "print": print_dependencies, - "print_lic": print_licenses, + "print-lic": print_licenses, "check": check_dependencies, "init": init_approved_file, } diff --git a/tools/pyinstaller/elftosb_version_info.txt b/tools/pyinstaller/elftosb_version_info.txt new file mode 100644 index 00000000..0c6ed648 --- /dev/null +++ b/tools/pyinstaller/elftosb_version_info.txt @@ -0,0 +1,43 @@ +# UTF-8 +# +# For more details about fixed file info 'ffi' see: +# http://msdn.microsoft.com/en-us/library/ms646997.aspx +VSVersionInfo( + ffi=FixedFileInfo( + # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) + # Set not needed items to zero 0. + filevers=(1, 0, 0, 0), + prodvers=(1, 0, 0, 0), + # Contains a bitmask that specifies the valid bits 'flags'r + mask=0x3f, + # Contains a bitmask that specifies the Boolean attributes of the file. + flags=0x1, + # The operating system for which this file was designed. + # 0x4 - NT and there is no need to change it. + OS=0x4, + # The general type of file. + # 0x1 - the file is an application. + fileType=0x1, + # The function of the file. + # 0x0 - the function is not defined for this fileType + subtype=0x0, + # Creation date and time stamp. + date=(0, 0) + ), + kids=[ + StringFileInfo( + [ + StringTable( + u'040904b0', + [StringStruct(u'CompanyName', u'NXP Semiconductors'), + StringStruct(u'FileDescription', u'MCUXpresso SPSDK Apps'), + StringStruct(u'FileVersion', u'1.0.0.0'), + StringStruct(u'InternalName', u'elftosb.exe'), + StringStruct(u'LegalCopyright', u'Copyright (C) 2020 NXP Semiconductors'), + StringStruct(u'OriginalFileName', u'elftosb.exe'), + StringStruct(u'ProductName', u'MCUXpresso SPSDK Apps'), + StringStruct(u'ProductVersion', u'1.0.0.0')]) + ]), + VarFileInfo([VarStruct(u'Translation', [1033, 1200])]) + ] +) diff --git a/tools/pyinstaller/hooks/hook-prettytable.py b/tools/pyinstaller/hooks/hook-lark.py similarity index 62% rename from tools/pyinstaller/hooks/hook-prettytable.py rename to tools/pyinstaller/hooks/hook-lark.py index ac9ea9fc..17671c3e 100644 --- a/tools/pyinstaller/hooks/hook-prettytable.py +++ b/tools/pyinstaller/hooks/hook-lark.py @@ -1,11 +1,11 @@ # -*- mode: python ; coding: utf-8 -*- # -# Copyright 2020 NXP +# Copyright 2021 NXP # # SPDX-License-Identifier: BSD-3-Clause """PyInstaller hook to collect metadata for package which iis not supported by PyInstaller yet.""" -from PyInstaller.utils.hooks import copy_metadata +from PyInstaller.utils.hooks import collect_data_files -datas = copy_metadata('prettytable') +datas = collect_data_files('lark') diff --git a/tools/pyinstaller/nxpcertgen_version_info.txt b/tools/pyinstaller/nxpcertgen_version_info.txt new file mode 100644 index 00000000..5f81f99d --- /dev/null +++ b/tools/pyinstaller/nxpcertgen_version_info.txt @@ -0,0 +1,43 @@ +# UTF-8 +# +# For more details about fixed file info 'ffi' see: +# http://msdn.microsoft.com/en-us/library/ms646997.aspx +VSVersionInfo( + ffi=FixedFileInfo( + # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) + # Set not needed items to zero 0. + filevers=(1, 0, 0, 0), + prodvers=(1, 0, 0, 0), + # Contains a bitmask that specifies the valid bits 'flags'r + mask=0x3f, + # Contains a bitmask that specifies the Boolean attributes of the file. + flags=0x1, + # The operating system for which this file was designed. + # 0x4 - NT and there is no need to change it. + OS=0x4, + # The general type of file. + # 0x1 - the file is an application. + fileType=0x1, + # The function of the file. + # 0x0 - the function is not defined for this fileType + subtype=0x0, + # Creation date and time stamp. + date=(0, 0) + ), + kids=[ + StringFileInfo( + [ + StringTable( + u'040904b0', + [StringStruct(u'CompanyName', u'NXP Semiconductors'), + StringStruct(u'FileDescription', u'MCUXpresso SPSDK Apps'), + StringStruct(u'FileVersion', u'1.0.0.0'), + StringStruct(u'InternalName', u'nxpcertgen.exe'), + StringStruct(u'LegalCopyright', u'Copyright (C) 2020 NXP Semiconductors'), + StringStruct(u'OriginalFileName', u'nxpcertgen.exe'), + StringStruct(u'ProductName', u'MCUXpresso SPSDK Apps'), + StringStruct(u'ProductVersion', u'1.0.0.0')]) + ]), + VarFileInfo([VarStruct(u'Translation', [1033, 1200])]) + ] +) diff --git a/tools/pyinstaller/nxpdevhsm_version_info.txt b/tools/pyinstaller/nxpdevhsm_version_info.txt new file mode 100644 index 00000000..62a3fa14 --- /dev/null +++ b/tools/pyinstaller/nxpdevhsm_version_info.txt @@ -0,0 +1,43 @@ +# UTF-8 +# +# For more details about fixed file info 'ffi' see: +# http://msdn.microsoft.com/en-us/library/ms646997.aspx +VSVersionInfo( + ffi=FixedFileInfo( + # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) + # Set not needed items to zero 0. + filevers=(1, 0, 0, 0), + prodvers=(1, 0, 0, 0), + # Contains a bitmask that specifies the valid bits 'flags'r + mask=0x3f, + # Contains a bitmask that specifies the Boolean attributes of the file. + flags=0x1, + # The operating system for which this file was designed. + # 0x4 - NT and there is no need to change it. + OS=0x4, + # The general type of file. + # 0x1 - the file is an application. + fileType=0x1, + # The function of the file. + # 0x0 - the function is not defined for this fileType + subtype=0x0, + # Creation date and time stamp. + date=(0, 0) + ), + kids=[ + StringFileInfo( + [ + StringTable( + u'040904b0', + [StringStruct(u'CompanyName', u'NXP Semiconductors'), + StringStruct(u'FileDescription', u'MCUXpresso SPSDK Apps'), + StringStruct(u'FileVersion', u'1.0.0.0'), + StringStruct(u'InternalName', u'nxpdevhsm.exe'), + StringStruct(u'LegalCopyright', u'Copyright (C) 2021 NXP Semiconductors'), + StringStruct(u'OriginalFileName', u'nxpdevhsm.exe'), + StringStruct(u'ProductName', u'MCUXpresso SPSDK Apps'), + StringStruct(u'ProductVersion', u'1.0.0.0')]) + ]), + VarFileInfo([VarStruct(u'Translation', [1033, 1200])]) + ] +) diff --git a/tools/pyinstaller/shadowregs_version_info.txt b/tools/pyinstaller/shadowregs_version_info.txt new file mode 100644 index 00000000..d4544854 --- /dev/null +++ b/tools/pyinstaller/shadowregs_version_info.txt @@ -0,0 +1,43 @@ +# UTF-8 +# +# For more details about fixed file info 'ffi' see: +# http://msdn.microsoft.com/en-us/library/ms646997.aspx +VSVersionInfo( + ffi=FixedFileInfo( + # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) + # Set not needed items to zero 0. + filevers=(1, 0, 0, 0), + prodvers=(1, 0, 0, 0), + # Contains a bitmask that specifies the valid bits 'flags'r + mask=0x3f, + # Contains a bitmask that specifies the Boolean attributes of the file. + flags=0x1, + # The operating system for which this file was designed. + # 0x4 - NT and there is no need to change it. + OS=0x4, + # The general type of file. + # 0x1 - the file is an application. + fileType=0x1, + # The function of the file. + # 0x0 - the function is not defined for this fileType + subtype=0x0, + # Creation date and time stamp. + date=(0, 0) + ), + kids=[ + StringFileInfo( + [ + StringTable( + u'040904b0', + [StringStruct(u'CompanyName', u'NXP Semiconductors'), + StringStruct(u'FileDescription', u'MCUXpresso SPSDK Apps'), + StringStruct(u'FileVersion', u'1.0.0.0'), + StringStruct(u'InternalName', u'shadowregs.exe'), + StringStruct(u'LegalCopyright', u'Copyright (C) 2020 NXP Semiconductors'), + StringStruct(u'OriginalFileName', u'shadowregs.exe'), + StringStruct(u'ProductName', u'MCUXpresso SPSDK Apps'), + StringStruct(u'ProductVersion', u'1.0.0.0')]) + ]), + VarFileInfo([VarStruct(u'Translation', [1033, 1200])]) + ] +) diff --git a/tools/sr_xls2xml.py b/tools/sr_xls2xml.py index 025d6ad8..47afb511 100644 --- a/tools/sr_xls2xml.py +++ b/tools/sr_xls2xml.py @@ -9,7 +9,7 @@ import os import re import sys -from typing import Any, Tuple, Dict +from typing import Any, Dict, Tuple import click import openpyxl

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 N0#8>zmvv4FO#t3(h*AIm literal 167700 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%0DL&uNzd- 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