Botan 3.12.0
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_time.h>
9
10#include <botan/assert.h>
11#include <botan/ber_dec.h>
12#include <botan/der_enc.h>
13#include <botan/exceptn.h>
14#include <botan/internal/calendar.h>
15#include <botan/internal/fmt.h>
16#include <botan/internal/parsing.h>
17#include <iomanip>
18
19namespace Botan {
20
22 return ASN1_Time(std::chrono::system_clock::time_point(std::chrono::seconds(time_since_epoch)));
23}
24
25ASN1_Time::ASN1_Time(const std::chrono::system_clock::time_point& time) {
26 const calendar_point cal(time);
27
28 m_year = cal.year();
29 m_month = cal.month();
30 m_day = cal.day();
31 m_hour = cal.hour();
32 m_minute = cal.minutes();
33 m_second = cal.seconds();
34
35 // NOLINTNEXTLINE(*-prefer-member-initializer)
36 m_tag = (m_year >= 2050) ? ASN1_Type::GeneralizedTime : ASN1_Type::UtcTime;
37}
38
39ASN1_Time::ASN1_Time(std::string_view t_spec, ASN1_Type tag) {
40 set_to(t_spec, tag);
41}
42
43ASN1_Time::ASN1_Time(std::string_view t_spec) {
44 if(t_spec.size() == 13) {
45 set_to(t_spec, ASN1_Type::UtcTime);
46 } else if(t_spec.size() == 15) {
47 set_to(t_spec, ASN1_Type::GeneralizedTime);
48 } else {
49 throw Invalid_Argument("Time string could not be parsed as GeneralizedTime or UTCTime.");
50 }
51}
52
54 BOTAN_ARG_CHECK(m_tag == ASN1_Type::UtcTime || m_tag == ASN1_Type::GeneralizedTime, "ASN1_Time: Bad encoding tag");
55
57}
58
60 const BER_Object ber_time = source.get_next_object();
61
62 if(ber_time.get_class() != ASN1_Class::Universal ||
63 (ber_time.type() != ASN1_Type::UtcTime && ber_time.type() != ASN1_Type::GeneralizedTime)) {
64 throw Decoding_Error(fmt("ASN1_Time: Unexpected tag {}/{}",
65 static_cast<uint32_t>(ber_time.type()),
66 static_cast<uint32_t>(ber_time.get_class())));
67 }
68
69 try {
70 set_to(ASN1::to_string(ber_time), ber_time.type());
71 } catch(Invalid_Argument& e) {
72 throw Decoding_Error(fmt("Invalid ASN1_Time encoding: {}", e.what()));
73 }
74}
75
76std::string ASN1_Time::to_string() const {
77 if(!time_is_set()) {
78 throw Invalid_State("ASN1_Time::to_string: No time set");
79 }
80
81 uint32_t full_year = m_year;
82
83 if(m_tag == ASN1_Type::UtcTime) {
84 if(m_year < 1950 || m_year >= 2050) {
85 throw Encoding_Error(fmt("ASN_Time: The time {} cannot be encoded as UTCTime", readable_string()));
86 }
87
88 full_year = (m_year >= 2000) ? (m_year - 2000) : (m_year - 1900);
89 }
90
91 const uint64_t year_factor = 10000000000;
92 const uint64_t mon_factor = 100000000;
93 const uint64_t day_factor = 1000000;
94 const uint64_t hour_factor = 10000;
95 const uint64_t min_factor = 100;
96
97 const uint64_t int_repr = year_factor * full_year + mon_factor * m_month + day_factor * m_day +
98 hour_factor * m_hour + min_factor * m_minute + m_second;
99
100 const std::string repr = std::to_string(int_repr) + "Z";
101
102 const size_t desired_size = (m_tag == ASN1_Type::UtcTime) ? 13 : 15;
103
104 const std::string zero_padding(desired_size - repr.size(), '0');
105
106 return zero_padding + repr;
107}
108
109std::string ASN1_Time::readable_string() const {
110 if(!time_is_set()) {
111 throw Invalid_State("ASN1_Time::readable_string: No time set");
112 }
113
114 // desired format: "%04d/%02d/%02d %02d:%02d:%02d UTC"
115 std::stringstream output;
116 output << std::setfill('0') << std::setw(4) << m_year << "/" << std::setw(2) << m_month << "/" << std::setw(2)
117 << m_day << " " << std::setw(2) << m_hour << ":" << std::setw(2) << m_minute << ":" << std::setw(2)
118 << m_second << " UTC";
119
120 return output.str();
121}
122
124 return (m_year != 0);
125}
126
127int32_t ASN1_Time::cmp(const ASN1_Time& other) const {
128 if(!time_is_set() || !other.time_is_set()) {
129 throw Invalid_State("ASN1_Time::cmp: Cannot compare empty times");
130 }
131
132 constexpr int32_t EARLIER = -1;
133 constexpr int32_t LATER = 1;
134 constexpr int32_t SAME_TIME = 0;
135
136 if(m_year < other.m_year) {
137 return EARLIER;
138 }
139 if(m_year > other.m_year) {
140 return LATER;
141 }
142 if(m_month < other.m_month) {
143 return EARLIER;
144 }
145 if(m_month > other.m_month) {
146 return LATER;
147 }
148 if(m_day < other.m_day) {
149 return EARLIER;
150 }
151 if(m_day > other.m_day) {
152 return LATER;
153 }
154 if(m_hour < other.m_hour) {
155 return EARLIER;
156 }
157 if(m_hour > other.m_hour) {
158 return LATER;
159 }
160 if(m_minute < other.m_minute) {
161 return EARLIER;
162 }
163 if(m_minute > other.m_minute) {
164 return LATER;
165 }
166 if(m_second < other.m_second) {
167 return EARLIER;
168 }
169 if(m_second > other.m_second) {
170 return LATER;
171 }
172
173 return SAME_TIME;
174}
175
176void ASN1_Time::set_to(std::string_view t_spec, ASN1_Type spec_tag) {
178 "Invalid tag for ASN1_Time");
179
180 if(spec_tag == ASN1_Type::GeneralizedTime) {
181 BOTAN_ARG_CHECK(t_spec.size() == 15, "Invalid GeneralizedTime input string");
182 } else if(spec_tag == ASN1_Type::UtcTime) {
183 BOTAN_ARG_CHECK(t_spec.size() == 13, "Invalid UTCTime input string");
184 }
185
186 BOTAN_ARG_CHECK(t_spec.back() == 'Z', "Botan does not support ASN1 times with timezones other than Z");
187
188 const size_t field_len = 2;
189
190 const size_t year_start = 0;
191 const size_t year_len = (spec_tag == ASN1_Type::UtcTime) ? 2 : 4;
192 const size_t month_start = year_start + year_len;
193 const size_t day_start = month_start + field_len;
194 const size_t hour_start = day_start + field_len;
195 const size_t min_start = hour_start + field_len;
196 const size_t sec_start = min_start + field_len;
197
198 m_year = to_u32bit(t_spec.substr(year_start, year_len));
199 m_month = to_u32bit(t_spec.substr(month_start, field_len));
200 m_day = to_u32bit(t_spec.substr(day_start, field_len));
201 m_hour = to_u32bit(t_spec.substr(hour_start, field_len));
202 m_minute = to_u32bit(t_spec.substr(min_start, field_len));
203 m_second = to_u32bit(t_spec.substr(sec_start, field_len));
204 m_tag = spec_tag;
205
206 if(spec_tag == ASN1_Type::UtcTime) {
207 if(m_year >= 50) {
208 m_year += 1900;
209 } else {
210 m_year += 2000;
211 }
212 }
213
214 if(!passes_sanity_check()) {
215 throw Invalid_Argument(fmt("ASN1_Time string '{}' does not seem to be valid", t_spec));
216 }
217}
218
219/*
220* Do a general sanity check on the time
221*/
222bool ASN1_Time::passes_sanity_check() const {
223 // AppVeyor's trust store includes a cert with expiration date in 3016 ...
224 if(m_year < 1950 || m_year > 3100) {
225 return false;
226 }
227 if(m_month == 0 || m_month > 12) {
228 return false;
229 }
230
231 const uint32_t days_in_month[12] = {31, 28 + 1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
232
233 if(m_day == 0 || m_day > days_in_month[m_month - 1]) {
234 return false;
235 }
236
237 if(m_month == 2 && m_day == 29) {
238 if(m_year % 4 != 0) {
239 return false; // not a leap year
240 }
241
242 if(m_year % 100 == 0 && m_year % 400 != 0) {
243 return false;
244 }
245 }
246
247 if(m_hour >= 24 || m_minute >= 60 || m_second > 60) {
248 return false;
249 }
250
251 if(m_tag == ASN1_Type::UtcTime) {
252 /*
253 UTCTime limits the value of components such that leap seconds
254 are not covered. See "UNIVERSAL 23" in "Information technology
255 Abstract Syntax Notation One (ASN.1): Specification of basic notation"
256
257 http://www.itu.int/ITU-T/studygroups/com17/languages/
258 */
259 if(m_second > 59) {
260 return false;
261 }
262 }
263
264 return true;
265}
266
267std::chrono::system_clock::time_point ASN1_Time::to_std_timepoint() const {
268 return calendar_point(m_year, m_month, m_day, m_hour, m_minute, m_second).to_std_timepoint();
269}
270
272 return calendar_point(m_year, m_month, m_day, m_hour, m_minute, m_second).seconds_since_epoch();
273}
274
275/*
276* Compare two ASN1_Times for in various ways
277*/
278bool operator==(const ASN1_Time& t1, const ASN1_Time& t2) {
279 return (t1.cmp(t2) == 0);
280}
281
282bool operator!=(const ASN1_Time& t1, const ASN1_Time& t2) {
283 return (t1.cmp(t2) != 0);
284}
285
286bool operator<=(const ASN1_Time& t1, const ASN1_Time& t2) {
287 return (t1.cmp(t2) <= 0);
288}
289
290bool operator>=(const ASN1_Time& t1, const ASN1_Time& t2) {
291 return (t1.cmp(t2) >= 0);
292}
293
294bool operator<(const ASN1_Time& t1, const ASN1_Time& t2) {
295 return (t1.cmp(t2) < 0);
296}
297
298bool operator>(const ASN1_Time& t1, const ASN1_Time& t2) {
299 return (t1.cmp(t2) > 0);
300}
301
302} // namespace Botan
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
uint64_t time_since_epoch() const
Return time since epoch.
std::chrono::system_clock::time_point to_std_timepoint() const
Returns a STL timepoint object.
void decode_from(BER_Decoder &from) override
Definition asn1_time.cpp:59
static ASN1_Time from_seconds_since_epoch(uint64_t seconds)
Create an ASN1_Time from seconds since epoch.
Definition asn1_time.cpp:21
std::string to_string() const
Return an internal string representation of the time.
Definition asn1_time.cpp:76
ASN1_Time()=default
Create an invalid ASN1_Time.
bool time_is_set() const
Return if the time has been set somehow.
int32_t cmp(const ASN1_Time &other) const
Compare this time against another.
void encode_into(DER_Encoder &to) const override
DER encode a ASN1_Time.
Definition asn1_time.cpp:53
std::string readable_string() const
Returns a human friendly string representation of no particular formatting.
BER_Object get_next_object()
Definition ber_dec.cpp:426
ASN1_Type type() const
Definition asn1_obj.h:146
ASN1_Class get_class() const
Definition asn1_obj.h:148
DER_Encoder & add_object(ASN1_Type type_tag, ASN1_Class class_tag, const uint8_t rep[], size_t length)
Definition der_enc.cpp:217
const char * what() const noexcept override
Definition exceptn.h:94
uint32_t hour() const
Definition calendar.h:33
uint32_t seconds() const
Definition calendar.h:41
uint32_t day() const
Definition calendar.h:30
uint64_t seconds_since_epoch() const
Definition calendar.cpp:67
std::chrono::system_clock::time_point to_std_timepoint() const
Definition calendar.cpp:71
uint32_t minutes() const
Definition calendar.h:36
uint32_t month() const
Definition calendar.h:27
uint32_t year() const
Definition calendar.h:24
std::string to_string(const BER_Object &obj)
Definition asn1_obj.cpp:190
uint32_t to_u32bit(std::string_view str_view)
Definition parsing.cpp:31
bool operator>(const ASN1_Time &t1, const ASN1_Time &t2)
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
ASN1_Type
Definition asn1_obj.h:43
bool operator<(const OID &a, const OID &b)
Definition asn1_oid.cpp:173
bool operator!=(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
Definition alg_id.cpp:68
bool operator>=(const ASN1_Time &t1, const ASN1_Time &t2)
bool operator==(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
Definition alg_id.cpp:53
bool operator<=(const ASN1_Time &t1, const ASN1_Time &t2)