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])) \
18 goto label; \
19 (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \
20 p += 2; \
21 }
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
DER_TimeToUTCTimeArena(PLArenaPool * arenaOpt,SECItem * dst,PRTime gmttime)30 DER_TimeToUTCTimeArena(PLArenaPool *arenaOpt, SECItem *dst, PRTime gmttime)
31 {
32 PRExplodedTime printableTime;
33 unsigned char *d;
34
35 if ((gmttime < January1st1950) || (gmttime >= January1st2050)) {
36 PORT_SetError(SEC_ERROR_INVALID_ARGS);
37 return SECFailure;
38 }
39
40 dst->len = 13;
41 if (arenaOpt) {
42 dst->data = d = (unsigned char *)PORT_ArenaAlloc(arenaOpt, dst->len);
43 } else {
44 dst->data = d = (unsigned char *)PORT_Alloc(dst->len);
45 }
46 dst->type = siUTCTime;
47 if (!d) {
48 return SECFailure;
49 }
50
51 /* Convert a PRTime to a printable format. */
52 PR_ExplodeTime(gmttime, PR_GMTParameters, &printableTime);
53
54 /* The month in UTC time is base one */
55 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 printableTime.tm_year %= 100;
60
61 d[0] = HIDIGIT(printableTime.tm_year);
62 d[1] = LODIGIT(printableTime.tm_year);
63 d[2] = HIDIGIT(printableTime.tm_month);
64 d[3] = LODIGIT(printableTime.tm_month);
65 d[4] = HIDIGIT(printableTime.tm_mday);
66 d[5] = LODIGIT(printableTime.tm_mday);
67 d[6] = HIDIGIT(printableTime.tm_hour);
68 d[7] = LODIGIT(printableTime.tm_hour);
69 d[8] = HIDIGIT(printableTime.tm_min);
70 d[9] = LODIGIT(printableTime.tm_min);
71 d[10] = HIDIGIT(printableTime.tm_sec);
72 d[11] = LODIGIT(printableTime.tm_sec);
73 d[12] = 'Z';
74 return SECSuccess;
75 }
76
77 SECStatus
DER_TimeToUTCTime(SECItem * dst,PRTime gmttime)78 DER_TimeToUTCTime(SECItem *dst, PRTime gmttime)
79 {
80 return DER_TimeToUTCTimeArena(NULL, dst, gmttime);
81 }
82
83 static SECStatus /* forward */
84 der_TimeStringToTime(PRTime *dst, const char *string, int generalized,
85 const char **endptr);
86
87 #define GEN_STRING 2 /* TimeString is a GeneralizedTime */
88 #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
DER_AsciiToTime(PRTime * dst,const char * string)99 DER_AsciiToTime(PRTime *dst, const char *string)
100 {
101 return der_TimeStringToTime(dst, string, UTC_STRING, NULL);
102 }
103
104 SECStatus
DER_UTCTimeToTime(PRTime * dst,const SECItem * time)105 DER_UTCTimeToTime(PRTime *dst, const SECItem *time)
106 {
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 unsigned int i;
112 char localBuf[20];
113 const char *end = NULL;
114 SECStatus rv;
115
116 if (!time || !time->data || time->len < 11 || time->len > 17) {
117 PORT_SetError(SEC_ERROR_INVALID_TIME);
118 return SECFailure;
119 }
120
121 for (i = 0; i < time->len; i++) {
122 if (time->data[i] == '\0') {
123 PORT_SetError(SEC_ERROR_INVALID_TIME);
124 return SECFailure;
125 }
126 localBuf[i] = time->data[i];
127 }
128 localBuf[i] = '\0';
129
130 rv = der_TimeStringToTime(dst, localBuf, UTC_STRING, &end);
131 if (rv == SECSuccess && *end != '\0') {
132 PORT_SetError(SEC_ERROR_INVALID_TIME);
133 return SECFailure;
134 }
135 return rv;
136 }
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
DER_TimeToGeneralizedTimeArena(PLArenaPool * arenaOpt,SECItem * dst,PRTime gmttime)147 DER_TimeToGeneralizedTimeArena(PLArenaPool *arenaOpt, SECItem *dst, PRTime gmttime)
148 {
149 PRExplodedTime printableTime;
150 unsigned char *d;
151
152 if ((gmttime < January1st1) || (gmttime >= January1st10000)) {
153 PORT_SetError(SEC_ERROR_INVALID_ARGS);
154 return SECFailure;
155 }
156 dst->len = 15;
157 if (arenaOpt) {
158 dst->data = d = (unsigned char *)PORT_ArenaAlloc(arenaOpt, dst->len);
159 } else {
160 dst->data = d = (unsigned char *)PORT_Alloc(dst->len);
161 }
162 dst->type = siGeneralizedTime;
163 if (!d) {
164 return SECFailure;
165 }
166
167 /* Convert a PRTime to a printable format. */
168 PR_ExplodeTime(gmttime, PR_GMTParameters, &printableTime);
169
170 /* The month in Generalized time is base one */
171 printableTime.tm_month++;
172
173 d[0] = (printableTime.tm_year / 1000) + '0';
174 d[1] = ((printableTime.tm_year % 1000) / 100) + '0';
175 d[2] = ((printableTime.tm_year % 100) / 10) + '0';
176 d[3] = (printableTime.tm_year % 10) + '0';
177 d[4] = HIDIGIT(printableTime.tm_month);
178 d[5] = LODIGIT(printableTime.tm_month);
179 d[6] = HIDIGIT(printableTime.tm_mday);
180 d[7] = LODIGIT(printableTime.tm_mday);
181 d[8] = HIDIGIT(printableTime.tm_hour);
182 d[9] = LODIGIT(printableTime.tm_hour);
183 d[10] = HIDIGIT(printableTime.tm_min);
184 d[11] = LODIGIT(printableTime.tm_min);
185 d[12] = HIDIGIT(printableTime.tm_sec);
186 d[13] = LODIGIT(printableTime.tm_sec);
187 d[14] = 'Z';
188 return SECSuccess;
189 }
190
191 SECStatus
DER_TimeToGeneralizedTime(SECItem * dst,PRTime gmttime)192 DER_TimeToGeneralizedTime(SECItem *dst, PRTime gmttime)
193 {
194 return DER_TimeToGeneralizedTimeArena(NULL, dst, gmttime);
195 }
196
197 SECStatus
DER_GeneralizedTimeToTime(PRTime * dst,const SECItem * time)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
der_TimeStringToTime(PRTime * dst,const char * string,int generalized,const char ** endptr)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 }
311