def __init__(self, config_dir=defines.DEFAULT_CONFIG_DIR, debug=False): # Initialize the internal properties self._config_dir_obj = ConfigDir(config_dir) self._img_config_parser = None self._parsegen_config = None self._stager = None # Generate Parsegen_config from parsegen_config.xml from sectools.features.isc.parsegen.config.parser import ParsegenCfgParser self._parsegen_config = ParsegenCfgParser(PARSEGEN_PATH) # Initialize the public properties self.debug = debug
def __init__(self, config_dir, parsegen_config, cfgparser=None, debug=False): # Initialize the internal properties self._config_dir_obj = ConfigDir(config_dir, cfgparser=cfgparser) self._img_config_parser = None self._stager = None self._authority = AUTHORITY_OEM self._parsegen_config = parsegen_config # ConfigParser named tuple self.cfgparser = cfgparser # Initialize the public properties self.debug = debug
class SecImageCore(object): """Provides interface for user to set up parameters (including input files, output directory, etc) and perform various operations (signing, encryption). User can select the config file using: - :meth:`chipset` - :meth:`config_path` User can specify images for processing using: - :meth:`set_image_path` - :meth:`set_meta_build_path` User can specify output using: - :meth:`output_dir` - :meth:`mini_build_path` User can finally perform the following operations: - :meth:`verify_inputs` - :meth:`sign` - :meth:`encrypt` - :meth:`validate` User can also perform multiple operations using :meth:`process` :param str config_dir: Path to the directory containing the config files for the supported chipsets. :param bool debug: If all the underlying modules should work in debug mode (save debug logs, etc). .. data:: debug (bool) Is debug mode enabled. The debug logs will be stored in :meth:`output_dir` """ def __init__(self, config_dir=defines.DEFAULT_CONFIG_DIR, debug=False): # Initialize the internal properties self._config_dir_obj = ConfigDir(config_dir) self._img_config_parser = None self._parsegen_config = None self._stager = None self._authority = defines.AUTHORITY_OEM # Generate Parsegen_config from parsegen_config.xml from sectools.features.isc.parsegen.config.parser import ParsegenCfgParser self._parsegen_config = ParsegenCfgParser(PARSEGEN_PATH) # Initialize the public properties self.debug = debug @property def config_dir(self): """(str) Path to the config directory provided in params. The config_dir path cannot be set directly. """ return self._config_dir_obj.config_dir @config_dir.setter def config_dir(self, config_dir): raise RuntimeError('Config dir cannot be set.') @property def available_chipsets(self): """(list[str]) A list of chipsets for which config files are available in the :meth:`config_dir`. The list of available chipsets cannot be set directly. """ return self._config_dir_obj.chipsets @available_chipsets.setter def available_chipsets(self, chipset_list): raise RuntimeError('Available chipsets cannot be set.') @property def chipset(self): """(str) Chipset that is selected for the current run. The chipset selection will update the :meth:`config_path` """ if self._img_config_parser is not None: return self._img_config_parser.chipset else: raise RuntimeError('Chipset Path is not set.') @chipset.setter def chipset(self, chipset): self.set_chipset(chipset) def set_chipset(self, chipset, overrides=None, spec_overrides=None): # Log if the chipset is changed try: selected_chipset = self.chipset if selected_chipset: logger.note('Switching chipset from "' + selected_chipset + '" to "' + chipset + '"') except Exception: pass # Update the config path based on the given chipset config_path = self._config_dir_obj.get_chipset_config_path(chipset) if config_path is None: raise RuntimeError('Config file is not found for chipset: ' + str(chipset)) self.set_config_path(config_path, overrides, spec_overrides) logger.info('Chipset is set to: ' + chipset) @property def config_path(self): """(str) Config path that is selected for the current run. The config_path selection will update the :meth:`chipset` """ if self._img_config_parser is not None: return self._img_config_parser.config_path else: raise RuntimeError('Config Path is not set.') @config_path.setter def config_path(self, config_path): self.set_config_path(config_path) def set_config_path(self, config_path, overrides=None, spec_overrides=None): # Log if the config_path is changed try: selected_config_path = self.config_path if selected_config_path: logger.note('Switching config_path: ' + '\n' ' ' + 'from: ' + selected_config_path + '\n' ' ' + ' to: ' + config_path) except Exception: pass # Update the config_path self._img_config_parser = ConfigParser(config_path, overrides, spec_overrides) logger.info('Config path is set to: ' + config_path) # Reset the stager at this point if self._stager is not None: logger.note('Resetting image list due to config path change.') self._stager = None @property def authority(self): """(str) Config path that is selected for the current run. The config_path selection will update the :meth:`chipset` """ return self._authority @authority.setter def authority(self, authority): if authority not in defines.AUTHORITIES_SUPPORTED: raise RuntimeError('Unknown authority for secure operations: ' + str(authority)) self._authority = authority @property def available_sign_ids(self): """(list[str]) A list of sign_ids available from the selected :meth:`config_path`/:meth:`chipset`. """ if self._img_config_parser is not None: return self._img_config_parser.sign_id_list else: raise RuntimeError( 'Set chipset or config Path before querying the available sign ids.' ) @available_sign_ids.setter def available_sign_ids(self, chipset_list): raise RuntimeError('Available sign ids cannot be set.') def set_image_path(self, image_path, sign_id): """Sets the image that will be processed. :param str image_path: Path to the image to be processed :param str sign_id: sign_id corresponding to the image_path. This must be one of the :meth:`available_sign_ids` """ if self._img_config_parser is not None and self._parsegen_config is not None: self._stager = stager.ImagePathsStager(image_path, self._img_config_parser, self._parsegen_config, self.authority, sign_id) else: raise RuntimeError( 'Set chipset or config Path before setting the image path.') def set_meta_build_path(self, meta_build_path, sign_id_list=[]): """Sets the meta build path from which the images to be processed will be queried. :param str meta_build_path: Path to the meta build. """ if self.meta_supports_sign_id(meta_build_path): self._stager = stager.MetaBuildStager(meta_build_path, self._config_dir_obj, self._parsegen_config, self.authority, sign_id_list) else: self._stager = stager.MetaBuildTmpStager( meta_build_path, self._config_dir_obj, self._img_config_parser, self._parsegen_config, self.authority, sign_id_list) @property def image_path_list(self): """(list[str]) List of source image paths that are being operated on. This is updated once one of :meth:`set_image_path`, :meth:`set_meta_build_path` is used to specify the images to be processed. """ if self._stager is not None: return self._stager.image_path_list else: raise RuntimeError('Image list is not set. ' 'Set one of image_path or meta_build_path.') @image_path_list.setter def image_path_list(self, image_path_list): raise RuntimeError( 'Image path list cannot be set directly. ' 'Use set_image_path or set_meta_build to set the image paths.') @property def image_info_list(self): """(list[ImageInfo]) A list of :class:`imageinfo.ImageInfo` objects that are being operated on. """ if self._stager is not None: return self._stager.image_info_list else: raise RuntimeError('Image Info list is not set. ' 'Set one of image_path or meta_build_path.') @image_info_list.setter def image_info_list(self, image_info_list): raise RuntimeError('Image Info list cannot be set directly.') @property def output_dir(self): """(str) Path to the output directory to use for saving the logs, output files. """ if self._stager is not None: return self._stager.output_dir else: raise RuntimeError('Image list is not set. ' 'Set one of image_path or meta_build_path. ' 'Then retry setting output directory.') @output_dir.setter def output_dir(self, output_dir): # Log if the output_dir is changed try: selected_output_dir = self.output_dir if selected_output_dir: logger.note('Switching output_dir: ' + '\n' ' ' + 'from: ' + selected_output_dir + '\n' ' ' + ' to: ' + output_dir) except Exception: pass # Update the output_dir self._stager.output_dir = output_dir logger.info('Output dir is set to: ' + output_dir) # Clear the minimized build setting try: self._stager.mini_build_path = '' except Exception: pass @property def mini_build_path(self): """(str) Path to the minimized build directory to use for saving the logs, output files. """ if self._stager is not None: return self._stager.mini_build_path else: raise RuntimeError('Image list is not set. ' 'Set one of image_path or meta_build_path. ' 'Then retry querying/setting minimized build.') @mini_build_path.setter def mini_build_path(self, mini_build_path): # Log if the mini_build_path is changed selected_mini_build_path = self.mini_build_path if selected_mini_build_path: logger.note('Switching mini_build_path: ' + '\n' ' ' + 'from: ' + selected_mini_build_path + '\n' ' ' + ' to: ' + mini_build_path) # Update the mini_build_path self._stager.mini_build_path = mini_build_path logger.info('Minimized Build is set to: ' + mini_build_path) # Clear the output dir setting try: self._stager.output_dir = '' except Exception: pass @classmethod def meta_supports_sign_id(self, meta_build_path): return stager.meta_build_stager.MetaBuildStager.meta_supports_sign_id( meta_build_path) # Helper method to override prefix of debug dumped files for images with multiple security policies @staticmethod def _get_prefix_override(existing_prefix, file_type): if existing_prefix is None: return file_type + "_wrapped" else: return existing_prefix + file_type + "_wrapped" def process( self, verify_setup=False, sign_attr=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 sign_attr: Add signing attributes to hash segment of images. :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 version = 'v' + '.'.join( tuple(str(x) for x in crypto.openssl.OPENSSL_VERSION_MIN)) + ' or greater ' path_info = 'is available at: "' + crypto.discovery_factory.get_impl( crypto.modules.MOD_OPENSSL) + '"' 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 individual_image_count = len(self.image_info_list) total_image_count = individual_image_count progress = ProgressNotifier(total_image_count, progress_cb, PROGRESS_TOTAL_STAGES) def _process(idx, image, sign_attr, integrity_check, sign, encrypt, decrypt, val_image, val_integrity_check, val_sign, val_encrypt): assert isinstance(image, ImageInfo) logger.info( '------------------------------------------------------') status_string = ('Processing ' + str(idx + 1) + '/' + str(total_image_count) + ': ' + 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 image.authority = self.authority 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, sign_attr, integrity_check, sign, encrypt, decrypt) # For validation if val_image or val_integrity_check or val_sign or val_encrypt: # Bypass validation when encrypted key provide is configured as UIE server because decryption will always fail if parsegen is not None and parsegen.is_encrypted( ) and encrypted_key_provider_id_supported( image.general_properties.UIE_key): logger.warning( "Skipping validation because encrypted key provider is configured as UIE server" ) else: 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]) finally: if file_logger_id is not None: logger.removeFileLogger(file_logger_id) logger.info( '------------------------------------------------------\n') for idx, image in enumerate(self.image_info_list): _process(idx, image, sign_attr, integrity_check, sign, encrypt, decrypt, val_image, val_integrity_check, val_sign, val_encrypt) progress.complete() def _process_secure_operation(self, image, progress, security_policy_list, sign_attr, i_integrity_check, i_sign, i_encrypt, i_decrypt, idx=0, prefix_override=None): from imageinfo import ImageInfo, StatusInfo # Check bounds if len(security_policy_list) == 0: raise RuntimeError('Security policy list must not be empty.') elif len(security_policy_list) <= idx: raise RuntimeError( 'Security policy list length must be more than index.') # Get the current security policy file_type = security_policy_list[idx].file_type integrity_check = security_policy_list[ idx].integrity_check and i_integrity_check sign = security_policy_list[idx].sign and i_sign encrypt = security_policy_list[idx].encrypt and i_encrypt decrypt = security_policy_list[idx].encrypt and i_decrypt # Create the encryptor object encdec = None if image.general_properties.selected_encryptor: c_path.create_debug_dir(image.dest_image.debug_dir_encdec) encdec = get_encdec(image, False) # Create the parsegen object file_type_backup = image.image_type.file_type encdec_backup = image.encdec image.image_type.file_type = file_type image.encdec = encdec try: parsegen = self._status_updater(self._create_parsegen_obj, image.status.parsegen, progress, True, image, False, sign, prefix_override, sign_attr) finally: image.image_type.file_type = file_type_backup image.encdec = encdec_backup # Validate the authority settings self.validate_authority_settings( self.authority, image.general_properties.secboot_version, image.general_properties.qti_sign, image.general_properties.oem_sign, image.general_properties.num_root_certs, encrypt) # Do not allow signed unencrypted elf images to be encrypted as the # Sign will not longer match the encrypted image's tosign if parsegen.file_type() == 'elf' and parsegen.is_signed( ) and not sign and not parsegen.is_encrypted() and encrypt: raise RuntimeError( 'Cannot encrypt a signed unencrypted image without resigning ' 'as the sign no longer matches the format change.') # Set the security mechanisms parsegen.integrity_check = security_policy_list[ idx].integrity_check and (parsegen.contains_integrity_check() or integrity_check) parsegen.sign = security_policy_list[idx].sign and ( parsegen.is_signed() or sign) parsegen.encrypt = security_policy_list[idx].encrypt and ( False if decrypt else (parsegen.is_encrypted() or encrypt)) # Get blob if populating encryption parameters: if encrypt: if encdec is None: raise RuntimeError('Encryptor is not set') parsegen.encryption_params = encdec.get_encryption_parameters_blob( ) # check if input image is a TA, if yes re-initialize encdec object with updated bitmap for manifest segment if is_TA(image.signing_attributes.sw_id): non_encrypt_segment_found, encrypted_segments_indices = self._get_encrypted_segments_index_list( parsegen._elf_parsegen.phdrs) if non_encrypt_segment_found and len( encrypted_segments_indices) > 0: logger.info( "Re-initializing encdec object for updated segment bitmap" ) encdec = get_encdec(image, False, encrypted_segments_indices) parsegen.encryption_params = encdec.get_encryption_parameters_blob( ) parsegen.encdec = encdec elif parsegen.encryption_params and parsegen.encdec is not None: parsegen.encdec.update_encryption_parameters( parsegen.encryption_params) # Dump any debug data self.dump_parsegen_debug_data(image, parsegen) # If the security policy list contains more formats, call them if idx < len(security_policy_list) - 1: data = parsegen.get_wrapped_data() import tempfile tmp_fd = tempfile.NamedTemporaryFile(delete=False) tmp_fd.close() store_data_to_file(tmp_fd.name, data) # Backup the source path here src_image_dir_base = image.src_image.image_dir_base src_image_dir_ext = image.src_image.image_dir_ext src_image_name = image.src_image.image_name image.src_image.image_dir_base = os.path.dirname(tmp_fd.name) image.src_image.image_dir_ext = '' image.src_image.image_name = os.path.basename(tmp_fd.name) # Override debug dumped file prefix prefix_override = SecImageCore._get_prefix_override( prefix_override, file_type) try: data = self._process_secure_operation( image, progress, security_policy_list, sign_attr, i_integrity_check, i_sign, i_encrypt, i_decrypt, idx + 1, prefix_override) finally: image.src_image.image_dir_base = src_image_dir_base image.src_image.image_dir_ext = src_image_dir_ext image.src_image.image_name = src_image_name os.remove(tmp_fd.name) parsegen.set_wrapped_data(data) # Sign the image if sign: self._status_updater(self._sign_image, image.status.sign, progress, True, image, parsegen) # Package and generate the output image file data = parsegen.get_data() if integrity_check: image.status.integrity_check.state = StatusInfo.SUCCESS if encrypt: image.status.encrypt.state = StatusInfo.SUCCESS if idx != 0: return data if decrypt: encryption_params_backup = parsegen.encryption_params parsegen.encryption_params = '' try: store_data_to_file(image.dest_image.decrypted_file, parsegen.get_data()) finally: parsegen.encryption_params = encryption_params_backup else: store_data_to_file(image.dest_image.image_path, data) logger.info(('Signed ' if sign else '') + ('& ' if sign and encrypt else '') + ('Encrypted ' if encrypt else '') + 'image is stored at ' + image.dest_image.image_path) image.image_under_operation = image.dest_image.image_path # Do any post processing self._status_updater(self._post_process, image.status.postprocess, progress, True, image, image.config.post_process.pil_splitter, getattr(self._stager, '_meta_build_path', None)) return parsegen def _process_validation(self, image, progress, security_policy_list, i_val_image, i_val_integrity_check, i_val_sign, i_val_encrypt, idx=0, prefix_override=None): # TODO: Need to figure how to do this #image.dest_image._mid = 'validation' # Check bounds if len(security_policy_list) == 0: raise RuntimeError('Security policy list must not be empty.') elif len(security_policy_list) <= idx: raise RuntimeError( 'Security policy list length must be more than index.') # Get the current security policy file_type = security_policy_list[idx].file_type val_image = i_val_image val_integrity_check = security_policy_list[ idx].integrity_check and i_val_integrity_check val_sign = security_policy_list[idx].sign and i_val_sign val_encrypt = security_policy_list[idx].encrypt and i_val_encrypt # Backup the source path here src_image_dir_base = image.src_image.image_dir_base src_image_dir_ext = image.src_image.image_dir_ext src_image_name = image.src_image.image_name # Update the souce image path image.src_image.image_dir_base = os.path.dirname( image.image_under_operation) image.src_image.image_dir_ext = '' image.src_image.image_name = os.path.basename( image.image_under_operation) try: # Create the encryptor object encdec = None if image.general_properties.selected_encryptor: c_path.create_debug_dir(image.dest_image.debug_dir_encdec) encdec = get_encdec(image, True) # Create the parsegen object file_type_backup = image.image_type.file_type encdec_backup = image.encdec image.image_type.file_type = file_type image.encdec = encdec try: parsegen = self._status_updater(self._create_parsegen_obj, image.status.validate_parsegen, progress, True, image, True, False, prefix_override) finally: image.image_type.file_type = file_type_backup image.encdec = encdec_backup # Validate the authority settings self.validate_authority_settings( self.authority, image.general_properties.secboot_version, image.general_properties.qti_sign, image.general_properties.oem_sign, image.general_properties.num_root_certs, parsegen.is_encrypted()) # Prevent validation when using an encrypted key provider from sectools.features.isc.encryption_service.unified import encrypted_key_provider_id_supported encrypted_key_provider_id = image.signing_attributes.UIE_key if parsegen.is_encrypted() and encrypted_key_provider_id_supported( encrypted_key_provider_id): raise RuntimeError( "The image is encrypted. Validation is not supported when using an encrypted key provider.\n" "Try again with validation disabled.") # Set the security mechanisms parsegen.integrity_check = parsegen.contains_integrity_check() parsegen.sign = parsegen.is_signed() parsegen.encrypt = parsegen.is_encrypted() # Validate parsegen if val_image: self._status_updater(self._validate_parsegen, image.status.validate_parsegen, progress, False, image, parsegen) # Validate integrity check if val_integrity_check: self._status_updater(self._validate_integrity_check, image.status.validate_integrity_check, progress, False, image, parsegen) # Validate sign if val_sign: self._status_updater(self._validate_sign, image.status.validate_sign, progress, False, image, parsegen) # Validate encrypt if val_encrypt: self._status_updater(self._validate_encrypt, image.status.validate_encrypt, progress, False, image, parsegen) # Dump any debug data self.dump_parsegen_debug_data(image, parsegen) # If the security policy list contains more formats, call them if idx < len(security_policy_list) - 1: data = parsegen.get_wrapped_data() import tempfile tmp_fd = tempfile.NamedTemporaryFile( prefix=security_policy_list[idx + 1].file_type, delete=False) tmp_fd.close() store_data_to_file(tmp_fd.name, data) # Backup the image_under_operation here image_under_operation_int = image.image_under_operation image.image_under_operation = tmp_fd.name # Override debug dumped file prefix prefix_override = SecImageCore._get_prefix_override( prefix_override, file_type) try: self._process_validation(image, progress, security_policy_list, i_val_image, i_val_integrity_check, i_val_sign, i_val_encrypt, idx + 1, prefix_override) finally: image.image_under_operation = image_under_operation_int os.remove(tmp_fd.name) finally: image.src_image.image_dir_base = src_image_dir_base image.src_image.image_dir_ext = src_image_dir_ext image.src_image.image_name = src_image_name return parsegen def _get_encrypted_segments_index_list(self, phdrs): encrypted_segments_indices = [] found_not_encrypt_segments = False for indx, phdr in enumerate(phdrs): if phdr.p_type == PT_LOAD and phdr.f_os_segment_type not in NON_ENCRYPT_SEGMENT_OS_TYPE: encrypted_segments_indices.append(indx + 2) elif phdr.f_os_segment_type in NON_ENCRYPT_SEGMENT_OS_TYPE: found_not_encrypt_segments = True logger.debug("LOAD segment " + str(indx + 2) + " won't be encrypted") return (found_not_encrypt_segments, encrypted_segments_indices) def _create_parsegen_obj(self, image, validating=False, signing=False, prefix_override=None, sign_attr=False): c_path.create_debug_dir(image.dest_image.debug_dir_parsegen) from sectools.features.isc.parsegen import get_parser parsegen = get_parser(image, validating, signing, prefix_override, sign_attr) # Check if parsegen authority settings are correct parsegen.authority = self.authority parsegen.validate_authority() return parsegen @staticmethod def validate_authority_settings(authority, secboot_version, qti_sign, oem_sign, num_root_certs, encrypt=False): if secboot_version != defines.SECBOOT_VERSION_1_0: if encrypt and authority != defines.highest_authority( [oem_sign, qti_sign]): # Reserve encryption for the highest active authority # raise RuntimeError("Encryption is not allowed for: " + str(authority)) if num_root_certs != 1 and authority == defines.AUTHORITY_QTI: raise RuntimeError("num_root_certs must be set to \"1\" for: " + str(authority)) def _sign_image(self, image, parsegen): from sectools.features.isc.signer import get_signer # Check all other authorities and display if they have signed for authority in [ x for x in defines.AUTHORITIES_SUPPORTED if x != self.authority ]: if parsegen.is_signed(authority): logger.info(image.image_under_operation + ' is already ' + str(authority) + '-signed.') # Prevent resigning by authorities that previously signed that aren't the most recently signed authority previously_signed_authorities = [] for authority in defines.AUTHORITIES_SUPPORTED: if parsegen.is_signed(authority): previously_signed_authorities.append(authority) last_signed_authority = defines.highest_authority([ defines.AUTHORITY_OEM in previously_signed_authorities, defines.AUTHORITY_QTI in previously_signed_authorities ]) if self.authority != last_signed_authority and self.authority in previously_signed_authorities: raise RuntimeError( "Cannot resign as {0} because image is already signed by {1}". format(self.authority, last_signed_authority)) # Display sign status of signing authority logger.info( image.image_under_operation + ' is already ' + self.authority + '-signed. ' 'Rerunning ' + self.authority + ' sign.' if parsegen.is_signed(self.authority) else 'Performing ' + self.authority + ' sign on image: ' + image.image_under_operation) signer = get_signer(image.config) sign_assets = signer.sign(parsegen.data_to_sign, image, image.dest_image.debug_dir_signer, parsegen.is_data_hash, parsegen.get_hash_segment_metadata()) parsegen.data_signature = sign_assets.signature parsegen.cert_chain = sign_assets.cert_chain # Dump any debug information self.dump_signer_debug_data(image, sign_assets, parsegen) @staticmethod def pil_split(src, prefix): from sectools.common.parsegen.elf.format import ParseGenElf, pack_phdrs, PF_OS_SEGMENT_HASH # Load the data p_obj = ParseGenElf(load_data_from_file(src)) # File names elfhdr = prefix + '.mdt' # Cleanup for eachpattern in [elfhdr, (prefix + '.b*')]: import glob for eachfile in glob.glob(eachpattern): try: os.remove(eachfile) except Exception: pass # Create the mdt data elfdata = p_obj.ehdr.pack() + pack_phdrs(p_obj.phdrs) # Dump the segments for idx, phdr in enumerate(p_obj.phdrs): path = prefix + ('.b%02d' % idx) store_data_to_file(path, p_obj.segments[phdr]) if phdr.f_os_segment_type == PF_OS_SEGMENT_HASH: elfdata += p_obj.segments[phdr] # Dump the final elfhdr mdt store_data_to_file(elfhdr, elfdata) def _post_process(self, image, pil_splitter_path, meta_build_path): ''' Replacement tags in postsign commands for images. ''' TAG_OUTPUT_DIR = '$(OUTPUT_DIR)' TAG_PIL_SPLITTER = '$(PIL_SPLITTER)' TAG_IMAGE_FILE = '$(IMAGE_FILE)' REPL_META_PATH = '$(META_BUILD)' if image.pil_split: image_file = image.image_under_operation SecImageCore.pil_split(image_file, os.path.splitext(image_file)[0]) if image.post_process_commands: # Ensure pil splitter is available if pil_splitter_path.find(REPL_META_PATH) != -1: if not meta_build_path: raise RuntimeError( 'Metabuild path is not available for pil splitter') pil_splitter_path = pil_splitter_path.replace( REPL_META_PATH, meta_build_path) if not c_path.validate_file(pil_splitter_path): raise RuntimeError('Cannot access pil splitter at: ' + pil_splitter_path) # Run all the commands for cmd in [ c.strip() for c in image.post_process_commands.split() ]: # Perform any needed replacements cmd = cmd.replace(TAG_OUTPUT_DIR, os.path.dirname(image.image_under_operation)) cmd = cmd.replace(TAG_PIL_SPLITTER, pil_splitter_path) cmd = cmd.replace(TAG_IMAGE_FILE, image.image_under_operation) logger.info('Running postsign command: ' + cmd) err = os.system(cmd) logger.info('Result: ' + str(err)) def _validate_parsegen(self, image, parsegen): pass def _validate_integrity_check(self, image, parsegen): pass def _validate_sign(self, image, parsegen): from sectools.features.isc.signer import get_signer if parsegen.is_signed(): signer = get_signer(image.config) if signer.validate(parsegen, image.validation_root_cert_hash, image): logger.info('Image ' + image.image_under_operation + ' signature is valid') else: raise RuntimeError('Image ' + image.image_under_operation + ' signature is not valid') else: raise CustomError('Image ' + image.image_under_operation + ' is not signed') def _validate_encrypt(self, image, parsegen): if parsegen.is_encrypted(): logger.info('Image ' + image.image_under_operation + ' is encrypted') else: raise CustomError('Image ' + image.image_under_operation + ' is not encrypted') def _status_updater(self, method, status, progress, raise_err, *args, **kwargs): from imageinfo import ImageInfo, StatusInfo try: retval = method(*args, **kwargs) status.state = StatusInfo.SUCCESS progress.push() return retval except Exception as e: status.state = StatusInfo.ERROR status.error = str(e) progress.push() if raise_err: raise else: if isinstance(e, CustomError): logger.info(status.error) else: logger.error(status.error) def dump_parsegen_debug_data(self, image, parsegen): if image.dest_image.debug_dir_parsegen is None: return so = parsegen fp = image.dest_image from imageinfo import DestImagePath assert isinstance(fp, DestImagePath) debug_logs = [] try: debug_logs.append( (so.get_data(False, False, False), fp.debug_file_parsegen_unsigned)) except Exception: pass try: debug_logs.append((so.data_to_sign, fp.debug_file_parsegen_tosign)) except Exception: pass try: debug_logs.append( (so.cert_chain, fp.debug_file_parsegen_cert_chain)) except Exception: pass try: debug_logs.append( (so.data_signature, fp.debug_file_parsegen_signature)) except Exception: pass try: debug_logs.append( (so.get_data(True, False, False), fp.debug_file_parsegen_integrity_check)) except Exception: pass try: debug_logs.append( (so.get_data(True, True, False), fp.debug_file_parsegen_signed)) except Exception: pass try: debug_logs.append( (so.get_data(True, True, True), fp.debug_file_parsegen_encrypted)) except Exception: pass for data, debug_file in debug_logs: try: store_data_to_file(debug_file, data) except Exception: logger.debug2('Failed to save debug file ' + debug_file + '\n' ' ' + str(sys.exc_info()[1])) def dump_signer_debug_data(self, image, sign_assets, parsegen): if image.dest_image.debug_dir_signer is None: return c_path.create_debug_dir(image.dest_image.debug_dir_signer) sa = sign_assets fp = image.dest_image from imageinfo import DestImagePath assert isinstance(fp, DestImagePath) # Backup parsegen authority authority = parsegen.authority # QTI Signature and Cert Chain parsegen.authority = defines.AUTHORITY_QTI data_signature_qti = parsegen.data_signature cert_chain_qti = parsegen.cert_chain # OEM Signature and Cert Chain parsegen.authority = defines.AUTHORITY_OEM data_signature = parsegen.data_signature cert_chain = parsegen.cert_chain # Restore authority parsegen.authority = authority debug_logs = [ (sa.root_cert, fp.debug_file_signer_root_cert), (sa.attestation_ca_cert, fp.debug_file_signer_attestation_ca_cert), (sa.attestation_cert, fp.debug_file_signer_attestation_cert), (data_signature, fp.debug_file_signer_signature), (cert_chain, fp.debug_file_signer_cert_chain), (data_signature_qti, fp.debug_file_signer_qti_signature), (cert_chain_qti, fp.debug_file_signer_qti_cert_chain) ] # Save the private attributes too debug_logs += [ (sa.root_key, fp.debug_file_signer_root_key), (sa.attestation_ca_key, fp.debug_file_signer_attestation_ca_key), (sa.attestation_key, fp.debug_file_signer_attestation_key) ] for data, debug_file in debug_logs: try: store_data_to_file(debug_file, data) except Exception: logger.debug2('Failed to save debug file ' + debug_file + '\n' ' ' + str(sys.exc_info()[1]))
class SecImageCoreBase(object): """Provides interface for user to set up parameters (including input files, output directory, etc) and perform various operations, such as signing. User can select the config file using: - :meth:`chipset` - :meth:`config_path` User can specify images for processing using: - :meth:`set_image_path` User can specify output using: - :meth:`output_dir` User can finally perform the following operations: - :meth:`verify_inputs` - :meth:`sign` - :meth:`validate` User can also perform multiple operations using :meth:`process` :param str config_dir: Path to the directory containing the config files for the supported chipsets. :param bool debug: If all the underlying modules should work in debug mode (save debug logs, etc). .. data:: debug (bool) Is debug mode enabled. The debug logs will be stored in :meth:`output_dir` """ AUTHORITIES_SUPPORTED = [AUTHORITY_OEM] dest_image_path_class = None def __init__(self, config_dir, parsegen_config, cfgparser=None, debug=False): # Initialize the internal properties self._config_dir_obj = ConfigDir(config_dir, cfgparser=cfgparser) self._img_config_parser = None self._stager = None self._authority = AUTHORITY_OEM self._parsegen_config = parsegen_config # ConfigParser named tuple self.cfgparser = cfgparser # Initialize the public properties self.debug = debug self.enforce_signed = False # Debug Logs self.debug_files = [] def highest_authority(self, auth_enabled_list): auths = [k for i, k in enumerate(self.AUTHORITIES_SUPPORTED) if auth_enabled_list[i]] return auths[0] if auths else '' @property def config_dir(self): """(str) Path to the config directory provided in params. The config_dir path cannot be set directly. """ return self._config_dir_obj.config_dir @config_dir.setter def config_dir(self, config_dir): raise RuntimeError('Config dir cannot be set.') @property def available_chipsets(self): """(list[str]) A list of chipsets for which config files are available in the :meth:`config_dir`. The list of available chipsets cannot be set directly. """ return self._config_dir_obj.chipsets @available_chipsets.setter def available_chipsets(self, chipset_list): raise RuntimeError('Available chipsets cannot be set.') @property def available_sign_ids(self): """(list[str]) A list of sign_ids available from the selected :meth:`config_path`/:meth:`chipset`. """ if self._img_config_parser is not None: return self._img_config_parser.sign_id_list else: raise RuntimeError('Set chipset or config Path before querying the available sign ids.') @available_sign_ids.setter def available_sign_ids(self, chipset_list): raise RuntimeError('Available sign ids cannot be set.') @property def chipset(self): """(str) Chipset that is selected for the current run. The chipset selection will update the :meth:`config_path` """ if self._img_config_parser is not None: return self._img_config_parser.chipset else: raise RuntimeError('Chipset Path is not set.') @chipset.setter def chipset(self, chipset): self.set_chipset(chipset) def set_chipset(self, chipset, overrides=None): # Log if the chipset is changed try: selected_chipset = self.chipset if selected_chipset: logger.note('Switching chipset from "' + selected_chipset + '" to "' + chipset + '"') except Exception: pass # Update the config path based on the given chipset config_path = self._config_dir_obj.get_chipset_config_path(chipset) if config_path is None: raise RuntimeError('Config file is not found for chipset: ' + str(chipset)) self.set_config_path(config_path, overrides) logger.debug('Chipset is set to: ' + chipset) @property def config_path(self): """(str) Config path that is selected for the current run. The config_path selection will update the :meth:`chipset` """ if self._img_config_parser is not None: return self._img_config_parser.config_path else: raise RuntimeError('Config Path is not set.') @config_path.setter def config_path(self, config_path): self.set_config_path(config_path) def set_config_path(self, config_path, overrides=None): # Log if the config_path is changed try: selected_config_path = self.config_path if selected_config_path: logger.note('Switching config_path: ' + '\n' ' ' + 'from: ' + selected_config_path + '\n' ' ' + ' to: ' + config_path) except Exception: pass # Update the config_path self._img_config_parser = ConfigParser( config_path, cfgparser=self.cfgparser, overrides=overrides) logger.debug('Config path is set to: ' + config_path) # Reset the stager at this point if self._stager is not None: logger.note('Resetting image list due to config path change.') self._stager = None @property def authority(self): """(str) Config path that is selected for the current run. The config_path selection will update the :meth:`chipset` """ return self._authority @authority.setter def authority(self, authority): if authority not in self.AUTHORITIES_SUPPORTED: raise RuntimeError('Unknown authority for secure operations: ' + str(authority)) self._authority = authority @property def image_path_list(self): """(list[str]) List of source image paths that are being operated on. Use :meth:`set_image_path` to specify the images to be processed. """ if self._stager is not None: return self._stager.image_path_list else: raise RuntimeError('Image list is not set. Set one of image_path.') @image_path_list.setter def image_path_list(self, image_path_list): raise RuntimeError('Image path list cannot be set directly. ' 'Use set_image_path to set the image paths.') @property def image_info_list(self): """(list[ImageInfo]) A list of :class:`imageinfo.ImageInfo` objects that are being operated on. """ if self._stager is not None: return self._stager.image_info_list else: raise RuntimeError('Image Info list is not set. Set one of image_path.') @image_info_list.setter def image_info_list(self, image_info_list): raise RuntimeError('Image Info list cannot be set directly.') @property def output_dir(self): """(str) Path to the output directory to use for saving the logs, output files. """ if self._stager is not None: return self._stager.output_dir else: raise RuntimeError('Image list is not set. ' 'Set one of image_path or meta_build_path. ' 'Then retry setting output directory.') @output_dir.setter def output_dir(self, output_dir): # Log if the output_dir is changed try: selected_output_dir = self.output_dir if selected_output_dir: logger.note('Switching output_dir: ' + '\n' ' ' + 'from: ' + selected_output_dir + '\n' ' ' + ' to: ' + output_dir) except Exception: pass # Update the output_dir self._stager.output_dir = output_dir logger.debug('Output dir is set to: ' + output_dir) def dump_parsegen_debug_data(self, image, parsegen): if image.dest_image.debug_dir_parsegen is None: return so = parsegen fp = image.dest_image assert isinstance(fp, self.dest_image_path_class) try: self.debug_files.append((so.get_data(False, False, False), fp.debug_file_parsegen_unsigned)) except Exception: pass try: self.debug_files.append((so.data_to_sign, fp.debug_file_parsegen_tosign)) except Exception: pass try: self.debug_files.append((so.cert_chain, fp.debug_file_parsegen_cert_chain)) except Exception: pass try: self.debug_files.append((so.data_signature, fp.debug_file_parsegen_signature)) except Exception: pass try: self.debug_files.append((so.get_data(True, False, False), fp.debug_file_parsegen_integrity_check)) except Exception: pass try: self.debug_files.append((so.get_data(True, True, False), fp.debug_file_parsegen_signed)) except Exception: pass for data, debug_file in self.debug_files: try: store_data_to_file(debug_file, data) except Exception: logger.debug2('Failed to save debug file ' + debug_file + '\n' ' ' + str(sys.exc_info()[1])) def dump_signer_debug_data(self, image, sign_assets, parsegen, more_debug_logs=[]): if image.dest_image.debug_dir_signer is None: return c_path.create_debug_dir(image.dest_image.debug_dir_signer) sa = sign_assets fp = image.dest_image assert isinstance(fp, self.dest_image_path_class) # Backup parsegen authority authority = parsegen.authority # OEM Signature and Cert Chain parsegen.authority = AUTHORITY_OEM data_signature = parsegen.data_signature cert_chain = parsegen.cert_chain # Restore authority parsegen.authority = authority debug_logs = [(sa.root_cert, fp.debug_file_signer_root_cert), (sa.attestation_ca_cert, fp.debug_file_signer_attestation_ca_cert), (sa.attestation_cert, fp.debug_file_signer_attestation_cert), (data_signature, fp.debug_file_signer_signature), (cert_chain, fp.debug_file_signer_cert_chain)] for dbg_log in more_debug_logs: debug_logs.append((dbg_log[0], getattr(fp, dbg_log[1]))) # Save the private attributes too debug_logs += [(sa.root_key, fp.debug_file_signer_root_key), (sa.attestation_ca_key, fp.debug_file_signer_attestation_ca_key), (sa.attestation_key, fp.debug_file_signer_attestation_key)] for data, debug_file in debug_logs: try: store_data_to_file(debug_file, data) except Exception: logger.debug2('Failed to save debug file ' + debug_file + '\n' ' ' + str(sys.exc_info()[1])) def _process_secure_operation(self, image, progress, security_policy_list, i_integrity_check, i_sign, idx=0, parsegens=None): # Check bounds if len(security_policy_list) == 0: raise RuntimeError('Security policy list must not be empty.') elif len(security_policy_list) <= idx: raise RuntimeError('Security policy list length must be more than index.') # Get the current security policy file_type = security_policy_list[idx].file_type integrity_check = security_policy_list[idx].integrity_check and i_integrity_check sign = security_policy_list[idx].sign and i_sign # Create the parsegen object file_type_backup = image.image_type.file_type image.image_type.file_type = file_type try: parsegen = self._status_updater(self._create_parsegen_obj, image.status.parsegen, progress, True, image, validating=False, signing=sign, parsegens=parsegens) finally: image.image_type.file_type = file_type_backup # Set the security mechanisms parsegen.integrity_check = security_policy_list[idx].integrity_check and (parsegen.contains_integrity_check() or integrity_check) parsegen.sign = security_policy_list[idx].sign and (parsegen.is_signed() or sign) # Dump any debug data self.dump_parsegen_debug_data(image, parsegen) # If the security policy list contains more formats, call them if idx < len(security_policy_list) - 1: tmp_file_path = c_path.create_tmp_file(data=parsegen.get_wrapped_data()) # Backup the source path here src_image_dir_base = image.src_image.image_dir_base src_image_dir_ext = image.src_image.image_dir_ext src_image_name = image.src_image.image_name image.src_image.image_dir_base = os.path.dirname(tmp_file_path) image.src_image.image_dir_ext = '' image.src_image.image_name = os.path.basename(tmp_file_path) try: data = self._process_secure_operation(image, progress, security_policy_list, i_integrity_check, i_sign, idx=idx + 1) finally: image.src_image.image_dir_base = src_image_dir_base image.src_image.image_dir_ext = src_image_dir_ext image.src_image.image_name = src_image_name os.remove(tmp_file_path) parsegen.set_wrapped_data(data) # Sign the image if sign: self._status_updater(self._sign_image, image.status.sign, progress, True, image, parsegen) # Package and generate the output image file data = parsegen.get_data() if integrity_check: image.status.integrity_check.state = StatusInfo.SUCCESS if idx != 0: return data store_data_to_file(image.dest_image.image_path, data) logger.info(('Signed ' if parsegen.sign else '') + 'image is stored at ' + image.dest_image.image_path, color=logger.GREEN) image.image_under_operation = image.dest_image.image_path return parsegen def _process_validation(self, image, progress, security_policy_list, i_val_image, i_val_integrity_check, i_val_sign, idx=0): # Check bounds if len(security_policy_list) == 0: raise RuntimeError('Security policy list must not be empty.') elif len(security_policy_list) <= idx: raise RuntimeError('Security policy list length must be more than index.') # Get the current security policy file_type = security_policy_list[idx].file_type val_image = i_val_image val_integrity_check = security_policy_list[idx].integrity_check and i_val_integrity_check val_sign = security_policy_list[idx].sign and i_val_sign # Backup the source path here src_image_dir_base = image.src_image.image_dir_base src_image_dir_ext = image.src_image.image_dir_ext src_image_name = image.src_image.image_name # Update the souce image path image.src_image.image_dir_base = os.path.dirname(image.image_under_operation) image.src_image.image_dir_ext = '' image.src_image.image_name = os.path.basename(image.image_under_operation) try: # Create the parsegen object file_type_backup = image.image_type.file_type image.image_type.file_type = file_type try: parsegen = self._status_updater(self._create_parsegen_obj, image.status.validate_parsegen, progress, True, image, True, False) finally: image.image_type.file_type = file_type_backup # Set the security mechanisms parsegen.integrity_check = parsegen.contains_integrity_check() parsegen.sign = parsegen.is_signed() # Validate parsegen if val_image: self._status_updater(self._validate_parsegen, image.status.validate_parsegen, progress, False, image, parsegen) # Validate integrity check if val_integrity_check: self._status_updater(self._validate_integrity_check, image.status.validate_integrity_check, progress, False, image, parsegen) # Validate sign if val_sign: self._status_updater(self._validate_sign, image.status.validate_sign, progress, False, image, parsegen) # Dump any debug data self.dump_parsegen_debug_data(image, parsegen) # If the security policy list contains more formats, call them if idx < len(security_policy_list) - 1: tmp_file_path = c_path.create_tmp_file(data=parsegen.get_wrapped_data()) # Backup the image_under_operation here image_under_operation_int = image.image_under_operation image.image_under_operation = tmp_file_path try: self._process_validation(image, progress, security_policy_list, i_val_image, i_val_integrity_check, i_val_sign, idx + 1) finally: image.image_under_operation = image_under_operation_int os.remove(tmp_file_path) finally: image.src_image.image_dir_base = src_image_dir_base image.src_image.image_dir_ext = src_image_dir_ext image.src_image.image_name = src_image_name return parsegen def _create_parsegen(self, image, progress, security_policy_list, idx=0): # Check bounds if len(security_policy_list) == 0: raise RuntimeError('Security policy list must not be empty.') elif len(security_policy_list) <= idx: raise RuntimeError('Security policy list length must be more than index.') file_type = security_policy_list[idx].file_type # Backup the source path here src_image_dir_base = image.src_image.image_dir_base src_image_dir_ext = image.src_image.image_dir_ext src_image_name = image.src_image.image_name # Update the souce image path image.src_image.image_dir_base = os.path.dirname(image.image_under_operation) image.src_image.image_dir_ext = '' image.src_image.image_name = os.path.basename(image.image_under_operation) try: # Create the parsegen object file_type_backup = image.image_type.file_type image.image_type.file_type = file_type try: parsegen = self._status_updater(self._create_parsegen_obj, image.status.parsegen, progress, True, image, validating=False, signing=False) finally: image.image_type.file_type = file_type_backup # Set the security mechanisms parsegen.integrity_check = parsegen.contains_integrity_check() parsegen.sign = parsegen.is_signed() # Dump any debug data self.dump_parsegen_debug_data(image, parsegen) # If the security policy list contains more formats, call them if idx < len(security_policy_list) - 1: tmp_file_path = c_path.create_tmp_file(data=parsegen.get_wrapped_data()) # Backup the image_under_operation here image_under_operation_int = image.image_under_operation image.image_under_operation = tmp_file_path try: self._create_parsegen(image, progress, security_policy_list, idx + 1) finally: image.image_under_operation = image_under_operation_int os.remove(tmp_file_path) finally: image.src_image.image_dir_base = src_image_dir_base image.src_image.image_dir_ext = src_image_dir_ext image.src_image.image_name = src_image_name return parsegen def _create_parsegen_obj(self, image, validating=False, signing=False, file_exists=True, parsegens=None, sign_attr=False): c_path.create_debug_dir(image.dest_image.debug_dir_parsegen) image_path = image.src_image.image_path image_name = os.path.basename(image_path) image_base, image_ext = os.path.splitext(image_name) data = load_data_from_file(image_path) if file_exists else None parsegen = ElfMbnV3Base(data, imageinfo=image, debug_dir=image.dest_image.debug_dir_parsegen, debug_prefix=image_base, debug_suffix=image_ext, validating=validating, signing=signing, parsegens=parsegens, sign_attr=sign_attr) # Check if parsegen authority settings are correct parsegen.authority = self.authority parsegen.validate_authority() return parsegen def _sign_image(self, image, parsegen): # Check all other authorities and display if they have signed for authority in [x for x in self.AUTHORITIES_SUPPORTED if x != self.authority]: if parsegen.is_signed(authority): logger.info(image.image_under_operation + ' is already ' + str(authority) + '-signed.') # Prevent re-signing by authorities that previously signed that aren't the most recently signed authority previously_signed_authorities = [] for authority in self.AUTHORITIES_SUPPORTED: if parsegen.is_signed(authority): previously_signed_authorities.append(authority) last_signed_authority = self.highest_authority([AUTHORITY_OEM in previously_signed_authorities]) if self.authority != last_signed_authority and self.authority in previously_signed_authorities: raise RuntimeError("Cannot resign as {0} because image is already signed by {1}" .format(self.authority, last_signed_authority)) # Display sign status of signing authority logger.info(image.image_under_operation + ' is already signed. ' 'Re-signing image.' if parsegen.is_signed(self.authority) else 'Signing image: ' + image.image_under_operation) signer = get_signer(image.signer_config) sign_assets = signer.sign( parsegen.data_to_sign, image, debug_dir=image.dest_image.debug_dir_signer, is_hash=parsegen.is_data_hash, parsegen=parsegen) parsegen.data_signature = sign_assets.signature parsegen.cert_chain = sign_assets.cert_chain # Dump any debug information self.dump_signer_debug_data(image, sign_assets, parsegen) def _validate_parsegen(self, image, parsegen): pass def _validate_integrity_check(self, image, parsegen): pass def _validate_sign(self, image, parsegen): if parsegen.is_signed(): signer = get_signer(image.signer_config) if signer.validate(parsegen, imageinfo=image): logger.info('Image ' + image.image_under_operation + ' signature is valid') else: raise RuntimeError('Image ' + image.image_under_operation + ' signature is not valid') else: message = 'Image ' + image.image_under_operation + ' is not signed' if image in self.image_info_list and self.enforce_signed: raise RuntimeError(message) raise CustomError(message) def _status_updater(self, method, status, progress, raise_err, *args, **kwargs): try: retval = method(*args, **kwargs) status.state = StatusInfo.SUCCESS return retval except CustomError as e: # No change in imageinfo status. logger.info(str(e)) except Exception as e: status.state = StatusInfo.ERROR status.error = str(e) if raise_err: raise else: logger.error(status.error) finally: progress.push()