Skip to main content

rs_matter/
cert.rs

1/*
2 *
3 *    Copyright (c) 2022-2026 Project CHIP Authors
4 *
5 *    Licensed under the Apache License, Version 2.0 (the "License");
6 *    you may not use this file except in compliance with the License.
7 *    You may obtain a copy of the License at
8 *
9 *        http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *    Unless required by applicable law or agreed to in writing, software
12 *    distributed under the License is distributed on an "AS IS" BASIS,
13 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *    See the License for the specific language governing permissions and
15 *    limitations under the License.
16 */
17
18use core::fmt::{self, Write};
19
20use num::FromPrimitive;
21use num_derive::FromPrimitive;
22
23use crate::crypto::{Crypto, PublicKey};
24use crate::dm::clusters::time_sync::UtcTime;
25use crate::error::{Error, ErrorCode};
26use crate::tlv::{FromTLV, Octets, TLVArray, TLVElement, TLVList, ToTLV};
27use crate::utils::epoch::MATTER_CERT_DOESNT_EXPIRE;
28use crate::utils::iter::TryFindIterator;
29
30use self::printer::CertPrinter;
31
32pub use self::asn1_writer::ASN1Writer;
33
34mod asn1_writer;
35pub mod der_utils;
36pub mod gen;
37mod printer;
38pub mod x509;
39
40/// As per "Certificate Sizes" of the Matter spec
41pub const MAX_CERT_TLV_LEN: usize = 400;
42/// As per "Certificate Sizes" of the Matter spec, DER formatted DAC and NOC
43/// chains must not be longer than this size
44pub const MAX_CERT_ASN1_LEN: usize = 600;
45/// Must be large enough to hold both TLV encoding and intermediate ASN.1 encoding during building
46/// in order to sign over the ASN1-encoded TBS certificate
47pub const MAX_CERT_TLV_AND_ASN1_LEN: usize = MAX_CERT_TLV_LEN + MAX_CERT_ASN1_LEN;
48
49// As per https://datatracker.ietf.org/doc/html/rfc5280
50
51const OID_PUB_KEY_ECPUBKEY: [u8; 7] = [0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01];
52const OID_EC_TYPE_PRIME256V1: [u8; 8] = [0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07];
53const OID_ECDSA_WITH_SHA256: [u8; 8] = [0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02];
54
55const MAX_DEPTH: usize = 10;
56
57#[derive(FromPrimitive)]
58pub enum CertTag {
59    SerialNum = 1,
60    SignAlgo = 2,
61    Issuer = 3,
62    NotBefore = 4,
63    NotAfter = 5,
64    Subject = 6,
65    PubKeyAlgo = 7,
66    EcCurveId = 8,
67    EcPubKey = 9,
68    Extensions = 10,
69    Signature = 11,
70}
71
72#[derive(FromPrimitive, Debug)]
73#[cfg_attr(feature = "defmt", derive(defmt::Format))]
74pub enum EcCurveIdValue {
75    Prime256V1 = 1,
76}
77
78pub fn get_ec_curve_id(algo: u8) -> Option<EcCurveIdValue> {
79    num::FromPrimitive::from_u8(algo)
80}
81
82#[derive(FromPrimitive, Debug)]
83#[cfg_attr(feature = "defmt", derive(defmt::Format))]
84pub enum PubKeyAlgoValue {
85    EcPubKey = 1,
86}
87
88pub fn get_pubkey_algo(algo: u8) -> Option<PubKeyAlgoValue> {
89    num::FromPrimitive::from_u8(algo)
90}
91
92#[derive(FromPrimitive, Debug)]
93#[cfg_attr(feature = "defmt", derive(defmt::Format))]
94pub enum SignAlgoValue {
95    ECDSAWithSHA256 = 1,
96}
97
98pub fn get_sign_algo(algo: u8) -> Option<SignAlgoValue> {
99    num::FromPrimitive::from_u8(algo)
100}
101
102#[derive(Default, Debug, Clone, FromTLV, ToTLV, PartialEq, Eq, Hash)]
103#[cfg_attr(feature = "defmt", derive(defmt::Format))]
104#[tlvargs(start = 1)]
105struct BasicConstraints {
106    is_ca: bool,
107    path: Option<u8>,
108}
109
110impl BasicConstraints {
111    pub fn encode(&self, w: &mut dyn CertConsumer) -> Result<(), Error> {
112        w.start_seq("")?;
113        if self.is_ca {
114            // Encode CA only if true
115            w.bool("CA:", true)?;
116        }
117        if let Some(len) = self.path {
118            w.integer("Path Len Constraint", &[len])?;
119        }
120        w.end_seq()
121    }
122}
123
124// #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
125// #[cfg_attr(feature = "defmt", derive(defmt::Format))]
126// #[repr(u8)]
127// enum ExtTag {
128//     BasicConstraints = 1,
129//     KeyUsage = 2,
130//     ExtKeyUsage = 3,
131//     SubjectKeyId = 4,
132//     AuthorityKeyId = 5,
133//     FutureExtensions = 6,
134// }
135
136#[derive(Debug, Clone, FromTLV, ToTLV, PartialEq, Eq, Hash)]
137#[cfg_attr(feature = "defmt", derive(defmt::Format))]
138#[tlvargs(start = 1, lifetime = "'a", datatype = "naked")]
139enum Extension<'a> {
140    BasicConstraints(BasicConstraints),
141    KeyUsage(u16),
142    ExtKeyUsage(TLVArray<'a, u8>),
143    SubjectKeyId(Octets<'a>),
144    AuthorityKeyId(Octets<'a>),
145    FutureExtensions(Octets<'a>),
146}
147
148impl<'a> Extension<'a> {
149    fn encode_all(
150        iter: impl Iterator<Item = Result<Self, Error>> + 'a,
151        w: &mut dyn CertConsumer,
152    ) -> Result<(), Error> {
153        w.start_ctx("X509v3 extensions:", 3)?;
154        w.start_seq("")?;
155
156        for extension in iter {
157            extension?.encode(w)?;
158        }
159
160        w.end_seq()?;
161        w.end_ctx()?;
162
163        Ok(())
164    }
165
166    fn encode(&self, w: &mut dyn CertConsumer) -> Result<(), Error> {
167        const OID_BASIC_CONSTRAINTS: [u8; 3] = [0x55, 0x1D, 0x13];
168        const OID_KEY_USAGE: [u8; 3] = [0x55, 0x1D, 0x0F];
169        const OID_EXT_KEY_USAGE: [u8; 3] = [0x55, 0x1D, 0x25];
170        const OID_SUBJ_KEY_IDENTIFIER: [u8; 3] = [0x55, 0x1D, 0x0E];
171        const OID_AUTH_KEY_ID: [u8; 3] = [0x55, 0x1D, 0x23];
172
173        match self {
174            Extension::BasicConstraints(t) => {
175                Self::encode_extension_start(
176                    "X509v3 Basic Constraints",
177                    true,
178                    &OID_BASIC_CONSTRAINTS,
179                    w,
180                )?;
181                t.encode(w)?;
182                Self::encode_extension_end(w)?;
183            }
184            Extension::KeyUsage(t) => {
185                Self::encode_extension_start("X509v3 Key Usage", true, &OID_KEY_USAGE, w)?;
186                Self::encode_key_usage(*t, w)?;
187                Self::encode_extension_end(w)?;
188            }
189            Extension::ExtKeyUsage(t) => {
190                Self::encode_extension_start(
191                    "X509v3 Extended Key Usage",
192                    true,
193                    &OID_EXT_KEY_USAGE,
194                    w,
195                )?;
196                Self::encode_extended_key_usage(t.iter(), w)?;
197                Self::encode_extension_end(w)?;
198            }
199            Extension::SubjectKeyId(t) => {
200                Self::encode_extension_start("Subject Key ID", false, &OID_SUBJ_KEY_IDENTIFIER, w)?;
201                w.ostr("", t.0)?;
202                Self::encode_extension_end(w)?;
203            }
204            Extension::AuthorityKeyId(t) => {
205                Self::encode_extension_start("Auth Key ID", false, &OID_AUTH_KEY_ID, w)?;
206                w.start_seq("")?;
207                w.ctx("", 0, t.0)?;
208                w.end_seq()?;
209                Self::encode_extension_end(w)?;
210            }
211            Extension::FutureExtensions(t) => {
212                // Per Matter Spec, the `future-extensions` TLV field
213                // carries one or more ASN.1 DER-encoded X.509 `Extension`
214                // structures verbatim. They MUST be reproduced byte-for-byte
215                // inside the TBS extensions SEQUENCE so that the
216                // signature-over-TBS verification matches what the issuer
217                // actually signed.
218                w.raw("Future Extensions", t.0)?;
219            }
220        }
221
222        Ok(())
223    }
224
225    fn encode_extension_start(
226        tag: &str,
227        critical: bool,
228        oid: &[u8],
229        w: &mut dyn CertConsumer,
230    ) -> Result<(), Error> {
231        w.start_seq(tag)?;
232        w.oid("", oid)?;
233        if critical {
234            w.bool("critical:", true)?;
235        }
236        w.start_compound_ostr("value:")
237    }
238
239    fn encode_extension_end(w: &mut dyn CertConsumer) -> Result<(), Error> {
240        w.end_compound_ostr()?;
241        w.end_seq()
242    }
243
244    #[allow(unused_assignments)]
245    fn encode_key_usage(key_usage: u16, w: &mut dyn CertConsumer) -> Result<(), Error> {
246        let mut key_usage_str = [0u8; 2];
247        Self::int_to_bitstring(key_usage, &mut key_usage_str);
248        w.bitstr(&Self::get_print_str(key_usage), true, &key_usage_str)?;
249        Ok(())
250    }
251
252    fn encode_extended_key_usage(
253        list: impl Iterator<Item = Result<u8, Error>>,
254        w: &mut dyn CertConsumer,
255    ) -> Result<(), Error> {
256        const OID_SERVER_AUTH: [u8; 8] = [0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01];
257        const OID_CLIENT_AUTH: [u8; 8] = [0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02];
258        const OID_CODE_SIGN: [u8; 8] = [0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03];
259        const OID_EMAIL_PROT: [u8; 8] = [0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x04];
260        const OID_TIMESTAMP: [u8; 8] = [0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x08];
261        const OID_OCSP_SIGN: [u8; 8] = [0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09];
262        let encoding = [
263            ("", &[0; 8]),
264            ("ServerAuth", &OID_SERVER_AUTH),
265            ("ClientAuth", &OID_CLIENT_AUTH),
266            ("CodeSign", &OID_CODE_SIGN),
267            ("EmailProtection", &OID_EMAIL_PROT),
268            ("Timestamp", &OID_TIMESTAMP),
269            ("OCSPSign", &OID_OCSP_SIGN),
270        ];
271
272        w.start_seq("")?;
273        for t in list {
274            let t = t? as usize;
275            if t > 0 && t <= encoding.len() {
276                w.oid(encoding[t].0, encoding[t].1)?;
277            } else {
278                error!("Skipping encoding key usage out of bounds");
279            }
280        }
281        w.end_seq()
282    }
283
284    fn get_print_str(key_usage: u16) -> heapless::String<256> {
285        const KEY_USAGE_DIGITAL_SIGN: u16 = 0x0001;
286        const KEY_USAGE_NON_REPUDIATION: u16 = 0x0002;
287        const KEY_USAGE_KEY_ENCIPHERMENT: u16 = 0x0004;
288        const KEY_USAGE_DATA_ENCIPHERMENT: u16 = 0x0008;
289        const KEY_USAGE_KEY_AGREEMENT: u16 = 0x0010;
290        const KEY_USAGE_KEY_CERT_SIGN: u16 = 0x0020;
291        const KEY_USAGE_CRL_SIGN: u16 = 0x0040;
292        const KEY_USAGE_ENCIPHER_ONLY: u16 = 0x0080;
293        const KEY_USAGE_DECIPHER_ONLY: u16 = 0x0100;
294
295        macro_rules! add_if {
296            ($key:ident, $bit:ident,$str:literal) => {
297                if ($key & $bit) != 0 {
298                    $str
299                } else {
300                    ""
301                }
302            };
303        }
304
305        let mut string = heapless::String::new();
306        write_unwrap!(
307            &mut string,
308            "{}{}{}{}{}{}{}{}{}",
309            add_if!(key_usage, KEY_USAGE_DIGITAL_SIGN, "digitalSignature "),
310            add_if!(key_usage, KEY_USAGE_NON_REPUDIATION, "nonRepudiation "),
311            add_if!(key_usage, KEY_USAGE_KEY_ENCIPHERMENT, "keyEncipherment "),
312            add_if!(key_usage, KEY_USAGE_DATA_ENCIPHERMENT, "dataEncipherment "),
313            add_if!(key_usage, KEY_USAGE_KEY_AGREEMENT, "keyAgreement "),
314            add_if!(key_usage, KEY_USAGE_KEY_CERT_SIGN, "keyCertSign "),
315            add_if!(key_usage, KEY_USAGE_CRL_SIGN, "CRLSign "),
316            add_if!(key_usage, KEY_USAGE_ENCIPHER_ONLY, "encipherOnly "),
317            add_if!(key_usage, KEY_USAGE_DECIPHER_ONLY, "decipherOnly "),
318        );
319
320        string
321    }
322
323    fn int_to_bitstring(mut a: u16, buf: &mut [u8]) {
324        if buf.len() >= 2 {
325            buf[0] = Self::reverse_byte((a & 0xff) as u8);
326            a >>= 8;
327            buf[1] = Self::reverse_byte((a & 0xff) as u8);
328        }
329    }
330
331    fn reverse_byte(byte: u8) -> u8 {
332        const LOOKUP: [u8; 16] = [
333            0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e, 0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b,
334            0x07, 0x0f,
335        ];
336        (LOOKUP[(byte & 0x0f) as usize] << 4) | LOOKUP[(byte >> 4) as usize]
337    }
338}
339
340#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, FromPrimitive)]
341#[cfg_attr(feature = "defmt", derive(defmt::Format))]
342#[repr(u8)]
343enum DNTag {
344    CommonName = 1,
345    Surname = 2,
346    SerialNum = 3,
347    CountryName = 4,
348    LocalityName = 5,
349    StateName = 6,
350    OrgName = 7,
351    OrgUnitName = 8,
352    Title = 9,
353    Name = 10,
354    GivenName = 11,
355    Intials = 12,
356    GenQalifier = 13,
357    DnQualifier = 14,
358    Pseudonym = 15,
359    DomainComponent = 16,
360    NodeId = 17,
361    FirmwareSignId = 18,
362    IcaId = 19,
363    RootCaId = 20,
364    FabricId = 21,
365    NocCat = 22,
366}
367
368#[derive(Debug, Clone, PartialEq, Eq, Hash)]
369#[cfg_attr(feature = "defmt", derive(defmt::Format))]
370enum DNValue<'a> {
371    Uint(u64),
372    Utf8(&'a str),
373    PrintableStr(&'a str),
374}
375
376#[derive(FromTLV, ToTLV, Debug, Clone, PartialEq, Eq, Hash)]
377#[cfg_attr(feature = "defmt", derive(defmt::Format))]
378#[tlvargs(lifetime = "'a")]
379struct DN<'a>(TLVElement<'a>);
380
381impl<'a> DN<'a> {
382    pub fn tag(&self) -> Result<DNTag, Error> {
383        let ctx = self.0.try_ctx()?.ok_or(ErrorCode::Invalid)? & 0x7f;
384
385        Ok(DNTag::from_u8(ctx).ok_or(ErrorCode::Invalid)?)
386    }
387
388    pub fn is_printable(&self) -> Result<bool, Error> {
389        let ctx = self.0.try_ctx()?.ok_or(ErrorCode::Invalid)?;
390
391        Ok(ctx >= 0x80)
392    }
393
394    fn uint(&self) -> Result<u64, Error> {
395        self.0.u64()
396    }
397
398    fn value(&self) -> Result<DNValue<'a>, Error> {
399        if let Ok(value) = self.0.utf8() {
400            if self.is_printable()? {
401                Ok(DNValue::PrintableStr(value))
402            } else {
403                Ok(DNValue::Utf8(value))
404            }
405        } else {
406            self.0.u64().map(DNValue::Uint)
407        }
408    }
409
410    fn encode_all(
411        values: impl Iterator<Item = Result<Self, Error>> + 'a,
412        tag: &str,
413        w: &mut dyn CertConsumer,
414    ) -> Result<(), Error> {
415        const OID_COMMON_NAME: [u8; 3] = [0x55_u8, 0x04, 0x03];
416        const OID_SURNAME: [u8; 3] = [0x55_u8, 0x04, 0x04];
417        const OID_SERIAL_NUMBER: [u8; 3] = [0x55_u8, 0x04, 0x05];
418        const OID_COUNTRY_NAME: [u8; 3] = [0x55_u8, 0x04, 0x06];
419        const OID_LOCALITY_NAME: [u8; 3] = [0x55_u8, 0x04, 0x07];
420        const OID_STATE_NAME: [u8; 3] = [0x55_u8, 0x04, 0x08];
421        const OID_ORGANIZATION_NAME: [u8; 3] = [0x55_u8, 0x04, 0x0A];
422        const OID_ORGANIZATIONAL_UNIT_NAME: [u8; 3] = [0x55_u8, 0x04, 0x0B];
423        const OID_TITLE: [u8; 3] = [0x55_u8, 0x04, 0x0C];
424        const OID_NAME: [u8; 3] = [0x55_u8, 0x04, 0x29];
425        const OID_GIVEN_NAME: [u8; 3] = [0x55_u8, 0x04, 0x2A];
426        const OID_INITIALS: [u8; 3] = [0x55_u8, 0x04, 0x2B];
427        const OID_GENERATION_QUALIFIER: [u8; 3] = [0x55_u8, 0x04, 0x2C];
428        const OID_DN_QUALIFIER: [u8; 3] = [0x55_u8, 0x04, 0x2E];
429        const OID_PSEUDONYM: [u8; 3] = [0x55_u8, 0x04, 0x41];
430        const OID_DOMAIN_COMPONENT: [u8; 10] = [
431            0x09_u8, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19,
432        ];
433        const OID_MATTER_NODE_ID: [u8; 10] = [
434            0x2B_u8, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x01, 0x01,
435        ];
436        const OID_MATTER_FW_SIGNING_ID: [u8; 10] = [
437            0x2B_u8, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x01, 0x02,
438        ];
439        const OID_MATTER_ICAC_ID: [u8; 10] = [
440            0x2B_u8, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x01, 0x03,
441        ];
442        const OID_MATTER_RCAC_ID: [u8; 10] = [
443            0x2B_u8, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x01, 0x04,
444        ];
445        const OID_MATTER_FABRIC_ID: [u8; 10] = [
446            0x2B_u8, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x01, 0x05,
447        ];
448        const OID_MATTER_CASE_AUTH_TAG: [u8; 10] = [
449            0x2B_u8, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x01, 0x06,
450        ];
451
452        const DN_ENCODING: [(&str, &[u8], Option<IntToStringLen>); 22] = [
453            ("Common Name:", &OID_COMMON_NAME, None),
454            ("Surname:", &OID_SURNAME, None),
455            ("Serial Number", &OID_SERIAL_NUMBER, None),
456            ("Country Name", &OID_COUNTRY_NAME, None),
457            ("Locality name", &OID_LOCALITY_NAME, None),
458            ("State Name", &OID_STATE_NAME, None),
459            ("Org Name", &OID_ORGANIZATION_NAME, None),
460            ("OU Name", &OID_ORGANIZATIONAL_UNIT_NAME, None),
461            ("Title", &OID_TITLE, None),
462            ("Name", &OID_NAME, None),
463            ("Given Name", &OID_GIVEN_NAME, None),
464            ("Initials", &OID_INITIALS, None),
465            ("Gen Qualifier", &OID_GENERATION_QUALIFIER, None),
466            ("DN Qualifier", &OID_DN_QUALIFIER, None),
467            ("Pseudonym", &OID_PSEUDONYM, None),
468            ("Domain Component", &OID_DOMAIN_COMPONENT, None),
469            (
470                "Chip Node Id:",
471                &OID_MATTER_NODE_ID,
472                Some(IntToStringLen::Len16),
473            ),
474            (
475                "Chip Firmware Signing Id:",
476                &OID_MATTER_FW_SIGNING_ID,
477                Some(IntToStringLen::Len16),
478            ),
479            (
480                "Chip ICA Id:",
481                &OID_MATTER_ICAC_ID,
482                Some(IntToStringLen::Len16),
483            ),
484            (
485                "Chip Root CA Id:",
486                &OID_MATTER_RCAC_ID,
487                Some(IntToStringLen::Len16),
488            ),
489            (
490                "Chip Fabric Id:",
491                &OID_MATTER_FABRIC_ID,
492                Some(IntToStringLen::Len16),
493            ),
494            (
495                "Chip NOC CAT Id:",
496                &OID_MATTER_CASE_AUTH_TAG,
497                Some(IntToStringLen::Len8),
498            ),
499        ];
500
501        w.start_seq(tag)?;
502        for dn in values {
503            let dn = dn?;
504            let tag = dn.tag();
505
506            if let Ok(tag) = &tag {
507                let index = *tag as usize - 1;
508                if index <= DN_ENCODING.len() {
509                    let this = &DN_ENCODING[index];
510                    dn.encode(this.0, this.1, w, this.2)?;
511                } else {
512                    // Non Matter DNs are encoded as
513                    error!("Invalid DN, too high {:?}", tag);
514                }
515            } else {
516                // Non Matter DNs are encoded as
517                error!("Non Matter DNs are not yet supported {:?}", tag);
518            }
519        }
520        w.end_seq()?;
521        Ok(())
522    }
523
524    fn encode(
525        &self,
526        name: &str,
527        oid: &[u8],
528        w: &mut dyn CertConsumer,
529        // Only applicable for integer values
530        expected_len: Option<IntToStringLen>,
531    ) -> Result<(), Error> {
532        w.start_set("")?;
533        w.start_seq("")?;
534        w.oid(name, oid)?;
535        match self.value()? {
536            DNValue::Uint(v) => match expected_len {
537                Some(IntToStringLen::Len16) => {
538                    let mut string = heapless::String::<32>::new();
539                    write_unwrap!(&mut string, "{:016X}", v);
540                    w.utf8str("", &string)?
541                }
542                Some(IntToStringLen::Len8) => {
543                    let mut string = heapless::String::<32>::new();
544                    write_unwrap!(&mut string, "{:08X}", v);
545                    w.utf8str("", &string)?
546                }
547                _ => {
548                    error!("Invalid encoding");
549                    Err(ErrorCode::Invalid)?
550                }
551            },
552            DNValue::Utf8(v) => {
553                w.utf8str("", v)?;
554            }
555            DNValue::PrintableStr(v) => {
556                w.printstr("", v)?;
557            }
558        }
559        w.end_seq()?;
560        w.end_set()
561    }
562}
563
564#[derive(Copy, Clone)]
565/// Describes the expected string length while encoding an integer as a string
566enum IntToStringLen {
567    Len16,
568    Len8,
569}
570
571#[derive(FromTLV, ToTLV, Debug, Clone, PartialEq, Eq, Hash)]
572#[cfg_attr(feature = "defmt", derive(defmt::Format))]
573#[tlvargs(lifetime = "'a")]
574pub struct CertRef<'a>(TLVElement<'a>);
575
576impl<'a> CertRef<'a> {
577    pub const fn new(tlv: TLVElement<'a>) -> Self {
578        Self(tlv)
579    }
580
581    fn serial_no(&self) -> Result<&[u8], Error> {
582        self.0.structure()?.find_ctx(1)?.str()
583    }
584
585    fn sign_algo(&self) -> Result<u8, Error> {
586        self.0.structure()?.find_ctx(2)?.u8()
587    }
588
589    fn issuer(&self) -> Result<TLVList<'a, DN<'a>>, Error> {
590        TLVList::new(self.0.structure()?.find_ctx(3)?)
591    }
592
593    fn not_before(&self) -> Result<u32, Error> {
594        self.0.structure()?.find_ctx(4)?.u32()
595    }
596
597    fn not_after(&self) -> Result<u32, Error> {
598        self.0.structure()?.find_ctx(5)?.u32()
599    }
600
601    fn subject(&self) -> Result<TLVList<'a, DN<'a>>, Error> {
602        TLVList::new(self.0.structure()?.find_ctx(6)?)
603    }
604
605    fn pubkey_algo(&self) -> Result<u8, Error> {
606        self.0.structure()?.find_ctx(7)?.u8()
607    }
608
609    fn ec_curve_id(&self) -> Result<u8, Error> {
610        self.0.structure()?.find_ctx(8)?.u8()
611    }
612
613    pub fn pubkey(&self) -> Result<&[u8], Error> {
614        self.0.structure()?.find_ctx(9)?.str()
615    }
616
617    fn extensions(&self) -> Result<TLVList<'a, Extension<'a>>, Error> {
618        TLVList::new(self.0.structure()?.find_ctx(10)?)
619    }
620
621    fn signature(&self) -> Result<&[u8], Error> {
622        self.0.structure()?.find_ctx(11)?.str()
623    }
624
625    pub fn get_node_id(&self) -> Result<u64, Error> {
626        let dn = self
627            .subject()?
628            .iter()
629            .do_try_find(|dn| Ok(dn.tag()? == DNTag::NodeId))?
630            .ok_or(ErrorCode::NoNodeId)?;
631
632        dn.uint()
633    }
634
635    pub fn get_cat_ids(&self, output: &mut [u32]) -> Result<(), Error> {
636        let mut offset = 0;
637
638        self.subject()?.iter().try_for_each(|dn| {
639            let dn = dn?;
640
641            if dn.tag()? == DNTag::NocCat {
642                if offset == output.len() {
643                    Err(ErrorCode::InvalidData)?;
644                }
645
646                output[offset] = dn.uint()? as u32;
647                offset += 1;
648            }
649
650            Ok(())
651        })
652    }
653
654    pub fn get_fabric_id(&self) -> Result<u64, Error> {
655        let dn = self
656            .subject()?
657            .iter()
658            .do_try_find(|dn| Ok(dn.tag()? == DNTag::FabricId))?
659            .ok_or(ErrorCode::NoFabricId)?;
660
661        dn.uint()
662    }
663
664    /// Subject CA-ID — the `RootCaId` of an RCAC or the `IcaId` of an
665    /// ICAC. Matter-issued RCACs always carry exactly one `RootCaId`
666    /// in their subject DN; ICACs always carry exactly one `IcaId`
667    /// (spec). Returns the first matching value; errors out if
668    /// neither tag is present.
669    pub fn get_ca_id(&self) -> Result<u64, Error> {
670        let dn = self
671            .subject()?
672            .iter()
673            .do_try_find(|dn| {
674                let tag = dn.tag()?;
675                Ok(tag == DNTag::RootCaId || tag == DNTag::IcaId)
676            })?
677            .ok_or(ErrorCode::InvalidData)?;
678
679        dn.uint()
680    }
681
682    fn get_subject_key_id(&self) -> Result<&[u8], Error> {
683        let extension = self
684            .extensions()?
685            .iter()
686            .do_try_find(|extension| Ok(matches!(extension, Extension::SubjectKeyId(_))))?
687            .ok_or(Error::new(ErrorCode::Invalid))?;
688
689        let Extension::SubjectKeyId(id) = extension else {
690            unreachable!();
691        };
692
693        Ok(id.0)
694    }
695
696    /// `BasicConstraints` extension value, or `None` if the extension is absent.
697    ///
698    /// Returned as `(is_ca, path_len_constraint)`. RCAC and ICAC SHALL set
699    /// `is_ca = true`; NOC SHALL set `is_ca = false`. Only ICAC SHALL carry a
700    /// `path_len_constraint` (value `0`) per Matter spec.
701    fn basic_constraints(&self) -> Result<Option<(bool, Option<u8>)>, Error> {
702        let ext = self
703            .extensions()?
704            .iter()
705            .do_try_find(|e| Ok(matches!(e, Extension::BasicConstraints(_))))?;
706
707        Ok(match ext {
708            Some(Extension::BasicConstraints(bc)) => Some((bc.is_ca, bc.path)),
709            _ => None,
710        })
711    }
712
713    /// `BasicConstraints.pathLenConstraint`, or `None` if absent (either the
714    /// extension itself is missing or the field is omitted).
715    ///
716    /// Exposed for the `AddTrustedRootCertificate` flow, which the Matter spec
717    /// requires to additionally reject an RCAC whose `pathLenConstraint` is
718    /// greater than `1` (see CHIP's `ValidateChipRCAC`).
719    pub fn basic_constraints_path_len(&self) -> Result<Option<u8>, Error> {
720        Ok(self.basic_constraints()?.and_then(|(_, p)| p))
721    }
722
723    /// Whether ANY `future-extensions` extension on this cert carries a DER
724    /// X.509 sub-extension with `critical = TRUE`.
725    ///
726    /// Matter TLV `future-extensions` is a DER blob spliced verbatim into the
727    /// X.509 TBS extensions SEQUENCE; per RFC 5280 §4.2 (and Matter Core
728    /// spec), a verifier MUST reject the certificate if it encounters a
729    /// critical extension it doesn't recognise. None of those sub-extensions
730    /// are recognised by this verifier, so any critical flag is a rejection.
731    fn has_critical_future_extension(&self) -> Result<bool, Error> {
732        for ext in self.extensions()?.iter() {
733            if let Extension::FutureExtensions(bytes) = ext? {
734                if der_blob_has_critical_extension(bytes.0)? {
735                    return Ok(true);
736                }
737            }
738        }
739        Ok(false)
740    }
741
742    /// `KeyUsage` extension bits (Matter-TLV encoding — see
743    /// [`crate::cert::x509::key_usage_tlv`]), or `None` if absent.
744    fn key_usage(&self) -> Result<Option<u16>, Error> {
745        let ext = self
746            .extensions()?
747            .iter()
748            .do_try_find(|e| Ok(matches!(e, Extension::KeyUsage(_))))?;
749
750        Ok(match ext {
751            Some(Extension::KeyUsage(bits)) => Some(bits),
752            _ => None,
753        })
754    }
755
756    /// Whether the `ExtendedKeyUsage` extension is present AND contains every
757    /// purpose in `required` (each value matches the small-integer Matter TLV
758    /// encoding: `1`=ServerAuth, `2`=ClientAuth, etc.).
759    ///
760    /// Returns `Ok(false)` if the extension is absent or any required purpose
761    /// is missing.
762    fn ext_key_usage_has_all(&self, required: &[u8]) -> Result<bool, Error> {
763        let ext = self
764            .extensions()?
765            .iter()
766            .do_try_find(|e| Ok(matches!(e, Extension::ExtKeyUsage(_))))?;
767
768        let Some(Extension::ExtKeyUsage(list)) = ext else {
769            return Ok(false);
770        };
771
772        for need in required {
773            let mut found = false;
774            for v in list.iter() {
775                if v? == *need {
776                    found = true;
777                    break;
778                }
779            }
780
781            if !found {
782                return Ok(false);
783            }
784        }
785
786        Ok(true)
787    }
788
789    /// Subject-DN-derived Matter certificate type.
790    ///
791    /// Per Matter Core spec ("Operational Certificate Encoding"), a NOC
792    /// has a `Matter-Node-Id` RDN, an ICAC a `Matter-ICAC-Id`, and an RCAC a
793    /// `Matter-RootCA-Id`. The three are mutually exclusive in a well-formed
794    /// cert; we pick the first one we recognise.
795    fn cert_type(&self) -> Result<MatterCertType, Error> {
796        for dn in self.subject()?.iter() {
797            match dn?.tag()? {
798                DNTag::NodeId => return Ok(MatterCertType::Noc),
799                DNTag::IcaId => return Ok(MatterCertType::Icac),
800                DNTag::RootCaId => return Ok(MatterCertType::Rcac),
801                _ => {}
802            }
803        }
804
805        Err(ErrorCode::InvalidData.into())
806    }
807
808    fn is_authority(&self, their: &CertRef) -> Result<bool, Error> {
809        let their_subject = their.get_subject_key_id()?;
810
811        let authority = self.extensions()?.iter().do_try_find(|extension| {
812            Ok(if let Extension::AuthorityKeyId(id) = extension {
813                id.0 == their_subject
814            } else {
815                false
816            })
817        })?;
818
819        Ok(authority.is_some())
820    }
821
822    /// Whether this certificate is self-signed (its `AuthorityKeyId` matches
823    /// its `SubjectKeyId`). Matter-issued RCACs are always self-signed; an
824    /// ICAC or NOC must not be (Matter Core spec).
825    pub fn is_self_signed(&self) -> Result<bool, Error> {
826        self.is_authority(self)
827    }
828
829    pub fn as_asn1(&self, buf: &mut [u8]) -> Result<usize, Error> {
830        let mut w = ASN1Writer::new(buf);
831        self.encode(&mut w)?;
832        Ok(w.as_slice().len())
833    }
834
835    /// Start a chain verification of this certificate.
836    ///
837    /// Every cert added to the chain (the leaf here, intermediates via
838    /// [`CertVerifier::add_cert`], and the self-signed root via
839    /// [`CertVerifier::finalise`]) is checked against `lkg_utc_secs`
840    /// for its `NotBefore` / `NotAfter` validity window, per Matter
841    /// Core spec which mandates use of the Last-Known-Good UTC
842    /// Time when no live trusted real-time-clock value is available.
843    /// Callers snapshot the value from
844    /// [`crate::Matter::last_known_utc_time`] (Matter-epoch seconds).
845    pub fn verify_chain_start<C: Crypto>(
846        &'a self,
847        crypto: C,
848        utc_time: UtcTime,
849    ) -> CertVerifier<'a, C> {
850        CertVerifier::new(self, crypto, utc_time)
851    }
852
853    fn encode(&self, w: &mut dyn CertConsumer) -> Result<(), Error> {
854        w.start_seq("")?;
855
856        w.start_ctx("Version:", 0)?;
857        w.integer("", &[2])?;
858        w.end_ctx()?;
859
860        w.integer("Serial Num:", self.serial_no()?)?;
861
862        w.start_seq("Signature Algorithm:")?;
863        let (str, oid) = match get_sign_algo(self.sign_algo()?).ok_or(ErrorCode::Invalid)? {
864            SignAlgoValue::ECDSAWithSHA256 => ("ECDSA with SHA256", OID_ECDSA_WITH_SHA256),
865        };
866        w.oid(str, &oid)?;
867        w.end_seq()?;
868
869        DN::encode_all(self.issuer()?.iter(), "Issuer:", w)?;
870
871        w.start_seq("Validity:")?;
872        w.utctime("Not Before:", self.not_before()?.into())?;
873        if self.not_after()? == 0 {
874            // As per the spec a Not-After value of 0, indicates no well-defined
875            // expiration date and should return in GeneralizedTime of 99991231235959Z
876            w.utctime("Not After:", MATTER_CERT_DOESNT_EXPIRE)?;
877        } else {
878            w.utctime("Not After:", self.not_after()?.into())?;
879        }
880        w.end_seq()?;
881
882        DN::encode_all(self.subject()?.iter(), "Subject:", w)?;
883
884        w.start_seq("")?;
885        w.start_seq("Public Key Algorithm")?;
886        let (str, pub_key) = match get_pubkey_algo(self.pubkey_algo()?).ok_or(ErrorCode::Invalid)? {
887            PubKeyAlgoValue::EcPubKey => ("ECPubKey", OID_PUB_KEY_ECPUBKEY),
888        };
889        w.oid(str, &pub_key)?;
890        let (str, curve_id) =
891            match get_ec_curve_id(self.ec_curve_id()?).ok_or(ErrorCode::Invalid)? {
892                EcCurveIdValue::Prime256V1 => ("Prime256v1", OID_EC_TYPE_PRIME256V1),
893            };
894        w.oid(str, &curve_id)?;
895        w.end_seq()?;
896
897        w.bitstr("Public-Key:", false, self.pubkey()?)?;
898        w.end_seq()?;
899
900        Extension::encode_all(self.extensions()?.iter(), w)?;
901
902        // We do not encode the Signature in the DER certificate
903
904        w.end_seq()
905    }
906}
907
908impl fmt::Display for CertRef<'_> {
909    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
910        let mut printer = CertPrinter::new(f);
911
912        self.encode(&mut printer).map_err(|_| fmt::Error)?;
913
914        // Signature is not encoded by the Cert Decoder
915        writeln!(
916            f,
917            "Signature: {:x?}",
918            self.signature().map_err(|_| fmt::Error)?
919        )
920    }
921}
922
923/// Matter operational-cert type, derived from the subject DN's CA-id tags.
924#[derive(Debug, Clone, Copy, PartialEq, Eq)]
925#[cfg_attr(feature = "defmt", derive(defmt::Format))]
926enum MatterCertType {
927    /// Root CA certificate (subject carries `Matter-RootCA-Id`).
928    Rcac,
929    /// Intermediate CA certificate (subject carries `Matter-ICAC-Id`).
930    Icac,
931    /// Node Operational Certificate (subject carries `Matter-Node-Id`).
932    Noc,
933}
934
935/// Walk a Matter TLV `future-extensions` blob — zero or more
936/// concatenated DER X.509 `Extension` SEQUENCEs (RFC 5280 §4.1:
937/// `SEQUENCE { OID, BOOLEAN OPTIONAL critical, OCTET STRING extnValue }`) —
938/// and return `true` on the first sub-extension whose `critical` flag is
939/// `TRUE`.
940fn der_blob_has_critical_extension(bytes: &[u8]) -> Result<bool, Error> {
941    use der::asn1::{AnyRef, ObjectIdentifier, OctetStringRef};
942    use der::{Decode, Reader, SliceReader, Tag};
943
944    let mut reader = SliceReader::new(bytes).map_err(|_| ErrorCode::InvalidData)?;
945    while !reader.is_finished() {
946        let any = AnyRef::decode(&mut reader).map_err(|_| ErrorCode::InvalidData)?;
947        let mut inner = SliceReader::new(any.value()).map_err(|_| ErrorCode::InvalidData)?;
948
949        // Skip the OID.
950        ObjectIdentifier::decode(&mut inner).map_err(|_| ErrorCode::InvalidData)?;
951
952        // OPTIONAL critical BOOLEAN before the mandatory OCTET STRING
953        // `extnValue`.
954        if !inner.is_finished()
955            && inner.peek_tag().map_err(|_| ErrorCode::InvalidData)? == Tag::Boolean
956            && bool::decode(&mut inner).map_err(|_| ErrorCode::InvalidData)?
957        {
958            return Ok(true);
959        }
960        // Consume `extnValue` so we land on the next `Extension`
961        // SEQUENCE (or the end of the blob).
962        let _ = OctetStringRef::decode(&mut inner).map_err(|_| ErrorCode::InvalidData)?;
963    }
964    Ok(false)
965}
966
967pub struct CertVerifier<'a, C> {
968    cert: &'a CertRef<'a>,
969    crypto: C,
970    utc_time: UtcTime,
971    /// Position of `cert` in the chain being verified. `0` for the leaf
972    /// (NOC), incremented at each step toward the root.
973    depth: u8,
974}
975
976impl<'a, C: Crypto> CertVerifier<'a, C> {
977    pub const fn new(cert: &'a CertRef<'a>, crypto: C, utc_time: UtcTime) -> Self {
978        Self {
979            cert,
980            crypto,
981            utc_time,
982            depth: 0,
983        }
984    }
985
986    /// Validate the Matter-mandated usage policy on `self.cert` for its
987    /// position in the chain against the spec rules from Matter Core spec
988    /// ("Operational Certificate Encoding"):
989    ///
990    /// - A leaf NOC (cert type `Noc`, depth `0`) SHALL have
991    ///   `BasicConstraints.cA = FALSE`, a `KeyUsage` containing
992    ///   `digitalSignature`, and an `ExtendedKeyUsage` containing both
993    ///   `serverAuth` and `clientAuth`.
994    /// - A CA cert (cert type `Icac` or `Rcac`) SHALL have
995    ///   `BasicConstraints.cA = TRUE` and a `KeyUsage` containing
996    ///   `keyCertSign`. If a `pathLenConstraint` is present, the number
997    ///   of non-self-issued intermediates below this cert
998    ///   (`depth - 1`) MUST NOT exceed it.
999    /// - A NOC SHALL NOT appear at depth > 0, and an ICAC/RCAC at
1000    ///   depth `0` is only valid as a standalone-root validation (e.g.
1001    ///   the `AddTrustedRootCertificate` flow), which still requires
1002    ///   the CA usage policy here.
1003    fn verify_usage(&self) -> Result<(), Error> {
1004        use self::x509::key_usage_tlv;
1005
1006        // Per RFC 5280 §4.2 / Matter Core spec, an unrecognised critical
1007        // extension SHALL force certificate rejection. Every sub-extension
1008        // carried in a `future-extensions` blob is by definition not one
1009        // we process here, so any critical flag is fatal.
1010        if self.cert.has_critical_future_extension()? {
1011            Err(ErrorCode::InvalidData)?;
1012        }
1013
1014        let cert_type = self.cert.cert_type()?;
1015        let key_usage = self
1016            .cert
1017            .key_usage()?
1018            .ok_or_else(|| Error::new(ErrorCode::InvalidData))?;
1019
1020        match cert_type {
1021            MatterCertType::Noc => {
1022                // A NOC is only legal at depth 0 (chain leaf).
1023                if self.depth != 0 {
1024                    Err(ErrorCode::InvalidData)?;
1025                }
1026
1027                // `BasicConstraints.cA` SHALL be FALSE. The extension
1028                // itself is mandatory on a NOC; treat absence as a
1029                // violation.
1030                let (is_ca, _) = self
1031                    .cert
1032                    .basic_constraints()?
1033                    .ok_or_else(|| Error::new(ErrorCode::InvalidData))?;
1034                if is_ca {
1035                    Err(ErrorCode::InvalidData)?;
1036                }
1037
1038                // `KeyUsage` MUST include `digitalSignature`.
1039                if (key_usage & key_usage_tlv::DIGITAL_SIGNATURE) == 0 {
1040                    Err(ErrorCode::InvalidData)?;
1041                }
1042
1043                // `ExtendedKeyUsage` MUST include both `serverAuth` (1)
1044                // and `clientAuth` (2) per the Matter NOC profile.
1045                const SERVER_AUTH: u8 = 1;
1046                const CLIENT_AUTH: u8 = 2;
1047                if !self
1048                    .cert
1049                    .ext_key_usage_has_all(&[SERVER_AUTH, CLIENT_AUTH])?
1050                {
1051                    Err(ErrorCode::InvalidData)?;
1052                }
1053            }
1054            MatterCertType::Icac | MatterCertType::Rcac => {
1055                let (is_ca, path_len) = self
1056                    .cert
1057                    .basic_constraints()?
1058                    .ok_or_else(|| Error::new(ErrorCode::InvalidData))?;
1059                if !is_ca {
1060                    Err(ErrorCode::InvalidData)?;
1061                }
1062
1063                // `KeyUsage` MUST include `keyCertSign`.
1064                if (key_usage & key_usage_tlv::KEY_CERT_SIGN) == 0 {
1065                    Err(ErrorCode::InvalidData)?;
1066                }
1067
1068                // RFC 5280 §4.2.1.9: `pathLenConstraint` gives the
1069                // maximum number of non-self-issued intermediate certs
1070                // that may follow this one in a valid path. The leaf
1071                // (depth 0) is not counted, so we compare against
1072                // `depth - 1`. When this cert is itself at depth 0
1073                // (standalone-root validation) the constraint is
1074                // trivially satisfied.
1075                if let (Some(max_intermediates), depth) = (path_len, self.depth) {
1076                    if depth > 0 && depth - 1 > max_intermediates {
1077                        Err(ErrorCode::InvalidData)?;
1078                    }
1079                }
1080            }
1081        }
1082
1083        Ok(())
1084    }
1085
1086    pub fn add_cert<'p>(
1087        self,
1088        parent: &'p CertRef<'p>,
1089        buf: &mut [u8],
1090    ) -> Result<CertVerifier<'p, C>, Error>
1091    where
1092        'a: 'p,
1093    {
1094        if !self.cert.is_authority(parent)? {
1095            Err(ErrorCode::InvalidAuthKey)?;
1096        }
1097
1098        {
1099            let len = self.cert.as_asn1(buf)?;
1100            let asn1 = &buf[..len];
1101
1102            let result = self
1103                .crypto
1104                .pub_key(parent.pubkey()?.try_into()?)?
1105                .verify(asn1, self.cert.signature()?.try_into()?)?;
1106
1107            if !result {
1108                Err(ErrorCode::InvalidSignature)?;
1109            }
1110        }
1111
1112        // Validity-period check against the Last-Known-Good UTC Time
1113        // (Matter Core spec). `NotBefore` / `NotAfter` are u32
1114        // Matter-epoch seconds; `NotAfter = 0` means unconstrained.
1115        let not_before = self.cert.not_before()? as u64;
1116        let not_after = self.cert.not_after()?;
1117
1118        if not_after > 0 && self.utc_time.any_secs() > not_after as u64 {
1119            Err(ErrorCode::InvalidTime)?;
1120        }
1121
1122        if let Some(secs) = self.utc_time.reliable_secs() {
1123            // Only check the NotBefore if we have a reliable UTC time;
1124            // if we have only the Last-Known-Good time, then we may be
1125            // in a gap between the LKG time and the real current time,
1126            // and we don't want to reject certs that were generated _after_
1127            // our clock lost sync
1128            if secs < not_before {
1129                Err(ErrorCode::InvalidTime)?;
1130            }
1131        }
1132
1133        // Matter spec usage checks (BasicConstraints / KeyUsage /
1134        // ExtendedKeyUsage / pathLenConstraint / cert type)
1135        self.verify_usage()?;
1136
1137        Ok(CertVerifier {
1138            cert: parent,
1139            crypto: self.crypto,
1140            utc_time: self.utc_time,
1141            depth: self.depth.saturating_add(1),
1142        })
1143    }
1144
1145    pub fn finalise(self, buf: &mut [u8]) -> Result<(), Error> {
1146        let cert = self.cert;
1147        self.add_cert(cert, buf)?;
1148
1149        Ok(())
1150    }
1151}
1152
1153pub trait CertConsumer {
1154    fn start_seq(&mut self, tag: &str) -> Result<(), Error>;
1155    fn end_seq(&mut self) -> Result<(), Error>;
1156    fn integer(&mut self, tag: &str, i: &[u8]) -> Result<(), Error>;
1157    fn printstr(&mut self, tag: &str, s: &str) -> Result<(), Error>;
1158    fn utf8str(&mut self, tag: &str, s: &str) -> Result<(), Error>;
1159    fn bitstr(&mut self, tag: &str, truncate: bool, s: &[u8]) -> Result<(), Error>;
1160    fn ostr(&mut self, tag: &str, s: &[u8]) -> Result<(), Error>;
1161    fn start_compound_ostr(&mut self, tag: &str) -> Result<(), Error>;
1162    fn end_compound_ostr(&mut self) -> Result<(), Error>;
1163    fn bool(&mut self, tag: &str, b: bool) -> Result<(), Error>;
1164    fn start_set(&mut self, tag: &str) -> Result<(), Error>;
1165    fn end_set(&mut self) -> Result<(), Error>;
1166    fn ctx(&mut self, tag: &str, id: u8, val: &[u8]) -> Result<(), Error>;
1167    fn start_ctx(&mut self, tag: &str, id: u8) -> Result<(), Error>;
1168    fn end_ctx(&mut self) -> Result<(), Error>;
1169    fn oid(&mut self, tag: &str, oid: &[u8]) -> Result<(), Error>;
1170    fn utctime(&mut self, tag: &str, epoch: u64) -> Result<(), Error>;
1171    /// Emit raw, pre-encoded DER bytes into the output stream verbatim.
1172    ///
1173    /// This is used to carry through TLV `future-extensions`, whose payload is
1174    /// already a fully DER-encoded X.509 Extension (or a sequence of them) and
1175    /// must be reproduced byte-for-byte for the TBS hash to match the signed
1176    /// data.
1177    fn raw(&mut self, tag: &str, data: &[u8]) -> Result<(), Error>;
1178}
1179
1180#[cfg(test)]
1181mod tests {
1182    use crate::crypto::test_only_crypto;
1183    use crate::dm::clusters::time_sync::UtcTime;
1184    use crate::error::ErrorCode;
1185    use crate::tlv::{FromTLV, TLVElement, TagType, ToTLV};
1186    use crate::utils::storage::WriteBuf;
1187
1188    use super::CertRef;
1189
1190    #[test]
1191    fn test_asn1_encode_success() {
1192        {
1193            let mut asn1_buf = [0u8; 1000];
1194            let c = CertRef::new(TLVElement::new(CHIP_CERT_INPUT1));
1195            let len = unwrap!(c.as_asn1(&mut asn1_buf));
1196            assert_eq!(ASN1_OUTPUT1, &asn1_buf[..len]);
1197        }
1198
1199        {
1200            let mut asn1_buf = [0u8; 1000];
1201            let c = CertRef::new(TLVElement::new(CHIP_CERT_INPUT2));
1202            let len = unwrap!(c.as_asn1(&mut asn1_buf));
1203            assert_eq!(ASN1_OUTPUT2, &asn1_buf[..len]);
1204        }
1205
1206        {
1207            let mut asn1_buf = [0u8; 1000];
1208            let c = CertRef::new(TLVElement::new(CHIP_CERT_TXT_IN_DN));
1209            let len = unwrap!(c.as_asn1(&mut asn1_buf));
1210            assert_eq!(ASN1_OUTPUT_TXT_IN_DN, &asn1_buf[..len]);
1211        }
1212    }
1213
1214    #[test]
1215    fn test_verify_chain_success() {
1216        let mut buf = [0; 1000];
1217        let noc = CertRef::new(TLVElement::new(NOC1_SUCCESS));
1218        let icac = CertRef::new(TLVElement::new(ICAC1_SUCCESS));
1219        let rca = CertRef::new(TLVElement::new(RCA1_SUCCESS));
1220        let time = UtcTime::Reliable(unwrap!(noc.not_before()) as u64 * 1_000_000);
1221        let a = noc.verify_chain_start(test_only_crypto(), time);
1222        unwrap!(
1223            unwrap!(unwrap!(a.add_cert(&icac, &mut buf)).add_cert(&rca, &mut buf))
1224                .finalise(&mut buf)
1225        );
1226    }
1227
1228    #[test]
1229    fn test_verify_chain_incomplete() {
1230        // The chain doesn't lead up to a self-signed certificate
1231
1232        let mut buf = [0; 1000];
1233        let noc = CertRef::new(TLVElement::new(NOC1_SUCCESS));
1234        let icac = CertRef::new(TLVElement::new(ICAC1_SUCCESS));
1235        let time = UtcTime::Reliable(unwrap!(noc.not_before()) as u64 * 1_000_000);
1236        let a = noc.verify_chain_start(test_only_crypto(), time);
1237        assert_eq!(
1238            Err(ErrorCode::InvalidAuthKey),
1239            unwrap!(a.add_cert(&icac, &mut buf))
1240                .finalise(&mut buf)
1241                .map_err(|e| e.code())
1242        );
1243    }
1244
1245    #[test]
1246    fn test_auth_key_chain_incorrect() {
1247        let mut buf = [0; 1000];
1248        let noc = CertRef::new(TLVElement::new(NOC1_AUTH_KEY_FAIL));
1249        let icac = CertRef::new(TLVElement::new(ICAC1_SUCCESS));
1250        let a = noc.verify_chain_start(test_only_crypto(), UtcTime::Reliable(0));
1251        assert_eq!(
1252            Err(ErrorCode::InvalidAuthKey),
1253            a.add_cert(&icac, &mut buf)
1254                .map(|_| ())
1255                .map_err(|e| e.code())
1256        );
1257    }
1258
1259    #[test]
1260    fn test_zero_value_of_not_after_field() {
1261        let mut buf = [0; 1000];
1262        let noc = CertRef::new(TLVElement::new(NOC_NOT_AFTER_ZERO));
1263        let rca = CertRef::new(TLVElement::new(RCA_FOR_NOC_NOT_AFTER_ZERO));
1264
1265        let time = UtcTime::Reliable(unwrap!(noc.not_before()) as u64 * 1_000_000);
1266        let v = noc.verify_chain_start(test_only_crypto(), time);
1267        let v = unwrap!(v.add_cert(&rca, &mut buf));
1268        unwrap!(v.finalise(&mut buf));
1269    }
1270
1271    #[test]
1272    fn test_cert_corrupted() {
1273        let mut buf = [0; 1000];
1274        let noc = CertRef::new(TLVElement::new(NOC1_CORRUPT_CERT));
1275        let icac = CertRef::new(TLVElement::new(ICAC1_SUCCESS));
1276        let a = noc.verify_chain_start(test_only_crypto(), UtcTime::Reliable(0));
1277        assert_eq!(
1278            Err(ErrorCode::InvalidSignature),
1279            a.add_cert(&icac, &mut buf)
1280                .map(|_| ())
1281                .map_err(|e| e.code())
1282        );
1283    }
1284
1285    #[test]
1286    fn test_tlv_conversions() {
1287        const TEST_INPUT: &[&[u8]; 4] = &[NOC1_SUCCESS, ICAC1_SUCCESS, ICAC2_SUCCESS, RCA1_SUCCESS];
1288
1289        for input in TEST_INPUT.iter() {
1290            info!("Testing next input...");
1291            let root = TLVElement::new(input);
1292            let cert = unwrap!(CertRef::from_tlv(&root));
1293            let mut buf = [0u8; 1024];
1294            let mut wb = WriteBuf::new(&mut buf);
1295            unwrap!(cert.to_tlv(&TagType::Anonymous, &mut wb));
1296
1297            let root2 = TLVElement::new(wb.as_slice());
1298            let cert2 = unwrap!(CertRef::from_tlv(&root2));
1299            assert_eq!(cert, cert2);
1300        }
1301    }
1302
1303    #[test]
1304    fn test_unordered_extensions() {
1305        let mut buf = [0; 1000];
1306
1307        let cert = CertRef::new(TLVElement::new(UNORDERED_EXTENSIONS_CHIP));
1308
1309        let asn1_len = unwrap!(cert.as_asn1(&mut buf));
1310        assert_eq!(&buf[..asn1_len], UNORDERED_EXTENSIONS_DER);
1311    }
1312
1313    /// Direct coverage of the `future-extensions` DER walker used by
1314    /// `CertRef::has_critical_future_extension` and gated by
1315    /// `CertVerifier::verify_usage`. Exercising it via synthetic bytes
1316    /// avoids having to rebuild and re-sign a full Matter cert just to
1317    /// inject a critical sub-extension.
1318    #[test]
1319    fn test_der_blob_has_critical_extension() {
1320        use super::der_blob_has_critical_extension;
1321
1322        // A minimal DER `Extension` SEQUENCE we can splice in/out of a
1323        // `future-extensions` blob:
1324        //   30 LL                                  -- SEQUENCE, length LL
1325        //     06 03 55 1D 63                       -- OID 2.5.29.99
1326        //     [optional 01 01 (00|FF)]             -- critical BOOLEAN
1327        //     04 00                                -- empty OCTET STRING
1328        const NON_CRITICAL: &[u8] = &[
1329            0x30, 0x0A, 0x06, 0x03, 0x55, 0x1D, 0x63, 0x01, 0x01, 0x00, 0x04, 0x00,
1330        ];
1331        const CRITICAL: &[u8] = &[
1332            0x30, 0x0A, 0x06, 0x03, 0x55, 0x1D, 0x63, 0x01, 0x01, 0xFF, 0x04, 0x00,
1333        ];
1334        const DEFAULTED: &[u8] = &[
1335            // BOOLEAN omitted entirely → defaults to FALSE.
1336            0x30, 0x07, 0x06, 0x03, 0x55, 0x1D, 0x63, 0x04, 0x00,
1337        ];
1338
1339        // Empty blob — vacuously fine.
1340        assert!(!unwrap!(der_blob_has_critical_extension(&[])));
1341
1342        // Single sub-extension, the three critical-flag spellings.
1343        assert!(!unwrap!(der_blob_has_critical_extension(NON_CRITICAL)));
1344        assert!(!unwrap!(der_blob_has_critical_extension(DEFAULTED)));
1345        assert!(unwrap!(der_blob_has_critical_extension(CRITICAL)));
1346
1347        // The walker must scan ALL sub-extensions, not just the first.
1348        let mut two_noncritical = heapless::Vec::<u8, 64>::new();
1349        unwrap!(two_noncritical.extend_from_slice(NON_CRITICAL));
1350        unwrap!(two_noncritical.extend_from_slice(DEFAULTED));
1351        assert!(!unwrap!(der_blob_has_critical_extension(&two_noncritical)));
1352
1353        let mut second_is_critical = heapless::Vec::<u8, 64>::new();
1354        unwrap!(second_is_critical.extend_from_slice(NON_CRITICAL));
1355        unwrap!(second_is_critical.extend_from_slice(CRITICAL));
1356        assert!(unwrap!(der_blob_has_critical_extension(
1357            &second_is_critical
1358        )));
1359    }
1360
1361    /// Sanity-check the per-extension accessors that `CertVerifier`
1362    /// gates the chain on. The baked NOC/ICAC/RCAC test data is the
1363    /// closest thing we have to real wire bytes — make sure the
1364    /// accessors return the spec-mandated shape for each cert type.
1365    #[test]
1366    fn test_extension_accessors_on_known_chain() {
1367        use super::x509::key_usage_tlv;
1368        use super::MatterCertType;
1369
1370        let noc = CertRef::new(TLVElement::new(NOC1_SUCCESS));
1371        let icac = CertRef::new(TLVElement::new(ICAC1_SUCCESS));
1372        let rca = CertRef::new(TLVElement::new(RCA1_SUCCESS));
1373
1374        // --- NOC (leaf, depth 0) ---
1375        assert_eq!(unwrap!(noc.cert_type()), MatterCertType::Noc);
1376
1377        let (noc_ca, noc_path) = unwrap!(unwrap!(noc.basic_constraints()));
1378        assert!(!noc_ca, "NOC must have cA = FALSE");
1379        assert_eq!(noc_path, None, "NOC must not carry pathLenConstraint");
1380
1381        let noc_ku = unwrap!(unwrap!(noc.key_usage()));
1382        assert_ne!(
1383            noc_ku & key_usage_tlv::DIGITAL_SIGNATURE,
1384            0,
1385            "NOC KeyUsage must include digitalSignature"
1386        );
1387
1388        assert!(
1389            unwrap!(noc.ext_key_usage_has_all(&[1, 2])),
1390            "NOC ExtKeyUsage must include both serverAuth (1) and clientAuth (2)"
1391        );
1392
1393        assert!(
1394            !unwrap!(noc.has_critical_future_extension()),
1395            "test NOC carries no future-extensions"
1396        );
1397
1398        // --- ICAC (intermediate CA) ---
1399        assert_eq!(unwrap!(icac.cert_type()), MatterCertType::Icac);
1400
1401        let (icac_ca, icac_path) = unwrap!(unwrap!(icac.basic_constraints()));
1402        assert!(icac_ca, "ICAC must have cA = TRUE");
1403        // Matter spec mandates pathLenConstraint=0 on an ICAC, but the
1404        // verifier only kicks in when the field is actually present
1405        // (RFC 5280 §4.2.1.9). The baked test data omits it entirely,
1406        // which we tolerate — assert the parser surfaces that faithfully.
1407        assert!(
1408            matches!(icac_path, None | Some(0)),
1409            "unexpected pathLenConstraint on ICAC: {icac_path:?}"
1410        );
1411
1412        let icac_ku = unwrap!(unwrap!(icac.key_usage()));
1413        assert_ne!(
1414            icac_ku & key_usage_tlv::KEY_CERT_SIGN,
1415            0,
1416            "ICAC KeyUsage must include keyCertSign"
1417        );
1418
1419        assert!(
1420            !unwrap!(icac.ext_key_usage_has_all(&[1, 2])),
1421            "ICAC has no ExtendedKeyUsage extension"
1422        );
1423
1424        // --- RCAC (root CA) ---
1425        assert_eq!(unwrap!(rca.cert_type()), MatterCertType::Rcac);
1426
1427        let (rca_ca, rca_path) = unwrap!(unwrap!(rca.basic_constraints()));
1428        assert!(rca_ca, "RCAC must have cA = TRUE");
1429        assert_eq!(rca_path, None, "RCAC must not carry pathLenConstraint");
1430        // Matches the failsafe `pathLen <= 1` accessor too.
1431        assert_eq!(unwrap!(rca.basic_constraints_path_len()), None);
1432
1433        let rca_ku = unwrap!(unwrap!(rca.key_usage()));
1434        assert_ne!(
1435            rca_ku & key_usage_tlv::KEY_CERT_SIGN,
1436            0,
1437            "RCAC KeyUsage must include keyCertSign"
1438        );
1439    }
1440
1441    // Group 1
1442    const NOC1_SUCCESS: &[u8] = &[
1443        0x15, 0x30, 0x1, 0x1, 0x1, 0x24, 0x2, 0x1, 0x37, 0x3, 0x24, 0x13, 0x1, 0x24, 0x15, 0x1,
1444        0x18, 0x26, 0x4, 0x80, 0x22, 0x81, 0x27, 0x26, 0x5, 0x80, 0x25, 0x4d, 0x3a, 0x37, 0x6,
1445        0x26, 0x11, 0x2, 0x5c, 0xbc, 0x0, 0x24, 0x15, 0x1, 0x18, 0x24, 0x7, 0x1, 0x24, 0x8, 0x1,
1446        0x30, 0x9, 0x41, 0x4, 0xba, 0x22, 0x56, 0x43, 0x4f, 0x59, 0x98, 0x32, 0x8d, 0xb8, 0xcb,
1447        0x3f, 0x24, 0x90, 0x9a, 0x96, 0x94, 0x43, 0x46, 0x67, 0xc2, 0x11, 0xe3, 0x80, 0x26, 0x65,
1448        0xfc, 0x65, 0x37, 0x77, 0x3, 0x25, 0x18, 0xd8, 0xdc, 0x85, 0xfa, 0xe6, 0x42, 0xe7, 0x55,
1449        0xc9, 0x37, 0xcc, 0xb, 0x78, 0x84, 0x3d, 0x2f, 0xac, 0x81, 0x88, 0x2e, 0x69, 0x0, 0xa5,
1450        0xfc, 0xcd, 0xe0, 0xad, 0xb2, 0x69, 0xca, 0x73, 0x37, 0xa, 0x35, 0x1, 0x28, 0x1, 0x18,
1451        0x24, 0x2, 0x1, 0x36, 0x3, 0x4, 0x2, 0x4, 0x1, 0x18, 0x30, 0x4, 0x14, 0x39, 0x68, 0x16,
1452        0x1e, 0xb5, 0x56, 0x6d, 0xd3, 0xf8, 0x61, 0xf2, 0x95, 0xf3, 0x55, 0xa0, 0xfb, 0xd2, 0x82,
1453        0xc2, 0x29, 0x30, 0x5, 0x14, 0xce, 0x60, 0xb4, 0x28, 0x96, 0x72, 0x27, 0x64, 0x81, 0xbc,
1454        0x4f, 0x0, 0x78, 0xa3, 0x30, 0x48, 0xfe, 0x6e, 0x65, 0x86, 0x18, 0x30, 0xb, 0x40, 0x2,
1455        0x88, 0x42, 0x0, 0x6f, 0xcc, 0xe0, 0xf0, 0x6c, 0xd9, 0xf9, 0x5e, 0xe4, 0xc2, 0xaa, 0x1f,
1456        0x57, 0x71, 0x62, 0xdb, 0x6b, 0x4e, 0xe7, 0x55, 0x3f, 0xc6, 0xc7, 0x9f, 0xf8, 0x30, 0xeb,
1457        0x16, 0x6e, 0x6d, 0xc6, 0x9c, 0xb, 0xb7, 0xe2, 0xb8, 0xe3, 0xe7, 0x57, 0x88, 0x7b, 0xda,
1458        0xe5, 0x79, 0x39, 0x6d, 0x2c, 0x37, 0xb2, 0x7f, 0xc3, 0x63, 0x2f, 0x7e, 0x70, 0xab, 0x5a,
1459        0x2c, 0xf7, 0x5b, 0x18,
1460    ];
1461
1462    const ICAC1_SUCCESS: &[u8] = &[
1463        21, 48, 1, 1, 0, 36, 2, 1, 55, 3, 36, 20, 0, 36, 21, 1, 24, 38, 4, 128, 34, 129, 39, 38, 5,
1464        128, 37, 77, 58, 55, 6, 36, 19, 1, 36, 21, 1, 24, 36, 7, 1, 36, 8, 1, 48, 9, 65, 4, 86, 25,
1465        119, 24, 63, 212, 255, 43, 88, 61, 233, 121, 52, 102, 223, 233, 0, 251, 109, 161, 239, 224,
1466        204, 220, 119, 48, 192, 111, 182, 45, 255, 190, 84, 160, 149, 117, 11, 139, 7, 188, 85,
1467        219, 156, 182, 85, 19, 8, 184, 223, 2, 227, 64, 107, 174, 52, 245, 12, 186, 201, 242, 191,
1468        241, 231, 80, 55, 10, 53, 1, 41, 1, 24, 36, 2, 96, 48, 4, 20, 206, 96, 180, 40, 150, 114,
1469        39, 100, 129, 188, 79, 0, 120, 163, 48, 72, 254, 110, 101, 134, 48, 5, 20, 212, 86, 147,
1470        190, 112, 121, 244, 156, 112, 107, 7, 111, 17, 28, 109, 229, 100, 164, 68, 116, 24, 48, 11,
1471        64, 243, 8, 190, 128, 155, 254, 245, 21, 205, 241, 217, 246, 204, 182, 247, 41, 81, 91, 33,
1472        155, 230, 223, 212, 116, 33, 162, 208, 148, 100, 89, 175, 253, 78, 212, 7, 69, 207, 140,
1473        45, 129, 249, 64, 104, 70, 68, 43, 164, 19, 126, 114, 138, 79, 104, 238, 20, 226, 88, 118,
1474        105, 56, 12, 92, 31, 171, 24,
1475    ];
1476
1477    // This cert has two of the fields in the extensions list swapped to a different order to be non-consecutive
1478    const ICAC2_SUCCESS: &[u8] = &[
1479        21, 48, 1, 16, 67, 38, 73, 198, 26, 31, 20, 101, 57, 46, 16, 143, 77, 160, 128, 161, 36, 2,
1480        1, 55, 3, 39, 20, 255, 90, 200, 17, 145, 105, 71, 215, 24, 38, 4, 123, 59, 211, 42, 38, 5,
1481        35, 11, 27, 52, 55, 6, 39, 19, 254, 111, 27, 53, 189, 134, 103, 200, 24, 36, 7, 1, 36, 8,
1482        1, 48, 9, 65, 4, 88, 188, 13, 87, 50, 3, 213, 248, 182, 12, 240, 164, 220, 127, 150, 65,
1483        81, 244, 125, 24, 48, 203, 83, 111, 133, 175, 182, 10, 40, 80, 147, 28, 39, 121, 183, 61,
1484        159, 178, 231, 133, 75, 189, 143, 136, 191, 254, 115, 228, 186, 129, 56, 137, 213, 177, 13,
1485        46, 97, 202, 95, 41, 5, 16, 24, 228, 55, 10, 53, 1, 41, 1, 36, 2, 0, 24, 48, 5, 20, 243,
1486        119, 107, 152, 3, 212, 205, 76, 85, 38, 158, 240, 27, 213, 11, 235, 33, 21, 38, 5, 48, 4,
1487        20, 88, 240, 172, 159, 2, 82, 193, 71, 83, 67, 184, 97, 99, 61, 125, 67, 232, 202, 171,
1488        107, 36, 2, 96, 24, 48, 11, 64, 70, 43, 150, 195, 194, 170, 43, 125, 91, 213, 210, 221,
1489        175, 131, 131, 85, 22, 247, 213, 18, 101, 189, 30, 134, 20, 226, 217, 145, 41, 225, 181,
1490        150, 28, 200, 52, 237, 218, 195, 144, 209, 205, 73, 88, 114, 139, 216, 85, 170, 63, 238,
1491        164, 69, 35, 69, 39, 87, 211, 234, 57, 98, 19, 43, 13, 0, 24,
1492    ];
1493
1494    // A single byte in the auth key id is changed in this
1495    const NOC1_AUTH_KEY_FAIL: &[u8] = &[
1496        0x15, 0x30, 0x1, 0x1, 0x1, 0x24, 0x2, 0x1, 0x37, 0x3, 0x24, 0x13, 0x1, 0x24, 0x15, 0x1,
1497        0x18, 0x26, 0x4, 0x80, 0x22, 0x81, 0x27, 0x26, 0x5, 0x80, 0x25, 0x4d, 0x3a, 0x37, 0x6,
1498        0x26, 0x11, 0x2, 0x5c, 0xbc, 0x0, 0x24, 0x15, 0x1, 0x18, 0x24, 0x7, 0x1, 0x24, 0x8, 0x1,
1499        0x30, 0x9, 0x41, 0x4, 0xba, 0x22, 0x56, 0x43, 0x4f, 0x59, 0x98, 0x32, 0x8d, 0xb8, 0xcb,
1500        0x3f, 0x24, 0x90, 0x9a, 0x96, 0x94, 0x43, 0x46, 0x67, 0xc2, 0x11, 0xe3, 0x80, 0x26, 0x65,
1501        0xfc, 0x65, 0x37, 0x77, 0x3, 0x25, 0x18, 0xd8, 0xdc, 0x85, 0xfa, 0xe6, 0x42, 0xe7, 0x55,
1502        0xc9, 0x37, 0xcc, 0xb, 0x78, 0x84, 0x3d, 0x2f, 0xac, 0x81, 0x88, 0x2e, 0x69, 0x0, 0xa5,
1503        0xfc, 0xcd, 0xe0, 0xad, 0xb2, 0x69, 0xca, 0x73, 0x37, 0xa, 0x35, 0x1, 0x28, 0x1, 0x18,
1504        0x24, 0x2, 0x1, 0x36, 0x3, 0x4, 0x2, 0x4, 0x1, 0x18, 0x30, 0x4, 0x14, 0x39, 0x68, 0x16,
1505        0x1e, 0xb5, 0x56, 0x6d, 0xd3, 0xf8, 0x61, 0xf2, 0x95, 0xf3, 0x55, 0xa0, 0xfb, 0xd2, 0x82,
1506        0xc2, 0x29, 0x30, 0x5, 0x14, 0xce, 0x61, 0xb4, 0x28, 0x96, 0x72, 0x27, 0x64, 0x81, 0xbc,
1507        0x4f, 0x0, 0x78, 0xa3, 0x30, 0x48, 0xfe, 0x6e, 0x65, 0x86, 0x18, 0x30, 0xb, 0x40, 0x2,
1508        0x88, 0x42, 0x0, 0x6f, 0xcc, 0xe0, 0xf0, 0x6c, 0xd9, 0xf9, 0x5e, 0xe4, 0xc2, 0xaa, 0x1f,
1509        0x57, 0x71, 0x62, 0xdb, 0x6b, 0x4e, 0xe7, 0x55, 0x3f, 0xc6, 0xc7, 0x9f, 0xf8, 0x30, 0xeb,
1510        0x16, 0x6e, 0x6d, 0xc6, 0x9c, 0xb, 0xb7, 0xe2, 0xb8, 0xe3, 0xe7, 0x57, 0x88, 0x7b, 0xda,
1511        0xe5, 0x79, 0x39, 0x6d, 0x2c, 0x37, 0xb2, 0x7f, 0xc3, 0x63, 0x2f, 0x7e, 0x70, 0xab, 0x5a,
1512        0x2c, 0xf7, 0x5b, 0x18,
1513    ];
1514
1515    // A single byte in the Certificate contents is changed in this
1516    const NOC1_CORRUPT_CERT: &[u8] = &[
1517        0x15, 0x30, 0x1, 0x1, 0x1, 0x24, 0x2, 0x1, 0x37, 0x3, 0x24, 0x13, 0x1, 0x24, 0x15, 0x1,
1518        0x18, 0x26, 0x4, 0x80, 0x22, 0x81, 0x27, 0x26, 0x5, 0x80, 0x25, 0x4d, 0x3a, 0x37, 0x6,
1519        0x26, 0x11, 0x2, 0x5c, 0xbc, 0x0, 0x24, 0x15, 0x1, 0x18, 0x24, 0x7, 0x1, 0x24, 0x8, 0x1,
1520        0x30, 0x9, 0x41, 0x4, 0xba, 0x23, 0x56, 0x43, 0x4f, 0x59, 0x98, 0x32, 0x8d, 0xb8, 0xcb,
1521        0x3f, 0x24, 0x90, 0x9a, 0x96, 0x94, 0x43, 0x46, 0x67, 0xc2, 0x11, 0xe3, 0x80, 0x26, 0x65,
1522        0xfc, 0x65, 0x37, 0x77, 0x3, 0x25, 0x18, 0xd8, 0xdc, 0x85, 0xfa, 0xe6, 0x42, 0xe7, 0x55,
1523        0xc9, 0x37, 0xcc, 0xb, 0x78, 0x84, 0x3d, 0x2f, 0xac, 0x81, 0x88, 0x2e, 0x69, 0x0, 0xa5,
1524        0xfc, 0xcd, 0xe0, 0xad, 0xb2, 0x69, 0xca, 0x73, 0x37, 0xa, 0x35, 0x1, 0x28, 0x1, 0x18,
1525        0x24, 0x2, 0x1, 0x36, 0x3, 0x4, 0x2, 0x4, 0x1, 0x18, 0x30, 0x4, 0x14, 0x39, 0x68, 0x16,
1526        0x1e, 0xb5, 0x56, 0x6d, 0xd3, 0xf8, 0x61, 0xf2, 0x95, 0xf3, 0x55, 0xa0, 0xfb, 0xd2, 0x82,
1527        0xc2, 0x29, 0x30, 0x5, 0x14, 0xce, 0x60, 0xb4, 0x28, 0x96, 0x72, 0x27, 0x64, 0x81, 0xbc,
1528        0x4f, 0x0, 0x78, 0xa3, 0x30, 0x48, 0xfe, 0x6e, 0x65, 0x86, 0x18, 0x30, 0xb, 0x40, 0x2,
1529        0x88, 0x42, 0x0, 0x6f, 0xcc, 0xe0, 0xf0, 0x6c, 0xd9, 0xf9, 0x5e, 0xe4, 0xc2, 0xaa, 0x1f,
1530        0x57, 0x71, 0x62, 0xdb, 0x6b, 0x4e, 0xe7, 0x55, 0x3f, 0xc6, 0xc7, 0x9f, 0xf8, 0x30, 0xeb,
1531        0x16, 0x6e, 0x6d, 0xc6, 0x9c, 0xb, 0xb7, 0xe2, 0xb8, 0xe3, 0xe7, 0x57, 0x88, 0x7b, 0xda,
1532        0xe5, 0x79, 0x39, 0x6d, 0x2c, 0x37, 0xb2, 0x7f, 0xc3, 0x63, 0x2f, 0x7e, 0x70, 0xab, 0x5a,
1533        0x2c, 0xf7, 0x5b, 0x18,
1534    ];
1535
1536    const RCA1_SUCCESS: &[u8] = &[
1537        0x15, 0x30, 0x1, 0x1, 0x0, 0x24, 0x2, 0x1, 0x37, 0x3, 0x24, 0x14, 0x0, 0x24, 0x15, 0x1,
1538        0x18, 0x26, 0x4, 0x80, 0x22, 0x81, 0x27, 0x26, 0x5, 0x80, 0x25, 0x4d, 0x3a, 0x37, 0x6,
1539        0x24, 0x14, 0x0, 0x24, 0x15, 0x1, 0x18, 0x24, 0x7, 0x1, 0x24, 0x8, 0x1, 0x30, 0x9, 0x41,
1540        0x4, 0x6d, 0x70, 0x7e, 0x4b, 0x98, 0xf6, 0x2b, 0xab, 0x44, 0xd6, 0xfe, 0xa3, 0x2e, 0x39,
1541        0xd8, 0xc3, 0x0, 0xa0, 0xe, 0xa8, 0x6c, 0x83, 0xff, 0x69, 0xd, 0xe8, 0x42, 0x1, 0xeb, 0xd,
1542        0xaa, 0x68, 0x5d, 0xcb, 0x97, 0x2, 0x80, 0x1d, 0xa8, 0x50, 0x2, 0x2e, 0x5a, 0xa2, 0x5a,
1543        0x2e, 0x51, 0x26, 0x4, 0xd2, 0x39, 0x62, 0xcd, 0x82, 0x38, 0x63, 0x28, 0xbf, 0x15, 0x1c,
1544        0xa6, 0x27, 0xe0, 0xd7, 0x37, 0xa, 0x35, 0x1, 0x29, 0x1, 0x18, 0x24, 0x2, 0x60, 0x30, 0x4,
1545        0x14, 0xd4, 0x56, 0x93, 0xbe, 0x70, 0x79, 0xf4, 0x9c, 0x70, 0x6b, 0x7, 0x6f, 0x11, 0x1c,
1546        0x6d, 0xe5, 0x64, 0xa4, 0x44, 0x74, 0x30, 0x5, 0x14, 0xd4, 0x56, 0x93, 0xbe, 0x70, 0x79,
1547        0xf4, 0x9c, 0x70, 0x6b, 0x7, 0x6f, 0x11, 0x1c, 0x6d, 0xe5, 0x64, 0xa4, 0x44, 0x74, 0x18,
1548        0x30, 0xb, 0x40, 0x3, 0xd, 0x77, 0xe1, 0x9e, 0xea, 0x9c, 0x5, 0x5c, 0xcc, 0x47, 0xe8, 0xb3,
1549        0x18, 0x1a, 0xd1, 0x74, 0xee, 0xc6, 0x2e, 0xa1, 0x20, 0x16, 0xbd, 0x20, 0xb4, 0x3d, 0xac,
1550        0x24, 0xbe, 0x17, 0xf9, 0xe, 0xb7, 0x9a, 0x98, 0xc8, 0xbc, 0x6a, 0xce, 0x99, 0x2a, 0x2e,
1551        0x63, 0x4c, 0x76, 0x6, 0x45, 0x93, 0xd3, 0x7c, 0x4, 0x0, 0xe4, 0xc7, 0x78, 0xe9, 0x83,
1552        0x5b, 0xc, 0x33, 0x61, 0x5c, 0x2e, 0x18,
1553    ];
1554
1555    const CHIP_CERT_INPUT1: &[u8] = &[
1556        0x15, 0x30, 0x01, 0x01, 0x00, 0x24, 0x02, 0x01, 0x37, 0x03, 0x24, 0x14, 0x00, 0x24, 0x15,
1557        0x03, 0x18, 0x26, 0x04, 0x80, 0x22, 0x81, 0x27, 0x26, 0x05, 0x80, 0x25, 0x4d, 0x3a, 0x37,
1558        0x06, 0x24, 0x13, 0x01, 0x24, 0x15, 0x03, 0x18, 0x24, 0x07, 0x01, 0x24, 0x08, 0x01, 0x30,
1559        0x09, 0x41, 0x04, 0x69, 0xda, 0xe9, 0x42, 0x88, 0xcf, 0x64, 0x94, 0x2d, 0xd5, 0x0a, 0x74,
1560        0x2d, 0x50, 0xe8, 0x5e, 0xbe, 0x15, 0x53, 0x24, 0xe5, 0xc5, 0x6b, 0xe5, 0x7f, 0xc1, 0x41,
1561        0x11, 0x21, 0xdd, 0x46, 0xa3, 0x0d, 0x63, 0xc3, 0xe3, 0x90, 0x7a, 0x69, 0x64, 0xdd, 0x66,
1562        0x78, 0x10, 0xa6, 0xc8, 0x0f, 0xfd, 0xb6, 0xf2, 0x9b, 0x88, 0x50, 0x93, 0x77, 0x9e, 0xf7,
1563        0xb4, 0xda, 0x94, 0x11, 0x33, 0x1e, 0xfe, 0x37, 0x0a, 0x35, 0x01, 0x29, 0x01, 0x18, 0x24,
1564        0x02, 0x60, 0x30, 0x04, 0x14, 0xdf, 0xfb, 0x79, 0xf1, 0x2b, 0xbf, 0x68, 0x18, 0x59, 0x7f,
1565        0xf7, 0xe8, 0xaf, 0x88, 0x91, 0x1c, 0x72, 0x32, 0xf7, 0x52, 0x30, 0x05, 0x14, 0xed, 0x31,
1566        0x5e, 0x1a, 0xb7, 0xb9, 0x7a, 0xca, 0x04, 0x79, 0x5d, 0x82, 0x57, 0x7a, 0xd7, 0x0a, 0x75,
1567        0xd0, 0xdb, 0x7a, 0x18, 0x30, 0x0b, 0x40, 0xe5, 0xd4, 0xe6, 0x0e, 0x98, 0x62, 0x2f, 0xaa,
1568        0x59, 0xe0, 0x28, 0x59, 0xc2, 0xd4, 0xcd, 0x34, 0x85, 0x7f, 0x93, 0xbe, 0x14, 0x35, 0xa3,
1569        0x76, 0x8a, 0xc9, 0x2f, 0x59, 0x39, 0xa0, 0xb0, 0x75, 0xe8, 0x8e, 0x11, 0xa9, 0xc1, 0x9e,
1570        0xaa, 0xab, 0xa0, 0xdb, 0xb4, 0x79, 0x63, 0xfc, 0x02, 0x03, 0x27, 0x25, 0xac, 0x21, 0x6f,
1571        0xef, 0x27, 0xab, 0x0f, 0x90, 0x09, 0x99, 0x05, 0xa8, 0x60, 0xd8, 0x18,
1572    ];
1573
1574    const CHIP_CERT_INPUT2: &[u8] = &[
1575        0x15, 0x30, 0x01, 0x01, 0x01, 0x24, 0x02, 0x01, 0x37, 0x03, 0x24, 0x13, 0x01, 0x24, 0x15,
1576        0x03, 0x18, 0x26, 0x04, 0x80, 0x22, 0x81, 0x27, 0x26, 0x05, 0x80, 0x25, 0x4d, 0x3a, 0x37,
1577        0x06, 0x26, 0x11, 0x69, 0xb6, 0x01, 0x00, 0x24, 0x15, 0x03, 0x18, 0x24, 0x07, 0x01, 0x24,
1578        0x08, 0x01, 0x30, 0x09, 0x41, 0x04, 0x93, 0x04, 0xc6, 0xc4, 0xe1, 0xbc, 0x9a, 0xc8, 0xf5,
1579        0xb3, 0x7f, 0x83, 0xd6, 0x7f, 0x79, 0xc5, 0x35, 0xdc, 0x7f, 0xac, 0x87, 0xca, 0xcd, 0x08,
1580        0x80, 0x4a, 0x55, 0x60, 0x80, 0x09, 0xd3, 0x9b, 0x4a, 0xc8, 0xe7, 0x7b, 0x4d, 0x5c, 0x82,
1581        0x88, 0x24, 0xdf, 0x1c, 0xfd, 0xef, 0xb4, 0xbc, 0xb7, 0x2f, 0x36, 0xf7, 0x2b, 0xb2, 0xcc,
1582        0x14, 0x69, 0x63, 0xcc, 0x89, 0xd2, 0x74, 0x3f, 0xd1, 0x98, 0x37, 0x0a, 0x35, 0x01, 0x28,
1583        0x01, 0x18, 0x24, 0x02, 0x01, 0x36, 0x03, 0x04, 0x02, 0x04, 0x01, 0x18, 0x30, 0x04, 0x14,
1584        0x9c, 0xe7, 0xd9, 0xa8, 0x6b, 0xf8, 0x71, 0xfa, 0x08, 0x10, 0xa3, 0xf2, 0x3a, 0x95, 0x30,
1585        0xb1, 0x9e, 0xae, 0xc4, 0x2c, 0x30, 0x05, 0x14, 0xdf, 0xfb, 0x79, 0xf1, 0x2b, 0xbf, 0x68,
1586        0x18, 0x59, 0x7f, 0xf7, 0xe8, 0xaf, 0x88, 0x91, 0x1c, 0x72, 0x32, 0xf7, 0x52, 0x18, 0x30,
1587        0x0b, 0x40, 0xcf, 0x01, 0x37, 0x65, 0xd6, 0x8a, 0xca, 0xd8, 0x33, 0x9f, 0x0f, 0x4f, 0xd5,
1588        0xed, 0x48, 0x42, 0x91, 0xca, 0xab, 0xf7, 0xae, 0xe1, 0x3b, 0x2b, 0xef, 0x9f, 0x43, 0x5a,
1589        0x96, 0xe0, 0xa5, 0x38, 0x8e, 0x39, 0xd0, 0x20, 0x8a, 0x0c, 0x92, 0x2b, 0x21, 0x7d, 0xf5,
1590        0x6c, 0x1d, 0x65, 0x6c, 0x0f, 0xd1, 0xe8, 0x55, 0x14, 0x5e, 0x27, 0xfd, 0xa4, 0xac, 0xf9,
1591        0x93, 0xdb, 0x29, 0x49, 0xaa, 0x71, 0x18,
1592    ];
1593
1594    const CHIP_CERT_TXT_IN_DN: &[u8] = &[
1595        0x15, 0x30, 0x1, 0x1, 0x1, 0x24, 0x2, 0x1, 0x37, 0x3, 0x2c, 0x84, 0x2, 0x55, 0x53, 0x2c,
1596        0x7, 0x6, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2c, 0x1, 0xb, 0x4d, 0x61, 0x74, 0x74, 0x65,
1597        0x72, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x27, 0x14, 0x1, 0x0, 0x0, 0x0, 0xfe, 0xff, 0xff, 0xff,
1598        0x18, 0x26, 0x4, 0x7f, 0xd2, 0x43, 0x29, 0x26, 0x5, 0x7f, 0x94, 0x5b, 0xe5, 0x37, 0x6,
1599        0x2c, 0x84, 0x2, 0x55, 0x53, 0x2c, 0x7, 0x6, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2c, 0x1,
1600        0xb, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x27, 0x14, 0x1,
1601        0x0, 0x0, 0x0, 0xfe, 0xff, 0xff, 0xff, 0x18, 0x24, 0x7, 0x1, 0x24, 0x8, 0x1, 0x30, 0x9,
1602        0x41, 0x4, 0x5b, 0x37, 0xdf, 0x65, 0x49, 0xc2, 0xd, 0xc8, 0xd7, 0x22, 0xa6, 0xb8, 0xac,
1603        0xb6, 0x60, 0xa8, 0xa7, 0x64, 0xce, 0x7b, 0xaf, 0x6c, 0x6c, 0x22, 0x4f, 0x7e, 0xe8, 0x43,
1604        0x49, 0x68, 0x4a, 0xd7, 0xd8, 0x9, 0xff, 0x65, 0x0, 0x33, 0xd1, 0x52, 0x7d, 0xcf, 0x1f,
1605        0xba, 0xac, 0x6a, 0x9c, 0x3a, 0xd8, 0xb4, 0x1e, 0xda, 0xc9, 0x9, 0xf7, 0xb5, 0xc7, 0x60,
1606        0xfd, 0x54, 0x2c, 0x89, 0x23, 0x75, 0x37, 0xa, 0x35, 0x1, 0x29, 0x1, 0x24, 0x2, 0x1, 0x18,
1607        0x24, 0x2, 0x60, 0x30, 0x4, 0x14, 0x72, 0xc2, 0x1, 0xf7, 0x57, 0x19, 0x13, 0xb3, 0x48,
1608        0xca, 0x0, 0xca, 0x7b, 0x45, 0xf4, 0x77, 0x46, 0x68, 0xc9, 0x7e, 0x30, 0x5, 0x14, 0x72,
1609        0xc2, 0x1, 0xf7, 0x57, 0x19, 0x13, 0xb3, 0x48, 0xca, 0x0, 0xca, 0x7b, 0x45, 0xf4, 0x77,
1610        0x46, 0x68, 0xc9, 0x7e, 0x18, 0x30, 0xb, 0x40, 0x65, 0x16, 0x4b, 0x16, 0x6a, 0xdf, 0xf1,
1611        0x8c, 0x15, 0x61, 0xa, 0x8c, 0xe9, 0x1b, 0xd7, 0x3, 0xe9, 0xc1, 0xf6, 0x77, 0xb7, 0x11,
1612        0xce, 0x13, 0x35, 0x5, 0x15, 0x2d, 0xf0, 0xda, 0x15, 0x11, 0x16, 0x75, 0xac, 0x55, 0x91,
1613        0xce, 0xe7, 0x86, 0x85, 0x1c, 0xdd, 0x9e, 0xfd, 0xad, 0x29, 0x66, 0x74, 0xbe, 0xbc, 0xb2,
1614        0xa3, 0xa3, 0x20, 0x9b, 0xcd, 0xe7, 0xb3, 0x9, 0xdb, 0x55, 0x2c, 0x6f, 0x18,
1615    ];
1616
1617    const ASN1_OUTPUT1: &[u8] = &[
1618        0x30, 0x82, 0x01, 0x80, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x00, 0x30, 0x0a, 0x06,
1619        0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x44, 0x31, 0x20, 0x30, 0x1e,
1620        0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x01, 0x04, 0x0c, 0x10, 0x30,
1621        0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1622        0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x01,
1623        0x05, 0x0c, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1624        0x30, 0x30, 0x30, 0x33, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30,
1625        0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x30, 0x31, 0x32, 0x33, 0x30, 0x30,
1626        0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x44, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a, 0x2b,
1627        0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x01, 0x03, 0x0c, 0x10, 0x30, 0x30, 0x30, 0x30,
1628        0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x20, 0x30,
1629        0x1e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x01, 0x05, 0x0c, 0x10,
1630        0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1631        0x33, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
1632        0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x69, 0xda,
1633        0xe9, 0x42, 0x88, 0xcf, 0x64, 0x94, 0x2d, 0xd5, 0x0a, 0x74, 0x2d, 0x50, 0xe8, 0x5e, 0xbe,
1634        0x15, 0x53, 0x24, 0xe5, 0xc5, 0x6b, 0xe5, 0x7f, 0xc1, 0x41, 0x11, 0x21, 0xdd, 0x46, 0xa3,
1635        0x0d, 0x63, 0xc3, 0xe3, 0x90, 0x7a, 0x69, 0x64, 0xdd, 0x66, 0x78, 0x10, 0xa6, 0xc8, 0x0f,
1636        0xfd, 0xb6, 0xf2, 0x9b, 0x88, 0x50, 0x93, 0x77, 0x9e, 0xf7, 0xb4, 0xda, 0x94, 0x11, 0x33,
1637        0x1e, 0xfe, 0xa3, 0x63, 0x30, 0x61, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
1638        0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
1639        0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
1640        0x0e, 0x04, 0x16, 0x04, 0x14, 0xdf, 0xfb, 0x79, 0xf1, 0x2b, 0xbf, 0x68, 0x18, 0x59, 0x7f,
1641        0xf7, 0xe8, 0xaf, 0x88, 0x91, 0x1c, 0x72, 0x32, 0xf7, 0x52, 0x30, 0x1f, 0x06, 0x03, 0x55,
1642        0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xed, 0x31, 0x5e, 0x1a, 0xb7, 0xb9, 0x7a,
1643        0xca, 0x04, 0x79, 0x5d, 0x82, 0x57, 0x7a, 0xd7, 0x0a, 0x75, 0xd0, 0xdb, 0x7a,
1644    ];
1645
1646    const ASN1_OUTPUT2: &[u8] = &[
1647        0x30, 0x82, 0x01, 0xa1, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x30, 0x0a, 0x06,
1648        0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x44, 0x31, 0x20, 0x30, 0x1e,
1649        0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x01, 0x03, 0x0c, 0x10, 0x30,
1650        0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31,
1651        0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x01,
1652        0x05, 0x0c, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1653        0x30, 0x30, 0x30, 0x33, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30,
1654        0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x30, 0x31, 0x32, 0x33, 0x30, 0x30,
1655        0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x44, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a, 0x2b,
1656        0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x01, 0x01, 0x0c, 0x10, 0x30, 0x30, 0x30, 0x30,
1657        0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x42, 0x36, 0x36, 0x39, 0x31, 0x20, 0x30,
1658        0x1e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x01, 0x05, 0x0c, 0x10,
1659        0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1660        0x33, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
1661        0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x93, 0x04,
1662        0xc6, 0xc4, 0xe1, 0xbc, 0x9a, 0xc8, 0xf5, 0xb3, 0x7f, 0x83, 0xd6, 0x7f, 0x79, 0xc5, 0x35,
1663        0xdc, 0x7f, 0xac, 0x87, 0xca, 0xcd, 0x08, 0x80, 0x4a, 0x55, 0x60, 0x80, 0x09, 0xd3, 0x9b,
1664        0x4a, 0xc8, 0xe7, 0x7b, 0x4d, 0x5c, 0x82, 0x88, 0x24, 0xdf, 0x1c, 0xfd, 0xef, 0xb4, 0xbc,
1665        0xb7, 0x2f, 0x36, 0xf7, 0x2b, 0xb2, 0xcc, 0x14, 0x69, 0x63, 0xcc, 0x89, 0xd2, 0x74, 0x3f,
1666        0xd1, 0x98, 0xa3, 0x81, 0x83, 0x30, 0x81, 0x80, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
1667        0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
1668        0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x20, 0x06, 0x03, 0x55, 0x1d, 0x25,
1669        0x01, 0x01, 0xff, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
1670        0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x1d, 0x06,
1671        0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x9c, 0xe7, 0xd9, 0xa8, 0x6b, 0xf8, 0x71,
1672        0xfa, 0x08, 0x10, 0xa3, 0xf2, 0x3a, 0x95, 0x30, 0xb1, 0x9e, 0xae, 0xc4, 0x2c, 0x30, 0x1f,
1673        0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xdf, 0xfb, 0x79, 0xf1,
1674        0x2b, 0xbf, 0x68, 0x18, 0x59, 0x7f, 0xf7, 0xe8, 0xaf, 0x88, 0x91, 0x1c, 0x72, 0x32, 0xf7,
1675        0x52,
1676    ];
1677
1678    const ASN1_OUTPUT_TXT_IN_DN: &[u8] = &[
1679        0x30, 0x82, 0x01, 0xa9, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x30, 0x0a, 0x06,
1680        0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x56, 0x31, 0x0b, 0x30, 0x09,
1681        0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03,
1682        0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x14, 0x30, 0x12,
1683        0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0b, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x52,
1684        0x6f, 0x6f, 0x74, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82,
1685        0xa2, 0x7c, 0x01, 0x04, 0x0c, 0x10, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x45, 0x30,
1686        0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x32,
1687        0x30, 0x38, 0x32, 0x30, 0x33, 0x30, 0x35, 0x35, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x32, 0x31,
1688        0x31, 0x32, 0x30, 0x38, 0x32, 0x30, 0x33, 0x30, 0x35, 0x35, 0x5a, 0x30, 0x56, 0x31, 0x0b,
1689        0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0f, 0x30, 0x0d,
1690        0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x14,
1691        0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0b, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72,
1692        0x20, 0x52, 0x6f, 0x6f, 0x74, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
1693        0x01, 0x82, 0xa2, 0x7c, 0x01, 0x04, 0x0c, 0x10, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46,
1694        0x45, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
1695        0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03,
1696        0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x5b, 0x37, 0xdf, 0x65, 0x49, 0xc2, 0x0d, 0xc8, 0xd7,
1697        0x22, 0xa6, 0xb8, 0xac, 0xb6, 0x60, 0xa8, 0xa7, 0x64, 0xce, 0x7b, 0xaf, 0x6c, 0x6c, 0x22,
1698        0x4f, 0x7e, 0xe8, 0x43, 0x49, 0x68, 0x4a, 0xd7, 0xd8, 0x09, 0xff, 0x65, 0x00, 0x33, 0xd1,
1699        0x52, 0x7d, 0xcf, 0x1f, 0xba, 0xac, 0x6a, 0x9c, 0x3a, 0xd8, 0xb4, 0x1e, 0xda, 0xc9, 0x09,
1700        0xf7, 0xb5, 0xc7, 0x60, 0xfd, 0x54, 0x2c, 0x89, 0x23, 0x75, 0xa3, 0x66, 0x30, 0x64, 0x30,
1701        0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
1702        0xff, 0x02, 0x01, 0x01, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
1703        0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
1704        0x14, 0x72, 0xc2, 0x01, 0xf7, 0x57, 0x19, 0x13, 0xb3, 0x48, 0xca, 0x00, 0xca, 0x7b, 0x45,
1705        0xf4, 0x77, 0x46, 0x68, 0xc9, 0x7e, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
1706        0x30, 0x16, 0x80, 0x14, 0x72, 0xc2, 0x01, 0xf7, 0x57, 0x19, 0x13, 0xb3, 0x48, 0xca, 0x00,
1707        0xca, 0x7b, 0x45, 0xf4, 0x77, 0x46, 0x68, 0xc9, 0x7e,
1708    ];
1709
1710    /// An NOC that contains a Not-After validity field of '0'
1711    const NOC_NOT_AFTER_ZERO: &[u8] = &[
1712        0x15, 0x30, 0x1, 0x1, 0x1, 0x24, 0x2, 0x1, 0x37, 0x3, 0x27, 0x14, 0xfc, 0x8d, 0xcf, 0x45,
1713        0x19, 0xff, 0x9a, 0x9a, 0x24, 0x15, 0x1, 0x18, 0x26, 0x4, 0x21, 0x39, 0x5a, 0x2c, 0x24,
1714        0x5, 0x0, 0x37, 0x6, 0x24, 0x15, 0x1, 0x26, 0x11, 0x6c, 0x4a, 0x95, 0xd2, 0x18, 0x24, 0x7,
1715        0x1, 0x24, 0x8, 0x1, 0x30, 0x9, 0x41, 0x4, 0x41, 0x7f, 0xb1, 0x61, 0xb0, 0xbe, 0x19, 0x41,
1716        0x81, 0xb9, 0x9f, 0xe8, 0x7b, 0xdd, 0xdf, 0xc4, 0x46, 0xe0, 0x74, 0xba, 0x83, 0x21, 0xda,
1717        0x3d, 0xf7, 0x88, 0x68, 0x14, 0xa6, 0x9d, 0xa9, 0x14, 0x88, 0x94, 0x1e, 0xd3, 0x86, 0x62,
1718        0xc7, 0x6f, 0xb4, 0x79, 0xd2, 0xaf, 0x34, 0xe7, 0xd6, 0x4d, 0x87, 0x29, 0x67, 0x10, 0x73,
1719        0xb9, 0x81, 0xe0, 0x9, 0xe1, 0x13, 0xbb, 0x6a, 0xd2, 0x21, 0xaa, 0x37, 0xa, 0x35, 0x1,
1720        0x28, 0x1, 0x18, 0x24, 0x2, 0x1, 0x36, 0x3, 0x4, 0x2, 0x4, 0x1, 0x18, 0x30, 0x4, 0x14,
1721        0x98, 0xaf, 0xa1, 0x3d, 0x41, 0x67, 0x7a, 0x34, 0x8c, 0x67, 0x6c, 0xcc, 0x17, 0x6e, 0xd5,
1722        0x58, 0xd8, 0x2b, 0x86, 0x8, 0x30, 0x5, 0x14, 0xf8, 0xcf, 0xd0, 0x45, 0x6b, 0xe, 0xd1,
1723        0x6f, 0xc5, 0x67, 0xdf, 0x81, 0xd7, 0xe9, 0xb7, 0xeb, 0x39, 0x78, 0xec, 0x40, 0x18, 0x30,
1724        0xb, 0x40, 0xf9, 0x80, 0x94, 0xbf, 0xcf, 0x72, 0xa5, 0x54, 0x87, 0x12, 0x35, 0xc, 0x38,
1725        0x79, 0xa8, 0xb, 0x21, 0x94, 0xb5, 0x71, 0x2, 0xcb, 0xb, 0xda, 0xf9, 0x6c, 0x54, 0xcb,
1726        0x50, 0x4b, 0x2, 0x5, 0xea, 0xff, 0xfd, 0xb2, 0x1b, 0x24, 0x30, 0x79, 0xb1, 0x69, 0x87,
1727        0xa5, 0x7, 0xc6, 0x76, 0x15, 0x70, 0xc0, 0xec, 0x14, 0xd3, 0x9f, 0x1a, 0xa7, 0xe1, 0xca,
1728        0x25, 0x2e, 0x44, 0xfc, 0x96, 0x4d, 0x18,
1729    ];
1730
1731    const RCA_FOR_NOC_NOT_AFTER_ZERO: &[u8] = &[
1732        0x15, 0x30, 0x1, 0x1, 0x0, 0x24, 0x2, 0x1, 0x37, 0x3, 0x27, 0x14, 0xfc, 0x8d, 0xcf, 0x45,
1733        0x19, 0xff, 0x9a, 0x9a, 0x24, 0x15, 0x1, 0x18, 0x26, 0x4, 0xb1, 0x2a, 0x38, 0x2c, 0x26,
1734        0x5, 0x31, 0x5e, 0x19, 0x2e, 0x37, 0x6, 0x27, 0x14, 0xfc, 0x8d, 0xcf, 0x45, 0x19, 0xff,
1735        0x9a, 0x9a, 0x24, 0x15, 0x1, 0x18, 0x24, 0x7, 0x1, 0x24, 0x8, 0x1, 0x30, 0x9, 0x41, 0x4,
1736        0x15, 0x69, 0x1e, 0x7b, 0x6a, 0xea, 0x5, 0xdb, 0xf8, 0x4b, 0xfd, 0xdc, 0x6c, 0x75, 0x46,
1737        0x74, 0xb0, 0x60, 0xdb, 0x4, 0x71, 0xb6, 0xd0, 0x52, 0xf2, 0xf8, 0xe6, 0xbb, 0xd, 0xe5,
1738        0x60, 0x1f, 0x84, 0x66, 0x4f, 0x3c, 0x90, 0x89, 0xa6, 0xc6, 0x99, 0x61, 0xfb, 0x89, 0xf7,
1739        0xa, 0xa6, 0xe4, 0xa2, 0x21, 0xd3, 0x37, 0x30, 0x1b, 0xd2, 0x11, 0xc5, 0xcc, 0x0, 0xf4,
1740        0x7a, 0x14, 0xfc, 0x3c, 0x37, 0xa, 0x35, 0x1, 0x29, 0x1, 0x18, 0x24, 0x2, 0x60, 0x30, 0x4,
1741        0x14, 0xf8, 0xcf, 0xd0, 0x45, 0x6b, 0xe, 0xd1, 0x6f, 0xc5, 0x67, 0xdf, 0x81, 0xd7, 0xe9,
1742        0xb7, 0xeb, 0x39, 0x78, 0xec, 0x40, 0x30, 0x5, 0x14, 0xf8, 0xcf, 0xd0, 0x45, 0x6b, 0xe,
1743        0xd1, 0x6f, 0xc5, 0x67, 0xdf, 0x81, 0xd7, 0xe9, 0xb7, 0xeb, 0x39, 0x78, 0xec, 0x40, 0x18,
1744        0x30, 0xb, 0x40, 0x4c, 0xae, 0xac, 0xc1, 0x26, 0xdd, 0x56, 0xc, 0x85, 0x86, 0xbc, 0xeb,
1745        0xa2, 0xb5, 0xb7, 0xdf, 0x49, 0x92, 0x62, 0xcd, 0x2a, 0xb6, 0x4e, 0xc5, 0x31, 0x7c, 0xd9,
1746        0xb, 0x1c, 0xe9, 0x6e, 0xe5, 0x82, 0xc7, 0xb8, 0xda, 0x22, 0x31, 0x7b, 0x23, 0x5a, 0x2a,
1747        0xe6, 0x76, 0x28, 0xb6, 0xd4, 0xc7, 0x7b, 0x1c, 0x9c, 0x85, 0x71, 0x5f, 0xe6, 0xf6, 0x21,
1748        0x50, 0x5c, 0xa7, 0x7c, 0xc7, 0x1d, 0x9a, 0x18,
1749    ];
1750
1751    const UNORDERED_EXTENSIONS_CHIP: &[u8] = &[
1752        0x15, 0x30, 0x01, 0x10, 0x44, 0x9d, 0xeb, 0xca, 0x2e, 0x2e, 0x98, 0x42, 0xe0, 0x87, 0x6f,
1753        0x8b, 0xfa, 0x23, 0xe4, 0x54, 0x24, 0x02, 0x01, 0x37, 0x03, 0x27, 0x14, 0xf6, 0x56, 0xb7,
1754        0x85, 0xf4, 0xbf, 0x30, 0x00, 0x18, 0x26, 0x04, 0xe2, 0xdc, 0xbc, 0x2a, 0x26, 0x05, 0x72,
1755        0xdb, 0xc2, 0x59, 0x37, 0x06, 0x27, 0x14, 0xf6, 0x56, 0xb7, 0x85, 0xf4, 0xbf, 0x30, 0x00,
1756        0x18, 0x24, 0x07, 0x01, 0x24, 0x08, 0x01, 0x30, 0x09, 0x41, 0x04, 0xac, 0x73, 0x46, 0xeb,
1757        0x93, 0xc3, 0x42, 0x58, 0xf1, 0x69, 0x63, 0x65, 0xa6, 0x9f, 0xbe, 0xcb, 0x33, 0xd4, 0x82,
1758        0xd9, 0xdf, 0xc7, 0x3e, 0x94, 0x61, 0x58, 0x83, 0xba, 0x2e, 0x3a, 0xb2, 0xdd, 0x19, 0xcb,
1759        0x8c, 0x12, 0x2e, 0x19, 0x0e, 0x90, 0x2c, 0xb8, 0xec, 0xb9, 0xaa, 0xea, 0x10, 0x00, 0xbb,
1760        0x60, 0xeb, 0xe3, 0x92, 0xb9, 0x2c, 0x78, 0xbb, 0x41, 0xfd, 0x5c, 0xdc, 0xc3, 0x0f, 0x46,
1761        0x37, 0x0a, 0x35, 0x01, 0x29, 0x01, 0x18, 0x30, 0x04, 0x14, 0x2b, 0x33, 0x55, 0x73, 0xc7,
1762        0xc9, 0x12, 0x46, 0x59, 0xe8, 0xe5, 0xfc, 0x50, 0xc5, 0x68, 0x76, 0xfc, 0x93, 0xdc, 0x0b,
1763        0x24, 0x02, 0x61, 0x30, 0x05, 0x14, 0x2b, 0x33, 0x55, 0x73, 0xc7, 0xc9, 0x12, 0x46, 0x59,
1764        0xe8, 0xe5, 0xfc, 0x50, 0xc5, 0x68, 0x76, 0xfc, 0x93, 0xdc, 0x0b, 0x18, 0x30, 0x0b, 0x40,
1765        0x48, 0x8a, 0x6d, 0xf0, 0xa5, 0x9c, 0x3d, 0xb5, 0x5a, 0x29, 0xeb, 0xf6, 0x9a, 0xba, 0x7a,
1766        0xd2, 0x49, 0xb8, 0xcc, 0xe7, 0x33, 0xe3, 0xaa, 0x45, 0x99, 0x6e, 0x34, 0x3a, 0xe3, 0x23,
1767        0x1d, 0x30, 0x94, 0x36, 0x77, 0x33, 0x50, 0x9a, 0x28, 0x9b, 0x25, 0x42, 0xba, 0xaf, 0x13,
1768        0x50, 0xda, 0xe8, 0x43, 0xb4, 0xe1, 0x49, 0x8c, 0x61, 0x0d, 0xab, 0x24, 0xcd, 0xe2, 0x1c,
1769        0xb2, 0x5a, 0x36, 0xd5, 0x18,
1770    ];
1771
1772    const UNORDERED_EXTENSIONS_DER: &[u8] = &[
1773        0x30, 0x82, 0x01, 0x4b, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x44, 0x9d, 0xeb, 0xca,
1774        0x2e, 0x2e, 0x98, 0x42, 0xe0, 0x87, 0x6f, 0x8b, 0xfa, 0x23, 0xe4, 0x54, 0x30, 0x0a, 0x06,
1775        0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x22, 0x31, 0x20, 0x30, 0x1e,
1776        0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x01, 0x04, 0x0c, 0x10, 0x30,
1777        0x30, 0x33, 0x30, 0x42, 0x46, 0x46, 0x34, 0x38, 0x35, 0x42, 0x37, 0x35, 0x36, 0x46, 0x36,
1778        0x30, 0x1e, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x39, 0x32, 0x30, 0x32, 0x30, 0x31, 0x39, 0x34,
1779        0x36, 0x5a, 0x17, 0x0d, 0x34, 0x37, 0x30, 0x39, 0x32, 0x30, 0x32, 0x31, 0x31, 0x39, 0x34,
1780        0x36, 0x5a, 0x30, 0x22, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01,
1781        0x82, 0xa2, 0x7c, 0x01, 0x04, 0x0c, 0x10, 0x30, 0x30, 0x33, 0x30, 0x42, 0x46, 0x46, 0x34,
1782        0x38, 0x35, 0x42, 0x37, 0x35, 0x36, 0x46, 0x36, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a,
1783        0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01,
1784        0x07, 0x03, 0x42, 0x00, 0x04, 0xac, 0x73, 0x46, 0xeb, 0x93, 0xc3, 0x42, 0x58, 0xf1, 0x69,
1785        0x63, 0x65, 0xa6, 0x9f, 0xbe, 0xcb, 0x33, 0xd4, 0x82, 0xd9, 0xdf, 0xc7, 0x3e, 0x94, 0x61,
1786        0x58, 0x83, 0xba, 0x2e, 0x3a, 0xb2, 0xdd, 0x19, 0xcb, 0x8c, 0x12, 0x2e, 0x19, 0x0e, 0x90,
1787        0x2c, 0xb8, 0xec, 0xb9, 0xaa, 0xea, 0x10, 0x00, 0xbb, 0x60, 0xeb, 0xe3, 0x92, 0xb9, 0x2c,
1788        0x78, 0xbb, 0x41, 0xfd, 0x5c, 0xdc, 0xc3, 0x0f, 0x46, 0xa3, 0x63, 0x30, 0x61, 0x30, 0x0f,
1789        0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff,
1790        0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x2b, 0x33, 0x55, 0x73,
1791        0xc7, 0xc9, 0x12, 0x46, 0x59, 0xe8, 0xe5, 0xfc, 0x50, 0xc5, 0x68, 0x76, 0xfc, 0x93, 0xdc,
1792        0x0b, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
1793        0x01, 0x86, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
1794        0x2b, 0x33, 0x55, 0x73, 0xc7, 0xc9, 0x12, 0x46, 0x59, 0xe8, 0xe5, 0xfc, 0x50, 0xc5, 0x68,
1795        0x76, 0xfc, 0x93, 0xdc, 0x0b,
1796    ];
1797}