1/*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25#include <SecureObjectSync/SOSInternal.h>
26#include <SecureObjectSync/SOSCircle.h>
27#include <SecureObjectSync/SOSCloudCircle.h>
28#include <SecureObjectSync/SOSKVSKeys.h>
29#include "utilities/SecCFError.h"
30#include "utilities/SecCFRelease.h"
31#include "utilities/SecCFWrappers.h"
32#include "utilities/iOSforOSX.h"
33
34#include <CoreFoundation/CoreFoundation.h>
35
36#include <Security/SecKey.h>
37#include <Security/SecKeyPriv.h>
38#include <Security/SecItem.h>
39#include <securityd/SecDbItem.h> // For SecError
40#include "utilities/iOSforOSX.h"
41
42#include <Security/SecBase64.h>
43
44#include <AssertMacros.h>
45
46CFStringRef kSOSErrorDomain = CFSTR("com.apple.security.sos.error");
47
48bool SOSErrorCreate(CFIndex errorCode, CFErrorRef *error, CFDictionaryRef formatOptions, CFStringRef format, ...) {
49    if (!errorCode) return true;
50    if (error && !*error) {
51        va_list va;
52        va_start(va, format);
53        SecCFCreateErrorWithFormatAndArguments(errorCode, kSOSErrorDomain, NULL, error, formatOptions, format, va);
54        va_end(va);
55    }
56    return false;
57}
58
59bool SOSCreateError(CFIndex errorCode, CFStringRef descriptionString, CFErrorRef previousError, CFErrorRef *newError) {
60    SOSCreateErrorWithFormat(errorCode, previousError, newError, NULL, CFSTR("%@"), descriptionString);
61    return true;
62}
63
64bool SOSCreateErrorWithFormat(CFIndex errorCode, CFErrorRef previousError, CFErrorRef *newError,
65                              CFDictionaryRef formatOptions, CFStringRef format, ...) {
66    va_list va;
67    va_start(va, format);
68    bool res = SOSCreateErrorWithFormatAndArguments(errorCode, previousError, newError, formatOptions, format, va);
69    va_end(va);
70    return res;
71}
72
73bool SOSCreateErrorWithFormatAndArguments(CFIndex errorCode, CFErrorRef previousError, CFErrorRef *newError,
74                                          CFDictionaryRef formatOptions, CFStringRef format, va_list args) {
75    SecCFCreateErrorWithFormatAndArguments(errorCode, kSOSErrorDomain, previousError, newError, formatOptions, format, args);
76    return true;
77}
78
79
80//
81// Utility Functions
82//
83
84static OSStatus GenerateECPairImp(int keySize, CFBooleanRef permanent, SecKeyRef* public, SecKeyRef *full)
85{
86    static const CFStringRef sTempNameToUse = CFSTR("GenerateECPair Temporary Key - Shouldn't be live");
87
88    CFNumberRef signing_bitsize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize);
89
90    CFDictionaryRef keygen_parameters = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
91                                                                     kSecAttrKeyType,       kSecAttrKeyTypeEC,
92                                                                     kSecAttrKeySizeInBits, signing_bitsize,
93                                                                     kSecAttrIsPermanent,   permanent,
94                                                                     kSecAttrLabel,         sTempNameToUse,
95                                                                     NULL);
96    CFReleaseNull(signing_bitsize);
97    OSStatus result = SecKeyGeneratePair(keygen_parameters, public, full);
98    CFReleaseNull(keygen_parameters);
99
100    return result;
101}
102
103OSStatus GenerateECPair(int keySize, SecKeyRef* public, SecKeyRef *full)
104{
105    return GenerateECPairImp(keySize, kCFBooleanFalse, public, full);
106}
107
108OSStatus GeneratePermanentECPair(int keySize, SecKeyRef* public, SecKeyRef *full)
109{
110    return GenerateECPairImp(keySize, kCFBooleanTrue, public, full);
111}
112
113static CFStringRef SOSCircleCopyDescriptionFromData(CFDataRef data)
114{
115    CFErrorRef error = NULL;
116    CFStringRef result = NULL;
117
118    SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, data, &error);
119
120    if (circle)
121        result = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@"), circle);
122
123    CFReleaseSafe(circle);
124
125    return result;
126}
127
128CFStringRef SOSChangesCopyDescription(CFDictionaryRef changes, bool is_sender)
129{
130    CFMutableStringRef string = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("<Changes: {\n"));
131
132    CFDictionaryForEach(changes, ^(const void *key, const void *value) {
133        CFStringRef value_description = NULL;
134        if (isString(key) && isData(value)) {
135            CFDataRef value_data = (CFDataRef) value;
136            switch (SOSKVSKeyGetKeyType(key)) {
137                case kCircleKey:
138                    value_description = SOSCircleCopyDescriptionFromData(value_data);
139                    break;
140                case kMessageKey:
141                    value_description = CFCopyDescription(value_data);
142                    break;
143                default:
144                    break;
145            }
146
147        }
148        CFStringAppendFormat(string, NULL, CFSTR("    '%@' %s %@\n"),
149                             key,
150                             is_sender ? "<=" : "=>",
151                             value_description ? value_description : value);
152
153        CFReleaseNull(value_description);
154    });
155
156    CFStringAppendFormat(string, NULL, CFSTR("}"));
157
158    return string;
159}
160
161CFStringRef SOSCopyIDOfKey(SecKeyRef key, CFErrorRef *error)
162{
163    const struct ccdigest_info * di = ccsha1_di();
164    CFDataRef publicBytes = NULL;
165    CFStringRef result = NULL;
166
167    uint8_t digest[di->output_size];
168    char encoded[2 * di->output_size]; // Big enough for base64 encoding.
169
170    require_quiet(SecError(SecKeyCopyPublicBytes(key, &publicBytes), error, CFSTR("Failed to export public bytes %@"), key), fail);
171
172    ccdigest(di, CFDataGetLength(publicBytes), CFDataGetBytePtr(publicBytes), digest);
173
174    size_t length = SecBase64Encode(digest, sizeof(digest), encoded, sizeof(encoded));
175    assert(length && length < sizeof(encoded));
176    if (length > 26)
177      length = 26;
178    encoded[length] = 0;
179    CFReleaseNull(publicBytes);
180    return CFStringCreateWithCString(kCFAllocatorDefault, encoded, kCFStringEncodingASCII);
181
182fail:
183    CFReleaseNull(publicBytes);
184    return result;
185}
186