镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

fix(sevsnp): verify the certificate chain up to AMD's roots; the host's table need not carry the ASK and ARK - #490

Open
nikolaichuk7 wants to merge 1 commit into
veraison:mainfrom
nikolaichuk7:sevsnp-amd-roots
Open

nikolaichuk7 wants to merge 1 commit into
veraison:mainfrom
nikolaichuk7:sevsnp-amd-roots

Conversation

@nikolaichuk7

@nikolaichuk7 nikolaichuk7 commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Description

Rebased on main after #488 was merged (1f2c320); one commit.

AWS hosts put the VLEK alone in the SEV-SNP certificate table, and the scheme required the table to carry the ASK and the ARK: a
VLEK report from an AWS guest failed at GetTrustAnchorIDs ("can't read ARK to compose TA ID") and at ValidateEvidenceIntegrity
("missing ARK certificate in evidence") unless the guest appended AMD's chain itself. go-sev-guest's embedded defaults hold no ASVK,
so a VLEK chain could not be built from them either.

The scheme now holds AMD's roots itself: the six chains AMD's KDS publishes and go-sev-guest embeds (ASK and ARK for a VCEK, ASVK
and ARK for a VLEK; Milan, Genoa, Turin), passed to go-sev-guest as TrustedRoots. The trust anchor to look up, and the one
compared with the provisioned trust anchor, is the ARK among those roots that certifies the intermediate that signed the report's
certificate; the table's ASK, ASVK and ARK entries, if any, play no part, and the table need only carry the report's signing key.
The Turin VLEK chain is trimmed of its trailing blank line, which kds.ParseProductCertChain refuses (google/go-sev-guest#206).

Tests, in scheme_test.go: the six roots parse into the expected subjects; a real VLEK report from an AWS EC2 c6a.large guest
(6 October 2026; test/evidence/sevsnp-aws-vlek-tsm-report.cbor, provenance in test/evidence/SOURCES.md), with the table as the
host supplies it, yields the ARK-Milan trust-anchor ID and validates, as does the same report with AMD's chain appended; another
nonce, a changed MEASUREMENT and another product line's ARK are refused. Without this change the host's table fails both lookups.

Fixes #489.

Checklist

  • Code compiles correctly
  • Created tests which fail without the change (if possible)
  • All tests passing
  • Extended the README / documentation, if necessary (test/evidence/SOURCES.md)

@nikolaichuk7
nikolaichuk7 requested a review from a team as a code owner October 12, 2026 15:23

@setrofim setrofim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As with the other pull, please move all tests for scheme.go into scheme_test.go.

@setrofim
setrofim requested a review from jraman567 October 12, 2026 17:30
@nikolaichuk7
nikolaichuk7 force-pushed the sevsnp-amd-roots branch 2 times, most recently from af0eb40 to d73ae83 Compare October 12, 2026 19:11
@nikolaichuk7

Copy link
Copy Markdown
Contributor Author

Done in d73ae83, rebased on #488's 283df9a: the roots tests are in scheme_test.go as well.

@jraman567

jraman567 commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

I need some more time to review this. There are two things I'm trying to figure out:

  • Is it OK for auxblob to only contain VLEK and not have ARK/ASK. The TSM report spec says it should contain the cert_table for provider = sev_guest. So, is it OK to just have a table with VLEK or do we need ARK and ASK also?
  • I don't think that using the cached intermediate certificates in go-sev-guest is OK. The ASKs are subject to revocation, so if an ASK/ASVK is revoked, go-sev-guest would reject further requests (as VCEK & VLEK would be issued by newer certificates).

My suggestion is to keep the verifier code as is. The verifier could expect the auxblob to contain all three certificates. A layer in front of the verifier (perhaps RATSd presently) could sanitize this and supply the certificate chain for validation. We have been thinking about a caching service, and I believe that's a good place to have this logic - we could download missing certificates there and also handle certificate revocation over there.

But I'm open to discussion. @yogeshbdeshpande @thomas-fossati @setrofim

@jraman567

Copy link
Copy Markdown
Contributor

AWS doc recommends downloading the ARK and ASVK on the guest (./snpguest certificates PEM ./). So, I think it's a good idea to update RATSd to collect these certificates and patch them to auxblob.

@nikolaichuk7

Copy link
Copy Markdown
Contributor Author

Thanks, both points deserve a careful answer, and checking them corrected one thing I had assumed.

On the table: the kernel ABI says auxblob is the GHCB cert_table and that "the certificate data returned is being provided by the hypervisor through the SNP_SET_EXT_CONFIG" (Documentation/virt/coco/sev-guest.rst, 2.3): the table holds what the hypervisor put there, and hosts differ. In the bench's attack runs of 11 October every one of 30 Google Cloud reports came with VCEK, ASK and ARK in the table, and every one of 30 AWS reports came with the VLEK alone; our guest appended AMD's ASVK and ARK to get them through, and records that it did. go-sev-guest expects tables without the intermediates: fillInAttestation fetches them from the KDS when certificate fetching is on, and this scheme turns fetching off. ratsd passes the host's auxblob through as it is (attesters/tsm/tsm.go), so with ratsd on AWS the scheme receives exactly that table and refuses it before go-sev-guest sees it ("missing ARK certificate in evidence"). If the Attester appends the ASVK and ARK itself, today's go-sev-guest (v0.14.2 pre-release) does accept the report, since ProductCerts.Decode recognises the ASVK by its name; v0.15.0 no longer takes the intermediate from the evidence, and then only TrustedRoots with an ASVK will do.

On the intermediates: today the intermediate is whatever the table carries; it is checked structurally and by signature up to the table's ARK, and that ARK must equal the provisioned trust anchor. This change takes the intermediate from AMD's published chains instead, the same ones the KDS serves and go-sev-guest embeds, and still requires the ARK that certifies it to equal the provisioned trust anchor; the Attester no longer has to ship it. Revocation is checked in neither case (CheckRevocations is false), so on that axis the change is neutral, and I agree it should not stay that way: I have the check ready as the next change, the intermediate that signed the report's certificate checked against the CRL at its distribution point on the KDS, signed by the ARK and current, fetched once per validity period so the KDS is not asked per report, failing closed; tested on AMD's CRLs and against the live KDS. On staleness of embedded roots, TrustedRoots can just as well be loaded from a bundle a caching service refreshes; I can add that as a scheme option with the embedded chains as the default.

On the layer in front: a caching service that supplies and refreshes AMD's chains is a good home for this, and TrustedRoots is the hook it would feed. Until it exists, evidence from an AWS guest with the table as the host supplies it cannot be verified by the scheme. Happy to go whichever way you prefer; which would you like?

@jraman567

jraman567 commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Thanks @nikolaichuk7! Would you be able to attend the Veraison weekly-sync tomorrow to discuss this? What's your time zone?

The meeting is at 11 am US ET. https://armltd.zoom.us/j/93024860563?pwd=dVpVcFRtSVFmV29HV3dHWENrZk5WQT09

Since this is a one-off vendor adjustment, would it be OK to have a separate config file for AWS which turns on Certificate Fetching (DisableCertFetching: false). Looks like that'd solve your problem given you have identified that fillInAttestation fills the missing certificates?

@nikolaichuk7

Copy link
Copy Markdown
Contributor Author

Thanks, Jag. Rather than argue it, I measured it: one test, dropped into five checkouts of this repository and run on the pinned go-sev-guest and on v0.15.0, against upstream's own sample and the two real reports in this PR's fixtures (AWS Milan VLEK of 6 October, Google Cloud Milan VCEK of 7 October), each with the table as the host supplies it and with variations. Each cell is GetTrustAnchorIDs followed by ValidateEvidenceIntegrity with AMD's ARK of the product line as the provisioned trust anchor; the number of requests to kdsintf.amd.com is counted at http.DefaultTransport.

report, as the Verifier receives it main (today)
pinned v0.14.2-pre
main (today)
v0.15.0
main + DisableCertFetching: false
pinned v0.14.2-pre
main + DisableCertFetching: false
v0.15.0
#488
pinned v0.14.2-pre
#488
v0.15.0
#488 + DisableCertFetching: false
pinned v0.14.2-pre
#488 + DisableCertFetching: false
v0.15.0
#488 + #490
pinned v0.14.2-pre
#488 + #490
v0.15.0
upstream's own sample: Genoa VCEK, PEM table, ratsd token ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅
GCP Milan VCEK, the host's table (VCEK+ASK+ARK, DER) ❌ ❌ ❌ ❌ ✅ ✅ ✅ ✅ ✅ ✅
GCP Milan VCEK, a host's table with the VCEK alone ❌ ❌ ❌ ❌ ❌ ❌ ❌ ❌ ✅ ✅
AWS Milan VLEK, the host's table (VLEK alone) ❌ ❌ ❌ ❌ ❌ ❌ ❌ ❌ ✅ ✅
AWS Milan VLEK, the guest appended ASVK+ARK ❌ ❌ ❌ ❌ ✅ ❌ ✅ ❌ ✅ ✅
GCP VCEK alone, then AWS VLEK alone in the same process: the VCEK ❌ ❌ ❌ ❌ ❌ ❌ ❌ ❌ ✅ ✅
GCP VCEK alone, then AWS VLEK alone in the same process: the VLEK ❌ ❌ ❌ ❌ ❌ ❌ ❌ ❌ ✅ ✅

Three things the table says:

  1. The flag alone changes nothing, on main or on top of fix(sevsnp): accept DER certificates in the host certificate table #488, and never produces a KDS request: the scheme refuses the table before go-sev-guest runs (GetTrustAnchorIDs composes the trust-anchor ID from the table's ARK; validateCertChain requires the ARK and ASK; validateReportIntegrity reads both). Those places have to change whichever way we go, and that is the part of this PR that is not about where the roots live.

  2. With them rewritten so that fetching can run, go-sev-guest on the pin does fetch vlek/v1/Milan/cert_chain once per process and verifies the AWS report. But its product-chain cache is keyed by product line alone while the KDS path differs by signer, so after a VCEK report of the same product line from a host that supplied the leaf alone, the VLEK report fails with "trusted root must have ASVK certificate for VLEK chain" until the process restarts (and the other order fails the VCEK report). I have that test as well and will report it to go-sev-guest.

  3. On go-sev-guest v0.15.0 the route closes: since "enforce public key pinning" (go-sev-guest PR Creation of gen-corim command #190) the roots come from GetDefaultRootCerts when none are given, the chain's intermediates are ignored, and the defaults hold no ASVK. The last column pair shows it: with fix(sevsnp): accept DER certificates in the host certificate table #488 alone, the AWS report that passed on the pin fails on v0.15.0 even with the chain appended by the guest ("root of trust missing intermediate certificate authority certificate for key VLEK"). The next dependency bump breaks AWS again unless the scheme holds TrustedRoots with an ASVK, which is what this PR supplies. Google's maintainer put it the same way in go-sev-guest#134: AWS "expects users to already add the ASVK … and ARK to their own trust anchors. The VLEK in the buffer is all that AWS provides."

So I would keep the roots with the scheme, embedded by default and loadable from a bundle a caching service refreshes (I can add that option here), rather than a per-vendor configuration with fetching on.

Why each ❌ (the scheme's error, then the integrity check's)
variant go-sev-guest report GetTrustAnchorIDs ValidateEvidenceIntegrity
main (today) pinned v0.14.2-pre GCP Milan VCEK, the host's table (VCEK+ASK+ARK, DER) FAIL: can't read ARK to compose TA ID: failed to read certificate FAIL: evidence Trust Anchor (ARK) doesn't match the provisioned one
main (today) pinned v0.14.2-pre GCP Milan VCEK, a host's table with the VCEK alone FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main (today) pinned v0.14.2-pre AWS Milan VLEK, the host's table (VLEK alone) FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main (today) pinned v0.14.2-pre AWS Milan VLEK, the guest appended ASVK+ARK FAIL: can't read ARK to compose TA ID: failed to read certificate FAIL: evidence Trust Anchor (ARK) doesn't match the provisioned one
main (today) pinned v0.14.2-pre GCP VCEK alone, then AWS VLEK alone in the same process: the VCEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main (today) pinned v0.14.2-pre GCP VCEK alone, then AWS VLEK alone in the same process: the VLEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main (today) v0.15.0 GCP Milan VCEK, the host's table (VCEK+ASK+ARK, DER) FAIL: can't read ARK to compose TA ID: failed to read certificate FAIL: evidence Trust Anchor (ARK) doesn't match the provisioned one
main (today) v0.15.0 GCP Milan VCEK, a host's table with the VCEK alone FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main (today) v0.15.0 AWS Milan VLEK, the host's table (VLEK alone) FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main (today) v0.15.0 AWS Milan VLEK, the guest appended ASVK+ARK FAIL: can't read ARK to compose TA ID: failed to read certificate FAIL: evidence Trust Anchor (ARK) doesn't match the provisioned one
main (today) v0.15.0 GCP VCEK alone, then AWS VLEK alone in the same process: the VCEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main (today) v0.15.0 GCP VCEK alone, then AWS VLEK alone in the same process: the VLEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main + DisableCertFetching: false pinned v0.14.2-pre GCP Milan VCEK, the host's table (VCEK+ASK+ARK, DER) FAIL: can't read ARK to compose TA ID: failed to read certificate FAIL: evidence Trust Anchor (ARK) doesn't match the provisioned one
main + DisableCertFetching: false pinned v0.14.2-pre GCP Milan VCEK, a host's table with the VCEK alone FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main + DisableCertFetching: false pinned v0.14.2-pre AWS Milan VLEK, the host's table (VLEK alone) FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main + DisableCertFetching: false pinned v0.14.2-pre AWS Milan VLEK, the guest appended ASVK+ARK FAIL: can't read ARK to compose TA ID: failed to read certificate FAIL: evidence Trust Anchor (ARK) doesn't match the provisioned one
main + DisableCertFetching: false pinned v0.14.2-pre GCP VCEK alone, then AWS VLEK alone in the same process: the VCEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main + DisableCertFetching: false pinned v0.14.2-pre GCP VCEK alone, then AWS VLEK alone in the same process: the VLEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main + DisableCertFetching: false v0.15.0 GCP Milan VCEK, the host's table (VCEK+ASK+ARK, DER) FAIL: can't read ARK to compose TA ID: failed to read certificate FAIL: evidence Trust Anchor (ARK) doesn't match the provisioned one
main + DisableCertFetching: false v0.15.0 GCP Milan VCEK, a host's table with the VCEK alone FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main + DisableCertFetching: false v0.15.0 AWS Milan VLEK, the host's table (VLEK alone) FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main + DisableCertFetching: false v0.15.0 AWS Milan VLEK, the guest appended ASVK+ARK FAIL: can't read ARK to compose TA ID: failed to read certificate FAIL: evidence Trust Anchor (ARK) doesn't match the provisioned one
main + DisableCertFetching: false v0.15.0 GCP VCEK alone, then AWS VLEK alone in the same process: the VCEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
main + DisableCertFetching: false v0.15.0 GCP VCEK alone, then AWS VLEK alone in the same process: the VLEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 pinned v0.14.2-pre GCP Milan VCEK, a host's table with the VCEK alone FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 pinned v0.14.2-pre AWS Milan VLEK, the host's table (VLEK alone) FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 pinned v0.14.2-pre GCP VCEK alone, then AWS VLEK alone in the same process: the VCEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 pinned v0.14.2-pre GCP VCEK alone, then AWS VLEK alone in the same process: the VLEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 v0.15.0 GCP Milan VCEK, a host's table with the VCEK alone FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 v0.15.0 AWS Milan VLEK, the host's table (VLEK alone) FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 v0.15.0 AWS Milan VLEK, the guest appended ASVK+ARK ok: ARK-Milan FAIL: VLEK could not be verified by any trusted roots. Last error: root of trust missing interme…
#488 v0.15.0 GCP VCEK alone, then AWS VLEK alone in the same process: the VCEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 v0.15.0 GCP VCEK alone, then AWS VLEK alone in the same process: the VLEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 + DisableCertFetching: false pinned v0.14.2-pre GCP Milan VCEK, a host's table with the VCEK alone FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 + DisableCertFetching: false pinned v0.14.2-pre AWS Milan VLEK, the host's table (VLEK alone) FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 + DisableCertFetching: false pinned v0.14.2-pre GCP VCEK alone, then AWS VLEK alone in the same process: the VCEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 + DisableCertFetching: false pinned v0.14.2-pre GCP VCEK alone, then AWS VLEK alone in the same process: the VLEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 + DisableCertFetching: false v0.15.0 GCP Milan VCEK, a host's table with the VCEK alone FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 + DisableCertFetching: false v0.15.0 AWS Milan VLEK, the host's table (VLEK alone) FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 + DisableCertFetching: false v0.15.0 AWS Milan VLEK, the guest appended ASVK+ARK ok: ARK-Milan FAIL: VLEK could not be verified by any trusted roots. Last error: root of trust missing interme…
#488 + DisableCertFetching: false v0.15.0 GCP VCEK alone, then AWS VLEK alone in the same process: the VCEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence
#488 + DisableCertFetching: false v0.15.0 GCP VCEK alone, then AWS VLEK alone in the same process: the VLEK FAIL: can't read ARK to compose TA ID: empty certificate FAIL: missing ARK certificate in evidence

The test, the loop, the two reports and the raw rows are on the branch sevsnp-matrix of my fork; its workflow runs the same matrix on GitHub's runner and writes the tables into the job summary: run 38306181232 (70 rows, identical to the local run).

On the sync: thanks for the invitation. I'm travelling this week and can't make tomorrow; next Tuesday works for me, and 11 am ET suits. Until then I'm glad to continue here.

nikolaichuk7 added a commit to nikolaichuk7/services that referenced this pull request Oct 13, 2026
…n#490 by tag; veraison#490 was rebased on main after veraison#488 merged

Signed-off-by: Serhii Nikolaichuk <nikolaichuk.s.f@gmail.com>
…'s table need not carry the ASK and ARK

AWS hosts put the VLEK alone in the SEV-SNP certificate table, and the
scheme required the table to carry the ASK and the ARK: a VLEK report
from an AWS guest failed at GetTrustAnchorIDs ("can't read ARK to compose
TA ID") and at ValidateEvidenceIntegrity ("missing ARK certificate in
evidence") unless the guest appended AMD's chain itself. go-sev-guest's
embedded defaults hold no ASVK, so a VLEK chain could not be built from
them either.

The scheme now holds AMD's roots itself: the six chains AMD's KDS
publishes and go-sev-guest embeds (ASK and ARK for a VCEK, ASVK and ARK
for a VLEK; Milan, Genoa, Turin), passed to go-sev-guest as TrustedRoots.
The trust anchor to look up, and the one compared with the provisioned
trust anchor, is the ARK among those roots that certifies the
intermediate that signed the report's certificate; the table's ASK, ASVK
and ARK entries, if any, play no part, and the table need only carry the
report's signing key. The Turin VLEK chain is trimmed of its trailing
blank line, which kds.ParseProductCertChain refuses
(google/go-sev-guest#206).

Tests, in scheme_test.go: the six roots parse into the expected
subjects; a real VLEK report from an AWS EC2 c6a.large guest (6 October
2026), with the table as the host supplies it, yields the ARK-Milan
trust-anchor ID and validates, as does the same report with AMD's chain
appended; another nonce, a changed MEASUREMENT and another product line's
ARK are refused. Without this change the host's table fails both lookups
above.

Signed-off-by: Serhii Nikolaichuk <nikolaichuk.s.f@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: SEV-SNP evidence from AWS guests cannot be verified: the host's table carries the VLEK alone, the scheme requires the ASK and ARK in the table

3 participants