def write_sb(cpu_params: CpuParams, image_file_name: str, img: SecureBootV1) -> None: """Write SB 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 """ path = os.path.join(cpu_params.data_dir, OUTPUT_IMAGES_SUBDIR, image_file_name) dbg_info = DebugInfo() img_data = img.export(dbg_info=dbg_info, # use the following parameters only for unit test header_padding8=b'\xdb\x00\x76\x7a\xf4\x81\x0b\x86', auth_padding=b'\x36\x72\xf4\x99\x92\x05\x34\xd2\xd5\x17\xa0\xf7') write_dbg_log(cpu_params.data_dir, image_file_name, dbg_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) img = SecureBootV1.parse(b'0' + img_data, 1) dbg_info2 = DebugInfo() img_data2 = img.export(dbg_info=dbg_info2, header_padding8=b'\xdb\x00\x76\x7a\xf4\x81\x0b\x86', auth_padding=b'\x36\x72\xf4\x99\x92\x05\x34\xd2\xd5\x17\xa0\xf7') assert dbg_info.lines == dbg_info2.lines assert img_data == img_data2 else: with open(path, 'wb') as f: f.write(img_data) mboot = init_flashloader(cpu_params) assert mboot.receive_sb_file(img_data) mboot.close()
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Export.""" self._header.length = self.size raw_data = self._header.export() dbg_info.append_binary_section("header", raw_data) raw_data += self.data dbg_info.append_binary_section("data", self.data) return raw_data
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Export to binary form (serialization). :param dbg_info: debug information about exported data :return: binary representation of the command """ hdr_data = self._header.export() dbg_info.append_binary_data('header', hdr_data) return hdr_data
def _log_test_output(dbg_info: DebugInfo) -> None: """Uses all methods of DebugInfo class to append data in different format :param dbg_info: instance used for logging """ dbg_info.append_section("SECTION") dbg_info.append("-test-line-") dbg_info.append_binary_section("bin", b"\x00\x11\x22\xFF") dbg_info.append_binary_data("data", b"\x00\x11\x22") dbg_info.append_hex_data(b"\x00\x11\x22\x00\x11\x22\x00\x11\x22\x00\x11\x22")
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Return command in binary form (serialization).""" # export cmd data = super().export(dbg_info) # export additional data if len(self._pattern) > 4: data += bytes(self._pattern[4:]) dbg_info.append_binary_data('pattern', self._pattern[4:]) data = SecBootBlckSize.align_block_fill_random(data) return data
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Export to binary form (serialization). :param dbg_info: debug information about exported data :return: binary representation of the command """ raw_data = super().export(dbg_info=dbg_info) raw_data += pack("4B", 0x00, self.hash_algorithm, self.engine, self.engine_cfg) dbg_info.append_binary_data('data', raw_data) return raw_data
def _log_test_output(dbg_info: DebugInfo) -> None: """Uses all methods of DebugInfo class to append data in different format :param dbg_info: instance used for logging """ dbg_info.append_section('SECTION') dbg_info.append('-test-line-') dbg_info.append_binary_section('bin', b'\x00\x11\x22\xFF') dbg_info.append_binary_data('data', b'\x00\x11\x22') dbg_info.append_hex_data( b'\x00\x11\x22\x00\x11\x22\x00\x11\x22\x00\x11\x22')
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 export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Export to binary form (serialization). :param dbg_info: debug information about exported data :return: binary representation of the command """ raw_data = super().export(dbg_info=dbg_info) data = pack(">4BL", self.certificate_format, self.hash_algorithm, self.source_index, self.target_index, self.cmd_data_location) raw_data += data dbg_info.append_binary_data('data', data) return raw_data
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Export to binary form (serialization). :param dbg_info: debug information about exported data :return: binary representation of the command """ assert self.size == CmdHeader.SIZE + 4 raw_data = super().export(dbg_info=dbg_info) data = pack(">L", self.features) dbg_info.append_binary_data('data', data) raw_data += data return raw_data
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Export into binary form (serialization). :param dbg_info: optional instance allowing to debug exported content :return: binary representation of the instance """ self._header.length = self.size raw_data = self._header.export() dbg_info.append_binary_section("header", raw_data) for srk in self._keys: item_data = srk.export() raw_data += item_data dbg_info.append_binary_section("srk_item", item_data) return raw_data
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Export to binary form (serialization). :param dbg_info: debug information about exported data :return: binary representation of the command """ self._header.length = self.size raw_data = super().export(dbg_info=dbg_info) data = pack(">4BL", self.key_index, self.sig_format, self.engine, self.engine_cfg, self.location) dbg_info.append_binary_data('data', data) raw_data += data for blk in self._blocks: blk_data = pack(">2L", blk[0], blk[1]) dbg_info.append_binary_data('block', blk_data) raw_data += blk_data return raw_data
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Serialization to binary form. :param dbg_info: optional instance allowing to debug exported data; provides commented export :return: binary representation of the instance """ raise NotImplementedError()
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Export.""" data = self._header.export() data += pack(">4B2H", 0, 0, 0, self.flag, len(self.modulus), len(self.exponent)) data += bytes(self.modulus) data += bytes(self.exponent) return data
def test_debug_info_disabled() -> None: """Test disabled output""" dbg_info = DebugInfo.disabled() assert not dbg_info.enabled _log_test_output(dbg_info) assert dbg_info.lines == [] assert dbg_info.info() == ''
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Return command in binary form (serialization).""" # export cmd data = super().export(dbg_info) # export additional data data = SecBootBlckSize.align_block_fill_random(data) return data
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 export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Export to binary form (serialization). :param dbg_info: debug information about exported data :return: binary representation of the command """ self._header.length = self.size raw_data = super().export(dbg_info=dbg_info) raw_data += pack(">LQ", self.features, self.uid) return raw_data
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Export to binary form (serialization). :param dbg_info: debug information about exported data :return: binary representation of the command """ raw_data = super().export(dbg_info=dbg_info) for val in self._data: raw_data += pack(">L", val) return raw_data
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Export to binary form (serialization). :param dbg_info: debug information about exported data :return: binary representation of the command """ raw_data = super().export(dbg_info=dbg_info) raw_data += pack(">LL", self.address, self.mask) if self.count is not None: raw_data += pack(">L", self.count) return raw_data
def export(self, padding8: Optional[bytes] = None, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Serialization to binary form. :param padding8: 8 padding bytes used for in the header, None to use random bytes This value shall be used only for regression testing to generate same results :param dbg_info: class allowing to debug output from the export :return: Serialize object into bytes """ major_version, minor_version = [int(v) for v in self.version.split('.')] product_version_words = [swap16(n) for n in self.product_version.nums] component_version_words = [swap16(n) for n in self.component_version.nums] signature2 = crypto_backend().random_bytes(4) padding = padding8 if padding8 else crypto_backend().random_bytes(8) if (major_version > 1) or ((major_version == 1) and (minor_version >= 2)): signature2 = self._SIGNATURE2 dbg_info.append_section('SB-file-Header') result = pack( self._FORMAT, self.digest, self._SIGNATURE1, # header version major_version, minor_version, self.flags, self.image_blocks, self.first_boot_tag_block, self.first_boot_section_id, self.key_count, self.key_dictionary_block, self.header_blocks, self.section_count, self.section_header_size, padding[0:2], signature2, pack_timestamp(self.timestamp), # product version product_version_words[0], 0, product_version_words[1], 0, product_version_words[2], 0, # component version component_version_words[0], 0, component_version_words[1], 0, component_version_words[2], 0, self.drive_tag, padding[2:] ) result = result[len(self.digest):] self.digest = crypto_backend().hash(result, 'sha1') dbg_info.append_binary_section('digest', self.digest) dbg_info.append_binary_section('attrs', result) return self.digest + result
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Serialization to binary representation. :param dbg_info: instance allowing to provide debug info about exported data :return: binary representation of the region (serialization). """ result = super().export() # KIB kib_data = self._kib.export() dbg_info.append_binary_section("BEE-KIB (non-crypted)", kib_data) aes = AES.new(self._sw_key, AES.MODE_ECB) result += aes.encrypt(kib_data) # padding result = extend_block(result, self.PRDB_OFFSET) # PRDB prdb_data = self._prdb.export() dbg_info.append_binary_section("BEE-PRDB (non-crypted)", prdb_data) aes = AES.new(self._kib.kib_key, AES.MODE_CBC, self._kib.kib_iv) result += aes.encrypt(prdb_data) # padding return extend_block(result, self.SIZE)
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Return binary representation of the class (serialization).""" self.update() dbg_info.append_section('Section') data = self._header.export() dbg_info.append_binary_data('Section-header', data) dbg_info.append_section('Commands') for cmd in self._commands: cmd_data = cmd.export(dbg_info) data += cmd_data return data
def test_debug_info() -> None: """Test basic DebugInfo methods""" dbg_info = DebugInfo() assert dbg_info.enabled _log_test_output(dbg_info) assert dbg_info.lines == [ '[SECTION]', '-test-line-', '[bin]', 'hex=001122ff', 'len=4=0x4', 'data=001122', 'hex=001122001122001122001122', 'len=12=0xc', ]
def test_debug_info() -> None: """Test basic DebugInfo methods""" dbg_info = DebugInfo() assert dbg_info.enabled _log_test_output(dbg_info) assert dbg_info.lines == [ "[SECTION]", "-test-line-", "[bin]", "hex=001122ff", "len=4=0x4", "data=001122", "hex=001122001122001122001122", "len=12=0xc", ]
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Export instance into binary form (serialization). :param dbg_info: optional instance providing debug info about exported content :return: binary form """ self._validate_data() self._header.length = self.size raw_data = self._header.export() dbg_info.append_binary_data("header", raw_data) raw_data += pack(">4B", 0, self.nonce_len, 0, self.mac_len) dbg_info.append("nonce=" + self.nonce.hex()) dbg_info.append("mac=" + self.mac.hex()) raw_data += self.data return raw_data
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Export command as binary.""" self._update_data() result = super().export(dbg_info) dbg_info.append_binary_section("load-data", self.data) return result + self.data
def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: """Return object serialized into bytes.""" dbg_info.append_section("Command:" + EnumCmdTag.name(self.header.tag)) cmd_data = self._header.export() # default implementation dbg_info.append_binary_data("cmd-header", cmd_data) return cmd_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 :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 test_debug_info_invalid(): dbg_info = DebugInfo() with pytest.raises(SPSDKError, match="Incorrect data length"): dbg_info.append_binary_data("data", bytes(20))