2  * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   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 
  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. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  25 #include <Security/SecureObjectSync/SOSInternal.h> 
  26 #include <Security/SecureObjectSync/SOSCircle.h> 
  27 #include <Security/SecureObjectSync/SOSCloudCircle.h> 
  28 #include <Security/SecureObjectSync/SOSKVSKeys.h> 
  29 #include "utilities/SecCFError.h" 
  30 #include "utilities/SecCFRelease.h" 
  31 #include "utilities/SecCFWrappers.h" 
  32 #include "utilities/iOSforOSX.h" 
  34 #include <CoreFoundation/CoreFoundation.h> 
  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" 
  42 #include <Security/SecBase64.h> 
  43 #include <utilities/der_plist.h> 
  44 #include <utilities/der_plist_internal.h> 
  45 #include <corecrypto/ccder.h> 
  46 #include <utilities/der_date.h> 
  48 #include <corecrypto/ccrng.h> 
  49 #include <corecrypto/ccrng_pbkdf2_prng.h> 
  51 #include <CommonCrypto/CommonRandomSPI.h> 
  53 #include <AssertMacros.h> 
  55 const CFStringRef kSecIDSErrorDomain 
= CFSTR("com.apple.security.ids.error"); 
  56 const CFStringRef kIDSOperationType 
= CFSTR("IDSMessageOperation"); 
  57 const CFStringRef kIDSMessageToSendKey 
= CFSTR("MessageToSendKey"); 
  58 const CFStringRef kIDSMessageUniqueID 
= CFSTR("MessageID"); 
  59 const CFStringRef kIDSMessageRecipientPeerID 
= CFSTR("RecipientPeerID"); 
  60 const CFStringRef kIDSMessageRecipientDeviceID 
= CFSTR("RecipientDeviceID"); 
  61 const CFStringRef kIDSMessageUsesAckModel 
