def align(size: int) -> int: """Align given size to block size. :param size: in bytes :return: size aligned up to block size """ return misc.align(size, SecBootBlckSize.BLOCK_SIZE)
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 export( self, header_padding8: Optional[bytes] = None, auth_padding: Optional[bytes] = None, dbg_info: DebugInfo = DebugInfo.disabled(), ) -> bytes: """Serialization to binary form. :param header_padding8: optional header padding, 8-bytes; recommended to use None to apply random value :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() dbg_info.append_section("SB-FILE-1.x") data = self._header.export(padding8=header_padding8, dbg_info=dbg_info) # header table dbg_info.append_section("Sections-Header-Table") for sect_hdr in self._sections_hdr_table: sect_hdr_data = sect_hdr.export() dbg_info.append_binary_data("Section-Header-Item", sect_hdr_data) data += sect_hdr_data # sections dbg_info.append_section("Sections") for sect in self._sections: sect_data = sect.export(dbg_info) if len(sect_data) != sect.size: raise SPSDKError("Invalid section data") data += sect_data # authentication: SHA1 auth_code = crypto_backend().hash(data, "sha1") dbg_info.append_binary_section("SHA1", auth_code) data += auth_code # padding 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) if padding_len != len(auth_padding): raise SPSDKError("Invalid padding length") data += auth_padding dbg_info.append_binary_section("padding", auth_padding) return data
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 export( self, header_padding8: Optional[bytes] = None, auth_padding: Optional[bytes] = None, dbg_info: DebugInfo = DebugInfo.disabled() ) -> bytes: """Serialization to binary form. :param header_padding8: optional header padding, 8-bytes; recommended to use None to apply random value :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 """ self.update() self.validate() dbg_info.append_section('SB-FILE-1.x') data = self._header.export(padding8=header_padding8, dbg_info=dbg_info) # header table dbg_info.append_section('Sections-Header-Table') for sect_hdr in self._sections_hdr_table: sect_hdr_data = sect_hdr.export() dbg_info.append_binary_data('Section-Header-Item', sect_hdr_data) data += sect_hdr_data # sections dbg_info.append_section('Sections') for sect in self._sections: sect_data = sect.export(dbg_info) assert len(sect_data) == sect.size data += sect_data # authentication: SHA1 auth_code = crypto_backend().hash(data, 'sha1') dbg_info.append_binary_section('SHA1', auth_code) data += auth_code # padding 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) data += auth_padding dbg_info.append_binary_section('padding', auth_padding) return data
def test_align(test_input: int, alignment: int, expected: int): assert align(test_input, alignment) == expected
def _burn_image_to_flash( cpu_params: CpuParams, img: BootImgRT, img_data: bytes, otpmk_bee_regions: Tuple[BeeFacRegion, ...] = tuple() ) -> None: """Burn image into external FLASH. This function is called only in production mode. :param cpu_params: processor specific parameters of the test :param img: RT10xx image instance :param img_data: exported image data :param otpmk_bee_regions: optional list of BEE regions for BEE OTPMK encryption """ assert TEST_IMG_CONTENT is False # start FLASH loader mboot = init_flashloader(cpu_params) assert mboot.get_property(PropertyTag.CURRENT_VERSION) # verify SRK fuses are properly burned if VERIFY_SRK_FUSES: assert verify_srk_fuses(mboot, srk_table4(cpu_params)) # ### Configure external FLASH on EVK: flex-spi-nor using options on address 0x2000 ### # call "%blhost%" -u 0x15A2,0x0073 -j -- fill-memory 0x2000 4 0xC0233007 word assert mboot.fill_memory(INT_RAM_ADDR_DATA, 4, cpu_params.ext_flash_cfg_word0) # call "%blhost%" -u 0x15A2,0x0073 -j -- fill-memory 0x2004 4 0x00000000 word assert mboot.fill_memory(INT_RAM_ADDR_DATA + 4, 4, cpu_params.ext_flash_cfg_word1) # call "%blhost%" -u 0x15A2,0x0073 -j -- configure-memory 9 0x2000 assert mboot.configure_memory(INT_RAM_ADDR_DATA, ExtMemId.FLEX_SPI_NOR) if not img.fcb.enabled: write_addr_ofs = img.ivt_offset imgdata_offset = 0 elif isinstance(img.fcb, PaddingFCB): write_addr_ofs = img.BEE_OFFSET if img.bee_encrypted else img.ivt_offset imgdata_offset = img.BEE_OFFSET if img.bee_encrypted else img.ivt_offset elif isinstance(img.fcb, FlexSPIConfBlockFCB): write_addr_ofs = 0 imgdata_offset = 0 else: assert False # ### Erase memory before writing image ### # call "%blhost%" -u 0x15A2,0x0073 -j -- flash-erase-region 0x60000000 21000 9 size = align(len(img_data) + write_addr_ofs, 0x1000) assert mboot.flash_erase_region(EXT_FLASH_ADDR, size, ExtMemId.FLEX_SPI_NOR) if not img.fcb.enabled or isinstance( img.fcb, PaddingFCB): # FCB not part of the image # ### Use tag 0xF000000F to notify Flashloader to program FlexSPI NOR config block to the start of device### # call "%blhost%" -u 0x15A2,0x0073 -j -- fill-memory 0x3000 4 0xF000000F word assert mboot.fill_memory(INT_RAM_ADDR_DATA, 4, FCB_FLASH_NOR_CFG_WORD) # ### Program configuration block ### # call "%blhost%" -u 0x15A2,0x0073 -j -- configure-memory 9 0x3000 assert mboot.configure_memory(INT_RAM_ADDR_DATA, ExtMemId.FLEX_SPI_NOR) # read flex_spi.fcb mem = mboot.read_memory(EXT_FLASH_ADDR, 512, ExtMemId.FLEX_SPI_NOR) with open(os.path.join(cpu_params.data_dir, 'flex_spi.fcb'), 'wb') as f: f.write(mem) if otpmk_bee_regions: assert img.address == EXT_FLASH_ADDR # is applicable for XIP images only _init_otpmk_bee_regions(mboot, otpmk_bee_regions) else: assert len(otpmk_bee_regions) == 0 # @echo ### Write image ### # call "%blhost%" -u 0x15A2,0x0073 -j -- write-memory 0x60001000 image.bin 9 mboot.write_memory(EXT_FLASH_ADDR + write_addr_ofs, img_data[imgdata_offset:]) # for HAB encrypted image write KEY BLOB if img.dek_key: # call "blhost" -u 0x15A2,0x0073 -j -- generate-key-blob hab_dek.bin blob.bin blob = mboot.generate_key_blob(img.dek_key) tgt_address = EXT_FLASH_ADDR + img.dek_img_offset # call "blhost" -u 0x15A2,0x0073 -j -- write-memory 0x60008000 blob.bin 9 assert mboot.write_memory(tgt_address, blob, ExtMemId.FLEX_SPI_NOR) if AUTHENTICATE and (img.address == EXT_FLASH_ADDR) and not otpmk_bee_regions: mboot.close() hab_audit_xip_app(cpu_params, True) else: # detect XIP image app_data = img.decrypted_app_data initial_pc = int.from_bytes(app_data[4:8], byteorder="little") if img.address == EXT_FLASH_ADDR: # if XIP # run XIP image immediately stack_ptr = int.from_bytes(app_data[:4], byteorder="little") assert mboot.execute(initial_pc, EXT_FLASH_ADDR + img.ivt_offset, stack_ptr) mboot.close()
def raw_size(self) -> int: """Aligned size of the certificate block.""" size = CertBlockHeader.SIZE size += self._header.cert_table_length size += self.RKH_SIZE * self.RKHT_SIZE return misc.align(size, self.alignment)