Beispiel #1
0
def test_elftosb_mbi_signed(data_dir, tmpdir, config_file, device,
                            sign_digest):
    runner = CliRunner()
    with use_working_directory(data_dir):
        config_file = f"{data_dir}/workspace/cfgs/{device}/{config_file}"
        ref_binary, new_binary, new_config = process_config_file(
            config_file, tmpdir)

        cmd = f"--image-conf {new_config}"
        result = runner.invoke(elftosb.main, cmd.split())
        assert os.path.isfile(new_binary)

        # validate file lengths
        with open(ref_binary, "rb") as f:
            ref_data = f.read()
        with open(new_binary, "rb") as f:
            new_data = f.read()
        assert len(ref_data) == len(new_data)

        # validate signatures

        signing_key = get_signing_key(config_file=config_file)
        signature_length = 2 * signing_key.pointQ.size_in_bytes()
        if sign_digest:
            sign_offset = 32 if sign_digest and sign_digest == "sha256" else 48
            assert internal_backend.ecc_verify(
                signing_key,
                new_data[-(signature_length + sign_offset):-sign_offset],
                new_data[:-(signature_length + sign_offset)],
            )
            assert internal_backend.ecc_verify(
                signing_key,
                ref_data[-(signature_length + sign_offset):-sign_offset],
                ref_data[:-(signature_length + sign_offset)],
            )
            # validate data before signature
            assert (ref_data[:-(signature_length + sign_offset)] ==
                    new_data[:-(signature_length + sign_offset)])
            # validate signature digest
            assert (internal_backend.hash(
                new_data[:-sign_offset],
                sign_digest) == new_data[-sign_offset:])
            assert (internal_backend.hash(
                ref_data[:-sign_offset],
                sign_digest) == ref_data[-sign_offset:])
        else:
            assert internal_backend.ecc_verify(signing_key,
                                               new_data[-signature_length:],
                                               new_data[:-signature_length])
            assert internal_backend.ecc_verify(signing_key,
                                               ref_data[-signature_length:],
                                               ref_data[:-signature_length])
            # validate data before signature
            assert ref_data[:-signature_length] == new_data[:-signature_length]
