]> git.saurik.com Git - apple/security.git/blob - utilities/src/der_date.c
Security-55471.14.tar.gz
[apple/security.git] / utilities / src / der_date.c
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 }