Skip to content

Commit 72036ee

Browse files
committed
verifier: Update SNP claims and documents
Adds more fields from the SNP evidence to the parsed claims, and use a nested structure for SNP claims. Updated the documents and default policy. Importantly, adds a `snp.generation` claim to tell the generation of the SNP platform. Assisted-by: Cursor Signed-off-by: Xynnn007 <xynnn@linux.alibaba.com>
1 parent eca7387 commit 72036ee

3 files changed

Lines changed: 209 additions & 64 deletions

File tree

attestation-service/docs/tcb_claims.md

Lines changed: 86 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -225,27 +225,92 @@ along with the full set of detached claims.
225225

226226
## AMD SEV-SNP
227227

228-
- `snp.measurement` Launch Digest covering initial guest memory
229-
- `snp.platform_smt_enabled`: Whether Simultaneous Multithreading is enabled on the system
230-
- `snp.platform_tsme_enabled`: Whether Transparent SME is enabled on the system
231-
- `snp.policy_abi_major`: Minimum ABI major version allowed for guest
232-
- `snp.policy_abi_minor`: Minimum ABI minor version allowed for guest
233-
- `snp.policy_debug_allowed`: Whether SNP debug features are allowed for guest
234-
- `snp.policy_migrate_ma`: Whether migration agent can be connected to guest
235-
- `snp.policy_single_socket`: Whether guest can be activated only on one socket
236-
- `snp.policy_smt_allowed`: Whether guest can run on a system with SMT enabled
237-
- `snp.reported_tcb_bootloader`: Reported SVN of ASP bootloader
238-
- `snp.reported_tcb_microcode`: Reported microcode version
239-
- `snp.reported_tcb_snp`: Reported SVN of SNP Firmware
240-
- `snp.reported_tcb_tee`: Reported SVN of ASP OS
241-
242-
The claims map only includes the reported TCB version.
243-
An SEV-SNP Attestation Report contains four sets of TCB version information.
244-
Often all four values are the same, but sometimes the reported TCB might lag
245-
behind the true firmware version. This is done to minimize churn of policies
246-
and certificates while the provider updates to provisional firmware.
247-
The actual firmware must always be newer than or equal to the reported TCB.
248-
Generally, policies should be evaluated against the reported TCB.
228+
229+
- `snp.generation`: string. Processor generation derived from the attestation report. One of `milan` (3rd Gen EPYC), `genoa` (4th Gen EPYC), or `turin` (5th Gen EPYC).
230+
231+
### Guest policy (`snp.evidence.policy`)
232+
233+
The guest policy is set by the guest owner at launch and cannot be changed for the lifetime of the guest.
234+
235+
- `snp.evidence.policy.abi_major`: u64. Minimum SNP firmware ABI major version required for this guest.
236+
- `snp.evidence.policy.abi_minor`: u64. Minimum SNP firmware ABI minor version required for this guest.
237+
- `snp.evidence.policy.smt_allowed`: Boolean. Whether the guest may run on a host with SMT enabled.
238+
- `snp.evidence.policy.migrate_ma_allowed`: Boolean. Whether the guest may be associated with a migration agent.
239+
- `snp.evidence.policy.debug_allowed`: Boolean. Whether SNP debug features are allowed for the guest.
240+
- `snp.evidence.policy.single_socket_required`: Boolean. Whether the guest may be activated on multiple sockets.
241+
- `snp.evidence.policy.cxl_allowed`: Boolean. Whether CXL may be populated with devices or memory.
242+
- `snp.evidence.policy.mem_aes_256_xts`: Boolean. Memory encryption mode requirement. `false`: AES-128-XEX or AES-256-XTS is allowed; `true`: AES-256-XTS is required.
243+
- `snp.evidence.policy.rapl_dis`: Boolean. Running Average Power Limit (RAPL) requirement.
244+
- `snp.evidence.policy.ciphertext_hiding`: Boolean. Ciphertext hiding requirement.
245+
- `snp.evidence.policy.page_swap_disabled`: Boolean. Guest access to page-move/swap commands. `false`: SNP page-move/swap commands are allowed; `true`: guest access to `SNP_PAGE_MOVE`, `SNP_SWAP_OUT`, and `SNP_SWAP_IN` is disabled.
246+
247+
### Platform info (`snp.evidence.plat_info`)
248+
249+
- `snp.evidence.plat_info.smt_enabled`: Boolean. Whether Simultaneous Multithreading is enabled on the host.
250+
- `snp.evidence.plat_info.tsme_enabled`: Boolean. Whether Transparent SME is enabled on the host.
251+
- `snp.evidence.plat_info.ecc_enabled`: Boolean. Whether the platform is currently using ECC memory.
252+
- `snp.evidence.plat_info.rapl_disabled`: Boolean. Whether the RAPL feature is disabled on the platform.
253+
- `snp.evidence.plat_info.ciphertext_hiding_enabled`: Boolean. Whether ciphertext hiding is enabled on the platform.
254+
- `snp.evidence.plat_info.alias_check_complete`: Boolean. Whether alias detection has completed since the last system reset with no aliasing addresses found.
255+
- `snp.evidence.plat_info.tio_enabled`: Boolean. Whether SEV-TIO is enabled on the platform.
256+
257+
### Key info (`snp.evidence.key_info`)
258+
259+
- `snp.evidence.key_info.author_key_en`: Boolean. Whether the author key digest is present in `author_key_digest`. `true`: digest is present; `false`: digest is zero.
260+
- `snp.evidence.key_info.mask_chip_key`: Boolean. Whether the report signature is masked. `false`: firmware signs with VCEK or VLEK; `true`: the signature field is zeroed instead of signing.
261+
- `snp.evidence.key_info.signing_key`: u32. Key used to sign this report. `0`: VCEK; `1`: VLEK; `7`: none (unsigned report).
262+
263+
### TCB versions
264+
265+
Four TCB sets are exposed. Each is an object with the subfields below. The same subfields and types exist on `current_tcb`, `reported_tcb`, `committed_tcb`, and `launch_tcb`.
266+
267+
- `snp.evidence.current_tcb`: object. Current platform TCB at report generation time.
268+
- `snp.evidence.reported_tcb`: object. TCB version used to derive the VCEK/VLEK that signed this report. **Policies should generally evaluate against this set.**
269+
- `snp.evidence.committed_tcb`: object. Committed TCB version.
270+
- `snp.evidence.launch_tcb`: object. Platform TCB at guest launch or import time.
271+
272+
Each TCB object contains the following fields (SVN / patch level):
273+
274+
- `fmc`: u8 | null. FMC firmware SVN. Present on Turin (`generation` = `turin`); `null` on Milan and Genoa.
275+
- `bootloader`: u8. PSP bootloader SVN.
276+
- `tee`: u8. PSP operating-system SVN.
277+
- `snp`: u8. SNP firmware SVN.
278+
- `microcode`: u8. Lowest current microcode patch level across all cores.
279+
280+
Often all four TCB values are the same, but the reported TCB may lag behind the true firmware version to minimize churn of policies and certificates while the provider updates to provisional firmware. The actual firmware must always be newer than or equal to the reported TCB.
281+
282+
Firmware version objects:
283+
284+
- `snp.evidence.current`: object. Current SNP firmware version.
285+
- `snp.evidence.current.major`: u8. Major version component.
286+
- `snp.evidence.current.minor`: u8. Minor version component.
287+
- `snp.evidence.current.build`: u8. Build version component.
288+
- `snp.evidence.committed`: object. Committed SNP firmware version.
289+
- `snp.evidence.committed.major`: u8. Major version component.
290+
- `snp.evidence.committed.minor`: u8. Minor version component.
291+
- `snp.evidence.committed.build`: u8. Build version component.
292+
293+
### Measurements and identifiers
294+
295+
- `snp.evidence.version`: u32. Attestation report format version. Supported values are 3 through 5.
296+
- `snp.evidence.guest_svn`: u32. Guest security version number provided at launch.
297+
- `snp.evidence.family_id`: string (hex). Family ID provided at launch.
298+
- `snp.evidence.image_id`: string (hex). Image ID provided at launch.
299+
- `snp.evidence.vmpl`: u32. Requested VMPL for the attestation report. Guest attestation expects `0`.
300+
- `snp.evidence.sig_algo`: u32. Signature algorithm identifier. `1`: ECDSA P-384 with SHA-384.
301+
- `snp.evidence.measurement`: string (hex). Launch digest covering initial guest memory (48 bytes).
302+
- `snp.evidence.report_data`: string (hex). Guest-provided report data from the attestation report (64 bytes). Also available at the policy root as `report_data`.
303+
- `snp.evidence.host_data`: string (hex). Hypervisor-provided host data from launch (32 bytes). Also available at the policy root as `init_data`.
304+
- `snp.evidence.id_key_digest`: string (hex). SHA-384 digest of the ID public key that signed the ID block.
305+
- `snp.evidence.author_key_digest`: string (hex). SHA-384 digest of the author public key that certified the ID key. Zero when `key_info.author_key_en` is `false`.
306+
- `snp.evidence.report_id`: string (hex). Report ID of this guest.
307+
- `snp.evidence.report_id_ma`: string (hex). Report ID of this guest's migration agent, if applicable.
308+
- `snp.evidence.chip_id`: string (hex). Unique chip identifier (64 bytes). Zero when `key_info.mask_chip_key` is `true`.
309+
- `snp.evidence.cpu_id_fam_id`: u8 | null. CPUID family ID (combined extended family and family). Present in report version 3+; `null` on older reports.
310+
- `snp.evidence.cpu_id_mod_id`: u8 | null. CPUID model ID (combined extended model and model). Present in report version 3+; `null` on older reports.
311+
- `snp.evidence.cpu_id_step`: u8 | null. CPUID stepping. Present in report version 3+; `null` on older reports.
312+
- `snp.evidence.launch_mit_vector`: u64 | null. Verified mitigation vector at guest launch. Present in report version 5+; `null` on older reports.
313+
- `snp.evidence.current_mit_vector`: u64 | null. Current verified mitigation vector. Present in report version 5+; `null` on older reports.
249314

