Coverage Report

Created: 2020-02-14 15:38

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