def parse( cls, data: bytes, offset: int = 0, dek: bytes = b"", mac: bytes = b"", counter: Optional[Counter] = None, ) -> "CertSectionV2": """Parse Certificate Section from bytes array. :param data: Raw data of parsed image :param offset: The offset of input data :param dek: The DEK value in bytes (required) :param mac: The MAC value in bytes (required) :param counter: The counter object (required) :return: parsed cert section v2 object :raises SPSDKError: Raised when dek, mac, counter are not valid :raises SPSDKError: Raised when there is invalid header HMAC, TAG, FLAGS, Mark :raises SPSDKError: Raised when there is invalid certificate block HMAC """ if not isinstance(dek, bytes): raise SPSDKError("DEK value has invalid format") if not isinstance(mac, bytes): raise SPSDKError("MAC value has invalid format") if not isinstance(counter, Counter): raise SPSDKError("Counter value has invalid format") index = offset header_encrypted = data[index:index + CmdHeader.SIZE] index += CmdHeader.SIZE header_hmac = data[index:index + cls.HMAC_SIZE] index += cls.HMAC_SIZE cert_block_hmac = data[index:index + cls.HMAC_SIZE] index += cls.HMAC_SIZE if header_hmac != crypto_backend().hmac(mac, header_encrypted): raise SPSDKError("Invalid Header HMAC") header_encrypted = crypto_backend().aes_ctr_decrypt( dek, header_encrypted, counter.value) header = CmdHeader.parse(header_encrypted) if header.tag != EnumCmdTag.TAG: raise SPSDKError(f"Invalid Header TAG: 0x{header.tag:02X}") if header.flags != (EnumSectionFlag.CLEARTEXT | EnumSectionFlag.LAST_SECT): raise SPSDKError(f"Invalid Header FLAGS: 0x{header.flags:02X}") if header.address != cls.SECT_MARK: raise SPSDKError(f"Invalid Section Mark: 0x{header.address:08X}") # Parse Certificate Block cert_block = CertBlockV2.parse(data, index) if cert_block_hmac != crypto_backend().hmac( mac, data[index:index + cert_block.raw_size]): raise SPSDKError("Invalid Certificate Block HMAC") index += cert_block.raw_size cert_section_obj = cls(cert_block) counter.increment(calc_cypher_block_count(index - offset)) return cert_section_obj
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
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 :raise Exception: raised when header is in incorrect format :raise Exception: raised when signature is incorrect """ assert kek, '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 if not cert_block.verify_data( data[index:index + cert_block.signature_size], data[offset:index]): raise Exception() 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() assert header.nonce 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) 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