michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "prtypes.h" michael@0: #include "prtime.h" michael@0: #include "secder.h" michael@0: #include "prlong.h" michael@0: #include "secerr.h" michael@0: michael@0: #define HIDIGIT(v) (((v) / 10) + '0') michael@0: #define LODIGIT(v) (((v) % 10) + '0') michael@0: michael@0: #define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9')) michael@0: #define CAPTURE(var,p,label) \ michael@0: { \ michael@0: if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) goto label; \ michael@0: (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \ michael@0: p += 2; \ michael@0: } michael@0: michael@0: static const PRTime January1st1 = (PRTime) LL_INIT(0xff234001U, 0x00d44000U); michael@0: static const PRTime January1st1950 = (PRTime) LL_INIT(0xfffdc1f8U, 0x793da000U); michael@0: static const PRTime January1st2050 = LL_INIT(0x0008f81e, 0x1b098000); michael@0: static const PRTime January1st10000 = LL_INIT(0x0384440c, 0xcc736000); michael@0: michael@0: /* gmttime must contains UTC time in micro-seconds unit */ michael@0: SECStatus michael@0: DER_TimeToUTCTimeArena(PLArenaPool* arenaOpt, SECItem *dst, PRTime gmttime) michael@0: { michael@0: PRExplodedTime printableTime; michael@0: unsigned char *d; michael@0: michael@0: if ( (gmttime < January1st1950) || (gmttime >= January1st2050) ) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: dst->len = 13; michael@0: if (arenaOpt) { michael@0: dst->data = d = (unsigned char*) PORT_ArenaAlloc(arenaOpt, dst->len); michael@0: } else { michael@0: dst->data = d = (unsigned char*) PORT_Alloc(dst->len); michael@0: } michael@0: dst->type = siUTCTime; michael@0: if (!d) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* Convert a PRTime to a printable format. */ michael@0: PR_ExplodeTime(gmttime, PR_GMTParameters, &printableTime); michael@0: michael@0: /* The month in UTC time is base one */ michael@0: printableTime.tm_month++; michael@0: michael@0: /* remove the century since it's added to the tm_year by the michael@0: PR_ExplodeTime routine, but is not needed for UTC time */ michael@0: printableTime.tm_year %= 100; michael@0: michael@0: d[0] = HIDIGIT(printableTime.tm_year); michael@0: d[1] = LODIGIT(printableTime.tm_year); michael@0: d[2] = HIDIGIT(printableTime.tm_month); michael@0: d[3] = LODIGIT(printableTime.tm_month); michael@0: d[4] = HIDIGIT(printableTime.tm_mday); michael@0: d[5] = LODIGIT(printableTime.tm_mday); michael@0: d[6] = HIDIGIT(printableTime.tm_hour); michael@0: d[7] = LODIGIT(printableTime.tm_hour); michael@0: d[8] = HIDIGIT(printableTime.tm_min); michael@0: d[9] = LODIGIT(printableTime.tm_min); michael@0: d[10] = HIDIGIT(printableTime.tm_sec); michael@0: d[11] = LODIGIT(printableTime.tm_sec); michael@0: d[12] = 'Z'; michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus michael@0: DER_TimeToUTCTime(SECItem *dst, PRTime gmttime) michael@0: { michael@0: return DER_TimeToUTCTimeArena(NULL, dst, gmttime); michael@0: } michael@0: michael@0: static SECStatus /* forward */ michael@0: der_TimeStringToTime(PRTime *dst, const char *string, int generalized, michael@0: const char **endptr); michael@0: michael@0: #define GEN_STRING 2 /* TimeString is a GeneralizedTime */ michael@0: #define UTC_STRING 0 /* TimeString is a UTCTime */ michael@0: michael@0: /* The caller of DER_AsciiToItem MUST ENSURE that either michael@0: ** a) "string" points to a null-terminated ASCII string, or michael@0: ** b) "string" points to a buffer containing a valid UTCTime, michael@0: ** whether null terminated or not, or michael@0: ** c) "string" contains at least 19 characters, with or without null char. michael@0: ** otherwise, this function may UMR and/or crash. michael@0: ** It suffices to ensure that the input "string" is at least 17 bytes long. michael@0: */ michael@0: SECStatus michael@0: DER_AsciiToTime(PRTime *dst, const char *string) michael@0: { michael@0: return der_TimeStringToTime(dst, string, UTC_STRING, NULL); michael@0: } michael@0: michael@0: SECStatus michael@0: DER_UTCTimeToTime(PRTime *dst, const SECItem *time) michael@0: { michael@0: /* Minimum valid UTCTime is yymmddhhmmZ which is 11 bytes. michael@0: ** Maximum valid UTCTime is yymmddhhmmss+0000 which is 17 bytes. michael@0: ** 20 should be large enough for all valid encoded times. michael@0: */ michael@0: unsigned int i; michael@0: char localBuf[20]; michael@0: const char *end = NULL; michael@0: SECStatus rv; michael@0: michael@0: if (!time || !time->data || time->len < 11 || time->len > 17) { michael@0: PORT_SetError(SEC_ERROR_INVALID_TIME); michael@0: return SECFailure; michael@0: } michael@0: michael@0: for (i = 0; i < time->len; i++) { michael@0: if (time->data[i] == '\0') { michael@0: PORT_SetError(SEC_ERROR_INVALID_TIME); michael@0: return SECFailure; michael@0: } michael@0: localBuf[i] = time->data[i]; michael@0: } michael@0: localBuf[i] = '\0'; michael@0: michael@0: rv = der_TimeStringToTime(dst, localBuf, UTC_STRING, &end); michael@0: if (rv == SECSuccess && *end != '\0') { michael@0: PORT_SetError(SEC_ERROR_INVALID_TIME); michael@0: return SECFailure; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: gmttime must contains UTC time in micro-seconds unit. michael@0: Note: the caller should make sure that Generalized time michael@0: should only be used for certifiate validities after the michael@0: year 2049. Otherwise, UTC time should be used. This routine michael@0: does not check this case, since it can be used to encode michael@0: certificate extension, which does not have this restriction. michael@0: */ michael@0: SECStatus michael@0: DER_TimeToGeneralizedTimeArena(PLArenaPool* arenaOpt, SECItem *dst, PRTime gmttime) michael@0: { michael@0: PRExplodedTime printableTime; michael@0: unsigned char *d; michael@0: michael@0: if ( (gmttime=January1st10000) ) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: dst->len = 15; michael@0: if (arenaOpt) { michael@0: dst->data = d = (unsigned char*) PORT_ArenaAlloc(arenaOpt, dst->len); michael@0: } else { michael@0: dst->data = d = (unsigned char*) PORT_Alloc(dst->len); michael@0: } michael@0: dst->type = siGeneralizedTime; michael@0: if (!d) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* Convert a PRTime to a printable format. */ michael@0: PR_ExplodeTime(gmttime, PR_GMTParameters, &printableTime); michael@0: michael@0: /* The month in Generalized time is base one */ michael@0: printableTime.tm_month++; michael@0: michael@0: d[0] = (printableTime.tm_year /1000) + '0'; michael@0: d[1] = ((printableTime.tm_year % 1000) / 100) + '0'; michael@0: d[2] = ((printableTime.tm_year % 100) / 10) + '0'; michael@0: d[3] = (printableTime.tm_year % 10) + '0'; michael@0: d[4] = HIDIGIT(printableTime.tm_month); michael@0: d[5] = LODIGIT(printableTime.tm_month); michael@0: d[6] = HIDIGIT(printableTime.tm_mday); michael@0: d[7] = LODIGIT(printableTime.tm_mday); michael@0: d[8] = HIDIGIT(printableTime.tm_hour); michael@0: d[9] = LODIGIT(printableTime.tm_hour); michael@0: d[10] = HIDIGIT(printableTime.tm_min); michael@0: d[11] = LODIGIT(printableTime.tm_min); michael@0: d[12] = HIDIGIT(printableTime.tm_sec); michael@0: d[13] = LODIGIT(printableTime.tm_sec); michael@0: d[14] = 'Z'; michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus michael@0: DER_TimeToGeneralizedTime(SECItem *dst, PRTime gmttime) michael@0: { michael@0: return DER_TimeToGeneralizedTimeArena(NULL, dst, gmttime); michael@0: } michael@0: michael@0: michael@0: SECStatus michael@0: DER_GeneralizedTimeToTime(PRTime *dst, const SECItem *time) michael@0: { michael@0: /* Minimum valid GeneralizedTime is ccyymmddhhmmZ which is 13 bytes. michael@0: ** Maximum valid GeneralizedTime is ccyymmddhhmmss+0000 which is 19 bytes. michael@0: ** 20 should be large enough for all valid encoded times. michael@0: */ michael@0: unsigned int i; michael@0: char localBuf[20]; michael@0: const char *end = NULL; michael@0: SECStatus rv; michael@0: michael@0: if (!time || !time->data || time->len < 13 || time->len > 19) { michael@0: PORT_SetError(SEC_ERROR_INVALID_TIME); michael@0: return SECFailure; michael@0: } michael@0: michael@0: for (i = 0; i < time->len; i++) { michael@0: if (time->data[i] == '\0') { michael@0: PORT_SetError(SEC_ERROR_INVALID_TIME); michael@0: return SECFailure; michael@0: } michael@0: localBuf[i] = time->data[i]; michael@0: } michael@0: localBuf[i] = '\0'; michael@0: michael@0: rv = der_TimeStringToTime(dst, localBuf, GEN_STRING, &end); michael@0: if (rv == SECSuccess && *end != '\0') { michael@0: PORT_SetError(SEC_ERROR_INVALID_TIME); michael@0: return SECFailure; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: static SECStatus michael@0: der_TimeStringToTime(PRTime *dst, const char *string, int generalized, michael@0: const char **endptr) michael@0: { michael@0: PRExplodedTime genTime; michael@0: long hourOff = 0, minOff = 0; michael@0: PRUint16 century; michael@0: char signum; michael@0: michael@0: if (string == NULL || dst == NULL) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* Verify time is formatted properly and capture information */ michael@0: memset(&genTime, 0, sizeof genTime); michael@0: michael@0: if (generalized == UTC_STRING) { michael@0: CAPTURE(genTime.tm_year, string, loser); michael@0: century = (genTime.tm_year < 50) ? 20 : 19; michael@0: } else { michael@0: CAPTURE(century, string, loser); michael@0: CAPTURE(genTime.tm_year, string, loser); michael@0: } michael@0: genTime.tm_year += century * 100; michael@0: michael@0: CAPTURE(genTime.tm_month, string, loser); michael@0: if ((genTime.tm_month == 0) || (genTime.tm_month > 12)) michael@0: goto loser; michael@0: michael@0: /* NSPR month base is 0 */ michael@0: --genTime.tm_month; michael@0: michael@0: CAPTURE(genTime.tm_mday, string, loser); michael@0: if ((genTime.tm_mday == 0) || (genTime.tm_mday > 31)) michael@0: goto loser; michael@0: michael@0: CAPTURE(genTime.tm_hour, string, loser); michael@0: if (genTime.tm_hour > 23) michael@0: goto loser; michael@0: michael@0: CAPTURE(genTime.tm_min, string, loser); michael@0: if (genTime.tm_min > 59) michael@0: goto loser; michael@0: michael@0: if (ISDIGIT(string[0])) { michael@0: CAPTURE(genTime.tm_sec, string, loser); michael@0: if (genTime.tm_sec > 59) michael@0: goto loser; michael@0: } michael@0: signum = *string++; michael@0: if (signum == '+' || signum == '-') { michael@0: CAPTURE(hourOff, string, loser); michael@0: if (hourOff > 23) michael@0: goto loser; michael@0: CAPTURE(minOff, string, loser); michael@0: if (minOff > 59) michael@0: goto loser; michael@0: if (signum == '-') { michael@0: hourOff = -hourOff; michael@0: minOff = -minOff; michael@0: } michael@0: } else if (signum != 'Z') { michael@0: goto loser; michael@0: } michael@0: michael@0: if (endptr) michael@0: *endptr = string; michael@0: michael@0: /* Convert the GMT offset to seconds and save it in genTime michael@0: * for the implode time call. michael@0: */ michael@0: genTime.tm_params.tp_gmt_offset = (PRInt32)((hourOff * 60L + minOff) * 60L); michael@0: *dst = PR_ImplodeTime(&genTime); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: PORT_SetError(SEC_ERROR_INVALID_TIME); michael@0: return SECFailure; michael@0: }