]> git.saurik.com Git - apple/security.git/blobdiff - keychain/SecureObjectSync/SOSAccountBackup.m
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / SecureObjectSync / SOSAccountBackup.m
diff --git a/keychain/SecureObjectSync/SOSAccountBackup.m b/keychain/SecureObjectSync/SOSAccountBackup.m
new file mode 100644 (file)
index 0000000..3a631f1
--- /dev/null
@@ -0,0 +1,661 @@
+//
+//  SOSAccountCircles.c
+//  sec
+//
+
+#include "SOSAccount.h"
+#include "SOSCloudKeychainClient.h"
+
+#include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
+
+#include "keychain/SecureObjectSync/SOSPeerInfoCollections.h"
+#include "keychain/SecureObjectSync/SOSPeerInfoV2.h"
+#include <Security/SecureObjectSync/SOSViews.h>
+#include "keychain/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
+#include "keychain/SecureObjectSync/SOSAccountTrustClassic+Expansion.h"
+
+#include "keychain/SecureObjectSync/SOSInternal.h"
+
+
+
+//
+// MARK: V0 Keybag keychain stuff
+//
+static bool SecItemUpdateOrAdd(CFDictionaryRef query, CFDictionaryRef update, CFErrorRef *error)
+{
+    OSStatus saveStatus = SecItemUpdate(query, update);
+    
+    if (errSecItemNotFound == saveStatus) {
+        CFMutableDictionaryRef add = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, query);
+        CFDictionaryForEach(update, ^(const void *key, const void *value) {
+            CFDictionaryAddValue(add, key, value);
+        });
+        saveStatus = SecItemAdd(add, NULL);
+        CFReleaseNull(add);
+    }
+    
+    return SecError(saveStatus, error, CFSTR("Error saving %@"), query);
+}
+
+static CFDictionaryRef SOSCopyV0Attributes() {
+    return  CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
+                                         kSecClass,           kSecClassGenericPassword,
+                                         kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
+                                         kSecAttrAccessible,  kSecAttrAccessibleWhenUnlocked,
+                                         kSecAttrAccount,     CFSTR("SecureBackupPublicKeybag"),
+                                         kSecAttrService,     CFSTR("SecureBackupService"),
+                                         kSecAttrSynchronizable, kCFBooleanTrue,
+                                         NULL);
+}
+
+bool SOSDeleteV0Keybag(CFErrorRef *error) {
+    CFDictionaryRef attributes = SOSCopyV0Attributes();
+    
+    OSStatus result = SecItemDelete(attributes);
+    
+    CFReleaseNull(attributes);
+    
+    return SecError(result != errSecItemNotFound ? result : errSecSuccess, error, CFSTR("Deleting V0 Keybag failed - %d"), (int)result);
+}
+
+static bool SOSSaveV0Keybag(CFDataRef v0Keybag, CFErrorRef *error) {
+    CFDictionaryRef attributes = SOSCopyV0Attributes();
+    
+    CFDictionaryRef update = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
+                                                          kSecValueData,           v0Keybag,
+                                                          NULL);
+    
+    
+    bool result = SecItemUpdateOrAdd(attributes, update, error);
+    CFReleaseNull(attributes);
+    CFReleaseNull(update);
+    
+    return result;
+}
+
+
+static bool SOSPeerInfoIsViewBackupEnabled(SOSPeerInfoRef peerInfo, CFStringRef viewName) {
+    if (CFEqualSafe(kSOSViewKeychainV0, viewName))
+        return false;
+
+    return SOSPeerInfoHasBackupKey(peerInfo) && SOSPeerInfoIsViewPermitted(peerInfo, viewName);
+}
+
+static CFSetRef SOSAccountCopyBackupPeersForView(SOSAccount*  account, CFStringRef viewName) {
+    CFMutableSetRef backupPeers = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
+
+    SOSCircleRef circle = [account.trust getCircle:NULL];
+
+    require_quiet(circle, exit);
+
+    SOSCircleForEachValidPeer(circle, account.accountKey, ^(SOSPeerInfoRef peer) {
+        if (SOSPeerInfoIsViewBackupEnabled(peer, viewName))
+            CFSetAddValue(backupPeers, peer);
+    });
+
+exit:
+    return backupPeers;
+}
+
+static void SOSAccountWithBackupPeersForView(SOSAccount*  account, CFStringRef viewName, void (^action)(CFSetRef peers)) {
+    CFSetRef backupPeersForView = SOSAccountCopyBackupPeersForView(account, viewName);
+
+    action(backupPeersForView);
+
+    CFReleaseNull(backupPeersForView);
+}
+
+
+static bool SOSAccountWithBSKBForView(SOSAccount*  account, CFStringRef viewName, CFErrorRef *error,
+                                      bool (^action)(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error)) {
+    __block SOSBackupSliceKeyBagRef bskb = NULL;
+    bool result = false;
+    CFDataRef rkbg = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, NULL);
+
+    SOSAccountWithBackupPeersForView(account, viewName, ^(CFSetRef peers) {
+        if(! rkbg) {
+            bskb = SOSBackupSliceKeyBagCreate(kCFAllocatorDefault, peers, error);
+        } else {
+            CFMutableDictionaryRef additionalKeys = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+            CFDictionaryAddValue(additionalKeys, bskbRkbgPrefix, rkbg);
+            bskb = SOSBackupSliceKeyBagCreateWithAdditionalKeys(kCFAllocatorDefault, peers, additionalKeys, error);
+            CFReleaseNull(additionalKeys);
+        }
+    });
+    CFReleaseNull(rkbg);
+
+    require_quiet(bskb, exit);
+
+    action(bskb, error);
+
+    result = true;
+
+exit:
+    CFReleaseNull(bskb);
+    return result;
+}
+
+CFStringRef SOSBackupCopyRingNameForView(CFStringRef viewName) {
+    return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-tomb"), viewName);
+}
+
+bool SOSAccountUpdateBackupRing(SOSAccount*  account, CFStringRef viewName, CFErrorRef *error,
+                                       SOSRingRef (^modify)(SOSRingRef existing, CFErrorRef *error)) {
+
+    CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
+
+    bool result = SOSAccountUpdateNamedRing(account, ringName, error, ^SOSRingRef(CFStringRef ringName, CFErrorRef *error) {
+        return SOSRingCreate(ringName, (__bridge CFStringRef) account.peerID, kSOSRingBackup, error);
+    }, modify);
+
+    CFReleaseNull(ringName);
+
+    return result;
+}
+
+static bool SOSAccountSetKeybagForViewBackupRing(SOSAccount*  account, CFStringRef viewName, SOSBackupSliceKeyBagRef keyBag, CFErrorRef *error) {
+    CFMutableSetRef backupViewSet = CFSetCreateMutableForCFTypes(NULL);
+    bool result = false;
+
+    if(!SecAllocationError(backupViewSet, error, CFSTR("No backup view set created"))){
+        secnotice("backupring", "Got error setting keybag for backup view '%@': %@", viewName, error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space."));
+        
+        return result;
+    }
+    CFSetAddValue(backupViewSet, viewName);
+
+    result = SOSAccountUpdateBackupRing(account, viewName, error, ^SOSRingRef(SOSRingRef existing, CFErrorRef *error) {
+        SOSRingRef newRing = NULL;
+        CFSetRef viewPeerSet = [account.trust copyPeerSetForView:viewName];
+        CFMutableSetRef cleared = CFSetCreateMutableForCFTypes(NULL);
+
+        SOSRingSetPeerIDs(existing, cleared);
+        SOSRingAddAll(existing, viewPeerSet);
+
+        require_quiet(SOSRingSetBackupKeyBag(existing, account.fullPeerInfo, backupViewSet, keyBag, error), exit);
+
+        newRing = CFRetainSafe(existing);
+    exit:
+        CFReleaseNull(viewPeerSet);
+        CFReleaseNull(cleared);
+        return newRing;
+    });
+
+    if (result && NULL != error && NULL != *error) {
+        secerror("Got Success and Error (dropping error): %@", *error);
+        CFReleaseNull(*error);
+    }
+    
+    if (!result) {
+        secnotice("backupring", "Got error setting keybag for backup view '%@': %@", viewName, error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space."));
+    }
+
+    CFReleaseNull(backupViewSet);
+    return result;
+}
+
+bool SOSAccountNewBKSBForView(SOSAccount* account, CFStringRef viewName, CFErrorRef *error)
+{
+    return SOSAccountWithBSKBForView(account, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
+        bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
+        return result;
+    });
+}
+
+bool SOSAccountIsBackupRingEmpty(SOSAccount*  account, CFStringRef viewName) {
+    CFStringRef backupRing = SOSBackupCopyRingNameForView(viewName);
+    SOSRingRef ring = [account.trust copyRing:backupRing err:NULL];
+    CFReleaseNull(backupRing);
+    int peercnt = 0;
+    if(ring) peercnt = SOSRingCountPeers(ring);
+    CFReleaseNull(ring);
+    return peercnt == 0;
+}
+
+bool SOSAccountIsMyPeerInBackupAndCurrentInView(SOSAccount*  account, CFStringRef viewname){
+    bool result = false;
+    CFErrorRef bsError = NULL;
+    CFDataRef backupSliceData = NULL;
+    SOSRingRef ring = NULL;
+    SOSBackupSliceKeyBagRef backupSlice = NULL;
+    
+    require_quiet(SOSPeerInfoIsViewBackupEnabled(account.peerInfo, viewname), errOut);
+    
+    CFStringRef ringName = SOSBackupCopyRingNameForView(viewname);
+    ring = [account.trust copyRing:ringName err:&bsError];
+    CFReleaseNull(ringName);
+    
+    require_quiet(ring, errOut);
+    
+    //grab the backup slice from the ring
+    backupSliceData = SOSRingGetPayload(ring, &bsError);
+    require_quiet(backupSliceData, errOut);
+
+    backupSlice = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, &bsError);
+    require_quiet(backupSlice, errOut);
+    
+    CFSetRef peers = SOSBSKBGetPeers(backupSlice);
+    SOSPeerInfoRef myPeer = account.peerInfo;
+    
+    SOSPeerInfoRef myPeerInBSKB = (SOSPeerInfoRef) CFSetGetValue(peers, myPeer);
+    require_quiet(isSOSPeerInfo(myPeerInBSKB), errOut);
+    
+    CFDataRef myBK = SOSPeerInfoCopyBackupKey(myPeer);
+    CFDataRef myPeerInBSKBBK = SOSPeerInfoCopyBackupKey(myPeerInBSKB);
+    result = CFEqualSafe(myBK, myPeerInBSKBBK);
+    CFReleaseNull(myBK);
+    CFReleaseNull(myPeerInBSKBBK);
+    
+errOut:
+    CFReleaseNull(ring);
+    CFReleaseNull(backupSlice);
+
+    if (bsError) {
+        secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData, backupSlice, bsError);
+    }
+    CFReleaseNull(bsError);
+    return result;
+}
+
+bool SOSAccountIsPeerInBackupAndCurrentInView(SOSAccount*  account, SOSPeerInfoRef testPeer, CFStringRef viewname){
+    bool result = false;
+    CFErrorRef bsError = NULL;
+    CFDataRef backupSliceData = NULL;
+    SOSRingRef ring = NULL;
+    SOSBackupSliceKeyBagRef backupSlice = NULL;
+
+    require_quiet(testPeer, errOut);
+
+    CFStringRef ringName = SOSBackupCopyRingNameForView(viewname);
+    
+    ring = [account.trust copyRing:ringName err:&bsError];
+    CFReleaseNull(ringName);
+    
+    require_quiet(ring, errOut);
+    
+    //grab the backup slice from the ring
+    backupSliceData = SOSRingGetPayload(ring, &bsError);
+    require_quiet(backupSliceData, errOut);
+
+    backupSlice = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, &bsError);
+    require_quiet(backupSlice, errOut);
+    
+    CFSetRef peers = SOSBSKBGetPeers(backupSlice);
+    
+    SOSPeerInfoRef peerInBSKB = (SOSPeerInfoRef) CFSetGetValue(peers, testPeer);
+    require_quiet(isSOSPeerInfo(peerInBSKB), errOut);
+
+    result = CFEqualSafe(testPeer, peerInBSKB);
+    
+errOut:
+    CFReleaseNull(ring);
+    CFReleaseNull(backupSlice);
+
+    if (bsError) {
+        secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData, backupSlice, bsError);
+    }
+    CFReleaseNull(bsError);
+    return result;
+
+}
+
+bool SOSAccountUpdateOurPeerInBackup(SOSAccount*  account, SOSRingRef oldRing, CFErrorRef *error){
+    bool result = false;
+    CFSetRef viewNames = SOSBackupRingGetViews(oldRing, error);
+    __block CFStringRef viewName = NULL;
+    require_quiet(viewNames, fail);
+    require_quiet(SecRequirementError(1 == CFSetGetCount(viewNames), error, CFSTR("Only support single view backup rings")), fail);
+
+    CFSetForEach(viewNames, ^(const void *value) {
+        if (isString(value)) {
+            viewName = CFRetainSafe((CFStringRef) value);
+        }
+    });
+
+    result = SOSAccountNewBKSBForView(account, viewName, error);
+
+fail:
+    CFReleaseNull(viewName);
+    return result;
+}
+
+void SOSAccountForEachBackupRingName(SOSAccount*  account, void (^operation)(CFStringRef value)) {
+    SOSPeerInfoRef myPeer = account.peerInfo;
+    if (myPeer) {
+        CFSetRef allViews = SOSViewCopyViewSet(kViewSetAll); // All non virtual views.
+
+        CFSetForEach(allViews, ^(const void *value) {
+            CFStringRef viewName = asString(value, NULL);
+
+            if (viewName) {
+                CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
+                operation(ringName);
+                CFReleaseNull(ringName);
+            }
+        });
+        CFReleaseNull(allViews);
+        // Only one "ring" now (other than backup rings) when there's more this will need to be modified.
+        operation(kSOSRecoveryRing);
+    }
+}
+
+
+void SOSAccountForEachRingName(SOSAccount* account, void (^operation)(CFStringRef value)) {
+    SOSPeerInfoRef myPeer = account.peerInfo;
+    if (myPeer) {
+        CFSetRef allViews = SOSViewCopyViewSet(kViewSetAll); // All non virtual views.
+
+        CFSetForEach(allViews, ^(const void *value) {
+            CFStringRef viewName = asString(value, NULL);
+
+            if (viewName) {
+                CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
+                operation(ringName);
+                CFReleaseNull(ringName);
+            }
+        });
+        CFReleaseNull(allViews);
+        // Only one "ring" now (other than backup rings) when there's more this will need to be modified.
+        operation(kSOSRecoveryRing);
+    }
+}
+
+void SOSAccountForEachBackupView(SOSAccount*  account,  void (^operation)(const void *value)) {
+    SOSPeerInfoRef myPeer = account.peerInfo;
+    
+    if (myPeer) {
+        CFMutableSetRef myBackupViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, SOSPeerInfoGetPermittedViews(myPeer));
+        CFSetRemoveValue(myBackupViews, kSOSViewKeychainV0);
+        CFSetForEach(myBackupViews, operation);
+        CFReleaseNull(myBackupViews);
+    }
+}
+
+
+static bool SOSAccountWithBSKBAndPeerInfosForView(SOSAccount*  account, CFArrayRef retiree, CFStringRef viewName, CFErrorRef *error,
+                                                  bool (^action)(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error)) {
+    __block SOSBackupSliceKeyBagRef bskb = NULL;
+    bool result = false;
+
+    SOSAccountWithBackupPeersForView(account, viewName, ^(CFSetRef peers) {
+        CFMutableSetRef newPeerList = CFSetCreateMutableCopy(kCFAllocatorDefault, CFSetGetCount(peers), peers);
+        CFArrayForEach(retiree, ^(const void *value) {
+            if (!isSOSPeerInfo(value)) {
+                secerror("Peer list contains a non-peerInfo element");
+            } else {
+                SOSPeerInfoRef retiringPeer = (SOSPeerInfoRef)value;
+                CFStringRef retiringPeerID = SOSPeerInfoGetPeerID(retiringPeer);
+
+                CFSetForEach(newPeerList, ^(const void *peerFromAccount) {
+                    CFStringRef peerFromAccountID = SOSPeerInfoGetPeerID((SOSPeerInfoRef)peerFromAccount);
+                    if (peerFromAccountID && retiringPeerID && CFStringCompare(peerFromAccountID, retiringPeerID, 0) == 0){
+                        CFSetRemoveValue(newPeerList, peerFromAccount);
+                    }
+                });
+            }
+        });
+        bskb = SOSBackupSliceKeyBagCreate(kCFAllocatorDefault, newPeerList, error);
+        CFReleaseNull(newPeerList);
+    });
+
+    require_quiet(bskb, exit);
+
+    action(bskb, error);
+
+    result = true;
+
+exit:
+    CFReleaseNull(bskb);
+    return result;
+}
+
+
+SOSRingRef SOSAccountCreateBackupRingForView(SOSAccount* account, CFStringRef ringBackupViewName, CFErrorRef *error) {
+    if(!account) return NULL;
+    if(!ringBackupViewName) return NULL;
+    SOSPeerInfoRef myPeerInfo = account.trust.peerInfo;
+    if(!myPeerInfo || !SOSPeerInfoHasBackupKey(myPeerInfo)) return NULL;
+    if(!SOSPeerInfoIsEnabledView(myPeerInfo, ringBackupViewName)) return NULL;
+    CFStringRef ringName = SOSBackupCopyRingNameForView(ringBackupViewName);
+    if(!ringName) return NULL;
+
+    SOSRingRef newRing = SOSRingCreate(ringName, (__bridge CFStringRef) account.peerID, kSOSRingBackup, error);
+    CFReleaseNull(ringName);
+    CFMutableSetRef filteredPeerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+    CFMutableSetRef filteredPeerInfos = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
+
+    SOSCircleForEachBackupCapablePeerForView(account.trust.trustedCircle, account.accountKey, ringBackupViewName, ^(SOSPeerInfoRef peer) {
+        CFSetAddValue(filteredPeerIDs, SOSPeerInfoGetPeerID(peer));
+        CFSetAddValue(filteredPeerInfos, peer);
+    });
+
+    CFMutableSetRef viewSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+    CFSetAddValue(viewSet, ringBackupViewName);
+
+    SOSBackupSliceKeyBagRef bskb = NULL;
+    CFDataRef recoveryKeyData = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, NULL);
+    if(recoveryKeyData) {
+        CFMutableDictionaryRef additionalKeys = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+        CFDictionaryAddValue(additionalKeys, bskbRkbgPrefix, recoveryKeyData);
+        bskb = SOSBackupSliceKeyBagCreateWithAdditionalKeys(kCFAllocatorDefault, filteredPeerInfos, additionalKeys, error);
+        CFReleaseNull(additionalKeys);
+    } else {
+        bskb = SOSBackupSliceKeyBagCreate(kCFAllocatorDefault, filteredPeerInfos, error);
+    }
+    if(bskb) {
+        SOSRingSetPeerIDs(newRing, filteredPeerIDs);
+        SOSRingSetBackupKeyBag(newRing, account.fullPeerInfo, viewSet, bskb, error);
+        SOSBackupRingSetViews(newRing, account.fullPeerInfo, viewSet, error);
+    } else {
+        CFReleaseNull(newRing);
+    }
+
+    CFReleaseNull(viewSet);
+    CFReleaseNull(filteredPeerIDs);
+    CFReleaseNull(filteredPeerInfos);
+    CFReleaseNull(bskb);
+    CFReleaseNull(recoveryKeyData);
+    return newRing;
+}
+
+void SOSAccountProcessBackupRings(SOSAccount*  account, CFErrorRef *error) {
+    SOSAccountForEachBackupView(account, ^(const void *value) {
+        CFStringRef viewName = (CFStringRef)value;
+        SOSAccountUpdateBackupRing(account, viewName, error, ^SOSRingRef(SOSRingRef existing, CFErrorRef *error) {
+            SOSRingRef newRing = SOSAccountCreateBackupRingForView(account, viewName, error);
+            return newRing;
+        });
+    });
+}
+
+bool SOSAccountValidateBackupRingForView(SOSAccount*  account, CFStringRef viewName, CFErrorRef *error) {
+    bool fixBackupRing = false;
+    CFStringRef ringName = NULL;
+    SOSRingRef ring = NULL;
+    CFDataRef recoveryKeyData = NULL;
+    SOSBackupSliceKeyBagRef currentBSKB = NULL;
+    CFMutableSetRef filteredPeerIDs = NULL;
+    CFMutableSetRef filteredPeerInfos = NULL;
+    CFSetRef ringPeerIDSet = NULL;
+
+    require_action_quiet(account && viewName, errOut, SecError(errSecParam, error, CFSTR("NULL account or viewName parameter")));
+    ringName = SOSBackupCopyRingNameForView(viewName);
+    require_action_quiet(ringName, errOut, CFSTR("No RingName for View"));
+    ring = SOSAccountCopyRingNamed(account, ringName, error);
+    require_quiet(ring, errOut);
+    recoveryKeyData = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, NULL);
+    currentBSKB = SOSRingCopyBackupSliceKeyBag(ring, NULL);
+    filteredPeerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+    filteredPeerInfos = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
+
+    SOSCircleForEachBackupCapablePeerForView(account.trust.trustedCircle, account.accountKey, viewName, ^(SOSPeerInfoRef peer) {
+        CFSetAddValue(filteredPeerIDs, SOSPeerInfoGetPeerID(peer));
+        CFSetAddValue(filteredPeerInfos, peer);
+    });
+
+    ringPeerIDSet = SOSRingCopyPeerIDs(ring);
+    if(!CFEqual(filteredPeerIDs, ringPeerIDSet)) {
+        fixBackupRing = true;
+    }
+
+    fixBackupRing &= !currentBSKB ||
+    !SOSBSKBAllPeersBackupKeysAreInKeyBag(currentBSKB, filteredPeerInfos) ||
+    !SOSBSKBHasThisRecoveryKey(currentBSKB, recoveryKeyData);
+
+errOut:
+    CFReleaseNull(ringName);
+    CFReleaseNull(ring);
+    CFReleaseNull(ringPeerIDSet);
+    CFReleaseNull(recoveryKeyData);
+    CFReleaseNull(currentBSKB);
+    CFReleaseNull(filteredPeerIDs);
+    CFReleaseNull(filteredPeerInfos);
+    return fixBackupRing;
+}
+
+bool SOSAccountSetBackupPublicKey(SOSAccountTransaction* aTxn, CFDataRef cfBackupKey, CFErrorRef *error)
+{
+    SOSAccount*  account = aTxn.account;
+    __block bool result = false;
+
+    account.backup_key = nil;
+    if(!SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
+        return SOSFullPeerInfoUpdateBackupKey(fpi, cfBackupKey, error);
+    })){
+        return result;
+    }
+    SOSAccountProcessBackupRings(account, NULL);
+    return true;
+
+}
+
+
+bool SOSAccountRemoveBackupPublickey(SOSAccountTransaction* aTxn, CFErrorRef *error)
+{
+    return SOSAccountSetBackupPublicKey(aTxn, NULL, error);
+}
+
+bool SOSAccountSetBSKBagForAllSlices(SOSAccount*  account, CFDataRef aks_bag, bool setupV0Only, CFErrorRef *error){
+    __block bool result = false;
+    SOSBackupSliceKeyBagRef backup_slice = NULL;
+    
+    if(![account isInCircle:error]) {
+        return result;
+    }
+
+    if (setupV0Only) {
+        result = SOSSaveV0Keybag(aks_bag, error);
+        require_action_quiet(result, exit, secnotice("keybag", "failed to set V0 keybag (%@)", *error));
+    } else {
+        result = true;
+
+        backup_slice = SOSBackupSliceKeyBagCreateDirect(kCFAllocatorDefault, aks_bag, error);
+
+        SOSAccountForEachBackupView(account, ^(const void *value) {
+            CFStringRef viewname = (CFStringRef) value;
+            result &= SOSAccountSetKeybagForViewBackupRing(account, viewname, backup_slice, error);
+        });
+    }
+
+exit:
+    CFReleaseNull(backup_slice);
+    return result;
+}
+
+static CF_RETURNS_RETAINED CFMutableArrayRef SOSAccountIsRetiredPeerIDInBackupPeerList(SOSAccount*  account, CFArrayRef peers, CFSetRef peersInBackup){
+    CFMutableArrayRef removals = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
+    
+    CFSetForEach(peersInBackup, ^(const void *value) {
+        SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
+        CFArrayForEach(peers, ^(const void *value) {
+            CFStringRef peerID = SOSPeerInfoGetPeerID((SOSPeerInfoRef)value);
+            CFStringRef piPeerID = SOSPeerInfoGetPeerID(peer);
+            if (peerID && piPeerID && CFStringCompare(piPeerID, peerID, 0) == 0){
+                CFArrayAppendValue(removals, peer);
+            }
+        });
+        
+    });
+    
+    return removals;
+}
+
+bool SOSAccountRemoveBackupPeers(SOSAccount*  account, CFArrayRef peers, CFErrorRef *error){
+    __block bool result = true;
+
+    SOSFullPeerInfoRef fpi = account.fullPeerInfo;
+    SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(fpi);
+    
+    CFSetRef permittedViews = SOSPeerInfoGetPermittedViews(myPeer);
+    CFSetForEach(permittedViews, ^(const void *value) {
+        CFStringRef viewName = (CFStringRef)value;
+        if(SOSPeerInfoIsViewBackupEnabled(myPeer, viewName)){
+            //grab current peers list
+            CFSetRef peersInBackup = SOSAccountCopyBackupPeersForView(account, viewName);
+            //get peer infos that have retired but are still in the backup peer list
+            CFMutableArrayRef removals = SOSAccountIsRetiredPeerIDInBackupPeerList(account, peers, peersInBackup);
+            result = SOSAccountWithBSKBAndPeerInfosForView(account, removals, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
+                bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
+                return result;
+            });
+            CFReleaseNull(removals);
+            CFReleaseNull(peersInBackup);
+        }
+    });
+    
+    return result;
+    
+}
+
+SOSBackupSliceKeyBagRef SOSAccountBackupSliceKeyBagForView(SOSAccount*  account, CFStringRef viewName, CFErrorRef* error){
+    CFDataRef backupSliceData = NULL;
+    CFStringRef ringName = NULL;
+    SOSRingRef ring = NULL;
+    SOSBackupSliceKeyBagRef bskb = NULL;
+
+    ringName = SOSBackupCopyRingNameForView(viewName);
+    ring = [account.trust copyRing:ringName err:NULL];
+    require_action_quiet(ring, exit, SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("failed to get ring")));
+
+    //grab the backup slice from the ring
+    backupSliceData = SOSRingGetPayload(ring, error);
+    require_action_quiet(backupSliceData, exit, secnotice("backup", "failed to get backup slice (%@)", *error));
+
+    bskb = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, error);
+
+exit:
+    CFReleaseNull(ring);
+    CFReleaseNull(ringName);
+
+    return bskb;
+}
+
+bool SOSAccountIsLastBackupPeer(SOSAccount*  account, CFErrorRef *error) {
+    __block bool retval = false;
+    SOSPeerInfoRef pi = account.peerInfo;
+
+    if(![account isInCircle:error]) {
+        return retval;
+    }
+
+    if(!SOSPeerInfoHasBackupKey(pi))
+        return retval;
+
+    SOSCircleRef circle = [account.trust getCircle:error];
+
+    if(SOSCircleCountValidSyncingPeers(circle, SOSAccountGetTrustedPublicCredential(account, error)) == 1){
+        retval = true;
+        return retval;
+    }
+    // We're in a circle with more than 1 ActiveValidPeers - are they in the backups?
+    SOSAccountForEachBackupView(account, ^(const void *value) {
+        CFStringRef viewname = (CFStringRef) value;
+        SOSBackupSliceKeyBagRef keybag = SOSAccountBackupSliceKeyBagForView(account, viewname, error);
+        require_quiet(keybag, inner_errOut);
+        retval |= ((SOSBSKBCountPeers(keybag) == 1) && (SOSBSKBPeerIsInKeyBag(keybag, pi)));
+    inner_errOut:
+        CFReleaseNull(keybag);
+    });
+
+    return retval;
+}