Example #1
0
    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
Example #2
0
    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)
Example #4
0
 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)
Example #5
0
    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)
Example #6
0
    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()
Example #7
0
    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