1use std::time::SystemTime;
13
14use rama_core::error::{BoxError, ErrorContext};
15use yasna::{
16 Tag,
17 models::{GeneralizedTime, ObjectIdentifier, UTCTime},
18};
19
20fn oid_ecdsa_sha256() -> ObjectIdentifier {
21 ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 4, 3, 2])
22}
23fn oid_rsa_sha256() -> ObjectIdentifier {
24 ObjectIdentifier::from_slice(&[1, 2, 840, 113549, 1, 1, 11])
25}
26fn oid_authority_key_id() -> ObjectIdentifier {
27 ObjectIdentifier::from_slice(&[2, 5, 29, 35])
28}
29fn oid_crl_number() -> ObjectIdentifier {
30 ObjectIdentifier::from_slice(&[2, 5, 29, 20])
31}
32
33#[derive(Debug, Clone, Copy)]
37pub enum CrlSignatureAlgorithm {
38 EcdsaSha256,
40 RsaSha256,
42}
43
44#[derive(Debug, Clone, Copy)]
46pub struct RevokedEntry<'a> {
47 pub serial: &'a [u8],
49 pub revocation_date: SystemTime,
51}
52
53pub struct CrlParams<'a> {
56 pub issuer_name_der: &'a [u8],
58 pub authority_key_id: &'a [u8],
60 pub this_update: SystemTime,
62 pub next_update: SystemTime,
64 pub crl_number: u64,
66 pub revoked: &'a [RevokedEntry<'a>],
68}
69
70pub fn build_crl(
76 params: &CrlParams<'_>,
77 alg: CrlSignatureAlgorithm,
78 sign_tbs: impl FnOnce(&[u8]) -> Result<Vec<u8>, BoxError>,
79) -> Result<Vec<u8>, BoxError> {
80 let this_update = x509_time(params.this_update)?;
83 let next_update = x509_time(params.next_update)?;
84 let revoked = params
85 .revoked
86 .iter()
87 .map(|e| Ok::<_, BoxError>((e.serial, x509_time(e.revocation_date)?)))
88 .collect::<Result<Vec<_>, _>>()?;
89
90 let aki_value = yasna::construct_der(|w| {
92 w.write_sequence(|w| {
93 w.next()
94 .write_tagged_implicit(Tag::context(0), |w| w.write_bytes(params.authority_key_id));
95 });
96 });
97 let crl_number_value = yasna::construct_der(|w| w.write_u64(params.crl_number));
98
99 let tbs_der = yasna::construct_der(|w| {
100 w.write_sequence(|w| {
101 w.next().write_i64(1);
103 write_alg(w.next(), alg);
105 w.next().write_der(params.issuer_name_der);
107 write_time(w.next(), &this_update);
108 write_time(w.next(), &next_update);
109 if !revoked.is_empty() {
111 w.next().write_sequence_of(|w| {
112 for (serial, date) in &revoked {
113 w.next().write_sequence(|w| {
114 w.next().write_bigint_bytes(serial, true);
115 write_time(w.next(), date);
116 });
117 }
118 });
119 }
120 w.next().write_tagged(Tag::context(0), |w| {
122 w.write_sequence(|w| {
123 w.next().write_sequence(|w| {
124 w.next().write_oid(&oid_authority_key_id());
125 w.next().write_bytes(&aki_value);
126 });
127 w.next().write_sequence(|w| {
128 w.next().write_oid(&oid_crl_number());
129 w.next().write_bytes(&crl_number_value);
130 });
131 });
132 });
133 });
134 });
135
136 let signature = sign_tbs(&tbs_der)?;
137
138 Ok(yasna::construct_der(|w| {
139 w.write_sequence(|w| {
140 w.next().write_der(&tbs_der);
141 write_alg(w.next(), alg);
142 w.next().write_bitvec_bytes(&signature, signature.len() * 8);
143 });
144 }))
145}
146
147#[must_use]
151pub fn crl_distribution_point_der(uri: &str) -> Vec<u8> {
152 yasna::construct_der(|w| {
153 w.write_sequence(|w| {
154 w.next().write_sequence(|w| {
155 w.next().write_tagged(Tag::context(0), |w| {
157 w.write_tagged_implicit(Tag::context(0), |w| {
159 w.write_sequence(|w| {
160 w.next().write_tagged_implicit(Tag::context(6), |w| {
162 w.write_bytes(uri.as_bytes());
163 });
164 });
165 });
166 });
167 });
168 });
169 })
170}
171
172fn write_alg(w: yasna::DERWriter<'_>, alg: CrlSignatureAlgorithm) {
173 w.write_sequence(|w| match alg {
174 CrlSignatureAlgorithm::EcdsaSha256 => {
175 w.next().write_oid(&oid_ecdsa_sha256());
176 }
177 CrlSignatureAlgorithm::RsaSha256 => {
178 w.next().write_oid(&oid_rsa_sha256());
179 w.next().write_null();
180 }
181 });
182}
183
184enum X509Time {
186 Utc(UTCTime),
187 General(GeneralizedTime),
188}
189
190fn x509_time(t: SystemTime) -> Result<X509Time, BoxError> {
191 let secs = t
192 .duration_since(SystemTime::UNIX_EPOCH)
193 .context("crl: timestamp before unix epoch")?
194 .as_secs();
195 let odt = time::OffsetDateTime::from_unix_timestamp(secs as i64)
196 .map_err(|e| BoxError::from(format!("crl: invalid timestamp: {e}")))?;
197 Ok(if odt.year() < 2050 {
198 X509Time::Utc(UTCTime::from_datetime(odt))
199 } else {
200 X509Time::General(GeneralizedTime::from_datetime(odt))
201 })
202}
203
204fn write_time(w: yasna::DERWriter<'_>, t: &X509Time) {
205 match t {
206 X509Time::Utc(u) => w.write_utctime(u),
207 X509Time::General(g) => w.write_generalized_time(g),
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214 use std::time::Duration;
215
216 const T0: u64 = 1_800_000_000;
218
219 fn params<'a>(issuer: &'a [u8], revoked: &'a [RevokedEntry<'a>]) -> CrlParams<'a> {
220 CrlParams {
221 issuer_name_der: issuer,
222 authority_key_id: &[0xAB; 20],
223 this_update: SystemTime::UNIX_EPOCH + Duration::from_secs(T0),
224 next_update: SystemTime::UNIX_EPOCH + Duration::from_secs(T0 + 7 * 86_400),
225 crl_number: 1,
226 revoked,
227 }
228 }
229
230 #[test]
233 fn builds_wellformed_empty_crl() {
234 let issuer = yasna::construct_der(|w| w.write_sequence(|_| {}));
235 let mut signed_tbs: Vec<u8> = Vec::new();
236 let der = build_crl(
237 ¶ms(&issuer, &[]),
238 CrlSignatureAlgorithm::EcdsaSha256,
239 |tbs| {
240 signed_tbs = tbs.to_vec();
241 Ok(vec![0xDE, 0xAD, 0xBE, 0xEF])
242 },
243 )
244 .expect("build crl");
245
246 assert!(
247 !signed_tbs.is_empty(),
248 "tbsCertList was handed to the signer"
249 );
250
251 yasna::parse_der(&der, |r| {
252 r.read_sequence(|r| {
253 let tbs = r.next().read_der()?;
254 assert_eq!(tbs, signed_tbs, "embedded tbs == signed tbs");
255 r.next().read_sequence(|r| {
256 let oid = r.next().read_oid()?;
257 assert_eq!(oid, oid_ecdsa_sha256());
258 Ok(())
259 })?;
260 let (sig, bits) = r.next().read_bitvec_bytes()?;
261 assert_eq!(sig, vec![0xDE, 0xAD, 0xBE, 0xEF]);
262 assert_eq!(bits, 32);
263 Ok(())
264 })
265 })
266 .expect("parse CertificateList");
267 }
268
269 #[test]
271 fn revoked_serial_present() {
272 let issuer = yasna::construct_der(|w| w.write_sequence(|_| {}));
273 let revoked = [RevokedEntry {
274 serial: &[0x12, 0x34, 0x56],
275 revocation_date: SystemTime::UNIX_EPOCH + Duration::from_secs(T0),
276 }];
277 let der = build_crl(
278 ¶ms(&issuer, &revoked),
279 CrlSignatureAlgorithm::RsaSha256,
280 |_| Ok(vec![0x00]),
281 )
282 .expect("build crl");
283
284 let serials = yasna::parse_der(&der, |r| {
285 r.read_sequence(|r| {
286 let serials = r.next().read_sequence(|r| {
287 let _version = r.next().read_i64()?;
288 let _alg = r.next().read_der()?;
289 let _issuer = r.next().read_der()?;
290 let _this = r.next().read_der()?;
291 let _next = r.next().read_der()?;
292 let mut serials: Vec<Vec<u8>> = Vec::new();
293 r.next().read_sequence_of(|r| {
294 r.read_sequence(|r| {
295 let (serial, _pos) = r.next().read_bigint_bytes()?;
296 let _date = r.next().read_der()?;
297 serials.push(serial);
298 Ok(())
299 })
300 })?;
301 let _exts = r.next().read_der()?;
302 Ok(serials)
303 })?;
304 let _alg = r.next().read_der()?;
306 let _sig = r.next().read_bitvec_bytes()?;
307 Ok(serials)
308 })
309 })
310 .expect("parse tbsCertList");
311
312 assert_eq!(serials, vec![vec![0x12, 0x34, 0x56]]);
313 }
314
315 #[test]
316 fn crl_distribution_point_is_a_sequence_carrying_the_uri() {
317 let uri = "http://127.0.0.1:9999/abc.crl";
318 let der = crl_distribution_point_der(uri);
319 yasna::parse_der(&der, |r| {
320 r.read_sequence(|r| {
321 let _dp = r.next().read_der()?;
322 Ok(())
323 })
324 })
325 .expect("CRLDistributionPoints is a SEQUENCE");
326 assert!(
327 der.windows(uri.len()).any(|w| w == uri.as_bytes()),
328 "URI present in the distribution point"
329 );
330 }
331}