def test_bootimage_rt10xx_aead_nonce_len(): """Test `aead_nonce_len`""" assert BootImgRT.aead_nonce_len(0) == 13 assert BootImgRT.aead_nonce_len(0xFFFF) == 13 assert BootImgRT.aead_nonce_len(0x10000) == 12 assert BootImgRT.aead_nonce_len(0xFFFFFF) == 12 assert BootImgRT.aead_nonce_len(0x1000000) == 11
def test_bee_unsigned_sw_key(cpu_params: CpuParams) -> None: """Test encrypted XIP unsigned image with user keys. It is supposed the SRK_KEY_SEL fuse is burned. It is supposed the user key is burned in SW_GP2 fuses. :param cpu_params: processor specific parameters of the test """ img = BootImgRT(EXT_FLASH_ADDR) img.add_image( load_binary(cpu_params.data_dir, f'{cpu_params.board}_iled_blinky_ext_FLASH.bin')) # the following parameters are fixed for the test only, to produce stable result; for production use random number cntr1 = bytes.fromhex('112233445566778899AABBCC00000000') kib_key1 = bytes.fromhex('C1C2C3C4C5C6C7C8C9CACBCCCDCECFC0') kib_iv1 = bytes.fromhex('1112131415161718191A1B1C1D1E1F10') cntr2 = bytes.fromhex('2233445566778899AABBCCDD00000000') kib_key2 = bytes.fromhex('C1C2C3C4C5C6C7C8C9CACBCCCDCECFC2') kib_iv2 = bytes.fromhex('2122232425262728292A2B2C2D2E2F20') # Add two regions as an example (even this is probably not real use case) # BEE region 0 sw_key = bytes.fromhex('0123456789abcdeffedcba9876543210') region = BeeRegionHeader(BeeProtectRegionBlock(counter=cntr1), sw_key, BeeKIB(kib_key1, kib_iv1)) region.add_fac(BeeFacRegion(EXT_FLASH_ADDR + 0x1000, 0x2000)) region.add_fac(BeeFacRegion(EXT_FLASH_ADDR + 0x3800, 0x800)) img.bee.add_region(region) # BEE region 1 (this is just example, the is no code in the region) sw_key = bytes.fromhex('F123456789abcdeffedcba987654321F') region = BeeRegionHeader(BeeProtectRegionBlock(counter=cntr2), sw_key, BeeKIB(kib_key2, kib_iv2)) region.add_fac(BeeFacRegion(EXT_FLASH_ADDR + 0x100000, 0x1000)) img.bee.add_region(region) # out_name = cpu_params.board + '_iled_blinky_ext_FLASH_bee_userkey_unsigned.bin' write_image(cpu_params, out_name, img)
def init_flashloader(cpu_params: CpuParams) -> McuBoot: """Load an execute flash-loader binary in i.MX-RT The function signs the flashloader if needed (if HAB enabled) :param cpu_params: processor specific parameters of the test :return: McuBoot instance to communicate with flash-loader :raises ConnectionError: if connection cannot be established """ devs = mboot_scan_usb(cpu_params.com_processor_name ) # check whether flashloader is already running if len(devs) == 0: # if flash-loader not running yet, it must be downloaded to RAM and launched flshldr_img = BootImgRT.parse( load_binary(cpu_params.data_dir, 'ivt_flashloader.bin')) devs = sdp_scan_usb(cpu_params.com_processor_name) if len(devs) != 1: raise ConnectionError('Cannot connect to ROM bootloader') with SDP(devs[0], cmd_exception=True) as sd: assert sd.is_opened try: sd.read(INT_RAM_ADDR_CODE, 4) # dummy read to receive response except SdpCommandError: # there is an exception if HAB is locked, cause read not supported pass if (sd.status_code == StatusCode.HAB_IS_LOCKED) and (sd.response_value == ResponseValue.LOCKED): auth_flshldr_img = BootImgRT(flshldr_img.address, BootImgRT.IVT_OFFSET_OTHER) _to_authenticated_image( cpu_params, auth_flshldr_img, flshldr_img.app.data, 0, flshldr_img.ivt.app_address ) # entry addr cannot be detected from img else: auth_flshldr_img = flshldr_img assert sd.write_file(auth_flshldr_img.address, auth_flshldr_img.export()) try: assert sd.jump_and_run(auth_flshldr_img.address + auth_flshldr_img.ivt_offset) except SdpCommandError: pass # SDP may return an exception if HAB locked for _ in range(10): # wait 10 sec until flash-loader is inited sleep(1) # Scan for MCU-BOOT device devs = mboot_scan_usb(cpu_params.com_processor_name) if len(devs) == 1: break if len(devs) != 1: raise ConnectionError('Cannot connect to Flash-Loader') result = McuBoot(devs[0], cmd_exception=True) result.open() assert result.is_opened result.reopen = False # reopen not supported for RT1050??? return result
def write_image( cpu_params: CpuParams, image_file_name: str, img: BootImgRT, otpmk_bee_regions: Tuple[BeeFacRegion, ...] = tuple(), ) -> None: """Write image to the external flash The method behaviour depends on TEST_IMG_CONTENT: - if True (TEST MODE), the method generates the image and compare with previous version - if False (PRODUCTION), the method generates the image and burn into FLASH :param cpu_params: processor specific parameters of the test :param image_file_name: of the image to be written (including file extension) :param img: image instance to be written :param otpmk_bee_regions: optional list of BEE regions for BEE OTPMK encryption """ path = os.path.join(cpu_params.data_dir, OUTPUT_IMAGES_SUBDIR, image_file_name) debug_info = DebugInfo() # use zulu datetime for test purposes only, to produce stable output; remove the parameter for production zulu = datetime(year=2020, month=4, day=8, hour=5, minute=54, second=33, tzinfo=timezone.utc) img_data = img.export(dbg_info=debug_info, zulu=zulu) assert len(img_data) == img.size write_dbg_log(cpu_params.data_dir, image_file_name, debug_info.lines, TEST_IMG_CONTENT) if TEST_IMG_CONTENT: assert img.info() # quick check info prints non-empty output compare_bin_files(path, img_data) # compare no-padding if (NO_PADDING and img.fcb.enabled and isinstance(img.fcb, PaddingFCB) and not img.bee_encrypted): img.fcb.enabled = False compare_bin_files(path.replace(".bin", "_nopadding.bin"), img.export(zulu=zulu)) img.fcb.enabled = False # test that parse image will return same content if img.fcb.enabled and not img.bee_encrypted: compare_bin_files(path, BootImgRT.parse(img_data).export()) # test that size matches len of exported data assert img.size == len(img_data) else: with open(path, "wb") as f: f.write(img_data) if (NO_PADDING and img.fcb.enabled and isinstance(img.fcb, PaddingFCB) and not img.bee_encrypted): with open(path.replace(".bin", "_nopadding.bin"), "wb") as f: f.write(img_data[img.ivt_offset:]) if img.ivt_offset == BootImgRT.IVT_OFFSET_NOR_FLASH: _burn_image_to_flash(cpu_params, img, img_data, otpmk_bee_regions) else: assert len(otpmk_bee_regions) == 0 _burn_image_to_sd(cpu_params, img, img_data)
def test_bootimage_rt10xx_basic(): """Simple test for BootImgRT""" img = BootImgRT(0x60000000) assert img.info() dbg_info = DebugInfo() img_data = img.export(dbg_info=dbg_info) # test parser returns same result dbg_info2 = DebugInfo() img_data2 = BootImgRT.parse(img_data).export(dbg_info=dbg_info2) assert dbg_info.lines == dbg_info2.lines assert img_data == img_data2
def test_bootimage_rt10xx_add_encrypted_image(): """Test add_image with encryption parameters""" img = BootImgRT(0x20000000) test_app_data = bytes([0]) * 1024 img.add_image(test_app_data, address=0x20000000, dek_key=b"") assert len(img.dek_key) == 16 # test invalid dek key length img = BootImgRT(0x20000000) with pytest.raises(SPSDKError): img.add_image(test_app_data, address=0x20000000, dek_key=b"x") # test image already added with pytest.raises(SPSDKError): img.add_image(test_app_data, address=0x20000000, dek_key=b"0123456789123456")
def test_signed_flashloader(cpu_params: CpuParams) -> None: """Test creation of signed FLASHLOADER image :param cpu_params: processor specific parameters of the test """ assert TEST_IMG_CONTENT # this should be used in test mode only to verify the flashloader image creation process image_name = 'ivt_flashloader' tgt_address = INT_RAM_ADDR_CODE flashloader_unsigned_img = BootImgRT.parse(load_binary(cpu_params.data_dir, image_name + '.bin')) app_data = flashloader_unsigned_img.app.data assert app_data boot_img = BootImgRT(tgt_address, BootImgRT.IVT_OFFSET_OTHER) _to_authenticated_image(cpu_params, boot_img, app_data, 0, flashloader_unsigned_img.ivt.app_address) write_image(cpu_params, image_name + '_signed.bin', boot_img)
def test_sb(cpu_params: CpuParams) -> None: """Test creation of SB file. :param cpu_params: processor specific parameters of the test """ # timestamp is fixed for the test, do not not for production timestamp = datetime(year=2020, month=4, day=24, hour=16, minute=33, second=32) # load application to add into SB img_name = f'{cpu_params.board}_iled_blinky_ext_FLASH_unsigned_nofcb' app_data = load_binary(cpu_params.data_dir, OUTPUT_IMAGES_SUBDIR, img_name + '.bin') boot_img = BootImgRT.parse(app_data) # parse to retrieve IVT offset sb = SecureBootV1(version=SB_FILE_VERSION, timestamp=timestamp) sect = BootSectionV1(0, SecureBootFlagsV1.ROM_SECTION_BOOTABLE) # load 0xc0233007 > 0x2000; sect.append(CmdFill(INT_RAM_ADDR_DATA, pack("<I", cpu_params.ext_flash_cfg_word0))) # enable flexspinor 0x2000; sect.append(CmdMemEnable(INT_RAM_ADDR_DATA, 4, ExtMemId.FLEX_SPI_NOR)) # erase 0x60000000..0x60100000; sect.append(CmdErase(EXT_FLASH_ADDR, align(boot_img.ivt_offset + boot_img.size, 0x1000))) # load 0xf000000f > 0x3000; sect.append(CmdFill(INT_RAM_ADDR_DATA, pack("<I", FCB_FLASH_NOR_CFG_WORD))) # enable flexspinor 0x3000; sect.append(CmdMemEnable(INT_RAM_ADDR_DATA, 4, ExtMemId.FLEX_SPI_NOR)) # load myBinFile > kAbsAddr_Ivt; app_data += b'\x49\x20\x93\x8e\x89\x8F\x43\x88' # this is random padding fixed for the test, not use for production sect.append(CmdLoad(EXT_FLASH_ADDR + boot_img.ivt_offset, app_data)) # sb.append(sect) # write_sb(cpu_params, img_name + '.sb', sb)
def test_sdhc(cpu_params: CpuParams, image_name: str, tgt_address: int, dcd: Optional[str], plain0_signed1_encr2: int) -> None: """ Test creation of unsigned image :param cpu_params: processor specific parameters of the test :param image_name: filename of the source image; without file extension; without board prefix :param tgt_address: address, where the image will be located in the memory (start address of the memory) :param dcd: file name of the DCD file to be included in the image; None if no DCD needed :param plain0_signed1_encr2: 0 for unsigned; 1 for signed; 2 for encrypted """ image_name = cpu_params.board + '_' + image_name # create bootable image object app_data = load_binary(cpu_params.data_dir, image_name + '.bin') boot_img = BootImgRT(tgt_address, BootImgRT.IVT_OFFSET_OTHER) boot_img.fcb = PaddingFCB(0, enabled=False) if dcd: boot_img.add_dcd_bin(load_binary(cpu_params.data_dir, dcd)) if plain0_signed1_encr2 == 0: boot_img.add_image(app_data) suffix = '_sdhc_unsigned.bin' elif plain0_signed1_encr2 == 1: _to_authenticated_image(cpu_params, boot_img, app_data, 0) suffix = '_sdhc_signed.bin' elif plain0_signed1_encr2 == 2: _to_authenticated_image(cpu_params, boot_img, app_data, 0) suffix = '_sdhc_encrypted.bin' else: assert False # write image to disk and to processor write_image(cpu_params, image_name + suffix, boot_img)
def test_sb_multiple_sections(cpu_params: CpuParams) -> None: """Test creation of SB file with multiple sections. :param cpu_params: processor specific parameters of the test """ if (cpu_params.id != ID_RT1050) and (cpu_params.id != ID_RT1060): return # this test case is supported only for RT1050 and RT1060 # timestamp is fixed for the test, do not not for production timestamp = datetime(year=2020, month=4, day=24, hour=15, minute=33, second=32, tzinfo=timezone.utc) # load application to add into SB img_name = f"{cpu_params.board}_iled_blinky_ext_FLASH_unsigned_nofcb" app_data = load_binary(cpu_params.data_dir, OUTPUT_IMAGES_SUBDIR, img_name + ".bin") boot_img = BootImgRT.parse(app_data) # parse to retrieve IVT offset sb = SecureBootV1(version=SB_FILE_VERSION, timestamp=timestamp) sect = BootSectionV1(0, SecureBootFlagsV1.ROM_SECTION_BOOTABLE) # load 0xc0233007 > 0x2000; sect.append( CmdFill( INT_RAM_ADDR_DATA, int.from_bytes(pack("<I", cpu_params.ext_flash_cfg_word0), "little"))) # enable flexspinor 0x2000; sect.append(CmdMemEnable(INT_RAM_ADDR_DATA, 4, ExtMemId.FLEX_SPI_NOR)) # erase 0x60000000..0x60010000; # Note: erasing of long flash region may fail on timeout # For example this fails on EVK-RT1060: sect.append(CmdErase(EXT_FLASH_ADDR, 0x100000)) sect.append(CmdErase(EXT_FLASH_ADDR, 0x10000)) # load 0xf000000f > 0x3000; sect.append( CmdFill(INT_RAM_ADDR_DATA, int.from_bytes(pack("<I", FCB_FLASH_NOR_CFG_WORD), "little"))) # enable flexspinor 0x3000; sect.append(CmdMemEnable(INT_RAM_ADDR_DATA, 4, ExtMemId.FLEX_SPI_NOR)) # load myBinFile > kAbsAddr_Ivt; app_data += b"\xdc\xe8\x6d\x5d\xe9\x8c\xf5\x7c" # this is random padding fixed for the test, not use for production sect.append(CmdLoad(EXT_FLASH_ADDR + boot_img.ivt_offset, app_data)) # sb.append(sect) # add second section, just for the test sect2 = BootSectionV1(1, SecureBootFlagsV1.ROM_SECTION_BOOTABLE) sect2.append( CmdLoad(0x6000F000, load_binary(cpu_params.srk_data_dir, "SRK_hash_table.bin"))) sb.append(sect2) # write_sb(cpu_params, "sb_file_2_sections" + ".sb", sb)
def test_signed(cpu_params: CpuParams, srk_key_index: int) -> None: """Test creation of signed image :param cpu_params: processor specific parameters of the test :param srk_key_index: index of the SRK key used """ image_name = f'{cpu_params.board}_iled_blinky_ext_FLASH' tgt_address = EXT_FLASH_ADDR boot_img = BootImgRT(tgt_address) app_data = load_binary(cpu_params.data_dir, image_name + '.bin') _to_authenticated_image(cpu_params, boot_img, app_data, srk_key_index) write_image(cpu_params, image_name + f'_signed_key{str(srk_key_index + 1)}.bin', boot_img)
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_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)
def test_bee_otmpk(cpu_params: CpuParams) -> None: """Test creation of signed image BEE encrypted using master key. It is supposed the SRK fuses are burned and HAB is enabled. It is supposed the SRK_KEY_SEL fuse is burned. :param cpu_params: processor specific parameters of the test """ image_name = f'{cpu_params.board}_iled_blinky_ext_FLASH' tgt_address = EXT_FLASH_ADDR boot_img = BootImgRT(tgt_address) boot_img.fcb.enabled = False app_data = load_binary(cpu_params.data_dir, image_name + '.bin') _to_authenticated_image(cpu_params, boot_img, app_data, 0) # use signed image write_image(cpu_params, image_name + '_bee_otmpk.bin', boot_img, (BeeFacRegion(EXT_FLASH_ADDR + 0x1000, 0x2000), ))
def test_hab_encrypted(cpu_params: CpuParams) -> None: """Test HAB encrypted image. :param cpu_params: processor specific parameters of the test """ image_name = f'{cpu_params.board}_iled_blinky_int_RAM' tgt_address = INT_RAM_ADDR_CODE srk_key_index = 0 app_data = load_binary(cpu_params.data_dir, image_name + '.bin') # Encryption params (nonce + dek): use only for test purpose, for production use None nonce = bytes.fromhex('24eb311ce02a61d74cad460739') dek = load_binary(cpu_params.rt10xx_data_dir, f'hab_dek.bin') # boot_img = BootImgRT(tgt_address) _to_authenticated_image(cpu_params, boot_img, app_data, srk_key_index, -1, dek, nonce) assert boot_img.dek_ram_address == 0x20008000 assert boot_img.dek_img_offset == 0x8000 write_image(cpu_params, image_name + f'_encrypted_key{str(srk_key_index + 1)}.bin', boot_img)
def main() -> None: """Main function.""" # Create Boot Image instance img = BootImgRT(address=0x20000000, version=0x40) # Add DCD segment with open(f"{DATA_DIR}/dcd.txt", "r") as f_txt: img.dcd = SegDCD.parse_txt(f_txt.read()) # Add application segment with open(f"{DATA_DIR}/plain_load_ivt_flashloader.bin", "rb") as f_bin: img.add_image(data=f_bin.read()) # Print image info print(img.info()) # Save into file with open(f"{DATA_DIR}/flashloader.imx", "wb") as f: f.write(img.export())
def test_nor_flash_fcb(cpu_params: CpuParams, fcb: bool) -> None: """Test unsigned image with FCB NOR FLASH block :param cpu_params: processor specific parameters of the test :param fcb: True to include FCB block to output image; False to exclude """ image_name = f'{cpu_params.board}_iled_blinky_ext_FLASH' # create bootable image object app_data = load_binary(cpu_params.data_dir, image_name + '.bin') boot_img = BootImgRT(EXT_FLASH_ADDR) boot_img.add_image(app_data) if fcb: boot_img.set_flexspi_fcb(load_binary(cpu_params.data_dir, 'flex_spi.fcb')) else: boot_img.fcb = PaddingFCB(0, enabled=False) # write image to disk and to processor suffix = '_unsigned_fcb.bin' if fcb else '_unsigned_nofcb.bin' write_image(cpu_params, image_name + suffix, boot_img)
def test_unsigned(cpu_params: CpuParams, image_name: str, tgt_address: int, dcd: Optional[str]) -> None: """ Test creation of unsigned image :param cpu_params: processor specific parameters of the test :param image_name: filename of the source image; without file extension; without {board} prefix :param tgt_address: address, where the image will be located in the memory (start address of the memory) :param dcd: file name of the DCD file to be included in the image; None if no DCD needed """ image_name = cpu_params.board + '_' + image_name # create bootable image object app_data = load_binary(cpu_params.data_dir, image_name + '.bin') boot_img = BootImgRT(tgt_address) boot_img.add_image(app_data) if dcd: boot_img.add_dcd_bin(load_binary(cpu_params.data_dir, dcd)) # write image to disk and to processor write_image(cpu_params, image_name + '_unsigned.bin', boot_img)
def test_nor_flash_fcb(cpu_params: CpuParams, fcb: bool) -> None: """Test unsigned image with FCB NOR FLASH block :param cpu_params: processor specific parameters of the test :param fcb: True to include FCB block to output image; False to exclude """ if (cpu_params.id != ID_RT1050) and (cpu_params.id != ID_RT1060): return # this test case is supported only for RT1050 and RT1060 image_name = f"{cpu_params.board}_iled_blinky_ext_FLASH" # create bootable image object app_data = load_binary(cpu_params.data_dir, image_name + ".bin") boot_img = BootImgRT(EXT_FLASH_ADDR) boot_img.add_image(app_data) if fcb: boot_img.set_flexspi_fcb( load_binary(cpu_params.data_dir, "flex_spi.fcb")) else: boot_img.fcb = PaddingFCB(0, enabled=False) # write image to disk and to processor suffix = "_unsigned_fcb.bin" if fcb else "_unsigned_nofcb.bin" write_image(cpu_params, image_name + suffix, boot_img)
def _to_authenticated_image(cpu_params: CpuParams, boot_img: BootImgRT, app_data: bytes, srk_key_index: int, entry_addr: int = -1, dek: Optional[bytes] = None, nonce: Optional[bytes] = None) -> None: """Configures given bootable image to authenticated image or encrypted image :param cpu_params: processor specific parameters of the test :param boot_img: bootable image to be updated (converted to signed or encrypted) :param app_data: data of the binary application :param srk_key_index: index of the SRK key used, 0-3 :param entry_addr: start address of the application; -1 to detect the address from the image :param dek: key for encrypted image: - None if image is not encrypted - empty value for image encrypted with random key - full key for test purposes :param nonce: optional initialization vector for AES encryption; None to use random value (recommended) :return: BootImageRT with application configured as signed or encrypted """ assert 0 <= srk_key_index <= 3 boot_img.add_image(app_data, address=entry_addr, dek_key=dek, nonce=nonce) # test method `decrypted_app_data` if dek: decr_app = boot_img.decrypted_app_data assert (len(decr_app) == align(len(app_data), MAC.AES128_BLK_LEN)) and (decr_app[:len(app_data)] == app_data) else: assert boot_img.decrypted_app_data == app_data csf_prefix = 'CSF' + str(srk_key_index + 1) + '_1_sha256_2048_65537_v3_usr_' img_prefix = 'IMG' + str(srk_key_index + 1) + '_1_sha256_2048_65537_v3_usr_' csf_priv_key = load_pem_private_key(load_binary(cpu_params.keys_data_dir, csf_prefix + 'key.pem'), password=PRIV_KEY_PASSWORD, backend=default_backend()) img_priv_key = load_pem_private_key(load_binary(cpu_params.keys_data_dir, img_prefix + 'key.pem'), password=PRIV_KEY_PASSWORD, backend=default_backend()) csf_priv_key_data = csf_priv_key.private_bytes(encoding=Encoding.PEM, format=PrivateFormat.PKCS8, encryption_algorithm=NoEncryption()) img_priv_key_data = img_priv_key.private_bytes(encoding=Encoding.PEM, format=PrivateFormat.PKCS8, encryption_algorithm=NoEncryption()) if dek is None: boot_img.add_csf_standard_auth(CSF_VERSION, srk_table4(cpu_params), srk_key_index, load_binary(cpu_params.cert_data_dir, csf_prefix + 'crt.pem'), csf_priv_key_data, load_binary(cpu_params.cert_data_dir, img_prefix + 'crt.pem'), img_priv_key_data) else: boot_img.add_csf_encrypted(CSF_VERSION, srk_table4(cpu_params), srk_key_index, load_binary(cpu_params.cert_data_dir, csf_prefix + 'crt.pem'), csf_priv_key_data, load_binary(cpu_params.cert_data_dir, img_prefix + 'crt.pem'), img_priv_key_data)
def test_invalid_parse(): with pytest.raises(SPSDKError): BootImgRT.parse(stream=5)
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_bootimage_rt10xx_missing_ivt(): # IVT header not found with pytest.raises(SPSDKError): BootImgRT.parse(b"00000000")
def test_bootimage_rt10xx_missing_ivt(): # IVT header not found with pytest.raises(ValueError): BootImgRT.parse(b'00000000')