1//
2//  der_dictionary.c
3//  utilities
4//
5//  Created by Mitch Adler on 6/18/12.
6//  Copyright (c) 2012 Apple Inc. All rights reserved.
7//
8
9#include <stdio.h>
10
11#include "utilities/SecCFRelease.h"
12#include "utilities/der_plist.h"
13#include "utilities/der_plist_internal.h"
14#include "utilities/SecCFWrappers.h"
15
16#include <corecrypto/ccder.h>
17#include <CoreFoundation/CoreFoundation.h>
18
19static const uint8_t* der_decode_key_value(CFAllocatorRef allocator, CFOptionFlags mutability,
20                                           CFPropertyListRef* key, CFPropertyListRef* value, CFErrorRef *error,
21                                           const uint8_t* der, const uint8_t *der_end)
22{
23    const uint8_t *payload_end = 0;
24    const uint8_t *payload = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &payload_end, der, der_end);
25
26    if (NULL == payload) {
27        SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("Unknown data encoding, expected CCDER_CONSTRUCTED_SEQUENCE"), NULL, error);
28        return NULL;
29    }
30
31    CFTypeRef keyObject = NULL;
32    CFTypeRef valueObject = NULL;
33
34
35    payload = der_decode_plist(allocator, mutability, &keyObject, error, payload, payload_end);
36    payload = der_decode_plist(allocator, mutability, &valueObject, error, payload, payload_end);
37
38    if (payload != NULL) {
39        *key = keyObject;
40        *value = valueObject;
41    } else {
42        CFReleaseNull(keyObject);
43        CFReleaseNull(valueObject);
44    }
45    return payload;
46}
47
48const uint8_t* der_decode_dictionary(CFAllocatorRef allocator, CFOptionFlags mutability,
49                                     CFDictionaryRef* dictionary, CFErrorRef *error,
50                                     const uint8_t* der, const uint8_t *der_end)
51{
52    if (NULL == der)
53        return NULL;
54
55    const uint8_t *payload_end = 0;
56    const uint8_t *payload = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SET, &payload_end, der, der_end);
57
58    if (NULL == payload) {
59        SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("Unknown data encoding, expected CCDER_CONSTRUCTED_SET"), NULL, error);
60        return NULL;
61    }
62
63
64    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
65
66    if (NULL == dict) {
67        SecCFDERCreateError(kSecDERErrorUnderlyingError, CFSTR("Failed to create data"), NULL, error);
68        payload = NULL;
69        goto exit;
70    }
71
72    while (payload != NULL && payload < payload_end) {
73        CFTypeRef key = NULL;
74        CFTypeRef value = NULL;
75
76        payload = der_decode_key_value(allocator, mutability, &key, &value, error, payload, payload_end);
77
78        if (payload) {
79            CFDictionaryAddValue(dict, key, value);
80            CFReleaseNull(key);
81            CFReleaseNull(value);
82        }
83    }
84
85
86exit:
87    if (payload == payload_end) {
88        *dictionary = dict;
89        dict = NULL;
90    }
91
92    CFReleaseNull(dict);
93
94    return payload;
95}
96
97struct size_context {
98    bool   success;
99    size_t size;
100    CFErrorRef *error;
101};
102
103static size_t der_sizeof_key_value(CFTypeRef key, CFTypeRef value, CFErrorRef *error) {
104    size_t key_size = der_sizeof_plist(key, error);
105    if (key_size == 0) {
106        return 0;
107    }
108    size_t value_size = der_sizeof_plist(value, error);
109    if (value_size == 0) {
110        return 0;
111    }
112    return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, key_size + value_size);
113}
114
115static void add_key_value_size(const void *key_void, const void *value_void, void *context_void)
116{
117    CFTypeRef key = (CFTypeRef) key_void;
118    CFTypeRef value = (CFTypeRef) value_void;
119    struct size_context *context = (struct size_context*) context_void;
120
121    if (!context->success)
122        return;
123
124    size_t kv_size = der_sizeof_key_value(key, value, context->error);
125    if (kv_size == 0) {
126        context->success = false;
127        return;
128    }
129
130    context->size += kv_size;
131}
132
133size_t der_sizeof_dictionary(CFDictionaryRef dict, CFErrorRef *error)
134{
135    struct size_context context = { .success = true, .size = 0, .error = error };
136
137
138    CFDictionaryApplyFunction(dict, add_key_value_size, &context);
139
140    if (!context.success)
141        return 0;
142
143    return ccder_sizeof(CCDER_CONSTRUCTED_SET, context.size);
144}
145
146static uint8_t* der_encode_key_value(CFPropertyListRef key, CFPropertyListRef value, CFErrorRef *error,
147                                     const uint8_t* der, uint8_t *der_end)
148{
149    return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
150           der_encode_plist(key, error, der,
151           der_encode_plist(value, error, der, der_end)));
152}
153
154struct encode_context {
155    bool         success;
156    CFErrorRef * error;
157    CFMutableArrayRef list;
158    CFAllocatorRef allocator;
159};
160
161static void add_sequence_to_array(const void *key_void, const void *value_void, void *context_void)
162{
163    struct encode_context *context = (struct encode_context *) context_void;
164    if (context->success) {
165        CFTypeRef key = (CFTypeRef) key_void;
166        CFTypeRef value = (CFTypeRef) value_void;
167
168        size_t der_size = der_sizeof_key_value(key, value, context->error);
169        if (der_size == 0) {
170            context-> success = false;
171        } else {
172            CFMutableDataRef encoded_kv = CFDataCreateMutable(context->allocator, der_size);
173            CFDataSetLength(encoded_kv, der_size);
174
175            uint8_t* const encode_begin = CFDataGetMutableBytePtr(encoded_kv);
176            uint8_t* encode_end = encode_begin + der_size;
177
178            encode_end = der_encode_key_value(key, value, context->error, encode_begin, encode_end);
179
180            if (encode_end != NULL) {
181                CFDataDeleteBytes(encoded_kv, CFRangeMake(0, (encode_end - encode_begin)));
182                CFArrayAppendValue(context->list, encoded_kv);
183            } else {
184                context-> success = false;
185            }
186
187            CFReleaseNull(encoded_kv);
188        }
189    }
190}
191
192static CFComparisonResult cfdata_compare_contents(const void *val1, const void *val2, void *context __unused)
193{
194    return CFDataCompare((CFDataRef) val1, (CFDataRef) val2);
195}
196
197
198uint8_t* der_encode_dictionary(CFDictionaryRef dictionary, CFErrorRef *error,
199                               const uint8_t *der, uint8_t *der_end)
200{
201    CFMutableArrayRef elements = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
202
203    struct encode_context context = { .success = true, .error = error, .list = elements };
204    CFDictionaryApplyFunction(dictionary, add_sequence_to_array, &context);
205
206    if (!context.success) {
207        CFReleaseNull(elements);
208        return NULL;
209    }
210
211    CFRange allOfThem = CFRangeMake(0, CFArrayGetCount(elements));
212
213    CFArraySortValues(elements, allOfThem, cfdata_compare_contents, NULL);
214
215    uint8_t* original_der_end = der_end;
216
217    for(CFIndex position = CFArrayGetCount(elements); position > 0;) {
218        --position;
219        CFDataRef data = CFArrayGetValueAtIndex(elements, position);
220        der_end = ccder_encode_body(CFDataGetLength(data), CFDataGetBytePtr(data), der, der_end);
221    }
222
223    CFReleaseNull(elements);
224
225    return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SET, original_der_end, der, der_end);
226
227}
228