1//
2//  SecCFCanonicalHashes.c
3//  utilities
4//
5//  Created by Mitch Adler on 2/8/12.
6//  Copyright (c) 2012 Apple Inc. All rights reserved.
7//
8
9#include <stdio.h>
10
11#include "utilities/SecCFCanonicalHashes.h"
12#include "utilities/comparison.h"
13
14#include <CoreFoundation/CFArray.h>
15#include <CoreFoundation/CFString.h>
16#include <CoreFoundation/CFData.h>
17#include <utilities/SecCFRelease.h>
18#include <corecrypto/ccdigest.h>
19
20struct AddKeyValueHashContext {
21    CFMutableArrayRef array;
22    const struct ccdigest_info* di;
23    bool fail;
24};
25
26static void AddKeyValueHashData(const void *key, const void *value, void *context)
27{
28    struct AddKeyValueHashContext* akvContext = (struct AddKeyValueHashContext*) context;
29
30    size_t key_len;
31    const void* key_data;
32    if (CFGetTypeID(key) == CFStringGetTypeID()) {
33        key_len = CFStringGetLength((CFStringRef) key);
34        key_data = CFStringGetCharactersPtr((CFStringRef) key);
35    } else {
36        akvContext->fail = true;
37        return;
38    }
39
40    size_t value_len;
41    const void* value_data;
42    if (CFGetTypeID(key) == CFStringGetTypeID()) {
43        value_len = CFStringGetLength((CFStringRef) value);
44        value_data = CFStringGetCharactersPtr((CFStringRef) value);
45    } else if (CFGetTypeID(key) == CFDataGetTypeID()) {
46        value_len = CFDataGetLength((CFDataRef)value);
47        value_data = CFDataGetBytePtr((CFDataRef)value);
48    } else {
49        akvContext->fail = true;
50        return;
51    }
52
53    UInt8 hashBuffer[akvContext->di->output_size];
54
55    ccdigest_di_decl(akvContext->di, finalContext);
56
57    ccdigest(akvContext->di, key_len, key_data, hashBuffer);
58
59    ccdigest_init(akvContext->di, finalContext);
60    ccdigest_update(akvContext->di, finalContext, sizeof(hashBuffer), hashBuffer);
61
62    ccdigest_update(akvContext->di, finalContext, sizeof(hashBuffer), hashBuffer);
63    ccdigest(akvContext->di, value_len, value_data, hashBuffer);
64
65    ccdigest_final(akvContext->di, finalContext, (void*)hashBuffer);
66
67    CFDataRef hash = CFDataCreate(kCFAllocatorDefault, hashBuffer, sizeof(hashBuffer));
68    CFArrayAppendValue(akvContext->array, hash);
69    CFReleaseSafe(hash);
70}
71
72static CFComparisonResult CFDataCompare(CFDataRef d1, CFDataRef d2)
73{
74    CFIndex d1_size = CFDataGetLength(d1);
75    CFIndex d2_size = CFDataGetLength(d2);
76
77    CFIndex comparison = memcmp(CFDataGetBytePtr(d1), CFDataGetBytePtr(d2), MIN(d1_size, d2_size));
78
79    if (comparison == 0)
80        comparison = d1_size - d2_size;
81
82    return (comparison > 0) ? kCFCompareGreaterThan : ((comparison < 0) ? kCFCompareLessThan : kCFCompareEqualTo);
83}
84
85static CFComparisonResult CFEqualComparitor(const void *val1, const void *val2, void * context __unused)
86{
87    return CFDataCompare((CFDataRef) val1, (CFDataRef) val2);
88}
89
90struct array_hashing_context {
91    const struct ccdigest_info* di;
92    struct ccdigest_ctx *       ctx;
93};
94
95static void hash_CFDatas(const void *value, void *context)
96{
97    struct array_hashing_context* ahc = (struct array_hashing_context*) context;
98
99    ccdigest_update(ahc->di, ahc->ctx, CFDataGetLength((CFDataRef) value), CFDataGetBytePtr((CFDataRef) value));
100}
101
102
103bool SecCFAppendCFDictionaryHash(CFDictionaryRef thisDictionary, const struct ccdigest_info* di, CFMutableDataRef toThis)
104{
105    CFMutableArrayRef hashArray = CFArrayCreateMutable(kCFAllocatorDefault, CFDictionaryGetCount(thisDictionary), &kCFTypeArrayCallBacks);
106
107    CFDictionaryApplyFunction(thisDictionary, &AddKeyValueHashData, hashArray);
108
109    CFRange wholeArray = CFRangeMake(0, CFArrayGetCount(hashArray));
110
111    CFArraySortValues(hashArray, wholeArray, &CFEqualComparitor, NULL);
112
113    ccdigest_di_decl(di, finalContext);
114
115    struct array_hashing_context ahc = { .di = di, .ctx = (struct ccdigest_ctx*)&finalContext};
116
117    CFArrayApplyFunction(hashArray, wholeArray, hash_CFDatas, &ahc);
118
119    return true;
120}
121