1//
2//  su-16-cfdate-der.c
3//  utilities
4//
5//  Created by Michael Brouwer on 7/10/12.
6//  Copyright (c) 2012 Apple Inc. All rights reserved.
7//
8
9#include "utilities/der_plist.h"
10#include "utilities/der_plist_internal.h"
11#include "utilities/der_date.h"
12
13#include "utilities/SecCFRelease.h"
14#include "utilities/array_size.h"
15
16#include <CoreFoundation/CoreFoundation.h>
17#include <corecrypto/ccder.h>
18
19#include "utilities_regressions.h"
20
21#define kMaxResultSize 100
22
23struct test_case {
24    CFAbsoluteTime value;
25    char      *expected;
26};
27
28static struct test_case test_cases[] =
29{
30    { .value = 0.0,                     .expected = "20010101000000Z", },
31    { .value = 1.0,                     .expected = "20010101000001Z", },
32    { .value = 0.1,                     .expected = "20010101000000.1Z", },
33    { .value = 0.0001,                  .expected = "20010101000000.0001Z", },
34    { .value = 128.0,                   .expected = "20010101000208Z", },
35    { .value = -1.0,                    .expected = "20001231235959Z", },
36    { .value = -0.9,                    .expected = "20001231235959.1Z", },
37    { .value = -129.0,                  .expected = "20001231235751Z", },
38    { .value = 1000.0,                  .expected = "20010101001640Z", },
39    { .value = 65280.0,                 .expected = "20010101180800Z", },
40    { .value = 41234576.0,              .expected = "20020423060256Z", },
41    { .value = -412343576.0,            .expected = "19871208120704Z", },
42    { .value = 381778873.238063,        .expected = "20130205174113.238063Z", },
43    { .value = 381778873.638063,        .expected = "20130205174113.638063Z", },
44    { .value = 1400603.141,             .expected = "20010117050323.141Z", },
45    { .value = -412343576.238063,       .expected = "19871208120703.761937Z", },
46    { .value = -412343576.638063,       .expected = "19871208120703.361937Z", },
47    { .value = 5.000539014,             .expected = "20010101000005.000539014Z", },
48    { .value = -5.00053901,             .expected = "20001231235954.99946099Z", },
49    { .value = 0.000539014,             .expected = "20010101000000.000539014Z", },
50    { .value = -0.00053901,             .expected = "20001231235959.99946099Z", },
51    { .value = 11031400603.141,         .expected = "23500729055643.141Z", }, // Michael's 400th birthday
52    // Pedantic tests within 1 second of the epoch.
53    { .value = 0.000100120234,          .expected = "20010101000000.000100120234Z", },
54    { .value =                  0.00000000000000000000000000000000000000000010012654182354326,
55      .expected = "20010101000000.00000000000000000000000000000000000000000010012654182354326Z", },
56    { .value =                 -0.00000000000000000000000000000000000000000010012654182354326,
57      .expected = "20001231235959.99999999999999999999999999999999999999999989987345817645674Z", },
58    { .value = 0.0001234522366234637,   .expected = "20010101000000.0001234522366234637Z", },
59};
60
61static CFStringRef string_create_with_hex(const uint8_t* start, const uint8_t* end) {
62    CFMutableStringRef s = CFStringCreateMutable(NULL, 0);
63    CFStringAppendFormat(s, 0, CFSTR(".size = %" PRIdPTR ", .res = { "), (intptr_t)(end - start));
64    while (start < end)
65        CFStringAppendFormat(s, 0, CFSTR("0x%02X, "), *start++);
66    CFStringAppend(s, CFSTR("},\n"));
67    return s;
68}
69
70static bool ok_der_date_is(int testnumber, const char *expected, const uint8_t *der, const uint8_t *der_end) {
71    size_t elen = strlen(expected);
72    size_t dlen;
73    const uint8_t *body = ccder_decode_tl(CCDER_GENERALIZED_TIME, &dlen, der, der_end);
74    if (!body) {
75        return fail("[%d] encoded date %@ expected %s not a generalized time", testnumber, string_create_with_hex(der, der_end), expected);
76    } else if (body + dlen != der_end) {
77        return fail("[%d] Trailing garbage in encoded string after generalized time got: %@ expected: %s", testnumber, string_create_with_hex(der, der_end), expected);
78    } else if (dlen != elen) {
79        return fail("[%d] encoded date len %zu != %zu got: %.*s expected: %s", testnumber, dlen , elen, (int)dlen, (char *)body, expected);
80    } else if (memcmp(body, expected, elen)) {
81        return fail("[%d] encoded got: %.*s expected: %s", testnumber, (int)dlen, (char *)body, expected);
82    } else {
83        return pass("[%d] properly encoded %s", testnumber, expected);
84    }
85}
86
87static bool ok_date_equals(int testnumber, CFDateRef decoded, CFDateRef expected) {
88    CFAbsoluteTime t1 = CFDateGetAbsoluteTime(decoded);
89    CFAbsoluteTime t2 = CFDateGetAbsoluteTime(expected);
90    if (-1.0 < t1 && t1 < 1.0) {
91        // Dates near the epoch can't be off by more than 1 nanosecond.  Other dates should be exactly right.
92        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);
93    } else {
94        return ok((decoded != NULL) && CFEqual(decoded, expected), "[%d] Didn't make equal value %a != %a (%g != %g).", testnumber, t1, t2, t1, t2);
95    }
96}
97
98#define kTestsPerTestCase 12
99static void one_test(const struct test_case * thisCase, int testnumber)
100{
101    uint8_t buffer[kMaxResultSize];
102    uint8_t* buffer_end = buffer + sizeof(buffer);
103
104    CFDateRef initialValue = CFDateCreate(NULL, thisCase->value);
105    CFErrorRef error = NULL;
106
107    uint8_t* encoded = der_encode_plist(initialValue, &error, buffer, buffer_end);
108    SKIP:
109    {
110        skip("der_encode_plist failed", 1,
111             ok(encoded != NULL, "[%d] der_encode_plist failed: %@", testnumber, error));
112        ok_der_date_is(testnumber, thisCase->expected, encoded, buffer_end);
113    }
114    CFReleaseNull(error);
115
116    encoded = der_encode_date(initialValue, &error, buffer, buffer_end);
117    SKIP:
118    {
119        skip("der_encode_date failed", 1,
120             ok(encoded != NULL, "[%d] der_encode_date failed: %@", testnumber, error));
121        ok_der_date_is(testnumber, thisCase->expected, encoded, buffer_end);
122    }
123    CFReleaseNull(error);
124
125    CFDateRef decoded = NULL;
126    const uint8_t* decode_end = der_decode_date(NULL, kCFPropertyListMutableContainers,
127                                                  &decoded, &error, encoded, buffer_end);
128    ok(error == NULL, "[%d] der_decode_date failed: %@", testnumber, error);
129    CFReleaseNull(error);
130
131    ok(decode_end == buffer_end, "[%d] didn't decode whole buffer", testnumber);
132    ok_date_equals(testnumber, decoded, initialValue);
133
134    CFPropertyListRef decoded_type = NULL;
135
136    decode_end = der_decode_plist(NULL, kCFPropertyListMutableContainers,
137                                  &decoded_type, &error, encoded, buffer_end);
138    ok(error == NULL, "[%d] der_decode_plist failed: %@", testnumber, error);
139    CFReleaseNull(error);
140
141    ok(decode_end == buffer_end, "[%d] didn't decode whole buffer", testnumber);
142    ok_date_equals(testnumber, decoded, initialValue);
143
144    is(der_sizeof_date(initialValue, NULL), ccder_sizeof(CCDER_GENERALIZED_TIME, strlen(thisCase->expected)), "[%d] der_sizeof_date mismatch", testnumber);
145    is(der_sizeof_plist(initialValue, NULL), ccder_sizeof(CCDER_GENERALIZED_TIME, strlen(thisCase->expected)), "[%d] der_sizeof_plist mismatch", testnumber);
146
147    CFReleaseSafe(initialValue);
148    CFReleaseNull(decoded);
149    CFReleaseNull(decoded_type);
150}
151
152#define kTestCount (array_size(test_cases) * kTestsPerTestCase)
153static void tests(void)
154{
155    for (int testnumber = 0; testnumber < array_size(test_cases); ++testnumber)
156        one_test(test_cases + testnumber, testnumber);
157}
158
159int su_16_cfdate_der(int argc, char *const *argv)
160{
161    plan_tests(kTestCount);
162    tests();
163
164    return 0;
165}
166