1/*
2 * Created by Michael Brouwer on 6/22/12.
3 * Copyright 2012 Apple Inc. All Rights Reserved.
4 */
5
6/*
7 * SOSCircle.c -  Implementation of the secure object syncing transport
8 */
9
10#include <AssertMacros.h>
11
12#include <CoreFoundation/CFArray.h>
13#include <SecureObjectSync/SOSCircle.h>
14#include <SecureObjectSync/SOSCloudCircleInternal.h>
15#include <SecureObjectSync/SOSInternal.h>
16#include <SecureObjectSync/SOSEngine.h>
17#include <SecureObjectSync/SOSPeer.h>
18#include <CoreFoundation/CoreFoundation.h>
19#include <Security/SecFramework.h>
20
21#include <Security/SecKey.h>
22#include <Security/SecKeyPriv.h>
23
24#include <utilities/SecCFWrappers.h>
25//#include "ckdUtilities.h"
26
27#include <utilities/der_plist.h>
28#include <utilities/der_plist_internal.h>
29
30#include <corecrypto/ccder.h>
31#include <corecrypto/ccdigest.h>
32#include <corecrypto/ccsha2.h>
33
34#include <stdlib.h>
35#include <assert.h>
36
37enum {
38    kOnlyCompatibleVersion = 1, // Sometime in the future this name will be improved to reflect history.
39
40    kAlwaysIncompatibleVersion = UINT64_MAX,
41};
42
43struct __OpaqueSOSCircle {
44    CFRuntimeBase _base;
45
46    CFStringRef name;
47    CFNumberRef generation;
48    CFMutableArrayRef peers;
49    CFMutableArrayRef applicants;
50    CFMutableArrayRef rejected_applicants;
51
52    CFMutableDictionaryRef signatures;
53};
54
55CFGiblisWithCompareFor(SOSCircle);
56
57// Move the next 2 lines to SOSPeer if we need it.
58static void SOSPeerRelease(CFAllocatorRef allocator, const void *value) {
59    SOSPeerDispose((SOSPeerRef)value);
60}
61
62static const CFDictionaryValueCallBacks dispose_peer_callbacks = { .release = SOSPeerRelease };
63
64SOSCircleRef SOSCircleCreate(CFAllocatorRef allocator, CFStringRef name, CFErrorRef *error) {
65    SOSCircleRef c = CFTypeAllocate(SOSCircle, struct __OpaqueSOSCircle, allocator);
66    int64_t gen = 1;
67
68    assert(name);
69
70    c->name = CFStringCreateCopy(allocator, name);
71    c->generation = CFNumberCreate(allocator, kCFNumberSInt64Type, &gen);
72    c->peers = CFArrayCreateMutableForCFTypes(allocator);
73    c->applicants = CFArrayCreateMutableForCFTypes(allocator);
74    c->rejected_applicants = CFArrayCreateMutableForCFTypes(allocator);
75    c->signatures = CFDictionaryCreateMutableForCFTypes(allocator);
76
77    return c;
78}
79
80static CFNumberRef SOSCircleGenerationCopy(CFNumberRef generation) {
81    int64_t value;
82    CFAllocatorRef allocator = CFGetAllocator(generation);
83    CFNumberGetValue(generation, kCFNumberSInt64Type, &value);
84    return CFNumberCreate(allocator, kCFNumberSInt64Type, &value);
85}
86
87SOSCircleRef SOSCircleCopyCircle(CFAllocatorRef allocator, SOSCircleRef otherCircle, CFErrorRef *error)
88{
89    SOSCircleRef c = CFTypeAllocate(SOSCircle, struct __OpaqueSOSCircle, allocator);
90
91    assert(otherCircle);
92    c->name = CFStringCreateCopy(allocator, otherCircle->name);
93    c->generation = SOSCircleGenerationCopy(otherCircle->generation);
94    c->peers = CFArrayCreateMutableCopy(allocator, 0, otherCircle->peers);
95    c->applicants = CFArrayCreateMutableCopy(allocator, 0, otherCircle->applicants);
96    c->rejected_applicants = CFArrayCreateMutableCopy(allocator, 0, otherCircle->rejected_applicants);
97    c->signatures = CFDictionaryCreateMutableCopy(allocator, 0, otherCircle->signatures);
98
99    return c;
100}
101
102static inline
103void SOSCircleAssertStable(SOSCircleRef circle)
104{
105    assert(circle);
106    assert(circle->name);
107    assert(circle->generation);
108    assert(circle->peers);
109    assert(circle->applicants);
110    assert(circle->rejected_applicants);
111    assert(circle->signatures);
112}
113
114static inline
115SOSCircleRef SOSCircleConvertAndAssertStable(CFTypeRef circleAsType)
116{
117    if (CFGetTypeID(circleAsType) != SOSCircleGetTypeID())
118        return NULL;
119
120    SOSCircleRef circle = (SOSCircleRef) circleAsType;
121
122    SOSCircleAssertStable(circle);
123
124    return circle;
125}
126
127
128static Boolean SOSCircleCompare(CFTypeRef lhs, CFTypeRef rhs) {
129    if (CFGetTypeID(lhs) != SOSCircleGetTypeID()
130     || CFGetTypeID(rhs) != SOSCircleGetTypeID())
131        return false;
132
133    SOSCircleRef left = SOSCircleConvertAndAssertStable(lhs);
134    SOSCircleRef right = SOSCircleConvertAndAssertStable(rhs);
135
136    // TODO: we should be doing set equality for peers and applicants.
137    return NULL != left && NULL != right
138        && CFEqual(left->generation, right->generation)
139        && CFEqual(left->peers, right->peers)
140        && CFEqual(left->applicants, right->applicants)
141        && CFEqual(left->rejected_applicants, right->rejected_applicants)
142        && CFEqual(left->signatures, right->signatures);
143}
144
145
146static bool SOSCircleDigestArray(const struct ccdigest_info *di, CFMutableArrayRef array, void *hash_result, CFErrorRef *error)
147{
148    __block bool success = true;
149    ccdigest_di_decl(di, array_digest);
150    const void * a_digest = array_digest;
151
152    ccdigest_init(di, array_digest);
153    CFArraySortValues(array, CFRangeMake(0, CFArrayGetCount(array)), SOSPeerInfoCompareByID, SOSPeerCmpPubKeyHash);
154    CFArrayForEach(array, ^(const void *peer) {
155        if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes((SOSPeerInfoRef)peer, di, a_digest, error))
156            success = false;
157    });
158    ccdigest_final(di, array_digest, hash_result);
159
160    return success;
161}
162
163static bool SOSCircleHash(const struct ccdigest_info *di, SOSCircleRef circle, void *hash_result, CFErrorRef *error) {
164    ccdigest_di_decl(di, circle_digest);
165    ccdigest_init(di, circle_digest);
166    int64_t gen = SOSCircleGetGenerationSint(circle);
167    ccdigest_update(di, circle_digest, sizeof(gen), &gen);
168
169    SOSCircleDigestArray(di, circle->peers, hash_result, error);
170    ccdigest_update(di, circle_digest, di->output_size, hash_result);
171    ccdigest_final(di, circle_digest, hash_result);
172    return true;
173}
174
175static bool SOSCircleSetSignature(SOSCircleRef circle, SecKeyRef pubkey, CFDataRef signature, CFErrorRef *error) {
176    bool result = false;
177
178    CFStringRef pubKeyID = SOSCopyIDOfKey(pubkey, error);
179    require_quiet(pubKeyID, fail);
180    CFDictionarySetValue(circle->signatures, pubKeyID, signature);
181    result = true;
182
183fail:
184    CFReleaseSafe(pubKeyID);
185    return result;
186}
187
188static bool SOSCircleRemoveSignatures(SOSCircleRef circle, CFErrorRef *error) {
189    CFDictionaryRemoveAllValues(circle->signatures);
190    return true;
191}
192
193static CFDataRef SOSCircleGetSignature(SOSCircleRef circle, SecKeyRef pubkey, CFErrorRef *error) {
194    CFStringRef pubKeyID = SOSCopyIDOfKey(pubkey, error);
195    CFDataRef result = NULL;
196    require_quiet(pubKeyID, fail);
197
198    CFTypeRef value = (CFDataRef)CFDictionaryGetValue(circle->signatures, pubKeyID);
199
200    if (isData(value)) result = (CFDataRef) value;
201
202fail:
203    CFReleaseSafe(pubKeyID);
204    return result;
205}
206
207bool SOSCircleSign(SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
208    if (!privKey) return false; // Really assertion but not always true for now.
209    CFAllocatorRef allocator = CFGetAllocator(circle);
210    uint8_t tmp[4096];
211    size_t tmplen = 4096;
212    const struct ccdigest_info *di = ccsha256_di();
213    uint8_t hash_result[di->output_size];
214
215    SOSCircleHash(di, circle, hash_result, error);
216    OSStatus stat =  SecKeyRawSign(privKey, kSecPaddingNone, hash_result, di->output_size, tmp, &tmplen);
217    if(stat) {
218        // TODO - Create a CFErrorRef;
219        secerror("Bad Circle SecKeyRawSign, stat: %ld", (long)stat);
220        SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Circle SecKeyRawSign"), (error != NULL) ? *error : NULL, error);
221        return false;
222    };
223    CFDataRef signature = CFDataCreate(allocator, tmp, tmplen);
224    SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(privKey);
225    SOSCircleSetSignature(circle, publicKey, signature, error);
226    CFReleaseNull(publicKey);
227    CFRelease(signature);
228    return true;
229}
230
231bool SOSCircleVerifySignatureExists(SOSCircleRef circle, SecKeyRef pubKey, CFErrorRef *error) {
232    if(!pubKey) {
233        // TODO ErrorRef
234        secerror("SOSCircleVerifySignatureExists no pubKey");
235        SOSCreateError(kSOSErrorBadFormat, CFSTR("SOSCircleVerifySignatureExists no pubKey"), (error != NULL) ? *error : NULL, error);
236        return false;
237    }
238    CFDataRef signature = SOSCircleGetSignature(circle, pubKey, error);
239    return NULL != signature;
240}
241
242bool SOSCircleVerify(SOSCircleRef circle, SecKeyRef pubKey, CFErrorRef *error) {
243    const struct ccdigest_info *di = ccsha256_di();
244    uint8_t hash_result[di->output_size];
245
246    SOSCircleHash(di, circle, hash_result, error);
247
248    CFDataRef signature = SOSCircleGetSignature(circle, pubKey, error);
249    if(!signature) return false;
250
251    return SecKeyRawVerify(pubKey, kSecPaddingNone, hash_result, di->output_size,
252                           CFDataGetBytePtr(signature), CFDataGetLength(signature)) == errSecSuccess;
253}
254
255bool SOSCircleVerifyPeerSigned(SOSCircleRef circle, SOSPeerInfoRef peer, CFErrorRef *error) {
256    SecKeyRef pub_key = SOSPeerInfoCopyPubKey(peer);
257    bool result = SOSCircleVerify(circle, pub_key, error);
258    CFReleaseSafe(pub_key);
259    return result;
260}
261
262
263static CFIndex CFArrayRemoveAllPassing(CFMutableArrayRef array, bool (^test)(const void *) ){
264    CFIndex numberRemoved = 0;
265
266    CFIndex position =  0;
267    while (position < CFArrayGetCount(array) && !test(CFArrayGetValueAtIndex(array, position)))
268        ++position;
269
270    while (position < CFArrayGetCount(array)) {
271        CFArrayRemoveValueAtIndex(array, position);
272        ++numberRemoved;
273        while (position < CFArrayGetCount(array) && !test(CFArrayGetValueAtIndex(array, position)))
274            ++position;
275    }
276
277    return numberRemoved;
278}
279
280static CFIndex CFArrayRemoveAllWithMatchingID(CFMutableArrayRef array, SOSPeerInfoRef peerInfo) {
281    CFStringRef peer_id = SOSPeerInfoGetPeerID(peerInfo);
282    if (!peer_id) return 0;
283
284    return CFArrayRemoveAllPassing(array,  ^ bool (const void *element) {
285        SOSPeerInfoRef peer = (SOSPeerInfoRef) element;
286
287        return CFEqual(peer_id, SOSPeerInfoGetPeerID(peer));
288    });
289}
290
291static void SOSCircleRejectNonValidApplicants(SOSCircleRef circle, SecKeyRef pubkey) {
292    CFArrayRef applicants = SOSCircleCopyApplicants(circle, NULL);
293    CFArrayForEach(applicants, ^(const void *value) {
294        SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
295        if(!SOSPeerInfoApplicationVerify(pi, pubkey, NULL)) {
296            CFArrayRemoveAllWithMatchingID(circle->applicants, pi);
297            CFArrayAppendValue(circle->rejected_applicants, pi);
298        }
299    });
300}
301
302bool SOSCircleGenerationSign(SOSCircleRef circle, SecKeyRef user_approver, SOSFullPeerInfoRef peerinfo, CFErrorRef *error) {
303
304    SecKeyRef ourKey = SOSFullPeerInfoCopyDeviceKey(peerinfo, error);
305    require_quiet(ourKey, fail);
306
307    SOSCircleRemoveRetired(circle, error); // Prune off retirees since we're signing this one
308    CFArrayRemoveAllValues(circle->rejected_applicants); // Dump rejects so we clean them up sometime.
309    SOSCircleRejectNonValidApplicants(circle, SecKeyCreatePublicFromPrivate(user_approver));
310    SOSCircleGenerationIncrement(circle);
311    require_quiet(SOSCircleRemoveSignatures(circle, error), fail);
312    require_quiet(SOSCircleSign(circle, user_approver, error), fail);
313    require_quiet(SOSCircleSign(circle, ourKey, error), fail);
314
315    CFReleaseNull(ourKey);
316    return true;
317
318fail:
319    CFReleaseNull(ourKey);
320    return false;
321}
322
323
324bool SOSCircleConcordanceSign(SOSCircleRef circle, SOSFullPeerInfoRef peerinfo, CFErrorRef *error) {
325    bool success = false;
326    SecKeyRef ourKey = SOSFullPeerInfoCopyDeviceKey(peerinfo, error);
327    require_quiet(ourKey, exit);
328
329    success = SOSCircleSign(circle, ourKey, error);
330
331exit:
332    CFReleaseNull(ourKey);
333    return success;
334}
335
336static inline SOSConcordanceStatus CheckPeerStatus(SOSCircleRef circle, SOSPeerInfoRef peer, CFErrorRef *error) {
337    SOSConcordanceStatus result = kSOSConcordanceNoPeer;
338    SecKeyRef pubKey = SOSPeerInfoCopyPubKey(peer);
339
340    require_action_quiet(SOSCircleHasActivePeer(circle, peer, error), exit, result = kSOSConcordanceNoPeer);
341    require_action_quiet(SOSCircleVerifySignatureExists(circle, pubKey, error), exit, result = kSOSConcordanceNoPeerSig);
342    require_action_quiet(SOSCircleVerify(circle, pubKey, error), exit, result = kSOSConcordanceBadPeerSig);
343
344    result = kSOSConcordanceTrusted;
345
346exit:
347    CFReleaseNull(pubKey);
348    return result;
349}
350
351static inline SOSConcordanceStatus CombineStatus(SOSConcordanceStatus status1, SOSConcordanceStatus status2)
352{
353    if (status1 == kSOSConcordanceTrusted || status2 == kSOSConcordanceTrusted)
354        return kSOSConcordanceTrusted;
355
356    if (status1 == kSOSConcordanceBadPeerSig || status2 == kSOSConcordanceBadPeerSig)
357        return kSOSConcordanceBadPeerSig;
358
359    if (status1 == kSOSConcordanceNoPeerSig || status2 == kSOSConcordanceNoPeerSig)
360        return kSOSConcordanceNoPeerSig;
361
362    return status1;
363}
364
365static inline bool SOSCircleIsEmpty(SOSCircleRef circle) {
366    return SOSCircleCountPeers(circle) == 0;
367}
368
369static inline bool SOSCircleIsOffering(SOSCircleRef circle) {
370    return SOSCircleCountPeers(circle) == 1;
371}
372
373static inline bool SOSCircleIsResignOffering(SOSCircleRef circle, SecKeyRef pubkey) {
374    return SOSCircleCountActiveValidPeers(circle, pubkey) == 1;
375}
376
377static inline SOSConcordanceStatus GetSignersStatus(SOSCircleRef signers_circle, SOSCircleRef status_circle,
378                                                    SecKeyRef user_pubKey, SOSPeerInfoRef exclude, CFErrorRef *error) {
379    CFStringRef excluded_id = exclude ? SOSPeerInfoGetPeerID(exclude) : NULL;
380
381    __block SOSConcordanceStatus status = kSOSConcordanceNoPeer;
382    SOSCircleForEachActiveValidPeer(signers_circle, user_pubKey, ^(SOSPeerInfoRef peer) {
383        SOSConcordanceStatus peerStatus = CheckPeerStatus(status_circle, peer, error);
384
385        if (peerStatus == kSOSConcordanceNoPeerSig &&
386            (CFEqualSafe(SOSPeerInfoGetPeerID(peer), excluded_id) || SOSPeerInfoIsCloudIdentity(peer)))
387            peerStatus = kSOSConcordanceNoPeer;
388
389        status = CombineStatus(status, peerStatus); // TODO: Use multiple error gathering.
390    });
391
392    return status;
393}
394
395static inline bool isOlderGeneration(SOSCircleRef current, SOSCircleRef proposed) {
396    return CFNumberCompare(current->generation, proposed->generation, NULL) == kCFCompareGreaterThan;
397}
398
399bool SOSCircleSharedTrustedPeers(SOSCircleRef current, SOSCircleRef proposed, SOSPeerInfoRef me) {
400    __block bool retval = false;
401    SOSCircleForEachPeer(current, ^(SOSPeerInfoRef peer) {
402        if(!CFEqual(me, peer) && SOSCircleHasPeer(proposed, peer, NULL)) retval = true;
403    });
404    return retval;
405}
406
407static void SOSCircleUpgradePeersByCircle(SOSCircleRef known_circle, SOSCircleRef proposed_circle) {
408    SOSCircleForEachPeer(known_circle, ^(SOSPeerInfoRef known_peer) {
409        SOSPeerInfoRef proposed_peer = SOSCircleCopyPeerInfo(proposed_circle, SOSPeerInfoGetPeerID(known_peer), NULL);
410        if(proposed_peer && CFEqualSafe(proposed_peer, known_peer) != 0) {
411            SOSCircleUpdatePeerInfo(known_circle, proposed_peer);
412        }
413    });
414}
415
416SOSConcordanceStatus SOSCircleConcordanceTrust(SOSCircleRef known_circle, SOSCircleRef proposed_circle,
417                                               SecKeyRef known_pubkey, SecKeyRef user_pubkey,
418                                               SOSPeerInfoRef exclude, CFErrorRef *error) {
419
420    if(user_pubkey == NULL) {
421        SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Concordance with no public key"), NULL, error);
422        return kSOSConcordanceNoUserKey; //TODO: - needs to return an error
423    }
424
425    if (SOSCircleIsEmpty(proposed_circle)) {
426        return kSOSConcordanceTrusted;
427    }
428
429    if(!SOSCircleVerifySignatureExists(proposed_circle, user_pubkey, error)) {
430        SOSCreateError(kSOSErrorBadSignature, CFSTR("No public signature"), (error != NULL) ? *error : NULL, error);
431        return kSOSConcordanceNoUserSig;
432    }
433
434    if(!SOSCircleVerify(proposed_circle, user_pubkey, error)) {
435        SOSCreateError(kSOSErrorBadSignature, CFSTR("Bad public signature"), (error != NULL) ? *error : NULL, error);
436        return kSOSConcordanceBadUserSig;
437    }
438
439    if (SOSCircleIsEmpty(known_circle) || SOSCircleIsOffering(proposed_circle)) {
440        return GetSignersStatus(proposed_circle, proposed_circle, user_pubkey, NULL, error);
441    }
442
443    if(isOlderGeneration(known_circle, proposed_circle)) {
444        SOSCreateError(kSOSErrorReplay, CFSTR("Bad generation"), NULL, error);
445        return kSOSConcordanceGenOld;
446    }
447
448
449    if(!SOSCircleVerify(known_circle, user_pubkey, error)) {
450        SOSCircleUpgradePeersByCircle(known_circle, proposed_circle);
451    }
452
453    if(known_pubkey == NULL) known_pubkey = user_pubkey;
454    if(!SOSCircleVerify(known_circle, known_pubkey, error)) known_pubkey = user_pubkey;
455    return GetSignersStatus(known_circle, proposed_circle, known_pubkey, exclude, error);
456}
457
458
459static const uint8_t* der_decode_mutable_dictionary(CFAllocatorRef allocator, CFOptionFlags mutability,
460                                                    CFMutableDictionaryRef* dictionary, CFErrorRef *error,
461                                                    const uint8_t* der, const uint8_t *der_end)
462{
463    CFDictionaryRef theDict;
464    const uint8_t* result = der_decode_dictionary(allocator, mutability, &theDict, error, der, der_end);
465
466    if (result != NULL)
467        *dictionary = (CFMutableDictionaryRef)theDict;
468
469    return result;
470}
471
472SOSCircleRef SOSCircleCreateFromDER(CFAllocatorRef allocator, CFErrorRef* error,
473                                      const uint8_t** der_p, const uint8_t *der_end) {
474    SOSCircleRef cir = CFTypeAllocate(SOSCircle, struct __OpaqueSOSCircle, allocator);
475
476    const uint8_t *sequence_end;
477
478    cir->name = NULL;
479    cir->generation = NULL;
480    cir->peers = NULL;
481    cir->applicants = NULL;
482    cir->rejected_applicants = NULL;
483    cir->signatures = NULL;
484
485    *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
486    require_action_quiet(sequence_end != NULL, fail,
487                         SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Circle DER"), (error != NULL) ? *error : NULL, error));
488
489    // Version first.
490    uint64_t version = 0;
491    *der_p = ccder_decode_uint64(&version, *der_p, der_end);
492
493    require_action_quiet(version == kOnlyCompatibleVersion, fail,
494                         SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("Bad Circle Version"), NULL, error));
495
496    *der_p = der_decode_string(allocator, 0, &cir->name, error, *der_p, sequence_end);
497    *der_p = der_decode_number(allocator, 0, &cir->generation, error, *der_p, sequence_end);
498
499    cir->peers = SOSPeerInfoArrayCreateFromDER(allocator, error, der_p, sequence_end);
500    cir->applicants = SOSPeerInfoArrayCreateFromDER(allocator, error, der_p, sequence_end);
501    cir->rejected_applicants = SOSPeerInfoArrayCreateFromDER(allocator, error, der_p, sequence_end);
502
503    *der_p = der_decode_mutable_dictionary(allocator, kCFPropertyListMutableContainersAndLeaves,
504                                           &cir->signatures, error, *der_p, sequence_end);
505
506    require_action_quiet(*der_p == sequence_end, fail,
507                         SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Circle DER"), (error != NULL) ? *error : NULL, error));
508
509    return cir;
510
511fail:
512    CFReleaseNull(cir);
513    return NULL;
514}
515
516SOSCircleRef SOSCircleCreateFromData(CFAllocatorRef allocator, CFDataRef circleData, CFErrorRef *error)
517{
518    size_t size = CFDataGetLength(circleData);
519    const uint8_t *der = CFDataGetBytePtr(circleData);
520    SOSCircleRef inflated = SOSCircleCreateFromDER(allocator, error, &der, der + size);
521    return inflated;
522}
523
524size_t SOSCircleGetDEREncodedSize(SOSCircleRef cir, CFErrorRef *error) {
525    SOSCircleAssertStable(cir);
526    size_t total_payload = 0;
527
528    require_quiet(accumulate_size(&total_payload, ccder_sizeof_uint64(kOnlyCompatibleVersion)),                        fail);
529    require_quiet(accumulate_size(&total_payload, der_sizeof_string(cir->name, error)),                                fail);
530    require_quiet(accumulate_size(&total_payload, der_sizeof_number(cir->generation, error)),                          fail);
531    require_quiet(accumulate_size(&total_payload, SOSPeerInfoArrayGetDEREncodedSize(cir->peers, error)),               fail);
532    require_quiet(accumulate_size(&total_payload, SOSPeerInfoArrayGetDEREncodedSize(cir->applicants, error)),          fail);
533    require_quiet(accumulate_size(&total_payload, SOSPeerInfoArrayGetDEREncodedSize(cir->rejected_applicants, error)), fail);
534    require_quiet(accumulate_size(&total_payload, der_sizeof_dictionary((CFDictionaryRef) cir->signatures, error)),    fail);
535
536    return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, total_payload);
537
538fail:
539    SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
540    return 0;
541}
542
543uint8_t* SOSCircleEncodeToDER(SOSCircleRef cir, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
544    SOSCircleAssertStable(cir);
545
546    return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
547           ccder_encode_uint64(kOnlyCompatibleVersion, der,
548           der_encode_string(cir->name, error, der,
549           der_encode_number(cir->generation, error, der,
550           SOSPeerInfoArrayEncodeToDER(cir->peers, error, der,
551           SOSPeerInfoArrayEncodeToDER(cir->applicants, error, der,
552           SOSPeerInfoArrayEncodeToDER(cir->rejected_applicants, error, der,
553           der_encode_dictionary((CFDictionaryRef) cir->signatures, error, der, der_end))))))));
554}
555
556CFDataRef SOSCircleCreateIncompatibleCircleDER(CFErrorRef* error)
557{
558    size_t total_payload = 0;
559    size_t encoded_size = 0;
560    uint8_t* der = 0;
561    uint8_t* der_end = 0;
562    CFMutableDataRef result = NULL;
563
564    require_quiet(accumulate_size(&total_payload, ccder_sizeof_uint64(kAlwaysIncompatibleVersion)), fail);
565
566    encoded_size = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, total_payload);
567
568    result = CFDataCreateMutableWithScratch(kCFAllocatorDefault, encoded_size);
569
570    der = CFDataGetMutableBytePtr(result);
571    der_end = der + CFDataGetLength(result);
572
573    der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
574              ccder_encode_uint64(kAlwaysIncompatibleVersion, der, der_end));
575
576fail:
577    if (der == NULL || der != der_end)
578        CFReleaseNull(result);
579
580    return result;
581}
582
583
584CFDataRef SOSCircleCopyEncodedData(SOSCircleRef circle, CFAllocatorRef allocator, CFErrorRef *error)
585{
586    size_t size = SOSCircleGetDEREncodedSize(circle, error);
587    if (size == 0)
588        return NULL;
589    uint8_t buffer[size];
590    uint8_t* start = SOSCircleEncodeToDER(circle, error, buffer, buffer + sizeof(buffer));
591    CFDataRef result = CFDataCreate(kCFAllocatorDefault, start, size);
592    return result;
593}
594
595static void SOSCircleDestroy(CFTypeRef aObj) {
596    SOSCircleRef c = (SOSCircleRef) aObj;
597
598    CFReleaseNull(c->name);
599    CFReleaseNull(c->generation);
600    CFReleaseNull(c->peers);
601    CFReleaseNull(c->applicants);
602    CFReleaseNull(c->rejected_applicants);
603    CFReleaseNull(c->signatures);
604}
605
606static CFStringRef SOSCircleCopyDescription(CFTypeRef aObj) {
607    SOSCircleRef c = (SOSCircleRef) aObj;
608
609    SOSCircleAssertStable(c);
610
611    return CFStringCreateWithFormat(NULL, NULL,
612                                    CFSTR("<SOSCircle@%p: [ \nName: %@, \nPeers: %@,\nApplicants: %@,\nRejects: %@,\nSignatures: %@\n ] >"),
613                                    c, c->name, c->peers, c->applicants, c->rejected_applicants, c->signatures);
614}
615
616CFStringRef SOSCircleGetName(SOSCircleRef circle) {
617    assert(circle);
618    assert(circle->name);
619    return circle->name;
620}
621
622const char *SOSCircleGetNameC(SOSCircleRef circle) {
623    CFStringRef name = SOSCircleGetName(circle);
624    if (!name)
625        return strdup("");
626    return CFStringToCString(name);
627}
628
629CFNumberRef SOSCircleGetGeneration(SOSCircleRef circle) {
630    assert(circle);
631    assert(circle->generation);
632    return circle->generation;
633}
634
635int64_t SOSCircleGetGenerationSint(SOSCircleRef circle) {
636    CFNumberRef gen = SOSCircleGetGeneration(circle);
637    int64_t value;
638    if(!gen) return 0;
639    CFNumberGetValue(gen, kCFNumberSInt64Type, &value);
640    return value;
641}
642
643void SOSCircleGenerationIncrement(SOSCircleRef circle) {
644    CFAllocatorRef allocator = CFGetAllocator(circle->generation);
645    int64_t value = SOSCircleGetGenerationSint(circle);
646    value++;
647    circle->generation = CFNumberCreate(allocator, kCFNumberSInt64Type, &value);
648}
649
650int SOSCircleCountPeers(SOSCircleRef circle) {
651    SOSCircleAssertStable(circle);
652    __block int count = 0;
653    SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
654        ++count;
655    });
656    return count;
657}
658
659int SOSCircleCountActivePeers(SOSCircleRef circle) {
660    SOSCircleAssertStable(circle);
661    __block int count = 0;
662    SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
663        ++count;
664    });
665    return count;
666}
667
668int SOSCircleCountActiveValidPeers(SOSCircleRef circle, SecKeyRef pubkey) {
669    SOSCircleAssertStable(circle);
670    __block int count = 0;
671    SOSCircleForEachActiveValidPeer(circle, pubkey, ^(SOSPeerInfoRef peer) {
672        ++count;
673    });
674    return count;
675}
676
677int SOSCircleCountRetiredPeers(SOSCircleRef circle) {
678    SOSCircleAssertStable(circle);
679    __block int count = 0;
680    SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
681        ++count;
682    });
683    return count;
684}
685
686int SOSCircleCountApplicants(SOSCircleRef circle) {
687    SOSCircleAssertStable(circle);
688
689    return (int)CFArrayGetCount(circle->applicants);
690}
691
692bool SOSCircleHasApplicant(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
693    SOSCircleAssertStable(circle);
694
695    return CFArrayHasValueMatching(circle->applicants, ^bool(const void *value) {
696        return SOSPeerInfoCompareByID(value, peerInfo, NULL) == 0;
697    });
698}
699
700CFMutableArrayRef SOSCircleCopyApplicants(SOSCircleRef circle, CFAllocatorRef allocator) {
701    SOSCircleAssertStable(circle);
702
703    return CFArrayCreateMutableCopy(allocator, 0, circle->applicants);
704}
705
706int SOSCircleCountRejectedApplicants(SOSCircleRef circle) {
707    SOSCircleAssertStable(circle);
708
709    return (int)CFArrayGetCount(circle->rejected_applicants);
710}
711
712bool SOSCircleHasRejectedApplicant(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
713    SOSCircleAssertStable(circle);
714
715    return CFArrayHasValueMatching(circle->rejected_applicants, ^bool(const void *value) {
716        return SOSPeerInfoCompareByID(value, peerInfo, NULL) == 0;
717    });
718}
719
720SOSPeerInfoRef SOSCircleCopyRejectedApplicant(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
721    SOSCircleAssertStable(circle);
722    return (SOSPeerInfoRef) CFArrayGetValueMatching(circle->rejected_applicants, ^bool(const void *value) {
723        return SOSPeerInfoCompareByID(value, peerInfo, NULL) == 0;
724    });
725}
726
727CFMutableArrayRef SOSCircleCopyRejectedApplicants(SOSCircleRef circle, CFAllocatorRef allocator) {
728    SOSCircleAssertStable(circle);
729
730    return CFArrayCreateMutableCopy(allocator, 0, circle->rejected_applicants);
731}
732
733
734bool SOSCircleHasPeerWithID(SOSCircleRef circle, CFStringRef peerid, CFErrorRef *error) {
735    SOSCircleAssertStable(circle);
736    __block bool found = false;
737    SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
738        if(peerid && peer && CFEqualSafe(peerid, SOSPeerInfoGetPeerID(peer))) found = true;
739    });
740    return found;
741}
742
743bool SOSCircleHasPeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
744    return SOSCircleHasPeerWithID(circle, SOSPeerInfoGetPeerID(peerInfo), error);
745}
746
747bool SOSCircleHasActivePeerWithID(SOSCircleRef circle, CFStringRef peerid, CFErrorRef *error) {
748    SOSCircleAssertStable(circle);
749    __block bool found = false;
750    SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
751        if(peerid && peer && CFEqualSafe(peerid, SOSPeerInfoGetPeerID(peer))) found = true;
752    });
753    return found;
754}
755
756bool SOSCircleHasActivePeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
757    if(!peerInfo) return false;
758    return SOSCircleHasActivePeerWithID(circle, SOSPeerInfoGetPeerID(peerInfo), error);
759}
760
761
762
763bool SOSCircleResetToEmpty(SOSCircleRef circle, CFErrorRef *error) {
764    CFArrayRemoveAllValues(circle->applicants);
765    CFArrayRemoveAllValues(circle->peers);
766    CFDictionaryRemoveAllValues(circle->signatures);
767
768    return true;
769}
770
771bool SOSCircleResetToOffering(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error){
772
773    return SOSCircleResetToEmpty(circle, error)
774        && SOSCircleRequestAdmission(circle, user_privkey, requestor, error)
775        && SOSCircleAcceptRequest(circle, user_privkey, requestor, SOSFullPeerInfoGetPeerInfo(requestor), error);
776}
777
778CFIndex SOSCircleRemoveRetired(SOSCircleRef circle, CFErrorRef *error) {
779    CFIndex n = CFArrayRemoveAllPassing(circle->peers,  ^ bool (const void *element) {
780        SOSPeerInfoRef peer = (SOSPeerInfoRef) element;
781
782        return SOSPeerInfoIsRetirementTicket(peer);
783    });
784
785    return n;
786}
787
788static bool SOSCircleRecordAdmission(SOSCircleRef circle, SecKeyRef user_pubkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
789    SOSCircleAssertStable(circle);
790
791    bool isPeer = SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(requestor), error);
792
793    require_action_quiet(!isPeer, fail, SOSCreateError(kSOSErrorAlreadyPeer, CFSTR("Cannot request admission when already a peer"), NULL, error));
794
795    CFIndex total = CFArrayRemoveAllWithMatchingID(circle->applicants, SOSFullPeerInfoGetPeerInfo(requestor));
796
797    (void) total; // Suppress unused warning in release code.
798    assert(total <= 1); // We should at most be in the list once.
799
800    total = CFArrayRemoveAllWithMatchingID(circle->rejected_applicants, SOSFullPeerInfoGetPeerInfo(requestor));
801
802    (void) total; // Suppress unused warning in release code.
803    assert(total <= 1); // We should at most be in the list once.
804
805
806    // Refetch the current PeerInfo as the promtion above can change it.
807    CFArrayAppendValue(circle->applicants, SOSFullPeerInfoGetPeerInfo(requestor));
808
809    return true;
810
811fail:
812    return false;
813
814}
815
816bool SOSCircleRequestReadmission(SOSCircleRef circle, SecKeyRef user_pubkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
817    bool success = false;
818
819    SOSPeerInfoRef peer = SOSFullPeerInfoGetPeerInfo(requestor);
820    require_quiet(SOSPeerInfoApplicationVerify(peer, user_pubkey, error), fail);
821    success = SOSCircleRecordAdmission(circle, user_pubkey, requestor, error);
822fail:
823    return success;
824}
825
826bool SOSCircleRequestAdmission(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
827    bool success = false;
828
829    SecKeyRef user_pubkey = SecKeyCreatePublicFromPrivate(user_privkey);
830    require_action_quiet(user_pubkey, fail, SOSCreateError(kSOSErrorBadKey, CFSTR("No public key for key"), NULL, error));
831
832    require(SOSFullPeerInfoPromoteToApplication(requestor, user_privkey, error), fail);
833
834    success = SOSCircleRecordAdmission(circle, user_pubkey, requestor, error);
835fail:
836    CFReleaseNull(user_pubkey);
837    return success;
838}
839
840
841bool SOSCircleUpdatePeerInfo(SOSCircleRef circle, SOSPeerInfoRef replacement_peer_info) {
842    __block bool replaced = false;
843    CFStringRef replacement_peer_id = SOSPeerInfoGetPeerID(replacement_peer_info);
844
845    CFMutableArrayModifyValues(circle->peers, ^const void *(const void *value) {
846        if (CFEqual(replacement_peer_id, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value))
847         && !CFEqual(replacement_peer_info, value)) {
848            replaced = true;
849            return replacement_peer_info;
850        }
851
852        return value;
853    });
854    return replaced;
855}
856
857bool SOSCircleRemovePeer(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, SOSPeerInfoRef peer_to_remove, CFErrorRef *error) {
858    SOSPeerInfoRef requestor_peer_info = SOSFullPeerInfoGetPeerInfo(requestor);
859
860    if (SOSCircleHasApplicant(circle, peer_to_remove, error)) {
861        return SOSCircleRejectRequest(circle, requestor, peer_to_remove, error);
862    }
863
864    if (!SOSCircleHasPeer(circle, requestor_peer_info, error)) {
865        SOSCreateError(kSOSErrorAlreadyPeer, CFSTR("Must be peer to remove peer"), NULL, error);
866        return false;
867    }
868
869    CFArrayRemoveAllWithMatchingID(circle->peers, peer_to_remove);
870
871    SOSCircleGenerationSign(circle, user_privkey, requestor, error);
872
873    return true;
874}
875
876bool SOSCircleAcceptRequest(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef device_approver, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
877    SOSCircleAssertStable(circle);
878
879    CFIndex total = CFArrayRemoveAllWithMatchingID(circle->applicants, peerInfo);
880    SecKeyRef publicKey = NULL;
881    bool result = false;
882
883    require_action_quiet(total != 0, fail,
884                         SOSCreateError(kSOSErrorNotApplicant, CFSTR("Cannot accept non-applicant"), NULL, error));
885
886    publicKey = SecKeyCreatePublicFromPrivate(user_privkey);
887    require_quiet(SOSPeerInfoApplicationVerify(peerInfo, publicKey, error), fail);
888
889    assert(total == 1);
890
891    CFArrayAppendValue(circle->peers, peerInfo);
892    result = SOSCircleGenerationSign(circle, user_privkey, device_approver, error);
893    secnotice("circle", "Accepted %@", peerInfo);
894
895fail:
896    CFReleaseNull(publicKey);
897    return result;
898}
899
900bool SOSCircleWithdrawRequest(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
901    SOSCircleAssertStable(circle);
902
903#ifndef NDEBUG
904    CFIndex total =
905#endif
906        CFArrayRemoveAllWithMatchingID(circle->applicants, peerInfo);
907
908    assert(total <= 1);
909
910    return true;
911}
912
913bool SOSCircleRemoveRejectedPeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
914    SOSCircleAssertStable(circle);
915
916#ifndef NDEBUG
917    CFIndex total =
918#endif
919    CFArrayRemoveAllWithMatchingID(circle->rejected_applicants, peerInfo);
920
921    assert(total <= 1);
922
923    return true;
924}
925
926
927bool SOSCircleRejectRequest(SOSCircleRef circle, SOSFullPeerInfoRef device_rejector,
928                            SOSPeerInfoRef peerInfo, CFErrorRef *error) {
929    SOSCircleAssertStable(circle);
930
931    if (CFEqual(SOSPeerInfoGetPeerID(peerInfo), SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(device_rejector))))
932        return SOSCircleWithdrawRequest(circle, peerInfo, error);
933
934	CFIndex total = CFArrayRemoveAllWithMatchingID(circle->applicants, peerInfo);
935
936    if (total == 0) {
937        SOSCreateError(kSOSErrorNotApplicant, CFSTR("Cannot reject non-applicant"), NULL, error);
938        return false;
939    }
940    assert(total == 1);
941
942    CFArrayAppendValue(circle->rejected_applicants, peerInfo);
943
944    // TODO: Maybe we sign the rejection with device_rejector.
945
946    return true;
947}
948
949bool SOSCircleAcceptRequests(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef device_approver,
950                             CFErrorRef *error) {
951    // Returns true if we accepted someone and therefore have to post the circle back to KVS
952    __block bool result = false;
953
954    SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) {
955        if (!SOSCircleAcceptRequest(circle, user_privkey, device_approver, peer, error))
956            printf("error in SOSCircleAcceptRequest\n");
957        else {
958            secnotice("circle", "Accepted peer: %@", peer);
959            result = true;
960        }
961    });
962
963    if (result) {
964        SOSCircleGenerationSign(circle, user_privkey, device_approver, error);
965        secnotice("circle", "Countersigned accepted requests");
966    }
967
968    return result;
969}
970
971bool SOSCirclePeerSigUpdate(SOSCircleRef circle, SecKeyRef userPrivKey, SOSFullPeerInfoRef fpi,
972                             CFErrorRef *error) {
973    // Returns true if we accepted someone and therefore have to post the circle back to KVS
974    __block bool result = false;
975    SecKeyRef userPubKey = SecKeyCreatePublicFromPrivate(userPrivKey);
976
977    // We're going to remove any applicants using a mismatched user key.
978    SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) {
979        if(!SOSPeerInfoApplicationVerify(peer, userPubKey, NULL)) {
980            if(!SOSCircleRejectRequest(circle, fpi, peer, NULL)) {
981                // do we care?
982            }
983        }
984    });
985
986    result = SOSCircleUpdatePeerInfo(circle, SOSFullPeerInfoGetPeerInfo(fpi));
987
988    if (result) {
989        SOSCircleGenerationSign(circle, userPrivKey, fpi, error);
990        secnotice("circle", "Generation signed updated signatures on peerinfo");
991    }
992
993    return result;
994}
995
996SOSPeerInfoRef SOSCircleCopyPeerInfo(SOSCircleRef circle, CFStringRef peer_id, CFErrorRef *error) {
997    __block SOSPeerInfoRef result = NULL;
998
999    CFArrayForEach(circle->peers, ^(const void *value) {
1000        if (result == NULL) {
1001            SOSPeerInfoRef tpi = (SOSPeerInfoRef)value;
1002            if (CFEqual(SOSPeerInfoGetPeerID(tpi), peer_id))
1003                result = tpi;
1004        }
1005    });
1006
1007    CFRetainSafe(result);
1008    return result;
1009}
1010
1011
1012static inline void SOSCircleForEachPeerMatching(SOSCircleRef circle,
1013                                                void (^action)(SOSPeerInfoRef peer),
1014                                                bool (^condition)(SOSPeerInfoRef peer)) {
1015    CFArrayForEach(circle->peers, ^(const void *value) {
1016        SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1017        if (condition(peer))
1018            action(peer);
1019    });
1020}
1021
1022void SOSCircleForEachPeer(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
1023    SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1024        return !SOSPeerInfoIsRetirementTicket(peer) && !SOSPeerInfoIsCloudIdentity(peer);
1025    });
1026}
1027
1028void SOSCircleForEachRetiredPeer(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
1029    SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1030        return SOSPeerInfoIsRetirementTicket(peer);
1031    });
1032}
1033
1034void SOSCircleForEachActivePeer(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
1035    SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1036        return true;
1037    });
1038}
1039
1040void SOSCircleForEachActiveValidPeer(SOSCircleRef circle, SecKeyRef user_public_key, void (^action)(SOSPeerInfoRef peer)) {
1041    SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1042        return SOSPeerInfoApplicationVerify(peer, user_public_key, NULL);
1043    });
1044}
1045
1046void SOSCircleForEachApplicant(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
1047    CFArrayForEach(circle->applicants, ^(const void*value) { action((SOSPeerInfoRef) value); } );
1048}
1049
1050
1051CFMutableArrayRef SOSCircleCopyPeers(SOSCircleRef circle, CFAllocatorRef allocator) {
1052    SOSCircleAssertStable(circle);
1053
1054    CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(allocator);
1055
1056    SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
1057        CFArrayAppendValue(result, peer);
1058    });
1059
1060    return result;
1061}
1062
1063CFMutableArrayRef SOSCircleCopyConcurringPeers(SOSCircleRef circle, CFErrorRef* error) {
1064    SOSCircleAssertStable(circle);
1065
1066    CFMutableArrayRef concurringPeers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1067
1068    SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
1069        CFErrorRef error = NULL;
1070        if (SOSCircleVerifyPeerSigned(circle, peer, &error)) {
1071            CFArrayAppendValue(concurringPeers, peer);
1072        } else if (error != NULL) {
1073            secerror("Error checking concurrence: %@", error);
1074        }
1075        CFReleaseNull(error);
1076    });
1077
1078    return concurringPeers;
1079}
1080
1081
1082//
1083// Stuff above this line is really SOSCircleInfo below the line is the active SOSCircle functionality
1084//
1085
1086static SOSPeerRef SOSCircleCopyPeer(SOSCircleRef circle, SOSFullPeerInfoRef myRef, SOSPeerSendBlock sendBlock,
1087                                    CFStringRef peer_id, CFErrorRef *error) {
1088    SOSPeerRef peer = NULL;
1089    SOSPeerInfoRef peer_info = SOSCircleCopyPeerInfo(circle, peer_id, error);
1090    //TODO: if (peer is legit member of us then good otherwise bail) {
1091    //}
1092    if (peer_info) {
1093        peer = SOSPeerCreate(myRef, peer_info, error, sendBlock);
1094        CFReleaseNull(peer_info);
1095    }
1096    return peer;
1097}
1098
1099
1100static bool SOSCircleDoWithPeer(SOSFullPeerInfoRef myRef, SOSCircleRef circle, SOSDataSourceFactoryRef factory,
1101                                SOSPeerSendBlock sendBlock, CFStringRef peer_id, bool readOnly,
1102                                CFErrorRef* error, bool (^do_action)(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error))
1103{
1104    bool success = false;
1105    SOSEngineRef engine = NULL;
1106    SOSPeerRef peer = NULL;
1107    SOSDataSourceRef ds = NULL;
1108
1109    peer = SOSCircleCopyPeer(circle, myRef, sendBlock, peer_id, error);
1110    require(peer, exit);
1111
1112    ds = factory->create_datasource(factory, SOSCircleGetName(circle), readOnly, error);
1113    require(ds, exit);
1114
1115    engine = SOSEngineCreate(ds, error); // Hand off DS to engine.
1116    ds = NULL;
1117    require(engine, exit);
1118
1119    success = do_action(engine, peer, error);
1120
1121exit:
1122    if (ds)
1123        ds->release(ds);
1124    if (engine)
1125        SOSEngineDispose(engine);
1126    if (peer)
1127        SOSPeerDispose(peer);
1128
1129    return success;
1130}
1131
1132bool SOSCircleSyncWithPeer(SOSFullPeerInfoRef myRef, SOSCircleRef circle, SOSDataSourceFactoryRef factory,
1133                           SOSPeerSendBlock sendBlock, CFStringRef peer_id,
1134                           CFErrorRef *error)
1135{
1136    return SOSCircleDoWithPeer(myRef, circle, factory, sendBlock, peer_id, true, error, ^bool(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
1137        return SOSPeerStartSync(peer, engine, error) != kSOSPeerCoderFailure;
1138    });
1139}
1140
1141bool SOSCircleHandlePeerMessage(SOSCircleRef circle, SOSFullPeerInfoRef myRef, SOSDataSourceFactoryRef factory,
1142                                SOSPeerSendBlock sendBlock, CFStringRef peer_id,
1143                                CFDataRef message, CFErrorRef *error) {
1144    return SOSCircleDoWithPeer(myRef, circle, factory, sendBlock, peer_id, false, error, ^bool(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
1145        return SOSPeerHandleMessage(peer, engine, message, error) != kSOSPeerCoderFailure;
1146    });
1147}
1148
1149
1150SOSFullPeerInfoRef SOSCircleGetiCloudFullPeerInfoRef(SOSCircleRef circle) {
1151    __block SOSFullPeerInfoRef cloud_full_peer = NULL;
1152    SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
1153        if (SOSPeerInfoIsCloudIdentity(peer)) {
1154            if (cloud_full_peer == NULL) {
1155                CFErrorRef localError = NULL;
1156                cloud_full_peer = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, &localError);
1157
1158                if (localError) {
1159                    secerror("Found cloud peer in circle but can't make full peer: %@", localError);
1160                    CFReleaseNull(localError);
1161                }
1162
1163            } else {
1164                secerror("More than one cloud identity found in circle: %@", circle);
1165            }
1166        }
1167    });
1168    return cloud_full_peer;
1169}
1170
1171
1172
1173