Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions src/cmcp_verify/sev_snp.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,15 @@ def verify_sev_snp_measurement(
result = SNPVerificationResult(verified=True)

# Step 1: Format check
if not measurement.startswith("sha384:"):
if not isinstance(measurement, str) or not measurement.startswith("sha384:"):
result.verified = False
result.failure_reason = "invalid_measurement_format"
result.unverified_fields.append("vcek_cert_chain")
result.details["vcek_chain"] = "requires_amd_kds_lookup"
return result

hex_part = measurement[len("sha384:"):]
if len(hex_part) != 96:
if len(hex_part) != 96 or any(c not in "0123456789abcdef" for c in hex_part):
result.verified = False
result.failure_reason = "invalid_measurement_format"
result.unverified_fields.append("vcek_cert_chain")
Expand Down Expand Up @@ -200,8 +200,19 @@ def verify_sev_snp_measurement(
# silently ignoring a mismatch would accept an SNP report for a
# different enclave whose measurement happens to match.
if report_data_hex is not None:
if (
not isinstance(report_data_hex, str)
or not report_data_hex
or len(report_data_hex) > 128
or len(report_data_hex) % 2 != 0
or any(c not in "0123456789abcdefABCDEF" for c in report_data_hex)
):
result.verified = False
result.failure_reason = "report_data_mismatch"
result.unverified_fields.append("vcek_cert_chain")
return result
extracted_rd = report.report_data
expected_rd = bytes.fromhex(report_data_hex[:128])
expected_rd = bytes.fromhex(report_data_hex)
# Pad expected to 64 bytes if shorter
if len(expected_rd) < 64:
expected_rd = expected_rd + b"\x00" * (64 - len(expected_rd))
Expand Down
10 changes: 2 additions & 8 deletions src/cmcp_verify/tpm.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,10 @@ def verify_tpm_measurement(

def _valid_measurement(measurement: str) -> bool:
"""Return True if measurement is "sha256:" followed by exactly 64 hex characters."""
if not measurement.startswith("sha256:"):
if not isinstance(measurement, str) or not measurement.startswith("sha256:"):
return False
hex_part = measurement[len("sha256:"):]
if len(hex_part) != 64:
return False
try:
bytes.fromhex(hex_part)
except ValueError:
return False
return True
return len(hex_part) == 64 and all(c in "0123456789abcdef" for c in hex_part)


def _parse_tpm2b_attest(
Expand Down
30 changes: 30 additions & 0 deletions tests/unit/test_sev_snp_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,33 @@ def test_vcek_cert_chain_unverified_on_format_failure():
bad = "sha256:" + "z" * 64
result = verify_sev_snp_measurement(bad, raw_evidence=None)
assert "vcek_cert_chain" in result.unverified_fields


def test_sev_snp_and_tpm_reject_non_hex_uppercase_and_oversized_report_data():
import pytest

from cmcp_verify.tpm import verify_tpm_measurement

for bad_m in ("sha384:" + "z" * 96, "sha384:" + "A" * 96, "sha384: " + "a" * 94 + " ", None):
res = verify_sev_snp_measurement(bad_m, raw_evidence=None) # type: ignore[arg-type]
assert res.verified is False
assert res.failure_reason == "invalid_measurement_format"

raw_m = b"\x77" * 48
nonce = b"\x44" * 64
report = make_snp_report(version=2, measurement_bytes=raw_m, report_data=nonce)
measurement = "sha384:" + hashlib.sha384(raw_m).hexdigest()
for bad_rd in (nonce.hex() + "aa", "zz" * 32, "", 123):
res = verify_sev_snp_measurement(
measurement,
raw_evidence=report,
report_data_hex=bad_rd, # type: ignore[arg-type]
)
assert res.verified is False
assert res.failure_reason == "report_data_mismatch"

for bad_tpm in ("sha256:" + "A" * 64, "sha256: " + "a" * 62 + " ", None):
tpm_res = verify_tpm_measurement(bad_tpm, raw_evidence=None) # type: ignore[arg-type]
assert tpm_res.verified is False
assert tpm_res.failure_reason == "invalid_measurement_format"
_ = pytest
Loading