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)]
174#[derive(IntoBytes, FromBytes, Immutable, KnownLayout)]
175pub struct IgvmCapabilityBitMap {
176 pub error_code: bool,
177 pub retry: bool,
178 pub skip_hw_unsealing: bool,
179 #[bits(29)]
180 _reserved: u32,
181}
182
183#[repr(C)]
185#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
186pub struct IgvmAttestRequestData {
187 pub data_size: u32,
189 pub version: IgvmAttestRequestVersion,
191 pub report_type: IgvmAttestReportType,
193 pub report_data_hash_type: IgvmAttestHashType,
195 pub variable_data_size: u32,
197}
198
199impl IgvmAttestRequestData {
200 pub fn new(
202 version: IgvmAttestRequestVersion,
203 data_size: u32,
204 report_type: IgvmAttestReportType,
205 report_data_hash_type: IgvmAttestHashType,
206 variable_data_size: u32,
207 ) -> Self {
208 Self {
209 data_size,
210 version,
211 report_type,
212 report_data_hash_type,
213 variable_data_size,
214 }
215 }
216}
217
218#[repr(C)]
221#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
222pub struct IgvmAttestRequestDataExt {
223 pub capability_bitmap: IgvmCapabilityBitMap,
225}
226
227impl IgvmAttestRequestDataExt {
228 pub fn new(capability_bitmap: IgvmCapabilityBitMap) -> Self {
230 Self { capability_bitmap }
231 }
232}
233
234#[bitfield(u32)]
238#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
239pub struct IgvmSignal {
240 pub retry: bool,
241 pub skip_hw_unsealing: bool,
242 #[bits(30)]
243 _reserved: u32,
244}
245
246#[repr(C)]
248#[derive(Default, Debug, IntoBytes, FromBytes)]
249pub struct IgvmAttestCommonResponseHeader {
250 pub data_size: u32,
252 pub version: IgvmAttestResponseVersion,
254}
255
256#[repr(C)]
258#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
259pub struct IgvmErrorInfo {
260 pub error_code: u32,
262 pub http_status_code: u32,
264 pub igvm_signal: IgvmSignal,
266 pub reserved: [u32; 3],
268}
269
270#[repr(C)]
272#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
273pub struct IgvmAttestKeyReleaseResponseHeader {
274 pub data_size: u32,
276 pub version: IgvmAttestResponseVersion,
278 pub error_info: IgvmErrorInfo,
280}
281
282#[repr(C)]
285#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
286pub struct IgvmAttestWrappedKeyResponseHeader {
287 pub data_size: u32,
289 pub version: IgvmAttestResponseVersion,
291 pub error_info: IgvmErrorInfo,
293}
294
295#[repr(C)]
297#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
298pub struct IgvmAttestAkCertResponseHeader {
299 pub data_size: u32,
301 pub version: IgvmAttestResponseVersion,
303 pub error_info: IgvmErrorInfo,
305}
306
307pub mod runtime_claims {
310 use base64_serde::base64_serde_type;
311 use guid::Guid;
312 use mesh::MeshPayload;
313 use serde::Deserialize;
314 use serde::Serialize;
315
316 base64_serde_type!(Base64Url, base64::engine::general_purpose::URL_SAFE_NO_PAD);
317
318 #[derive(Debug, Deserialize, Serialize)]
322 #[serde(rename_all = "kebab-case")]
323 pub struct RuntimeClaims {
324 pub keys: Vec<RsaJwk>,
326 pub vm_configuration: AttestationVmConfig,
328 #[serde(default, skip_serializing_if = "String::is_empty")]
330 pub user_data: String,
331 }
332
333 impl RuntimeClaims {
334 pub fn key_release_request_runtime_claims(
336 exponent: &[u8],
337 modulus: &[u8],
338 attestation_vm_config: &AttestationVmConfig,
339 ) -> Self {
340 let transfer_key_jwks = RsaJwk::get_transfer_key_jwks(exponent, modulus);
341 Self {
342 keys: transfer_key_jwks,
343 vm_configuration: attestation_vm_config.clone(),
344 user_data: "".to_string(),
345 }
346 }
347
348 pub fn ak_cert_runtime_claims(
350 ak_pub_exponent: &[u8],
351 ak_pub_modulus: &[u8],
352 ek_pub_exponent: &[u8],
353 ek_pub_modulus: &[u8],
354 attestation_vm_config: &AttestationVmConfig,
355 user_data: &[u8],
356 ) -> Self {
357 let tpm_jwks = RsaJwk::get_tpm_jwks(
358 ak_pub_exponent,
359 ak_pub_modulus,
360 ek_pub_exponent,
361 ek_pub_modulus,
362 );
363 Self {
364 keys: tpm_jwks,
365 vm_configuration: attestation_vm_config.clone(),
366 user_data: hex::encode(user_data),
367 }
368 }
369 }
370
371 #[derive(Debug, Deserialize, Serialize)]
373 pub struct RsaJwk {
374 pub kid: String,
376 pub key_ops: Vec<String>,
378 pub kty: String,
380 #[serde(with = "Base64Url")]
382 pub e: Vec<u8>,
383 #[serde(with = "Base64Url")]
385 pub n: Vec<u8>,
386 }
387
388 impl RsaJwk {
389 pub fn get_transfer_key_jwks(exponent: &[u8], modulus: &[u8]) -> Vec<RsaJwk> {
391 let jwk = RsaJwk {
392 kid: "HCLTransferKey".to_string(),
393 key_ops: vec!["encrypt".to_string()],
394 kty: "RSA".to_string(),
395 e: exponent.to_vec(),
396 n: modulus.to_vec(),
397 };
398
399 vec![jwk]
400 }
401
402 pub fn get_tpm_jwks(
404 ak_pub_exponent: &[u8],
405 ak_pub_modulus: &[u8],
406 ek_pub_exponent: &[u8],
407 ek_pub_modulus: &[u8],
408 ) -> Vec<RsaJwk> {
409 let ak_pub = RsaJwk {
410 kid: "HCLAkPub".to_string(),
411 key_ops: vec!["sign".to_string()],
412 kty: "RSA".to_string(),
413 e: ak_pub_exponent.to_vec(),
414 n: ak_pub_modulus.to_vec(),
415 };
416 let ek_pub = RsaJwk {
417 kid: "HCLEkPub".to_string(),
418 key_ops: vec!["encrypt".to_string()],
419 kty: "RSA".to_string(),
420 e: ek_pub_exponent.to_vec(),
421 n: ek_pub_modulus.to_vec(),
422 };
423
424 vec![ak_pub, ek_pub]
425 }
426 }
427
428 #[derive(Clone, Debug, Deserialize, Serialize, MeshPayload)]
430 #[serde(rename_all = "kebab-case")]
431 pub struct VmgsProvisioner {
432 #[serde(with = "serde_helpers::as_string")]
434 pub id: Guid,
435 pub signer: String,
438 }
439
440 #[derive(Clone, Copy, Debug, Deserialize, Serialize, MeshPayload)]
442 pub enum HardwareSealingPolicy {
443 #[serde(rename = "none")]
444 None,
445 #[serde(rename = "hash")]
446 Hash,
447 #[serde(rename = "signer")]
448 Signer,
449 }
450
451 #[derive(Clone, Debug, Deserialize, Serialize, MeshPayload)]
453 #[serde(rename_all = "kebab-case")]
454 pub struct AttestationVmConfig {
455 #[serde(skip_serializing_if = "Option::is_none")]
457 pub current_time: Option<i64>,
458 pub root_cert_thumbprint: String,
460 pub console_enabled: bool,
462 pub interactive_console_enabled: bool,
464 pub secure_boot: bool,
466 pub tpm_enabled: bool,
468 pub tpm_persisted: bool,
479 pub filtered_vpci_devices_allowed: bool,
481 #[serde(rename = "vmUniqueId")]
483 pub vm_unique_id: String,
484 #[serde(skip_serializing_if = "Option::is_none")]
486 pub vmgs_provisioner: Option<VmgsProvisioner>,
487 pub hardware_sealing_policy: HardwareSealingPolicy,
489 }
490
491 impl Default for AttestationVmConfig {
492 fn default() -> Self {
493 Self {
494 current_time: None,
495 root_cert_thumbprint: String::new(),
496 console_enabled: false,
497 interactive_console_enabled: false,
498 secure_boot: false,
499 tpm_enabled: true,
500 tpm_persisted: true,
501 filtered_vpci_devices_allowed: false,
502 vm_unique_id: String::new(),
503 vmgs_provisioner: None,
504 hardware_sealing_policy: HardwareSealingPolicy::None,
505 }
506 }
507 }
508}