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,"interactive-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 interactive_console_enabled: false,
497 secure_boot: false,
498 tpm_enabled: false,
499 tpm_persisted: false,
500 filtered_vpci_devices_allowed: true,
501 vm_unique_id: String::new(),
502 };
503 let result = serde_json::to_string(&attestation_vm_config);
504 assert!(result.is_ok());
505
506 let vm_config = result.unwrap();
507 assert_eq!(vm_config, EXPECTED_JWK);
508 }
509
510 #[test]
511 fn test_vm_configuration_with_time() {
512 const EXPECTED_JWK: &str = r#"{"current-time":1691103220,"root-cert-thumbprint":"","console-enabled":false,"interactive-console-enabled":false,"secure-boot":false,"tpm-enabled":false,"tpm-persisted":false,"filtered-vpci-devices-allowed":true,"vmUniqueId":""}"#;
513
514 let attestation_vm_config = AttestationVmConfig {
515 current_time: None,
516 root_cert_thumbprint: String::new(),
517 console_enabled: false,
518 interactive_console_enabled: false,
519 secure_boot: false,
520 tpm_enabled: false,
521 tpm_persisted: false,
522 filtered_vpci_devices_allowed: true,
523 vm_unique_id: String::new(),
524 };
525 let attestation_vm_config =
526 attestation_vm_config_with_time(&attestation_vm_config, 1691103220);
527 let result = serde_json::to_string(&attestation_vm_config);
528 assert!(result.is_ok());
529
530 let vm_config = result.unwrap();
531 assert_eq!(vm_config, EXPECTED_JWK);
532 }
533
534 #[test]
535 fn test_empty_response() {
536 let result = parse_response_header(&[]);
537 assert!(result.is_err());
538 assert_eq!(
539 result.unwrap_err().to_string(),
540 Error::ResponseSizeTooSmall { response_size: 0 }.to_string()
541 );
542 }
543
544 #[test]
545 fn test_invalid_response_size_smaller_than_header_size() {
546 const INVALID_RESPONSE: [u8; 4] = [0x04, 0x00, 0x00, 0x00];
547 let result = parse_response_header(&INVALID_RESPONSE);
548 assert!(result.is_err());
549 assert_eq!(
550 result.unwrap_err().to_string(),
551 Error::ResponseSizeTooSmall { response_size: 4 }.to_string()
552 );
553 }
554
555 #[test]
556 fn test_valid_v1_response_size_match() {
557 const VALID_RESPONSE: [u8; 42] = [
558 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
559 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
560 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
561 ];
562
563 let result = parse_response_header(&VALID_RESPONSE);
564 assert!(result.is_ok());
565 let header = result.unwrap();
566 assert_eq!(VALID_RESPONSE.len(), header.data_size as usize);
567 assert_eq!(IgvmAttestResponseVersion::VERSION_1, header.version);
568 }
569
570 #[test]
571 fn test_valid_v2_response_size_match() {
572 const VALID_RESPONSE: [u8; 42] = [
573 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
574 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
575 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
576 ];
577
578 let result = parse_response_header(&VALID_RESPONSE);
579 assert!(result.is_ok());
580 let header = result.unwrap();
581 assert_eq!(VALID_RESPONSE.len(), header.data_size as usize);
582 assert_eq!(IgvmAttestResponseVersion::VERSION_2, header.version);
583 }
584
585 #[test]
586 fn test_valid_v1_response_size_smaller_than_specified() {
587 const VALID_RESPONSE: [u8; 42] = [
588 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
589 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
590 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
591 ];
592
593 let header = parse_response_header(&VALID_RESPONSE);
594 assert!(header.is_ok());
595 assert_eq!(0x29, header.unwrap().data_size as usize);
596 }
597
598 #[test]
599 fn test_valid_v2_response_size_smaller_than_specified() {
600 const VALID_RESPONSE: [u8; 42] = [
601 0x29, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
602 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
603 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
604 ];
605
606 let header = parse_response_header(&VALID_RESPONSE);
607 assert!(header.is_ok());
608 assert_eq!(0x29, header.unwrap().data_size as usize);
609 }
610
611 #[test]
612 fn test_invalid_v1_response_size() {
613 const INVALID_RESPONSE: [u8; 42] = [
614 0x2b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
615 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
616 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
617 ];
618
619 let result = parse_response_header(&INVALID_RESPONSE);
620 assert!(result.is_err());
621 assert_eq!(
622 result.unwrap_err().to_string(),
623 Error::ResponseSizeMismatch {
624 size: INVALID_RESPONSE.len(),
625 specified_size: 0x2b
626 }
627 .to_string()
628 );
629 }
630
631 #[test]
632 fn test_invalid_v2_response_size() {
633 const INVALID_RESPONSE: [u8; 42] = [
634 0x2b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
635 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
636 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
637 ];
638
639 let result = parse_response_header(&INVALID_RESPONSE);
640 assert!(result.is_err());
641 assert_eq!(
642 result.unwrap_err().to_string(),
643 Error::ResponseSizeMismatch {
644 size: INVALID_RESPONSE.len(),
645 specified_size: 0x2b
646 }
647 .to_string()
648 );
649 }
650
651 #[test]
652 fn test_invalid_header_version() {
653 const INVALID_RESPONSE: [u8; 42] = [
654 0x2a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
655 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
656 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
657 ];
658
659 let result = parse_response_header(&INVALID_RESPONSE);
660 assert!(result.is_err());
661 assert_eq!(
662 result.unwrap_err().to_string(),
663 Error::InvalidResponseHeaderVersion {
664 version: IgvmAttestResponseVersion(3),
665 latest_version: IGVM_ATTEST_RESPONSE_CURRENT_VERSION
666 }
667 .to_string()
668 );
669 }
670
671 #[test]
672 fn test_invalid_v2_response_size_smaller_than_specified_header_size() {
673 const INVALID_RESPONSE: [u8; 28] = [
674 0x1c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
675 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
676 ];
677
678 let result = parse_response_header(&INVALID_RESPONSE);
679 assert!(result.is_err());
680 assert_eq!(
681 result.unwrap_err().to_string(),
682 Error::ResponseHeaderInvalidFormat {
683 response_size: 0x1c
684 }
685 .to_string()
686 );
687 }
688
689 #[test]
690 fn test_failed_response_with_retryable_error() {
691 const INVALID_RESPONSE: [u8; 42] = [
693 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x04, 0x00, 0x00, 0x93, 0x01,
694 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
695 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
696 ];
697
698 let result = parse_response_header(&INVALID_RESPONSE);
699 assert!(result.is_err());
700 assert_eq!(
701 result.unwrap_err().to_string(),
702 Error::Attestation {
703 igvm_error_code: 1103,
704 http_status_code: 403,
705 retry_signal: true
706 }
707 .to_string()
708 );
709 }
710
711 #[test]
712 fn test_failed_response_with_non_retryable_error() {
713 const INVALID_RESPONSE: [u8; 42] = [
715 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x04, 0x00, 0x00, 0xf7, 0x01,
716 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
717 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
718 ];
719
720 let result = parse_response_header(&INVALID_RESPONSE);
721 assert!(result.is_err());
722 assert_eq!(
723 result.unwrap_err().to_string(),
724 Error::Attestation {
725 igvm_error_code: 1103,
726 http_status_code: 503,
727 retry_signal: false
728 }
729 .to_string()
730 );
731 }
732}