]>
Commit | Line | Data |
---|---|---|
427c49bc A |
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 | ||
21 | CFAbsoluteTime SecCFGregorianDateGetAbsoluteTime(CFGregorianDate g, CFTimeInterval timeZoneOffset, CFErrorRef *error); | |
22 | CFGregorianDate SecCFAbsoluteTimeGetGregorianDate(CFAbsoluteTime at, CFTimeInterval timeZoneOffset, CFErrorRef *error); | |
23 | ||
24 | /* Cumalitive number of days in the year for months up to month i. */ | |
25 | static int mdays[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; | |
26 | ||
27 | CFAbsoluteTime 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 | ||
65 | CFGregorianDate 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 | ||
79 | static 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 | ||
100 | static 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 | ||
112 | static 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 | ||
118 | static 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 | ||
125 | static 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 | ||
166 | static 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 | ||
194 | static 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 | ||
232 | const 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 | ||
239 | const 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 | ||
259 | const 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 | ||
279 | extern char *__dtoa(double _d, int mode, int ndigits, int *decpt, int *sign, char **rve); | |
280 | extern void __freedtoa(char *); | |
281 | ||
282 | static 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 | ||
293 | size_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 | ||
301 | size_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 | ||
307 | size_t der_sizeof_date(CFDateRef date, CFErrorRef *error) | |
308 | { | |
309 | return der_sizeof_generalizedtime(CFDateGetAbsoluteTime(date), error); | |
310 | } | |
311 | ||
312 | ||
313 | static 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 | ||
322 | static 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 | ||
333 | static 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 | ||
339 | static 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 */ | |
376 | uint8_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 | ||
393 | uint8_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 | ||
401 | uint8_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 | } |