1use base64_serde::base64_serde_type;
9use openhcl_attestation_protocol::igvm_attest::get::IGVM_ATTEST_RESPONSE_CURRENT_VERSION;
10use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestCommonResponseHeader;
11use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestHashType;
12use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestReportType;
13use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestType;
14use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestVersion;
15use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestResponseVersion;
16use openhcl_attestation_protocol::igvm_attest::get::IgvmCapabilityBitMap;
17use openhcl_attestation_protocol::igvm_attest::get::IgvmErrorInfo;
18use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::AttestationVmConfig;
19use tee_call::TeeType;
20use thiserror::Error;
21use zerocopy::FromBytes;
22use zerocopy::FromZeros;
23use zerocopy::IntoBytes;
24
25pub mod ak_cert;
26pub mod key_release;
27pub mod wrapped_key;
28
29base64_serde_type!(Base64Url, base64::engine::general_purpose::URL_SAFE_NO_PAD);
30
31#[expect(missing_docs)] #[derive(Debug, Error)]
33pub enum Error {
34 #[error(
35 "the size of the attestation report {report_size} is invalid, expected {expected_size}"
36 )]
37 InvalidAttestationReportSize {
38 report_size: usize,
39 expected_size: usize,
40 },
41 #[error("the size of the attestation response {response_size} is too small to parse")]
42 ResponseSizeTooSmall { response_size: usize },
43 #[error(
44 "the header of the attestation response (size {response_size}) is not in correct format"
45 )]
46 ResponseHeaderInvalidFormat { response_size: usize },
47 #[error(
48 "response size {specified_size} specified in the header not match the actual size {size}"
49 )]
50 ResponseSizeMismatch { size: usize, specified_size: usize },
51 #[error("response header version {version:?} larger than current version {latest_version:?}")]
52 InvalidResponseHeaderVersion {
53 version: IgvmAttestResponseVersion,
54 latest_version: IgvmAttestResponseVersion,
55 },
56 #[error(
57 "attest failed ({igvm_error_code}-{http_status_code}), retry recommendation ({retry_signal})"
58 )]
59 Attestation {
60 igvm_error_code: u32,
61 http_status_code: u32,
62 retry_signal: bool,
63 },
64}
65
66pub enum ReportType {
68 Vbs,
70 Snp,
72 Tdx,
74 Tvm,
76}
77
78impl ReportType {
79 fn to_external_type(&self) -> IgvmAttestReportType {
81 match self {
82 Self::Vbs => IgvmAttestReportType::VBS_VM_REPORT,
83 Self::Snp => IgvmAttestReportType::SNP_VM_REPORT,
84 Self::Tdx => IgvmAttestReportType::TDX_VM_REPORT,
85 Self::Tvm => IgvmAttestReportType::TVM_REPORT,
86 }
87 }
88}
89
90pub struct IgvmAttestRequestHelper {
92 request_type: IgvmAttestRequestType,
94 report_type: ReportType,
96 runtime_claims: Vec<u8>,
98 runtime_claims_hash: [u8; tee_call::REPORT_DATA_SIZE],
101 hash_type: IgvmAttestHashType,
103}
104
105impl IgvmAttestRequestHelper {
106 pub fn prepare_key_release_request(
108 tee_type: TeeType,
109 rsa_exponent: &[u8],
110 rsa_modulus: &[u8],
111 host_time: i64,
112 attestation_vm_config: &AttestationVmConfig,
113 ) -> Self {
114 let report_type = match tee_type {
115 TeeType::Snp => ReportType::Snp,
116 TeeType::Tdx => ReportType::Tdx,
117 TeeType::Vbs => ReportType::Vbs,
118 };
119
120 let attestation_vm_config =
121 attestation_vm_config_with_time(attestation_vm_config, host_time);
122 let runtime_claims =
123 openhcl_attestation_protocol::igvm_attest::get::runtime_claims::RuntimeClaims::key_release_request_runtime_claims(rsa_exponent, rsa_modulus, &attestation_vm_config);
124 let runtime_claims = runtime_claims_to_bytes(&runtime_claims);
125
126 let hash_type = IgvmAttestHashType::SHA_256;
127 let hash = crate::crypto::sha_256(runtime_claims.as_bytes());
128 let mut runtime_claims_hash = [0u8; tee_call::REPORT_DATA_SIZE];
129 runtime_claims_hash[0..hash.len()].copy_from_slice(&hash);
130
131 Self {
132 request_type: IgvmAttestRequestType::KEY_RELEASE_REQUEST,
133 report_type,
134 runtime_claims,
135 runtime_claims_hash,
136 hash_type,
137 }
138 }
139
140 pub fn prepare_ak_cert_request(
142 tee_type: Option<TeeType>,
143 ak_pub_exponent: &[u8],
144 ak_pub_modulus: &[u8],
145 ek_pub_exponent: &[u8],
146 ek_pub_modulus: &[u8],
147 attestation_vm_config: &AttestationVmConfig,
148 guest_input: &[u8],
149 ) -> Self {
150 let report_type = match tee_type {
151 Some(TeeType::Snp) => ReportType::Snp,
152 Some(TeeType::Tdx) => ReportType::Tdx,
153 Some(TeeType::Vbs) => ReportType::Vbs,
154 None => ReportType::Tvm,
155 };
156
157 let runtime_claims =
158 openhcl_attestation_protocol::igvm_attest::get::runtime_claims::RuntimeClaims::ak_cert_runtime_claims(
159 ak_pub_exponent,
160 ak_pub_modulus,
161 ek_pub_exponent,
162 ek_pub_modulus,
163 attestation_vm_config,
164 guest_input,
165 );
166
167 let runtime_claims = runtime_claims_to_bytes(&runtime_claims);
168
169 let hash_type = IgvmAttestHashType::SHA_256;
170 let hash = crate::crypto::sha_256(runtime_claims.as_bytes());
171 let mut runtime_claims_hash = [0u8; tee_call::REPORT_DATA_SIZE];
172 runtime_claims_hash[0..hash.len()].copy_from_slice(&hash);
173
174 Self {
175 request_type: IgvmAttestRequestType::AK_CERT_REQUEST,
176 report_type,
177 runtime_claims,
178 runtime_claims_hash,
179 hash_type,
180 }
181 }
182
183 pub fn get_runtime_claims_hash(&self) -> &[u8; tee_call::REPORT_DATA_SIZE] {
185 &self.runtime_claims_hash
186 }
187
188 pub fn set_request_type(&mut self, request_type: IgvmAttestRequestType) {
190 self.request_type = request_type
191 }
192
193 pub fn create_request(
195 &self,
196 version: IgvmAttestRequestVersion,
197 attestation_report: &[u8],
198 ) -> Result<Vec<u8>, Error> {
199 create_request(
200 version,
201 self.request_type,
202 &self.runtime_claims,
203 attestation_report,
204 &self.report_type,
205 self.hash_type,
206 )
207 }
208}
209
210pub fn parse_response_header(response: &[u8]) -> Result<IgvmAttestCommonResponseHeader, Error> {
212 let header = IgvmAttestCommonResponseHeader::read_from_prefix(response)
215 .map_err(|_| Error::ResponseSizeTooSmall {
216 response_size: response.len(),
217 })?
218 .0; if header.data_size as usize > response.len() {
222 Err(Error::ResponseSizeMismatch {
223 size: response.len(),
224 specified_size: header.data_size as usize,
225 })?
226 }
227 if header.version > IGVM_ATTEST_RESPONSE_CURRENT_VERSION {
228 Err(Error::InvalidResponseHeaderVersion {
229 version: header.version,
230 latest_version: IGVM_ATTEST_RESPONSE_CURRENT_VERSION,
231 })?
232 }
233
234 if header.version >= IgvmAttestResponseVersion::VERSION_2 {
236 let igvm_error_info = IgvmErrorInfo::read_from_prefix(
238 &response[size_of::<IgvmAttestCommonResponseHeader>()..],
239 )
240 .map_err(|_| Error::ResponseHeaderInvalidFormat {
241 response_size: response.len(),
242 })?
243 .0; if 0 != igvm_error_info.error_code {
246 Err(Error::Attestation {
247 igvm_error_code: igvm_error_info.error_code,
248 http_status_code: igvm_error_info.http_status_code,
249 retry_signal: igvm_error_info.igvm_signal.retry(),
250 })?
251 }
252 }
253 Ok(IgvmAttestCommonResponseHeader {
254 data_size: header.data_size,
255 version: header.version,
256 })
257}
258
259fn create_request(
263 version: IgvmAttestRequestVersion,
264 request_type: IgvmAttestRequestType,
265 runtime_claims: &[u8],
266 attestation_report: &[u8],
267 report_type: &ReportType,
268 hash_type: IgvmAttestHashType,
269) -> Result<Vec<u8>, Error> {
270 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestBase;
271 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestData;
272 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestDataExt;
273 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestHeader;
274
275 let expected_report_size = get_report_size(report_type);
276 if attestation_report.len() != expected_report_size {
277 Err(Error::InvalidAttestationReportSize {
278 report_size: attestation_report.len(),
279 expected_size: expected_report_size,
280 })?
281 }
282
283 let runtime_claims_len = runtime_claims.len();
284 let include_extension = version >= IgvmAttestRequestVersion::VERSION_2;
286 let extension_size = if include_extension {
287 size_of::<IgvmAttestRequestDataExt>()
288 } else {
289 0
290 };
291 let report_size = size_of::<IgvmAttestRequestBase>() + extension_size + runtime_claims_len;
292 let user_data_size = size_of::<IgvmAttestRequestData>() + extension_size + runtime_claims_len;
293 let mut request = IgvmAttestRequestBase::new_zeroed();
294
295 request.header = IgvmAttestRequestHeader::new(report_size as u32, request_type, 0);
296
297 request.attestation_report[..attestation_report.len()].copy_from_slice(attestation_report);
298
299 request.request_data = IgvmAttestRequestData::new(
300 version,
301 user_data_size as u32,
302 report_type.to_external_type(),
303 hash_type,
304 runtime_claims_len as u32,
305 );
306
307 let mut buffer = Vec::with_capacity(report_size);
308 buffer.extend_from_slice(request.as_bytes());
309
310 if include_extension {
311 let capability_bitmap = IgvmCapabilityBitMap::new()
312 .with_error_code(true)
313 .with_retry(true);
314 let ext = IgvmAttestRequestDataExt::new(capability_bitmap);
315 buffer.extend_from_slice(ext.as_bytes());
316 }
317
318 buffer.extend_from_slice(runtime_claims);
319
320 Ok(buffer)
321}
322
323fn get_report_size(report_type: &ReportType) -> usize {
325 match report_type {
326 ReportType::Vbs => openhcl_attestation_protocol::igvm_attest::get::VBS_VM_REPORT_SIZE,
327 ReportType::Snp => openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE,
328 ReportType::Tdx => openhcl_attestation_protocol::igvm_attest::get::TDX_VM_REPORT_SIZE,
329 ReportType::Tvm => openhcl_attestation_protocol::igvm_attest::get::TVM_REPORT_SIZE,
330 }
331}
332
333fn attestation_vm_config_with_time(
335 vm_config: &AttestationVmConfig,
336 host_epoch: i64,
337) -> AttestationVmConfig {
338 let mut vm_config = vm_config.clone();
339 vm_config.current_time = Some(host_epoch);
340 vm_config
341}
342
343fn runtime_claims_to_bytes(
345 runtime_claims: &openhcl_attestation_protocol::igvm_attest::get::runtime_claims::RuntimeClaims,
346) -> Vec<u8> {
347 let runtime_claims = serde_json::to_string(runtime_claims).expect("JSON serialization failed");
348 runtime_claims.as_bytes().to_vec()
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354
355 #[test]
356 fn test_create_request() {
357 use openhcl_attestation_protocol::igvm_attest::get::IGVM_ATTEST_REQUEST_CURRENT_VERSION;
358
359 let result = create_request(
360 IGVM_ATTEST_REQUEST_CURRENT_VERSION,
361 IgvmAttestRequestType::AK_CERT_REQUEST,
362 &[],
363 &[0u8; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE],
364 &ReportType::Snp,
365 IgvmAttestHashType::SHA_256,
366 );
367 assert!(result.is_ok());
368
369 let result = create_request(
370 IGVM_ATTEST_REQUEST_CURRENT_VERSION,
371 IgvmAttestRequestType::AK_CERT_REQUEST,
372 &[],
373 &[0u8; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE + 1],
374 &ReportType::Snp,
375 IgvmAttestHashType::SHA_256,
376 );
377 assert!(result.is_err());
378 }
379
380 #[test]
381 fn test_create_request_version1_no_extension() {
382 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestBase;
383 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestVersion;
384
385 let runtime_claims = vec![1u8, 2, 3];
386 let attestation_report =
387 vec![0u8; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE];
388
389 let buffer = create_request(
390 IgvmAttestRequestVersion::VERSION_1,
391 IgvmAttestRequestType::AK_CERT_REQUEST,
392 &runtime_claims,
393 &attestation_report,
394 &ReportType::Snp,
395 IgvmAttestHashType::SHA_256,
396 )
397 .expect("request generation");
398
399 let (request, _) =
400 IgvmAttestRequestBase::read_from_prefix(&buffer).expect("parse IgvmAttestRequest");
401 assert_eq!(
402 request.request_data.version,
403 IgvmAttestRequestVersion::VERSION_1
404 );
405
406 let expected_size = (size_of::<
407 openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestData,
408 >() + runtime_claims.len()) as u32;
409 assert_eq!(request.request_data.data_size, expected_size);
410
411 let header_size = size_of::<IgvmAttestRequestBase>();
412 assert_eq!(
413 buffer.len(),
414 header_size + runtime_claims.len(),
415 "no extension appended for version 1"
416 );
417 assert_eq!(&buffer[header_size..], runtime_claims.as_slice());
418 }
419
420 #[test]
421 fn test_create_request_version2_with_extension() {
422 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestBase;
423 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestDataExt;
424 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestVersion;
425
426 let runtime_claims = vec![4u8, 5, 6, 7];
427 let attestation_report =
428 vec![0u8; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE];
429
430 let buffer = create_request(
431 IgvmAttestRequestVersion::VERSION_2,
432 IgvmAttestRequestType::AK_CERT_REQUEST,
433 &runtime_claims,
434 &attestation_report,
435 &ReportType::Snp,
436 IgvmAttestHashType::SHA_256,
437 )
438 .expect("request generation");
439
440 let (request, _) =
441 IgvmAttestRequestBase::read_from_prefix(&buffer).expect("parse IgvmAttestRequest");
442 assert_eq!(
443 request.request_data.version,
444 IgvmAttestRequestVersion::VERSION_2
445 );
446
447 let expected_extension_size = size_of::<IgvmAttestRequestDataExt>();
448 let expected_size = (size_of::<
449 openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestData,
450 >() + expected_extension_size
451 + runtime_claims.len()) as u32;
452 assert_eq!(request.request_data.data_size, expected_size);
453
454 let header_size = size_of::<IgvmAttestRequestBase>();
455 let ext_offset = header_size;
456
457 let (ext, _) = IgvmAttestRequestDataExt::read_from_prefix(&buffer[ext_offset..])
458 .expect("parse IgvmAttestRequestDataExt");
459 assert!(ext.capability_bitmap.error_code());
460 assert!(ext.capability_bitmap.retry());
461
462 assert_eq!(
463 buffer.len(),
464 header_size + expected_extension_size + runtime_claims.len()
465 );
466 assert_eq!(
467 &buffer[header_size + expected_extension_size..],
468 runtime_claims.as_slice()
469 );
470 }
471
472 #[test]
473 fn test_transfer_key_jwk() {
474 const EXPECTED_JWK: &str = r#"[{"kid":"HCLTransferKey","key_ops":["encrypt"],"kty":"RSA","e":"RVhQT05FTlQ","n":"TU9EVUxVUw"}]"#;
475
476 let rsa_jwk = openhcl_attestation_protocol::igvm_attest::get::runtime_claims::RsaJwk::get_transfer_key_jwks(
477 b"EXPONENT",
478 b"MODULUS",
479 );
480
481 let result = serde_json::to_string(&rsa_jwk);
482 assert!(result.is_ok());
483
484 let transfer_key_jwk = result.unwrap();
485 assert_eq!(transfer_key_jwk, EXPECTED_JWK);
486 }
487
488 #[test]
489 fn test_vm_configuration_no_time() {
490 const EXPECTED_JWK: &str = r#"{"root-cert-thumbprint":"","console-enabled":false,"secure-boot":false,"tpm-enabled":false,"tpm-persisted":false,"filtered-vpci-devices-allowed":true,"vmUniqueId":""}"#;
491
492 let attestation_vm_config = AttestationVmConfig {
493 current_time: None,
494 root_cert_thumbprint: String::new(),
495 console_enabled: false,
496 secure_boot: false,
497 tpm_enabled: false,
498 tpm_persisted: false,
499 filtered_vpci_devices_allowed: true,
500 vm_unique_id: String::new(),
501 };
502 let result = serde_json::to_string(&attestation_vm_config);
503 assert!(result.is_ok());
504
505 let vm_config = result.unwrap();
506 assert_eq!(vm_config, EXPECTED_JWK);
507 }
508
509 #[test]
510 fn test_vm_configuration_with_time() {
511 const EXPECTED_JWK: &str = r#"{"current-time":1691103220,"root-cert-thumbprint":"","console-enabled":false,"secure-boot":false,"tpm-enabled":false,"tpm-persisted":false,"filtered-vpci-devices-allowed":true,"vmUniqueId":""}"#;
512
513 let attestation_vm_config = AttestationVmConfig {
514 current_time: None,
515 root_cert_thumbprint: String::new(),
516 console_enabled: false,
517 secure_boot: false,
518 tpm_enabled: false,
519 tpm_persisted: false,
520 filtered_vpci_devices_allowed: true,
521 vm_unique_id: String::new(),
522 };
523 let attestation_vm_config =
524 attestation_vm_config_with_time(&attestation_vm_config, 1691103220);
525 let result = serde_json::to_string(&attestation_vm_config);
526 assert!(result.is_ok());
527
528 let vm_config = result.unwrap();
529 assert_eq!(vm_config, EXPECTED_JWK);
530 }
531
532 #[test]
533 fn test_empty_response() {
534 let result = parse_response_header(&[]);
535 assert!(result.is_err());
536 assert_eq!(
537 result.unwrap_err().to_string(),
538 Error::ResponseSizeTooSmall { response_size: 0 }.to_string()
539 );
540 }
541
542 #[test]
543 fn test_invalid_response_size_smaller_than_header_size() {
544 const INVALID_RESPONSE: [u8; 4] = [0x04, 0x00, 0x00, 0x00];
545 let result = parse_response_header(&INVALID_RESPONSE);
546 assert!(result.is_err());
547 assert_eq!(
548 result.unwrap_err().to_string(),
549 Error::ResponseSizeTooSmall { response_size: 4 }.to_string()
550 );
551 }
552
553 #[test]
554 fn test_valid_v1_response_size_match() {
555 const VALID_RESPONSE: [u8; 42] = [
556 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
557 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
558 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
559 ];
560
561 let result = parse_response_header(&VALID_RESPONSE);
562 assert!(result.is_ok());
563 let header = result.unwrap();
564 assert_eq!(VALID_RESPONSE.len(), header.data_size as usize);
565 assert_eq!(IgvmAttestResponseVersion::VERSION_1, header.version);
566 }
567
568 #[test]
569 fn test_valid_v2_response_size_match() {
570 const VALID_RESPONSE: [u8; 42] = [
571 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
572 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
573 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
574 ];
575
576 let result = parse_response_header(&VALID_RESPONSE);
577 assert!(result.is_ok());
578 let header = result.unwrap();
579 assert_eq!(VALID_RESPONSE.len(), header.data_size as usize);
580 assert_eq!(IgvmAttestResponseVersion::VERSION_2, header.version);
581 }
582
583 #[test]
584 fn test_valid_v1_response_size_smaller_than_specified() {
585 const VALID_RESPONSE: [u8; 42] = [
586 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
587 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
588 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
589 ];
590
591 let header = parse_response_header(&VALID_RESPONSE);
592 assert!(header.is_ok());
593 assert_eq!(0x29, header.unwrap().data_size as usize);
594 }
595
596 #[test]
597 fn test_valid_v2_response_size_smaller_than_specified() {
598 const VALID_RESPONSE: [u8; 42] = [
599 0x29, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
600 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
601 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
602 ];
603
604 let header = parse_response_header(&VALID_RESPONSE);
605 assert!(header.is_ok());
606 assert_eq!(0x29, header.unwrap().data_size as usize);
607 }
608
609 #[test]
610 fn test_invalid_v1_response_size() {
611 const INVALID_RESPONSE: [u8; 42] = [
612 0x2b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
613 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
614 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
615 ];
616
617 let result = parse_response_header(&INVALID_RESPONSE);
618 assert!(result.is_err());
619 assert_eq!(
620 result.unwrap_err().to_string(),
621 Error::ResponseSizeMismatch {
622 size: INVALID_RESPONSE.len(),
623 specified_size: 0x2b
624 }
625 .to_string()
626 );
627 }
628
629 #[test]
630 fn test_invalid_v2_response_size() {
631 const INVALID_RESPONSE: [u8; 42] = [
632 0x2b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
633 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
634 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
635 ];
636
637 let result = parse_response_header(&INVALID_RESPONSE);
638 assert!(result.is_err());
639 assert_eq!(
640 result.unwrap_err().to_string(),
641 Error::ResponseSizeMismatch {
642 size: INVALID_RESPONSE.len(),
643 specified_size: 0x2b
644 }
645 .to_string()
646 );
647 }
648
649 #[test]
650 fn test_invalid_header_version() {
651 const INVALID_RESPONSE: [u8; 42] = [
652 0x2a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
653 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
654 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
655 ];
656
657 let result = parse_response_header(&INVALID_RESPONSE);
658 assert!(result.is_err());
659 assert_eq!(
660 result.unwrap_err().to_string(),
661 Error::InvalidResponseHeaderVersion {
662 version: IgvmAttestResponseVersion(3),
663 latest_version: IGVM_ATTEST_RESPONSE_CURRENT_VERSION
664 }
665 .to_string()
666 );
667 }
668
669 #[test]
670 fn test_invalid_v2_response_size_smaller_than_specified_header_size() {
671 const INVALID_RESPONSE: [u8; 28] = [
672 0x1c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
673 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
674 ];
675
676 let result = parse_response_header(&INVALID_RESPONSE);
677 assert!(result.is_err());
678 assert_eq!(
679 result.unwrap_err().to_string(),
680 Error::ResponseHeaderInvalidFormat {
681 response_size: 0x1c
682 }
683 .to_string()
684 );
685 }
686
687 #[test]
688 fn test_failed_response_with_retryable_error() {
689 const INVALID_RESPONSE: [u8; 42] = [
691 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x04, 0x00, 0x00, 0x93, 0x01,
692 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
693 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
694 ];
695
696 let result = parse_response_header(&INVALID_RESPONSE);
697 assert!(result.is_err());
698 assert_eq!(
699 result.unwrap_err().to_string(),
700 Error::Attestation {
701 igvm_error_code: 1103,
702 http_status_code: 403,
703 retry_signal: true
704 }
705 .to_string()
706 );
707 }
708
709 #[test]
710 fn test_failed_response_with_non_retryable_error() {
711 const INVALID_RESPONSE: [u8; 42] = [
713 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x04, 0x00, 0x00, 0xf7, 0x01,
714 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
715 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
716 ];
717
718 let result = parse_response_header(&INVALID_RESPONSE);
719 assert!(result.is_err());
720 assert_eq!(
721 result.unwrap_err().to_string(),
722 Error::Attestation {
723 igvm_error_code: 1103,
724 http_status_code: 503,
725 retry_signal: false
726 }
727 .to_string()
728 );
729 }
730}