claim169_core/model/
x509.rs1use serde::{Deserialize, Serialize};
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
21#[serde(untagged)]
22pub enum CertHashAlgorithm {
23 Numeric(i64),
25 Named(String),
27}
28
29impl Default for CertHashAlgorithm {
30 fn default() -> Self {
31 CertHashAlgorithm::Numeric(-16)
33 }
34}
35
36#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
41#[serde(rename_all = "camelCase")]
42pub struct CertificateHash {
43 pub algorithm: CertHashAlgorithm,
45
46 #[serde(with = "crate::serde_utils::base64_bytes")]
48 pub hash_value: Vec<u8>,
49}
50
51impl CertificateHash {
52 pub fn validate_length(&self) -> bool {
58 let expected = match &self.algorithm {
59 CertHashAlgorithm::Numeric(-16) => Some(32), CertHashAlgorithm::Numeric(-43) => Some(48), CertHashAlgorithm::Numeric(-44) => Some(64), _ => None,
63 };
64 expected
65 .map(|length| self.hash_value.len() == length)
66 .unwrap_or(true)
67 }
68}
69
70#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
83#[serde(rename_all = "camelCase")]
84pub struct X509Headers {
85 #[serde(
89 skip_serializing_if = "Option::is_none",
90 with = "crate::serde_utils::option_vec_base64"
91 )]
92 pub x5bag: Option<Vec<Vec<u8>>>,
93
94 #[serde(
101 skip_serializing_if = "Option::is_none",
102 with = "crate::serde_utils::option_vec_base64"
103 )]
104 pub x5chain: Option<Vec<Vec<u8>>>,
105
106 #[serde(skip_serializing_if = "Option::is_none")]
110 pub x5t: Option<CertificateHash>,
111
112 #[serde(skip_serializing_if = "Option::is_none")]
120 pub x5u: Option<String>,
121}
122
123impl X509Headers {
124 pub fn new() -> Self {
126 Self::default()
127 }
128
129 pub fn is_empty(&self) -> bool {
131 self.x5bag.is_none() && self.x5chain.is_none() && self.x5t.is_none() && self.x5u.is_none()
132 }
133
134 pub fn has_certificates(&self) -> bool {
136 self.x5bag.is_some() || self.x5chain.is_some()
137 }
138
139 pub fn x5u_is_https(&self) -> bool {
144 self.x5u
145 .as_ref()
146 .map(|uri| uri.starts_with("https://"))
147 .unwrap_or(true)
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_x509_headers_default() {
157 let headers = X509Headers::default();
158 assert!(headers.is_empty());
159 assert!(!headers.has_certificates());
160 }
161
162 #[test]
163 fn test_x509_headers_with_x5bag() {
164 let headers = X509Headers {
165 x5bag: Some(vec![vec![1, 2, 3]]),
166 ..Default::default()
167 };
168 assert!(!headers.is_empty());
169 assert!(headers.has_certificates());
170 }
171
172 #[test]
173 fn test_x509_headers_with_x5chain() {
174 let headers = X509Headers {
175 x5chain: Some(vec![vec![1, 2, 3], vec![4, 5, 6]]),
176 ..Default::default()
177 };
178 assert!(!headers.is_empty());
179 assert!(headers.has_certificates());
180 }
181
182 #[test]
183 fn test_x509_headers_with_x5t() {
184 let headers = X509Headers {
185 x5t: Some(CertificateHash {
186 algorithm: CertHashAlgorithm::Numeric(-16),
187 hash_value: vec![0xab; 32],
188 }),
189 ..Default::default()
190 };
191 assert!(!headers.is_empty());
192 assert!(!headers.has_certificates()); }
194
195 #[test]
196 fn test_x509_headers_with_x5u() {
197 let headers = X509Headers {
198 x5u: Some("https://example.com/cert.pem".to_string()),
199 ..Default::default()
200 };
201 assert!(!headers.is_empty());
202 assert!(!headers.has_certificates()); }
204
205 #[test]
206 fn test_cert_hash_algorithm_serde() {
207 let numeric = CertHashAlgorithm::Numeric(-16);
209 let json = serde_json::to_string(&numeric).unwrap();
210 assert_eq!(json, "-16");
211
212 let named = CertHashAlgorithm::Named("sha-256".to_string());
214 let json = serde_json::to_string(&named).unwrap();
215 assert_eq!(json, r#""sha-256""#);
216 }
217
218 #[test]
219 fn test_x509_headers_json_serialization() {
220 let headers = X509Headers {
221 x5chain: Some(vec![vec![1, 2, 3]]),
222 x5u: Some("https://example.com/cert.pem".to_string()),
223 ..Default::default()
224 };
225
226 let json = serde_json::to_string(&headers).unwrap();
227 assert!(json.contains("x5chain"));
228 assert!(json.contains("x5u"));
229 assert!(!json.contains("x5bag")); assert!(!json.contains("x5t")); }
232
233 #[test]
234 fn test_x509_headers_equality() {
235 let h1 = X509Headers {
236 x5u: Some("https://example.com".to_string()),
237 ..Default::default()
238 };
239 let h2 = X509Headers {
240 x5u: Some("https://example.com".to_string()),
241 ..Default::default()
242 };
243 let h3 = X509Headers {
244 x5u: Some("https://other.com".to_string()),
245 ..Default::default()
246 };
247
248 assert_eq!(h1, h2);
249 assert_ne!(h1, h3);
250 }
251
252 #[test]
253 fn test_certificate_hash_validate_length_sha256() {
254 let hash = CertificateHash {
255 algorithm: CertHashAlgorithm::Numeric(-16),
256 hash_value: vec![0xab; 32],
257 };
258 assert!(hash.validate_length());
259 }
260
261 #[test]
262 fn test_certificate_hash_validate_length_sha256_wrong() {
263 let hash = CertificateHash {
264 algorithm: CertHashAlgorithm::Numeric(-16),
265 hash_value: vec![0xab; 16], };
267 assert!(!hash.validate_length());
268 }
269
270 #[test]
271 fn test_certificate_hash_validate_length_sha384() {
272 let hash = CertificateHash {
273 algorithm: CertHashAlgorithm::Numeric(-43),
274 hash_value: vec![0xab; 48],
275 };
276 assert!(hash.validate_length());
277 }
278
279 #[test]
280 fn test_certificate_hash_validate_length_sha512() {
281 let hash = CertificateHash {
282 algorithm: CertHashAlgorithm::Numeric(-44),
283 hash_value: vec![0xab; 64],
284 };
285 assert!(hash.validate_length());
286 }
287
288 #[test]
289 fn test_certificate_hash_validate_length_unknown_algorithm() {
290 let hash = CertificateHash {
291 algorithm: CertHashAlgorithm::Named("custom-hash".to_string()),
292 hash_value: vec![0xab; 20],
293 };
294 assert!(hash.validate_length()); }
296
297 #[test]
298 fn test_x5u_is_https_with_https() {
299 let headers = X509Headers {
300 x5u: Some("https://example.com/cert.pem".to_string()),
301 ..Default::default()
302 };
303 assert!(headers.x5u_is_https());
304 }
305
306 #[test]
307 fn test_x5u_is_https_with_http() {
308 let headers = X509Headers {
309 x5u: Some("http://example.com/cert.pem".to_string()),
310 ..Default::default()
311 };
312 assert!(!headers.x5u_is_https());
313 }
314
315 #[test]
316 fn test_x5u_is_https_with_no_uri() {
317 let headers = X509Headers::default();
318 assert!(headers.x5u_is_https());
319 }
320
321 #[test]
322 fn test_x5u_is_https_with_ftp() {
323 let headers = X509Headers {
324 x5u: Some("ftp://example.com/cert.pem".to_string()),
325 ..Default::default()
326 };
327 assert!(!headers.x5u_is_https());
328 }
329}