def validate_sig(self, to_sign, signature, cert_chain_der): # Check that openssl is available try: crypto_functions.are_available([crypto_functions.MOD_OPENSSL]) except Exception as e: raise RuntimeError('Cannot validate signing: ' + str(e)) hmac_params = crypto_functions.get_hmacparams_from_certificate_chain( cert_chain_der[0]) hash_algo = crypto_functions.get_hash_algorithm_from_certicate_chain( cert_chain_der[0]) cert_chain_pem = [] for cert in cert_chain_der: cert_chain_pem.append(crypto_functions.cert_der_to_pem(cert)) public_key = crypto_functions.get_public_key_from_cert_chain( cert_chain_pem) decrypted_hash = crypto_functions.decrypt_with_public_key( signature, public_key) hasher = Hasher() image_hash = hasher.qcom_hmac(to_sign, hmac_params, hash_algo) return image_hash == decrypted_hash
def validate_sig_using_hash(self, image_hash, signature, cert_chain_der): # Check that openssl is available try: crypto_functions.are_available([crypto_functions.MOD_OPENSSL]) except Exception as e: raise RuntimeError('Cannot validate signing: ' + str(e)) use_pss = crypto_functions.cert_uses_pss(cert_chain_der[0], 'DER') use_dsa = crypto_functions.cert_uses_dsa(cert_chain_der[0], 'DER') if use_pss: logger.info('image is signed with RSAPSS') if use_dsa: logger.info('image is signed with ECDSA') if not use_dsa and not use_pss: logger.info('image is signed with PKCS') hash_algo = crypto_functions.get_hash_algorithm_from_certicate_chain( cert_chain_der[0]) cert_chain_pem = [] for cert in cert_chain_der: cert_chain_pem.append(crypto_functions.cert_der_to_pem(cert)) public_key = crypto_functions.get_public_key_from_cert_chain( cert_chain_pem) decrypted_hash = crypto_functions.decrypt_with_public_key( signature, public_key, image_hash, use_pss, use_dsa, pss_digest_algorithm=hash_algo) return image_hash == decrypted_hash
def __init__(self, imageinfo, debug_dir=None): # Check that crypto binaries are available try: crypto_functions.are_available([crypto_functions.MOD_OPENSSL]) except Exception as e: raise RuntimeError('Cannot proceed with encryption/decryption: ' + str(e)) BaseEncdec.__init__(self, imageinfo, debug_dir=debug_dir)
def sign(self, hash_to_sign, imageinfo, binary_to_sign=None, debug_dir=None, sha_algo=None): # Check that openssl is available try: crypto_functions.are_available([crypto_functions.MOD_OPENSSL]) except Exception as e: raise RuntimeError('Cannot sign: ' + str(e)) return self._sign(hash_to_sign, imageinfo, binary_to_sign, sha_algo)
def validate_sig(self, to_sign, signature, cert_chain_der): # Check that openssl is available try: crypto_functions.are_available([crypto_functions.MOD_OPENSSL]) except Exception as e: raise RuntimeError('Cannot validate signing: ' + str(e)) # Get the hash use_pss = crypto_functions.cert_uses_pss(cert_chain_der[0], 'DER') hmac_params = crypto_functions.get_hmacparams_from_certificate_chain( cert_chain_der[0]) hash_algo = crypto_functions.get_hash_algorithm_from_certicate_chain( cert_chain_der[0]) image_hash = Hasher().get_hash( to_sign, hmac_params=hmac_params if not use_pss else None, sha_algo=hash_algo) # Validate the hash return self.validate_sig_using_hash(image_hash, signature, cert_chain_der)
def process(self, verify_setup=False, integrity_check=False, sign=False, encrypt=False, decrypt=False, val_image=False, val_integrity_check=False, val_sign=False, val_encrypt=False, root_cert_hash=None, progress_cb=PROGRESS_CB_PYPASS): """Performs the secure-image related operations specified from the params. :param bool verify_setup: Verify that the configuration of the object is correct and return. This will ignore any other params. :param bool integrity_check: Add integrity check to the image. :param bool sign: Sign the images. (Re-sign if image is already signed) :param bool encrypt: Encrypt the images. (Re-encrypt if image is already encrypted) :param bool val_image: Validate the integrity of the image against the config file. :param bool val_integrity_check: Validate the integrity check in the image. :param bool val_sign: Validate that the image is signed and validate the integrity of the signing related data. :param bool val_encrypt: Validate that the image is encrypted and validate the integrity of the encryption related data. :param cb progress_cb: Callback method to get a status update during processing. Callback method should have this format: :: def progress_cb(status_string, progress_percent): \""" :param str status_string: the current status. :param int progress_percent: the progress (in percent) \""" ... """ from imageinfo import ImageInfo, StatusInfo from sectools.features.isc.signer.remote import RemoteSignerNote # Ensure that one or more image files is provided for processing if self._stager is None or not self.image_path_list: raise RuntimeError( 'Please specify one or more images for processing') # Ensure the correct set of operations is provided if not (verify_setup or integrity_check or sign or encrypt or decrypt or val_image or val_integrity_check or val_sign or val_encrypt): raise RuntimeError( 'Please specify one or more operations to perform.') # Print the openssl path version = '' path_info = 'is unavailable. Please run "which openssl" and "openssl version" to check openssl version info, and upgrade to required version' try: from sectools.common import crypto from sectools.common.crypto import crypto_functions version = 'v' + crypto.discovery.openssl.OPENSSL_VERSION_MIN + ' or greater ' crypto_functions.are_available([crypto_functions.MOD_OPENSSL]) path_info = 'is available at: "' + crypto.openssl_binary_implementation.openssl_binary_path + '"' except Exception as e: pass logger.info('Openssl ' + version + path_info) if verify_setup: logger.note('The inputs provided (config, cmd args) are valid.') return # Start processing images total_images = len(self.image_info_list) progress = ProgressNotifier(total_images, progress_cb, PROGRESS_TOTAL_STAGES) for idx, image in enumerate(self.image_info_list): assert isinstance(image, ImageInfo) logger.info( '------------------------------------------------------') status_string = ('Processing ' + str(idx + 1) + '/' + str(total_images) + ': ' + image.image_under_operation) logger.info(status_string + '\n') # Send a progress notification to the toplevel progress.status = status_string progress.cur = idx progress.cur_stage = 0 file_logger_id = None try: # Create the required directory structure for this image image_output_dir = image.dest_image.image_dir try: c_path.create_dir(image_output_dir) except Exception as e: raise RuntimeError('Could not create output directory: ' + image_output_dir + '\n' ' ' + 'Error: ' + str(e)) # Enable/Disable debug image.dest_image.debug_enable = self.debug c_path.create_debug_dir(image.dest_image.debug_dir) # Set the root cert hash image.validation_root_cert_hash = root_cert_hash # Enable file logging to the directory file_logger_id = logger.add_file_logger( c_path.join(image_output_dir, 'SecImage_log.txt'), logger.verbosity) # Create the security policies list for this image security_policy_list = create_security_policy_list(image) # Parsegen object parsegen = None # For secure operations if integrity_check or sign or encrypt or decrypt: parsegen = self._process_secure_operation( image, progress, security_policy_list, integrity_check, sign, encrypt, decrypt) # For validation if val_image or val_integrity_check or val_sign or val_encrypt: parsegen = self._process_validation( image, progress, security_policy_list, val_image, val_integrity_check, val_sign, val_encrypt) # Print the image data if parsegen is not None: logger.info('\n' + str(parsegen)) # Set overall processing to true if not ((val_image and image.status.validate_parsegen.state == StatusInfo.ERROR) or (val_integrity_check and image.status.validate_integrity_check.state == StatusInfo.ERROR) or (val_sign and image.status.validate_sign.state == StatusInfo.ERROR) or (val_encrypt and image.status.validate_encrypt.state == StatusInfo.ERROR)): image.status.overall.state = StatusInfo.SUCCESS except RemoteSignerNote as e: logger.info('NOTE: ' + str(e), color=logger.YELLOW) except Exception: logger.error(traceback.format_exc()) logger.error(sys.exc_info()[1]) if file_logger_id is not None: logger.removeFileLogger(file_logger_id) logger.info( '------------------------------------------------------\n') progress.complete()
def sign_hash(self, hash_to_sign, imageinfo, binary_to_sign=None, debug_dir=None, sha_algo=None, binary_to_sign_len=None): # Check that openssl is available try: crypto_functions.are_available([crypto_functions.MOD_OPENSSL]) except Exception as e: raise RuntimeError('Cannot sign: ' + str(e)) # abstract some of the image information signing_attributes = imageinfo.signing_attributes general_properties = imageinfo.general_properties # GET OPENSSL DATA openssl_configfile = self.openssl_info.openssl_config openssl_attest_ca_xts = self.openssl_info.attest_ca_xts openssl_ca_cert_xts = self.openssl_info.ca_cert_xts # GET SIGNING ATTRIBUTE DATA debug_val = int(signing_attributes.debug, 16) if signing_attributes.debug is not None else None oem_id = int(signing_attributes.oem_id, 16) & 0xFFFF model_id = int(signing_attributes.model_id, 16) & 0xFFFF num_certs_in_certchain = general_properties.num_certs_in_certchain app_id = int(signing_attributes.app_id, 16) if signing_attributes.app_id is not None else None crash_dump = int( signing_attributes.crash_dump, 16) if signing_attributes.crash_dump is not None else None rot_en = int(signing_attributes.rot_en, 16) if signing_attributes.rot_en is not None else None mask_soc_hw_version = int( signing_attributes.mask_soc_hw_version, 16) if signing_attributes.mask_soc_hw_version is not None else None in_use_soc_hw_version = signing_attributes.in_use_soc_hw_version if signing_attributes.in_use_soc_hw_version is not None else None use_serial_number_in_signing = signing_attributes.use_serial_number_in_signing if signing_attributes.use_serial_number_in_signing is not None else None # GET CERTIFICATE INFORMATION cert_dict = {} cert_dict['id'] = imageinfo.cert_config cert_dict['chip'] = self.config.metadata.chipset cert_dict['keysize'] = general_properties.key_size cert_dict['exponent'] = general_properties.exponent cert_dict['mrc_index'] = general_properties.mrc_index # Can't use imageinfo.data_prov_basepath because MockImage can't use it cert_dict['dp_path'] = self.config.data_provisioning.base_path self.cert_data_object = CertData(cert_dict) crypto_params_dict = self.cert_data_object.get_crypto_params() # Create the attestation_certificate_key_pair attestation_certificate_key_pair = None root_certificate_params = crypto_params_dict[ 'root_certificate_properties'] root_certificate_params_is_valid, generate_new_root_certificate = self._validate_certificate_params_dict( root_certificate_params) if root_certificate_params_is_valid: if generate_new_root_certificate: logger.info('Generating new Root certificate and a random key') generated_root_key_pair = crypto_functions.gen_rsa_key_pair( general_properties.key_size, key_exponent=signing_attributes.exponent) root_cert, root_key_pair = crypto_functions.create_root_certficate( root_certificate_params, generated_root_key_pair, 7300, openssl_configfile, 1) else: logger.info( 'Using a predefined Root certificate and a predefined key') logger.info('Key Used: ' + root_certificate_params['private_key_path']) logger.info('Certificate Used: ' + root_certificate_params['certificate_path']) root_cert, root_key_pair = self._get_certificate_and_key_pair_from_files( root_certificate_params) else: logger.error( "Root certificate params are invalid! Please check config file." ) raise RuntimeError( "Root certificate params are invalid! Please check config file." ) if num_certs_in_certchain > 2: logger.debug( "Generating Attestation CA certificate, since certchain size is greater than 2" ) attestation_ca_certificate_params = crypto_params_dict[ 'attest_ca_certificate_properties'] attestation_ca_params_is_valid, generate_new_attestation_ca = self._validate_certificate_params_dict( attestation_ca_certificate_params) if attestation_ca_params_is_valid: if generate_new_attestation_ca: logger.info( 'Generating new Attestation CA certificate and a random key' ) generated_attestation_ca__key_pair = crypto_functions.gen_rsa_key_pair( general_properties.key_size, key_exponent=signing_attributes.exponent) attestation_ca_certificate, attestation_ca_certificate_key_pair = \ crypto_functions.create_certificate(attestation_ca_certificate_params, generated_attestation_ca__key_pair, root_cert, root_key_pair, days=7300, configfile=openssl_configfile, serial_num=1, extfile_name=openssl_ca_cert_xts) else: logger.info( 'Using a predefined Attestation CA certificate and a predefined key' ) logger.info( 'Key Used: ' + attestation_ca_certificate_params['private_key_path']) logger.info( 'Certificate Used: ' + attestation_ca_certificate_params['certificate_path']) attestation_ca_certificate, attestation_ca_certificate_key_pair = self._get_certificate_and_key_pair_from_files( attestation_ca_certificate_params) else: logger.error( "Attestation CA certificate params are invalid! Please check config file." ) raise RuntimeError( "Attestation CA certificate params are invalid! Please check config file." ) attestation_certificate_params = crypto_params_dict[ 'attest_certificate_properties'] attestation_certificate_params_is_valid, generate_new_attestation_certificate = self._validate_certificate_params_dict( attestation_certificate_params) if attestation_certificate_params_is_valid: if generate_new_attestation_certificate: # TCG support if self._is_oid_supported(signing_attributes) is True: if self.validate_oid_from_config( attestation_ca_certificate_params[ 'certificate_path'], signing_attributes) is False: raise ConfigError("{0} min and max are not set correctly in configuration."\ "Signing will not continue.".format(signing_attributes.object_id.name) ) attestation_certificate_extensions_path = self._generate_attestation_certificate_extensions( openssl_attest_ca_xts, signing_attributes.object_id.name, signing_attributes.object_id.min, signing_attributes.object_id.max) else: attestation_certificate_extensions_path = openssl_attest_ca_xts # Get the binary to sign length if binary_to_sign_len is None: if binary_to_sign is not None: binary_to_sign_len = len(binary_to_sign) else: raise RuntimeError( 'Length of binary could not be computed') logger.info( 'Generating new Attestation certificate and a random key') hmac_params = signerutils.get_hmac_params_from_config( signing_attributes) certificate_ou_sw_id = "01 " + hmac_params.sw_id_str + " SW_ID" certificate_ou_hw_id = "02 " + hmac_params.msm_id_str + " HW_ID" certificate_ou_oem_id = "04 " + "%0.4X" % oem_id + " OEM_ID" certificate_ou_sw_size = "05 " + "%0.8X" % binary_to_sign_len + " SW_SIZE" certificate_ou_model_id = "06 " + "%0.4X" % model_id + " MODEL_ID" certificate_hash_alg = SHA1_OU_STRING if sha_algo == 'sha1' else SHA256_OU_STRING certificate_ou = [ certificate_ou_sw_id, certificate_ou_hw_id, certificate_ou_oem_id, certificate_ou_sw_size, certificate_ou_model_id, certificate_hash_alg ] # Optional attributes if debug_val is not None: certificate_ou_debug_id = "03 " + "%0.16X" % debug_val + " DEBUG" certificate_ou.append(certificate_ou_debug_id) if app_id is not None: certificate_app_id = "08 " + "%0.16X" % app_id + " APP_ID" certificate_ou.append(certificate_app_id) if crash_dump is not None: certificate_crash_dump = "09 " + "%0.16X" % crash_dump + " CRASH_DUMP" certificate_ou.append(certificate_crash_dump) if rot_en is not None: certificate_rot_en = "10 " + "%0.16X" % rot_en + " ROT_EN" certificate_ou.append(certificate_rot_en) if mask_soc_hw_version is not None: certificate_mask_soc_hw_version = "12 " + "%0.4X" % mask_soc_hw_version + " MASK_SOC_HW_VERSION" certificate_ou.append(certificate_mask_soc_hw_version) if in_use_soc_hw_version == 1: certificate_in_use_soc_hw_version = "13 " + "%0.4X" % in_use_soc_hw_version + " IN_USE_SOC_HW_VERSION" certificate_ou.append(certificate_in_use_soc_hw_version) if use_serial_number_in_signing is not None: certificate_use_serial_number_in_signing = "14 " + "%0.4X" % use_serial_number_in_signing + " USE_SERIAL_NUMBER_IN_SIGNING" certificate_ou.append( certificate_use_serial_number_in_signing) if 'OU' in attestation_certificate_params.keys(): if type(attestation_certificate_params['OU']) == list: for item in attestation_certificate_params['OU']: certificate_ou.append(item) else: certificate_ou.append( attestation_certificate_params['OU']) attestation_certificate_params['OU'] = certificate_ou if attestation_certificate_key_pair is None: attestation_certificate_key_pair = crypto_functions.gen_rsa_key_pair( key_exponent=signing_attributes.exponent, key_size_in_bits=general_properties.key_size) if num_certs_in_certchain > 2: # sign the attestation cert with the attestation_ca_cert attestation_certificate, attestation_certificate_key_pair = \ crypto_functions.create_certificate(attestation_certificate_params, attestation_certificate_key_pair, attestation_ca_certificate, attestation_ca_certificate_key_pair, days=7300, configfile=openssl_configfile, serial_num=1, extfile_name=attestation_certificate_extensions_path) else: # sign the attestation cert with the root cert attestation_certificate, attestation_certificate_key_pair = \ crypto_functions.create_certificate(attestation_certificate_params, attestation_certificate_key_pair, root_cert, root_key_pair, days=7300, configfile=openssl_configfile, serial_num=1, extfile_name=attestation_certificate_extensions_path) attestation_certificate = crypto_functions.cert_pem_to_der( attestation_certificate) # Clean temp file if self._is_oid_supported(signing_attributes) is True: c_path.clean_file(attestation_certificate_extensions_path) else: # generate_new_attestation_certificate == False logger.info( 'Using a predefined Attestation certificate and a predefined key' ) logger.info('Key Used: ' + attestation_certificate_params['private_key_path']) logger.info('Certificate Used: ' + attestation_certificate_params['certificate_path']) attestation_certificate, attestation_certificate_key_pair = self._get_certificate_and_key_pair_from_files( attestation_certificate_params) attestation_certificate = crypto_functions.cert_pem_to_der( attestation_certificate) # Since the get_hmac_params_from_certificate_chain always works with the first cert in the cert chain, # this function will work for a single der certificate as well. hmac_params = crypto_functions.get_hmacparams_from_certificate_chain( attestation_certificate) hasher = Hasher() hash_to_sign = hasher.qcom_hmac(binary_to_sign, hmac_params, sha_algo) signature = crypto_functions.encrypt_with_private_key( hash_to_sign, attestation_certificate_key_pair['private_key']) else: logger.error( "Attestation certificate params are invalid! Please check config file." ) raise RuntimeError( "Attestation certificate params are invalid! Please check config file." ) if num_certs_in_certchain > 2: attestation_ca_certificate = crypto_functions.cert_pem_to_der( attestation_ca_certificate) else: attestation_ca_certificate = None root_cert = crypto_functions.cert_pem_to_der(root_cert) root_cert_list = self.cert_data_object.get_rootcerts( general_properties.num_root_certs) certificate_list = self._get_certificate_list( general_properties.num_root_certs, num_certs_in_certchain, attestation_certificate, attestation_ca_certificate, root_cert, root_cert_list) cert_chain = crypto_functions.create_certificate_chain( certificate_list) signer_output = SignerOutput() signer_output.root_cert = root_cert signer_output.attestation_ca_cert = attestation_ca_certificate signer_output.attestation_cert = attestation_certificate signer_output.signature = signature signer_output.cert_chain = cert_chain signer_output.root_cert_list = root_cert_list signer_output.root_key = root_key_pair['private_key'] # Make sure the variable is defined try: attestation_ca_certificate_key_pair except Exception: pass else: signer_output.attestation_ca_key = attestation_ca_certificate_key_pair[ 'private_key'] signer_output.attestation_key = attestation_certificate_key_pair[ 'private_key'] return signer_output