xwt_cert_fingerprint/
lib.rs

1//! Fingerprint computation.
2
3#![no_std]
4
5#[cfg(feature = "alloc")]
6extern crate alloc;
7
8/// RFC 7469 fingerprint.
9///
10/// Only available if the `alloc` feature is active.
11#[cfg(feature = "alloc")]
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct Rfc7469;
14
15#[cfg(feature = "alloc")]
16impl Rfc7469 {
17    /// Compute RFC 7469 signature for the [`x509_cert::Certificate`].
18    #[cfg(feature = "x509-cert")]
19    pub fn compute_x509_cert(cert: &x509_cert::Certificate) -> alloc::string::String {
20        Self::compute_for_public_key_bytes(
21            cert.tbs_certificate
22                .subject_public_key_info
23                .subject_public_key
24                .raw_bytes(),
25        )
26    }
27
28    /// Compute RFC 7469 signature for the [`rustls_pki_types::TrustAnchor`].
29    #[cfg(feature = "rustls-pki-types")]
30    pub fn compute_rustls_pki_types(
31        cert: &rustls_pki_types::TrustAnchor<'_>,
32    ) -> alloc::string::String {
33        Self::compute_for_public_key_bytes(&cert.subject_public_key_info)
34    }
35
36    /// Compute RFC 7469 signature for the given public key bytes.
37    pub fn compute_for_public_key_bytes(public_key_bytes: &[u8]) -> alloc::string::String {
38        use base64::Engine;
39        use sha2::Digest;
40
41        let digest = sha2::Sha256::digest(public_key_bytes);
42        base64::engine::general_purpose::STANDARD.encode(digest)
43    }
44}
45
46/// Alloc-free writer of a der encoded data into the digest.
47#[cfg(feature = "x509-cert")]
48struct DigestDerWriter<T>(pub T);
49
50#[cfg(feature = "x509-cert")]
51impl<T> x509_cert::der::Writer for DigestDerWriter<T>
52where
53    T: sha2::Digest,
54{
55    fn write(&mut self, slice: &[u8]) -> x509_cert::der::Result<()> {
56        self.0.update(slice);
57        Ok(())
58    }
59}
60
61/// A SHA-256 fingerprint of the certificate.
62///
63/// Note for anyone who wants to verify the output, it should be similar to
64/// the output for the following command:
65/// ```shell
66/// openssl x509 -noout -fingerprint -sha256 -in certificate.pem
67/// ```
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub struct Sha256([u8; 32]);
70
71/// A reference to a SHA-256 fingerprint of the certificate.
72///
73/// Note for anyone who wants to verify the output, it should be similar to
74/// the output for the following command:
75/// ```shell
76/// openssl x509 -noout -fingerprint -sha256 -in certificate.pem
77/// ```
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub struct Sha256Ref<'a>(&'a [u8; 32]);
80
81impl Sha256 {
82    /// Compute the SHA-256 fingerprint of the given [`x509_cert::Certificate`].
83    #[cfg(feature = "x509-cert")]
84    pub fn compute_x509_cert(cert: &x509_cert::Certificate) -> Self {
85        use sha2::Digest;
86        use x509_cert::der::Encode;
87
88        let digest = sha2::Sha256::new();
89        let mut digest = DigestDerWriter(digest);
90        cert.encode(&mut digest).unwrap();
91        let DigestDerWriter(digest) = digest;
92        Self::from_fingerprint_bytes(digest.finalize().into())
93    }
94
95    /// Compute the SHA-256 fingerprint of the given
96    /// [`rustls_pki_types::CertificateDer`].
97    #[cfg(feature = "rustls-pki-types")]
98    pub fn compute_rustls_pki_types(cert: rustls_pki_types::CertificateDer<'_>) -> Self {
99        Self::compute_for_der(&cert)
100    }
101
102    /// Compute the SHA-256 fingerprint of the certificate data encoded
103    /// in the DER format.
104    pub fn compute_for_der(cert: &[u8]) -> Self {
105        use sha2::Digest;
106        Self::from_fingerprint_bytes(sha2::Sha256::digest(cert).into())
107    }
108
109    /// Create a [`Sha256`] fingerprint object from the precomputed fingerprint
110    /// bytes.
111    /// Might be useful to someone who want to use the display implementation.
112    pub const fn from_fingerprint_bytes(bytes: [u8; 32]) -> Self {
113        Self(bytes)
114    }
115
116    /// Return the 32-byte array of the signature data.
117    pub const fn into_inner(self) -> [u8; 32] {
118        self.0
119    }
120
121    /// Return the reference to this signature.
122    pub const fn as_ref(&self) -> Sha256Ref<'_> {
123        Sha256Ref(&self.0)
124    }
125}
126
127impl<'a> Sha256Ref<'a> {
128    /// Create a [`Sha256Ref`] fingerprint object from the precomputed
129    /// fingerprint bytes.
130    /// Might be useful to someone who want to use the display implementation.
131    pub const fn from_fingerprint_bytes(bytes: &'a [u8; 32]) -> Self {
132        Self(bytes)
133    }
134
135    /// Return a ref to the 32-byte array of the signature data.
136    pub const fn into_inner(self) -> &'a [u8; 32] {
137        self.0
138    }
139}
140
141impl core::fmt::Display for Sha256 {
142    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
143        self.as_ref().fmt(f)
144    }
145}
146
147impl core::fmt::Display for Sha256Ref<'_> {
148    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
149        let mut first = true;
150        for byte in self.0 {
151            if first {
152                first = false;
153            } else {
154                f.write_str(":")?;
155            }
156            write!(f, "{:02X}", byte)?;
157        }
158        Ok(())
159    }
160}