1use std::{
2 fmt::{self, Display, Formatter},
3 str::FromStr,
4};
5
6use openssl::{hash::MessageDigest, x509::X509Ref};
7
8use crate::Error;
9
10#[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#[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 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 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#[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#[derive(Clone, Eq, PartialEq)]
124pub struct CertificateFingerprint {
125 hash_function: HashFunction,
126 fingerprint: Vec<u8>,
127}
128
129impl CertificateFingerprint {
130 #[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 #[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}