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