1/*
2 * Created by Michael Brouwer on 6/22/12.
3 * Copyright 2012 Apple Inc. All Rights Reserved.
4 */
5
6/*
7 * SOSAccount.c -  Implementation of the secure object syncing account.
8 * An account contains a SOSCircle for each protection domain synced.
9 */
10
11#include <SecureObjectSync/SOSInternal.h>
12#include <SecureObjectSync/SOSAccount.h>
13#include <SecureObjectSync/SOSCircle.h>
14#include <SecureObjectSync/SOSCloudCircle.h>
15#include <SecureObjectSync/SOSEngine.h>
16#include <SecureObjectSync/SOSPeer.h>
17#include <SecureObjectSync/SOSFullPeerInfo.h>
18#include <SecureObjectSync/SOSPeerInfo.h>
19#include <SecureObjectSync/SOSPeerInfoInternal.h>
20#include <SecureObjectSync/SOSUserKeygen.h>
21#include <Security/SecKeyPriv.h>
22#include <Security/SecItemPriv.h>
23#include <CoreFoundation/CFArray.h>
24#include <dispatch/dispatch.h>
25#include <stdlib.h>
26#include <assert.h>
27#include <AssertMacros.h>
28#include <utilities/SecCFWrappers.h>
29
30#include <utilities/der_plist.h>
31#include <utilities/der_plist_internal.h>
32#include <utilities/iOSforOSX.h>
33
34#include <utilities/SecAKSWrappers.h>
35
36#include <corecrypto/ccder.h>
37
38#include <securityd/SOSCloudCircleServer.h>
39#include <securityd/SecDbItem.h> // For SecError
40
41#include <utilities/debugging.h>
42#include <utilities/iCloudKeychainTrace.h>
43
44#include <notify.h>
45
46static CFStringRef kicloud_identity_name = CFSTR("Cloud Identity");
47
48//
49// Forward statics.
50//
51
52static bool SOSAccountHandleUpdateCircle(SOSAccountRef account, SOSCircleRef newCircle, bool writeUpdate, bool initialSync, CFErrorRef *error);
53
54//
55// DER Encoding utilities
56//
57
58//
59// Encodes data or a zero length data
60//
61static size_t der_sizeof_data_or_null(CFDataRef data, CFErrorRef* error)
62{
63	if (data) {
64		return der_sizeof_data(data, error);
65	} else {
66		return der_sizeof_null(kCFNull, error);
67	}
68}
69
70static uint8_t* der_encode_data_or_null(CFDataRef data, CFErrorRef* error, const uint8_t* der, uint8_t* der_end)
71{
72    if (data) {
73		return der_encode_data(data, error, der, der_end);
74	} else {
75		return der_encode_null(kCFNull, error, der, der_end);
76	}
77}
78
79
80static const uint8_t* der_decode_data_or_null(CFAllocatorRef allocator, CFDataRef* data,
81                                              CFErrorRef* error,
82                                              const uint8_t* der, const uint8_t* der_end)
83{
84    CFTypeRef value = NULL;
85	der = der_decode_plist(allocator, 0, &value, error, der, der_end);
86	if (value && CFGetTypeID(value) != CFDataGetTypeID()) {
87		CFReleaseNull(value);
88	}
89	if (data) {
90		*data = value;
91	}
92	return der;
93}
94
95
96//
97// Mark: public_bytes encode/decode
98//
99
100static size_t der_sizeof_public_bytes(SecKeyRef publicKey, CFErrorRef* error)
101{
102    CFDataRef publicData = NULL;
103
104    if (publicKey)
105        SecKeyCopyPublicBytes(publicKey, &publicData);
106
107    size_t size = der_sizeof_data_or_null(publicData, error);
108
109    CFReleaseNull(publicData);
110
111    return size;
112}
113
114static uint8_t* der_encode_public_bytes(SecKeyRef publicKey, CFErrorRef* error, const uint8_t* der, uint8_t* der_end)
115{
116    CFDataRef publicData = NULL;
117
118    if (publicKey)
119        SecKeyCopyPublicBytes(publicKey, &publicData);
120
121    uint8_t *result = der_encode_data_or_null(publicData, error, der, der_end);
122
123    CFReleaseNull(publicData);
124
125    return result;
126}
127
128static const uint8_t* der_decode_public_bytes(CFAllocatorRef allocator, CFIndex algorithmID, SecKeyRef* publicKey, CFErrorRef* error, const uint8_t* der, const uint8_t* der_end)
129{
130    CFDataRef dataFound = NULL;
131    der = der_decode_data_or_null(allocator, &dataFound, error, der, der_end);
132
133    if (der && dataFound && publicKey) {
134        *publicKey = SecKeyCreateFromPublicData(allocator, algorithmID, dataFound);
135    }
136    CFReleaseNull(dataFound);
137
138    return der;
139}
140
141
142//
143// Cloud Paramters encode/decode
144//
145
146static size_t der_sizeof_cloud_parameters(SecKeyRef publicKey, CFDataRef paramters, CFErrorRef* error)
147{
148    size_t public_key_size = der_sizeof_public_bytes(publicKey, error);
149    size_t parameters_size = der_sizeof_data_or_null(paramters, error);
150
151    return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, public_key_size + parameters_size);
152}
153
154static uint8_t* der_encode_cloud_parameters(SecKeyRef publicKey, CFDataRef paramters, CFErrorRef* error,
155                                            const uint8_t* der, uint8_t* der_end)
156{
157    uint8_t* original_der_end = der_end;
158
159    return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, original_der_end, der,
160                                       der_encode_public_bytes(publicKey, error, der,
161                                                               der_encode_data_or_null(paramters, error, der, der_end)));
162}
163
164static const uint8_t* der_decode_cloud_parameters(CFAllocatorRef allocator,
165                                                  CFIndex algorithmID, SecKeyRef* publicKey,
166                                                  CFDataRef *parameters,
167                                                  CFErrorRef* error,
168                                                  const uint8_t* der, const uint8_t* der_end)
169{
170    const uint8_t *sequence_end;
171    der = ccder_decode_sequence_tl(&sequence_end, der, der_end);
172    der = der_decode_public_bytes(allocator, algorithmID, publicKey, error, der, sequence_end);
173    der = der_decode_data_or_null(allocator, parameters, error, der, sequence_end);
174
175    return der;
176}
177
178
179//
180// bool encoding/decoding
181//
182
183
184static const uint8_t* ccder_decode_bool(bool* boolean, const uint8_t* der, const uint8_t *der_end)
185{
186    if (NULL == der)
187        return NULL;
188
189    size_t payload_size = 0;
190    const uint8_t *payload = ccder_decode_tl(CCDER_BOOLEAN, &payload_size, der, der_end);
191
192    if (NULL == payload || (der_end - payload) < 1 || payload_size != 1) {
193        return NULL;
194    }
195
196    if (boolean)
197        *boolean = (*payload != 0);
198
199    return payload + payload_size;
200}
201
202
203static size_t ccder_sizeof_bool(bool value __unused, CFErrorRef *error)
204{
205    return ccder_sizeof(CCDER_BOOLEAN, 1);
206}
207
208
209static uint8_t* ccder_encode_bool(bool value, const uint8_t *der, uint8_t *der_end)
210{
211    uint8_t value_byte = value;
212
213    return ccder_encode_tl(CCDER_BOOLEAN, 1, der,
214           ccder_encode_body(1, &value_byte, der, der_end));
215}
216
217struct __OpaqueSOSAccount {
218    CFRuntimeBase           _base;
219
220    dispatch_queue_t        queue;
221
222    CFDictionaryRef         gestalt;
223
224    CFMutableDictionaryRef  circle_identities;
225    CFMutableDictionaryRef  circles;
226    CFMutableDictionaryRef  retired_peers;
227
228    bool      user_public_trusted;
229    CFDataRef user_key_parameters;
230    SecKeyRef user_public;
231    SecKeyRef previous_public;
232    enum DepartureReason    departure_code;
233
234    // Non-persistent data
235
236    SOSDataSourceFactoryRef factory;
237    SecKeyRef _user_private;
238    dispatch_source_t user_private_timer;
239    int               lock_notification_token;
240
241    // Live Notification
242    CFMutableArrayRef       change_blocks;
243
244    SOSAccountKeyInterestBlock        update_interest_block;
245    SOSAccountDataUpdateBlock         update_block;
246    SOSAccountMessageProcessedBlock   processed_message_block;
247
248    CFMutableDictionaryRef  deferred_updates;
249
250    CFMutableDictionaryRef  pending_changes;
251};
252
253CFGiblisWithCompareFor(SOSAccount);
254
255static inline bool SOSAccountHasLeft(SOSAccountRef account) {
256    switch(account->departure_code) {
257        case kSOSWithdrewMembership: /* Fallthrough */
258        case kSOSMembershipRevoked: /* Fallthrough */
259        case kSOSLeftUntrustedCircle:
260            return true;
261        case kSOSNeverAppliedToCircle: /* Fallthrough */
262        case kSOSNeverLeftCircle: /* Fallthrough */
263        default:
264            return false;
265    }
266}
267
268// Private static functions.
269
270static bool SOSUpdateKeyInterest(SOSAccountRef account, bool getNewKeysOnly, CFErrorRef *error);
271
272static bool SOSAccountEnsureFactoryCircles(SOSAccountRef a)
273{
274    bool result = false;
275    if (a)
276    {
277        require(a->factory, xit);
278        CFArrayRef circle_names = a->factory->copy_names(a->factory);
279        require(circle_names, xit);
280        CFArrayForEach(circle_names, ^(const void*name) {
281            if (isString(name))
282                SOSAccountEnsureCircle(a, (CFStringRef)name, NULL);
283        });
284
285        CFReleaseNull(circle_names);
286        result = true;
287    }
288xit:
289    return result;
290}
291
292static SOSAccountRef SOSAccountCreateBasic(CFAllocatorRef allocator,
293                                           CFDictionaryRef gestalt,
294                                           SOSDataSourceFactoryRef factory,
295                                           SOSAccountKeyInterestBlock interest_block,
296                                           SOSAccountDataUpdateBlock update_block) {
297    SOSAccountRef a = CFTypeAllocate(SOSAccount, struct __OpaqueSOSAccount, allocator);
298
299    a->queue = dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL);
300
301    a->gestalt = gestalt;
302    CFRetain(a->gestalt);
303
304    a->circles = CFDictionaryCreateMutableForCFTypes(allocator);
305    a->circle_identities = CFDictionaryCreateMutableForCFTypes(allocator);
306    a->retired_peers = CFDictionaryCreateMutableForCFTypes(allocator);
307
308    a->factory = factory; // We adopt the factory. kthanksbai.
309
310    a->change_blocks = CFArrayCreateMutableForCFTypes(allocator);
311
312    a->update_interest_block = Block_copy(interest_block);
313    a->update_block = Block_copy(update_block);
314
315    a->pending_changes = CFDictionaryCreateMutableForCFTypes(allocator);
316    a->departure_code = kSOSNeverAppliedToCircle;
317
318    return a;
319}
320
321
322static SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircleNamedIfPresent(SOSAccountRef account, CFStringRef name, CFErrorRef *error) {
323    if (CFDictionaryGetValue(account->circles, name) == NULL) {
324        SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("No circle named '%@'"), name);
325        return NULL;
326    }
327
328    return (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name);
329}
330
331
332static void SOSAccountForEachKnownCircle(SOSAccountRef account,
333                                         void (^handle_incompatible)(CFStringRef name),
334                                         void (^handle_no_peer)(SOSCircleRef circle),
335                                         void (^handle_peer)(SOSCircleRef circle, SOSFullPeerInfoRef full_peer)) {
336    CFDictionaryForEach(account->circles, ^(const void *key, const void *value) {
337        if (isNull(value)) {
338            if (handle_incompatible)
339                handle_incompatible((CFStringRef)key);
340        } else {
341            SOSCircleRef circle = (SOSCircleRef) value;
342            CFRetainSafe(circle);
343            SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, SOSCircleGetName(circle), NULL);
344            if (!fpi) {
345                if (handle_no_peer)
346                    handle_no_peer(circle);
347            } else {
348                CFRetainSafe(fpi);
349                if (handle_peer)
350                    handle_peer(circle, fpi);
351                CFReleaseSafe(fpi);
352            }
353            CFReleaseSafe(circle);
354        }
355    });
356}
357
358
359bool SOSAccountUpdateGestalt(SOSAccountRef account, CFDictionaryRef new_gestalt)
360{
361    if (CFEqual(new_gestalt, account->gestalt))
362        return false;
363
364    SOSAccountForEachKnownCircle(account, NULL, NULL, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) {
365        if (SOSFullPeerInfoUpdateGestalt(full_peer, new_gestalt, NULL)) {
366            SOSAccountModifyCircle(account, SOSCircleGetName(circle),
367                                   NULL, ^(SOSCircleRef circle_to_change) {
368                                       (void) SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(full_peer));
369                                   });
370        };
371    });
372
373    CFReleaseNull(account->gestalt);
374    account->gestalt = new_gestalt;
375    CFRetain(account->gestalt);
376
377    return true;
378}
379
380SOSAccountRef SOSAccountCreate(CFAllocatorRef allocator,
381                               CFDictionaryRef gestalt,
382                               SOSDataSourceFactoryRef factory,
383                               SOSAccountKeyInterestBlock interest_block,
384                               SOSAccountDataUpdateBlock update_block) {
385    SOSAccountRef a = SOSAccountCreateBasic(allocator, gestalt, factory, interest_block, update_block);
386
387    SOSAccountEnsureFactoryCircles(a);
388
389    return a;
390}
391
392static void SOSAccountDestroy(CFTypeRef aObj) {
393    SOSAccountRef a = (SOSAccountRef) aObj;
394
395    if (a->factory)
396        a->factory->release(a->factory);
397
398    CFReleaseNull(a->gestalt);
399    CFReleaseNull(a->circle_identities);
400    CFReleaseNull(a->circles);
401    CFReleaseNull(a->retired_peers);
402
403    a->user_public_trusted = false;
404    CFReleaseNull(a->user_public);
405    CFReleaseNull(a->user_key_parameters);
406
407    SOSAccountPurgePrivateCredential(a);
408    CFReleaseNull(a->previous_public);
409
410    CFReleaseNull(a->change_blocks);
411    Block_release(a->update_interest_block);
412    Block_release(a->update_block);
413    CFReleaseNull(a->processed_message_block);
414    CFReleaseNull(a->pending_changes);
415    CFReleaseNull(a->deferred_updates);
416    a->departure_code = kSOSNeverAppliedToCircle;
417
418    dispatch_release(a->queue);
419}
420
421static void SOSAccountSetToNew(SOSAccountRef a) {
422    CFAllocatorRef allocator = CFGetAllocator(a);
423    CFReleaseNull(a->circle_identities);
424    CFReleaseNull(a->circles);
425    CFReleaseNull(a->retired_peers);
426
427    CFReleaseNull(a->user_key_parameters);
428    CFReleaseNull(a->user_public);
429    CFReleaseNull(a->previous_public);
430    CFReleaseNull(a->_user_private);
431
432    CFReleaseNull(a->pending_changes);
433    CFReleaseNull(a->deferred_updates);
434
435    a->user_public_trusted = false;
436    a->departure_code = kSOSNeverAppliedToCircle;
437    a->user_private_timer = 0;
438    a->lock_notification_token = 0;
439
440    // keeping gestalt;
441    // keeping factory;
442    // Live Notification
443    // change_blocks;
444    // update_interest_block;
445    // update_block;
446
447    a->circles = CFDictionaryCreateMutableForCFTypes(allocator);
448    a->circle_identities = CFDictionaryCreateMutableForCFTypes(allocator);
449    a->retired_peers = CFDictionaryCreateMutableForCFTypes(allocator);
450    a->pending_changes = CFDictionaryCreateMutableForCFTypes(allocator);
451
452    SOSAccountEnsureFactoryCircles(a);
453}
454
455
456static CFStringRef SOSAccountCopyDescription(CFTypeRef aObj) {
457    SOSAccountRef a = (SOSAccountRef) aObj;
458
459    return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSAccount@%p: Gestalt: %@\n Circles: %@ CircleIDs: %@>"), a, a->gestalt, a->circles, a->circle_identities);
460}
461
462static Boolean SOSAccountCompare(CFTypeRef lhs, CFTypeRef rhs)
463{
464    SOSAccountRef laccount = (SOSAccountRef) lhs;
465    SOSAccountRef raccount = (SOSAccountRef) rhs;
466
467    return CFEqual(laccount->gestalt, raccount->gestalt)
468        && CFEqual(laccount->circles, raccount->circles)
469        && CFEqual(laccount->circle_identities, raccount->circle_identities);
470    //  ??? retired_peers
471}
472
473#if OLD_CODERS_SUPPORTED
474
475//
476// MARK: Persistent Encode decode
477//
478SOSAccountRef SOSAccountCreateFromDER_V1(CFAllocatorRef allocator,
479                                      SOSDataSourceFactoryRef factory,
480                                      SOSAccountKeyInterestBlock interest_block,
481                                      SOSAccountDataUpdateBlock update_block,
482                                      CFErrorRef* error,
483                                      const uint8_t** der_p, const uint8_t *der_end)
484{
485    SOSAccountRef account = NULL;
486
487    const uint8_t *sequence_end;
488    *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
489
490    {
491        CFDictionaryRef decoded_gestalt = NULL;
492        *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error,
493                                       *der_p, der_end);
494
495        if (*der_p == 0)
496            return NULL;
497
498        account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory, interest_block, update_block);
499        CFReleaseNull(decoded_gestalt);
500    }
501
502    CFArrayRef array = NULL;
503    *der_p = der_decode_array(kCFAllocatorDefault, 0, &array, error, *der_p, sequence_end);
504
505    *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, sequence_end);
506    *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, sequence_end);
507    *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, sequence_end);
508    *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *) &account->retired_peers, error, *der_p, sequence_end);
509    if (*der_p != sequence_end)
510        *der_p = NULL;
511
512    __block bool success = true;
513
514    require_quiet(array && *der_p, fail);
515
516    CFArrayForEach(array, ^(const void *value) {
517        if (success) {
518            if (isString(value)) {
519                CFDictionaryAddValue(account->circles, value, kCFNull);
520            } else {
521                CFDataRef circleData = NULL;
522                CFDataRef fullPeerInfoData = NULL;
523
524                if (isData(value)) {
525                    circleData = (CFDataRef) value;
526                } else if (isArray(value)) {
527                    CFArrayRef pair = (CFArrayRef) value;
528
529                    CFTypeRef circleObject = CFArrayGetValueAtIndex(pair, 0);
530                    CFTypeRef fullPeerInfoObject = CFArrayGetValueAtIndex(pair, 1);
531
532                    if (CFArrayGetCount(pair) == 2 && isData(circleObject) && isData(fullPeerInfoObject)) {
533                        circleData = (CFDataRef) circleObject;
534                        fullPeerInfoData = (CFDataRef) fullPeerInfoObject;
535                    }
536                }
537
538                if (circleData) {
539                    SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, circleData, error);
540                    require_action_quiet(circle, fail, success = false);
541
542                    CFStringRef circleName = SOSCircleGetName(circle);
543                    CFDictionaryAddValue(account->circles, circleName, circle);
544
545                    if (fullPeerInfoData) {
546                        SOSFullPeerInfoRef full_peer = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, error);
547                        require_action_quiet(full_peer, fail, success = false);
548
549                        CFDictionaryAddValue(account->circle_identities, circleName, full_peer);
550                        CFReleaseNull(full_peer);
551                    }
552                fail:
553                    CFReleaseNull(circle);
554                }
555            }
556        }
557    });
558    CFReleaseNull(array);
559
560    require_quiet(success, fail);
561    require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail,
562                         SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error));
563
564    return account;
565
566fail:
567    // Create a default error if we don't have one:
568    SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Account DER"), NULL, error);
569    CFReleaseNull(account);
570    return NULL;
571}
572
573SOSAccountRef SOSAccountCreateFromDER_V2(CFAllocatorRef allocator,
574                                         SOSDataSourceFactoryRef factory,
575                                         SOSAccountKeyInterestBlock interest_block,
576                                         SOSAccountDataUpdateBlock update_block,
577                                         CFErrorRef* error,
578                                         const uint8_t** der_p, const uint8_t *der_end)
579{
580    SOSAccountRef account = NULL;
581    const uint8_t *dersave = *der_p;
582    const uint8_t *derend = der_end;
583
584    const uint8_t *sequence_end;
585    *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
586
587    {
588        CFDictionaryRef decoded_gestalt = NULL;
589        *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error,
590                                       *der_p, der_end);
591
592        if (*der_p == 0)
593            return NULL;
594
595        account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory, interest_block, update_block);
596        CFReleaseNull(decoded_gestalt);
597    }
598
599    CFArrayRef array = NULL;
600    *der_p = der_decode_array(kCFAllocatorDefault, 0, &array, error, *der_p, sequence_end);
601
602    uint64_t tmp_departure_code = kSOSNeverAppliedToCircle;
603    *der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, sequence_end);
604    *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, sequence_end);
605    *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, sequence_end);
606    *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, sequence_end);
607    *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *) &account->retired_peers, error, *der_p, sequence_end);
608    if (*der_p != sequence_end)
609        *der_p = NULL;
610    account->departure_code = (enum DepartureReason) tmp_departure_code;
611
612    __block bool success = true;
613
614    require_quiet(array && *der_p, fail);
615
616    CFArrayForEach(array, ^(const void *value) {
617        if (success) {
618            if (isString(value)) {
619                CFDictionaryAddValue(account->circles, value, kCFNull);
620            } else {
621                CFDataRef circleData = NULL;
622                CFDataRef fullPeerInfoData = NULL;
623
624                if (isData(value)) {
625                    circleData = (CFDataRef) value;
626                } else if (isArray(value)) {
627                    CFArrayRef pair = (CFArrayRef) value;
628
629                    CFTypeRef circleObject = CFArrayGetValueAtIndex(pair, 0);
630                    CFTypeRef fullPeerInfoObject = CFArrayGetValueAtIndex(pair, 1);
631
632                    if (CFArrayGetCount(pair) == 2 && isData(circleObject) && isData(fullPeerInfoObject)) {
633                        circleData = (CFDataRef) circleObject;
634                        fullPeerInfoData = (CFDataRef) fullPeerInfoObject;
635                    }
636                }
637
638                if (circleData) {
639                    SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, circleData, error);
640                    require_action_quiet(circle, fail, success = false);
641
642                    CFStringRef circleName = SOSCircleGetName(circle);
643                    CFDictionaryAddValue(account->circles, circleName, circle);
644
645                    if (fullPeerInfoData) {
646                        SOSFullPeerInfoRef full_peer = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, error);
647                        require_action_quiet(full_peer, fail, success = false);
648
649                        CFDictionaryAddValue(account->circle_identities, circleName, full_peer);
650                    }
651                fail:
652                    CFReleaseNull(circle);
653                }
654            }
655        }
656    });
657    CFReleaseNull(array);
658
659    require_quiet(success, fail);
660    require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail,
661                         SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error));
662
663    return account;
664
665fail:
666    // Create a default error if we don't have one:
667    account->factory = NULL; // give the factory back.
668    CFReleaseNull(account);
669    // Try the der inflater from the previous release.
670    account = SOSAccountCreateFromDER_V1(allocator, factory, interest_block, update_block, error, &dersave, derend);
671    if(account) account->departure_code = kSOSNeverAppliedToCircle;
672    return account;
673}
674
675#endif /* OLD_CODERS_SUPPORTED */
676
677#define CURRENT_ACCOUNT_PERSISTENT_VERSION 6
678
679SOSAccountRef SOSAccountCreateFromDER(CFAllocatorRef allocator,
680                                         SOSDataSourceFactoryRef factory,
681                                         SOSAccountKeyInterestBlock interest_block,
682                                         SOSAccountDataUpdateBlock update_block,
683                                         CFErrorRef* error,
684                                         const uint8_t** der_p, const uint8_t *der_end)
685{
686    SOSAccountRef account = NULL;
687#if UPGRADE_FROM_PREVIOUS_VERSION
688    const uint8_t *dersave = *der_p;
689    const uint8_t *derend = der_end;
690#endif
691    uint64_t version = 0;
692
693    const uint8_t *sequence_end;
694    *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
695    *der_p = ccder_decode_uint64(&version, *der_p, sequence_end);
696    if(!(*der_p) || version < CURRENT_ACCOUNT_PERSISTENT_VERSION) {
697#if UPGRADE_FROM_PREVIOUS_VERSION
698        return SOSAccountCreateFromDER_V3(allocator, factory, interest_block, update_block, error, &dersave, derend);
699#else
700        return NULL;
701#endif
702    }
703
704    {
705        CFDictionaryRef decoded_gestalt = NULL;
706        *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error,
707                                       *der_p, der_end);
708
709        if (*der_p == 0)
710            return NULL;
711
712        account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory, interest_block, update_block);
713        CFReleaseNull(decoded_gestalt);
714    }
715
716    CFArrayRef array = NULL;
717    *der_p = der_decode_array(kCFAllocatorDefault, 0, &array, error, *der_p, sequence_end);
718
719    uint64_t tmp_departure_code = kSOSNeverAppliedToCircle;
720    *der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, sequence_end);
721    *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, sequence_end);
722    *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, sequence_end);
723    *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->previous_public, error, *der_p, sequence_end);
724    *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, sequence_end);
725    *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *) &account->retired_peers, error, *der_p, sequence_end);
726    if (*der_p != sequence_end)
727        *der_p = NULL;
728    account->departure_code = (enum DepartureReason) tmp_departure_code;
729
730    __block bool success = true;
731
732    require_quiet(array && *der_p, fail);
733
734    CFArrayForEach(array, ^(const void *value) {
735        if (success) {
736            if (isString(value)) {
737                CFDictionaryAddValue(account->circles, value, kCFNull);
738            } else {
739                CFDataRef circleData = NULL;
740                CFDataRef fullPeerInfoData = NULL;
741
742                if (isData(value)) {
743                    circleData = (CFDataRef) value;
744                } else if (isArray(value)) {
745                    CFArrayRef pair = (CFArrayRef) value;
746
747                    CFTypeRef circleObject = CFArrayGetValueAtIndex(pair, 0);
748                    CFTypeRef fullPeerInfoObject = CFArrayGetValueAtIndex(pair, 1);
749
750                    if (CFArrayGetCount(pair) == 2 && isData(circleObject) && isData(fullPeerInfoObject)) {
751                        circleData = (CFDataRef) circleObject;
752                        fullPeerInfoData = (CFDataRef) fullPeerInfoObject;
753                    }
754                }
755
756                if (circleData) {
757                    SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, circleData, error);
758                    require_action_quiet(circle, fail, success = false);
759
760                    CFStringRef circleName = SOSCircleGetName(circle);
761                    CFDictionaryAddValue(account->circles, circleName, circle);
762
763                    if (fullPeerInfoData) {
764                        SOSFullPeerInfoRef full_peer = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, error);
765                        require_action_quiet(full_peer, fail, success = false);
766
767                        CFDictionaryAddValue(account->circle_identities, circleName, full_peer);
768                    }
769                fail:
770                    CFReleaseNull(circle);
771                }
772            }
773        }
774    });
775    CFReleaseNull(array);
776
777    require_quiet(success, fail);
778    require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail,
779                         SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error));
780
781    return account;
782
783fail:
784    account->factory = NULL; // give the factory back.
785    CFReleaseNull(account);
786    return NULL;
787}
788
789
790SOSAccountRef SOSAccountCreateFromDER_V3(CFAllocatorRef allocator,
791                                         SOSDataSourceFactoryRef factory,
792                                         SOSAccountKeyInterestBlock interest_block,
793                                         SOSAccountDataUpdateBlock update_block,
794                                         CFErrorRef* error,
795                                         const uint8_t** der_p, const uint8_t *der_end)
796{
797    SOSAccountRef account = NULL;
798    uint64_t version = 0;
799
800    const uint8_t *sequence_end;
801    *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
802    *der_p = ccder_decode_uint64(&version, *der_p, sequence_end);
803    if(!(*der_p) || version != 3) {
804        // In this case we want to silently fail so that an account gets newly created.
805        return NULL;
806    }
807
808    {
809        CFDictionaryRef decoded_gestalt = NULL;
810        *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error,
811                                       *der_p, der_end);
812
813        if (*der_p == 0)
814            return NULL;
815
816        account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory, interest_block, update_block);
817        CFReleaseNull(decoded_gestalt);
818    }
819
820    CFArrayRef array = NULL;
821    *der_p = der_decode_array(kCFAllocatorDefault, 0, &array, error, *der_p, sequence_end);
822
823    uint64_t tmp_departure_code = kSOSNeverAppliedToCircle;
824    *der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, sequence_end);
825    *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, sequence_end);
826    *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, sequence_end);
827    *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, sequence_end);
828    *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *) &account->retired_peers, error, *der_p, sequence_end);
829    if (*der_p != sequence_end)
830        *der_p = NULL;
831    account->departure_code = (enum DepartureReason) tmp_departure_code;
832
833    __block bool success = true;
834
835    require_quiet(array && *der_p, fail);
836
837    CFArrayForEach(array, ^(const void *value) {
838        if (success) {
839            if (isString(value)) {
840                CFDictionaryAddValue(account->circles, value, kCFNull);
841            } else {
842                CFDataRef circleData = NULL;
843                CFDataRef fullPeerInfoData = NULL;
844
845                if (isData(value)) {
846                    circleData = (CFDataRef) value;
847                } else if (isArray(value)) {
848                    CFArrayRef pair = (CFArrayRef) value;
849
850                    CFTypeRef circleObject = CFArrayGetValueAtIndex(pair, 0);
851                    CFTypeRef fullPeerInfoObject = CFArrayGetValueAtIndex(pair, 1);
852
853                    if (CFArrayGetCount(pair) == 2 && isData(circleObject) && isData(fullPeerInfoObject)) {
854                        circleData = (CFDataRef) circleObject;
855                        fullPeerInfoData = (CFDataRef) fullPeerInfoObject;
856                    }
857                }
858
859                if (circleData) {
860                    SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, circleData, error);
861                    require_action_quiet(circle, fail, success = false);
862
863                    CFStringRef circleName = SOSCircleGetName(circle);
864                    CFDictionaryAddValue(account->circles, circleName, circle);
865
866                    if (fullPeerInfoData) {
867                        SOSFullPeerInfoRef full_peer = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, error);
868                        require_action_quiet(full_peer, fail, success = false);
869
870                        CFDictionaryAddValue(account->circle_identities, circleName, full_peer);
871                    }
872                fail:
873                    CFReleaseNull(circle);
874                }
875            }
876        }
877    });
878    CFReleaseNull(array);
879
880    require_quiet(success, fail);
881    require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail,
882                         SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error));
883
884    return account;
885
886fail:
887    // Create a default error if we don't have one:
888    account->factory = NULL; // give the factory back.
889    CFReleaseNull(account);
890    // Don't try the der inflater from the previous release.
891    // account = SOSAccountCreateFromDER_V2(allocator, factory, interest_block, update_block, error, &dersave, derend);
892    if(account) account->departure_code = kSOSNeverAppliedToCircle;
893    return account;
894}
895
896SOSAccountRef SOSAccountCreateFromData(CFAllocatorRef allocator, CFDataRef circleData,
897                                       SOSDataSourceFactoryRef factory,
898                                       SOSAccountKeyInterestBlock interest_block,
899                                       SOSAccountDataUpdateBlock update_block,
900                                       CFErrorRef* error)
901{
902    size_t size = CFDataGetLength(circleData);
903    const uint8_t *der = CFDataGetBytePtr(circleData);
904    SOSAccountRef account = SOSAccountCreateFromDER(allocator, factory, interest_block, update_block,
905                                                    error,
906                                                    &der, der + size);
907    return account;
908}
909
910static CFMutableArrayRef SOSAccountCopyCircleArrayToEncode(SOSAccountRef account)
911{
912    CFMutableArrayRef arrayToEncode = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
913
914    CFDictionaryForEach(account->circles, ^(const void *key, const void *value) {
915        if (isNull(value)) {
916            CFArrayAppendValue(arrayToEncode, key); // Encode the name of the circle that's out of date.
917        } else {
918            SOSCircleRef circle = (SOSCircleRef) value;
919            CFDataRef encodedCircle = SOSCircleCopyEncodedData(circle, kCFAllocatorDefault, NULL);
920            CFTypeRef arrayEntry = encodedCircle;
921            CFRetainSafe(arrayEntry);
922
923            SOSFullPeerInfoRef full_peer = (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, key);
924
925            if (full_peer) {
926                CFDataRef encodedPeer = SOSFullPeerInfoCopyEncodedData(full_peer, kCFAllocatorDefault, NULL);
927                CFTypeRef originalArrayEntry = arrayEntry;
928                arrayEntry = CFArrayCreateForCFTypes(kCFAllocatorDefault, encodedCircle, encodedPeer, NULL);
929
930                CFReleaseSafe(originalArrayEntry);
931                CFReleaseNull(encodedPeer);
932            }
933
934            CFArrayAppendValue(arrayToEncode, arrayEntry);
935
936            CFReleaseSafe(arrayEntry);
937            CFReleaseNull(encodedCircle);
938        }
939
940    });
941
942    return arrayToEncode;
943}
944
945size_t SOSAccountGetDEREncodedSize(SOSAccountRef account, CFErrorRef *error)
946{
947    size_t sequence_size = 0;
948    CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
949    uint64_t version = CURRENT_ACCOUNT_PERSISTENT_VERSION;
950
951    require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(version)),                                    fail);
952    require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->gestalt, error)),                  fail);
953    require_quiet(accumulate_size(&sequence_size, der_sizeof_array(arrayToEncode, error)),                          fail);
954    require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(account->departure_code)),                    fail);
955    require_quiet(accumulate_size(&sequence_size, ccder_sizeof_bool(account->user_public_trusted, error)),          fail);
956    require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->user_public, error)),            fail);
957    require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->previous_public, error)),        fail);
958    require_quiet(accumulate_size(&sequence_size, der_sizeof_data_or_null(account->user_key_parameters, error)),    fail);
959    require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->retired_peers, error)),            fail);
960
961    CFReleaseNull(arrayToEncode);
962    return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, sequence_size);
963
964fail:
965    CFReleaseNull(arrayToEncode);
966    SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
967    return 0;
968}
969
970uint8_t* SOSAccountEncodeToDER(SOSAccountRef account, CFErrorRef* error, const uint8_t* der, uint8_t* der_end)
971{
972    CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
973    uint64_t version = CURRENT_ACCOUNT_PERSISTENT_VERSION;
974    der_end =  ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
975        ccder_encode_uint64(version, der,
976        der_encode_dictionary(account->gestalt, error, der,
977        der_encode_array(arrayToEncode, error, der,
978        ccder_encode_uint64(account->departure_code, der,
979        ccder_encode_bool(account->user_public_trusted, der,
980        der_encode_public_bytes(account->user_public, error, der,
981        der_encode_public_bytes(account->previous_public, error, der,
982        der_encode_data_or_null(account->user_key_parameters, error, der,
983        der_encode_dictionary(account->retired_peers, error, der, der_end))))))))));
984
985    CFReleaseNull(arrayToEncode);
986
987    return der_end;
988}
989
990
991
992size_t SOSAccountGetDEREncodedSize_V3(SOSAccountRef account, CFErrorRef *error)
993{
994    size_t sequence_size = 0;
995    CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
996    uint64_t version = CURRENT_ACCOUNT_PERSISTENT_VERSION;
997
998    require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(version)),                                    fail);
999    require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->gestalt, error)),                  fail);
1000    require_quiet(accumulate_size(&sequence_size, der_sizeof_array(arrayToEncode, error)),                          fail);
1001    require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(account->departure_code)),                    fail);
1002    require_quiet(accumulate_size(&sequence_size, ccder_sizeof_bool(account->user_public_trusted, error)),          fail);
1003    require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->user_public, error)),            fail);
1004    require_quiet(accumulate_size(&sequence_size, der_sizeof_data_or_null(account->user_key_parameters, error)),    fail);
1005    require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->retired_peers, error)),            fail);
1006
1007    CFReleaseNull(arrayToEncode);
1008    return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, sequence_size);
1009
1010fail:
1011    CFReleaseNull(arrayToEncode);
1012    SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
1013    return 0;
1014}
1015
1016uint8_t* SOSAccountEncodeToDER_V3(SOSAccountRef account, CFErrorRef* error, const uint8_t* der, uint8_t* der_end)
1017{
1018    CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
1019    uint64_t version = 3;
1020    der_end =  ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
1021        ccder_encode_uint64(version, der,
1022        der_encode_dictionary(account->gestalt, error, der,
1023        der_encode_array(arrayToEncode, error, der,
1024        ccder_encode_uint64(account->departure_code, der,
1025        ccder_encode_bool(account->user_public_trusted, der,
1026        der_encode_public_bytes(account->user_public, error, der,
1027        der_encode_data_or_null(account->user_key_parameters, error, der,
1028        der_encode_dictionary(account->retired_peers, error, der, der_end)))))))));
1029
1030    CFReleaseNull(arrayToEncode);
1031
1032    return der_end;
1033}
1034
1035#if OLD_CODERS_SUPPORTED
1036
1037/* Original V2 encoders */
1038
1039size_t SOSAccountGetDEREncodedSize_V2(SOSAccountRef account, CFErrorRef *error)
1040{
1041    size_t sequence_size = 0;
1042    CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
1043
1044    require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->gestalt, error)),                  fail);
1045    require_quiet(accumulate_size(&sequence_size, der_sizeof_array(arrayToEncode, error)),                          fail);
1046    require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(account->departure_code)), fail);
1047    require_quiet(accumulate_size(&sequence_size, ccder_sizeof_bool(account->user_public_trusted, error)),          fail);
1048    require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->user_public, error)),            fail);
1049    require_quiet(accumulate_size(&sequence_size, der_sizeof_data_or_null(account->user_key_parameters, error)),    fail);
1050    require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->retired_peers, error)),            fail);
1051
1052    CFReleaseNull(arrayToEncode);
1053    return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, sequence_size);
1054
1055fail:
1056    CFReleaseNull(arrayToEncode);
1057    SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
1058    return 0;
1059}
1060
1061uint8_t* SOSAccountEncodeToDER_V2(SOSAccountRef account, CFErrorRef* error, const uint8_t* der, uint8_t* der_end)
1062{
1063    CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
1064
1065    der_end =  ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
1066               der_encode_dictionary(account->gestalt, error, der,
1067               der_encode_array(arrayToEncode, error, der,
1068               ccder_encode_uint64(account->departure_code, der,
1069               ccder_encode_bool(account->user_public_trusted, der,
1070               der_encode_public_bytes(account->user_public, error, der,
1071               der_encode_data_or_null(account->user_key_parameters, error, der,
1072               der_encode_dictionary(account->retired_peers, error, der, der_end))))))));
1073
1074    CFReleaseNull(arrayToEncode);
1075
1076    return der_end;
1077}
1078
1079
1080/* Original V1 encoders */
1081
1082
1083size_t SOSAccountGetDEREncodedSize_V1(SOSAccountRef account, CFErrorRef *error)
1084{
1085    size_t sequence_size = 0;
1086    CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
1087
1088    require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->gestalt, error)),                  fail);
1089    require_quiet(accumulate_size(&sequence_size, der_sizeof_array(arrayToEncode, error)),                          fail);
1090    require_quiet(accumulate_size(&sequence_size, ccder_sizeof_bool(account->user_public_trusted, error)),          fail);
1091    require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->user_public, error)),            fail);
1092    require_quiet(accumulate_size(&sequence_size, der_sizeof_data_or_null(account->user_key_parameters, error)),    fail);
1093    require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->retired_peers, error)),            fail);
1094
1095    CFReleaseNull(arrayToEncode);
1096    return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, sequence_size);
1097
1098fail:
1099    CFReleaseNull(arrayToEncode);
1100    SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
1101    return 0;
1102}
1103
1104uint8_t* SOSAccountEncodeToDER_V1(SOSAccountRef account, CFErrorRef* error, const uint8_t* der, uint8_t* der_end)
1105{
1106    CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
1107
1108    der_end =  ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
1109        der_encode_dictionary(account->gestalt, error, der,
1110        der_encode_array(arrayToEncode, error, der,
1111        ccder_encode_bool(account->user_public_trusted, der,
1112        der_encode_public_bytes(account->user_public, error, der,
1113        der_encode_data_or_null(account->user_key_parameters, error, der,
1114               der_encode_dictionary(account->retired_peers, error, der, der_end)))))));
1115
1116    CFReleaseNull(arrayToEncode);
1117
1118    return der_end;
1119}
1120#endif /* OLD_CODERS_SUPPORTED */
1121
1122/************************/
1123
1124CFDataRef SOSAccountCopyEncodedData(SOSAccountRef account, CFAllocatorRef allocator, CFErrorRef *error)
1125{
1126    size_t size = SOSAccountGetDEREncodedSize(account, error);
1127    if (size == 0)
1128        return NULL;
1129    uint8_t buffer[size];
1130    uint8_t* start = SOSAccountEncodeToDER(account, error, buffer, buffer + sizeof(buffer));
1131    CFDataRef result = CFDataCreate(kCFAllocatorDefault, start, size);
1132    return result;
1133}
1134
1135dispatch_queue_t SOSAccountGetQueue(SOSAccountRef account) {
1136    return account->queue;
1137}
1138
1139//
1140// MARK: User Credential management
1141//
1142
1143void SOSAccountPurgePrivateCredential(SOSAccountRef account)
1144{
1145    CFReleaseNull(account->_user_private);
1146    if (account->user_private_timer) {
1147        dispatch_source_cancel(account->user_private_timer);
1148        dispatch_release(account->user_private_timer);
1149        account->user_private_timer = NULL;
1150        xpc_transaction_end();
1151    }
1152    if (account->lock_notification_token) {
1153        notify_cancel(account->lock_notification_token);
1154        account->lock_notification_token = 0;
1155    }
1156}
1157
1158static void SOSAccountSetPrivateCredential(SOSAccountRef account, SecKeyRef private) {
1159    if (!private)
1160        return SOSAccountPurgePrivateCredential(account);
1161
1162    CFRetain(private);
1163    CFReleaseSafe(account->_user_private);
1164    account->_user_private = private;
1165
1166    bool resume_timer = false;
1167    if (!account->user_private_timer) {
1168        xpc_transaction_begin();
1169        resume_timer = true;
1170        account->user_private_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, account->queue);
1171        dispatch_source_set_event_handler(account->user_private_timer, ^{
1172            SOSAccountPurgePrivateCredential(account);
1173        });
1174
1175        notify_register_dispatch(kUserKeybagStateChangeNotification, &account->lock_notification_token, account->queue, ^(int token) {
1176            bool locked = false;
1177            CFErrorRef lockCheckError = NULL;
1178
1179            if (!SecAKSGetIsLocked(&locked, &lockCheckError)) {
1180                secerror("Checking for locked after change failed: %@", lockCheckError);
1181            }
1182
1183            if (locked) {
1184                SOSAccountPurgePrivateCredential(account);
1185            }
1186        });
1187    }
1188
1189    // (Re)set the timer's fire time to now + 120 seconds with a 5 second fuzz factor.
1190    dispatch_time_t purgeTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * 60 * NSEC_PER_SEC));
1191    dispatch_source_set_timer(account->user_private_timer, purgeTime, DISPATCH_TIME_FOREVER, (int64_t)(5 * NSEC_PER_SEC));
1192    if (resume_timer)
1193        dispatch_resume(account->user_private_timer);
1194}
1195
1196SecKeyRef SOSAccountGetPrivateCredential(SOSAccountRef account, CFErrorRef* error)
1197{
1198    if (account->_user_private == NULL) {
1199        SOSCreateError(kSOSErrorPrivateKeyAbsent, CFSTR("Private Key not available - failed to prompt user recently"), NULL, error);
1200    }
1201    return account->_user_private;
1202}
1203
1204static bool SOSAccountHasPublicKey(SOSAccountRef account, CFErrorRef* error)
1205{
1206    if (account->user_public == NULL || account->user_public_trusted == false) {
1207        SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Public Key not available - failed to register before call"), NULL, error);
1208        return false;
1209    }
1210
1211    return true;
1212}
1213
1214static bool SOSAccountIsMyPeerActiveInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error);
1215
1216static void SOSAccountGenerationSignatureUpdate(SOSAccountRef account, SecKeyRef privKey) {
1217    SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
1218        SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircle(account, circle, NULL);
1219        if(SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(fpi), NULL) &&
1220           !SOSCircleVerify(circle, account->user_public, NULL)) {
1221            SOSAccountModifyCircle(account, SOSCircleGetName(circle), NULL, ^(SOSCircleRef circle) {
1222                SOSFullPeerInfoRef cloud_fpi = SOSCircleGetiCloudFullPeerInfoRef(circle);
1223                require_quiet(cloud_fpi != NULL, gen_sign);
1224                require_quiet(SOSFullPeerInfoUpgradeSignatures(cloud_fpi, privKey, NULL), gen_sign);
1225                if(!SOSCircleUpdatePeerInfo(circle, SOSFullPeerInfoGetPeerInfo(cloud_fpi))) {
1226                }
1227            gen_sign: // finally generation sign this.
1228                SOSCircleGenerationSign(circle, privKey, fpi, NULL);
1229                account->departure_code = kSOSNeverLeftCircle;
1230            });
1231        }
1232    });
1233}
1234
1235/* this one is meant to be local - not published over KVS. */
1236static void SOSAccountPeerSignatureUpdate(SOSAccountRef account, SecKeyRef privKey) {
1237    SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
1238        SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircle(account, circle, NULL);
1239        SOSFullPeerInfoUpgradeSignatures(fpi, privKey, NULL);
1240    });
1241}
1242
1243static void SOSAccountSetPreviousPublic(SOSAccountRef account) {
1244    CFReleaseNull(account->previous_public);
1245    account->previous_public = account->user_public;
1246    CFRetain(account->previous_public);
1247}
1248
1249static void SOSAccountSetTrustedUserPublicKey(SOSAccountRef account, bool public_was_trusted, SecKeyRef privKey)
1250{
1251    if (!privKey) return;
1252    SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(privKey);
1253
1254    if (account->user_public && account->user_public_trusted && CFEqual(publicKey, account->user_public)) return;
1255
1256    if(public_was_trusted && account->user_public) {
1257        CFReleaseNull(account->previous_public);
1258        account->previous_public = account->user_public;
1259        CFRetain(account->previous_public);
1260    }
1261
1262    CFReleaseNull(account->user_public);
1263    account->user_public = publicKey;
1264    account->user_public_trusted = true;
1265
1266    if(!account->previous_public) {
1267        account->previous_public = account->user_public;
1268        CFRetain(account->previous_public);
1269    }
1270
1271	secnotice("trust", "trusting new public key: %@", account->user_public);
1272}
1273
1274static void SOSAccountProcessDeferredUpdates(SOSAccountRef account) {
1275    CFErrorRef error = NULL;
1276    if (account->deferred_updates && !SOSAccountHandleUpdates(account, account->deferred_updates, &error))
1277        secerror("Failed to handle updates when setting public key (%@)", error);
1278
1279    CFReleaseNull(account->deferred_updates);
1280}
1281
1282
1283bool SOSAccountTryUserCredentials(SOSAccountRef account, CFStringRef user_account __unused, CFDataRef user_password, CFErrorRef *error)
1284{
1285    bool success = false;
1286
1287    if (!SOSAccountHasPublicKey(account, error))
1288        return false;
1289
1290    if (account->user_key_parameters) {
1291        SecKeyRef new_key = SOSUserKeygen(user_password, account->user_key_parameters, error);
1292        if (new_key) {
1293            SecKeyRef new_public_key = SecKeyCreatePublicFromPrivate(new_key);
1294
1295            if (CFEqualSafe(new_public_key, account->user_public)) {
1296                SOSAccountSetPrivateCredential(account, new_key);
1297                success = true;
1298            } else {
1299                SOSCreateError(kSOSErrorWrongPassword, CFSTR("Password passed in incorrect: ▇█████▇▇██"), NULL, error);
1300            }
1301            CFReleaseSafe(new_public_key);
1302            CFReleaseSafe(new_key);
1303        }
1304    } else {
1305        SOSCreateError(kSOSErrorProcessingFailure, CFSTR("Have public key but no parameters??"), NULL, error);
1306    }
1307
1308    return success;
1309}
1310
1311static bool SOSAccountPublishCloudParameters(SOSAccountRef account, CFErrorRef* error)
1312{
1313    bool success = false;
1314    CFMutableDataRef cloudParameters = CFDataCreateMutableWithScratch(kCFAllocatorDefault,
1315                                                                      der_sizeof_cloud_parameters(account->user_public,
1316                                                                                                  account->user_key_parameters,
1317                                                                                                  error));
1318    if (der_encode_cloud_parameters(account->user_public, account->user_key_parameters, error,
1319                                    CFDataGetMutableBytePtr(cloudParameters),
1320                                    CFDataGetMutablePastEndPtr(cloudParameters)) != NULL) {
1321
1322        CFDictionaryRef changes = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1323                                                               kSOSKVSKeyParametersKey, cloudParameters,
1324                                                               NULL);
1325
1326        CFErrorRef changeError = NULL;
1327        if (account->update_block(changes, &changeError)) {
1328            success = true;
1329        } else {
1330            SOSCreateErrorWithFormat(kSOSErrorSendFailure, changeError, error, NULL,
1331                                     CFSTR("update parameters key failed [%@]"), changes);
1332        }
1333        CFReleaseSafe(changes);
1334        CFReleaseSafe(changeError);
1335    } else {
1336        SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Encoding parameters failed"), NULL, error);
1337    }
1338
1339    CFReleaseNull(cloudParameters);
1340
1341    return success;
1342}
1343
1344bool SOSAccountAssertUserCredentials(SOSAccountRef account, CFStringRef user_account __unused, CFDataRef user_password, CFErrorRef *error)
1345{
1346    bool public_was_trusted = account->user_public_trusted;
1347    account->user_public_trusted = false;
1348    SecKeyRef user_private = NULL;
1349
1350    if (account->user_public && account->user_key_parameters) {
1351        // We have an untrusted public key – see if our generation makes the same key:
1352        // if so we trust it and we have the private key.
1353        // if not we still don't trust it.
1354        require_quiet(user_private = SOSUserKeygen(user_password, account->user_key_parameters, error), exit);
1355        SecKeyRef public_candidate = SecKeyCreatePublicFromPrivate(user_private);
1356        if (!CFEqualSafe(account->user_public, public_candidate)) {
1357            secnotice("trust", "Public keys don't match:  calculated: %@, expected: %@",
1358                      account->user_public, public_candidate);
1359            debugDumpUserParameters(CFSTR("params"), account->user_key_parameters);
1360            CFReleaseNull(user_private);
1361        } else {
1362            SOSAccountPeerSignatureUpdate(account, user_private);
1363            SOSAccountSetTrustedUserPublicKey(account, public_was_trusted, user_private);
1364        }
1365        CFReleaseSafe(public_candidate);
1366    }
1367
1368    if (!account->user_public_trusted) {
1369        // We may or may not have parameters here.
1370        // In any case we tried using them and they didn't match
1371        // So forget all that and start again, assume we're the first to push anything useful.
1372
1373        CFReleaseNull(account->user_key_parameters);
1374        account->user_key_parameters = SOSUserKeyCreateGenerateParameters(error);
1375        require_quiet(user_private = SOSUserKeygen(user_password, account->user_key_parameters, error), exit);
1376
1377        SOSAccountPeerSignatureUpdate(account, user_private);
1378        SOSAccountSetTrustedUserPublicKey(account, public_was_trusted, user_private);
1379
1380        CFErrorRef publishError = NULL;
1381        if (!SOSAccountPublishCloudParameters(account, &publishError))
1382            secerror("Failed to publish new cloud parameters: %@", publishError);
1383        CFReleaseSafe(publishError);
1384    }
1385
1386    SOSAccountProcessDeferredUpdates(account);
1387    SOSAccountGenerationSignatureUpdate(account, user_private);
1388    SOSAccountSetPrivateCredential(account, user_private);
1389exit:
1390    CFReleaseSafe(user_private);
1391
1392    return account->user_public_trusted;
1393}
1394
1395//
1396// MARK: Circle management
1397//
1398
1399int SOSAccountCountCircles(SOSAccountRef a) {
1400    assert(a);
1401    assert(a->circle_identities);
1402    assert(a->circles);
1403    return (int)CFDictionaryGetCount(a->circles);
1404}
1405
1406static SecKeyRef GeneratePermanentFullECKey_internal(int keySize, CFStringRef name, CFTypeRef accessibility, CFBooleanRef sync,  CFErrorRef* error)
1407{
1408    SecKeyRef full_key = NULL;
1409
1410    CFNumberRef key_size_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize);
1411
1412    CFDictionaryRef priv_key_attrs = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1413                                                                  kSecAttrIsPermanent,    kCFBooleanTrue,
1414                                                                  NULL);
1415
1416    CFDictionaryRef keygen_parameters = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1417        kSecAttrKeyType,        kSecAttrKeyTypeEC,
1418        kSecAttrKeySizeInBits,  key_size_num,
1419        kSecPrivateKeyAttrs,    priv_key_attrs,
1420        kSecAttrAccessible,     accessibility,
1421        kSecAttrAccessGroup,    kSOSInternalAccessGroup,
1422        kSecAttrLabel,          name,
1423        kSecAttrSynchronizable, sync,
1424        kSecUseTombstones,      kCFBooleanTrue,
1425        NULL);
1426
1427    CFReleaseNull(priv_key_attrs);
1428
1429    CFReleaseNull(key_size_num);
1430    OSStatus status = SecKeyGeneratePair(keygen_parameters, NULL, &full_key);
1431    CFReleaseNull(keygen_parameters);
1432
1433    if (status)
1434        secerror("status: %ld", (long)status);
1435    if (status != errSecSuccess && error != NULL && *error == NULL) {
1436        *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL);
1437    }
1438
1439    return full_key;
1440}
1441
1442static SecKeyRef GeneratePermanentFullECKey(int keySize, CFStringRef name, CFErrorRef* error) {
1443    return GeneratePermanentFullECKey_internal(keySize, name, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, kCFBooleanFalse, error);
1444}
1445
1446static SecKeyRef GeneratePermanentFullECKeyForCloudIdentity(int keySize, CFStringRef name, CFErrorRef* error) {
1447    return GeneratePermanentFullECKey_internal(keySize, name, kSecAttrAccessibleWhenUnlocked, kCFBooleanTrue, error);
1448}
1449
1450
1451SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error) {
1452    if (CFDictionaryGetValue(account->circles, name) == NULL) {
1453        SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("No circle named '%@'"), name);
1454        return NULL;
1455    }
1456    SOSFullPeerInfoRef circle_full_peer_info = (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name);
1457
1458
1459    if (circle_full_peer_info == NULL) {
1460        CFStringRef keyName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("ID for %@-%@"), SOSPeerGestaltGetName(account->gestalt), name);
1461        SecKeyRef full_key = GeneratePermanentFullECKey(256, keyName, error);
1462        CFReleaseNull(keyName);
1463
1464        if (full_key) {
1465            circle_full_peer_info = SOSFullPeerInfoCreate(kCFAllocatorDefault, account->gestalt, full_key, error);
1466
1467            CFReleaseNull(full_key);
1468
1469            if (!circle_full_peer_info) {
1470                secerror("Can't make FullPeerInfo for %@-%@ (%@) - is AKS ok?", SOSPeerGestaltGetName(account->gestalt), name, error ? (void*)*error : (void*)CFSTR("-"));
1471                return circle_full_peer_info;
1472            }
1473
1474            CFDictionarySetValue(account->circle_identities, name, circle_full_peer_info);
1475            CFReleaseNull(circle_full_peer_info);
1476            circle_full_peer_info = (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name);
1477        }
1478        else
1479            secerror("No full_key: %@:", error ? *error : NULL);
1480    }
1481
1482    return circle_full_peer_info;
1483}
1484
1485static bool SOSAccountDestroyCirclePeerInfoNamed(SOSAccountRef account, CFStringRef name, CFErrorRef* error) {
1486    if (CFDictionaryGetValue(account->circles, name) == NULL) {
1487        SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("No circle named '%@'"), name);
1488        return false;
1489    }
1490
1491    SOSFullPeerInfoRef circle_full_peer_info = (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name);
1492
1493    if (circle_full_peer_info) {
1494        SOSPeerPurgeAllFor(SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(circle_full_peer_info)));
1495
1496        SOSFullPeerInfoPurgePersistentKey(circle_full_peer_info, NULL);
1497    }
1498
1499    CFDictionaryRemoveValue(account->circle_identities, name);
1500
1501    return true;
1502}
1503
1504static bool SOSAccountDestroyCirclePeerInfo(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
1505    return SOSAccountDestroyCirclePeerInfoNamed(account, SOSCircleGetName(circle), error);
1506}
1507
1508SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
1509    return SOSAccountGetMyFullPeerInCircleNamed(account, SOSCircleGetName(circle), error);
1510}
1511
1512SOSPeerInfoRef SOSAccountGetMyPeerInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
1513    SOSFullPeerInfoRef fpi =  SOSAccountGetMyFullPeerInCircleNamed(account, SOSCircleGetName(circle), error);
1514
1515    return fpi ? SOSFullPeerInfoGetPeerInfo(fpi) : NULL;
1516}
1517
1518SOSPeerInfoRef SOSAccountGetMyPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error)
1519{
1520    SOSFullPeerInfoRef fpi =  SOSAccountGetMyFullPeerInCircleNamed(account, name, error);
1521
1522    return fpi ? SOSFullPeerInfoGetPeerInfo(fpi) : NULL;
1523}
1524
1525CFArrayRef SOSAccountCopyAccountIdentityPeerInfos(SOSAccountRef account, CFAllocatorRef allocator, CFErrorRef* error)
1526{
1527    CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(allocator);
1528
1529    CFDictionaryForEach(account->circle_identities, ^(const void *key, const void *value) {
1530        SOSFullPeerInfoRef fpi = (SOSFullPeerInfoRef) value;
1531
1532        CFArrayAppendValue(result, SOSFullPeerInfoGetPeerInfo(fpi));
1533    });
1534
1535    return result;
1536}
1537
1538bool SOSAccountIsAccountIdentity(SOSAccountRef account, SOSPeerInfoRef peer_info, CFErrorRef *error)
1539{
1540    __block bool matches = false;
1541    CFDictionaryForEach(account->circle_identities, ^(const void *key, const void *value) {
1542        if (!matches) {
1543            matches = CFEqual(peer_info, SOSFullPeerInfoGetPeerInfo((SOSFullPeerInfoRef) value));
1544        }
1545    });
1546
1547    return matches;
1548}
1549
1550bool SOSAccountSyncWithAllPeers(SOSAccountRef account, CFErrorRef *error)
1551{
1552    __block bool result = true;
1553    SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
1554        if (!SOSAccountSyncWithAllPeersInCircle(account, circle, error))
1555            result = false;
1556    });
1557
1558    return result;
1559}
1560
1561bool SOSAccountSyncWithAllPeersInCircle(SOSAccountRef account, SOSCircleRef circle,
1562                                CFErrorRef *error)
1563{
1564    SOSPeerInfoRef my_peer = SOSAccountGetMyPeerInCircle(account, circle, error);
1565    if (!my_peer)
1566        return false;
1567
1568    __block bool didSync = false;
1569    __block bool result = true;
1570
1571    if (SOSCircleHasPeer(circle, my_peer, NULL)) {
1572        SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
1573            if (!CFEqual(SOSPeerInfoGetPeerID(my_peer), SOSPeerInfoGetPeerID(peer)))
1574            {
1575                bool local_didSync = false;
1576                if (!SOSAccountSyncWithPeer(account, circle, peer, &local_didSync, error))
1577                    result = false;
1578                if (!didSync && local_didSync)
1579                {
1580                    didSync = true;
1581                }
1582            }
1583        });
1584
1585        if (didSync)
1586        {
1587            SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers, 1);
1588        }
1589    }
1590
1591    return result;
1592}
1593
1594bool SOSAccountSyncWithPeer(SOSAccountRef account, SOSCircleRef circle,
1595                            SOSPeerInfoRef thisPeer, bool* didSendData, CFErrorRef* error)
1596{
1597    CFStringRef peer_id = SOSPeerInfoGetPeerID(thisPeer);
1598    CFStringRef peer_write_key = SOSMessageKeyCreateWithAccountAndPeer(account, circle, peer_id);
1599    SOSFullPeerInfoRef myRef = SOSAccountGetMyFullPeerInCircle(account, circle, error);
1600
1601    __block bool sentData = false;
1602
1603
1604    SOSPeerSendBlock writeToKVSKey = ^bool (CFDataRef data, CFErrorRef* error) {
1605        secnotice("account", "writing data of size %ld:", data?CFDataGetLength(data):0);
1606        sentData = (NULL != data);
1607        CFDictionaryRef writeToDo = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, peer_write_key, data, NULL);
1608        bool written = account->update_block(writeToDo, error);
1609        if (account->processed_message_block)
1610            account->processed_message_block(circle, NULL, data);
1611        CFRelease(writeToDo);
1612        return written;
1613    };
1614
1615    if (NULL != didSendData)
1616    {
1617        *didSendData = sentData;
1618    }
1619
1620    bool result = SOSCircleSyncWithPeer(myRef, circle, account->factory, writeToKVSKey, peer_id, error);
1621    CFReleaseNull(peer_write_key);
1622    return result;
1623}
1624
1625static bool SOSAccountIsActivePeerInCircleNamed(SOSAccountRef account, CFStringRef circle_name, CFStringRef peerid, CFErrorRef* error) {
1626    SOSCircleRef circle = SOSAccountFindCircle(account, circle_name, error);
1627    if(!circle) return false;
1628    return SOSCircleHasActivePeerWithID(circle, peerid, error);
1629}
1630
1631static bool SOSAccountIsMyPeerActiveInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
1632    SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, SOSCircleGetName(circle), NULL);
1633    if(!fpi) return false;
1634    return SOSCircleHasActivePeer(circle, SOSFullPeerInfoGetPeerInfo(fpi), error);
1635}
1636
1637bool SOSAccountCleanupAfterPeer(SOSAccountRef account, size_t seconds, SOSCircleRef circle,
1638                                SOSPeerInfoRef cleanupPeer, CFErrorRef* error)
1639{
1640    if(!SOSAccountIsMyPeerActiveInCircle(account, circle, NULL)) return true;
1641
1642    CFMutableDictionaryRef keysToWrite = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1643
1644    SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef circlePeer) {
1645        CFStringRef from_key = SOSMessageKeyCreateWithCircleAndPeerInfos(circle, cleanupPeer, circlePeer);
1646        CFStringRef to_key = SOSMessageKeyCreateWithCircleAndPeerInfos(circle, circlePeer, cleanupPeer);
1647
1648        CFDictionaryAddValue(keysToWrite, from_key, kCFNull);
1649        CFDictionaryAddValue(keysToWrite, to_key, kCFNull);
1650
1651        CFReleaseNull(from_key);
1652        CFReleaseNull(to_key);
1653    });
1654
1655    if(SOSPeerInfoRetireRetirementTicket(seconds, cleanupPeer)) {
1656        CFStringRef resignationKey = SOSRetirementKeyCreateWithCircleAndPeer(circle, SOSPeerInfoGetPeerID(cleanupPeer));
1657        CFDictionarySetValue(keysToWrite, resignationKey, kCFNull);
1658        CFDictionaryRemoveValue(account->retired_peers, resignationKey);
1659        CFReleaseNull(resignationKey);
1660    }
1661
1662    bool success = account->update_block(keysToWrite, error);
1663
1664    CFReleaseNull(keysToWrite);
1665
1666    return success;
1667}
1668
1669bool SOSAccountCleanupRetirementTickets(SOSAccountRef account, size_t seconds, CFErrorRef* error) {
1670    CFMutableDictionaryRef keysToWrite = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1671
1672    CFDictionaryRef copyToIterate = CFDictionaryCreateCopy(kCFAllocatorDefault, account->retired_peers);
1673
1674    CFDictionaryForEach(copyToIterate, ^(const void* resignationKey, const void* value) {
1675        CFStringRef circle_name = NULL;
1676        CFStringRef retiree_peerid = NULL;
1677        SOSPeerInfoRef pi = NULL;
1678        SOSKVSKeyType keytype = SOSKVSKeyGetKeyTypeAndParse(resignationKey, &circle_name, &retiree_peerid, NULL);
1679        require_quiet(keytype == kRetirementKey && circle_name && retiree_peerid && isData(value), forget);
1680        pi = SOSPeerInfoCreateFromData(NULL, error, (CFDataRef) value);
1681        require_quiet(pi && CFEqualSafe(retiree_peerid, SOSPeerInfoGetPeerID(pi)), forget);
1682
1683        require_quiet(!SOSAccountIsActivePeerInCircleNamed(account, circle_name, retiree_peerid, NULL), keep);
1684        require_quiet(SOSPeerInfoRetireRetirementTicket(seconds, pi), keep);
1685
1686        // Happy day, it's time and it's a ticket we should eradicate from KVS.
1687        CFDictionarySetValue(keysToWrite, resignationKey, kCFNull);
1688
1689    forget:
1690        CFDictionaryRemoveValue(account->retired_peers, resignationKey);
1691    keep:
1692        CFReleaseSafe(pi);
1693        CFReleaseSafe(circle_name);
1694        CFReleaseSafe(retiree_peerid);
1695    });
1696    CFReleaseNull(copyToIterate);
1697
1698    bool success = true;
1699    if(CFDictionaryGetCount(keysToWrite)) {
1700        success = account->update_block(keysToWrite, error);
1701    }
1702    CFReleaseNull(keysToWrite);
1703
1704    return success;
1705}
1706
1707bool SOSAccountScanForRetired(SOSAccountRef account, SOSCircleRef circle, CFErrorRef *error) {
1708    SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
1709        CFStringRef key = SOSRetirementKeyCreateWithCircleAndPeer(circle, SOSPeerInfoGetPeerID(peer));
1710        if(key && !CFDictionaryGetValueIfPresent(account->retired_peers, key, NULL)) {
1711            CFDataRef value = SOSPeerInfoCopyEncodedData(peer, NULL, NULL);
1712            if(value) {
1713                CFDictionarySetValue(account->retired_peers, key, value);
1714                SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, peer, error);
1715            }
1716            CFReleaseSafe(value);
1717        }
1718        CFReleaseSafe(key);
1719    });
1720    return true;
1721}
1722
1723SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccountRef account, SOSCircleRef starting_circle, CFErrorRef *error) {
1724    CFStringRef circle_to_mod = SOSCircleGetName(starting_circle);
1725    SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error);
1726    if(!new_circle) return NULL;
1727
1728    CFDictionaryForEach(account->retired_peers, ^(const void* resignationKey, const void* value) {
1729        CFStringRef circle_name = NULL;
1730        CFStringRef retiree_peerid = NULL;
1731
1732        SOSKVSKeyType keytype = SOSKVSKeyGetKeyTypeAndParse(resignationKey, &circle_name, &retiree_peerid, NULL);
1733        if(keytype == kRetirementKey && CFEqualSafe(circle_name, circle_to_mod) && SOSCircleHasPeerWithID(new_circle, retiree_peerid, NULL)) {
1734            if(isData(value)) {
1735                SOSPeerInfoRef pi = SOSPeerInfoCreateFromData(NULL, error, (CFDataRef) value);
1736                SOSCircleUpdatePeerInfo(new_circle, pi);
1737                CFReleaseSafe(pi);
1738            }
1739        }
1740        CFReleaseSafe(circle_name);
1741        CFReleaseSafe(retiree_peerid);
1742    });
1743
1744    if(SOSCircleCountPeers(new_circle) == 0) {
1745        SOSCircleResetToEmpty(new_circle, NULL);
1746    }
1747
1748    return new_circle;
1749}
1750
1751
1752//
1753// Circle Finding
1754//
1755SOSCircleRef SOSAccountFindCompatibleCircle(SOSAccountRef a, CFStringRef name)
1756{
1757    CFTypeRef entry = CFDictionaryGetValue(a->circles, name);
1758
1759    if (CFGetTypeID(entry) == SOSCircleGetTypeID())
1760        return (SOSCircleRef) entry;
1761
1762    return NULL;
1763}
1764
1765SOSCircleRef SOSAccountFindCircle(SOSAccountRef a, CFStringRef name, CFErrorRef *error)
1766{
1767    CFTypeRef entry = CFDictionaryGetValue(a->circles, name);
1768
1769    require_action_quiet(!isNull(entry), fail,
1770                         SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("Incompatible circle in KVS"), NULL, error));
1771
1772    require_action_quiet(entry, fail,
1773                         SOSCreateError(kSOSErrorNoCircle, CFSTR("No circle found"), NULL, error));
1774
1775
1776    return (SOSCircleRef) entry;
1777
1778fail:
1779    return NULL;
1780}
1781
1782SOSCircleRef SOSAccountEnsureCircle(SOSAccountRef a, CFStringRef name, CFErrorRef *error)
1783{
1784    CFErrorRef localError = NULL;
1785
1786    SOSCircleRef circle = SOSAccountFindCircle(a, name, &localError);
1787
1788    require_action_quiet(circle || !isSOSErrorCoded(localError, kSOSErrorIncompatibleCircle), fail,
1789                         if (error) { *error = localError; localError = NULL; });
1790
1791
1792    if (NULL == circle) {
1793        circle = SOSCircleCreate(NULL, name, NULL);
1794        if (circle){
1795            CFDictionaryAddValue(a->circles, name, circle);
1796            CFRelease(circle);
1797            circle = SOSAccountFindCircle(a, name, &localError);
1798        }
1799        SOSUpdateKeyInterest(a, false, NULL);
1800    }
1801
1802fail:
1803    CFReleaseNull(localError);
1804    return circle;
1805}
1806
1807
1808void SOSAccountAddChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock)
1809{
1810    CFArrayAppendValue(a->change_blocks, changeBlock);
1811}
1812
1813void SOSAccountRemoveChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock)
1814{
1815    CFArrayRemoveAllValue(a->change_blocks, changeBlock);
1816}
1817
1818static void DifferenceAndCall(CFArrayRef old_members, CFArrayRef new_members, void (^updatedCircle)(CFArrayRef additions, CFArrayRef removals))
1819{
1820    CFMutableArrayRef additions = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, new_members);
1821    CFMutableArrayRef removals = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, old_members);
1822
1823
1824    CFArrayForEach(old_members, ^(const void * value) {
1825        CFArrayRemoveAllValue(additions, value);
1826    });
1827
1828    CFArrayForEach(new_members, ^(const void * value) {
1829        CFArrayRemoveAllValue(removals, value);
1830    });
1831
1832    updatedCircle(additions, removals);
1833
1834    CFReleaseSafe(additions);
1835    CFReleaseSafe(removals);
1836}
1837
1838static void SOSAccountNotifyOfChange(SOSAccountRef account, SOSCircleRef oldCircle, SOSCircleRef newCircle)
1839{
1840    CFMutableArrayRef old_members = SOSCircleCopyPeers(oldCircle, kCFAllocatorDefault);
1841    CFMutableArrayRef new_members = SOSCircleCopyPeers(newCircle, kCFAllocatorDefault);
1842
1843    CFMutableArrayRef old_applicants = SOSCircleCopyApplicants(oldCircle, kCFAllocatorDefault);
1844    CFMutableArrayRef new_applicants = SOSCircleCopyApplicants(newCircle, kCFAllocatorDefault);
1845
1846    DifferenceAndCall(old_members, new_members, ^(CFArrayRef added_members, CFArrayRef removed_members) {
1847        DifferenceAndCall(old_applicants, new_applicants, ^(CFArrayRef added_applicants, CFArrayRef removed_applicants) {
1848            CFArrayForEach(account->change_blocks, ^(const void * notificationBlock) {
1849                ((SOSAccountCircleMembershipChangeBlock) notificationBlock)(newCircle, added_members, removed_members, added_applicants, removed_applicants);
1850            });
1851        });
1852    });
1853
1854    CFReleaseNull(old_applicants);
1855    CFReleaseNull(new_applicants);
1856
1857    CFReleaseNull(old_members);
1858    CFReleaseNull(new_members);
1859}
1860
1861void SOSAccountForEachCircle(SOSAccountRef account, void (^process)(SOSCircleRef circle))
1862{
1863    CFDictionaryForEach(account->circles, ^(const void* key, const void* value) {
1864        assert(value);
1865        process((SOSCircleRef)value);
1866    });
1867}
1868
1869static void AppendCircleKeyName(CFMutableArrayRef array, CFStringRef name) {
1870    CFStringRef circle_key = SOSCircleKeyCreateWithName(name, NULL);
1871    CFArrayAppendValue(array, circle_key);
1872    CFReleaseNull(circle_key);
1873}
1874
1875static inline void AppendCircleInterests(CFMutableArrayRef circle_keys, CFMutableArrayRef retiree_keys, CFMutableArrayRef message_keys, SOSCircleRef circle, SOSFullPeerInfoRef me) {
1876    CFStringRef my_peer_id = NULL;
1877
1878    if (me) {
1879        SOSPeerInfoRef my_peer = me ? SOSFullPeerInfoGetPeerInfo(me) : NULL;
1880        my_peer_id = SOSPeerInfoGetPeerID(my_peer);
1881    }
1882
1883    if (circle_keys) {
1884        CFStringRef circleName = SOSCircleGetName(circle);
1885        AppendCircleKeyName(circle_keys, circleName);
1886    }
1887
1888    SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
1889        if (!CFEqualSafe(my_peer_id, SOSPeerInfoGetPeerID(peer))) {
1890            CFStringRef peer_name = SOSPeerInfoGetPeerID(peer);
1891            if (retiree_keys) {
1892                CFStringRef retirementKey = SOSRetirementKeyCreateWithCircleAndPeer(circle, peer_name);
1893                CFArrayAppendValue(retiree_keys, retirementKey);
1894                CFReleaseNull(retirementKey);
1895            }
1896
1897            if (my_peer_id && message_keys) {
1898                CFStringRef messageKey = SOSMessageKeyCreateWithCircleAndPeerNames(circle, peer_name, my_peer_id);
1899                CFArrayAppendValue(message_keys, messageKey);
1900                CFRelease(messageKey);
1901            }
1902        }
1903    });
1904}
1905
1906static void SOSAccountCopyKeyInterests(SOSAccountRef account,
1907                                       CFMutableArrayRef alwaysKeys,
1908                                       CFMutableArrayRef afterFirstUnlockKeys,
1909                                       CFMutableArrayRef whenUnlockedKeys)
1910{
1911    CFArrayAppendValue(afterFirstUnlockKeys, kSOSKVSKeyParametersKey);
1912
1913    SOSAccountForEachKnownCircle(account, ^(CFStringRef name) {
1914        AppendCircleKeyName(afterFirstUnlockKeys, name);
1915    }, ^(SOSCircleRef circle) {
1916        AppendCircleInterests(afterFirstUnlockKeys, afterFirstUnlockKeys, whenUnlockedKeys, circle, NULL);
1917    }, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) {
1918        bool inCircle = SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(full_peer), NULL);
1919
1920        AppendCircleInterests(afterFirstUnlockKeys, afterFirstUnlockKeys, inCircle ? whenUnlockedKeys : NULL, circle, full_peer);
1921    });
1922}
1923
1924static bool SOSUpdateKeyInterest(SOSAccountRef account, bool getNewKeysOnly, CFErrorRef *error)
1925{
1926    if (account->update_interest_block) {
1927
1928        CFMutableArrayRef alwaysKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1929        CFMutableArrayRef afterFirstUnlockKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1930        CFMutableArrayRef whenUnlockedKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1931
1932        SOSAccountCopyKeyInterests(account, alwaysKeys, afterFirstUnlockKeys, whenUnlockedKeys);
1933
1934        account->update_interest_block(getNewKeysOnly, alwaysKeys, afterFirstUnlockKeys, whenUnlockedKeys);
1935
1936        CFReleaseNull(alwaysKeys);
1937        CFReleaseNull(afterFirstUnlockKeys);
1938        CFReleaseNull(whenUnlockedKeys);
1939    }
1940
1941    return true;
1942}
1943
1944static bool SOSAccountSendPendingChanges(SOSAccountRef account, CFErrorRef *error) {
1945    CFErrorRef changeError = NULL;
1946
1947    if (CFDictionaryGetCount(account->pending_changes) == 0)
1948        return true;
1949
1950    bool success = account->update_block(account->pending_changes, &changeError);
1951    if (success) {
1952        CFDictionaryRemoveAllValues(account->pending_changes);
1953    } else {
1954        SOSCreateErrorWithFormat(kSOSErrorSendFailure, changeError, error, NULL,
1955                                 CFSTR("Send changes block failed [%@]"), account->pending_changes);
1956    }
1957
1958    return success;
1959}
1960
1961static bool SOSAccountAddCircleToPending(SOSAccountRef account, SOSCircleRef circle, CFErrorRef *error)
1962{
1963    bool success = false;
1964    CFDataRef circle_data = SOSCircleCopyEncodedData(circle, kCFAllocatorDefault, error);
1965
1966    if (circle_data) {
1967        CFStringRef circle_key = SOSCircleKeyCreateWithCircle(circle, NULL);
1968
1969        CFDictionarySetValue(account->pending_changes, circle_key, circle_data);
1970        success = true;
1971
1972        CFReleaseNull(circle_data);
1973        CFReleaseNull(circle_key);
1974    }
1975
1976    return success;
1977}
1978
1979
1980static void SOSAccountRecordRetiredPeerInCircleNamed(SOSAccountRef account, CFStringRef circleName, SOSPeerInfoRef retiree)
1981{
1982    // Replace Peer with RetiredPeer, if were a peer.
1983    SOSAccountModifyCircle(account, circleName, NULL, ^(SOSCircleRef circle) {
1984        if (SOSCircleUpdatePeerInfo(circle, retiree)) {
1985            CFErrorRef cleanupError = NULL;
1986            SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retiree, &cleanupError);
1987            secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
1988            CFReleaseSafe(cleanupError);
1989        }
1990    });
1991}
1992
1993static bool sosAccountLeaveCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
1994    SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircle(account, circle, NULL);
1995    if(!fpi) return false;
1996	CFErrorRef localError = NULL;
1997	SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);;
1998	CFStringRef retire_key = SOSRetirementKeyCreateWithCircleAndPeer(circle, SOSPeerInfoGetPeerID(retire_peer));
1999	CFDataRef retire_value = NULL;
2000    bool retval = false;
2001    bool writeCircle = false;
2002
2003    // Create a Retirement Ticket and store it in the retired_peers of the account.
2004    require_action_quiet(retire_peer, errout, secerror("Create ticket failed for peer %@: %@", fpi, localError));
2005	retire_value = SOSPeerInfoCopyEncodedData(retire_peer, NULL, &localError);
2006    require_action_quiet(retire_value, errout, secerror("Failed to encode retirement peer %@: %@", retire_peer, localError));
2007
2008    // See if we need to repost the circle we could either be an applicant or a peer already in the circle
2009    if(SOSCircleHasApplicant(circle, retire_peer, NULL)) {
2010	    // Remove our application if we have one.
2011	    SOSCircleWithdrawRequest(circle, retire_peer, NULL);
2012        writeCircle = true;
2013    } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
2014        if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
2015            CFErrorRef cleanupError = NULL;
2016            SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retire_peer, &cleanupError);
2017            secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
2018            CFReleaseSafe(cleanupError);
2019        }
2020        writeCircle = true;
2021    }
2022
2023    // Store the retirement record locally.
2024    CFDictionarySetValue(account->retired_peers, retire_key, retire_value);
2025
2026    // Write pending change to KVS
2027    CFDictionarySetValue(account->pending_changes, retire_key, retire_value);
2028
2029    // Kill peer key but don't return error if we can't.
2030    if(!SOSAccountDestroyCirclePeerInfo(account, circle, &localError))
2031        secerror("Couldn't purge key for peer %@ on retirement: %@", fpi, localError);
2032
2033    if (writeCircle) {
2034        SOSAccountAddCircleToPending(account, circle, NULL);
2035    }
2036    retval = true;
2037
2038errout:
2039    CFReleaseNull(localError);
2040    CFReleaseNull(retire_peer);
2041    CFReleaseNull(retire_key);
2042    CFReleaseNull(retire_value);
2043    return retval;
2044}
2045
2046/*
2047    NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
2048    local value that has been overwritten by a distant value. If there is no
2049    conflict between the local and the distant values when doing the initial
2050    sync (e.g. if the cloud has no data stored or the client has not stored
2051    any data yet), you'll never see that notification.
2052
2053    NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
2054    with server but initial round trip with server does not imply
2055    NSUbiquitousKeyValueStoreInitialSyncChange.
2056 */
2057
2058//
2059// MARK: Handle Circle Updates
2060//
2061
2062
2063static bool SOSAccountHandleUpdateCircle(SOSAccountRef account, SOSCircleRef prospective_circle, bool writeUpdate, bool initialSync, CFErrorRef *error)
2064{
2065    bool success = true;
2066
2067    secnotice("signing", "start: %@", prospective_circle);
2068    if (!account->user_public || !account->user_public_trusted) {
2069        SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Can't handle updates with no trusted public key here"), NULL, error);
2070        return false;
2071    }
2072
2073    if (!prospective_circle) {
2074        secerror("##### Can't update to a NULL circle ######");
2075        return false; // Can't update one we don't have.
2076    }
2077
2078    CFStringRef newCircleName = SOSCircleGetName(prospective_circle);
2079    SOSCircleRef oldCircle = SOSAccountFindCompatibleCircle(account, newCircleName);
2080    SOSFullPeerInfoRef me_full = SOSAccountGetMyFullPeerInCircle(account, oldCircle, NULL);
2081    SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(me_full);
2082
2083    if (initialSync)
2084        secerror("##### Processing initial sync. Old (local) circle: %@, New (cloud) circle: %@", oldCircle, prospective_circle);
2085
2086    if (!oldCircle)
2087        return false; // Can't update one we don't have.
2088
2089    SOSAccountScanForRetired(account, prospective_circle, error);
2090    SOSCircleRef newCircle = SOSAccountCloneCircleWithRetirement(account, prospective_circle, error);
2091    if(!newCircle) return false;
2092
2093    SOSCircleUpdatePeerInfo(newCircle, me);
2094
2095    typedef enum {
2096        accept,
2097        countersign,
2098        leave,
2099        revert,
2100        ignore
2101    } circle_action_t;
2102
2103    circle_action_t circle_action = ignore;
2104    enum DepartureReason leave_reason = kSOSNeverLeftCircle;
2105
2106    SecKeyRef old_circle_key = NULL;
2107    if(SOSCircleVerify(oldCircle, account->user_public, NULL)) old_circle_key = account->user_public;
2108    else if(account->previous_public && SOSCircleVerify(oldCircle, account->previous_public, NULL)) old_circle_key = account->previous_public;
2109    bool userTrustedOldCircle = (old_circle_key != NULL);
2110
2111    SOSConcordanceStatus concstat =
2112        SOSCircleConcordanceTrust(oldCircle, newCircle,
2113                                  old_circle_key, account->user_public,
2114                                  me, error);
2115
2116    CFStringRef concStr = NULL;
2117    switch(concstat) {
2118        case kSOSConcordanceTrusted:
2119            circle_action = countersign;
2120            concStr = CFSTR("Trusted");
2121            break;
2122        case kSOSConcordanceGenOld:
2123            circle_action = userTrustedOldCircle ? revert : ignore;
2124            concStr = CFSTR("Generation Old");
2125            break;
2126        case kSOSConcordanceBadUserSig:
2127        case kSOSConcordanceBadPeerSig:
2128            circle_action = userTrustedOldCircle ? revert : accept;
2129            concStr = CFSTR("Bad Signature");
2130            break;
2131        case kSOSConcordanceNoUserSig:
2132            circle_action = userTrustedOldCircle ? revert : accept;
2133            concStr = CFSTR("No User Signature");
2134            break;
2135        case kSOSConcordanceNoPeerSig:
2136            circle_action = accept; // We might like this one eventually but don't countersign.
2137            concStr = CFSTR("No trusted peer signature");
2138            secerror("##### No trusted peer signature found, accepting hoping for concordance later %@", newCircle);
2139            break;
2140        case kSOSConcordanceNoPeer:
2141            circle_action = leave;
2142            leave_reason = kSOSLeftUntrustedCircle;
2143            concStr = CFSTR("No trusted peer left");
2144            break;
2145        case kSOSConcordanceNoUserKey:
2146            secerror("##### No User Public Key Available, this shouldn't ever happen!!!");
2147            abort();
2148            break;
2149        default:
2150            secerror("##### Bad Error Return from ConcordanceTrust");
2151            abort();
2152            break;
2153    }
2154
2155    secnotice("signing", "Decided on action %d based on concordance state %d and %s circle.", circle_action, concstat, userTrustedOldCircle ? "trusted" : "untrusted");
2156
2157    SOSCircleRef circleToPush = NULL;
2158
2159    if (circle_action == leave) {
2160        circle_action = ignore;
2161
2162        if (me && SOSCircleHasPeer(oldCircle, me, NULL)) {
2163            if (sosAccountLeaveCircle(account, newCircle, error)) {
2164                account->departure_code = leave_reason;
2165                circleToPush = newCircle;
2166                circle_action = accept;
2167                me = NULL;
2168                me_full = NULL;
2169            }
2170        }
2171        else {
2172            // We are not in this circle, but we need to update account with it, since we got it from cloud
2173            secnotice("updatecircle", "We are not in this circle, but we need to update account with it");
2174            circle_action = accept;
2175        }
2176    }
2177
2178    if (circle_action == countersign) {
2179        if (me && SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleVerifyPeerSigned(newCircle, me, NULL)) {
2180            CFErrorRef signing_error = NULL;
2181
2182            if (me_full && SOSCircleConcordanceSign(newCircle, me_full, &signing_error)) {
2183                circleToPush = newCircle;
2184                secnotice("signing", "Concurred with: %@", newCircle);
2185            } else {
2186                secerror("Failed to concurrence sign, error: %@  Old: %@ New: %@", signing_error, oldCircle, newCircle);
2187            }
2188            CFReleaseSafe(signing_error);
2189        }
2190        circle_action = accept;
2191    }
2192
2193    if (circle_action == accept) {
2194        if (me && SOSCircleHasActivePeer(oldCircle, me, NULL) && !SOSCircleHasPeer(newCircle, me, NULL)) {
2195            //  Don't destroy evidence of other code determining reason for leaving.
2196            if(!SOSAccountHasLeft(account)) account->departure_code = kSOSMembershipRevoked;
2197        }
2198
2199        if (me
2200            && SOSCircleHasActivePeer(oldCircle, me, NULL)
2201            && !(SOSCircleCountPeers(oldCircle) == 1 && SOSCircleHasPeer(oldCircle, me, NULL)) // If it was our offering, don't change ID to avoid ghosts
2202            && !SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleHasApplicant(newCircle, me, NULL)) {
2203            secnotice("circle", "Purging my peer (ID: %@) for circle '%@'!!!", SOSPeerInfoGetPeerID(me), SOSCircleGetName(oldCircle));
2204            SOSAccountDestroyCirclePeerInfo(account, oldCircle, NULL);
2205            me = NULL;
2206            me_full = NULL;
2207        }
2208
2209        if (me && SOSCircleHasRejectedApplicant(newCircle, me, NULL)) {
2210            SOSPeerInfoRef  reject = SOSCircleCopyRejectedApplicant(newCircle, me, NULL);
2211            if(CFEqualSafe(reject, me) && SOSPeerInfoApplicationVerify(me, account->user_public, NULL)) {
2212                secnotice("circle", "Rejected, Purging my applicant peer (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me), SOSCircleGetName(oldCircle));
2213                SOSAccountDestroyCirclePeerInfo(account, oldCircle, NULL);
2214                me = NULL;
2215                me_full = NULL;
2216            } else {
2217                SOSCircleRequestReadmission(newCircle, account->user_public, me_full, NULL);
2218                writeUpdate = true;
2219            }
2220        }
2221
2222        CFRetain(oldCircle); // About to replace the oldCircle
2223        CFDictionarySetValue(account->circles, newCircleName, newCircle);
2224        SOSAccountSetPreviousPublic(account);
2225
2226        secnotice("signing", "%@, Accepting circle: %@", concStr, newCircle);
2227
2228        if (me_full && account->user_public_trusted
2229            && SOSCircleHasApplicant(oldCircle, me, NULL)
2230            && SOSCircleCountPeers(newCircle) > 0
2231            && !SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleHasApplicant(newCircle, me, NULL)) {
2232            // We weren't rejected (above would have set me to NULL.
2233            // We were applying and we weren't accepted.
2234            // Our application is declared lost, let us reapply.
2235
2236            if (SOSCircleRequestReadmission(newCircle, account->user_public, me_full, NULL))
2237                writeUpdate = true;
2238        }
2239
2240        if (me && SOSCircleHasActivePeer(oldCircle, me, NULL)) {
2241            SOSAccountCleanupRetirementTickets(account, RETIREMENT_FINALIZATION_SECONDS, NULL);
2242        }
2243
2244        SOSAccountNotifyOfChange(account, oldCircle, newCircle);
2245
2246        CFReleaseNull(oldCircle);
2247
2248        if (writeUpdate)
2249            circleToPush = newCircle;
2250
2251        success = SOSUpdateKeyInterest(account, true, error);
2252    }
2253
2254    if (circle_action == revert) {
2255        secnotice("signing", "%@, Rejecting: %@ re-publishing %@", concStr, newCircle, oldCircle);
2256
2257        circleToPush = oldCircle;
2258    }
2259
2260
2261    if (circleToPush != NULL) {
2262        success = (success
2263                   && SOSAccountAddCircleToPending(account, circleToPush, error)
2264                   && SOSAccountSendPendingChanges(account, error));
2265    }
2266
2267    CFReleaseSafe(newCircle);
2268
2269    return success;
2270}
2271
2272static bool SOSAccountUpdateCircleFromRemote(SOSAccountRef account, SOSCircleRef newCircle, bool initialSync, CFErrorRef *error)
2273{
2274    return SOSAccountHandleUpdateCircle(account, newCircle, false, initialSync, error);
2275}
2276
2277bool SOSAccountUpdateCircle(SOSAccountRef account, SOSCircleRef newCircle, CFErrorRef *error)
2278{
2279    return SOSAccountHandleUpdateCircle(account, newCircle, true, false, error);
2280}
2281
2282bool SOSAccountModifyCircle(SOSAccountRef account,
2283                            CFStringRef circleName,
2284                            CFErrorRef* error,
2285                            void (^action)(SOSCircleRef circle))
2286{
2287    bool success = false;
2288
2289    SOSCircleRef circle = NULL;
2290    SOSCircleRef accountCircle = SOSAccountFindCircle(account, circleName, error);
2291    require_quiet(accountCircle, fail);
2292
2293    circle = SOSCircleCopyCircle(kCFAllocatorDefault, accountCircle, error);
2294    require_quiet(circle, fail);
2295
2296    action(circle);
2297    success = SOSAccountUpdateCircle(account, circle, error);
2298
2299fail:
2300    CFReleaseSafe(circle);
2301    return success;
2302}
2303
2304static SOSCircleRef SOSAccountCreateCircleFrom(CFStringRef circleName, CFTypeRef value, CFErrorRef *error) {
2305    if (value && !isData(value) && !isNull(value)) {
2306        CFStringRef description = CFCopyTypeIDDescription(CFGetTypeID(value));
2307        SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, NULL, error, NULL,
2308                                 CFSTR("Expected data or NULL got %@"), description);
2309        CFReleaseSafe(description);
2310        return NULL;
2311    }
2312
2313    SOSCircleRef circle = NULL;
2314    if (!value || isNull(value)) {
2315        circle = SOSCircleCreate(kCFAllocatorDefault, circleName, error);
2316    } else {
2317        circle = SOSCircleCreateFromData(NULL, (CFDataRef) value, error);
2318        if (circle) {
2319            CFStringRef name = SOSCircleGetName(circle);
2320            if (!CFEqualSafe(name, circleName)) {
2321                SOSCreateErrorWithFormat(kSOSErrorNameMismatch, NULL, error, NULL,
2322                                     CFSTR("Expected circle named %@, got %@"), circleName, name);
2323                CFReleaseNull(circle);
2324            }
2325        }
2326    }
2327    return circle;
2328}
2329
2330static SOSCCStatus SOSCCCircleStatus(SOSCircleRef circle)
2331{
2332    if (SOSCircleCountPeers(circle) == 0)
2333        return kSOSCCCircleAbsent;
2334
2335    return kSOSCCNotInCircle;
2336}
2337
2338static SOSCCStatus SOSCCThisDeviceStatusInCircle(SOSCircleRef circle, SOSPeerInfoRef this_peer)
2339{
2340    if (SOSCircleCountPeers(circle) == 0)
2341        return kSOSCCCircleAbsent;
2342
2343    if (SOSCircleHasPeer(circle, this_peer, NULL))
2344        return kSOSCCInCircle;
2345
2346    if (SOSCircleHasApplicant(circle, this_peer, NULL))
2347        return kSOSCCRequestPending;
2348
2349    return kSOSCCNotInCircle;
2350}
2351
2352static SOSCCStatus UnionStatus(SOSCCStatus accumulated_status, SOSCCStatus additional_circle_status)
2353{
2354    switch (additional_circle_status) {
2355        case kSOSCCInCircle:
2356            return accumulated_status;
2357        case kSOSCCRequestPending:
2358            return (accumulated_status == kSOSCCInCircle) ?
2359            kSOSCCRequestPending :
2360            accumulated_status;
2361        case kSOSCCNotInCircle:
2362            return (accumulated_status == kSOSCCInCircle ||
2363                    accumulated_status == kSOSCCRequestPending) ?
2364            kSOSCCNotInCircle :
2365            accumulated_status;
2366        case kSOSCCCircleAbsent:
2367            return (accumulated_status == kSOSCCInCircle ||
2368                    accumulated_status == kSOSCCRequestPending ||
2369                    accumulated_status == kSOSCCNotInCircle) ?
2370            kSOSCCCircleAbsent :
2371            accumulated_status;
2372        default:
2373            return additional_circle_status;
2374    };
2375
2376}
2377
2378SOSCCStatus SOSAccountIsInCircles(SOSAccountRef account, CFErrorRef* error)
2379{
2380    if (!SOSAccountHasPublicKey(account, error)) {
2381        return kSOSCCError;
2382    }
2383
2384    __block bool set_once = false;
2385    __block SOSCCStatus status = kSOSCCInCircle;
2386
2387    SOSAccountForEachKnownCircle(account, ^(CFStringRef name) {
2388        set_once = true;
2389        status = kSOSCCError;
2390        SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("Incompatible circle"), NULL, error);
2391    }, ^(SOSCircleRef circle) {
2392        set_once = true;
2393        status = UnionStatus(status, SOSCCCircleStatus(circle));
2394    }, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) {
2395        set_once = true;
2396        SOSCCStatus circle_status = SOSCCThisDeviceStatusInCircle(circle, SOSFullPeerInfoGetPeerInfo(full_peer));
2397        status = UnionStatus(status, circle_status);
2398    });
2399
2400    if (!set_once)
2401        status = kSOSCCCircleAbsent;
2402
2403    return status;
2404}
2405
2406static SOSPeerInfoRef GenerateNewCloudIdentityPeerInfo(CFErrorRef *error) {
2407    SecKeyRef cloud_key = GeneratePermanentFullECKeyForCloudIdentity(256, kicloud_identity_name, error);
2408    SOSPeerInfoRef cloud_peer = NULL;
2409    CFDictionaryRef query = NULL;
2410    CFDictionaryRef change = NULL;
2411    CFStringRef new_name = NULL;
2412
2413    CFDictionaryRef gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
2414                                                           kPIUserDefinedDeviceName, CFSTR("iCloud"),
2415                                                           NULL);
2416    require_action_quiet(gestalt, fail, SecError(errSecAllocate, error, CFSTR("Can't allocate gestalt")));
2417
2418    cloud_peer = SOSPeerInfoCreateCloudIdentity(kCFAllocatorDefault, gestalt, cloud_key, error);
2419
2420    require(cloud_peer, fail);
2421
2422    query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
2423                                         kSecClass,             kSecClassKey,
2424                                         kSecAttrSynchronizable,kCFBooleanTrue,
2425                                         kSecUseTombstones,     kCFBooleanTrue,
2426                                         kSecValueRef,          cloud_key,
2427                                         NULL);
2428
2429    new_name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
2430                                        CFSTR("Cloud Identity - '%@'"), SOSPeerInfoGetPeerID(cloud_peer));
2431
2432    change = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
2433                                          kSecAttrLabel,        new_name,
2434                                          NULL);
2435
2436    SecError(SecItemUpdate(query, change), error, CFSTR("Couldn't update name"));
2437
2438fail:
2439    CFReleaseNull(new_name);
2440    CFReleaseNull(query);
2441    CFReleaseNull(change);
2442    CFReleaseNull(gestalt);
2443    CFReleaseNull(cloud_key);
2444
2445    return cloud_peer;
2446}
2447
2448static SOSFullPeerInfoRef CopyCloudKeychainIdentity(SOSPeerInfoRef cloudPeer, CFErrorRef *error) {
2449    return SOSFullPeerInfoCreateCloudIdentity(NULL, cloudPeer, error);
2450}
2451
2452static bool SOSAccountResetThisCircleToOffering(SOSAccountRef account, SOSCircleRef circle, SecKeyRef user_key, CFErrorRef *error) {
2453    SOSFullPeerInfoRef myCirclePeer = SOSAccountGetMyFullPeerInCircle(account, circle, error);
2454    if (!myCirclePeer)
2455        return false;
2456
2457    SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) {
2458        bool result = false;
2459        SOSFullPeerInfoRef cloud_identity = NULL;
2460        CFErrorRef localError = NULL;
2461
2462        require_quiet(SOSCircleResetToOffering(circle, user_key, myCirclePeer, &localError), err_out);
2463
2464        {
2465            SOSPeerInfoRef cloud_peer = GenerateNewCloudIdentityPeerInfo(error);
2466            require_quiet(cloud_peer, err_out);
2467            cloud_identity = CopyCloudKeychainIdentity(cloud_peer, error);
2468            require_quiet(cloud_identity, err_out);
2469        }
2470
2471        account->departure_code = kSOSNeverLeftCircle;
2472        require_quiet(SOSCircleRequestAdmission(circle, user_key, cloud_identity, &localError), err_out);
2473        require_quiet(SOSCircleAcceptRequest(circle, user_key, myCirclePeer, SOSFullPeerInfoGetPeerInfo(cloud_identity), &localError), err_out);
2474        result = true;
2475        SOSAccountPublishCloudParameters(account, NULL);
2476
2477    err_out:
2478        if (result == false)
2479            secerror("error resetting circle (%@) to offering: %@", circle, localError);
2480        if (localError && error && *error == NULL) {
2481            *error = localError;
2482            localError = NULL;
2483        }
2484        CFReleaseNull(localError);
2485        CFReleaseNull(cloud_identity);
2486    });
2487
2488    return true;
2489}
2490
2491static bool SOSAccountJoinThisCircle(SOSAccountRef account, SecKeyRef user_key,
2492                                     SOSCircleRef circle, bool use_cloud_peer, CFErrorRef* error) {
2493    __block bool result = false;
2494    __block SOSFullPeerInfoRef cloud_full_peer = NULL;
2495
2496    SOSFullPeerInfoRef myCirclePeer = SOSAccountGetMyFullPeerInCircle(account, circle, error);
2497    require_action_quiet(myCirclePeer, fail,
2498                         SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Can't find/create peer for circle: %@"), circle));
2499    if (use_cloud_peer) {
2500        cloud_full_peer = SOSCircleGetiCloudFullPeerInfoRef(circle);
2501    }
2502
2503    if (SOSCircleCountPeers(circle) == 0) {
2504        result = SOSAccountResetThisCircleToOffering(account, circle, user_key, error);
2505    } else {
2506        SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) {
2507            result = SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
2508            account->departure_code = kSOSNeverLeftCircle;
2509            if(result && cloud_full_peer) {
2510                CFErrorRef localError = NULL;
2511                CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
2512                require_quiet(cloudid, finish);
2513                require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
2514                require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
2515            finish:
2516                if (localError){
2517                    secerror("Failed to join with cloud identity: %@", localError);
2518                    CFReleaseNull(localError);
2519                }
2520            }
2521        });
2522    }
2523
2524fail:
2525    CFReleaseNull(cloud_full_peer);
2526    return result;
2527}
2528
2529static bool SOSAccountJoinCircles_internal(SOSAccountRef account, bool use_cloud_identity, CFErrorRef* error) {
2530    SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
2531    if (!user_key)
2532        return false;
2533
2534    __block bool success = true;
2535
2536    SOSAccountForEachKnownCircle(account, ^(CFStringRef name) { // Incompatible
2537        success = false;
2538        SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("Incompatible circle"), NULL, error);
2539    }, ^(SOSCircleRef circle) {                                 // No Peer
2540        success = SOSAccountJoinThisCircle(account, user_key, circle, use_cloud_identity, error) && success;
2541    }, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) {   // Have Peer
2542        SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(full_peer);
2543        if(SOSCircleHasPeer(circle, myPeer, NULL)) goto already_present;
2544        if(SOSCircleHasApplicant(circle, myPeer, NULL))  goto already_applied;
2545        if(SOSCircleHasRejectedApplicant(circle, myPeer, NULL)) {
2546            SOSCircleRemoveRejectedPeer(circle, myPeer, NULL);
2547        }
2548
2549        secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(circle));
2550        CFErrorRef localError = NULL;
2551        if (!SOSAccountDestroyCirclePeerInfo(account, circle, &localError)) {
2552            secerror("Failed to destroy peer (%@) during application, error=%@", myPeer, localError);
2553            CFReleaseNull(localError);
2554        }
2555    already_applied:
2556        success = SOSAccountJoinThisCircle(account, user_key, circle, use_cloud_identity, error) && success;
2557        return;
2558    already_present:
2559        success = true;
2560        return;
2561    });
2562
2563    if(success) account->departure_code = kSOSNeverLeftCircle;
2564
2565    return success;
2566}
2567
2568bool SOSAccountJoinCircles(SOSAccountRef account, CFErrorRef* error) {
2569    return SOSAccountJoinCircles_internal(account, false, error);
2570}
2571
2572
2573bool SOSAccountJoinCirclesAfterRestore(SOSAccountRef account, CFErrorRef* error) {
2574    return SOSAccountJoinCircles_internal(account, true, error);
2575}
2576
2577
2578bool SOSAccountLeaveCircles(SOSAccountRef account, CFErrorRef* error)
2579{
2580    __block bool result = true;
2581
2582    SOSAccountForEachKnownCircle(account, NULL, NULL, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer) {
2583        SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) {
2584		result = sosAccountLeaveCircle(account, circle, error); // TODO: What about multiple errors!
2585		});
2586    });
2587
2588    account->departure_code = kSOSWithdrewMembership;
2589
2590    return SOSAccountSendPendingChanges(account, error) && result;
2591}
2592
2593bool SOSAccountBail(SOSAccountRef account, uint64_t limit_in_seconds, CFErrorRef* error)
2594{
2595    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2596    dispatch_group_t group = dispatch_group_create();
2597    __block bool result = false;
2598
2599    secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds);
2600    // Add a task to the group
2601    dispatch_group_async(group, queue, ^{
2602        SOSAccountForEachKnownCircle(account, NULL, NULL, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer) {
2603            SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) {
2604                result = sosAccountLeaveCircle(account, circle, error); // TODO: What about multiple errors!
2605            });
2606        });
2607
2608        account->departure_code = kSOSWithdrewMembership;
2609        if(result) result = SOSAccountSendPendingChanges(account, error);
2610    });
2611    dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
2612
2613    dispatch_group_wait(group, milestone);
2614    dispatch_release(group);
2615    return result;
2616}
2617
2618
2619static void for_each_applicant_in_each_circle(SOSAccountRef account, CFArrayRef peer_infos,
2620                                              void (^action)(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer)) {
2621    SOSAccountForEachKnownCircle(account, NULL, NULL, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) {
2622        SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(full_peer);
2623        CFErrorRef peer_error = NULL;
2624        if (SOSCircleHasPeer(circle, me, &peer_error)) {
2625            CFArrayForEach(peer_infos, ^(const void *value) {
2626                SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
2627                if (SOSCircleHasApplicant(circle, peer, NULL)) {
2628                    SOSAccountModifyCircle(account, SOSCircleGetName(circle), NULL, ^(SOSCircleRef circle) {
2629                        action(circle, full_peer, peer);
2630                    });
2631                }
2632            });
2633        }
2634        if (peer_error)
2635            secerror("Got error in SOSCircleHasPeer: %@", peer_error);
2636        CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here.
2637    });
2638}
2639
2640bool SOSAccountAcceptApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
2641    SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
2642    if (!user_key)
2643        return false;
2644
2645    __block bool success = true;
2646	__block int64_t num_peers = 0;
2647
2648    for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
2649        if (!SOSCircleAcceptRequest(circle, user_key, myCirclePeer, peer, error))
2650            success = false;
2651		else
2652			num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
2653    });
2654
2655    return success;
2656}
2657
2658bool SOSAccountRejectApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
2659    __block bool success = true;
2660	__block int64_t num_peers = 0;
2661
2662    for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
2663        if (!SOSCircleRejectRequest(circle, myCirclePeer, peer, error))
2664            success = false;
2665		else
2666			num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
2667    });
2668
2669    return success;
2670}
2671
2672bool SOSAccountResetToOffering(SOSAccountRef account, CFErrorRef* error) {
2673    SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
2674    if (!user_key)
2675        return false;
2676
2677    __block bool result = true;
2678
2679    SOSAccountForEachKnownCircle(account, ^(CFStringRef name) {
2680        SOSCircleRef circle = SOSCircleCreate(NULL, name, NULL);
2681        if (circle)
2682            CFDictionaryAddValue(account->circles, name, circle);
2683
2684        SOSAccountResetThisCircleToOffering(account, circle, user_key, error);
2685    }, ^(SOSCircleRef circle) {
2686        SOSAccountResetThisCircleToOffering(account, circle, user_key, error);
2687    }, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) {
2688        SOSAccountResetThisCircleToOffering(account, circle, user_key, error);
2689    });
2690
2691    return result;
2692}
2693
2694bool SOSAccountResetToEmpty(SOSAccountRef account, CFErrorRef* error) {
2695    if (!SOSAccountHasPublicKey(account, error))
2696        return false;
2697
2698    __block bool result = true;
2699    SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
2700        SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) {
2701            if (!SOSCircleResetToEmpty(circle, error))
2702            {
2703                secerror("error: %@", *error);
2704                result = false;
2705            }
2706            account->departure_code = kSOSWithdrewMembership;
2707        });
2708    });
2709
2710    return result;
2711}
2712
2713CFArrayRef SOSAccountCopyApplicants(SOSAccountRef account, CFErrorRef *error) {
2714    if (!SOSAccountHasPublicKey(account, error))
2715        return NULL;
2716    CFMutableArrayRef applicants = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2717
2718    SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
2719        SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) {
2720            CFArrayAppendValue(applicants, peer);
2721        });
2722    });
2723
2724    return applicants;
2725}
2726
2727CFArrayRef SOSAccountCopyPeers(SOSAccountRef account, CFErrorRef *error) {
2728    if (!SOSAccountHasPublicKey(account, error))
2729        return NULL;
2730
2731    CFMutableArrayRef peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2732
2733    SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
2734        SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
2735            CFArrayAppendValue(peers, peer);
2736        });
2737    });
2738
2739    return peers;
2740}
2741
2742CFArrayRef SOSAccountCopyActivePeers(SOSAccountRef account, CFErrorRef *error) {
2743    if (!SOSAccountHasPublicKey(account, error))
2744        return NULL;
2745
2746    CFMutableArrayRef peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2747
2748    SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
2749        SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
2750            CFArrayAppendValue(peers, peer);
2751        });
2752    });
2753
2754    return peers;
2755}
2756
2757CFArrayRef SOSAccountCopyActiveValidPeers(SOSAccountRef account, CFErrorRef *error) {
2758    if (!SOSAccountHasPublicKey(account, error))
2759        return NULL;
2760
2761    CFMutableArrayRef peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2762
2763    SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
2764        SOSCircleForEachActiveValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
2765            CFArrayAppendValue(peers, peer);
2766        });
2767    });
2768
2769    return peers;
2770}
2771
2772
2773CFArrayRef SOSAccountCopyConcurringPeers(SOSAccountRef account, CFErrorRef *error)
2774{
2775    if (!SOSAccountHasPublicKey(account, error))
2776        return NULL;
2777
2778    CFMutableArrayRef concurringPeers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2779
2780    SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
2781        CFMutableArrayRef circleConcurring = SOSCircleCopyConcurringPeers(circle, NULL);
2782        CFArrayAppendArray(concurringPeers, circleConcurring, CFRangeMake(0, CFArrayGetCount(circleConcurring)));
2783        CFReleaseSafe(circleConcurring);
2784    });
2785
2786    return concurringPeers;
2787}
2788
2789CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccountRef account, CFErrorRef* error)
2790{
2791    return CFSTR("We're compatible, go away");
2792}
2793
2794enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccountRef account, CFErrorRef* error)
2795{
2796    return account->departure_code;
2797}
2798
2799//
2800// TODO: Handle '|' and "¬" in other strings.
2801//
2802const CFStringRef kSOSKVSKeyParametersKey = CFSTR(">KeyParameters");
2803const CFStringRef kSOSKVSInitialSyncKey = CFSTR("^InitialSync");
2804const CFStringRef kSOSKVSAccountChangedKey = CFSTR("^AccountChanged");
2805
2806const CFStringRef sWarningPrefix = CFSTR("!");
2807const CFStringRef sAncientCirclePrefix = CFSTR("@");
2808const CFStringRef sCirclePrefix = CFSTR("o");
2809const CFStringRef sRetirementPrefix = CFSTR("-");
2810const CFStringRef sCircleSeparator = CFSTR("|");
2811const CFStringRef sFromToSeparator = CFSTR(":");
2812
2813static CFStringRef stringEndingIn(CFMutableStringRef in, CFStringRef token) {
2814    if(token == NULL) return CFStringCreateCopy(NULL, in);
2815    CFRange tokenAt = CFStringFind(in, token, 0);
2816    if(tokenAt.location == kCFNotFound) return NULL;
2817    CFStringRef retval = CFStringCreateWithSubstring(NULL, in, CFRangeMake(0, tokenAt.location));
2818    CFStringDelete(in, CFRangeMake(0, tokenAt.location+1));
2819    return retval;
2820}
2821
2822SOSKVSKeyType SOSKVSKeyGetKeyTypeAndParse(CFStringRef key, CFStringRef *circle, CFStringRef *from, CFStringRef *to)
2823{
2824    SOSKVSKeyType retval = kUnknownKey;
2825
2826    if(CFStringHasPrefix(key, sCirclePrefix)) retval = kCircleKey;
2827    else if(CFStringHasPrefix(key, sRetirementPrefix)) retval = kRetirementKey;
2828    else if(CFStringHasPrefix(key, kSOSKVSKeyParametersKey)) retval = kParametersKey;
2829    else if(CFStringHasPrefix(key, kSOSKVSInitialSyncKey)) retval = kInitialSyncKey;
2830    else if(CFStringHasPrefix(key, kSOSKVSAccountChangedKey)) retval = kAccountChangedKey;
2831    else retval = kMessageKey;
2832
2833    switch(retval) {
2834        case kCircleKey:
2835            if (circle) {
2836                CFRange fromRange = CFRangeMake(1, CFStringGetLength(key)-1);
2837                *circle = CFStringCreateWithSubstring(NULL, key, fromRange);
2838            }
2839            break;
2840        case kMessageKey: {
2841                CFStringRef mCircle = NULL;
2842                CFStringRef mFrom = NULL;
2843                CFStringRef mTo = NULL;
2844                CFMutableStringRef keycopy = CFStringCreateMutableCopy(NULL, 128, key);
2845
2846                if( ((mCircle = stringEndingIn(keycopy, sCircleSeparator)) != NULL) &&
2847                    ((mFrom = stringEndingIn(keycopy, sFromToSeparator)) != NULL) &&
2848                    (CFStringGetLength(mFrom) > 0)  ) {
2849                        mTo = stringEndingIn(keycopy, NULL);
2850                        if (circle) *circle = CFStringCreateCopy(NULL, mCircle);
2851                        if (from) *from = CFStringCreateCopy(NULL, mFrom);
2852                        if (to && mTo) *to = CFStringCreateCopy(NULL, mTo);
2853                } else {
2854                    retval = kUnknownKey;
2855                }
2856                CFReleaseNull(mCircle);
2857                CFReleaseNull(mFrom);
2858                CFReleaseNull(mTo);
2859                CFReleaseNull(keycopy);
2860            }
2861            break;
2862        case kRetirementKey: {
2863                CFStringRef mCircle = NULL;
2864                CFStringRef mPeer = NULL;
2865                CFMutableStringRef keycopy = CFStringCreateMutableCopy(NULL, 128, key);
2866                CFStringDelete(keycopy, CFRangeMake(0, 1));
2867                if( ((mCircle = stringEndingIn(keycopy, sCircleSeparator)) != NULL) &&
2868                    ((mPeer = stringEndingIn(keycopy, NULL)) != NULL)) {
2869                    if (circle) *circle = CFStringCreateCopy(NULL, mCircle);
2870                    if (from) *from = CFStringCreateCopy(NULL, mPeer);
2871                } else {
2872                    retval = kUnknownKey;
2873                }
2874                // TODO - Update our circle
2875                CFReleaseNull(mCircle);
2876                CFReleaseNull(mPeer);
2877                CFReleaseNull(keycopy);
2878            }
2879            break;
2880        case kAccountChangedKey:
2881        case kParametersKey:
2882        case kInitialSyncKey:
2883        case kUnknownKey:
2884            break;
2885    }
2886
2887    return retval;
2888}
2889
2890
2891SOSKVSKeyType SOSKVSKeyGetKeyType(CFStringRef key)
2892{
2893    return SOSKVSKeyGetKeyTypeAndParse(key, NULL, NULL, NULL);
2894}
2895
2896CFStringRef SOSCircleKeyCreateWithCircle(SOSCircleRef circle, CFErrorRef *error)
2897{
2898    return SOSCircleKeyCreateWithName(SOSCircleGetName(circle), error);
2899}
2900
2901
2902CFStringRef SOSCircleKeyCreateWithName(CFStringRef circleName, CFErrorRef *error)
2903{
2904    return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), sCirclePrefix, circleName);
2905}
2906
2907CFStringRef SOSCircleKeyCopyCircleName(CFStringRef key, CFErrorRef *error)
2908{
2909    CFStringRef circleName = NULL;
2910
2911    if (kCircleKey != SOSKVSKeyGetKeyTypeAndParse(key, &circleName, NULL, NULL)) {
2912        SOSCreateErrorWithFormat(kSOSErrorNoCircleName, NULL, error, NULL, CFSTR("Couldn't find circle name in key '%@'"), key);
2913
2914        CFReleaseNull(circleName);
2915    }
2916
2917    return circleName;
2918}
2919
2920CFStringRef SOSMessageKeyCopyCircleName(CFStringRef key, CFErrorRef *error)
2921{
2922    CFStringRef circleName = NULL;
2923
2924    if (SOSKVSKeyGetKeyTypeAndParse(key, &circleName, NULL, NULL) != kMessageKey) {
2925        SOSCreateErrorWithFormat(kSOSErrorNoCircleName, NULL, error, NULL, CFSTR("Couldn't find circle name in key '%@'"), key);
2926
2927        CFReleaseNull(circleName);
2928    }
2929    return circleName;
2930}
2931
2932CFStringRef SOSMessageKeyCopyFromPeerName(CFStringRef messageKey, CFErrorRef *error)
2933{
2934    CFStringRef fromPeer = NULL;
2935
2936    if (SOSKVSKeyGetKeyTypeAndParse(messageKey, NULL, &fromPeer, NULL) != kMessageKey) {
2937        SOSCreateErrorWithFormat(kSOSErrorNoCircleName, NULL, error, NULL, CFSTR("Couldn't find from peer in key '%@'"), messageKey);
2938
2939        CFReleaseNull(fromPeer);
2940    }
2941    return fromPeer;
2942}
2943
2944CFStringRef SOSMessageKeyCreateWithCircleAndPeerNames(SOSCircleRef circle, CFStringRef from_peer_name, CFStringRef to_peer_name)
2945{
2946    return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@%@%@%@"),
2947                                    SOSCircleGetName(circle), sCircleSeparator, from_peer_name, sFromToSeparator, to_peer_name);
2948}
2949
2950CFStringRef SOSMessageKeyCreateWithCircleAndPeerInfos(SOSCircleRef circle, SOSPeerInfoRef from_peer, SOSPeerInfoRef to_peer)
2951{
2952    return SOSMessageKeyCreateWithCircleAndPeerNames(circle, SOSPeerInfoGetPeerID(from_peer), SOSPeerInfoGetPeerID(to_peer));
2953}
2954
2955CFStringRef SOSMessageKeyCreateWithAccountAndPeer(SOSAccountRef account, SOSCircleRef circle, CFStringRef peer_name) {
2956    // TODO: Handle errors!
2957    CFErrorRef error = NULL;
2958
2959    SOSFullPeerInfoRef me = SOSAccountGetMyFullPeerInCircle(account, circle, &error);
2960    SOSPeerInfoRef my_pi = SOSFullPeerInfoGetPeerInfo(me);
2961    CFStringRef result = SOSMessageKeyCreateWithCircleAndPeerNames(circle, SOSPeerInfoGetPeerID(my_pi), peer_name);
2962    CFReleaseSafe(error);
2963    return result;
2964}
2965
2966CFStringRef SOSRetirementKeyCreateWithCircleAndPeer(SOSCircleRef circle, CFStringRef retirement_peer_name)
2967{
2968     return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@%@%@"),
2969                                    sRetirementPrefix, SOSCircleGetName(circle), sCircleSeparator, retirement_peer_name);
2970}
2971
2972
2973static SOSPeerCoderStatus SOSAccountHandlePeerMessage(SOSAccountRef account,
2974                                        CFStringRef circle_id,
2975                                        CFStringRef peer_name,
2976                                        CFDataRef message,
2977                                        SOSAccountSendBlock send_block,
2978                                        CFErrorRef *error)
2979{
2980    bool success = false;
2981    CFStringRef peer_key = NULL;
2982
2983    SOSCircleRef circle = SOSAccountFindCircle(account, circle_id, error);
2984    require_quiet(circle, fail);
2985    SOSFullPeerInfoRef myFullPeer = SOSAccountGetMyFullPeerInCircle(account, circle, error);
2986    SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(myFullPeer);
2987    require_action_quiet(SOSCircleHasPeer(circle, myPeer, NULL), fail, SOSCreateErrorWithFormat(kSOSErrorNotReady, NULL, error, NULL, CFSTR("Not in circle, can't handle message")));
2988
2989    peer_key = SOSMessageKeyCreateWithAccountAndPeer(account, circle, peer_name);
2990
2991    SOSPeerSendBlock peer_send_block = ^bool (CFDataRef message, CFErrorRef *error) {
2992        return send_block(circle, peer_key, message, error);
2993    };
2994
2995    success = SOSCircleHandlePeerMessage(circle, myFullPeer, account->factory, peer_send_block, peer_name, message, error);
2996
2997fail:
2998    CFReleaseNull(peer_key);
2999    return success;
3000}
3001
3002bool SOSAccountHandleUpdates(SOSAccountRef account,
3003                             CFDictionaryRef updates,
3004                             CFErrorRef *error) {
3005
3006    if(CFDictionaryGetValue(updates, kSOSKVSAccountChangedKey) != NULL) {
3007        SOSAccountSetToNew(account);
3008    }
3009
3010    CFTypeRef parameters = CFDictionaryGetValue(updates, kSOSKVSKeyParametersKey);
3011    if (isData(parameters)) {
3012        SecKeyRef newKey = NULL;
3013        CFDataRef newParameters = NULL;
3014        const uint8_t *parse_end = der_decode_cloud_parameters(kCFAllocatorDefault, kSecECDSAAlgorithmID,
3015                                                               &newKey, &newParameters, error,
3016                                                               CFDataGetBytePtr(parameters), CFDataGetPastEndPtr(parameters));
3017
3018        if (parse_end == CFDataGetPastEndPtr(parameters)) {
3019            if (CFEqualSafe(account->user_public, newKey)) {
3020                secnotice("updates", "Got same public key sent our way. Ignoring.");
3021            } else if (CFEqualSafe(account->previous_public, newKey)) {
3022                secnotice("updates", "Got previous public key repeated. Ignoring.");
3023            } else {
3024                CFReleaseNull(account->user_public);
3025                SOSAccountPurgePrivateCredential(account);
3026                CFReleaseNull(account->user_key_parameters);
3027
3028                account->user_public_trusted = false;
3029
3030                account->user_public = newKey;
3031                newKey = NULL;
3032
3033                account->user_key_parameters = newParameters;
3034                newParameters = NULL;
3035
3036                secnotice("updates", "Got new parameters for public key: %@", account->user_public);
3037                debugDumpUserParameters(CFSTR("params"), account->user_key_parameters);
3038            }
3039        }
3040
3041        CFReleaseNull(newKey);
3042        CFReleaseNull(newParameters);
3043    }
3044
3045    if (!account->user_public_trusted) {
3046        if (!account->deferred_updates) {
3047            account->deferred_updates = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
3048        }
3049
3050        CFDictionaryForEach(updates, ^(const void *key, const void *value) {
3051            if (!CFEqualSafe(key, kSOSKVSKeyParametersKey) && !CFEqualSafe(key, kSOSKVSAccountChangedKey))
3052                CFDictionarySetValue(account->deferred_updates, key, value);
3053        });
3054        secnotice("updates", "No public peer key, deferring updates: %@", updates);
3055        return true;
3056    }
3057
3058    // Iterate though keys in updates.  Perform circle change update.
3059    // Then instantiate circles and engines and peers for all peers that
3060    // are receiving a message in updates.
3061    __block bool is_initial_sync = CFDictionaryContainsKey(updates, kSOSKVSInitialSyncKey);
3062
3063    CFDictionaryForEach(updates, ^(const void *key, const void *value) {
3064        CFStringRef circle_name = NULL;
3065        CFErrorRef localError = NULL;
3066        SOSCircleRef circle = NULL;
3067
3068        if (SOSKVSKeyGetKeyTypeAndParse(key, &circle_name, NULL, NULL) == kCircleKey) {
3069            circle = SOSAccountCreateCircleFrom(circle_name, value, &localError);
3070            if (!circle) {
3071                if (isSOSErrorCoded(localError, kSOSErrorIncompatibleCircle)) {
3072                    SOSAccountDestroyCirclePeerInfoNamed(account, circle_name, NULL);
3073                    CFDictionarySetValue(account->circles, circle_name, kCFNull);
3074                } else {
3075                    SOSCreateErrorWithFormat(kSOSErrorNameMismatch, localError, error, NULL,
3076                                         CFSTR("Bad key for message, no circle '%@'"), key);
3077                    goto circle_done;
3078                }
3079            }
3080
3081            if (!SOSAccountUpdateCircleFromRemote(account, circle, is_initial_sync, &localError)) {
3082                SOSCreateErrorWithFormat(kSOSErrorProcessingFailure, localError, error, NULL,
3083                                         CFSTR("Error handling circle change '%@'"), key);
3084                secnotice("update", "Error updating circle '%@': %@", key, circle);
3085                goto circle_done;
3086            }
3087        }
3088        circle_done:
3089        CFReleaseSafe(circle_name);
3090        CFReleaseNull(circle);
3091        CFReleaseNull(localError);
3092    });
3093
3094    CFDictionaryForEach(updates, ^(const void *key, const void *value) {
3095        CFErrorRef localError = NULL;
3096        CFStringRef circle_name = NULL;
3097        CFStringRef from_name = NULL;
3098        CFStringRef to_name = NULL;
3099        switch (SOSKVSKeyGetKeyTypeAndParse(key, &circle_name, &from_name, &to_name)) {
3100            case kParametersKey:
3101            case kInitialSyncKey:
3102            case kCircleKey:
3103                break;
3104            case kMessageKey:
3105            {
3106                SOSFullPeerInfoRef my_peer = NULL;
3107
3108                require_action_quiet(isData(value), message_error, SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, localError, error, NULL, CFSTR("Non-Data for message(%@) from '%@'"), value, key));
3109                require_quiet(my_peer = SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, circle_name, &localError), message_error);
3110
3111                CFStringRef my_id = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(my_peer));
3112                require_quiet(SOSAccountIsActivePeerInCircleNamed(account, circle_name, my_id, &localError), skip);
3113                require_quiet(CFEqual(my_id, to_name), skip);
3114                require_quiet(!CFEqual(my_id, from_name), skip);
3115
3116                SOSAccountSendBlock cacheInDictionary = ^ bool (SOSCircleRef circle, CFStringRef key, CFDataRef new_message, CFErrorRef* error) {
3117                    CFDictionarySetValue(account->pending_changes, key, new_message);
3118
3119                    if (account->processed_message_block) {
3120                        account->processed_message_block(circle, value, new_message);
3121                    }
3122
3123                    return true;
3124                };
3125
3126                if (SOSAccountHandlePeerMessage(account, circle_name, from_name, value, cacheInDictionary, &localError) == kSOSPeerCoderFailure) {
3127                    SOSCreateErrorWithFormat(kSOSErrorNameMismatch, localError, error, NULL,
3128                                             CFSTR("Error handling peer message from '%@'"), key);
3129                    localError = NULL; // Released by SOSCreateErrorWithFormat
3130                    goto message_error;
3131                }
3132
3133            message_error:
3134            skip:
3135                break;
3136            }
3137            case kRetirementKey:
3138                if(isData(value)) {
3139                    SOSPeerInfoRef pi = SOSPeerInfoCreateFromData(NULL, error, (CFDataRef) value);
3140                    if(pi && CFEqual(from_name, SOSPeerInfoGetPeerID(pi)) && SOSPeerInfoInspectRetirementTicket(pi, error)) {
3141                        CFDictionarySetValue(account->retired_peers, key, value);
3142                        SOSAccountRecordRetiredPeerInCircleNamed(account, circle_name, pi);
3143                    }
3144                    CFReleaseSafe(pi);
3145                }
3146                break;
3147
3148            case kAccountChangedKey: // Handled at entry to function to make sure these are processed first.
3149                break;
3150
3151            case kUnknownKey:
3152                secnotice("updates", "Unknown key '%@', ignoring", key);
3153                break;
3154
3155        }
3156
3157        CFReleaseNull(circle_name);
3158        CFReleaseNull(from_name);
3159        CFReleaseNull(to_name);
3160
3161        if (error && *error)
3162            secerror("Peer message processing error for: %@ -> %@ (%@)", key, value, *error);
3163        if (localError)
3164            secerror("Peer message local processing error for: %@ -> %@ (%@)", key, value, localError);
3165
3166        CFReleaseNull(localError);
3167    });
3168
3169    return SOSAccountSendPendingChanges(account, error);
3170}
3171
3172void SOSAccountSetMessageProcessedBlock(SOSAccountRef account, SOSAccountMessageProcessedBlock processedBlock)
3173{
3174    CFRetainSafe(processedBlock);
3175    CFReleaseNull(account->processed_message_block);
3176    account->processed_message_block = processedBlock;
3177}
3178
3179CFStringRef SOSInterestListCopyDescription(CFArrayRef interests)
3180{
3181    CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0);
3182    CFStringAppendFormat(description, NULL, CFSTR("<Interest: "));
3183
3184    CFArrayForEach(interests, ^(const void* string) {
3185        if (isString(string))
3186            CFStringAppendFormat(description, NULL, CFSTR(" '%@'"), string);
3187    });
3188    CFStringAppend(description, CFSTR(">"));
3189
3190    return description;
3191}
3192