Botan 2.19.1
Crypto and TLS for C&
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_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
17namespace Botan {
18
19ASN1_Time::ASN1_Time(const std::chrono::system_clock::time_point& time)
20 {
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
33ASN1_Time::ASN1_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 "ASN1_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
53std::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
93std::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
114 {
115 return (m_year != 0);
116 }
117
118int32_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
141void 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*/
218bool 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
261std::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
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*/
275bool operator==(const ASN1_Time& t1, const ASN1_Time& t2)
276 { return (t1.cmp(t2) == 0); }
277bool operator!=(const ASN1_Time& t1, const ASN1_Time& t2)
278 { return (t1.cmp(t2) != 0); }
279
280bool operator<=(const ASN1_Time& t1, const ASN1_Time& t2)
281 { return (t1.cmp(t2) <= 0); }
282bool operator>=(const ASN1_Time& t1, const ASN1_Time& t2)
283 { return (t1.cmp(t2) >= 0); }
284
285bool operator<(const ASN1_Time& t1, const ASN1_Time& t2)
286 { return (t1.cmp(t2) < 0); }
287bool operator>(const ASN1_Time& t1, const ASN1_Time& t2)
288 { return (t1.cmp(t2) > 0); }
289
290}
#define BOTAN_ARG_CHECK(expr, msg)
Definition: assert.h:37
#define BOTAN_ASSERT(expr, assertion_made)
Definition: assert.h:55
uint64_t time_since_epoch() const
Return time since epoch.
Definition: asn1_time.cpp:266
std::chrono::system_clock::time_point to_std_timepoint() const
Returns a STL timepoint object.
Definition: asn1_time.cpp:261
std::string to_string() const
Return an internal string representation of the time.
Definition: asn1_time.cpp:53
void decode_from(BER_Decoder &) override
Definition: asn1_time.cpp:46
ASN1_Time()=default
Create an invalid ASN1_Time.
bool time_is_set() const
Return if the time has been set somehow.
Definition: asn1_time.cpp:113
int32_t cmp(const ASN1_Time &other) const
Compare this time against another.
Definition: asn1_time.cpp:118
std::string readable_string() const
Returns a human friendly string replesentation of no particular formatting.
Definition: asn1_time.cpp:93
void encode_into(DER_Encoder &) const override
DER encode a ASN1_Time.
Definition: asn1_time.cpp:38
BER_Object get_next_object()
Definition: ber_dec.cpp:237
ASN1_Tag type() const
Definition: asn1_obj.h:116
DER_Encoder & add_object(ASN1_Tag type_tag, ASN1_Tag class_tag, const uint8_t rep[], size_t length)
Definition: der_enc.cpp:249
uint32_t get_minutes() const
Definition: calendar.h:38
std::chrono::system_clock::time_point to_std_timepoint() const
Definition: calendar.cpp:60
uint32_t get_month() const
Definition: calendar.h:29
uint32_t get_seconds() const
Definition: calendar.h:43
uint32_t get_hour() const
Definition: calendar.h:35
uint32_t get_year() const
Definition: calendar.h:26
uint32_t get_day() const
Definition: calendar.h:32
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:213
Definition: alg_id.cpp:13
bool operator>=(const ASN1_Time &, const ASN1_Time &)
Definition: asn1_time.cpp:282
bool operator<=(const ASN1_Time &, const ASN1_Time &)
Definition: asn1_time.cpp:280
bool operator<(const OID &a, const OID &b)
Definition: asn1_oid.cpp:132
bool operator>(const ASN1_Time &, const ASN1_Time &)
Definition: asn1_time.cpp:287
bool operator!=(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
Definition: alg_id.cpp:82
bool operator==(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
Definition: alg_id.cpp:65
ASN1_Tag
Definition: asn1_obj.h:25
@ UTC_TIME
Definition: asn1_obj.h:54
@ GENERALIZED_TIME
Definition: asn1_obj.h:55
@ UTC_OR_GENERALIZED_TIME
Definition: asn1_obj.h:56
@ UNIVERSAL
Definition: asn1_obj.h:26
uint32_t to_u32bit(const std::string &str)
Definition: parsing.cpp:35
calendar_point calendar_value(const std::chrono::system_clock::time_point &time_point)
Definition: calendar.cpp:111