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_VERSION_1: u32 = 1;
41pub const IGVM_ATTEST_RESPONSE_VERSION_2: u32 = 2;
42pub const IGVM_ATTEST_RESPONSE_CURRENT_VERSION: u32 = IGVM_ATTEST_RESPONSE_VERSION_2;
43
44#[repr(C)]
48#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
49pub struct IgvmAttestRequest {
50 pub header: IgvmAttestRequestHeader,
52 pub attestation_report: [u8; ATTESTATION_REPORT_SIZE_MAX],
54 pub request_data: IgvmAttestRequestData,
56 }
61
62open_enum! {
63 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
65 pub enum IgvmAttestReportType: u32 {
66 INVALID_REPORT = 0,
68 VBS_VM_REPORT = 1,
70 SNP_VM_REPORT = 2,
72 TVM_REPORT = 3,
74 TDX_VM_REPORT = 4,
76 }
77}
78
79open_enum! {
80 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
82 pub enum IgvmAttestRequestType: u32 {
83 INVALID_REQUEST = 0,
85 KEY_RELEASE_REQUEST = 1,
87 AK_CERT_REQUEST = 2,
89 WRAPPED_KEY_REQUEST = 3,
91 }
92}
93
94open_enum! {
95 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
97 pub enum IgvmAttestHashType: u32 {
98 INVALID_HASH = 0,
100 SHA_256 = 1,
102 SHA_384 = 2,
104 SHA_512 = 3,
106 }
107}
108
109#[repr(C)]
111#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
112pub struct IgvmAttestRequestHeader {
113 pub signature: u32,
115 pub version: u32,
117 pub report_size: u32,
119 pub request_type: IgvmAttestRequestType,
121 pub status: u32,
123 pub reserved: [u32; 3],
125}
126
127impl IgvmAttestRequestHeader {
128 pub fn new(report_size: u32, request_type: IgvmAttestRequestType, status: u32) -> Self {
130 Self {
131 signature: ATTESTATION_SIGNATURE,
132 version: ATTESTATION_VERSION,
133 report_size,
134 request_type,
135 status,
136 reserved: [0u32; 3],
137 }
138 }
139}
140
141const IGVM_ATTEST_VERSION_CURRENT: u32 = 2;
142
143#[bitfield(u32)]
147#[derive(IntoBytes, FromBytes, Immutable, KnownLayout)]
148pub struct IgvmCapabilityBitMap {
149 pub error_code: bool,
150 pub retry: bool,
151 #[bits(30)]
152 _reserved: u32,
153}
154
155#[repr(C)]
157#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
158pub struct IgvmAttestRequestData {
159 pub data_size: u32,
161 pub version: u32,
163 pub report_type: IgvmAttestReportType,
165 pub report_data_hash_type: IgvmAttestHashType,
167 pub variable_data_size: u32,
169 pub capability_bitmap: IgvmCapabilityBitMap,
171}
172
173impl IgvmAttestRequestData {
174 pub fn new(
176 data_size: u32,
177 report_type: IgvmAttestReportType,
178 report_data_hash_type: IgvmAttestHashType,
179 variable_data_size: u32,
180 capability_bitmap: IgvmCapabilityBitMap,
181 ) -> Self {
182 Self {
183 data_size,
184 version: IGVM_ATTEST_VERSION_CURRENT,
185 report_type,
186 report_data_hash_type,
187 variable_data_size,
188 capability_bitmap,
189 }
190 }
191}
192
193#[bitfield(u32)]
196#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
197pub struct IgvmSignal {
198 pub retry: bool,
199 #[bits(31)]
200 _reserved: u32,
201}
202
203#[repr(C)]
205#[derive(Default, Debug, IntoBytes, FromBytes)]
206pub struct IgvmAttestCommonResponseHeader {
207 pub data_size: u32,
209 pub version: u32,
211}
212
213#[repr(C)]
215#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
216pub struct IgvmErrorInfo {
217 pub error_code: u32,
219 pub http_status_code: u32,
221 pub igvm_signal: IgvmSignal,
223 pub reserved: [u32; 3],
225}
226
227#[repr(C)]
229#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
230pub struct IgvmAttestKeyReleaseResponseHeader {
231 pub data_size: u32,
233 pub version: u32,
235 pub error_info: IgvmErrorInfo,
237}
238
239#[repr(C)]
242#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
243pub struct IgvmAttestWrappedKeyResponseHeader {
244 pub data_size: u32,
246 pub version: u32,
248 pub error_info: IgvmErrorInfo,
250}
251
252#[repr(C)]
254#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
255pub struct IgvmAttestAkCertResponseHeader {
256 pub data_size: u32,
258 pub version: u32,
260 pub error_info: IgvmErrorInfo,
262}
263
264pub mod runtime_claims {
267 use base64_serde::base64_serde_type;
268 use mesh::MeshPayload;
269 use serde::Deserialize;
270 use serde::Serialize;
271
272 base64_serde_type!(Base64Url, base64::engine::general_purpose::URL_SAFE_NO_PAD);
273
274 #[derive(Debug, Deserialize, Serialize)]
278 #[serde(rename_all = "kebab-case")]
279 pub struct RuntimeClaims {
280 pub keys: Vec<RsaJwk>,
282 pub vm_configuration: AttestationVmConfig,
284 #[serde(default, skip_serializing_if = "String::is_empty")]
286 pub user_data: String,
287 }
288
289 impl RuntimeClaims {
290 pub fn key_release_request_runtime_claims(
292 exponent: &[u8],
293 modulus: &[u8],
294 attestation_vm_config: &AttestationVmConfig,
295 ) -> Self {
296 let transfer_key_jwks = RsaJwk::get_transfer_key_jwks(exponent, modulus);
297 Self {
298 keys: transfer_key_jwks,
299 vm_configuration: attestation_vm_config.clone(),
300 user_data: "".to_string(),
301 }
302 }
303
304 pub fn ak_cert_runtime_claims(
306 ak_pub_exponent: &[u8],
307 ak_pub_modulus: &[u8],
308 ek_pub_exponent: &[u8],
309 ek_pub_modulus: &[u8],
310 attestation_vm_config: &AttestationVmConfig,
311 user_data: &[u8],
312 ) -> Self {
313 let tpm_jwks = RsaJwk::get_tpm_jwks(
314 ak_pub_exponent,
315 ak_pub_modulus,
316 ek_pub_exponent,
317 ek_pub_modulus,
318 );
319 Self {
320 keys: tpm_jwks,
321 vm_configuration: attestation_vm_config.clone(),
322 user_data: hex::encode(user_data),
323 }
324 }
325 }
326
327 #[derive(Debug, Deserialize, Serialize)]
329 pub struct RsaJwk {
330 pub kid: String,
332 pub key_ops: Vec<String>,
334 pub kty: String,
336 #[serde(with = "Base64Url")]
338 pub e: Vec<u8>,
339 #[serde(with = "Base64Url")]
341 pub n: Vec<u8>,
342 }
343
344 impl RsaJwk {
345 pub fn get_transfer_key_jwks(exponent: &[u8], modulus: &[u8]) -> Vec<RsaJwk> {
347 let jwk = RsaJwk {
348 kid: "HCLTransferKey".to_string(),
349 key_ops: vec!["encrypt".to_string()],
350 kty: "RSA".to_string(),
351 e: exponent.to_vec(),
352 n: modulus.to_vec(),
353 };
354
355 vec![jwk]
356 }
357
358 pub fn get_tpm_jwks(
360 ak_pub_exponent: &[u8],
361 ak_pub_modulus: &[u8],
362 ek_pub_exponent: &[u8],
363 ek_pub_modulus: &[u8],
364 ) -> Vec<RsaJwk> {
365 let ak_pub = RsaJwk {
366 kid: "HCLAkPub".to_string(),
367 key_ops: vec!["sign".to_string()],
368 kty: "RSA".to_string(),
369 e: ak_pub_exponent.to_vec(),
370 n: ak_pub_modulus.to_vec(),
371 };
372 let ek_pub = RsaJwk {
373 kid: "HCLEkPub".to_string(),
374 key_ops: vec!["encrypt".to_string()],
375 kty: "RSA".to_string(),
376 e: ek_pub_exponent.to_vec(),
377 n: ek_pub_modulus.to_vec(),
378 };
379
380 vec![ak_pub, ek_pub]
381 }
382 }
383
384 #[derive(Clone, Debug, Deserialize, Serialize, MeshPayload)]
386 #[serde(rename_all = "kebab-case")]
387 pub struct AttestationVmConfig {
388 #[serde(skip_serializing_if = "Option::is_none")]
390 pub current_time: Option<i64>,
391 pub root_cert_thumbprint: String,
393 pub console_enabled: bool,
395 pub secure_boot: bool,
397 pub tpm_enabled: bool,
399 pub tpm_persisted: bool,
401 pub filtered_vpci_devices_allowed: bool,
403 #[serde(rename = "vmUniqueId")]
405 pub vm_unique_id: String,
406 }
407}