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