--- /dev/null
+/*
+ * Created by Michael Brouwer on 6/22/12.
+ * Copyright 2012 Apple Inc. All Rights Reserved.
+ */
+
+/*
+ * SOSCircle.c - Implementation of the secure object syncing transport
+ */
+
+#include <AssertMacros.h>
+
+#include <CoreFoundation/CFArray.h>
+#include <SecureObjectSync/SOSCircle.h>
+#include <SecureObjectSync/SOSCloudCircleInternal.h>
+#include <SecureObjectSync/SOSInternal.h>
+#include <SecureObjectSync/SOSEngine.h>
+#include <SecureObjectSync/SOSPeer.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/SecFramework.h>
+
+#include <Security/SecKey.h>
+#include <Security/SecKeyPriv.h>
+
+#include <utilities/SecCFWrappers.h>
+//#include "ckdUtilities.h"
+
+#include <utilities/der_plist.h>
+#include <utilities/der_plist_internal.h>
+
+#include <corecrypto/ccder.h>
+#include <corecrypto/ccdigest.h>
+#include <corecrypto/ccsha2.h>
+
+#include <stdlib.h>
+#include <assert.h>
+
+enum {
+ kOnlyCompatibleVersion = 1, // Sometime in the future this name will be improved to reflect history.
+
+ kAlwaysIncompatibleVersion = UINT64_MAX,
+};
+
+struct __OpaqueSOSCircle {
+ CFRuntimeBase _base;
+
+ CFStringRef name;
+ CFNumberRef generation;
+ CFMutableArrayRef peers;
+ CFMutableArrayRef applicants;
+ CFMutableArrayRef rejected_applicants;
+
+ CFMutableDictionaryRef signatures;
+};
+
+CFGiblisWithCompareFor(SOSCircle);
+
+// Move the next 2 lines to SOSPeer if we need it.
+static void SOSPeerRelease(CFAllocatorRef allocator, const void *value) {
+ SOSPeerDispose((SOSPeerRef)value);
+}
+
+static const CFDictionaryValueCallBacks dispose_peer_callbacks = { .release = SOSPeerRelease };
+
+SOSCircleRef SOSCircleCreate(CFAllocatorRef allocator, CFStringRef name, CFErrorRef *error) {
+ SOSCircleRef c = CFTypeAllocate(SOSCircle, struct __OpaqueSOSCircle, allocator);
+ int64_t gen = 1;
+
+ assert(name);
+
+ c->name = CFStringCreateCopy(allocator, name);
+ c->generation = CFNumberCreate(allocator, kCFNumberSInt64Type, &gen);
+ c->peers = CFArrayCreateMutableForCFTypes(allocator);
+ c->applicants = CFArrayCreateMutableForCFTypes(allocator);
+ c->rejected_applicants = CFArrayCreateMutableForCFTypes(allocator);
+ c->signatures = CFDictionaryCreateMutableForCFTypes(allocator);
+
+ return c;
+}
+
+static CFNumberRef SOSCircleGenerationCopy(CFNumberRef generation) {
+ int64_t value;
+ CFAllocatorRef allocator = CFGetAllocator(generation);
+ CFNumberGetValue(generation, kCFNumberSInt64Type, &value);
+ return CFNumberCreate(allocator, kCFNumberSInt64Type, &value);
+}
+
+SOSCircleRef SOSCircleCopyCircle(CFAllocatorRef allocator, SOSCircleRef otherCircle, CFErrorRef *error)
+{
+ SOSCircleRef c = CFTypeAllocate(SOSCircle, struct __OpaqueSOSCircle, allocator);
+
+ assert(otherCircle);
+ c->name = CFStringCreateCopy(allocator, otherCircle->name);
+ c->generation = SOSCircleGenerationCopy(otherCircle->generation);
+ c->peers = CFArrayCreateMutableCopy(allocator, 0, otherCircle->peers);
+ c->applicants = CFArrayCreateMutableCopy(allocator, 0, otherCircle->applicants);
+ c->rejected_applicants = CFArrayCreateMutableCopy(allocator, 0, otherCircle->rejected_applicants);
+ c->signatures = CFDictionaryCreateMutableCopy(allocator, 0, otherCircle->signatures);
+
+ return c;
+}
+
+static inline
+void SOSCircleAssertStable(SOSCircleRef circle)
+{
+ assert(circle);
+ assert(circle->name);
+ assert(circle->generation);
+ assert(circle->peers);
+ assert(circle->applicants);
+ assert(circle->rejected_applicants);
+ assert(circle->signatures);
+}
+
+static inline
+SOSCircleRef SOSCircleConvertAndAssertStable(CFTypeRef circleAsType)
+{
+ if (CFGetTypeID(circleAsType) != SOSCircleGetTypeID())
+ return NULL;
+
+ SOSCircleRef circle = (SOSCircleRef) circleAsType;
+
+ SOSCircleAssertStable(circle);
+
+ return circle;
+}
+
+
+static Boolean SOSCircleCompare(CFTypeRef lhs, CFTypeRef rhs) {
+ if (CFGetTypeID(lhs) != SOSCircleGetTypeID()
+ || CFGetTypeID(rhs) != SOSCircleGetTypeID())
+ return false;
+
+ SOSCircleRef left = SOSCircleConvertAndAssertStable(lhs);
+ SOSCircleRef right = SOSCircleConvertAndAssertStable(rhs);
+
+ // TODO: we should be doing set equality for peers and applicants.
+ return NULL != left && NULL != right
+ && CFEqual(left->generation, right->generation)
+ && CFEqual(left->peers, right->peers)
+ && CFEqual(left->applicants, right->applicants)
+ && CFEqual(left->rejected_applicants, right->rejected_applicants)
+ && CFEqual(left->signatures, right->signatures);
+}
+
+
+static bool SOSCircleDigestArray(const struct ccdigest_info *di, CFMutableArrayRef array, void *hash_result, CFErrorRef *error)
+{
+ __block bool success = true;
+ ccdigest_di_decl(di, array_digest);
+ const void * a_digest = array_digest;
+
+ ccdigest_init(di, array_digest);
+ CFArraySortValues(array, CFRangeMake(0, CFArrayGetCount(array)), SOSPeerInfoCompareByID, SOSPeerCmpPubKeyHash);
+ CFArrayForEach(array, ^(const void *peer) {
+ if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes((SOSPeerInfoRef)peer, di, a_digest, error))
+ success = false;
+ });
+ ccdigest_final(di, array_digest, hash_result);
+
+ return success;
+}
+
+static bool SOSCircleHash(const struct ccdigest_info *di, SOSCircleRef circle, void *hash_result, CFErrorRef *error) {
+ ccdigest_di_decl(di, circle_digest);
+ ccdigest_init(di, circle_digest);
+ int64_t gen = SOSCircleGetGenerationSint(circle);
+ ccdigest_update(di, circle_digest, sizeof(gen), &gen);
+
+ SOSCircleDigestArray(di, circle->peers, hash_result, error);
+ ccdigest_update(di, circle_digest, di->output_size, hash_result);
+ ccdigest_final(di, circle_digest, hash_result);
+ return true;
+}
+
+static bool SOSCircleSetSignature(SOSCircleRef circle, SecKeyRef pubkey, CFDataRef signature, CFErrorRef *error) {
+ bool result = false;
+
+ CFStringRef pubKeyID = SOSCopyIDOfKey(pubkey, error);
+ require_quiet(pubKeyID, fail);
+ CFDictionarySetValue(circle->signatures, pubKeyID, signature);
+ result = true;
+
+fail:
+ CFReleaseSafe(pubKeyID);
+ return result;
+}
+
+static bool SOSCircleRemoveSignatures(SOSCircleRef circle, CFErrorRef *error) {
+ CFDictionaryRemoveAllValues(circle->signatures);
+ return true;
+}
+
+static CFDataRef SOSCircleGetSignature(SOSCircleRef circle, SecKeyRef pubkey, CFErrorRef *error) {
+ CFStringRef pubKeyID = SOSCopyIDOfKey(pubkey, error);
+ CFDataRef result = NULL;
+ require_quiet(pubKeyID, fail);
+
+ CFTypeRef value = (CFDataRef)CFDictionaryGetValue(circle->signatures, pubKeyID);
+
+ if (isData(value)) result = (CFDataRef) value;
+
+fail:
+ CFReleaseSafe(pubKeyID);
+ return result;
+}
+
+bool SOSCircleSign(SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
+ if (!privKey) return false; // Really assertion but not always true for now.
+ CFAllocatorRef allocator = CFGetAllocator(circle);
+ uint8_t tmp[4096];
+ size_t tmplen = 4096;
+ const struct ccdigest_info *di = ccsha256_di();
+ uint8_t hash_result[di->output_size];
+
+ SOSCircleHash(di, circle, hash_result, error);
+ OSStatus stat = SecKeyRawSign(privKey, kSecPaddingNone, hash_result, di->output_size, tmp, &tmplen);
+ if(stat) {
+ // TODO - Create a CFErrorRef;
+ secerror("Bad Circle SecKeyRawSign, stat: %ld", (long)stat);
+ SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Circle SecKeyRawSign"), (error != NULL) ? *error : NULL, error);
+ return false;
+ };
+ CFDataRef signature = CFDataCreate(allocator, tmp, tmplen);
+ SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(privKey);
+ SOSCircleSetSignature(circle, publicKey, signature, error);
+ CFReleaseNull(publicKey);
+ CFRelease(signature);
+ return true;
+}
+
+bool SOSCircleVerifySignatureExists(SOSCircleRef circle, SecKeyRef pubKey, CFErrorRef *error) {
+ if(!pubKey) {
+ // TODO ErrorRef
+ secerror("SOSCircleVerifySignatureExists no pubKey");
+ SOSCreateError(kSOSErrorBadFormat, CFSTR("SOSCircleVerifySignatureExists no pubKey"), (error != NULL) ? *error : NULL, error);
+ return false;
+ }
+ CFDataRef signature = SOSCircleGetSignature(circle, pubKey, error);
+ return NULL != signature;
+}
+
+bool SOSCircleVerify(SOSCircleRef circle, SecKeyRef pubKey, CFErrorRef *error) {
+ const struct ccdigest_info *di = ccsha256_di();
+ uint8_t hash_result[di->output_size];
+
+ SOSCircleHash(di, circle, hash_result, error);
+
+ CFDataRef signature = SOSCircleGetSignature(circle, pubKey, error);
+ if(!signature) return false;
+
+ return SecKeyRawVerify(pubKey, kSecPaddingNone, hash_result, di->output_size,
+ CFDataGetBytePtr(signature), CFDataGetLength(signature)) == errSecSuccess;
+}
+
+bool SOSCircleVerifyPeerSigned(SOSCircleRef circle, SOSPeerInfoRef peer, CFErrorRef *error) {
+ SecKeyRef pub_key = SOSPeerInfoCopyPubKey(peer);
+ bool result = SOSCircleVerify(circle, pub_key, error);
+ CFReleaseSafe(pub_key);
+ return result;
+}
+
+
+static CFIndex CFArrayRemoveAllPassing(CFMutableArrayRef array, bool (^test)(const void *) ){
+ CFIndex numberRemoved = 0;
+
+ CFIndex position = 0;
+ while (position < CFArrayGetCount(array) && !test(CFArrayGetValueAtIndex(array, position)))
+ ++position;
+
+ while (position < CFArrayGetCount(array)) {
+ CFArrayRemoveValueAtIndex(array, position);
+ ++numberRemoved;
+ while (position < CFArrayGetCount(array) && !test(CFArrayGetValueAtIndex(array, position)))
+ ++position;
+ }
+
+ return numberRemoved;
+}
+
+static CFIndex CFArrayRemoveAllWithMatchingID(CFMutableArrayRef array, SOSPeerInfoRef peerInfo) {
+ CFStringRef peer_id = SOSPeerInfoGetPeerID(peerInfo);
+
+ return CFArrayRemoveAllPassing(array, ^ bool (const void *element) {
+ SOSPeerInfoRef peer = (SOSPeerInfoRef) element;
+
+ return CFEqual(peer_id, SOSPeerInfoGetPeerID(peer));
+ });
+}
+
+static void SOSCircleRejectNonValidApplicants(SOSCircleRef circle, SecKeyRef pubkey) {
+ CFArrayRef applicants = SOSCircleCopyApplicants(circle, NULL);
+ CFArrayForEach(applicants, ^(const void *value) {
+ SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
+ if(!SOSPeerInfoApplicationVerify(pi, pubkey, NULL)) {
+ CFArrayRemoveAllWithMatchingID(circle->applicants, pi);
+ CFArrayAppendValue(circle->rejected_applicants, pi);
+ }
+ });
+}
+
+bool SOSCircleGenerationSign(SOSCircleRef circle, SecKeyRef user_approver, SOSFullPeerInfoRef peerinfo, CFErrorRef *error) {
+
+ SecKeyRef ourKey = SOSFullPeerInfoCopyDeviceKey(peerinfo, error);
+ require_quiet(ourKey, fail);
+
+ SOSCircleRemoveRetired(circle, error); // Prune off retirees since we're signing this one
+ CFArrayRemoveAllValues(circle->rejected_applicants); // Dump rejects so we clean them up sometime.
+ SOSCircleRejectNonValidApplicants(circle, SecKeyCreatePublicFromPrivate(user_approver));
+ SOSCircleGenerationIncrement(circle);
+ require_quiet(SOSCircleRemoveSignatures(circle, error), fail);
+ require_quiet(SOSCircleSign(circle, user_approver, error), fail);
+ require_quiet(SOSCircleSign(circle, ourKey, error), fail);
+
+ CFReleaseNull(ourKey);
+ return true;
+
+fail:
+ CFReleaseNull(ourKey);
+ return false;
+}
+
+
+bool SOSCircleConcordanceSign(SOSCircleRef circle, SOSFullPeerInfoRef peerinfo, CFErrorRef *error) {
+ bool success = false;
+ SecKeyRef ourKey = SOSFullPeerInfoCopyDeviceKey(peerinfo, error);
+ require_quiet(ourKey, exit);
+
+ success = SOSCircleSign(circle, ourKey, error);
+
+exit:
+ CFReleaseNull(ourKey);
+ return success;
+}
+
+static inline SOSConcordanceStatus CheckPeerStatus(SOSCircleRef circle, SOSPeerInfoRef peer, CFErrorRef *error) {
+ SOSConcordanceStatus result = kSOSConcordanceNoPeer;
+ SecKeyRef pubKey = SOSPeerInfoCopyPubKey(peer);
+
+ require_action_quiet(SOSCircleHasActivePeer(circle, peer, error), exit, result = kSOSConcordanceNoPeer);
+ require_action_quiet(SOSCircleVerifySignatureExists(circle, pubKey, error), exit, result = kSOSConcordanceNoPeerSig);
+ require_action_quiet(SOSCircleVerify(circle, pubKey, error), exit, result = kSOSConcordanceBadPeerSig);
+
+ result = kSOSConcordanceTrusted;
+
+exit:
+ CFReleaseNull(pubKey);
+ return result;
+}
+
+static inline SOSConcordanceStatus CombineStatus(SOSConcordanceStatus status1, SOSConcordanceStatus status2)
+{
+ if (status1 == kSOSConcordanceTrusted || status2 == kSOSConcordanceTrusted)
+ return kSOSConcordanceTrusted;
+
+ if (status1 == kSOSConcordanceBadPeerSig || status2 == kSOSConcordanceBadPeerSig)
+ return kSOSConcordanceBadPeerSig;
+
+ if (status1 == kSOSConcordanceNoPeerSig || status2 == kSOSConcordanceNoPeerSig)
+ return kSOSConcordanceNoPeerSig;
+
+ return status1;
+}
+
+static inline bool SOSCircleIsEmpty(SOSCircleRef circle) {
+ return SOSCircleCountPeers(circle) == 0;
+}
+
+static inline bool SOSCircleIsOffering(SOSCircleRef circle) {
+ return SOSCircleCountPeers(circle) == 1;
+}
+
+static inline bool SOSCircleIsResignOffering(SOSCircleRef circle, SecKeyRef pubkey) {
+ return SOSCircleCountActiveValidPeers(circle, pubkey) == 1;
+}
+
+static inline SOSConcordanceStatus GetSignersStatus(SOSCircleRef signers_circle, SOSCircleRef status_circle,
+ SecKeyRef user_pubKey, SOSPeerInfoRef exclude, CFErrorRef *error) {
+ CFStringRef excluded_id = exclude ? SOSPeerInfoGetPeerID(exclude) : NULL;
+
+ __block SOSConcordanceStatus status = kSOSConcordanceNoPeer;
+ SOSCircleForEachActiveValidPeer(signers_circle, user_pubKey, ^(SOSPeerInfoRef peer) {
+ SOSConcordanceStatus peerStatus = CheckPeerStatus(status_circle, peer, error);
+
+ if (peerStatus == kSOSConcordanceNoPeerSig &&
+ (CFEqualSafe(SOSPeerInfoGetPeerID(peer), excluded_id) || SOSPeerInfoIsCloudIdentity(peer)))
+ peerStatus = kSOSConcordanceNoPeer;
+
+ status = CombineStatus(status, peerStatus); // TODO: Use multiple error gathering.
+ });
+
+ return status;
+}
+
+static inline bool isOlderGeneration(SOSCircleRef current, SOSCircleRef proposed) {
+ return CFNumberCompare(current->generation, proposed->generation, NULL) == kCFCompareGreaterThan;
+}
+
+bool SOSCircleSharedTrustedPeers(SOSCircleRef current, SOSCircleRef proposed, SOSPeerInfoRef me) {
+ __block bool retval = false;
+ SOSCircleForEachPeer(current, ^(SOSPeerInfoRef peer) {
+ if(!CFEqual(me, peer) && SOSCircleHasPeer(proposed, peer, NULL)) retval = true;
+ });
+ return retval;
+}
+
+static void SOSCircleUpgradePeersByCircle(SOSCircleRef known_circle, SOSCircleRef proposed_circle) {
+ SOSCircleForEachPeer(known_circle, ^(SOSPeerInfoRef known_peer) {
+ SOSPeerInfoRef proposed_peer = SOSCircleCopyPeerInfo(proposed_circle, SOSPeerInfoGetPeerID(known_peer), NULL);
+ if(proposed_peer && CFEqualSafe(proposed_peer, known_peer) != 0) {
+ SOSCircleUpdatePeerInfo(known_circle, proposed_peer);
+ }
+ });
+}
+
+SOSConcordanceStatus SOSCircleConcordanceTrust(SOSCircleRef known_circle, SOSCircleRef proposed_circle,
+ SecKeyRef known_pubkey, SecKeyRef user_pubkey,
+ SOSPeerInfoRef exclude, CFErrorRef *error) {
+
+ if(user_pubkey == NULL) {
+ SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Concordance with no public key"), NULL, error);
+ return kSOSConcordanceNoUserKey; //TODO: - needs to return an error
+ }
+
+ if (SOSCircleIsEmpty(proposed_circle)) {
+ return kSOSConcordanceTrusted;
+ }
+
+ if(!SOSCircleVerifySignatureExists(proposed_circle, user_pubkey, error)) {
+ SOSCreateError(kSOSErrorBadSignature, CFSTR("No public signature"), (error != NULL) ? *error : NULL, error);
+ return kSOSConcordanceNoUserSig;
+ }
+
+ if(!SOSCircleVerify(proposed_circle, user_pubkey, error)) {
+ SOSCreateError(kSOSErrorBadSignature, CFSTR("Bad public signature"), (error != NULL) ? *error : NULL, error);
+ return kSOSConcordanceBadUserSig;
+ }
+
+ if (SOSCircleIsEmpty(known_circle) || SOSCircleIsOffering(proposed_circle)) {
+ return GetSignersStatus(proposed_circle, proposed_circle, user_pubkey, NULL, error);
+ }
+
+ if(isOlderGeneration(known_circle, proposed_circle)) {
+ SOSCreateError(kSOSErrorReplay, CFSTR("Bad generation"), NULL, error);
+ return kSOSConcordanceGenOld;
+ }
+
+
+ if(!SOSCircleVerify(known_circle, user_pubkey, error)) {
+ SOSCircleUpgradePeersByCircle(known_circle, proposed_circle);
+ }
+
+ if(known_pubkey == NULL) known_pubkey = user_pubkey;
+ if(!SOSCircleVerify(known_circle, known_pubkey, error)) known_pubkey = user_pubkey;
+ return GetSignersStatus(known_circle, proposed_circle, known_pubkey, exclude, error);
+}
+
+
+static const uint8_t* der_decode_mutable_dictionary(CFAllocatorRef allocator, CFOptionFlags mutability,
+ CFMutableDictionaryRef* dictionary, CFErrorRef *error,
+ const uint8_t* der, const uint8_t *der_end)
+{
+ CFDictionaryRef theDict;
+ const uint8_t* result = der_decode_dictionary(allocator, mutability, &theDict, error, der, der_end);
+
+ if (result != NULL)
+ *dictionary = (CFMutableDictionaryRef)theDict;
+
+ return result;
+}
+
+SOSCircleRef SOSCircleCreateFromDER(CFAllocatorRef allocator, CFErrorRef* error,
+ const uint8_t** der_p, const uint8_t *der_end) {
+ SOSCircleRef cir = CFTypeAllocate(SOSCircle, struct __OpaqueSOSCircle, allocator);
+
+ const uint8_t *sequence_end;
+
+ cir->name = NULL;
+ cir->generation = NULL;
+ cir->peers = NULL;
+ cir->applicants = NULL;
+ cir->rejected_applicants = NULL;
+ cir->signatures = NULL;
+
+ *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
+ require_action_quiet(sequence_end != NULL, fail,
+ SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Circle DER"), (error != NULL) ? *error : NULL, error));
+
+ // Version first.
+ uint64_t version = 0;
+ *der_p = ccder_decode_uint64(&version, *der_p, der_end);
+
+ require_action_quiet(version == kOnlyCompatibleVersion, fail,
+ SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("Bad Circle Version"), NULL, error));
+
+ *der_p = der_decode_string(allocator, 0, &cir->name, error, *der_p, sequence_end);
+ *der_p = der_decode_number(allocator, 0, &cir->generation, error, *der_p, sequence_end);
+
+ cir->peers = SOSPeerInfoArrayCreateFromDER(allocator, error, der_p, sequence_end);
+ cir->applicants = SOSPeerInfoArrayCreateFromDER(allocator, error, der_p, sequence_end);
+ cir->rejected_applicants = SOSPeerInfoArrayCreateFromDER(allocator, error, der_p, sequence_end);
+
+ *der_p = der_decode_mutable_dictionary(allocator, kCFPropertyListMutableContainersAndLeaves,
+ &cir->signatures, error, *der_p, sequence_end);
+
+ require_action_quiet(*der_p == sequence_end, fail,
+ SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Circle DER"), (error != NULL) ? *error : NULL, error));
+
+ return cir;
+
+fail:
+ CFReleaseNull(cir);
+ return NULL;
+}
+
+SOSCircleRef SOSCircleCreateFromData(CFAllocatorRef allocator, CFDataRef circleData, CFErrorRef *error)
+{
+ size_t size = CFDataGetLength(circleData);
+ const uint8_t *der = CFDataGetBytePtr(circleData);
+ SOSCircleRef inflated = SOSCircleCreateFromDER(allocator, error, &der, der + size);
+ return inflated;
+}
+
+size_t SOSCircleGetDEREncodedSize(SOSCircleRef cir, CFErrorRef *error) {
+ SOSCircleAssertStable(cir);
+ size_t total_payload = 0;
+
+ require_quiet(accumulate_size(&total_payload, ccder_sizeof_uint64(kOnlyCompatibleVersion)), fail);
+ require_quiet(accumulate_size(&total_payload, der_sizeof_string(cir->name, error)), fail);
+ require_quiet(accumulate_size(&total_payload, der_sizeof_number(cir->generation, error)), fail);
+ require_quiet(accumulate_size(&total_payload, SOSPeerInfoArrayGetDEREncodedSize(cir->peers, error)), fail);
+ require_quiet(accumulate_size(&total_payload, SOSPeerInfoArrayGetDEREncodedSize(cir->applicants, error)), fail);
+ require_quiet(accumulate_size(&total_payload, SOSPeerInfoArrayGetDEREncodedSize(cir->rejected_applicants, error)), fail);
+ require_quiet(accumulate_size(&total_payload, der_sizeof_dictionary((CFDictionaryRef) cir->signatures, error)), fail);
+
+ return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, total_payload);
+
+fail:
+ SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
+ return 0;
+}
+
+uint8_t* SOSCircleEncodeToDER(SOSCircleRef cir, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
+ SOSCircleAssertStable(cir);
+
+ return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
+ ccder_encode_uint64(kOnlyCompatibleVersion, der,
+ der_encode_string(cir->name, error, der,
+ der_encode_number(cir->generation, error, der,
+ SOSPeerInfoArrayEncodeToDER(cir->peers, error, der,
+ SOSPeerInfoArrayEncodeToDER(cir->applicants, error, der,
+ SOSPeerInfoArrayEncodeToDER(cir->rejected_applicants, error, der,
+ der_encode_dictionary((CFDictionaryRef) cir->signatures, error, der, der_end))))))));
+}
+
+CFDataRef SOSCircleCreateIncompatibleCircleDER(CFErrorRef* error)
+{
+ size_t total_payload = 0;
+ size_t encoded_size = 0;
+ uint8_t* der = 0;
+ uint8_t* der_end = 0;
+ CFMutableDataRef result = NULL;
+
+ require_quiet(accumulate_size(&total_payload, ccder_sizeof_uint64(kAlwaysIncompatibleVersion)), fail);
+
+ encoded_size = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, total_payload);
+
+ result = CFDataCreateMutableWithScratch(kCFAllocatorDefault, encoded_size);
+
+ der = CFDataGetMutableBytePtr(result);
+ der_end = der + CFDataGetLength(result);
+
+ der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
+ ccder_encode_uint64(kAlwaysIncompatibleVersion, der, der_end));
+
+fail:
+ if (der == NULL || der != der_end)
+ CFReleaseNull(result);
+
+ return result;
+}
+
+
+CFDataRef SOSCircleCopyEncodedData(SOSCircleRef circle, CFAllocatorRef allocator, CFErrorRef *error)
+{
+ size_t size = SOSCircleGetDEREncodedSize(circle, error);
+ if (size == 0)
+ return NULL;
+ uint8_t buffer[size];
+ uint8_t* start = SOSCircleEncodeToDER(circle, error, buffer, buffer + sizeof(buffer));
+ CFDataRef result = CFDataCreate(kCFAllocatorDefault, start, size);
+ return result;
+}
+
+static void SOSCircleDestroy(CFTypeRef aObj) {
+ SOSCircleRef c = (SOSCircleRef) aObj;
+
+ CFReleaseNull(c->name);
+ CFReleaseNull(c->generation);
+ CFReleaseNull(c->peers);
+ CFReleaseNull(c->applicants);
+ CFReleaseNull(c->rejected_applicants);
+ CFReleaseNull(c->signatures);
+}
+
+static CFStringRef SOSCircleCopyDescription(CFTypeRef aObj) {
+ SOSCircleRef c = (SOSCircleRef) aObj;
+
+ SOSCircleAssertStable(c);
+
+ return CFStringCreateWithFormat(NULL, NULL,
+ CFSTR("<SOSCircle@%p: [ \nName: %@, \nPeers: %@,\nApplicants: %@,\nRejects: %@,\nSignatures: %@\n ] >"),
+ c, c->name, c->peers, c->applicants, c->rejected_applicants, c->signatures);
+}
+
+CFStringRef SOSCircleGetName(SOSCircleRef circle) {
+ assert(circle);
+ assert(circle->name);
+ return circle->name;
+}
+
+const char *SOSCircleGetNameC(SOSCircleRef circle) {
+ CFStringRef name = SOSCircleGetName(circle);
+ if (!name)
+ return strdup("");
+ return CFStringToCString(name);
+}
+
+CFNumberRef SOSCircleGetGeneration(SOSCircleRef circle) {
+ assert(circle);
+ assert(circle->generation);
+ return circle->generation;
+}
+
+int64_t SOSCircleGetGenerationSint(SOSCircleRef circle) {
+ CFNumberRef gen = SOSCircleGetGeneration(circle);
+ int64_t value;
+ if(!gen) return 0;
+ CFNumberGetValue(gen, kCFNumberSInt64Type, &value);
+ return value;
+}
+
+void SOSCircleGenerationIncrement(SOSCircleRef circle) {
+ CFAllocatorRef allocator = CFGetAllocator(circle->generation);
+ int64_t value = SOSCircleGetGenerationSint(circle);
+ value++;
+ circle->generation = CFNumberCreate(allocator, kCFNumberSInt64Type, &value);
+}
+
+int SOSCircleCountPeers(SOSCircleRef circle) {
+ SOSCircleAssertStable(circle);
+ __block int count = 0;
+ SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
+ ++count;
+ });
+ return count;
+}
+
+int SOSCircleCountActivePeers(SOSCircleRef circle) {
+ SOSCircleAssertStable(circle);
+ __block int count = 0;
+ SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
+ ++count;
+ });
+ return count;
+}
+
+int SOSCircleCountActiveValidPeers(SOSCircleRef circle, SecKeyRef pubkey) {
+ SOSCircleAssertStable(circle);
+ __block int count = 0;
+ SOSCircleForEachActiveValidPeer(circle, pubkey, ^(SOSPeerInfoRef peer) {
+ ++count;
+ });
+ return count;
+}
+
+int SOSCircleCountRetiredPeers(SOSCircleRef circle) {
+ SOSCircleAssertStable(circle);
+ __block int count = 0;
+ SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
+ ++count;
+ });
+ return count;
+}
+
+int SOSCircleCountApplicants(SOSCircleRef circle) {
+ SOSCircleAssertStable(circle);
+
+ return (int)CFArrayGetCount(circle->applicants);
+}
+
+bool SOSCircleHasApplicant(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
+ SOSCircleAssertStable(circle);
+
+ return CFArrayHasValueMatching(circle->applicants, ^bool(const void *value) {
+ return SOSPeerInfoCompareByID(value, peerInfo, NULL) == 0;
+ });
+}
+
+CFMutableArrayRef SOSCircleCopyApplicants(SOSCircleRef circle, CFAllocatorRef allocator) {
+ SOSCircleAssertStable(circle);
+
+ return CFArrayCreateMutableCopy(allocator, 0, circle->applicants);
+}
+
+int SOSCircleCountRejectedApplicants(SOSCircleRef circle) {
+ SOSCircleAssertStable(circle);
+
+ return (int)CFArrayGetCount(circle->rejected_applicants);
+}
+
+bool SOSCircleHasRejectedApplicant(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
+ SOSCircleAssertStable(circle);
+
+ return CFArrayHasValueMatching(circle->rejected_applicants, ^bool(const void *value) {
+ return SOSPeerInfoCompareByID(value, peerInfo, NULL) == 0;
+ });
+}
+
+SOSPeerInfoRef SOSCircleCopyRejectedApplicant(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
+ SOSCircleAssertStable(circle);
+ return (SOSPeerInfoRef) CFArrayGetValueMatching(circle->rejected_applicants, ^bool(const void *value) {
+ return SOSPeerInfoCompareByID(value, peerInfo, NULL) == 0;
+ });
+}
+
+CFMutableArrayRef SOSCircleCopyRejectedApplicants(SOSCircleRef circle, CFAllocatorRef allocator) {
+ SOSCircleAssertStable(circle);
+
+ return CFArrayCreateMutableCopy(allocator, 0, circle->rejected_applicants);
+}
+
+
+bool SOSCircleHasPeerWithID(SOSCircleRef circle, CFStringRef peerid, CFErrorRef *error) {
+ SOSCircleAssertStable(circle);
+ __block bool found = false;
+ SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
+ if(peerid && peer && CFEqualSafe(peerid, SOSPeerInfoGetPeerID(peer))) found = true;
+ });
+ return found;
+}
+
+bool SOSCircleHasPeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
+ return SOSCircleHasPeerWithID(circle, SOSPeerInfoGetPeerID(peerInfo), error);
+}
+
+bool SOSCircleHasActivePeerWithID(SOSCircleRef circle, CFStringRef peerid, CFErrorRef *error) {
+ SOSCircleAssertStable(circle);
+ __block bool found = false;
+ SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
+ if(peerid && peer && CFEqualSafe(peerid, SOSPeerInfoGetPeerID(peer))) found = true;
+ });
+ return found;
+}
+
+bool SOSCircleHasActivePeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
+ if(!peerInfo) return false;
+ return SOSCircleHasActivePeerWithID(circle, SOSPeerInfoGetPeerID(peerInfo), error);
+}
+
+
+
+bool SOSCircleResetToEmpty(SOSCircleRef circle, CFErrorRef *error) {
+ CFArrayRemoveAllValues(circle->applicants);
+ CFArrayRemoveAllValues(circle->peers);
+ CFDictionaryRemoveAllValues(circle->signatures);
+
+ return true;
+}
+
+bool SOSCircleResetToOffering(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error){
+
+ return SOSCircleResetToEmpty(circle, error)
+ && SOSCircleRequestAdmission(circle, user_privkey, requestor, error)
+ && SOSCircleAcceptRequest(circle, user_privkey, requestor, SOSFullPeerInfoGetPeerInfo(requestor), error);
+}
+
+CFIndex SOSCircleRemoveRetired(SOSCircleRef circle, CFErrorRef *error) {
+ CFIndex n = CFArrayRemoveAllPassing(circle->peers, ^ bool (const void *element) {
+ SOSPeerInfoRef peer = (SOSPeerInfoRef) element;
+
+ return SOSPeerInfoIsRetirementTicket(peer);
+ });
+
+ return n;
+}
+
+static bool SOSCircleRecordAdmission(SOSCircleRef circle, SecKeyRef user_pubkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
+ SOSCircleAssertStable(circle);
+
+ bool isPeer = SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(requestor), error);
+
+ require_action_quiet(!isPeer, fail, SOSCreateError(kSOSErrorAlreadyPeer, CFSTR("Cannot request admission when already a peer"), NULL, error));
+
+ CFIndex total = CFArrayRemoveAllWithMatchingID(circle->applicants, SOSFullPeerInfoGetPeerInfo(requestor));
+
+ (void) total; // Suppress unused warning in release code.
+ assert(total <= 1); // We should at most be in the list once.
+
+ total = CFArrayRemoveAllWithMatchingID(circle->rejected_applicants, SOSFullPeerInfoGetPeerInfo(requestor));
+
+ (void) total; // Suppress unused warning in release code.
+ assert(total <= 1); // We should at most be in the list once.
+
+
+ // Refetch the current PeerInfo as the promtion above can change it.
+ CFArrayAppendValue(circle->applicants, SOSFullPeerInfoGetPeerInfo(requestor));
+
+ return true;
+
+fail:
+ return false;
+
+}
+
+bool SOSCircleRequestReadmission(SOSCircleRef circle, SecKeyRef user_pubkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
+ bool success = false;
+
+ SOSPeerInfoRef peer = SOSFullPeerInfoGetPeerInfo(requestor);
+ require_quiet(SOSPeerInfoApplicationVerify(peer, user_pubkey, error), fail);
+ success = SOSCircleRecordAdmission(circle, user_pubkey, requestor, error);
+fail:
+ return success;
+}
+
+bool SOSCircleRequestAdmission(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
+ bool success = false;
+
+ SecKeyRef user_pubkey = SecKeyCreatePublicFromPrivate(user_privkey);
+ require_action_quiet(user_pubkey, fail, SOSCreateError(kSOSErrorBadKey, CFSTR("No public key for key"), NULL, error));
+
+ require(SOSFullPeerInfoPromoteToApplication(requestor, user_privkey, error), fail);
+
+ success = SOSCircleRecordAdmission(circle, user_pubkey, requestor, error);
+fail:
+ CFReleaseNull(user_pubkey);
+ return success;
+}
+
+
+bool SOSCircleUpdatePeerInfo(SOSCircleRef circle, SOSPeerInfoRef replacement_peer_info) {
+ __block bool replaced = false;
+ CFStringRef replacement_peer_id = SOSPeerInfoGetPeerID(replacement_peer_info);
+
+ CFMutableArrayModifyValues(circle->peers, ^const void *(const void *value) {
+ if (CFEqual(replacement_peer_id, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value))
+ && !CFEqual(replacement_peer_info, value)) {
+ replaced = true;
+ return replacement_peer_info;
+ }
+
+ return value;
+ });
+ return replaced;
+}
+
+bool SOSCircleRemovePeer(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, SOSPeerInfoRef peer_to_remove, CFErrorRef *error) {
+ SOSPeerInfoRef requestor_peer_info = SOSFullPeerInfoGetPeerInfo(requestor);
+
+ if (SOSCircleHasApplicant(circle, peer_to_remove, error)) {
+ return SOSCircleRejectRequest(circle, requestor, peer_to_remove, error);
+ }
+
+ if (!SOSCircleHasPeer(circle, requestor_peer_info, error)) {
+ SOSCreateError(kSOSErrorAlreadyPeer, CFSTR("Must be peer to remove peer"), NULL, error);
+ return false;
+ }
+
+ CFArrayRemoveAllWithMatchingID(circle->peers, peer_to_remove);
+
+ SOSCircleGenerationSign(circle, user_privkey, requestor, error);
+
+ return true;
+}
+
+bool SOSCircleAcceptRequest(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef device_approver, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
+ SOSCircleAssertStable(circle);
+
+ CFIndex total = CFArrayRemoveAllWithMatchingID(circle->applicants, peerInfo);
+ SecKeyRef publicKey = NULL;
+ bool result = false;
+
+ require_action_quiet(total != 0, fail,
+ SOSCreateError(kSOSErrorNotApplicant, CFSTR("Cannot accept non-applicant"), NULL, error));
+
+ publicKey = SecKeyCreatePublicFromPrivate(user_privkey);
+ require_quiet(SOSPeerInfoApplicationVerify(peerInfo, publicKey, error), fail);
+
+ assert(total == 1);
+
+ CFArrayAppendValue(circle->peers, peerInfo);
+ result = SOSCircleGenerationSign(circle, user_privkey, device_approver, error);
+ secnotice("circle", "Accepted %@", peerInfo);
+
+fail:
+ CFReleaseNull(publicKey);
+ return result;
+}
+
+bool SOSCircleWithdrawRequest(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
+ SOSCircleAssertStable(circle);
+
+#ifndef NDEBUG
+ CFIndex total =
+#endif
+ CFArrayRemoveAllWithMatchingID(circle->applicants, peerInfo);
+
+ assert(total <= 1);
+
+ return true;
+}
+
+bool SOSCircleRemoveRejectedPeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
+ SOSCircleAssertStable(circle);
+
+#ifndef NDEBUG
+ CFIndex total =
+#endif
+ CFArrayRemoveAllWithMatchingID(circle->rejected_applicants, peerInfo);
+
+ assert(total <= 1);
+
+ return true;
+}
+
+
+bool SOSCircleRejectRequest(SOSCircleRef circle, SOSFullPeerInfoRef device_rejector,
+ SOSPeerInfoRef peerInfo, CFErrorRef *error) {
+ SOSCircleAssertStable(circle);
+
+ if (CFEqual(SOSPeerInfoGetPeerID(peerInfo), SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(device_rejector))))
+ return SOSCircleWithdrawRequest(circle, peerInfo, error);
+
+ CFIndex total = CFArrayRemoveAllWithMatchingID(circle->applicants, peerInfo);
+
+ if (total == 0) {
+ SOSCreateError(kSOSErrorNotApplicant, CFSTR("Cannot reject non-applicant"), NULL, error);
+ return false;
+ }
+ assert(total == 1);
+
+ CFArrayAppendValue(circle->rejected_applicants, peerInfo);
+
+ // TODO: Maybe we sign the rejection with device_rejector.
+
+ return true;
+}
+
+bool SOSCircleAcceptRequests(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef device_approver,
+ CFErrorRef *error) {
+ // Returns true if we accepted someone and therefore have to post the circle back to KVS
+ __block bool result = false;
+
+ SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) {
+ if (!SOSCircleAcceptRequest(circle, user_privkey, device_approver, peer, error))
+ printf("error in SOSCircleAcceptRequest\n");
+ else {
+ secnotice("circle", "Accepted peer: %@", peer);
+ result = true;
+ }
+ });
+
+ if (result) {
+ SOSCircleGenerationSign(circle, user_privkey, device_approver, error);
+ secnotice("circle", "Countersigned accepted requests");
+ }
+
+ return result;
+}
+
+bool SOSCirclePeerSigUpdate(SOSCircleRef circle, SecKeyRef userPrivKey, SOSFullPeerInfoRef fpi,
+ CFErrorRef *error) {
+ // Returns true if we accepted someone and therefore have to post the circle back to KVS
+ __block bool result = false;
+ SecKeyRef userPubKey = SecKeyCreatePublicFromPrivate(userPrivKey);
+
+ // We're going to remove any applicants using a mismatched user key.
+ SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) {
+ if(!SOSPeerInfoApplicationVerify(peer, userPubKey, NULL)) {
+ if(!SOSCircleRejectRequest(circle, fpi, peer, NULL)) {
+ // do we care?
+ }
+ }
+ });
+
+ result = SOSCircleUpdatePeerInfo(circle, SOSFullPeerInfoGetPeerInfo(fpi));
+
+ if (result) {
+ SOSCircleGenerationSign(circle, userPrivKey, fpi, error);
+ secnotice("circle", "Generation signed updated signatures on peerinfo");
+ }
+
+ return result;
+}
+
+SOSPeerInfoRef SOSCircleCopyPeerInfo(SOSCircleRef circle, CFStringRef peer_id, CFErrorRef *error) {
+ __block SOSPeerInfoRef result = NULL;
+
+ CFArrayForEach(circle->peers, ^(const void *value) {
+ if (result == NULL) {
+ SOSPeerInfoRef tpi = (SOSPeerInfoRef)value;
+ if (CFEqual(SOSPeerInfoGetPeerID(tpi), peer_id))
+ result = tpi;
+ }
+ });
+
+ CFRetainSafe(result);
+ return result;
+}
+
+
+static inline void SOSCircleForEachPeerMatching(SOSCircleRef circle,
+ void (^action)(SOSPeerInfoRef peer),
+ bool (^condition)(SOSPeerInfoRef peer)) {
+ CFArrayForEach(circle->peers, ^(const void *value) {
+ SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
+ if (condition(peer))
+ action(peer);
+ });
+}
+
+void SOSCircleForEachPeer(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
+ SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
+ return !SOSPeerInfoIsRetirementTicket(peer) && !SOSPeerInfoIsCloudIdentity(peer);
+ });
+}
+
+void SOSCircleForEachRetiredPeer(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
+ SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
+ return SOSPeerInfoIsRetirementTicket(peer);
+ });
+}
+
+void SOSCircleForEachActivePeer(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
+ SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
+ return true;
+ });
+}
+
+void SOSCircleForEachActiveValidPeer(SOSCircleRef circle, SecKeyRef user_public_key, void (^action)(SOSPeerInfoRef peer)) {
+ SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
+ return SOSPeerInfoApplicationVerify(peer, user_public_key, NULL);
+ });
+}
+
+void SOSCircleForEachApplicant(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
+ CFArrayForEach(circle->applicants, ^(const void*value) { action((SOSPeerInfoRef) value); } );
+}
+
+
+CFMutableArrayRef SOSCircleCopyPeers(SOSCircleRef circle, CFAllocatorRef allocator) {
+ SOSCircleAssertStable(circle);
+
+ CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(allocator);
+
+ SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
+ CFArrayAppendValue(result, peer);
+ });
+
+ return result;
+}
+
+CFMutableArrayRef SOSCircleCopyConcurringPeers(SOSCircleRef circle, CFErrorRef* error) {
+ SOSCircleAssertStable(circle);
+
+ CFMutableArrayRef concurringPeers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
+
+ SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
+ CFErrorRef error = NULL;
+ if (SOSCircleVerifyPeerSigned(circle, peer, &error)) {
+ CFArrayAppendValue(concurringPeers, peer);
+ } else if (error != NULL) {
+ secerror("Error checking concurrence: %@", error);
+ }
+ CFReleaseNull(error);
+ });
+
+ return concurringPeers;
+}
+
+
+//
+// Stuff above this line is really SOSCircleInfo below the line is the active SOSCircle functionality
+//
+
+static SOSPeerRef SOSCircleCopyPeer(SOSCircleRef circle, SOSFullPeerInfoRef myRef, SOSPeerSendBlock sendBlock,
+ CFStringRef peer_id, CFErrorRef *error) {
+ SOSPeerRef peer = NULL;
+ SOSPeerInfoRef peer_info = SOSCircleCopyPeerInfo(circle, peer_id, error);
+ //TODO: if (peer is legit member of us then good otherwise bail) {
+ //}
+ if (peer_info) {
+ peer = SOSPeerCreate(myRef, peer_info, error, sendBlock);
+ CFReleaseNull(peer_info);
+ }
+ return peer;
+}
+
+
+static bool SOSCircleDoWithPeer(SOSFullPeerInfoRef myRef, SOSCircleRef circle, SOSDataSourceFactoryRef factory,
+ SOSPeerSendBlock sendBlock, CFStringRef peer_id, bool readOnly,
+ CFErrorRef* error, bool (^do_action)(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error))
+{
+ bool success = false;
+ SOSEngineRef engine = NULL;
+ SOSPeerRef peer = NULL;
+ SOSDataSourceRef ds = NULL;
+
+ peer = SOSCircleCopyPeer(circle, myRef, sendBlock, peer_id, error);
+ require(peer, exit);
+
+ ds = factory->create_datasource(factory, SOSCircleGetName(circle), readOnly, error);
+ require(ds, exit);
+
+ engine = SOSEngineCreate(ds, error); // Hand off DS to engine.
+ ds = NULL;
+ require(engine, exit);
+
+ success = do_action(engine, peer, error);
+
+exit:
+ if (ds)
+ ds->release(ds);
+ if (engine)
+ SOSEngineDispose(engine);
+ if (peer)
+ SOSPeerDispose(peer);
+
+ return success;
+}
+
+bool SOSCircleSyncWithPeer(SOSFullPeerInfoRef myRef, SOSCircleRef circle, SOSDataSourceFactoryRef factory,
+ SOSPeerSendBlock sendBlock, CFStringRef peer_id,
+ CFErrorRef *error)
+{
+ return SOSCircleDoWithPeer(myRef, circle, factory, sendBlock, peer_id, true, error, ^bool(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
+ return SOSPeerStartSync(peer, engine, error) != kSOSPeerCoderFailure;
+ });
+}
+
+bool SOSCircleHandlePeerMessage(SOSCircleRef circle, SOSFullPeerInfoRef myRef, SOSDataSourceFactoryRef factory,
+ SOSPeerSendBlock sendBlock, CFStringRef peer_id,
+ CFDataRef message, CFErrorRef *error) {
+ return SOSCircleDoWithPeer(myRef, circle, factory, sendBlock, peer_id, false, error, ^bool(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
+ return SOSPeerHandleMessage(peer, engine, message, error) != kSOSPeerCoderFailure;
+ });
+}
+
+
+SOSFullPeerInfoRef SOSCircleGetiCloudFullPeerInfoRef(SOSCircleRef circle) {
+ __block SOSFullPeerInfoRef cloud_full_peer = NULL;
+ SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
+ if (SOSPeerInfoIsCloudIdentity(peer)) {
+ if (cloud_full_peer == NULL) {
+ CFErrorRef localError = NULL;
+ cloud_full_peer = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, &localError);
+
+ if (localError) {
+ secerror("Found cloud peer in circle but can't make full peer: %@", localError);
+ CFReleaseNull(localError);
+ }
+
+ } else {
+ secerror("More than one cloud identity found in circle: %@", circle);
+ }
+ }
+ });
+ return cloud_full_peer;
+}
+
+
+