rc_crypto/certificate/id/
cert_id.rs1use std::fmt::Display;
16
17use smallvec::SmallVec;
18use thiserror::Error;
19use valuable::Valuable;
20use x509_parser::prelude::{ParsedExtension, X509Certificate};
21
22use crate::{
23 cached_string_repr::CachedStringRepr, certificate::id::DangerousComparableId, hex::colon_string,
24};
25
26#[derive(Debug, Error)]
28#[error("no Subject Key Identifier found")]
29pub struct ErrorNoSKI;
30
31#[derive(Debug, Error)]
33#[error("certificate ID is an invalid length")]
34pub struct InvalidLength {
35 actual_len: usize,
36}
37
38#[derive(Debug, Error)]
40pub enum InvalidCertId {
41 #[error(transparent)]
43 NoSKI(#[from] ErrorNoSKI),
44
45 #[error(transparent)]
47 InvalidLength(#[from] InvalidLength),
48}
49
50#[derive(Debug, Hash, Clone)] pub struct CertId {
70 bytes: SmallVec<[u8; 20]>,
71
72 rendered: CachedStringRepr,
76}
77
78impl CertId {
79 const MIN_LENGTH: usize = 16;
81
82 pub fn as_hex_str(&self) -> &str {
85 self.rendered.get_or_init(|| colon_string(&self.bytes))
86 }
87
88 pub(super) fn as_bytes(&self) -> &[u8] {
90 &self.bytes
91 }
92
93 pub fn as_dangerous_comparable(&self) -> DangerousComparableId<'_, Self> {
97 DangerousComparableId::from(self)
98 }
99}
100
101impl Display for CertId {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.write_str(self.as_hex_str())
104 }
105}
106
107impl From<CertId> for Vec<u8> {
108 fn from(id: CertId) -> Vec<u8> {
109 id.bytes.into_vec()
110 }
111}
112
113impl TryFrom<&[u8]> for CertId {
114 type Error = InvalidLength;
115
116 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
117 if value.len() < Self::MIN_LENGTH {
118 return Err(InvalidLength {
119 actual_len: value.len(),
120 });
121 }
122 Ok(Self {
123 bytes: SmallVec::from_slice(value),
124 rendered: Default::default(),
125 })
126 }
127}
128
129impl TryFrom<Vec<u8>> for CertId {
130 type Error = InvalidLength;
131
132 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
133 if value.len() < Self::MIN_LENGTH {
134 return Err(InvalidLength {
135 actual_len: value.len(),
136 });
137 }
138 Ok(Self {
139 bytes: SmallVec::from_vec(value),
140 rendered: Default::default(),
141 })
142 }
143}
144
145impl<'a> TryFrom<&X509Certificate<'a>> for CertId {
146 type Error = InvalidCertId;
147
148 fn try_from(cert: &X509Certificate<'a>) -> Result<Self, Self::Error> {
149 let bytes = cert
150 .iter_extensions()
151 .find_map(|v| match v.parsed_extension() {
152 ParsedExtension::SubjectKeyIdentifier(ski) => Some(ski.0),
153 _ => None,
154 })
155 .ok_or(ErrorNoSKI)?;
156 Ok(CertId::try_from(bytes)?)
157 }
158}
159
160impl Valuable for CertId {
161 fn as_value(&self) -> valuable::Value<'_> {
162 valuable::Value::String(self.as_hex_str())
163 }
164
165 fn visit(&self, visit: &mut dyn valuable::Visit) {
166 visit.visit_value(self.as_value());
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use rc_x509_test_helpers::assert_valuable_repr;
173 use static_assertions::assert_not_impl_any;
174 use x509_parser::prelude::FromDer;
175
176 use super::*;
177
178 use crate::certificate::tests::cert_fixture;
179
180 const FIXTURE_SKI_STR: &str = "dc:8d:b6:27:52:78:58:4c:fd:a2:43:db:cb:2b:e0:57:68:6e:2b:8e";
181
182 assert_not_impl_any!(CertId: PartialEq, Eq);
187
188 fn fixture_ski() -> CertId {
189 let der = cert_fixture().as_der();
190 let cert = X509Certificate::from_der(&der).expect("valid DER").1;
191
192 CertId::try_from(&cert).expect("extract SKI")
193 }
194
195 #[test]
196 fn test_fixture() {
197 let aki = fixture_ski();
198
199 assert_eq!(aki.as_hex_str(), FIXTURE_SKI_STR,);
200 }
201
202 #[test]
203 fn test_valuable_repr() {
204 let aki = fixture_ski();
205
206 assert_valuable_repr(&aki, FIXTURE_SKI_STR);
207 }
208
209 #[test]
210 fn test_danger_eq() {
211 let ski = fixture_ski();
212
213 assert_eq!(ski.as_dangerous_comparable(), ski);
214 }
215}