Example #1
0
    def __init__(
        self,
        dek: Optional[bytes] = None,
        mac: Optional[bytes] = None,
        nonce: Optional[bytes] = None,
        timestamp: Optional[datetime] = None,
    ):
        """Initialize SBV2xAdvancedParams.

        :param dek: DEK key
        :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)
        self._nonce: bytes = nonce if nonce else SBV2xAdvancedParams._create_nonce(
        )
        if timestamp is None:
            timestamp = datetime.now()
        self._timestamp = datetime.fromtimestamp(int(timestamp.timestamp()))
        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")
Example #2
0
def test_seg_bee() -> None:
    """Test SegBEE class - BEE segment of the bootable image"""
    # empty segment
    seg = SegBEE([])
    assert seg.info()
    assert seg.export() == b''
    assert seg.size == 0
    seg.validate()
    # single region
    sw_key = crypto_backend().random_bytes(16)
    hdr = BeeRegionHeader(sw_key=sw_key)
    hdr.add_fac(BeeFacRegion(0x00000000, 0x00010000, 0))
    seg.add_region(hdr)
    assert seg.info()
    seg.validate()
    data = seg.export()
    assert (data is not None) and (len(data) == seg.size)
    parsed_seg = SegBEE.parse(b'\xFF' + data, 1, [sw_key])
    assert seg == parsed_seg
    # two regions
    sw_key2 = crypto_backend().random_bytes(16)
    hdr2 = BeeRegionHeader(sw_key=sw_key2)
    hdr2.add_fac(BeeFacRegion(0x10000000, 0x00010000, 1))
    hdr2.add_fac(BeeFacRegion(0x20000000, 0x00010000, 2))
    seg.add_region(hdr2)
    assert seg.info()
    seg.validate()
    data = seg.export()
    assert (data is not None) and (len(data) == seg.size)
    parsed_seg = SegBEE.parse(b'\xFF' + data, 1, [sw_key, sw_key2])
    assert seg == parsed_seg
    # total number of FACs exceeded
    hdr.add_fac(BeeFacRegion(0xF0000000, 0x00010000, 3))
    with pytest.raises(ValueError):
        seg.validate()
Example #3
0
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))
Example #4
0
    def export(self, padding: Optional[bytes] = None) -> bytes:
        """Serialize image object.

        :param padding: header padding (8 bytes) for testing purpose; None to use random values (recommended)
        :return: exported bytes
        :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
        """
        if len(self.dek) != 32 or len(self.mac) != 32:
            raise SPSDKError("Invalid dek or mac")
        # validate params
        if not self._boot_sections:
            raise SPSDKError("No boot section")
        if self.signed and (self._cert_section is None):
            raise SPSDKError(
                "Certificate section is required for signed images")
        # update internals
        self.update()
        # Add Image Header data
        data = self._header.export(padding=padding)
        # Add Image Header HMAC data
        data += crypto_backend().hmac(self.mac, data)
        # Add DEK and MAC keys
        data += crypto_backend().aes_key_wrap(self.kek, self.dek + self.mac)
        # Add Padding
        data += padding if padding else crypto_backend().random_bytes(8)
        # Add Certificates data
        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:
            cert_sect_bin = self._cert_section.export(dek=self.dek,
                                                      mac=self.mac,
                                                      counter=counter)
            counter.increment(calc_cypher_block_count(len(cert_sect_bin)))
            data += cert_sect_bin
        # Add Boot Sections data
        for sect in self._boot_sections:
            data += sect.export(dek=self.dek, mac=self.mac, counter=counter)
        # Add Signature data
        if self.signed:
            private_key_pem_data = self.private_key_pem_data
            if private_key_pem_data is None:
                raise SPSDKError(
                    "Private key not assigned, cannot sign the image")
            certificate_block = self.cert_block
            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)
        if len(data) != self.raw_size:
            raise SPSDKError("Invalid length of exported data")
        return data
Example #5
0
    def __init__(self, kib_key: Optional[bytes] = None, kib_iv: Optional[bytes] = None):
        """Constructor.

        :param kib_key: AES key
        :param kib_iv: AES initialization vector
        """
        # Key Info Block (KIB)
        self.kib_key = kib_key if kib_key else crypto_backend().random_bytes(16)
        self.kib_iv = kib_iv if kib_iv else crypto_backend().random_bytes(16)
Example #6
0
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))
Example #7
0
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))
Example #8
0
def test_certificate_section_v2(data_dir: str) -> None:
    with pytest.raises(AssertionError):
        CertSectionV2(None)

    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)
    data = cs.export(dek, mac, Counter(nonce))
    assert data
    assert CertSectionV2.parse(data, 0, dek, mac, Counter(nonce))

    with pytest.raises(Exception):
        CertSectionV2.parse(data, 0, dek,
                            crypto_backend().random_bytes(32), Counter(nonce))
Example #9
0
    def export(self) -> bytes:
        """Master boot image (binary)."""
        data = self._update_ivt(self.data)

        # signed or encrypted
        if MasterBootImageType.is_signed(self.image_type):
            assert self._priv_key_pem_data
            cb = self.cert_block
            assert (cb is not None) and cb.verify_private_key(self._priv_key_pem_data)  # type: ignore
            # encrypt
            encr_data = self._encrypt(data)
            encr_data = (self._update_ivt(encr_data[:self.HMAC_OFFSET]) +  # header
                         encr_data[self.HMAC_OFFSET:self.app_len] +  # encrypted image
                         self._certificate(encr_data) +  # certificate + encoded image header + CTR init vector
                         encr_data[self.app_len:])  # TZ encoded data
            encr_data += crypto_backend().rsa_sign(self._priv_key_pem_data, encr_data)  # signature
            # hmac + key store
            if MasterBootImageType.has_hmac(self.image_type):
                hmac_keystore = self._hmac(encr_data[:self.HMAC_OFFSET])
                if self.key_store:
                    hmac_keystore += self.key_store.export()
                encr_data = encr_data[:self.HMAC_OFFSET] + hmac_keystore + encr_data[self.HMAC_OFFSET:]
            return bytes(encr_data)

        return bytes(data)
Example #10
0
    def parse(cls, data: bytes, offset: int = 0) -> "SecureBootV1":
        """Convert binary data into the instance (deserialization).

        :param data: given binary data to be converted
        :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
        obj._header = SecureBootHeaderV1.parse(data, cur_pos)
        cur_pos += obj._header.size
        # sections header table
        for _ in range(obj._header.section_count):
            sect_header = SectionHeaderItemV1.parse(data, cur_pos)
            obj._sections_hdr_table.append(sect_header)
            cur_pos += sect_header.size
        # sections
        new_pos = offset + obj._header.first_boot_tag_block * SecBootBlckSize.BLOCK_SIZE
        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)
            obj.append(boot_sect)
            cur_pos += boot_sect.size
        # authentication code
        sha1_auth = crypto_backend().hash(data[offset:cur_pos], "sha1")
        if sha1_auth != data[cur_pos:cur_pos + len(sha1_auth)]:
            raise ValueError("Authentication failure: digest does not match")
        # done
        return obj
