class AttributeExtractor(object): def __init__(self, cert_data=None, hash_segment_metadata=None): self.image_region = "Attestation Cert" self.attributes = SigningAttributes() if (cert_data, hash_segment_metadata) == (None, None): raise RuntimeError( "cert_data and hash_segment_data cannot both be None") if hash_segment_metadata is not None: self._init_using_hash_segment_and_cert(hash_segment_metadata, cert_data) self.image_region = "Hash Segment/Attestation Cert" else: self._init_using_cert(cert_data) def _init_using_hash_segment_and_cert(self, hash_segment_metadata, cert_data): self.attributes.update_from_hash_segment_and_attest_cert( hash_segment_metadata, cert_data) def _init_using_cert(self, cert_data): self.attributes.update_from_attest_cert(cert_data) def __repr__(self): return str(self.attributes)
def initialize(self, imageinfo): """ The following should be set at the end of the call self.certs[BaseSignerV2.ROOT].cert self.certs[BaseSignerV2.CA].cert self.certs[BaseSignerV2.ATTEST].cert self.signature """ # Initialize the base signer BaseSignerV2.initialize(self, imageinfo) # Create the request packet attrs = SigningAttributes() attrs.update_from_image_info_attrs(self.signing_attributes) request = { 'machine': platform.node(), 'sign_id': imageinfo.sign_id, 'to_sign': BinString(self.hash_to_sign), 'authority': imageinfo.authority, 'signing_attributes': self.get_general_properties_dict(imageinfo.general_properties), } # Send the signing request remote_host, remote_port = self.resolve_remote_server_info() client = QcomRemoteClient(host=remote_host, port=remote_port) logger.info('Signing with the remote server at ' + remote_host + ':' + str(remote_port) + '. Please wait for signing to complete.') response = client.sign_hash(request=request) # Check return code if response['retcode'] != 0: raise RuntimeError('Qcom remote signing failed [' + str(response['retcode']) + ']: ' + str(response['errstr'])) # Set the local vars root_cert, ca_cert, attest_cert, signature = (str(response['sig_pkg']['root']), str(response['sig_pkg']['ca']), str(response['sig_pkg']['attest']), str(response['sig_pkg']['signature'])) # Make sure all assets are present if signature is None: raise RuntimeError("Signature is missing") if attest_cert is None: raise RuntimeError("Attestation Certificate is missing") if ca_cert is None: raise RuntimeError("CA Certificate is missing") if root_cert is None: raise RuntimeError("Root Certificate is missing") # Set all the variables self.certs[self.ROOT].cert = cert.get_cert_in_format(root_cert, utils.FORMAT_DER, utils.FORMAT_PEM) self.certs[self.CA].cert = cert.get_cert_in_format(ca_cert, utils.FORMAT_DER, utils.FORMAT_PEM) self.certs[self.ATTEST].cert = cert.get_cert_in_format(attest_cert, utils.FORMAT_DER, utils.FORMAT_PEM) self.signature = signature
def validate_signing_attributes(self, cert_chain_der, imageinfo): if not cert_chain_der: raise RuntimeError( 'Cert chain must contain at least 1 certificate.') attest_cert = cert_chain_der[0] attr_config = SigningAttributes() attr_config.update_from_image_info_attrs(imageinfo.signing_attributes) attr_attest = SigningAttributes() attr_attest.update_from_attest_cert(attest_cert) return attr_attest.compare(attr_config, attr_attest.EXCLUDE_NON_ATTEST)
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 __init__(self, cert_data=None, hash_segment_metadata=None): self.image_region = "Attestation Cert" self.attributes = SigningAttributes() if (cert_data, hash_segment_metadata) == (None, None): raise RuntimeError( "cert_data and hash_segment_data cannot both be None") if hash_segment_metadata is not None: self._init_using_hash_segment_and_cert(hash_segment_metadata, cert_data) self.image_region = "Hash Segment/Attestation Cert" else: self._init_using_cert(cert_data)
def update_tosign_data(cls, hash_to_sign, binary_to_sign, imageinfo, pf): pf.file_data['to_sign']['data'] = hash_to_sign signing_config = \ { 'binary_len': len(binary_to_sign), 'sign_id': imageinfo.sign_id, 'chipset': imageinfo.chipset, } # Check if signing attributes should be sent as well signer_attributes = imageinfo.config.signing.signer_attributes.remote_client_signer_attributes if signer_attributes and signer_attributes.send_signing_overrides: attrs = SigningAttributes() attrs.update_from_image_info_attrs(imageinfo.signing_attributes) signing_config['signing_attributes'] = attrs.get_json() pf.file_data['signing_config']['data'] = json.dumps(signing_config)
def update_image_serial_bound_notes(authority, parsegen, imageinfo): if authority == AUTHORITY_QTI: cert_chain_der = cert.split_cert_chain_bin( parsegen._mbn_parsegen.cert_chain_qti) else: cert_chain_der = cert.split_cert_chain_bin( parsegen._mbn_parsegen.cert_chain) hash_segment_meta_data = parsegen.get_hash_segment_metadata( authority=authority) if not cert_chain_der and not hash_segment_meta_data: return image_signing_attributes = AttributeExtractor( cert_data=cert_chain_der[0] if cert_chain_der else None, hash_segment_metadata=hash_segment_meta_data, attributes=SigningAttributes()).attributes msm_part = None soc_hw_version = None soc_vers = image_signing_attributes.soc_vers in_use_soc_hw_version = image_signing_attributes.in_use_soc_hw_version if in_use_soc_hw_version: soc_hw_version = image_signing_attributes.hw_id[:-8] else: msm_part = image_signing_attributes.hw_id[:-8] platform_independent = False if (image_signing_attributes.metadata_major_version, image_signing_attributes.metadata_minor_version) == \ (METADATA_MAJOR_VERSION_1, METADATA_MINOR_VERSION_0): if not image_signing_attributes.in_use_soc_hw_version and not image_signing_attributes.in_use_hw_id: platform_independent = True try: serial_binding_feature_set = serial_bound_feature_manager.get_feature_set( ChipsetProfile(parsegen.secboot_version, in_use_soc_hw_version, msm_part, soc_vers, soc_hw_version, platform_independent)) serial_binding_feature_set.update_image_serial_bound_notes( image_signing_attributes, imageinfo.create_serial_bound_notes(authority)) except RuntimeError: pass
def sign_hash(self, hash_to_sign, imageinfo, binary_to_sign=None, debug_dir=None, sha_algo=None, binary_to_sign_len=None): # Find the hash_package packages_folder = imageinfo.dest_image.image_dir to_sign_package = Package(packages_folder, src.get_class_ToSignPackageFiles()) try: if to_sign_package.package is None: raise RemoteSignerError( 'Please place the to_sign package in ' + packages_folder, RemoteSignerError.E_PACKAGE_MISSING) # Read the hash package to_sign_package.update_data() to_sign, signing_config = src.use_tosign_data(to_sign_package.pf) signing_config = json.loads(signing_config) # Get the binary length binary_to_sign_len = int(signing_config['binary_len']) # Use the signing overrides signing_attributes = signing_config.get('signing_attributes', None) if signing_attributes is not None: signer_attributes = imageinfo.config.signing.signer_attributes.remote_signer_attributes if signer_attributes and signer_attributes.allow_signing_overrides: sa = SigningAttributes() sa.update_from_json(signing_attributes) sa.update_image_info_attrs(imageinfo.signing_attributes) else: raise RemoteSignerError( 'Signing attributes override is not accepted.', RemoteSignerError.E_PACKAGE_MISSING) except RemoteSignerError: raise except RuntimeError as e: raise RemoteSignerError(e.message) # Sign using openssl locally signer_output = OpenSSLSigner.sign_hash( self, hash_to_sign, imageinfo, binary_to_sign=binary_to_sign, debug_dir=debug_dir, sha_algo=sha_algo, binary_to_sign_len=binary_to_sign_len) # Generate the signature package signature_package = Package(packages_folder, src.get_class_SignaturePackageFiles()) src.update_signature_package(signer_output.signature, signer_output.attestation_cert, signer_output.attestation_ca_cert, [signer_output.root_cert], signature_package.pf) signature_package.package = src.get_signature_package_path(imageinfo) signature_package.save_package() return signer_output
def validate_signing_attributes(self, cert_chain_der, imageinfo, extracted_image_attributes): if not cert_chain_der: raise RuntimeError('Cert chain must contain at least 1 certificate.') attr_config = SigningAttributes() attr_config.update_from_image_info_attrs(imageinfo.signing_attributes) return extracted_image_attributes.compare(attr_config, extracted_image_attributes.EXCLUDE_NON_ATTEST)