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