def _extract_hash_segment_algorithm(self, hash_table_size=0): if hash_table_size > 0: hash_count = self.get_hash_table(get_hash_count=True) hash_size = hash_table_size / (hash_count + 2) # add 2: +1 for Hash Table Entry 0 (ELF + Program Header). +1 for Hash Table Entry 1 (Dummy Entry) logger.debug("Number of hash entries: " + str(hash_count)) logger.debug("Hash table size: " + str(hash_table_size)) logger.debug("Hash size: " + str(hash_size)) if hash_size in SecParseGenElf.HASH_SIZE_TO_ALGO_MAP.keys(): self._mbn_parsegen.extracted_segment_hash_algorithm = SecParseGenElf.HASH_SIZE_TO_ALGO_MAP[hash_size] else: self._mbn_parsegen.extracted_segment_hash_algorithm = ParseGenMbn.UNKNOWN_ALGORITHM logger.debug("Determined hash table algorithm: " + self._mbn_parsegen.extracted_segment_hash_algorithm) # Determine if parsegen was created during validation (-a) and print error if hash algorithm mismatch is present extracted_segment_hash_algorithm = self._mbn_parsegen.extracted_segment_hash_algorithm segment_hash_algorithm = self.segment_hash_algorithm if self.segment_hash_algorithm is not None else "sha256" if extracted_segment_hash_algorithm != segment_hash_algorithm and (self.validating or self.is_encrypted()): from sectools.common.utils.c_misc import create_mismatch_table errstr = list() mismatches = list() mismatches.append(("Hash Segment Algorithm", extracted_segment_hash_algorithm, segment_hash_algorithm)) create_mismatch_table(mismatches, errstr, data_type_to_compare="Attribute", image_region="Hash Segment") logger.warning('Following validations failed for the image:\n ' + '\n '.join([(str(i + 1) + '. ' + e) for i, e in enumerate(errstr)]))
def validate_more(self, parsegen, imageinfo, attribute_extractor, cert_chain_der, errstr, exclude_attributes=None): # Signing attributes validation if imageinfo is not None: if not cert_chain_der: errstr.append( 'Certificate chain for ' + parsegen.authority + ' is not present in image signing attributes verification') else: mismatches = self.validate_signing_attributes( cert_chain_der, imageinfo, attribute_extractor.attributes, excluded_attributes=exclude_attributes) create_mismatch_table( mismatches, errstr, operation="signing", data_type_to_compare="Attribute", image_region=attribute_extractor.image_region) if errstr: raise RuntimeError( 'Following validations failed for the image:\n ' + '\n '.join([ '%d. %s' % (signed_authority, e) for signed_authority, e in enumerate(errstr, start=1) ]))
def _decode_binary_blob(self, binary_blob, validating): if len(binary_blob) != self.SPEC_SIZE_BYTES: raise RuntimeError( "L2 Associated Data blob is of the wrong size") offset = 0 end = self.L2_ASSOCIATED_DATA_SIZE_FLD_LEN_BYTES self.l2_associated_data_size, = struct.unpack( ">H", binary_blob[offset:end]) offset = end + EncryptionParamsSectionBody_1_0_L2_1_0.RSVD_BYTE_LEN_BYTES * 2 end = offset + self.MAJOR_VERSION_FLD_LEN_BYTES + \ self.MINOR_VERSION_FLD_LEN_BYTES + \ self.KEY_LADDER_LEN_FLD_LEN_BYTES + \ EncryptionParamsSectionBody_1_0_L2_1_0.RSVD_BYTE_LEN_BYTES major_version, minor_version, self.key_ladder_length, tmp = struct.unpack( "=BBBB", binary_blob[offset:end]) offset = end if (major_version, minor_version) != (self.MAJOR_VERSION, self.MINOR_VERSION): raise RuntimeError(( "Encryption Parameters L2 Associated Data version \"{0}.{1}\" does not match " "expected version \"{2}.{3}\"\n Ensure that the correct selected_encryptor value is " "set.".format(major_version, minor_version, self.MAJOR_VERSION, self.MINOR_VERSION))) image_id = int( round(math.log(self._decode_image_id(binary_blob[offset:]), 2))) if image_id != self.image_id: if validating: errstr = list() mismatches = list() mismatches.append( ("sw_id", "0x%X" % image_id, "0x%X" % self.image_id)) create_mismatch_table(mismatches, errstr, operation="encryption", data_type_to_compare="Attribute", image_region="Encryption Parameters") raise RuntimeError( 'Following validations failed for the image:\n ' + '\n '.join([(str(i + 1) + '. ' + e) for i, e in enumerate(errstr)])) else: logger.warning( ("Extracted Encryption Parameters sw_id" " value \"{0}\" does not match config value " "\"{1}\"\n\t Encryption Parameters sw_id value " "will be updated with value \"{1}\"".format( hex(image_id), hex(self.image_id))))
def validate_hash_segment_metadata(self, authority=None): from sectools.common.utils.c_misc import create_mismatch_table # Validate metedata for unsigned image. # If image is signed, validation is done in signer. if not self.is_signed(authority): hash_segment_metadata = self.get_hash_segment_metadata(authority) if hash_segment_metadata is not None: from sectools.features.isc.signer.signerutils.attribute_extractor import AttributeExtractor from sectools.features.isc.signer.signerutils.attributes import SigningAttributes errstr = [] extracted_image_attributes = AttributeExtractor( hash_segment_metadata=hash_segment_metadata).attributes attr_config = SigningAttributes() attr_config.update_from_image_info_attrs(self.imageinfo) mismatches = extracted_image_attributes.compare( attr_config, extracted_image_attributes.EXCLUDE_HASH_SEGMENT_METADATA) create_mismatch_table(mismatches, errstr, operation="signing", data_type_to_compare="Attribute", image_region="Hash Segment") if errstr: raise RuntimeError( 'Following validations failed for the image:\n ' + '\n '.join([(str(authority + 1) + '. ' + e) for authority, e in enumerate( errstr)])) # Ensure metadata sw_ids match hash_segment_metadata = self.get_hash_segment_metadata( defines.AUTHORITY_OEM) hash_segment_metadata_qti = self.get_hash_segment_metadata( defines.AUTHORITY_QTI) if hash_segment_metadata and hash_segment_metadata_qti and hash_segment_metadata[ "sw_id"] != hash_segment_metadata_qti["sw_id"]: errstr = [] mismatches = [('sw_id', [hex(hash_segment_metadata["sw_id"])], [hex(hash_segment_metadata_qti["sw_id"])])] create_mismatch_table(mismatches, errstr, operation="signing", data_type_to_compare="Attribute", image_region="Metadata", image_region2="Metadata QTI") if errstr: raise RuntimeError( 'Following validations failed for the image:\n ' + '\n '.join([(str(authority + 1) + '. ' + e) for authority, e in enumerate(errstr)]))
def validate_header(self, hdr): possible_image_sizes = self._compute_possible_image_sizes(hdr) try: extracted_max_num_root_certs = possible_image_sizes[hdr.image_size] if (self.config_max_num_root_certs and extracted_max_num_root_certs is not None and extracted_max_num_root_certs != self.config_max_num_root_certs): error_str = [] create_mismatch_table( [("max_num_root_certs", extracted_max_num_root_certs, self.config_max_num_root_certs)], error_str, operation="signing", image_region="Hash Segment") raise RuntimeError("\n".join(error_str)) except KeyError: # Check for QTI sign asset related errors qti_segment, qti_err_string = '', '' if hdr.supports_qti_signing(): qti_segment = (' ' + 'QTI Signature Size: ' + hex_addr(hdr.sig_size_qti) + '\n' ' ' + 'QTI Cert Chain Size: ' + hex_addr( hdr.cert_chain_size_qti) + '\n') # Handles case where incoming data header contains residual data in QTI sign/cc fields if (hdr.image_size + hdr.sig_size_qti + hdr.cert_chain_size_qti in possible_image_sizes or hdr.cert_chain_size_qti == MBN_PTR_MAX): qti_err_string = ('Unsigned hash table header must not contain junk data in ' 'QTI attribute fields when double-signing.' + '\n\n') raise RuntimeError("""MBN header verification failed. Image size does not match the sum of its segments: Code Size: {header_code_size} {qti_segment} Signature Size: {signature_size} Cert Chain Size: {cert_chain_size} Image Size (expected): *{expected_image_size} Image Size (from file): *{image_size} {qti_err_string} Header: {header} """.format(header_code_size=hex_addr(hdr.code_size), qti_segment=qti_segment, signature_size=hex_addr(hdr.sig_size), cert_chain_size=hex_addr(hdr.cert_chain_size), expected_image_size=(str([hex_addr(s) for s in possible_image_sizes]) + ' (Code + Signature + Cert Chain)'), image_size=hex_addr(hdr.image_size), qti_err_string=qti_err_string, header=repr(hdr)))
def _decode_binary_blob(self, binary_blob, validating): if len(binary_blob) != EncryptionParamsSectionBody.L2AssociatedData.SPEC_SIZE_BYTES: raise RuntimeError("L2 Associated Data blob is of the wrong size") string_offset = 0 string_end = EncryptionParamsSectionBody.L2AssociatedData.L2_ASSOCIATED_DATA_SIZE_FLD_LEN_BYTES self.l2_associated_data_size, = struct.unpack(">H", binary_blob[string_offset:string_end]) string_offset = string_end + EncryptionParamsSectionBody.RSVD_BYTE_LEN_BYTES * 2 string_end = string_offset + EncryptionParamsSectionBody.L2AssociatedData.MAJOR_VERSION_FLD_LEN_BYTES + \ EncryptionParamsSectionBody.L2AssociatedData.MINOR_VERSION_FLD_LEN_BYTES + \ EncryptionParamsSectionBody.L2AssociatedData.KEY_LADDER_LEN_FLD_LEN_BYTES + \ EncryptionParamsSectionBody.RSVD_BYTE_LEN_BYTES major_version, minor_version, self.key_ladder_length, tmp = struct.unpack("=BBBB", binary_blob[string_offset:string_end]) string_offset = string_end if (major_version, minor_version) != (self.major_version, self.minor_version): raise RuntimeError(("Encryption Parameters L2 Associated Data version \"{0}.{1}\" does not match expected version \"{2}.{3}\"" "\n Ensure that the correct encryptor value is set.").format(major_version, minor_version, self.major_version, self.minor_version)) if (major_version, minor_version) == (EncryptionParamsSectionBody.L2AssociatedData.MAJOR_VERSION_FLD_VAL_1, EncryptionParamsSectionBody.L2AssociatedData.MINOR_VERSION_FLD_VAL_0): string_end = string_offset + EncryptionParamsSectionBody.L2AssociatedData.IMAGE_ID_BITMAP_FLD_VERSION_1_0_LEN_BYTES image_id_bitmap, = struct.unpack("=I", binary_blob[string_offset:string_end]) elif (major_version, minor_version) == (EncryptionParamsSectionBody.L2AssociatedData.MAJOR_VERSION_FLD_VAL_1, EncryptionParamsSectionBody.L2AssociatedData.MINOR_VERSION_FLD_VAL_1): string_end = string_offset + EncryptionParamsSectionBody.L2AssociatedData.IMAGE_ID_BITMAP_FLD_VERSION_1_1_LEN_BYTES image_id_bitmap_upper, image_id_bitmap_lower = struct.unpack("=QQ", binary_blob[string_offset:string_end]) image_id_bitmap = image_id_bitmap_lower * (2 ** 64) + image_id_bitmap_upper else: raise RuntimeError("Configured Encryption Parameters L2 Associated Data version \"{0}.{1}\" is invalid.".format(self.major_version, self.minor_version)) image_id = int(math.log(image_id_bitmap, 2)) if image_id != self.image_id: if validating: errstr = list() mismatches = list() mismatches.append((EncryptionParamsSectionBody.L2AssociatedData.IMAGE_ID, "0x%X" % image_id, "0x%X" % self.image_id)) create_mismatch_table(mismatches, errstr, operation="encryption", data_type_to_compare="Attribute", image_region="Encryption Parameters") logger.error('Following validations failed for the image:\n ' + '\n '.join([(str(i + 1) + '. ' + e) for i, e in enumerate(errstr)])) else: logger.warning(("Extracted Encryption Parameters " + EncryptionParamsSectionBody.L2AssociatedData.IMAGE_ID + " value \"{0}\" does not match config value \"{1}\"" "\n\t Encryption Parameters " + EncryptionParamsSectionBody.L2AssociatedData.IMAGE_ID + " value will be updated with value \"{1}\"").format(hex(image_id), hex(self.image_id)))
def _unpack(self, data, checking_if_licmngr_seg): # Validate size of segment matches expected size if len(data) != self.get_size(): raise RuntimeError( "License Manager segment is of invalid size {0} bytes." "\n Allowed size is {1} bytes.".format( len(data), self.get_size())) # Extract segment values ( namesz, descsz, type, name, version, client_id, lib_id, is_cass_signed, ) = struct.unpack(self._get_format(), data) # Validate extracted values error = list() if namesz != self.namesz: error += [ "License Manager segment contains invalid namesz value {0}.". format(namesz), "namesz must be {0}.".format(self.namesz) ] if descsz != self.descsz: error += [ "License Manager segment contains invalid descsz value {0}.". format(descsz), "descsz must be {0}.".format(self.descsz) ] if type != self.type: error += [ "License Manager segment contains invalid type value {0}.". format(type), "type must be {0}.".format(self.type) ] if name != self.name: error += [ "License Manager segment contains invalid name value {0}.". format(name), "name must be {0}.".format(self.name) ] if error: raise RuntimeError("\n ".join(error)) if not checking_if_licmngr_seg: # Update existing segment if not self.validating: if version != self.version: logger.warning( "License Manager segment contains unknown version value {0}.\n " "Updating version value to {1}".format( version, self.version)) if client_id != self.client_id: logger.warning( "License Manager segment's client_id value {0} does not match configured value {1}.\n " "Updating client_id value to {1}".format( hex(client_id), hex(self.client_id))) if lib_id != self.lib_id: logger.warning( "License Manager segment's lib_id value {0} does not match configured value {1}.\n " "Updating lib_id value to {1}".format( hex(lib_id), hex(self.lib_id))) if is_cass_signed != self.is_cass_signed: logger.warning( "License Manager segment's is_cass_signed value {0} does not match configured value {1}.\n " "Updating is_cass_signed value to {1}".format( is_cass_signed, self.is_cass_signed)) # Validate segment against config else: errstr = list() mismatches = list() if client_id != self.client_id: mismatches.append( ("client_id", hex(client_id), hex(self.client_id))) if lib_id != self.lib_id: mismatches.append( ("lib_id", hex(lib_id), hex(self.lib_id))) create_mismatch_table(mismatches, errstr, operation="license management", data_type_to_compare="Attribute", image_region="License Manager Segment") if errstr: raise RuntimeError( 'Following validations failed for the image:\n ' + '\n '.join([(str(i + 1) + '. ' + e) for i, e in enumerate(errstr)])) self.version = version self.client_id = client_id self.lib_id = lib_id self.is_cass_signed = is_cass_signed
def validate(self, image, root_cert_hash=None, imageinfo=None): if image.is_signed(): # Create error string errstr = [] # Extracted hash segment metadata for all authorities metadata = {AUTHORITY_QTI: None, AUTHORITY_OEM: None} for signed_authority, data_to_sign, data_signature, cert_chain, hash_segment_metadata in image.get_signing_assets(): # Save extracted hash segment metadata for signing attribute validation metadata[signed_authority] = hash_segment_metadata # Check if empty if not data_signature and not cert_chain: if signed_authority != image.authority: logger.warning(signed_authority + ' signature is not present') else: raise RuntimeError(signed_authority + ' signature is not present') continue # Extract the cert chain list cert_chain_der = crypto.cert.split_cert_chain_bin(cert_chain) # Extract signing attributes from image extracted_image_attributes = AttributeExtractor(cert_data=cert_chain_der[0], hash_segment_metadata=hash_segment_metadata).attributes # Signature verification if not self.validate_sig(data_to_sign, data_signature, cert_chain_der, signed_authority=signed_authority, extracted_image_attributes=extracted_image_attributes): errstr.append(signed_authority + ' signature is invalid') # OID Validation if len(cert_chain_der) == 3: if not self.validate_oid_from_certs(cert_chain_der[1], cert_chain_der[0]): errstr.append('OID values in the certificate are invalid') # Extract the cert chain list cert_chain_blob = image.cert_chain cert_chain_der = crypto.cert.split_cert_chain_bin(cert_chain_blob) # Extract signing attributes from image attribute_extractor = AttributeExtractor(cert_data=cert_chain_der[0], hash_segment_metadata=metadata[image.authority]) extracted_image_attributes = attribute_extractor.attributes # Root cert hash validation if root_cert_hash: if not cert_chain_der: errstr.append('Root certificate for ' + image.authority + ' is not present in image for root cert hash verification') elif not self.validate_root_cert_hash(cert_chain_der, root_cert_hash, extracted_image_attributes): errstr.append('Root certificate from image does not match the given root cert hash value') # Signing attributes validation if imageinfo is not None: if not cert_chain_der: errstr.append('Certificate chain for ' + image.authority + ' is not present in image signing attributes verification') else: mismatches = self.validate_signing_attributes(cert_chain_der, imageinfo, extracted_image_attributes) create_mismatch_table(mismatches, errstr, operation="signing", data_type_to_compare="Attribute", image_region=attribute_extractor.image_region) if errstr: raise RuntimeError('Following validations failed for the image:\n ' + '\n '.join([(str(signed_authority + 1) + '. ' + e) for signed_authority, e in enumerate(errstr)])) return True else: raise RuntimeError("Image supplied is not signed.")