= CFSTR("UsesAckModel"); 
  63 bool SOSErrorCreate(CFIndex errorCode
, CFErrorRef 
*error
, CFDictionaryRef formatOptions
, CFStringRef format
, ...) { 
  64     if (!errorCode
) return true; 
  65     if (error 
&& !*error
) { 
  68         SecCFCreateErrorWithFormatAndArguments(errorCode
, kSOSErrorDomain
, NULL
, error
, formatOptions
, format
, va
); 
  74 bool SOSCreateError(CFIndex errorCode
, CFStringRef descriptionString
, CFErrorRef previousError
, CFErrorRef 
*newError
) { 
  75     SOSCreateErrorWithFormat(errorCode
, previousError
, newError
, NULL
, CFSTR("%@"), descriptionString
); 
  79 bool SOSCreateErrorWithFormat(CFIndex errorCode
, CFErrorRef previousError
, CFErrorRef 
*newError
, 
  80                               CFDictionaryRef formatOptions
, CFStringRef format
, ...) { 
  83     bool res 
= SOSCreateErrorWithFormatAndArguments(errorCode
, previousError
, newError
, formatOptions
, format
, va
); 
  88 bool SOSCreateErrorWithFormatAndArguments(CFIndex errorCode
, CFErrorRef previousError
, CFErrorRef 
*newError
, 
  89                                           CFDictionaryRef formatOptions
, CFStringRef format
, va_list args
) { 
  90     return SecCFCreateErrorWithFormatAndArguments(errorCode
, kSOSErrorDomain
, previousError
, newError
, formatOptions
, format
, args
); 
  98 static OSStatus 
GenerateECPairImp(int keySize
, CFBooleanRef permanent
, SecKeyRef
* public, SecKeyRef 
*full
) 
 100     static const CFStringRef sTempNameToUse 
= CFSTR("GenerateECPair Temporary Key - Shouldn't be live"); 
 102     CFNumberRef signing_bitsize 
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &keySize
); 
 104     CFDictionaryRef keygen_parameters 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, 
 105                                                                      kSecAttrKeyType
,       kSecAttrKeyTypeEC
, 
 106                                                                      kSecAttrKeySizeInBits
, signing_bitsize
, 
 107                                                                      kSecAttrIsPermanent
,   permanent
, 
 108                                                                      kSecAttrLabel
,         sTempNameToUse
, 
 110     CFReleaseNull(signing_bitsize
); 
 111     OSStatus result 
= SecKeyGeneratePair(keygen_parameters
, public, full
); 
 112     CFReleaseNull(keygen_parameters
); 
 117 OSStatus 
GenerateECPair(int keySize
, SecKeyRef
* public, SecKeyRef 
*full
) 
 119     return GenerateECPairImp(keySize
, kCFBooleanFalse
, public, full
); 
 122 OSStatus 
GeneratePermanentECPair(int keySize
, SecKeyRef
* public, SecKeyRef 
*full
) 
 124     return GenerateECPairImp(keySize
, kCFBooleanTrue
, public, full
); 
 127 static CFStringRef 
SOSCircleCopyDescriptionFromData(CFDataRef data
) 
 129     CFErrorRef error 
= NULL
; 
 130     CFStringRef result 
= NULL
; 
 132     SOSCircleRef circle 
= SOSCircleCreateFromData(kCFAllocatorDefault
, data
, &error
); 
 135         result 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@"), circle
); 
 137     CFReleaseSafe(circle
); 
 142 CFStringRef 
SOSItemsChangedCopyDescription(CFDictionaryRef changes
, bool is_sender
) 
 144     CFMutableStringRef string 
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("<Changes: {\n")); 
 146     CFDictionaryForEach(changes
, ^(const void *key
, const void *value
) { 
 147         CFStringRef value_description 
= NULL
; 
 148         if (isString(key
) && isData(value
)) { 
 149             CFDataRef value_data 
= (CFDataRef
) value
; 
 150             switch (SOSKVSKeyGetKeyType(key
)) { 
 152                     value_description 
= SOSCircleCopyDescriptionFromData(value_data
); 
 155                     value_description 
= CFCopyDescription(value_data
); 
 162         CFStringAppendFormat(string
, NULL
, CFSTR("    '%@' %s %@\n"), 
 164                              is_sender 
? "<=" : "=>", 
 165                              value_description 
? value_description 
: value
); 
 167         CFReleaseNull(value_description
); 
 170     CFStringAppendFormat(string
, NULL
, CFSTR("}")); 
 176 CFStringRef 
SOSCopyIDOfDataBuffer(CFDataRef data
, CFErrorRef 
*error
) { 
 177     const struct ccdigest_info 
* di 
= ccsha1_di(); 
 178     uint8_t digest
[di
->output_size
]; 
 179     char encoded
[2 * di
->output_size
]; // Big enough for base64 encoding. 
 181     ccdigest(di
, CFDataGetLength(data
), CFDataGetBytePtr(data
), digest
); 
 183     size_t length 
= SecBase64Encode(digest
, sizeof(digest
), encoded
, sizeof(encoded
)); 
 184     assert(length 
&& length 
< sizeof(encoded
)); 
 188     return CFStringCreateWithCString(kCFAllocatorDefault
, encoded
, kCFStringEncodingASCII
); 
 191 CFStringRef 
SOSCopyIDOfDataBufferWithLength(CFDataRef data
, CFIndex len
, CFErrorRef 
*error
) { 
 192     CFStringRef retval 
= NULL
; 
 193     CFStringRef tmp 
= SOSCopyIDOfDataBuffer(data
, error
); 
 194     if(tmp
) retval 
= CFStringCreateWithSubstring(kCFAllocatorDefault
, tmp
, CFRangeMake(0, len
)); 
 199 CFStringRef 
SOSCopyIDOfKey(SecKeyRef key
, CFErrorRef 
*error
) { 
 200     CFDataRef publicBytes 
= NULL
; 
 201     CFStringRef result 
= NULL
; 
 202     require_quiet(SecError(SecKeyCopyPublicBytes(key
, &publicBytes
), error
, CFSTR("Failed to export public bytes %@"), key
), fail
); 
 203     result 
= SOSCopyIDOfDataBuffer(publicBytes
, error
); 
 205     CFReleaseNull(publicBytes
); 
 209 CFStringRef 
SOSCopyIDOfKeyWithLength(SecKeyRef key
, CFIndex len
, CFErrorRef 
*error
) { 
 210     CFStringRef retval 
= NULL
; 
 211     CFStringRef tmp 
= SOSCopyIDOfKey(key
, error
); 
 212     if(tmp
) retval 
= CFStringCreateWithSubstring(kCFAllocatorDefault
, tmp
, CFRangeMake(0, len
)); 
 218 CFGiblisGetSingleton(ccec_const_cp_t
, SOSGetBackupKeyCurveParameters
, sBackupKeyCurveParameters
, ^{ 
 219     *sBackupKeyCurveParameters 
= ccec_cp_256(); 
 224 // We're expecting full entropy here, so we just need to stretch 
 225 // via the PBKDF entropy rng. We'll choose a few iterations and no salt 
 226 // since we don't get sent any. 
 228 const int kBackupKeyIterations 
= 20; 
 229 const uint8_t sBackupKeySalt
[] = { 0 }; 
 231 bool SOSPerformWithDeviceBackupFullKey(ccec_const_cp_t cp
, CFDataRef entropy
, CFErrorRef 
*error
, void (^operation
)(ccec_full_ctx_t fullKey
)) 
 234     ccec_full_ctx_decl_cp(cp
, fullKey
); 
 236     require_quiet(SOSGenerateDeviceBackupFullKey(fullKey
, cp
, entropy
, error
), exit
); 
 242     ccec_full_ctx_clear_cp(cp
, fullKey
); 
 248 bool SOSGenerateDeviceBackupFullKey(ccec_full_ctx_t generatedKey
, ccec_const_cp_t cp
, CFDataRef entropy
, CFErrorRef
* error
) 
 252     struct ccrng_pbkdf2_prng_state pbkdf2_prng
; 
 253     const int kBackupKeyMaxBytes 
= 1024; // This may be a function of the cp but will be updated when we use a formally deterministic key generation. 
 255     cc_result 
= ccrng_pbkdf2_prng_init(&pbkdf2_prng
, kBackupKeyMaxBytes
, 
 256                                        CFDataGetLength(entropy
), CFDataGetBytePtr(entropy
), 
 257                                        sizeof(sBackupKeySalt
), sBackupKeySalt
, 
 258                                        kBackupKeyIterations
); 
 259     require_action_quiet(cc_result 
== 0, exit
, SOSErrorCreate(kSOSErrorProcessingFailure
, error
, NULL
, CFSTR("pbkdf rng init failed: %d"), cc_result
)); 
 261     cc_result 
= ccec_compact_generate_key(cp
, (struct ccrng_state 
*) &pbkdf2_prng
, generatedKey
); 
 262     require_action_quiet(cc_result 
== 0, exit
, SOSErrorCreate(kSOSErrorProcessingFailure
, error
, NULL
, CFSTR("Generate key failed: %d"), cc_result
)); 
 266     bzero(&pbkdf2_prng
, sizeof(pbkdf2_prng
)); 
 271 CFDataRef 
SOSCopyDeviceBackupPublicKey(CFDataRef entropy
, CFErrorRef 
*error
) 
 273     CFDataRef result 
= NULL
; 
 274     CFMutableDataRef publicKeyData 
= NULL
; 
 276     ccec_full_ctx_decl_cp(SOSGetBackupKeyCurveParameters(), fullKey
); 
 278     require_quiet(SOSGenerateDeviceBackupFullKey(fullKey
, SOSGetBackupKeyCurveParameters(), entropy
, error
), exit
); 
 280     size_t space 
= ccec_compact_export_size(false, fullKey
); 
 281     publicKeyData 
= CFDataCreateMutableWithScratch(kCFAllocatorDefault
, space
); 
 282     require_quiet(SecAllocationError(publicKeyData
, error
, CFSTR("Mutable data allocation")), exit
); 
 284     ccec_compact_export(false, CFDataGetMutableBytePtr(publicKeyData
), fullKey
); 
 286     CFTransferRetained(result
, publicKeyData
); 
 289     CFReleaseNull(publicKeyData
); 
 294 CFDataRef 
SOSDateCreate(void) { 
 295     CFDateRef now 
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent()); 
 296     size_t bufsiz 
= der_sizeof_date(now
, NULL
); 
 298     der_encode_date(now
, NULL
, buf
, buf
+bufsiz
); 
 300     return CFDataCreate(NULL
, buf
, bufsiz
); 
 304 CFDataRef 
CFDataCreateWithDER(CFAllocatorRef allocator
, CFIndex size
, uint8_t*(^operation
)(size_t size
, uint8_t *buffer
)) { 
 305     __block CFMutableDataRef result 
= NULL
; 
 306     if(!size
) return NULL
; 
 307     if((result 
= CFDataCreateMutableWithScratch(allocator
, size
)) == NULL
) return NULL
; 
 308     uint8_t *ptr 
= CFDataGetMutableBytePtr(result
); 
 309     uint8_t *derptr 
= operation(size
, ptr
); 
 310     if(derptr 
== ptr
) return result
; // most probable case 
 311     if(!derptr 
|| derptr 
< ptr
) { // DER op failed  - or derptr ended up prior to allocated buffer 
 312         CFReleaseNull(result
); 
 313     } else if(derptr 
> ptr
) { // This is a possible case where we don't end up using the entire allocated buffer 
 314         size_t diff 
= derptr 
- ptr
; // The unused space ends up being the beginning of the allocation 
 315         CFDataDeleteBytes(result
, CFRangeMake(0, diff
));