1use openhcl_attestation_protocol::igvm_attest::get::AK_CERT_RESPONSE_HEADER_VERSION;
8use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestAkCertResponseHeader;
9use thiserror::Error;
10use zerocopy::FromBytes;
11
12#[derive(Debug, Error)]
14pub enum AkCertError {
15 #[error(
16 "AK cert response is too small to parse. Found {size} bytes but expected at least {minimum_size}"
17 )]
18 SizeTooSmall { size: usize, minimum_size: usize },
19 #[error(
20 "AK cert response size {specified_size} specified in the header is larger then the actual size {size}"
21 )]
22 SizeMismatch { size: usize, specified_size: usize },
23 #[error(
24 "AK cert response header version {version} does match the expected version {expected_version}"
25 )]
26 HeaderVersionMismatch { version: u32, expected_version: u32 },
27}
28
29pub fn parse_response(response: &[u8]) -> Result<Vec<u8>, AkCertError> {
33 const HEADER_SIZE: usize = size_of::<IgvmAttestAkCertResponseHeader>();
34
35 let header = IgvmAttestAkCertResponseHeader::read_from_prefix(response)
36 .map_err(|_| AkCertError::SizeTooSmall {
37 size: response.len(),
38 minimum_size: HEADER_SIZE,
39 })? .0;
41
42 let size = header.data_size as usize;
43 if size > response.len() {
44 Err(AkCertError::SizeMismatch {
45 size: response.len(),
46 specified_size: size,
47 })?
48 }
49
50 if header.version != AK_CERT_RESPONSE_HEADER_VERSION {
51 Err(AkCertError::HeaderVersionMismatch {
52 version: header.version,
53 expected_version: AK_CERT_RESPONSE_HEADER_VERSION,
54 })?
55 }
56
57 Ok(response[HEADER_SIZE..size].to_vec())
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn test_undersized_response() {
66 const HEADER_SIZE: usize = size_of::<IgvmAttestAkCertResponseHeader>();
67 let properly_sized_response: [u8; HEADER_SIZE] = [1; HEADER_SIZE];
68 let undersized_response = &properly_sized_response[..HEADER_SIZE - 1];
69
70 let result = parse_response(&[]);
72 assert!(result.is_err());
73 assert_eq!(
74 result.unwrap_err().to_string(),
75 "AK cert response is too small to parse. Found 0 bytes but expected at least 8"
76 );
77
78 let undersized_parse_ = parse_response(undersized_response);
80 assert!(undersized_parse_.is_err());
81 assert_eq!(
82 undersized_parse_.unwrap_err().to_string(),
83 format!(
84 "AK cert response is too small to parse. Found {} bytes but expected at least {}",
85 HEADER_SIZE - 1,
86 HEADER_SIZE
87 )
88 );
89
90 let properly_sized_parse = parse_response(&properly_sized_response);
93 assert!(
94 !properly_sized_parse
95 .unwrap_err()
96 .to_string()
97 .starts_with("AK cert response is too small to parse"),
98 );
99 }
100
101 #[test]
102 fn test_valid_response_size_match() {
103 const VALID_RESPONSE: [u8; 56] = [
104 0x38, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
105 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
106 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
107 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x25,
108 ];
109
110 const HEADER_SIZE: usize = size_of::<IgvmAttestAkCertResponseHeader>();
111
112 let result = IgvmAttestAkCertResponseHeader::read_from_prefix(&VALID_RESPONSE);
113 assert!(result.is_ok());
114 let header = result.unwrap().0;
115
116 let result = parse_response(&VALID_RESPONSE);
117 assert!(result.is_ok());
118
119 let payload = result.unwrap();
120 assert_eq!(payload.len(), header.data_size as usize - HEADER_SIZE);
121 assert_eq!(
122 payload,
123 &VALID_RESPONSE[HEADER_SIZE..header.data_size as usize]
124 );
125 }
126
127 #[test]
128 fn test_valid_response_size_smaller_than_specified() {
129 const VALID_RESPONSE: [u8; 56] = [
130 0x37, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
131 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
132 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
133 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x25,
134 ];
135
136 const HEADER_SIZE: usize = size_of::<IgvmAttestAkCertResponseHeader>();
137
138 let result = IgvmAttestAkCertResponseHeader::read_from_prefix(&VALID_RESPONSE);
139 assert!(result.is_ok());
140 let header = result.unwrap().0;
141
142 let result = parse_response(&VALID_RESPONSE);
143 assert!(result.is_ok());
144
145 let payload = result.unwrap();
146 assert_eq!(payload.len(), header.data_size as usize - HEADER_SIZE);
147 assert_eq!(
148 payload,
149 &VALID_RESPONSE[HEADER_SIZE..header.data_size as usize]
150 );
151 }
152
153 #[test]
154 fn test_invalid_header_version() {
155 const INVALID_RESPONSE: [u8; 56] = [
156 0x38, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
157 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
158 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
159 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x25,
160 ];
161
162 let result = parse_response(&INVALID_RESPONSE);
163 assert!(result.is_err());
164 }
165
166 #[test]
167 fn test_invalid_response_size() {
168 const INVALID_RESPONSE: [u8; 56] = [
169 0x39, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82,
170 0x02, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x3b, 0xa3, 0x33, 0x97, 0xef,
171 0x2f, 0x9e, 0xef, 0xbd, 0x35, 0x5e, 0xda, 0xdd, 0x27, 0x38, 0x42, 0x30, 0x0d, 0x06,
172 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x25,
173 ];
174
175 let result = parse_response(&INVALID_RESPONSE);
176 assert!(result.is_err());
177 }
178}