1// 2// SecCFDER_CFDate.c 3// utilities 4// 5// Created by Michael Brouwer on 7/7/12. 6// Copyright (c) 2012 Apple Inc. All rights reserved. 7// 8 9#include "utilities/SecCFRelease.h" 10#include "utilities/der_date.h" 11#include "utilities/der_plist.h" 12#include "utilities/der_plist_internal.h" 13 14#include <corecrypto/ccder.h> 15#include <CoreFoundation/CoreFoundation.h> 16 17#include <math.h> 18 19#define NULL_TIME NAN 20 21CFAbsoluteTime SecCFGregorianDateGetAbsoluteTime(CFGregorianDate g, CFTimeInterval timeZoneOffset, CFErrorRef *error); 22CFGregorianDate SecCFAbsoluteTimeGetGregorianDate(CFAbsoluteTime at, CFTimeInterval timeZoneOffset, CFErrorRef *error); 23 24/* Cumalitive number of days in the year for months up to month i. */ 25static int mdays[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; 26 27CFAbsoluteTime SecCFGregorianDateGetAbsoluteTime(CFGregorianDate g, CFTimeInterval timeZoneOffset, CFErrorRef *error) { 28 int day = g.day; 29 int is_leap_year = g.year % 4 == 0 && (g.year % 100 != 0 || g.year % 400 == 0) ? 1 : 0; 30 if (g.month < 1 || g.month > 12 || day < 1 || day > 31 || g.hour >= 24 || g.minute >= 60 || g.second >= 60.0 31 || (g.month == 2 && day > mdays[g.month] - mdays[g.month - 1] + is_leap_year) 32 || (g.month != 2 && day > mdays[g.month] - mdays[g.month - 1])) { 33 /* Invalid date. */ 34 SecCFDERCreateError(-1000, CFSTR("Invalid date."), 0, error); 35 return NULL_TIME; 36 } 37 38 int dy = g.year - 2001; 39 if (dy < 0) { 40 dy += 1; 41 day -= 1; 42 } 43 44 int leap_days = dy / 4 - dy / 100 + dy / 400; 45 day += ((g.year - 2001) * 365 + leap_days) + mdays[g.month - 1] - 1; 46 if (g.month > 2) 47 day += is_leap_year; 48 49#if 0 50 int64_t time = day; 51 time *= 24; 52 time += g.hour; 53 time *= 60; 54 time += g.minute; 55 time *= 60; 56 time += lrint(g.second); 57 time -= lrint(timeZoneOffset); 58 return time; 59#else 60 CFAbsoluteTime absTime = (CFAbsoluteTime)((day * 24 + g.hour) * 60 + g.minute) * 60 + g.second; 61 return absTime - timeZoneOffset; 62#endif 63} 64 65CFGregorianDate SecCFAbsoluteTimeGetGregorianDate(CFAbsoluteTime at, CFTimeInterval timeZoneOffset, CFErrorRef *error) { 66 CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(0, timeZoneOffset); 67 if (!tz) { 68 SecCFDERCreateError(-1000, CFSTR("timezone creation failed."), 0, error); 69 CFGregorianDate g = {}; 70 return g; 71 } else { 72 CFGregorianDate g = CFAbsoluteTimeGetGregorianDate(at, tz); 73 CFRelease(tz); 74 return g; 75 } 76} 77 78 79static int der_get_char(const uint8_t **der_p, const uint8_t *der_end, 80 CFErrorRef *error) { 81 const uint8_t *der = *der_p; 82 if (!der) { 83 /* Don't create a new error in this case. */ 84 return -1; 85 } 86 87 if (der >= der_end) { 88 SecCFDERCreateError(kSecDERErrorUnknownEncoding, 89 CFSTR("Unexpected end of datetime"), 0, error); 90 *der_p = NULL; 91 return -1; 92 } 93 94 int ch = *der++; 95 *der_p = der; 96 return ch; 97} 98 99 100static int der_decode_decimal(const uint8_t **der_p, const uint8_t *der_end, 101 CFErrorRef *error) { 102 char ch = der_get_char(der_p, der_end, error); 103 if (ch < '0' || ch > '9') { 104 SecCFDERCreateError(kSecDERErrorUnknownEncoding, 105 CFSTR("Not a decimal digit"), 0, error); 106 *der_p = NULL; 107 return -1; 108 } 109 return ch - '0'; 110} 111 112static int der_decode_decimal_pair(const uint8_t **der_p, const uint8_t *der_end, 113 CFErrorRef *error) { 114 return (10 * der_decode_decimal(der_p, der_end, error)) 115 + der_decode_decimal(der_p, der_end, error); 116} 117 118static int der_peek_byte(const uint8_t *der, const uint8_t *der_end) { 119 if (!der || der >= der_end) 120 return -1; 121 122 return *der; 123} 124 125static const uint8_t *der_decode_decimal_fraction(double *fraction, CFErrorRef *error, 126 const uint8_t* der, const uint8_t *der_end) { 127 int ch = der_peek_byte(der, der_end); 128 if (ch == -1) { 129 der = NULL; 130 } else if (ch == '.') { 131 uint64_t divisor = 1; 132 uint64_t value = 0; 133 int last = -1; 134 while (++der < der_end) { 135 last = ch; 136 ch = *der; 137 if (ch < '0' || ch > '9') { 138 break; 139 } 140 if (divisor < UINT64_MAX / 10) { 141 divisor *= 10; 142 value *= 10; 143 value += (ch - '0'); 144 } 145 } 146 if (der >= der_end) 147 der = NULL; 148 else if (last == '0') { 149 SecCFDERCreateError(kSecDERErrorUnknownEncoding, 150 CFSTR("fraction ends in 0"), 0, error); 151 der = NULL; 152 } else if (last == '.') { 153 SecCFDERCreateError(kSecDERErrorUnknownEncoding, 154 CFSTR("fraction without digits"), 0, error); 155 der = NULL; 156 } else { 157 *fraction = (double)value / divisor; 158 } 159 } else { 160 *fraction = 0.0; 161 } 162 163 return der; 164} 165 166static const CFTimeInterval der_decode_timezone_offset(const uint8_t **der_p, 167 const uint8_t *der_end, 168 CFErrorRef *error) { 169 CFTimeInterval timeZoneOffset; 170 int ch = der_get_char(der_p, der_end, error); 171 if (ch == 'Z') { 172 /* Zulu time. */ 173 timeZoneOffset = 0.0; 174 } else { 175 /* ZONE INDICATOR */ 176 int multiplier; 177 if (ch == '-') 178 multiplier = -60; 179 else if (ch == '+') 180 multiplier = +60; 181 else { 182 SecCFDERCreateError(kSecDERErrorUnknownEncoding, 183 CFSTR("Invalid datetime character"), 0, error); 184 timeZoneOffset = NULL_TIME; 185 } 186 187 timeZoneOffset = multiplier * 188 (der_decode_decimal_pair(der_p, der_end, error) 189 * 60 + der_decode_decimal_pair(der_p, der_end, error)); 190 } 191 return timeZoneOffset; 192} 193 194static const uint8_t* der_decode_commontime_body(CFAbsoluteTime *at, CFErrorRef *error, SInt32 year, 195 const uint8_t* der, const uint8_t *der_end) 196{ 197 CFGregorianDate g; 198 g.year = year; 199 g.month = der_decode_decimal_pair(&der, der_end, error); 200 g.day = der_decode_decimal_pair(&der, der_end, error); 201 g.hour = der_decode_decimal_pair(&der, der_end, error); 202 g.minute = der_decode_decimal_pair(&der, der_end, error); 203 g.second = der_decode_decimal_pair(&der, der_end, error); 204 double fraction; 205 der = der_decode_decimal_fraction(&fraction, error, der, der_end); 206 207 CFTimeInterval timeZoneOffset = der_decode_timezone_offset(&der, der_end, error); 208 209#if 0 210 secdebug("dateparse", 211 "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g", 212 length, bytes, g.year, g.month, 213 g.day, g.hour, g.minute, g.second, 214 timeZoneOffset / 60); 215#endif 216 217 if (der) { 218 if (der != der_end) { 219 SecCFDERCreateError(kSecDERErrorUnknownEncoding, 220 CFSTR("trailing garbage at end of datetime"), 0, error); 221 return NULL; 222 } 223 224 *at = SecCFGregorianDateGetAbsoluteTime(g, timeZoneOffset, error) + fraction; 225 if (*at == NULL_TIME) 226 return NULL; 227 } 228 229 return der; 230} 231 232const uint8_t* der_decode_generalizedtime_body(CFAbsoluteTime *at, CFErrorRef *error, 233 const uint8_t* der, const uint8_t *der_end) 234{ 235 SInt32 year = 100 * der_decode_decimal_pair(&der, der_end, error) + der_decode_decimal_pair(&der, der_end, error); 236 return der_decode_commontime_body(at, error, year, der, der_end); 237} 238 239const uint8_t* der_decode_universaltime_body(CFAbsoluteTime *at, CFErrorRef *error, 240 const uint8_t* der, const uint8_t *der_end) 241{ 242 SInt32 year = der_decode_decimal_pair(&der, der_end, error); 243 if (year < 50) { 244 /* 0 <= year < 50 : assume century 21 */ 245 year += 2000; 246 } else if (year < 70) { 247 /* 50 <= year < 70 : illegal per PKIX */ 248 SecCFDERCreateError(kSecDERErrorUnknownEncoding, 249 CFSTR("Invalid universal time year between 50 and 70"), 0, error); 250 der = NULL; 251 } else { 252 /* 70 < year <= 99 : assume century 20 */ 253 year += 1900; 254 } 255 256 return der_decode_commontime_body(at, error, year, der, der_end); 257} 258 259const uint8_t* der_decode_date(CFAllocatorRef allocator, CFOptionFlags mutability, 260 CFDateRef* date, CFErrorRef *error, 261 const uint8_t* der, const uint8_t *der_end) 262{ 263 if (NULL == der) 264 return NULL; 265 266 der = ccder_decode_constructed_tl(CCDER_GENERALIZED_TIME, &der_end, der, der_end); 267 CFAbsoluteTime at; 268 der = der_decode_generalizedtime_body(&at, error, der, der_end); 269 if (der) { 270 *date = CFDateCreate(allocator, at); 271 if (NULL == *date) { 272 SecCFDERCreateError(kSecDERErrorUnderlyingError, CFSTR("Failed to create date"), NULL, error); 273 return NULL; 274 } 275 } 276 return der; 277} 278 279extern char *__dtoa(double _d, int mode, int ndigits, int *decpt, int *sign, char **rve); 280extern void __freedtoa(char *); 281 282static size_t ccder_sizeof_nanoseconds(CFAbsoluteTime at) { 283 int dotoff; 284 int sign; 285 char *end; 286 char *str = __dtoa(at, 0, 0, &dotoff, &sign, &end); 287 ptrdiff_t len = end - str; 288 __freedtoa(str); 289 return len < dotoff ? 0 : len - dotoff; 290 //return len < dotoff ? 0 : len - dotoff > 9 ? 9 : len - dotoff; 291} 292 293size_t der_sizeof_generalizedtime_body(CFAbsoluteTime at, CFErrorRef *error) 294{ 295 size_t subsec_digits = ccder_sizeof_nanoseconds(at); 296 297 /* Generalized zulu time YYYYMMDDhhmmss[.ssss]Z */ 298 return subsec_digits ? 16 + subsec_digits : 15; 299} 300 301size_t der_sizeof_generalizedtime(CFAbsoluteTime at, CFErrorRef *error) 302{ 303 return ccder_sizeof(CCDER_GENERALIZED_TIME, 304 der_sizeof_generalizedtime_body(at, error)); 305} 306 307size_t der_sizeof_date(CFDateRef date, CFErrorRef *error) 308{ 309 return der_sizeof_generalizedtime(CFDateGetAbsoluteTime(date), error); 310} 311 312 313static uint8_t *ccder_encode_byte(uint8_t byte, 314 const uint8_t *der, uint8_t *der_end) { 315 if (der + 1 > der_end) { 316 return NULL; 317 } 318 *--der_end = byte; 319 return der_end; 320} 321 322static uint8_t *ccder_encode_decimal_pair(int v, const uint8_t *der, 323 uint8_t *der_end) { 324 if (der + 2 > der_end) { 325 return NULL; 326 } 327 assert(v < 100); 328 *--der_end = '0' + v % 10; 329 *--der_end = '0' + v / 10; 330 return der_end; 331} 332 333static uint8_t *ccder_encode_decimal_quad(int v, const uint8_t *der, 334 uint8_t *der_end) { 335 return ccder_encode_decimal_pair(v / 100, der, 336 ccder_encode_decimal_pair(v % 100, der, der_end)); 337} 338 339static uint8_t *ccder_encode_nanoseconds(CFAbsoluteTime at, const uint8_t *der, 340 uint8_t *der_end) { 341 int dotoff; 342 int sign; 343 char *end; 344 char *str = __dtoa(at, 0, 0, &dotoff, &sign, &end); 345 char *begin = str + (dotoff < 0 ? 0 : dotoff); 346 // Compute 1.0000000 - fraction in ascii space 347 if (at < 0.0 && begin < end) { 348 char *p = end - 1; 349 // Borrow for last digit 350 *p = ('9' + 1) - (*p - '0'); 351 while (p-- > begin) { 352 // Every other digit is a 9 since we borrowed from the last one 353 *p = '9' - (*p - '0'); 354 } 355 } 356 357 ptrdiff_t len = end - str; 358 if (len > dotoff) { 359 if (dotoff < 0) { 360 assert(-1.0 < at && at < 1.0); 361 der_end = ccder_encode_body(len, (const uint8_t *)str, der, der_end); 362 der_end = ccder_encode_body_nocopy(-dotoff, der, der_end); 363 if (der_end) 364 memset(der_end, at < 0.0 ? '9' : '0', -dotoff); 365 } else { 366 der_end = ccder_encode_body(len - dotoff, (const uint8_t *)(str + dotoff), der, der_end); 367 } 368 der_end = ccder_encode_byte('.', der, der_end); 369 } 370 __freedtoa(str); 371 372 return der_end; 373} 374 375/* Encode generalized zulu time YYYYMMDDhhmmss[.ssss]Z */ 376uint8_t* der_encode_generalizedtime_body(CFAbsoluteTime at, CFErrorRef *error, 377 const uint8_t *der, uint8_t *der_end) 378{ 379 CFGregorianDate g = SecCFAbsoluteTimeGetGregorianDate(floor(at), 0.0, error); 380 if (g.year == 0) 381 return NULL; 382 383 return ccder_encode_decimal_quad(g.year, der, 384 ccder_encode_decimal_pair(g.month, der, 385 ccder_encode_decimal_pair(g.day, der, 386 ccder_encode_decimal_pair(g.hour, der, 387 ccder_encode_decimal_pair(g.minute, der, 388 ccder_encode_decimal_pair(g.second, der, 389 ccder_encode_nanoseconds(at, der, 390 ccder_encode_byte('Z', der, der_end)))))))); 391} 392 393uint8_t* der_encode_generalizedtime(CFAbsoluteTime at, CFErrorRef *error, 394 const uint8_t *der, uint8_t *der_end) 395{ 396 return ccder_encode_constructed_tl(CCDER_GENERALIZED_TIME, der_end, der, 397 der_encode_generalizedtime_body(at, error, der, der_end)); 398} 399 400 401uint8_t* der_encode_date(CFDateRef date, CFErrorRef *error, 402 const uint8_t *der, uint8_t *der_end) 403{ 404 return der_encode_generalizedtime(CFDateGetAbsoluteTime(date), error, 405 der, der_end); 406} 407