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