Blame nss/lib/util/dertime.c

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