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