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}), skip hw unsealing recommendation ({skip_hw_unsealing_signal})"
58 )]
59 Attestation {
60 igvm_error_code: u32,
61 http_status_code: u32,
62 retry_signal: bool,
63 skip_hw_unsealing_signal: bool,
64 },
65}
66
67pub enum ReportType {
69 Vbs,
71 Snp,
73 Tdx,
75 Tvm,
77}
78
79impl ReportType {
80 fn to_external_type(&self) -> IgvmAttestReportType {
82 match self {
83 Self::Vbs => IgvmAttestReportType::VBS_VM_REPORT,
84 Self::Snp => IgvmAttestReportType::SNP_VM_REPORT,
85 Self::Tdx => IgvmAttestReportType::TDX_VM_REPORT,
86 Self::Tvm => IgvmAttestReportType::TVM_REPORT,
87 }
88 }
89}
90
91pub struct IgvmAttestRequestHelper {
93 request_type: IgvmAttestRequestType,
95 report_type: ReportType,
97 runtime_claims: Vec<u8>,
99 runtime_claims_hash: [u8; tee_call::REPORT_DATA_SIZE],
102 hash_type: IgvmAttestHashType,
104}
105
106impl IgvmAttestRequestHelper {
107 pub fn prepare_key_release_request(
109 tee_type: TeeType,
110 rsa_exponent: &[u8],
111 rsa_modulus: &[u8],
112 host_time: i64,
113 attestation_vm_config: &AttestationVmConfig,
114 ) -> Self {
115 let report_type = match tee_type {
116 TeeType::Snp => ReportType::Snp,
117 TeeType::Tdx => ReportType::Tdx,
118 TeeType::Vbs => ReportType::Vbs,
119 };
120
121 let attestation_vm_config =
122 attestation_vm_config_with_time(attestation_vm_config, host_time);
123 let runtime_claims =
124 openhcl_attestation_protocol::igvm_attest::get::runtime_claims::RuntimeClaims::key_release_request_runtime_claims(rsa_exponent, rsa_modulus, &attestation_vm_config);
125 let runtime_claims = runtime_claims_to_bytes(&runtime_claims);
126
127 let hash_type = IgvmAttestHashType::SHA_256;
128 let hash = crypto::sha_256::sha_256(runtime_claims.as_bytes());
129 let mut runtime_claims_hash = [0u8; tee_call::REPORT_DATA_SIZE];
130 runtime_claims_hash[0..hash.len()].copy_from_slice(&hash);
131
132 Self {
133 request_type: IgvmAttestRequestType::KEY_RELEASE_REQUEST,
134 report_type,
135 runtime_claims,
136 runtime_claims_hash,
137 hash_type,
138 }
139 }
140
141 pub fn prepare_ak_cert_request(
143 tee_type: Option<TeeType>,
144 ak_pub_exponent: &[u8],
145 ak_pub_modulus: &[u8],
146 ek_pub_exponent: &[u8],
147 ek_pub_modulus: &[u8],
148 attestation_vm_config: &AttestationVmConfig,
149 guest_input: &[u8],
150 ) -> Self {
151 let report_type = match tee_type {
152 Some(TeeType::Snp) => ReportType::Snp,
153 Some(TeeType::Tdx) => ReportType::Tdx,
154 Some(TeeType::Vbs) => ReportType::Vbs,
155 None => ReportType::Tvm,
156 };
157
158 let runtime_claims =
159 openhcl_attestation_protocol::igvm_attest::get::runtime_claims::RuntimeClaims::ak_cert_runtime_claims(
160 ak_pub_exponent,
161 ak_pub_modulus,
162 ek_pub_exponent,
163 ek_pub_modulus,
164 attestation_vm_config,
165 guest_input,
166 );
167
168 let runtime_claims = runtime_claims_to_bytes(&runtime_claims);
169
170 let hash_type = IgvmAttestHashType::SHA_256;
171 let hash = crypto::sha_256::sha_256(runtime_claims.as_bytes());
172 let mut runtime_claims_hash = [0u8; tee_call::REPORT_DATA_SIZE];
173 runtime_claims_hash[0..hash.len()].copy_from_slice(&hash);
174
175 Self {
176 request_type: IgvmAttestRequestType::AK_CERT_REQUEST,
177 report_type,
178 runtime_claims,
179 runtime_claims_hash,
180 hash_type,
181 }
182 }
183
184 pub fn get_runtime_claims_hash(&self) -> &[u8; tee_call::REPORT_DATA_SIZE] {
186 &self.runtime_claims_hash
187 }
188
189 pub fn set_request_type(&mut self, request_type: IgvmAttestRequestType) {
191 self.request_type = request_type
192 }
193
194 pub fn create_request(
196 &self,
197 version: IgvmAttestRequestVersion,
198 attestation_report: &[u8],
199 ) -> Result<Vec<u8>, Error> {
200 create_request(
201 version,
202 self.request_type,
203 &self.runtime_claims,
204 attestation_report,
205 &self.report_type,
206 self.hash_type,
207 )
208 }
209}
210
211pub fn parse_response_header(response: &[u8]) -> Result<IgvmAttestCommonResponseHeader, Error> {
213 let header = IgvmAttestCommonResponseHeader::read_from_prefix(response)
216 .map_err(|_| Error::ResponseSizeTooSmall {
217 response_size: response.len(),
218 })?
219 .0; if header.data_size as usize > response.len() {
223 Err(Error::ResponseSizeMismatch {
224 size: response.len(),
225 specified_size: header.data_size as usize,
226 })?
227 }
228 if header.version > IGVM_ATTEST_RESPONSE_CURRENT_VERSION {
229 Err(Error::InvalidResponseHeaderVersion {
230 version: header.version,
231 latest_version: IGVM_ATTEST_RESPONSE_CURRENT_VERSION,
232 })?
233 }
234
235 if header.version >= IgvmAttestResponseVersion::VERSION_2 {
237 let igvm_error_info = IgvmErrorInfo::read_from_prefix(
239 &response[size_of::<IgvmAttestCommonResponseHeader>()..],
240 )
241 .map_err(|_| Error::ResponseHeaderInvalidFormat {
242 response_size: response.len(),
243 })?
244 .0; if 0 != igvm_error_info.error_code {
247 Err(Error::Attestation {
248 igvm_error_code: igvm_error_info.error_code,
249 http_status_code: igvm_error_info.http_status_code,
250 retry_signal: igvm_error_info.igvm_signal.retry(),
251 skip_hw_unsealing_signal: igvm_error_info.igvm_signal.skip_hw_unsealing(),
252 })?
253 }
254 }
255 Ok(IgvmAttestCommonResponseHeader {
256 data_size: header.data_size,
257 version: header.version,
258 })
259}
260
261fn create_request(
265 version: IgvmAttestRequestVersion,
266 request_type: IgvmAttestRequestType,
267 runtime_claims: &[u8],
268 attestation_report: &[u8],
269 report_type: &ReportType,
270 hash_type: IgvmAttestHashType,
271) -> Result<Vec<u8>, Error> {
272 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestBase;
273 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestData;
274 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestDataExt;
275 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestHeader;
276
277 let expected_report_size = get_report_size(report_type);
278 if attestation_report.len() != expected_report_size {
279 Err(Error::InvalidAttestationReportSize {
280 report_size: attestation_report.len(),
281 expected_size: expected_report_size,
282 })?
283 }
284
285 let runtime_claims_len = runtime_claims.len();
286 let include_extension = version >= IgvmAttestRequestVersion::VERSION_2;
288 let extension_size = if include_extension {
289 size_of::<IgvmAttestRequestDataExt>()
290 } else {
291 0
292 };
293 let report_size = size_of::<IgvmAttestRequestBase>() + extension_size + runtime_claims_len;
294 let user_data_size = size_of::<IgvmAttestRequestData>() + extension_size + runtime_claims_len;
295 let mut request = IgvmAttestRequestBase::new_zeroed();
296
297 request.header = IgvmAttestRequestHeader::new(report_size as u32, request_type, 0);
298
299 request.attestation_report[..attestation_report.len()].copy_from_slice(attestation_report);
300
301 request.request_data = IgvmAttestRequestData::new(
302 version,
303 user_data_size as u32,
304 report_type.to_external_type(),
305 hash_type,
306 runtime_claims_len as u32,
307 );
308
309 let mut buffer = Vec::with_capacity(report_size);
310 buffer.extend_from_slice(request.as_bytes());
311
312 if include_extension {
313 let capability_bitmap = IgvmCapabilityBitMap::new()
314 .with_error_code(true)
315 .with_retry(true)
316 .with_skip_hw_unsealing(true);
317 let ext = IgvmAttestRequestDataExt::new(capability_bitmap);
318 buffer.extend_from_slice(ext.as_bytes());
319 }
320
321 buffer.extend_from_slice(runtime_claims);
322
323 Ok(buffer)
324}
325
326fn get_report_size(report_type: &ReportType) -> usize {
328 match report_type {
329 ReportType::Vbs => openhcl_attestation_protocol::igvm_attest::get::VBS_VM_REPORT_SIZE,
330 ReportType::Snp => openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE,
331 ReportType::Tdx => openhcl_attestation_protocol::igvm_attest::get::TDX_VM_REPORT_SIZE,
332 ReportType::Tvm => openhcl_attestation_protocol::igvm_attest::get::TVM_REPORT_SIZE,
333 }
334}
335
336fn attestation_vm_config_with_time(
338 vm_config: &AttestationVmConfig,
339 host_epoch: i64,
340) -> AttestationVmConfig {
341 let mut vm_config = vm_config.clone();
342 vm_config.current_time = Some(host_epoch);
343 vm_config
344}
345
346fn runtime_claims_to_bytes(
348 runtime_claims: &openhcl_attestation_protocol::igvm_attest::get::runtime_claims::RuntimeClaims,
349) -> Vec<u8> {
350 let runtime_claims = serde_json::to_string(runtime_claims).expect("JSON serialization failed");
351 runtime_claims.as_bytes().to_vec()
352}
353
354#[cfg(test)]
355mod tests {
356 use super::*;
357
358 #[test]
359 fn test_create_request() {
360 use openhcl_attestation_protocol::igvm_attest::get::IGVM_ATTEST_REQUEST_CURRENT_VERSION;
361
362 let result = create_request(
363 IGVM_ATTEST_REQUEST_CURRENT_VERSION,
364 IgvmAttestRequestType::AK_CERT_REQUEST,
365 &[],
366 &[0u8; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE],
367 &ReportType::Snp,
368 IgvmAttestHashType::SHA_256,
369 );
370 assert!(result.is_ok());
371
372 let result = create_request(
373 IGVM_ATTEST_REQUEST_CURRENT_VERSION,
374 IgvmAttestRequestType::AK_CERT_REQUEST,
375 &[],
376 &[0u8; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE + 1],
377 &ReportType::Snp,
378 IgvmAttestHashType::SHA_256,
379 );
380 assert!(result.is_err());
381 }
382
383 #[test]
384 fn test_create_request_version1_no_extension() {
385 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestBase;
386 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestVersion;
387
388 let runtime_claims = vec![1u8, 2, 3];
389 let attestation_report =
390 vec![0u8; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE];
391
392 let buffer = create_request(
393 IgvmAttestRequestVersion::VERSION_1,
394 IgvmAttestRequestType::AK_CERT_REQUEST,
395 &runtime_claims,
396 &attestation_report,
397 &ReportType::Snp,
398 IgvmAttestHashType::SHA_256,
399 )
400 .expect("request generation");
401
402 let (request, _) =
403 IgvmAttestRequestBase::read_from_prefix(&buffer).expect("parse IgvmAttestRequest");
404 assert_eq!(
405 request.request_data.version,
406 IgvmAttestRequestVersion::VERSION_1
407 );
408
409 let expected_size = (size_of::<
410 openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestData,
411 >() + runtime_claims.len()) as u32;
412 assert_eq!(request.request_data.data_size, expected_size);
413
414 let header_size = size_of::<IgvmAttestRequestBase>();
415 assert_eq!(
416 buffer.len(),
417 header_size + runtime_claims.len(),
418 "no extension appended for version 1"
419 );
420 assert_eq!(&buffer[header_size..], runtime_claims.as_slice());
421 }
422
423 #[test]
424 fn test_create_request_version2_with_extension() {
425 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestBase;
426 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestDataExt;
427 use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestVersion;
428
429 let runtime_claims = vec![4u8, 5, 6, 7];
430 let attestation_report =
431 vec![0u8; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE];
432
433 let buffer = create_request(
434 IgvmAttestRequestVersion::VERSION_2,
435 IgvmAttestRequestType::AK_CERT_REQUEST,
436 &runtime_claims,
437 &attestation_report,
438 &ReportType::Snp,
439 IgvmAttestHashType::SHA_256,
440 )
441 .expect("request generation");
442
443 let (request, _) =
444 IgvmAttestRequestBase::read_from_prefix(&buffer).expect("parse IgvmAttestRequest");
445 assert_eq!(
446 request.request_data.version,
447 IgvmAttestRequestVersion::VERSION_2
448 );
449
450 let expected_extension_size = size_of::<IgvmAttestRequestDataExt>();
451 let expected_size = (size_of::<
452 openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestData,
453 >() + expected_extension_size
454 + runtime_claims.len()) as u32;
455 assert_eq!(request.request_data.data_size, expected_size);
456
457 let header_size = size_of::<IgvmAttestRequestBase>();
458 let ext_offset = header_size;
459
460 let (ext, _) = IgvmAttestRequestDataExt::read_from_prefix(&buffer[ext_offset..])
461 .expect("parse IgvmAttestRequestDataExt");
462 assert!(ext.capability_bitmap.error_code());
463 assert!(ext.capability_bitmap.retry());
464 assert!(ext.capability_bitmap.skip_hw_unsealing());
465
466 assert_eq!(
467 buffer.len(),
468 header_size + expected_extension_size + runtime_claims.len()
469 );
470 assert_eq!(
471 &buffer[header_size + expected_extension_size..],
472 runtime_claims.as_slice()
473 );
474 }
475
476 #[test]
477 fn test_transfer_key_jwk() {
478 const EXPECTED_JWK: &str = r#"[{"kid":"HCLTransferKey","key_ops":["encrypt"],"kty":"RSA","e":"RVhQT05FTlQ","n":"TU9EVUxVUw"}]"#;
479
480 let rsa_jwk = openhcl_attestation_protocol::igvm_attest::get::runtime_claims::RsaJwk::get_transfer_key_jwks(
481 b"EXPONENT",
482 b"MODULUS",
483 );
484
485 let result = serde_json::to_string(&rsa_jwk);
486 assert!(result.is_ok());
487
488 let transfer_key_jwk = result.unwrap();
489 assert_eq!(transfer_key_jwk, EXPECTED_JWK);
490 }
491
492 #[test]
493 fn test_vm_configuration_no_time() {
494 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":""}"#;
495
496 let attestation_vm_config = AttestationVmConfig {
497 current_time: None,
498 root_cert_thumbprint: String::new(),
499 console_enabled: false,
500 interactive_console_enabled: false,
501 secure_boot: false,
502 tpm_enabled: false,
503 tpm_persisted: false,
504 filtered_vpci_devices_allowed: true,
505 vm_unique_id: String::new(),
506 };
507 let result = serde_json::to_string(&attestation_vm_config);
508 assert!(result.is_ok());
509
510 let vm_config = result.unwrap();
511 assert_eq!(vm_config, EXPECTED_JWK);
512 }
513
514 #[test]
515 fn test_vm_configuration_with_time() {
516 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":""}"#;
517
518 let attestation_vm_config = AttestationVmConfig {
519 current_time: None,
520 root_cert_thumbprint: String::new(),
521 console_enabled: false,
522 interactive_console_enabled: false,
523 secure_boot: false,
524 tpm_enabled: false,
525 tpm_persisted: false,
526 filtered_vpci_devices_allowed: true,
527 vm_unique_id: String::new(),
528 };
529 let attestation_vm_config =
530 attestation_vm_config_with_time(&attestation_vm_config, 1691103220);
531 let result = serde_json::to_string(&attestation_vm_config);
532 assert!(result.is_ok());
533
534 let vm_config = result.unwrap();
535 assert_eq!(vm_config, EXPECTED_JWK);
536 }
537
538 #[test]
539 fn test_empty_response() {
540 let result = parse_response_header(&[]);
541 assert!(result.is_err());
542 assert_eq!(
543 result.unwrap_err().to_string(),
544 Error::ResponseSizeTooSmall { response_size: 0 }.to_string()
545 );
546 }
547
548 #[test]
549 fn test_invalid_response_size_smaller_than_header_size() {
550 const INVALID_RESPONSE: [u8; 4] = [0x04, 0x00, 0x00, 0x00];
551 let result = parse_response_header(&INVALID_RESPONSE);
552 assert!(result.is_err());
553 assert_eq!(
554 result.unwrap_err().to_string(),
555 Error::ResponseSizeTooSmall { response_size: 4 }.to_string()
556 );
557 }
558
559 #[test]
560 fn test_valid_v1_response_size_match() {
561 const VALID_RESPONSE: [u8; 42] = [
562 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
563 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
564 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
565 ];
566
567 let result = parse_response_header(&VALID_RESPONSE);
568 assert!(result.is_ok());
569 let header = result.unwrap();
570 assert_eq!(VALID_RESPONSE.len(), header.data_size as usize);
571 assert_eq!(IgvmAttestResponseVersion::VERSION_1, header.version);
572 }
573
574 #[test]
575 fn test_valid_v2_response_size_match() {
576 const VALID_RESPONSE: [u8; 42] = [
577 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
578 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
579 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
580 ];
581
582 let result = parse_response_header(&VALID_RESPONSE);
583 assert!(result.is_ok());
584 let header = result.unwrap();
585 assert_eq!(VALID_RESPONSE.len(), header.data_size as usize);
586 assert_eq!(IgvmAttestResponseVersion::VERSION_2, header.version);
587 }
588
589 #[test]
590 fn test_valid_v1_response_size_smaller_than_specified() {
591 const VALID_RESPONSE: [u8; 42] = [
592 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
593 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
594 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
595 ];
596
597 let header = parse_response_header(&VALID_RESPONSE);
598 assert!(header.is_ok());
599 assert_eq!(0x29, header.unwrap().data_size as usize);
600 }
601
602 #[test]
603 fn test_valid_v2_response_size_smaller_than_specified() {
604 const VALID_RESPONSE: [u8; 42] = [
605 0x29, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
606 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
607 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
608 ];
609
610 let header = parse_response_header(&VALID_RESPONSE);
611 assert!(header.is_ok());
612 assert_eq!(0x29, header.unwrap().data_size as usize);
613 }
614
615 #[test]
616 fn test_invalid_v1_response_size() {
617 const INVALID_RESPONSE: [u8; 42] = [
618 0x2b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
619 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
620 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
621 ];
622
623 let result = parse_response_header(&INVALID_RESPONSE);
624 assert!(result.is_err());
625 assert_eq!(
626 result.unwrap_err().to_string(),
627 Error::ResponseSizeMismatch {
628 size: INVALID_RESPONSE.len(),
629 specified_size: 0x2b
630 }
631 .to_string()
632 );
633 }
634
635 #[test]
636 fn test_invalid_v2_response_size() {
637 const INVALID_RESPONSE: [u8; 42] = [
638 0x2b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
639 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
640 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
641 ];
642
643 let result = parse_response_header(&INVALID_RESPONSE);
644 assert!(result.is_err());
645 assert_eq!(
646 result.unwrap_err().to_string(),
647 Error::ResponseSizeMismatch {
648 size: INVALID_RESPONSE.len(),
649 specified_size: 0x2b
650 }
651 .to_string()
652 );
653 }
654
655 #[test]
656 fn test_invalid_header_version() {
657 const INVALID_RESPONSE: [u8; 42] = [
658 0x2a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
659 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
660 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
661 ];
662
663 let result = parse_response_header(&INVALID_RESPONSE);
664 assert!(result.is_err());
665 assert_eq!(
666 result.unwrap_err().to_string(),
667 Error::InvalidResponseHeaderVersion {
668 version: IgvmAttestResponseVersion(3),
669 latest_version: IGVM_ATTEST_RESPONSE_CURRENT_VERSION
670 }
671 .to_string()
672 );
673 }
674
675 #[test]
676 fn test_invalid_v2_response_size_smaller_than_specified_header_size() {
677 const INVALID_RESPONSE: [u8; 28] = [
678 0x1c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
679 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
680 ];
681
682 let result = parse_response_header(&INVALID_RESPONSE);
683 assert!(result.is_err());
684 assert_eq!(
685 result.unwrap_err().to_string(),
686 Error::ResponseHeaderInvalidFormat {
687 response_size: 0x1c
688 }
689 .to_string()
690 );
691 }
692
693 #[test]
694 fn test_failed_response_with_retryable_error() {
695 const INVALID_RESPONSE: [u8; 42] = [
697 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x04, 0x00, 0x00, 0x93, 0x01,
698 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
699 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
700 ];
701
702 let result = parse_response_header(&INVALID_RESPONSE);
703 assert!(result.is_err());
704 assert_eq!(
705 result.unwrap_err().to_string(),
706 Error::Attestation {
707 igvm_error_code: 1103,
708 http_status_code: 403,
709 retry_signal: true,
710 skip_hw_unsealing_signal: false
711 }
712 .to_string()
713 );
714 }
715
716 #[test]
717 fn test_failed_response_with_non_retryable_error() {
718 const INVALID_RESPONSE: [u8; 42] = [
720 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x04, 0x00, 0x00, 0xf7, 0x01,
721 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
722 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
723 ];
724
725 let result = parse_response_header(&INVALID_RESPONSE);
726 assert!(result.is_err());
727 assert_eq!(
728 result.unwrap_err().to_string(),
729 Error::Attestation {
730 igvm_error_code: 1103,
731 http_status_code: 503,
732 retry_signal: false,
733 skip_hw_unsealing_signal: false
734 }
735 .to_string()
736 );
737 }
738
739 #[test]
740 fn test_failed_response_with_skip_hw_unsealing_signal() {
741 const INVALID_RESPONSE: [u8; 42] = [
744 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x04, 0x00, 0x00, 0x90, 0x01,
745 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
746 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
747 ];
748
749 let result = parse_response_header(&INVALID_RESPONSE);
750 assert!(result.is_err());
751 assert_eq!(
752 result.unwrap_err().to_string(),
753 Error::Attestation {
754 igvm_error_code: 1103,
755 http_status_code: 400,
756 retry_signal: true,
757 skip_hw_unsealing_signal: true
758 }
759 .to_string()
760 );
761 }
762
763 #[test]
764 fn test_failed_response_with_skip_hw_unsealing_only() {
765 const INVALID_RESPONSE: [u8; 42] = [
768 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x04, 0x00, 0x00, 0x90, 0x01,
769 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
770 0x00, 0x00, 0x00, 0x00, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
771 ];
772
773 let result = parse_response_header(&INVALID_RESPONSE);
774 assert!(result.is_err());
775 assert_eq!(
776 result.unwrap_err().to_string(),
777 Error::Attestation {
778 igvm_error_code: 1103,
779 http_status_code: 400,
780 retry_signal: false,
781 skip_hw_unsealing_signal: true
782 }
783 .to_string()
784 );
785 }
786}