Back to index

lightning-sunbird  0.9+nobinonly
dertime.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #include "prtypes.h"
00038 #include "prtime.h"
00039 #include "secder.h"
00040 #include "prlong.h"
00041 #include "secerr.h"
00042 
00043 #define HIDIGIT(v) (((v) / 10) + '0')
00044 #define LODIGIT(v) (((v) % 10) + '0')
00045 
00046 #define C_SINGLE_QUOTE '\047'
00047 
00048 #define DIGITHI(dig) (((dig) - '0') * 10)
00049 #define DIGITLO(dig) ((dig) - '0')
00050 #define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9'))
00051 #define CAPTURE(var,p,label)                              \
00052 {                                                  \
00053     if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) goto label; \
00054     (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0');         \
00055 }
00056 
00057 #define SECMIN ((time_t) 60L)
00058 #define SECHOUR (60L*SECMIN)
00059 #define SECDAY (24L*SECHOUR)
00060 #define SECYEAR (365L*SECDAY)
00061 
00062 static long monthToDayInYear[12] = {
00063     0,
00064     31,
00065     31+28,
00066     31+28+31,
00067     31+28+31+30,
00068     31+28+31+30+31,
00069     31+28+31+30+31+30,
00070     31+28+31+30+31+30+31,
00071     31+28+31+30+31+30+31+31,
00072     31+28+31+30+31+30+31+31+30,
00073     31+28+31+30+31+30+31+31+30+31,
00074     31+28+31+30+31+30+31+31+30+31+30,
00075 };
00076 
00077 static const PRTime January1st1     = (PRTime) LL_INIT(0xff234001U, 0x00d44000U);
00078 static const PRTime January1st1950  = (PRTime) LL_INIT(0xfffdc1f8U, 0x793da000U);
00079 static const PRTime January1st2050  = LL_INIT(0x0008f81e, 0x1b098000);
00080 static const PRTime January1st10000 = LL_INIT(0x0384440c, 0xcc736000);
00081 
00082 /* gmttime must contains UTC time in micro-seconds unit */
00083 SECStatus
00084 DER_TimeToUTCTimeArena(PRArenaPool* arenaOpt, SECItem *dst, int64 gmttime)
00085 {
00086     PRExplodedTime printableTime;
00087     unsigned char *d;
00088 
00089     if ( (gmttime < January1st1950) || (gmttime >= January1st2050) ) {
00090         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00091         return SECFailure;
00092     }
00093 
00094     dst->len = 13;
00095     if (arenaOpt) {
00096         dst->data = d = (unsigned char*) PORT_ArenaAlloc(arenaOpt, dst->len);
00097     } else {
00098         dst->data = d = (unsigned char*) PORT_Alloc(dst->len);
00099     }
00100     dst->type = siUTCTime;
00101     if (!d) {
00102        return SECFailure;
00103     }
00104 
00105     /* Convert an int64 time to a printable format.  */
00106     PR_ExplodeTime(gmttime, PR_GMTParameters, &printableTime);
00107 
00108     /* The month in UTC time is base one */
00109     printableTime.tm_month++;
00110 
00111     /* remove the century since it's added to the tm_year by the 
00112        PR_ExplodeTime routine, but is not needed for UTC time */
00113     printableTime.tm_year %= 100; 
00114 
00115     d[0] = HIDIGIT(printableTime.tm_year);
00116     d[1] = LODIGIT(printableTime.tm_year);
00117     d[2] = HIDIGIT(printableTime.tm_month);
00118     d[3] = LODIGIT(printableTime.tm_month);
00119     d[4] = HIDIGIT(printableTime.tm_mday);
00120     d[5] = LODIGIT(printableTime.tm_mday);
00121     d[6] = HIDIGIT(printableTime.tm_hour);
00122     d[7] = LODIGIT(printableTime.tm_hour);
00123     d[8] = HIDIGIT(printableTime.tm_min);
00124     d[9] = LODIGIT(printableTime.tm_min);
00125     d[10] = HIDIGIT(printableTime.tm_sec);
00126     d[11] = LODIGIT(printableTime.tm_sec);
00127     d[12] = 'Z';
00128     return SECSuccess;
00129 }
00130 
00131 SECStatus
00132 DER_TimeToUTCTime(SECItem *dst, int64 gmttime)
00133 {
00134     return DER_TimeToUTCTimeArena(NULL, dst, gmttime);
00135 }
00136 
00137 /* The caller of DER_AsciiToItem MUST ENSURE that either
00138 ** a) "string" points to a null-terminated ASCII string, or
00139 ** b) "string" points to a buffer containing a valid UTCTime, 
00140 **     whether null terminated or not.
00141 ** otherwise, this function may UMR and/or crash.
00142 ** It suffices to ensure that the input "string" is at least 17 bytes long.
00143 */
00144 SECStatus
00145 DER_AsciiToTime(int64 *dst, const char *string)
00146 {
00147     long year, month, mday, hour, minute, second, hourOff, minOff, days;
00148     int64 result, tmp1, tmp2;
00149 
00150     if (string == NULL) {
00151        goto loser;
00152     }
00153     
00154     /* Verify time is formatted properly and capture information */
00155     second = 0;
00156     hourOff = 0;
00157     minOff = 0;
00158     CAPTURE(year,string+0,loser);
00159     if (year < 50) {
00160        /* ASSUME that year # is in the 2000's, not the 1900's */
00161        year += 100;
00162     }
00163     CAPTURE(month,string+2,loser);
00164     if ((month == 0) || (month > 12)) goto loser;
00165     CAPTURE(mday,string+4,loser);
00166     if ((mday == 0) || (mday > 31)) goto loser;
00167     CAPTURE(hour,string+6,loser);
00168     if (hour > 23) goto loser;
00169     CAPTURE(minute,string+8,loser);
00170     if (minute > 59) goto loser;
00171     if (ISDIGIT(string[10])) {
00172        CAPTURE(second,string+10,loser);
00173        if (second > 59) goto loser;
00174        string += 2;
00175     }
00176     if (string[10] == '+') {
00177        CAPTURE(hourOff,string+11,loser);
00178        if (hourOff > 23) goto loser;
00179        CAPTURE(minOff,string+13,loser);
00180        if (minOff > 59) goto loser;
00181     } else if (string[10] == '-') {
00182        CAPTURE(hourOff,string+11,loser);
00183        if (hourOff > 23) goto loser;
00184        hourOff = -hourOff;
00185        CAPTURE(minOff,string+13,loser);
00186        if (minOff > 59) goto loser;
00187        minOff = -minOff;
00188     } else if (string[10] != 'Z') {
00189        goto loser;
00190     }
00191     
00192     
00193     /* Convert pieces back into a single value year  */
00194     LL_I2L(tmp1, (year-70L));
00195     LL_I2L(tmp2, SECYEAR);
00196     LL_MUL(result, tmp1, tmp2);
00197     
00198     LL_I2L(tmp1, ( (mday-1L)*SECDAY + hour*SECHOUR + minute*SECMIN -
00199                 hourOff*SECHOUR - minOff*SECMIN + second ) );
00200     LL_ADD(result, result, tmp1);
00201 
00202     /*
00203     ** Have to specially handle the day in the month and the year, to
00204     ** take into account leap days. The return time value is in
00205     ** seconds since January 1st, 12:00am 1970, so start examining
00206     ** the time after that. We can't represent a time before that.
00207     */
00208 
00209     /* Using two digit years, we can only represent dates from 1970
00210        to 2069. As a result, we cannot run into the leap year rule
00211        that states that 1700, 2100, etc. are not leap years (but 2000
00212        is). In other words, there are no years in the span of time
00213        that we can represent that are == 0 mod 4 but are not leap
00214        years. Whew.
00215        */
00216 
00217     days = monthToDayInYear[month-1];
00218     days += (year - 68)/4;
00219 
00220     if (((year % 4) == 0) && (month < 3)) {
00221        days--;
00222     }
00223    
00224     LL_I2L(tmp1, (days * SECDAY) );
00225     LL_ADD(result, result, tmp1 );
00226 
00227     /* convert to micro seconds */
00228     LL_I2L(tmp1, PR_USEC_PER_SEC);
00229     LL_MUL(result, result, tmp1);
00230 
00231     *dst = result;
00232     return SECSuccess;
00233 
00234   loser:
00235     PORT_SetError(SEC_ERROR_INVALID_TIME);
00236     return SECFailure;
00237        
00238 }
00239 
00240 SECStatus
00241 DER_UTCTimeToTime(int64 *dst, const SECItem *time)
00242 {
00243     const char * string;
00244     char localBuf[20]; 
00245 
00246     /* Minimum valid UTCTime is yymmddhhmmZ       which is 11 bytes. 
00247     ** Maximum valid UTCTime is yymmddhhmmss+0000 which is 17 bytes.
00248     ** 20 should be large enough for all valid encoded times. 
00249     */
00250     if (!time || !time->data || time->len < 11) {
00251        PORT_SetError(SEC_ERROR_INVALID_TIME);
00252        return SECFailure;
00253     }
00254     if (time->len >= sizeof localBuf) { 
00255        string = (const char *)time->data;
00256     } else {
00257        memset(localBuf, 0, sizeof localBuf);
00258        memcpy(localBuf, time->data, time->len);
00259         string = (const char *)localBuf;
00260     }
00261     return DER_AsciiToTime(dst, string);
00262 }
00263 
00264 /*
00265    gmttime must contains UTC time in micro-seconds unit.
00266    Note: the caller should make sure that Generalized time
00267    should only be used for certifiate validities after the
00268    year 2049.  Otherwise, UTC time should be used.  This routine
00269    does not check this case, since it can be used to encode
00270    certificate extension, which does not have this restriction. 
00271  */
00272 SECStatus
00273 DER_TimeToGeneralizedTimeArena(PRArenaPool* arenaOpt, SECItem *dst, int64 gmttime)
00274 {
00275     PRExplodedTime printableTime;
00276     unsigned char *d;
00277 
00278     if ( (gmttime<January1st1) || (gmttime>=January1st10000) ) {
00279         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00280         return SECFailure;
00281     }
00282     dst->len = 15;
00283     if (arenaOpt) {
00284         dst->data = d = (unsigned char*) PORT_ArenaAlloc(arenaOpt, dst->len);
00285     } else {
00286         dst->data = d = (unsigned char*) PORT_Alloc(dst->len);
00287     }
00288     dst->type = siGeneralizedTime;
00289     if (!d) {
00290        return SECFailure;
00291     }
00292 
00293     /* Convert an int64 time to a printable format.  */
00294     PR_ExplodeTime(gmttime, PR_GMTParameters, &printableTime);
00295 
00296     /* The month in Generalized time is base one */
00297     printableTime.tm_month++;
00298 
00299     d[0] = (printableTime.tm_year /1000) + '0';
00300     d[1] = ((printableTime.tm_year % 1000) / 100) + '0';
00301     d[2] = ((printableTime.tm_year % 100) / 10) + '0';
00302     d[3] = (printableTime.tm_year % 10) + '0';
00303     d[4] = HIDIGIT(printableTime.tm_month);
00304     d[5] = LODIGIT(printableTime.tm_month);
00305     d[6] = HIDIGIT(printableTime.tm_mday);
00306     d[7] = LODIGIT(printableTime.tm_mday);
00307     d[8] = HIDIGIT(printableTime.tm_hour);
00308     d[9] = LODIGIT(printableTime.tm_hour);
00309     d[10] = HIDIGIT(printableTime.tm_min);
00310     d[11] = LODIGIT(printableTime.tm_min);
00311     d[12] = HIDIGIT(printableTime.tm_sec);
00312     d[13] = LODIGIT(printableTime.tm_sec);
00313     d[14] = 'Z';
00314     return SECSuccess;
00315 }
00316 
00317 SECStatus
00318 DER_TimeToGeneralizedTime(SECItem *dst, int64 gmttime)
00319 {
00320     return DER_TimeToGeneralizedTimeArena(NULL, dst, gmttime);
00321 }
00322 
00323 
00324 /*
00325     The caller should make sure that the generalized time should only
00326     be used for the certificate validity after the year 2051; otherwise,
00327     the certificate should be consider invalid!?
00328  */
00329 SECStatus
00330 DER_GeneralizedTimeToTime(int64 *dst, const SECItem *time)
00331 {
00332     PRExplodedTime genTime;
00333     const char *string;
00334     long hourOff, minOff;
00335     uint16 century;
00336     char localBuf[20];
00337 
00338     /* Minimum valid GeneralizedTime is ccyymmddhhmmZ       which is 13 bytes.
00339     ** Maximum valid GeneralizedTime is ccyymmddhhmmss+0000 which is 19 bytes.
00340     ** 20 should be large enough for all valid encoded times. 
00341     */
00342     if (!time || !time->data || time->len < 13)
00343         goto loser;
00344     if (time->len >= sizeof localBuf) {
00345         string = (const char *)time->data;
00346     } else {
00347        memset(localBuf, 0, sizeof localBuf);
00348         memcpy(localBuf, time->data, time->len);
00349        string = (const char *)localBuf;
00350     }
00351 
00352     memset(&genTime, 0, sizeof genTime);
00353 
00354     /* Verify time is formatted properly and capture information */
00355     hourOff = 0;
00356     minOff = 0;
00357 
00358     CAPTURE(century, string+0, loser);
00359     century *= 100;
00360     CAPTURE(genTime.tm_year,string+2,loser);
00361     genTime.tm_year += century;
00362 
00363     CAPTURE(genTime.tm_month,string+4,loser);
00364     if ((genTime.tm_month == 0) || (genTime.tm_month > 12)) goto loser;
00365 
00366     /* NSPR month base is 0 */
00367     --genTime.tm_month;
00368     
00369     CAPTURE(genTime.tm_mday,string+6,loser);
00370     if ((genTime.tm_mday == 0) || (genTime.tm_mday > 31)) goto loser;
00371     
00372     CAPTURE(genTime.tm_hour,string+8,loser);
00373     if (genTime.tm_hour > 23) goto loser;
00374     
00375     CAPTURE(genTime.tm_min,string+10,loser);
00376     if (genTime.tm_min > 59) goto loser;
00377     
00378     if (ISDIGIT(string[12])) {
00379        CAPTURE(genTime.tm_sec,string+12,loser);
00380        if (genTime.tm_sec > 59) goto loser;
00381        string += 2;
00382     }
00383     if (string[12] == '+') {
00384        CAPTURE(hourOff,string+13,loser);
00385        if (hourOff > 23) goto loser;
00386        CAPTURE(minOff,string+15,loser);
00387        if (minOff > 59) goto loser;
00388     } else if (string[12] == '-') {
00389        CAPTURE(hourOff,string+13,loser);
00390        if (hourOff > 23) goto loser;
00391        hourOff = -hourOff;
00392        CAPTURE(minOff,string+15,loser);
00393        if (minOff > 59) goto loser;
00394        minOff = -minOff;
00395     } else if (string[12] != 'Z') {
00396        goto loser;
00397     }
00398 
00399     /* Since the values of hourOff and minOff are small, there will
00400        be no loss of data by the conversion to int8 */
00401     /* Convert the GMT offset to seconds and save it it genTime
00402        for the implode time process */
00403     genTime.tm_params.tp_gmt_offset = (PRInt32)((hourOff * 60L + minOff) * 60L);
00404     *dst = PR_ImplodeTime (&genTime);
00405     return SECSuccess;
00406 
00407   loser:
00408     PORT_SetError(SEC_ERROR_INVALID_TIME);
00409     return SECFailure;
00410        
00411 }