2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 #include "utilities/der_plist.h"
26 #include "utilities/der_plist_internal.h"
27 #include "utilities/der_date.h"
29 #include "utilities/SecCFRelease.h"
30 #include "utilities/array_size.h"
31 #include <utilities/SecCFWrappers.h>
33 #include <CoreFoundation/CoreFoundation.h>
34 #include <corecrypto/ccder.h>
35 #include <dispatch/dispatch.h>
37 #include "utilities_regressions.h"
39 #define kMaxResultSize 100
46 static struct test_case test_cases
[] =
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", },
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
));
84 CFStringAppendFormat(s
, 0, CFSTR("0x%02X, "), *start
++);
85 CFStringAppend(s
, CFSTR("},\n"));
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
);
92 const uint8_t *body
= ccder_decode_tl(CCDER_GENERALIZED_TIME
, &dlen
, der
, der_end
);
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
);
102 return pass("[%d] properly encoded %s", testnumber
, expected
);
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
);
113 return ok((decoded
!= NULL
) && CFEqual(decoded
, expected
), "[%d] Didn't make equal value %a != %a (%g != %g).", testnumber
, t1
, t2
, t1
, t2
);
117 static CFCalendarRef sZuluCalendar
= NULL
;
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
);
127 return sZuluCalendar
;
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
);
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
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
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
);
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;
159 dispatch_group_t dgroup
= dispatch_group_create();
160 dispatch_queue_t queue
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
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
);
169 dispatch_group_wait(dgroup
, failTime
);
170 dispatch_release(dgroup
);
174 // We expect this to fail until this is fixed:
175 // <rdar://problem/16372688> CFCalendarDecomposeAbsoluteTime is not thread safe
177 static void testWithUnguardedZuluCalendar() {
178 const bool useSharedZuluCalendar
= true;
179 __block
bool success
= true;
180 __block
int successCount
= 0;
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
))
187 matches
= year
==expectedYear
&& month
==expectedMonth
&& day
==expectedDay
&&
188 hour
==expectedHour
&& minute
==expectedMinute
&& second
==expectedSecond
;
189 // assert(matches); // enable to catch crash
196 todo("<rdar://problem/16372688> CFCalendarDecomposeAbsoluteTime is not thread safe, not yet fixed");
199 ok(success
,"unexpected result from SecAbsoluteTimeGetGregorianDate, failed, successes: %d", successCount
);
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;
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
;
218 ok(success
,"unexpected result from CFCalendarDecomposeAbsoluteTime");
221 #define kTestsPerTestCase 12
222 static void one_test(const struct test_case
* thisCase
, int testnumber
)
224 uint8_t buffer
[kMaxResultSize
];
225 uint8_t* buffer_end
= buffer
+ sizeof(buffer
);
227 CFDateRef initialValue
= CFDateCreate(NULL
, thisCase
->value
);
228 CFErrorRef error
= NULL
;
230 uint8_t* encoded
= der_encode_plist(initialValue
, &error
, buffer
, buffer_end
);
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
);
237 CFReleaseNull(error
);
239 encoded
= der_encode_date(initialValue
, &error
, buffer
, buffer_end
);
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
);
246 CFReleaseNull(error
);
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
);
254 ok(decode_end
== buffer_end
, "[%d] didn't decode whole buffer", testnumber
);
255 ok_date_equals(testnumber
, decoded
, initialValue
);
257 CFPropertyListRef decoded_type
= NULL
;
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
);
264 ok(decode_end
== buffer_end
, "[%d] didn't decode whole buffer", testnumber
);
265 ok_date_equals(testnumber
, decoded
, initialValue
);
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
);
270 CFReleaseSafe(initialValue
);
271 CFReleaseNull(decoded
);
272 CFReleaseNull(decoded_type
);
275 #define kTestCount (array_size(test_cases) * kTestsPerTestCase)
276 static void tests(void)
278 for (int testnumber
= 0; testnumber
< array_size(test_cases
); ++testnumber
)
279 one_test(test_cases
+ testnumber
, testnumber
);
281 // testWithUnguardedZuluCalendar();
285 int su_16_cfdate_der(int argc
, char *const *argv
)
287 plan_tests(kTestCount
);