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 }
103}
104
105open_enum! {
106 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
108 pub enum IgvmAttestRequestType: u32 {
109 INVALID_REQUEST = 0,
111 KEY_RELEASE_REQUEST = 1,
113 AK_CERT_REQUEST = 2,
115 WRAPPED_KEY_REQUEST = 3,
117 }
118}
119
120open_enum! {
121 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
123 pub enum IgvmAttestHashType: u32 {
124 INVALID_HASH = 0,
126 SHA_256 = 1,
128 SHA_384 = 2,
130 SHA_512 = 3,
132 }
133}
134
135#[repr(C)]
137#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
138pub struct IgvmAttestRequestHeader {
139 pub signature: u32,
141 pub version: u32,
143 pub report_size: u32,
145 pub request_type: IgvmAttestRequestType,
147 pub status: u32,
149 pub reserved: [u32; 3],
151}
152
153impl IgvmAttestRequestHeader {
154 pub fn new(report_size: u32, request_type: IgvmAttestRequestType, status: u32) -> Self {
156 Self {
157 signature: ATTESTATION_SIGNATURE,
158 version: ATTESTATION_VERSION,
159 report_size,
160 request_type,
161 status,
162 reserved: [0u32; 3],
163 }
164 }
165}
166
167#[bitfield(u32)]
172#[derive(IntoBytes, FromBytes, Immutable, KnownLayout)]
173pub struct IgvmCapabilityBitMap {
174 pub error_code: bool,
175 pub retry: bool,
176 pub skip_hw_unsealing: bool,
177 #[bits(29)]
178 _reserved: u32,
179}
180
181#[repr(C)]
183#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
184pub struct IgvmAttestRequestData {
185 pub data_size: u32,
187 pub version: IgvmAttestRequestVersion,
189 pub report_type: IgvmAttestReportType,
191 pub report_data_hash_type: IgvmAttestHashType,
193 pub variable_data_size: u32,
195}
196
197impl IgvmAttestRequestData {
198 pub fn new(
200 version: IgvmAttestRequestVersion,
201 data_size: u32,
202 report_type: IgvmAttestReportType,
203 report_data_hash_type: IgvmAttestHashType,
204 variable_data_size: u32,
205 ) -> Self {
206 Self {
207 data_size,
208 version,
209 report_type,
210 report_data_hash_type,
211 variable_data_size,
212 }
213 }
214}
215
216#[repr(C)]
219#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
220pub struct IgvmAttestRequestDataExt {
221 pub capability_bitmap: IgvmCapabilityBitMap,
223}
224
225impl IgvmAttestRequestDataExt {
226 pub fn new(capability_bitmap: IgvmCapabilityBitMap) -> Self {
228 Self { capability_bitmap }
229 }
230}
231
232#[bitfield(u32)]
236#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
237pub struct IgvmSignal {
238 pub retry: bool,
239 pub skip_hw_unsealing: bool,
240 #[bits(30)]
241 _reserved: u32,
242}
243
244#[repr(C)]
246#[derive(Default, Debug, IntoBytes, FromBytes)]
247pub struct IgvmAttestCommonResponseHeader {
248 pub data_size: u32,
250 pub version: IgvmAttestResponseVersion,
252}
253
254#[repr(C)]
256#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
257pub struct IgvmErrorInfo {
258 pub error_code: u32,
260 pub http_status_code: u32,
262 pub igvm_signal: IgvmSignal,
264 pub reserved: [u32; 3],
266}
267
268#[repr(C)]
270#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
271pub struct IgvmAttestKeyReleaseResponseHeader {
272 pub data_size: u32,
274 pub version: IgvmAttestResponseVersion,
276 pub error_info: IgvmErrorInfo,
278}
279
280#[repr(C)]
283#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
284pub struct IgvmAttestWrappedKeyResponseHeader {
285 pub data_size: u32,
287 pub version: IgvmAttestResponseVersion,
289 pub error_info: IgvmErrorInfo,
291}
292
293#[repr(C)]
295#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
296pub struct IgvmAttestAkCertResponseHeader {
297 pub data_size: u32,
299 pub version: IgvmAttestResponseVersion,
301 pub error_info: IgvmErrorInfo,
303}
304
305pub mod runtime_claims {
308 use base64_serde::base64_serde_type;
309 use mesh::MeshPayload;
310 use serde::Deserialize;
311 use serde::Serialize;
312
313 base64_serde_type!(Base64Url, base64::engine::general_purpose::URL_SAFE_NO_PAD);
314
315 #[derive(Debug, Deserialize, Serialize)]
319 #[serde(rename_all = "kebab-case")]
320 pub struct RuntimeClaims {
321 pub keys: Vec<RsaJwk>,
323 pub vm_configuration: AttestationVmConfig,
325 #[serde(default, skip_serializing_if = "String::is_empty")]
327 pub user_data: String,
328 }
329
330 impl RuntimeClaims {
331 pub fn key_release_request_runtime_claims(
333 exponent: &[u8],
334 modulus: &[u8],
335 attestation_vm_config: &AttestationVmConfig,
336 ) -> Self {
337 let transfer_key_jwks = RsaJwk::get_transfer_key_jwks(exponent, modulus);
338 Self {
339 keys: transfer_key_jwks,
340 vm_configuration: attestation_vm_config.clone(),
341 user_data: "".to_string(),
342 }
343 }
344
345 pub fn ak_cert_runtime_claims(
347 ak_pub_exponent: &[u8],
348 ak_pub_modulus: &[u8],
349 ek_pub_exponent: &[u8],
350 ek_pub_modulus: &[u8],
351 attestation_vm_config: &AttestationVmConfig,
352 user_data: &[u8],
353 ) -> Self {
354 let tpm_jwks = RsaJwk::get_tpm_jwks(
355 ak_pub_exponent,
356 ak_pub_modulus,
357 ek_pub_exponent,
358 ek_pub_modulus,
359 );
360 Self {
361 keys: tpm_jwks,
362 vm_configuration: attestation_vm_config.clone(),
363 user_data: hex::encode(user_data),
364 }
365 }
366 }
367
368 #[derive(Debug, Deserialize, Serialize)]
370 pub struct RsaJwk {
371 pub kid: String,
373 pub key_ops: Vec<String>,
375 pub kty: String,
377 #[serde(with = "Base64Url")]
379 pub e: Vec<u8>,
380 #[serde(with = "Base64Url")]
382 pub n: Vec<u8>,
383 }
384
385 impl RsaJwk {
386 pub fn get_transfer_key_jwks(exponent: &[u8], modulus: &[u8]) -> Vec<RsaJwk> {
388 let jwk = RsaJwk {
389 kid: "HCLTransferKey".to_string(),
390 key_ops: vec!["encrypt".to_string()],
391 kty: "RSA".to_string(),
392 e: exponent.to_vec(),
393 n: modulus.to_vec(),
394 };
395
396 vec![jwk]
397 }
398
399 pub fn get_tpm_jwks(
401 ak_pub_exponent: &[u8],
402 ak_pub_modulus: &[u8],
403 ek_pub_exponent: &[u8],
404 ek_pub_modulus: &[u8],
405 ) -> Vec<RsaJwk> {
406 let ak_pub = RsaJwk {
407 kid: "HCLAkPub".to_string(),
408 key_ops: vec!["sign".to_string()],
409 kty: "RSA".to_string(),
410 e: ak_pub_exponent.to_vec(),
411 n: ak_pub_modulus.to_vec(),
412 };
413 let ek_pub = RsaJwk {
414 kid: "HCLEkPub".to_string(),
415 key_ops: vec!["encrypt".to_string()],
416 kty: "RSA".to_string(),
417 e: ek_pub_exponent.to_vec(),
418 n: ek_pub_modulus.to_vec(),
419 };
420
421 vec![ak_pub, ek_pub]
422 }
423 }
424
425 #[derive(Clone, Debug, Deserialize, Serialize, MeshPayload)]
427 #[serde(rename_all = "kebab-case")]
428 pub struct AttestationVmConfig {
429 #[serde(skip_serializing_if = "Option::is_none")]
431 pub current_time: Option<i64>,
432 pub root_cert_thumbprint: String,
434 pub console_enabled: bool,
436 pub interactive_console_enabled: bool,
438 pub secure_boot: bool,
440 pub tpm_enabled: bool,
442 pub tpm_persisted: bool,
444 pub filtered_vpci_devices_allowed: bool,
446 #[serde(rename = "vmUniqueId")]
448 pub vm_unique_id: String,
449 }
450}