1use bitfield_struct::bitfield;
9use open_enum::open_enum;
10use zerocopy::FromBytes;
11use zerocopy::Immutable;
12use zerocopy::IntoBytes;
13use zerocopy::KnownLayout;
14
15const ATTESTATION_VERSION: u32 = 2;
16const ATTESTATION_SIGNATURE: u32 = 0x414c4348; const ATTESTATION_REPORT_SIZE_MAX: usize = SNP_VM_REPORT_SIZE;
20
21pub const VBS_VM_REPORT_SIZE: usize = hvdef::vbs::VBS_REPORT_SIZE;
22pub const SNP_VM_REPORT_SIZE: usize = x86defs::snp::SNP_REPORT_SIZE;
23pub const TDX_VM_REPORT_SIZE: usize = x86defs::tdx::TDX_REPORT_SIZE;
24pub const TVM_REPORT_SIZE: usize = 0;
26
27const PAGE_SIZE: usize = 4096;
28
29pub const WRAPPED_KEY_RESPONSE_BUFFER_SIZE: usize = 16 * PAGE_SIZE;
32pub const KEY_RELEASE_RESPONSE_BUFFER_SIZE: usize = 16 * PAGE_SIZE;
35pub const AK_CERT_RESPONSE_BUFFER_SIZE: usize = PAGE_SIZE;
38
39pub const IGVM_ATTEST_RESPONSE_CURRENT_VERSION: IgvmAttestResponseVersion =
41 IgvmAttestResponseVersion::VERSION_2;
42
43open_enum! {
44 #[derive(Default, IntoBytes, Immutable, KnownLayout, FromBytes)]
46 pub enum IgvmAttestResponseVersion: u32 {
47 VERSION_1 = 1,
49 VERSION_2 = 2,
51 }
52}
53
54pub const IGVM_ATTEST_REQUEST_CURRENT_VERSION: IgvmAttestRequestVersion =
56 IgvmAttestRequestVersion::VERSION_2;
57
58open_enum! {
59 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
61 pub enum IgvmAttestRequestVersion: u32 {
62 VERSION_1 = 1,
64 VERSION_2 = 2,
66 }
67}
68
69#[repr(C)]
73#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
74pub struct IgvmAttestRequestBase {
75 pub header: IgvmAttestRequestHeader,
77 pub attestation_report: [u8; ATTESTATION_REPORT_SIZE_MAX],
79 pub request_data: IgvmAttestRequestData,
81 }
87
88open_enum! {
89 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
91 pub enum IgvmAttestReportType: u32 {
92 INVALID_REPORT = 0,
94 VBS_VM_REPORT = 1,
96 SNP_VM_REPORT = 2,
98 TVM_REPORT = 3,
100 TDX_VM_REPORT = 4,
102 CCA_VM_REPORT = 5,
104 }
105}
106
107open_enum! {
108 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
110 pub enum IgvmAttestRequestType: u32 {
111 INVALID_REQUEST = 0,
113 KEY_RELEASE_REQUEST = 1,
115 AK_CERT_REQUEST = 2,
117 WRAPPED_KEY_REQUEST = 3,
119 }
120}
121
122open_enum! {
123 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
125 pub enum IgvmAttestHashType: u32 {
126 INVALID_HASH = 0,
128 SHA_256 = 1,
130 SHA_384 = 2,
132 SHA_512 = 3,
134 }
135}
136
137#[repr(C)]
139#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
140pub struct IgvmAttestRequestHeader {
141 pub signature: u32,
143 pub version: u32,
145 pub report_size: u32,
147 pub request_type: IgvmAttestRequestType,
149 pub status: u32,
151 pub reserved: [u32; 3],
153}
154
155impl IgvmAttestRequestHeader {
156 pub fn new(report_size: u32, request_type: IgvmAttestRequestType, status: u32) -> Self {
158 Self {
159 signature: ATTESTATION_SIGNATURE,
160 version: ATTESTATION_VERSION,
161 report_size,
162 request_type,
163 status,
164 reserved: [0u32; 3],
165 }
166 }
167}
168
169#[bitfield(u32)]
183#[derive(IntoBytes, FromBytes, Immutable, KnownLayout)]
184pub struct IgvmCapabilityBitMap {
185 pub error_code: bool,
186 pub retry: bool,
187 pub skip_hw_unsealing: bool,
188 pub use_rsa_aes_key_wrap_384: bool,
189 pub corim_endorsement: bool,
190 #[bits(27)]
191 _reserved: u32,
192}
193
194#[repr(C)]
196#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
197pub struct IgvmAttestRequestData {
198 pub data_size: u32,
200 pub version: IgvmAttestRequestVersion,
202 pub report_type: IgvmAttestReportType,
204 pub report_data_hash_type: IgvmAttestHashType,
206 pub variable_data_size: u32,
208}
209
210impl IgvmAttestRequestData {
211 pub fn new(
213 version: IgvmAttestRequestVersion,
214 data_size: u32,
215 report_type: IgvmAttestReportType,
216 report_data_hash_type: IgvmAttestHashType,
217 variable_data_size: u32,
218 ) -> Self {
219 Self {
220 data_size,
221 version,
222 report_type,
223 report_data_hash_type,
224 variable_data_size,
225 }
226 }
227}
228
229#[repr(C)]
232#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
233pub struct IgvmAttestRequestDataExt {
234 pub capability_bitmap: IgvmCapabilityBitMap,
236}
237
238impl IgvmAttestRequestDataExt {
239 pub fn new(capability_bitmap: IgvmCapabilityBitMap) -> Self {
241 Self { capability_bitmap }
242 }
243}
244
245#[bitfield(u32)]
256#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
257pub struct IgvmSignal {
258 pub retry: bool,
259 pub skip_hw_unsealing: bool,
260 pub rsa_aes_key_wrap_384_used: bool,
261 pub corim_endorsement_requested: bool,
262 #[bits(28)]
263 _reserved: u32,
264}
265
266#[repr(C)]
268#[derive(Default, Debug, IntoBytes, FromBytes)]
269pub struct IgvmAttestCommonResponseHeader {
270 pub data_size: u32,
272 pub version: IgvmAttestResponseVersion,
274}
275
276#[repr(C)]
278#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
279pub struct IgvmErrorInfo {
280 pub error_code: u32,
282 pub http_status_code: u32,
284 pub igvm_signal: IgvmSignal,
286 pub reserved: [u32; 3],
288}
289
290#[repr(C)]
292#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
293pub struct IgvmAttestKeyReleaseResponseHeader {
294 pub data_size: u32,
296 pub version: IgvmAttestResponseVersion,
298 pub error_info: IgvmErrorInfo,
300}
301
302#[repr(C)]
305#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
306pub struct IgvmAttestWrappedKeyResponseHeader {
307 pub data_size: u32,
309 pub version: IgvmAttestResponseVersion,
311 pub error_info: IgvmErrorInfo,
313}
314
315#[repr(C)]
317#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
318pub struct IgvmAttestAkCertResponseHeader {
319 pub data_size: u32,
321 pub version: IgvmAttestResponseVersion,
323 pub error_info: IgvmErrorInfo,
325}
326
327pub mod runtime_claims {
330 use base64_serde::base64_serde_type;
331 use guid::Guid;
332 use mesh::MeshPayload;
333 use serde::Deserialize;
334 use serde::Serialize;
335
336 base64_serde_type!(Base64Url, base64::engine::general_purpose::URL_SAFE_NO_PAD);
337
338 #[derive(Debug, Deserialize, Serialize)]
342 #[serde(rename_all = "kebab-case")]
343 pub struct RuntimeClaims {
344 pub keys: Vec<RsaJwk>,
346 pub vm_configuration: AttestationVmConfig,
348 #[serde(default, skip_serializing_if = "String::is_empty")]
350 pub user_data: String,
351 }
352
353 impl RuntimeClaims {
354 pub fn key_release_request_runtime_claims(
356 exponent: &[u8],
357 modulus: &[u8],
358 attestation_vm_config: &AttestationVmConfig,
359 ) -> Self {
360 let transfer_key_jwks = RsaJwk::get_transfer_key_jwks(exponent, modulus);
361 Self {
362 keys: transfer_key_jwks,
363 vm_configuration: attestation_vm_config.clone(),
364 user_data: "".to_string(),
365 }
366 }
367
368 pub fn ak_cert_runtime_claims(
370 ak_pub_exponent: &[u8],
371 ak_pub_modulus: &[u8],
372 ek_pub_exponent: &[u8],
373 ek_pub_modulus: &[u8],
374 attestation_vm_config: &AttestationVmConfig,
375 user_data: &[u8],
376 ) -> Self {
377 let tpm_jwks = RsaJwk::get_tpm_jwks(
378 ak_pub_exponent,
379 ak_pub_modulus,
380 ek_pub_exponent,
381 ek_pub_modulus,
382 );
383 Self {
384 keys: tpm_jwks,
385 vm_configuration: attestation_vm_config.clone(),
386 user_data: hex::encode(user_data),
387 }
388 }
389 }
390
391 #[derive(Debug, Deserialize, Serialize)]
393 pub struct RsaJwk {
394 pub kid: String,
396 pub key_ops: Vec<String>,
398 pub kty: String,
400 #[serde(with = "Base64Url")]
402 pub e: Vec<u8>,
403 #[serde(with = "Base64Url")]
405 pub n: Vec<u8>,
406 }
407
408 impl RsaJwk {
409 pub fn get_transfer_key_jwks(exponent: &[u8], modulus: &[u8]) -> Vec<RsaJwk> {
411 let jwk = RsaJwk {
412 kid: "HCLTransferKey".to_string(),
413 key_ops: vec!["encrypt".to_string()],
414 kty: "RSA".to_string(),
415 e: exponent.to_vec(),
416 n: modulus.to_vec(),
417 };
418
419 vec![jwk]
420 }
421
422 pub fn get_tpm_jwks(
424 ak_pub_exponent: &[u8],
425 ak_pub_modulus: &[u8],
426 ek_pub_exponent: &[u8],
427 ek_pub_modulus: &[u8],
428 ) -> Vec<RsaJwk> {
429 let ak_pub = RsaJwk {
430 kid: "HCLAkPub".to_string(),
431 key_ops: vec!["sign".to_string()],
432 kty: "RSA".to_string(),
433 e: ak_pub_exponent.to_vec(),
434 n: ak_pub_modulus.to_vec(),
435 };
436 let ek_pub = RsaJwk {
437 kid: "HCLEkPub".to_string(),
438 key_ops: vec!["encrypt".to_string()],
439 kty: "RSA".to_string(),
440 e: ek_pub_exponent.to_vec(),
441 n: ek_pub_modulus.to_vec(),
442 };
443
444 vec![ak_pub, ek_pub]
445 }
446 }
447
448 #[derive(Clone, Debug, Deserialize, Serialize, MeshPayload)]
450 #[serde(rename_all = "kebab-case")]
451 pub struct VmgsProvisioner {
452 #[serde(with = "serde_helpers::as_string")]
454 pub id: Guid,
455 pub signer: String,
458 }
459
460 #[derive(Clone, Copy, Debug, Deserialize, Serialize, MeshPayload)]
462 pub enum HardwareSealingPolicy {
463 #[serde(rename = "none")]
464 None,
465 #[serde(rename = "hash")]
466 Hash,
467 #[serde(rename = "signer")]
468 Signer,
469 }
470
471 #[derive(Clone, Copy, Debug, Deserialize, Serialize, MeshPayload)]
473 pub enum AttestationTpmVersion {
474 #[serde(rename = "1.38")]
476 V138,
477 #[serde(rename = "185")]
479 V185,
480 }
481
482 #[derive(Clone, Debug, Deserialize, Serialize, MeshPayload)]
484 #[serde(rename_all = "kebab-case")]
485 pub struct AttestationVmConfig {
486 #[serde(skip_serializing_if = "Option::is_none")]
488 pub current_time: Option<i64>,
489 pub root_cert_thumbprint: String,
491 pub console_enabled: bool,
493 pub interactive_console_enabled: bool,
495 pub secure_boot: bool,
497 pub tpm_enabled: bool,
499 pub tpm_version: AttestationTpmVersion,
501 pub tpm_persisted: bool,
512 pub filtered_vpci_devices_allowed: bool,
514 #[serde(rename = "vmUniqueId")]
516 pub vm_unique_id: String,
517 #[serde(skip_serializing_if = "Option::is_none")]
519 pub vmgs_provisioner: Option<VmgsProvisioner>,
520 pub hardware_sealing_policy: HardwareSealingPolicy,
522 }
523
524 impl Default for AttestationVmConfig {
525 fn default() -> Self {
526 Self {
527 current_time: None,
528 root_cert_thumbprint: String::new(),
529 console_enabled: false,
530 interactive_console_enabled: false,
531 secure_boot: false,
532 tpm_enabled: true,
533 tpm_version: AttestationTpmVersion::V138,
534 tpm_persisted: true,
535 filtered_vpci_devices_allowed: false,
536 vm_unique_id: String::new(),
537 vmgs_provisioner: None,
538 hardware_sealing_policy: HardwareSealingPolicy::None,
539 }
540 }
541 }
542}