]> git.saurik.com Git - apple/security.git/blob - keychain/SecureObjectSync/SOSInternal.m
Security-59754.80.3.tar.gz
[apple/security.git] / keychain / SecureObjectSync / SOSInternal.m
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 "keychain/SecureObjectSync/SOSInternal.h"
26 #include "keychain/SecureObjectSync/SOSCircle.h"
27 #include <Security/SecureObjectSync/SOSCloudCircle.h>
28 #include "keychain/SecureObjectSync/SOSKVSKeys.h"
29 #include <Security/SecureObjectSync/SOSViews.h>
30 #include "utilities/SecCFError.h"
31 #include "utilities/SecCFRelease.h"
32 #include "utilities/SecCFWrappers.h"
33 #include "utilities/iOSforOSX.h"
34
35 #include <CoreFoundation/CoreFoundation.h>
36
37 #include <Security/SecKey.h>
38 #include <Security/SecKeyPriv.h>
39 #include <Security/SecItem.h>
40 #include "keychain/securityd/SecDbItem.h" // For SecError
41 #include "utilities/iOSforOSX.h"
42
43 #include <Security/SecBase64.h>
44 #include <utilities/der_plist.h>
45 #include <utilities/der_plist_internal.h>
46 #include <corecrypto/ccder.h>
47 #include <utilities/der_date.h>
48
49 #include <corecrypto/ccrng.h>
50 #include <corecrypto/ccdigest.h>
51 #include <corecrypto/ccsha2.h>
52 #include <corecrypto/ccpbkdf2.h>
53
54 #include <os/lock.h>
55
56 #include <AssertMacros.h>
57
58 const CFStringRef kSOSErrorDomain = CFSTR("com.apple.security.sos.error");
59 const CFStringRef kSOSDSIDKey = CFSTR("AccountDSID");
60 const CFStringRef SOSTransportMessageTypeIDSV2 = CFSTR("IDS2.0");
61 const CFStringRef SOSTransportMessageTypeKVS = CFSTR("KVS");
62 const CFStringRef kSOSCountKey = CFSTR("numberOfErrorsDeep");
63
64 bool SOSErrorCreate(CFIndex errorCode, CFErrorRef *error, CFDictionaryRef formatOptions, CFStringRef format, ...)
65 CF_FORMAT_FUNCTION(4, 5);
66
67
68 bool SOSErrorCreate(CFIndex errorCode, CFErrorRef *error, CFDictionaryRef formatOptions, CFStringRef format, ...) {
69 if (!errorCode) return true;
70 if (error && !*error) {
71 va_list va;
72 va_start(va, format);
73 SecCFCreateErrorWithFormatAndArguments(errorCode, kSOSErrorDomain, NULL, error, formatOptions, format, va);
74 va_end(va);
75 }
76 return false;
77 }
78
79 bool SOSCreateError(CFIndex errorCode, CFStringRef descriptionString, CFErrorRef previousError, CFErrorRef *newError) {
80 SOSCreateErrorWithFormat(errorCode, previousError, newError, NULL, CFSTR("%@"), descriptionString);
81 return false;
82 }
83
84 bool SOSCreateErrorWithFormat(CFIndex errorCode, CFErrorRef previousError, CFErrorRef *newError,
85 CFDictionaryRef formatOptions, CFStringRef format, ...) {
86 va_list va;
87 va_start(va, format);
88 bool res = SOSCreateErrorWithFormatAndArguments(errorCode, previousError, newError, formatOptions, format, va);
89 va_end(va);
90 return res;
91 }
92
93 bool SOSCreateErrorWithFormatAndArguments(CFIndex errorCode, CFErrorRef previousError, CFErrorRef *newError,
94 CFDictionaryRef formatOptions, CFStringRef format, va_list args) {
95 return SecCFCreateErrorWithFormatAndArguments(errorCode, kSOSErrorDomain, previousError, newError, formatOptions, format, args);
96 }
97
98
99 //
100 // Utility Functions
101 //
102
103 static OSStatus GenerateECPairImp(int keySize, CFBooleanRef permanent, SecKeyRef* public, SecKeyRef *full)
104 {
105 static const CFStringRef sTempNameToUse = CFSTR("GenerateECPair Temporary Key - Shouldn't be live");
106
107 CFNumberRef signing_bitsize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize);
108
109 CFDictionaryRef keygen_parameters = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
110 kSecAttrKeyType, kSecAttrKeyTypeEC,
111 kSecAttrKeySizeInBits, signing_bitsize,
112 kSecAttrIsPermanent, permanent,
113 kSecAttrLabel, sTempNameToUse,
114 NULL);
115 CFReleaseNull(signing_bitsize);
116 OSStatus result = SecKeyGeneratePair(keygen_parameters, public, full);
117 CFReleaseNull(keygen_parameters);
118
119 return result;
120 }
121
122 OSStatus GenerateECPair(int keySize, SecKeyRef* public, SecKeyRef *full)
123 {
124 return GenerateECPairImp(keySize, kCFBooleanFalse, public, full);
125 }
126
127 OSStatus GeneratePermanentECPair(int keySize, SecKeyRef* public, SecKeyRef *full)
128 {
129 return GenerateECPairImp(keySize, kCFBooleanTrue, public, full);
130 }
131
132 static CFStringRef SOSCircleCopyDescriptionFromData(CFDataRef data)
133 {
134 CFErrorRef error = NULL;
135 CFStringRef result = NULL;
136
137 SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, data, &error);
138
139 if (circle)
140 result = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@"), circle);
141
142 CFReleaseSafe(circle);
143
144 return result;
145 }
146
147 CFStringRef SOSItemsChangedCopyDescription(CFDictionaryRef changes, bool is_sender)
148 {
149 CFMutableStringRef string = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("<Changes: {\n"));
150
151 CFDictionaryForEach(changes, ^(const void *key, const void *value) {
152 CFStringRef value_description = NULL;
153 if (isString(key) && isData(value)) {
154 CFDataRef value_data = (CFDataRef) value;
155 switch (SOSKVSKeyGetKeyType(key)) {
156 case kCircleKey:
157 value_description = SOSCircleCopyDescriptionFromData(value_data);
158 break;
159 case kMessageKey:
160 value_description = CFCopyDescription(value_data);
161 break;
162 default:
163 break;
164 }
165
166 }
167 CFStringAppendFormat(string, NULL, CFSTR(" '%@' %s %@\n"),
168 key,
169 is_sender ? "<=" : "=>",
170 value_description ? value_description : value);
171
172 CFReleaseNull(value_description);
173 });
174
175 CFStringAppendFormat(string, NULL, CFSTR("}"));
176
177 return string;
178 }
179
180 CFStringRef SOSCopyHashBufAsString(uint8_t *digest, size_t len) {
181 char encoded[2 * len + 1]; // Big enough for base64 encoding.
182
183 size_t length = SecBase64Encode(digest, len, encoded, sizeof(encoded));
184 assert(length && length < sizeof(encoded));
185 if (length > kSOSPeerIDLengthMax)
186 length = kSOSPeerIDLengthMax;
187 encoded[length] = 0;
188 return CFStringCreateWithCString(kCFAllocatorDefault, encoded, kCFStringEncodingASCII);
189 }
190
191 CFStringRef SOSCopyIDOfDataBuffer(CFDataRef data, CFErrorRef *error) {
192 const struct ccdigest_info * di = ccsha1_di();
193 uint8_t digest[di->output_size];
194 ccdigest(di, CFDataGetLength(data), CFDataGetBytePtr(data), digest);
195 return SOSCopyHashBufAsString(digest, sizeof(digest));
196 }
197
198 CFStringRef SOSCopyIDOfDataBufferWithLength(CFDataRef data, CFIndex len, CFErrorRef *error) {
199 CFStringRef retval = NULL;
200 CFStringRef tmp = SOSCopyIDOfDataBuffer(data, error);
201 if(tmp) retval = CFStringCreateWithSubstring(kCFAllocatorDefault, tmp, CFRangeMake(0, len));
202 CFReleaseNull(tmp);
203 return retval;
204 }
205
206 CFStringRef SOSCopyIDOfKey(SecKeyRef key, CFErrorRef *error) {
207 CFDataRef publicBytes = NULL;
208 CFStringRef result = NULL;
209 require_action_quiet(key, errOut, SOSErrorCreate(kSOSErrorNoKey, error, NULL, CFSTR("NULL key passed to SOSCopyIDOfKey")));
210 require_quiet(SecError(SecKeyCopyPublicBytes(key, &publicBytes), error, CFSTR("Failed to export public bytes %@"), key), errOut);
211 result = SOSCopyIDOfDataBuffer(publicBytes, error);
212 errOut:
213 CFReleaseNull(publicBytes);
214 return result;
215 }
216
217 CFStringRef SOSCopyIDOfKeyWithLength(SecKeyRef key, CFIndex len, CFErrorRef *error) {
218 CFStringRef retval = NULL;
219 CFStringRef tmp = SOSCopyIDOfKey(key, error);
220 if(tmp) retval = CFStringCreateWithSubstring(kCFAllocatorDefault, tmp, CFRangeMake(0, len));
221 CFReleaseNull(tmp);
222 return retval;
223 }
224
225
226 CFGiblisGetSingleton(ccec_const_cp_t, SOSGetBackupKeyCurveParameters, sBackupKeyCurveParameters, ^{
227 *sBackupKeyCurveParameters = ccec_cp_256();
228 });
229
230
231 //
232 // We're expecting full entropy here, so we just need to stretch
233 // via the PBKDF entropy rng. We'll choose a few iterations and no salt
234 // since we don't get sent any.
235 //
236 const int kBackupKeyIterations = 20;
237 const uint8_t sBackupKeySalt[] = { 0 };
238
239 bool SOSPerformWithDeviceBackupFullKey(ccec_const_cp_t cp, CFDataRef entropy, CFErrorRef *error, void (^operation)(ccec_full_ctx_t fullKey))
240 {
241 bool result = false;
242 ccec_full_ctx_decl_cp(cp, fullKey);
243
244 require_quiet(SOSGenerateDeviceBackupFullKey(fullKey, cp, entropy, error), exit);
245
246 operation(fullKey);
247
248 result = true;
249 exit:
250 ccec_full_ctx_clear_cp(cp, fullKey);
251
252 return result;
253 }
254
255
256 bool SOSGenerateDeviceBackupFullKey(ccec_full_ctx_t generatedKey, ccec_const_cp_t cp, CFDataRef entropy, CFErrorRef* error)
257 {
258 bool result = false;
259 int cc_result = 0;
260
261 #define drbg_output_size 1024
262
263 uint8_t drbg_output[drbg_output_size];
264 cc_result = ccpbkdf2_hmac(ccsha256_di(), CFDataGetLength(entropy), CFDataGetBytePtr(entropy),
265 sizeof(sBackupKeySalt), sBackupKeySalt,
266 kBackupKeyIterations,
267 drbg_output_size, drbg_output);
268 require_action_quiet(cc_result == 0, exit,
269 SOSErrorCreate(kSOSErrorProcessingFailure, error, NULL, CFSTR("ccpbkdf2_hmac failed: %d"), cc_result));
270
271 cc_result = ccec_generate_key_deterministic(cp,
272 drbg_output_size,
273 drbg_output,
274 ccrng(NULL),
275 CCEC_GENKEY_DETERMINISTIC_SECBKP,
276 generatedKey);
277 require_action_quiet(cc_result == 0, exit,
278 SOSErrorCreate(kSOSErrorProcessingFailure, error, NULL, CFSTR("ccec_generate_key_deterministic failed: %d"), cc_result));
279
280 result = true;
281 exit:
282 return result;
283 }
284
285 CFDataRef SOSCopyDeviceBackupPublicKey(CFDataRef entropy, CFErrorRef *error)
286 {
287 CFDataRef result = NULL;
288 CFMutableDataRef publicKeyData = NULL;
289
290 ccec_full_ctx_decl_cp(SOSGetBackupKeyCurveParameters(), fullKey);
291
292 require_quiet(SOSGenerateDeviceBackupFullKey(fullKey, SOSGetBackupKeyCurveParameters(), entropy, error), exit);
293
294 size_t space = ccec_compact_export_size(false, ccec_ctx_pub(fullKey));
295 publicKeyData = CFDataCreateMutableWithScratch(kCFAllocatorDefault, space);
296 require_quiet(SecAllocationError(publicKeyData, error, CFSTR("Mutable data allocation")), exit);
297
298 ccec_compact_export(false, CFDataGetMutableBytePtr(publicKeyData), fullKey);
299
300 CFTransferRetained(result, publicKeyData);
301
302 exit:
303 CFReleaseNull(publicKeyData);
304 return result;
305 }
306
307
308 CFDataRef SOSDateCreate(void) {
309 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
310 size_t bufsiz = der_sizeof_date(now, NULL);
311 uint8_t buf[bufsiz];
312 der_encode_date(now, NULL, buf, buf+bufsiz);
313 CFReleaseNull(now);
314 return CFDataCreate(NULL, buf, bufsiz);
315 }
316
317
318 CFDataRef CFDataCreateWithDER(CFAllocatorRef allocator, CFIndex size, uint8_t*(^operation)(size_t size, uint8_t *buffer)) {
319 __block CFMutableDataRef result = NULL;
320 if(!size) return NULL;
321 if((result = CFDataCreateMutableWithScratch(allocator, size)) == NULL) return NULL;
322 uint8_t *ptr = CFDataGetMutableBytePtr(result);
323 uint8_t *derptr = operation(size, ptr);
324 if(derptr == ptr) return result; // most probable case
325 if(!derptr || derptr < ptr) { // DER op failed - or derptr ended up prior to allocated buffer
326 CFReleaseNull(result);
327 } else if(derptr > ptr) { // This is a possible case where we don't end up using the entire allocated buffer
328 size_t diff = derptr - ptr; // The unused space ends up being the beginning of the allocation
329 CFDataDeleteBytes(result, CFRangeMake(0, diff));
330 }
331 return result;
332 }
333
334 @implementation SOSCachedNotification
335 + (NSString *)notificationName:(const char *)notificationString {
336 #if TARGET_OS_OSX
337 return [NSString stringWithFormat:@"user.uid.%d.%s", getuid(), notificationString];
338 #else
339 return @(notificationString);
340 #endif
341 }
342
343 @end
344
345 bool SOSCachedNotificationOperation(const char *notificationString, bool (^operation) (int token, bool gtg)) {
346 static os_unfair_lock token_lock = OS_UNFAIR_LOCK_INIT;
347 static NSMutableDictionary *tokenCache = NULL;
348 int token = NOTIFY_TOKEN_INVALID;
349
350 @autoreleasepool {
351 os_unfair_lock_lock(&token_lock);
352 if (tokenCache == NULL) {
353 tokenCache = [NSMutableDictionary dictionary];
354 }
355 NSString *notification = [SOSCachedNotification notificationName:notificationString];
356 if (notification == NULL) {
357 os_unfair_lock_unlock(&token_lock);
358 return false;
359 }
360
361 NSNumber *cachedToken = tokenCache[notification];
362 if (cachedToken == NULL) {
363 uint32_t status;
364
365 status = notify_register_check([notification UTF8String], &token);
366 if (status == NOTIFY_STATUS_OK) {
367 tokenCache[notification] = @(token);
368 } else {
369 secnotice("cachedStatus", "Failed to retreive token for %@: error %d",
370 notification, status);
371 }
372 } else {
373 token = [cachedToken intValue];
374 }
375 os_unfair_lock_unlock(&token_lock);
376 }
377
378 return operation(token, (token != NOTIFY_TOKEN_INVALID));
379 }
380
381 uint64_t SOSGetCachedCircleBitmask(void) {
382 __block uint64_t retval = 0; // If the following call fails and we return 0 the caller, checking CC_STATISVALID will see we didn't get anything.
383 SOSCachedNotificationOperation(kSOSCCCircleChangedNotification, ^bool(int token, bool gtg) {
384 if(gtg) {
385 notify_get_state(token, &retval);
386 }
387 return false;
388 });
389 return retval;
390 }
391
392 const SOSCCStatus kSOSNoCachedValue = -99;
393
394 SOSCCStatus SOSGetCachedCircleStatus(CFErrorRef *error) {
395 uint64_t statusMask = SOSGetCachedCircleBitmask();
396 SOSCCStatus retval = kSOSNoCachedValue;
397
398 if(statusMask & CC_STATISVALID) {
399 if(statusMask & CC_UKEY_TRUSTED) {
400 retval = (SOSCCStatus) statusMask & CC_MASK;
401 } else {
402 retval = kSOSCCError;
403 if(error) {
404 CFReleaseNull(*error);
405 if(statusMask & CC_PEER_IS_IN) {
406 SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Public Key isn't available, this peer is in the circle, but invalid. The iCloud Password must be provided to keychain syncing subsystem to repair this."), NULL, error);
407 } else {
408 SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Public Key isn't available. The iCloud Password must be provided to keychain syncing subsystem to repair this."), NULL, error);
409 }
410 }
411 }
412 }
413 return retval;
414 }
415
416 uint64_t SOSCachedViewBitmask(void) {
417 __block uint64_t retval = 0;
418 if(SOSGetCachedCircleStatus(NULL) == kSOSCCInCircle) {
419 SOSCachedNotificationOperation(kSOSCCViewMembershipChangedNotification, ^bool(int token, bool gtg) {
420 if(gtg) {
421 notify_get_state(token, &retval);
422 return true;
423 }
424 return false;
425 });
426 }
427 return retval;
428 }
429
430 CFSetRef SOSCreateCachedViewStatus(void) {
431 __block CFSetRef retval = NULL;
432 uint64_t state = SOSCachedViewBitmask();
433 if(state) {
434 retval = SOSViewCreateSetFromBitmask(state);
435 }
436 return retval;
437 }
438
439
440 NSDate *SOSCreateRandomDateBetweenNowPlus(NSTimeInterval starting, NSTimeInterval ending) {
441 uint64_t randomTime;
442 uint64_t span = ending - starting;
443 NSTimeInterval resultInterval = (span/2) + starting; // fallback in case call below fails.
444 if(SecRandomCopyBytes(NULL, sizeof(randomTime), &randomTime) == 0) {
445 resultInterval = (randomTime % span) + starting;
446 }
447 return [[NSDate alloc] initWithTimeIntervalSinceNow:resultInterval];
448 }
449
450
451 dispatch_queue_t SOSCCCredentialQueue(void) {
452 static dispatch_queue_t credQueue = NULL;
453 static dispatch_once_t onceToken;
454 dispatch_once(&onceToken, ^{
455 credQueue = dispatch_queue_create("com.apple.SOSCredentialsQueue", DISPATCH_QUEUE_SERIAL);
456 });
457 return credQueue;
458 }