]> git.saurik.com Git - apple/security.git/blobdiff - keychain/SecureObjectSync/SOSRingUtils.c
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / SecureObjectSync / SOSRingUtils.c
diff --git a/keychain/SecureObjectSync/SOSRingUtils.c b/keychain/SecureObjectSync/SOSRingUtils.c
new file mode 100644 (file)
index 0000000..e1592cc
--- /dev/null
@@ -0,0 +1,826 @@
+/*
+ * Copyright (c) 2015 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+/*
+ * SOSRingUtils.c - Functions for building rings
+ */
+
+#include <AssertMacros.h>
+
+#include "keychain/SecureObjectSync/SOSInternal.h"
+#include "keychain/SecureObjectSync/SOSPeer.h"
+#include "keychain/SecureObjectSync/SOSPeerInfoInternal.h"
+#include "keychain/SecureObjectSync/SOSPeerInfoCollections.h"
+#include "keychain/SecureObjectSync/SOSCircle.h"
+#include <Security/SecFramework.h>
+
+#include <Security/SecKey.h>
+#include <Security/SecKeyPriv.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <utilities/SecCFWrappers.h>
+
+//#include "ckdUtilities.h"
+
+#include <corecrypto/ccder.h>
+#include <corecrypto/ccdigest.h>
+#include <corecrypto/ccsha2.h>
+
+
+#include <utilities/der_plist.h>
+#include <utilities/der_plist_internal.h>
+#include <corecrypto/ccder.h>
+#include <utilities/der_date.h>
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "SOSRing.h"
+#include "SOSRingUtils.h"
+
+CFGiblisWithCompareFor(SOSRing);
+
+/* unSignedInformation Dictionary Keys */
+CFStringRef sApplicantsKey              = CFSTR("Applicants");
+CFStringRef sRejectionsKey              = CFSTR("Rejections");
+CFStringRef sLastPeerToModifyKey        = CFSTR("LastModifier");
+
+/* signedInformation Dictionary Keys */
+CFStringRef sPeerIDsKey                 = CFSTR("PeerIDs");
+CFStringRef sPayloadKey                 = CFSTR("Payload");
+CFStringRef sBackupViewSetKey           = CFSTR("BackupViews");
+CFStringRef sGenerationKey              = CFSTR("Generation");
+CFStringRef sNameKey                    = CFSTR("RingName");
+CFStringRef sTypeKey                    = CFSTR("RingType");
+CFStringRef sIdentifierKey              = CFSTR("Identifier");
+CFStringRef sRingVersionKey              = CFSTR("RingVersion");
+
+#define RINGVERSION 1
+
+SOSRingRef SOSRingAllocate(void) {
+    return (SOSRingRef) CFTypeAllocate(SOSRing, struct __OpaqueSOSRing, ALLOCATOR);
+}
+
+__unused static bool addValueToDict(CFMutableDictionaryRef thedict, CFStringRef key, CFTypeRef value) {
+    if(!value) return false;
+    CFDictionaryAddValue(thedict, key, value);
+    return true;
+}
+
+static bool setValueInDict(CFMutableDictionaryRef thedict, CFStringRef key, CFTypeRef value) {
+    if(!value) return false;
+    CFDictionarySetValue(thedict, key, value);
+    return true;
+}
+
+static CFMutableSetRef CFSetCreateMutableForSOSPeerIDs(void) {
+    return CFSetCreateMutable(ALLOCATOR, 0, &kCFTypeSetCallBacks);
+}
+
+
+static inline
+SOSRingRef SOSRingConvertAndAssertStable(CFTypeRef ringAsType) {
+    if (CFGetTypeID(ringAsType) != SOSRingGetTypeID())
+        return NULL;
+
+    SOSRingRef ring = (SOSRingRef) ringAsType;
+
+    SOSRingAssertStable(ring);
+
+    return ring;
+}
+
+// MARK: Ring Name
+
+CFStringRef SOSRingGetName(SOSRingRef ring) {
+    assert(ring);
+    assert(ring->signedInformation);
+    return asString(CFDictionaryGetValue(ring->signedInformation, sNameKey), NULL);
+}
+
+const char *SOSRingGetNameC(SOSRingRef ring) {
+    CFStringRef name = asString(SOSRingGetName(ring), NULL);
+    if (!name)
+        return strdup("");
+    return CFStringToCString(name);
+}
+
+static inline bool SOSRingSetName(SOSRingRef ring, CFStringRef name) {
+    assert(ring);
+    assert(ring->signedInformation);
+    return setValueInDict(ring->signedInformation, sNameKey, name);
+}
+
+// MARK: Ring Type
+
+static bool SOSRingCheckType(SOSRingType type, CFErrorRef *error) {
+    if(type < kSOSRingTypeCount) return true;
+    SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Bad Ring Type Specification"), (error != NULL) ? *error : NULL, error);
+    return false;
+}
+
+uint32_t SOSRingGetType(SOSRingRef ring) {
+    uint32_t retval = kSOSRingTypeError; // Error return
+    SOSRingAssertStable(ring);
+    if(!ring->signedInformation) return retval;
+    CFNumberRef ringtype = (CFNumberRef) CFDictionaryGetValue(ring->signedInformation, sTypeKey);
+    CFNumberGetValue(ringtype, kCFNumberSInt32Type, &retval);
+    return retval;
+}
+
+static inline bool SOSRingSetType(SOSRingRef ring, uint32_t ringtype) {
+    bool retval = false;
+    CFNumberRef cfrtype = NULL;
+    SOSRingAssertStable(ring);
+    require_action_quiet(SOSRingCheckType(ringtype, NULL), errOut, secnotice("ring", "Bad ring type specification"));
+    cfrtype = CFNumberCreate(ALLOCATOR, kCFNumberSInt32Type, &ringtype);
+    retval = setValueInDict(ring->signedInformation, sTypeKey, cfrtype);
+errOut:
+    CFReleaseNull(cfrtype);
+    return retval;
+}
+
+// MARK: Version
+
+uint32_t SOSRingGetVersion(SOSRingRef ring) {
+    uint32_t version = 0;
+    assert(ring);
+    assert(ring->signedInformation);
+    CFNumberRef cfversion = CFDictionaryGetValue(ring->signedInformation, sRingVersionKey);
+    require_action_quiet(cfversion, errOut, secnotice("ring", "Could not create version number"));
+    CFNumberGetValue(cfversion, kCFNumberSInt32Type, &version);
+errOut:
+    return version;
+}
+
+static inline bool SOSRingSetVersion(SOSRingRef ring) {
+    assert(ring);
+    assert(ring->signedInformation);
+    int32_t thisversion = RINGVERSION;
+    CFNumberRef version = CFNumberCreate(ALLOCATOR, kCFNumberSInt32Type, &thisversion);
+    require_action_quiet(version, errOut, secnotice("ring", "Could not create version number"));
+    CFDictionarySetValue(ring->signedInformation, sRingVersionKey, version);
+    CFReleaseNull(version);
+    return true;
+errOut:
+    return false;
+}
+
+// MARK: Identifier
+
+CFStringRef SOSRingGetIdentifier(SOSRingRef ring) {
+    assert(ring);
+    assert(ring->signedInformation);
+    return CFDictionaryGetValue(ring->signedInformation, sIdentifierKey);
+}
+
+static inline bool SOSRingSetIdentifier(SOSRingRef ring) {
+    assert(ring);
+    assert(ring->signedInformation);
+    bool retval = false;
+    CFStringRef identifier = NULL;
+    CFUUIDRef uuid = CFUUIDCreate(ALLOCATOR);
+    require_action_quiet(uuid, errOut, secnotice("ring", "Could not create ring identifier"));
+    identifier = CFUUIDCreateString(ALLOCATOR, uuid);
+    CFDictionarySetValue(ring->signedInformation, sIdentifierKey, identifier);
+    retval = true;
+errOut:
+    CFReleaseNull(uuid);
+    CFReleaseNull(identifier);
+    return retval;
+}
+
+// MARK: Ring Identity
+
+bool SOSRingIsSame(SOSRingRef ring1, SOSRingRef ring2) {
+    CFStringRef name1 = SOSRingGetName(ring1);
+    CFStringRef name2 = SOSRingGetName(ring2);
+    require_action_quiet(name1 && name2, errOut, secnotice("ring", "Cannot get both names to consider rings the same"));
+    if(CFEqualSafe(name1, name2) != true) return false;
+
+    uint32_t type1 = SOSRingGetType(ring1);
+    uint32_t type2 = SOSRingGetVersion(ring2);
+    require_action_quiet(type1 && type2, errOut, secnotice("ring", "Cannot get both types to consider rings the same"));
+    if(type1 != type2) return false;
+
+    CFStringRef identifier1 = SOSRingGetIdentifier(ring1);
+    CFStringRef identifier2 = SOSRingGetIdentifier(ring2);
+    require_action_quiet(identifier1 && identifier2, errOut, secnotice("ring", "Cannot get both identifiers to consider rings the same"));
+    if(CFEqualSafe(identifier1, identifier2) != true) return false;
+
+    return true;
+errOut:
+    return false;
+
+}
+
+static Boolean SOSRingCompare(CFTypeRef lhs, CFTypeRef rhs) {
+    if (CFGetTypeID(lhs) != SOSRingGetTypeID()
+        || CFGetTypeID(rhs) != SOSRingGetTypeID())
+        return false;
+
+    SOSRingRef left = SOSRingConvertAndAssertStable(lhs);
+    SOSRingRef right = SOSRingConvertAndAssertStable(rhs);
+
+    return NULL != left && NULL != right
+    && CFEqualSafe(left->unSignedInformation, right->unSignedInformation)
+    && CFEqualSafe(left->signedInformation, right->signedInformation)
+    && CFEqualSafe(left->data, right->data)
+    && CFEqualSafe(left->signatures, right->signatures);
+}
+
+
+// MARK: Ring Generation Count
+
+SOSGenCountRef SOSRingGetGeneration(SOSRingRef ring) {
+    assert(ring);
+    assert(ring->signedInformation);
+    return CFDictionaryGetValue(ring->signedInformation, sGenerationKey);
+}
+
+static inline bool SOSRingSetGeneration(SOSRingRef ring, SOSGenCountRef gen) {
+    assert(ring);
+    assert(ring->signedInformation);
+    return setValueInDict(ring->signedInformation, sGenerationKey, gen);
+}
+
+void SOSRingGenerationIncrement(SOSRingRef ring) {
+    SOSGenCountRef gen = SOSRingGetGeneration(ring);
+    SOSGenCountRef newgen = SOSGenerationIncrementAndCreate(gen);
+    SOSRingSetGeneration(ring, newgen);
+    CFReleaseNull(newgen);
+}
+
+bool SOSRingIsOlderGeneration(SOSRingRef olderRing, SOSRingRef newerRing) {
+    SOSGenCountRef old = SOSRingGetGeneration(olderRing);
+    SOSGenCountRef new = SOSRingGetGeneration(newerRing);
+    return SOSGenerationIsOlder(old, new);
+}
+
+void SOSRingGenerationCreateWithBaseline(SOSRingRef newring, SOSRingRef baseline) {
+    if(!newring) return;
+    SOSGenCountRef gen = SOSGenerationCreateWithBaseline(SOSRingGetGeneration(baseline));
+    SOSRingSetGeneration(newring, gen);
+    CFReleaseNull(gen);
+}
+
+// MARK: Last Modifier
+CFStringRef SOSRingGetLastModifier(SOSRingRef ring) {
+    assert(ring);
+    assert(ring->unSignedInformation);
+    return CFDictionaryGetValue(ring->unSignedInformation, sLastPeerToModifyKey);
+}
+
+bool SOSRingSetLastModifier(SOSRingRef ring, CFStringRef peerID) {
+    assert(ring);
+    assert(ring->unSignedInformation);
+    return setValueInDict(ring->unSignedInformation, sLastPeerToModifyKey, peerID);
+}
+
+
+// MARK: Ring Applicants
+
+CFMutableSetRef SOSRingGetApplicants(SOSRingRef ring) {
+    SOSRingAssertStable(ring);
+    return (CFMutableSetRef) CFDictionaryGetValue(ring->unSignedInformation, sApplicantsKey);
+}
+
+bool SOSRingSetApplicants(SOSRingRef ring, CFMutableSetRef applicants) {
+    SOSRingAssertStable(ring);
+    return setValueInDict(ring->unSignedInformation, sApplicantsKey, applicants);
+}
+
+int SOSRingCountApplicants(SOSRingRef ring) {
+    SOSRingAssertStable(ring);
+    return (int)CFSetGetCount(SOSRingGetApplicants(ring));
+}
+
+bool SOSRingHasApplicant(SOSRingRef ring, CFStringRef peerID) {
+    SOSRingAssertStable(ring);
+    return CFSetContainsValue(SOSRingGetApplicants(ring), peerID);
+}
+
+CFMutableSetRef SOSRingCopyApplicants(SOSRingRef ring) {
+    SOSRingAssertStable(ring);
+    CFSetRef applicants = SOSRingGetApplicants(ring);
+    return CFSetCreateMutableCopy(ALLOCATOR, 0, applicants);
+}
+
+bool SOSRingAddApplicant(SOSRingRef ring, CFStringRef peerid) {
+    CFMutableSetRef applicants = SOSRingGetApplicants(ring);
+    CFSetAddValue(applicants, peerid);
+    return true;
+}
+
+bool SOSRingRemoveApplicant(SOSRingRef ring, CFStringRef peerid) {
+    CFMutableSetRef applicants = SOSRingGetApplicants(ring);
+    CFSetRemoveValue(applicants, peerid);
+    return true;
+}
+
+// MARK: Ring Rejections
+
+static inline CFMutableSetRef SOSRingGetRejections(SOSRingRef ring) {
+    SOSRingAssertStable(ring);
+    return (CFMutableSetRef) CFDictionaryGetValue(ring->unSignedInformation, sRejectionsKey);
+}
+
+static inline bool SOSRingSetRejections(SOSRingRef ring, CFMutableSetRef rejections) {
+    SOSRingAssertStable(ring);
+    return setValueInDict(ring->unSignedInformation, sRejectionsKey, rejections);
+}
+
+int SOSRingCountRejections(SOSRingRef ring) {
+    CFSetRef rejects = SOSRingGetRejections(ring);
+    return (int)CFSetGetCount(rejects);
+}
+
+bool SOSRingHasRejection(SOSRingRef ring, CFStringRef peerID) {
+    SOSRingAssertStable(ring);
+    return CFSetContainsValue(SOSRingGetRejections(ring), peerID);
+}
+
+CFMutableSetRef SOSRingCopyRejections(SOSRingRef ring) {
+    CFSetRef rejects = SOSRingGetRejections(ring);
+    return CFSetCreateMutableCopy(ALLOCATOR, 0, rejects);
+}
+
+
+bool SOSRingAddRejection(SOSRingRef ring, CFStringRef peerid) {
+    CFMutableSetRef rejects = SOSRingGetRejections(ring);
+    CFSetAddValue(rejects, peerid);
+    return true;
+}
+
+bool SOSRingRemoveRejection(SOSRingRef ring, CFStringRef peerid) {
+    CFMutableSetRef rejects = SOSRingGetRejections(ring);
+    CFSetRemoveValue(rejects, peerid);
+    return true;
+}
+
+// MARK: Ring Payload
+
+CFDataRef SOSRingGetPayload_Internal(SOSRingRef ring) {
+    SOSRingAssertStable(ring);
+    return (CFDataRef) CFDictionaryGetValue(ring->signedInformation, sPayloadKey);
+}
+
+bool SOSRingSetPayload_Internal(SOSRingRef ring, CFDataRef payload) {
+    SOSRingAssertStable(ring);
+    return setValueInDict(ring->signedInformation, sPayloadKey, payload);
+}
+
+// MARK: Ring Backup Viewset
+
+
+CFSetRef SOSRingGetBackupViewset_Internal(SOSRingRef ring) {
+    SOSRingAssertStable(ring);
+    return asSet(CFDictionaryGetValue(ring->signedInformation, sBackupViewSetKey), NULL);
+}
+
+bool SOSRingSetBackupViewset_Internal(SOSRingRef ring, CFSetRef viewSet) {
+    SOSRingAssertStable(ring);
+    return setValueInDict(ring->signedInformation, sBackupViewSetKey, viewSet);
+}
+
+
+
+// MARK: Ring PeerIDs
+
+static inline CFMutableSetRef SOSRingGetPeerIDs(SOSRingRef ring) {
+    SOSRingAssertStable(ring);
+    return (CFMutableSetRef) asSet(CFDictionaryGetValue(ring->signedInformation, sPeerIDsKey), NULL);
+}
+
+bool SOSRingSetPeerIDs(SOSRingRef ring, CFMutableSetRef peers) {
+    SOSRingAssertStable(ring);
+    return setValueInDict(ring->signedInformation, sPeerIDsKey, peers);
+}
+
+int SOSRingCountPeerIDs(SOSRingRef ring) {
+    CFSetRef peerIDs = SOSRingGetPeerIDs(ring);
+    return (int)CFSetGetCount(peerIDs);
+}
+
+
+bool SOSRingHasPeerID(SOSRingRef ring, CFStringRef peerID) {
+    SOSRingAssertStable(ring);
+    return CFSetContainsValue(SOSRingGetPeerIDs(ring), peerID);
+}
+
+CFMutableSetRef SOSRingCopyPeerIDs(SOSRingRef ring) {
+    CFSetRef peerIDs = SOSRingGetPeerIDs(ring);
+    return CFSetCreateMutableCopy(ALLOCATOR, 0, peerIDs);
+}
+
+void SOSRingAddAll(SOSRingRef ring, CFSetRef peerInfosOrIDs) {
+    CFSetForEach(peerInfosOrIDs, ^(const void *value) {
+        CFStringRef peerID = value;
+
+        if (isSOSPeerInfo(value))
+            peerID = SOSPeerInfoGetPeerID((SOSPeerInfoRef)value);
+
+        if (isString(peerID))
+            SOSRingAddPeerID(ring, peerID);
+    });
+}
+
+bool SOSRingAddPeerID(SOSRingRef ring, CFStringRef peerid) {
+    CFMutableSetRef peerIDs = SOSRingGetPeerIDs(ring);
+    CFSetAddValue(peerIDs, peerid);
+    return true;
+}
+
+bool SOSRingRemovePeerID(SOSRingRef ring, CFStringRef peerid) {
+    CFMutableSetRef peerIDs = SOSRingGetPeerIDs(ring);
+    CFSetRemoveValue(peerIDs, peerid);
+    return true;
+}
+
+void SOSRingForEachPeerID(SOSRingRef ring, void (^action)(CFStringRef peerID)) {
+    SOSRingAssertStable(ring);
+    CFMutableSetRef peerIDs = SOSRingGetPeerIDs(ring);
+    if(!peerIDs) return;
+    CFSetForEach(peerIDs, ^(const void*value) {
+        CFStringRef peerID = (CFStringRef) value;
+            action(peerID);
+    });
+}
+
+// MARK: SOSRing Ops
+
+SOSRingRef SOSRingCreate_Internal(CFStringRef name, SOSRingType type, CFErrorRef *error) {
+    SOSRingRef r = SOSRingAllocate();
+    SOSGenCountRef gen = SOSGenerationCreate();
+
+    require_action_quiet(name, errout0,
+        SOSCreateError(kSOSErrorNoCircleName, CFSTR("No ring name"), NULL, error));
+    require_action_quiet(SOSRingCheckType(type, error), errout0,
+        SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Unknown ring type"), NULL, error));
+    require_action_quiet((r->unSignedInformation = CFDictionaryCreateMutableForCFTypes(ALLOCATOR)), errout,
+        SOSCreateError(kSOSErrorAllocationFailure, CFSTR("Failed to allocate unsigned information area"), NULL, error));
+    require_action_quiet((r->signedInformation = CFDictionaryCreateMutableForCFTypes(ALLOCATOR)), errout,
+        SOSCreateError(kSOSErrorAllocationFailure, CFSTR("Failed to allocate signed information area"), NULL, error));
+    require_action_quiet((r->signatures = CFDictionaryCreateMutableForCFTypes(ALLOCATOR)), errout,
+        SOSCreateError(kSOSErrorAllocationFailure, CFSTR("Failed to allocate signature area"), NULL, error));
+    require_action_quiet((r->data = CFDictionaryCreateMutableForCFTypes(ALLOCATOR)), errout,
+        SOSCreateError(kSOSErrorAllocationFailure, CFSTR("Failed to allocate data area"), NULL, error));
+
+    require_action_quiet(SOSRingSetName(r, name), errout,
+        SOSCreateError(kSOSErrorAllocationFailure, CFSTR("Failed to allocate ring name area"), NULL, error));
+    require_action_quiet(SOSRingSetType(r, type), errout,
+        SOSCreateError(kSOSErrorAllocationFailure, CFSTR("Failed to allocate ring type"), NULL, error));
+    require_action_quiet(SOSRingSetVersion(r), errout,
+        SOSCreateError(kSOSErrorAllocationFailure, CFSTR("Failed to allocate ring version"), NULL, error));
+    require_action_quiet(SOSRingSetIdentifier(r), errout,
+        SOSCreateError(kSOSErrorAllocationFailure, CFSTR("Failed to allocate ring identifier"), NULL, error));
+    
+    CFMutableSetRef peerIDs = CFSetCreateMutableForSOSPeerIDs();
+    require_action_quiet(SOSRingSetApplicants(r, peerIDs), errout,
+        SOSCreateError(kSOSErrorAllocationFailure, CFSTR("Failed to allocate applicant area"), NULL, error));
+    CFReleaseNull(peerIDs);
+    
+    CFMutableSetRef rejectedIDs = CFSetCreateMutableForSOSPeerIDs();
+    require_action_quiet(SOSRingSetRejections(r, rejectedIDs), errout,
+        SOSCreateError(kSOSErrorAllocationFailure, CFSTR("Failed to allocate rejection area"), NULL, error));
+    CFReleaseNull(rejectedIDs);
+    
+    require_action_quiet(SOSRingSetGeneration(r, gen), errout,
+        SOSCreateError(kSOSErrorAllocationFailure, CFSTR("Failed to allocate generation count"), NULL, error));
+    
+    peerIDs = CFSetCreateMutableForSOSPeerIDs();
+    require_action_quiet(SOSRingSetPeerIDs(r, peerIDs), errout,
+        SOSCreateError(kSOSErrorAllocationFailure, CFSTR("Failed to allocate PeerID"), NULL, error));
+    CFReleaseNull(gen);
+    CFReleaseNull(peerIDs);
+    
+    return r;
+errout:
+    CFReleaseNull(r->unSignedInformation);
+    CFReleaseNull(r->signedInformation);
+    CFReleaseNull(r->signatures);
+    CFReleaseNull(r->data);
+errout0:
+    CFReleaseNull(gen);
+    CFReleaseNull(r);
+    return NULL;
+}
+
+
+static void SOSRingDestroy(CFTypeRef aObj) {
+    SOSRingRef c = (SOSRingRef) aObj;
+
+    CFReleaseNull(c->unSignedInformation);
+    CFReleaseNull(c->signedInformation);
+    CFReleaseNull(c->data);
+    CFReleaseNull(c->signatures);
+}
+
+
+SOSRingRef SOSRingCopyRing(SOSRingRef original, CFErrorRef *error) {
+    SOSRingRef r = CFTypeAllocate(SOSRing, struct __OpaqueSOSRing, ALLOCATOR);
+
+    assert(original);
+    r->unSignedInformation = CFDictionaryCreateMutableCopy(ALLOCATOR, 0, original->unSignedInformation);
+    r->signedInformation = CFDictionaryCreateMutableCopy(ALLOCATOR, 0, original->signedInformation);
+    r->signatures = CFDictionaryCreateMutableCopy(ALLOCATOR, 0, original->signatures);
+    r->data = CFDictionaryCreateMutableCopy(ALLOCATOR, 0, original->data);
+
+    return r;
+}
+
+bool SOSRingIsEmpty_Internal(SOSRingRef ring) {
+    return CFSetGetCount(SOSRingGetPeerIDs(ring)) == 0;
+}
+
+bool SOSRingIsOffering_Internal(SOSRingRef ring) {
+    return SOSRingCountPeers(ring) == 1;
+}
+
+bool SOSRingResetToEmpty_Internal(SOSRingRef ring, CFErrorRef *error) {
+    SOSGenCountRef gen = NULL;
+    CFSetRemoveAllValues(SOSRingGetApplicants(ring));
+    CFSetRemoveAllValues(SOSRingGetRejections(ring));
+    CFSetRemoveAllValues(SOSRingGetPeerIDs(ring));
+    CFDictionaryRemoveAllValues(ring->signatures);
+    SOSRingSetGeneration(ring, gen = SOSGenerationCreate());
+    CFReleaseNull(gen);
+    return true;
+}
+
+// MARK: PeerIDs in Ring
+
+int SOSRingCountPeers(SOSRingRef ring) {
+    SOSRingAssertStable(ring);
+    return (int) CFSetGetCount(SOSRingGetPeerIDs(ring));
+}
+
+
+bool SOSRingHasPeerWithID(SOSRingRef ring, CFStringRef peerid, CFErrorRef *error) {
+    SOSRingAssertStable(ring);
+    return CFSetContainsValue(SOSRingGetPeerIDs(ring), peerid);
+}
+
+// MARK: Ring Signatures
+
+
+static inline CFDictionaryRef SOSRingGetSignatures(SOSRingRef ring) {
+    return ring->signatures;
+}
+
+static inline CFDataRef SOSRingGetSignatureForPeerID(SOSRingRef ring, CFStringRef peerID) {
+    if(!ring || !peerID) return NULL;
+    CFDataRef result = NULL;
+    CFTypeRef value = (CFDataRef)CFDictionaryGetValue(SOSRingGetSignatures(ring), peerID);
+    if (isData(value)) result = (CFDataRef) value;
+    return result;
+}
+
+static CFDataRef SOSRingCreateHash(const struct ccdigest_info *di, SOSRingRef ring, CFErrorRef *error) {
+    uint8_t hash_result[di->output_size];
+
+    size_t dersize = der_sizeof_plist(ring->signedInformation, error);
+    if(dersize == 0) {
+        return false;
+    }
+    uint8_t *der = malloc(dersize);
+    if (der == NULL) {
+        return false;
+    }
+    if (der_encode_plist(ring->signedInformation, error, der, der+dersize) == NULL) {
+        free(der);
+        return false;
+    }
+
+    ccdigest(di, dersize, der, hash_result);
+    free(der);
+    return CFDataCreate(NULL, hash_result, di->output_size);
+}
+
+static bool SOSRingSetSignature(SOSRingRef ring, SecKeyRef privKey, CFDataRef signature, CFErrorRef *error) {
+    bool result = false;
+    SecKeyRef pubkey = SecKeyCreatePublicFromPrivate(privKey);
+    CFStringRef pubKeyID = SOSCopyIDOfKey(pubkey, error);
+    require_quiet(pubKeyID, fail);
+    CFDictionarySetValue(ring->signatures, pubKeyID, signature);
+    result = true;
+fail:
+    CFReleaseSafe(pubkey);
+    CFReleaseSafe(pubKeyID);
+    return result;
+}
+
+bool SOSRingRemoveSignatures(SOSRingRef ring, CFErrorRef *error) {
+    CFDictionaryRemoveAllValues(ring->signatures);
+    return true;
+}
+
+static CFDataRef SOSCopySignedHash(SecKeyRef privKey, CFDataRef hash, CFErrorRef *error) {
+    size_t siglen = SecKeyGetSize(privKey, kSecKeySignatureSize)+16;
+    uint8_t sig[siglen];
+    OSStatus stat =  SecKeyRawSign(privKey, kSecPaddingNone, CFDataGetBytePtr(hash), CFDataGetLength(hash), sig, &siglen);
+    if(stat) {
+        return NULL;
+    }
+    return CFDataCreate(NULL, sig, siglen);
+}
+
+static bool SOSRingSign(SOSRingRef ring, SecKeyRef privKey, CFErrorRef *error) {
+    if (!ring || !privKey) {
+        SOSCreateError(kSOSErrorBadSignature, CFSTR("SOSRingSign Lacking ring or private key"),
+            (error != NULL) ? *error : NULL, error);
+        return false;
+    }
+    const struct ccdigest_info *di = ccsha256_di();
+    CFDataRef hash = SOSRingCreateHash(di, ring, error);
+    CFDataRef signature = SOSCopySignedHash(privKey, hash, error);
+    SOSRingSetSignature(ring, privKey, signature, error);
+    CFRelease(signature);
+    CFReleaseNull(hash);
+    return true;
+}
+
+bool SOSRingVerifySignatureExists(SOSRingRef ring, SecKeyRef pubKey, CFErrorRef *error) {
+    CFStringRef pubKeyID = SOSCopyIDOfKey(pubKey, error);
+    CFDataRef signature = SOSRingGetSignatureForPeerID(ring, pubKeyID);
+    CFReleaseNull(pubKeyID);
+    return NULL != signature;
+}
+
+bool SOSRingVerify(SOSRingRef ring, SecKeyRef pubKey, CFErrorRef *error) {
+    CFStringRef pubKeyID = SOSCopyIDOfKey(pubKey, error);
+    CFDataRef signature = SOSRingGetSignatureForPeerID(ring, pubKeyID);
+    CFReleaseNull(pubKeyID);
+    if(!signature) return false;
+    CFDataRef hash = SOSRingCreateHash(ccsha256_di(), ring, error);
+    bool success = SecKeyRawVerify(pubKey, kSecPaddingNone, CFDataGetBytePtr(hash), CFDataGetLength(hash),
+                           CFDataGetBytePtr(signature), CFDataGetLength(signature)) == errSecSuccess;
+    CFReleaseNull(hash);
+    return success;
+}
+
+bool SOSRingVerifyPeerSigned(SOSRingRef ring, SOSPeerInfoRef peer, CFErrorRef *error) {
+    bool result = false;
+    SecKeyRef pubkey = SOSPeerInfoCopyPubKey(peer, error);
+    require_quiet(pubkey, fail);
+
+    result = SOSRingVerify(ring, pubkey, error);
+
+fail:
+    CFReleaseSafe(pubkey);
+    return result;
+}
+
+static bool SOSRingEnsureRingConsistency(SOSRingRef ring, CFErrorRef *error) {
+    secnotice("Development", "SOSRingEnsureRingConsistency requires ring membership and generation count consistency check", NULL);
+    return true;
+}
+
+bool SOSRingGenerationSign_Internal(SOSRingRef ring, SecKeyRef privKey, CFErrorRef *error) {
+    if(!privKey || !ring) return false;
+    bool retval = false;
+    SOSRingGenerationIncrement(ring);
+    require_quiet(SOSRingEnsureRingConsistency(ring, error), fail);
+    require_quiet(SOSRingRemoveSignatures(ring, error), fail);
+    require_quiet(SOSRingSign(ring, privKey, error), fail);
+    retval = true;
+fail:
+    return retval;
+}
+
+// MARK: Concordance
+
+bool SOSRingConcordanceSign_Internal(SOSRingRef ring, SecKeyRef privKey, CFErrorRef *error) {
+    if(!privKey || !ring) return false;
+    bool retval = false;
+    require_quiet(SOSRingSign(ring, privKey, error), fail);
+    retval = true;
+fail:
+    return retval;
+}
+
+
+
+// MARK: Debugging
+
+static inline void CFSetForEachPeerID(CFSetRef set, void (^operation)(CFStringRef peerID)) {
+    CFSetForEach(set, ^(const void *value) {
+        CFStringRef peerID = (CFStringRef) value;
+        operation(peerID);
+    });
+}
+
+static CFStringRef CreateCommaSeparatedPeerIDs(CFSetRef peers) {
+    CFMutableStringRef result = CFStringCreateMutable(kCFAllocatorDefault, 0);
+
+    __block bool addSeparator = false;
+
+    CFSetForEachPeerID(peers, ^(CFStringRef peerID) {
+        if (addSeparator) {
+            CFStringAppendCString(result, ", ", kCFStringEncodingUTF8);
+        }
+        CFStringAppend(result, peerID);
+
+        addSeparator = true;
+    });
+
+    return result;
+}
+
+CFDictionaryRef SOSRingCopyPeerIDList(SOSRingRef ring) {
+    CFStringRef peerIDS = CreateCommaSeparatedPeerIDs(SOSRingGetPeerIDs(ring));
+    CFStringRef applicantIDs = CreateCommaSeparatedPeerIDs(SOSRingGetApplicants(ring));
+    CFStringRef rejectIDs = CreateCommaSeparatedPeerIDs(SOSRingGetRejections(ring));
+    CFDictionaryRef list = CFDictionaryCreateForCFTypes(ALLOCATOR,
+                                        CFSTR("MEMBER"), peerIDS,
+                                        CFSTR("APPLICANTS"), applicantIDs,
+                                        CFSTR("REJECTS"), rejectIDs,
+                                        NULL);
+    
+    CFReleaseNull(peerIDS);
+    CFReleaseNull(applicantIDs);
+    CFReleaseNull(rejectIDs);
+    return list;
+}
+
+ CFStringRef SOSRingCopySignerList(SOSRingRef ring) {
+    __block bool addSeparator = false;
+   CFMutableStringRef signers = CFStringCreateMutable(ALLOCATOR, 0);
+    CFDictionaryForEach(ring->signatures, ^(const void *key, const void *value) {
+        CFStringRef peerID = (CFStringRef) key;
+        if (addSeparator)
+            CFStringAppendCString(signers, ", ", kCFStringEncodingUTF8);
+        CFStringAppend(signers, peerID);
+        addSeparator = true;
+    });
+    return signers;
+}
+
+static CFStringRef SOSRingCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOpts) {
+    SOSRingRef ring = (SOSRingRef) aObj;
+
+    SOSRingAssertStable(ring);
+
+    CFDictionaryRef peers = SOSRingCopyPeerIDList(ring);
+    CFStringRef signers = SOSRingCopySignerList(ring);
+
+    CFDataRef payload = SOSRingGetPayload(ring, NULL);
+
+    CFStringRef gcString = SOSGenerationCountCopyDescription(SOSRingGetGeneration(ring));
+
+    CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0);
+
+    CFStringAppendFormat(description, formatOpts, CFSTR("<SOSRing@%p: '%@', Version %u, "), ring, SOSRingGetName(ring), SOSRingGetVersion(ring));
+    CFStringAppendFormat(description, formatOpts, CFSTR("UUID: %@, "), SOSRingGetIdentifier(ring));
+    SOSGenerationCountWithDescription(SOSRingGetGeneration(ring), ^(CFStringRef gcString) {
+        CFStringAppendFormat(description, formatOpts, CFSTR("Gen: %@, "), gcString);
+    });
+    CFStringAppendFormat(description, formatOpts, CFSTR("Mod: %@, "), SOSRingGetLastModifier(ring));
+    
+    CFStringAppendFormat(description, formatOpts, CFSTR("D: %ld "), payload ? CFDataGetLength(payload) : 0);
+
+    SOSBackupSliceKeyBagRef payloadAsBSKB = SOSRingCopyBackupSliceKeyBag(ring, NULL);
+
+    if (payloadAsBSKB) {
+        CFStringAppendFormat(description, formatOpts, CFSTR("%@ "), payloadAsBSKB);
+    }
+
+    CFReleaseSafe(payloadAsBSKB);
+
+    CFStringAppendFormat(description, formatOpts, CFSTR("P: [%@], "), CFDictionaryGetValue(peers, CFSTR("MEMBER")));
+    CFStringAppendFormat(description, formatOpts, CFSTR("A: [%@], "), CFDictionaryGetValue(peers, CFSTR("APPLICANTS")));
+    CFStringAppendFormat(description, formatOpts, CFSTR("R: [%@], "), CFDictionaryGetValue(peers, CFSTR("REJECTS")));
+    CFStringAppendFormat(description, formatOpts, CFSTR("S: [%@]>"), signers);
+
+    CFReleaseNull(gcString);
+    CFReleaseNull(peers);
+    CFReleaseNull(signers);
+    CFReleaseNull(peers);
+    
+    return description;
+}
+
+#define SIGLEN 128
+
+