Skip to main content

underhill_attestation/igvm_attest/
ak_cert.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! The module for `AK_CERT_REQUEST` request type that supports parsing the
5//! response.
6use crate::igvm_attest::Error as CommonError;
7use crate::igvm_attest::parse_response_header;
8use der::Decode;
9use der::Reader;
10use der::SliceReader;
11use der::Tag;
12use der::TagNumber;
13use der::Tagged;
14use der::asn1::AnyRef;
15use der::asn1::BitStringRef;
16use der::asn1::BmpString;
17use der::asn1::GeneralizedTime;
18use der::asn1::IntRef;
19use der::asn1::ObjectIdentifier;
20use der::asn1::OctetStringRef;
21use der::asn1::PrintableStringRef;
22use der::asn1::TeletexStringRef;
23use der::asn1::UtcTime;
24use der::asn1::Utf8StringRef;
25use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::AkCertMetadata;
26use thiserror::Error;
27use zerocopy::FromBytes;
28
29/// The certificate or renewal decision returned by the host.
30#[derive(Debug, PartialEq, Eq)]
31pub enum AkCertResponse {
32    /// Certificate bytes, possibly empty if the service is unavailable.
33    Certificate(Vec<u8>),
34    /// Keep the existing certificate.
35    NoRenewal,
36}
37
38/// An error extracting renewal metadata from a certificate.
39#[derive(Debug, Error)]
40pub enum AkCertMetadataError {
41    #[error("invalid AK certificate metadata")]
42    Decode(#[from] der::Error),
43    #[error("AK certificate has no {0} common name")]
44    MissingCommonName(&'static str),
45}
46
47/// Extract renewal hints, not a certificate trust decision.
48///
49/// TPM NV storage can contain padding or stale bytes after the DER certificate.
50/// Only metadata and its enclosing structure are decoded. Algorithm parameters,
51/// public key/signature contents, non-CN attributes, non-AKI extension values,
52/// and individual AKI GeneralName contents are opaque, bounded DER values.
53/// Do not replace this with the full X.509 decoder: its owned certificate and
54/// GeneralNames machinery significantly increases the OpenHCL binary size.
55pub fn parse_metadata(data: &[u8]) -> Result<AkCertMetadata, AkCertMetadataError> {
56    // Deliberately do not finish this reader: only bytes outside the certificate
57    // may be ignored. Every nested reader below must be consumed completely.
58    SliceReader::new(data)?.sequence(|certificate| {
59        let metadata = certificate.sequence(|tbs| {
60            let version = match optional_field(tbs, TagNumber(0).context_specific(true))? {
61                Some(field) => {
62                    let version = u8::from_der(field.value())?;
63                    // v1 is DEFAULT and must be omitted in DER.
64                    if !(1..=2).contains(&version) {
65                        return Err(der::Error::from(Tag::Integer.value_error()).into());
66                    }
67                    version
68                }
69                None => 0,
70            };
71            check_serial(tbs.decode()?)?;
72            skip_algorithm(tbs)?;
73            let issuer = common_name(tbs, "issuer")?;
74            let (not_before, not_after) = tbs.sequence(|validity| {
75                Ok::<_, der::Error>((
76                    time_content(validity.decode()?)?,
77                    time_content(validity.decode()?)?,
78                ))
79            })?;
80            let subject = common_name(tbs, "subject")?;
81            // SPKI: validate the envelope without decoding the key itself.
82            tbs.sequence(|spki| {
83                skip_algorithm(spki)?;
84                spki.decode::<AnyRef<'_>>()?
85                    .decode_as::<BitStringRef<'_>>()?;
86                Ok::<_, der::Error>(())
87            })?;
88            for number in [1, 2] {
89                if let Some(uid) = optional_field(tbs, TagNumber(number).context_specific(false))? {
90                    if version == 0 {
91                        return Err(der::Error::from(uid.tag().value_error()).into());
92                    }
93                    AnyRef::new(Tag::BitString, uid.value())?.decode_as::<BitStringRef<'_>>()?;
94                }
95            }
96            let authority = match optional_field(tbs, TagNumber(3).context_specific(true))? {
97                Some(field) => {
98                    if version != 2 {
99                        return Err(der::Error::from(field.tag().value_error()).into());
100                    }
101                    authority_key_identifier(field.value())?
102                }
103                None => None,
104            };
105            Ok::<_, AkCertMetadataError>(AkCertMetadata {
106                issuer,
107                subject,
108                not_before,
109                not_after,
110                authority: authority.map(hex::encode_upper).unwrap_or_default(),
111            })
112        })?;
113        skip_algorithm(certificate)?;
114        certificate
115            .decode::<AnyRef<'_>>()?
116            .decode_as::<BitStringRef<'_>>()?;
117        Ok(metadata)
118    })
119}
120
121/// Unlike the general context-specific decoder, never skip unknown fields.
122/// Reading optional fields in schema order and finishing the enclosing reader
123/// rejects duplicates, out-of-order fields, and incorrect constructed bits.
124fn optional_field<'a>(reader: &mut SliceReader<'a>, tag: Tag) -> der::Result<Option<AnyRef<'a>>> {
125    if !reader.is_finished() && Tag::peek(reader)? == tag {
126        reader.decode().map(Some)
127    } else {
128        Ok(None)
129    }
130}
131
132fn check_serial(serial: AnyRef<'_>) -> der::Result<()> {
133    // Match the previous X.509 profile's signed INTEGER and 21-byte limit.
134    if serial.decode_as::<IntRef<'_>>()?.as_bytes().len() > 21 {
135        return Err(Tag::Integer.value_error().into());
136    }
137    Ok(())
138}
139
140fn skip_algorithm(reader: &mut SliceReader<'_>) -> der::Result<()> {
141    reader.sequence(|algorithm| {
142        algorithm.decode::<ObjectIdentifier>()?;
143        if !algorithm.is_finished() {
144            algorithm.decode::<AnyRef<'_>>()?;
145        }
146        Ok(())
147    })
148}
149
150fn common_name(
151    reader: &mut SliceReader<'_>,
152    name: &'static str,
153) -> Result<String, AkCertMetadataError> {
154    const COMMON_NAME: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.5.4.3");
155    let mut cn = None;
156    reader.sequence(|rdns| {
157        while !rdns.is_finished() {
158            let set = rdns.decode::<AnyRef<'_>>()?;
159            set.tag().assert_eq(Tag::Set)?;
160            let mut attributes = SliceReader::new(set.value())?;
161            if attributes.is_finished() {
162                return Err(Tag::Set.value_error().into());
163            }
164            let mut previous = None;
165            while !attributes.is_finished() {
166                let encoded = attributes.tlv_bytes()?;
167                if previous.is_some_and(|previous| previous > encoded) {
168                    return Err(Tag::Set.non_canonical_error().into());
169                }
170                previous = Some(encoded);
171                AnyRef::from_der(encoded)?.sequence(|attribute| {
172                    let oid = attribute.decode::<ObjectIdentifier>()?;
173                    let value = attribute.decode::<AnyRef<'_>>()?;
174                    // Preserve Name::common_name's first-CN behavior. All other
175                    // attributes are borrowed, not allocated or interpreted.
176                    if oid == COMMON_NAME && cn.is_none() {
177                        cn = Some(value);
178                    }
179                    Ok::<_, der::Error>(())
180                })?;
181            }
182            attributes.finish()?;
183        }
184        Ok::<_, der::Error>(())
185    })?;
186    Ok(directory_string(
187        cn.ok_or(AkCertMetadataError::MissingCommonName(name))?,
188    )?)
189}
190
191fn directory_string(value: AnyRef<'_>) -> der::Result<String> {
192    // Match the previous DirectoryString's supported encodings, including der's
193    // ASCII-only Teletex support. Only the selected CN is decoded/allocated.
194    match value.tag() {
195        Tag::Utf8String => Ok(value.decode_as::<Utf8StringRef<'_>>()?.as_str().to_owned()),
196        Tag::PrintableString => Ok(value
197            .decode_as::<PrintableStringRef<'_>>()?
198            .as_str()
199            .to_owned()),
200        Tag::TeletexString => Ok(value
201            .decode_as::<TeletexStringRef<'_>>()?
202            .as_str()
203            .to_owned()),
204        Tag::BmpString => Ok(value.decode_as::<BmpString>()?.to_string()),
205        tag => Err(tag.value_error().into()),
206    }
207}
208
209fn authority_key_identifier(extensions: &[u8]) -> der::Result<Option<&[u8]>> {
210    const AUTHORITY_KEY_IDENTIFIER: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.5.29.35");
211    let mut seen = false;
212    let mut key = None;
213    AnyRef::from_der(extensions)?.sequence(|extensions| {
214        if extensions.is_finished() {
215            return Err(Tag::Sequence.value_error().into());
216        }
217        while !extensions.is_finished() {
218            extensions.sequence(|extension| {
219                let oid = extension.decode::<ObjectIdentifier>()?;
220                if let Some(critical) = optional_field(extension, Tag::Boolean)? {
221                    // FALSE is the default and must be omitted in DER.
222                    if !critical.decode_as::<bool>()? {
223                        return Err(Tag::Boolean.non_canonical_error().into());
224                    }
225                }
226                let value = extension.decode::<&OctetStringRef>()?;
227                if oid == AUTHORITY_KEY_IDENTIFIER {
228                    if seen {
229                        return Err(Tag::Sequence.value_error().into());
230                    }
231                    seen = true;
232                    key = decode_authority_key_identifier(value.as_bytes())?;
233                }
234                Ok::<_, der::Error>(())
235            })?;
236        }
237        Ok(key)
238    })
239}
240
241fn decode_authority_key_identifier(data: &[u8]) -> der::Result<Option<&[u8]>> {
242    AnyRef::from_der(data)?.sequence(|aki| {
243        let key = optional_field(aki, TagNumber(0).context_specific(false))?;
244        if let Some(issuer) = optional_field(aki, TagNumber(1).context_specific(true))? {
245            // Check the GeneralNames envelope, but deliberately do not instantiate
246            // the general X.509 name decoder (e.g. directoryName/otherName).
247            let mut names = SliceReader::new(issuer.value())?;
248            if names.is_finished() {
249                return Err(issuer.tag().value_error().into());
250            }
251            while !names.is_finished() {
252                let name = names.decode::<AnyRef<'_>>()?;
253                match name.tag() {
254                    Tag::ContextSpecific {
255                        number: TagNumber(0 | 3 | 4 | 5),
256                        constructed: true,
257                    }
258                    | Tag::ContextSpecific {
259                        number: TagNumber(1 | 2 | 6 | 7 | 8),
260                        constructed: false,
261                    } => {}
262                    tag => return Err(tag.value_error().into()),
263                }
264            }
265            names.finish()?;
266        }
267        if let Some(serial) = optional_field(aki, TagNumber(2).context_specific(false))? {
268            check_serial(AnyRef::new(Tag::Integer, serial.value())?)?;
269        }
270        Ok(key.map(|key| key.value()))
271    })
272}
273
274fn time_content(time: AnyRef<'_>) -> der::Result<String> {
275    match time.tag() {
276        Tag::UtcTime => {
277            time.decode_as::<UtcTime>()?;
278        }
279        Tag::GeneralizedTime => {
280            time.decode_as::<GeneralizedTime>()?;
281        }
282        tag => return Err(tag.value_error().into()),
283    }
284    // The DER time decoders validate the date and require ASCII digits followed
285    // by Z. Preserve that exact content, including the original year width.
286    Ok(core::str::from_utf8(time.value())
287        .map_err(|_| time.tag().value_error())?
288        .to_owned())
289}
290
291/// AkCertError is returned by parse_ak_cert_response() in emuplat/tpm.rs
292#[derive(Debug, Error)]
293pub enum AkCertError {
294    #[error(
295        "AK cert response is too small to parse. Found {size} bytes but expected at least {minimum_size}"
296    )]
297    SizeTooSmall { size: usize, minimum_size: usize },
298    #[error(
299        "AK cert response size {specified_size} specified in the header is larger then the actual size {size}"
300    )]
301    SizeMismatch { size: usize, specified_size: usize },
302    #[error(
303        "AK cert response header version {version} does match the expected version {expected_version}"
304    )]
305    HeaderVersionMismatch { version: u32, expected_version: u32 },
306    #[error("error in parsing response header")]
307    ParseHeader(#[source] CommonError),
308    #[error("invalid response header version: {0}")]
309    InvalidResponseVersion(u32),
310}
311
312/// Parse an `AK_CERT_REQUEST` response, including V2 renewal decisions.
313pub fn parse_response(response: &[u8]) -> Result<AkCertResponse, AkCertError> {
314    use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestAkCertResponseHeader;
315    use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestCommonResponseHeader;
316    use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestResponseVersion;
317
318    let header = parse_response_header(response).map_err(AkCertError::ParseHeader)?;
319
320    // Extract payload as per header version
321    let header_size = match header.version {
322        IgvmAttestResponseVersion::VERSION_1 => size_of::<IgvmAttestCommonResponseHeader>(),
323        IgvmAttestResponseVersion::VERSION_2 => size_of::<IgvmAttestAkCertResponseHeader>(),
324        invalid_version => return Err(AkCertError::InvalidResponseVersion(invalid_version.0)),
325    };
326    let data_size = header.data_size as usize;
327
328    if data_size < header_size {
329        return Err(AkCertError::SizeTooSmall {
330            size: data_size,
331            minimum_size: header_size,
332        });
333    }
334
335    if header.version == IgvmAttestResponseVersion::VERSION_2 {
336        let (header, _) = IgvmAttestAkCertResponseHeader::read_from_prefix(&response[..data_size])
337            .map_err(|_| AkCertError::SizeTooSmall {
338                size: data_size,
339                minimum_size: header_size,
340            })?;
341        if header.error_info.igvm_signal.no_renew_cert() {
342            return Ok(AkCertResponse::NoRenewal);
343        }
344    }
345
346    Ok(AkCertResponse::Certificate(
347        response[header_size..data_size].to_vec(),
348    ))
349}
350
351#[cfg(test)]
352mod tests {
353    use super::*;
354    use der::Encode;
355    use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestAkCertResponseHeader;
356    use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestCommonResponseHeader;
357    use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestResponseVersion;
358    use openhcl_attestation_protocol::igvm_attest::get::IgvmSignal;
359    use test_igvm_agent_lib::ak_cert::certificate;
360    use test_with_tracing::test;
361    use zerocopy::IntoBytes;
362
363    #[test]
364    fn test_certificate_metadata() {
365        let cert = certificate();
366        let metadata = parse_metadata(&cert).unwrap();
367        assert_eq!(metadata.issuer, "Test \"AK\" issuer");
368        assert_eq!(metadata.subject, "Test \"AK\" issuer");
369        assert_eq!(metadata.not_before, "261009180232Z");
370        assert_eq!(metadata.not_after, "20561120180232Z");
371        assert_eq!(
372            metadata.authority,
373            "9FF3935C2C83D84F1DB5242A0E7D3C9B1D191D3B"
374        );
375
376        let mut padded = cert.clone();
377        padded.resize(4096, 0);
378        assert_eq!(parse_metadata(&padded).unwrap(), metadata);
379        padded[cert.len()..].fill(0xa5);
380        assert_eq!(parse_metadata(&padded).unwrap(), metadata);
381    }
382
383    #[test]
384    fn test_certificate_without_authority_key_identifier() {
385        // Changing the extension OID leaves valid DER, but no AKI extension.
386        let cert = with_aki(&[der_value(TagNumber(0).context_specific(false), &[1])]);
387        assert_eq!(parse_metadata(&cert).unwrap().authority, "01");
388        let cert = edit_children(&cert, &[0, 7, 0, 0], |extension| {
389            extension[0] = ObjectIdentifier::new_unwrap("2.5.29.36").to_der().unwrap();
390        });
391        assert_eq!(parse_metadata(&cert).unwrap().authority, "");
392    }
393
394    #[test]
395    fn test_certificate_missing_common_name() {
396        for name_index in [3, 5] {
397            let cert = edit_children(&certificate(), &[0, name_index, 0, 0], |attribute| {
398                attribute[0] = ObjectIdentifier::new_unwrap("2.5.4.10").to_der().unwrap();
399            });
400            assert!(matches!(
401                parse_metadata(&cert),
402                Err(AkCertMetadataError::MissingCommonName(_))
403            ));
404        }
405    }
406
407    #[test]
408    fn test_invalid_certificate_metadata() {
409        let cert = certificate();
410        for size in 0..cert.len() {
411            assert!(parse_metadata(&cert[..size]).is_err());
412        }
413        assert!(parse_metadata(&[0; 4096]).is_err());
414        // Truncated key identifier inside a fully bounded AKI sequence.
415        let cert = with_aki(&[vec![0x80, 2, 1]]);
416        assert!(parse_metadata(&cert).is_err());
417    }
418
419    fn der_value(tag: Tag, value: &[u8]) -> Vec<u8> {
420        AnyRef::new(tag, value).unwrap().to_der().unwrap()
421    }
422
423    fn der_children(data: &[u8]) -> Vec<Vec<u8>> {
424        let value = AnyRef::from_der(data).unwrap();
425        let mut reader = SliceReader::new(value.value()).unwrap();
426        let mut children = Vec::new();
427        while !reader.is_finished() {
428            children.push(reader.tlv_bytes().unwrap().to_vec());
429        }
430        reader.finish().unwrap();
431        children
432    }
433
434    // Re-encode every enclosing length with der, so mutations exercise nested
435    // validation, not just the outer certificate's length check.
436    fn edit_children(data: &[u8], path: &[usize], edit: impl FnOnce(&mut Vec<Vec<u8>>)) -> Vec<u8> {
437        let mut children = der_children(data);
438        if let Some((&index, rest)) = path.split_first() {
439            children[index] = edit_children(&children[index], rest, edit);
440        } else {
441            edit(&mut children);
442        }
443        der_value(AnyRef::from_der(data).unwrap().tag(), &children.concat())
444    }
445
446    fn with_aki(fields: &[Vec<u8>]) -> Vec<u8> {
447        let extension = der_value(
448            Tag::Sequence,
449            &[
450                ObjectIdentifier::new_unwrap("2.5.29.35").to_der().unwrap(),
451                der_value(
452                    Tag::OctetString,
453                    &der_value(Tag::Sequence, &fields.concat()),
454                ),
455            ]
456            .concat(),
457        );
458        edit_children(&certificate(), &[0], |tbs| {
459            tbs.truncate(7);
460            tbs.push(der_value(
461                TagNumber(3).context_specific(true),
462                &der_value(Tag::Sequence, &extension),
463            ));
464        })
465    }
466
467    #[test]
468    fn test_metadata_without_optional_fields() {
469        let fixture = certificate();
470        let expected = AkCertMetadata {
471            authority: String::new(),
472            ..parse_metadata(&fixture).unwrap()
473        };
474        // v3 without extensions, and v1 with its default version omitted.
475        for omit_version in [false, true] {
476            let cert = edit_children(&fixture, &[0], |tbs| {
477                tbs.truncate(7);
478                if omit_version {
479                    tbs.remove(0);
480                }
481            });
482            assert_eq!(parse_metadata(&cert).unwrap(), expected);
483        }
484        assert_eq!(parse_metadata(&with_aki(&[])).unwrap(), expected);
485
486        let issuer = der_value(
487            TagNumber(1).context_specific(true),
488            &der_value(TagNumber(2).context_specific(false), b"issuer.example"),
489        );
490        let serial = der_value(TagNumber(2).context_specific(false), &[1]);
491        // An AKI may identify the authority by issuer/serial without a key ID.
492        assert_eq!(
493            parse_metadata(&with_aki(&[issuer.clone(), serial.clone()])).unwrap(),
494            expected
495        );
496        let key = der_value(TagNumber(0).context_specific(false), &[0xab, 0xcd]);
497        assert_eq!(
498            parse_metadata(&with_aki(&[key, issuer, serial]))
499                .unwrap()
500                .authority,
501            "ABCD"
502        );
503    }
504
505    #[test]
506    fn test_metadata_duplicate_aki_extension() {
507        // Duplicate detection must not depend on whether a key ID was present.
508        for fields in [
509            vec![],
510            vec![der_value(TagNumber(0).context_specific(false), &[1])],
511        ] {
512            let cert = edit_children(&with_aki(&fields), &[0, 7, 0], |extensions| {
513                extensions.push(extensions[0].clone());
514            });
515            assert!(parse_metadata(&cert).is_err());
516        }
517    }
518
519    #[test]
520    fn test_metadata_invalid_aki_fields() {
521        let key = der_value(TagNumber(0).context_specific(false), &[1]);
522        let serial = der_value(TagNumber(2).context_specific(false), &[1]);
523        let malformed_fields = [
524            vec![key.clone(), key.clone()],
525            vec![serial.clone(), key],
526            vec![serial.clone(), serial],
527            vec![der_value(TagNumber(0).context_specific(true), &[1])],
528            vec![der_value(TagNumber(1).context_specific(false), &[])],
529            vec![der_value(TagNumber(1).context_specific(true), &[])],
530            // A GeneralName truncated within the fully bounded issuer wrapper.
531            vec![der_value(
532                TagNumber(1).context_specific(true),
533                &[0x82, 2, b'a'],
534            )],
535            vec![der_value(
536                TagNumber(1).context_specific(true),
537                &der_value(Tag::Null, &[]),
538            )],
539            vec![der_value(TagNumber(2).context_specific(true), &[1])],
540            vec![der_value(TagNumber(2).context_specific(false), &[])],
541            vec![der_value(TagNumber(2).context_specific(false), &[0, 1])],
542            vec![der_value(TagNumber(2).context_specific(false), &[1; 22])],
543            vec![der_value(TagNumber(3).context_specific(false), &[])],
544        ];
545        for fields in malformed_fields {
546            assert!(parse_metadata(&with_aki(&fields)).is_err(), "{fields:?}");
547        }
548        // The extnValue must contain exactly one complete AKI SEQUENCE.
549        for payload in [
550            der_value(Tag::Null, &[]),
551            vec![0x30, 1],
552            vec![0x30, 0, 0x05, 0],
553        ] {
554            let cert = edit_children(&with_aki(&[]), &[0, 7, 0, 0], |extension| {
555                extension[1] = der_value(Tag::OctetString, &payload);
556            });
557            assert!(parse_metadata(&cert).is_err());
558        }
559    }
560
561    #[test]
562    fn test_metadata_optional_field_order_and_tags() {
563        let fixture = certificate();
564        let issuer_uid = der_value(TagNumber(1).context_specific(false), &[0, 0x80]);
565        let subject_uid = der_value(TagNumber(2).context_specific(false), &[0, 0x40]);
566        for uids in [
567            vec![issuer_uid.clone()],
568            vec![subject_uid.clone()],
569            vec![issuer_uid.clone(), subject_uid.clone()],
570        ] {
571            let cert = edit_children(&fixture, &[0], |tbs| {
572                tbs.splice(7..7, uids);
573            });
574            assert_eq!(
575                parse_metadata(&cert).unwrap(),
576                parse_metadata(&fixture).unwrap()
577            );
578        }
579        for uids in [
580            vec![issuer_uid.clone(), issuer_uid.clone()],
581            vec![subject_uid.clone(), issuer_uid],
582            vec![subject_uid.clone(), subject_uid],
583            vec![der_value(TagNumber(1).context_specific(true), &[0])],
584            vec![der_value(TagNumber(2).context_specific(false), &[8, 0])],
585            vec![der_value(TagNumber(4).context_specific(false), &[])],
586        ] {
587            let cert = edit_children(&fixture, &[0], |tbs| {
588                tbs.splice(7..7, uids);
589            });
590            assert!(parse_metadata(&cert).is_err());
591        }
592        for index in [0, 7] {
593            let cert = edit_children(&fixture, &[0], |tbs| {
594                tbs.insert(index, tbs[index].clone());
595            });
596            assert!(parse_metadata(&cert).is_err());
597        }
598        for version in [0u8, 1, 3, 255] {
599            let cert = edit_children(&fixture, &[0, 0], |fields| {
600                fields[0] = version.to_der().unwrap();
601            });
602            assert!(parse_metadata(&cert).is_err());
603        }
604        let cert = edit_children(&fixture, &[0], |tbs| {
605            tbs.push(der_value(TagNumber(1).context_specific(false), &[0]));
606        });
607        assert!(parse_metadata(&cert).is_err());
608    }
609
610    #[test]
611    fn test_metadata_nested_sequences_are_fully_consumed() {
612        let fixture = certificate();
613        // Certificate, TBS, version wrapper, algorithms, Name attribute,
614        // validity, SPKI, extensions wrapper, and an individual extension.
615        for path in [
616            vec![],
617            vec![0],
618            vec![0, 0],
619            vec![0, 2],
620            vec![1],
621            vec![0, 3, 0, 0],
622            vec![0, 4],
623            vec![0, 5, 0, 0],
624            vec![0, 6],
625            vec![0, 6, 0],
626            vec![0, 7],
627            vec![0, 7, 0, 0],
628        ] {
629            let cert = edit_children(&fixture, &path, |fields| {
630                // One added field could be valid optional algorithm parameters.
631                fields.push(der_value(Tag::Null, &[]));
632                fields.push(der_value(Tag::Null, &[]));
633            });
634            assert!(parse_metadata(&cert).is_err(), "extra field at {path:?}");
635            let mut missing_optional = path == [0];
636            let cert = edit_children(&fixture, &path, |fields| {
637                fields.pop().unwrap();
638                // Only the second AlgorithmIdentifier field is optional.
639                if [vec![0, 2], vec![1], vec![0, 6, 0]].contains(&path) {
640                    missing_optional = fields.len() == 1;
641                }
642            });
643            if missing_optional {
644                assert!(parse_metadata(&cert).is_ok(), "optional field at {path:?}");
645            } else {
646                assert!(parse_metadata(&cert).is_err(), "missing field at {path:?}");
647            }
648        }
649        for index in 0..3 {
650            let cert = edit_children(&fixture, &[], |fields| {
651                fields[index] = der_value(Tag::Null, &[]);
652            });
653            assert!(parse_metadata(&cert).is_err());
654        }
655        for index in 0..8 {
656            let cert = edit_children(&fixture, &[0], |fields| {
657                fields[index] = der_value(Tag::Null, &[]);
658            });
659            assert!(parse_metadata(&cert).is_err());
660        }
661        let cert = edit_children(&fixture, &[0, 3], |rdns| {
662            rdns[0] = der_value(Tag::Set, &[]);
663        });
664        assert!(parse_metadata(&cert).is_err());
665        let cert = edit_children(&fixture, &[0, 3, 0], |attributes| {
666            attributes.push(vec![0x30, 2, 0x06]);
667        });
668        assert!(parse_metadata(&cert).is_err());
669    }
670
671    #[test]
672    fn test_metadata_directory_strings() {
673        let fixture = certificate();
674        for name in [3, 5] {
675            for (tag, content, expected) in [
676                (Tag::Utf8String, "AK é 水 😀".as_bytes(), "AK é 水 😀"),
677                (
678                    Tag::PrintableString,
679                    b"AZaz09 '()+,-./:=?".as_slice(),
680                    "AZaz09 '()+,-./:=?",
681                ),
682                (Tag::BmpString, b"\x00A\x00K\x00 \x00\xe9\x6c\x34", "AK é水"),
683                (
684                    Tag::TeletexString,
685                    b"Test \"AK\" @issuer\x7f",
686                    "Test \"AK\" @issuer\x7f",
687                ),
688                // Preserve the previous decoder's acceptance of empty strings.
689                (Tag::Utf8String, b"", ""),
690                (Tag::PrintableString, b"", ""),
691                (Tag::BmpString, b"", ""),
692                (Tag::TeletexString, b"", ""),
693            ] {
694                let cert = edit_children(&fixture, &[0, name, 0, 0], |attribute| {
695                    attribute[1] = der_value(tag, content);
696                });
697                let metadata = parse_metadata(&cert).unwrap();
698                assert_eq!(
699                    if name == 3 {
700                        metadata.issuer
701                    } else {
702                        metadata.subject
703                    },
704                    expected,
705                    "{tag:?} at name {name}"
706                );
707            }
708            for value in [
709                der_value(Tag::Utf8String, &[0xff]),
710                der_value(Tag::Utf8String, &[0xc0, 0xaf]),
711                der_value(Tag::Utf8String, &[0xe2, 0x82]),
712                der_value(Tag::PrintableString, b"invalid@CN"),
713                der_value(Tag::PrintableString, "é".as_bytes()),
714                der_value(Tag::PrintableString, b"invalid\0CN"),
715                der_value(Tag::BmpString, &[0]),
716                der_value(Tag::BmpString, &[0xd8, 0x00]),
717                der_value(Tag::BmpString, &[0xdc, 0x00]),
718                // A valid UTF-16 surrogate pair is not a BMP character.
719                der_value(Tag::BmpString, &[0xd8, 0x3d, 0xde, 0x00]),
720                der_value(Tag::BmpString, &[0xff, 0xff]),
721                // der's TeletexString supports ASCII, not Latin-1 or UTF-8.
722                der_value(Tag::TeletexString, &[0x80]),
723                der_value(Tag::TeletexString, &[0xe9]),
724                der_value(Tag::TeletexString, "é".as_bytes()),
725                der_value(Tag::OctetString, b"not a DirectoryString"),
726                der_value(Tag::Ia5String, b"not a DirectoryString"),
727                der_value(Tag::Integer, &[1]),
728                der_value(Tag::Null, &[]),
729                der_value(Tag::Sequence, &[]),
730                der_value(TagNumber(0).context_specific(false), b"CN"),
731                // UniversalString was not supported by DirectoryString either.
732                vec![0x1c, 4, 0, 0, 0, b'A'],
733            ] {
734                let cert = edit_children(&fixture, &[0, name, 0, 0], |attribute| {
735                    attribute[1] = value.clone();
736                });
737                assert!(parse_metadata(&cert).is_err(), "{value:?} at name {name}");
738            }
739        }
740    }
741
742    #[test]
743    fn test_metadata_times() {
744        let fixture = certificate();
745        for index in [0, 1] {
746            // Both validity fields accept either time encoding. Preserve the
747            // exact content, including leading zeroes and generalized years
748            // that could also have been represented as UTCTime.
749            for (tag, content) in [
750                (Tag::UtcTime, "700101000000Z"),
751                (Tag::UtcTime, "991231235959Z"),
752                (Tag::UtcTime, "000229000000Z"),
753                (Tag::UtcTime, "261009180232Z"),
754                (Tag::UtcTime, "491231235959Z"),
755                (Tag::GeneralizedTime, "19700101000000Z"),
756                (Tag::GeneralizedTime, "20000229000000Z"),
757                (Tag::GeneralizedTime, "20561120180232Z"),
758                (Tag::GeneralizedTime, "99991231235959Z"),
759            ] {
760                let cert = edit_children(&fixture, &[0, 4], |validity| {
761                    validity[index] = der_value(tag, content.as_bytes());
762                });
763                let metadata = parse_metadata(&cert).unwrap();
764                assert_eq!(
765                    if index == 0 {
766                        metadata.not_before
767                    } else {
768                        metadata.not_after
769                    },
770                    content
771                );
772            }
773            for (tag, content) in [
774                (Tag::UtcTime, b"261309180232Z".as_slice()),
775                (Tag::UtcTime, b"260009180232Z"),
776                (Tag::UtcTime, b"260229180232Z"),
777                (Tag::UtcTime, b"261009240232Z"),
778                (Tag::UtcTime, b"261009186032Z"),
779                (Tag::UtcTime, b"261009180260Z"),
780                (Tag::UtcTime, b"261009180232"),
781                (Tag::UtcTime, b"2610091802Z"),
782                (Tag::UtcTime, b"261009180232+0000"),
783                (Tag::UtcTime, b"261009180232z"),
784                (Tag::UtcTime, b"261009180232.0Z"),
785                (Tag::UtcTime, b"26100918023\xffZ"),
786                // Retain der's existing rejection of pre-UNIX-epoch dates.
787                (Tag::UtcTime, b"691231235959Z"),
788                (Tag::GeneralizedTime, b"19691231235959Z"),
789                (Tag::GeneralizedTime, b"20561132180232Z"),
790                (Tag::GeneralizedTime, b"20561320180232Z"),
791                (Tag::GeneralizedTime, b"21000229180232Z"),
792                (Tag::GeneralizedTime, b"20561120240232Z"),
793                (Tag::GeneralizedTime, b"20561120186032Z"),
794                (Tag::GeneralizedTime, b"20561120180260Z"),
795                (Tag::GeneralizedTime, b"20561120180232"),
796                (Tag::GeneralizedTime, b"205611201802Z"),
797                (Tag::GeneralizedTime, b"20561120180232+0000"),
798                (Tag::GeneralizedTime, b"20561120180232z"),
799                (Tag::GeneralizedTime, b"20561120180232.0Z"),
800                (Tag::GeneralizedTime, b"2056112018023\xffZ"),
801                (Tag::UtcTime, b"20561120180232Z"),
802                (Tag::GeneralizedTime, b"261009180232Z"),
803                (Tag::Utf8String, b"261009180232Z"),
804                (Tag::OctetString, b"20561120180232Z"),
805                (Tag::Null, b""),
806                (Tag::Sequence, b""),
807            ] {
808                let cert = edit_children(&fixture, &[0, 4], |validity| {
809                    validity[index] = der_value(tag, content);
810                });
811                assert!(
812                    parse_metadata(&cert).is_err(),
813                    "{tag:?} {content:?} at {index}"
814                );
815            }
816        }
817    }
818
819    fn v2_response(signal: IgvmSignal, payload: &[u8]) -> Vec<u8> {
820        let header = IgvmAttestAkCertResponseHeader {
821            data_size: (size_of::<IgvmAttestAkCertResponseHeader>() + payload.len()) as u32,
822            version: IgvmAttestResponseVersion::VERSION_2,
823            error_info: openhcl_attestation_protocol::igvm_attest::get::IgvmErrorInfo {
824                igvm_signal: signal,
825                ..Default::default()
826            },
827        };
828        let mut response = header.as_bytes().to_vec();
829        response.extend_from_slice(payload);
830        response
831    }
832
833    #[test]
834    fn test_v2_certificate_and_no_renewal() {
835        for payload in [b"".as_slice(), b"certificate"] {
836            assert_eq!(
837                parse_response(&v2_response(IgvmSignal::new(), payload)).unwrap(),
838                AkCertResponse::Certificate(payload.to_vec())
839            );
840            // The signal takes precedence over any payload, as in the legacy agent ABI.
841            let signal = IgvmSignal::new().with_no_renew_cert(true);
842            assert_eq!(signal.as_bytes(), &32u32.to_le_bytes());
843            assert_eq!(
844                parse_response(&v2_response(signal, payload)).unwrap(),
845                AkCertResponse::NoRenewal
846            );
847        }
848        for bit in [0, 1, 2, 3, 4, 6, 31] {
849            let signal = IgvmSignal::from(1u32 << bit);
850            assert_eq!(
851                parse_response(&v2_response(signal, b"cert")).unwrap(),
852                AkCertResponse::Certificate(b"cert".to_vec())
853            );
854        }
855    }
856
857    #[test]
858    fn test_no_renewal_does_not_override_errors_or_bad_sizes() {
859        let response = v2_response(IgvmSignal::new().with_no_renew_cert(true), &[]);
860        for size in 0..response.len() {
861            assert!(parse_response(&response[..size]).is_err());
862            // Bytes present outside the declared size must not supply the signal.
863            let mut invalid = response.clone();
864            invalid[..4].copy_from_slice(&(size as u32).to_le_bytes());
865            assert!(parse_response(&invalid).is_err());
866        }
867        let mut invalid = response.clone();
868        invalid[..4].copy_from_slice(&((response.len() + 1) as u32).to_le_bytes());
869        assert!(parse_response(&invalid).is_err());
870
871        let mut error = response.clone();
872        error[8..12].copy_from_slice(&1u32.to_le_bytes());
873        assert!(matches!(
874            parse_response(&error),
875            Err(AkCertError::ParseHeader(CommonError::Attestation { .. }))
876        ));
877        for version in [0u32, 3, u32::MAX] {
878            let mut invalid = response.clone();
879            invalid[4..8].copy_from_slice(&version.to_le_bytes());
880            assert!(parse_response(&invalid).is_err());
881        }
882    }
883
884    #[test]
885    fn test_undersized_response() {
886        const HEADER_SIZE: usize = size_of::<IgvmAttestAkCertResponseHeader>();
887        let properly_sized_response: [u8; HEADER_SIZE] = [1; HEADER_SIZE];
888        let undersized_response = &properly_sized_response[..HEADER_SIZE - 1];
889
890        // Empty response counts as an undersized response
891        let result = parse_response(&[]);
892        assert!(result.is_err());
893        assert_eq!(
894            result.unwrap_err().to_string(),
895            AkCertError::ParseHeader(CommonError::ResponseSizeTooSmall { response_size: 0 })
896                .to_string()
897        );
898
899        // Response has to be at least `HEADER_SIZE` bytes long, so `HEADER_SIZE - 1` bytes is too small.
900        let undersized_parse_ = parse_response(undersized_response);
901        assert!(undersized_parse_.is_err());
902        assert_eq!(
903            undersized_parse_.unwrap_err().to_string(),
904            AkCertError::ParseHeader(CommonError::ResponseSizeTooSmall {
905                response_size: HEADER_SIZE - 1
906            })
907            .to_string()
908        );
909
910        // When we finally have `HEADER_SIZE` bytes, we no longer see the failure as `AkCertError::SizeTooSmall`,
911        // but we still see a different error since the response is not valid.
912        let properly_sized_parse = parse_response(&properly_sized_response);
913        assert!(
914            !properly_sized_parse
915                .unwrap_err()
916                .to_string()
917                .starts_with("AK cert response is too small to parse"),
918        );
919    }
920
921    fn v1_response(payload: &[u8]) -> Vec<u8> {
922        let data_size = (size_of::<IgvmAttestCommonResponseHeader>() + payload.len()) as u32;
923        let mut response = data_size.to_le_bytes().to_vec();
924        response.extend_from_slice(&IgvmAttestResponseVersion::VERSION_1.0.to_le_bytes());
925        response.extend_from_slice(payload);
926        response
927    }
928
929    #[test]
930    fn test_valid_response_size_match() {
931        let cert = certificate();
932        assert_eq!(
933            parse_response(&v1_response(&cert)).unwrap(),
934            AkCertResponse::Certificate(cert)
935        );
936    }
937
938    #[test]
939    fn test_parse_response_small_size() {
940        let mut response = [0u8; 8];
941        // data_size = 4 (little-endian u32)
942        response[0..4].copy_from_slice(&4u32.to_le_bytes());
943        // version = VERSION_1 = 1 (little-endian u32)
944        response[4..8].copy_from_slice(&1u32.to_le_bytes());
945
946        assert!(parse_response(&response).is_err());
947    }
948
949    #[test]
950    fn test_valid_response_size_smaller_than_specified() {
951        let cert = certificate();
952        let mut response = v1_response(&cert);
953        // Bytes beyond the declared response size must not become payload.
954        response.extend_from_slice(b"trailing bytes");
955        assert_eq!(
956            parse_response(&response).unwrap(),
957            AkCertResponse::Certificate(cert)
958        );
959    }
960}