Beispiel #2
0
 def create_ctrk_table(rot_pub_keys: List[str]) -> bytes:
     """Creates ctrk table."""
     if len(rot_pub_keys) == 1:
         return bytes()
     ctrk_table = bytes()
     for pub_key_path in rot_pub_keys:
         pub_key = crypto.load_public_key(pub_key_path)
         assert isinstance(pub_key, crypto.EllipticCurvePublicKey)
         key_length = pub_key.key_size
         data = ecc_key_to_bytes(key=pub_key, length=key_length // 8)
         ctrk_hash = internal_backend.hash(data=data,
                                           algorithm=f'sha{key_length}')
         ctrk_table += ctrk_hash
     return ctrk_table
Beispiel #3
0
    def _process_block(self, block_number: int, block_data: bytes) -> bytes:
        """Process single block."""
        if self.is_encrypted:
            assert self.key_derivator
            block_key = self.key_derivator.get_block_key(block_number)
            encrypted_block = internal_backend.aes_cbc_encrypt(
                block_key, block_data)
        else:
            encrypted_block = block_data

        full_block = pack(f"<L{len(self.final_hash)}s{len(encrypted_block)}s",
                          block_number, self.final_hash, encrypted_block)
        block_hash = internal_backend.hash(full_block, self.hash_type)
        self.final_hash = block_hash
        return full_block
Beispiel #4
0
    def _get_rot_meta(used_root_cert: int, rot_pub_keys: List[str]) -> bytes:
        """Creates the RoT meta-data required by the device to corroborate.

        The meta-data is created by getting the public numbers (modulus and exponent)
        from each of the RoT public keys, hashing them and combing together.

        :return: binary representing the rot-meta data
        """
        rot_meta = bytearray(128)
        for index, rot_key in enumerate(rot_pub_keys):
            rot = crypto.load_public_key(file_path=rot_key)
            assert isinstance(rot, crypto.RSAPublicKey)
            data = rsa_key_to_bytes(key=rot, exp_length=3, modulus_length=None)
            result = internal_backend.hash(data)
            rot_meta[index * 32:(index + 1) * 32] = result
        return bytes(rot_meta)
Beispiel #5
0
    def info(self) -> str:
        """String representation of DebugCredential.

        :return: binary representation of the debug credential
        """
        msg = f"Version : {self.VERSION}\n"
        msg += f"SOCC    : {self.socc}\n"
        msg += f"UUID    : {self.uuid.hex().upper()}\n"
        msg += f"CC_SOCC : {hex(self.cc_socu)}\n"
        msg += f"CC_VU   : {hex(self.cc_vu)}\n"
        msg += f"BEACON  : {self.cc_beacon}\n"
        ctrk_records_num = self.rot_meta[0] >> 4
        if ctrk_records_num == 1:
            msg += "CRTK table not present \n"
        else:
            msg += f"CRTK table has {ctrk_records_num} entries\n"
            # Compute and show RKTH HASH
            key_length = 256 if (len(self.rot_meta) - 4) == 32 else 384
            ctrk_hash = internal_backend.hash(data=self.rot_meta[4:],
                                              algorithm=f"sha{key_length}")
            msg += f"CRTK Hash: {ctrk_hash.hex()}"
        return msg
Beispiel #6
0
    def parse(
        cls,
        data: bytes,
        offset: int = 0,
        kek: bytes = bytes(),
        plain_sections: bool = False,
    ) -> "BootImageV21":
        """Parse image from bytes.

        :param data: Raw data of parsed image
        :param offset: The offset of input data
        :param kek: The Key for unwrapping DEK and MAC keys (required)
        :param plain_sections: Sections are not encrypted; this is used only for debugging,
            not supported by ROM code
        :return: BootImageV21 parsed object
        :raises Exception: raised when header is in incorrect format
        :raises Exception: raised when signature is incorrect
        :raises SPSDKError: Raised when kek is empty
        :raises Exception: raised when header's nonce not present"
        """
        if not kek:
            raise SPSDKError("kek cannot be empty")
        index = offset
        header_raw_data = data[index:index + ImageHeaderV2.SIZE]
        index += ImageHeaderV2.SIZE
        # TODO not used right now: hmac_data = data[index: index + cls.HEADER_MAC_SIZE]
        index += cls.HEADER_MAC_SIZE
        key_blob = data[index:index + cls.KEY_BLOB_SIZE]
        index += cls.KEY_BLOB_SIZE
        key_blob_unwrap = crypto_backend().aes_key_unwrap(kek, key_blob[:-8])
        dek = key_blob_unwrap[:32]
        mac = key_blob_unwrap[32:]
        # Parse Header
        header = ImageHeaderV2.parse(header_raw_data)
        if header.offset_to_certificate_block != (index - offset):
            raise Exception()
        # Parse Certificate Block
        cert_block = CertBlockV2.parse(data, index)
        index += cert_block.raw_size

        # Verify Signature
        signature_index = index
        # The image may containt SHA, in such a case the signature is placed
        # after SHA. Thus we must shift the index by SHA size.
        if header.flags & BootImageV21.FLAGS_SHA_PRESENT_BIT:
            signature_index += BootImageV21.SHA_256_SIZE
        result = cert_block.verify_data(
            data[signature_index:signature_index + cert_block.signature_size],
            data[offset:signature_index],
        )

        if not result:
            raise Exception()
        # Check flags, if 0x8000 bit is set, the SB file contains SHA-256 between
        # certificate and signature.
        if header.flags & BootImageV21.FLAGS_SHA_PRESENT_BIT:
            bootable_section_sha256 = data[index:index +
                                           BootImageV21.SHA_256_SIZE]
            index += BootImageV21.SHA_256_SIZE
        index += cert_block.signature_size
        # Check first Boot Section HMAC
        # TODO: not implemented yet
        # hmac_data_calc = crypto_backend().hmac(mac, data[index + CmdHeader.SIZE: index + CmdHeader.SIZE + ((2) * 32)])
        # if hmac_data != hmac_data_calc:
        #    raise Exception()
        if not header.nonce:
            raise SPSDKError("Header's nonce not present")
        counter = Counter(header.nonce)
        counter.increment(calc_cypher_block_count(index - offset))
        boot_section = BootSectionV2.parse(data,
                                           index,
                                           dek=dek,
                                           mac=mac,
                                           counter=counter,
                                           plain_sect=plain_sections)
        if header.flags & BootImageV21.FLAGS_SHA_PRESENT_BIT:
            computed_bootable_section_sha256 = internal_backend.hash(
                data[index:], algorithm="sha256")

            if bootable_section_sha256 != computed_bootable_section_sha256:
                raise SPSDKError(desc=(
                    "Error: invalid Bootable section SHA."
                    f"Expected {bootable_section_sha256.decode('utf-8')},"
                    f"got {computed_bootable_section_sha256.decode('utf-8')}"))
        adv_params = SBV2xAdvancedParams(dek=dek,
                                         mac=mac,
                                         nonce=header.nonce,
                                         timestamp=header.timestamp)
        obj = cls(
            kek=kek,
            product_version=str(header.product_version),
            component_version=str(header.component_version),
            build_number=header.build_number,
            advanced_params=adv_params,
        )
        obj.cert_block = cert_block
        obj.add_boot_section(boot_section)
        return obj
Beispiel #7
0
    def export(self,
               padding: Optional[bytes] = None,
               dbg_info: Optional[List[str]] = None) -> bytes:
        """Serialize image object.

        :param padding: header padding (8 bytes) for testing purpose; None to use random values (recommended)
        :param dbg_info: optional list, where debug info is exported in text form
        :return: exported bytes
        :raises SPSDKError: Raised when there is no boot section to be added
        :raises SPSDKError: Raised when certificate is not assigned
        :raises SPSDKError: Raised when private key is not assigned
        :raises SPSDKError: Raised when private header's nonce is invalid
        :raises SPSDKError: Raised when private key does not match certificate
        :raises SPSDKError: Raised when there is no debug info
        """
        # validate params
        if not self._boot_sections:
            raise SPSDKError("At least one Boot Section must be added")
        if self.cert_block is None:
            raise SPSDKError("Certificate is not assigned")
        if self.private_key_pem_data is None:
            raise SPSDKError("Private key not assigned, cannot sign the image")
        # Update internals
        if dbg_info is not None:
            dbg_info.append("[sb_file]")
        bs_dbg_info: Optional[List[str]] = list() if dbg_info else None
        self.update()
        # Export Boot Sections
        bs_data = bytes()
        # TODO: implement helper method for get key size in bytes. Now is working only with internal backend
        bs_offset = (ImageHeaderV2.SIZE + self.HEADER_MAC_SIZE +
                     self.KEY_BLOB_SIZE + self.cert_block.raw_size +
                     self.cert_block.signature_size)
        if self.header.flags & self.FLAGS_SHA_PRESENT_BIT:
            bs_offset += self.SHA_256_SIZE

        if not self._header.nonce:
            raise SPSDKError("Invalid header's nonce")
        counter = Counter(self._header.nonce,
                          calc_cypher_block_count(bs_offset))
        for sect in self._boot_sections:
            bs_data += sect.export(dek=self.dek,
                                   mac=self.mac,
                                   counter=counter,
                                   dbg_info=bs_dbg_info)
        # Export Header
        signed_data = self._header.export(padding=padding)
        if dbg_info:
            dbg_info.append("[header]")
            dbg_info.append(signed_data.hex())
        #  Add HMAC data
        first_bs_hmac_count = self._boot_sections[0].hmac_count
        hmac_data = bs_data[CmdHeader.SIZE:CmdHeader.SIZE +
                            (first_bs_hmac_count * 32) + 32]
        hmac = crypto_backend().hmac(self.mac, hmac_data)
        signed_data += hmac
        if dbg_info:
            dbg_info.append("[hmac]")
            dbg_info.append(hmac.hex())
        # Add KeyBlob data
        key_blob = crypto_backend().aes_key_wrap(self.kek, self.dek + self.mac)
        key_blob += b"\00" * (self.KEY_BLOB_SIZE - len(key_blob))
        signed_data += key_blob
        if dbg_info:
            dbg_info.append("[key_blob]")
            dbg_info.append(key_blob.hex())
        # Add Certificates data
        signed_data += self.cert_block.export()
        if dbg_info:
            dbg_info.append("[cert_block]")
            dbg_info.append(self.cert_block.export().hex())
        # Add SHA-256 of Bootable sections if requested
        if self.header.flags & self.FLAGS_SHA_PRESENT_BIT:
            signed_data += internal_backend.hash(bs_data)
        # Add Signature data
        if not self.cert_block.verify_private_key(self.private_key_pem_data):
            raise SPSDKError("Private key does not match certificate")
        signature = crypto_backend().rsa_sign(self.private_key_pem_data,
                                              signed_data)
        if dbg_info:
            dbg_info.append("[signature]")
            dbg_info.append(signature.hex())
            dbg_info.append("[boot_sections]")
            if not bs_dbg_info:
                raise SPSDKError("No debug information")
            dbg_info.extend(bs_dbg_info)
        return signed_data + signature + bs_data
Beispiel #8
0
def test_hash():
    plain_text = b'testestestestestestestestestestestestestestestestestestestest'
    text_sha256 = unhexlify(
        "41116FE4EFB90A050AABB83419E19BF2196A0E76AB8E3034C8D674042EE23621")
    calc_sha256 = internal_backend.hash(plain_text, 'sha256')
    assert calc_sha256 == text_sha256
Beispiel #9
0
def test_hash_invalid():
    with pytest.raises(SPSDKError):
        internal_backend.hash(data=b"n", algorithm="")