quantcrypt/cms/
directory_cert_store.rs1use cms::enveloped_data::RecipientIdentifier;
2use std::fs;
3use std::path::Path;
4
5use crate::cms::cert_store_trait::CertificateStore;
6use crate::{certificates::Certificate, QuantCryptError};
7
8type Result<T> = std::result::Result<T, QuantCryptError>;
9
10pub struct DirectoryCertificateStore {
33 ta_certificates: Vec<Certificate>,
34 ee_certificates: Vec<Certificate>,
35}
36
37impl DirectoryCertificateStore {
38 pub fn new(path: &str) -> Result<DirectoryCertificateStore> {
48 let mut ta_certificates = vec![];
49 let mut ee_certificates = vec![];
50
51 let dir_path = Path::new(path);
52
53 if dir_path.exists() && dir_path.is_dir() {
55 for entry in
57 fs::read_dir(dir_path).map_err(|_| QuantCryptError::InvalidDirectoryPath)?
58 {
59 let entry = entry.map_err(|_| QuantCryptError::InvalidDirectoryPath)?;
60 let path = entry.path();
61 let path_str = path.to_str().ok_or(QuantCryptError::InvalidDirectoryPath)?;
62
63 let cert = Certificate::from_file(path_str);
65 if let Ok(cert) = cert {
66 let is_self_signed = cert.verify_self_signed().map_or(false, |result| result);
68
69 let is_valid = cert.is_valid();
70
71 if is_valid {
73 if is_self_signed {
74 ta_certificates.push(cert);
75 } else {
76 ee_certificates.push(cert);
77 }
78 }
79 }
80 }
81 } else {
82 return Err(QuantCryptError::InvalidDirectoryPath);
83 }
84
85 Ok(DirectoryCertificateStore {
86 ta_certificates,
87 ee_certificates,
88 })
89 }
90
91 fn find_parent(&self, cert: &Certificate) -> Option<Certificate> {
93 if !cert.is_valid() {
95 return None;
96 }
97
98 for ta_cert in &self.ta_certificates {
100 if !ta_cert.is_valid() {
101 continue;
102 }
103 let cert = ta_cert.verify_child(cert).map_or(None, |result| {
104 if result {
105 return Some(ta_cert.clone());
106 }
107 None
108 });
109 if cert.is_some() {
110 return cert;
111 }
112 }
113
114 for ee_cert in &self.ee_certificates {
116 if !ee_cert.is_valid() {
117 continue;
118 }
119 let cert = ee_cert.verify_child(cert).map_or(None, |result| {
120 if result {
121 return self.find_parent(ee_cert);
122 }
123 None
124 });
125 if cert.is_some() {
126 return cert;
127 }
128 }
129 None
130 }
131}
132
133impl CertificateStore for DirectoryCertificateStore {
134 fn find(&self, ri: RecipientIdentifier) -> Option<Certificate> {
135 match ri {
136 cms::enveloped_data::RecipientIdentifier::IssuerAndSerialNumber(issuer) => {
137 let serial = issuer.serial_number;
138 let issuer = issuer.issuer;
139 for cert in self.ee_certificates.clone() {
140 if cert.get_serial_number() == serial && cert.get_issuer() == issuer {
141 if cert.is_key_encipherment_enabled() {
143 let parent = self.find_parent(&cert);
145 if parent.is_some() {
146 return Some(cert);
147 }
148 }
149 }
150 }
151 }
152 cms::enveloped_data::RecipientIdentifier::SubjectKeyIdentifier(ski) => {
153 for cert in self.ee_certificates.clone() {
154 if let Ok(cert_ski) = cert.get_subject_key_identifier() {
155 if cert_ski == ski {
156 if cert.is_key_encipherment_enabled() {
158 let parent = self.find_parent(&cert);
160 if parent.is_some() {
161 return Some(cert);
162 }
163 }
164 }
165 }
166 }
167 }
168 }
169 None
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use x509_cert::builder::Profile;
176
177 use crate::{
178 certificates::{CertValidity, CertificateBuilder},
179 dsas::{DsaAlgorithm, DsaKeyGenerator},
180 kems::{KemAlgorithm, KemKeyGenerator},
181 };
182
183 use super::*;
184
185 #[test]
186 fn test_directory_recipient_auth() {
187 let dir_path = "test/data/chain";
188
189 let (ta_pk, ta_sk) = DsaKeyGenerator::new(DsaAlgorithm::MlDsa44)
191 .generate()
192 .unwrap();
193 let cert = CertificateBuilder::new(
194 Profile::Root,
195 None,
196 CertValidity::new(None, "2035-01-01T00:00:00Z").unwrap(),
197 "CN=example.com".to_string(),
198 ta_pk.clone(),
199 &ta_sk,
200 )
201 .unwrap()
202 .build()
203 .unwrap();
204
205 let cert_path = format!(
207 "{}/{}_{}_ta.der",
208 dir_path,
209 cert.get_public_key_oid(),
210 cert.get_public_key_oid_friendly_name()
211 );
212 cert.to_der_file(&cert_path).unwrap();
213
214 let (sub_pk, sub_sk) = DsaKeyGenerator::new(DsaAlgorithm::MlDsa44)
216 .generate()
217 .unwrap();
218 let cert_sub = CertificateBuilder::new(
219 Profile::SubCA {
220 issuer: cert.get_subject(),
221 path_len_constraint: None,
222 },
223 None,
224 CertValidity::new(None, "2035-01-01T00:00:00Z").unwrap(),
225 "CN=sub.example.com".to_string(),
226 sub_pk.clone(),
227 &ta_sk,
228 )
229 .unwrap()
230 .build()
231 .unwrap();
232
233 let cert_path = format!(
235 "{}/{}_{}_sub.der",
236 dir_path,
237 cert_sub.get_public_key_oid(),
238 cert_sub.get_public_key_oid_friendly_name()
239 );
240 cert_sub.to_pem_file(&cert_path).unwrap();
241
242 let (ee_pk, _) = KemKeyGenerator::new(KemAlgorithm::MlKem512)
244 .generate()
245 .unwrap();
246 let cert_ee = CertificateBuilder::new(
247 Profile::Leaf {
248 issuer: cert_sub.get_subject(),
249 enable_key_agreement: false,
250 enable_key_encipherment: true,
251 },
252 None,
253 CertValidity::new(None, "2035-01-01T00:00:00Z").unwrap(),
254 "CN=ee.sub.example.com".to_string(),
255 ee_pk,
256 &sub_sk,
257 )
258 .unwrap()
259 .build()
260 .unwrap();
261
262 let cert_path = format!(
264 "{}/{}_{}_ee.der",
265 dir_path,
266 cert_ee.get_public_key_oid(),
267 cert_ee.get_public_key_oid_friendly_name()
268 );
269 cert_ee.to_der_file(&cert_path).unwrap();
270
271 let auth = DirectoryCertificateStore::new(dir_path).unwrap();
272
273 let ta_ri = RecipientIdentifier::IssuerAndSerialNumber(cms::cert::IssuerAndSerialNumber {
275 issuer: cert.get_issuer(),
276 serial_number: cert.get_serial_number(),
277 });
278
279 let ta_cert = auth.find(ta_ri);
280 assert!(ta_cert.is_none());
282
283 let ta_ri =
285 RecipientIdentifier::SubjectKeyIdentifier(cert.get_subject_key_identifier().unwrap());
286 let ta_cert = auth.find(ta_ri);
287 assert!(ta_cert.is_none());
288
289 let sub_ri = RecipientIdentifier::IssuerAndSerialNumber(cms::cert::IssuerAndSerialNumber {
291 issuer: cert_sub.get_issuer(),
292 serial_number: cert_sub.get_serial_number(),
293 });
294
295 let sub_cert = auth.find(sub_ri);
296 assert!(sub_cert.is_none());
297
298 let sub_ri = RecipientIdentifier::SubjectKeyIdentifier(
300 cert_sub.get_subject_key_identifier().unwrap(),
301 );
302 let sub_cert = auth.find(sub_ri);
303 assert!(sub_cert.is_none());
304
305 let ee_ri = RecipientIdentifier::IssuerAndSerialNumber(cms::cert::IssuerAndSerialNumber {
307 issuer: cert_ee.get_issuer(),
308 serial_number: cert_ee.get_serial_number(),
309 });
310
311 let ee_cert = auth.find(ee_ri).unwrap();
312 assert_eq!(ee_cert.get_subject().to_string(), "CN=ee.sub.example.com");
313
314 let ee_ri = RecipientIdentifier::SubjectKeyIdentifier(
316 cert_ee.get_subject_key_identifier().unwrap(),
317 );
318 let ee_cert = auth.find(ee_ri).unwrap();
319 assert_eq!(ee_cert.get_subject().to_string(), "CN=ee.sub.example.com");
320 }
321}