]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
1 | /* |
2 | * Copyright (c) 2012-2014 Apple 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 | ||
427c49bc A |
24 | |
25 | #include "utilities/der_plist.h" | |
26 | #include "utilities/der_plist_internal.h" | |
27 | #include "utilities/der_date.h" | |
28 | ||
29 | #include "utilities/SecCFRelease.h" | |
30 | #include "utilities/array_size.h" | |
d87e1158 | 31 | #include <utilities/SecCFWrappers.h> |
427c49bc A |
32 | |
33 | #include <CoreFoundation/CoreFoundation.h> | |
34 | #include <corecrypto/ccder.h> | |
d87e1158 | 35 | #include <dispatch/dispatch.h> |
427c49bc A |
36 | |
37 | #include "utilities_regressions.h" | |
38 | ||
39 | #define kMaxResultSize 100 | |
40 | ||
41 | struct test_case { | |
42 | CFAbsoluteTime value; | |
43 | char *expected; | |
44 | }; | |
45 | ||
46 | static struct test_case test_cases[] = | |
47 | { | |
48 | { .value = 0.0, .expected = "20010101000000Z", }, | |
49 | { .value = 1.0, .expected = "20010101000001Z", }, | |
50 | { .value = 0.1, .expected = "20010101000000.1Z", }, | |
51 | { .value = 0.0001, .expected = "20010101000000.0001Z", }, | |
52 | { .value = 128.0, .expected = "20010101000208Z", }, | |
53 | { .value = -1.0, .expected = "20001231235959Z", }, | |
54 | { .value = -0.9, .expected = "20001231235959.1Z", }, | |
55 | { .value = -129.0, .expected = "20001231235751Z", }, | |
56 | { .value = 1000.0, .expected = "20010101001640Z", }, | |
57 | { .value = 65280.0, .expected = "20010101180800Z", }, | |
58 | { .value = 41234576.0, .expected = "20020423060256Z", }, | |
59 | { .value = -412343576.0, .expected = "19871208120704Z", }, | |
60 | { .value = 381778873.238063, .expected = "20130205174113.238063Z", }, | |
61 | { .value = 381778873.638063, .expected = "20130205174113.638063Z", }, | |
62 | { .value = 1400603.141, .expected = "20010117050323.141Z", }, | |
63 | { .value = -412343576.238063, .expected = "19871208120703.761937Z", }, | |
64 | { .value = -412343576.638063, .expected = "19871208120703.361937Z", }, | |
65 | { .value = 5.000539014, .expected = "20010101000005.000539014Z", }, | |
66 | { .value = -5.00053901, .expected = "20001231235954.99946099Z", }, | |
67 | { .value = 0.000539014, .expected = "20010101000000.000539014Z", }, | |
68 | { .value = -0.00053901, .expected = "20001231235959.99946099Z", }, | |
69 | { .value = 11031400603.141, .expected = "23500729055643.141Z", }, // Michael's 400th birthday | |
70 | // Pedantic tests within 1 second of the epoch. | |
71 | { .value = 0.000100120234, .expected = "20010101000000.000100120234Z", }, | |
72 | { .value = 0.00000000000000000000000000000000000000000010012654182354326, | |
73 | .expected = "20010101000000.00000000000000000000000000000000000000000010012654182354326Z", }, | |
74 | { .value = -0.00000000000000000000000000000000000000000010012654182354326, | |
75 | .expected = "20001231235959.99999999999999999999999999999999999999999989987345817645674Z", }, | |
76 | { .value = 0.0001234522366234637, .expected = "20010101000000.0001234522366234637Z", }, | |
77 | }; | |
78 | ||
79 | static CFStringRef string_create_with_hex(const uint8_t* start, const uint8_t* end) { | |
80 | CFMutableStringRef s = CFStringCreateMutable(NULL, 0); | |
81 | CFStringAppendFormat(s, 0, CFSTR(".size = %" PRIdPTR ", .res = { "), (intptr_t)(end - start)); | |
d8f41ccd A |
82 | if (start) |
83 | while (start < end) | |
84 | CFStringAppendFormat(s, 0, CFSTR("0x%02X, "), *start++); | |
427c49bc A |
85 | CFStringAppend(s, CFSTR("},\n")); |
86 | return s; | |
87 | } | |
88 | ||
89 | static bool ok_der_date_is(int testnumber, const char *expected, const uint8_t *der, const uint8_t *der_end) { | |
90 | size_t elen = strlen(expected); | |
91 | size_t dlen; | |
92 | const uint8_t *body = ccder_decode_tl(CCDER_GENERALIZED_TIME, &dlen, der, der_end); | |
93 | if (!body) { | |
94 | return fail("[%d] encoded date %@ expected %s not a generalized time", testnumber, string_create_with_hex(der, der_end), expected); | |
95 | } else if (body + dlen != der_end) { | |
96 | return fail("[%d] Trailing garbage in encoded string after generalized time got: %@ expected: %s", testnumber, string_create_with_hex(der, der_end), expected); | |
97 | } else if (dlen != elen) { | |
98 | return fail("[%d] encoded date len %zu != %zu got: %.*s expected: %s", testnumber, dlen , elen, (int)dlen, (char *)body, expected); | |
99 | } else if (memcmp(body, expected, elen)) { | |
100 | return fail("[%d] encoded got: %.*s expected: %s", testnumber, (int)dlen, (char *)body, expected); | |
101 | } else { | |
102 | return pass("[%d] properly encoded %s", testnumber, expected); | |
103 | } | |
104 | } | |
105 | ||
106 | static bool ok_date_equals(int testnumber, CFDateRef decoded, CFDateRef expected) { | |
107 | CFAbsoluteTime t1 = CFDateGetAbsoluteTime(decoded); | |
108 | CFAbsoluteTime t2 = CFDateGetAbsoluteTime(expected); | |
109 | if (-1.0 < t1 && t1 < 1.0) { | |
110 | // Dates near the epoch can't be off by more than 1 nanosecond. Other dates should be exactly right. | |
111 | return ok((decoded != NULL) && fabs(t1 - t2) < 7e-18, "[%d] Delta too big %g %a != %a (%g != %g).", testnumber, fabs(t1 - t2), t1, t2, t1, t2); | |
112 | } else { | |
113 | return ok((decoded != NULL) && CFEqual(decoded, expected), "[%d] Didn't make equal value %a != %a (%g != %g).", testnumber, t1, t2, t1, t2); | |
114 | } | |
115 | } | |
116 | ||
d87e1158 A |
117 | static CFCalendarRef sZuluCalendar = NULL; |
118 | ||
119 | static CFCalendarRef SecCFCalendarGetZulu() { | |
120 | static dispatch_once_t onceToken; | |
121 | dispatch_once(&onceToken, ^{ | |
122 | sZuluCalendar = CFCalendarCreateWithIdentifier(kCFAllocatorDefault, kCFGregorianCalendar); | |
123 | CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault, 0.0); | |
124 | CFCalendarSetTimeZone(sZuluCalendar, tz); | |
125 | CFReleaseSafe(tz); | |
126 | }); | |
127 | return sZuluCalendar; | |
128 | } | |
129 | ||
130 | static bool SecAbsoluteTimeGetGregorianDate(CFTimeInterval at, int *year, int *month, int *day, int *hour, int *minute, int *second, CFErrorRef *error) { | |
131 | // TODO: Remove CFCalendarDecomposeAbsoluteTime dependancy because CFTimeZoneCreateWithTimeIntervalFromGMT is expensive and requires filesystem access to timezone files when we are only doing zulu time anyway | |
132 | if (!CFCalendarDecomposeAbsoluteTime(SecCFCalendarGetZulu(), at, "yMdHms", year, month, day, hour, minute, second)) { | |
133 | SecCFDERCreateError(-1000, CFSTR("Failed to encode date."), 0, error); | |
134 | return false; | |
135 | } | |
136 | return true; | |
137 | } | |
138 | ||
139 | // &year, &month, &day, &hour, &minute, &second | |
140 | CFTimeInterval referenceTimeDate = 416957062.807688; // 2014 3 19 21 24 22 | |
141 | #define expectedYear 2014 | |
142 | #define expectedMonth 3 | |
143 | #define expectedDay 19 | |
144 | #define expectedHour 21 | |
145 | #define expectedMinute 24 | |
146 | #define expectedSecond 22 | |
147 | ||
148 | static bool parallelizeZulu(bool useSharedZuluCalendar, void(^action)(CFCalendarRef zuluCalendar, bool *STOP)) { | |
149 | // If useSharedZuluCalendar is false, NULL will be passed to zuluCalendar parameter of action | |
150 | int ix; | |
151 | static int kThreadLimit = 75000; // on a J86, this took 49963 threads to fail | |
152 | static const int64_t kFailureTimeLimit = (NSEC_PER_SEC * 10); // Assume failure after 10s | |
153 | dispatch_time_t failTime = dispatch_time(DISPATCH_TIME_NOW, kFailureTimeLimit); | |
154 | ||
155 | // This is a __block variable since it can get modified (due the the <rdar://problem/16372688>) | |
156 | __block CFCalendarRef zuluCalendar = useSharedZuluCalendar ? SecCFCalendarGetZulu() : NULL; | |
157 | __block bool stop = false; | |
158 | ||
159 | dispatch_group_t dgroup = dispatch_group_create(); | |
160 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); | |
161 | ||
162 | for (ix=0;ix<kThreadLimit && !stop;ix++) { | |
163 | dispatch_group_enter(dgroup); | |
164 | dispatch_async(queue, ^{ | |
165 | action(zuluCalendar, &stop); | |
166 | dispatch_group_leave(dgroup); | |
167 | }); | |
168 | } | |
169 | dispatch_group_wait(dgroup, failTime); | |
170 | dispatch_release(dgroup); | |
171 | return !stop; | |
172 | } | |
173 | ||
174 | // We expect this to fail until this is fixed: | |
175 | // <rdar://problem/16372688> CFCalendarDecomposeAbsoluteTime is not thread safe | |
176 | // | |
177 | static void testWithUnguardedZuluCalendar() { | |
178 | const bool useSharedZuluCalendar = true; | |
179 | __block bool success = true; | |
180 | __block int successCount = 0; | |
181 | ||
182 | success &= parallelizeZulu(useSharedZuluCalendar, ^(CFCalendarRef zuluCalendar, bool *STOP) { | |
183 | int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0; | |
184 | bool matches = false; | |
185 | if (!SecAbsoluteTimeGetGregorianDate(referenceTimeDate, &year, &month, &day, &hour, &minute, &second, NULL)) | |
186 | success = false; | |
187 | matches = year==expectedYear && month==expectedMonth && day==expectedDay && | |
188 | hour==expectedHour && minute==expectedMinute && second==expectedSecond; | |
189 | // assert(matches); // enable to catch crash | |
190 | if (matches) | |
191 | successCount++; | |
192 | else | |
193 | *STOP = true; | |
194 | }); | |
195 | ||
196 | todo("<rdar://problem/16372688> CFCalendarDecomposeAbsoluteTime is not thread safe, not yet fixed"); | |
197 | ||
198 | TODO: { | |
199 | ok(success,"unexpected result from SecAbsoluteTimeGetGregorianDate, failed, successes: %d", successCount); | |
200 | } | |
201 | } | |
202 | ||
203 | static void testDoWithZulu() { | |
204 | const bool useSharedZuluCalendar = false; | |
205 | int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0; | |
206 | __block bool success = true; | |
207 | ||
208 | success &= parallelizeZulu(useSharedZuluCalendar, ^(CFCalendarRef zuluCalendar, bool *STOP) { | |
209 | SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar) { | |
210 | bool matches = false; | |
211 | success &= CFCalendarDecomposeAbsoluteTime(zuluCalendar, referenceTimeDate, "yMdHms", &year, &month, &day, &hour, &minute, &second); | |
212 | matches = year==expectedYear && month==expectedMonth && day==expectedDay && | |
213 | hour==expectedHour && minute==expectedMinute && second==expectedSecond; | |
214 | *STOP = !matches; | |
215 | }); | |
216 | }); | |
217 | ||
218 | ok(success,"unexpected result from CFCalendarDecomposeAbsoluteTime"); | |
219 | } | |
220 | ||
427c49bc A |
221 | #define kTestsPerTestCase 12 |
222 | static void one_test(const struct test_case * thisCase, int testnumber) | |
223 | { | |
224 | uint8_t buffer[kMaxResultSize]; | |
225 | uint8_t* buffer_end = buffer + sizeof(buffer); | |
226 | ||
227 | CFDateRef initialValue = CFDateCreate(NULL, thisCase->value); | |
228 | CFErrorRef error = NULL; | |
229 | ||
230 | uint8_t* encoded = der_encode_plist(initialValue, &error, buffer, buffer_end); | |
231 | SKIP: | |
232 | { | |
233 | skip("der_encode_plist failed", 1, | |
234 | ok(encoded != NULL, "[%d] der_encode_plist failed: %@", testnumber, error)); | |
235 | ok_der_date_is(testnumber, thisCase->expected, encoded, buffer_end); | |
236 | } | |
237 | CFReleaseNull(error); | |
238 | ||
239 | encoded = der_encode_date(initialValue, &error, buffer, buffer_end); | |
240 | SKIP: | |
241 | { | |
242 | skip("der_encode_date failed", 1, | |
243 | ok(encoded != NULL, "[%d] der_encode_date failed: %@", testnumber, error)); | |
244 | ok_der_date_is(testnumber, thisCase->expected, encoded, buffer_end); | |
245 | } | |
246 | CFReleaseNull(error); | |
247 | ||
248 | CFDateRef decoded = NULL; | |
249 | const uint8_t* decode_end = der_decode_date(NULL, kCFPropertyListMutableContainers, | |
250 | &decoded, &error, encoded, buffer_end); | |
251 | ok(error == NULL, "[%d] der_decode_date failed: %@", testnumber, error); | |
252 | CFReleaseNull(error); | |
253 | ||
254 | ok(decode_end == buffer_end, "[%d] didn't decode whole buffer", testnumber); | |
255 | ok_date_equals(testnumber, decoded, initialValue); | |
256 | ||
257 | CFPropertyListRef decoded_type = NULL; | |
258 | ||
259 | decode_end = der_decode_plist(NULL, kCFPropertyListMutableContainers, | |
260 | &decoded_type, &error, encoded, buffer_end); | |
261 | ok(error == NULL, "[%d] der_decode_plist failed: %@", testnumber, error); | |
262 | CFReleaseNull(error); | |
263 | ||
264 | ok(decode_end == buffer_end, "[%d] didn't decode whole buffer", testnumber); | |
265 | ok_date_equals(testnumber, decoded, initialValue); | |
266 | ||
267 | is(der_sizeof_date(initialValue, NULL), ccder_sizeof(CCDER_GENERALIZED_TIME, strlen(thisCase->expected)), "[%d] der_sizeof_date mismatch", testnumber); | |
268 | is(der_sizeof_plist(initialValue, NULL), ccder_sizeof(CCDER_GENERALIZED_TIME, strlen(thisCase->expected)), "[%d] der_sizeof_plist mismatch", testnumber); | |
269 | ||
270 | CFReleaseSafe(initialValue); | |
271 | CFReleaseNull(decoded); | |
272 | CFReleaseNull(decoded_type); | |
273 | } | |
274 | ||
275 | #define kTestCount (array_size(test_cases) * kTestsPerTestCase) | |
276 | static void tests(void) | |
277 | { | |
278 | for (int testnumber = 0; testnumber < array_size(test_cases); ++testnumber) | |
279 | one_test(test_cases + testnumber, testnumber); | |
d87e1158 A |
280 | |
281 | testWithUnguardedZuluCalendar(); | |
282 | testDoWithZulu(); | |
427c49bc A |
283 | } |
284 | ||
285 | int su_16_cfdate_der(int argc, char *const *argv) | |
286 | { | |
d87e1158 | 287 | plan_tests(kTestCount+2); |
427c49bc A |
288 | tests(); |
289 | ||
290 | return 0; | |
291 | } |