/src/nss/lib/util/dertime.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | #include "prtypes.h" |
6 | | #include "prtime.h" |
7 | | #include "secder.h" |
8 | | #include "prlong.h" |
9 | | #include "secerr.h" |
10 | | |
11 | 0 | #define HIDIGIT(v) (((v) / 10) + '0') |
12 | 0 | #define LODIGIT(v) (((v) % 10) + '0') |
13 | | |
14 | 0 | #define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9')) |
15 | | #define CAPTURE(var, p, label) \ |
16 | 0 | { \ |
17 | 0 | if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) \ |
18 | 0 | goto label; \ |
19 | 0 | (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \ |
20 | 0 | p += 2; \ |
21 | 0 | } |
22 | | |
23 | | static const PRTime January1st1 = PR_INT64(0xff23400100d44000); |
24 | | static const PRTime January1st1950 = PR_INT64(0xfffdc1f8793da000); |
25 | | static const PRTime January1st2050 = PR_INT64(0x0008f81e1b098000); |
26 | | static const PRTime January1st10000 = PR_INT64(0x0384440ccc736000); |
27 | | |
28 | | /* gmttime must contains UTC time in micro-seconds unit */ |
29 | | SECStatus |
30 | | DER_TimeToUTCTimeArena(PLArenaPool *arenaOpt, SECItem *dst, PRTime gmttime) |
31 | 0 | { |
32 | 0 | PRExplodedTime printableTime; |
33 | 0 | unsigned char *d; |
34 | |
|
35 | 0 | if ((gmttime < January1st1950) || (gmttime >= January1st2050)) { |
36 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
37 | 0 | return SECFailure; |
38 | 0 | } |
39 | | |
40 | 0 | dst->len = 13; |
41 | 0 | if (arenaOpt) { |
42 | 0 | dst->data = d = (unsigned char *)PORT_ArenaAlloc(arenaOpt, dst->len); |
43 | 0 | } else { |
44 | 0 | dst->data = d = (unsigned char *)PORT_Alloc(dst->len); |
45 | 0 | } |
46 | 0 | dst->type = siUTCTime; |
47 | 0 | if (!d) { |
48 | 0 | return SECFailure; |
49 | 0 | } |
50 | | |
51 | | /* Convert a PRTime to a printable format. */ |
52 | 0 | PR_ExplodeTime(gmttime, PR_GMTParameters, &printableTime); |
53 | | |
54 | | /* The month in UTC time is base one */ |
55 | 0 | printableTime.tm_month++; |
56 | | |
57 | | /* remove the century since it's added to the tm_year by the |
58 | | PR_ExplodeTime routine, but is not needed for UTC time */ |
59 | 0 | printableTime.tm_year %= 100; |
60 | |
|
61 | 0 | d[0] = HIDIGIT(printableTime.tm_year); |
62 | 0 | d[1] = LODIGIT(printableTime.tm_year); |
63 | 0 | d[2] = HIDIGIT(printableTime.tm_month); |
64 | 0 | d[3] = LODIGIT(printableTime.tm_month); |
65 | 0 | d[4] = HIDIGIT(printableTime.tm_mday); |
66 | 0 | d[5] = LODIGIT(printableTime.tm_mday); |
67 | 0 | d[6] = HIDIGIT(printableTime.tm_hour); |
68 | 0 | d[7] = LODIGIT(printableTime.tm_hour); |
69 | 0 | d[8] = HIDIGIT(printableTime.tm_min); |
70 | 0 | d[9] = LODIGIT(printableTime.tm_min); |
71 | 0 | d[10] = HIDIGIT(printableTime.tm_sec); |
72 | 0 | d[11] = LODIGIT(printableTime.tm_sec); |
73 | 0 | d[12] = 'Z'; |
74 | 0 | return SECSuccess; |
75 | 0 | } |
76 | | |
77 | | SECStatus |
78 | | DER_TimeToUTCTime(SECItem *dst, PRTime gmttime) |
79 | 0 | { |
80 | 0 | return DER_TimeToUTCTimeArena(NULL, dst, gmttime); |
81 | 0 | } |
82 | | |
83 | | static SECStatus /* forward */ |
84 | | der_TimeStringToTime(PRTime *dst, const char *string, int generalized, |
85 | | const char **endptr); |
86 | | |
87 | 0 | #define GEN_STRING 2 /* TimeString is a GeneralizedTime */ |
88 | 0 | #define UTC_STRING 0 /* TimeString is a UTCTime */ |
89 | | |
90 | | /* The caller of DER_AsciiToItem MUST ENSURE that either |
91 | | ** a) "string" points to a null-terminated ASCII string, or |
92 | | ** b) "string" points to a buffer containing a valid UTCTime, |
93 | | ** whether null terminated or not, or |
94 | | ** c) "string" contains at least 19 characters, with or without null char. |
95 | | ** otherwise, this function may UMR and/or crash. |
96 | | ** It suffices to ensure that the input "string" is at least 17 bytes long. |
97 | | */ |
98 | | SECStatus |
99 | | DER_AsciiToTime(PRTime *dst, const char *string) |
100 | 0 | { |
101 | 0 | return der_TimeStringToTime(dst, string, UTC_STRING, NULL); |
102 | 0 | } |
103 | | |
104 | | SECStatus |
105 | | DER_UTCTimeToTime(PRTime *dst, const SECItem *time) |
106 | 0 | { |
107 | | /* Minimum valid UTCTime is yymmddhhmmZ which is 11 bytes. |
108 | | ** Maximum valid UTCTime is yymmddhhmmss+0000 which is 17 bytes. |
109 | | ** 20 should be large enough for all valid encoded times. |
110 | | */ |
111 | 0 | unsigned int i; |
112 | 0 | char localBuf[20]; |
113 | 0 | const char *end = NULL; |
114 | 0 | SECStatus rv; |
115 | |
|
116 | 0 | if (!time || !time->data || time->len < 11 || time->len > 17) { |
117 | 0 | PORT_SetError(SEC_ERROR_INVALID_TIME); |
118 | 0 | return SECFailure; |
119 | 0 | } |
120 | | |
121 | 0 | for (i = 0; i < time->len; i++) { |
122 | 0 | if (time->data[i] == '\0') { |
123 | 0 | PORT_SetError(SEC_ERROR_INVALID_TIME); |
124 | 0 | return SECFailure; |
125 | 0 | } |
126 | 0 | localBuf[i] = time->data[i]; |
127 | 0 | } |
128 | 0 | localBuf[i] = '\0'; |
129 | |
|
130 | 0 | rv = der_TimeStringToTime(dst, localBuf, UTC_STRING, &end); |
131 | 0 | if (rv == SECSuccess && *end != '\0') { |
132 | 0 | PORT_SetError(SEC_ERROR_INVALID_TIME); |
133 | 0 | return SECFailure; |
134 | 0 | } |
135 | 0 | return rv; |
136 | 0 | } |
137 | | |
138 | | /* |
139 | | gmttime must contains UTC time in micro-seconds unit. |
140 | | Note: the caller should make sure that Generalized time |
141 | | should only be used for certifiate validities after the |
142 | | year 2049. Otherwise, UTC time should be used. This routine |
143 | | does not check this case, since it can be used to encode |
144 | | certificate extension, which does not have this restriction. |
145 | | */ |
146 | | SECStatus |
147 | | DER_TimeToGeneralizedTimeArena(PLArenaPool *arenaOpt, SECItem *dst, PRTime gmttime) |
148 | 0 | { |
149 | 0 | PRExplodedTime printableTime; |
150 | 0 | unsigned char *d; |
151 | |
|
152 | 0 | if ((gmttime < January1st1) || (gmttime >= January1st10000)) { |
153 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
154 | 0 | return SECFailure; |
155 | 0 | } |
156 | 0 | dst->len = 15; |
157 | 0 | if (arenaOpt) { |
158 | 0 | dst->data = d = (unsigned char *)PORT_ArenaAlloc(arenaOpt, dst->len); |
159 | 0 | } else { |
160 | 0 | dst->data = d = (unsigned char *)PORT_Alloc(dst->len); |
161 | 0 | } |
162 | 0 | dst->type = siGeneralizedTime; |
163 | 0 | if (!d) { |
164 | 0 | return SECFailure; |
165 | 0 | } |
166 | | |
167 | | /* Convert a PRTime to a printable format. */ |
168 | 0 | PR_ExplodeTime(gmttime, PR_GMTParameters, &printableTime); |
169 | | |
170 | | /* The month in Generalized time is base one */ |
171 | 0 | printableTime.tm_month++; |
172 | |
|
173 | 0 | d[0] = (printableTime.tm_year / 1000) + '0'; |
174 | 0 | d[1] = ((printableTime.tm_year % 1000) / 100) + '0'; |
175 | 0 | d[2] = ((printableTime.tm_year % 100) / 10) + '0'; |
176 | 0 | d[3] = (printableTime.tm_year % 10) + '0'; |
177 | 0 | d[4] = HIDIGIT(printableTime.tm_month); |
178 | 0 | d[5] = LODIGIT(printableTime.tm_month); |
179 | 0 | d[6] = HIDIGIT(printableTime.tm_mday); |
180 | 0 | d[7] = LODIGIT(printableTime.tm_mday); |
181 | 0 | d[8] = HIDIGIT(printableTime.tm_hour); |
182 | 0 | d[9] = LODIGIT(printableTime.tm_hour); |
183 | 0 | d[10] = HIDIGIT(printableTime.tm_min); |
184 | 0 | d[11] = LODIGIT(printableTime.tm_min); |
185 | 0 | d[12] = HIDIGIT(printableTime.tm_sec); |
186 | 0 | d[13] = LODIGIT(printableTime.tm_sec); |
187 | 0 | d[14] = 'Z'; |
188 | 0 | return SECSuccess; |
189 | 0 | } |
190 | | |
191 | | SECStatus |
192 | | DER_TimeToGeneralizedTime(SECItem *dst, PRTime gmttime) |
193 | 0 | { |
194 | 0 | return DER_TimeToGeneralizedTimeArena(NULL, dst, gmttime); |
195 | 0 | } |
196 | | |
197 | | SECStatus |
198 | | DER_GeneralizedTimeToTime(PRTime *dst, const SECItem *time) |
199 | 0 | { |
200 | | /* Minimum valid GeneralizedTime is ccyymmddhhmmZ which is 13 bytes. |
201 | | ** Maximum valid GeneralizedTime is ccyymmddhhmmss+0000 which is 19 bytes. |
202 | | ** 20 should be large enough for all valid encoded times. |
203 | | */ |
204 | 0 | unsigned int i; |
205 | 0 | char localBuf[20]; |
206 | 0 | const char *end = NULL; |
207 | 0 | SECStatus rv; |
208 | |
|
209 | 0 | if (!time || !time->data || time->len < 13 || time->len > 19) { |
210 | 0 | PORT_SetError(SEC_ERROR_INVALID_TIME); |
211 | 0 | return SECFailure; |
212 | 0 | } |
213 | | |
214 | 0 | for (i = 0; i < time->len; i++) { |
215 | 0 | if (time->data[i] == '\0') { |
216 | 0 | PORT_SetError(SEC_ERROR_INVALID_TIME); |
217 | 0 | return SECFailure; |
218 | 0 | } |
219 | 0 | localBuf[i] = time->data[i]; |
220 | 0 | } |
221 | 0 | localBuf[i] = '\0'; |
222 | |
|
223 | 0 | rv = der_TimeStringToTime(dst, localBuf, GEN_STRING, &end); |
224 | 0 | if (rv == SECSuccess && *end != '\0') { |
225 | 0 | PORT_SetError(SEC_ERROR_INVALID_TIME); |
226 | 0 | return SECFailure; |
227 | 0 | } |
228 | 0 | return rv; |
229 | 0 | } |
230 | | |
231 | | static SECStatus |
232 | | der_TimeStringToTime(PRTime *dst, const char *string, int generalized, |
233 | | const char **endptr) |
234 | 0 | { |
235 | 0 | PRExplodedTime genTime; |
236 | 0 | long hourOff = 0, minOff = 0; |
237 | 0 | PRUint16 century; |
238 | 0 | char signum; |
239 | |
|
240 | 0 | if (string == NULL || dst == NULL) { |
241 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
242 | 0 | return SECFailure; |
243 | 0 | } |
244 | | |
245 | | /* Verify time is formatted properly and capture information */ |
246 | 0 | memset(&genTime, 0, sizeof genTime); |
247 | |
|
248 | 0 | if (generalized == UTC_STRING) { |
249 | 0 | CAPTURE(genTime.tm_year, string, loser); |
250 | 0 | century = (genTime.tm_year < 50) ? 20 : 19; |
251 | 0 | } else { |
252 | 0 | CAPTURE(century, string, loser); |
253 | 0 | CAPTURE(genTime.tm_year, string, loser); |
254 | 0 | } |
255 | 0 | genTime.tm_year += century * 100; |
256 | |
|
257 | 0 | CAPTURE(genTime.tm_month, string, loser); |
258 | 0 | if ((genTime.tm_month == 0) || (genTime.tm_month > 12)) |
259 | 0 | goto loser; |
260 | | |
261 | | /* NSPR month base is 0 */ |
262 | 0 | --genTime.tm_month; |
263 | |
|
264 | 0 | CAPTURE(genTime.tm_mday, string, loser); |
265 | 0 | if ((genTime.tm_mday == 0) || (genTime.tm_mday > 31)) |
266 | 0 | goto loser; |
267 | | |
268 | 0 | CAPTURE(genTime.tm_hour, string, loser); |
269 | 0 | if (genTime.tm_hour > 23) |
270 | 0 | goto loser; |
271 | | |
272 | 0 | CAPTURE(genTime.tm_min, string, loser); |
273 | 0 | if (genTime.tm_min > 59) |
274 | 0 | goto loser; |
275 | | |
276 | 0 | if (ISDIGIT(string[0])) { |
277 | 0 | CAPTURE(genTime.tm_sec, string, loser); |
278 | 0 | if (genTime.tm_sec > 59) |
279 | 0 | goto loser; |
280 | 0 | } |
281 | 0 | signum = *string++; |
282 | 0 | if (signum == '+' || signum == '-') { |
283 | 0 | CAPTURE(hourOff, string, loser); |
284 | 0 | if (hourOff > 23) |
285 | 0 | goto loser; |
286 | 0 | CAPTURE(minOff, string, loser); |
287 | 0 | if (minOff > 59) |
288 | 0 | goto loser; |
289 | 0 | if (signum == '-') { |
290 | 0 | hourOff = -hourOff; |
291 | 0 | minOff = -minOff; |
292 | 0 | } |
293 | 0 | } else if (signum != 'Z') { |
294 | 0 | goto loser; |
295 | 0 | } |
296 | | |
297 | 0 | if (endptr) |
298 | 0 | *endptr = string; |
299 | | |
300 | | /* Convert the GMT offset to seconds and save it in genTime |
301 | | * for the implode time call. |
302 | | */ |
303 | 0 | genTime.tm_params.tp_gmt_offset = (PRInt32)((hourOff * 60L + minOff) * 60L); |
304 | 0 | *dst = PR_ImplodeTime(&genTime); |
305 | 0 | return SECSuccess; |
306 | | |
307 | 0 | loser: |
308 | 0 | PORT_SetError(SEC_ERROR_INVALID_TIME); |
309 | 0 | return SECFailure; |
310 | 0 | } |