rs_matter/cert/
mod.rs

1/*
2 *
3 *    Copyright (c) 2020-2022 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 crate::{
21    crypto::KeyPair,
22    error::{Error, ErrorCode},
23    tlv::{self, FromTLV, OctetStr, TLVArray, TLVElement, TLVWriter, TagType, ToTLV},
24    utils::writebuf::WriteBuf,
25};
26use log::error;
27use num_derive::FromPrimitive;
28
29pub use self::asn1_writer::ASN1Writer;
30use self::printer::CertPrinter;
31
32pub const MAX_CERT_TLV_LEN: usize = 1024; // TODO
33
34// As per https://datatracker.ietf.org/doc/html/rfc5280
35
36const OID_PUB_KEY_ECPUBKEY: [u8; 7] = [0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01];
37const OID_EC_TYPE_PRIME256V1: [u8; 8] = [0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07];
38const OID_ECDSA_WITH_SHA256: [u8; 8] = [0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02];
39
40#[derive(FromPrimitive)]
41pub enum CertTags {
42    SerialNum = 1,
43    SignAlgo = 2,
44    Issuer = 3,
45    NotBefore = 4,
46    NotAfter = 5,
47    Subject = 6,
48    PubKeyAlgo = 7,
49    EcCurveId = 8,
50    EcPubKey = 9,
51    Extensions = 10,
52    Signature = 11,
53}
54
55#[derive(FromPrimitive, Debug)]
56pub enum EcCurveIdValue {
57    Prime256V1 = 1,
58}
59
60pub fn get_ec_curve_id(algo: u8) -> Option<EcCurveIdValue> {
61    num::FromPrimitive::from_u8(algo)
62}
63
64#[derive(FromPrimitive, Debug)]
65pub enum PubKeyAlgoValue {
66    EcPubKey = 1,
67}
68
69pub fn get_pubkey_algo(algo: u8) -> Option<PubKeyAlgoValue> {
70    num::FromPrimitive::from_u8(algo)
71}
72
73#[derive(FromPrimitive, Debug)]
74pub enum SignAlgoValue {
75    ECDSAWithSHA256 = 1,
76}
77
78pub fn get_sign_algo(algo: u8) -> Option<SignAlgoValue> {
79    num::FromPrimitive::from_u8(algo)
80}
81
82const KEY_USAGE_DIGITAL_SIGN: u16 = 0x0001;
83const KEY_USAGE_NON_REPUDIATION: u16 = 0x0002;
84const KEY_USAGE_KEY_ENCIPHERMENT: u16 = 0x0004;
85const KEY_USAGE_DATA_ENCIPHERMENT: u16 = 0x0008;
86const KEY_USAGE_KEY_AGREEMENT: u16 = 0x0010;
87const KEY_USAGE_KEY_CERT_SIGN: u16 = 0x0020;
88const KEY_USAGE_CRL_SIGN: u16 = 0x0040;
89const KEY_USAGE_ENCIPHER_ONLY: u16 = 0x0080;
90const KEY_USAGE_DECIPHER_ONLY: u16 = 0x0100;
91
92fn reverse_byte(byte: u8) -> u8 {
93    const LOOKUP: [u8; 16] = [
94        0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e, 0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b, 0x07,
95        0x0f,
96    ];
97    (LOOKUP[(byte & 0x0f) as usize] << 4) | LOOKUP[(byte >> 4) as usize]
98}
99
100fn int_to_bitstring(mut a: u16, buf: &mut [u8]) {
101    if buf.len() >= 2 {
102        buf[0] = reverse_byte((a & 0xff) as u8);
103        a >>= 8;
104        buf[1] = reverse_byte((a & 0xff) as u8);
105    }
106}
107
108macro_rules! add_if {
109    ($key:ident, $bit:ident,$str:literal) => {
110        if ($key & $bit) != 0 {
111            $str
112        } else {
113            ""
114        }
115    };
116}
117
118fn get_print_str(key_usage: u16) -> heapless::String<256> {
119    let mut string = heapless::String::new();
120    write!(
121        &mut string,
122        "{}{}{}{}{}{}{}{}{}",
123        add_if!(key_usage, KEY_USAGE_DIGITAL_SIGN, "digitalSignature "),
124        add_if!(key_usage, KEY_USAGE_NON_REPUDIATION, "nonRepudiation "),
125        add_if!(key_usage, KEY_USAGE_KEY_ENCIPHERMENT, "keyEncipherment "),
126        add_if!(key_usage, KEY_USAGE_DATA_ENCIPHERMENT, "dataEncipherment "),
127        add_if!(key_usage, KEY_USAGE_KEY_AGREEMENT, "keyAgreement "),
128        add_if!(key_usage, KEY_USAGE_KEY_CERT_SIGN, "keyCertSign "),
129        add_if!(key_usage, KEY_USAGE_CRL_SIGN, "CRLSign "),
130        add_if!(key_usage, KEY_USAGE_ENCIPHER_ONLY, "encipherOnly "),
131        add_if!(key_usage, KEY_USAGE_DECIPHER_ONLY, "decipherOnly "),
132    )
133    .unwrap();
134
135    string
136}
137
138#[allow(unused_assignments)]
139fn encode_key_usage(key_usage: u16, w: &mut dyn CertConsumer) -> Result<(), Error> {
140    let mut key_usage_str = [0u8; 2];
141    int_to_bitstring(key_usage, &mut key_usage_str);
142    w.bitstr(&get_print_str(key_usage), true, &key_usage_str)?;
143    Ok(())
144}
145
146fn encode_extended_key_usage(
147    list: impl Iterator<Item = u8>,
148    w: &mut dyn CertConsumer,
149) -> Result<(), Error> {
150    const OID_SERVER_AUTH: [u8; 8] = [0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01];
151    const OID_CLIENT_AUTH: [u8; 8] = [0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02];
152    const OID_CODE_SIGN: [u8; 8] = [0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03];
153    const OID_EMAIL_PROT: [u8; 8] = [0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x04];
154    const OID_TIMESTAMP: [u8; 8] = [0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x08];
155    const OID_OCSP_SIGN: [u8; 8] = [0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09];
156    let encoding = [
157        ("", &[0; 8]),
158        ("ServerAuth", &OID_SERVER_AUTH),
159        ("ClientAuth", &OID_CLIENT_AUTH),
160        ("CodeSign", &OID_CODE_SIGN),
161        ("EmailProtection", &OID_EMAIL_PROT),
162        ("Timestamp", &OID_TIMESTAMP),
163        ("OCSPSign", &OID_OCSP_SIGN),
164    ];
165
166    w.start_seq("")?;
167    for t in list {
168        let t = t as usize;
169        if t > 0 && t <= encoding.len() {
170            w.oid(encoding[t].0, encoding[t].1)?;
171        } else {
172            error!("Skipping encoding key usage out of bounds");
173        }
174    }
175    w.end_seq()
176}
177
178#[derive(FromTLV, ToTLV, Default, Debug)]
179#[tlvargs(start = 1)]
180struct BasicConstraints {
181    is_ca: bool,
182    path: Option<u8>,
183}
184
185impl BasicConstraints {
186    pub fn encode(&self, w: &mut dyn CertConsumer) -> Result<(), Error> {
187        w.start_seq("")?;
188        if self.is_ca {
189            // Encode CA only if true
190            w.bool("CA:", true)?;
191        }
192        if let Some(len) = self.path {
193            w.integer("Path Len Constraint", &[len])?;
194        }
195        w.end_seq()
196    }
197}
198
199fn encode_extension_start(
200    tag: &str,
201    critical: bool,
202    oid: &[u8],
203    w: &mut dyn CertConsumer,
204) -> Result<(), Error> {
205    w.start_seq(tag)?;
206    w.oid("", oid)?;
207    if critical {
208        w.bool("critical:", true)?;
209    }
210    w.start_compound_ostr("value:")
211}
212
213fn encode_extension_end(w: &mut dyn CertConsumer) -> Result<(), Error> {
214    w.end_compound_ostr()?;
215    w.end_seq()
216}
217
218#[derive(FromTLV, ToTLV, Default, Debug)]
219#[tlvargs(lifetime = "'a", start = 1, datatype = "list")]
220struct Extensions<'a> {
221    basic_const: Option<BasicConstraints>,
222    key_usage: Option<u16>,
223    ext_key_usage: Option<TLVArray<'a, u8>>,
224    subj_key_id: Option<OctetStr<'a>>,
225    auth_key_id: Option<OctetStr<'a>>,
226    future_extensions: Option<OctetStr<'a>>,
227}
228
229impl<'a> Extensions<'a> {
230    fn encode(&self, w: &mut dyn CertConsumer) -> Result<(), Error> {
231        const OID_BASIC_CONSTRAINTS: [u8; 3] = [0x55, 0x1D, 0x13];
232        const OID_KEY_USAGE: [u8; 3] = [0x55, 0x1D, 0x0F];
233        const OID_EXT_KEY_USAGE: [u8; 3] = [0x55, 0x1D, 0x25];
234        const OID_SUBJ_KEY_IDENTIFIER: [u8; 3] = [0x55, 0x1D, 0x0E];
235        const OID_AUTH_KEY_ID: [u8; 3] = [0x55, 0x1D, 0x23];
236
237        w.start_ctx("X509v3 extensions:", 3)?;
238        w.start_seq("")?;
239        if let Some(t) = &self.basic_const {
240            encode_extension_start("X509v3 Basic Constraints", true, &OID_BASIC_CONSTRAINTS, w)?;
241            t.encode(w)?;
242            encode_extension_end(w)?;
243        }
244        if let Some(t) = self.key_usage {
245            encode_extension_start("X509v3 Key Usage", true, &OID_KEY_USAGE, w)?;
246            encode_key_usage(t, w)?;
247            encode_extension_end(w)?;
248        }
249        if let Some(t) = &self.ext_key_usage {
250            encode_extension_start("X509v3 Extended Key Usage", true, &OID_EXT_KEY_USAGE, w)?;
251            encode_extended_key_usage(t.iter(), w)?;
252            encode_extension_end(w)?;
253        }
254        if let Some(t) = &self.subj_key_id {
255            encode_extension_start("Subject Key ID", false, &OID_SUBJ_KEY_IDENTIFIER, w)?;
256            w.ostr("", t.0)?;
257            encode_extension_end(w)?;
258        }
259        if let Some(t) = &self.auth_key_id {
260            encode_extension_start("Auth Key ID", false, &OID_AUTH_KEY_ID, w)?;
261            w.start_seq("")?;
262            w.ctx("", 0, t.0)?;
263            w.end_seq()?;
264            encode_extension_end(w)?;
265        }
266        if let Some(t) = &self.future_extensions {
267            error!("Future Extensions Not Yet Supported: {:x?}", t.0);
268        }
269        w.end_seq()?;
270        w.end_ctx()?;
271        Ok(())
272    }
273}
274
275#[derive(FromPrimitive, Copy, Clone)]
276enum DnTags {
277    CommonName = 1,
278    Surname = 2,
279    SerialNum = 3,
280    CountryName = 4,
281    LocalityName = 5,
282    StateName = 6,
283    OrgName = 7,
284    OrgUnitName = 8,
285    Title = 9,
286    Name = 10,
287    GivenName = 11,
288    Intials = 12,
289    GenQalifier = 13,
290    DnQualifier = 14,
291    Pseudonym = 15,
292    DomainComponent = 16,
293    NodeId = 17,
294    FirmwareSignId = 18,
295    IcaId = 19,
296    RootCaId = 20,
297    FabricId = 21,
298    NocCat = 22,
299}
300
301#[derive(Debug)]
302enum DistNameValue<'a> {
303    Uint(u64),
304    Utf8Str(&'a [u8]),
305    PrintableStr(&'a [u8]),
306}
307
308const MAX_DN_ENTRIES: usize = 5;
309
310#[derive(Default, Debug)]
311struct DistNames<'a> {
312    // The order in which the DNs arrive is important, as the signing
313    // requires that the ASN1 notation retains the same order
314    dn: heapless::Vec<(u8, DistNameValue<'a>), MAX_DN_ENTRIES>,
315}
316
317impl<'a> DistNames<'a> {
318    fn u64(&self, match_id: DnTags) -> Option<u64> {
319        self.dn
320            .iter()
321            .find(|(id, _)| *id == match_id as u8)
322            .and_then(|(_, value)| {
323                if let DistNameValue::Uint(u) = *value {
324                    Some(u)
325                } else {
326                    None
327                }
328            })
329    }
330
331    fn u32_arr(&self, match_id: DnTags, output: &mut [u32]) {
332        let mut out_index = 0;
333        for (_, val) in self.dn.iter().filter(|(id, _)| *id == match_id as u8) {
334            if let DistNameValue::Uint(a) = val {
335                if out_index < output.len() {
336                    // CatIds are actually just 32-bit
337                    output[out_index] = *a as u32;
338                    out_index += 1;
339                }
340            }
341        }
342    }
343}
344
345const PRINTABLE_STR_THRESHOLD: u8 = 0x80;
346
347impl<'a> FromTLV<'a> for DistNames<'a> {
348    fn from_tlv(t: &TLVElement<'a>) -> Result<Self, Error> {
349        let mut d = Self {
350            dn: heapless::Vec::new(),
351        };
352        let iter = t.confirm_list()?.enter().ok_or(ErrorCode::Invalid)?;
353        for t in iter {
354            if let TagType::Context(tag) = t.get_tag() {
355                if let Ok(value) = t.u64() {
356                    d.dn.push((tag, DistNameValue::Uint(value)))
357                        .map_err(|_| ErrorCode::BufferTooSmall)?;
358                } else if let Ok(value) = t.slice() {
359                    if tag > PRINTABLE_STR_THRESHOLD {
360                        d.dn.push((
361                            tag - PRINTABLE_STR_THRESHOLD,
362                            DistNameValue::PrintableStr(value),
363                        ))
364                        .map_err(|_| ErrorCode::BufferTooSmall)?;
365                    } else {
366                        d.dn.push((tag, DistNameValue::Utf8Str(value)))
367                            .map_err(|_| ErrorCode::BufferTooSmall)?;
368                    }
369                }
370            }
371        }
372        Ok(d)
373    }
374}
375
376impl<'a> ToTLV for DistNames<'a> {
377    fn to_tlv(&self, tw: &mut TLVWriter, tag: TagType) -> Result<(), Error> {
378        tw.start_list(tag)?;
379        for (name, value) in &self.dn {
380            match value {
381                DistNameValue::Uint(v) => tw.u64(TagType::Context(*name), *v)?,
382                DistNameValue::Utf8Str(v) => tw.utf8(TagType::Context(*name), v)?,
383                DistNameValue::PrintableStr(v) => {
384                    tw.utf8(TagType::Context(*name + PRINTABLE_STR_THRESHOLD), v)?
385                }
386            }
387        }
388        tw.end_container()
389    }
390}
391
392impl<'a> DistNames<'a> {
393    fn encode(&self, tag: &str, w: &mut dyn CertConsumer) -> Result<(), Error> {
394        const OID_COMMON_NAME: [u8; 3] = [0x55_u8, 0x04, 0x03];
395        const OID_SURNAME: [u8; 3] = [0x55_u8, 0x04, 0x04];
396        const OID_SERIAL_NUMBER: [u8; 3] = [0x55_u8, 0x04, 0x05];
397        const OID_COUNTRY_NAME: [u8; 3] = [0x55_u8, 0x04, 0x06];
398        const OID_LOCALITY_NAME: [u8; 3] = [0x55_u8, 0x04, 0x07];
399        const OID_STATE_NAME: [u8; 3] = [0x55_u8, 0x04, 0x08];
400        const OID_ORGANIZATION_NAME: [u8; 3] = [0x55_u8, 0x04, 0x0A];
401        const OID_ORGANIZATIONAL_UNIT_NAME: [u8; 3] = [0x55_u8, 0x04, 0x0B];
402        const OID_TITLE: [u8; 3] = [0x55_u8, 0x04, 0x0C];
403        const OID_NAME: [u8; 3] = [0x55_u8, 0x04, 0x29];
404        const OID_GIVEN_NAME: [u8; 3] = [0x55_u8, 0x04, 0x2A];
405        const OID_INITIALS: [u8; 3] = [0x55_u8, 0x04, 0x2B];
406        const OID_GENERATION_QUALIFIER: [u8; 3] = [0x55_u8, 0x04, 0x2C];
407        const OID_DN_QUALIFIER: [u8; 3] = [0x55_u8, 0x04, 0x2E];
408        const OID_PSEUDONYM: [u8; 3] = [0x55_u8, 0x04, 0x41];
409        const OID_DOMAIN_COMPONENT: [u8; 10] = [
410            0x09_u8, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19,
411        ];
412        const OID_MATTER_NODE_ID: [u8; 10] = [
413            0x2B_u8, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x01, 0x01,
414        ];
415        const OID_MATTER_FW_SIGNING_ID: [u8; 10] = [
416            0x2B_u8, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x01, 0x02,
417        ];
418        const OID_MATTER_ICAC_ID: [u8; 10] = [
419            0x2B_u8, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x01, 0x03,
420        ];
421        const OID_MATTER_RCAC_ID: [u8; 10] = [
422            0x2B_u8, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x01, 0x04,
423        ];
424        const OID_MATTER_FABRIC_ID: [u8; 10] = [
425            0x2B_u8, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x01, 0x05,
426        ];
427        const OID_MATTER_CASE_AUTH_TAG: [u8; 10] = [
428            0x2B_u8, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x01, 0x06,
429        ];
430
431        const DN_ENCODING: [(&str, &[u8], Option<IntToStringLen>); 22] = [
432            ("Common Name:", &OID_COMMON_NAME, None),
433            ("Surname:", &OID_SURNAME, None),
434            ("Serial Number", &OID_SERIAL_NUMBER, None),
435            ("Country Name", &OID_COUNTRY_NAME, None),
436            ("Locality name", &OID_LOCALITY_NAME, None),
437            ("State Name", &OID_STATE_NAME, None),
438            ("Org Name", &OID_ORGANIZATION_NAME, None),
439            ("OU Name", &OID_ORGANIZATIONAL_UNIT_NAME, None),
440            ("Title", &OID_TITLE, None),
441            ("Name", &OID_NAME, None),
442            ("Given Name", &OID_GIVEN_NAME, None),
443            ("Initials", &OID_INITIALS, None),
444            ("Gen Qualifier", &OID_GENERATION_QUALIFIER, None),
445            ("DN Qualifier", &OID_DN_QUALIFIER, None),
446            ("Pseudonym", &OID_PSEUDONYM, None),
447            ("Domain Component", &OID_DOMAIN_COMPONENT, None),
448            (
449                "Chip Node Id:",
450                &OID_MATTER_NODE_ID,
451                Some(IntToStringLen::Len16),
452            ),
453            (
454                "Chip Firmware Signing Id:",
455                &OID_MATTER_FW_SIGNING_ID,
456                Some(IntToStringLen::Len16),
457            ),
458            (
459                "Chip ICA Id:",
460                &OID_MATTER_ICAC_ID,
461                Some(IntToStringLen::Len16),
462            ),
463            (
464                "Chip Root CA Id:",
465                &OID_MATTER_RCAC_ID,
466                Some(IntToStringLen::Len16),
467            ),
468            (
469                "Chip Fabric Id:",
470                &OID_MATTER_FABRIC_ID,
471                Some(IntToStringLen::Len16),
472            ),
473            (
474                "Chip NOC CAT Id:",
475                &OID_MATTER_CASE_AUTH_TAG,
476                Some(IntToStringLen::Len8),
477            ),
478        ];
479
480        w.start_seq(tag)?;
481        for (id, value) in &self.dn {
482            let tag: Option<DnTags> = num::FromPrimitive::from_u8(*id);
483            if tag.is_some() {
484                let index = (id - 1) as usize;
485                if index <= DN_ENCODING.len() {
486                    let this = &DN_ENCODING[index];
487                    encode_dn_value(value, this.0, this.1, w, this.2)?;
488                } else {
489                    // Non Matter DNs are encoded as
490                    error!("Invalid DN, too high {}", id);
491                }
492            } else {
493                // Non Matter DNs are encoded as
494                error!("Non Matter DNs are not yet supported {}", id);
495            }
496        }
497        w.end_seq()?;
498        Ok(())
499    }
500}
501
502#[derive(Copy, Clone)]
503/// Describes the expected string length while encoding an integer as a string
504enum IntToStringLen {
505    Len16,
506    Len8,
507}
508
509fn encode_dn_value(
510    value: &DistNameValue,
511    name: &str,
512    oid: &[u8],
513    w: &mut dyn CertConsumer,
514    // Only applicable for integer values
515    expected_len: Option<IntToStringLen>,
516) -> Result<(), Error> {
517    w.start_set("")?;
518    w.start_seq("")?;
519    w.oid(name, oid)?;
520    match value {
521        DistNameValue::Uint(v) => match expected_len {
522            Some(IntToStringLen::Len16) => {
523                let mut string = heapless::String::<32>::new();
524                write!(&mut string, "{:016X}", v).unwrap();
525                w.utf8str("", &string)?
526            }
527            Some(IntToStringLen::Len8) => {
528                let mut string = heapless::String::<32>::new();
529                write!(&mut string, "{:08X}", v).unwrap();
530                w.utf8str("", &string)?
531            }
532            _ => {
533                error!("Invalid encoding");
534                Err(ErrorCode::Invalid)?
535            }
536        },
537        DistNameValue::Utf8Str(v) => {
538            w.utf8str("", core::str::from_utf8(v)?)?;
539        }
540        DistNameValue::PrintableStr(v) => {
541            w.printstr("", core::str::from_utf8(v)?)?;
542        }
543    }
544    w.end_seq()?;
545    w.end_set()
546}
547
548#[derive(FromTLV, ToTLV, Default, Debug)]
549#[tlvargs(lifetime = "'a", start = 1)]
550pub struct Cert<'a> {
551    serial_no: OctetStr<'a>,
552    sign_algo: u8,
553    issuer: DistNames<'a>,
554    not_before: u32,
555    not_after: u32,
556    subject: DistNames<'a>,
557    pubkey_algo: u8,
558    ec_curve_id: u8,
559    pubkey: OctetStr<'a>,
560    extensions: Extensions<'a>,
561    signature: OctetStr<'a>,
562}
563
564// TODO: Instead of parsing the TLVs everytime, we should just cache this, but the encoding
565// rules in terms of sequence may get complicated. Need to look into this
566impl<'a> Cert<'a> {
567    pub fn new(cert_bin: &'a [u8]) -> Result<Self, Error> {
568        let root = tlv::get_root_node(cert_bin)?;
569        Cert::from_tlv(&root)
570    }
571
572    pub fn get_node_id(&self) -> Result<u64, Error> {
573        self.subject
574            .u64(DnTags::NodeId)
575            .ok_or_else(|| Error::from(ErrorCode::NoNodeId))
576    }
577
578    pub fn get_cat_ids(&self, output: &mut [u32]) {
579        self.subject.u32_arr(DnTags::NocCat, output)
580    }
581
582    pub fn get_fabric_id(&self) -> Result<u64, Error> {
583        self.subject
584            .u64(DnTags::FabricId)
585            .ok_or_else(|| Error::from(ErrorCode::NoFabricId))
586    }
587
588    pub fn get_pubkey(&self) -> &[u8] {
589        self.pubkey.0
590    }
591
592    pub fn get_subject_key_id(&self) -> Result<&[u8], Error> {
593        if let Some(id) = self.extensions.subj_key_id.as_ref() {
594            Ok(id.0)
595        } else {
596            Err(ErrorCode::Invalid.into())
597        }
598    }
599
600    pub fn is_authority(&self, their: &Cert) -> Result<bool, Error> {
601        if let Some(our_auth_key) = &self.extensions.auth_key_id {
602            let their_subject = their.get_subject_key_id()?;
603            if our_auth_key.0 == their_subject {
604                Ok(true)
605            } else {
606                Ok(false)
607            }
608        } else {
609            Ok(false)
610        }
611    }
612
613    pub fn get_signature(&self) -> &[u8] {
614        self.signature.0
615    }
616
617    pub fn as_tlv(&self, buf: &mut [u8]) -> Result<usize, Error> {
618        let mut wb = WriteBuf::new(buf);
619        let mut tw = TLVWriter::new(&mut wb);
620        self.to_tlv(&mut tw, TagType::Anonymous)?;
621        Ok(wb.as_slice().len())
622    }
623
624    pub fn as_asn1(&self, buf: &mut [u8]) -> Result<usize, Error> {
625        let mut w = ASN1Writer::new(buf);
626        self.encode(&mut w)?;
627        Ok(w.as_slice().len())
628    }
629
630    pub fn verify_chain_start(&self) -> CertVerifier {
631        CertVerifier::new(self)
632    }
633
634    fn encode(&self, w: &mut dyn CertConsumer) -> Result<(), Error> {
635        w.start_seq("")?;
636
637        w.start_ctx("Version:", 0)?;
638        w.integer("", &[2])?;
639        w.end_ctx()?;
640
641        w.integer("Serial Num:", self.serial_no.0)?;
642
643        w.start_seq("Signature Algorithm:")?;
644        let (str, oid) = match get_sign_algo(self.sign_algo).ok_or(ErrorCode::Invalid)? {
645            SignAlgoValue::ECDSAWithSHA256 => ("ECDSA with SHA256", OID_ECDSA_WITH_SHA256),
646        };
647        w.oid(str, &oid)?;
648        w.end_seq()?;
649
650        self.issuer.encode("Issuer:", w)?;
651
652        w.start_seq("Validity:")?;
653        w.utctime("Not Before:", self.not_before)?;
654        w.utctime("Not After:", self.not_after)?;
655        w.end_seq()?;
656
657        self.subject.encode("Subject:", w)?;
658
659        w.start_seq("")?;
660        w.start_seq("Public Key Algorithm")?;
661        let (str, pub_key) = match get_pubkey_algo(self.pubkey_algo).ok_or(ErrorCode::Invalid)? {
662            PubKeyAlgoValue::EcPubKey => ("ECPubKey", OID_PUB_KEY_ECPUBKEY),
663        };
664        w.oid(str, &pub_key)?;
665        let (str, curve_id) = match get_ec_curve_id(self.ec_curve_id).ok_or(ErrorCode::Invalid)? {
666            EcCurveIdValue::Prime256V1 => ("Prime256v1", OID_EC_TYPE_PRIME256V1),
667        };
668        w.oid(str, &curve_id)?;
669        w.end_seq()?;
670
671        w.bitstr("Public-Key:", false, self.pubkey.0)?;
672        w.end_seq()?;
673
674        self.extensions.encode(w)?;
675
676        // We do not encode the Signature in the DER certificate
677
678        w.end_seq()
679    }
680}
681
682impl<'a> fmt::Display for Cert<'a> {
683    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
684        let mut printer = CertPrinter::new(f);
685        let _ = self
686            .encode(&mut printer)
687            .map_err(|e| error!("Error decoding certificate: {}", e));
688        // Signature is not encoded by the Cert Decoder
689        writeln!(f, "Signature: {:x?}", self.get_signature())
690    }
691}
692
693pub struct CertVerifier<'a> {
694    cert: &'a Cert<'a>,
695}
696
697impl<'a> CertVerifier<'a> {
698    pub fn new(cert: &'a Cert) -> Self {
699        Self { cert }
700    }
701
702    pub fn add_cert(self, parent: &'a Cert) -> Result<CertVerifier<'a>, Error> {
703        if !self.cert.is_authority(parent)? {
704            Err(ErrorCode::InvalidAuthKey)?;
705        }
706        let mut asn1 = [0u8; MAX_ASN1_CERT_SIZE];
707        let len = self.cert.as_asn1(&mut asn1)?;
708        let asn1 = &asn1[..len];
709
710        let k = KeyPair::new_from_public(parent.get_pubkey())?;
711        k.verify_msg(asn1, self.cert.get_signature()).map_err(|e| {
712            error!(
713                "Error in signature verification of certificate: {:x?}",
714                self.cert.get_subject_key_id()
715            );
716            e
717        })?;
718
719        // TODO: other validation checks
720        Ok(CertVerifier::new(parent))
721    }
722
723    pub fn finalise(self) -> Result<(), Error> {
724        let cert = self.cert;
725        self.add_cert(cert)?;
726        Ok(())
727    }
728}
729
730pub trait CertConsumer {
731    fn start_seq(&mut self, tag: &str) -> Result<(), Error>;
732    fn end_seq(&mut self) -> Result<(), Error>;
733    fn integer(&mut self, tag: &str, i: &[u8]) -> Result<(), Error>;
734    fn printstr(&mut self, tag: &str, s: &str) -> Result<(), Error>;
735    fn utf8str(&mut self, tag: &str, s: &str) -> Result<(), Error>;
736    fn bitstr(&mut self, tag: &str, truncate: bool, s: &[u8]) -> Result<(), Error>;
737    fn ostr(&mut self, tag: &str, s: &[u8]) -> Result<(), Error>;
738    fn start_compound_ostr(&mut self, tag: &str) -> Result<(), Error>;
739    fn end_compound_ostr(&mut self) -> Result<(), Error>;
740    fn bool(&mut self, tag: &str, b: bool) -> Result<(), Error>;
741    fn start_set(&mut self, tag: &str) -> Result<(), Error>;
742    fn end_set(&mut self) -> Result<(), Error>;
743    fn ctx(&mut self, tag: &str, id: u8, val: &[u8]) -> Result<(), Error>;
744    fn start_ctx(&mut self, tag: &str, id: u8) -> Result<(), Error>;
745    fn end_ctx(&mut self) -> Result<(), Error>;
746    fn oid(&mut self, tag: &str, oid: &[u8]) -> Result<(), Error>;
747    fn utctime(&mut self, tag: &str, epoch: u32) -> Result<(), Error>;
748}
749
750const MAX_DEPTH: usize = 10;
751const MAX_ASN1_CERT_SIZE: usize = 1000;
752
753mod asn1_writer;
754mod printer;
755
756#[cfg(test)]
757mod tests {
758    use log::info;
759
760    use crate::cert::Cert;
761    use crate::tlv::{self, FromTLV, TLVWriter, TagType, ToTLV};
762    use crate::utils::writebuf::WriteBuf;
763
764    #[test]
765    fn test_asn1_encode_success() {
766        {
767            let mut asn1_buf = [0u8; 1000];
768            let c = Cert::new(&test_vectors::CHIP_CERT_INPUT1).unwrap();
769            let len = c.as_asn1(&mut asn1_buf).unwrap();
770            assert_eq!(&test_vectors::ASN1_OUTPUT1, &asn1_buf[..len]);
771        }
772
773        {
774            let mut asn1_buf = [0u8; 1000];
775            let c = Cert::new(&test_vectors::CHIP_CERT_INPUT2).unwrap();
776            let len = c.as_asn1(&mut asn1_buf).unwrap();
777            assert_eq!(&test_vectors::ASN1_OUTPUT2, &asn1_buf[..len]);
778        }
779
780        {
781            let mut asn1_buf = [0u8; 1000];
782            let c = Cert::new(&test_vectors::CHIP_CERT_TXT_IN_DN).unwrap();
783            let len = c.as_asn1(&mut asn1_buf).unwrap();
784            assert_eq!(&test_vectors::ASN1_OUTPUT_TXT_IN_DN, &asn1_buf[..len]);
785        }
786    }
787
788    #[test]
789    fn test_verify_chain_success() {
790        let noc = Cert::new(&test_vectors::NOC1_SUCCESS).unwrap();
791        let icac = Cert::new(&test_vectors::ICAC1_SUCCESS).unwrap();
792        let rca = Cert::new(&test_vectors::RCA1_SUCCESS).unwrap();
793        let a = noc.verify_chain_start();
794        a.add_cert(&icac)
795            .unwrap()
796            .add_cert(&rca)
797            .unwrap()
798            .finalise()
799            .unwrap();
800    }
801
802    #[test]
803    fn test_verify_chain_incomplete() {
804        // The chain doesn't lead up to a self-signed certificate
805
806        use crate::error::ErrorCode;
807        let noc = Cert::new(&test_vectors::NOC1_SUCCESS).unwrap();
808        let icac = Cert::new(&test_vectors::ICAC1_SUCCESS).unwrap();
809        let a = noc.verify_chain_start();
810        assert_eq!(
811            Err(ErrorCode::InvalidAuthKey),
812            a.add_cert(&icac).unwrap().finalise().map_err(|e| e.code())
813        );
814    }
815
816    #[test]
817    fn test_auth_key_chain_incorrect() {
818        use crate::error::ErrorCode;
819
820        let noc = Cert::new(&test_vectors::NOC1_AUTH_KEY_FAIL).unwrap();
821        let icac = Cert::new(&test_vectors::ICAC1_SUCCESS).unwrap();
822        let a = noc.verify_chain_start();
823        assert_eq!(
824            Err(ErrorCode::InvalidAuthKey),
825            a.add_cert(&icac).map(|_| ()).map_err(|e| e.code())
826        );
827    }
828
829    #[test]
830    fn test_cert_corrupted() {
831        use crate::error::ErrorCode;
832
833        let noc = Cert::new(&test_vectors::NOC1_CORRUPT_CERT).unwrap();
834        let icac = Cert::new(&test_vectors::ICAC1_SUCCESS).unwrap();
835        let a = noc.verify_chain_start();
836        assert_eq!(
837            Err(ErrorCode::InvalidSignature),
838            a.add_cert(&icac).map(|_| ()).map_err(|e| e.code())
839        );
840    }
841
842    #[test]
843    fn test_tlv_conversions() {
844        let test_input: [&[u8]; 3] = [
845            &test_vectors::NOC1_SUCCESS,
846            &test_vectors::ICAC1_SUCCESS,
847            &test_vectors::RCA1_SUCCESS,
848        ];
849
850        for input in test_input.iter() {
851            info!("Testing next input...");
852            let root = tlv::get_root_node(input).unwrap();
853            let cert = Cert::from_tlv(&root).unwrap();
854            let mut buf = [0u8; 1024];
855            let mut wb = WriteBuf::new(&mut buf);
856            let mut tw = TLVWriter::new(&mut wb);
857            cert.to_tlv(&mut tw, TagType::Anonymous).unwrap();
858            assert_eq!(*input, wb.as_slice());
859        }
860    }
861
862    mod test_vectors {
863        // Group 1
864        pub const NOC1_SUCCESS: [u8; 247] = [
865            0x15, 0x30, 0x1, 0x1, 0x1, 0x24, 0x2, 0x1, 0x37, 0x3, 0x24, 0x13, 0x1, 0x24, 0x15, 0x1,
866            0x18, 0x26, 0x4, 0x80, 0x22, 0x81, 0x27, 0x26, 0x5, 0x80, 0x25, 0x4d, 0x3a, 0x37, 0x6,
867            0x26, 0x11, 0x2, 0x5c, 0xbc, 0x0, 0x24, 0x15, 0x1, 0x18, 0x24, 0x7, 0x1, 0x24, 0x8,
868            0x1, 0x30, 0x9, 0x41, 0x4, 0xba, 0x22, 0x56, 0x43, 0x4f, 0x59, 0x98, 0x32, 0x8d, 0xb8,
869            0xcb, 0x3f, 0x24, 0x90, 0x9a, 0x96, 0x94, 0x43, 0x46, 0x67, 0xc2, 0x11, 0xe3, 0x80,
870            0x26, 0x65, 0xfc, 0x65, 0x37, 0x77, 0x3, 0x25, 0x18, 0xd8, 0xdc, 0x85, 0xfa, 0xe6,
871            0x42, 0xe7, 0x55, 0xc9, 0x37, 0xcc, 0xb, 0x78, 0x84, 0x3d, 0x2f, 0xac, 0x81, 0x88,
872            0x2e, 0x69, 0x0, 0xa5, 0xfc, 0xcd, 0xe0, 0xad, 0xb2, 0x69, 0xca, 0x73, 0x37, 0xa, 0x35,
873            0x1, 0x28, 0x1, 0x18, 0x24, 0x2, 0x1, 0x36, 0x3, 0x4, 0x2, 0x4, 0x1, 0x18, 0x30, 0x4,
874            0x14, 0x39, 0x68, 0x16, 0x1e, 0xb5, 0x56, 0x6d, 0xd3, 0xf8, 0x61, 0xf2, 0x95, 0xf3,
875            0x55, 0xa0, 0xfb, 0xd2, 0x82, 0xc2, 0x29, 0x30, 0x5, 0x14, 0xce, 0x60, 0xb4, 0x28,
876            0x96, 0x72, 0x27, 0x64, 0x81, 0xbc, 0x4f, 0x0, 0x78, 0xa3, 0x30, 0x48, 0xfe, 0x6e,
877            0x65, 0x86, 0x18, 0x30, 0xb, 0x40, 0x2, 0x88, 0x42, 0x0, 0x6f, 0xcc, 0xe0, 0xf0, 0x6c,
878            0xd9, 0xf9, 0x5e, 0xe4, 0xc2, 0xaa, 0x1f, 0x57, 0x71, 0x62, 0xdb, 0x6b, 0x4e, 0xe7,
879            0x55, 0x3f, 0xc6, 0xc7, 0x9f, 0xf8, 0x30, 0xeb, 0x16, 0x6e, 0x6d, 0xc6, 0x9c, 0xb,
880            0xb7, 0xe2, 0xb8, 0xe3, 0xe7, 0x57, 0x88, 0x7b, 0xda, 0xe5, 0x79, 0x39, 0x6d, 0x2c,
881            0x37, 0xb2, 0x7f, 0xc3, 0x63, 0x2f, 0x7e, 0x70, 0xab, 0x5a, 0x2c, 0xf7, 0x5b, 0x18,
882        ];
883        pub const ICAC1_SUCCESS: [u8; 237] = [
884            21, 48, 1, 1, 0, 36, 2, 1, 55, 3, 36, 20, 0, 36, 21, 1, 24, 38, 4, 128, 34, 129, 39,
885            38, 5, 128, 37, 77, 58, 55, 6, 36, 19, 1, 36, 21, 1, 24, 36, 7, 1, 36, 8, 1, 48, 9, 65,
886            4, 86, 25, 119, 24, 63, 212, 255, 43, 88, 61, 233, 121, 52, 102, 223, 233, 0, 251, 109,
887            161, 239, 224, 204, 220, 119, 48, 192, 111, 182, 45, 255, 190, 84, 160, 149, 117, 11,
888            139, 7, 188, 85, 219, 156, 182, 85, 19, 8, 184, 223, 2, 227, 64, 107, 174, 52, 245, 12,
889            186, 201, 242, 191, 241, 231, 80, 55, 10, 53, 1, 41, 1, 24, 36, 2, 96, 48, 4, 20, 206,
890            96, 180, 40, 150, 114, 39, 100, 129, 188, 79, 0, 120, 163, 48, 72, 254, 110, 101, 134,
891            48, 5, 20, 212, 86, 147, 190, 112, 121, 244, 156, 112, 107, 7, 111, 17, 28, 109, 229,
892            100, 164, 68, 116, 24, 48, 11, 64, 243, 8, 190, 128, 155, 254, 245, 21, 205, 241, 217,
893            246, 204, 182, 247, 41, 81, 91, 33, 155, 230, 223, 212, 116, 33, 162, 208, 148, 100,
894            89, 175, 253, 78, 212, 7, 69, 207, 140, 45, 129, 249, 64, 104, 70, 68, 43, 164, 19,
895            126, 114, 138, 79, 104, 238, 20, 226, 88, 118, 105, 56, 12, 92, 31, 171, 24,
896        ];
897        // A single byte in the auth key id is changed in this
898        pub const NOC1_AUTH_KEY_FAIL: [u8; 247] = [
899            0x15, 0x30, 0x1, 0x1, 0x1, 0x24, 0x2, 0x1, 0x37, 0x3, 0x24, 0x13, 0x1, 0x24, 0x15, 0x1,
900            0x18, 0x26, 0x4, 0x80, 0x22, 0x81, 0x27, 0x26, 0x5, 0x80, 0x25, 0x4d, 0x3a, 0x37, 0x6,
901            0x26, 0x11, 0x2, 0x5c, 0xbc, 0x0, 0x24, 0x15, 0x1, 0x18, 0x24, 0x7, 0x1, 0x24, 0x8,
902            0x1, 0x30, 0x9, 0x41, 0x4, 0xba, 0x22, 0x56, 0x43, 0x4f, 0x59, 0x98, 0x32, 0x8d, 0xb8,
903            0xcb, 0x3f, 0x24, 0x90, 0x9a, 0x96, 0x94, 0x43, 0x46, 0x67, 0xc2, 0x11, 0xe3, 0x80,
904            0x26, 0x65, 0xfc, 0x65, 0x37, 0x77, 0x3, 0x25, 0x18, 0xd8, 0xdc, 0x85, 0xfa, 0xe6,
905            0x42, 0xe7, 0x55, 0xc9, 0x37, 0xcc, 0xb, 0x78, 0x84, 0x3d, 0x2f, 0xac, 0x81, 0x88,
906            0x2e, 0x69, 0x0, 0xa5, 0xfc, 0xcd, 0xe0, 0xad, 0xb2, 0x69, 0xca, 0x73, 0x37, 0xa, 0x35,
907            0x1, 0x28, 0x1, 0x18, 0x24, 0x2, 0x1, 0x36, 0x3, 0x4, 0x2, 0x4, 0x1, 0x18, 0x30, 0x4,
908            0x14, 0x39, 0x68, 0x16, 0x1e, 0xb5, 0x56, 0x6d, 0xd3, 0xf8, 0x61, 0xf2, 0x95, 0xf3,
909            0x55, 0xa0, 0xfb, 0xd2, 0x82, 0xc2, 0x29, 0x30, 0x5, 0x14, 0xce, 0x61, 0xb4, 0x28,
910            0x96, 0x72, 0x27, 0x64, 0x81, 0xbc, 0x4f, 0x0, 0x78, 0xa3, 0x30, 0x48, 0xfe, 0x6e,
911            0x65, 0x86, 0x18, 0x30, 0xb, 0x40, 0x2, 0x88, 0x42, 0x0, 0x6f, 0xcc, 0xe0, 0xf0, 0x6c,
912            0xd9, 0xf9, 0x5e, 0xe4, 0xc2, 0xaa, 0x1f, 0x57, 0x71, 0x62, 0xdb, 0x6b, 0x4e, 0xe7,
913            0x55, 0x3f, 0xc6, 0xc7, 0x9f, 0xf8, 0x30, 0xeb, 0x16, 0x6e, 0x6d, 0xc6, 0x9c, 0xb,
914            0xb7, 0xe2, 0xb8, 0xe3, 0xe7, 0x57, 0x88, 0x7b, 0xda, 0xe5, 0x79, 0x39, 0x6d, 0x2c,
915            0x37, 0xb2, 0x7f, 0xc3, 0x63, 0x2f, 0x7e, 0x70, 0xab, 0x5a, 0x2c, 0xf7, 0x5b, 0x18,
916        ];
917        // A single byte in the Certificate contents is changed in this
918        pub const NOC1_CORRUPT_CERT: [u8; 247] = [
919            0x15, 0x30, 0x1, 0x1, 0x1, 0x24, 0x2, 0x1, 0x37, 0x3, 0x24, 0x13, 0x1, 0x24, 0x15, 0x1,
920            0x18, 0x26, 0x4, 0x80, 0x22, 0x81, 0x27, 0x26, 0x5, 0x80, 0x25, 0x4d, 0x3a, 0x37, 0x6,
921            0x26, 0x11, 0x2, 0x5c, 0xbc, 0x0, 0x24, 0x15, 0x1, 0x18, 0x24, 0x7, 0x1, 0x24, 0x8,
922            0x1, 0x30, 0x9, 0x41, 0x4, 0xba, 0x23, 0x56, 0x43, 0x4f, 0x59, 0x98, 0x32, 0x8d, 0xb8,
923            0xcb, 0x3f, 0x24, 0x90, 0x9a, 0x96, 0x94, 0x43, 0x46, 0x67, 0xc2, 0x11, 0xe3, 0x80,
924            0x26, 0x65, 0xfc, 0x65, 0x37, 0x77, 0x3, 0x25, 0x18, 0xd8, 0xdc, 0x85, 0xfa, 0xe6,
925            0x42, 0xe7, 0x55, 0xc9, 0x37, 0xcc, 0xb, 0x78, 0x84, 0x3d, 0x2f, 0xac, 0x81, 0x88,
926            0x2e, 0x69, 0x0, 0xa5, 0xfc, 0xcd, 0xe0, 0xad, 0xb2, 0x69, 0xca, 0x73, 0x37, 0xa, 0x35,
927            0x1, 0x28, 0x1, 0x18, 0x24, 0x2, 0x1, 0x36, 0x3, 0x4, 0x2, 0x4, 0x1, 0x18, 0x30, 0x4,
928            0x14, 0x39, 0x68, 0x16, 0x1e, 0xb5, 0x56, 0x6d, 0xd3, 0xf8, 0x61, 0xf2, 0x95, 0xf3,
929            0x55, 0xa0, 0xfb, 0xd2, 0x82, 0xc2, 0x29, 0x30, 0x5, 0x14, 0xce, 0x60, 0xb4, 0x28,
930            0x96, 0x72, 0x27, 0x64, 0x81, 0xbc, 0x4f, 0x0, 0x78, 0xa3, 0x30, 0x48, 0xfe, 0x6e,
931            0x65, 0x86, 0x18, 0x30, 0xb, 0x40, 0x2, 0x88, 0x42, 0x0, 0x6f, 0xcc, 0xe0, 0xf0, 0x6c,
932            0xd9, 0xf9, 0x5e, 0xe4, 0xc2, 0xaa, 0x1f, 0x57, 0x71, 0x62, 0xdb, 0x6b, 0x4e, 0xe7,
933            0x55, 0x3f, 0xc6, 0xc7, 0x9f, 0xf8, 0x30, 0xeb, 0x16, 0x6e, 0x6d, 0xc6, 0x9c, 0xb,
934            0xb7, 0xe2, 0xb8, 0xe3, 0xe7, 0x57, 0x88, 0x7b, 0xda, 0xe5, 0x79, 0x39, 0x6d, 0x2c,
935            0x37, 0xb2, 0x7f, 0xc3, 0x63, 0x2f, 0x7e, 0x70, 0xab, 0x5a, 0x2c, 0xf7, 0x5b, 0x18,
936        ];
937        pub const RCA1_SUCCESS: [u8; 237] = [
938            0x15, 0x30, 0x1, 0x1, 0x0, 0x24, 0x2, 0x1, 0x37, 0x3, 0x24, 0x14, 0x0, 0x24, 0x15, 0x1,
939            0x18, 0x26, 0x4, 0x80, 0x22, 0x81, 0x27, 0x26, 0x5, 0x80, 0x25, 0x4d, 0x3a, 0x37, 0x6,
940            0x24, 0x14, 0x0, 0x24, 0x15, 0x1, 0x18, 0x24, 0x7, 0x1, 0x24, 0x8, 0x1, 0x30, 0x9,
941            0x41, 0x4, 0x6d, 0x70, 0x7e, 0x4b, 0x98, 0xf6, 0x2b, 0xab, 0x44, 0xd6, 0xfe, 0xa3,
942            0x2e, 0x39, 0xd8, 0xc3, 0x0, 0xa0, 0xe, 0xa8, 0x6c, 0x83, 0xff, 0x69, 0xd, 0xe8, 0x42,
943            0x1, 0xeb, 0xd, 0xaa, 0x68, 0x5d, 0xcb, 0x97, 0x2, 0x80, 0x1d, 0xa8, 0x50, 0x2, 0x2e,
944            0x5a, 0xa2, 0x5a, 0x2e, 0x51, 0x26, 0x4, 0xd2, 0x39, 0x62, 0xcd, 0x82, 0x38, 0x63,
945            0x28, 0xbf, 0x15, 0x1c, 0xa6, 0x27, 0xe0, 0xd7, 0x37, 0xa, 0x35, 0x1, 0x29, 0x1, 0x18,
946            0x24, 0x2, 0x60, 0x30, 0x4, 0x14, 0xd4, 0x56, 0x93, 0xbe, 0x70, 0x79, 0xf4, 0x9c, 0x70,
947            0x6b, 0x7, 0x6f, 0x11, 0x1c, 0x6d, 0xe5, 0x64, 0xa4, 0x44, 0x74, 0x30, 0x5, 0x14, 0xd4,
948            0x56, 0x93, 0xbe, 0x70, 0x79, 0xf4, 0x9c, 0x70, 0x6b, 0x7, 0x6f, 0x11, 0x1c, 0x6d,
949            0xe5, 0x64, 0xa4, 0x44, 0x74, 0x18, 0x30, 0xb, 0x40, 0x3, 0xd, 0x77, 0xe1, 0x9e, 0xea,
950            0x9c, 0x5, 0x5c, 0xcc, 0x47, 0xe8, 0xb3, 0x18, 0x1a, 0xd1, 0x74, 0xee, 0xc6, 0x2e,
951            0xa1, 0x20, 0x16, 0xbd, 0x20, 0xb4, 0x3d, 0xac, 0x24, 0xbe, 0x17, 0xf9, 0xe, 0xb7,
952            0x9a, 0x98, 0xc8, 0xbc, 0x6a, 0xce, 0x99, 0x2a, 0x2e, 0x63, 0x4c, 0x76, 0x6, 0x45,
953            0x93, 0xd3, 0x7c, 0x4, 0x0, 0xe4, 0xc7, 0x78, 0xe9, 0x83, 0x5b, 0xc, 0x33, 0x61, 0x5c,
954            0x2e, 0x18,
955        ];
956        pub const CHIP_CERT_INPUT1: [u8; 237] = [
957            0x15, 0x30, 0x01, 0x01, 0x00, 0x24, 0x02, 0x01, 0x37, 0x03, 0x24, 0x14, 0x00, 0x24,
958            0x15, 0x03, 0x18, 0x26, 0x04, 0x80, 0x22, 0x81, 0x27, 0x26, 0x05, 0x80, 0x25, 0x4d,
959            0x3a, 0x37, 0x06, 0x24, 0x13, 0x01, 0x24, 0x15, 0x03, 0x18, 0x24, 0x07, 0x01, 0x24,
960            0x08, 0x01, 0x30, 0x09, 0x41, 0x04, 0x69, 0xda, 0xe9, 0x42, 0x88, 0xcf, 0x64, 0x94,
961            0x2d, 0xd5, 0x0a, 0x74, 0x2d, 0x50, 0xe8, 0x5e, 0xbe, 0x15, 0x53, 0x24, 0xe5, 0xc5,
962            0x6b, 0xe5, 0x7f, 0xc1, 0x41, 0x11, 0x21, 0xdd, 0x46, 0xa3, 0x0d, 0x63, 0xc3, 0xe3,
963            0x90, 0x7a, 0x69, 0x64, 0xdd, 0x66, 0x78, 0x10, 0xa6, 0xc8, 0x0f, 0xfd, 0xb6, 0xf2,
964            0x9b, 0x88, 0x50, 0x93, 0x77, 0x9e, 0xf7, 0xb4, 0xda, 0x94, 0x11, 0x33, 0x1e, 0xfe,
965            0x37, 0x0a, 0x35, 0x01, 0x29, 0x01, 0x18, 0x24, 0x02, 0x60, 0x30, 0x04, 0x14, 0xdf,
966            0xfb, 0x79, 0xf1, 0x2b, 0xbf, 0x68, 0x18, 0x59, 0x7f, 0xf7, 0xe8, 0xaf, 0x88, 0x91,
967            0x1c, 0x72, 0x32, 0xf7, 0x52, 0x30, 0x05, 0x14, 0xed, 0x31, 0x5e, 0x1a, 0xb7, 0xb9,
968            0x7a, 0xca, 0x04, 0x79, 0x5d, 0x82, 0x57, 0x7a, 0xd7, 0x0a, 0x75, 0xd0, 0xdb, 0x7a,
969            0x18, 0x30, 0x0b, 0x40, 0xe5, 0xd4, 0xe6, 0x0e, 0x98, 0x62, 0x2f, 0xaa, 0x59, 0xe0,
970            0x28, 0x59, 0xc2, 0xd4, 0xcd, 0x34, 0x85, 0x7f, 0x93, 0xbe, 0x14, 0x35, 0xa3, 0x76,
971            0x8a, 0xc9, 0x2f, 0x59, 0x39, 0xa0, 0xb0, 0x75, 0xe8, 0x8e, 0x11, 0xa9, 0xc1, 0x9e,
972            0xaa, 0xab, 0xa0, 0xdb, 0xb4, 0x79, 0x63, 0xfc, 0x02, 0x03, 0x27, 0x25, 0xac, 0x21,
973            0x6f, 0xef, 0x27, 0xab, 0x0f, 0x90, 0x09, 0x99, 0x05, 0xa8, 0x60, 0xd8, 0x18,
974        ];
975        pub const CHIP_CERT_INPUT2: [u8; 247] = [
976            0x15, 0x30, 0x01, 0x01, 0x01, 0x24, 0x02, 0x01, 0x37, 0x03, 0x24, 0x13, 0x01, 0x24,
977            0x15, 0x03, 0x18, 0x26, 0x04, 0x80, 0x22, 0x81, 0x27, 0x26, 0x05, 0x80, 0x25, 0x4d,
978            0x3a, 0x37, 0x06, 0x26, 0x11, 0x69, 0xb6, 0x01, 0x00, 0x24, 0x15, 0x03, 0x18, 0x24,
979            0x07, 0x01, 0x24, 0x08, 0x01, 0x30, 0x09, 0x41, 0x04, 0x93, 0x04, 0xc6, 0xc4, 0xe1,
980            0xbc, 0x9a, 0xc8, 0xf5, 0xb3, 0x7f, 0x83, 0xd6, 0x7f, 0x79, 0xc5, 0x35, 0xdc, 0x7f,
981            0xac, 0x87, 0xca, 0xcd, 0x08, 0x80, 0x4a, 0x55, 0x60, 0x80, 0x09, 0xd3, 0x9b, 0x4a,
982            0xc8, 0xe7, 0x7b, 0x4d, 0x5c, 0x82, 0x88, 0x24, 0xdf, 0x1c, 0xfd, 0xef, 0xb4, 0xbc,
983            0xb7, 0x2f, 0x36, 0xf7, 0x2b, 0xb2, 0xcc, 0x14, 0x69, 0x63, 0xcc, 0x89, 0xd2, 0x74,
984            0x3f, 0xd1, 0x98, 0x37, 0x0a, 0x35, 0x01, 0x28, 0x01, 0x18, 0x24, 0x02, 0x01, 0x36,
985            0x03, 0x04, 0x02, 0x04, 0x01, 0x18, 0x30, 0x04, 0x14, 0x9c, 0xe7, 0xd9, 0xa8, 0x6b,
986            0xf8, 0x71, 0xfa, 0x08, 0x10, 0xa3, 0xf2, 0x3a, 0x95, 0x30, 0xb1, 0x9e, 0xae, 0xc4,
987            0x2c, 0x30, 0x05, 0x14, 0xdf, 0xfb, 0x79, 0xf1, 0x2b, 0xbf, 0x68, 0x18, 0x59, 0x7f,
988            0xf7, 0xe8, 0xaf, 0x88, 0x91, 0x1c, 0x72, 0x32, 0xf7, 0x52, 0x18, 0x30, 0x0b, 0x40,
989            0xcf, 0x01, 0x37, 0x65, 0xd6, 0x8a, 0xca, 0xd8, 0x33, 0x9f, 0x0f, 0x4f, 0xd5, 0xed,
990            0x48, 0x42, 0x91, 0xca, 0xab, 0xf7, 0xae, 0xe1, 0x3b, 0x2b, 0xef, 0x9f, 0x43, 0x5a,
991            0x96, 0xe0, 0xa5, 0x38, 0x8e, 0x39, 0xd0, 0x20, 0x8a, 0x0c, 0x92, 0x2b, 0x21, 0x7d,
992            0xf5, 0x6c, 0x1d, 0x65, 0x6c, 0x0f, 0xd1, 0xe8, 0x55, 0x14, 0x5e, 0x27, 0xfd, 0xa4,
993            0xac, 0xf9, 0x93, 0xdb, 0x29, 0x49, 0xaa, 0x71, 0x18,
994        ];
995        pub const CHIP_CERT_TXT_IN_DN: [u8; 304] = [
996            0x15, 0x30, 0x1, 0x1, 0x1, 0x24, 0x2, 0x1, 0x37, 0x3, 0x2c, 0x84, 0x2, 0x55, 0x53,
997            0x2c, 0x7, 0x6, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2c, 0x1, 0xb, 0x4d, 0x61, 0x74,
998            0x74, 0x65, 0x72, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x27, 0x14, 0x1, 0x0, 0x0, 0x0, 0xfe,
999            0xff, 0xff, 0xff, 0x18, 0x26, 0x4, 0x7f, 0xd2, 0x43, 0x29, 0x26, 0x5, 0x7f, 0x94, 0x5b,
1000            0xe5, 0x37, 0x6, 0x2c, 0x84, 0x2, 0x55, 0x53, 0x2c, 0x7, 0x6, 0x47, 0x6f, 0x6f, 0x67,
1001            0x6c, 0x65, 0x2c, 0x1, 0xb, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x52, 0x6f, 0x6f,
1002            0x74, 0x27, 0x14, 0x1, 0x0, 0x0, 0x0, 0xfe, 0xff, 0xff, 0xff, 0x18, 0x24, 0x7, 0x1,
1003            0x24, 0x8, 0x1, 0x30, 0x9, 0x41, 0x4, 0x5b, 0x37, 0xdf, 0x65, 0x49, 0xc2, 0xd, 0xc8,
1004            0xd7, 0x22, 0xa6, 0xb8, 0xac, 0xb6, 0x60, 0xa8, 0xa7, 0x64, 0xce, 0x7b, 0xaf, 0x6c,
1005            0x6c, 0x22, 0x4f, 0x7e, 0xe8, 0x43, 0x49, 0x68, 0x4a, 0xd7, 0xd8, 0x9, 0xff, 0x65, 0x0,
1006            0x33, 0xd1, 0x52, 0x7d, 0xcf, 0x1f, 0xba, 0xac, 0x6a, 0x9c, 0x3a, 0xd8, 0xb4, 0x1e,
1007            0xda, 0xc9, 0x9, 0xf7, 0xb5, 0xc7, 0x60, 0xfd, 0x54, 0x2c, 0x89, 0x23, 0x75, 0x37, 0xa,
1008            0x35, 0x1, 0x29, 0x1, 0x24, 0x2, 0x1, 0x18, 0x24, 0x2, 0x60, 0x30, 0x4, 0x14, 0x72,
1009            0xc2, 0x1, 0xf7, 0x57, 0x19, 0x13, 0xb3, 0x48, 0xca, 0x0, 0xca, 0x7b, 0x45, 0xf4, 0x77,
1010            0x46, 0x68, 0xc9, 0x7e, 0x30, 0x5, 0x14, 0x72, 0xc2, 0x1, 0xf7, 0x57, 0x19, 0x13, 0xb3,
1011            0x48, 0xca, 0x0, 0xca, 0x7b, 0x45, 0xf4, 0x77, 0x46, 0x68, 0xc9, 0x7e, 0x18, 0x30, 0xb,
1012            0x40, 0x65, 0x16, 0x4b, 0x16, 0x6a, 0xdf, 0xf1, 0x8c, 0x15, 0x61, 0xa, 0x8c, 0xe9,
1013            0x1b, 0xd7, 0x3, 0xe9, 0xc1, 0xf6, 0x77, 0xb7, 0x11, 0xce, 0x13, 0x35, 0x5, 0x15, 0x2d,
1014            0xf0, 0xda, 0x15, 0x11, 0x16, 0x75, 0xac, 0x55, 0x91, 0xce, 0xe7, 0x86, 0x85, 0x1c,
1015            0xdd, 0x9e, 0xfd, 0xad, 0x29, 0x66, 0x74, 0xbe, 0xbc, 0xb2, 0xa3, 0xa3, 0x20, 0x9b,
1016            0xcd, 0xe7, 0xb3, 0x9, 0xdb, 0x55, 0x2c, 0x6f, 0x18,
1017        ];
1018
1019        pub const ASN1_OUTPUT1: [u8; 388] = [
1020            0x30, 0x82, 0x01, 0x80, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x00, 0x30, 0x0a,
1021            0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x44, 0x31, 0x20,
1022            0x30, 0x1e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x01, 0x04,
1023            0x0c, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1024            0x30, 0x30, 0x30, 0x30, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
1025            0x01, 0x82, 0xa2, 0x7c, 0x01, 0x05, 0x0c, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1026            0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x30, 0x1e, 0x17, 0x0d,
1027            0x32, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17,
1028            0x0d, 0x33, 0x30, 0x31, 0x32, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a,
1029            0x30, 0x44, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82,
1030            0xa2, 0x7c, 0x01, 0x03, 0x0c, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1031            0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a,
1032            0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x01, 0x05, 0x0c, 0x10, 0x30, 0x30,
1033            0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33,
1034            0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
1035            0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x69,
1036            0xda, 0xe9, 0x42, 0x88, 0xcf, 0x64, 0x94, 0x2d, 0xd5, 0x0a, 0x74, 0x2d, 0x50, 0xe8,
1037            0x5e, 0xbe, 0x15, 0x53, 0x24, 0xe5, 0xc5, 0x6b, 0xe5, 0x7f, 0xc1, 0x41, 0x11, 0x21,
1038            0xdd, 0x46, 0xa3, 0x0d, 0x63, 0xc3, 0xe3, 0x90, 0x7a, 0x69, 0x64, 0xdd, 0x66, 0x78,
1039            0x10, 0xa6, 0xc8, 0x0f, 0xfd, 0xb6, 0xf2, 0x9b, 0x88, 0x50, 0x93, 0x77, 0x9e, 0xf7,
1040            0xb4, 0xda, 0x94, 0x11, 0x33, 0x1e, 0xfe, 0xa3, 0x63, 0x30, 0x61, 0x30, 0x0f, 0x06,
1041            0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff,
1042            0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
1043            0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xdf,
1044            0xfb, 0x79, 0xf1, 0x2b, 0xbf, 0x68, 0x18, 0x59, 0x7f, 0xf7, 0xe8, 0xaf, 0x88, 0x91,
1045            0x1c, 0x72, 0x32, 0xf7, 0x52, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
1046            0x30, 0x16, 0x80, 0x14, 0xed, 0x31, 0x5e, 0x1a, 0xb7, 0xb9, 0x7a, 0xca, 0x04, 0x79,
1047            0x5d, 0x82, 0x57, 0x7a, 0xd7, 0x0a, 0x75, 0xd0, 0xdb, 0x7a,
1048        ];
1049        pub const ASN1_OUTPUT2: [u8; 421] = [
1050            0x30, 0x82, 0x01, 0xa1, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x30, 0x0a,
1051            0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x44, 0x31, 0x20,
1052            0x30, 0x1e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x01, 0x03,
1053            0x0c, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1054            0x30, 0x30, 0x30, 0x31, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
1055            0x01, 0x82, 0xa2, 0x7c, 0x01, 0x05, 0x0c, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1056            0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x30, 0x1e, 0x17, 0x0d,
1057            0x32, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17,
1058            0x0d, 0x33, 0x30, 0x31, 0x32, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a,
1059            0x30, 0x44, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82,
1060            0xa2, 0x7c, 0x01, 0x01, 0x0c, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1061            0x30, 0x30, 0x30, 0x31, 0x42, 0x36, 0x36, 0x39, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a,
1062            0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x01, 0x05, 0x0c, 0x10, 0x30, 0x30,
1063            0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33,
1064            0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
1065            0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x93,
1066            0x04, 0xc6, 0xc4, 0xe1, 0xbc, 0x9a, 0xc8, 0xf5, 0xb3, 0x7f, 0x83, 0xd6, 0x7f, 0x79,
1067            0xc5, 0x35, 0xdc, 0x7f, 0xac, 0x87, 0xca, 0xcd, 0x08, 0x80, 0x4a, 0x55, 0x60, 0x80,
1068            0x09, 0xd3, 0x9b, 0x4a, 0xc8, 0xe7, 0x7b, 0x4d, 0x5c, 0x82, 0x88, 0x24, 0xdf, 0x1c,
1069            0xfd, 0xef, 0xb4, 0xbc, 0xb7, 0x2f, 0x36, 0xf7, 0x2b, 0xb2, 0xcc, 0x14, 0x69, 0x63,
1070            0xcc, 0x89, 0xd2, 0x74, 0x3f, 0xd1, 0x98, 0xa3, 0x81, 0x83, 0x30, 0x81, 0x80, 0x30,
1071            0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30,
1072            0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07,
1073            0x80, 0x30, 0x20, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01, 0xff, 0x04, 0x16, 0x30,
1074            0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b,
1075            0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
1076            0x04, 0x16, 0x04, 0x14, 0x9c, 0xe7, 0xd9, 0xa8, 0x6b, 0xf8, 0x71, 0xfa, 0x08, 0x10,
1077            0xa3, 0xf2, 0x3a, 0x95, 0x30, 0xb1, 0x9e, 0xae, 0xc4, 0x2c, 0x30, 0x1f, 0x06, 0x03,
1078            0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xdf, 0xfb, 0x79, 0xf1, 0x2b,
1079            0xbf, 0x68, 0x18, 0x59, 0x7f, 0xf7, 0xe8, 0xaf, 0x88, 0x91, 0x1c, 0x72, 0x32, 0xf7,
1080            0x52,
1081        ];
1082        pub const ASN1_OUTPUT_TXT_IN_DN: [u8; 429] = [
1083            0x30, 0x82, 0x01, 0xa9, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x30, 0x0a,
1084            0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x56, 0x31, 0x0b,
1085            0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0f, 0x30,
1086            0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
1087            0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0b, 0x4d, 0x61, 0x74,
1088            0x74, 0x65, 0x72, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a,
1089            0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x01, 0x04, 0x0c, 0x10, 0x46, 0x46,
1090            0x46, 0x46, 0x46, 0x46, 0x46, 0x45, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31,
1091            0x30, 0x20, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x32, 0x30, 0x38, 0x32, 0x30, 0x33, 0x30,
1092            0x35, 0x35, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x32, 0x31, 0x31, 0x32, 0x30, 0x38, 0x32,
1093            0x30, 0x33, 0x30, 0x35, 0x35, 0x5a, 0x30, 0x56, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
1094            0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55,
1095            0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x14, 0x30, 0x12,
1096            0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0b, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20,
1097            0x52, 0x6f, 0x6f, 0x74, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
1098            0x01, 0x82, 0xa2, 0x7c, 0x01, 0x04, 0x0c, 0x10, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46,
1099            0x46, 0x45, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x59, 0x30, 0x13,
1100            0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
1101            0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x5b, 0x37, 0xdf, 0x65, 0x49,
1102            0xc2, 0x0d, 0xc8, 0xd7, 0x22, 0xa6, 0xb8, 0xac, 0xb6, 0x60, 0xa8, 0xa7, 0x64, 0xce,
1103            0x7b, 0xaf, 0x6c, 0x6c, 0x22, 0x4f, 0x7e, 0xe8, 0x43, 0x49, 0x68, 0x4a, 0xd7, 0xd8,
1104            0x09, 0xff, 0x65, 0x00, 0x33, 0xd1, 0x52, 0x7d, 0xcf, 0x1f, 0xba, 0xac, 0x6a, 0x9c,
1105            0x3a, 0xd8, 0xb4, 0x1e, 0xda, 0xc9, 0x09, 0xf7, 0xb5, 0xc7, 0x60, 0xfd, 0x54, 0x2c,
1106            0x89, 0x23, 0x75, 0xa3, 0x66, 0x30, 0x64, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
1107            0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01, 0x30,
1108            0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
1109            0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x72, 0xc2,
1110            0x01, 0xf7, 0x57, 0x19, 0x13, 0xb3, 0x48, 0xca, 0x00, 0xca, 0x7b, 0x45, 0xf4, 0x77,
1111            0x46, 0x68, 0xc9, 0x7e, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
1112            0x16, 0x80, 0x14, 0x72, 0xc2, 0x01, 0xf7, 0x57, 0x19, 0x13, 0xb3, 0x48, 0xca, 0x00,
1113            0xca, 0x7b, 0x45, 0xf4, 0x77, 0x46, 0x68, 0xc9, 0x7e,
1114        ];
1115    }
1116}