/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_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 | 0 | { |
21 | 0 | calendar_point cal = calendar_value(time); |
22 | 0 |
|
23 | 0 | m_year = cal.get_year(); |
24 | 0 | m_month = cal.get_month(); |
25 | 0 | m_day = cal.get_day(); |
26 | 0 | m_hour = cal.get_hour(); |
27 | 0 | m_minute = cal.get_minutes(); |
28 | 0 | m_second = cal.get_seconds(); |
29 | 0 |
|
30 | 0 | m_tag = (m_year >= 2050) ? GENERALIZED_TIME : UTC_TIME; |
31 | 0 | } |
32 | | |
33 | | X509_Time::X509_Time(const std::string& t_spec, ASN1_Tag tag) |
34 | 0 | { |
35 | 0 | set_to(t_spec, tag); |
36 | 0 | } |
37 | | |
38 | | void X509_Time::encode_into(DER_Encoder& der) const |
39 | 0 | { |
40 | 0 | BOTAN_ARG_CHECK(m_tag == UTC_TIME || m_tag == GENERALIZED_TIME, |
41 | 0 | "X509_Time: Bad encoding tag"); |
42 | 0 |
|
43 | 0 | der.add_object(m_tag, UNIVERSAL, to_string()); |
44 | 0 | } |
45 | | |
46 | | void X509_Time::decode_from(BER_Decoder& source) |
47 | 53.4k | { |
48 | 53.4k | BER_Object ber_time = source.get_next_object(); |
49 | 53.4k | |
50 | 53.4k | set_to(ASN1::to_string(ber_time), ber_time.type()); |
51 | 53.4k | } |
52 | | |
53 | | std::string X509_Time::to_string() const |
54 | 0 | { |
55 | 0 | if(time_is_set() == false) |
56 | 0 | throw Invalid_State("X509_Time::to_string: No time set"); |
57 | 0 | |
58 | 0 | uint32_t full_year = m_year; |
59 | 0 |
|
60 | 0 | if(m_tag == UTC_TIME) |
61 | 0 | { |
62 | 0 | if(m_year < 1950 || m_year >= 2050) |
63 | 0 | throw Encoding_Error("X509_Time: The time " + readable_string() + |
64 | 0 | " cannot be encoded as a UTCTime"); |
65 | 0 | |
66 | 0 | full_year = (m_year >= 2000) ? (m_year - 2000) : (m_year - 1900); |
67 | 0 | } |
68 | 0 |
|
69 | 0 | const uint64_t YEAR_FACTOR = 10000000000ULL; |
70 | 0 | const uint64_t MON_FACTOR = 100000000; |
71 | 0 | const uint64_t DAY_FACTOR = 1000000; |
72 | 0 | const uint64_t HOUR_FACTOR = 10000; |
73 | 0 | const uint64_t MIN_FACTOR = 100; |
74 | 0 |
|
75 | 0 | const uint64_t int_repr = |
76 | 0 | YEAR_FACTOR * full_year + |
77 | 0 | MON_FACTOR * m_month + |
78 | 0 | DAY_FACTOR * m_day + |
79 | 0 | HOUR_FACTOR * m_hour + |
80 | 0 | MIN_FACTOR * m_minute + |
81 | 0 | m_second; |
82 | 0 |
|
83 | 0 | std::string repr = std::to_string(int_repr) + "Z"; |
84 | 0 |
|
85 | 0 | uint32_t desired_size = (m_tag == UTC_TIME) ? 13 : 15; |
86 | 0 |
|
87 | 0 | while(repr.size() < desired_size) |
88 | 0 | repr = "0" + repr; |
89 | 0 |
|
90 | 0 | return repr; |
91 | 0 | } |
92 | | |
93 | | std::string X509_Time::readable_string() const |
94 | 5.37k | { |
95 | 5.37k | if(time_is_set() == false) |
96 | 0 | throw Invalid_State("X509_Time::readable_string: No time set"); |
97 | 5.37k | |
98 | 5.37k | // desired format: "%04d/%02d/%02d %02d:%02d:%02d UTC" |
99 | 5.37k | std::stringstream output; |
100 | 5.37k | output << std::setfill('0') |
101 | 5.37k | << std::setw(4) << m_year << "/" |
102 | 5.37k | << std::setw(2) << m_month << "/" |
103 | 5.37k | << std::setw(2) << m_day |
104 | 5.37k | << " " |
105 | 5.37k | << std::setw(2) << m_hour << ":" |
106 | 5.37k | << std::setw(2) << m_minute << ":" |
107 | 5.37k | << std::setw(2) << m_second |
108 | 5.37k | << " UTC"; |
109 | 5.37k | |
110 | 5.37k | return output.str(); |
111 | 5.37k | } |
112 | | |
113 | | bool X509_Time::time_is_set() const |
114 | 5.37k | { |
115 | 5.37k | return (m_year != 0); |
116 | 5.37k | } |
117 | | |
118 | | int32_t X509_Time::cmp(const X509_Time& other) const |
119 | 0 | { |
120 | 0 | if(time_is_set() == false) |
121 | 0 | throw Invalid_State("X509_Time::cmp: No time set"); |
122 | 0 | |
123 | 0 | const int32_t EARLIER = -1, LATER = 1, SAME_TIME = 0; |
124 | 0 |
|
125 | 0 | if(m_year < other.m_year) return EARLIER; |
126 | 0 | if(m_year > other.m_year) return LATER; |
127 | 0 | if(m_month < other.m_month) return EARLIER; |
128 | 0 | if(m_month > other.m_month) return LATER; |
129 | 0 | if(m_day < other.m_day) return EARLIER; |
130 | 0 | if(m_day > other.m_day) return LATER; |
131 | 0 | if(m_hour < other.m_hour) return EARLIER; |
132 | 0 | if(m_hour > other.m_hour) return LATER; |
133 | 0 | if(m_minute < other.m_minute) return EARLIER; |
134 | 0 | if(m_minute > other.m_minute) return LATER; |
135 | 0 | if(m_second < other.m_second) return EARLIER; |
136 | 0 | if(m_second > other.m_second) return LATER; |
137 | 0 | |
138 | 0 | return SAME_TIME; |
139 | 0 | } |
140 | | |
141 | | void X509_Time::set_to(const std::string& t_spec, ASN1_Tag spec_tag) |
142 | 60.4k | { |
143 | 60.4k | if(spec_tag == UTC_OR_GENERALIZED_TIME) |
144 | 3.70k | { |
145 | 3.70k | try |
146 | 3.70k | { |
147 | 3.70k | set_to(t_spec, GENERALIZED_TIME); |
148 | 3.70k | return; |
149 | 3.70k | } |
150 | 3.40k | catch(Invalid_Argument&) {} // Not a generalized time. Continue |
151 | 3.70k | |
152 | 3.70k | try |
153 | 3.40k | { |
154 | 3.40k | set_to(t_spec, UTC_TIME); |
155 | 3.40k | return; |
156 | 3.40k | } |
157 | 247 | catch(Invalid_Argument&) {} // Not a UTC time. Continue |
158 | 3.40k | |
159 | 3.40k | throw Invalid_Argument("Time string could not be parsed as GeneralizedTime or UTCTime."); |
160 | 56.7k | } |
161 | 56.7k | |
162 | 56.7k | BOTAN_ASSERT(spec_tag == UTC_TIME || spec_tag == GENERALIZED_TIME, "Invalid tag."); |
163 | 56.7k | |
164 | 56.7k | BOTAN_ARG_CHECK(t_spec.size() > 0, "Time string must not be empty."); |
165 | 56.2k | |
166 | 56.2k | BOTAN_ARG_CHECK(t_spec.back() == 'Z', "Botan does not support times with timezones other than Z"); |
167 | 55.8k | |
168 | 55.8k | if(spec_tag == GENERALIZED_TIME) |
169 | 7.24k | { |
170 | 7.24k | BOTAN_ARG_CHECK(t_spec.size() == 15, "Invalid GeneralizedTime string"); |
171 | 7.24k | } |
172 | 48.2k | else if(spec_tag == UTC_TIME) |
173 | 48.2k | { |
174 | 48.2k | BOTAN_ARG_CHECK(t_spec.size() == 13, "Invalid UTCTime string"); |
175 | 48.2k | } |
176 | 55.4k | |
177 | 55.4k | const size_t YEAR_SIZE = (spec_tag == UTC_TIME) ? 2 : 4; |
178 | 51.6k | |
179 | 51.6k | std::vector<std::string> params; |
180 | 51.6k | std::string current; |
181 | 51.6k | |
182 | 162k | for(size_t j = 0; j != YEAR_SIZE; ++j) |
183 | 110k | current += t_spec[j]; |
184 | 51.6k | params.push_back(current); |
185 | 51.6k | current.clear(); |
186 | 51.6k | |
187 | 568k | for(size_t j = YEAR_SIZE; j != t_spec.size() - 1; ++j) |
188 | 516k | { |
189 | 516k | current += t_spec[j]; |
190 | 516k | if(current.size() == 2) |
191 | 258k | { |
192 | 258k | params.push_back(current); |
193 | 258k | current.clear(); |
194 | 258k | } |
195 | 516k | } |
196 | 51.6k | |
197 | 51.6k | m_year = to_u32bit(params[0]); |
198 | 51.6k | m_month = to_u32bit(params[1]); |
199 | 51.6k | m_day = to_u32bit(params[2]); |
200 | 51.6k | m_hour = to_u32bit(params[3]); |
201 | 51.6k | m_minute = to_u32bit(params[4]); |
202 | 51.6k | m_second = (params.size() == 6) ? to_u32bit(params[5]) : 0; |
203 | 51.6k | m_tag = spec_tag; |
204 | 51.6k | |
205 | 51.6k | if(spec_tag == UTC_TIME) |
206 | 47.3k | { |
207 | 47.3k | if(m_year >= 50) m_year += 1900; |
208 | 41.4k | else m_year += 2000; |
209 | 47.3k | } |
210 | 51.6k | |
211 | 51.6k | if(!passes_sanity_check()) |
212 | 2.60k | throw Invalid_Argument("Time " + t_spec + " does not seem to be valid"); |
213 | 51.6k | } |
214 | | |
215 | | /* |
216 | | * Do a general sanity check on the time |
217 | | */ |
218 | | bool X509_Time::passes_sanity_check() const |
219 | 51.0k | { |
220 | 51.0k | // AppVeyor's trust store includes a cert with expiration date in 3016 ... |
221 | 51.0k | if(m_year < 1950 || m_year > 3100) |
222 | 432 | return false; |
223 | 50.6k | if(m_month == 0 || m_month > 12) |
224 | 451 | return false; |
225 | 50.1k | |
226 | 50.1k | const uint32_t days_in_month[12] = { 31, 28+1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; |
227 | 50.1k | |
228 | 50.1k | if(m_day == 0 || m_day > days_in_month[m_month-1]) |
229 | 447 | return false; |
230 | 49.7k | |
231 | 49.7k | if(m_month == 2 && m_day == 29) |
232 | 2.17k | { |
233 | 2.17k | if(m_year % 4 != 0) |
234 | 205 | return false; // not a leap year |
235 | 1.97k | |
236 | 1.97k | if(m_year % 100 == 0 && m_year % 400 != 0) |
237 | 202 | return false; |
238 | 49.3k | } |
239 | 49.3k | |
240 | 49.3k | if(m_hour >= 24 || m_minute >= 60 || m_second > 60) |
241 | 673 | return false; |
242 | 48.6k | |
243 | 48.6k | if (m_tag == UTC_TIME) |
244 | 45.9k | { |
245 | 45.9k | /* |
246 | 45.9k | UTCTime limits the value of components such that leap seconds |
247 | 45.9k | are not covered. See "UNIVERSAL 23" in "Information technology |
248 | 45.9k | Abstract Syntax Notation One (ASN.1): Specification of basic notation" |
249 | 45.9k | |
250 | 45.9k | http://www.itu.int/ITU-T/studygroups/com17/languages/ |
251 | 45.9k | */ |
252 | 45.9k | if(m_second > 59) |
253 | 199 | { |
254 | 199 | return false; |
255 | 199 | } |
256 | 48.4k | } |
257 | 48.4k | |
258 | 48.4k | return true; |
259 | 48.4k | } |
260 | | |
261 | | std::chrono::system_clock::time_point X509_Time::to_std_timepoint() const |
262 | 0 | { |
263 | 0 | return calendar_point(m_year, m_month, m_day, m_hour, m_minute, m_second).to_std_timepoint(); |
264 | 0 | } |
265 | | |
266 | | uint64_t X509_Time::time_since_epoch() const |
267 | 0 | { |
268 | 0 | auto tp = this->to_std_timepoint(); |
269 | 0 | return std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch()).count(); |
270 | 0 | } |
271 | | |
272 | | /* |
273 | | * Compare two X509_Times for in various ways |
274 | | */ |
275 | | bool operator==(const X509_Time& t1, const X509_Time& t2) |
276 | 0 | { return (t1.cmp(t2) == 0); } |
277 | | bool operator!=(const X509_Time& t1, const X509_Time& t2) |
278 | 0 | { return (t1.cmp(t2) != 0); } |
279 | | |
280 | | bool operator<=(const X509_Time& t1, const X509_Time& t2) |
281 | 0 | { return (t1.cmp(t2) <= 0); } |
282 | | bool operator>=(const X509_Time& t1, const X509_Time& t2) |
283 | 0 | { return (t1.cmp(t2) >= 0); } |
284 | | |
285 | | bool operator<(const X509_Time& t1, const X509_Time& t2) |
286 | 0 | { return (t1.cmp(t2) < 0); } |
287 | | bool operator>(const X509_Time& t1, const X509_Time& t2) |
288 | 0 | { return (t1.cmp(t2) > 0); } |
289 | | |
290 | | } |