1//
2//  SOSInternal.c
3//  sec
4//
5//  Created by Mitch Adler on 7/18/12.
6//
7//
8
9#include <SecureObjectSync/SOSInternal.h>
10#include <SecureObjectSync/SOSCircle.h>
11#include <SecureObjectSync/SOSCloudCircle.h>
12#include <SecureObjectSync/SOSAccount.h>
13
14#include "utilities/SecCFError.h"
15#include "utilities/SecCFRelease.h"
16#include "utilities/SecCFWrappers.h"
17#include "utilities/iOSforOSX.h"
18
19#include <CoreFoundation/CoreFoundation.h>
20
21#include <Security/SecKey.h>
22#include <Security/SecKeyPriv.h>
23#include <Security/SecItem.h>
24#include <securityd/SecDbItem.h> // For SecError
25#include "utilities/iOSforOSX.h"
26
27#include <Security/SecBase64.h>
28
29#include <AssertMacros.h>
30
31CFStringRef kSOSErrorDomain = CFSTR("com.apple.security.sos.error");
32
33bool SOSCreateError(CFIndex errorCode, CFStringRef descriptionString, CFErrorRef previousError, CFErrorRef *newError) {
34    SOSCreateErrorWithFormat(errorCode, previousError, newError, NULL, CFSTR("%@"), descriptionString);
35    return true;
36}
37
38bool SOSCreateErrorWithFormat(CFIndex errorCode, CFErrorRef previousError, CFErrorRef *newError,
39                              CFDictionaryRef formatOptions, CFStringRef format, ...) {
40    va_list va;
41    va_start(va, format);
42    bool res = SOSCreateErrorWithFormatAndArguments(errorCode, previousError, newError, formatOptions, format, va);
43    va_end(va);
44    return res;
45}
46
47bool SOSCreateErrorWithFormatAndArguments(CFIndex errorCode, CFErrorRef previousError, CFErrorRef *newError,
48                                          CFDictionaryRef formatOptions, CFStringRef format, va_list args)
49{
50    SecCFCreateErrorWithFormatAndArguments(errorCode, kSOSErrorDomain, previousError, newError, formatOptions, format, args);
51    return true;
52}
53
54
55//
56// Utility Functions
57//
58
59static OSStatus GenerateECPairImp(int keySize, CFBooleanRef permanent, SecKeyRef* public, SecKeyRef *full)
60{
61    static const CFStringRef sTempNameToUse = CFSTR("GenerateECPair Temporary Key - Shouldn't be live");
62
63    CFNumberRef signing_bitsize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize);
64
65    CFDictionaryRef keygen_parameters = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
66                                                                     kSecAttrKeyType,       kSecAttrKeyTypeEC,
67                                                                     kSecAttrKeySizeInBits, signing_bitsize,
68                                                                     kSecAttrIsPermanent,   permanent,
69                                                                     kSecAttrLabel,         sTempNameToUse,
70                                                                     NULL);
71    CFReleaseNull(signing_bitsize);
72    OSStatus result = SecKeyGeneratePair(keygen_parameters, public, full);
73    CFReleaseNull(keygen_parameters);
74
75    return result;
76}
77
78OSStatus GenerateECPair(int keySize, SecKeyRef* public, SecKeyRef *full)
79{
80    return GenerateECPairImp(keySize, kCFBooleanFalse, public, full);
81}
82
83OSStatus GeneratePermanentECPair(int keySize, SecKeyRef* public, SecKeyRef *full)
84{
85    return GenerateECPairImp(keySize, kCFBooleanTrue, public, full);
86}
87
88static CFStringRef SOSCircleCopyDescriptionFromData(CFDataRef data)
89{
90    CFErrorRef error;
91    CFStringRef result = NULL;
92
93    SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, data, &error);
94
95    if (circle)
96        result = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@"), circle);
97
98    CFReleaseSafe(circle);
99
100    return result;
101}
102
103CFStringRef SOSChangesCopyDescription(CFDictionaryRef changes, bool is_sender)
104{
105    CFMutableStringRef string = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("<Changes: {\n"));
106
107    CFDictionaryForEach(changes, ^(const void *key, const void *value) {
108        CFStringRef value_description = NULL;
109        if (isString(key) && isData(value)) {
110            CFDataRef value_data = (CFDataRef) value;
111            switch (SOSKVSKeyGetKeyType(key)) {
112                case kCircleKey:
113                    value_description = SOSCircleCopyDescriptionFromData(value_data);
114                    break;
115                case kMessageKey:
116                    value_description = SOSMessageCopyDescription(value_data);
117                    break;
118                default:
119                    break;
120            }
121
122        }
123        CFStringAppendFormat(string, NULL, CFSTR("    '%@' %s %@\n"),
124                             key,
125                             is_sender ? "<=" : "=>",
126                             value_description ? value_description : value);
127
128        CFReleaseNull(value_description);
129    });
130
131    CFStringAppendFormat(string, NULL, CFSTR("}"));
132
133    return string;
134}
135
136CFStringRef SOSCopyIDOfKey(SecKeyRef key, CFErrorRef *error)
137{
138    const struct ccdigest_info * di = ccsha1_di();
139    CFDataRef publicBytes = NULL;
140    CFStringRef result = NULL;
141
142    uint8_t digest[di->output_size];
143    char encoded[2 * di->output_size]; // Big enough for base64 encoding.
144
145    require_quiet(SecError(SecKeyCopyPublicBytes(key, &publicBytes), error, CFSTR("Failed to export public bytes %@"), key), fail);
146
147    ccdigest(di, CFDataGetLength(publicBytes), CFDataGetBytePtr(publicBytes), digest);
148
149    size_t length = SecBase64Encode(digest, sizeof(digest), encoded, sizeof(encoded));
150    assert(length && length < sizeof(encoded));
151    if (length > 26)
152      length = 26;
153    encoded[length] = 0;
154    CFReleaseNull(publicBytes);
155    return CFStringCreateWithCString(kCFAllocatorDefault, encoded, kCFStringEncodingASCII);
156
157fail:
158    CFReleaseNull(publicBytes);
159    return result;
160}
161