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
));