Skip to main content
This is unreleased documentation for the main (development) branch of crypto-glue.

crypto_glue/x509/
display.rs

1use crate::x509::Certificate;
2use const_oid::AssociatedOid;
3use std::fmt;
4use x509_cert::der::referenced::OwnedToRef;
5use x509_cert::der::Decode;
6use x509_cert::ext::pkix::name::GeneralName;
7use x509_cert::ext::pkix::AuthorityKeyIdentifier;
8use x509_cert::ext::pkix::BasicConstraints;
9use x509_cert::ext::pkix::ExtendedKeyUsage;
10use x509_cert::ext::pkix::KeyUsage;
11use x509_cert::ext::pkix::SubjectAltName;
12use x509_cert::ext::pkix::SubjectKeyIdentifier;
13use x509_cert::spki::SubjectPublicKeyInfoRef;
14
15const INDENT: usize = 2;
16const INDENT_TWO: usize = INDENT * 2;
17const INDENT_THREE: usize = INDENT * 3;
18const INDENT_FOUR: usize = INDENT * 4;
19
20pub struct X509Display<'a> {
21    cert: &'a Certificate,
22}
23
24impl<'a> From<&'a Certificate> for X509Display<'a> {
25    fn from(cert: &'a Certificate) -> Self {
26        X509Display { cert }
27    }
28}
29
30impl fmt::Display for X509Display<'_> {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        writeln!(f, "--")?;
33        writeln!(f, "Certificate:")?;
34
35        // cert.tbs_certificate
36        writeln!(f, "{:indent$}Data:", "", indent = INDENT)?;
37
38        writeln!(
39            f,
40            "{:indent$}Version: {:?}",
41            "",
42            self.cert.tbs_certificate.version,
43            indent = INDENT_TWO,
44        )?;
45
46        writeln!(f, "{:indent$}Serial:", "", indent = INDENT_TWO)?;
47        writeln!(
48            f,
49            "{}",
50            BytesDisplay {
51                bytes: self.cert.tbs_certificate.serial_number.as_bytes(),
52                indent: INDENT_THREE
53            }
54        )?;
55
56        writeln!(
57            f,
58            "{:indent$}Subject: {}",
59            "",
60            self.cert.tbs_certificate.subject,
61            indent = INDENT_TWO
62        )?;
63
64        writeln!(
65            f,
66            "{:indent$}Issuer: {}",
67            "",
68            self.cert.tbs_certificate.issuer,
69            indent = INDENT_TWO
70        )?;
71
72        if let Some(issuer_unique_id) = &self.cert.tbs_certificate.issuer_unique_id {
73            writeln!(f, "{:indent$}Issuer Unique ID:", "", indent = INDENT_TWO)?;
74            if let Some(bytes) = issuer_unique_id.as_bytes() {
75                writeln!(
76                    f,
77                    "{}",
78                    BytesDisplay {
79                        bytes,
80                        indent: INDENT_THREE
81                    }
82                )?;
83            } else {
84                writeln!(f, "{:indent$}INVALID", "", indent = INDENT_THREE)?;
85            }
86        }
87
88        if let Some(subject_unique_id) = &self.cert.tbs_certificate.subject_unique_id {
89            writeln!(f, "{:indent$}Subject Unique ID:", "", indent = INDENT_TWO)?;
90            if let Some(bytes) = subject_unique_id.as_bytes() {
91                writeln!(
92                    f,
93                    "{}",
94                    BytesDisplay {
95                        bytes,
96                        indent: INDENT_THREE
97                    }
98                )?;
99            } else {
100                writeln!(f, "{:indent$}INVALID", "", indent = INDENT_THREE)?;
101            }
102        }
103
104        writeln!(f, "{:indent$}Validity", "", indent = INDENT_TWO)?;
105        writeln!(
106            f,
107            "{:indent$}Not Before: {}",
108            "",
109            self.cert.tbs_certificate.validity.not_before,
110            indent = INDENT_THREE
111        )?;
112        writeln!(
113            f,
114            "{:indent$}Not After: {}",
115            "",
116            self.cert.tbs_certificate.validity.not_after,
117            indent = INDENT_THREE
118        )?;
119
120        write!(
121            f,
122            "{:indent$}Signature Algorithm: ",
123            "",
124            indent = INDENT_TWO
125        )?;
126        match &self.cert.tbs_certificate.signature.oid {
127            &const_oid::db::rfc5912::ECDSA_WITH_SHA_256 => writeln!(f, "ecdsa-with-sha256")?,
128            &const_oid::db::rfc5912::ECDSA_WITH_SHA_384 => writeln!(f, "ecdsa-with-sha384")?,
129            &const_oid::db::rfc5912::ECDSA_WITH_SHA_512 => writeln!(f, "ecdsa-with-sha512")?,
130            &const_oid::db::rfc5912::SHA_256_WITH_RSA_ENCRYPTION => {
131                writeln!(f, "sha256-with-rsa-encryption")?
132            }
133            oid => writeln!(f, "OID= {:?}", oid)?,
134        }
135
136        // SUBJECT PUBLIC KEY
137        writeln!(
138            f,
139            "{}",
140            SubjectPublicKeyDisplay {
141                spki: self
142                    .cert
143                    .tbs_certificate
144                    .subject_public_key_info
145                    .owned_to_ref(),
146                indent: INDENT_TWO
147            }
148        )?;
149
150        for extension in self
151            .cert
152            .tbs_certificate
153            .extensions
154            .iter()
155            .flat_map(|exts| exts.iter())
156        {
157            match extension.extn_id {
158                BasicConstraints::OID => {
159                    write!(f, "{:indent$}Basic Constraints:", "", indent = INDENT_TWO)?;
160                    if let Ok(bc) = BasicConstraints::from_der(extension.extn_value.as_bytes()) {
161                        if extension.critical {
162                            write!(f, " critical")?;
163                        }
164                        writeln!(f)?;
165
166                        writeln!(f, "{:indent$}CA: {}", "", bc.ca, indent = INDENT_THREE)?;
167                        if let Some(path_len_constraint) = bc.path_len_constraint {
168                            writeln!(
169                                f,
170                                "{:indent$}Path Length: {}",
171                                "",
172                                path_len_constraint,
173                                indent = INDENT_THREE
174                            )?;
175                        }
176                    } else {
177                        writeln!(f, "INVALID")?
178                    }
179                }
180                SubjectKeyIdentifier::OID => {
181                    write!(
182                        f,
183                        "{:indent$}Subject Key Identifier:",
184                        "",
185                        indent = INDENT_TWO
186                    )?;
187                    if let Ok(ski) = SubjectKeyIdentifier::from_der(extension.extn_value.as_bytes())
188                    {
189                        if extension.critical {
190                            write!(f, " critical")?;
191                        }
192                        writeln!(f)?;
193                        writeln!(
194                            f,
195                            "{}",
196                            BytesDisplay {
197                                bytes: ski.as_ref().as_bytes(),
198                                indent: INDENT_THREE
199                            }
200                        )?;
201                    } else {
202                        writeln!(f, "INVALID")?
203                    }
204                }
205                AuthorityKeyIdentifier::OID => {
206                    write!(
207                        f,
208                        "{:indent$}Authority Key Identifier:",
209                        "",
210                        indent = INDENT_TWO
211                    )?;
212                    if let Ok(aki) =
213                        AuthorityKeyIdentifier::from_der(extension.extn_value.as_bytes())
214                    {
215                        if extension.critical {
216                            write!(f, " critical")?;
217                        }
218                        writeln!(f)?;
219
220                        if let Some(bytes) = &aki.key_identifier {
221                            writeln!(
222                                f,
223                                "{}",
224                                BytesDisplay {
225                                    bytes: bytes.as_bytes(),
226                                    indent: INDENT_THREE
227                                }
228                            )?;
229                        }
230
231                        if let Some(aki_cert_issuer) = &aki.authority_cert_issuer {
232                            for name in aki_cert_issuer.iter() {
233                                writeln!(
234                                    f,
235                                    "{}",
236                                    GeneralNameDisplay {
237                                        name,
238                                        indent: INDENT_THREE
239                                    }
240                                )?;
241                            }
242                        }
243
244                        if let Some(bytes) = &aki.authority_cert_serial_number {
245                            writeln!(
246                                f,
247                                "{}",
248                                BytesDisplay {
249                                    bytes: bytes.as_bytes(),
250                                    indent: INDENT_THREE
251                                }
252                            )?;
253                        }
254                    } else {
255                        writeln!(f, "INVALID")?
256                    }
257                }
258                KeyUsage::OID => {
259                    write!(f, "{:indent$}Key Usage:", "", indent = INDENT_TWO)?;
260                    let ku = if let Ok(ku) = KeyUsage::from_der(extension.extn_value.as_bytes()) {
261                        if extension.critical {
262                            write!(f, " critical")?;
263                        }
264                        writeln!(f)?;
265                        ku
266                    } else {
267                        writeln!(f, "INVALID")?;
268                        continue;
269                    };
270
271                    if ku.digital_signature() {
272                        writeln!(f, "{:indent$}Digital Signature", "", indent = INDENT_THREE)?;
273                    }
274
275                    if ku.non_repudiation() {
276                        writeln!(f, "{:indent$}Non Repudiation", "", indent = INDENT_THREE)?;
277                    }
278
279                    if ku.key_encipherment() {
280                        writeln!(f, "{:indent$}Key Encipherment", "", indent = INDENT_THREE)?;
281                    }
282
283                    if ku.data_encipherment() {
284                        writeln!(f, "{:indent$}Data Encipherment", "", indent = INDENT_THREE)?;
285                    }
286
287                    if ku.key_agreement() {
288                        writeln!(f, "{:indent$}Key Agreement", "", indent = INDENT_THREE)?;
289                    }
290
291                    if ku.key_cert_sign() {
292                        writeln!(f, "{:indent$}Key Cert Sign", "", indent = INDENT_THREE)?;
293                    }
294
295                    if ku.crl_sign() {
296                        writeln!(f, "{:indent$}CRL Sign", "", indent = INDENT_THREE)?;
297                    }
298
299                    if ku.encipher_only() {
300                        writeln!(f, "{:indent$}Encipher Only", "", indent = INDENT_THREE)?;
301                    }
302
303                    if ku.decipher_only() {
304                        writeln!(f, "{:indent$}Decipher Only", "", indent = INDENT_THREE)?;
305                    }
306                }
307
308                ExtendedKeyUsage::OID => {
309                    write!(f, "{:indent$}Extended Key Usage:", "", indent = INDENT_TWO)?;
310                    let eku = if let Ok(eku) =
311                        ExtendedKeyUsage::from_der(extension.extn_value.as_bytes())
312                    {
313                        if extension.critical {
314                            write!(f, " critical")?;
315                        }
316                        writeln!(f)?;
317                        eku
318                    } else {
319                        writeln!(f, "INVALID")?;
320                        continue;
321                    };
322
323                    for oid in eku.as_ref().iter() {
324                        write!(f, "{:indent$}", "", indent = INDENT_THREE)?;
325                        match oid {
326                            &const_oid::db::rfc5280::ID_KP_SERVER_AUTH => {
327                                writeln!(f, "Server Authentication")?
328                            }
329                            &const_oid::db::rfc5280::ID_KP_CLIENT_AUTH => {
330                                writeln!(f, "Client Authentication")?
331                            }
332                            &const_oid::db::rfc5280::ID_KP_CODE_SIGNING => {
333                                writeln!(f, "Code Signing")?
334                            }
335                            oid => writeln!(f, "OID= {:?}", oid)?,
336                        }
337                    }
338                }
339                SubjectAltName::OID => {
340                    write!(f, "{:indent$}Subject Alt Name:", "", indent = INDENT_TWO)?;
341                    let san = if let Ok(san) =
342                        SubjectAltName::from_der(extension.extn_value.as_bytes())
343                    {
344                        if extension.critical {
345                            write!(f, " critical")?;
346                        }
347                        writeln!(f)?;
348                        san
349                    } else {
350                        writeln!(f, "INVALID")?;
351                        continue;
352                    };
353
354                    for name in san.0.iter() {
355                        writeln!(
356                            f,
357                            "{}",
358                            GeneralNameDisplay {
359                                name,
360                                indent: INDENT_THREE
361                            }
362                        )?;
363                    }
364                }
365
366                oid => {
367                    // Probably in RFC5280
368                    writeln!(
369                        f,
370                        "{:indent$}Unknown Extension: OID= {:?}",
371                        "",
372                        oid,
373                        indent = INDENT_TWO
374                    )?
375                }
376            }
377        }
378
379        write!(f, "{:indent$}Signature Algorithm: ", "", indent = INDENT)?;
380        match &self.cert.signature_algorithm.oid {
381            &const_oid::db::rfc5912::ECDSA_WITH_SHA_256 => writeln!(f, "ecdsa-with-sha256")?,
382            &const_oid::db::rfc5912::ECDSA_WITH_SHA_384 => writeln!(f, "ecdsa-with-sha384")?,
383            &const_oid::db::rfc5912::ECDSA_WITH_SHA_512 => writeln!(f, "ecdsa-with-sha512")?,
384            &const_oid::db::rfc5912::SHA_256_WITH_RSA_ENCRYPTION => {
385                writeln!(f, "sha256-with-rsa-encryption")?
386            }
387            oid => writeln!(f, "OID= {:?}", oid)?,
388        }
389
390        writeln!(f, "{:indent$}Signature:", "", indent = INDENT)?;
391        if let Some(bytes) = self.cert.signature.as_bytes() {
392            writeln!(
393                f,
394                "{}",
395                BytesDisplay {
396                    bytes,
397                    indent: INDENT_TWO
398                }
399            )?;
400        } else {
401            writeln!(f, "{:indent$}INVALID", "", indent = INDENT_TWO)?;
402        }
403
404        Ok(())
405    }
406}
407
408struct BytesDisplay<'a> {
409    bytes: &'a [u8],
410    indent: usize,
411}
412
413impl fmt::Display for BytesDisplay<'_> {
414    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
415        write!(f, "{:indent$}", "", indent = self.indent)?;
416
417        let mut iter = self.bytes.iter().peekable();
418
419        let mut count = 0;
420        while let Some(byte) = iter.next() {
421            write!(f, "{:02x}", byte)?;
422
423            count += 1;
424
425            if count % 18 == 0 {
426                write!(f, "\n{:indent$}", "", indent = self.indent)?;
427            } else if iter.peek().is_some() {
428                write!(f, ":")?;
429            }
430        }
431
432        Ok(())
433    }
434}
435
436struct GeneralNameDisplay<'a> {
437    name: &'a GeneralName,
438    indent: usize,
439}
440
441impl fmt::Display for GeneralNameDisplay<'_> {
442    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443        write!(f, "{:indent$}", "", indent = self.indent)?;
444        match self.name {
445            GeneralName::OtherName(_othername) => {
446                write!(f, "Other: TODO")
447            }
448            GeneralName::Rfc822Name(ia5str) => {
449                write!(f, "Email: {ia5str}")
450            }
451            GeneralName::DnsName(ia5str) => {
452                write!(f, "DNS: {ia5str}")
453            }
454            GeneralName::DirectoryName(name) => {
455                write!(f, "DN: {name}")
456            }
457            GeneralName::EdiPartyName(_edi_party_name) => {
458                write!(f, "EdiParty: TODO")
459            }
460            GeneralName::UniformResourceIdentifier(ia5str) => {
461                write!(f, "URI: {ia5str}")
462            }
463            GeneralName::IpAddress(_octet_str) => {
464                write!(f, "IpAddress: TODO")
465            }
466            GeneralName::RegisteredId(oid) => {
467                write!(f, "OID: {oid}")
468            }
469        }
470    }
471}
472
473pub struct SubjectPublicKeyDisplay<'a> {
474    spki: SubjectPublicKeyInfoRef<'a>,
475    indent: usize,
476}
477
478impl<'a> From<SubjectPublicKeyInfoRef<'a>> for SubjectPublicKeyDisplay<'a> {
479    fn from(spki: SubjectPublicKeyInfoRef<'a>) -> Self {
480        Self { spki, indent: 0 }
481    }
482}
483
484impl fmt::Display for SubjectPublicKeyDisplay<'_> {
485    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
486        writeln!(
487            f,
488            "{:indent$}Subject Public Key Info:",
489            "",
490            indent = self.indent
491        )?;
492
493        let indent = self.indent + INDENT;
494
495        write!(f, "{:indent$}Algorithm: ", "", indent = indent)?;
496
497        let alg_oid = &self.spki.algorithm.oid;
498
499        match alg_oid {
500            &const_oid::db::rfc5912::ID_EC_PUBLIC_KEY => writeln!(f, "id-ec-public-key")?,
501            oid => writeln!(f, "OID= {:?}", oid)?,
502        }
503
504        if let Ok(param_oid) = self.spki.algorithm.parameters_oid() {
505            write!(f, "{:indent$}Parameters: ", "", indent = indent)?;
506            match param_oid {
507                const_oid::db::rfc5912::SECP_384_R_1 => writeln!(f, "SEC P384 R1")?,
508                const_oid::db::rfc5912::SECP_256_R_1 => writeln!(f, "SEC P256 R1")?,
509                oid => writeln!(f, "OID= {:?}", oid)?,
510            }
511        }
512
513        writeln!(f, "{:indent$}Public Key:", "", indent = indent)?;
514        let indent = self.indent + INDENT_TWO;
515
516        if let Some(sk_bytes) = self.spki.subject_public_key.as_bytes() {
517            write!(
518                f,
519                "{}",
520                BytesDisplay {
521                    bytes: sk_bytes,
522                    indent,
523                }
524            )?;
525        } else {
526            write!(f, "{:indent$}INVALID", "", indent = indent)?;
527        };
528
529        Ok(())
530    }
531}