Example #11
0
    def encrypt_block(self, key: bytes, start_addr: int, data: bytes) -> bytes:
        """Encrypt block located in any FAC region.

        :param key: user for encryption
        :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
        """
        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:
            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:
                    if start_addr + len(data) > fac.end_addr:
                        raise SPSDKError("Invalid range of region")
                    cntr_key = Counter(
                        self.counter,
                        ctr_value=start_addr >> 4,
                        ctr_byteorder_encoding="big",
                    )
                    return crypto_backend().aes_ctr_encrypt(
                        key, data, cntr_key.value)
        return data
Example #12
0
    def store_ctr_init_vector(self, ctr_iv: bytes = None) -> None:
        """Stores the Counter init vector, if not specified the random value is used.

        param ctr_iv: Counter Initial Vector.
        """
        self.ctr_init_vector = ctr_iv or crypto_backend().random_bytes(
            self._CTR_INIT_VECTOR_SIZE)
Example #13
0
 def _create_nonce() -> bytes:
     """Return random nonce."""
     nonce = bytearray(crypto_backend().random_bytes(16))
     # clear nonce bit at offsets 31 and 63
     nonce[9] &= 0x7F
     nonce[13] &= 0x7F
     return bytes(nonce)
Example #14
0
def test_boot_section_v2():
    boot_section = BootSectionV2(0, CmdErase(address=0, length=100000),
                                 CmdLoad(address=0, data=b"0123456789"),
                                 CmdReset())

    assert boot_section.uid == 0
    assert not boot_section.is_last
    assert boot_section.hmac_count == 1
    assert boot_section.raw_size == 144

    dek = crypto_backend().random_bytes(32)
    mac = crypto_backend().random_bytes(32)
    nonce = crypto_backend().random_bytes(16)
    data = boot_section.export(dek, mac, Counter(nonce))
    assert data
    assert BootSectionV2.parse(data, 0, False, dek, mac, Counter(nonce))

    with pytest.raises(SPSDKError,
                       match="Invalid type of dek, should be bytes"):
        BootSectionV2.parse(data=data,
                            offset=0,
                            plain_sect=False,
                            dek=4,
                            mac=mac,
                            counter=Counter(nonce))

    with pytest.raises(SPSDKError,
                       match="Invalid type of mac, should be bytes"):
        BootSectionV2.parse(data=data,
                            offset=0,
                            plain_sect=False,
                            dek=dek,
                            mac=4,
                            counter=Counter(nonce))

    with pytest.raises(SPSDKError, match="Invalid type of counter"):
        BootSectionV2.parse(data=data,
                            offset=0,
                            plain_sect=False,
                            dek=dek,
                            mac=mac,
                            counter=5)

    with pytest.raises(SPSDKError):
        assert BootSectionV2.parse(data, 0, False, dek,
                                   crypto_backend().random_bytes(32),
                                   Counter(nonce))
