Coverage Report

Created: 2021-05-04 09:02

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