• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::ops::{Add, Sub};
2 use std::time::Duration;
3 use std::time::SystemTime;
4 
5 use chrono::offset::{TimeZone, Utc};
6 use chrono::{DateTime, Datelike};
7 use der_parser::ber::{ber_read_element_header, BerObjectContent, BerTag, MAX_OBJECT_SIZE};
8 use der_parser::der::{parse_der_generalizedtime, parse_der_utctime, DerObject};
9 use der_parser::error::{BerError, DerResult};
10 use nom::branch::alt;
11 use nom::bytes::complete::take;
12 use nom::combinator::{complete, map_res, opt};
13 
14 use crate::error::{X509Error, X509Result};
15 use crate::traits::FromDer;
16 
17 /// An ASN.1 timestamp.
18 #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
19 pub struct ASN1Time(DateTime<Utc>);
20 
21 impl ASN1Time {
from_der_opt(i: &[u8]) -> X509Result<Option<Self>>22     pub(crate) fn from_der_opt(i: &[u8]) -> X509Result<Option<Self>> {
23         opt(map_res(parse_choice_of_time, der_to_utctime))(i)
24             .map_err(|_| X509Error::InvalidDate.into())
25     }
26 
27     #[inline]
from_datetime_utc(dt: DateTime<Utc>) -> Self28     pub(crate) fn from_datetime_utc(dt: DateTime<Utc>) -> Self {
29         ASN1Time(dt)
30     }
31 
32     /// Makes a new `ASN1Time` from the number of non-leap seconds since Epoch
from_timestamp(secs: i64) -> Self33     pub fn from_timestamp(secs: i64) -> Self {
34         ASN1Time(Utc.timestamp(secs, 0))
35     }
36 
37     /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
38     #[inline]
timestamp(&self) -> i6439     pub fn timestamp(&self) -> i64 {
40         self.0.timestamp()
41     }
42 
43     /// Returns a `ASN1Time` which corresponds to the current date.
44     #[inline]
now() -> Self45     pub fn now() -> Self {
46         ASN1Time(SystemTime::now().into())
47     }
48 
49     /// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`.
50     #[inline]
to_rfc2822(self) -> String51     pub fn to_rfc2822(self) -> String {
52         self.0.to_rfc2822()
53     }
54 }
55 
56 impl<'a> FromDer<'a> for ASN1Time {
from_der(i: &[u8]) -> X509Result<Self>57     fn from_der(i: &[u8]) -> X509Result<Self> {
58         map_res(parse_choice_of_time, der_to_utctime)(i).map_err(|_| X509Error::InvalidDate.into())
59     }
60 }
61 
parse_choice_of_time(i: &[u8]) -> DerResult62 fn parse_choice_of_time(i: &[u8]) -> DerResult {
63     alt((
64         complete(parse_der_utctime),
65         complete(parse_der_generalizedtime),
66         complete(parse_malformed_date),
67     ))(i)
68 }
69 
70 // allow relaxed parsing of UTCTime (ex: 370116130016+0000)
parse_malformed_date(i: &[u8]) -> DerResult71 fn parse_malformed_date(i: &[u8]) -> DerResult {
72     #[allow(clippy::trivially_copy_pass_by_ref)]
73     fn check_char(b: &u8) -> bool {
74         (0x20 <= *b && *b <= 0x7f) || (*b == b'+')
75     }
76     let (rem, hdr) = ber_read_element_header(i)?;
77     let len = hdr.len.primitive()?;
78     if len > MAX_OBJECT_SIZE {
79         return Err(nom::Err::Error(BerError::InvalidLength));
80     }
81     match hdr.tag {
82         BerTag::UtcTime => {
83             // if we are in this function, the PrintableString could not be validated.
84             // Accept it without validating charset, because some tools do not respect the charset
85             // restrictions (for ex. they use '*' while explicingly disallowed)
86             let (rem, data) = take(len as usize)(rem)?;
87             if !data.iter().all(check_char) {
88                 return Err(nom::Err::Error(BerError::BerValueError));
89             }
90             let s = std::str::from_utf8(data).map_err(|_| BerError::BerValueError)?;
91             let content = BerObjectContent::UTCTime(s);
92             let obj = DerObject::from_header_and_content(hdr, content);
93             Ok((rem, obj))
94         }
95         _ => Err(nom::Err::Error(BerError::InvalidTag)),
96     }
97 }
98 
der_to_utctime(obj: DerObject) -> Result<ASN1Time, X509Error>99 pub(crate) fn der_to_utctime(obj: DerObject) -> Result<ASN1Time, X509Error> {
100     if let BerObjectContent::UTCTime(s) = obj.content {
101         let dt = if s.ends_with('Z') {
102             // UTC
103             if s.len() == 11 {
104                 // some implementations do not encode the number of seconds
105                 // accept certificate even if date is not correct
106                 Utc.datetime_from_str(s, "%y%m%d%H%MZ")
107             } else {
108                 Utc.datetime_from_str(s, "%y%m%d%H%M%SZ")
109             }
110         } else {
111             DateTime::parse_from_str(s, "%y%m%d%H%M%S%z").map(|dt| dt.with_timezone(&Utc))
112         };
113         match dt {
114             Ok(mut tm) => {
115                 if tm.year() < 50 {
116                     tm = tm
117                         .with_year(tm.year() + 100)
118                         .ok_or(X509Error::InvalidDate)?;
119                 }
120                 // tm = tm.with_year(tm.year() + 1900).ok_or(X509Error::InvalidDate)?;
121                 // eprintln!("date: {}", tm.rfc822());
122                 Ok(ASN1Time::from_datetime_utc(tm))
123             }
124             Err(_e) => Err(X509Error::InvalidDate),
125         }
126     } else if let BerObjectContent::GeneralizedTime(s) = obj.content {
127         let dt = if s.ends_with('Z') {
128             // UTC
129             if s.len() == 11 {
130                 // some implementations do not encode the number of seconds
131                 // accept certificate even if date is not correct
132                 Utc.datetime_from_str(s, "%Y%m%d%H%MZ")
133             } else {
134                 Utc.datetime_from_str(s, "%Y%m%d%H%M%SZ")
135             }
136         } else {
137             DateTime::parse_from_str(s, "%Y%m%d%H%M%S%z").map(|dt| dt.with_timezone(&Utc))
138         };
139         dt.map(ASN1Time::from_datetime_utc)
140             .or(Err(X509Error::InvalidDate))
141     } else {
142         Err(X509Error::InvalidDate)
143     }
144 }
145 
146 impl Add<Duration> for ASN1Time {
147     type Output = Option<ASN1Time>;
148 
149     #[inline]
add(self, rhs: Duration) -> Option<ASN1Time>150     fn add(self, rhs: Duration) -> Option<ASN1Time> {
151         let secs = rhs.as_secs();
152         // u32::MAX is not supported in rust 1.34
153         const MAX_U32: u64 = 4_294_967_295;
154         if secs > MAX_U32 {
155             return None;
156         }
157         let duration = chrono::Duration::seconds(secs as i64);
158         let dt = self.0.checked_add_signed(duration)?;
159         Some(ASN1Time(dt))
160     }
161 }
162 
163 impl Sub<ASN1Time> for ASN1Time {
164     type Output = Option<Duration>;
165 
166     #[inline]
sub(self, rhs: ASN1Time) -> Option<Duration>167     fn sub(self, rhs: ASN1Time) -> Option<Duration> {
168         self.0.signed_duration_since(rhs.0).to_std().ok()
169     }
170 }
171