250315
## TPM
251316

attestation-service/src/ear_token/ear_default_policy_cpu.rego

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,17 @@ executables := 3 if {
103103
input.snp
104104

105105
# In the future, we might calculate this measurement here various components
106-
input.snp.measurement in query_reference_value("snp_launch_measurement")
106+
input.snp.evidence.measurement in query_reference_value("snp_launch_measurement")
107107
}
108108

109109
hardware := 2 if {
110110
input.snp
111111

112112
# Check the reported TCB to validate the ASP FW
113-
input.snp.reported_tcb_bootloader in query_reference_value("snp_bootloader")
114-
input.snp.reported_tcb_microcode in query_reference_value("snp_microcode")
115-
input.snp.reported_tcb_snp in query_reference_value("snp_snp_svn")
116-
input.snp.reported_tcb_tee in query_reference_value("snp_tee_svn")
113+
input.snp.evidence.reported_tcb.bootloader in query_reference_value("snp_bootloader")
114+
input.snp.evidence.reported_tcb.microcode in query_reference_value("snp_microcode")
115+
input.snp.evidence.reported_tcb.snp in query_reference_value("snp_snp_svn")
116+
input.snp.evidence.reported_tcb.tee in query_reference_value("snp_tee_svn")
117117
}
118118

119119
# For the 'configuration' trust claim 2 stands for
@@ -123,14 +123,14 @@ hardware := 2 if {
123123
configuration := 2 if {
124124
input.snp
125125

126-
input.snp.policy_debug_allowed == false
127-
input.snp.policy_migrate_ma == false
128-
input.snp.platform_smt_enabled == query_reference_value("snp_smt_enabled")
129-
input.snp.platform_tsme_enabled == query_reference_value("snp_tsme_enabled")
130-
input.snp.policy_abi_major == query_reference_value("snp_guest_abi_major")
131-
input.snp.policy_abi_minor == query_reference_value("snp_guest_abi_minor")
132-
input.snp.policy_single_socket == query_reference_value("snp_single_socket")
133-
input.snp.policy_smt_allowed == query_reference_value("snp_smt_allowed")
126+
input.snp.evidence.policy.debug_allowed == false
127+
input.snp.evidence.policy.migrate_ma_allowed == false
128+
input.snp.evidence.plat_info.smt_enabled == query_reference_value("snp_smt_enabled")
129+
input.snp.evidence.plat_info.tsme_enabled == query_reference_value("snp_tsme_enabled")
130+
input.snp.evidence.policy.abi_major == query_reference_value("snp_guest_abi_major")
131+
input.snp.evidence.policy.abi_minor == query_reference_value("snp_guest_abi_minor")
132+
input.snp.evidence.policy.single_socket_required == query_reference_value("snp_single_socket")
133+
input.snp.evidence.policy.smt_allowed == query_reference_value("snp_smt_allowed")
134134
}
135135

136136
# For the `configuration` trust claim 3 stands for
@@ -143,8 +143,8 @@ configuration := 2 if {
143143
else := 3 if {
144144
input.snp
145145

146-
input.snp.policy_debug_allowed == false
147-
input.snp.policy_migrate_ma == false
146+
input.snp.evidence.policy.debug_allowed == false
147+
input.snp.evidence.policy.migrate_ma_allowed == false
148148
}
149149

150150
##### TDX

deps/verifier/src/snp/mod.rs

Lines changed: 108 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,8 @@ pub(crate) struct VendorCertificates {
320320
pub(crate) asvk: Certificate,
321321
}
322322

323-
#[derive(Debug, Clone, PartialEq, Eq, EnumString, Display, EnumIter, Hash)]
323+
#[derive(Debug, Clone, PartialEq, Eq, EnumString, Display, EnumIter, Hash, Serialize, Copy)]
324+
#[serde(rename_all = "lowercase")]
324325
#[strum(serialize_all = "PascalCase", ascii_case_insensitive)]
325326
pub(crate) enum ProcessorGeneration {
326327
/// 3rd Gen AMD EPYC Processor (Standard)
@@ -499,7 +500,7 @@ impl Verifier for Snp {
499500
}
500501
}
501502

502-
let claims_map = parse_tee_evidence(&report);
503+
let claims_map = parse_tee_evidence(&report, proc_gen);
503504
let json = json!(claims_map);
504505
Ok(vec![(json, "cpu".to_string(), HardwareType::AmdSnp)])
505506
}
@@ -597,30 +598,100 @@ pub(crate) fn verify_report_tcb(
597598
/// Parses the attestation report and extracts the TEE evidence claims.
598599
/// Returns a JSON-formatted map of parsed claims.
599600
/// Note: Uses hex encoding for consistency with other verifiers (TDX, SGX, vTPM).
600-
pub(crate) fn parse_tee_evidence(report: &AttestationReport) -> TeeEvidenceParsedClaim {
601+
pub(crate) fn parse_tee_evidence(
602+
report: &AttestationReport,
603+
proc_gen: ProcessorGeneration,
604+
) -> TeeEvidenceParsedClaim {
601605
let claims_map = json!({
602-
// policy fields
603-
"policy_abi_major": report.policy.abi_major(),
604-
"policy_abi_minor": report.policy.abi_minor(),
605-
"policy_smt_allowed": report.policy.smt_allowed(),
606-
"policy_migrate_ma": report.policy.migrate_ma_allowed(),
607-
"policy_debug_allowed": report.policy.debug_allowed(),
608-
"policy_single_socket": report.policy.single_socket_required(),
609-
610-
// versioning info
611-
"reported_tcb_bootloader": report.reported_tcb.bootloader,
612-
"reported_tcb_tee": report.reported_tcb.tee,
613-
"reported_tcb_snp": report.reported_tcb.snp,
614-
"reported_tcb_microcode": report.reported_tcb.microcode,
615-
616-
// platform info
617-
"platform_tsme_enabled": report.plat_info.tsme_enabled(),
618-
"platform_smt_enabled": report.plat_info.smt_enabled(),
619-
620-
// measurements
621-
"measurement": hex::encode(report.measurement),
622-
"report_data": hex::encode(report.report_data),
623606
"init_data": hex::encode(report.host_data),
607+
"report_data": hex::encode(report.report_data),
608+
"generation": proc_gen,
609+
"evidence": {
610+
"version": report.version,
611+
"guest_svn": report.guest_svn,
612+
"policy": {
613+
"abi_major": report.policy.abi_major(),
614+
"abi_minor": report.policy.abi_minor(),
615+
"smt_allowed": report.policy.smt_allowed(),
616+
"migrate_ma_allowed": report.policy.migrate_ma_allowed(),
617+
"debug_allowed": report.policy.debug_allowed(),
618+
"single_socket_required": report.policy.single_socket_required(),
619+
"cxl_allowed": report.policy.cxl_allowed(),
620+
"mem_aes_256_xts": report.policy.mem_aes_256_xts(),
621+
"rapl_dis": report.policy.rapl_dis(),
622+
"ciphertext_hiding": report.policy.ciphertext_hiding(),
623+
"page_swap_disabled": report.policy.page_swap_disabled(),
624+
},
625+
"family_id": hex::encode(report.family_id),
626+
"image_id": hex::encode(report.image_id),
627+
"vmpl": report.vmpl,
628+
"sig_algo": report.sig_algo,
629+
"current_tcb": {
630+
"fmc": report.current_tcb.fmc,
631+
"bootloader": report.current_tcb.bootloader,
632+
"tee": report.current_tcb.tee,
633+
"snp": report.current_tcb.snp,
634+
"microcode": report.current_tcb.microcode,
635+
},
636+
"plat_info": {
637+
"smt_enabled": report.plat_info.smt_enabled(),
638+
"tsme_enabled": report.plat_info.tsme_enabled(),
639+
"ecc_enabled": report.plat_info.ecc_enabled(),
640+
"rapl_disabled": report.plat_info.rapl_disabled(),
641+
"ciphertext_hiding_enabled": report.plat_info.ciphertext_hiding_enabled(),
642+
"alias_check_complete": report.plat_info.alias_check_complete(),
643+
"tio_enabled": report.plat_info.tio_enabled(),
644+
},
645+
"key_info": {
646+
"author_key_en": report.key_info.author_key_en(),
647+
"mask_chip_key": report.key_info.mask_chip_key(),
648+
"signing_key": report.key_info.signing_key(),
649+
},
650+
"measurement": hex::encode(report.measurement),
651+
"report_data": hex::encode(report.report_data),
652+
"host_data": hex::encode(report.host_data),
653+
"id_key_digest": hex::encode(report.id_key_digest),
654+
"author_key_digest": hex::encode(report.author_key_digest),
655+
"report_id": hex::encode(report.report_id),
656+
"report_id_ma": hex::encode(report.report_id_ma),
657+
"reported_tcb": {
658+
"fmc": report.reported_tcb.fmc,
659+
"bootloader": report.reported_tcb.bootloader,
660+
"tee": report.reported_tcb.tee,
661+
"snp": report.reported_tcb.snp,
662+
"microcode": report.reported_tcb.microcode,
663+
},
664+
"cpu_id_fam_id": report.cpuid_fam_id,
665+
"cpu_id_mod_id": report.cpuid_mod_id,
666+
"cpu_id_step": report.cpuid_step,
667+
"chip_id": hex::encode(report.chip_id),
668+
"committed_tcb": {
669+
"fmc": report.committed_tcb.fmc,
670+
"bootloader": report.committed_tcb.bootloader,
671+
"tee": report.committed_tcb.tee,
672+
"snp": report.committed_tcb.snp,
673+
"microcode": report.committed_tcb.microcode,
674+
},
675+
"current": {
676+
"major": report.current.major,
677+
"minor": report.current.minor,
678+
"build": report.current.build,
679+
},
680+
"committed": {
681+
"major": report.committed.major,
682+
"minor": report.committed.minor,
683+
"build": report.committed.build,
684+
},
685+
"launch_tcb": {
686+
"fmc": report.launch_tcb.fmc,
687+
"bootloader": report.launch_tcb.bootloader,
688+
"tee": report.launch_tcb.tee,
689+
"snp": report.launch_tcb.snp,
690+
"microcode": report.launch_tcb.microcode,
691+
},
692+
"launch_mit_vector": report.launch_mit_vector,
693+
"current_mit_vector": report.current_mit_vector,
694+
}
624695
});
625696

626697
claims_map as TeeEvidenceParsedClaim
@@ -991,12 +1062,21 @@ mod tests {
9911062
let attestation_report = AttestationReport::from_bytes(VCEK_REPORT).unwrap();
9921063

9931064
// Generate parsed claims
994-
let claims = parse_tee_evidence(&attestation_report);
1065+
let claims = parse_tee_evidence(&attestation_report, ProcessorGeneration::Milan);
9951066

9961067
// Extract the three key fields that should be hex-encoded
997-
let measurement = claims.get("measurement").and_then(|v| v.as_str()).unwrap();
998-
let report_data = claims.get("report_data").and_then(|v| v.as_str()).unwrap();
999-
let init_data = claims.get("init_data").and_then(|v| v.as_str()).unwrap();
1068+
let measurement = claims
1069+
.pointer("/evidence/measurement")
1070+
.and_then(|v| v.as_str())
1071+
.unwrap();
1072+
let report_data = claims
1073+
.pointer("/report_data")
1074+
.and_then(|v| v.as_str())
1075+
.unwrap();
1076+
let init_data = claims
1077+
.pointer("/init_data")
1078+
.and_then(|v| v.as_str())
1079+
.unwrap();
10001080

10011081
// Verify they are valid hex strings by checking:
10021082
// 1. All characters are valid hex digits (0-9, a-f)

0 commit comments

Comments
 (0)