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