use std::fmt::Display;
use aws_lc_rs::digest::{SHA256, digest};
use x509_parser::prelude::X509Certificate;
use crate::{cached_string_repr::CachedStringRepr, hex::colon_string};
const FINGERPRINT_LEN: usize = aws_lc_rs::digest::SHA256_OUTPUT_LEN;
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Fingerprint {
digest: [u8; FINGERPRINT_LEN],
rendered: CachedStringRepr,
}
impl Fingerprint {
pub fn as_hex_str(&self) -> &str {
self.rendered.get_or_init(|| colon_string(self.as_bytes()))
}
pub fn as_bytes(&self) -> &[u8] {
&self.digest
}
}
impl Display for Fingerprint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_hex_str())
}
}
impl<'a> From<&'a X509Certificate<'a>> for Fingerprint {
fn from(cert: &'a X509Certificate<'a>) -> Self {
let hash = digest(&SHA256, cert.as_raw());
Self {
digest: hash
.as_ref()
.try_into()
.expect("sha256 digest length is fixed"),
rendered: Default::default(),
}
}
}
impl valuable::Valuable for Fingerprint {
fn as_value(&self) -> valuable::Value<'_> {
valuable::Value::String(self.as_hex_str())
}
fn visit(&self, visit: &mut dyn valuable::Visit) {
visit.visit_value(self.as_value());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fixture() {
let input = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32,
];
let f = Fingerprint {
digest: input,
rendered: Default::default(),
};
assert_eq!(f.as_bytes(), &input);
let want = "01:02:03:04:05:06:07:08:09:0a:0b:0c:0d:0e:0f:10:11:12:13:14:15:16:17:18:19:1a:1b:1c:1d:1e:1f:20";
assert_eq!(f.as_hex_str(), want);
assert_eq!(f.to_string(), want);
}
}