msf_srtp/
fingerprint.rs

1use std::{
2    fmt::{self, Display, Formatter},
3    str::FromStr,
4};
5
6use openssl::{hash::MessageDigest, x509::X509Ref};
7
8use crate::Error;
9
10/// Unknown hash function.
11#[derive(Debug, Copy, Clone)]
12pub struct UnknownHashFunction;
13
14impl Display for UnknownHashFunction {
15    #[inline]
16    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
17        f.write_str("unknown hash function")
18    }
19}
20
21impl std::error::Error for UnknownHashFunction {}
22
23/// Hash function.
24#[derive(Debug, Copy, Clone, Eq, PartialEq)]
25pub enum HashFunction {
26    Md5,
27    Sha1,
28    Sha224,
29    Sha256,
30    Sha384,
31    Sha512,
32}
33
34impl HashFunction {
35    /// Get the OpenSSL message digest.
36    pub(crate) fn into_message_digest(self) -> MessageDigest {
37        match self {
38            Self::Md5 => MessageDigest::md5(),
39            Self::Sha1 => MessageDigest::sha1(),
40            Self::Sha224 => MessageDigest::sha224(),
41            Self::Sha256 => MessageDigest::sha256(),
42            Self::Sha384 => MessageDigest::sha384(),
43            Self::Sha512 => MessageDigest::sha512(),
44        }
45    }
46
47    /// Get size of the resulting hash in bits.
48    pub(crate) fn hash_size(self) -> usize {
49        match self {
50            Self::Md5 => 128,
51            Self::Sha1 => 160,
52            Self::Sha224 => 224,
53            Self::Sha256 => 256,
54            Self::Sha384 => 384,
55            Self::Sha512 => 512,
56        }
57    }
58}
59
60impl Display for HashFunction {
61    #[inline]
62    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
63        let s = match self {
64            Self::Md5 => "md5",
65            Self::Sha1 => "sha-1",
66            Self::Sha224 => "sha-224",
67            Self::Sha256 => "sha-256",
68            Self::Sha384 => "sha-384",
69            Self::Sha512 => "sha-512",
70        };
71
72        f.write_str(s)
73    }
74}
75
76impl FromStr for HashFunction {
77    type Err = UnknownHashFunction;
78
79    fn from_str(s: &str) -> Result<Self, Self::Err> {
80        let res = match s {
81            "md5" => Self::Md5,
82            "sha-1" => Self::Sha1,
83            "sha-224" => Self::Sha224,
84            "sha-256" => Self::Sha256,
85            "sha-384" => Self::Sha384,
86            "sha-512" => Self::Sha512,
87            _ => return Err(UnknownHashFunction),
88        };
89
90        Ok(res)
91    }
92}
93
94/// Invalid fingerprint.
95#[derive(Debug, Copy, Clone)]
96pub enum InvalidFingerprint {
97    UnknownHashFunction,
98    InvalidData,
99}
100
101impl Display for InvalidFingerprint {
102    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
103        match self {
104            Self::UnknownHashFunction => Display::fmt(&UnknownHashFunction, f),
105            Self::InvalidData => f.write_str("invalid data"),
106        }
107    }
108}
109
110impl std::error::Error for InvalidFingerprint {}
111
112impl From<UnknownHashFunction> for InvalidFingerprint {
113    #[inline]
114    fn from(_: UnknownHashFunction) -> Self {
115        Self::UnknownHashFunction
116    }
117}
118
119/// Certificate fingerprint.
120///
121/// The fingerprint can be formatted/parsed to/from an uppercase hex string
122/// prefixed with the name of the hash function.
123#[derive(Clone, Eq, PartialEq)]
124pub struct CertificateFingerprint {
125    hash_function: HashFunction,
126    fingerprint: Vec<u8>,
127}
128
129impl CertificateFingerprint {
130    /// Create fingerprint of a given certificate.
131    #[inline]
132    pub fn new(cert: &X509Ref, hash_function: HashFunction) -> Result<Self, Error> {
133        let digest = cert.digest(hash_function.into_message_digest())?;
134
135        let res = Self {
136            hash_function,
137            fingerprint: digest.to_vec(),
138        };
139
140        Ok(res)
141    }
142
143    /// Verify that this fingerprint matches a given certificate.
144    #[inline]
145    pub fn verify(&self, cert: &X509Ref) -> Result<bool, Error> {
146        let other = Self::new(cert, self.hash_function)?;
147
148        Ok(self == &other)
149    }
150}
151
152impl Display for CertificateFingerprint {
153    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
154        Display::fmt(&self.hash_function, f)?;
155
156        let mut bytes = self.fingerprint.iter();
157
158        if let Some(b) = bytes.next() {
159            write!(f, " {b:02X}")?;
160        }
161
162        for b in bytes {
163            write!(f, ":{b:02X}")?;
164        }
165
166        Ok(())
167    }
168}
169
170impl FromStr for CertificateFingerprint {
171    type Err = InvalidFingerprint;
172
173    fn from_str(s: &str) -> Result<Self, Self::Err> {
174        let s = s.trim();
175
176        if let Some(space) = s.find(' ') {
177            let (hash_function, rest) = s.split_at(space);
178
179            let digest = rest.trim();
180
181            let hash_function = HashFunction::from_str(hash_function)?;
182
183            let hash_size = hash_function.hash_size() >> 3;
184
185            let mut fingerprint = Vec::with_capacity(hash_size);
186
187            for byte in digest.split(':') {
188                let byte =
189                    u8::from_str_radix(byte, 16).map_err(|_| InvalidFingerprint::InvalidData)?;
190
191                fingerprint.push(byte);
192            }
193
194            if fingerprint.len() != hash_size {
195                return Err(InvalidFingerprint::InvalidData);
196            }
197
198            let res = Self {
199                hash_function,
200                fingerprint,
201            };
202
203            Ok(res)
204        } else {
205            Err(InvalidFingerprint::InvalidData)
206        }
207    }
208}