1use base64_serde::base64_serde_type;
9use openhcl_attestation_protocol::igvm_attest::get::IGVM_ATTEST_REQUEST_CURRENT_VERSION;
10use openhcl_attestation_protocol::igvm_attest::get::IGVM_ATTEST_RESPONSE_CURRENT_VERSION;
11use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestCommonResponseHeader;
12use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestHashType;
13use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestReportType;
14use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestType;
15use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestVersion;
16use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestResponseVersion;
17use openhcl_attestation_protocol::igvm_attest::get::IgvmCapabilityBitMap;
18use openhcl_attestation_protocol::igvm_attest::get::IgvmErrorInfo;
19use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::AttestationVmConfig;
20use tee_call::TeeType;
21use thiserror::Error;
22use zerocopy::FromBytes;
23use zerocopy::FromZeros;
24use zerocopy::IntoBytes;
25
26pub mod ak_cert;
27pub mod key_release;
28pub mod wrapped_key;
29
30base64_serde_type!(Base64Url, base64::engine::general_purpose::URL_SAFE_NO_PAD);
31
32#[expect(missing_docs)] #[derive(Debug, Error)]
34pub enum Error {
35 #[error(
36 "the size of the attestation report {report_size} is invalid, expected {expected_size}"
37 )]
38 InvalidAttestationReportSize {
39 report_size: usize,
40 expected_size: usize,
41 },
42 #[error("the size of the attestation response {response_size} is too small to parse")]
43 ResponseSizeTooSmall { response_size: usize },
44 #[error(
45 "the header of the attestation response (size {response_size}) is not in correct format"
46 )]
47 ResponseHeaderInvalidFormat { response_size: usize },
48 #[error(
49 "response size {specified_size} specified in the header not match the actual size {size}"
50 )]
51 ResponseSizeMismatch { size: usize, specified_size: usize },
52 #[error("response header version {version:?} larger than current version {latest_version:?}")]
53 InvalidResponseHeaderVersion {
54 version: IgvmAttestResponseVersion,
55 latest_version: IgvmAttestResponseVersion,
56 },
57 #[error(
58 "attest failed ({igvm_error_code}-{http_status_code}), retry recommendation ({retry_signal}), skip hw unsealing recommendation ({skip_hw_unsealing_signal})"
59 )]
60 Attestation {
61 igvm_error_code: u32,
62 http_status_code: u32,
63 retry_signal: bool,
64 skip_hw_unsealing_signal: bool,
65 },
66}
67
68pub enum ReportType {
70 Vbs,
72 Snp,
74 Tdx,
76 Cca,
78 Tvm,
80}
81
82impl ReportType {
83 fn to_external_type(&self) -> IgvmAttestReportType {
85 match self {
86 Self::Vbs => IgvmAttestReportType::VBS_VM_REPORT,
87 Self::Snp => IgvmAttestReportType::SNP_VM_REPORT,
88 Self::Tdx => IgvmAttestReportType::TDX_VM_REPORT,
89 Self::Cca => IgvmAttestReportType::CCA_VM_REPORT,
90 Self::Tvm => IgvmAttestReportType::TVM_REPORT,
91 }
92 }
93}
94
95pub struct IgvmAttestRequestHelper {
97 request_type: IgvmAttestRequestType,
99 report_type: ReportType,
101 runtime_claims: Vec<u8>,
103 has_ak_cert_metadata: bool,
105 runtime_claims_hash: [u8; tee_call::REPORT_DATA_SIZE],
108 hash_type: IgvmAttestHashType,
110}
111
112impl IgvmAttestRequestHelper {
113 pub fn prepare_key_release_request(
115 tee_type: TeeType,
116 rsa_exponent: &[u8],
117 rsa_modulus: &[u8],
118 host_time: i64,
119 attestation_vm_config: &AttestationVmConfig,
120 ) -> Self {
121 let report_type = match tee_type {
122 TeeType::Snp => ReportType::Snp,
123 TeeType::Tdx => ReportType::Tdx,
124 TeeType::Cca => ReportType::Cca,
125 TeeType::Vbs => ReportType::Vbs,
126 };
127
128 let attestation_vm_config =
129 attestation_vm_config_with_time(attestation_vm_config, host_time);
130 let runtime_claims =
131 openhcl_attestation_protocol::igvm_attest::get::runtime_claims::RuntimeClaims::key_release_request_runtime_claims(rsa_exponent, rsa_modulus, &attestation_vm_config);
132 let runtime_claims = runtime_claims_to_bytes(&runtime_claims);
133
134 let hash_type = IgvmAttestHashType::SHA_256;
135 let hash = crypto::sha_256::sha_256(runtime_claims.as_bytes());
136 let mut runtime_claims_hash = [0u8; tee_call::REPORT_DATA_SIZE];
137 runtime_claims_hash[0..hash.len()].copy_from_slice(&hash);
138
139 Self {
140 request_type: IgvmAttestRequestType::KEY_RELEASE_REQUEST,
141 report_type,
142 runtime_claims,
143 has_ak_cert_metadata: false,
144 runtime_claims_hash,
145 hash_type,
146 }
147 }
148
149 pub fn prepare_ak_cert_request(
151 tee_type: Option<TeeType>,
152 ak_pub_exponent: &[u8],
153 ak_pub_modulus: &[u8],
154 ek_pub_exponent: &[u8],
155 ek_pub_modulus: &[u8],
156 attestation_vm_config: &AttestationVmConfig,
157 guest_input: &[u8],
158 akcert: Option<
159 openhcl_attestation_protocol::igvm_attest::get::runtime_claims::AkCertMetadata,
160 >,
161 ) -> Self {
162 let report_type = match tee_type {
163 Some(TeeType::Snp) => ReportType::Snp,
164 Some(TeeType::Tdx) => ReportType::Tdx,
165 Some(TeeType::Cca) => ReportType::Cca,
166 Some(TeeType::Vbs) => ReportType::Vbs,
167 None => ReportType::Tvm,
168 };
169
170 let mut runtime_claims =
171 openhcl_attestation_protocol::igvm_attest::get::runtime_claims::RuntimeClaims::ak_cert_runtime_claims(
172 ak_pub_exponent,
173 ak_pub_modulus,
174 ek_pub_exponent,
175 ek_pub_modulus,
176 attestation_vm_config,
177 guest_input,
178 );
179 runtime_claims.akcert = akcert;
180
181 let mut serialized_claims = runtime_claims_to_bytes(&runtime_claims);
182 if runtime_claims.akcert.is_some()
183 && request_size(IGVM_ATTEST_REQUEST_CURRENT_VERSION, serialized_claims.len())
184 > get_protocol::IGVM_ATTEST_MSG_REQ_REPORT_MAX_SIZE
185 {
186 runtime_claims.akcert = None;
190 serialized_claims = runtime_claims_to_bytes(&runtime_claims);
191 }
192 let has_ak_cert_metadata = runtime_claims.akcert.is_some();
193 let runtime_claims = serialized_claims;
194
195 let hash_type = IgvmAttestHashType::SHA_256;
196 let hash = crypto::sha_256::sha_256(runtime_claims.as_bytes());
197 let mut runtime_claims_hash = [0u8; tee_call::REPORT_DATA_SIZE];
198 runtime_claims_hash[0..hash.len()].copy_from_slice(&hash);
199
200 Self {
201 request_type: IgvmAttestRequestType::AK_CERT_REQUEST,
202 report_type,
203 runtime_claims,
204 has_ak_cert_metadata,
205 runtime_claims_hash,
206 hash_type,
207 }
208 }
209
210 pub fn has_ak_cert_metadata(&self) -> bool {
212 self.has_ak_cert_metadata
213 }
214
215 pub fn request_size(&self, version: IgvmAttestRequestVersion) -> usize {
217 request_size(version, self.runtime_claims.len())
218 }
219
220 pub fn get_runtime_claims_hash(&self) -> &[u8; tee_call::REPORT_DATA_SIZE] {
222 &self.runtime_claims_hash
223 }
224
225 pub fn set_request_type(&mut self, request_type: IgvmAttestRequestType) {
227 self.request_type = request_type
228 }
229
230 pub fn create_request(
232 &self,
233 version: IgvmAttestRequestVersion,
234 attestation_report: &[u8],
235 ) -> Result<Vec<u8>, Error> {
236 create_request(
237 version,
238 self.request_type,
239 &self.runtime_claims,
240 attestation_report,
241 &self.report_type,
242 self.hash_type,
243 )
244 }
245}
246
247pub fn parse_response_header(response: &[u8]) -> Result<IgvmAttestCommonResponseHeader, Error> {
249 let header = IgvmAttestCommonResponseHeader::read_from_prefix(response)
252 .map_err(|_| Error::ResponseSizeTooSmall {
253 response_size: response.len(),
254 })?
255 .0; if header.data_size as usize > response.len() {
259 Err(Error::ResponseSizeMismatch {
260 size: response.len(),
261 specified_size: header.data_size as usize,
262 })?
263 }
264 let response = &response[..header.data_size as usize];
265 if response.len() < size_of::<IgvmAttestCommonResponseHeader>() {
266 return Err(Error::ResponseSizeTooSmall {
267 response_size: response.len(),
268 });
269 }
270 if header.version > IGVM_ATTEST_RESPONSE_CURRENT_VERSION {
271 Err(Error::InvalidResponseHeaderVersion {
272 version: header.version,
273 latest_version: IGVM_ATTEST_RESPONSE_CURRENT_VERSION,
274 })?
275 }
276
277 if header.version >= IgvmAttestResponseVersion::VERSION_2 {
279 let igvm_error_info = IgvmErrorInfo::read_from_prefix(
281 &response[size_of::<IgvmAttestCommonResponseHeader>()..],
282 )
283 .map_err(|_| Error::ResponseHeaderInvalidFormat {
284 response_size: response.len(),
285 })?
286 .0; if 0 != igvm_error_info.error_code {
289 Err(Error::Attestation {
290 igvm_error_code: igvm_error_info.error_code,
291 http_status_code: igvm_error_info.http_status_code,
292 retry_signal: igvm_error_info.igvm_signal.retry(),
293 skip_hw_unsealing_signal: igvm_error_info.igvm_signal.skip_hw_unsealing(),
294 })?
295 }
296 }
297 Ok(IgvmAttestCommonResponseHeader {
298 data_size: header.data_size,
299 version: header.version,
300 })
301}
302
303fn request_size(version: IgvmAttestRequestVersion, runtime_claims_len: usize) -> usize {
305 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestBase;
306 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestDataExt;
307
308 let extension_size = if version >= IgvmAttestRequestVersion::VERSION_2 {
309 size_of::<IgvmAttestRequestDataExt>()
310 } else {
311 0
312 };
313 size_of::<IgvmAttestRequestBase>() + extension_size + runtime_claims_len
314}
315
316fn create_request(
320 version: IgvmAttestRequestVersion,
321 request_type: IgvmAttestRequestType,
322 runtime_claims: &[u8],
323 attestation_report: &[u8],
324 report_type: &ReportType,
325 hash_type: IgvmAttestHashType,
326) -> Result<Vec<u8>, Error> {
327 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestBase;
328 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestData;
329 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestDataExt;
330 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestHeader;
331
332 let expected_report_size = get_report_size(report_type);
333 if attestation_report.len() != expected_report_size {
334 Err(Error::InvalidAttestationReportSize {
335 report_size: attestation_report.len(),
336 expected_size: expected_report_size,
337 })?
338 }
339
340 let runtime_claims_len = runtime_claims.len();
341 let include_extension = version >= IgvmAttestRequestVersion::VERSION_2;
343 let report_size = request_size(version, runtime_claims_len);
344 let user_data_size =
345 report_size - size_of::<IgvmAttestRequestBase>() + size_of::<IgvmAttestRequestData>();
346 let mut request = IgvmAttestRequestBase::new_zeroed();
347
348 request.header = IgvmAttestRequestHeader::new(report_size as u32, request_type, 0);
349
350 request.attestation_report[..attestation_report.len()].copy_from_slice(attestation_report);
351
352 request.request_data = IgvmAttestRequestData::new(
353 version,
354 user_data_size as u32,
355 report_type.to_external_type(),
356 hash_type,
357 runtime_claims_len as u32,
358 );
359
360 let mut buffer = Vec::with_capacity(report_size);
361 buffer.extend_from_slice(request.as_bytes());
362
363 if include_extension {
364 let capability_bitmap = IgvmCapabilityBitMap::new()
365 .with_error_code(true)
366 .with_retry(true)
367 .with_skip_hw_unsealing(true)
368 .with_use_rsa_aes_key_wrap_384(true)
369 .with_corim_endorsement(matches!(report_type, &ReportType::Tdx));
372 let ext = IgvmAttestRequestDataExt::new(capability_bitmap);
373 buffer.extend_from_slice(ext.as_bytes());
374 }
375
376 buffer.extend_from_slice(runtime_claims);
377
378 Ok(buffer)
379}
380
381fn get_report_size(report_type: &ReportType) -> usize {
383 match report_type {
384 ReportType::Vbs => openhcl_attestation_protocol::igvm_attest::get::VBS_VM_REPORT_SIZE,
385 ReportType::Snp => openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE,
386 ReportType::Tdx => openhcl_attestation_protocol::igvm_attest::get::TDX_VM_REPORT_SIZE,
387 ReportType::Tvm => openhcl_attestation_protocol::igvm_attest::get::TVM_REPORT_SIZE,
388 ReportType::Cca => todo!(),
389 }
390}
391
392fn attestation_vm_config_with_time(
394 vm_config: &AttestationVmConfig,
395 host_epoch: i64,
396) -> AttestationVmConfig {
397 let mut vm_config = vm_config.clone();
398 vm_config.current_time = Some(host_epoch);
399 vm_config
400}
401
402fn runtime_claims_to_bytes(
404 runtime_claims: &openhcl_attestation_protocol::igvm_attest::get::runtime_claims::RuntimeClaims,
405) -> Vec<u8> {
406 let runtime_claims = serde_json::to_string(runtime_claims).expect("JSON serialization failed");
407 runtime_claims.as_bytes().to_vec()
408}
409
410#[cfg(test)]
411mod tests {
412 use super::*;
413 use crypto::rsa::RsaKeyPair;
414 use crypto::rsa::RsaPublicKeyComponents;
415 use get_protocol::IGVM_ATTEST_MSG_REQ_REPORT_MAX_SIZE;
416 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestBase;
417 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestDataExt;
418 use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::AkCertMetadata;
419 use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::AttestationTpmVersion;
420 use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::HardwareSealingPolicy;
421 use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::RuntimeClaims;
422 use test_with_tracing::test;
423
424 const AK_CERT_TEES: [Option<TeeType>; 5] = [
425 Some(TeeType::Snp),
426 Some(TeeType::Tdx),
427 Some(TeeType::Cca),
428 Some(TeeType::Vbs),
429 None,
430 ];
431
432 struct AkCertTestInput {
433 ak: RsaPublicKeyComponents,
434 ek: RsaPublicKeyComponents,
435 config: AttestationVmConfig,
436 guest_input: Vec<u8>,
437 }
438
439 impl AkCertTestInput {
440 fn new() -> Self {
441 Self {
442 ak: RsaKeyPair::generate(2048).unwrap().to_components().unwrap(),
443 ek: RsaKeyPair::generate(2048).unwrap().to_components().unwrap(),
444 config: AttestationVmConfig {
445 current_time: None,
446 root_cert_thumbprint: "A".repeat(64),
447 console_enabled: false,
448 interactive_console_enabled: false,
449 ipmi_enabled: false,
450 secure_boot: true,
451 tpm_enabled: true,
452 tpm_version: AttestationTpmVersion::V185,
453 tpm_persisted: true,
454 hardware_sealing_policy: HardwareSealingPolicy::Signer,
455 filtered_vpci_devices_allowed: false,
456 vm_unique_id: "01234567-89ab-cdef-0123-456789abcdef".to_owned(),
457 vmgs_provisioner: None,
458 },
459 guest_input: vec![0x42; 32],
460 }
461 }
462
463 fn claims(&self, metadata: Option<AkCertMetadata>) -> Vec<u8> {
464 let mut claims = RuntimeClaims::ak_cert_runtime_claims(
465 &self.ak.public_exponent,
466 &self.ak.modulus,
467 &self.ek.public_exponent,
468 &self.ek.modulus,
469 &self.config,
470 &self.guest_input,
471 );
472 claims.akcert = metadata;
473 runtime_claims_to_bytes(&claims)
474 }
475
476 fn prepare(
477 &self,
478 tee: Option<TeeType>,
479 metadata: Option<AkCertMetadata>,
480 ) -> IgvmAttestRequestHelper {
481 IgvmAttestRequestHelper::prepare_ak_cert_request(
482 tee,
483 &self.ak.public_exponent,
484 &self.ak.modulus,
485 &self.ek.public_exponent,
486 &self.ek.modulus,
487 &self.config,
488 &self.guest_input,
489 metadata,
490 )
491 }
492
493 fn metadata_for_request_size(&self, size: usize) -> AkCertMetadata {
494 let certificate = test_igvm_agent_lib::ak_cert::certificate();
495 let mut metadata = ak_cert::parse_metadata(&certificate).unwrap();
496 metadata.subject.clear();
497 let base_size = size_of::<IgvmAttestRequestBase>()
498 + size_of::<IgvmAttestRequestDataExt>()
499 + self.claims(Some(metadata.clone())).len();
500 metadata.subject = "s".repeat(size.checked_sub(base_size).unwrap());
501 metadata
502 }
503
504 fn assert_fallback(&self, metadata: AkCertMetadata) {
505 let oversized_claims = self.claims(Some(metadata.clone()));
506 assert!(
507 size_of::<IgvmAttestRequestBase>()
508 + size_of::<IgvmAttestRequestDataExt>()
509 + oversized_claims.len()
510 > IGVM_ATTEST_MSG_REQ_REPORT_MAX_SIZE
511 );
512 for (tee, same_tee) in AK_CERT_TEES.into_iter().zip(AK_CERT_TEES) {
513 let fallback = self.prepare(tee, Some(metadata.clone()));
514 let without_metadata = self.prepare(same_tee, None);
515 assert!(!fallback.has_ak_cert_metadata());
516 assert!(!without_metadata.has_ak_cert_metadata());
517 assert_eq!(fallback.runtime_claims, self.claims(None));
518 assert_eq!(fallback.runtime_claims, without_metadata.runtime_claims);
519 assert_eq!(
520 fallback.get_runtime_claims_hash(),
521 without_metadata.get_runtime_claims_hash()
522 );
523 assert_ne!(
524 &fallback.get_runtime_claims_hash()[..32],
525 crypto::sha_256::sha_256(&oversized_claims).as_ref()
526 );
527 assert_request_layout_and_hash(&fallback);
528 assert!(
529 fallback.request_size(IGVM_ATTEST_REQUEST_CURRENT_VERSION)
530 <= IGVM_ATTEST_MSG_REQ_REPORT_MAX_SIZE
531 );
532 }
533 }
534 }
535
536 fn assert_request_layout_and_hash(helper: &IgvmAttestRequestHelper) {
537 let hash = crypto::sha_256::sha_256(&helper.runtime_claims);
538 assert_eq!(&helper.get_runtime_claims_hash()[..32], hash.as_ref());
539 assert_eq!(&helper.get_runtime_claims_hash()[32..], &[0; 32]);
540 for version in [
541 IgvmAttestRequestVersion::VERSION_1,
542 IgvmAttestRequestVersion::VERSION_2,
543 ] {
544 let extension_size = if version == IgvmAttestRequestVersion::VERSION_2 {
545 size_of::<IgvmAttestRequestDataExt>()
546 } else {
547 0
548 };
549 let claims_offset = size_of::<IgvmAttestRequestBase>() + extension_size;
550 assert_eq!(
551 helper.request_size(version),
552 claims_offset + helper.runtime_claims.len()
553 );
554 if matches!(helper.report_type, ReportType::Cca) {
557 continue;
558 }
559 let report = vec![0x5a; get_report_size(&helper.report_type)];
560 let wire = helper.create_request(version, &report).unwrap();
561 assert_eq!(wire.len(), helper.request_size(version));
562 assert_eq!(&wire[claims_offset..], &helper.runtime_claims);
563 let (base, _) = IgvmAttestRequestBase::read_from_prefix(&wire).unwrap();
564 assert_eq!(base.header.report_size as usize, wire.len());
565 assert_eq!(base.request_data.version, version);
566 assert_eq!(
567 base.request_data.report_type,
568 helper.report_type.to_external_type()
569 );
570 assert_eq!(&base.attestation_report[..report.len()], &report);
571 }
572 }
573
574 #[test]
575 fn test_ak_cert_metadata_size_boundary() {
576 let input = AkCertTestInput::new();
577 assert_eq!(IGVM_ATTEST_MSG_REQ_REPORT_MAX_SIZE, 4096);
578 assert_eq!(
579 IGVM_ATTEST_REQUEST_CURRENT_VERSION,
580 IgvmAttestRequestVersion::VERSION_2
581 );
582 for size in [4096, 4097] {
583 let metadata = input.metadata_for_request_size(size);
584 let claims = input.claims(Some(metadata.clone()));
585 let raw = create_request(
588 IGVM_ATTEST_REQUEST_CURRENT_VERSION,
589 IgvmAttestRequestType::AK_CERT_REQUEST,
590 &claims,
591 &[],
592 &ReportType::Tvm,
593 IgvmAttestHashType::SHA_256,
594 )
595 .unwrap();
596 assert_eq!(raw.len(), size);
597 assert!(size_of::<IgvmAttestRequestBase>() + claims.len() <= 4096);
598 if size == 4096 {
599 for tee in AK_CERT_TEES {
600 let helper = input.prepare(tee, Some(metadata.clone()));
601 assert!(helper.has_ak_cert_metadata());
602 assert_eq!(helper.runtime_claims, claims);
603 assert_eq!(
604 helper.request_size(IGVM_ATTEST_REQUEST_CURRENT_VERSION),
605 4096
606 );
607 assert_request_layout_and_hash(&helper);
608 }
609 } else {
610 input.assert_fallback(metadata);
611 }
612 }
613 }
614
615 #[test]
616 fn test_ak_cert_metadata_large_hex_authority_fallback() {
617 let input = AkCertTestInput::new();
618 let mut metadata = input.metadata_for_request_size(4096);
619 metadata.subject.clear();
620 metadata.authority = hex::encode_upper([0xab; 2048]);
621 input.assert_fallback(metadata);
622 }
623
624 #[test]
625 fn test_ak_cert_metadata_escaped_common_names_fallback() {
626 let input = AkCertTestInput::new();
627 for escaped_name in ['"', '\\', '\n', '\0'] {
628 for issuer in [false, true] {
629 let mut metadata = input.metadata_for_request_size(4096);
630 let name = if issuer {
631 &mut metadata.issuer
632 } else {
633 &mut metadata.subject
634 };
635 assert!(name.is_ascii() && !name.is_empty());
638 name.replace_range(..1, &escaped_name.to_string());
639 input.assert_fallback(metadata);
640 }
641 }
642 }
643
644 #[test]
645 fn test_metadata_free_guest_v1_and_key_release_unchanged() {
646 let mut input = AkCertTestInput::new();
647 input.guest_input = vec![0xab; 4096];
649 for (tee, same_tee) in AK_CERT_TEES.into_iter().zip(AK_CERT_TEES) {
650 let guest = input.prepare(tee, None);
651 assert!(!guest.has_ak_cert_metadata());
652 assert_eq!(guest.runtime_claims, input.claims(None));
653 assert!(guest.request_size(IgvmAttestRequestVersion::VERSION_1) > 4096);
654 assert_request_layout_and_hash(&guest);
655
656 if let Some(tee) = same_tee {
657 let key_release = IgvmAttestRequestHelper::prepare_key_release_request(
658 tee,
659 &input.ak.public_exponent,
660 &input.ak.modulus,
661 12345,
662 &input.config,
663 );
664 assert!(!key_release.has_ak_cert_metadata());
665 let claims = RuntimeClaims::key_release_request_runtime_claims(
666 &input.ak.public_exponent,
667 &input.ak.modulus,
668 &attestation_vm_config_with_time(&input.config, 12345),
669 );
670 assert_eq!(key_release.runtime_claims, runtime_claims_to_bytes(&claims));
671 assert_request_layout_and_hash(&key_release);
672 }
673 }
674 }
675
676 #[test]
677 fn test_ak_cert_metadata_claims_are_measured() {
678 use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::RuntimeClaims;
679
680 let config = AttestationVmConfig {
681 current_time: None,
682 root_cert_thumbprint: String::new(),
683 console_enabled: false,
684 interactive_console_enabled: false,
685 ipmi_enabled: false,
686 secure_boot: false,
687 tpm_enabled: true,
688 tpm_version: AttestationTpmVersion::V185,
689 tpm_persisted: true,
690 hardware_sealing_policy: HardwareSealingPolicy::Signer,
691 filtered_vpci_devices_allowed: false,
692 vm_unique_id: String::new(),
693 vmgs_provisioner: None,
694 };
695 let certificate = test_igvm_agent_lib::ak_cert::certificate();
696 let mut metadata = ak_cert::parse_metadata(&certificate).unwrap();
697 metadata.subject = "AK \"subject\"\\\n".to_owned();
698 for tee in [
699 Some(TeeType::Snp),
700 Some(TeeType::Tdx),
701 Some(TeeType::Cca),
702 Some(TeeType::Vbs),
703 None,
704 ] {
705 let with_cert = IgvmAttestRequestHelper::prepare_ak_cert_request(
706 tee,
707 &[1],
708 &[2],
709 &[3],
710 &[4],
711 &config,
712 &[],
713 Some(metadata.clone()),
714 );
715 let claims: serde_json::Value =
716 serde_json::from_slice(&with_cert.runtime_claims).unwrap();
717 assert_eq!(
718 claims["akcert"],
719 serde_json::json!({
720 "issuer": metadata.issuer,
721 "subject": metadata.subject,
722 "not_before": metadata.not_before,
723 "not_after": metadata.not_after,
724 "authority": metadata.authority,
725 })
726 );
727 let hash = crypto::sha_256::sha_256(&with_cert.runtime_claims);
728 assert_eq!(&with_cert.get_runtime_claims_hash()[..32], hash.as_ref());
729 assert_eq!(&with_cert.get_runtime_claims_hash()[32..], &[0; 32]);
730
731 let without_cert = IgvmAttestRequestHelper::prepare_ak_cert_request(
732 None,
733 &[1],
734 &[2],
735 &[3],
736 &[4],
737 &config,
738 &[],
739 None,
740 );
741 let claims: serde_json::Value =
742 serde_json::from_slice(&without_cert.runtime_claims).unwrap();
743 assert!(claims.get("akcert").is_none());
744 let decoded: RuntimeClaims =
745 serde_json::from_slice(&without_cert.runtime_claims).unwrap();
746 assert!(decoded.akcert.is_none());
747 assert_ne!(
748 with_cert.get_runtime_claims_hash(),
749 without_cert.get_runtime_claims_hash()
750 );
751
752 let legacy =
753 RuntimeClaims::ak_cert_runtime_claims(&[1], &[2], &[3], &[4], &config, &[]);
754 assert_eq!(
755 without_cert.runtime_claims,
756 serde_json::to_vec(&legacy).unwrap()
757 );
758 }
759 let key_release = RuntimeClaims::key_release_request_runtime_claims(&[1], &[2], &config);
760 let claims = serde_json::to_value(key_release).unwrap();
761 assert!(claims.get("akcert").is_none());
762 }
763
764 #[test]
765 fn test_create_request() {
766 use openhcl_attestation_protocol::igvm_attest::get::IGVM_ATTEST_REQUEST_CURRENT_VERSION;
767
768 let result = create_request(
769 IGVM_ATTEST_REQUEST_CURRENT_VERSION,
770 IgvmAttestRequestType::AK_CERT_REQUEST,
771 &[],
772 &[0u8; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE],
773 &ReportType::Snp,
774 IgvmAttestHashType::SHA_256,
775 );
776 assert!(result.is_ok());
777
778 let result = create_request(
779 IGVM_ATTEST_REQUEST_CURRENT_VERSION,
780 IgvmAttestRequestType::AK_CERT_REQUEST,
781 &[],
782 &[0u8; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE + 1],
783 &ReportType::Snp,
784 IgvmAttestHashType::SHA_256,
785 );
786 assert!(result.is_err());
787 }
788
789 #[test]
790 fn test_create_request_version1_no_extension() {
791 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestBase;
792 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestVersion;
793
794 let runtime_claims = vec![1u8, 2, 3];
795 let attestation_report =
796 vec![0u8; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE];
797
798 let buffer = create_request(
799 IgvmAttestRequestVersion::VERSION_1,
800 IgvmAttestRequestType::AK_CERT_REQUEST,
801 &runtime_claims,
802 &attestation_report,
803 &ReportType::Snp,
804 IgvmAttestHashType::SHA_256,
805 )
806 .expect("request generation");
807
808 let (request, _) =
809 IgvmAttestRequestBase::read_from_prefix(&buffer).expect("parse IgvmAttestRequest");
810 assert_eq!(
811 request.request_data.version,
812 IgvmAttestRequestVersion::VERSION_1
813 );
814
815 let expected_size = (size_of::<
816 openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestData,
817 >() + runtime_claims.len()) as u32;
818 assert_eq!(request.request_data.data_size, expected_size);
819
820 let header_size = size_of::<IgvmAttestRequestBase>();
821 assert_eq!(
822 buffer.len(),
823 header_size + runtime_claims.len(),
824 "no extension appended for version 1"
825 );
826 assert_eq!(&buffer[header_size..], runtime_claims.as_slice());
827 }
828
829 #[test]
830 fn test_create_request_version2_with_extension() {
831 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestBase;
832 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestDataExt;
833 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestVersion;
834
835 let runtime_claims = vec![4u8, 5, 6, 7];
836 let attestation_report =
837 vec![0u8; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE];
838
839 let buffer = create_request(
840 IgvmAttestRequestVersion::VERSION_2,
841 IgvmAttestRequestType::AK_CERT_REQUEST,
842 &runtime_claims,
843 &attestation_report,
844 &ReportType::Snp,
845 IgvmAttestHashType::SHA_256,
846 )
847 .expect("request generation");
848
849 let (request, _) =
850 IgvmAttestRequestBase::read_from_prefix(&buffer).expect("parse IgvmAttestRequest");
851 assert_eq!(
852 request.request_data.version,
853 IgvmAttestRequestVersion::VERSION_2
854 );
855
856 let expected_extension_size = size_of::<IgvmAttestRequestDataExt>();
857 let expected_size = (size_of::<
858 openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestData,
859 >() + expected_extension_size
860 + runtime_claims.len()) as u32;
861 assert_eq!(request.request_data.data_size, expected_size);
862
863 let header_size = size_of::<IgvmAttestRequestBase>();
864 let ext_offset = header_size;
865
866 let (ext, _) = IgvmAttestRequestDataExt::read_from_prefix(&buffer[ext_offset..])
867 .expect("parse IgvmAttestRequestDataExt");
868 assert!(ext.capability_bitmap.error_code());
869 assert!(ext.capability_bitmap.retry());
870 assert!(ext.capability_bitmap.skip_hw_unsealing());
871 assert!(!ext.capability_bitmap.corim_endorsement());
874
875 assert_eq!(
876 buffer.len(),
877 header_size + expected_extension_size + runtime_claims.len()
878 );
879 assert_eq!(
880 &buffer[header_size + expected_extension_size..],
881 runtime_claims.as_slice()
882 );
883 }
884
885 #[test]
886 fn test_create_request_version2_tdx_requests_corim() {
887 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestBase;
888 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestDataExt;
889 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestVersion;
890
891 let runtime_claims = vec![4u8, 5, 6, 7];
892 let attestation_report =
893 vec![0u8; openhcl_attestation_protocol::igvm_attest::get::TDX_VM_REPORT_SIZE];
894
895 let buffer = create_request(
896 IgvmAttestRequestVersion::VERSION_2,
897 IgvmAttestRequestType::KEY_RELEASE_REQUEST,
898 &runtime_claims,
899 &attestation_report,
900 &ReportType::Tdx,
901 IgvmAttestHashType::SHA_256,
902 )
903 .expect("request generation");
904
905 let header_size = size_of::<IgvmAttestRequestBase>();
906 let (ext, _) = IgvmAttestRequestDataExt::read_from_prefix(&buffer[header_size..])
907 .expect("parse IgvmAttestRequestDataExt");
908 assert!(ext.capability_bitmap.corim_endorsement());
910 }
911
912 #[test]
913 fn test_transfer_key_jwk() {
914 const EXPECTED_JWK: &str = r#"[{"kid":"HCLTransferKey","key_ops":["encrypt"],"kty":"RSA","e":"RVhQT05FTlQ","n":"TU9EVUxVUw"}]"#;
915
916 let rsa_jwk = openhcl_attestation_protocol::igvm_attest::get::runtime_claims::RsaJwk::get_transfer_key_jwks(
917 b"EXPONENT",
918 b"MODULUS",
919 );
920
921 let result = serde_json::to_string(&rsa_jwk);
922 assert!(result.is_ok());
923
924 let transfer_key_jwk = result.unwrap();
925 assert_eq!(transfer_key_jwk, EXPECTED_JWK);
926 }
927
928 #[test]
929 fn test_vm_configuration_no_time() {
930 const EXPECTED_JWK: &str = r#"{"root-cert-thumbprint":"","console-enabled":false,"interactive-console-enabled":false,"ipmi-enabled":true,"secure-boot":false,"tpm-enabled":false,"tpm-version":"1.38","tpm-persisted":false,"filtered-vpci-devices-allowed":true,"vmUniqueId":"","hardware-sealing-policy":"signer"}"#;
931
932 let attestation_vm_config = AttestationVmConfig {
933 current_time: None,
934 root_cert_thumbprint: String::new(),
935 console_enabled: false,
936 interactive_console_enabled: false,
937 ipmi_enabled: true,
938 secure_boot: false,
939 tpm_enabled: false,
940 tpm_version: AttestationTpmVersion::V138,
941 tpm_persisted: false,
942 hardware_sealing_policy: HardwareSealingPolicy::Signer,
943 filtered_vpci_devices_allowed: true,
944 vm_unique_id: String::new(),
945 vmgs_provisioner: None,
946 };
947 let result = serde_json::to_string(&attestation_vm_config);
948 assert!(result.is_ok());
949
950 let vm_config = result.unwrap();
951 assert_eq!(vm_config, EXPECTED_JWK);
952 }
953
954 #[test]
955 fn test_vm_configuration_with_time() {
956 const EXPECTED_JWK: &str = r#"{"current-time":1691103220,"root-cert-thumbprint":"","console-enabled":false,"interactive-console-enabled":false,"ipmi-enabled":false,"secure-boot":false,"tpm-enabled":false,"tpm-version":"185","tpm-persisted":false,"filtered-vpci-devices-allowed":true,"vmUniqueId":"","hardware-sealing-policy":"hash"}"#;
957
958 let attestation_vm_config = AttestationVmConfig {
959 current_time: None,
960 root_cert_thumbprint: String::new(),
961 console_enabled: false,
962 interactive_console_enabled: false,
963 ipmi_enabled: false,
964 secure_boot: false,
965 tpm_enabled: false,
966 tpm_version: AttestationTpmVersion::V185,
967 tpm_persisted: false,
968 hardware_sealing_policy: HardwareSealingPolicy::Hash,
969 filtered_vpci_devices_allowed: true,
970 vm_unique_id: String::new(),
971 vmgs_provisioner: None,
972 };
973 let attestation_vm_config =
974 attestation_vm_config_with_time(&attestation_vm_config, 1691103220);
975 let result = serde_json::to_string(&attestation_vm_config);
976 assert!(result.is_ok());
977
978 let vm_config = result.unwrap();
979 assert_eq!(vm_config, EXPECTED_JWK);
980 }
981
982 #[test]
983 fn test_empty_response() {
984 let result = parse_response_header(&[]);
985 assert!(result.is_err());
986 assert_eq!(
987 result.unwrap_err().to_string(),
988 Error::ResponseSizeTooSmall { response_size: 0 }.to_string()
989 );
990 }
991
992 #[test]
993 fn test_invalid_response_size_smaller_than_header_size() {
994 const INVALID_RESPONSE: [u8; 4] = [0x04, 0x00, 0x00, 0x00];
995 let result = parse_response_header(&INVALID_RESPONSE);
996 assert!(result.is_err());
997 assert_eq!(
998 result.unwrap_err().to_string(),
999 Error::ResponseSizeTooSmall { response_size: 4 }.to_string()
1000 );
1001 }
1002
1003 #[test]
1004 fn test_valid_v1_response_size_match() {
1005 const VALID_RESPONSE: [u8; 42] = [
1006 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
1007 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
1008 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
1009 ];
1010
1011 let result = parse_response_header(&VALID_RESPONSE);
1012 assert!(result.is_ok());
1013 let header = result.unwrap();
1014 assert_eq!(VALID_RESPONSE.len(), header.data_size as usize);
1015 assert_eq!(IgvmAttestResponseVersion::VERSION_1, header.version);
1016 }
1017
1018 #[test]
1019 fn test_valid_v2_response_size_match() {
1020 const VALID_RESPONSE: [u8; 42] = [
1021 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1022 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1023 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
1024 ];
1025
1026 let result = parse_response_header(&VALID_RESPONSE);
1027 assert!(result.is_ok());
1028 let header = result.unwrap();
1029 assert_eq!(VALID_RESPONSE.len(), header.data_size as usize);
1030 assert_eq!(IgvmAttestResponseVersion::VERSION_2, header.version);
1031 }
1032
1033 #[test]
1034 fn test_valid_v1_response_size_smaller_than_specified() {
1035 const VALID_RESPONSE: [u8; 42] = [
1036 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
1037 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
1038 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
1039 ];
1040
1041 let header = parse_response_header(&VALID_RESPONSE);
1042 assert!(header.is_ok());
1043 assert_eq!(0x29, header.unwrap().data_size as usize);
1044 }
1045
1046 #[test]
1047 fn test_valid_v2_response_size_smaller_than_specified() {
1048 const VALID_RESPONSE: [u8; 42] = [
1049 0x29, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1050 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1051 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
1052 ];
1053
1054 let header = parse_response_header(&VALID_RESPONSE);
1055 assert!(header.is_ok());
1056 assert_eq!(0x29, header.unwrap().data_size as usize);
1057 }
1058
1059 #[test]
1060 fn test_invalid_v1_response_size() {
1061 const INVALID_RESPONSE: [u8; 42] = [
1062 0x2b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
1063 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
1064 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
1065 ];
1066
1067 let result = parse_response_header(&INVALID_RESPONSE);
1068 assert!(result.is_err());
1069 assert_eq!(
1070 result.unwrap_err().to_string(),
1071 Error::ResponseSizeMismatch {
1072 size: INVALID_RESPONSE.len(),
1073 specified_size: 0x2b
1074 }
1075 .to_string()
1076 );
1077 }
1078
1079 #[test]
1080 fn test_invalid_v2_response_size() {
1081 const INVALID_RESPONSE: [u8; 42] = [
1082 0x2b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
1083 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
1084 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
1085 ];
1086
1087 let result = parse_response_header(&INVALID_RESPONSE);
1088 assert!(result.is_err());
1089 assert_eq!(
1090 result.unwrap_err().to_string(),
1091 Error::ResponseSizeMismatch {
1092 size: INVALID_RESPONSE.len(),
1093 specified_size: 0x2b
1094 }
1095 .to_string()
1096 );
1097 }
1098
1099 #[test]
1100 fn test_invalid_header_version() {
1101 const INVALID_RESPONSE: [u8; 42] = [
1102 0x2a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
1103 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
1104 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
1105 ];
1106
1107 let result = parse_response_header(&INVALID_RESPONSE);
1108 assert!(result.is_err());
1109 assert_eq!(
1110 result.unwrap_err().to_string(),
1111 Error::InvalidResponseHeaderVersion {
1112 version: IgvmAttestResponseVersion(3),
1113 latest_version: IGVM_ATTEST_RESPONSE_CURRENT_VERSION
1114 }
1115 .to_string()
1116 );
1117 }
1118
1119 #[test]
1120 fn test_invalid_v2_response_size_smaller_than_specified_header_size() {
1121 const INVALID_RESPONSE: [u8; 28] = [
1122 0x1c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1124 ];
1125
1126 let result = parse_response_header(&INVALID_RESPONSE);
1127 assert!(result.is_err());
1128 assert_eq!(
1129 result.unwrap_err().to_string(),
1130 Error::ResponseHeaderInvalidFormat {
1131 response_size: 0x1c
1132 }
1133 .to_string()
1134 );
1135 }
1136
1137 #[test]
1138 fn test_failed_response_with_retryable_error() {
1139 const INVALID_RESPONSE: [u8; 42] = [
1141 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x04, 0x00, 0x00, 0x93, 0x01,
1142 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1143 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
1144 ];
1145
1146 let result = parse_response_header(&INVALID_RESPONSE);
1147 assert!(result.is_err());
1148 assert_eq!(
1149 result.unwrap_err().to_string(),
1150 Error::Attestation {
1151 igvm_error_code: 1103,
1152 http_status_code: 403,
1153 retry_signal: true,
1154 skip_hw_unsealing_signal: false
1155 }
1156 .to_string()
1157 );
1158 }
1159
1160 #[test]
1161 fn test_failed_response_with_non_retryable_error() {
1162 const INVALID_RESPONSE: [u8; 42] = [
1164 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x04, 0x00, 0x00, 0xf7, 0x01,
1165 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1166 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
1167 ];
1168
1169 let result = parse_response_header(&INVALID_RESPONSE);
1170 assert!(result.is_err());
1171 assert_eq!(
1172 result.unwrap_err().to_string(),
1173 Error::Attestation {
1174 igvm_error_code: 1103,
1175 http_status_code: 503,
1176 retry_signal: false,
1177 skip_hw_unsealing_signal: false
1178 }
1179 .to_string()
1180 );
1181 }
1182
1183 #[test]
1184 fn test_failed_response_with_skip_hw_unsealing_signal() {
1185 const INVALID_RESPONSE: [u8; 42] = [
1188 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x04, 0x00, 0x00, 0x90, 0x01,
1189 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1190 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
1191 ];
1192
1193 let result = parse_response_header(&INVALID_RESPONSE);
1194 assert!(result.is_err());
1195 assert_eq!(
1196 result.unwrap_err().to_string(),
1197 Error::Attestation {
1198 igvm_error_code: 1103,
1199 http_status_code: 400,
1200 retry_signal: true,
1201 skip_hw_unsealing_signal: true
1202 }
1203 .to_string()
1204 );
1205 }
1206
1207 #[test]
1208 fn test_failed_response_with_skip_hw_unsealing_only() {
1209 const INVALID_RESPONSE: [u8; 42] = [
1212 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x04, 0x00, 0x00, 0x90, 0x01,
1213 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1214 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
1215 ];
1216
1217 let result = parse_response_header(&INVALID_RESPONSE);
1218 assert!(result.is_err());
1219 assert_eq!(
1220 result.unwrap_err().to_string(),
1221 Error::Attestation {
1222 igvm_error_code: 1103,
1223 http_status_code: 400,
1224 retry_signal: false,
1225 skip_hw_unsealing_signal: true
1226 }
1227 .to_string()
1228 );
1229 }
1230}