security/nss/lib/util/dertime.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial