2 * Copyright (c) 2013-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@
28 #include <AssertMacros.h>
29 #include "SOSAccountPriv.h"
32 #include <utilities/SecCFWrappers.h>
33 #include <utilities/SecCoreCrypto.h>
34 #include <utilities/SecBuffer.h>
36 #include <Security/SecureObjectSync/SOSKVSKeys.h>
37 #include <SOSPeerInfoDER.h>
39 #include <Security/SecureObjectSync/SOSTransport.h>
41 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
42 #include <os/state_private.h>
44 #include "SOSAccountPriv.h"
45 #define kSecServerPeerInfoAvailable "com.apple.security.fpiAvailable"
48 static SOSAccountRef
SOSAccountCreateFromRemainingDER_v6(CFAllocatorRef allocator
,
49 SOSDataSourceFactoryRef factory
,
51 const uint8_t** der_p
, const uint8_t *der_end
)
53 SOSAccountRef result
= NULL
;
54 SOSAccountRef account
= NULL
;
55 CFArrayRef array
= NULL
;
56 CFDictionaryRef retiredPeers
= NULL
;
57 CFStringRef circle_name
= factory
->copy_name(factory
);
60 CFDictionaryRef decoded_gestalt
= NULL
;
61 *der_p
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListImmutable
, &decoded_gestalt
, error
,
67 account
= SOSAccountCreateBasic(allocator
, decoded_gestalt
, factory
);
68 CFReleaseNull(decoded_gestalt
);
71 *der_p
= der_decode_array(kCFAllocatorDefault
, 0, &array
, error
, *der_p
, der_end
);
73 uint64_t tmp_departure_code
= kSOSNeverAppliedToCircle
;
74 *der_p
= ccder_decode_uint64(&tmp_departure_code
, *der_p
, der_end
);
75 *der_p
= ccder_decode_bool(&account
->user_public_trusted
, *der_p
, der_end
);
76 *der_p
= der_decode_public_bytes(kCFAllocatorDefault
, kSecECDSAAlgorithmID
, &account
->user_public
, error
, *der_p
, der_end
);
77 *der_p
= der_decode_public_bytes(kCFAllocatorDefault
, kSecECDSAAlgorithmID
, &account
->previous_public
, error
, *der_p
, der_end
);
78 *der_p
= der_decode_data_or_null(kCFAllocatorDefault
, &account
->user_key_parameters
, error
, *der_p
, der_end
);
80 *der_p
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListMutableContainers
, (CFDictionaryRef
*) &retiredPeers
, error
, *der_p
, der_end
);
81 require_action_quiet(*der_p
== der_end
, fail
, *der_p
= NULL
);
83 account
->departure_code
= (enum DepartureReason
) tmp_departure_code
;
85 CFDictionaryForEach(retiredPeers
, ^(const void *key
, const void *value
) {
87 SOSPeerInfoRef retiree
= SOSPeerInfoCreateFromData(kCFAllocatorDefault
, NULL
, (CFDataRef
) value
);
89 CFSetAddValue(account
->retirees
, retiree
);
91 CFReleaseNull(retiree
);
95 require_quiet(array
&& *der_p
, fail
);
97 CFArrayForEach(array
, ^(const void *value
) {
98 CFDataRef circleData
= NULL
;
99 CFDataRef fullPeerInfoData
= NULL
;
102 circleData
= (CFDataRef
) value
;
103 } else if (isArray(value
)) {
104 CFArrayRef pair
= (CFArrayRef
) value
;
106 CFTypeRef circleObject
= CFArrayGetValueAtIndex(pair
, 0);
107 CFTypeRef fullPeerInfoObject
= CFArrayGetValueAtIndex(pair
, 1);
109 if (CFArrayGetCount(pair
) == 2 && isData(circleObject
) && isData(fullPeerInfoObject
)) {
110 circleData
= (CFDataRef
) circleObject
;
111 fullPeerInfoData
= (CFDataRef
) fullPeerInfoObject
;
116 SOSCircleRef circle
= SOSCircleCreateFromData(kCFAllocatorDefault
, circleData
, error
);
117 require_quiet(circle
&& CFEqualSafe(circle_name
, SOSCircleGetName(circle
)), fail
);
119 account
->trusted_circle
= CFRetainSafe(circle
);
121 if(fullPeerInfoData
) {
122 account
->my_identity
= SOSFullPeerInfoCreateFromData(kCFAllocatorDefault
, fullPeerInfoData
, NULL
);
126 CFReleaseNull(circle
);
129 CFReleaseNull(array
);
131 require_action_quiet(SOSAccountEnsureFactoryCircles(account
), fail
,
132 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Cannot EnsureFactoryCircles"), (error
!= NULL
) ? *error
: NULL
, error
));
134 result
= CFRetainSafe(account
);
137 CFReleaseNull(account
);
141 static size_t der_sizeof_data_optional(CFDataRef data
)
143 return data
? der_sizeof_data(data
, NULL
) : 0;
147 static uint8_t* der_encode_data_optional(CFDataRef data
, CFErrorRef
*error
,
148 const uint8_t *der
, uint8_t *der_end
)
150 return data
? der_encode_data(data
, error
, der
, der_end
) : der_end
;
154 static const uint8_t* der_decode_data_optional(CFAllocatorRef allocator
, CFOptionFlags mutability
,
155 CFDataRef
* data
, CFErrorRef
*error
,
156 const uint8_t* der
, const uint8_t *der_end
)
158 const uint8_t *dt_end
= der_decode_data(allocator
, mutability
, data
, NULL
, der
, der_end
);
160 return dt_end
? dt_end
: der
;
163 static SOSAccountRef
SOSAccountCreateFromRemainingDER_v7(CFAllocatorRef allocator
,
164 SOSDataSourceFactoryRef factory
,
166 const uint8_t** der_p
, const uint8_t *der_end
)
168 SOSAccountRef account
= NULL
;
169 SOSAccountRef result
= NULL
;
172 CFDictionaryRef decoded_gestalt
= NULL
;
173 *der_p
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListImmutable
, &decoded_gestalt
, error
,
179 account
= SOSAccountCreateBasic(allocator
, decoded_gestalt
, factory
);
180 CFReleaseNull(decoded_gestalt
);
183 account
->trusted_circle
= SOSCircleCreateFromDER(kCFAllocatorDefault
, error
, der_p
, der_end
);
184 *der_p
= der_decode_fullpeer_or_null(kCFAllocatorDefault
, &account
->my_identity
, error
, *der_p
, der_end
);
186 uint64_t tmp_departure_code
= kSOSNeverAppliedToCircle
;
187 *der_p
= ccder_decode_uint64(&tmp_departure_code
, *der_p
, der_end
);
188 *der_p
= ccder_decode_bool(&account
->user_public_trusted
, *der_p
, der_end
);
189 *der_p
= der_decode_public_bytes(kCFAllocatorDefault
, kSecECDSAAlgorithmID
, &account
->user_public
, error
, *der_p
, der_end
);
190 *der_p
= der_decode_public_bytes(kCFAllocatorDefault
, kSecECDSAAlgorithmID
, &account
->previous_public
, error
, *der_p
, der_end
);
191 *der_p
= der_decode_data_or_null(kCFAllocatorDefault
, &account
->user_key_parameters
, error
, *der_p
, der_end
);
192 account
->retirees
= SOSPeerInfoSetCreateFromArrayDER(kCFAllocatorDefault
, &kSOSPeerSetCallbacks
, error
, der_p
, der_end
);
193 *der_p
= der_decode_data_optional(kCFAllocatorDefault
, kCFPropertyListImmutable
, &account
->backup_key
, error
, *der_p
, der_end
);
195 account
->departure_code
= (enum DepartureReason
) tmp_departure_code
;
197 require_action_quiet(*der_p
&& *der_p
== der_end
, fail
,
198 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Didn't consume all bytes v7"), (error
!= NULL
) ? *error
: NULL
, error
));
200 result
= CFRetainSafe(account
);
203 CFReleaseNull(account
);
208 static SOSAccountRef
SOSAccountCreateFromRemainingDER_v8(CFAllocatorRef allocator
,
209 SOSDataSourceFactoryRef factory
,
211 const uint8_t** der_p
, const uint8_t *der_end
)
213 SOSAccountRef account
= NULL
;
214 SOSAccountRef result
= NULL
;
217 CFDictionaryRef decoded_gestalt
= NULL
;
218 *der_p
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListImmutable
, &decoded_gestalt
, error
,
224 account
= SOSAccountCreateBasic(allocator
, decoded_gestalt
, factory
);
225 CFReleaseNull(decoded_gestalt
);
227 CFReleaseNull(account
->trusted_circle
);
228 account
->trusted_circle
= SOSCircleCreateFromDER(kCFAllocatorDefault
, error
, der_p
, der_end
);
229 CFReleaseNull(account
->my_identity
);
230 *der_p
= der_decode_fullpeer_or_null(kCFAllocatorDefault
, &account
->my_identity
, error
, *der_p
, der_end
);
232 uint64_t tmp_departure_code
= kSOSNeverAppliedToCircle
;
233 *der_p
= ccder_decode_uint64(&tmp_departure_code
, *der_p
, der_end
);
234 *der_p
= ccder_decode_bool(&account
->user_public_trusted
, *der_p
, der_end
);
235 *der_p
= der_decode_public_bytes(kCFAllocatorDefault
, kSecECDSAAlgorithmID
, &account
->user_public
, error
, *der_p
, der_end
);
236 *der_p
= der_decode_public_bytes(kCFAllocatorDefault
, kSecECDSAAlgorithmID
, &account
->previous_public
, error
, *der_p
, der_end
);
237 *der_p
= der_decode_data_or_null(kCFAllocatorDefault
, &account
->user_key_parameters
, error
, *der_p
, der_end
);
238 CFReleaseNull(account
->retirees
);
239 account
->retirees
= SOSPeerInfoSetCreateFromArrayDER(kCFAllocatorDefault
, &kSOSPeerSetCallbacks
, error
, der_p
, der_end
);
240 *der_p
= der_decode_data_optional(kCFAllocatorDefault
, kCFPropertyListImmutable
, &account
->backup_key
, error
, *der_p
, der_end
);
242 CFDictionaryRef expansion
= NULL
;
243 *der_p
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListImmutable
, &expansion
, error
,
249 CFReleaseNull(account
->expansion
);
250 account
->expansion
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, expansion
);
251 CFReleaseNull(expansion
);
254 account
->departure_code
= (enum DepartureReason
) tmp_departure_code
;
256 require_action_quiet(*der_p
&& *der_p
== der_end
, fail
,
257 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Didn't consume all bytes v7"), (error
!= NULL
) ? *error
: NULL
, error
));
259 result
= CFRetainSafe(account
);
262 CFReleaseNull(account
);
267 // Version History for Account
269 // 1-5 - InnsbruckTaos/Cab; Never supported even for upgrading.
270 // 6 - First version used in the field.
271 // 7 - One Circle version
272 // 8 - Adding expansion dictionary
275 #define CURRENT_ACCOUNT_PERSISTENT_VERSION 8
277 SOSAccountRef
SOSAccountCreateFromDER(CFAllocatorRef allocator
,
278 SOSDataSourceFactoryRef factory
,
280 const uint8_t** der_p
, const uint8_t *der_end
)
282 SOSAccountRef account
= NULL
;
283 SOSAccountRef result
= NULL
;
284 uint64_t version
= 0;
286 const uint8_t *sequence_end
;
287 *der_p
= ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, &sequence_end
, *der_p
, der_end
);
288 *der_p
= ccder_decode_uint64(&version
, *der_p
, sequence_end
);
289 require_action_quiet(*der_p
, errOut
,
290 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Version parsing failed"), (error
!= NULL
) ? *error
: NULL
, error
));
293 case CURRENT_ACCOUNT_PERSISTENT_VERSION
:
294 account
= SOSAccountCreateFromRemainingDER_v8(allocator
, factory
, error
, der_p
, sequence_end
);
298 account
= SOSAccountCreateFromRemainingDER_v7(allocator
, factory
, error
, der_p
, sequence_end
);
302 account
= SOSAccountCreateFromRemainingDER_v6(allocator
, factory
, error
, der_p
, sequence_end
);
306 SOSCreateErrorWithFormat(kSOSErrorBadFormat
, (error
!= NULL
) ? *error
: NULL
, error
,
307 NULL
, CFSTR("Unsupported version (%llu)"), version
);
311 require_quiet(account
, errOut
);
313 require_quiet(*der_p
&& *der_p
== sequence_end
, errOut
);
315 /* I may not always have an identity, but when I do, it has a private key */
316 if(account
->my_identity
) {
317 require_action_quiet(SOSFullPeerInfoPrivKeyExists(account
->my_identity
), errOut
, secnotice("account", "No private key associated with my_identity, resetting"));
318 notify_post(kSecServerPeerInfoAvailable
);
319 if(account
->deviceID
)
320 SOSFullPeerInfoUpdateDeviceID(account
->my_identity
, account
->deviceID
, error
);
323 require_action_quiet(SOSAccountEnsureFactoryCircles(account
), errOut
,
324 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Cannot EnsureFactoryCircles"), (error
!= NULL
) ? *error
: NULL
, error
));
326 SOSPeerInfoRef myPI
= SOSAccountGetMyPeerInfo(account
);
328 if(SOSAccountHasCompletedInitialSync(account
)) {
329 CFMutableSetRef viewsToEnsure
= SOSViewCopyViewSet(kViewSetAlwaysOn
);
331 // Previous version PeerInfo if we were syncing legacy keychain, ensure we include those legacy views.
332 if(!SOSPeerInfoVersionIsCurrent(myPI
) && SOSAccountIsInCircle(account
, NULL
)) {
333 CFSetRef V0toAdd
= SOSViewCopyViewSet(kViewSetV0
);
334 CFSetUnion(viewsToEnsure
, V0toAdd
);
335 CFReleaseNull(V0toAdd
);
338 SOSAccountUpdateFullPeerInfo(account
, viewsToEnsure
, SOSViewsGetV0ViewSet()); // We don't permit V0 view proper, only sub-views
339 CFReleaseNull(viewsToEnsure
);
342 SOSPeerInfoRef oldPI
= myPI
;
343 // if UpdateFullPeerInfo did something - we need to make sure we have the right Ref
344 myPI
= SOSAccountGetMyPeerInfo(account
);
345 if(oldPI
!= myPI
) secnotice("canary", "Caught spot where PIs differ in account setup");
346 CFStringRef transportTypeInflatedFromDER
= SOSPeerInfoCopyTransportType(myPI
);
347 if (CFStringCompare(transportTypeInflatedFromDER
, CFSTR("IDS"), 0) == 0 || CFStringCompare(transportTypeInflatedFromDER
, CFSTR("KVS"), 0) == 0)
348 SOSFullPeerInfoUpdateTransportType(account
->my_identity
, SOSTransportMessageTypeIDSV2
, NULL
); //update the transport type to the current IDS V2 type
350 CFReleaseNull(transportTypeInflatedFromDER
);
353 SOSAccountWithTransactionSync(account
, ^(SOSAccountRef account
, SOSAccountTransactionRef txn
) {
354 account
->key_interests_need_updating
= true;
357 result
= CFRetainSafe(account
);
360 CFReleaseNull(account
);
364 SOSAccountRef
SOSAccountCreateFromData(CFAllocatorRef allocator
, CFDataRef circleData
,
365 SOSDataSourceFactoryRef factory
,
368 size_t size
= CFDataGetLength(circleData
);
369 const uint8_t *der
= CFDataGetBytePtr(circleData
);
370 SOSAccountRef account
= SOSAccountCreateFromDER(allocator
, factory
,
376 CFMutableSetRef
SOSPeerInfoSetCreateFromArrayDER(CFAllocatorRef allocator
, const CFSetCallBacks
*callbacks
, CFErrorRef
* error
,
377 const uint8_t** der_p
, const uint8_t *der_end
);
378 size_t SOSPeerInfoSetGetDEREncodedArraySize(CFSetRef pia
, CFErrorRef
*error
);
379 uint8_t* SOSPeerInfoSetEncodeToArrayDER(CFSetRef pia
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
);
381 size_t SOSAccountGetDEREncodedSize(SOSAccountRef account
, CFErrorRef
*error
)
383 size_t sequence_size
= 0;
384 uint64_t version
= CURRENT_ACCOUNT_PERSISTENT_VERSION
;
386 require_quiet(accumulate_size(&sequence_size
, ccder_sizeof_uint64(version
)), fail
);
387 require_quiet(accumulate_size(&sequence_size
, der_sizeof_dictionary(account
->gestalt
, error
)), fail
);
388 require_quiet(accumulate_size(&sequence_size
, SOSCircleGetDEREncodedSize(account
->trusted_circle
, error
)), fail
);
389 require_quiet(accumulate_size(&sequence_size
, der_sizeof_fullpeer_or_null(account
->my_identity
, error
)), fail
);
390 require_quiet(accumulate_size(&sequence_size
, ccder_sizeof_uint64(account
->departure_code
)), fail
);
391 require_quiet(accumulate_size(&sequence_size
, ccder_sizeof_bool(account
->user_public_trusted
, error
)), fail
);
392 require_quiet(accumulate_size(&sequence_size
, der_sizeof_public_bytes(account
->user_public
, error
)), fail
);
393 require_quiet(accumulate_size(&sequence_size
, der_sizeof_public_bytes(account
->previous_public
, error
)), fail
);
394 require_quiet(accumulate_size(&sequence_size
, der_sizeof_data_or_null(account
->user_key_parameters
, error
)), fail
);
395 require_quiet(accumulate_size(&sequence_size
, SOSPeerInfoSetGetDEREncodedArraySize(account
->retirees
, error
)), fail
);
396 accumulate_size(&sequence_size
, der_sizeof_data_optional(account
->backup_key
));
397 require_quiet(accumulate_size(&sequence_size
, der_sizeof_dictionary(account
->expansion
, error
)), fail
);
399 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, sequence_size
);
402 SecCFDERCreateError(kSecDERErrorUnknownEncoding
, CFSTR("don't know how to encode"), NULL
, error
);
406 uint8_t* SOSAccountEncodeToDER(SOSAccountRef account
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
)
408 uint64_t version
= CURRENT_ACCOUNT_PERSISTENT_VERSION
;
409 der_end
= ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, der_end
, der
,
410 ccder_encode_uint64(version
, der
,
411 der_encode_dictionary(account
->gestalt
, error
, der
,
412 SOSCircleEncodeToDER(account
->trusted_circle
, error
, der
,
413 der_encode_fullpeer_or_null(account
->my_identity
, error
, der
,
414 ccder_encode_uint64(account
->departure_code
, der
,
415 ccder_encode_bool(account
->user_public_trusted
, der
,
416 der_encode_public_bytes(account
->user_public
, error
, der
,
417 der_encode_public_bytes(account
->previous_public
, error
, der
,
418 der_encode_data_or_null(account
->user_key_parameters
, error
, der
,
419 SOSPeerInfoSetEncodeToArrayDER(account
->retirees
, error
, der
,
420 der_encode_data_optional(account
->backup_key
, error
, der
,
421 der_encode_dictionary(account
->expansion
, error
, der
,
424 der_end
)))))))))))));
429 /************************/
431 CFDataRef
SOSAccountCopyEncodedData(SOSAccountRef account
, CFAllocatorRef allocator
, CFErrorRef
*error
)
433 return CFDataCreateWithDER(kCFAllocatorDefault
, SOSAccountGetDEREncodedSize(account
, error
), ^uint8_t*(size_t size
, uint8_t *buffer
) {
434 return SOSAccountEncodeToDER(account
, error
, buffer
, (uint8_t *) buffer
+ size
);