]> git.saurik.com Git - apple/security.git/blame - OSX/sec/SOSCircle/SecureObjectSync/SOSCircle.c
Security-57740.20.22.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSCircle.c
CommitLineData
427c49bc 1/*
d8f41ccd
A
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@
427c49bc
A
22 */
23
d8f41ccd 24
427c49bc
A
25/*
26 * SOSCircle.c - Implementation of the secure object syncing transport
27 */
28
29#include <AssertMacros.h>
30
31#include <CoreFoundation/CFArray.h>
5c19dc3a
A
32#include <Security/SecureObjectSync/SOSCircle.h>
33#include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
34#include <Security/SecureObjectSync/SOSInternal.h>
35#include <Security/SecureObjectSync/SOSEngine.h>
36#include <Security/SecureObjectSync/SOSPeer.h>
37#include <Security/SecureObjectSync/SOSPeerInfoInternal.h>
38#include <Security/SecureObjectSync/SOSGenCount.h>
39#include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
40#include <Security/SecureObjectSync/SOSGenCount.h>
427c49bc
A
41#include <CoreFoundation/CoreFoundation.h>
42#include <Security/SecFramework.h>
43
44#include <Security/SecKey.h>
45#include <Security/SecKeyPriv.h>
fa7225c8 46#include <utilities/SecBuffer.h>
427c49bc
A
47
48#include <utilities/SecCFWrappers.h>
5c19dc3a 49#include <Security/SecureObjectSync/SOSCirclePriv.h>
427c49bc 50
5c19dc3a 51//#include "ckdUtilities.h"
427c49bc
A
52
53#include <corecrypto/ccder.h>
54#include <corecrypto/ccdigest.h>
55#include <corecrypto/ccsha2.h>
56
57#include <stdlib.h>
58#include <assert.h>
59
427c49bc
A
60CFGiblisWithCompareFor(SOSCircle);
61
427c49bc
A
62SOSCircleRef SOSCircleCreate(CFAllocatorRef allocator, CFStringRef name, CFErrorRef *error) {
63 SOSCircleRef c = CFTypeAllocate(SOSCircle, struct __OpaqueSOSCircle, allocator);
427c49bc
A
64 assert(name);
65
66 c->name = CFStringCreateCopy(allocator, name);
e0e0d90e 67 c->generation = SOSGenerationCreate();
d8f41ccd
A
68 c->peers = CFSetCreateMutableForSOSPeerInfosByID(allocator);
69 c->applicants = CFSetCreateMutableForSOSPeerInfosByID(allocator);
70 c->rejected_applicants = CFSetCreateMutableForSOSPeerInfosByID(allocator);
427c49bc 71 c->signatures = CFDictionaryCreateMutableForCFTypes(allocator);
427c49bc
A
72 return c;
73}
74
d8f41ccd
A
75static CFMutableSetRef CFSetOfPeerInfoDeepCopy(CFAllocatorRef allocator, CFSetRef peerInfoSet)
76{
77 __block CFMutableSetRef result = CFSetCreateMutableForSOSPeerInfosByID(allocator);
78 CFSetForEach(peerInfoSet, ^(const void *value) {
79 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
80 CFErrorRef localError = NULL;
81 SOSPeerInfoRef copiedPeer = SOSPeerInfoCreateCopy(allocator, pi, &localError);
82 if (copiedPeer) {
83 CFSetAddValue(result, copiedPeer);
84 } else {
85 secerror("Failed to copy peer: %@ (%@)", pi, localError);
86 }
87 CFReleaseSafe(copiedPeer);
88 CFReleaseSafe(localError);
89 });
90 return result;
91}
92
427c49bc
A
93SOSCircleRef SOSCircleCopyCircle(CFAllocatorRef allocator, SOSCircleRef otherCircle, CFErrorRef *error)
94{
95 SOSCircleRef c = CFTypeAllocate(SOSCircle, struct __OpaqueSOSCircle, allocator);
96
97 assert(otherCircle);
98 c->name = CFStringCreateCopy(allocator, otherCircle->name);
e0e0d90e 99 c->generation = SOSGenerationCopy(otherCircle->generation);
d8f41ccd
A
100
101 c->peers = CFSetOfPeerInfoDeepCopy(allocator, otherCircle->peers);
102 c->applicants = CFSetOfPeerInfoDeepCopy(allocator, otherCircle->applicants);
103 c->rejected_applicants = CFSetOfPeerInfoDeepCopy(allocator, otherCircle->rejected_applicants);
104
427c49bc
A
105 c->signatures = CFDictionaryCreateMutableCopy(allocator, 0, otherCircle->signatures);
106
107 return c;
108}
109
427c49bc
A
110static Boolean SOSCircleCompare(CFTypeRef lhs, CFTypeRef rhs) {
111 if (CFGetTypeID(lhs) != SOSCircleGetTypeID()
112 || CFGetTypeID(rhs) != SOSCircleGetTypeID())
113 return false;
114
115 SOSCircleRef left = SOSCircleConvertAndAssertStable(lhs);
116 SOSCircleRef right = SOSCircleConvertAndAssertStable(rhs);
117
118 // TODO: we should be doing set equality for peers and applicants.
119 return NULL != left && NULL != right
d8f41ccd
A
120 && CFEqualSafe(left->generation, right->generation)
121 && SOSPeerInfoSetContainsIdenticalPeers(left->peers, right->peers)
122 && SOSPeerInfoSetContainsIdenticalPeers(left->applicants, right->applicants)
123 && SOSPeerInfoSetContainsIdenticalPeers(left->rejected_applicants, right->rejected_applicants)
124 && CFEqualSafe(left->signatures, right->signatures);
427c49bc
A
125}
126
d8f41ccd
A
127static CFMutableArrayRef CFSetCopyValuesCFArray(CFSetRef set)
128{
129 CFIndex count = CFSetGetCount(set);
130
131 CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
132 if (count > 0) {
133 const void * values[count];
134 CFSetGetValues(set, values);
135 for (int current = 0; current < count; ++current) {
136 CFArrayAppendValue(result, values[current]);
137 }
138 }
139
140 return result;
141}
427c49bc
A
142
143static bool SOSCircleDigestArray(const struct ccdigest_info *di, CFMutableArrayRef array, void *hash_result, CFErrorRef *error)
144{
145 __block bool success = true;
146 ccdigest_di_decl(di, array_digest);
147 const void * a_digest = array_digest;
148
149 ccdigest_init(di, array_digest);
150 CFArraySortValues(array, CFRangeMake(0, CFArrayGetCount(array)), SOSPeerInfoCompareByID, SOSPeerCmpPubKeyHash);
151 CFArrayForEach(array, ^(const void *peer) {
152 if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes((SOSPeerInfoRef)peer, di, a_digest, error))
153 success = false;
154 });
155 ccdigest_final(di, array_digest, hash_result);
156
157 return success;
158}
159
d8f41ccd
A
160static bool SOSCircleDigestSet(const struct ccdigest_info *di, CFMutableSetRef set, void *hash_result, CFErrorRef *error)
161{
162 CFMutableArrayRef values = CFSetCopyValuesCFArray(set);
163
164 bool result = SOSCircleDigestArray(di, values, hash_result, error);
165
166 CFReleaseSafe(values);
167
168 return result;
169}
170
fa7225c8 171static bool SOSCircleHashGenAndPeers(const struct ccdigest_info *di, SOSGenCountRef gen, CFMutableSetRef peers, void*hash_result, CFErrorRef *error) {
427c49bc
A
172 ccdigest_di_decl(di, circle_digest);
173 ccdigest_init(di, circle_digest);
fa7225c8
A
174 int64_t generation = SOSGetGenerationSint(gen);
175 ccdigest_update(di, circle_digest, sizeof(generation), &generation);
176
177 SOSCircleDigestSet(di, peers, hash_result, error);
427c49bc
A
178 ccdigest_update(di, circle_digest, di->output_size, hash_result);
179 ccdigest_final(di, circle_digest, hash_result);
180 return true;
181}
182
fa7225c8
A
183static bool SOSCircleHash(const struct ccdigest_info *di, SOSCircleRef circle, void *hash_result, CFErrorRef *error) {
184 return SOSCircleHashGenAndPeers(di, SOSCircleGetGeneration(circle), circle->peers, hash_result, error);
185}
186
187static bool SOSCircleHashNextGenWithAdditionalPeer(const struct ccdigest_info *di, SOSCircleRef circle, SOSPeerInfoRef additionalPeer, void *hash_result, CFErrorRef *error) {
188 CFMutableSetRef peers = CFSetCreateMutableCopy(NULL, 0, circle->peers);
189 CFSetAddValue(peers, additionalPeer);
190
191 SOSGenCountRef nextGen = SOSGenerationIncrementAndCreate(circle->generation);
192
193 return SOSCircleHashGenAndPeers(di, nextGen, peers, hash_result, error);
194}
195
196bool SOSCircleSetSignature(SOSCircleRef circle, SecKeyRef pubkey, CFDataRef signature, CFErrorRef *error) {
427c49bc
A
197 bool result = false;
198
199 CFStringRef pubKeyID = SOSCopyIDOfKey(pubkey, error);
200 require_quiet(pubKeyID, fail);
201 CFDictionarySetValue(circle->signatures, pubKeyID, signature);
202 result = true;
203
204fail:
205 CFReleaseSafe(pubKeyID);
206 return result;
207}
208
209static bool SOSCircleRemoveSignatures(SOSCircleRef circle, CFErrorRef *error) {
210 CFDictionaryRemoveAllValues(circle->signatures);
211 return true;
212}
213
fa7225c8 214CFDataRef SOSCircleGetSignature(SOSCircleRef circle, SecKeyRef pubkey, CFErrorRef *error) {
427c49bc
A
215 CFStringRef pubKeyID = SOSCopyIDOfKey(pubkey, error);
216 CFDataRef result = NULL;
217 require_quiet(pubKeyID, fail);
218
219 CFTypeRef value = (CFDataRef)CFDictionaryGetValue(circle->signatures, pubKeyID);
220
221 if (isData(value)) result = (CFDataRef) value;
222
223fail:
224 CFReleaseSafe(pubKeyID);
225 return result;
226}
227
fa7225c8
A
228#define circle_signature_di() ccsha256_di()
229
230static CFDataRef SecKeyCopyRawHashSignature(const struct ccdigest_info *di, const uint8_t* hashToSign, SecKeyRef privKey, CFErrorRef *error) {
231 CFDataRef result = NULL;
232
233 CFMutableDataRef signature = CFDataCreateMutableWithScratch(kCFAllocatorDefault, SecKeyGetSize(privKey, kSecKeySignatureSize));
234 size_t signatureSpace = CFDataGetLength(signature);
235
236 OSStatus status = SecKeyRawSign(privKey, kSecPaddingNone, hashToSign, di->output_size, CFDataGetMutableBytePtr(signature), &signatureSpace);
237 require_quiet(SecError(status, error, CFSTR("Signing failed: %d"), status), fail);
238
239 if (signatureSpace < (size_t)CFDataGetLength(signature)) {
240 CFDataSetLength(signature, signatureSpace);
241 }
242
243 CFTransferRetained(result, signature);
244fail:
245 CFReleaseNull(signature);
246 return result;
247}
248
427c49bc 249bool SOSCircleSign(SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
fa7225c8
A
250 const struct ccdigest_info *di = circle_signature_di();
251
252 __block CFDataRef signature = NULL;
253 bool didSign = false;
254 require_quiet(privKey, fail);
255
256 PerformWithBuffer(di->output_size, ^(size_t size, uint8_t *hash_result) {
257 if (SOSCircleHash(di, circle, hash_result, error)) {
258 signature = SecKeyCopyRawHashSignature(di, hash_result, privKey, error);
259 }
260 });
261 require_quiet(signature, fail);
262 require_quiet(SOSCircleSetSignature(circle, privKey, signature, error), fail);
263
264 didSign = true;
265
266fail:
267 CFReleaseNull(signature);
268 return didSign;
269}
270
271CFDataRef SOSCircleCopyNextGenSignatureWithPeerAdded(SOSCircleRef circle, SOSPeerInfoRef peer, SecKeyRef privKey, CFErrorRef *error) {
272 const struct ccdigest_info *di = circle_signature_di();
273
274 __block CFDataRef signature = NULL;
275 require_quiet(privKey, fail);
276
277 PerformWithBuffer(di->output_size, ^(size_t size, uint8_t *hash_result) {
278 if (SOSCircleHashNextGenWithAdditionalPeer(di, circle, peer, hash_result, error)) {
279 signature = SecKeyCopyRawHashSignature(di, hash_result, privKey, error);
280 }
281 });
282
283fail:
284 return signature;
427c49bc
A
285}
286
fa7225c8 287
5c19dc3a
A
288static bool SOSCircleConcordanceRingSign(SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
289 secnotice("Development", "SOSCircleEnsureRingConsistency requires ring signing op", NULL);
290 return true;
291}
292
293
427c49bc
A
294bool SOSCircleVerifySignatureExists(SOSCircleRef circle, SecKeyRef pubKey, CFErrorRef *error) {
295 if(!pubKey) {
296 // TODO ErrorRef
297 secerror("SOSCircleVerifySignatureExists no pubKey");
298 SOSCreateError(kSOSErrorBadFormat, CFSTR("SOSCircleVerifySignatureExists no pubKey"), (error != NULL) ? *error : NULL, error);
299 return false;
300 }
301 CFDataRef signature = SOSCircleGetSignature(circle, pubKey, error);
302 return NULL != signature;
303}
304
305bool SOSCircleVerify(SOSCircleRef circle, SecKeyRef pubKey, CFErrorRef *error) {
306 const struct ccdigest_info *di = ccsha256_di();
307 uint8_t hash_result[di->output_size];
308
309 SOSCircleHash(di, circle, hash_result, error);
310
311 CFDataRef signature = SOSCircleGetSignature(circle, pubKey, error);
312 if(!signature) return false;
313
fa7225c8
A
314 return SecError(SecKeyRawVerify(pubKey, kSecPaddingNone, hash_result, di->output_size,
315 CFDataGetBytePtr(signature), CFDataGetLength(signature)), error, CFSTR("Signature verification failed."));;
427c49bc
A
316}
317
318bool SOSCircleVerifyPeerSigned(SOSCircleRef circle, SOSPeerInfoRef peer, CFErrorRef *error) {
fa7225c8
A
319 bool result = false;
320 SecKeyRef pub_key = SOSPeerInfoCopyPubKey(peer, error);
321 require_quiet(pub_key, fail);
322
323 result = SOSCircleVerify(circle, pub_key, error);
324fail:
427c49bc
A
325 CFReleaseSafe(pub_key);
326 return result;
327}
328
d8f41ccd
A
329static void CFSetRemoveAllPassing(CFMutableSetRef set, bool (^test)(const void *) ){
330 CFMutableArrayRef toBeRemoved = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
427c49bc 331
d8f41ccd
A
332 CFSetForEach(set, ^(const void *value) {
333 if (test(value))
334 CFArrayAppendValue(toBeRemoved, value);
335 });
427c49bc 336
d8f41ccd
A
337 CFArrayForEach(toBeRemoved, ^(const void *value) {
338 CFSetRemoveValue(set, value);
427c49bc 339 });
d8f41ccd 340 CFReleaseNull(toBeRemoved);
427c49bc
A
341}
342
343static void SOSCircleRejectNonValidApplicants(SOSCircleRef circle, SecKeyRef pubkey) {
d8f41ccd
A
344 CFMutableSetRef applicants = SOSCircleCopyApplicants(circle, NULL);
345 CFSetForEach(applicants, ^(const void *value) {
427c49bc
A
346 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
347 if(!SOSPeerInfoApplicationVerify(pi, pubkey, NULL)) {
5c19dc3a 348 CFSetTransferObject(pi, circle->applicants, circle->rejected_applicants);
427c49bc
A
349 }
350 });
d8f41ccd 351 CFReleaseNull(applicants);
427c49bc
A
352}
353
d8f41ccd
A
354static SOSPeerInfoRef SOSCircleCopyPeerInfo(SOSCircleRef circle, CFStringRef peer_id, CFErrorRef *error) {
355 __block SOSPeerInfoRef result = NULL;
356
357 CFSetForEach(circle->peers, ^(const void *value) {
358 if (result == NULL) {
359 SOSPeerInfoRef tpi = (SOSPeerInfoRef)value;
360 if (CFEqual(SOSPeerInfoGetPeerID(tpi), peer_id))
361 result = tpi;
362 }
363 });
427c49bc 364
d8f41ccd
A
365 CFRetainSafe(result);
366 return result;
367}
368
369static bool SOSCircleUpgradePeerInfo(SOSCircleRef circle, SecKeyRef user_approver, SOSFullPeerInfoRef peerinfo) {
370 bool retval = false;
371 SecKeyRef userPubKey = SecKeyCreatePublicFromPrivate(user_approver);
372 SOSPeerInfoRef fpi_pi = SOSFullPeerInfoGetPeerInfo(peerinfo);
373 SOSPeerInfoRef pi = SOSCircleCopyPeerInfo(circle, SOSPeerInfoGetPeerID(fpi_pi), NULL);
374 require_quiet(pi, out);
375 require_quiet(SOSPeerInfoApplicationVerify(pi, userPubKey, NULL), re_sign);
376 CFReleaseNull(userPubKey);
377 CFReleaseNull(pi);
378 return true;
379
380re_sign:
5c19dc3a 381 secnotice("circle", "SOSCircleGenerationSign: Upgraded peer's Application Signature");
d8f41ccd
A
382 SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(peerinfo, NULL);
383 require_quiet(device_key, out);
384 SOSPeerInfoRef new_pi = SOSPeerInfoCopyAsApplication(pi, user_approver, device_key, NULL);
385 if(SOSCircleUpdatePeerInfo(circle, new_pi))
386 retval = true;
387 CFReleaseNull(new_pi);
388 CFReleaseNull(device_key);
389out:
390 CFReleaseNull(userPubKey);
391 CFReleaseNull(pi);
392 return retval;
393}
394
5c19dc3a
A
395static bool SOSCircleEnsureRingConsistency(SOSCircleRef circle, CFErrorRef *error) {
396 secnotice("Development", "SOSCircleEnsureRingConsistency requires ring membership and generation count consistency check", NULL);
397 return true;
398}
399
400bool SOSCircleSignOldStyleResetToOfferingCircle(SOSCircleRef circle, SOSFullPeerInfoRef peerinfo, SecKeyRef user_approver, CFErrorRef *error){
401
402 SecKeyRef ourKey = SOSFullPeerInfoCopyDeviceKey(peerinfo, error);
403 SecKeyRef publicKey = NULL;
404 require_quiet(ourKey, fail);
405
406 // Check if we're using an invalid peerinfo for this op. There are cases where we might not be "upgraded".
407 require_quiet(SOSCircleUpgradePeerInfo(circle, user_approver, peerinfo), fail);
408 SOSCircleRemoveRetired(circle, error); // Prune off retirees since we're signing this one
409 CFSetRemoveAllValues(circle->rejected_applicants); // Dump rejects so we clean them up sometime.
410 publicKey = SecKeyCreatePublicFromPrivate(user_approver);
411 SOSCircleRejectNonValidApplicants(circle, publicKey);
412 require_quiet(SOSCircleEnsureRingConsistency(circle, error), fail);
413 require_quiet(SOSCircleRemoveSignatures(circle, error), fail);
414 require_quiet(SOSCircleSign(circle, user_approver, error), fail);
415 require_quiet(SOSCircleSign(circle, ourKey, error), fail);
416
417 CFReleaseNull(ourKey);
418 CFReleaseNull(publicKey);
419 return true;
420
421fail:
422 CFReleaseNull(ourKey);
423 CFReleaseNull(publicKey);
424 return false;
425}
426
fa7225c8
A
427bool SOSCirclePreGenerationSign(SOSCircleRef circle, SecKeyRef userPubKey, CFErrorRef *error) {
428 bool retval = false;
429
427c49bc 430 SOSCircleRemoveRetired(circle, error); // Prune off retirees since we're signing this one
d8f41ccd 431 CFSetRemoveAllValues(circle->rejected_applicants); // Dump rejects so we clean them up sometime.
fa7225c8 432 SOSCircleRejectNonValidApplicants(circle, userPubKey);
e0e0d90e 433
fa7225c8
A
434 require_quiet(SOSCircleRemoveSignatures(circle, error), errOut);
435
436 retval = true;
e0e0d90e 437
fa7225c8
A
438errOut:
439 return retval;
440
441}
e0e0d90e 442
fa7225c8
A
443static bool SOSCircleGenerationSign_Internal(SOSCircleRef circle, SecKeyRef userKey, SOSFullPeerInfoRef fpi, CFErrorRef *error) {
444 // require_quiet(SOSCircleEnsureRingConsistency(circle, error), fail); Placeholder - this was never implemented
445 bool retval = false;
446 if (SOSCircleCountPeers(circle) != 0) {
447 SecKeyRef ourKey = SOSFullPeerInfoCopyDeviceKey(fpi, error);
448 require_quiet(ourKey, errOut);
449
450 // Check if we're using an invalid peerinfo for this op. There are cases where we might not be "upgraded".
451 require_quiet(SOSCircleUpgradePeerInfo(circle, userKey, fpi), errOut);
452
453 require_quiet(SOSCircleSign(circle, userKey, error), errOut);
454 require_quiet(SOSCircleSign(circle, ourKey, error), errOut);
e0e0d90e
A
455 CFReleaseNull(ourKey);
456 }
fa7225c8 457 retval = true;
427c49bc 458
fa7225c8
A
459errOut:
460 return retval;
427c49bc
A
461}
462
fa7225c8
A
463bool SOSCircleGenerationSign(SOSCircleRef circle, SecKeyRef userKey, SOSFullPeerInfoRef fpi, CFErrorRef *error) {
464 bool retval = false;
465 SecKeyRef publicKey = NULL;
466 publicKey = SecKeyCreatePublicFromPrivate(userKey);
d8f41ccd 467
fa7225c8
A
468 require_quiet(SOSCirclePreGenerationSign(circle, publicKey, error), errOut);
469 SOSCircleGenerationIncrement(circle);
470 require_quiet(SOSCircleGenerationSign_Internal(circle, userKey, fpi, error), errOut);
471 retval = true;
472
473errOut:
474 CFReleaseNull(publicKey);
475 return retval;
476}
d8f41ccd 477
d8f41ccd 478
fa7225c8
A
479static bool SOSCircleGenerationSignWithGenCount(SOSCircleRef circle, SecKeyRef userKey, SOSFullPeerInfoRef fpi, SOSGenCountRef gencount, CFErrorRef *error) {
480 bool retval = false;
481 SOSGenCountRef currentGen = SOSCircleGetGeneration(circle);
482 require_action_quiet(SOSGenerationIsOlder(currentGen, gencount), errOut, SOSCreateError(kSOSErrorReplay, CFSTR("Generation Count for new circle is too old"), NULL, error));
483 require_quiet(SOSCirclePreGenerationSign(circle, userKey, error), errOut);
484 SOSCircleSetGeneration(circle, gencount);
485 require_quiet(SOSCircleGenerationSign_Internal(circle, userKey, fpi, error), errOut);
486 retval = true;
487
488errOut:
489 return retval;
d8f41ccd 490}
427c49bc 491
fa7225c8 492
427c49bc
A
493bool SOSCircleConcordanceSign(SOSCircleRef circle, SOSFullPeerInfoRef peerinfo, CFErrorRef *error) {
494 bool success = false;
495 SecKeyRef ourKey = SOSFullPeerInfoCopyDeviceKey(peerinfo, error);
496 require_quiet(ourKey, exit);
497
498 success = SOSCircleSign(circle, ourKey, error);
5c19dc3a 499 SOSCircleConcordanceRingSign(circle, ourKey, error);
427c49bc
A
500
501exit:
502 CFReleaseNull(ourKey);
503 return success;
504}
505
d8f41ccd 506static inline SOSConcordanceStatus CheckPeerStatus(SOSCircleRef circle, SOSPeerInfoRef peer, SecKeyRef user_public_key, CFErrorRef *error) {
427c49bc 507 SOSConcordanceStatus result = kSOSConcordanceNoPeer;
fa7225c8
A
508 SecKeyRef pubKey = SOSPeerInfoCopyPubKey(peer, error);
509 require_quiet(pubKey, exit);
427c49bc 510
d8f41ccd 511 require_action_quiet(SOSCircleHasActiveValidPeer(circle, peer, user_public_key, error), exit, result = kSOSConcordanceNoPeer);
427c49bc
A
512 require_action_quiet(SOSCircleVerifySignatureExists(circle, pubKey, error), exit, result = kSOSConcordanceNoPeerSig);
513 require_action_quiet(SOSCircleVerify(circle, pubKey, error), exit, result = kSOSConcordanceBadPeerSig);
d8f41ccd 514
427c49bc
A
515 result = kSOSConcordanceTrusted;
516
517exit:
518 CFReleaseNull(pubKey);
519 return result;
520}
521
522static inline SOSConcordanceStatus CombineStatus(SOSConcordanceStatus status1, SOSConcordanceStatus status2)
523{
524 if (status1 == kSOSConcordanceTrusted || status2 == kSOSConcordanceTrusted)
525 return kSOSConcordanceTrusted;
526
527 if (status1 == kSOSConcordanceBadPeerSig || status2 == kSOSConcordanceBadPeerSig)
528 return kSOSConcordanceBadPeerSig;
529
530 if (status1 == kSOSConcordanceNoPeerSig || status2 == kSOSConcordanceNoPeerSig)
531 return kSOSConcordanceNoPeerSig;
532
533 return status1;
534}
535
536static inline bool SOSCircleIsEmpty(SOSCircleRef circle) {
537 return SOSCircleCountPeers(circle) == 0;
538}
539
5c19dc3a 540static inline bool SOSCircleHasDegenerateGeneration(SOSCircleRef deGenCircle){
fa7225c8 541 CFIndex testPtr;
5c19dc3a
A
542 CFNumberRef genCountTest = SOSCircleGetGeneration(deGenCircle);
543 CFNumberGetValue(genCountTest, kCFNumberCFIndexType, &testPtr);
544 return (testPtr== 0);
545}
546
547
548static inline bool SOSCircleIsDegenerateReset(SOSCircleRef deGenCircle){
549 return SOSCircleHasDegenerateGeneration(deGenCircle) && SOSCircleIsEmpty(deGenCircle);
550}
551
552
d8f41ccd 553__unused static inline bool SOSCircleIsResignOffering(SOSCircleRef circle, SecKeyRef pubkey) {
427c49bc
A
554 return SOSCircleCountActiveValidPeers(circle, pubkey) == 1;
555}
556
557static inline SOSConcordanceStatus GetSignersStatus(SOSCircleRef signers_circle, SOSCircleRef status_circle,
558 SecKeyRef user_pubKey, SOSPeerInfoRef exclude, CFErrorRef *error) {
559 CFStringRef excluded_id = exclude ? SOSPeerInfoGetPeerID(exclude) : NULL;
560
561 __block SOSConcordanceStatus status = kSOSConcordanceNoPeer;
d8f41ccd
A
562 SOSCircleForEachActivePeer(signers_circle, ^(SOSPeerInfoRef peer) {
563 SOSConcordanceStatus peerStatus = CheckPeerStatus(status_circle, peer, user_pubKey, error);
427c49bc
A
564
565 if (peerStatus == kSOSConcordanceNoPeerSig &&
566 (CFEqualSafe(SOSPeerInfoGetPeerID(peer), excluded_id) || SOSPeerInfoIsCloudIdentity(peer)))
567 peerStatus = kSOSConcordanceNoPeer;
568
569 status = CombineStatus(status, peerStatus); // TODO: Use multiple error gathering.
570 });
571
572 return status;
573}
574
fa7225c8
A
575// Is current older than proposed?
576bool SOSCircleIsOlderGeneration(SOSCircleRef older, SOSCircleRef newer) {
577 return SOSGenerationIsOlder(older->generation, newer->generation);
427c49bc
A
578}
579
5c19dc3a 580static inline bool SOSCircleIsValidReset(SOSCircleRef current, SOSCircleRef proposed) {
fa7225c8
A
581 bool retval = false;
582 retval = SOSCircleIsEmpty(proposed);
583 require_quiet(retval, errOut);
584 retval = SOSCircleIsOlderGeneration(current, proposed);
585errOut:
586 return retval;
5c19dc3a
A
587}
588
589
427c49bc
A
590bool SOSCircleSharedTrustedPeers(SOSCircleRef current, SOSCircleRef proposed, SOSPeerInfoRef me) {
591 __block bool retval = false;
592 SOSCircleForEachPeer(current, ^(SOSPeerInfoRef peer) {
593 if(!CFEqual(me, peer) && SOSCircleHasPeer(proposed, peer, NULL)) retval = true;
594 });
595 return retval;
596}
597
e3d460c9
A
598static SOSConcordanceStatus GetOfferingStatus(SOSCircleRef circle, SecKeyRef user_pubKey, CFErrorRef *error) {
599 __block SOSConcordanceStatus status = kSOSConcordanceNoPeer;
600 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
601 status = CheckPeerStatus(circle, peer, user_pubKey, error);
602 if(status != kSOSConcordanceTrusted) status = kSOSConcordanceNoPeer;
603 });
604 return status;
605}
606
427c49bc
A
607
608SOSConcordanceStatus SOSCircleConcordanceTrust(SOSCircleRef known_circle, SOSCircleRef proposed_circle,
609 SecKeyRef known_pubkey, SecKeyRef user_pubkey,
5c19dc3a 610 SOSPeerInfoRef me, CFErrorRef *error) {
427c49bc 611 if(user_pubkey == NULL) {
fa7225c8
A
612 SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Concordance with no user public key"), NULL, error);
613 return kSOSConcordanceNoUserKey;
427c49bc 614 }
5c19dc3a
A
615
616 if(SOSCircleIsDegenerateReset(proposed_circle)) {
617 return kSOSConcordanceTrusted;
618 }
427c49bc 619
5c19dc3a 620 if (SOSCircleIsValidReset(known_circle, proposed_circle)) {
427c49bc
A
621 return kSOSConcordanceTrusted;
622 }
623
624 if(!SOSCircleVerifySignatureExists(proposed_circle, user_pubkey, error)) {
fa7225c8 625 SOSCreateError(kSOSErrorBadSignature, CFSTR("No public signature to match current user key"), (error != NULL) ? *error : NULL, error);
427c49bc
A
626 return kSOSConcordanceNoUserSig;
627 }
628
629 if(!SOSCircleVerify(proposed_circle, user_pubkey, error)) {
fa7225c8 630 SOSCreateError(kSOSErrorBadSignature, CFSTR("Bad user public signature"), (error != NULL) ? *error : NULL, error);
5c19dc3a 631 debugDumpCircle(CFSTR("proposed_circle"), proposed_circle);
427c49bc
A
632 return kSOSConcordanceBadUserSig;
633 }
634
5c19dc3a 635 if (SOSCircleIsEmpty(known_circle)) {
427c49bc
A
636 return GetSignersStatus(proposed_circle, proposed_circle, user_pubkey, NULL, error);
637 }
5c19dc3a
A
638
639 if(SOSCircleHasDegenerateGeneration(proposed_circle) && SOSCircleIsOffering(proposed_circle)){
640 return GetSignersStatus(proposed_circle, proposed_circle, user_pubkey, NULL, error);
641 }
642
fa7225c8
A
643 if(SOSCircleIsOlderGeneration(proposed_circle, known_circle)) {
644 SOSCreateError(kSOSErrorReplay, CFSTR("Bad generation - proposed circle gencount is older than known circle gencount"), NULL, error);
5c19dc3a
A
645 debugDumpCircle(CFSTR("isOlderGeneration known_circle"), known_circle);
646 debugDumpCircle(CFSTR("isOlderGeneration proposed_circle"), proposed_circle);
427c49bc
A
647 return kSOSConcordanceGenOld;
648 }
649
5c19dc3a 650 if(SOSCircleIsOffering(proposed_circle)){
e3d460c9 651 return GetOfferingStatus(proposed_circle, user_pubkey, error);
427c49bc 652 }
427c49bc 653
5c19dc3a 654 return GetSignersStatus(known_circle, proposed_circle, user_pubkey, me, error);
427c49bc
A
655}
656
427c49bc
A
657
658static void SOSCircleDestroy(CFTypeRef aObj) {
659 SOSCircleRef c = (SOSCircleRef) aObj;
660
661 CFReleaseNull(c->name);
662 CFReleaseNull(c->generation);
663 CFReleaseNull(c->peers);
664 CFReleaseNull(c->applicants);
665 CFReleaseNull(c->rejected_applicants);
666 CFReleaseNull(c->signatures);
667}
668
5c19dc3a 669static CFMutableStringRef defaultDescription(CFTypeRef aObj){
427c49bc 670 SOSCircleRef c = (SOSCircleRef) aObj;
d8f41ccd
A
671
672 CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0);
5c19dc3a
A
673
674 SOSGenerationCountWithDescription(c->generation, ^(CFStringRef genDescription) {
675 CFStringAppendFormat(description, NULL, CFSTR("<SOSCircle@%p: '%@' %@ P:["), c, c->name, genDescription);
676 });
d8f41ccd
A
677
678 __block CFStringRef separator = CFSTR("");
5c19dc3a 679 SOSCircleForEachActivePeer(c, ^(SOSPeerInfoRef peer) {
d8f41ccd
A
680 CFStringRef sig = NULL;
681 if (SOSCircleVerifyPeerSigned(c, peer, NULL)) {
682 sig = CFSTR("√");
683 } else {
fa7225c8
A
684 SecKeyRef pub_key = SOSPeerInfoCopyPubKey(peer, NULL);
685 CFDataRef signature = pub_key ? SOSCircleGetSignature(c, pub_key, NULL) : NULL;
d8f41ccd
A
686 sig = (signature == NULL) ? CFSTR("-") : CFSTR("?");
687 CFReleaseNull(pub_key);
688 }
5c19dc3a 689
d8f41ccd
A
690 CFStringAppendFormat(description, NULL, CFSTR("%@%@ %@"), separator, peer, sig);
691 separator = CFSTR(",");
692 });
5c19dc3a
A
693
694 //applicants
d8f41ccd
A
695 CFStringAppend(description, CFSTR("], A:["));
696 separator = CFSTR("");
5c19dc3a
A
697 if(CFSetGetCount(c->applicants) == 0 )
698 CFStringAppendFormat(description, NULL, CFSTR("-"));
699 else{
700
701 SOSCircleForEachApplicant(c, ^(SOSPeerInfoRef peer) {
702 CFStringAppendFormat(description, NULL, CFSTR("%@%@"), separator, peer);
703 separator = CFSTR(",");
704 });
705 }
706
707 //rejected
d8f41ccd
A
708 CFStringAppend(description, CFSTR("], R:["));
709 separator = CFSTR("");
5c19dc3a
A
710 if(CFSetGetCount(c->rejected_applicants) == 0)
711 CFStringAppendFormat(description, NULL, CFSTR("-"));
712 else{
713 CFSetForEach(c->rejected_applicants, ^(const void *value) {
714 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
715 CFStringAppendFormat(description, NULL, CFSTR("%@%@"), separator, peer);
716 separator = CFSTR(",");
717 });
718 }
d8f41ccd 719 CFStringAppend(description, CFSTR("]>"));
5c19dc3a
A
720 return description;
721
722}
723static CFMutableStringRef descriptionWithFormatOptions(CFTypeRef aObj, CFDictionaryRef formatOptions){
724 SOSCircleRef c = (SOSCircleRef) aObj;
d8f41ccd 725
5c19dc3a
A
726 CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0);
727
728 if(CFDictionaryContainsKey(formatOptions, CFSTR("SyncD"))) {
729 CFStringRef generationDescription = SOSGenerationCountCopyDescription(c->generation);
730 CFStringAppendFormat(description, NULL, CFSTR("<C: gen:'%@' %@>\n"), generationDescription, c->name);
731 CFReleaseNull(generationDescription);
732 __block CFStringRef separator = CFSTR("\t\t");
733 SOSCircleForEachActivePeer(c, ^(SOSPeerInfoRef peer) {
734 CFStringRef sig = NULL;
735 if (SOSCircleVerifyPeerSigned(c, peer, NULL)) {
736 sig = CFSTR("√");
737 } else {
fa7225c8
A
738 SecKeyRef pub_key = SOSPeerInfoCopyPubKey(peer, NULL);
739 CFDataRef signature = pub_key ? SOSCircleGetSignature(c, pub_key, NULL) : NULL;
5c19dc3a
A
740 sig = (signature == NULL) ? CFSTR("-") : CFSTR("?");
741 CFReleaseNull(pub_key);
742 }
743
744 CFStringAppendFormat(description, formatOptions, CFSTR("%@%@ %@"), separator, peer, sig);
745 separator = CFSTR("\n\t\t");
746 });
747 CFStringAppend(description, CFSTR("\n\t\t<A:["));
748 separator = CFSTR("");
749
750 //applicants list
751 if(CFSetGetCount(c->applicants) == 0 )
752 CFStringAppendFormat(description, NULL, CFSTR("-"));
753 else{
754
755 SOSCircleForEachApplicant(c, ^(SOSPeerInfoRef peer) {
756 CFStringAppendFormat(description, formatOptions, CFSTR("%@A: %@"), separator, peer);
757 separator = CFSTR("\n\t\t\t");
758 });
759 }
760 //rejected list
761 CFStringAppend(description, CFSTR("]> \n\t\t<R:["));
762 separator = CFSTR("");
763 if(CFSetGetCount(c->rejected_applicants) == 0)
764 CFStringAppendFormat(description, NULL, CFSTR("-"));
765 else{
766 CFSetForEach(c->rejected_applicants, ^(const void *value) {
767 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
768 CFStringAppendFormat(description, formatOptions, CFSTR("%@R: %@"), separator, peer);
769 separator = CFSTR("\n\t\t");
770 });
771 }
772 CFStringAppend(description, CFSTR("]>"));
773 }
774
775 else{
776 CFReleaseNull(description);
777 description = defaultDescription(aObj);
778 }
779
780 return description;
781
782}
783
784
785static CFStringRef SOSCircleCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) {
786 SOSCircleRef c = (SOSCircleRef) aObj;
787 SOSCircleAssertStable(c);
788 CFMutableStringRef description = NULL;
789
790 if(formatOptions != NULL){
791 description = descriptionWithFormatOptions(aObj, formatOptions);
792 }
793 else{
794 description = defaultDescription(aObj);
795 }
d8f41ccd 796 return description;
427c49bc
A
797}
798
799CFStringRef SOSCircleGetName(SOSCircleRef circle) {
800 assert(circle);
801 assert(circle->name);
802 return circle->name;
803}
804
805const char *SOSCircleGetNameC(SOSCircleRef circle) {
806 CFStringRef name = SOSCircleGetName(circle);
807 if (!name)
808 return strdup("");
809 return CFStringToCString(name);
810}
811
e0e0d90e 812SOSGenCountRef SOSCircleGetGeneration(SOSCircleRef circle) {
427c49bc
A
813 assert(circle);
814 assert(circle->generation);
815 return circle->generation;
816}
817
e0e0d90e 818void SOSCircleSetGeneration(SOSCircleRef circle, SOSGenCountRef gencount) {
5c19dc3a
A
819 assert(circle);
820 CFReleaseNull(circle->generation);
821 circle->generation = CFRetainSafe(gencount);
822}
823
427c49bc 824int64_t SOSCircleGetGenerationSint(SOSCircleRef circle) {
e0e0d90e
A
825 SOSGenCountRef gen = SOSCircleGetGeneration(circle);
826 return SOSGetGenerationSint(gen);
427c49bc
A
827}
828
e0e0d90e
A
829void SOSCircleGenerationSetValue(SOSCircleRef circle, int64_t value) {
830 CFAssignRetained(circle->generation, SOSGenerationCreateWithValue(value));
d8f41ccd
A
831}
832
d8f41ccd 833void SOSCircleGenerationIncrement(SOSCircleRef circle) {
e0e0d90e
A
834 SOSGenCountRef old = circle->generation;
835 circle->generation = SOSGenerationIncrementAndCreate(old);
836 CFReleaseNull(old);
427c49bc
A
837}
838
839int SOSCircleCountPeers(SOSCircleRef circle) {
840 SOSCircleAssertStable(circle);
841 __block int count = 0;
842 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
843 ++count;
844 });
845 return count;
846}
847
848int SOSCircleCountActivePeers(SOSCircleRef circle) {
849 SOSCircleAssertStable(circle);
850 __block int count = 0;
851 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
852 ++count;
853 });
854 return count;
855}
856
857int SOSCircleCountActiveValidPeers(SOSCircleRef circle, SecKeyRef pubkey) {
858 SOSCircleAssertStable(circle);
859 __block int count = 0;
860 SOSCircleForEachActiveValidPeer(circle, pubkey, ^(SOSPeerInfoRef peer) {
861 ++count;
862 });
863 return count;
864}
865
fa7225c8
A
866int SOSCircleCountValidSyncingPeers(SOSCircleRef circle, SecKeyRef pubkey) {
867 SOSCircleAssertStable(circle);
868 __block int count = 0;
869 SOSCircleForEachValidSyncingPeer(circle, pubkey, ^(SOSPeerInfoRef peer) {
870 ++count;
871 });
872 return count;
873
874}
875
427c49bc
A
876int SOSCircleCountRetiredPeers(SOSCircleRef circle) {
877 SOSCircleAssertStable(circle);
878 __block int count = 0;
879 SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
880 ++count;
881 });
882 return count;
883}
884
885int SOSCircleCountApplicants(SOSCircleRef circle) {
886 SOSCircleAssertStable(circle);
887
d8f41ccd 888 return (int)CFSetGetCount(circle->applicants);
427c49bc
A
889}
890
891bool SOSCircleHasApplicant(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
892 SOSCircleAssertStable(circle);
893
d8f41ccd 894 return CFSetContainsValue(circle->applicants, peerInfo);
427c49bc
A
895}
896
d8f41ccd 897CFMutableSetRef SOSCircleCopyApplicants(SOSCircleRef circle, CFAllocatorRef allocator) {
427c49bc
A
898 SOSCircleAssertStable(circle);
899
d8f41ccd 900 return CFSetCreateMutableCopy(allocator, 0, circle->applicants);
427c49bc
A
901}
902
903int SOSCircleCountRejectedApplicants(SOSCircleRef circle) {
904 SOSCircleAssertStable(circle);
905
d8f41ccd 906 return (int)CFSetGetCount(circle->rejected_applicants);
427c49bc
A
907}
908
909bool SOSCircleHasRejectedApplicant(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
910 SOSCircleAssertStable(circle);
d8f41ccd 911 return CFSetContainsValue(circle->rejected_applicants, peerInfo);
427c49bc
A
912}
913
914SOSPeerInfoRef SOSCircleCopyRejectedApplicant(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
915 SOSCircleAssertStable(circle);
d8f41ccd 916 return CFRetainSafe((SOSPeerInfoRef)CFSetGetValue(circle->rejected_applicants, peerInfo));
427c49bc
A
917}
918
919CFMutableArrayRef SOSCircleCopyRejectedApplicants(SOSCircleRef circle, CFAllocatorRef allocator) {
920 SOSCircleAssertStable(circle);
921
d8f41ccd 922 return CFSetCopyValuesCFArray(circle->rejected_applicants);
427c49bc
A
923}
924
427c49bc
A
925bool SOSCircleHasPeerWithID(SOSCircleRef circle, CFStringRef peerid, CFErrorRef *error) {
926 SOSCircleAssertStable(circle);
927 __block bool found = false;
928 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
929 if(peerid && peer && CFEqualSafe(peerid, SOSPeerInfoGetPeerID(peer))) found = true;
930 });
931 return found;
932}
933
d8f41ccd
A
934SOSPeerInfoRef SOSCircleCopyPeerWithID(SOSCircleRef circle, CFStringRef peerid, CFErrorRef *error) {
935 SOSCircleAssertStable(circle);
5c19dc3a 936 __block SOSPeerInfoRef found = NULL;
d8f41ccd
A
937 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
938 if(peerid && peer && CFEqualSafe(peerid, SOSPeerInfoGetPeerID(peer))) found = peer;
939 });
5c19dc3a 940 return found ? SOSPeerInfoCreateCopy(kCFAllocatorDefault, found, NULL) : NULL;
d8f41ccd
A
941}
942
427c49bc 943bool SOSCircleHasPeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
949d2ff0 944 if(!peerInfo) return false;
427c49bc
A
945 return SOSCircleHasPeerWithID(circle, SOSPeerInfoGetPeerID(peerInfo), error);
946}
947
948bool SOSCircleHasActivePeerWithID(SOSCircleRef circle, CFStringRef peerid, CFErrorRef *error) {
949 SOSCircleAssertStable(circle);
950 __block bool found = false;
951 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
952 if(peerid && peer && CFEqualSafe(peerid, SOSPeerInfoGetPeerID(peer))) found = true;
953 });
954 return found;
955}
956
957bool SOSCircleHasActivePeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
958 if(!peerInfo) return false;
959 return SOSCircleHasActivePeerWithID(circle, SOSPeerInfoGetPeerID(peerInfo), error);
960}
961
d8f41ccd
A
962bool SOSCircleHasActiveValidPeerWithID(SOSCircleRef circle, CFStringRef peerid, SecKeyRef user_public_key, CFErrorRef *error) {
963 SOSCircleAssertStable(circle);
964 __block bool found = false;
965 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
966 if(peerid && peer && CFEqualSafe(peerid, SOSPeerInfoGetPeerID(peer)) && SOSPeerInfoApplicationVerify(peer, user_public_key, NULL)) found = true;
967 });
968 return found;
969}
970
971bool SOSCircleHasActiveValidPeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, SecKeyRef user_public_key, CFErrorRef *error) {
972 if(!peerInfo) return false;
973 return SOSCircleHasActiveValidPeerWithID(circle, SOSPeerInfoGetPeerID(peerInfo), user_public_key, error);
974}
427c49bc
A
975
976
977bool SOSCircleResetToEmpty(SOSCircleRef circle, CFErrorRef *error) {
d8f41ccd
A
978 CFSetRemoveAllValues(circle->applicants);
979 CFSetRemoveAllValues(circle->rejected_applicants);
980 CFSetRemoveAllValues(circle->peers);
427c49bc 981 CFDictionaryRemoveAllValues(circle->signatures);
e0e0d90e
A
982 SOSGenCountRef oldGen = SOSCircleGetGeneration(circle);
983 SOSGenCountRef newGen = SOSGenerationCreateWithBaseline(oldGen);
984 SOSCircleSetGeneration(circle, newGen);
fa7225c8
A
985 CFReleaseSafe(newGen);
986 return true;
987}
988
989bool SOSCircleResetToEmptyWithSameGeneration(SOSCircleRef circle, CFErrorRef *error) {
990 SOSGenCountRef gen = SOSGenerationCopy(SOSCircleGetGeneration(circle));
991 SOSCircleResetToEmpty(circle, error);
992 SOSCircleSetGeneration(circle, gen);
427c49bc
A
993 return true;
994}
995
996bool SOSCircleResetToOffering(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error){
997
998 return SOSCircleResetToEmpty(circle, error)
999 && SOSCircleRequestAdmission(circle, user_privkey, requestor, error)
1000 && SOSCircleAcceptRequest(circle, user_privkey, requestor, SOSFullPeerInfoGetPeerInfo(requestor), error);
1001}
1002
d8f41ccd
A
1003bool SOSCircleRemoveRetired(SOSCircleRef circle, CFErrorRef *error) {
1004 CFSetRemoveAllPassing(circle->peers, ^ bool (const void *element) {
427c49bc
A
1005 SOSPeerInfoRef peer = (SOSPeerInfoRef) element;
1006
1007 return SOSPeerInfoIsRetirementTicket(peer);
1008 });
1009
d8f41ccd 1010 return true;
427c49bc
A
1011}
1012
949d2ff0 1013static bool SOSCircleRecordAdmissionRequest(SOSCircleRef circle, SecKeyRef user_pubkey, SOSPeerInfoRef requestorPeerInfo, CFErrorRef *error) {
427c49bc
A
1014 SOSCircleAssertStable(circle);
1015
949d2ff0 1016 bool isPeer = SOSCircleHasPeer(circle, requestorPeerInfo, error);
427c49bc
A
1017
1018 require_action_quiet(!isPeer, fail, SOSCreateError(kSOSErrorAlreadyPeer, CFSTR("Cannot request admission when already a peer"), NULL, error));
1019
fa7225c8 1020 // This adds to applicants and will take off rejected if it's there.
5c19dc3a 1021 CFSetTransferObject(requestorPeerInfo, circle->rejected_applicants, circle->applicants);
427c49bc
A
1022
1023 return true;
1024
1025fail:
1026 return false;
1027
1028}
1029
949d2ff0 1030bool SOSCircleRequestReadmission(SOSCircleRef circle, SecKeyRef user_pubkey, SOSPeerInfoRef peer, CFErrorRef *error) {
427c49bc
A
1031 bool success = false;
1032
427c49bc 1033 require_quiet(SOSPeerInfoApplicationVerify(peer, user_pubkey, error), fail);
949d2ff0 1034 success = SOSCircleRecordAdmissionRequest(circle, user_pubkey, peer, error);
427c49bc
A
1035fail:
1036 return success;
1037}
1038
1039bool SOSCircleRequestAdmission(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
1040 bool success = false;
1041
1042 SecKeyRef user_pubkey = SecKeyCreatePublicFromPrivate(user_privkey);
1043 require_action_quiet(user_pubkey, fail, SOSCreateError(kSOSErrorBadKey, CFSTR("No public key for key"), NULL, error));
1044
1045 require(SOSFullPeerInfoPromoteToApplication(requestor, user_privkey, error), fail);
1046
949d2ff0 1047 success = SOSCircleRecordAdmissionRequest(circle, user_pubkey, SOSFullPeerInfoGetPeerInfo(requestor), error);
427c49bc
A
1048fail:
1049 CFReleaseNull(user_pubkey);
1050 return success;
1051}
1052
5c19dc3a
A
1053static bool sosCircleUpdatePeerInfoSet(CFMutableSetRef theSet, SOSPeerInfoRef replacement_peer_info) {
1054 CFTypeRef old = NULL;
1055 if(!replacement_peer_info) return false;
1056 if(!(old = CFSetGetValue(theSet, replacement_peer_info))) return false;
1057 if(CFEqualSafe(old, replacement_peer_info)) return false;
1058 CFSetReplaceValue(theSet, replacement_peer_info);
1059 return true;
1060}
427c49bc
A
1061
1062bool SOSCircleUpdatePeerInfo(SOSCircleRef circle, SOSPeerInfoRef replacement_peer_info) {
5c19dc3a
A
1063 if(sosCircleUpdatePeerInfoSet(circle->peers, replacement_peer_info)) return true;
1064 if(sosCircleUpdatePeerInfoSet(circle->applicants, replacement_peer_info)) return true;
1065 if(sosCircleUpdatePeerInfoSet(circle->rejected_applicants, replacement_peer_info)) return true;
1066 return false;
427c49bc 1067}
5c19dc3a 1068
e0e0d90e 1069static bool SOSCircleRemovePeerInternal(SOSCircleRef circle, SOSFullPeerInfoRef requestor, SOSPeerInfoRef peer_to_remove, CFErrorRef *error) {
427c49bc 1070 SOSPeerInfoRef requestor_peer_info = SOSFullPeerInfoGetPeerInfo(requestor);
e0e0d90e
A
1071
1072 if (SOSCircleHasPeer(circle, peer_to_remove, NULL)) {
1073 if (!SOSCircleHasPeer(circle, requestor_peer_info, error)) {
1074 SOSCreateError(kSOSErrorAlreadyPeer, CFSTR("Must be peer to remove peer"), NULL, error);
1075 return false;
1076 }
1077 CFSetRemoveValue(circle->peers, peer_to_remove);
1078 }
1079
427c49bc
A
1080 if (SOSCircleHasApplicant(circle, peer_to_remove, error)) {
1081 return SOSCircleRejectRequest(circle, requestor, peer_to_remove, error);
1082 }
1083
e0e0d90e
A
1084 return true;
1085}
1086
1087bool SOSCircleRemovePeers(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFSetRef peersToRemove, CFErrorRef *error) {
427c49bc 1088
e0e0d90e 1089 bool success = false;
427c49bc 1090
e0e0d90e
A
1091 __block bool removed_all = true;
1092 CFSetForEach(peersToRemove, ^(const void *value) {
1093 SOSPeerInfoRef peerInfo = asSOSPeerInfo(value);
1094 if (peerInfo) {
1095 removed_all &= SOSCircleRemovePeerInternal(circle, requestor, peerInfo, error);
1096 }
1097 });
427c49bc 1098
e0e0d90e
A
1099 require_quiet(removed_all, exit);
1100
1101 require_quiet(SOSCircleGenerationSign(circle, user_privkey, requestor, error), exit);
1102
1103 success = true;
1104
1105exit:
1106 return success;
1107}
1108
1109bool SOSCircleRemovePeer(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, SOSPeerInfoRef peer_to_remove, CFErrorRef *error) {
1110 bool success = false;
1111
1112 require_quiet(SOSCircleRemovePeerInternal(circle, requestor, peer_to_remove, error), exit);
1113
1114 require_quiet(SOSCircleGenerationSign(circle, user_privkey, requestor, error), exit);
1115
1116 success = true;
1117exit:
1118 return success;
427c49bc
A
1119}
1120
1121bool SOSCircleAcceptRequest(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef device_approver, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
1122 SOSCircleAssertStable(circle);
1123
427c49bc
A
1124 SecKeyRef publicKey = NULL;
1125 bool result = false;
1126
d8f41ccd 1127 require_action_quiet(CFSetContainsValue(circle->applicants, peerInfo), fail,
427c49bc
A
1128 SOSCreateError(kSOSErrorNotApplicant, CFSTR("Cannot accept non-applicant"), NULL, error));
1129
1130 publicKey = SecKeyCreatePublicFromPrivate(user_privkey);
1131 require_quiet(SOSPeerInfoApplicationVerify(peerInfo, publicKey, error), fail);
1132
5c19dc3a 1133 CFSetTransferObject(peerInfo, circle->applicants, circle->peers);
427c49bc 1134
427c49bc 1135 result = SOSCircleGenerationSign(circle, user_privkey, device_approver, error);
427c49bc
A
1136
1137fail:
1138 CFReleaseNull(publicKey);
1139 return result;
1140}
1141
1142bool SOSCircleWithdrawRequest(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
1143 SOSCircleAssertStable(circle);
1144
d8f41ccd 1145 CFSetRemoveValue(circle->applicants, peerInfo);
427c49bc 1146
427c49bc
A
1147 return true;
1148}
1149
1150bool SOSCircleRemoveRejectedPeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
1151 SOSCircleAssertStable(circle);
1152
d8f41ccd 1153 CFSetRemoveValue(circle->rejected_applicants, peerInfo);
427c49bc
A
1154
1155 return true;
1156}
1157
1158
1159bool SOSCircleRejectRequest(SOSCircleRef circle, SOSFullPeerInfoRef device_rejector,
1160 SOSPeerInfoRef peerInfo, CFErrorRef *error) {
1161 SOSCircleAssertStable(circle);
1162
1163 if (CFEqual(SOSPeerInfoGetPeerID(peerInfo), SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(device_rejector))))
1164 return SOSCircleWithdrawRequest(circle, peerInfo, error);
1165
d8f41ccd 1166 if (!CFSetContainsValue(circle->applicants, peerInfo)) {
427c49bc
A
1167 SOSCreateError(kSOSErrorNotApplicant, CFSTR("Cannot reject non-applicant"), NULL, error);
1168 return false;
1169 }
427c49bc 1170
5c19dc3a 1171 CFSetTransferObject(peerInfo, circle->applicants, circle->rejected_applicants);
427c49bc
A
1172
1173 // TODO: Maybe we sign the rejection with device_rejector.
1174
1175 return true;
1176}
1177
1178bool SOSCircleAcceptRequests(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef device_approver,
1179 CFErrorRef *error) {
1180 // Returns true if we accepted someone and therefore have to post the circle back to KVS
1181 __block bool result = false;
1182
1183 SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) {
5c19dc3a
A
1184 if (!SOSCircleAcceptRequest(circle, user_privkey, device_approver, peer, error)) {
1185 secnotice("circle", "error in SOSCircleAcceptRequest\n");
1186 } else {
427c49bc
A
1187 secnotice("circle", "Accepted peer: %@", peer);
1188 result = true;
1189 }
1190 });
1191
1192 if (result) {
1193 SOSCircleGenerationSign(circle, user_privkey, device_approver, error);
1194 secnotice("circle", "Countersigned accepted requests");
1195 }
1196
1197 return result;
1198}
1199
1200bool SOSCirclePeerSigUpdate(SOSCircleRef circle, SecKeyRef userPrivKey, SOSFullPeerInfoRef fpi,
1201 CFErrorRef *error) {
1202 // Returns true if we accepted someone and therefore have to post the circle back to KVS
1203 __block bool result = false;
1204 SecKeyRef userPubKey = SecKeyCreatePublicFromPrivate(userPrivKey);
1205
1206 // We're going to remove any applicants using a mismatched user key.
1207 SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) {
1208 if(!SOSPeerInfoApplicationVerify(peer, userPubKey, NULL)) {
1209 if(!SOSCircleRejectRequest(circle, fpi, peer, NULL)) {
1210 // do we care?
1211 }
1212 }
1213 });
1214
1215 result = SOSCircleUpdatePeerInfo(circle, SOSFullPeerInfoGetPeerInfo(fpi));
1216
1217 if (result) {
1218 SOSCircleGenerationSign(circle, userPrivKey, fpi, error);
1219 secnotice("circle", "Generation signed updated signatures on peerinfo");
1220 }
1221
1222 return result;
1223}
1224
427c49bc
A
1225static inline void SOSCircleForEachPeerMatching(SOSCircleRef circle,
1226 void (^action)(SOSPeerInfoRef peer),
1227 bool (^condition)(SOSPeerInfoRef peer)) {
d8f41ccd 1228 CFSetForEach(circle->peers, ^(const void *value) {
427c49bc
A
1229 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1230 if (condition(peer))
1231 action(peer);
1232 });
1233}
1234
d8f41ccd
A
1235static inline bool isHiddenPeer(SOSPeerInfoRef peer) {
1236 return SOSPeerInfoIsRetirementTicket(peer) || SOSPeerInfoIsCloudIdentity(peer);
1237}
1238
427c49bc
A
1239void SOSCircleForEachPeer(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
1240 SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
d8f41ccd 1241 return !isHiddenPeer(peer);
427c49bc
A
1242 });
1243}
1244
1245void SOSCircleForEachRetiredPeer(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
1246 SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1247 return SOSPeerInfoIsRetirementTicket(peer);
1248 });
1249}
1250
1251void SOSCircleForEachActivePeer(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
1252 SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1253 return true;
1254 });
1255}
1256
1257void SOSCircleForEachActiveValidPeer(SOSCircleRef circle, SecKeyRef user_public_key, void (^action)(SOSPeerInfoRef peer)) {
1258 SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1259 return SOSPeerInfoApplicationVerify(peer, user_public_key, NULL);
1260 });
1261}
1262
5c19dc3a
A
1263void SOSCircleForEachValidPeer(SOSCircleRef circle, SecKeyRef user_public_key, void (^action)(SOSPeerInfoRef peer)) {
1264 SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1265 return !isHiddenPeer(peer) && SOSPeerInfoApplicationVerify(peer, user_public_key, NULL);
1266 });
1267}
1268
fa7225c8
A
1269void SOSCircleForEachValidSyncingPeer(SOSCircleRef circle, SecKeyRef user_public_key, void (^action)(SOSPeerInfoRef peer)) {
1270 SOSCircleForEachValidPeer(circle, user_public_key, action);
1271}
1272
427c49bc 1273void SOSCircleForEachApplicant(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
d8f41ccd 1274 CFSetForEach(circle->applicants, ^(const void*value) { action((SOSPeerInfoRef) value); } );
427c49bc
A
1275}
1276
1277
d8f41ccd 1278CFMutableSetRef SOSCircleCopyPeers(SOSCircleRef circle, CFAllocatorRef allocator) {
427c49bc
A
1279 SOSCircleAssertStable(circle);
1280
d8f41ccd 1281 CFMutableSetRef result = CFSetCreateMutableForSOSPeerInfosByID(allocator);
427c49bc
A
1282
1283 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
d8f41ccd 1284 CFSetAddValue(result, peer);
427c49bc
A
1285 });
1286
1287 return result;
1288}
1289
d8f41ccd 1290bool SOSCircleAppendConcurringPeers(SOSCircleRef circle, CFMutableArrayRef appendHere, CFErrorRef *error) {
427c49bc 1291 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
d8f41ccd
A
1292 CFErrorRef localError = NULL;
1293 if (SOSCircleVerifyPeerSigned(circle, peer, &localError)) {
5c19dc3a
A
1294 SOSPeerInfoRef peerInfo = SOSPeerInfoCreateCopy(kCFAllocatorDefault, peer, error);
1295 CFArrayAppendValue(appendHere, peerInfo);
1296 CFRelease(peerInfo);
427c49bc 1297 } else if (error != NULL) {
d8f41ccd 1298 secerror("Error checking concurrence: %@", localError);
427c49bc 1299 }
d8f41ccd 1300 CFReleaseNull(localError);
427c49bc
A
1301 });
1302
d8f41ccd 1303 return true;
427c49bc
A
1304}
1305
d8f41ccd
A
1306CFMutableArrayRef SOSCircleCopyConcurringPeers(SOSCircleRef circle, CFErrorRef* error) {
1307 SOSCircleAssertStable(circle);
427c49bc 1308
d8f41ccd 1309 CFMutableArrayRef concurringPeers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
427c49bc 1310
d8f41ccd
A
1311 if (!SOSCircleAppendConcurringPeers(circle, concurringPeers, error))
1312 CFReleaseNull(concurringPeers);
427c49bc 1313
d8f41ccd 1314 return concurringPeers;
427c49bc
A
1315}
1316
5c19dc3a 1317SOSFullPeerInfoRef SOSCircleCopyiCloudFullPeerInfoRef(SOSCircleRef circle, CFErrorRef *error) {
427c49bc 1318 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
5c19dc3a 1319 __block CFErrorRef searchError = NULL;
427c49bc
A
1320 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
1321 if (SOSPeerInfoIsCloudIdentity(peer)) {
1322 if (cloud_full_peer == NULL) {
5c19dc3a
A
1323 if (searchError) {
1324 secerror("More than one cloud identity found, first had error, trying new one.");
1325 }
1326 CFReleaseNull(searchError);
1327 cloud_full_peer = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, &searchError);
1328 if (!cloud_full_peer) {
1329 secnotice("icloud-identity", "Failed to make FullPeer for iCloud Identity: %@ (%@)", cloud_full_peer, searchError);
427c49bc 1330 }
427c49bc 1331 } else {
5c19dc3a 1332 secerror("Additional cloud identity found in circle after successful creation: %@", circle);
427c49bc
A
1333 }
1334 }
1335 });
5c19dc3a
A
1336 // If we didn't find one at all, report the error.
1337 if (cloud_full_peer == NULL && searchError == NULL) {
1338 SOSErrorCreate(kSOSErrorNoiCloudPeer, &searchError, NULL, CFSTR("No iCloud identity PeerInfo found in circle"));
1339 secnotice("icloud-identity", "No iCloud identity PeerInfo found in circle");
1340 }
1341 if (error) {
1342 CFTransferRetained(*error, searchError);
1343 }
1344 CFReleaseNull(searchError);
427c49bc
A
1345 return cloud_full_peer;
1346}
5c19dc3a
A
1347
1348void debugDumpCircle(CFStringRef message, SOSCircleRef circle) {
1349 CFErrorRef error;
1350
1351 secinfo("circledebug", "%@: %@", message, circle);
1352 if (!circle)
1353 return;
1354
1355 CFDataRef derdata = SOSCircleCopyEncodedData(circle, kCFAllocatorDefault, &error);
1356 if (derdata) {
1357 CFStringRef hex = CFDataCopyHexString(derdata);
1358 secinfo("circledebug", "Full contents: %@", hex);
1359 if (hex) CFRelease(hex);
1360 CFRelease(derdata);
1361 }
1362}
fa7225c8
A
1363
1364bool SOSCircleAcceptPeerFromHSA2(SOSCircleRef circle, SecKeyRef userKey, SOSGenCountRef gencount, SecKeyRef pPubKey, CFDataRef signature, SOSFullPeerInfoRef fpi, CFErrorRef *error) {
1365 SOSPeerInfoRef peerInfo = SOSFullPeerInfoGetPeerInfo(fpi);
1366 CFSetAddValue(circle->peers, peerInfo);
1367 // Gen sign first, then add signature from our approver - remember gensign removes all existing sigs.
1368 return SOSCircleGenerationSignWithGenCount(circle, userKey, fpi, gencount, error) && SOSCircleSetSignature(circle, pPubKey, signature, error) && SOSCircleVerify(circle, pPubKey, error);
1369}
1370
1371
1372/*
1373 ccstatus: Not in Circle (1)
1374 Account user public is trusted
1375 Generation Count: [2016-05-19 15:53 4]
1376
1377 */
1378
1379void SOSCircleLogState(char *category, SOSCircleRef circle, SecKeyRef pubKey, CFStringRef myPID) {
1380 if(!circle) return;
1381 CFStringRef genString = SOSGenerationCountCopyDescription(SOSCircleGetGeneration(circle));
1382 char sigchr = 'v';
1383 if(pubKey && SOSCircleVerify(circle, pubKey, NULL)) {
1384 sigchr = 'V';
1385 }
1386 secnotice(category, "CIRCLE: [%20@] UserSigned: %c", genString, sigchr);
1387 if(CFSetGetCount(circle->peers) == 0 )
1388 secnotice(category, "Peers In Circle: None");
1389 else{
1390 secnotice(category, "Peers In Circle:");
1391 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
1392 char sigchr = 'v';
1393 if (SOSCircleVerifyPeerSigned(circle, peer, NULL)) {
1394 sigchr = 'V';
1395 }
1396 SOSPeerInfoLogState(category, peer, pubKey, myPID, sigchr);
1397 });
1398 }
1399
1400 //applicants
1401 if(CFSetGetCount(circle->applicants) == 0 )
1402 secnotice(category, "Applicants To Circle: None");
1403 else{
1404 secnotice(category, "Applicants To Circle:");
1405
1406 SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) {
1407 SOSPeerInfoLogState(category, peer, pubKey, myPID, 'v');
1408 });
1409 }
1410
1411 //rejected
1412 if(CFSetGetCount(circle->rejected_applicants) == 0)
1413 secnotice(category, "Rejected Applicants To Circle: None");
1414 else{
1415 secnotice(category, "Rejected Applicants To Circle:");
1416 CFSetForEach(circle->rejected_applicants, ^(const void *value) {
1417 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1418 SOSPeerInfoLogState(category, peer, pubKey, myPID, 'v');
1419 });
1420 }
1421 CFReleaseNull(genString);
1422}