1use 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; const 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 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 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 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 error!("Invalid DN, too high {}", id);
491 }
492 } else {
493 error!("Non Matter DNs are not yet supported {}", id);
495 }
496 }
497 w.end_seq()?;
498 Ok(())
499 }
500}
501
502#[derive(Copy, Clone)]
503enum 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 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
564impl<'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 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 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 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 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 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 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 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}