Example #15
0
    def sign(self, image: bytes) -> bytes:
        """Do calculation of RSA signature and return updated image with it.

        :param image: Input raw image.
        :return: Image enriched by RSA signature at end of image.
        """
        assert self.priv_key_data
        return image + crypto_backend().rsa_sign(self.priv_key_data, image)
Example #16
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 #17
0
    def __init__(self, app: Union[bytes, bytearray],
                 load_addr: int,
                 image_type: MasterBootImageType = MasterBootImageType.PLAIN_IMAGE,
                 trust_zone: Optional[TrustZone] = None, app_table: Optional[MultipleImageTable] = None,
                 cert_block: Optional[CertBlock] = None, priv_key_pem_data: Optional[bytes] = None,
                 hmac_key: Union[bytes, str] = None, key_store: KeyStore = None,
                 enable_hw_user_mode_keys: bool = False, ctr_init_vector: bytes = None) -> None:
        """Constructor.

        :param app: input image (binary)
        :param load_addr: address in RAM, where 'RAM' image will be copied;
                    for XIP images address, where the image is located in FLASH memory
        :param image_type: type of the master boot image
        :param trust_zone: TrustZone instance; None to use default settings (TrustZone enabled)
        :param app_table: optional table with additional images; None if no additional images needed
        :param cert_block: block of certificates; None for unsigned image
        :param priv_key_pem_data: private key to sign the image, decrypted binary data in PEM format
        :param hmac_key: optional key for HMAC generation (either binary ot HEX string; 32 bytes);
                                None if HMAC is not in the image
                If key_store.key_source == KeySourceType.KEYSTORE, this is a user-key from key-store
                If key_store.key_source == KeySourceType.OTP, this is a master-key burned in OTP
        :param key_store: optional key store binary content; None if key store is not in the image
        :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
        """
        if not isinstance(app, (bytes, bytearray)):
            raise TypeError("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
        self.load_addr = load_addr
        self.image_type = image_type
        alignment = MasterBootImage._IMAGE_ALIGNMENT
        self.app = misc.align_block(bytes(app), alignment)
        self.app_table = app_table
        # hmac + key store
        self.hmac_key = bytes.fromhex(hmac_key) if isinstance(hmac_key, str) else hmac_key
        self.key_store = key_store
        # trust zone
        self.trust_zone = trust_zone or TrustZone.enabled()
        # security stuff
        self.cert_block = cert_block
        if self.cert_block:
            self.cert_block.alignment = 4  #type: ignore   # this value is used by elf-to-sb-gui
            self.signature_len = self.cert_block.signature_size  #type: ignore
        else:
            self.signature_len = 0
        self._priv_key_pem_data = priv_key_pem_data
        self.enable_hw_user_mode_keys = enable_hw_user_mode_keys
        self.ctr_init_vector = ctr_init_vector
        if MasterBootImageType.is_encrypted(self.image_type) and not ctr_init_vector:
            self.ctr_init_vector = crypto_backend().random_bytes(self._CTR_INIT_VECTOR_SIZE)
        self._verify_private_key()
        # validate parameters
        self._validate_new_instance()
Example #18
0
def test_boot_section_v2_invalid_export():
    boot_section = BootSectionV2(0, CmdErase(address=0, length=100000),
                                 CmdLoad(address=0, data=b"0123456789"),
                                 CmdReset())
    dek = 32
    mac = 4
    nonce = crypto_backend().random_bytes(16)
    with pytest.raises(SPSDKError,
                       match="Invalid type of dek, should be bytes"):
        boot_section.export(dek, mac, Counter(nonce))
    dek = crypto_backend().random_bytes(32)
    with pytest.raises(SPSDKError,
                       match="Invalid type of mac, should be bytes"):
        boot_section.export(dek, mac, Counter(nonce))
    counter = 5
    mac = crypto_backend().random_bytes(32)
    with pytest.raises(SPSDKError, match="Invalid type of counter"):
        boot_section.export(dek, mac, counter)
Example #19
0
def test_bee_region_header() -> None:
    """Test BeeRegionHeader class"""
    sw_key = crypto_backend().random_bytes(16)
    hdr = BeeRegionHeader(sw_key=sw_key)
    hdr.add_fac(BeeFacRegion(0x00000000, 0x00010000, 0))
    verify_base_class_features(hdr, decrypt_key=sw_key)
    hdr.add_fac(BeeFacRegion(0x10000000, 0x00010000, 1))
    hdr.add_fac(BeeFacRegion(0x20000000, 0x00010000, 2))
    hdr.add_fac(BeeFacRegion(0xF0000000, 0x00010000, 3))
    verify_base_class_features(hdr, decrypt_key=sw_key)
Example #20
0
    def finalize(self, image: bytes) -> bytes:
        """Finalize the image for export by adding HMAC a optionally KeyStore.

        :param image: Input image.
        :return: Finalized image suitable for export.
        """
        ret = image
        if self.attach_sign_digest:
            ret += crypto_backend().hash(image, self.attach_sign_digest)
        return ret
Example #21
0
    def __init__(
        self,
        signed: bool,
        kek: bytes,
        *sections: BootSectionV2,
        product_version: str = "1.0.0",
        component_version: str = "1.0.0",
        build_number: int = 0,
        advanced_params: SBV2xAdvancedParams = SBV2xAdvancedParams(),
    ) -> None:
        """Initialize Secure Boot Image V2.0.

        :param signed: True if image is signed, False otherwise
        :param kek: key for wrapping DEK and MAC keys
        :param product_version: The product version (default: 1.0.0)
        :param component_version: The component version (default: 1.0.0)
        :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
        self._signed = signed
        self._private_key_pem_data: Optional[bytes] = None
        flags = 0x08 if self.signed else 0x04
        # Set private attributes
        self._dek: bytes = advanced_params.dek
        self._mac: bytes = advanced_params.mac
        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,
            component_version=component_version,
            build_number=build_number,
            flags=flags,
            nonce=advanced_params.nonce,
            timestamp=advanced_params.timestamp,
        )
        self._cert_section: Optional[CertSectionV2] = None
        self._boot_sections: List[BootSectionV2] = []
        # Generate nonce
        if self._header.nonce is None:
            nonce = bytearray(crypto_backend().random_bytes(16))
            # clear nonce bit at offsets 31 and 63
            nonce[9] &= 0x7F
            nonce[13] &= 0x7F
            self._header.nonce = bytes(nonce)
        # Sections
        for section in sections:
            self.add_boot_section(section)
Example #22
0
    def __init__(
        self,
        version: str = "1.0",
        flags: int = 0,
        drive_tag: int = 0,
        product_version: BcdVersion3Format = BcdVersion3.DEFAULT,
        component_version: BcdVersion3Format = BcdVersion3.DEFAULT,
        dek: Optional[bytes] = None,
        mac: Optional[bytes] = None,
        digest: bytes = b"\0" * 20,
        timestamp: Optional[datetime] = None,
    ):
        """Initialize Secure Boot Image V1.x.

        :param version: string in format #.#
            Major version of the boot image format, currently 1.
            Minor version of the boot image format, currently 1 or 2.
        :param flags: for the header, 0 by default: Flags associated with the entire image.
        :param product_version: Product version.
        :param component_version: Component version.
        :param drive_tag: For header: identifier for the disk drive or partition containing this image.
        :param dek: DEK key for encrypted SB file; this is not supported yet
        :param mac: MAC for encrypted SB file, this is not supported yet
        :param digest: SHA-1 digest of all fields of the header (it will be updated before export anyway)
            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
        """
        self._dek = dek if dek else crypto_backend().random_bytes(32)
        self._mac = mac if mac else crypto_backend().random_bytes(32)
        self._header = SecureBootHeaderV1(
            version=version,
            product_version=product_version,
            component_version=component_version,
            flags=flags,
            drive_tag=drive_tag,
            digest=digest,
            timestamp=timestamp,
        )
        self._sections_hdr_table: List[SectionHeaderItemV1] = []
        self._sections: List[BootSectionV1] = []
        self._signature = None
Example #23
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
        """
        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
Example #24
0
def test_boot_section_v2():
    boot_section = BootSectionV2(0, CmdErase(address=0, length=100000),
                                 CmdLoad(address=0, data=b'0123456789'),
                                 CmdReset())

    assert boot_section.uid == 0
    assert not boot_section.is_last
    assert boot_section.hmac_count == 1
    assert boot_section.raw_size == 144

    dek = crypto_backend().random_bytes(32)
    mac = crypto_backend().random_bytes(32)
    nonce = crypto_backend().random_bytes(16)
    data = boot_section.export(dek, mac, Counter(nonce))
    assert data
    assert BootSectionV2.parse(data, 0, False, dek, mac, Counter(nonce))

    with pytest.raises(Exception):
        assert BootSectionV2.parse(data, 0, False, dek,
                                   crypto_backend().random_bytes(32),
                                   Counter(nonce))
Example #25
0
    def __init__(self,
                 dek: Optional[bytes] = None,
                 mac: Optional[bytes] = None,
                 nonce: Optional[bytes] = None,
                 timestamp: Optional[datetime] = None):
        """Initialize SBV2xAdvancedParams.

        :param dek: DEK key
        :param mac: MAC key
        :param nonce: nonce
        :param timestamp: fixed timestamp for the header; use None to use current date/time
        """
        self._dek: bytes = dek if dek else crypto_backend().random_bytes(32)
        self._mac: bytes = mac if mac else crypto_backend().random_bytes(32)
        self._nonce: bytes = nonce if nonce else SBV2xAdvancedParams._create_nonce(
        )
        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
Example #26
0
    def compute_hmac(self, data: bytes) -> bytes:
        """Compute HMAC hash.

        :param data: Data to be hashed.
        :return: Result HMAC hash of input data.
        """
        if not self.hmac_key:
            return bytes()

        key = KeyStore.derive_hmac_key(self.hmac_key)
        result = crypto_backend().hmac(key, data)
        assert len(result) == self.HMAC_SIZE
        return result
Example #27
0
    def __init__(self,
                 prdb: Optional[BeeProtectRegionBlock] = None,
                 sw_key: Optional[bytes] = None,
                 kib: Optional[BeeKIB] = None):
        """Constructor.

        :param prdb: protect region block; None to use default
        :param sw_key: key used to encrypt KIB content
        :param kib: keys block; None to use default
        """
        self._prdb = prdb if (prdb is not None) else BeeProtectRegionBlock()
        self._sw_key = sw_key if (
            sw_key is not None) else crypto_backend().random_bytes(16)
        self._kib = kib if (kib is not None) else BeeKIB()
Example #28
0
    def _hmac(self, data: bytes) -> bytes:
        """Calculate HMAC for provided data.

        :param data: to calculate hmac
        :return: calculated hmac; empty bytes if the block does not contain any HMAC
        """
        if not MasterBootImageType.has_hmac(self.image_type):
            return bytes()

        assert self.hmac_key and len(self.hmac_key) == self._HMAC_KEY_LENGTH
        key = KeyStore.derive_hmac_key(self.hmac_key)
        assert len(key) == self._HMAC_DERIVED_KEY_LEN
        result = crypto_backend().hmac(key, data)
        assert len(result) == self.HMAC_SIZE
        return result
Example #29
0
def align_block(data: bytes, alignment: int = 4, padding: int = 0) -> bytes:
    """Align binary data block length to specified boundary by adding padding bytes to the end.

    :param data: to be aligned
    :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
    """
    assert isinstance(data, bytes)
    assert alignment > 0
    assert -1 <= padding <= 255
    curr_size = len(data)
    num_padding = align(curr_size, alignment) - curr_size
    if not num_padding:
        return data
    if padding == -1:
        return data + crypto_backend().random_bytes(num_padding)
    return data + bytes([padding]) * num_padding
Example #30
0
    def encrypt_block(self, key: bytes, start_addr: int, data: bytes) -> bytes:
        """Encrypt block located in any FAC region.

        :param key: user for encryption
        :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
        """
        assert len(data) == BEE_ENCR_BLOCK_SIZE
        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
            for fac in self.fac_regions:
                if fac.start_addr <= start_addr < fac.end_addr:
                    assert start_addr + len(data) <= fac.end_addr
                    cntr_key = Counter(self.counter,
                                       ctr_value=start_addr >> 4,
                                       ctr_byteorder_encoding='big')
                    return crypto_backend().aes_ctr_encrypt(
                        key, data, cntr_key.value)
        return data