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