1#![cfg_attr(not(feature = "std"), no_std)]
2#![deny(unsafe_code)]
3
4#[cfg(feature = "alloc")]
5extern crate alloc;
6#[cfg(feature = "alloc")]
7use alloc::vec::Vec;
8use core::fmt;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Error {
12 InvalidDer,
13 InvalidCertificate,
14 InvalidSpki,
15 InvalidPublicKey,
16 Truncated,
17 InvalidOid,
18 InvalidExtension,
19 InvalidValidity,
20 InvalidTime,
21}
22
23impl fmt::Display for Error {
24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25 match self {
26 Self::InvalidDer => f.write_str("invalid DER encoding"),
27 Self::InvalidCertificate => f.write_str("invalid X.509 certificate structure"),
28 Self::InvalidSpki => f.write_str("invalid SubjectPublicKeyInfo"),
29 Self::InvalidPublicKey => f.write_str("invalid public key BIT STRING"),
30 Self::Truncated => f.write_str("truncated DER input"),
31 Self::InvalidOid => f.write_str("invalid OID encoding"),
32 Self::InvalidExtension => f.write_str("invalid extension"),
33 Self::InvalidValidity => f.write_str("invalid validity period"),
34 Self::InvalidTime => f.write_str("invalid time encoding"),
35 }
36 }
37}
38
39#[cfg(feature = "std")]
40impl std::error::Error for Error {}
41
42#[derive(Debug)]
43struct Tlv<'a> {
44 tag: u8,
45 raw: &'a [u8],
46 value: &'a [u8],
47}
48
49fn read_tlv(data: &[u8]) -> Result<Tlv<'_>, Error> {
50 if data.is_empty() {
51 return Err(Error::Truncated);
52 }
53 let tag = data[0];
54 let consumed = 1;
55
56 if data.len() <= consumed {
57 return Err(Error::Truncated);
58 }
59 let len_byte = data[consumed];
60
61 let (len, len_size) = if len_byte & 0x80 == 0 {
62 (len_byte as usize, 1)
63 } else {
64 let num_bytes = (len_byte & 0x7f) as usize;
65 if num_bytes == 0 || num_bytes > core::mem::size_of::<usize>() {
66 return Err(Error::InvalidDer);
67 }
68 if data.len() <= consumed + num_bytes {
69 return Err(Error::Truncated);
70 }
71 if num_bytes > 1 && data[consumed + 1] == 0 {
72 return Err(Error::InvalidDer);
73 }
74 let mut l = 0usize;
75 for i in 0..num_bytes {
76 l = (l << 8) | data[consumed + 1 + i] as usize;
77 }
78 if l < 128 {
79 return Err(Error::InvalidDer);
80 }
81 (l, 1 + num_bytes)
82 };
83
84 let start = consumed + len_size;
85 let end = start.checked_add(len).ok_or(Error::InvalidDer)?;
86 if end > data.len() {
87 return Err(Error::Truncated);
88 }
89
90 Ok(Tlv {
91 tag,
92 raw: &data[..end],
93 value: &data[start..end],
94 })
95}
96
97fn skip_tlv(data: &[u8], offset: &mut usize) -> Result<(), Error> {
98 let tlv = read_tlv(&data[*offset..])?;
99 *offset += tlv.raw.len();
100 Ok(())
101}
102
103fn skip_tag(data: &[u8], offset: &mut usize, expected_tag: u8) -> Result<(), Error> {
104 if *offset >= data.len() || data[*offset] != expected_tag {
105 return Err(Error::InvalidCertificate);
106 }
107 skip_tlv(data, offset)
108}
109
110pub fn extract_key_from_spki(spki_der: &[u8]) -> Result<&[u8], Error> {
117 let spki = read_tlv(spki_der)?;
118 if spki.tag != 0x30 {
119 return Err(Error::InvalidSpki);
120 }
121 if spki.value.first() == Some(&0x30) {
125 let mut offset = 0;
127 skip_tlv(spki.value, &mut offset)?;
128 let inner = &spki.value[offset..];
129 let bs = read_tlv(inner)?;
130 if bs.tag != 0x03 {
131 return Err(Error::InvalidSpki);
132 }
133 if bs.value.is_empty() {
134 return Err(Error::InvalidPublicKey);
135 }
136 Ok(&bs.value[1..])
137 } else {
138 let after = &spki_der[spki.raw.len()..];
140 if after.is_empty() {
141 return Err(Error::InvalidPublicKey);
142 }
143 let bs = read_tlv(after)?;
144 if bs.tag != 0x03 {
145 return Err(Error::InvalidSpki);
146 }
147 if bs.value.is_empty() {
148 return Err(Error::InvalidPublicKey);
149 }
150 Ok(&bs.value[1..])
151 }
152}
153
154pub fn extract_spki_from_cert<'a>(cert_der: &'a [u8]) -> Result<&'a [u8], Error> {
159 let fields = walk_tbs(cert_der)?;
160 Ok(fields.spki)
161}
162
163#[cfg(feature = "alloc")]
168pub fn extract_public_key_from_cert(cert_der: &[u8]) -> Result<Vec<u8>, Error> {
169 let spki = extract_spki_from_cert(cert_der)?;
170 let key = extract_key_from_spki(spki)?;
171 Ok(key.to_vec())
172}
173
174pub const OID_SAN: &[u8] = &[0x55, 0x1d, 0x11];
178pub const OID_KEY_USAGE: &[u8] = &[0x55, 0x1d, 0x0f];
180pub const OID_EKU: &[u8] = &[0x55, 0x1d, 0x25];
182pub const OID_BASIC_CONSTRAINTS: &[u8] = &[0x55, 0x1d, 0x13];
184pub const OID_EKU_SERVER_AUTH: &[u8] = &[0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01];
186
187pub const OID_ED25519: &[u8] = &[0x2b, 0x65, 0x70];
190pub const OID_ECDSA_SHA256: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02];
192pub const OID_ECDSA_SHA384: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03];
194pub const OID_EC_PUBLIC_KEY: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
196pub const OID_RSA_SHA256: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b];
198pub const OID_RSA_SHA384: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c];
200pub const OID_RSA_SHA512: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d];
202pub const OID_RSA_PSS: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a];
204pub const OID_EC_PUBLIC_KEY_ALG: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
206
207struct TbsFields<'a> {
211 issuer_dn: &'a [u8],
213 subject_dn: &'a [u8],
215 spki: &'a [u8],
217}
218
219fn walk_tbs(cert_der: &[u8]) -> Result<TbsFields<'_>, Error> {
222 let cert = read_tlv(cert_der)?;
223 if cert.tag != 0x30 {
224 return Err(Error::InvalidCertificate);
225 }
226 let tbs = read_tlv(cert.value)?;
227 if tbs.tag != 0x30 {
228 return Err(Error::InvalidCertificate);
229 }
230 let inner = tbs.value;
231 let mut offset = 0;
232
233 if offset >= inner.len() {
234 return Err(Error::InvalidCertificate);
235 }
236
237 if inner[offset] == 0xa0 {
239 skip_tlv(inner, &mut offset)?;
240 }
241
242 skip_tag(inner, &mut offset, 0x02)?;
244 skip_tag(inner, &mut offset, 0x30)?;
246
247 if offset >= inner.len() || inner[offset] != 0x30 {
249 return Err(Error::InvalidCertificate);
250 }
251 let issuer_tlv = read_tlv(&inner[offset..])?;
252 let issuer_dn = &inner[offset..offset + issuer_tlv.raw.len()];
253
254 offset += issuer_tlv.raw.len();
256
257 skip_tag(inner, &mut offset, 0x30)?;
259
260 if offset >= inner.len() || inner[offset] != 0x30 {
262 return Err(Error::InvalidCertificate);
263 }
264 let subject_tlv = read_tlv(&inner[offset..])?;
265 let subject_dn = &inner[offset..offset + subject_tlv.raw.len()];
266 offset += subject_tlv.raw.len();
267
268 while offset < inner.len() && (inner[offset] & 0xa0) == 0xa0 {
271 skip_tlv(inner, &mut offset)?;
272 }
273
274 if offset >= inner.len() || inner[offset] != 0x30 {
276 return Err(Error::InvalidCertificate);
277 }
278 let spki_tlv = read_tlv(&inner[offset..])?;
279 let spki = &inner[offset..offset + spki_tlv.raw.len()];
280
281 Ok(TbsFields {
282 issuer_dn,
283 subject_dn,
284 spki,
285 })
286}
287
288pub fn find_extension<'a>(cert_der: &'a [u8], oid: &[u8]) -> Option<&'a [u8]> {
295 let (tbs_inner, extensions_start) = find_extensions_section(cert_der)?;
296 let wrapper = read_tlv(&tbs_inner[extensions_start..]).ok()?;
298 let ext_seq = read_tlv(wrapper.value).ok()?;
299 find_oid_in_extensions(ext_seq.value, oid)
300}
301
302fn find_extensions_section(cert_der: &[u8]) -> Option<(&[u8], usize)> {
305 let cert = read_tlv(cert_der).ok()?;
306 if cert.tag != 0x30 {
307 return None;
308 }
309 let tbs = read_tlv(cert.value).ok()?;
310 if tbs.tag != 0x30 {
311 return None;
312 }
313 let inner = tbs.value;
314 let mut offset = 0;
315
316 if offset < inner.len() && inner[offset] == 0xa0 {
317 let _ = skip_tlv(inner, &mut offset);
318 }
319 let _ = skip_tag(inner, &mut offset, 0x02);
320 let _ = skip_tag(inner, &mut offset, 0x30);
321 let _ = skip_tag(inner, &mut offset, 0x30);
322 let _ = skip_tag(inner, &mut offset, 0x30);
323 let _ = skip_tag(inner, &mut offset, 0x30);
324
325 while offset < inner.len()
327 && (inner[offset] == 0x81 || inner[offset] == 0x82 || inner[offset] == 0xa1 || inner[offset] == 0xa2)
328 {
329 let _ = skip_tlv(inner, &mut offset);
330 }
331
332 let _ = skip_tag(inner, &mut offset, 0x30);
334
335 if offset < inner.len() && inner[offset] == 0xa3 {
337 Some((inner, offset))
338 } else {
339 None
340 }
341}
342
343fn find_oid_in_extensions<'a>(extensions: &'a [u8], oid: &[u8]) -> Option<&'a [u8]> {
344 let mut offset = 0;
345 while offset < extensions.len() {
346 let ext = read_tlv(&extensions[offset..]).ok()?;
347 offset += ext.raw.len();
348 if ext.tag != 0x30 {
349 continue;
350 }
351 let mut off = 0;
352 let oid_tlv = read_tlv(&ext.value[off..]).ok()?;
354 if oid_tlv.tag != 0x06 {
355 continue;
356 }
357 off += oid_tlv.raw.len();
358
359 if off < ext.value.len() && ext.value[off] == 0x01 {
361 let bool_tlv = read_tlv(&ext.value[off..]).ok()?;
362 off += bool_tlv.raw.len();
363 }
364
365 if off >= ext.value.len() || ext.value[off] != 0x04 {
367 continue;
368 }
369 let val_tlv = read_tlv(&ext.value[off..]).ok()?;
370
371 if oid_tlv.value == oid {
372 return Some(val_tlv.value);
373 }
374 }
375 None
376}
377
378pub fn extract_issuer_dn(cert_der: &[u8]) -> Result<&[u8], Error> {
386 let fields = walk_tbs(cert_der)?;
387 let issuer_tlv = read_tlv(fields.issuer_dn)?;
389 Ok(issuer_tlv.value)
390}
391
392pub fn extract_subject_dn(cert_der: &[u8]) -> Result<&[u8], Error> {
398 let fields = walk_tbs(cert_der)?;
399 let subject_tlv = read_tlv(fields.subject_dn)?;
400 Ok(subject_tlv.value)
401}
402
403pub fn dn_equal(a: &[u8], b: &[u8]) -> bool {
413 let pairs_a = flatten_dn(a);
414 let pairs_b = flatten_dn(b);
415 if pairs_a.len() != pairs_b.len() {
416 return false;
417 }
418 for (pa, pb) in pairs_a.iter().zip(pairs_b.iter()) {
419 if pa.0 != pb.0 || pa.1 != pb.1 {
420 return false;
421 }
422 }
423 true
424}
425
426#[cfg(feature = "alloc")]
428pub fn debug_dn_pairs(dn: &[u8]) -> alloc::string::String {
429 let pairs = flatten_dn(dn);
430 let parts: alloc::vec::Vec<_> = pairs
431 .into_iter()
432 .map(|(oid, val)| {
433 let oid_name = oid_to_name(oid);
434 let val_str = if val.iter().all(|b| b.is_ascii_graphic() || *b == b' ') {
435 alloc::string::String::from_utf8_lossy(val).into_owned()
436 } else {
437 alloc::format!("{:02x?}", val)
438 };
439 alloc::format!("{oid_name}={val_str}")
440 })
441 .collect();
442 parts.join(", ")
443}
444
445fn oid_to_name(oid: &[u8]) -> &'static str {
446 match oid {
447 &[0x55, 0x04, 0x03] => "CN",
448 &[0x55, 0x04, 0x06] => "C",
449 &[0x55, 0x04, 0x07] => "L",
450 &[0x55, 0x04, 0x08] => "ST",
451 &[0x55, 0x04, 0x0a] => "O",
452 &[0x55, 0x04, 0x0b] => "OU",
453 _ => "(unknown OID)",
454 }
455}
456
457fn flatten_dn(dn: &[u8]) -> alloc::vec::Vec<(&[u8], &[u8])> {
463 let mut pairs = alloc::vec::Vec::new();
464 let mut offset = 0;
465 while offset < dn.len() && dn[offset] == 0x31 {
466 let rdn = match read_tlv(&dn[offset..]) {
468 Ok(t) => t,
469 Err(_) => break,
470 };
471 offset += rdn.raw.len();
472
473 let mut rdn_off = 0;
475 while rdn_off < rdn.value.len() && rdn.value[rdn_off] == 0x30 {
476 let attr = match read_tlv(&rdn.value[rdn_off..]) {
477 Ok(t) => t,
478 Err(_) => break,
479 };
480 rdn_off += attr.raw.len();
481
482 if let Some((oid, val)) = parse_attribute(attr.value) {
484 pairs.push((oid, val));
485 }
486 }
487 }
488 pairs.sort_by(|a, b| a.0.cmp(b.0).then(a.1.cmp(b.1)));
490 pairs
491}
492
493fn parse_attribute(attr_value: &[u8]) -> Option<(&[u8], &[u8])> {
495 let mut off = 0;
496 let oid_tlv = read_tlv(&attr_value[off..]).ok()?;
497 if oid_tlv.tag != 0x06 {
498 return None;
499 }
500 off += oid_tlv.raw.len();
501 if off >= attr_value.len() {
502 return None;
503 }
504 let val_tlv = read_tlv(&attr_value[off..]).ok()?;
507 Some((oid_tlv.value, val_tlv.value))
508}
509
510pub fn extract_signature_algorithm_oid(cert_der: &[u8]) -> Result<&[u8], Error> {
518 let cert = read_tlv(cert_der)?;
519 if cert.tag != 0x30 {
520 return Err(Error::InvalidCertificate);
521 }
522 let tbs = read_tlv(cert.value)?;
523 if tbs.tag != 0x30 {
524 return Err(Error::InvalidCertificate);
525 }
526 let sig_alg_offset = tbs.raw.len();
528 if sig_alg_offset >= cert.value.len() {
529 return Err(Error::InvalidCertificate);
530 }
531 let sig_alg = read_tlv(&cert.value[sig_alg_offset..])?;
532 if sig_alg.tag != 0x30 {
533 return Err(Error::InvalidCertificate);
534 }
535 let oid = read_tlv(sig_alg.value)?;
537 if oid.tag != 0x06 {
538 return Err(Error::InvalidCertificate);
539 }
540 Ok(oid.value)
541}
542
543pub fn extract_tbs_cert(cert_der: &[u8]) -> Result<&[u8], Error> {
548 let cert = read_tlv(cert_der)?;
549 if cert.tag != 0x30 {
550 return Err(Error::InvalidCertificate);
551 }
552 let tbs = read_tlv(cert.value)?;
553 if tbs.tag != 0x30 {
554 return Err(Error::InvalidCertificate);
555 }
556 Ok(tbs.raw)
557}
558
559pub fn extract_signature_value(cert_der: &[u8]) -> Result<&[u8], Error> {
564 let cert = read_tlv(cert_der)?;
565 if cert.tag != 0x30 {
566 return Err(Error::InvalidCertificate);
567 }
568 let tbs = read_tlv(cert.value)?;
569 if tbs.tag != 0x30 {
570 return Err(Error::InvalidCertificate);
571 }
572 let after_tbs = &cert.value[tbs.raw.len()..];
573 let sig_alg = read_tlv(after_tbs)?;
574 if sig_alg.tag != 0x30 {
575 return Err(Error::InvalidCertificate);
576 }
577 let after_sig_alg = &after_tbs[sig_alg.raw.len()..];
578 let sig = read_tlv(after_sig_alg)?;
579 if sig.tag != 0x03 {
580 return Err(Error::InvalidCertificate);
581 }
582 if sig.value.is_empty() {
583 return Err(Error::InvalidCertificate);
584 }
585 Ok(&sig.value[1..])
586}
587
588#[cfg(feature = "alloc")]
594pub fn parse_san_dns_names(cert_der: &[u8]) -> Result<Vec<Vec<u8>>, Error> {
595 let san_value = find_extension(cert_der, OID_SAN).ok_or(Error::InvalidExtension)?;
596 let general_names = read_tlv(san_value)?;
597 if general_names.tag != 0x30 {
598 return Err(Error::InvalidExtension);
599 }
600 let mut dns_names = Vec::new();
601 let mut offset = 0;
602 while offset < general_names.value.len() {
603 if general_names.value[offset] == 0x82 {
604 let dns = read_tlv(&general_names.value[offset..])?;
606 dns_names.push(dns.value.to_vec());
607 offset += dns.raw.len();
608 } else {
609 skip_tlv(general_names.value, &mut offset)?;
610 }
611 }
612 Ok(dns_names)
613}
614
615pub fn check_san_dns_name(cert_der: &[u8], server_name: &str) -> Result<bool, Error> {
622 let san_value = match find_extension(cert_der, OID_SAN) {
623 Some(v) => v,
624 None => return Ok(false),
625 };
626 let general_names = read_tlv(san_value)?;
627 if general_names.tag != 0x30 {
628 return Err(Error::InvalidExtension);
629 }
630 let mut offset = 0;
631 while offset < general_names.value.len() {
632 if general_names.value[offset] == 0x82 {
633 let dns = read_tlv(&general_names.value[offset..])?;
634 if dns_name_matches(dns.value, server_name) {
635 return Ok(true);
636 }
637 offset += dns.raw.len();
638 } else {
639 skip_tlv(general_names.value, &mut offset)?;
640 }
641 }
642 Ok(false)
643}
644
645fn dns_name_matches(san_entry: &[u8], server_name: &str) -> bool {
646 let Ok(san_str) = core::str::from_utf8(san_entry) else {
647 return false;
648 };
649 server_name_matches_wildcard(san_str, server_name)
650}
651
652fn server_name_matches_wildcard(dns_name: &str, server_name: &str) -> bool {
653 let dns_name = dns_name.to_ascii_lowercase();
654 let server_name = server_name.to_ascii_lowercase();
655
656 if let Some(rest) = dns_name.strip_prefix("*.") {
657 let Some(dot_pos) = server_name.find('.') else {
658 return false;
659 };
660 let suffix = &server_name[dot_pos..];
661 rest.eq_ignore_ascii_case(suffix)
662 && !server_name[..dot_pos].is_empty()
663 && server_name[dot_pos + 1..].contains('.') == rest.contains('.')
664 } else {
665 dns_name == server_name
666 }
667}
668
669pub mod key_usage {
673 pub const DIGITAL_SIGNATURE: u8 = 0;
674 pub const KEY_ENCIPHERMENT: u8 = 2;
675 pub const KEY_CERT_SIGN: u8 = 5;
676}
677
678pub fn parse_key_usage(cert_der: &[u8]) -> Option<u16> {
682 let ku_value = find_extension(cert_der, OID_KEY_USAGE)?;
683 let bs = read_tlv(ku_value).ok()?;
684 if bs.tag != 0x03 {
685 return None;
686 }
687 let unused = bs.value.first().copied().unwrap_or(0);
688 let bits = &bs.value[1..];
689 let mut mask: u16 = 0;
690 for (i, &byte) in bits.iter().enumerate() {
691 mask |= (byte as u16) << (8 * i);
692 }
693 if unused > 0 {
695 mask &= !(0xffff << (16 - unused as usize));
696 }
697 Some(mask)
698}
699
700pub fn has_eku_server_auth(cert_der: &[u8]) -> Option<bool> {
708 let eku_value = find_extension(cert_der, OID_EKU)?;
709 let seq = read_tlv(eku_value).ok()?;
710 if seq.tag != 0x30 {
711 return Some(false);
712 }
713 let mut offset = 0;
714 while offset < seq.value.len() {
715 let oid = read_tlv(&seq.value[offset..]).ok()?;
716 if oid.tag == 0x06 && oid.value == OID_EKU_SERVER_AUTH {
717 return Some(true);
718 }
719 offset += oid.raw.len();
720 }
721 Some(false)
722}
723
724pub fn is_ca(cert_der: &[u8]) -> Option<bool> {
731 let bc_value = find_extension(cert_der, OID_BASIC_CONSTRAINTS)?;
732 let seq = read_tlv(bc_value).ok()?;
733 if seq.tag != 0x30 {
734 return Some(false);
735 }
736 if seq.value.first() == Some(&0x01) {
738 let bool_tlv = read_tlv(seq.value).ok()?;
739 return Some(bool_tlv.value == [0xff]);
740 }
741 Some(false)
742}
743
744#[derive(Debug, Clone, Copy, PartialEq, Eq)]
748pub struct X509Time {
749 pub year: u16,
750 pub month: u8,
751 pub day: u8,
752 pub hour: u8,
753 pub minute: u8,
754 pub second: u8,
755}
756
757impl X509Time {
758 pub fn to_unix_seconds(self) -> u64 {
760 let days = days_from_civil(self.year as i32, self.month, self.day);
761 let unix_epoch_days = days_from_civil(1970, 1, 1);
762 let day_offset = (days - unix_epoch_days) as u64;
763 day_offset * 86400 + self.hour as u64 * 3600 + self.minute as u64 * 60 + self.second as u64
764 }
765}
766
767pub fn parse_validity(cert_der: &[u8]) -> Result<(X509Time, X509Time), Error> {
771 let cert = read_tlv(cert_der)?;
772 if cert.tag != 0x30 {
773 return Err(Error::InvalidCertificate);
774 }
775 let tbs = read_tlv(cert.value)?;
776 if tbs.tag != 0x30 {
777 return Err(Error::InvalidCertificate);
778 }
779 let inner = tbs.value;
780 let mut offset = 0;
781
782 if offset < inner.len() && inner[offset] == 0xa0 {
784 skip_tlv(inner, &mut offset)?;
785 }
786 skip_tag(inner, &mut offset, 0x02)?; skip_tag(inner, &mut offset, 0x30)?; skip_tag(inner, &mut offset, 0x30)?; if offset >= inner.len() || inner[offset] != 0x30 {
792 return Err(Error::InvalidValidity);
793 }
794 let validity = read_tlv(&inner[offset..])?;
795
796 let mut voff = 0;
797 let not_before = parse_x509_time_element(&validity.value, &mut voff)?;
798 let not_after = parse_x509_time_element(&validity.value, &mut voff)?;
799
800 Ok((not_before, not_after))
801}
802
803fn parse_x509_time_element(data: &[u8], offset: &mut usize) -> Result<X509Time, Error> {
804 if *offset >= data.len() {
806 return Err(Error::InvalidTime);
807 }
808 let tag = data[*offset];
809 if tag != 0x17 && tag != 0x18 {
810 return Err(Error::InvalidTime);
811 }
812 let tlv = read_tlv(&data[*offset..])?;
813 *offset += tlv.raw.len();
814
815 let bytes = tlv.value;
816
817 if tag == 0x17 {
818 if bytes.len() < 11 || bytes.last() != Some(&b'Z') {
820 return Err(Error::InvalidTime);
821 }
822 let yy = parse_two_digits(bytes, 0)?;
823 let year = if yy >= 50 { 1900 + yy as u16 } else { 2000 + yy as u16 };
824 Ok(X509Time {
825 year,
826 month: parse_two_digits(bytes, 2)?,
827 day: parse_two_digits(bytes, 4)?,
828 hour: parse_two_digits(bytes, 6)?,
829 minute: parse_two_digits(bytes, 8)?,
830 second: if bytes.len() >= 13 {
831 parse_two_digits(bytes, 10)?
832 } else {
833 0
834 },
835 })
836 } else {
837 if bytes.len() < 15 || bytes.last() != Some(&b'Z') {
839 return Err(Error::InvalidTime);
840 }
841 Ok(X509Time {
842 year: parse_four_digits(bytes, 0)?,
843 month: parse_two_digits(bytes, 4)?,
844 day: parse_two_digits(bytes, 6)?,
845 hour: parse_two_digits(bytes, 8)?,
846 minute: parse_two_digits(bytes, 10)?,
847 second: parse_two_digits(bytes, 12)?,
848 })
849 }
850}
851
852fn parse_two_digits(bytes: &[u8], pos: usize) -> Result<u8, Error> {
853 if pos + 1 >= bytes.len() {
854 return Err(Error::InvalidTime);
855 }
856 let hi = digit(bytes[pos])?;
857 let lo = digit(bytes[pos + 1])?;
858 Ok(hi * 10 + lo)
859}
860
861fn parse_four_digits(bytes: &[u8], pos: usize) -> Result<u16, Error> {
862 let hi = parse_two_digits(bytes, pos)? as u16;
863 let lo = parse_two_digits(bytes, pos + 2)? as u16;
864 Ok(hi * 100 + lo)
865}
866
867fn digit(b: u8) -> Result<u8, Error> {
868 if b.is_ascii_digit() {
869 Ok(b - b'0')
870 } else {
871 Err(Error::InvalidTime)
872 }
873}
874
875fn days_from_civil(y: i32, m: u8, d: u8) -> i32 {
879 let y = y as i32 - i32::from(m <= 2);
880 let era = if y >= 0 { y } else { y - 399 } / 400;
881 let yoe = (y - era * 400) as u32;
882 let doy = (153 * (if m as u32 > 2 { m as u32 - 3 } else { m as u32 + 9 }) + 2) / 5 + d as u32 - 1;
883 let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
884 era as i32 * 146097 + doe as i32
885}
886
887pub fn extract_spki_algorithm_oid(spki_der: &[u8]) -> Result<&[u8], Error> {
894 let top = read_tlv(spki_der)?;
895 if top.tag != 0x30 {
896 return Err(Error::InvalidSpki);
897 }
898 let alg_id = if top.value.first() == Some(&0x30) {
903 read_tlv(top.value)?
904 } else {
905 top
906 };
907 if alg_id.tag != 0x30 {
908 return Err(Error::InvalidSpki);
909 }
910 let oid = read_tlv(alg_id.value)?;
911 if oid.tag != 0x06 {
912 return Err(Error::InvalidSpki);
913 }
914 Ok(oid.value)
915}
916
917#[cfg(test)]
918mod tests {
919 use super::*;
920
921 fn decode_hex(s: &str) -> Vec<u8> {
922 (0..s.len())
923 .step_by(2)
924 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
925 .collect()
926 }
927
928 #[test]
929 fn parse_empty() {
930 assert_eq!(read_tlv(b"").unwrap_err(), Error::Truncated);
931 }
932
933 #[test]
934 fn parse_truncated_tag() {
935 assert_eq!(read_tlv(b"\x30").unwrap_err(), Error::Truncated);
936 }
937
938 #[test]
939 fn parse_truncated_value() {
940 assert_eq!(read_tlv(b"\x30\x05").unwrap_err(), Error::Truncated);
941 }
942
943 #[test]
944 fn parse_null() {
945 let tlv = read_tlv(b"\x05\x00").unwrap();
946 assert_eq!(tlv.tag, 0x05);
947 assert!(tlv.value.is_empty());
948 assert_eq!(tlv.raw, b"\x05\x00");
949 }
950
951 #[test]
952 fn parse_integer() {
953 let tlv = read_tlv(b"\x02\x03\x01\x00\x01").unwrap();
954 assert_eq!(tlv.tag, 0x02);
955 assert_eq!(tlv.value, b"\x01\x00\x01");
956 assert_eq!(tlv.raw, b"\x02\x03\x01\x00\x01");
957 }
958
959 #[test]
960 fn parse_long_form_length() {
961 let payload = [0x42u8; 256];
962 let mut raw = vec![0x04, 0x82, 0x01, 0x00];
963 raw.extend_from_slice(&payload);
964 let tlv = read_tlv(&raw).unwrap();
965 assert_eq!(tlv.tag, 0x04);
966 assert_eq!(tlv.value.len(), 256);
967 }
968
969 #[test]
970 fn parse_sequence() {
971 let tlv = read_tlv(b"\x30\x06\x02\x01\x01\x02\x01\x02").unwrap();
972 assert_eq!(tlv.tag, 0x30);
973 assert_eq!(tlv.value, b"\x02\x01\x01\x02\x01\x02");
974 }
975
976 #[test]
977 fn spki_roundtrip() {
978 let der = decode_hex(
979 "3059301306072a8648ce3d020106082a8648ce3d030107034200\
980 04deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef\
981 deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef\
982 deadbeefdeadbeefdeadbeefdeadbeef",
983 );
984 let key = extract_key_from_spki(&der).unwrap();
985 assert_eq!(key.len(), 65);
986 assert_eq!(key[0], 0x04);
987 }
988
989 #[test]
990 fn extract_from_real_cert() {
991 let der = include_bytes!("tests/p256_cert.der");
992 let spki = extract_spki_from_cert(der).unwrap();
993 let key = extract_key_from_spki(spki).unwrap();
994 assert_eq!(key.len(), 65);
995 assert_eq!(key[0], 0x04);
996 }
997
998 #[test]
999 #[cfg(feature = "alloc")]
1000 fn extract_pk_alloc() {
1001 let der = include_bytes!("tests/p256_cert.der");
1002 let pk = extract_public_key_from_cert(der).unwrap();
1003 assert_eq!(pk.len(), 65);
1004 assert_eq!(pk[0], 0x04);
1005 }
1006
1007 #[test]
1008 fn reject_invalid_spki() {
1009 assert_eq!(extract_key_from_spki(b"\x05\x00").unwrap_err(), Error::InvalidSpki);
1010 }
1011
1012 #[test]
1013 fn reject_truncated_cert() {
1014 assert_eq!(extract_spki_from_cert(b"\x30\x03\x02\x01").unwrap_err(), Error::Truncated);
1015 }
1016
1017 #[test]
1018 fn time_parsing_utc_time() {
1019 let utc = b"\x17\x0d230101120000Z";
1020 let mut offset = 0;
1021 let t = parse_x509_time_element(utc, &mut offset).unwrap();
1022 assert_eq!(t.year, 2023);
1023 assert_eq!(t.month, 1);
1024 assert_eq!(t.day, 1);
1025 assert_eq!(t.hour, 12);
1026 assert_eq!(t.minute, 0);
1027 assert_eq!(t.second, 0);
1028 assert_eq!(t.to_unix_seconds(), 1672574400);
1030 }
1031
1032 #[test]
1033 fn time_parsing_generalized_time() {
1034 let gt = b"\x18\x0f20230101120000Z";
1035 let mut offset = 0;
1036 let t = parse_x509_time_element(gt, &mut offset).unwrap();
1037 assert_eq!(t.year, 2023);
1038 assert_eq!(t.month, 1);
1039 assert_eq!(t.day, 1);
1040 assert_eq!(t.hour, 12);
1041 assert_eq!(t.minute, 0);
1042 assert_eq!(t.second, 0);
1043 assert_eq!(t.to_unix_seconds(), 1672574400);
1044 }
1045
1046 #[test]
1047 fn time_parsing_pre_2000_utc() {
1048 let utc = b"\x17\x0d990101000000Z";
1050 let mut offset = 0;
1051 let t = parse_x509_time_element(utc, &mut offset).unwrap();
1052 assert_eq!(t.year, 1999);
1053 assert_eq!(t.month, 1);
1054 assert_eq!(t.day, 1);
1055 }
1056
1057 #[test]
1058 fn validity_from_cert() {
1059 let der = include_bytes!("tests/p256_cert.der");
1060 let (nb, na) = parse_validity(der).unwrap();
1061 assert!(nb.year > 2000);
1062 assert!(na.year > nb.year);
1063 }
1064
1065 #[test]
1066 fn extract_dns_names() {
1067 let der = include_bytes!("tests/p256_cert.der");
1070 let _ = parse_san_dns_names(der);
1072 }
1073}