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