]> git.saurik.com Git - apple/security.git/blobdiff - OSX/sec/SOSCircle/SecureObjectSync/SOSAccount.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccount.c
diff --git a/OSX/sec/SOSCircle/SecureObjectSync/SOSAccount.c b/OSX/sec/SOSCircle/SecureObjectSync/SOSAccount.c
new file mode 100644 (file)
index 0000000..b901abf
--- /dev/null
@@ -0,0 +1,1746 @@
+/*
+ * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
+ */
+
+/*
+ * SOSAccount.c -  Implementation of the secure object syncing account.
+ * An account contains a SOSCircle for each protection domain synced.
+ */
+
+#include "SOSAccountPriv.h"
+#include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
+#include <Security/SecureObjectSync/SOSTransportCircle.h>
+#include <Security/SecureObjectSync/SOSTransportMessage.h>
+#include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
+#include <Security/SecureObjectSync/SOSKVSKeys.h>
+#include <Security/SecureObjectSync/SOSTransport.h>
+#include <Security/SecureObjectSync/SOSTransportKeyParameter.h>
+#include <Security/SecureObjectSync/SOSTransportKeyParameterKVS.h>
+#include <Security/SecureObjectSync/SOSEngine.h>
+#include <Security/SecureObjectSync/SOSPeerCoder.h>
+#include <Security/SecureObjectSync/SOSInternal.h>
+#include <Security/SecureObjectSync/SOSRing.h>
+#include <Security/SecureObjectSync/SOSRingUtils.h>
+#include <Security/SecureObjectSync/SOSPeerInfoSecurityProperties.h>
+#include <Security/SecureObjectSync/SOSPeerInfoV2.h>
+#include <Security/SecItemInternal.h>
+#include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
+#include <SOSCircle/Regressions/SOSRegressionUtilities.h>
+
+CFGiblisWithCompareFor(SOSAccount);
+
+const CFStringRef SOSTransportMessageTypeIDS = CFSTR("IDS");
+const CFStringRef SOSTransportMessageTypeKVS = CFSTR("KVS");
+const CFStringRef kSOSDSIDKey = CFSTR("AccountDSID");
+
+const CFStringRef kSOSUnsyncedViewsKey = CFSTR("unsynced");
+
+
+#define DATE_LENGTH 25
+const CFStringRef kSOSAccountDebugScope = CFSTR("Scope");
+
+
+bool SOSAccountEnsureFactoryCircles(SOSAccountRef a)
+{
+    bool result = false;
+    CFStringRef circle_name = NULL;
+
+    require_quiet(a, xit);
+    require_quiet(a->factory, xit);
+
+    circle_name = SOSDataSourceFactoryCopyName(a->factory);
+    require(circle_name, xit);
+
+    SOSAccountEnsureCircle(a, circle_name, NULL);
+
+    result = true;
+
+xit:
+    // We don't own name, so don't release it.
+    CFReleaseNull(circle_name);
+    return result;
+}
+
+
+SOSAccountRef SOSAccountCreateBasic(CFAllocatorRef allocator,
+                                    CFDictionaryRef gestalt,
+                                    SOSDataSourceFactoryRef factory) {
+    SOSAccountRef a = CFTypeAllocate(SOSAccount, struct __OpaqueSOSAccount, allocator);
+
+    a->queue = dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL);
+
+    a->notification_cleanups = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+
+    a->gestalt = CFRetainSafe(gestalt);
+
+    a->trusted_circle = NULL;
+    a->trusted_rings = CFDictionaryCreateMutableForCFTypes(allocator);
+    a->backups = CFDictionaryCreateMutableForCFTypes(allocator);
+    a->my_identity = NULL;
+    a->retirees = CFSetCreateMutableForSOSPeerInfosByID(allocator);
+
+    a->factory = factory; // We adopt the factory. kthanksbai.
+    
+    a->_user_private = NULL;
+    a->_password_tmp = NULL;
+    a->user_private_timer = NULL;
+
+    a->change_blocks = CFArrayCreateMutableForCFTypes(allocator);
+
+    a->departure_code = kSOSNeverAppliedToCircle;
+
+    a->key_transport = (SOSTransportKeyParameterRef)SOSTransportKeyParameterKVSCreate(a, NULL);
+    a->circle_transport = NULL;
+    a->kvs_message_transport = NULL;
+    a->ids_message_transport = NULL;
+    a->expansion = CFDictionaryCreateMutableForCFTypes(allocator);
+    
+    return a;
+}
+
+SOSSecurityPropertyResultCode SOSAccountUpdateSecurityProperty(SOSAccountRef account, CFStringRef property, SOSSecurityPropertyActionCode actionCode, CFErrorRef *error) {
+    SOSSecurityPropertyResultCode retval = kSOSCCGeneralSecurityPropertyError;
+    bool updateCircle = false;
+    require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
+    require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
+    retval = SOSFullPeerInfoUpdateSecurityProperty(account->my_identity, actionCode, property, error);
+    
+    if(actionCode == kSOSCCSecurityPropertyEnable && retval == kSOSCCSecurityPropertyValid) {
+        updateCircle = true;
+    } else if(actionCode == kSOSCCSecurityPropertyDisable && retval == kSOSCCSecurityPropertyNotValid) {
+        updateCircle = true;
+    } else if(actionCode == kSOSCCSecurityPropertyPending) {
+        updateCircle = true;
+    }
+    
+    if (updateCircle) {
+        SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
+            secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for security property change");
+            return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
+        });
+    }
+    
+errOut:
+    return retval;
+}
+
+SOSSecurityPropertyResultCode SOSAccountSecurityPropertyStatus(SOSAccountRef account, CFStringRef property, CFErrorRef *error) {
+    SOSSecurityPropertyResultCode retval = kSOSCCGeneralViewError;
+    require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
+    require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
+    retval = SOSFullPeerInfoSecurityPropertyStatus(account->my_identity, property, error);
+errOut:
+    return retval;
+}
+
+bool SOSAccountUpdateGestalt(SOSAccountRef account, CFDictionaryRef new_gestalt)
+{
+    if (CFEqualSafe(new_gestalt, account->gestalt))
+        return false;
+
+    if (account->trusted_circle && account->my_identity
+        && SOSFullPeerInfoUpdateGestalt(account->my_identity, new_gestalt, NULL)) {
+        SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
+            secnotice("circleChange", "dCalling SOSCircleUpdatePeerInfo for gestalt change");
+            return SOSCircleUpdatePeerInfo(circle_to_change, SOSAccountGetMyPeerInfo(account));
+        });
+    }
+
+    CFRetainAssign(account->gestalt, new_gestalt);
+    return true;
+}
+
+bool SOSAccountUpdateDSID(SOSAccountRef account, CFStringRef dsid){
+    SOSAccountSetValue(account, kSOSDSIDKey, dsid, NULL);
+    //send new DSID over account changed
+    SOSTransportCircleSendOfficialDSID(account->circle_transport, dsid, NULL);
+    
+    return true;
+}
+
+bool SOSAccountUpdateFullPeerInfo(SOSAccountRef account, CFSetRef minimumViews) {
+    if (account->trusted_circle && account->my_identity) {
+        if(SOSFullPeerInfoUpdateToCurrent(account->my_identity, minimumViews)) {
+            SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
+                secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
+                return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
+            });
+        }
+    }
+    
+    return true;
+}
+
+SOSViewResultCode SOSAccountUpdateView(SOSAccountRef account, CFStringRef viewname, SOSViewActionCode actionCode, CFErrorRef *error) {
+    SOSViewResultCode retval = kSOSCCGeneralViewError;
+    SOSViewResultCode currentStatus = kSOSCCGeneralViewError;
+    bool updateCircle = false;
+    require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
+    require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
+    require_action_quiet((actionCode == kSOSCCViewEnable) || (actionCode == kSOSCCViewDisable), errOut, CFSTR("Invalid View Action"));
+    currentStatus = SOSAccountViewStatus(account, viewname, error);
+    require_action_quiet((currentStatus == kSOSCCViewNotMember) || (currentStatus == kSOSCCViewMember), errOut, CFSTR("View Membership Not Actionable"));
+
+    if (CFEqualSafe(viewname, kSOSViewKeychainV0)) {
+        // The V0 view switches on and off all on it's own, we allow people the delusion
+        // of control and status if it's what we're stuck at., otherwise error.
+        if (SOSAccountSyncingV0(account)) {
+            require_action_quiet(actionCode = kSOSCCViewDisable, errOut, CFSTR("Can't disable V0 view and it's on right now"));
+            retval = kSOSCCViewMember;
+        } else {
+            require_action_quiet(actionCode = kSOSCCViewEnable, errOut, CFSTR("Can't enable V0 and it's off right now"));
+            retval = kSOSCCViewNotMember;
+        }
+    } else if (SOSAccountSyncingV0(account) && SOSViewsIsV0Subview(viewname)) {
+        // Subviews of V0 syncing can't be turned off if V0 is on.
+        require_action_quiet(actionCode = kSOSCCViewDisable, errOut, CFSTR("Have V0 peer can't disable"));
+        retval = kSOSCCViewMember;
+    } else {
+        if(actionCode == kSOSCCViewEnable && currentStatus == kSOSCCViewNotMember) {
+            retval = SOSFullPeerInfoUpdateViews(account->my_identity, actionCode, viewname, error);
+            if(retval == kSOSCCViewMember) updateCircle = true;
+        } else if(actionCode == kSOSCCViewDisable && currentStatus == kSOSCCViewMember) {
+            retval = SOSFullPeerInfoUpdateViews(account->my_identity, actionCode, viewname, error);
+            if(retval == kSOSCCViewNotMember) updateCircle = true;
+        } else {
+            retval = currentStatus;
+        }
+
+        if (updateCircle) {
+            SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
+                secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
+                return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
+            });
+        }
+    }
+    
+errOut:
+    return retval;
+}
+
+SOSViewResultCode SOSAccountViewStatus(SOSAccountRef account, CFStringRef viewname, CFErrorRef *error) {
+    SOSViewResultCode retval = kSOSCCGeneralViewError;
+    require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
+    require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
+    retval = SOSFullPeerInfoViewStatus(account->my_identity, viewname, error);
+errOut:
+    return retval;
+}
+
+static void dumpViewSet(CFStringRef label, CFSetRef views) {
+    if(views) {
+        secnotice("circleChange", "%@ list: %@", label, views);
+    } else {
+        secnotice("circleChange", "No %@ list provided.", label);
+    }
+}
+
+bool SOSAccountUpdateViewSets(SOSAccountRef account, CFSetRef enabledViews, CFSetRef disabledViews) {
+    bool updateCircle = false;
+    dumpViewSet(CFSTR("Enabled"), enabledViews);
+    dumpViewSet(CFSTR("Disabled"), disabledViews);
+    
+    require_action_quiet(account->trusted_circle, errOut, secnotice("views", "Attempt to set viewsets with no trusted circle"));
+    require_action_quiet(account->my_identity, errOut, secnotice("views", "Attempt to set viewsets with no fullPeerInfo"));
+    require_action_quiet(enabledViews || disabledViews, errOut, secnotice("views", "No work to do"));
+    
+    // Copy my views
+    SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
+    SOSPeerInfoRef  pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, SOSFullPeerInfoGetPeerInfo(fpi), NULL);
+    
+    require_action_quiet(pi, errOut, secnotice("views", "Couldn't copy PeerInfoRef"));
+    
+    
+    if(!SOSPeerInfoVersionIsCurrent(pi)) {
+        if(!SOSPeerInfoUpdateToV2(pi, NULL)) {
+            secnotice("views", "Unable to update peer to V2- can't update views");
+            return false;
+        }
+    }
+    
+    if(enabledViews) updateCircle = SOSViewSetEnable(pi, enabledViews);
+    if(disabledViews) updateCircle |= SOSViewSetDisable(pi, disabledViews);
+    
+    /* UPDATE FULLPEERINFO VIEWS */
+    
+    if (updateCircle && SOSFullPeerInfoUpdateToThisPeer(fpi, pi, NULL)) {
+        SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
+            secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
+            return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
+        });
+    }
+    
+errOut:
+    return updateCircle;
+}
+
+
+SOSAccountRef SOSAccountCreate(CFAllocatorRef allocator,
+                               CFDictionaryRef gestalt,
+                               SOSDataSourceFactoryRef factory) {
+    SOSAccountRef a = SOSAccountCreateBasic(allocator, gestalt, factory);
+
+    
+    SOSAccountEnsureFactoryCircles(a);
+
+    return a;
+}
+
+static void SOSAccountDestroy(CFTypeRef aObj) {
+    SOSAccountRef a = (SOSAccountRef) aObj;
+
+    // We don't own the factory, merely have a reference to the singleton
+    //    Don't free it.
+    //   a->factory
+
+    SOSAccountCleanupNotificationForAllPeers(a);
+
+    SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(a->factory, SOSCircleGetName(a->trusted_circle), NULL);
+
+    if (engine)
+        SOSEngineSetSyncCompleteListenerQueue(engine, NULL);
+
+    dispatch_sync(a->queue, ^{
+        CFReleaseNull(a->gestalt);
+
+        CFReleaseNull(a->my_identity);
+        CFReleaseNull(a->trusted_circle);
+        CFReleaseNull(a->trusted_rings);
+        CFReleaseNull(a->backups);
+        CFReleaseNull(a->retirees);
+
+        a->user_public_trusted = false;
+        CFReleaseNull(a->user_public);
+        CFReleaseNull(a->user_key_parameters);
+
+        SOSAccountPurgePrivateCredential(a);
+        CFReleaseNull(a->previous_public);
+        CFReleaseNull(a->_user_private);
+        CFReleaseNull(a->_password_tmp);
+
+        a->departure_code = kSOSNeverAppliedToCircle;
+        CFReleaseNull(a->kvs_message_transport);
+        CFReleaseNull(a->ids_message_transport);
+        CFReleaseNull(a->key_transport);
+        CFReleaseNull(a->circle_transport);
+        dispatch_release(a->queue);
+        CFReleaseNull(a->notification_cleanups);
+
+        dispatch_release(a->user_private_timer);
+        CFReleaseNull(a->change_blocks);
+        CFReleaseNull(a->expansion);
+
+    });
+}
+
+static OSStatus do_delete(CFDictionaryRef query) {
+    OSStatus result;
+    
+    result = SecItemDelete(query);
+    if (result) {
+        secerror("SecItemDelete: %d", (int)result);
+    }
+     return result;
+}
+
+static int
+do_keychain_delete_aks_bags()
+{
+    OSStatus result;
+    CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
+                                 kSecClass,           kSecClassGenericPassword,
+                                 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
+                                 kSecAttrAccount,     CFSTR("SecureBackupPublicKeybag"),
+                                 kSecAttrService,     CFSTR("SecureBackupService"),
+                                 kSecAttrSynchronizable, kCFBooleanTrue,
+                                 kSecUseTombstones,     kCFBooleanFalse,
+                                 NULL);
+
+    result = do_delete(item);
+    CFReleaseSafe(item);
+    
+    return result;
+}
+
+static int
+do_keychain_delete_identities()
+{
+    OSStatus result;
+    CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
+                                kSecClass, kSecClassKey,
+                                kSecAttrSynchronizable, kCFBooleanTrue,
+                                kSecUseTombstones, kCFBooleanFalse,
+                                kSecAttrAccessGroup, CFSTR("com.apple.security.sos"),
+                                NULL);
+  
+    result = do_delete(item);
+    CFReleaseSafe(item);
+    
+    return result;
+}
+
+static int
+do_keychain_delete_lakitu()
+{
+    OSStatus result;
+    CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
+                                                        kSecClass, kSecClassGenericPassword,
+                                                        kSecAttrSynchronizable, kCFBooleanTrue,
+                                                        kSecUseTombstones, kCFBooleanFalse,
+                                                        kSecAttrAccessGroup, CFSTR("com.apple.lakitu"),
+                                                        kSecAttrAccount, CFSTR("EscrowServiceBypassToken"),
+                                                        kSecAttrService, CFSTR("EscrowService"),
+                                                        NULL);
+    
+    result = do_delete(item);
+    CFReleaseSafe(item);
+    
+    return result;
+}
+
+static int
+do_keychain_delete_sbd()
+{
+    OSStatus result;
+    CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
+                                                        kSecClass, kSecClassGenericPassword,
+                                                        kSecAttrSynchronizable, kCFBooleanTrue,
+                                                        kSecUseTombstones, kCFBooleanFalse,
+                                                        kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
+                                                        NULL);
+    
+    result = do_delete(item);
+    CFReleaseSafe(item);
+    
+    return result;
+}
+
+void SOSAccountSetToNew(SOSAccountRef a) {
+    secnotice("accountChange", "Setting Account to New");
+    int result = 0;
+    
+    CFReleaseNull(a->my_identity);
+    CFReleaseNull(a->trusted_circle);
+    CFReleaseNull(a->trusted_rings);
+    CFReleaseNull(a->backups);
+    CFReleaseNull(a->retirees);
+
+    CFReleaseNull(a->user_key_parameters);
+    CFReleaseNull(a->user_public);
+    CFReleaseNull(a->previous_public);
+    CFReleaseNull(a->_user_private);
+    CFReleaseNull(a->_password_tmp);
+
+    CFReleaseNull(a->key_transport);
+    CFReleaseNull(a->circle_transport);
+    CFReleaseNull(a->kvs_message_transport);
+    CFReleaseNull(a->ids_message_transport);
+    CFReleaseNull(a->expansion);
+    
+    /* remove all syncable items */
+    result = do_keychain_delete_aks_bags();
+    secdebug("set to new", "result for deleting aks bags: %d", result);
+
+    result = do_keychain_delete_identities();
+    secdebug("set to new", "result for deleting identities: %d", result);
+    result = do_keychain_delete_lakitu();
+    secdebug("set to new", "result for deleting lakitu: %d", result);
+    
+    result = do_keychain_delete_sbd();
+    secdebug("set to new", "result for deleting sbd: %d", result);
+
+    a->user_public_trusted = false;
+    a->departure_code = kSOSNeverAppliedToCircle;
+    a->user_private_timer = 0;
+    a->lock_notification_token = 0;
+
+    // keeping gestalt;
+    // keeping factory;
+    // Live Notification
+    // change_blocks;
+    // update_interest_block;
+    // update_block;
+
+    a->key_transport = (SOSTransportKeyParameterRef)SOSTransportKeyParameterKVSCreate(a, NULL);
+    a->circle_transport = NULL;
+    a->kvs_message_transport = NULL;
+    a->ids_message_transport = NULL;
+
+    a->trusted_rings = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+    a->backups = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+
+    a->retirees = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
+    a->expansion = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+
+    SOSAccountEnsureFactoryCircles(a); // Does rings too
+}
+
+
+static CFStringRef SOSAccountCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) {
+    SOSAccountRef a = (SOSAccountRef) aObj;
+    
+    CFStringRef gestaltDescription = CFDictionaryCopyCompactDescription(a->gestalt);
+
+    CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSAccount@%p: Gestalt: %@ Circle: %@ Me: %@>"), a, gestaltDescription, a->trusted_circle, a->my_identity);
+
+    CFReleaseNull(gestaltDescription);
+
+    return result;
+}
+
+CFStringRef SOSAccountCreateCompactDescription(SOSAccountRef a) {
+
+    CFStringRef gestaltDescription = CFDictionaryCopySuperCompactDescription(a->gestalt);
+    
+    CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), gestaltDescription);
+    
+    CFReleaseNull(gestaltDescription);
+    
+    return result;
+}
+
+static Boolean SOSAccountCompare(CFTypeRef lhs, CFTypeRef rhs)
+{
+    SOSAccountRef laccount = (SOSAccountRef) lhs;
+    SOSAccountRef raccount = (SOSAccountRef) rhs;
+
+    return CFEqualSafe(laccount->gestalt, raccount->gestalt)
+        && CFEqualSafe(laccount->trusted_circle, raccount->trusted_circle)
+        && CFEqualSafe(laccount->trusted_rings, raccount->trusted_rings)
+        && CFEqualSafe(laccount->my_identity, raccount->my_identity);
+}
+
+dispatch_queue_t SOSAccountGetQueue(SOSAccountRef account) {
+    return account->queue;
+}
+
+void SOSAccountSetUserPublicTrustedForTesting(SOSAccountRef account){
+    account->user_public_trusted = true;
+}
+
+SOSFullPeerInfoRef SOSAccountCopyAccountIdentityPeerInfo(SOSAccountRef account, CFAllocatorRef allocator, CFErrorRef* error)
+{
+    return CFRetainSafe(account->my_identity);
+}
+
+static bool SOSAccountThisDeviceCanSyncWithCircle(SOSAccountRef account) {
+    bool ok = false;
+    __block CFErrorRef error = NULL;
+
+    if (!SOSAccountHasPublicKey(account, &error)) {
+        CFReleaseSafe(error);
+        return false;
+    }
+    
+    bool hasID = true;
+    
+    require_action_quiet(account->my_identity, xit,
+                         SOSCreateError(kSOSErrorBadFormat, CFSTR("Account identity not set"), NULL, &error));
+    
+    if ((whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent) && account->ids_message_transport) {
+        CFStringRef deviceID = SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
+        if(deviceID == NULL || CFStringGetLength(deviceID) == 0){
+            hasID = false;
+            secerror("Cannot sync with all peers at this time, securityd needs the IDS device ID first.");
+            
+            __block bool success = false;
+            
+            SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
+                success = (sync_error == NULL);
+                if (!success) {
+                    CFRetainAssign(error, sync_error);
+                }
+            });
+            
+            if(!success){
+                secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", error);
+            }
+            else{
+                secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
+            }
+        }
+        CFReleaseNull(deviceID);
+    }
+    
+    require_action_quiet(account->trusted_circle, xit,
+                         SOSCreateError(kSOSErrorBadFormat, CFSTR("Account trusted circle not set"), NULL, &error));
+    
+    require_action_quiet(hasID, xit,
+                         SOSCreateError(kSOSErrorBadFormat, CFSTR("Missing IDS device ID"), NULL, &error));
+    ok = SOSCircleHasPeerWithID(account->trusted_circle,
+                                SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account->my_identity)), &error);
+xit:
+    if (!ok) {
+        secerror("sync with device failure: %@", error);
+    }
+    CFReleaseSafe(error);
+    return ok;
+}
+
+static bool SOSAccountIsThisPeerIDMe(SOSAccountRef account, CFStringRef circleName, CFStringRef peerID) {
+    SOSPeerInfoRef mypi = SOSFullPeerInfoGetPeerInfo(account->my_identity);
+    CFStringRef myPeerID = SOSPeerInfoGetPeerID(mypi);
+
+    return myPeerID && CFEqualSafe(myPeerID, peerID);
+}
+
+static bool isDefaultsWriteSetupToSyncOverIDS(){
+    return ((whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent));
+}
+
+bool SOSAccountSyncWithAllPeers(SOSAccountRef account, CFErrorRef *error)
+{
+    bool result = true;
+    __block bool SyncingCompletedOverIDS = true;
+    __block bool SyncingCompletedOverKVS = true;
+    __block CFErrorRef localError = NULL;
+    SOSCircleRef circle  = SOSAccountGetCircle(account, error);
+    CFMutableDictionaryRef circleToPeerIDs = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+    CFMutableArrayRef peerIds = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
+    
+    require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account), xit,
+                         SOSCreateError(kSOSErrorNoCircle, CFSTR("This device cannot sync with circle"),
+                                        NULL, &localError));
+
+    SOSCircleForEachValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
+        if (!SOSAccountIsThisPeerIDMe(account, SOSCircleGetName(circle), SOSPeerInfoGetPeerID(peer))) {
+            if (isDefaultsWriteSetupToSyncOverIDS() && SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer)) {
+                secdebug("IDS Transport", "Syncing with IDS capable peers using IDS!");
+                CFMutableDictionaryRef circleToIdsId = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, SOSCircleGetName(circle), SOSPeerInfoGetPeerID((peer)), NULL);
+                SyncingCompletedOverIDS = SOSTransportMessageSyncWithPeers(account->ids_message_transport, circleToIdsId, &localError);
+                CFReleaseNull(circleToIdsId);
+            } else {
+                CFArrayAppendValue(peerIds, SOSPeerInfoGetPeerID(peer));
+            }
+        }
+    });
+    if (CFArrayGetCount(peerIds)) {
+        secnotice("KVS", "Syncing with KVS capable peers");
+        CFDictionarySetValue(circleToPeerIDs, SOSCircleGetName(circle), peerIds);
+        SyncingCompletedOverKVS &= SOSTransportMessageSyncWithPeers(account->kvs_message_transport, circleToPeerIDs, &localError);
+    }
+
+    SOSEngineRef engine = SOSTransportMessageGetEngine(account->kvs_message_transport);
+    result = SOSEngineSyncWithPeers(engine, account->ids_message_transport, account->kvs_message_transport, &localError);
+
+    result &= ((SyncingCompletedOverIDS) &&
+               (SyncingCompletedOverKVS || (CFDictionaryGetCount(circleToPeerIDs) == 0)));
+
+    if (result)
+        SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers, 1);
+
+xit:
+    CFReleaseNull(circleToPeerIDs);
+
+    if (!result) {
+        secdebug("Account", "Could not sync with all peers: %@", localError);
+        CFErrorPropagate(localError, error);
+        localError = NULL;
+    }
+    CFReleaseNull(peerIds);
+    CFReleaseSafe(localError);
+    return result;
+}
+
+bool SOSAccountCleanupAfterPeer(SOSAccountRef account, size_t seconds, SOSCircleRef circle,
+                                SOSPeerInfoRef cleanupPeer, CFErrorRef* error)
+{
+    bool success = true;
+    
+    SOSPeerInfoRef myPeerInfo = SOSFullPeerInfoGetPeerInfo(account->my_identity);
+    require_action_quiet(account->my_identity && myPeerInfo, xit, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("I have no peer")));
+    require_quiet(SOSCircleHasActivePeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), error), xit);
+    
+    CFStringRef cleanupPeerID = SOSPeerInfoGetPeerID(cleanupPeer);
+
+    CFStringRef circle_name = SOSCircleGetName(circle);
+
+    CFMutableDictionaryRef circleToPeerIDs = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+    CFArrayAppendValue(CFDictionaryEnsureCFArrayAndGetCurrentValue(circleToPeerIDs, circle_name), cleanupPeerID);
+
+    CFErrorRef localError = NULL;
+    if (!(success &= SOSTransportMessageCleanupAfterPeerMessages(account->kvs_message_transport, circleToPeerIDs, &localError))) {
+        secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID, localError);
+    }
+
+    if (account->ids_message_transport && !SOSTransportMessageCleanupAfterPeerMessages(account->ids_message_transport, circleToPeerIDs, &localError)) {
+        secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID, localError);
+    }
+
+    CFReleaseNull(localError);
+
+    if((success &= SOSPeerInfoRetireRetirementTicket(seconds, cleanupPeer))) {
+        if (!(success &= SOSTransportCircleExpireRetirementRecords(account->circle_transport, circleToPeerIDs, &localError))) {
+            secnotice("account", "Failed to cleanup after peer %@ retirement: %@", cleanupPeerID, localError);
+        }
+    }
+    CFReleaseNull(localError);
+    CFReleaseNull(circleToPeerIDs);
+
+xit:
+    return success;
+}
+
+bool SOSAccountCleanupRetirementTickets(SOSAccountRef account, size_t seconds, CFErrorRef* error) {
+    CFMutableSetRef retirees_to_remove = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
+
+    __block bool success = true;
+
+    CFSetForEach(account->retirees, ^(const void *value) {
+        SOSPeerInfoRef retiree = (SOSPeerInfoRef) value;
+
+        if (retiree) {
+            // Remove the entry if it's not a retired peer or if it's retirment ticket has expired AND he's no longer in the circle.
+            if (!SOSPeerInfoIsRetirementTicket(retiree) ||
+                (SOSPeerInfoRetireRetirementTicket(seconds, retiree) && !SOSCircleHasActivePeer(account->trusted_circle, retiree, NULL))) {
+                CFSetAddValue(retirees_to_remove, retiree);
+            };
+        }
+    });
+
+    CFMutableArrayRef retirees_to_cleanup = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
+    CFSetForEach(retirees_to_remove, ^(const void *value) {
+        CFArrayAppendValue(retirees_to_cleanup, value);
+        CFSetRemoveValue(account->retirees, value);
+    });
+
+    CFReleaseNull(retirees_to_remove);
+
+    CFDictionaryRef retirements_to_remove = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
+                                                                         SOSCircleGetName(account->trusted_circle), retirees_to_cleanup,
+                                                                         NULL);
+
+    CFReleaseNull(retirees_to_cleanup);
+
+    success = SOSTransportCircleExpireRetirementRecords(account->circle_transport, retirements_to_remove, error);
+
+    CFReleaseNull(retirements_to_remove);
+
+    return success;
+}
+
+bool SOSAccountScanForRetired(SOSAccountRef account, SOSCircleRef circle, CFErrorRef *error) {
+    SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
+        CFSetSetValue(account->retirees, peer);
+        CFErrorRef cleanupError = NULL;
+        if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, peer, &cleanupError)) {
+            secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) â€“ moving on", cleanupError);
+        }
+        CFReleaseSafe(cleanupError);
+    });
+    return true;
+}
+
+SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccountRef account, SOSCircleRef starting_circle, CFErrorRef *error) {
+    SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error);
+    if(!new_circle) return NULL;
+
+    if (account->retirees) {
+        CFSetForEach(account->retirees, ^(const void* value) {
+            SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
+            if (isSOSPeerInfo(pi)) {
+                SOSCircleUpdatePeerInfo(new_circle, pi);
+            }
+        });
+    }
+
+    if(SOSCircleCountPeers(new_circle) == 0) {
+        SOSCircleResetToEmpty(new_circle, NULL);
+    }
+
+    return new_circle;
+}
+
+//
+// MARK: Circle Membership change notificaion
+//
+
+void SOSAccountAddChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) {
+    SOSAccountCircleMembershipChangeBlock copy = Block_copy(changeBlock);
+    CFArrayAppendValue(a->change_blocks, copy);
+    CFReleaseNull(copy);
+}
+
+void SOSAccountRemoveChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) {
+    CFArrayRemoveAllValue(a->change_blocks, changeBlock);
+}
+
+void SOSAccountAddSyncablePeerBlock(SOSAccountRef a, CFStringRef ds_name, SOSAccountSyncablePeersBlock changeBlock) {
+    if (!changeBlock) return;
+
+    CFRetainSafe(ds_name);
+    SOSAccountCircleMembershipChangeBlock block_to_register = ^void (SOSCircleRef new_circle,
+                                                                     CFSetRef added_peers, CFSetRef removed_peers,
+                                                                     CFSetRef added_applicants, CFSetRef removed_applicants) {
+
+        if (!CFEqualSafe(SOSCircleGetName(new_circle), ds_name))
+            return;
+
+        SOSPeerInfoRef myPi = SOSFullPeerInfoGetPeerInfo(a->my_identity);
+        CFStringRef myPi_id = myPi ? SOSPeerInfoGetPeerID(myPi) : NULL;
+
+        CFMutableArrayRef peer_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
+        CFMutableArrayRef added_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
+        CFMutableArrayRef removed_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
+
+        if (SOSCircleHasPeer(new_circle, myPi, NULL)) {
+            SOSCircleForEachPeer(new_circle, ^(SOSPeerInfoRef peer) {
+                CFArrayAppendValueIfNot(peer_ids, SOSPeerInfoGetPeerID(peer), myPi_id);
+            });
+
+            CFSetForEach(added_peers, ^(const void *value) {
+                CFArrayAppendValueIfNot(added_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
+            });
+
+            CFSetForEach(removed_peers, ^(const void *value) {
+                CFArrayAppendValueIfNot(removed_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
+            });
+        }
+
+        if (CFArrayGetCount(peer_ids) || CFSetContainsValue(removed_peers, myPi))
+            changeBlock(peer_ids, added_ids, removed_ids);
+
+        CFReleaseSafe(peer_ids);
+        CFReleaseSafe(added_ids);
+        CFReleaseSafe(removed_ids);
+    };
+
+    CFRetainSafe(changeBlock);
+    SOSAccountAddChangeBlock(a, block_to_register);
+
+    CFSetRef empty = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
+    if (a->trusted_circle && CFEqualSafe(ds_name, SOSCircleGetName(a->trusted_circle))) {
+        block_to_register(a->trusted_circle, empty, empty, empty, empty);
+    }
+    CFReleaseSafe(empty);
+}
+
+void SOSAccountPurgeIdentity(SOSAccountRef account) {
+    if (account->my_identity) {
+        // Purge private key but don't return error if we can't.
+        CFErrorRef purgeError = NULL;
+        if (!SOSFullPeerInfoPurgePersistentKey(account->my_identity, &purgeError)) {
+            secwarning("Couldn't purge persistent key for %@ [%@]", account->my_identity, purgeError);
+        }
+        CFReleaseNull(purgeError);
+
+        CFReleaseNull(account->my_identity);
+    }
+}
+
+bool sosAccountLeaveCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
+    SOSFullPeerInfoRef fpi = account->my_identity;
+    if(!fpi) return false;
+
+       CFErrorRef localError = NULL;
+
+    bool retval = false;
+
+    SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
+    if (!retire_peer) {
+        secerror("Create ticket failed for peer %@: %@", fpi, localError);
+    } else {
+        // See if we need to repost the circle we could either be an applicant or a peer already in the circle
+        if(SOSCircleHasApplicant(circle, retire_peer, NULL)) {
+            // Remove our application if we have one.
+            SOSCircleWithdrawRequest(circle, retire_peer, NULL);
+        } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
+            if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
+                CFErrorRef cleanupError = NULL;
+                if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retire_peer, &cleanupError)) {
+                    secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
+                }
+                CFReleaseSafe(cleanupError);
+            }
+        }
+
+        // Store the retirement record locally.
+        CFSetAddValue(account->retirees, retire_peer);
+
+        // Write retirement to Transport
+        CFErrorRef postError = NULL;
+        if (!SOSTransportCirclePostRetirement(account->circle_transport, SOSCircleGetName(circle), retire_peer, &postError)){
+            secwarning("Couldn't post retirement (%@)", postError);
+        }
+        if(!SOSTransportCircleFlushChanges(account->circle_transport, &postError)){
+            secwarning("Couldn't flush retirement data (%@)", postError);
+        }
+        CFReleaseNull(postError);
+    }
+
+    SOSAccountPurgeIdentity(account);
+
+    retval = true;
+
+    CFReleaseNull(localError);
+    CFReleaseNull(retire_peer);
+    return retval;
+}
+
+bool sosAccountLeaveRing(SOSAccountRef account, SOSRingRef ring, CFErrorRef* error) {
+    SOSFullPeerInfoRef fpi = account->my_identity;
+    if(!fpi) return false;
+    SOSPeerInfoRef     pi = SOSFullPeerInfoGetPeerInfo(fpi);
+    CFStringRef        peerID = SOSPeerInfoGetPeerID(pi);
+    
+    CFErrorRef localError = NULL;
+    
+    bool retval = false;
+    bool writeRing = false;
+    bool writePeerInfo = false;
+    
+    if(SOSRingHasPeerID(ring, peerID)) {
+        writePeerInfo = true;
+    }
+    
+#if 0
+    // this was circle behavior - at some point
+    if(SOSRingHasApplicant(ring, peerID)) {
+        writeRing = true;
+    }
+#endif
+    
+    if(writePeerInfo || writeRing) {
+        SOSRingWithdraw(ring, NULL, fpi, error);
+    }
+    
+    // Write leave thing to Transport
+    CFDataRef peerInfoData = SOSFullPeerInfoCopyEncodedData(fpi, kCFAllocatorDefault, error);
+    SOSTransportCircleSendPeerInfo(account->circle_transport, peerID, peerInfoData, NULL); // TODO: Handle errors?
+    
+    if (writeRing) {
+        CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
+        
+        if (ring_data) {
+            SOSTransportCircleRingPostRing(account->circle_transport, SOSRingGetName(ring), ring_data, NULL); // TODO: Handle errors?
+        }
+        CFReleaseNull(ring_data);
+    }
+    retval = true;
+    CFReleaseNull(localError);
+    return retval;
+}
+
+bool SOSAccountPostDebugScope(SOSAccountRef account, CFTypeRef scope, CFErrorRef *error) {
+    bool result = false;
+    SOSTransportCircleRef transport = account->circle_transport;
+    if (transport) {
+        result = SOSTransportCircleSendDebugInfo(transport, kSOSAccountDebugScope, scope, error);
+    }
+    return result;
+}
+
+/*
+ NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
+ local value that has been overwritten by a distant value. If there is no
+ conflict between the local and the distant values when doing the initial
+ sync (e.g. if the cloud has no data stored or the client has not stored
+ any data yet), you'll never see that notification.
+
+ NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
+ with server but initial round trip with server does not imply
+ NSUbiquitousKeyValueStoreInitialSyncChange.
+ */
+
+
+//
+// MARK: Status summary
+//
+
+static SOSCCStatus SOSCCThisDeviceStatusInCircle(SOSCircleRef circle, SOSPeerInfoRef this_peer) {
+    if (!circle)
+        return kSOSCCNotInCircle;
+
+    if (circle && SOSCircleCountPeers(circle) == 0)
+        return kSOSCCCircleAbsent;
+
+    if (this_peer) {
+        
+        if(SOSPeerInfoIsRetirementTicket(this_peer))
+            return kSOSCCNotInCircle;
+        
+        if (SOSCircleHasPeer(circle, this_peer, NULL))
+            return kSOSCCInCircle;
+
+        if (SOSCircleHasApplicant(circle, this_peer, NULL))
+            return kSOSCCRequestPending;
+    }
+
+    return kSOSCCNotInCircle;
+}
+
+bool SOSAccountIsInCircle(SOSAccountRef account, CFErrorRef *error) {
+    SOSCCStatus result = SOSAccountGetCircleStatus(account, error);
+    
+    if (result != kSOSCCInCircle && result != kSOSCCError) {
+        SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("Not in circle"));
+        return false;
+    }
+    
+    return true;
+}
+
+SOSCCStatus SOSAccountGetCircleStatus(SOSAccountRef account, CFErrorRef* error) {
+    if (!SOSAccountHasPublicKey(account, error)) {
+        return kSOSCCError;
+    }
+
+    return SOSCCThisDeviceStatusInCircle(account->trusted_circle, SOSAccountGetMyPeerInfo(account));
+}
+
+//
+// MARK: Account Reset Circles
+//
+
+static bool SOSAccountResetCircleToOffering(SOSAccountRef account, SecKeyRef user_key, CFErrorRef *error) {
+    bool result = false;
+
+    require(SOSAccountHasCircle(account, error), fail);
+    require(SOSAccountEnsureFullPeerAvailable(account, error), fail);
+    
+    SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
+        bool result = false;
+        SOSFullPeerInfoRef cloud_identity = NULL;
+        CFErrorRef localError = NULL;
+
+        require_quiet(SOSCircleResetToOffering(circle, user_key, account->my_identity, &localError), err_out);
+
+        {
+            SOSPeerInfoRef cloud_peer = GenerateNewCloudIdentityPeerInfo(error);
+            require_quiet(cloud_peer, err_out);
+            cloud_identity = CopyCloudKeychainIdentity(cloud_peer, error);
+            CFReleaseNull(cloud_peer);
+            require_quiet(cloud_identity, err_out);
+        }
+
+        account->departure_code = kSOSNeverLeftCircle;
+        require_quiet(SOSCircleRequestAdmission(circle, user_key, cloud_identity, &localError), err_out);
+        require_quiet(SOSCircleAcceptRequest(circle, user_key, account->my_identity, SOSFullPeerInfoGetPeerInfo(cloud_identity), &localError), err_out);
+        result = true;
+        SOSAccountPublishCloudParameters(account, NULL);
+
+    err_out:
+        if (result == false)
+            secerror("error resetting circle (%@) to offering: %@", circle, localError);
+        if (localError && error && *error == NULL) {
+            *error = localError;
+            localError = NULL;
+        }
+        CFReleaseNull(localError);
+        CFReleaseNull(cloud_identity);
+        return result;
+    });
+
+    result = true;
+
+fail:
+    return result;
+}
+
+
+bool SOSAccountResetToOffering(SOSAccountRef account, CFErrorRef* error) {
+    SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
+    if (!user_key)
+        return false;
+
+    CFReleaseNull(account->my_identity);
+
+    return user_key && SOSAccountResetCircleToOffering(account, user_key, error);
+}
+
+bool SOSAccountResetToEmpty(SOSAccountRef account, CFErrorRef* error) {
+    if (!SOSAccountHasPublicKey(account, error))
+        return false;
+
+    CFReleaseNull(account->my_identity);
+
+    __block bool result = true;
+    account->departure_code = kSOSWithdrewMembership;
+    result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
+        result = SOSCircleResetToEmpty(circle, error);
+        return result;
+    });
+
+    if (!result) {
+        secerror("error: %@", error ? *error : NULL);
+    }
+
+    return result;
+}
+
+
+//
+// MARK: Waiting for in-sync
+//
+
+static bool SOSAccountHasBeenInSync(SOSAccountRef account) {
+    CFTypeRef unsyncedObject = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
+    CFSetRef unsynced = asSet(unsyncedObject, NULL);
+
+    return !(unsyncedObject == kCFBooleanTrue || (unsynced && (CFSetGetCount(unsynced) > 0)));
+}
+
+static bool SOSAccountUpdateOutOfSyncViews(SOSAccountRef account, CFSetRef viewsInSync) {
+    bool notifyOfChange = false;
+
+    SOSCCStatus circleStatus = SOSAccountGetCircleStatus(account, NULL);
+    bool inOrApplying = (circleStatus == kSOSCCInCircle) || (circleStatus == kSOSCCRequestPending);
+
+    CFTypeRef unsyncedObject = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
+
+    if (!inOrApplying) {
+        if (unsyncedObject != NULL) {
+            SOSAccountClearValue(account, kSOSUnsyncedViewsKey, NULL);
+            secnotice("initial-sync", "in sync, clearing pending");
+            notifyOfChange = true;
+        }
+    } else if (circleStatus == kSOSCCInCircle) {
+        __block CFMutableSetRef viewsToSync = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+        SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
+            SOSPeerInfoWithEnabledViewSet(peer, ^(CFSetRef enabled) {
+                CFSetUnion(viewsToSync, enabled);
+            });
+        });
+
+        if (viewsInSync) {
+            CFSetSubtract(viewsToSync, viewsInSync);
+
+        }
+
+        if (unsyncedObject == kCFBooleanTrue) {
+            if (CFSetGetCount(viewsToSync) == 0) {
+                secnotice("initial-sync", "No views to wait for");
+                SOSAccountClearValue(account, kSOSUnsyncedViewsKey, NULL);
+            } else {
+                __block CFSetRef newViews = NULL;
+                SOSPeerInfoWithEnabledViewSet(SOSAccountGetMyPeerInfo(account), ^(CFSetRef enabled) {
+                    newViews = CFSetCreateIntersection(kCFAllocatorDefault, enabled, viewsToSync);
+                });
+                secnotice("initial-sync", "Pending views set from True: %@", newViews);
+                SOSAccountSetValue(account, kSOSUnsyncedViewsKey, newViews, NULL);
+                CFReleaseNull(newViews);
+            }
+            notifyOfChange = true;
+        } else if (isSet(unsyncedObject)) {
+            CFSetRef waiting = (CFMutableSetRef) unsyncedObject;
+            CFSetRef newViews = CFSetCreateIntersection(kCFAllocatorDefault, waiting, viewsToSync);
+            if (!CFEqualSafe(waiting, newViews)) {
+                secnotice("initial-sync", "Pending views updated: %@", newViews);
+                SOSAccountSetValue(account, kSOSUnsyncedViewsKey, newViews, NULL);
+                notifyOfChange = true;
+            }
+            CFReleaseNull(newViews);
+        }
+
+        CFReleaseNull(viewsToSync);
+    }
+
+    if (notifyOfChange) {
+        secnotice("initial-sync-notify", "In sync: Posting: %s", kSOSCCInitialSyncChangedNotification);
+        notify_post(kSOSCCInitialSyncChangedNotification);
+        // Make sure we update the engine
+        account->circle_rings_retirements_need_attention = true;
+    }
+
+    return SOSAccountHasBeenInSync(account);
+}
+
+static void SOSAccountPeerGotInSync(SOSAccountRef account, CFStringRef peerID) {
+    secnotice("initial-sync", "Heard PeerID is in sync: %@", peerID);
+
+    if (account->trusted_circle) {
+        SOSPeerInfoRef peer = SOSCircleCopyPeerWithID(account->trusted_circle, peerID, NULL);
+        if (peer) {
+            CFSetRef views = SOSPeerInfoCopyEnabledViews(peer);
+            SOSAccountUpdateOutOfSyncViews(account, views);
+            CFReleaseNull(views);
+        }
+        CFReleaseNull(peer);
+    }
+}
+
+void SOSAccountCleanupNotificationForAllPeers(SOSAccountRef account) {
+    SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
+
+    CFDictionaryForEach(account->notification_cleanups, ^(const void *key, const void *value) {
+        if (engine) {
+            SOSEngineSetSyncCompleteListener(engine, key, NULL);
+        }
+        dispatch_async(account->queue, value);
+    });
+
+    CFDictionaryRemoveAllValues(account->notification_cleanups);
+}
+
+static void SOSAccountCleanupNotificationForPeer(SOSAccountRef account, CFStringRef peerID) {
+    dispatch_block_t cleanup = CFDictionaryGetValue(account->notification_cleanups, peerID);
+
+    if (cleanup) {
+        SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
+
+        if (engine) {
+            SOSEngineSetSyncCompleteListener(engine, peerID, NULL);
+        }
+
+        dispatch_async(account->queue, cleanup);
+    }
+
+    CFDictionaryRemoveValue(account->notification_cleanups, peerID);
+
+}
+
+static void SOSAccountRegisterCleanupBlock(SOSAccountRef account, CFStringRef peerID, dispatch_block_t block) {
+    dispatch_block_t copy = Block_copy(block);
+    CFDictionarySetValue(account->notification_cleanups, peerID, copy);
+    CFReleaseNull(copy);
+}
+
+void SOSAccountEnsureSyncChecking(SOSAccountRef account) {
+    if (CFDictionaryGetCount(account->notification_cleanups) == 0) {
+        SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
+
+        SOSEngineSetSyncCompleteListenerQueue(engine, account->queue);
+
+        if (engine) {
+            SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
+                CFStringRef peerID = CFStringCreateCopy(kCFAllocatorDefault, SOSPeerInfoGetPeerID(peer));
+
+                SOSAccountRegisterCleanupBlock(account, peerID, ^{
+                    CFReleaseSafe(peerID);
+                });
+
+                SOSEngineSetSyncCompleteListener(engine, peerID, ^{
+                    SOSAccountPeerGotInSync(account, peerID);
+                    SOSAccountCleanupNotificationForPeer(account, peerID);
+                    SOSAccountFinishTransaction(account);
+                });
+            });
+        } else {
+            secerror("Couldn't find engine to setup notifications!!!");
+        }
+    }
+}
+
+void SOSAccountCancelSyncChecking(SOSAccountRef account) {
+    SOSAccountCleanupNotificationForAllPeers(account);
+    SOSAccountUpdateOutOfSyncViews(account, NULL);
+}
+
+bool SOSAccountCheckHasBeenInSync(SOSAccountRef account) {
+    bool hasBeenInSync = false;
+
+    if (!SOSAccountIsInCircle(account, NULL)) {
+        SOSAccountCancelSyncChecking(account);
+    } else {
+        hasBeenInSync = SOSAccountHasBeenInSync(account);
+        if (!hasBeenInSync) {
+            hasBeenInSync = SOSAccountUpdateOutOfSyncViews(account, NULL);
+            if (hasBeenInSync) {
+                // Cancel and declare victory
+                SOSAccountCancelSyncChecking(account);
+            } else {
+                // Make sure we're watching in case this is the fist attempt
+                SOSAccountEnsureSyncChecking(account);
+            }
+        }
+    }
+
+    return hasBeenInSync;
+}
+
+//
+// MARK: Joining
+//
+
+static bool SOSAccountJoinCircle(SOSAccountRef account, SecKeyRef user_key,
+                                bool use_cloud_peer, CFErrorRef* error) {
+    __block bool result = false;
+    __block SOSFullPeerInfoRef cloud_full_peer = NULL;
+
+    require_action_quiet(account->trusted_circle, fail, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Don't have circle when joining???")));
+    require_quiet(SOSAccountEnsureFullPeerAvailable(account, error), fail);
+
+    SOSFullPeerInfoRef myCirclePeer = account->my_identity;
+
+    if (use_cloud_peer) {
+        cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(account->trusted_circle, NULL);
+    } else {
+        SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
+    }
+
+    if (SOSCircleCountPeers(account->trusted_circle) == 0) {
+        result = SOSAccountResetCircleToOffering(account, user_key, error);
+    } else {
+        SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
+            result = SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
+            account->departure_code = kSOSNeverLeftCircle;
+            if(result && cloud_full_peer) {
+                CFErrorRef localError = NULL;
+                CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
+                require_quiet(cloudid, finish);
+                require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
+                require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
+            finish:
+                if (localError){
+                    secerror("Failed to join with cloud identity: %@", localError);
+                    CFReleaseNull(localError);
+                }
+            }
+            return result;
+        });
+    }
+
+fail:
+    CFReleaseNull(cloud_full_peer);
+    return result;
+}
+
+static bool SOSAccountJoinCircles_internal(SOSAccountRef account, bool use_cloud_identity, CFErrorRef* error) {
+    bool success = false;
+
+    SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
+    require_quiet(user_key, done); // Fail if we don't get one.
+
+    require_action_quiet(account->trusted_circle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
+    
+    if (account->my_identity != NULL) {
+        SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(account->my_identity);
+        success = SOSCircleHasPeer(account->trusted_circle, myPeer, NULL);
+        require_quiet(!success, done);
+
+        SOSCircleRemoveRejectedPeer(account->trusted_circle, myPeer, NULL); // If we were rejected we should remove it now.
+
+        if (!SOSCircleHasApplicant(account->trusted_circle, myPeer, NULL)) {
+               secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(account->trusted_circle));
+            
+                       CFReleaseNull(account->my_identity);
+            myPeer = NULL;
+        }
+    }
+
+    success = SOSAccountJoinCircle(account, user_key, use_cloud_identity, error);
+
+    require_quiet(success, done);
+    
+    account->departure_code = kSOSNeverLeftCircle;
+
+done:
+    return success;
+}
+
+bool SOSAccountJoinCircles(SOSAccountRef account, CFErrorRef* error) {
+    return SOSAccountJoinCircles_internal(account, false, error);
+}
+
+CFStringRef SOSAccountCopyDeviceID(SOSAccountRef account, CFErrorRef *error){
+    CFStringRef result = NULL;
+
+    require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
+
+    result = SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
+
+fail:
+    return result;
+}
+
+bool SOSAccountSetMyDSID(SOSAccountRef account, CFStringRef IDS, CFErrorRef* error){
+    bool result = true;
+
+    if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture){
+        secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
+        if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
+            require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
+            
+            result = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef circle) {
+                
+                SOSFullPeerInfoUpdateDeviceID(account->my_identity, IDS, error);
+                SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeIDS, error);
+                SOSFullPeerInfoUpdateTransportPreference(account->my_identity, kCFBooleanTrue, error);
+                
+                return SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), NULL);
+            });
+        }
+        else
+            result = false;
+    }
+    else if(whichTransportType == kSOSTransportPresent){
+        secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
+        if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
+            require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
+            
+            result = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef circle) {
+                
+                SOSFullPeerInfoUpdateDeviceID(account->my_identity, IDS, error);
+                SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeKVS, error);
+                SOSFullPeerInfoUpdateTransportPreference(account->my_identity, kCFBooleanTrue, error);
+                
+                return SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), NULL);
+            });
+        }
+        else
+            result = false;
+
+    }
+   
+    SOSCCSyncWithAllPeers();
+    
+fail:
+    return result;
+}
+
+
+bool SOSAccountSendIDSTestMessage(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
+    bool result = true;
+    if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
+        //construct message dictionary, circle -> peerID -> message
+        
+        CFMutableDictionaryRef circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+        CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+        
+        CFStringRef operation = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSSendOneMessage);
+        CFDataRef operationData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, operation, kCFStringEncodingUTF8, 0);
+        
+        CFDataRef messageData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, message, kCFStringEncodingUTF8, 0);
+        CFMutableDataRef mutableData = CFDataCreateMutable(kCFAllocatorDefault, CFDataGetLength(operationData) +  CFDataGetLength(messageData));
+        
+        CFDataAppend(mutableData, operationData);
+        CFDataAppend(mutableData, messageData);
+        
+        SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
+            CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), mutableData);
+        });
+        
+        CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerToMessage);
+        result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
+        
+        CFReleaseNull(operation);
+        CFReleaseNull(mutableData);
+        CFReleaseNull(operationData);
+        CFReleaseNull(messageData);
+        CFReleaseNull(peerToMessage);
+        CFReleaseNull(circleToPeerMessages);
+    }
+    return result;
+}
+
+bool SOSAccountStartPingTest(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
+    bool result = true;
+    if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
+        //construct message dictionary, circle -> peerID -> message
+        
+        CFMutableDictionaryRef circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+        CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+        
+        CFStringRef operation = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSStartPingTestMessage);
+        CFDataRef operationData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, operation, kCFStringEncodingUTF8, 0);
+        
+        CFDataRef messageData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, message, kCFStringEncodingUTF8, 0);
+        CFMutableDataRef mutableData = CFDataCreateMutable(kCFAllocatorDefault, CFDataGetLength(operationData) +  CFDataGetLength(messageData));
+        
+        CFDataAppend(mutableData, operationData);
+        CFDataAppend(mutableData, messageData);
+        
+        SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
+            CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), mutableData);
+        });
+        
+        CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerToMessage);
+        result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
+        
+        CFReleaseNull(operation);
+        CFReleaseNull(mutableData);
+        CFReleaseNull(operationData);
+        CFReleaseNull(messageData);
+        CFReleaseNull(peerToMessage);
+        CFReleaseNull(circleToPeerMessages);
+    }
+    return result;
+}
+
+bool SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(SOSAccountRef account, CFErrorRef *error){
+    bool result = true;
+    if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
+        
+        __block bool success = false;
+        
+        dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
+        dispatch_retain(wait_for); // Both this scope and the block own it
+        
+        SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
+            success = (sync_error == NULL);
+            if (error) {
+                CFRetainAssign(*error, sync_error);
+            }
+            
+            dispatch_semaphore_signal(wait_for);
+            dispatch_release(wait_for);
+        });
+        
+        dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
+        dispatch_release(wait_for);
+        
+        if(!success){
+            secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", *error);
+        }
+        else{
+            secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
+        }
+    }
+    return result;
+}
+
+bool SOSAccountJoinCirclesAfterRestore(SOSAccountRef account, CFErrorRef* error) {
+    return SOSAccountJoinCircles_internal(account, true, error);
+}
+
+
+bool SOSAccountLeaveCircle(SOSAccountRef account, CFErrorRef* error)
+{
+    bool result = true;
+
+    result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
+        return sosAccountLeaveCircle(account, circle, error);
+    });
+
+    account->departure_code = kSOSWithdrewMembership;
+
+    return result;
+}
+
+bool SOSAccountBail(SOSAccountRef account, uint64_t limit_in_seconds, CFErrorRef* error) {
+    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+    dispatch_group_t group = dispatch_group_create();
+    __block bool result = false;
+    secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds);
+    // Add a task to the group
+    dispatch_group_async(group, queue, ^{
+        SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
+            return sosAccountLeaveCircle(account, circle, error);
+        });
+    });
+    dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
+    dispatch_group_wait(group, milestone);
+
+    account->departure_code = kSOSWithdrewMembership;
+
+    dispatch_release(group);
+    return result;
+}
+
+
+//
+// MARK: Application
+//
+
+static void for_each_applicant_in_each_circle(SOSAccountRef account, CFArrayRef peer_infos,
+                                              bool (^action)(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer)) {
+        SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(account->my_identity);
+        CFErrorRef peer_error = NULL;
+        if (account->trusted_circle && me &&
+            SOSCircleHasPeer(account->trusted_circle, me, &peer_error)) {
+            SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle) {
+                __block bool modified = false;
+                CFArrayForEach(peer_infos, ^(const void *value) {
+                    SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
+                    if (isSOSPeerInfo(peer) && SOSCircleHasApplicant(circle, peer, NULL)) {
+                        if (action(circle, account->my_identity, peer)) {
+                            modified = true;
+                        }
+                    }
+                });
+                return modified;
+            });
+        }
+        if (peer_error)
+            secerror("Got error in SOSCircleHasPeer: %@", peer_error);
+        CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here.
+}
+
+bool SOSAccountAcceptApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
+    SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
+    if (!user_key)
+        return false;
+
+    __block bool success = true;
+    __block int64_t num_peers = 0;
+
+    for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
+        bool accepted = SOSCircleAcceptRequest(circle, user_key, myCirclePeer, peer, error);
+        if (!accepted)
+            success = false;
+               else
+                       num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
+        return accepted;
+    });
+
+    return success;
+}
+
+bool SOSAccountRejectApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
+    __block bool success = true;
+    __block int64_t num_peers = 0;
+
+    for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
+        bool rejected = SOSCircleRejectRequest(circle, myCirclePeer, peer, error);
+        if (!rejected)
+            success = false;
+               else
+                       num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
+        return rejected;
+    });
+
+    return success;
+}
+
+
+CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccountRef account, CFErrorRef* error) {
+    return CFSTR("We're compatible, go away");
+}
+
+enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccountRef account, CFErrorRef* error) {
+    return account->departure_code;
+}
+
+void SOSAccountSetLastDepartureReason(SOSAccountRef account, enum DepartureReason reason) {
+       account->departure_code = reason;
+}
+
+
+CFArrayRef SOSAccountCopyGeneration(SOSAccountRef account, CFErrorRef *error) {
+    CFArrayRef result = NULL;
+    CFNumberRef generation = NULL;
+
+    require_quiet(SOSAccountHasPublicKey(account, error), fail);
+    require_action_quiet(account->trusted_circle, fail, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle")));
+
+    generation = (CFNumberRef)SOSCircleGetGeneration(account->trusted_circle);
+    result = CFArrayCreateForCFTypes(kCFAllocatorDefault, generation, NULL);
+
+fail:
+    return result;
+}
+
+bool SOSValidateUserPublic(SOSAccountRef account, CFErrorRef *error) {
+    if (!SOSAccountHasPublicKey(account, error))
+        return NULL;
+
+    return account->user_public_trusted;
+}
+
+bool SOSAccountEnsurePeerRegistration(SOSAccountRef account, CFErrorRef *error) {
+    // TODO: this result is never set or used
+    bool result = true;
+
+    secnotice("updates", "Ensuring peer registration.");
+
+    require_quiet(account->trusted_circle, done);
+    require_quiet(account->my_identity, done);
+    // If we are not in the circle, there is no point in setting up peers
+    require_quiet(SOSAccountIsMyPeerActive(account, NULL), done);
+
+    // This code only uses the SOSFullPeerInfoRef for two things:
+    //  - Finding out if this device is in the trusted circle
+    //  - Using the peerID for this device to see if the current peer is "me"
+    //  - It is used indirectly by passing account->my_identity to SOSPeerCoderInitializeForPeer
+    
+    CFStringRef my_id = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
+
+    SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
+        if (!SOSPeerInfoPeerIDEqual(peer, my_id)) {
+            CFErrorRef localError = NULL;
+            SOSTransportMessageRef messageTransport = NULL;
+            
+            if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
+                 messageTransport = SOSPeerInfoHasDeviceID(peer) ? account->ids_message_transport : account->kvs_message_transport;
+            }
+            else
+                messageTransport = account->kvs_message_transport;
+            
+            SOSPeerCoderInitializeForPeer(messageTransport->engine, account->my_identity, peer, &localError);
+            if (localError)
+                secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, account->my_identity, localError);
+            CFReleaseSafe(localError);
+        }
+    });
+
+    //Initialize our device ID
+    if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
+        CFStringRef deviceID = SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
+        if( deviceID == NULL || CFStringGetLength(deviceID) == 0){
+            
+            __block bool success = false;
+            __block CFErrorRef localError = NULL;
+            
+                SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
+                    success = (sync_error == NULL);
+                    if (localError) {
+                        CFRetainAssign(localError, sync_error);
+                    }
+                });
+                
+                if(!success){
+                    secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError);
+                }
+                else{
+                    secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
+                }
+            CFReleaseNull(localError);
+        }
+        CFReleaseNull(deviceID);
+    }
+    
+done:
+    return result;
+}
+
+static inline bool SOSAccountEnsureExpansion(SOSAccountRef account, CFErrorRef *error) {
+    if (!account->expansion) {
+        account->expansion = CFDictionaryCreateMutableForCFTypes(NULL);
+    }
+
+    return SecAllocationError(account->expansion, error, CFSTR("Can't Alloc Account Expansion dictionary"));
+}
+
+bool SOSAccountClearValue(SOSAccountRef account, const void *key, CFErrorRef *error) {
+    bool success = SOSAccountEnsureExpansion(account, error);
+    require_quiet(success, errOut);
+
+    CFDictionaryRemoveValue(account->expansion, key);
+errOut:
+    return success;
+}
+
+bool SOSAccountSetValue(SOSAccountRef account, const void *key, const void *value, CFErrorRef *error) {
+    bool success = SOSAccountEnsureExpansion(account, error);
+    require_quiet(success, errOut);
+
+    CFDictionarySetValue(account->expansion, key, value);
+errOut:
+    return success;
+}
+
+
+const void *SOSAccountGetValue(SOSAccountRef account, const void *key, CFErrorRef *error) {
+    if (!account->expansion) {
+        return NULL;
+    }
+    return CFDictionaryGetValue(account->expansion, key);
+}
+
+void SOSAccountFinishTransaction(SOSAccountRef account) {
+    if(account->circle_rings_retirements_need_attention){
+        CFErrorRef localError = NULL;
+        if(!SOSTransportCircleFlushChanges(account->circle_transport, &localError)) {
+            secerror("flush circle failed %@", localError);
+        }
+        CFReleaseSafe(localError);
+
+        SOSAccountNotifyEngines(account); // For now our only rings are backup rings.
+    }
+
+    SOSAccountCheckHasBeenInSync(account);
+
+    account->circle_rings_retirements_need_attention = false;
+}
+