quantcrypt/asn1/
cert_builder.rs1use std::error::Error;
2use std::str::FromStr;
3
4use chrono::{DateTime, Datelike, TimeZone, Timelike};
5use pkcs8::spki::SubjectPublicKeyInfo;
6use rand::RngCore;
7use rand_core::OsRng;
8use x509_cert::builder::Builder;
9pub use x509_cert::builder::Profile;
10use x509_cert::ext::AsExtension;
11use x509_cert::time::Time;
12use x509_cert::{name::Name, serial_number::SerialNumber, time::Validity};
13
14use crate::{errors::QuantCryptError, keys::PrivateKey, keys::PublicKey};
15
16use crate::asn1::certificate::Certificate;
17
18type Result<T> = std::result::Result<T, QuantCryptError>;
19
20#[derive(Clone)]
22pub struct CertValidity {
23 pub not_before: der::asn1::UtcTime,
25 pub not_after: der::asn1::UtcTime,
27}
28
29impl CertValidity {
30 fn date_time_to_asn(
31 time: &DateTime<chrono::Utc>,
32 ) -> std::result::Result<der::asn1::UtcTime, Box<dyn Error>> {
33 let dt = der::DateTime::new(
34 time.year() as u16,
35 time.month() as u8,
36 time.day() as u8,
37 time.hour() as u8,
38 time.minute() as u8,
39 time.second() as u8,
40 )?;
41 let result = der::asn1::UtcTime::from_date_time(dt)?;
42 Ok(result)
43 }
44
45 pub fn new(not_before: Option<&str>, not_after: &str) -> Result<CertValidity> {
61 let not_after = DateTime::parse_from_rfc3339(not_after)
62 .map_err(|_| QuantCryptError::InvalidNotAfter)?;
63
64 let not_after = chrono::Utc.from_utc_datetime(¬_after.naive_utc());
66
67 if not_after <= chrono::Utc::now() {
69 return Err(QuantCryptError::InvalidNotAfter);
70 }
71
72 let not_after_dt = CertValidity::date_time_to_asn(¬_after)
73 .map_err(|_| QuantCryptError::InvalidNotAfter)?;
74
75 if let Some(not_before) = not_before {
76 let not_before = DateTime::parse_from_rfc3339(not_before)
77 .map_err(|_| QuantCryptError::InvalidNotBefore)?;
78
79 let not_before = chrono::Utc.from_utc_datetime(¬_before.naive_utc());
81
82 if not_before > not_after {
83 return Err(QuantCryptError::InvalidNotBefore);
84 }
85
86 let not_before_dt = CertValidity::date_time_to_asn(¬_before)
87 .map_err(|_| QuantCryptError::InvalidNotBefore)?;
88
89 Ok(CertValidity {
90 not_before: not_before_dt,
91 not_after: not_after_dt,
92 })
93 } else {
94 let not_before = chrono::Utc::now();
96
97 if not_before > not_after {
98 return Err(QuantCryptError::InvalidNotAfter);
99 }
100
101 let not_before_dt = CertValidity::date_time_to_asn(¬_before)
102 .map_err(|_| QuantCryptError::InvalidNotBefore)?;
103
104 Ok(CertValidity {
105 not_before: not_before_dt,
106 not_after: not_after_dt,
107 })
108 }
109 }
110}
111
112pub struct CertificateBuilder<'a> {
164 builder: x509_cert::builder::CertificateBuilder<'a, PrivateKey>,
165}
166
167impl<'a> CertificateBuilder<'a> {
168 pub fn new(
170 profile: Profile,
171 serial_number: Option<[u8; 20]>,
172 validity: CertValidity,
173 subject: String,
174 cert_public_key: PublicKey,
175 signer: &'a PrivateKey,
176 ) -> Result<CertificateBuilder<'a>> {
177 let subject = Name::from_str(&subject).map_err(|_| QuantCryptError::BadSubject)?;
178
179 let spki = SubjectPublicKeyInfo::from_key(cert_public_key)
180 .map_err(|_| QuantCryptError::BadPublicKey)?;
181
182 let validity = Validity {
183 not_before: Time::UtcTime(validity.not_before),
184 not_after: Time::UtcTime(validity.not_after),
185 };
186
187 let serial_number = if let Some(serial_number) = serial_number {
188 SerialNumber::new(&serial_number).map_err(|_| QuantCryptError::BadSerialNumber)?
189 } else {
190 CertificateBuilder::get_random_serial()?
191 };
192
193 let builder = x509_cert::builder::CertificateBuilder::new(
194 profile,
195 serial_number,
196 validity,
197 subject,
198 spki,
199 signer,
200 )
201 .map_err(|_| QuantCryptError::Unknown)?;
202
203 Ok(CertificateBuilder { builder })
204 }
205
206 pub fn add_extension(&mut self, extension: impl AsExtension) -> Result<&mut Self> {
207 self.builder
208 .add_extension(&extension)
209 .map_err(|_| QuantCryptError::BadExtension)?;
210
211 Ok(self)
212 }
213
214 fn get_random_serial() -> Result<SerialNumber> {
216 let mut serial = [0u8; 20];
217 OsRng.fill_bytes(&mut serial);
218 serial[0] = 0x01;
219 let serial = SerialNumber::new(&serial).map_err(|_| QuantCryptError::BadSerialNumber)?;
220 Ok(serial)
221 }
222
223 pub fn build(self) -> Result<Certificate> {
224 let cert_inner = self.builder.build().map_err(|_| QuantCryptError::Unknown)?;
225 let cert = Certificate::new(cert_inner);
226 Ok(cert)
227 }
228}
229
230#[cfg(test)]
231mod test {
232
233 use crate::{dsas::DsaAlgorithm, dsas::DsaKeyGenerator};
234
235 use super::*;
236
237 #[test]
238 fn gen_pq_hackathon_artifacts_r4() {
239 let dsa_algs: Vec<DsaAlgorithm> = DsaAlgorithm::all();
241
242 for dsa_alg in dsa_algs.iter() {
243 let (pk_root, sk_root) = DsaKeyGenerator::new(*dsa_alg).generate().unwrap();
245
246 let profile = Profile::Root;
247 let serial_no = None; let validity = CertValidity::new(None, "2034-01-01T00:00:00Z").unwrap(); let subject = "CN=example.com".to_string();
250 let cert_public_key = pk_root.clone();
251 let signer = &sk_root;
252
253 let builder = CertificateBuilder::new(
256 profile,
257 serial_no,
258 validity.clone(),
259 subject.clone(),
260 cert_public_key,
261 signer,
262 )
263 .unwrap();
264 let cert_root = builder.build().unwrap();
265
266 assert!(cert_root.verify_self_signed().unwrap());
268
269 let dsa_alg_name = dsa_alg.to_string();
270
271 let mut save_dir = "artifacts/r4_certs/non-ipd";
272
273 if crate::is_ipd_mode_enabled() {
275 save_dir = "artifacts/r4_certs/ipd";
276 }
277
278 let file_name = format!("{}/{}-{}_ta.der", save_dir, dsa_alg_name, dsa_alg.get_oid());
279
280 cert_root.to_der_file(&file_name).unwrap();
282 }
283 }
284
285 #[test]
286 fn gen_pq_hackathon_artifacts_r3() {
287 let dsa_algs: Vec<DsaAlgorithm> = DsaAlgorithm::all();
289
290 for dsa_alg in dsa_algs.iter() {
291 let (pk_root, sk_root) = DsaKeyGenerator::new(*dsa_alg).generate().unwrap();
293
294 let profile = Profile::Root;
295 let serial_no = None; let validity = CertValidity::new(None, "2034-01-01T00:00:00Z").unwrap(); let subject = "CN=example.com".to_string();
298 let cert_public_key = pk_root.clone();
299 let signer = &sk_root;
300
301 let builder = CertificateBuilder::new(
304 profile,
305 serial_no,
306 validity.clone(),
307 subject.clone(),
308 cert_public_key,
309 signer,
310 )
311 .unwrap();
312 let cert_root = builder.build().unwrap();
313
314 assert!(cert_root.verify_self_signed().unwrap());
316
317 let mut save_dir = "artifacts/r3_certs/non-ipd";
318
319 if crate::is_ipd_mode_enabled() {
321 save_dir = "artifacts/r3_certs/ipd";
322 }
323
324 let file_name = format!("{}/{}_ta.pem", save_dir, dsa_alg.get_oid());
325
326 cert_root.to_pem_file(&file_name).unwrap();
328 }
329 }
330}