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 /* gmttime must contains UTC time in micro-seconds unit */
00078 SECStatus
00079 DER_TimeToUTCTimeArena(PRArenaPool* arenaOpt, SECItem *dst, int64 gmttime)
00080 {
00081     PRExplodedTime printableTime;
00082     unsigned char *d;
00083 
00084     dst->len = 13;
00085     if (arenaOpt) {
00086         dst->data = d = (unsigned char*) PORT_ArenaAlloc(arenaOpt, dst->len);
00087     } else {
00088         dst->data = d = (unsigned char*) PORT_Alloc(dst->len);
00089     }
00090     dst->type = siUTCTime;
00091     if (!d) {
00092        return SECFailure;
00093     }
00094 
00095     /* Convert an int64 time to a printable format.  */
00096     PR_ExplodeTime(gmttime, PR_GMTParameters, &printableTime);
00097 
00098     /* The month in UTC time is base one */
00099     printableTime.tm_month++;
00100 
00101     /* UTC time does not handle the years before 1950 */
00102     if (printableTime.tm_year < 1950)
00103            return SECFailure;
00104 
00105     /* remove the century since it's added to the tm_year by the 
00106        PR_ExplodeTime routine, but is not needed for UTC time */
00107     printableTime.tm_year %= 100; 
00108 
00109     d[0] = HIDIGIT(printableTime.tm_year);
00110     d[1] = LODIGIT(printableTime.tm_year);
00111     d[2] = HIDIGIT(printableTime.tm_month);
00112     d[3] = LODIGIT(printableTime.tm_month);
00113     d[4] = HIDIGIT(printableTime.tm_mday);
00114     d[5] = LODIGIT(printableTime.tm_mday);
00115     d[6] = HIDIGIT(printableTime.tm_hour);
00116     d[7] = LODIGIT(printableTime.tm_hour);
00117     d[8] = HIDIGIT(printableTime.tm_min);
00118     d[9] = LODIGIT(printableTime.tm_min);
00119     d[10] = HIDIGIT(printableTime.tm_sec);
00120     d[11] = LODIGIT(printableTime.tm_sec);
00121     d[12] = 'Z';
00122     return SECSuccess;
00123 }
00124 
00125 SECStatus
00126 DER_TimeToUTCTime(SECItem *dst, int64 gmttime)
00127 {
00128     return DER_TimeToUTCTimeArena(NULL, dst, gmttime);
00129 }
00130 
00131 /* The caller of DER_AsciiToItem MUST ENSURE that either
00132 ** a) "string" points to a null-terminated ASCII string, or
00133 ** b) "string" points to a buffer containing a valid UTCTime, 
00134 **     whether null terminated or not.
00135 ** otherwise, this function may UMR and/or crash.
00136 ** It suffices to ensure that the input "string" is at least 17 bytes long.
00137 */
00138 SECStatus
00139 DER_AsciiToTime(int64 *dst, const char *string)
00140 {
00141     long year, month, mday, hour, minute, second, hourOff, minOff, days;
00142     int64 result, tmp1, tmp2;
00143 
00144     if (string == NULL) {
00145        goto loser;
00146     }
00147     
00148     /* Verify time is formatted properly and capture information */
00149     second = 0;
00150     hourOff = 0;
00151     minOff = 0;
00152     CAPTURE(year,string+0,loser);
00153     if (year < 50) {
00154        /* ASSUME that year # is in the 2000's, not the 1900's */
00155        year += 100;
00156     }
00157     CAPTURE(month,string+2,loser);
00158     if ((month == 0) || (month > 12)) goto loser;
00159     CAPTURE(mday,string+4,loser);
00160     if ((mday == 0) || (mday > 31)) goto loser;
00161     CAPTURE(hour,string+6,loser);
00162     if (hour > 23) goto loser;
00163     CAPTURE(minute,string+8,loser);
00164     if (minute > 59) goto loser;
00165     if (ISDIGIT(string[10])) {
00166        CAPTURE(second,string+10,loser);
00167        if (second > 59) goto loser;
00168        string += 2;
00169     }
00170     if (string[10] == '+') {
00171        CAPTURE(hourOff,string+11,loser);
00172        if (hourOff > 23) goto loser;
00173        CAPTURE(minOff,string+13,loser);
00174        if (minOff > 59) goto loser;
00175     } else if (string[10] == '-') {
00176        CAPTURE(hourOff,string+11,loser);
00177        if (hourOff > 23) goto loser;
00178        hourOff = -hourOff;
00179        CAPTURE(minOff,string+13,loser);
00180        if (minOff > 59) goto loser;
00181        minOff = -minOff;
00182     } else if (string[10] != 'Z') {
00183        goto loser;
00184     }
00185     
00186     
00187     /* Convert pieces back into a single value year  */
00188     LL_I2L(tmp1, (year-70L));
00189     LL_I2L(tmp2, SECYEAR);
00190     LL_MUL(result, tmp1, tmp2);
00191     
00192     LL_I2L(tmp1, ( (mday-1L)*SECDAY + hour*SECHOUR + minute*SECMIN -
00193                 hourOff*SECHOUR - minOff*SECMIN + second ) );
00194     LL_ADD(result, result, tmp1);
00195 
00196     /*
00197     ** Have to specially handle the day in the month and the year, to
00198     ** take into account leap days. The return time value is in
00199     ** seconds since January 1st, 12:00am 1970, so start examining
00200     ** the time after that. We can't represent a time before that.
00201     */
00202 
00203     /* Using two digit years, we can only represent dates from 1970
00204        to 2069. As a result, we cannot run into the leap year rule
00205        that states that 1700, 2100, etc. are not leap years (but 2000
00206        is). In other words, there are no years in the span of time
00207        that we can represent that are == 0 mod 4 but are not leap
00208        years. Whew.
00209        */
00210 
00211     days = monthToDayInYear[month-1];
00212     days += (year - 68)/4;
00213 
00214     if (((year % 4) == 0) && (month < 3)) {
00215        days--;
00216     }
00217    
00218     LL_I2L(tmp1, (days * SECDAY) );
00219     LL_ADD(result, result, tmp1 );
00220 
00221     /* convert to micro seconds */
00222     LL_I2L(tmp1, PR_USEC_PER_SEC);
00223     LL_MUL(result, result, tmp1);
00224 
00225     *dst = result;
00226     return SECSuccess;
00227 
00228   loser:
00229     PORT_SetError(SEC_ERROR_INVALID_TIME);
00230     return SECFailure;
00231        
00232 }
00233 
00234 SECStatus
00235 DER_UTCTimeToTime(int64 *dst, const SECItem *time)
00236 {
00237     const char * string;
00238     char localBuf[20]; 
00239 
00240     /* Minimum valid UTCTime is yymmddhhmmZ       which is 11 bytes. 
00241     ** Maximum valid UTCTime is yymmddhhmmss+0000 which is 17 bytes.
00242     ** 20 should be large enough for all valid encoded times. 
00243     */
00244     if (!time || !time->data || time->len < 11) {
00245        PORT_SetError(SEC_ERROR_INVALID_TIME);
00246        return SECFailure;
00247     }
00248     if (time->len >= sizeof localBuf) { 
00249        string = (const char *)time->data;
00250     } else {
00251        memset(localBuf, 0, sizeof localBuf);
00252        memcpy(localBuf, time->data, time->len);
00253         string = (const char *)localBuf;
00254     }
00255     return DER_AsciiToTime(dst, string);
00256 }
00257 
00258 /*
00259    gmttime must contains UTC time in micro-seconds unit.
00260    Note: the caller should make sure that Generalized time
00261    should only be used for certifiate validities after the
00262    year 2049.  Otherwise, UTC time should be used.  This routine
00263    does not check this case, since it can be used to encode
00264    certificate extension, which does not have this restriction. 
00265  */
00266 SECStatus
00267 DER_TimeToGeneralizedTimeArena(PRArenaPool* arenaOpt, SECItem *dst, int64 gmttime)
00268 {
00269     PRExplodedTime printableTime;
00270     unsigned char *d;
00271 
00272     dst->len = 15;
00273     if (arenaOpt) {
00274         dst->data = d = (unsigned char*) PORT_ArenaAlloc(arenaOpt, dst->len);
00275     } else {
00276         dst->data = d = (unsigned char*) PORT_Alloc(dst->len);
00277     }
00278     dst->type = siGeneralizedTime;
00279     if (!d) {
00280        return SECFailure;
00281     }
00282 
00283     /*Convert a int64 time to a printable format. This is a temporary call
00284          until we change to NSPR 2.0
00285      */
00286     PR_ExplodeTime(gmttime, PR_GMTParameters, &printableTime);
00287 
00288     /* The month in Generalized time is base one */
00289     printableTime.tm_month++;
00290 
00291     d[0] = (printableTime.tm_year /1000) + '0';
00292     d[1] = ((printableTime.tm_year % 1000) / 100) + '0';
00293     d[2] = ((printableTime.tm_year % 100) / 10) + '0';
00294     d[3] = (printableTime.tm_year % 10) + '0';
00295     d[4] = HIDIGIT(printableTime.tm_month);
00296     d[5] = LODIGIT(printableTime.tm_month);
00297     d[6] = HIDIGIT(printableTime.tm_mday);
00298     d[7] = LODIGIT(printableTime.tm_mday);
00299     d[8] = HIDIGIT(printableTime.tm_hour);
00300     d[9] = LODIGIT(printableTime.tm_hour);
00301     d[10] = HIDIGIT(printableTime.tm_min);
00302     d[11] = LODIGIT(printableTime.tm_min);
00303     d[12] = HIDIGIT(printableTime.tm_sec);
00304     d[13] = LODIGIT(printableTime.tm_sec);
00305     d[14] = 'Z';
00306     return SECSuccess;
00307 }
00308 
00309 SECStatus
00310 DER_TimeToGeneralizedTime(SECItem *dst, int64 gmttime)
00311 {
00312     return DER_TimeToGeneralizedTimeArena(NULL, dst, gmttime);
00313 }
00314 
00315 
00316 /*
00317     The caller should make sure that the generalized time should only
00318     be used for the certificate validity after the year 2051; otherwise,
00319     the certificate should be consider invalid!?
00320  */
00321 SECStatus
00322 DER_GeneralizedTimeToTime(int64 *dst, const SECItem *time)
00323 {
00324     PRExplodedTime genTime;
00325     const char *string;
00326     long hourOff, minOff;
00327     uint16 century;
00328     char localBuf[20];
00329 
00330     /* Minimum valid GeneralizedTime is ccyymmddhhmmZ       which is 13 bytes.
00331     ** Maximum valid GeneralizedTime is ccyymmddhhmmss+0000 which is 19 bytes.
00332     ** 20 should be large enough for all valid encoded times. 
00333     */
00334     if (!time || !time->data || time->len < 13)
00335         goto loser;
00336     if (time->len >= sizeof localBuf) {
00337         string = (const char *)time->data;
00338     } else {
00339        memset(localBuf, 0, sizeof localBuf);
00340         memcpy(localBuf, time->data, time->len);
00341        string = (const char *)localBuf;
00342     }
00343 
00344     memset(&genTime, 0, sizeof genTime);
00345 
00346     /* Verify time is formatted properly and capture information */
00347     hourOff = 0;
00348     minOff = 0;
00349 
00350     CAPTURE(century, string+0, loser);
00351     century *= 100;
00352     CAPTURE(genTime.tm_year,string+2,loser);
00353     genTime.tm_year += century;
00354 
00355     CAPTURE(genTime.tm_month,string+4,loser);
00356     if ((genTime.tm_month == 0) || (genTime.tm_month > 12)) goto loser;
00357 
00358     /* NSPR month base is 0 */
00359     --genTime.tm_month;
00360     
00361     CAPTURE(genTime.tm_mday,string+6,loser);
00362     if ((genTime.tm_mday == 0) || (genTime.tm_mday > 31)) goto loser;
00363     
00364     CAPTURE(genTime.tm_hour,string+8,loser);
00365     if (genTime.tm_hour > 23) goto loser;
00366     
00367     CAPTURE(genTime.tm_min,string+10,loser);
00368     if (genTime.tm_min > 59) goto loser;
00369     
00370     if (ISDIGIT(string[12])) {
00371        CAPTURE(genTime.tm_sec,string+12,loser);
00372        if (genTime.tm_sec > 59) goto loser;
00373        string += 2;
00374     }
00375     if (string[12] == '+') {
00376        CAPTURE(hourOff,string+13,loser);
00377        if (hourOff > 23) goto loser;
00378        CAPTURE(minOff,string+15,loser);
00379        if (minOff > 59) goto loser;
00380     } else if (string[12] == '-') {
00381        CAPTURE(hourOff,string+13,loser);
00382        if (hourOff > 23) goto loser;
00383        hourOff = -hourOff;
00384        CAPTURE(minOff,string+15,loser);
00385        if (minOff > 59) goto loser;
00386        minOff = -minOff;
00387     } else if (string[12] != 'Z') {
00388        goto loser;
00389     }
00390 
00391     /* Since the values of hourOff and minOff are small, there will
00392        be no loss of data by the conversion to int8 */
00393     /* Convert the GMT offset to seconds and save it it genTime
00394        for the implode time process */
00395     genTime.tm_params.tp_gmt_offset = (PRInt32)((hourOff * 60L + minOff) * 60L);
00396     *dst = PR_ImplodeTime (&genTime);
00397     return SECSuccess;
00398 
00399   loser:
00400     PORT_SetError(SEC_ERROR_INVALID_TIME);
00401     return SECFailure;
00402        
00403 }