/src/boringssl/crypto/asn1/a_time.cc
Line | Count | Source |
1 | | // Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // https://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include <openssl/asn1.h> |
16 | | #include <openssl/posix_time.h> |
17 | | |
18 | | #include <assert.h> |
19 | | #include <stdio.h> |
20 | | #include <string.h> |
21 | | #include <time.h> |
22 | | |
23 | | #include <openssl/asn1t.h> |
24 | | #include <openssl/bytestring.h> |
25 | | #include <openssl/err.h> |
26 | | #include <openssl/mem.h> |
27 | | |
28 | | #include "internal.h" |
29 | | |
30 | | |
31 | | using namespace bssl; |
32 | | |
33 | | // This is an implementation of the ASN1 Time structure which is: Time ::= |
34 | | // CHOICE { utcTime UTCTime, generalTime GeneralizedTime } written by Steve |
35 | | // Henson. |
36 | | |
37 | | BSSL_NAMESPACE_BEGIN |
38 | | |
39 | | IMPLEMENT_ASN1_MSTRING(ASN1_TIME, B_ASN1_TIME) |
40 | | |
41 | | BSSL_NAMESPACE_END |
42 | | |
43 | | IMPLEMENT_ASN1_FUNCTIONS_const(ASN1_TIME) |
44 | | |
45 | 0 | ASN1_TIME *ASN1_TIME_set_posix(ASN1_TIME *s, int64_t posix_time) { |
46 | 0 | return ASN1_TIME_adj(s, posix_time, 0, 0); |
47 | 0 | } |
48 | | |
49 | 0 | ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t time) { |
50 | 0 | return ASN1_TIME_adj(s, time, 0, 0); |
51 | 0 | } |
52 | | |
53 | 0 | static int fits_in_utc_time(const struct tm *tm) { |
54 | 0 | return 50 <= tm->tm_year && tm->tm_year < 150; |
55 | 0 | } |
56 | | |
57 | | ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, int64_t posix_time, int offset_day, |
58 | 0 | long offset_sec) { |
59 | 0 | struct tm tm; |
60 | |
|
61 | 0 | if (!OPENSSL_posix_to_tm(posix_time, &tm)) { |
62 | 0 | OPENSSL_PUT_ERROR(ASN1, ASN1_R_ERROR_GETTING_TIME); |
63 | 0 | return nullptr; |
64 | 0 | } |
65 | 0 | if (offset_day || offset_sec) { |
66 | 0 | if (!OPENSSL_gmtime_adj(&tm, offset_day, offset_sec)) { |
67 | 0 | return nullptr; |
68 | 0 | } |
69 | 0 | } |
70 | 0 | if (fits_in_utc_time(&tm)) { |
71 | 0 | return ASN1_UTCTIME_adj(s, posix_time, offset_day, offset_sec); |
72 | 0 | } |
73 | 0 | return ASN1_GENERALIZEDTIME_adj(s, posix_time, offset_day, offset_sec); |
74 | 0 | } |
75 | | |
76 | 0 | int ASN1_TIME_check(const ASN1_TIME *t) { |
77 | 0 | if (t->type == V_ASN1_GENERALIZEDTIME) { |
78 | 0 | return ASN1_GENERALIZEDTIME_check(t); |
79 | 0 | } else if (t->type == V_ASN1_UTCTIME) { |
80 | 0 | return ASN1_UTCTIME_check(t); |
81 | 0 | } |
82 | 0 | return 0; |
83 | 0 | } |
84 | | |
85 | | // Convert an ASN1_TIME structure to GeneralizedTime |
86 | | ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(const ASN1_TIME *in, |
87 | 0 | ASN1_GENERALIZEDTIME **out) { |
88 | 0 | if (!ASN1_TIME_check(in)) { |
89 | 0 | return nullptr; |
90 | 0 | } |
91 | | |
92 | 0 | UniquePtr<ASN1_GENERALIZEDTIME> tmp; |
93 | 0 | ASN1_GENERALIZEDTIME *ret = nullptr; |
94 | 0 | if (!out || !*out) { |
95 | 0 | tmp.reset(ASN1_GENERALIZEDTIME_new()); |
96 | 0 | if (tmp == nullptr) { |
97 | 0 | return nullptr; |
98 | 0 | } |
99 | 0 | ret = tmp.get(); |
100 | 0 | } else { |
101 | 0 | ret = *out; |
102 | 0 | } |
103 | | |
104 | | // If already GeneralizedTime just copy across |
105 | 0 | if (in->type == V_ASN1_GENERALIZEDTIME) { |
106 | 0 | if (!ASN1_STRING_set(ret, in->data, in->length)) { |
107 | 0 | return nullptr; |
108 | 0 | } |
109 | 0 | } else { |
110 | 0 | assert(in->type == V_ASN1_UTCTIME); |
111 | | // `ASN1_TIME_check` implies a bound on the string's lengths. In particular, |
112 | | // the longest possible UTCTime is "YYMMDDHHMMSS+HHMM", with the (invalid) |
113 | | // timezone offsets. |
114 | 0 | static constexpr size_t kMaxUTCTimeLength = 17; |
115 | 0 | BSSL_CHECK(in->length > 0 && |
116 | 0 | static_cast<size_t>(in->length) <= kMaxUTCTimeLength); |
117 | 0 | char buf[2 /* century */ + kMaxUTCTimeLength + 1 /* NUL */]; |
118 | | // Work out the century and prepend. |
119 | 0 | if (in->data[0] >= '5') { |
120 | 0 | snprintf(buf, sizeof(buf), "19%.*s", in->length, |
121 | 0 | reinterpret_cast<const char *>(in->data)); |
122 | 0 | } else { |
123 | 0 | snprintf(buf, sizeof(buf), "20%.*s", in->length, |
124 | 0 | reinterpret_cast<const char *>(in->data)); |
125 | 0 | } |
126 | 0 | if (!ASN1_STRING_set(ret, buf, -1 /* use strlen */)) { |
127 | 0 | return nullptr; |
128 | 0 | } |
129 | 0 | } |
130 | | |
131 | 0 | if (out != nullptr && *out == nullptr) { |
132 | 0 | *out = ret; |
133 | 0 | } |
134 | 0 | tmp.release(); // Ownership passed to caller. |
135 | 0 | return ret; |
136 | 0 | } |
137 | | |
138 | 0 | int ASN1_TIME_set_string(ASN1_TIME *s, const char *str) { |
139 | 0 | return ASN1_UTCTIME_set_string(s, str) || |
140 | 0 | ASN1_GENERALIZEDTIME_set_string(s, str); |
141 | 0 | } |
142 | | |
143 | 0 | int ASN1_TIME_set_string_X509(ASN1_TIME *s, const char *str) { |
144 | 0 | CBS cbs; |
145 | 0 | CBS_init(&cbs, (const uint8_t *)str, strlen(str)); |
146 | 0 | int type; |
147 | 0 | struct tm tm; |
148 | 0 | if (CBS_parse_utc_time(&cbs, /*out_tm=*/nullptr, |
149 | 0 | /*allow_timezone_offset=*/0)) { |
150 | 0 | type = V_ASN1_UTCTIME; |
151 | 0 | } else if (CBS_parse_generalized_time(&cbs, &tm, |
152 | 0 | /*allow_timezone_offset=*/0)) { |
153 | 0 | type = V_ASN1_GENERALIZEDTIME; |
154 | 0 | if (fits_in_utc_time(&tm)) { |
155 | 0 | type = V_ASN1_UTCTIME; |
156 | 0 | CBS_skip(&cbs, 2); |
157 | 0 | } |
158 | 0 | } else { |
159 | 0 | return 0; |
160 | 0 | } |
161 | | |
162 | 0 | if (s != nullptr) { |
163 | 0 | if (!ASN1_STRING_set(s, CBS_data(&cbs), CBS_len(&cbs))) { |
164 | 0 | return 0; |
165 | 0 | } |
166 | 0 | s->type = type; |
167 | 0 | } |
168 | 0 | return 1; |
169 | 0 | } |
170 | | |
171 | | static int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *t, |
172 | 0 | int allow_timezone_offset) { |
173 | 0 | if (t == nullptr) { |
174 | 0 | if (OPENSSL_posix_to_tm(time(nullptr), tm)) { |
175 | 0 | return 1; |
176 | 0 | } |
177 | 0 | return 0; |
178 | 0 | } |
179 | | |
180 | 0 | if (t->type == V_ASN1_UTCTIME) { |
181 | 0 | return asn1_utctime_to_tm(tm, t, allow_timezone_offset); |
182 | 0 | } else if (t->type == V_ASN1_GENERALIZEDTIME) { |
183 | 0 | return asn1_generalizedtime_to_tm(tm, t); |
184 | 0 | } |
185 | | |
186 | 0 | return 0; |
187 | 0 | } |
188 | | |
189 | | int ASN1_TIME_diff(int *out_days, int *out_seconds, const ASN1_TIME *from, |
190 | 0 | const ASN1_TIME *to) { |
191 | 0 | struct tm tm_from, tm_to; |
192 | 0 | if (!asn1_time_to_tm(&tm_from, from, /*allow_timezone_offset=*/1)) { |
193 | 0 | return 0; |
194 | 0 | } |
195 | 0 | if (!asn1_time_to_tm(&tm_to, to, /*allow_timezone_offset=*/1)) { |
196 | 0 | return 0; |
197 | 0 | } |
198 | 0 | return OPENSSL_gmtime_diff(out_days, out_seconds, &tm_from, &tm_to); |
199 | 0 | } |
200 | | |
201 | 0 | int ASN1_TIME_to_posix_nonstandard(const ASN1_TIME *t, int64_t *out_time) { |
202 | 0 | struct tm tm; |
203 | 0 | if (!asn1_time_to_tm(&tm, t, /*allow_timezone_offset=*/1)) { |
204 | 0 | return 0; |
205 | 0 | } |
206 | 0 | return OPENSSL_tm_to_posix(&tm, out_time); |
207 | 0 | } |
208 | | |
209 | | // The functions below do *not* permissively allow the use of four digit |
210 | | // timezone offsets in UTC times, as is done elsewhere in the code. They are |
211 | | // both new API, and used internally to X509_cmp_time. This is to discourage the |
212 | | // use of nonstandard times in new code, and to ensure that this code behaves |
213 | | // correctly in X509_cmp_time which historically did its own time validations |
214 | | // slightly different than the many other copies of X.509 time validation |
215 | | // sprinkled through the codebase. The custom checks in X509_cmp_time meant that |
216 | | // it did not allow four digit timezone offsets in UTC times. |
217 | 0 | int ASN1_TIME_to_time_t(const ASN1_TIME *t, time_t *out_time) { |
218 | 0 | struct tm tm; |
219 | 0 | if (!asn1_time_to_tm(&tm, t, /*allow_timezone_offset=*/0)) { |
220 | 0 | return 0; |
221 | 0 | } |
222 | 0 | return OPENSSL_timegm(&tm, out_time); |
223 | 0 | } |
224 | | |
225 | 0 | int ASN1_TIME_to_posix(const ASN1_TIME *t, int64_t *out_time) { |
226 | 0 | struct tm tm; |
227 | 0 | if (!asn1_time_to_tm(&tm, t, /*allow_timezone_offset=*/0)) { |
228 | 0 | return 0; |
229 | 0 | } |
230 | 0 | return OPENSSL_tm_to_posix(&tm, out_time); |
231 | 0 | } |
232 | | |
233 | | int bssl::asn1_parse_time(CBS *cbs, ASN1_TIME *out, |
234 | 366k | int allow_utc_timezone_offset) { |
235 | 366k | if (CBS_peek_asn1_tag(cbs, CBS_ASN1_UTCTIME)) { |
236 | 365k | return asn1_parse_utc_time(cbs, out, /*tag=*/0, allow_utc_timezone_offset); |
237 | 365k | } |
238 | 373 | return asn1_parse_generalized_time(cbs, out, /*tag=*/0); |
239 | 366k | } |
240 | | |
241 | 5.50k | int bssl::asn1_marshal_time(CBB *cbb, const ASN1_TIME *in) { |
242 | 5.50k | if (in->type != V_ASN1_UTCTIME && in->type != V_ASN1_GENERALIZEDTIME) { |
243 | 0 | OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE); |
244 | 0 | return 0; |
245 | 0 | } |
246 | 5.50k | return asn1_marshal_octet_string(cbb, in, |
247 | 5.50k | static_cast<CBS_ASN1_TAG>(in->type)); |
248 | 5.50k | } |