Coverage Report

Created: 2026-07-07 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nss/lib/util/dertime.c
Line
Count
Source
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
969k
#define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9'))
15
#define CAPTURE(var, p, label)                        \
16
308k
    {                                                 \
17
308k
        if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1]))     \
18
308k
            goto label;                               \
19
308k
        (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \
20
302k
        p += 2;                                       \
21
302k
    }
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
9.74k
#define GEN_STRING 2 /* TimeString is a GeneralizedTime */
88
97.9k
#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
44.7k
{
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
44.7k
    unsigned int i;
112
44.7k
    char localBuf[20];
113
44.7k
    const char *end = NULL;
114
44.7k
    SECStatus rv;
115
116
44.7k
    if (!time || !time->data || time->len < 11 || time->len > 17) {
117
122
        PORT_SetError(SEC_ERROR_INVALID_TIME);
118
122
        return SECFailure;
119
122
    }
120
121
620k
    for (i = 0; i < time->len; i++) {
122
576k
        if (time->data[i] == '\0') {
123
525
            PORT_SetError(SEC_ERROR_INVALID_TIME);
124
525
            return SECFailure;
125
525
        }
126
576k
        localBuf[i] = time->data[i];
127
576k
    }
128
44.1k
    localBuf[i] = '\0';
129
130
44.1k
    rv = der_TimeStringToTime(dst, localBuf, UTC_STRING, &end);
131
44.1k
    if (rv == SECSuccess && *end != '\0') {
132
100
        PORT_SetError(SEC_ERROR_INVALID_TIME);
133
100
        return SECFailure;
134
100
    }
135
44.0k
    return rv;
136
44.1k
}
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
10.3k
{
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
10.3k
    unsigned int i;
205
10.3k
    char localBuf[20];
206
10.3k
    const char *end = NULL;
207
10.3k
    SECStatus rv;
208
209
10.3k
    if (!time || !time->data || time->len < 13 || time->len > 19) {
210
125
        PORT_SetError(SEC_ERROR_INVALID_TIME);
211
125
        return SECFailure;
212
125
    }
213
214
149k
    for (i = 0; i < time->len; i++) {
215
139k
        if (time->data[i] == '\0') {
216
458
            PORT_SetError(SEC_ERROR_INVALID_TIME);
217
458
            return SECFailure;
218
458
        }
219
139k
        localBuf[i] = time->data[i];
220
139k
    }
221
9.74k
    localBuf[i] = '\0';
222
223
9.74k
    rv = der_TimeStringToTime(dst, localBuf, GEN_STRING, &end);
224
9.74k
    if (rv == SECSuccess && *end != '\0') {
225
57
        PORT_SetError(SEC_ERROR_INVALID_TIME);
226
57
        return SECFailure;
227
57
    }
228
9.68k
    return rv;
229
9.74k
}
230
231
static SECStatus
232
der_TimeStringToTime(PRTime *dst, const char *string, int generalized,
233
                     const char **endptr)
234
53.8k
{
235
53.8k
    PRExplodedTime genTime;
236
53.8k
    long hourOff = 0, minOff = 0;
237
53.8k
    PRUint16 century;
238
53.8k
    char signum;
239
240
53.8k
    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
53.8k
    memset(&genTime, 0, sizeof genTime);
247
248
53.8k
    if (generalized == UTC_STRING) {
249
44.1k
        CAPTURE(genTime.tm_year, string, loser);
250
43.3k
        century = (genTime.tm_year < 50) ? 20 : 19;
251
43.3k
    } else {
252
9.74k
        CAPTURE(century, string, loser);
253
8.99k
        CAPTURE(genTime.tm_year, string, loser);
254
8.31k
    }
255
51.6k
    genTime.tm_year += century * 100;
256
257
51.6k
    CAPTURE(genTime.tm_month, string, loser);
258
50.8k
    if ((genTime.tm_month == 0) || (genTime.tm_month > 12))
259
354
        goto loser;
260
261
    /* NSPR month base is 0 */
262
50.5k
    --genTime.tm_month;
263
264
50.5k
    CAPTURE(genTime.tm_mday, string, loser);
265
49.8k
    if ((genTime.tm_mday == 0) || (genTime.tm_mday > 31))
266
292
        goto loser;
267
268
98.4k
    CAPTURE(genTime.tm_hour, string, loser);
269
98.4k
    if (genTime.tm_hour > 23)
270
169
        goto loser;
271
272
96.9k
    CAPTURE(genTime.tm_min, string, loser);
273
96.9k
    if (genTime.tm_min > 59)
274
162
        goto loser;
275
276
48.0k
    if (ISDIGIT(string[0])) {
277
43.9k
        CAPTURE(genTime.tm_sec, string, loser);
278
43.6k
        if (genTime.tm_sec > 59)
279
108
            goto loser;
280
43.6k
    }
281
47.5k
    signum = *string++;
282
47.5k
    if (signum == '+' || signum == '-') {
283
588
        CAPTURE(hourOff, string, loser);
284
184
        if (hourOff > 23)
285
87
            goto loser;
286
97
        CAPTURE(minOff, string, loser);
287
97
        if (minOff > 59)
288
0
            goto loser;
289
0
        if (signum == '-') {
290
0
            hourOff = -hourOff;
291
0
            minOff = -minOff;
292
0
        }
293
46.9k
    } else if (signum != 'Z') {
294
380
        goto loser;
295
380
    }
296
297
46.5k
    if (endptr)
298
46.5k
        *endptr = string;
299
300
    /* Convert the GMT offset to seconds and save it in genTime
301
     * for the implode time call.
302
     */
303
46.5k
    genTime.tm_params.tp_gmt_offset = (PRInt32)((hourOff * 60L + minOff) * 60L);
304
46.5k
    *dst = PR_ImplodeTime(&genTime);
305
46.5k
    return SECSuccess;
306
307
7.28k
loser:
308
7.28k
    PORT_SetError(SEC_ERROR_INVALID_TIME);
309
7.28k
    return SECFailure;
310
47.5k
}