def check_pcrs(self, agentAttestState, tpm_policy, pcrs, data, virtual, ima_measurement_list, allowlist, ima_keyrings, mb_measurement_list, mb_refstate_str, hash_alg) -> Failure: failure = Failure(Component.PCR_VALIDATION) if isinstance(tpm_policy, str): tpm_policy = json.loads(tpm_policy) pcr_allowlist = tpm_policy.copy() if 'mask' in pcr_allowlist: del pcr_allowlist['mask'] # convert all pcr num keys to integers pcr_allowlist = {int(k): v for k, v in list(pcr_allowlist.items())} mb_policy, mb_policy_name, mb_refstate_data = measured_boot.get_policy( mb_refstate_str) mb_pcrs_hashes, boot_aggregates, mb_measurement_data, mb_failure = self.parse_mb_bootlog( mb_measurement_list, hash_alg) failure.merge(mb_failure) pcrs_in_quote = set( ) # PCRs in quote that were already used for some kind of validation pcrs = AbstractTPM.__parse_pcrs(pcrs, virtual) pcr_nums = set(pcrs.keys()) # Validate data PCR if config.TPM_DATA_PCR in pcr_nums and data is not None: expectedval = self.sim_extend(data, hash_alg=hash_alg) if expectedval != pcrs[config.TPM_DATA_PCR]: logger.error( "%sPCR #%s: invalid bind data %s from quote does not match expected value %s", ("", "v")[virtual], config.TPM_DATA_PCR, pcrs[config.TPM_DATA_PCR], expectedval) failure.add_event(f"invalid_pcr_{config.TPM_DATA_PCR}", { "got": pcrs[config.TPM_DATA_PCR], "expected": expectedval }, True) pcrs_in_quote.add(config.TPM_DATA_PCR) else: logger.error( "Binding %sPCR #%s was not included in the quote, but is required", ("", "v")[virtual], config.TPM_DATA_PCR) failure.add_event( f"missing_pcr_{config.TPM_DATA_PCR}", f"Data PCR {config.TPM_DATA_PCR} is missing in quote, but is required", True) # Check for ima PCR if config.IMA_PCR in pcr_nums: if ima_measurement_list is None: logger.error( "IMA PCR in policy, but no measurement list provided") failure.add_event( f"unused_pcr_{config.IMA_PCR}", "IMA PCR in policy, but no measurement list provided", True) else: ima_failure = AbstractTPM.__check_ima(agentAttestState, pcrs[config.IMA_PCR], ima_measurement_list, allowlist, ima_keyrings, boot_aggregates, hash_alg) failure.merge(ima_failure) pcrs_in_quote.add(config.IMA_PCR) # Collect mismatched measured boot PCRs as measured_boot failures mb_pcr_failure = Failure(Component.MEASURED_BOOT) # Handle measured boot PCRs only if the parsing worked if not mb_failure: for pcr_num in set(config.MEASUREDBOOT_PCRS) & pcr_nums: if mb_refstate_data: if not mb_measurement_list: logger.error( "Measured Boot PCR %d in policy, but no measurement list provided", pcr_num) failure.add_event( f"unused_pcr_{pcr_num}", f"Measured Boot PCR {pcr_num} in policy, but no measurement list provided", True) continue val_from_log_int = mb_pcrs_hashes.get(str(pcr_num), 0) val_from_log_hex = hex(val_from_log_int)[2:] val_from_log_hex_stripped = val_from_log_hex.lstrip('0') pcrval_stripped = pcrs[pcr_num].lstrip('0') if val_from_log_hex_stripped != pcrval_stripped: logger.error( "For PCR %d and hash %s the boot event log has value %r but the agent returned %r", str(hash_alg), pcr_num, val_from_log_hex, pcrs[pcr_num]) mb_pcr_failure.add_event( f"invalid_pcr_{pcr_num}", { "context": "SHA256 boot event log PCR value does not match", "got": pcrs[pcr_num], "expected": val_from_log_hex }, True) if pcr_num in pcr_allowlist and pcrs[ pcr_num] not in pcr_allowlist[pcr_num]: logger.error( "%sPCR #%s: %s from quote does not match expected value %s", ("", "v")[virtual], pcr_num, pcrs[pcr_num], pcr_allowlist[pcr_num]) failure.add_event( f"invalid_pcr_{pcr_num}", { "context": "PCR value is not in allowlist", "got": pcrs[pcr_num], "expected": pcr_allowlist[pcr_num] }, True) pcrs_in_quote.add(pcr_num) failure.merge(mb_pcr_failure) # Check the remaining non validated PCRs for pcr_num in pcr_nums - pcrs_in_quote: if pcr_num not in list(pcr_allowlist.keys()): logger.warning( "%sPCR #%s in quote not found in %stpm_policy, skipping.", ("", "v")[virtual], pcr_num, ("", "v")[virtual]) continue if pcrs[pcr_num] not in pcr_allowlist[pcr_num]: logger.error( "%sPCR #%s: %s from quote does not match expected value %s", ("", "v")[virtual], pcr_num, pcrs[pcr_num], pcr_allowlist[pcr_num]) failure.add_event( f"invalid_pcr_{pcr_num}", { "context": "PCR value is not in allowlist", "got": pcrs[pcr_num], "expected": pcr_allowlist[pcr_num] }, True) pcrs_in_quote.add(pcr_num) missing = set(pcr_allowlist.keys()) - pcrs_in_quote if len(missing) > 0: logger.error("%sPCRs specified in policy not in quote: %s", ("", "v")[virtual], missing) failure.add_event("missing_pcrs", { "context": "PCRs are missing in quote", "data": list(missing) }, True) if not mb_failure and mb_refstate_data: mb_policy_failure = measured_boot.evaluate_policy( mb_policy, mb_policy_name, mb_refstate_data, mb_measurement_data, pcrs_in_quote, ("", "v")[virtual], agentAttestState.get_agent_id()) failure.merge(mb_policy_failure) return failure
def check_pcrs(self, agentAttestState, tpm_policy, pcrs, data, virtual, ima_measurement_list, allowlist, ima_keyring, mb_measurement_list, mb_refstate_str): if isinstance(tpm_policy, str): tpm_policy = json.loads(tpm_policy) pcr_allowlist = tpm_policy.copy() if 'mask' in pcr_allowlist: del pcr_allowlist['mask'] # convert all pcr num keys to integers pcr_allowlist = {int(k): v for k, v in list(pcr_allowlist.items())} mb_policy, mb_refstate_data = measured_boot.get_policy(mb_refstate_str) mb_pcrs_sha256, boot_aggregates, mb_measurement_data, success = self.parse_mb_bootlog( mb_measurement_list) if not success: return False pcrs_in_quote = set( ) # PCRs in quote that were already used for some kind of validation pcrs = self.__parse_pcrs(pcrs, virtual) pcr_nums = set(pcrs.keys()) # Skip validation if TPM is stubbed. if config.STUB_TPM: return True # Validate data PCR if config.TPM_DATA_PCR in pcr_nums and data is not None: expectedval = self.sim_extend(data) if expectedval != pcrs[config.TPM_DATA_PCR]: logger.error( "%sPCR #%s: invalid bind data %s from quote does not match expected value %s", ("", "v")[virtual], config.TPM_DATA_PCR, pcrs[config.TPM_DATA_PCR], expectedval) return False pcrs_in_quote.add(config.TPM_DATA_PCR) else: logger.error( "Binding %sPCR #%s was not included in the quote, but is required", ("", "v")[virtual], config.TPM_DATA_PCR) return False # Check for ima PCR if config.IMA_PCR in pcr_nums: if ima_measurement_list is None: logger.error( "IMA PCR in policy, but no measurement list provided") return False if not self.__check_ima(agentAttestState, pcrs[config.IMA_PCR], ima_measurement_list, allowlist, ima_keyring, boot_aggregates): return False pcrs_in_quote.add(config.IMA_PCR) # Handle measured boot PCRs for pcr_num in set(config.MEASUREDBOOT_PCRS) & pcr_nums: if mb_refstate_data: if not mb_measurement_list: logger.error( "Measured Boot PCR %d in policy, but no measurement list provided", pcr_num) return False val_from_log_int = mb_pcrs_sha256.get(str(pcr_num), 0) val_from_log_hex = hex(val_from_log_int)[2:] val_from_log_hex_stripped = val_from_log_hex.lstrip('0') pcrval_stripped = pcrs[pcr_num].lstrip('0') if val_from_log_hex_stripped != pcrval_stripped: logger.error( "For PCR %d and hash SHA256 the boot event log has value %r but the agent returned %r", pcr_num, val_from_log_hex, pcrs[pcr_num]) return False if pcr_num in pcr_allowlist and pcrs[ pcr_num] not in pcr_allowlist[pcr_num]: logger.error( "%sPCR #%s: %s from quote does not match expected value %s", ("", "v")[virtual], pcr_num, pcrs[pcr_num], pcr_allowlist[pcr_num]) return False pcrs_in_quote.add(pcr_num) # Check the remaining non validated PCRs for pcr_num in pcr_nums - pcrs_in_quote: if pcr_num not in list(pcr_allowlist.keys()): logger.warning( "%sPCR #%s in quote not found in %stpm_policy, skipping.", ("", "v")[virtual], pcr_num, ("", "v")[virtual]) continue if pcrs[pcr_num] not in pcr_allowlist[pcr_num]: logger.error( "%sPCR #%s: %s from quote does not match expected value %s", ("", "v")[virtual], pcr_num, pcrs[pcr_num], pcr_allowlist[pcr_num]) return False pcrs_in_quote.add(pcr_num) missing = set(pcr_allowlist.keys()) - pcrs_in_quote if len(missing) > 0: logger.error("%sPCRs specified in policy not in quote: %s", ("", "v")[virtual], missing) return False if mb_refstate_data: success = measured_boot.evaluate_policy( mb_policy, mb_refstate_data, mb_measurement_data, pcrs_in_quote, ("", "v")[virtual], agentAttestState.get_agent_id()) if not success: return False return True