feat: add key derivation tests to sev_verify harness, with cross-CVM check - #285
Closed
markg-github wants to merge 7 commits into
Closed
feat: add key derivation tests to sev_verify harness, with cross-CVM check#285markg-github wants to merge 7 commits into
markg-github wants to merge 7 commits into
Conversation
Co-Authored-By: Claude <noreply@anthropic.com>
Always verify that values above the active bound are rejected, even when the bound is 0 (no ID block). Tests 3 values above the bound for both guest SVN and each TCB component. Co-Authored-By: Claude <noreply@anthropic.com>
Import calculate_measurement from attestation_test instead of duplicating it. Conditionally add generate_id_block step when sev_verify.id_block is available. Co-Authored-By: Claude <noreply@anthropic.com>
Same fix as pr/id-block — snphost ok can report failures on systems that are otherwise fully functional for SNP guest testing. Co-Authored-By: Claude <noreply@anthropic.com>
…mands Replace the guest-side Python script approach with individual snpguest key commands over vsock, pulling derived keys to the host for comparison. All test logic (determinism, VMPL isolation, root key difference, SVN/TCB bound enforcement, GFS sensitivity and field mixing) now lives in the sev_verify test module as callable steps. Use callable steps that loop over snpguest key via run_guest_command/fetch_guest_file_bytes while the VM is running. SVN and TCB sweeps are fully dynamic based on the runtime attestation report. TCB above-bound tests use committed+1..committed+3 per component from the runtime report, capped at 0xFF to avoid 8-bit overflow. Remove key-derivation guest module. The guest-side Python script and systemd service are no longer needed and snpguest is available in the generic guest image. Co-Authored-By: Claude <noreply@anthropic.com>
Derives a key at the end of CVM 1, stops it, launches CVM 2, derives the same key with identical parameters, and compares. Proves the key is bound to platform identity rather than transient VM state. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a new SEV-SNP key-derivation certification test to the sev_verify harness, including a cross-CVM (two independent guest launches) determinism check driven entirely from the host via vsock-issued snpguest key commands.
Changes:
- Introduces a new
c3_0_0_2test module that parses the attestation report on-host and runs multiple derived-key property checks (determinism, VMPL/root key differences, SVN/TCB bounds & sensitivity, GFS mixing, cross-CVM match). - Registers the new test at certification level
3.0.0-2in thec3_0manifest. - Updates prerequisites behavior (
snp_ok) and guest image build dependencies (addspython3package).
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| sev_verify/cert_tests/common/snp_ok.py | Changes severity of snphost ok step within prereq gating test. |
| sev_verify/cert_tests/c3_0/manifest.toml | Registers new key-derivation test at level 3.0.0-2. |
| sev_verify/cert_tests/c3_0/c3_0_0_2/key_derivation_test.py | New host-driven vsock key-derivation test suite + cross-CVM check. |
| sev_verify/cert_tests/c3_0/c3_0_0_2/init.py | Adds package init for the new test module directory. |
| modules/build/guest/mkosi.conf | Adds python3 to guest build packages. |
Suppressed comments (4)
sev_verify/cert_tests/c3_0/c3_0_0_2/key_derivation_test.py:222
- If either VMPL key read fails (returns
None),k0 != k1can evaluate to True and incorrectly pass the isolation check. Fail explicitly when key files cannot be read.
k0 = _read_key(ctx.artifact_dir / "vmpl0_key.bin")
k1 = _read_key(ctx.artifact_dir / "vmpl1_key.bin")
if k0 != k1:
return StepHandlerResult(exit_code=0, stdout="VMPL0 and VMPL1 keys differ (proper isolation)")
return StepHandlerResult(exit_code=1, stderr="VMPL0 and VMPL1 keys are identical")
sev_verify/cert_tests/c3_0/c3_0_0_2/key_derivation_test.py:234
- If either key read fails (returns
None),kv != kmcan incorrectly pass and claim the root keys differ. Explicitly fail if either key cannot be read.
kv = _read_key(ctx.artifact_dir / "vcek_key.bin")
km = _read_key(ctx.artifact_dir / "vmrk_key.bin")
if kv != km:
return StepHandlerResult(exit_code=0, stdout="VCEK and VMRK keys differ")
return StepHandlerResult(exit_code=1, stderr="VCEK and VMRK keys are identical")
sev_verify/cert_tests/c3_0/c3_0_0_2/key_derivation_test.py:355
- If either derived key read fails (returns
None),k1 != k2can incorrectly pass and claim GFS sensitivity. Fail explicitly when key files cannot be read.
k1 = _read_key(ctx.artifact_dir / "gfs1_key.bin")
k2 = _read_key(ctx.artifact_dir / "gfs2_key.bin")
if k1 != k2:
return StepHandlerResult(exit_code=0, stdout="GFS=0x01 and GFS=0x02 keys differ")
return StepHandlerResult(exit_code=1, stderr="GFS=0x01 and GFS=0x02 keys are identical")
sev_verify/cert_tests/c3_0/c3_0_0_2/key_derivation_test.py:384
baselinemay beNoneif the key file can't be read; in that casek != baselinecan incorrectly pass for all bits. Treat missing baseline/bit keys as a hard failure to avoid false positives.
ok, err = _derive_key(ctx, "gfs_baseline.bin", gfs=0)
if not ok:
return StepHandlerResult(exit_code=1, stderr=f"Baseline derivation failed: {err}")
baseline = _read_key(ctx.artifact_dir / "gfs_baseline.bin")
bits = [
(0, "Image ID"),
(1, "Family ID"),
(2, "Measurement"),
(3, "Guest SVN Policy"),
(4, "Guest SVN"),
(5, "TCB Version"),
]
failed = []
lines = []
for bit, label in bits:
ok, err = _derive_key(ctx, f"gfs_bit{bit}.bin", gfs=1 << bit)
if not ok:
failed.append(f"GFS bit {bit} ({label}): derivation failed: {err}")
continue
k = _read_key(ctx.artifact_dir / f"gfs_bit{bit}.bin")
if k != baseline:
lines.append(f"GFS bit {bit} ({label}): differs from baseline")
else:
failed.append(f"GFS bit {bit} ({label}): same as baseline")
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
48
to
52
| Step.for_host( | ||
| name="snphost ok", | ||
| type="required", | ||
| type="info", | ||
| command="snphost ok", | ||
| ), |
Comment on lines
+201
to
+205
| k1 = _read_key(ctx.artifact_dir / "det_key1.bin") | ||
| k2 = _read_key(ctx.artifact_dir / "det_key2.bin") | ||
| if k1 == k2: | ||
| return StepHandlerResult(exit_code=0, stdout="Keys match (deterministic)") | ||
| return StepHandlerResult(exit_code=1, stderr="Keys differ — derivation is not deterministic") |
Comment on lines
+72
to
+80
| class ReportInfo: | ||
| version: Optional[int] = None | ||
| guest_svn: int = 0 | ||
| committed_tcb: TcbVersion = None | ||
|
|
||
| def __post_init__(self): | ||
| if self.committed_tcb is None: | ||
| self.committed_tcb = TcbVersion() | ||
|
|
Comment on lines
+156
to
+158
| info = _parse_report_info(result.stdout) | ||
| c = info.committed_tcb | ||
| (ctx.artifact_dir / "report_info.txt").write_text( |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adapts the SNP key derivation tests to run within the
sev_verifyharness, with all test logic on the host using vsock to issuesnpguest keycommands on the guest. No guest-side Python script is needed —snpguestis already available in the generic guest image.What's tested
Approach
The attestation report is fetched and pulled to the host first (while the VM is running), then callable steps loop over
snpguest keyvia vsock with dynamic bounds from the report. SVN and TCB sweeps are fully runtime-driven — no static assumptions about platform values.The cross-CVM check saves a key from CVM 1, stops it, launches CVM 2, rederives the same key, and compares on the host.
Tested on AMD EPYC 9004 hardware. All 17 steps pass.
Closes #269