]> git.saurik.com Git - apple/security.git/blame - libsecurity_keychain/lib/cssmdatetime.cpp
Security-55471.14.4.tar.gz
[apple/security.git] / libsecurity_keychain / lib / cssmdatetime.cpp
CommitLineData
b1ab9ed8
A
1/*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * cssmdatetime.cpp -- CSSM date and time utilities for the Mac
24 */
25
26#ifdef __MWERKS__
27#define _CPP_CSSM_DATE_TIME_UTILS
28#endif
29
30#include "cssmdatetime.h"
31
32#include <string.h>
33#include <stdio.h>
34#include <security_utilities/errors.h>
b1ab9ed8
A
35#include <CoreFoundation/CFDate.h>
36#include <CoreFoundation/CFTimeZone.h>
37#include <ctype.h>
38#include <stdlib.h>
427c49bc 39#include <SecBase.h>
b1ab9ed8
A
40namespace Security
41{
42
43namespace CSSMDateTimeUtils
44{
45
46#define UTC_TIME_NOSEC_LEN 11
47#define UTC_TIME_STRLEN 13
48#define GENERALIZED_TIME_STRLEN 15
49#define LOCALIZED_UTC_TIME_STRLEN 17
50#define LOCALIZED_TIME_STRLEN 19
51#define MAX_TIME_STR_LEN 30
52
53
54void
55GetCurrentMacLongDateTime(sint64 &outMacDate)
56{
57 CFTimeZoneRef timeZone = CFTimeZoneCopyDefault();
58 CFAbsoluteTime absTime = CFAbsoluteTimeGetCurrent();
59 absTime += CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
60 CFRelease(timeZone);
61 outMacDate = sint64(double(absTime + kCFAbsoluteTimeIntervalSince1904));
62}
63
64void
65TimeStringToMacSeconds (const CSSM_DATA &inUTCTime, uint32 &ioMacDate)
66{
67 sint64 ldt;
68 TimeStringToMacLongDateTime(inUTCTime, ldt);
69 ioMacDate = uint32(ldt);
70}
71
72/*
73 * Given a CSSM_DATA containing either a UTC-style or "generalized time"
74 * time string, convert to 32-bit Mac time in seconds.
75 * Returns nonzero on error.
76 */
77void
78TimeStringToMacLongDateTime (const CSSM_DATA &inUTCTime, sint64 &outMacDate)
79{
80 char szTemp[5];
427c49bc 81 size_t len;
b1ab9ed8
A
82 int isUtc;
83 sint32 x;
84 sint32 i;
85 char *cp;
86
87 CFGregorianDate date;
88 ::memset( &date, 0, sizeof(date) );
89
90 if ((inUTCTime.Data == NULL) || (inUTCTime.Length == 0))
91 {
427c49bc 92 MacOSError::throwMe(errSecParam);
b1ab9ed8
A
93 }
94
95 /* tolerate NULL terminated or not */
96 len = inUTCTime.Length;
97 if (inUTCTime.Data[len - 1] == '\0')
98 len--;
99
100 switch(len)
101 {
102 case UTC_TIME_STRLEN: // 2-digit year, not Y2K compliant
103 isUtc = 1;
104 break;
105 case GENERALIZED_TIME_STRLEN: // 4-digit year
106 isUtc = 0;
107 break;
108 default: // unknown format
427c49bc 109 MacOSError::throwMe(errSecParam);
b1ab9ed8
A
110 }
111
112 cp = (char *)inUTCTime.Data;
113
114 /* check that all characters except last are digits */
115 for(i=0; i<(sint32)(len - 1); i++) {
116 if ( !(isdigit(cp[i])) ) {
427c49bc 117 MacOSError::throwMe(errSecParam);
b1ab9ed8
A
118 }
119 }
120
121 /* check last character is a 'Z' */
122 if(cp[len - 1] != 'Z' ) {
427c49bc 123 MacOSError::throwMe(errSecParam);
b1ab9ed8
A
124 }
125
126 /* YEAR */
127 szTemp[0] = *cp++;
128 szTemp[1] = *cp++;
129 if(!isUtc) {
130 /* two more digits */
131 szTemp[2] = *cp++;
132 szTemp[3] = *cp++;
133 szTemp[4] = '\0';
134 }
135 else {
136 szTemp[2] = '\0';
137 }
138 x = atoi( szTemp );
139 if(isUtc) {
140 /*
141 * 2-digit year.
142 * 0 <= year <= 50 : assume century 21
143 * 50 < year < 70 : illegal per PKIX
144 * 70 < year <= 99 : assume century 20
145 */
146 if(x <= 50) {
147 x += 100;
148 }
149 else if(x < 70) {
427c49bc 150 MacOSError::throwMe(errSecParam);
b1ab9ed8
A
151 }
152 /* else century 20, OK */
153
154 /* bug fix... we need to end up with a 4-digit year! */
155 x += 1900;
156 }
157 /* by definition - tm_year is year - 1900 */
158 //tmp->tm_year = x - 1900;
159 date.year = x;
160
161 /* MONTH */
162 szTemp[0] = *cp++;
163 szTemp[1] = *cp++;
164 szTemp[2] = '\0';
165 x = atoi( szTemp );
166 /* in the string, months are from 1 to 12 */
167 if((x > 12) || (x <= 0)) {
427c49bc 168 MacOSError::throwMe(errSecParam);
b1ab9ed8
A
169 }
170 /* in a tm, 0 to 11 */
171 //tmp->tm_mon = x - 1;
172 date.month = x;
173
174 /* DAY */
175 szTemp[0] = *cp++;
176 szTemp[1] = *cp++;
177 szTemp[2] = '\0';
178 x = atoi( szTemp );
179 /* 1..31 in both formats */
180 if((x > 31) || (x <= 0)) {
427c49bc 181 MacOSError::throwMe(errSecParam);
b1ab9ed8
A
182 }
183 //tmp->tm_mday = x;
184 date.day = x;
185
186 /* HOUR */
187 szTemp[0] = *cp++;
188 szTemp[1] = *cp++;
189 szTemp[2] = '\0';
190 x = atoi( szTemp );
191 if((x > 23) || (x < 0)) {
427c49bc 192 MacOSError::throwMe(errSecParam);
b1ab9ed8
A
193 }
194 //tmp->tm_hour = x;
195 date.hour = x;
196
197 /* MINUTE */
198 szTemp[0] = *cp++;
199 szTemp[1] = *cp++;
200 szTemp[2] = '\0';
201 x = atoi( szTemp );
202 if((x > 59) || (x < 0)) {
427c49bc 203 MacOSError::throwMe(errSecParam);
b1ab9ed8
A
204 }
205 //tmp->tm_min = x;
206 date.minute = x;
207
208 /* SECOND */
209 szTemp[0] = *cp++;
210 szTemp[1] = *cp++;
211 szTemp[2] = '\0';
212 x = atoi( szTemp );
213 if((x > 59) || (x < 0)) {
427c49bc 214 MacOSError::throwMe(errSecParam);
b1ab9ed8
A
215 }
216 //tmp->tm_sec = x;
217 date.second = x;
218
219 CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
220 CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(date, timeZone);
221 CFRelease(timeZone);
222
223 // Adjust abstime to local timezone
224 timeZone = CFTimeZoneCopyDefault();
225 absTime += CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
226 CFRelease(timeZone);
227
228 outMacDate = sint64(double(absTime + kCFAbsoluteTimeIntervalSince1904));
229}
230
231void MacSecondsToTimeString(uint32 inMacDate, uint32 inLength, void *outData)
232{
233 sint64 ldt = sint64(uint64(inMacDate));
234 MacLongDateTimeToTimeString(ldt, inLength, outData);
235}
236
237void MacLongDateTimeToTimeString(const sint64 &inMacDate,
238 uint32 inLength, void *outData)
239{
240 // @@@ this code is close, but on the fringe case of a daylight savings time it will be off for a little while
241 CFAbsoluteTime absTime = inMacDate - kCFAbsoluteTimeIntervalSince1904;
242
243 // Remove local timezone component from absTime
244 CFTimeZoneRef timeZone = CFTimeZoneCopyDefault();
245 absTime -= CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
246 CFRelease(timeZone);
247
248 timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
249 CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(absTime, timeZone);
250 CFRelease(timeZone);
251
252 if (inLength == 16)
253 {
254 sprintf((char *)(outData), "%04d%02d%02d%02d%02d%02dZ",
255 int(date.year % 10000), date.month, date.day,
256 date.hour, date.minute, int(date.second));
257 }
258 else if (inLength == 14)
259 {
260 /* UTC - 2 year digits - code which parses this assumes that
261 * (2-digit) years between 0 and 49 are in century 21 */
262 sprintf((char *)(outData), "%02d%02d%02d%02d%02d%02dZ",
263 int(date.year % 100), date.month, date.day,
264 date.hour, date.minute, int(date.second));
265 }
266 else
427c49bc 267 MacOSError::throwMe(errSecParam);
b1ab9ed8
A
268}
269
270void
271CFDateToCssmDate(CFDateRef date, char *outCssmDate)
272{
273 CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
274 CFGregorianDate gd = CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime(date), timeZone);
275 sprintf(outCssmDate, "%04d%02d%02d%02d%02d%02dZ", (int)gd.year, gd.month, gd.day, gd.hour, gd.minute, (unsigned int)gd.second);
276 CFRelease(timeZone);
277}
278
279void
280CssmDateToCFDate(const char *cssmDate, CFDateRef *outCFDate)
281{
282 CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
283 CFGregorianDate gd;
284 unsigned int year, month, day, hour, minute, second;
285 sscanf(cssmDate, "%4d%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute, &second);
286 gd.year = year;
287 gd.month = month;
288 gd.day = day;
289 gd.hour = hour;
290 gd.minute = minute;
291 gd.second = second;
292 *outCFDate = CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(gd, timeZone));
293 CFRelease(timeZone);
294}
295
296int
297CssmDateStringToCFDate(const char *cssmDate, unsigned int len, CFDateRef *outCFDate)
298{
299 CFTimeZoneRef timeZone;
300 CFGregorianDate gd;
301 CFTimeInterval ti=0;
302 char szTemp[5];
427c49bc
A
303 unsigned isUtc=0, isLocal=0, i;
304 int x;
b1ab9ed8
A
305 unsigned noSeconds=0;
306 char *cp;
307
308 if((cssmDate == NULL) || (len == 0) || (outCFDate == NULL))
309 return 1;
310
311 /* tolerate NULL terminated or not */
312 if(cssmDate[len - 1] == '\0')
313 len--;
314
315 switch(len) {
316 case UTC_TIME_NOSEC_LEN: // 2-digit year, no seconds, not y2K compliant
317 isUtc = 1;
318 noSeconds = 1;
319 break;
320 case UTC_TIME_STRLEN: // 2-digit year, not Y2K compliant
321 isUtc = 1;
322 break;
323 case GENERALIZED_TIME_STRLEN: // 4-digit year
324 //isUtc = 0;
325 break;
326 case LOCALIZED_UTC_TIME_STRLEN: // "YYMMDDhhmmssThhmm" (where T=[+,-])
327 isUtc = 1;
328 // deliberate fallthrough
329 case LOCALIZED_TIME_STRLEN: // "YYYYMMDDhhmmssThhmm" (where T=[+,-])
330 isLocal = 1;
331 break;
332 default: // unknown format
333 return 1;
334 }
335
336 cp = (char *)cssmDate;
337
338 /* check that all characters except last (or timezone indicator, if localized) are digits */
339 for(i=0; i<(len - 1); i++) {
340 if ( !(isdigit(cp[i])) )
341 if ( !isLocal || !(cp[i]=='+' || cp[i]=='-') )
342 return 1;
343 }
344 /* check last character is a 'Z', unless localized */
345 if(!isLocal && cp[len - 1] != 'Z' ) {
346 return 1;
347 }
348
349 /* YEAR */
350 szTemp[0] = *cp++;
351 szTemp[1] = *cp++;
352 if(!isUtc) {
353 /* two more digits */
354 szTemp[2] = *cp++;
355 szTemp[3] = *cp++;
356 szTemp[4] = '\0';
357 }
358 else {
359 szTemp[2] = '\0';
360 }
361 x = atoi( szTemp );
362 if(isUtc) {
363 /*
364 * 2-digit year.
365 * 0 <= year < 50 : assume century 21
366 * 50 <= year < 70 : illegal per PKIX
367 * 70 < year <= 99 : assume century 20
368 */
369 if(x < 50) {
370 x += 2000;
371 }
372 else if(x < 70) {
373 return 1;
374 }
375 else {
376 /* century 20 */
377 x += 1900;
378 }
379 }
380 gd.year = x;
381
382 /* MONTH */
383 szTemp[0] = *cp++;
384 szTemp[1] = *cp++;
385 szTemp[2] = '\0';
386 x = atoi( szTemp );
387 /* in the string, months are from 1 to 12 */
388 if((x > 12) || (x <= 0)) {
389 return 1;
390 }
391 gd.month = x;
392
393 /* DAY */
394 szTemp[0] = *cp++;
395 szTemp[1] = *cp++;
396 szTemp[2] = '\0';
397 x = atoi( szTemp );
398 /* 1..31 in both formats */
399 if((x > 31) || (x <= 0)) {
400 return 1;
401 }
402 gd.day = x;
403
404 /* HOUR */
405 szTemp[0] = *cp++;
406 szTemp[1] = *cp++;
407 szTemp[2] = '\0';
408 x = atoi( szTemp );
409 if((x > 23) || (x < 0)) {
410 return 1;
411 }
412 gd.hour = x;
413
414 /* MINUTE */
415 szTemp[0] = *cp++;
416 szTemp[1] = *cp++;
417 szTemp[2] = '\0';
418 x = atoi( szTemp );
419 if((x > 59) || (x < 0)) {
420 return 1;
421 }
422 gd.minute = x;
423
424 /* SECOND */
425 if(noSeconds) {
426 gd.second = 0;
427 }
428 else {
429 szTemp[0] = *cp++;
430 szTemp[1] = *cp++;
431 szTemp[2] = '\0';
432 x = atoi( szTemp );
433 if((x > 59) || (x < 0)) {
434 return 1;
435 }
436 gd.second = x;
437 }
438
439 if (isLocal) {
440 /* ZONE INDICATOR */
441 ti = (*cp++ == '+') ? 1 : -1;
442 /* ZONE HH OFFSET */
443 szTemp[0] = *cp++;
444 szTemp[1] = *cp++;
445 szTemp[2] = '\0';
446 x = atoi( szTemp ) * 60 * 60;
447 ti *= x;
448 /* ZONE MM OFFSET */
449 szTemp[0] = *cp++;
450 szTemp[1] = *cp++;
451 szTemp[2] = '\0';
452 x = atoi( szTemp ) * 60;
453 ti += ((ti < 0) ? (x*-1) : x);
454 }
455 timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, ti);
456 if (!timeZone) return 1;
457 *outCFDate = CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(gd, timeZone));
458 CFRelease(timeZone);
459
460 return 0;
461}
462
463}; // end namespace CSSMDateTimeUtils
464
465} // end namespace Security