Example #1
0
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()
Example #2
0
 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
Example #3
0
    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
Example #4
0
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")
Example #5
0
 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
Example #6
0
    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
Example #7
0
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')
Example #8
0
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
Example #9
0
    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
Example #10
0
    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
Example #11
0
    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
Example #12
0
    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
Example #13
0
    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()
Example #14
0
 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
Example #15
0
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() == ''
Example #16
0
 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
Example #17
0
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)
Example #18
0
    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
Example #19
0
    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
Example #20
0
    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
Example #21
0
    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
Example #22
0
    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)
Example #23
0
 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
Example #24
0
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',
    ]
Example #25
0
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",
    ]
Example #26
0
    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
Example #27
0
 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
Example #28
0
 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
Example #29
0
    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
Example #30
0
def test_debug_info_invalid():
    dbg_info = DebugInfo()
    with pytest.raises(SPSDKError, match="Incorrect data length"):
        dbg_info.append_binary_data("data", bytes(20))