SOSRingUtils.c   [plain text]


/*
 * 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 <Security/SecureObjectSync/SOSInternal.h>
#include <Security/SecureObjectSync/SOSPeer.h>
#include <Security/SecureObjectSync/SOSPeerInfoInternal.h>
#include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
#include <Security/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;
}

static 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