Botan  2.8.0
Crypto and TLS for C++11
asn1_time.cpp
Go to the documentation of this file.
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  {
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 
33 X509_Time::X509_Time(const std::string& t_spec, ASN1_Tag tag)
34  {
35  set_to(t_spec, tag);
36  }
37 
39  {
40  BOTAN_ARG_CHECK(m_tag == UTC_TIME || m_tag == GENERALIZED_TIME,
41  "X509_Time: Bad encoding tag");
42 
43  der.add_object(m_tag, UNIVERSAL, to_string());
44  }
45 
47  {
48  BER_Object ber_time = source.get_next_object();
49 
50  set_to(ASN1::to_string(ber_time), ber_time.type());
51  }
52 
53 std::string X509_Time::to_string() const
54  {
55  if(time_is_set() == false)
56  throw Invalid_State("X509_Time::as_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("X509_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 
93 std::string X509_Time::readable_string() const
94  {
95  if(time_is_set() == false)
96  throw Invalid_State("X509_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 
114  {
115  return (m_year != 0);
116  }
117 
118 int32_t X509_Time::cmp(const X509_Time& other) const
119  {
120  if(time_is_set() == false)
121  throw Invalid_State("X509_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 
141 void X509_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 */
218 bool X509_Time::passes_sanity_check() const
219  {
220  if(m_year < 1950 || m_year > 2200)
221  return false;
222  if(m_month == 0 || m_month > 12)
223  return false;
224 
225  const uint32_t days_in_month[12] = { 31, 28+1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
226 
227  if(m_day == 0 || m_day > days_in_month[m_month-1])
228  return false;
229 
230  if(m_month == 2 && m_day == 29)
231  {
232  if(m_year % 4 != 0)
233  return false; // not a leap year
234 
235  if(m_year % 100 == 0 && m_year % 400 != 0)
236  return false;
237  }
238 
239  if(m_hour >= 24 || m_minute >= 60 || m_second > 60)
240  return false;
241 
242  if (m_tag == UTC_TIME)
243  {
244  /*
245  UTCTime limits the value of components such that leap seconds
246  are not covered. See "UNIVERSAL 23" in "Information technology
247  Abstract Syntax Notation One (ASN.1): Specification of basic notation"
248 
249  http://www.itu.int/ITU-T/studygroups/com17/languages/
250  */
251  if (m_hour > 23 || m_minute > 59 || m_second > 59)
252  {
253  return false;
254  }
255  }
256 
257  return true;
258  }
259 
260 std::chrono::system_clock::time_point X509_Time::to_std_timepoint() const
261  {
262  return calendar_point(m_year, m_month, m_day, m_hour, m_minute, m_second).to_std_timepoint();
263  }
264 
266  {
267  auto tp = this->to_std_timepoint();
268  return std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch()).count();
269  }
270 
271 /*
272 * Compare two X509_Times for in various ways
273 */
274 bool operator==(const X509_Time& t1, const X509_Time& t2)
275  { return (t1.cmp(t2) == 0); }
276 bool operator!=(const X509_Time& t1, const X509_Time& t2)
277  { return (t1.cmp(t2) != 0); }
278 
279 bool operator<=(const X509_Time& t1, const X509_Time& t2)
280  { return (t1.cmp(t2) <= 0); }
281 bool operator>=(const X509_Time& t1, const X509_Time& t2)
282  { return (t1.cmp(t2) >= 0); }
283 
284 bool operator<(const X509_Time& t1, const X509_Time& t2)
285  { return (t1.cmp(t2) < 0); }
286 bool operator>(const X509_Time& t1, const X509_Time& t2)
287  { return (t1.cmp(t2) > 0); }
288 
289 }
bool operator!=(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
Definition: alg_id.cpp:90
DER_Encoder & add_object(ASN1_Tag type_tag, ASN1_Tag class_tag, const uint8_t rep[], size_t length)
Definition: der_enc.cpp:249
bool operator>=(const X509_Time &t1, const X509_Time &t2)
Definition: asn1_time.cpp:281
int32_t cmp(const X509_Time &other) const
Compare this time against another.
Definition: asn1_time.cpp:118
uint32_t get_seconds() const
Definition: calendar.h:43
calendar_point calendar_value(const std::chrono::system_clock::time_point &time_point)
Definition: calendar.cpp:106
uint32_t get_month() const
Definition: calendar.h:29
void decode_from(BER_Decoder &) override
Definition: asn1_time.cpp:46
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:210
ASN1_Tag
Definition: asn1_obj.h:22
uint32_t to_u32bit(const std::string &str)
Definition: parsing.cpp:31
bool time_is_set() const
Return if the time has been set somehow.
Definition: asn1_time.cpp:113
uint32_t get_minutes() const
Definition: calendar.h:38
ASN1_Tag type() const
Definition: asn1_obj.h:115
X509_Time()=default
Create an invalid X509_Time.
void encode_into(DER_Encoder &) const override
DER encode a X509_Time.
Definition: asn1_time.cpp:38
#define BOTAN_ASSERT(expr, assertion_made)
Definition: assert.h:55
bool operator<(const OID &a, const OID &b)
Definition: asn1_oid.cpp:105
std::string readable_string() const
Returns a human friendly string replesentation of no particular formatting.
Definition: asn1_time.cpp:93
uint32_t get_hour() const
Definition: calendar.h:35
Definition: alg_id.cpp:13
#define BOTAN_ARG_CHECK(expr, msg)
Definition: assert.h:37
std::string to_string() const
Return an internal string representation of the time.
Definition: asn1_time.cpp:53
BER_Object get_next_object()
Definition: ber_dec.cpp:237
uint32_t get_day() const
Definition: calendar.h:32
std::chrono::system_clock::time_point to_std_timepoint() const
Returns a STL timepoint object.
Definition: asn1_time.cpp:260
uint64_t time_since_epoch() const
Return time since epoch.
Definition: asn1_time.cpp:265
std::chrono::system_clock::time_point to_std_timepoint() const
Definition: calendar.cpp:60
bool operator==(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
Definition: alg_id.cpp:75
uint32_t get_year() const
Definition: calendar.h:26
bool operator>(const X509_Time &t1, const X509_Time &t2)
Definition: asn1_time.cpp:286
bool operator<=(const X509_Time &t1, const X509_Time &t2)
Definition: asn1_time.cpp:279