Coverage Report

Created: 2024-05-20 06:23

/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
15.2k
#define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9'))
15
#define CAPTURE(var, p, label)                        \
16
4.95k
    {                                                 \
17
4.95k
        if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1]))     \
18
4.95k
            goto label;                               \
19
4.95k
        (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \
20
4.43k
        p += 2;                                       \
21
4.43k
    }
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
180
#define GEN_STRING 2 /* TimeString is a GeneralizedTime */
88
2.18k
#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
1.03k
{
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
1.03k
    unsigned int i;
112
1.03k
    char localBuf[20];
113
1.03k
    const char *end = NULL;
114
1.03k
    SECStatus rv;
115
116
1.03k
    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
14.2k
    for (i = 0; i < time->len; i++) {
122
13.1k
        if (time->data[i] == '\0') {
123
35
            PORT_SetError(SEC_ERROR_INVALID_TIME);
124
35
            return SECFailure;
125
35
        }
126
13.1k
        localBuf[i] = time->data[i];
127
13.1k
    }
128
1.00k
    localBuf[i] = '\0';
129
130
1.00k
    rv = der_TimeStringToTime(dst, localBuf, UTC_STRING, &end);
131
1.00k
    if (rv == SECSuccess && *end != '\0') {
132
3
        PORT_SetError(SEC_ERROR_INVALID_TIME);
133
3
        return SECFailure;
134
3
    }
135
998
    return rv;
136
1.00k
}
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
205
{
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
205
    unsigned int i;
205
205
    char localBuf[20];
206
205
    const char *end = NULL;
207
205
    SECStatus rv;
208
209
205
    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
2.67k
    for (i = 0; i < time->len; i++) {
215
2.49k
        if (time->data[i] == '\0') {
216
25
            PORT_SetError(SEC_ERROR_INVALID_TIME);
217
25
            return SECFailure;
218
25
        }
219
2.47k
        localBuf[i] = time->data[i];
220
2.47k
    }
221
180
    localBuf[i] = '\0';
222
223
180
    rv = der_TimeStringToTime(dst, localBuf, GEN_STRING, &end);
224
180
    if (rv == SECSuccess && *end != '\0') {
225
0
        PORT_SetError(SEC_ERROR_INVALID_TIME);
226
0
        return SECFailure;
227
0
    }
228
180
    return rv;
229
180
}
230
231
static SECStatus
232
der_TimeStringToTime(PRTime *dst, const char *string, int generalized,
233
                     const char **endptr)
234
1.18k
{
235
1.18k
    PRExplodedTime genTime;
236
1.18k
    long hourOff = 0, minOff = 0;
237
1.18k
    PRUint16 century;
238
1.18k
    char signum;
239
240
1.18k
    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
1.18k
    memset(&genTime, 0, sizeof genTime);
247
248
1.18k
    if (generalized == UTC_STRING) {
249
1.00k
        CAPTURE(genTime.tm_year, string, loser);
250
866
        century = (genTime.tm_year < 50) ? 20 : 19;
251
866
    } else {
252
180
        CAPTURE(century, string, loser);
253
97
        CAPTURE(genTime.tm_year, string, loser);
254
51
    }
255
917
    genTime.tm_year += century * 100;
256
257
917
    CAPTURE(genTime.tm_month, string, loser);
258
847
    if ((genTime.tm_month == 0) || (genTime.tm_month > 12))
259
49
        goto loser;
260
261
    /* NSPR month base is 0 */
262
798
    --genTime.tm_month;
263
264
798
    CAPTURE(genTime.tm_mday, string, loser);
265
741
    if ((genTime.tm_mday == 0) || (genTime.tm_mday > 31))
266
22
        goto loser;
267
268
1.40k
    CAPTURE(genTime.tm_hour, string, loser);
269
1.40k
    if (genTime.tm_hour > 23)
270
15
        goto loser;
271
272
1.29k
    CAPTURE(genTime.tm_min, string, loser);
273
1.29k
    if (genTime.tm_min > 59)
274
9
        goto loser;
275
276
620
    if (ISDIGIT(string[0])) {
277
523
        CAPTURE(genTime.tm_sec, string, loser);
278
507
        if (genTime.tm_sec > 59)
279
12
            goto loser;
280
507
    }
281
592
    signum = *string++;
282
592
    if (signum == '+' || signum == '-') {
283
49
        CAPTURE(hourOff, string, loser);
284
11
        if (hourOff > 23)
285
3
            goto loser;
286
8
        CAPTURE(minOff, string, loser);
287
8
        if (minOff > 59)
288
0
            goto loser;
289
0
        if (signum == '-') {
290
0
            hourOff = -hourOff;
291
0
            minOff = -minOff;
292
0
        }
293
543
    } else if (signum != 'Z') {
294
22
        goto loser;
295
22
    }
296
297
521
    if (endptr)
298
521
        *endptr = string;
299
300
    /* Convert the GMT offset to seconds and save it in genTime
301
     * for the implode time call.
302
     */
303
521
    genTime.tm_params.tp_gmt_offset = (PRInt32)((hourOff * 60L + minOff) * 60L);
304
521
    *dst = PR_ImplodeTime(&genTime);
305
521
    return SECSuccess;
306
307
660
loser:
308
660
    PORT_SetError(SEC_ERROR_INVALID_TIME);
309
660
    return SECFailure;
310
592
}