1 /*
2 * X.509 Time Types
3 * (C) 1999-2007 Jack Lloyd
4 *
5 * Botan is released under the Simplified BSD License (see license.txt)
6 */
7 
8 #include <botan/asn1_obj.h>
9 #include <botan/der_enc.h>
10 #include <botan/ber_dec.h>
11 #include <botan/exceptn.h>
12 #include <botan/parsing.h>
13 #include <botan/calendar.h>
14 #include <sstream>
15 #include <iomanip>
16 
17 namespace Botan {
18 
ASN1_Time(const std::chrono::system_clock::time_point & time)19 ASN1_Time::ASN1_Time(const std::chrono::system_clock::time_point& time)
20    {
21    calendar_point cal = calendar_value(time);
22 
23    m_year   = cal.get_year();
24    m_month  = cal.get_month();
25    m_day    = cal.get_day();
26    m_hour   = cal.get_hour();
27    m_minute = cal.get_minutes();
28    m_second = cal.get_seconds();
29 
30    m_tag = (m_year >= 2050) ? GENERALIZED_TIME : UTC_TIME;
31    }
32 
ASN1_Time(const std::string & t_spec,ASN1_Tag tag)33 ASN1_Time::ASN1_Time(const std::string& t_spec, ASN1_Tag tag)
34    {
35    set_to(t_spec, tag);
36    }
37 
encode_into(DER_Encoder & der) const38 void ASN1_Time::encode_into(DER_Encoder& der) const
39    {
40    BOTAN_ARG_CHECK(m_tag == UTC_TIME || m_tag == GENERALIZED_TIME,
41                    "ASN1_Time: Bad encoding tag");
42 
43    der.add_object(m_tag, UNIVERSAL, to_string());
44    }
45 
decode_from(BER_Decoder & source)46 void ASN1_Time::decode_from(BER_Decoder& source)
47    {
48    BER_Object ber_time = source.get_next_object();
49 
50    set_to(ASN1::to_string(ber_time), ber_time.type());
51    }
52 
to_string() const53 std::string ASN1_Time::to_string() const
54    {
55    if(time_is_set() == false)
56       throw Invalid_State("ASN1_Time::to_string: No time set");
57 
58    uint32_t full_year = m_year;
59 
60    if(m_tag == UTC_TIME)
61       {
62       if(m_year < 1950 || m_year >= 2050)
63          throw Encoding_Error("ASN1_Time: The time " + readable_string() +
64                               " cannot be encoded as a UTCTime");
65 
66       full_year = (m_year >= 2000) ? (m_year - 2000) : (m_year - 1900);
67       }
68 
69    const uint64_t YEAR_FACTOR = 10000000000ULL;
70    const uint64_t MON_FACTOR  = 100000000;
71    const uint64_t DAY_FACTOR  = 1000000;
72    const uint64_t HOUR_FACTOR = 10000;
73    const uint64_t MIN_FACTOR  = 100;
74 
75    const uint64_t int_repr =
76       YEAR_FACTOR * full_year +
77       MON_FACTOR * m_month +
78       DAY_FACTOR * m_day +
79       HOUR_FACTOR * m_hour +
80       MIN_FACTOR * m_minute +
81       m_second;
82 
83    std::string repr = std::to_string(int_repr) + "Z";
84 
85    uint32_t desired_size = (m_tag == UTC_TIME) ? 13 : 15;
86 
87    while(repr.size() < desired_size)
88       repr = "0" + repr;
89 
90    return repr;
91    }
92 
readable_string() const93 std::string ASN1_Time::readable_string() const
94    {
95    if(time_is_set() == false)
96       throw Invalid_State("ASN1_Time::readable_string: No time set");
97 
98    // desired format: "%04d/%02d/%02d %02d:%02d:%02d UTC"
99    std::stringstream output;
100    output << std::setfill('0')
101           << std::setw(4) << m_year << "/"
102           << std::setw(2) << m_month << "/"
103           << std::setw(2) << m_day
104           << " "
105           << std::setw(2) << m_hour << ":"
106           << std::setw(2) << m_minute << ":"
107           << std::setw(2) << m_second
108           << " UTC";
109 
110    return output.str();
111    }
112 
time_is_set() const113 bool ASN1_Time::time_is_set() const
114    {
115    return (m_year != 0);
116    }
117 
cmp(const ASN1_Time & other) const118 int32_t ASN1_Time::cmp(const ASN1_Time& other) const
119    {
120    if(time_is_set() == false)
121       throw Invalid_State("ASN1_Time::cmp: No time set");
122 
123    const int32_t EARLIER = -1, LATER = 1, SAME_TIME = 0;
124 
125    if(m_year < other.m_year)     return EARLIER;
126    if(m_year > other.m_year)     return LATER;
127    if(m_month < other.m_month)   return EARLIER;
128    if(m_month > other.m_month)   return LATER;
129    if(m_day < other.m_day)       return EARLIER;
130    if(m_day > other.m_day)       return LATER;
131    if(m_hour < other.m_hour)     return EARLIER;
132    if(m_hour > other.m_hour)     return LATER;
133    if(m_minute < other.m_minute) return EARLIER;
134    if(m_minute > other.m_minute) return LATER;
135    if(m_second < other.m_second) return EARLIER;
136    if(m_second > other.m_second) return LATER;
137 
138    return SAME_TIME;
139    }
140 
set_to(const std::string & t_spec,ASN1_Tag spec_tag)141 void ASN1_Time::set_to(const std::string& t_spec, ASN1_Tag spec_tag)
142    {
143    if(spec_tag == UTC_OR_GENERALIZED_TIME)
144       {
145       try
146          {
147          set_to(t_spec, GENERALIZED_TIME);
148          return;
149          }
150       catch(Invalid_Argument&) {} // Not a generalized time. Continue
151 
152       try
153          {
154          set_to(t_spec, UTC_TIME);
155          return;
156          }
157       catch(Invalid_Argument&) {} // Not a UTC time. Continue
158 
159       throw Invalid_Argument("Time string could not be parsed as GeneralizedTime or UTCTime.");
160       }
161 
162    BOTAN_ASSERT(spec_tag == UTC_TIME || spec_tag == GENERALIZED_TIME, "Invalid tag.");
163 
164    BOTAN_ARG_CHECK(t_spec.size() > 0, "Time string must not be empty.");
165 
166    BOTAN_ARG_CHECK(t_spec.back() == 'Z', "Botan does not support times with timezones other than Z");
167 
168    if(spec_tag == GENERALIZED_TIME)
169       {
170       BOTAN_ARG_CHECK(t_spec.size() == 15, "Invalid GeneralizedTime string");
171       }
172    else if(spec_tag == UTC_TIME)
173       {
174       BOTAN_ARG_CHECK(t_spec.size() == 13, "Invalid UTCTime string");
175       }
176 
177    const size_t YEAR_SIZE = (spec_tag == UTC_TIME) ? 2 : 4;
178 
179    std::vector<std::string> params;
180    std::string current;
181 
182    for(size_t j = 0; j != YEAR_SIZE; ++j)
183       current += t_spec[j];
184    params.push_back(current);
185    current.clear();
186 
187    for(size_t j = YEAR_SIZE; j != t_spec.size() - 1; ++j)
188       {
189       current += t_spec[j];
190       if(current.size() == 2)
191          {
192          params.push_back(current);
193          current.clear();
194          }
195       }
196 
197    m_year   = to_u32bit(params[0]);
198    m_month  = to_u32bit(params[1]);
199    m_day    = to_u32bit(params[2]);
200    m_hour   = to_u32bit(params[3]);
201    m_minute = to_u32bit(params[4]);
202    m_second = (params.size() == 6) ? to_u32bit(params[5]) : 0;
203    m_tag    = spec_tag;
204 
205    if(spec_tag == UTC_TIME)
206       {
207       if(m_year >= 50) m_year += 1900;
208       else             m_year += 2000;
209       }
210 
211    if(!passes_sanity_check())
212       throw Invalid_Argument("Time " + t_spec + " does not seem to be valid");
213    }
214 
215 /*
216 * Do a general sanity check on the time
217 */
passes_sanity_check() const218 bool ASN1_Time::passes_sanity_check() const
219    {
220    // AppVeyor's trust store includes a cert with expiration date in 3016 ...
221    if(m_year < 1950 || m_year > 3100)
222       return false;
223    if(m_month == 0 || m_month > 12)
224       return false;
225 
226    const uint32_t days_in_month[12] = { 31, 28+1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
227 
228    if(m_day == 0 || m_day > days_in_month[m_month-1])
229       return false;
230 
231    if(m_month == 2 && m_day == 29)
232       {
233       if(m_year % 4 != 0)
234          return false; // not a leap year
235 
236       if(m_year % 100 == 0 && m_year % 400 != 0)
237          return false;
238       }
239 
240    if(m_hour >= 24 || m_minute >= 60 || m_second > 60)
241       return false;
242 
243    if (m_tag == UTC_TIME)
244       {
245       /*
246       UTCTime limits the value of components such that leap seconds
247       are not covered. See "UNIVERSAL 23" in "Information technology
248       Abstract Syntax Notation One (ASN.1): Specification of basic notation"
249 
250       http://www.itu.int/ITU-T/studygroups/com17/languages/
251       */
252       if(m_second > 59)
253          {
254          return false;
255          }
256       }
257 
258    return true;
259    }
260 
to_std_timepoint() const261 std::chrono::system_clock::time_point ASN1_Time::to_std_timepoint() const
262    {
263    return calendar_point(m_year, m_month, m_day, m_hour, m_minute, m_second).to_std_timepoint();
264    }
265 
time_since_epoch() const266 uint64_t ASN1_Time::time_since_epoch() const
267    {
268    auto tp = this->to_std_timepoint();
269    return std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch()).count();
270    }
271 
272 /*
273 * Compare two ASN1_Times for in various ways
274 */
operator ==(const ASN1_Time & t1,const ASN1_Time & t2)275 bool operator==(const ASN1_Time& t1, const ASN1_Time& t2)
276    { return (t1.cmp(t2) == 0); }
operator !=(const ASN1_Time & t1,const ASN1_Time & t2)277 bool operator!=(const ASN1_Time& t1, const ASN1_Time& t2)
278    { return (t1.cmp(t2) != 0); }
279 
operator <=(const ASN1_Time & t1,const ASN1_Time & t2)280 bool operator<=(const ASN1_Time& t1, const ASN1_Time& t2)
281    { return (t1.cmp(t2) <= 0); }
operator >=(const ASN1_Time & t1,const ASN1_Time & t2)282 bool operator>=(const ASN1_Time& t1, const ASN1_Time& t2)
283    { return (t1.cmp(t2) >= 0); }
284 
operator <(const ASN1_Time & t1,const ASN1_Time & t2)285 bool operator<(const ASN1_Time& t1, const ASN1_Time& t2)
286    { return (t1.cmp(t2) < 0); }
operator >(const ASN1_Time & t1,const ASN1_Time & t2)287 bool operator>(const ASN1_Time& t1, const ASN1_Time& t2)
288    { return (t1.cmp(t2) > 0); }
289 
290 }
291