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)]
171#[derive(IntoBytes, FromBytes, Immutable, KnownLayout)]
172pub struct IgvmCapabilityBitMap {
173 pub error_code: bool,
174 pub retry: bool,
175 #[bits(30)]
176 _reserved: u32,
177}
178
179#[repr(C)]
181#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
182pub struct IgvmAttestRequestData {
183 pub data_size: u32,
185 pub version: IgvmAttestRequestVersion,
187 pub report_type: IgvmAttestReportType,
189 pub report_data_hash_type: IgvmAttestHashType,
191 pub variable_data_size: u32,
193}
194
195impl IgvmAttestRequestData {
196 pub fn new(
198 version: IgvmAttestRequestVersion,
199 data_size: u32,
200 report_type: IgvmAttestReportType,
201 report_data_hash_type: IgvmAttestHashType,
202 variable_data_size: u32,
203 ) -> Self {
204 Self {
205 data_size,
206 version,
207 report_type,
208 report_data_hash_type,
209 variable_data_size,
210 }
211 }
212}
213
214#[repr(C)]
217#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
218pub struct IgvmAttestRequestDataExt {
219 pub capability_bitmap: IgvmCapabilityBitMap,
221}
222
223impl IgvmAttestRequestDataExt {
224 pub fn new(capability_bitmap: IgvmCapabilityBitMap) -> Self {
226 Self { capability_bitmap }
227 }
228}
229
230#[bitfield(u32)]
233#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
234pub struct IgvmSignal {
235 pub retry: bool,
236 #[bits(31)]
237 _reserved: u32,
238}
239
240#[repr(C)]
242#[derive(Default, Debug, IntoBytes, FromBytes)]
243pub struct IgvmAttestCommonResponseHeader {
244 pub data_size: u32,
246 pub version: IgvmAttestResponseVersion,
248}
249
250#[repr(C)]
252#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
253pub struct IgvmErrorInfo {
254 pub error_code: u32,
256 pub http_status_code: u32,
258 pub igvm_signal: IgvmSignal,
260 pub reserved: [u32; 3],
262}
263
264#[repr(C)]
266#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
267pub struct IgvmAttestKeyReleaseResponseHeader {
268 pub data_size: u32,
270 pub version: IgvmAttestResponseVersion,
272 pub error_info: IgvmErrorInfo,
274}
275
276#[repr(C)]
279#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
280pub struct IgvmAttestWrappedKeyResponseHeader {
281 pub data_size: u32,
283 pub version: IgvmAttestResponseVersion,
285 pub error_info: IgvmErrorInfo,
287}
288
289#[repr(C)]
291#[derive(Default, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
292pub struct IgvmAttestAkCertResponseHeader {
293 pub data_size: u32,
295 pub version: IgvmAttestResponseVersion,
297 pub error_info: IgvmErrorInfo,
299}
300
301pub mod runtime_claims {
304 use base64_serde::base64_serde_type;
305 use mesh::MeshPayload;
306 use serde::Deserialize;
307 use serde::Serialize;
308
309 base64_serde_type!(Base64Url, base64::engine::general_purpose::URL_SAFE_NO_PAD);
310
311 #[derive(Debug, Deserialize, Serialize)]
315 #[serde(rename_all = "kebab-case")]
316 pub struct RuntimeClaims {
317 pub keys: Vec<RsaJwk>,
319 pub vm_configuration: AttestationVmConfig,
321 #[serde(default, skip_serializing_if = "String::is_empty")]
323 pub user_data: String,
324 }
325
326 impl RuntimeClaims {
327 pub fn key_release_request_runtime_claims(
329 exponent: &[u8],
330 modulus: &[u8],
331 attestation_vm_config: &AttestationVmConfig,
332 ) -> Self {
333 let transfer_key_jwks = RsaJwk::get_transfer_key_jwks(exponent, modulus);
334 Self {
335 keys: transfer_key_jwks,
336 vm_configuration: attestation_vm_config.clone(),
337 user_data: "".to_string(),
338 }
339 }
340
341 pub fn ak_cert_runtime_claims(
343 ak_pub_exponent: &[u8],
344 ak_pub_modulus: &[u8],
345 ek_pub_exponent: &[u8],
346 ek_pub_modulus: &[u8],
347 attestation_vm_config: &AttestationVmConfig,
348 user_data: &[u8],
349 ) -> Self {
350 let tpm_jwks = RsaJwk::get_tpm_jwks(
351 ak_pub_exponent,
352 ak_pub_modulus,
353 ek_pub_exponent,
354 ek_pub_modulus,
355 );
356 Self {
357 keys: tpm_jwks,
358 vm_configuration: attestation_vm_config.clone(),
359 user_data: hex::encode(user_data),
360 }
361 }
362 }
363
364 #[derive(Debug, Deserialize, Serialize)]
366 pub struct RsaJwk {
367 pub kid: String,
369 pub key_ops: Vec<String>,
371 pub kty: String,
373 #[serde(with = "Base64Url")]
375 pub e: Vec<u8>,
376 #[serde(with = "Base64Url")]
378 pub n: Vec<u8>,
379 }
380
381 impl RsaJwk {
382 pub fn get_transfer_key_jwks(exponent: &[u8], modulus: &[u8]) -> Vec<RsaJwk> {
384 let jwk = RsaJwk {
385 kid: "HCLTransferKey".to_string(),
386 key_ops: vec!["encrypt".to_string()],
387 kty: "RSA".to_string(),
388 e: exponent.to_vec(),
389 n: modulus.to_vec(),
390 };
391
392 vec![jwk]
393 }
394
395 pub fn get_tpm_jwks(
397 ak_pub_exponent: &[u8],
398 ak_pub_modulus: &[u8],
399 ek_pub_exponent: &[u8],
400 ek_pub_modulus: &[u8],
401 ) -> Vec<RsaJwk> {
402 let ak_pub = RsaJwk {
403 kid: "HCLAkPub".to_string(),
404 key_ops: vec!["sign".to_string()],
405 kty: "RSA".to_string(),
406 e: ak_pub_exponent.to_vec(),
407 n: ak_pub_modulus.to_vec(),
408 };
409 let ek_pub = RsaJwk {
410 kid: "HCLEkPub".to_string(),
411 key_ops: vec!["encrypt".to_string()],
412 kty: "RSA".to_string(),
413 e: ek_pub_exponent.to_vec(),
414 n: ek_pub_modulus.to_vec(),
415 };
416
417 vec![ak_pub, ek_pub]
418 }
419 }
420
421 #[derive(Clone, Debug, Deserialize, Serialize, MeshPayload)]
423 #[serde(rename_all = "kebab-case")]
424 pub struct AttestationVmConfig {
425 #[serde(skip_serializing_if = "Option::is_none")]
427 pub current_time: Option<i64>,
428 pub root_cert_thumbprint: String,
430 pub console_enabled: bool,
432 pub secure_boot: bool,
434 pub tpm_enabled: bool,
436 pub tpm_persisted: bool,
438 pub filtered_vpci_devices_allowed: bool,
440 #[serde(rename = "vmUniqueId")]
442 pub vm_unique_id: String,
443 }
444}