2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 * SOSCircle.c - Implementation of the secure object syncing transport
29 #include <AssertMacros.h>
31 #include <CoreFoundation/CFArray.h>
32 #include "keychain/SecureObjectSync/SOSTypes.h"
33 #include "keychain/SecureObjectSync/SOSPeerInfo.h"
34 #include "keychain/SecureObjectSync/SOSPeer.h"
35 #include "keychain/SecureObjectSync/SOSCircle.h"
36 #include "keychain/SecureObjectSync/SOSCloudCircle.h"
37 #include "keychain/SecureObjectSync/SOSCloudCircleInternal.h"
38 #include "keychain/SecureObjectSync/SOSInternal.h"
39 #include "keychain/SecureObjectSync/SOSEnginePriv.h"
40 #include "keychain/SecureObjectSync/SOSPeerInfoInternal.h"
41 #include "keychain/SecureObjectSync/SOSGenCount.h"
42 #include "keychain/SecureObjectSync/SOSPeerInfoCollections.h"
43 #include <CoreFoundation/CoreFoundation.h>
44 #include <Security/SecFramework.h>
46 #include <Security/SecKey.h>
47 #include <Security/SecKeyPriv.h>
48 #include <utilities/SecBuffer.h>
50 #include <utilities/SecCFWrappers.h>
51 #include <utilities/SecCFError.h>
53 #include "keychain/SecureObjectSync/SOSCirclePriv.h"
55 //#include "ckdUtilities.h"
57 #include <corecrypto/ccder.h>
58 #include <corecrypto/ccdigest.h>
59 #include <corecrypto/ccsha2.h>
62 #include <utilities/simulatecrash_assert.h>
64 CFGiblisWithCompareFor(SOSCircle
);
66 SOSCircleRef
SOSCircleCreate(CFAllocatorRef allocator
, CFStringRef name
, CFErrorRef
*error
) {
67 SOSCircleRef c
= CFTypeAllocate(SOSCircle
, struct __OpaqueSOSCircle
, allocator
);
70 c
->name
= CFStringCreateCopy(allocator
, name
);
71 c
->generation
= SOSGenerationCreate();
72 c
->peers
= CFSetCreateMutableForSOSPeerInfosByID(allocator
);
73 c
->applicants
= CFSetCreateMutableForSOSPeerInfosByID(allocator
);
74 c
->rejected_applicants
= CFSetCreateMutableForSOSPeerInfosByID(allocator
);
75 c
->signatures
= CFDictionaryCreateMutableForCFTypes(allocator
);
79 static CFMutableSetRef
CFSetOfPeerInfoDeepCopy(CFAllocatorRef allocator
, CFSetRef peerInfoSet
)
81 __block CFMutableSetRef result
= CFSetCreateMutableForSOSPeerInfosByID(allocator
);
82 CFSetForEach(peerInfoSet
, ^(const void *value
) {
83 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) value
;
84 CFErrorRef localError
= NULL
;
85 SOSPeerInfoRef copiedPeer
= SOSPeerInfoCreateCopy(allocator
, pi
, &localError
);
87 CFSetAddValue(result
, copiedPeer
);
89 secerror("Failed to copy peer: %@ (%@)", pi
, localError
);
91 CFReleaseSafe(copiedPeer
);
92 CFReleaseSafe(localError
);
97 SOSCircleRef
SOSCircleCopyCircle(CFAllocatorRef allocator
, SOSCircleRef otherCircle
, CFErrorRef
*error
)
99 SOSCircleRef c
= CFTypeAllocate(SOSCircle
, struct __OpaqueSOSCircle
, allocator
);
102 c
->name
= CFStringCreateCopy(allocator
, otherCircle
->name
);
103 c
->generation
= SOSGenerationCopy(otherCircle
->generation
);
105 c
->peers
= CFSetOfPeerInfoDeepCopy(allocator
, otherCircle
->peers
);
106 c
->applicants
= CFSetOfPeerInfoDeepCopy(allocator
, otherCircle
->applicants
);
107 c
->rejected_applicants
= CFSetOfPeerInfoDeepCopy(allocator
, otherCircle
->rejected_applicants
);
109 c
->signatures
= CFDictionaryCreateMutableCopy(allocator
, 0, otherCircle
->signatures
);
114 static Boolean
SOSCircleCompare(CFTypeRef lhs
, CFTypeRef rhs
) {
115 if (CFGetTypeID(lhs
) != SOSCircleGetTypeID()
116 || CFGetTypeID(rhs
) != SOSCircleGetTypeID())
119 SOSCircleRef left
= SOSCircleConvertAndAssertStable(lhs
);
120 SOSCircleRef right
= SOSCircleConvertAndAssertStable(rhs
);
122 // TODO: we should be doing set equality for peers and applicants.
123 return NULL
!= left
&& NULL
!= right
124 && CFEqualSafe(left
->generation
, right
->generation
)
125 && SOSPeerInfoSetContainsIdenticalPeers(left
->peers
, right
->peers
)
126 && SOSPeerInfoSetContainsIdenticalPeers(left
->applicants
, right
->applicants
)
127 && SOSPeerInfoSetContainsIdenticalPeers(left
->rejected_applicants
, right
->rejected_applicants
)
128 && CFEqualSafe(left
->signatures
, right
->signatures
);
131 static CFMutableArrayRef
CFSetCopyValuesCFArray(CFSetRef set
)
133 CFIndex count
= CFSetGetCount(set
);
135 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
137 const void * values
[count
];
138 CFSetGetValues(set
, values
);
139 for (int current
= 0; current
< count
; ++current
) {
140 CFArrayAppendValue(result
, values
[current
]);
147 static bool SOSCircleDigestArray(const struct ccdigest_info
*di
, CFMutableArrayRef array
, void *hash_result
, CFErrorRef
*error
)
149 __block
bool success
= true;
150 ccdigest_di_decl(di
, array_digest
);
151 void * a_digest
= (void * )array_digest
;
153 ccdigest_init(di
, array_digest
);
154 CFArraySortValues(array
, CFRangeMake(0, CFArrayGetCount(array
)), SOSPeerInfoCompareByID
, (void *)SOSPeerCmpPubKeyHash
);
155 CFArrayForEach(array
, ^(const void *peer
) {
156 if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes((SOSPeerInfoRef
)peer
, di
, a_digest
, error
))
159 ccdigest_final(di
, array_digest
, hash_result
);
164 static bool SOSCircleDigestSet(const struct ccdigest_info
*di
, CFMutableSetRef set
, void *hash_result
, CFErrorRef
*error
)
166 CFMutableArrayRef values
= CFSetCopyValuesCFArray(set
);
168 bool result
= SOSCircleDigestArray(di
, values
, hash_result
, error
);
170 CFReleaseSafe(values
);
175 static bool SOSCircleHashGenAndPeers(const struct ccdigest_info
*di
, SOSGenCountRef gen
, CFMutableSetRef peers
, void*hash_result
, CFErrorRef
*error
) {
176 ccdigest_di_decl(di
, circle_digest
);
177 ccdigest_init(di
, circle_digest
);
178 int64_t generation
= SOSGetGenerationSint(gen
);
179 ccdigest_update(di
, circle_digest
, sizeof(generation
), &generation
);
181 SOSCircleDigestSet(di
, peers
, hash_result
, error
);
182 ccdigest_update(di
, circle_digest
, di
->output_size
, hash_result
);
183 ccdigest_final(di
, circle_digest
, hash_result
);
187 static bool SOSCircleHash(const struct ccdigest_info
*di
, SOSCircleRef circle
, void *hash_result
, CFErrorRef
*error
) {
188 return SOSCircleHashGenAndPeers(di
, SOSCircleGetGeneration(circle
), circle
->peers
, hash_result
, error
);
191 static bool SOSCircleHashNextGenWithAdditionalPeer(const struct ccdigest_info
*di
, SOSCircleRef circle
, SOSPeerInfoRef additionalPeer
, void *hash_result
, CFErrorRef
*error
) {
193 CFMutableSetRef peers
= CFSetCreateMutableCopy(NULL
, 0, circle
->peers
);
194 CFSetAddValue(peers
, additionalPeer
);
196 SOSGenCountRef nextGen
= SOSGenerationIncrementAndCreate(circle
->generation
);
198 result
= SOSCircleHashGenAndPeers(di
, nextGen
, peers
, hash_result
, error
);
200 CFReleaseNull(nextGen
);
201 CFReleaseNull(peers
);
206 bool SOSCircleSetSignature(SOSCircleRef circle
, SecKeyRef pubkey
, CFDataRef signature
, CFErrorRef
*error
) {
209 CFStringRef pubKeyID
= SOSCopyIDOfKey(pubkey
, error
);
210 require_quiet(pubKeyID
, fail
);
211 CFDictionarySetValue(circle
->signatures
, pubKeyID
, signature
);
215 CFReleaseSafe(pubKeyID
);
219 static bool SOSCircleRemoveSignatures(SOSCircleRef circle
, CFErrorRef
*error
) {
220 CFDictionaryRemoveAllValues(circle
->signatures
);
224 CFDataRef
SOSCircleGetSignature(SOSCircleRef circle
, SecKeyRef pubkey
, CFErrorRef
*error
) {
225 CFStringRef pubKeyID
= SOSCopyIDOfKey(pubkey
, error
);
226 CFDataRef result
= NULL
;
227 require_quiet(pubKeyID
, fail
);
229 CFTypeRef value
= (CFDataRef
)CFDictionaryGetValue(circle
->signatures
, pubKeyID
);
231 if (isData(value
)) result
= (CFDataRef
) value
;
234 CFReleaseSafe(pubKeyID
);
238 CFDictionaryRef
SOSCircleCopyAllSignatures(SOSCircleRef circle
) {
239 return CFDictionaryCreateCopy(kCFAllocatorDefault
, circle
->signatures
);
242 #define circle_signature_di() ccsha256_di()
244 static CFDataRef
SecKeyCopyRawHashSignature(const struct ccdigest_info
*di
, const uint8_t* hashToSign
, SecKeyRef privKey
, CFErrorRef
*error
) {
245 CFDataRef result
= NULL
;
247 CFMutableDataRef signature
= CFDataCreateMutableWithScratch(kCFAllocatorDefault
, SecKeyGetSize(privKey
, kSecKeySignatureSize
));
248 size_t signatureSpace
= CFDataGetLength(signature
);
250 OSStatus status
= SecKeyRawSign(privKey
, kSecPaddingNone
, hashToSign
, di
->output_size
, CFDataGetMutableBytePtr(signature
), &signatureSpace
);
251 require_quiet(SecError(status
, error
, CFSTR("Signing failed: %d"), (int)status
), fail
);
253 if (signatureSpace
< (size_t)CFDataGetLength(signature
)) {
254 CFDataSetLength(signature
, signatureSpace
);
257 CFTransferRetained(result
, signature
);
259 CFReleaseNull(signature
);
263 bool SOSCircleSign(SOSCircleRef circle
, SecKeyRef privKey
, CFErrorRef
*error
) {
264 const struct ccdigest_info
*di
= circle_signature_di();
266 __block CFDataRef signature
= NULL
;
267 bool didSign
= false;
268 require_quiet(privKey
, fail
);
270 PerformWithBuffer(di
->output_size
, ^(size_t size
, uint8_t *hash_result
) {
271 if (SOSCircleHash(di
, circle
, hash_result
, error
)) {
272 signature
= SecKeyCopyRawHashSignature(di
, hash_result
, privKey
, error
);
275 require_quiet(signature
, fail
);
276 require_quiet(SOSCircleSetSignature(circle
, privKey
, signature
, error
), fail
);
281 CFReleaseNull(signature
);
285 CFDataRef
SOSCircleCopyNextGenSignatureWithPeerAdded(SOSCircleRef circle
, SOSPeerInfoRef peer
, SecKeyRef privKey
, CFErrorRef
*error
) {
286 const struct ccdigest_info
*di
= circle_signature_di();
288 __block CFDataRef signature
= NULL
;
289 require_quiet(privKey
, fail
);
291 PerformWithBuffer(di
->output_size
, ^(size_t size
, uint8_t *hash_result
) {
292 if (SOSCircleHashNextGenWithAdditionalPeer(di
, circle
, peer
, hash_result
, error
)) {
293 signature
= SecKeyCopyRawHashSignature(di
, hash_result
, privKey
, error
);
302 static bool SOSCircleConcordanceRingSign(SOSCircleRef circle
, SecKeyRef privKey
, CFErrorRef
*error
) {
303 secnotice("Development", "SOSCircleEnsureRingConsistency requires ring signing op", NULL
);
308 bool SOSCircleVerifySignatureExists(SOSCircleRef circle
, SecKeyRef pubKey
, CFErrorRef
*error
) {
310 secerror("SOSCircleVerifySignatureExists no pubKey");
311 SOSCreateError(kSOSErrorBadFormat
, CFSTR("SOSCircleVerifySignatureExists no pubKey"), (error
!= NULL
) ? *error
: NULL
, error
);
314 CFDataRef signature
= SOSCircleGetSignature(circle
, pubKey
, error
);
315 return NULL
!= signature
;
318 CFStringRef
SOSCircleCopyHashString(SOSCircleRef circle
) {
319 const struct ccdigest_info
*di
= ccsha256_di();
320 uint8_t hash_result
[di
->output_size
];
321 SOSCircleHash(di
, circle
, hash_result
, NULL
);
322 return SOSCopyHashBufAsString(hash_result
, sizeof(hash_result
));
325 bool SOSCircleVerify(SOSCircleRef circle
, SecKeyRef pubKey
, CFErrorRef
*error
) {
326 const struct ccdigest_info
*di
= ccsha256_di();
327 uint8_t hash_result
[di
->output_size
];
329 SOSCircleHash(di
, circle
, hash_result
, error
);
331 CFDataRef signature
= SOSCircleGetSignature(circle
, pubKey
, error
);
332 if(!signature
) return false;
334 return SecError(SecKeyRawVerify(pubKey
, kSecPaddingNone
, hash_result
, di
->output_size
,
335 CFDataGetBytePtr(signature
), CFDataGetLength(signature
)), error
, CFSTR("Signature verification failed."));;
338 bool SOSCircleVerifyPeerSignatureExists(SOSCircleRef circle
, SOSPeerInfoRef peer
) {
340 SecKeyRef pub_key
= SOSPeerInfoCopyPubKey(peer
, NULL
);
341 require_quiet(pub_key
, fail
);
342 result
= SOSCircleVerifySignatureExists(circle
, pub_key
, NULL
);
344 CFReleaseSafe(pub_key
);
348 bool SOSCircleVerifyPeerSigned(SOSCircleRef circle
, SOSPeerInfoRef peer
, CFErrorRef
*error
) {
350 SecKeyRef pub_key
= SOSPeerInfoCopyPubKey(peer
, error
);
351 require_quiet(pub_key
, fail
);
353 result
= SOSCircleVerify(circle
, pub_key
, error
);
355 CFReleaseSafe(pub_key
);
359 static void CFSetRemoveAllPassing(CFMutableSetRef set
, bool (^test
)(const void *) ){
360 CFMutableArrayRef toBeRemoved
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, NULL
);
362 CFSetForEach(set
, ^(const void *value
) {
364 CFArrayAppendValue(toBeRemoved
, value
);
367 CFArrayForEach(toBeRemoved
, ^(const void *value
) {
368 CFSetRemoveValue(set
, value
);
370 CFReleaseNull(toBeRemoved
);
373 static void SOSCircleRejectNonValidApplicants(SOSCircleRef circle
, SecKeyRef pubkey
) {
374 CFMutableSetRef applicants
= SOSCircleCopyApplicants(circle
, NULL
);
375 CFSetForEach(applicants
, ^(const void *value
) {
376 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) value
;
377 if(!SOSPeerInfoApplicationVerify(pi
, pubkey
, NULL
)) {
378 CFSetTransferObject(pi
, circle
->applicants
, circle
->rejected_applicants
);
381 CFReleaseNull(applicants
);
384 static SOSPeerInfoRef
SOSCircleCopyPeerInfo(SOSCircleRef circle
, CFStringRef peer_id
, CFErrorRef
*error
) {
385 __block SOSPeerInfoRef result
= NULL
;
387 CFSetForEach(circle
->peers
, ^(const void *value
) {
388 if (result
== NULL
) {
389 SOSPeerInfoRef tpi
= (SOSPeerInfoRef
)value
;
390 if (CFEqual(SOSPeerInfoGetPeerID(tpi
), peer_id
))
395 CFRetainSafe(result
);
399 static bool SOSCircleUpgradePeerInfo(SOSCircleRef circle
, SecKeyRef user_approver
, SOSFullPeerInfoRef peerinfo
) {
401 SecKeyRef userPubKey
= SecKeyCreatePublicFromPrivate(user_approver
);
402 SOSPeerInfoRef fpi_pi
= SOSFullPeerInfoGetPeerInfo(peerinfo
);
403 SOSPeerInfoRef pi
= SOSCircleCopyPeerInfo(circle
, SOSPeerInfoGetPeerID(fpi_pi
), NULL
);
404 require_quiet(pi
, out
);
405 require_quiet(SOSPeerInfoApplicationVerify(pi
, userPubKey
, NULL
), re_sign
);
406 CFReleaseNull(userPubKey
);
411 secnotice("circle", "SOSCircleGenerationSign: Upgraded peer's Application Signature");
412 SecKeyRef device_key
= SOSFullPeerInfoCopyDeviceKey(peerinfo
, NULL
);
413 require_quiet(device_key
, out
);
414 SOSPeerInfoRef new_pi
= SOSPeerInfoCopyAsApplication(pi
, user_approver
, device_key
, NULL
);
415 if(SOSCircleUpdatePeerInfo(circle
, new_pi
))
417 CFReleaseNull(new_pi
);
418 CFReleaseNull(device_key
);
420 CFReleaseNull(userPubKey
);
425 static bool SOSCircleEnsureRingConsistency(SOSCircleRef circle
, CFErrorRef
*error
) {
426 secnotice("Development", "SOSCircleEnsureRingConsistency requires ring membership and generation count consistency check", NULL
);
430 bool SOSCircleSignOldStyleResetToOfferingCircle(SOSCircleRef circle
, SOSFullPeerInfoRef peerinfo
, SecKeyRef user_approver
, CFErrorRef
*error
){
432 SecKeyRef ourKey
= SOSFullPeerInfoCopyDeviceKey(peerinfo
, error
);
433 SecKeyRef publicKey
= NULL
;
434 require_quiet(ourKey
, fail
);
436 // Check if we're using an invalid peerinfo for this op. There are cases where we might not be "upgraded".
437 require_quiet(SOSCircleUpgradePeerInfo(circle
, user_approver
, peerinfo
), fail
);
438 SOSCircleRemoveRetired(circle
, error
); // Prune off retirees since we're signing this one
439 CFSetRemoveAllValues(circle
->rejected_applicants
); // Dump rejects so we clean them up sometime.
440 publicKey
= SecKeyCreatePublicFromPrivate(user_approver
);
441 SOSCircleRejectNonValidApplicants(circle
, publicKey
);
442 require_quiet(SOSCircleEnsureRingConsistency(circle
, error
), fail
);
443 require_quiet(SOSCircleRemoveSignatures(circle
, error
), fail
);
444 require_quiet(SOSCircleSign(circle
, user_approver
, error
), fail
);
445 require_quiet(SOSCircleSign(circle
, ourKey
, error
), fail
);
447 CFReleaseNull(ourKey
);
448 CFReleaseNull(publicKey
);
452 CFReleaseNull(ourKey
);
453 CFReleaseNull(publicKey
);
457 bool SOSCirclePreGenerationSign(SOSCircleRef circle
, SecKeyRef userPubKey
, CFErrorRef
*error
) {
460 SOSCircleRemoveRetired(circle
, error
); // Prune off retirees since we're signing this one
461 CFSetRemoveAllValues(circle
->rejected_applicants
); // Dump rejects so we clean them up sometime.
462 SOSCircleRejectNonValidApplicants(circle
, userPubKey
);
464 require_quiet(SOSCircleRemoveSignatures(circle
, error
), errOut
);
473 static bool SOSCircleGenerationSign_Internal(SOSCircleRef circle
, SecKeyRef userKey
, SOSFullPeerInfoRef fpi
, CFErrorRef
*error
) {
474 // require_quiet(SOSCircleEnsureRingConsistency(circle, error), fail); Placeholder - this was never implemented
476 SecKeyRef ourKey
= NULL
;
477 if (SOSCircleCountPeers(circle
) != 0) {
478 ourKey
= SOSFullPeerInfoCopyDeviceKey(fpi
, error
);
479 require_quiet(ourKey
, errOut
);
481 // Check if we're using an invalid peerinfo for this op. There are cases where we might not be "upgraded".
482 require_quiet(SOSCircleUpgradePeerInfo(circle
, userKey
, fpi
), errOut
);
484 require_quiet(SOSCircleSign(circle
, userKey
, error
), errOut
);
485 require_quiet(SOSCircleSign(circle
, ourKey
, error
), errOut
);
486 CFReleaseNull(ourKey
);
491 CFReleaseNull(ourKey
);
495 bool SOSCircleGenerationSign(SOSCircleRef circle
, SecKeyRef userKey
, SOSFullPeerInfoRef fpi
, CFErrorRef
*error
) {
497 SecKeyRef publicKey
= NULL
;
498 publicKey
= SecKeyCreatePublicFromPrivate(userKey
);
500 require_quiet(SOSCirclePreGenerationSign(circle
, publicKey
, error
), errOut
);
501 SOSCircleGenerationIncrement(circle
);
502 require_quiet(SOSCircleGenerationSign_Internal(circle
, userKey
, fpi
, error
), errOut
);
506 CFReleaseNull(publicKey
);
511 static bool SOSCircleGenerationSignWithGenCount(SOSCircleRef circle
, SecKeyRef userKey
, SOSFullPeerInfoRef fpi
, SOSGenCountRef gencount
, CFErrorRef
*error
) {
513 SOSGenCountRef currentGen
= SOSCircleGetGeneration(circle
);
514 require_action_quiet(SOSGenerationIsOlder(currentGen
, gencount
), errOut
, SOSCreateError(kSOSErrorReplay
, CFSTR("Generation Count for new circle is too old"), NULL
, error
));
515 require_quiet(SOSCirclePreGenerationSign(circle
, userKey
, error
), errOut
);
516 SOSCircleSetGeneration(circle
, gencount
);
517 require_quiet(SOSCircleGenerationSign_Internal(circle
, userKey
, fpi
, error
), errOut
);
525 bool SOSCircleConcordanceSign(SOSCircleRef circle
, SOSFullPeerInfoRef peerinfo
, CFErrorRef
*error
) {
526 bool success
= false;
527 SecKeyRef ourKey
= SOSFullPeerInfoCopyDeviceKey(peerinfo
, error
);
528 require_quiet(ourKey
, exit
);
530 success
= SOSCircleSign(circle
, ourKey
, error
);
531 SOSCircleConcordanceRingSign(circle
, ourKey
, error
);
534 CFReleaseNull(ourKey
);
538 static inline SOSConcordanceStatus
CheckPeerStatus(SOSCircleRef circle
, SOSPeerInfoRef peer
, SecKeyRef user_public_key
, CFErrorRef
*error
) {
539 SOSConcordanceStatus result
= kSOSConcordanceNoPeer
;
540 SecKeyRef pubKey
= SOSPeerInfoCopyPubKey(peer
, error
);
541 require_quiet(pubKey
, exit
);
543 require_action_quiet(SOSCircleHasActiveValidPeer(circle
, peer
, user_public_key
, error
), exit
, result
= kSOSConcordanceNoPeer
);
544 require_action_quiet(SOSCircleVerifySignatureExists(circle
, pubKey
, error
), exit
, result
= kSOSConcordanceNoPeerSig
);
545 require_action_quiet(SOSCircleVerify(circle
, pubKey
, error
), exit
, result
= kSOSConcordanceBadPeerSig
);
547 result
= kSOSConcordanceTrusted
;
550 CFReleaseNull(pubKey
);
554 static inline SOSConcordanceStatus
CombineStatus(SOSConcordanceStatus status1
, SOSConcordanceStatus status2
)
556 if (status1
== kSOSConcordanceTrusted
|| status2
== kSOSConcordanceTrusted
)
557 return kSOSConcordanceTrusted
;
559 if (status1
== kSOSConcordanceBadPeerSig
|| status2
== kSOSConcordanceBadPeerSig
)
560 return kSOSConcordanceBadPeerSig
;
562 if (status1
== kSOSConcordanceNoPeerSig
|| status2
== kSOSConcordanceNoPeerSig
)
563 return kSOSConcordanceNoPeerSig
;
568 static inline bool SOSCircleIsEmpty(SOSCircleRef circle
) {
569 return SOSCircleCountPeers(circle
) == 0;
572 static inline bool SOSCircleHasDegenerateGeneration(SOSCircleRef deGenCircle
){
574 CFNumberRef genCountTest
= SOSCircleGetGeneration(deGenCircle
);
575 CFNumberGetValue(genCountTest
, kCFNumberCFIndexType
, &testPtr
);
576 return (testPtr
== 0);
580 static inline bool SOSCircleIsDegenerateReset(SOSCircleRef deGenCircle
){
581 return SOSCircleHasDegenerateGeneration(deGenCircle
) && SOSCircleIsEmpty(deGenCircle
);
584 static inline SOSConcordanceStatus
GetSignersStatus(SOSCircleRef signers_circle
, SOSCircleRef status_circle
,
585 SecKeyRef user_pubKey
, SOSPeerInfoRef exclude
, CFErrorRef
*error
) {
586 CFStringRef excluded_id
= exclude
? SOSPeerInfoGetPeerID(exclude
) : NULL
;
588 __block SOSConcordanceStatus status
= kSOSConcordanceNoPeer
;
589 SOSCircleForEachActivePeer(signers_circle
, ^(SOSPeerInfoRef peer
) {
590 SOSConcordanceStatus peerStatus
= CheckPeerStatus(status_circle
, peer
, user_pubKey
, error
);
592 if (peerStatus
== kSOSConcordanceNoPeerSig
&&
593 (CFEqualSafe(SOSPeerInfoGetPeerID(peer
), excluded_id
) || SOSPeerInfoIsCloudIdentity(peer
)))
594 peerStatus
= kSOSConcordanceNoPeer
;
596 status
= CombineStatus(status
, peerStatus
); // TODO: Use multiple error gathering.
602 // Is current older than proposed?
603 bool SOSCircleIsOlderGeneration(SOSCircleRef older
, SOSCircleRef newer
) {
604 return SOSGenerationIsOlder(older
->generation
, newer
->generation
);
607 static inline bool SOSCircleIsValidReset(SOSCircleRef current
, SOSCircleRef proposed
) {
609 retval
= SOSCircleIsEmpty(proposed
);
610 require_quiet(retval
, errOut
);
611 retval
= SOSCircleIsOlderGeneration(current
, proposed
);
617 bool SOSCircleSharedTrustedPeers(SOSCircleRef current
, SOSCircleRef proposed
, SOSPeerInfoRef me
) {
618 __block
bool retval
= false;
619 SOSCircleForEachPeer(current
, ^(SOSPeerInfoRef peer
) {
620 if(!CFEqual(me
, peer
) && SOSCircleHasPeer(proposed
, peer
, NULL
)) retval
= true;
625 static SOSConcordanceStatus
GetOfferingStatus(SOSCircleRef circle
, SecKeyRef user_pubKey
, CFErrorRef
*error
) {
626 __block SOSConcordanceStatus status
= kSOSConcordanceNoPeer
;
627 SOSCircleForEachPeer(circle
, ^(SOSPeerInfoRef peer
) {
628 status
= CheckPeerStatus(circle
, peer
, user_pubKey
, error
);
629 if(status
!= kSOSConcordanceTrusted
) status
= kSOSConcordanceNoPeer
;
635 SOSConcordanceStatus
SOSCircleConcordanceTrust(SOSCircleRef known_circle
, SOSCircleRef proposed_circle
,
636 SecKeyRef known_pubkey
, SecKeyRef user_pubkey
,
637 SOSPeerInfoRef me
, CFErrorRef
*error
) {
638 if(user_pubkey
== NULL
) {
639 SOSCreateError(kSOSErrorPublicKeyAbsent
, CFSTR("Concordance with no user public key"), NULL
, error
);
640 return kSOSConcordanceNoUserKey
;
643 if(SOSCircleIsDegenerateReset(proposed_circle
)) {
644 return kSOSConcordanceTrusted
;
647 if (SOSCircleIsValidReset(known_circle
, proposed_circle
)) {
648 return kSOSConcordanceTrusted
;
651 if(!SOSCircleVerifySignatureExists(proposed_circle
, user_pubkey
, error
)) {
652 SOSCreateError(kSOSErrorBadSignature
, CFSTR("No public signature to match current user key"), (error
!= NULL
) ? *error
: NULL
, error
);
653 return kSOSConcordanceNoUserSig
;
656 if(!SOSCircleVerify(proposed_circle
, user_pubkey
, error
)) {
657 SOSCreateError(kSOSErrorBadSignature
, CFSTR("Bad user public signature"), (error
!= NULL
) ? *error
: NULL
, error
);
658 debugDumpCircle(CFSTR("proposed_circle"), proposed_circle
);
659 return kSOSConcordanceBadUserSig
;
662 if (SOSCircleIsEmpty(known_circle
)) {
663 return GetSignersStatus(proposed_circle
, proposed_circle
, user_pubkey
, NULL
, error
);
666 if(SOSCircleHasDegenerateGeneration(proposed_circle
) && SOSCircleIsOffering(proposed_circle
)){
667 return GetSignersStatus(proposed_circle
, proposed_circle
, user_pubkey
, NULL
, error
);
670 if(SOSCircleIsOlderGeneration(proposed_circle
, known_circle
)) {
671 SOSCreateError(kSOSErrorReplay
, CFSTR("Bad generation - proposed circle gencount is older than known circle gencount"), NULL
, error
);
672 debugDumpCircle(CFSTR("isOlderGeneration known_circle"), known_circle
);
673 debugDumpCircle(CFSTR("isOlderGeneration proposed_circle"), proposed_circle
);
674 return kSOSConcordanceGenOld
;
677 if(SOSCircleIsOffering(proposed_circle
)){
678 return GetOfferingStatus(proposed_circle
, user_pubkey
, error
);
681 return GetSignersStatus(known_circle
, proposed_circle
, user_pubkey
, me
, error
);
685 static void SOSCircleDestroy(CFTypeRef aObj
) {
686 SOSCircleRef c
= (SOSCircleRef
) aObj
;
688 CFReleaseNull(c
->name
);
689 CFReleaseNull(c
->generation
);
690 CFReleaseNull(c
->peers
);
691 CFReleaseNull(c
->applicants
);
692 CFReleaseNull(c
->rejected_applicants
);
693 CFReleaseNull(c
->signatures
);
696 static CFMutableStringRef
defaultDescriptionCreate(CFTypeRef aObj
){
697 SOSCircleRef c
= (SOSCircleRef
) aObj
;
698 CFStringRef initPeerSep
= CFSTR("\n");
699 CFStringRef peerSep
= CFSTR("\n");
701 CFMutableStringRef description
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
703 SOSGenerationCountWithDescription(c
->generation
, ^(CFStringRef genDescription
) {
704 CFStringAppendFormat(description
, NULL
, CFSTR("<SOSCircle@%p: '%@' %@ P:["), c
, c
->name
, genDescription
);
707 __block CFStringRef separator
= initPeerSep
;
708 SOSCircleForEachActivePeer(c
, ^(SOSPeerInfoRef peer
) {
709 CFStringRef sig
= NULL
;
710 if (SOSCircleVerifyPeerSigned(c
, peer
, NULL
)) {
713 SecKeyRef pub_key
= SOSPeerInfoCopyPubKey(peer
, NULL
);
714 CFDataRef signature
= pub_key
? SOSCircleGetSignature(c
, pub_key
, NULL
) : NULL
;
715 sig
= (signature
== NULL
) ? CFSTR("-") : CFSTR("?");
716 CFReleaseNull(pub_key
);
719 CFStringAppendFormat(description
, NULL
, CFSTR("%@%@ %@"), separator
, peer
, sig
);
724 CFStringAppend(description
, CFSTR("], A:["));
725 separator
= initPeerSep
;
726 if(CFSetGetCount(c
->applicants
) == 0 )
727 CFStringAppendFormat(description
, NULL
, CFSTR("-"));
730 SOSCircleForEachApplicant(c
, ^(SOSPeerInfoRef peer
) {
731 CFStringAppendFormat(description
, NULL
, CFSTR("%@%@"), separator
, peer
);
737 CFStringAppend(description
, CFSTR("], R:["));
738 separator
= initPeerSep
;
739 if(CFSetGetCount(c
->rejected_applicants
) == 0)
740 CFStringAppendFormat(description
, NULL
, CFSTR("-"));
742 CFSetForEach(c
->rejected_applicants
, ^(const void *value
) {
743 SOSPeerInfoRef peer
= (SOSPeerInfoRef
) value
;
744 CFStringAppendFormat(description
, NULL
, CFSTR("%@%@"), separator
, peer
);
748 CFStringAppend(description
, CFSTR("]>"));
752 static CFMutableStringRef
descriptionCreateWithFormatOptions(CFTypeRef aObj
, CFDictionaryRef formatOptions
){
753 SOSCircleRef c
= (SOSCircleRef
) aObj
;
755 CFMutableStringRef description
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
757 if(CFDictionaryContainsKey(formatOptions
, CFSTR("SyncD"))) {
758 CFStringRef generationDescription
= SOSGenerationCountCopyDescription(c
->generation
);
759 CFStringAppendFormat(description
, NULL
, CFSTR("<C: gen:'%@' %@>\n"), generationDescription
, c
->name
);
760 CFReleaseNull(generationDescription
);
761 __block CFStringRef separator
= CFSTR("\t\t");
762 SOSCircleForEachActivePeer(c
, ^(SOSPeerInfoRef peer
) {
763 CFStringRef sig
= NULL
;
764 if (SOSCircleVerifyPeerSigned(c
, peer
, NULL
)) {
767 SecKeyRef pub_key
= SOSPeerInfoCopyPubKey(peer
, NULL
);
768 CFDataRef signature
= pub_key
? SOSCircleGetSignature(c
, pub_key
, NULL
) : NULL
;
769 sig
= (signature
== NULL
) ? CFSTR("-") : CFSTR("?");
770 CFReleaseNull(pub_key
);
773 CFStringAppendFormat(description
, formatOptions
, CFSTR("%@%@ %@"), separator
, peer
, sig
);
774 separator
= CFSTR("\n\t\t");
776 CFStringAppend(description
, CFSTR("\n\t\t<A:["));
777 separator
= CFSTR("");
780 if(CFSetGetCount(c
->applicants
) == 0 )
781 CFStringAppendFormat(description
, NULL
, CFSTR("-"));
784 SOSCircleForEachApplicant(c
, ^(SOSPeerInfoRef peer
) {
785 CFStringAppendFormat(description
, formatOptions
, CFSTR("%@A: %@"), separator
, peer
);
786 separator
= CFSTR("\n\t\t\t");
790 CFStringAppend(description
, CFSTR("]> \n\t\t<R:["));
791 separator
= CFSTR("");
792 if(CFSetGetCount(c
->rejected_applicants
) == 0)
793 CFStringAppendFormat(description
, NULL
, CFSTR("-"));
795 CFSetForEach(c
->rejected_applicants
, ^(const void *value
) {
796 SOSPeerInfoRef peer
= (SOSPeerInfoRef
) value
;
797 CFStringAppendFormat(description
, formatOptions
, CFSTR("%@R: %@"), separator
, peer
);
798 separator
= CFSTR("\n\t\t");
801 CFStringAppend(description
, CFSTR("]>"));
805 CFReleaseNull(description
);
806 description
= defaultDescriptionCreate(aObj
);
814 static CFStringRef
SOSCircleCopyFormatDescription(CFTypeRef aObj
, CFDictionaryRef formatOptions
) {
815 SOSCircleRef c
= (SOSCircleRef
) aObj
;
816 SOSCircleAssertStable(c
);
817 CFMutableStringRef description
= NULL
;
819 if(formatOptions
!= NULL
){
820 description
= descriptionCreateWithFormatOptions(aObj
, formatOptions
);
823 description
= defaultDescriptionCreate(aObj
);
828 CFStringRef
SOSCircleGetName(SOSCircleRef circle
) {
829 if(!circle
|| !circle
->name
) {
835 const char *SOSCircleGetNameC(SOSCircleRef circle
) {
836 CFStringRef name
= SOSCircleGetName(circle
);
839 return CFStringToCString(name
);
842 SOSGenCountRef
SOSCircleGetGeneration(SOSCircleRef circle
) {
844 assert(circle
->generation
);
845 return circle
->generation
;
848 void SOSCircleSetGeneration(SOSCircleRef circle
, SOSGenCountRef gencount
) {
850 CFReleaseNull(circle
->generation
);
851 circle
->generation
= CFRetainSafe(gencount
);
854 int64_t SOSCircleGetGenerationSint(SOSCircleRef circle
) {
855 SOSGenCountRef gen
= SOSCircleGetGeneration(circle
);
856 return SOSGetGenerationSint(gen
);
859 void SOSCircleGenerationSetValue(SOSCircleRef circle
, int64_t value
) {
860 CFAssignRetained(circle
->generation
, SOSGenerationCreateWithValue(value
));
863 void SOSCircleGenerationIncrement(SOSCircleRef circle
) {
864 SOSGenCountRef old
= circle
->generation
;
865 circle
->generation
= SOSGenerationIncrementAndCreate(old
);
869 int SOSCircleCountPeers(SOSCircleRef circle
) {
870 SOSCircleAssertStable(circle
);
871 __block
int count
= 0;
872 SOSCircleForEachPeer(circle
, ^(SOSPeerInfoRef peer
) {
878 int SOSCircleCountActivePeers(SOSCircleRef circle
) {
879 SOSCircleAssertStable(circle
);
880 __block
int count
= 0;
881 SOSCircleForEachActivePeer(circle
, ^(SOSPeerInfoRef peer
) {
887 int SOSCircleCountActiveValidPeers(SOSCircleRef circle
, SecKeyRef pubkey
) {
888 SOSCircleAssertStable(circle
);
889 __block
int count
= 0;
890 SOSCircleForEachActiveValidPeer(circle
, pubkey
, ^(SOSPeerInfoRef peer
) {
896 int SOSCircleCountValidSyncingPeers(SOSCircleRef circle
, SecKeyRef pubkey
) {
897 SOSCircleAssertStable(circle
);
898 __block
int count
= 0;
899 SOSCircleForEachValidSyncingPeer(circle
, pubkey
, ^(SOSPeerInfoRef peer
) {
906 int SOSCircleCountRetiredPeers(SOSCircleRef circle
) {
907 SOSCircleAssertStable(circle
);
908 __block
int count
= 0;
909 SOSCircleForEachRetiredPeer(circle
, ^(SOSPeerInfoRef peer
) {
915 int SOSCircleCountApplicants(SOSCircleRef circle
) {
916 SOSCircleAssertStable(circle
);
918 return (int)CFSetGetCount(circle
->applicants
);
921 bool SOSCircleHasApplicant(SOSCircleRef circle
, SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
922 SOSCircleAssertStable(circle
);
924 return CFSetContainsValue(circle
->applicants
, peerInfo
);
927 CFMutableSetRef
SOSCircleCopyApplicants(SOSCircleRef circle
, CFAllocatorRef allocator
) {
928 SOSCircleAssertStable(circle
);
930 return CFSetCreateMutableCopy(allocator
, 0, circle
->applicants
);
933 int SOSCircleCountRejectedApplicants(SOSCircleRef circle
) {
934 SOSCircleAssertStable(circle
);
936 return (int)CFSetGetCount(circle
->rejected_applicants
);
939 bool SOSCircleHasRejectedApplicant(SOSCircleRef circle
, SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
940 SOSCircleAssertStable(circle
);
941 return CFSetContainsValue(circle
->rejected_applicants
, peerInfo
);
944 SOSPeerInfoRef
SOSCircleCopyRejectedApplicant(SOSCircleRef circle
, SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
945 SOSCircleAssertStable(circle
);
946 return CFRetainSafe((SOSPeerInfoRef
)CFSetGetValue(circle
->rejected_applicants
, peerInfo
));
949 CFMutableArrayRef
SOSCircleCopyRejectedApplicants(SOSCircleRef circle
, CFAllocatorRef allocator
) {
950 SOSCircleAssertStable(circle
);
952 return CFSetCopyValuesCFArray(circle
->rejected_applicants
);
955 bool SOSCircleResetToEmpty(SOSCircleRef circle
, CFErrorRef
*error
) {
956 CFSetRemoveAllValues(circle
->applicants
);
957 CFSetRemoveAllValues(circle
->rejected_applicants
);
958 CFSetRemoveAllValues(circle
->peers
);
959 CFDictionaryRemoveAllValues(circle
->signatures
);
960 SOSGenCountRef oldGen
= SOSCircleGetGeneration(circle
);
961 SOSGenCountRef newGen
= SOSGenerationCreateWithBaseline(oldGen
);
962 SOSCircleSetGeneration(circle
, newGen
);
963 CFReleaseSafe(newGen
);
967 bool SOSCircleResetToEmptyWithSameGeneration(SOSCircleRef circle
, CFErrorRef
*error
) {
968 SOSGenCountRef gen
= SOSGenerationCopy(SOSCircleGetGeneration(circle
));
969 SOSCircleResetToEmpty(circle
, error
);
970 SOSCircleSetGeneration(circle
, gen
);
975 bool SOSCircleResetToOffering(SOSCircleRef circle
, SecKeyRef user_privkey
, SOSFullPeerInfoRef requestor
, CFErrorRef
*error
){
977 return SOSCircleResetToEmpty(circle
, error
)
978 && SOSCircleRequestAdmission(circle
, user_privkey
, requestor
, error
)
979 && SOSCircleAcceptRequest(circle
, user_privkey
, requestor
, SOSFullPeerInfoGetPeerInfo(requestor
), error
);
982 bool SOSCircleRemoveRetired(SOSCircleRef circle
, CFErrorRef
*error
) {
983 CFSetRemoveAllPassing(circle
->peers
, ^ bool (const void *element
) {
984 SOSPeerInfoRef peer
= (SOSPeerInfoRef
) element
;
986 return SOSPeerInfoIsRetirementTicket(peer
);
992 static bool SOSCircleRecordAdmissionRequest(SOSCircleRef circle
, SecKeyRef user_pubkey
, SOSPeerInfoRef requestorPeerInfo
, CFErrorRef
*error
) {
993 SOSCircleAssertStable(circle
);
995 bool isPeer
= SOSCircleHasPeer(circle
, requestorPeerInfo
, error
);
997 require_action_quiet(!isPeer
, fail
, SOSCreateError(kSOSErrorAlreadyPeer
, CFSTR("Cannot request admission when already a peer"), NULL
, error
));
999 // This adds to applicants and will take off rejected if it's there.
1000 CFSetTransferObject(requestorPeerInfo
, circle
->rejected_applicants
, circle
->applicants
);
1009 bool SOSCircleRequestReadmission(SOSCircleRef circle
, SecKeyRef user_pubkey
, SOSPeerInfoRef peer
, CFErrorRef
*error
) {
1010 bool success
= false;
1012 require_quiet(SOSPeerInfoApplicationVerify(peer
, user_pubkey
, error
), fail
);
1013 success
= SOSCircleRecordAdmissionRequest(circle
, user_pubkey
, peer
, error
);
1018 bool SOSCircleRequestAdmission(SOSCircleRef circle
, SecKeyRef user_privkey
, SOSFullPeerInfoRef requestor
, CFErrorRef
*error
) {
1019 bool success
= false;
1021 SecKeyRef user_pubkey
= SecKeyCreatePublicFromPrivate(user_privkey
);
1022 require_action_quiet(user_pubkey
, fail
, SOSCreateError(kSOSErrorBadKey
, CFSTR("No public key for key"), NULL
, error
));
1024 require(SOSFullPeerInfoPromoteToApplication(requestor
, user_privkey
, error
), fail
);
1026 success
= SOSCircleRecordAdmissionRequest(circle
, user_pubkey
, SOSFullPeerInfoGetPeerInfo(requestor
), error
);
1028 CFReleaseNull(user_pubkey
);
1032 static bool sosCircleUpdatePeerInfoSet(CFMutableSetRef theSet
, SOSPeerInfoRef replacement_peer_info
) {
1033 CFTypeRef old
= NULL
;
1034 if(!replacement_peer_info
) return false;
1035 if(!(old
= CFSetGetValue(theSet
, replacement_peer_info
))) return false;
1036 if(CFEqualSafe(old
, replacement_peer_info
)) return false;
1037 CFSetReplaceValue(theSet
, replacement_peer_info
);
1041 bool SOSCircleUpdatePeerInfo(SOSCircleRef circle
, SOSPeerInfoRef replacement_peer_info
) {
1042 if(sosCircleUpdatePeerInfoSet(circle
->peers
, replacement_peer_info
)) return true;
1043 if(sosCircleUpdatePeerInfoSet(circle
->applicants
, replacement_peer_info
)) return true;
1044 if(sosCircleUpdatePeerInfoSet(circle
->rejected_applicants
, replacement_peer_info
)) return true;
1048 static bool SOSCircleRemovePeerInternal(SOSCircleRef circle
, SOSFullPeerInfoRef requestor
, SOSPeerInfoRef peer_to_remove
, CFErrorRef
*error
) {
1049 SOSPeerInfoRef requestor_peer_info
= SOSFullPeerInfoGetPeerInfo(requestor
);
1051 if (SOSCircleHasPeer(circle
, peer_to_remove
, NULL
)) {
1052 if (!SOSCircleHasPeer(circle
, requestor_peer_info
, error
)) {
1053 SOSCreateError(kSOSErrorAlreadyPeer
, CFSTR("Must be peer to remove peer"), NULL
, error
);
1056 CFSetRemoveValue(circle
->peers
, peer_to_remove
);
1059 if (SOSCircleHasApplicant(circle
, peer_to_remove
, error
)) {
1060 return SOSCircleRejectRequest(circle
, requestor
, peer_to_remove
, error
);
1066 bool SOSCircleRemovePeers(SOSCircleRef circle
, SecKeyRef user_privkey
, SOSFullPeerInfoRef requestor
, CFSetRef peersToRemove
, CFErrorRef
*error
) {
1068 bool success
= false;
1070 __block
bool removed_all
= true;
1071 CFSetForEach(peersToRemove
, ^(const void *value
) {
1072 SOSPeerInfoRef peerInfo
= asSOSPeerInfo(value
);
1074 removed_all
&= SOSCircleRemovePeerInternal(circle
, requestor
, peerInfo
, error
);
1078 require_quiet(removed_all
, exit
);
1080 require_quiet(SOSCircleGenerationSign(circle
, user_privkey
, requestor
, error
), exit
);
1089 bool SOSCircleRemovePeersByID(SOSCircleRef circle
, SecKeyRef user_privkey
, SOSFullPeerInfoRef requestor
, CFSetRef peersToRemove
, CFErrorRef
*error
) {
1091 bool success
= false;
1093 __block
bool removed_all
= true;
1094 CFSetForEach(peersToRemove
, ^(const void *value
) {
1095 CFStringRef peerID
= asString(value
, NULL
);
1097 SOSPeerInfoRef peerInfo
= SOSCircleCopyPeerInfo(circle
, peerID
, NULL
);
1099 removed_all
&= SOSCircleRemovePeerInternal(circle
, requestor
, peerInfo
, error
);
1100 CFReleaseNull(peerInfo
);
1105 require_quiet(removed_all
, exit
);
1107 require_quiet(SOSCircleGenerationSign(circle
, user_privkey
, requestor
, error
), exit
);
1115 static bool SOSCircleRemovePeerUnsigned(SOSCircleRef circle
, SOSPeerInfoRef peer_to_remove
) {
1116 bool retval
= false;
1117 if (SOSCircleHasPeer(circle
, peer_to_remove
, NULL
)) {
1118 CFSetRemoveValue(circle
->peers
, peer_to_remove
);
1124 bool SOSCircleRemovePeersByIDUnsigned(SOSCircleRef circle
, CFSetRef peersToRemove
) {
1125 __block
bool removed_all
= true;
1126 CFSetForEach(peersToRemove
, ^(const void *value
) {
1127 CFStringRef peerID
= asString(value
, NULL
);
1128 SOSPeerInfoRef peerInfo
= SOSCircleCopyPeerInfo(circle
, peerID
, NULL
);
1129 removed_all
&= SOSCircleRemovePeerUnsigned(circle
, peerInfo
);
1130 CFReleaseNull(peerInfo
);
1135 bool SOSCircleRemovePeer(SOSCircleRef circle
, SecKeyRef user_privkey
, SOSFullPeerInfoRef requestor
, SOSPeerInfoRef peer_to_remove
, CFErrorRef
*error
) {
1136 bool success
= false;
1138 require_quiet(SOSCircleRemovePeerInternal(circle
, requestor
, peer_to_remove
, error
), exit
);
1140 require_quiet(SOSCircleGenerationSign(circle
, user_privkey
, requestor
, error
), exit
);
1147 bool SOSCircleAcceptRequest(SOSCircleRef circle
, SecKeyRef user_privkey
, SOSFullPeerInfoRef device_approver
, SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
1148 SOSCircleAssertStable(circle
);
1150 SecKeyRef publicKey
= NULL
;
1151 bool result
= false;
1153 require_action_quiet(CFSetContainsValue(circle
->applicants
, peerInfo
), fail
,
1154 SOSCreateError(kSOSErrorNotApplicant
, CFSTR("Cannot accept non-applicant"), NULL
, error
));
1156 publicKey
= SecKeyCreatePublicFromPrivate(user_privkey
);
1157 require_quiet(SOSPeerInfoApplicationVerify(peerInfo
, publicKey
, error
), fail
);
1159 CFSetTransferObject(peerInfo
, circle
->applicants
, circle
->peers
);
1161 result
= SOSCircleGenerationSign(circle
, user_privkey
, device_approver
, error
);
1164 CFReleaseNull(publicKey
);
1168 bool SOSCircleWithdrawRequest(SOSCircleRef circle
, SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
1169 SOSCircleAssertStable(circle
);
1171 CFSetRemoveValue(circle
->applicants
, peerInfo
);
1176 bool SOSCircleRemoveRejectedPeer(SOSCircleRef circle
, SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
1177 SOSCircleAssertStable(circle
);
1179 CFSetRemoveValue(circle
->rejected_applicants
, peerInfo
);
1185 bool SOSCircleRejectRequest(SOSCircleRef circle
, SOSFullPeerInfoRef device_rejector
,
1186 SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
1187 SOSCircleAssertStable(circle
);
1189 if (CFEqual(SOSPeerInfoGetPeerID(peerInfo
), SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(device_rejector
))))
1190 return SOSCircleWithdrawRequest(circle
, peerInfo
, error
);
1192 if (!CFSetContainsValue(circle
->applicants
, peerInfo
)) {
1193 SOSCreateError(kSOSErrorNotApplicant
, CFSTR("Cannot reject non-applicant"), NULL
, error
);
1197 CFSetTransferObject(peerInfo
, circle
->applicants
, circle
->rejected_applicants
);
1199 // TODO: Maybe we sign the rejection with device_rejector.
1204 bool SOSCircleAcceptRequests(SOSCircleRef circle
, SecKeyRef user_privkey
, SOSFullPeerInfoRef device_approver
,
1205 CFErrorRef
*error
) {
1206 // Returns true if we accepted someone and therefore have to post the circle back to KVS
1207 __block
bool result
= false;
1209 SOSCircleForEachApplicant(circle
, ^(SOSPeerInfoRef peer
) {
1210 if (!SOSCircleAcceptRequest(circle
, user_privkey
, device_approver
, peer
, error
)) {
1211 secnotice("circle", "error in SOSCircleAcceptRequest\n");
1213 secnotice("circle", "Accepted peer: %@", peer
);
1219 SOSCircleGenerationSign(circle
, user_privkey
, device_approver
, error
);
1220 secnotice("circle", "Countersigned accepted requests");
1226 bool SOSCirclePeerSigUpdate(SOSCircleRef circle
, SecKeyRef userPrivKey
, SOSFullPeerInfoRef fpi
,
1227 CFErrorRef
*error
) {
1228 // Returns true if we accepted someone and therefore have to post the circle back to KVS
1229 __block
bool result
= false;
1230 SecKeyRef userPubKey
= SecKeyCreatePublicFromPrivate(userPrivKey
);
1232 // We're going to remove any applicants using a mismatched user key.
1233 SOSCircleForEachApplicant(circle
, ^(SOSPeerInfoRef peer
) {
1234 if(!SOSPeerInfoApplicationVerify(peer
, userPubKey
, NULL
)) {
1235 if(!SOSCircleRejectRequest(circle
, fpi
, peer
, NULL
)) {
1241 result
= SOSCircleUpdatePeerInfo(circle
, SOSFullPeerInfoGetPeerInfo(fpi
));
1244 SOSCircleGenerationSign(circle
, userPrivKey
, fpi
, error
);
1245 secnotice("circle", "Generation signed updated signatures on peerinfo");
1252 // Peer iteration and membership
1255 static inline void SOSCircleForEachPeerMatching(SOSCircleRef circle
,
1256 void (^action
)(SOSPeerInfoRef peer
),
1257 bool (^condition
)(SOSPeerInfoRef peer
)) {
1258 CFSetForEach(circle
->peers
, ^(const void *value
) {
1259 SOSPeerInfoRef peer
= (SOSPeerInfoRef
) value
;
1260 if (condition(peer
))
1265 static inline bool isHiddenPeer(SOSPeerInfoRef peer
) {
1266 return SOSPeerInfoIsRetirementTicket(peer
) || SOSPeerInfoIsCloudIdentity(peer
);
1269 void SOSCircleForEachPeer(SOSCircleRef circle
, void (^action
)(SOSPeerInfoRef peer
)) {
1270 SOSCircleForEachPeerMatching(circle
, action
, ^bool(SOSPeerInfoRef peer
) {
1271 return !isHiddenPeer(peer
);
1275 void SOSCircleForEachRetiredPeer(SOSCircleRef circle
, void (^action
)(SOSPeerInfoRef peer
)) {
1276 SOSCircleForEachPeerMatching(circle
, action
, ^bool(SOSPeerInfoRef peer
) {
1277 return SOSPeerInfoIsRetirementTicket(peer
);
1281 void SOSCircleForEachiCloudIdentityPeer(SOSCircleRef circle
, void (^action
)(SOSPeerInfoRef peer
)) {
1282 SOSCircleForEachPeerMatching(circle
, action
, ^bool(SOSPeerInfoRef peer
) {
1283 return SOSPeerInfoIsCloudIdentity(peer
);
1288 void SOSCircleForEachActivePeer(SOSCircleRef circle
, void (^action
)(SOSPeerInfoRef peer
)) {
1289 SOSCircleForEachPeerMatching(circle
, action
, ^bool(SOSPeerInfoRef peer
) {
1294 void SOSCircleForEachActiveValidPeer(SOSCircleRef circle
, SecKeyRef user_public_key
, void (^action
)(SOSPeerInfoRef peer
)) {
1295 SOSCircleForEachPeerMatching(circle
, action
, ^bool(SOSPeerInfoRef peer
) {
1296 return SOSPeerInfoApplicationVerify(peer
, user_public_key
, NULL
);
1300 void SOSCircleForEachValidPeer(SOSCircleRef circle
, SecKeyRef user_public_key
, void (^action
)(SOSPeerInfoRef peer
)) {
1301 SOSCircleForEachPeerMatching(circle
, action
, ^bool(SOSPeerInfoRef peer
) {
1302 return !isHiddenPeer(peer
) && SOSPeerInfoApplicationVerify(peer
, user_public_key
, NULL
);
1306 void SOSCircleForEachValidSyncingPeer(SOSCircleRef circle
, SecKeyRef user_public_key
, void (^action
)(SOSPeerInfoRef peer
)) {
1307 SOSCircleForEachValidPeer(circle
, user_public_key
, action
);
1310 void SOSCircleForEachBackupCapablePeerForView(SOSCircleRef circle
, SecKeyRef user_public_key
, CFStringRef viewName
, void (^action
)(SOSPeerInfoRef peer
)) {
1311 SOSCircleForEachPeerMatching(circle
, action
, ^bool(SOSPeerInfoRef peer
) {
1312 return (!isHiddenPeer(peer
) && SOSPeerInfoIsEnabledView(peer
, viewName
) /* let the wookie win --- && SOSPeerInfoHasBackupKey(peer)*/ && SOSPeerInfoApplicationVerify(peer
, user_public_key
, NULL
));
1316 void SOSCircleForEachApplicant(SOSCircleRef circle
, void (^action
)(SOSPeerInfoRef peer
)) {
1317 CFSetForEach(circle
->applicants
, ^(const void*value
) { action((SOSPeerInfoRef
) value
); } );
1321 bool SOSCircleHasPeerWithID(SOSCircleRef circle
, CFStringRef peerid
, CFErrorRef
*error
) {
1322 SOSCircleAssertStable(circle
);
1326 SOSPeerInfoRef found
= asSOSPeerInfo(CFSetGetValue(circle
->peers
, peerid
));
1327 return found
&& !isHiddenPeer(found
);
1330 SOSPeerInfoRef
SOSCircleCopyPeerWithID(SOSCircleRef circle
, CFStringRef peerid
, CFErrorRef
*error
) {
1331 SOSCircleAssertStable(circle
);
1333 SOSPeerInfoRef found
= asSOSPeerInfo(CFSetGetValue(circle
->peers
, peerid
));
1334 return found
? SOSPeerInfoCreateCopy(kCFAllocatorDefault
, found
, NULL
) : NULL
;
1337 bool SOSCircleHasPeer(SOSCircleRef circle
, SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
1338 if(!peerInfo
) return false;
1339 return SOSCircleHasPeerWithID(circle
, SOSPeerInfoGetPeerID(peerInfo
), error
);
1342 bool SOSCircleHasActivePeerWithID(SOSCircleRef circle
, CFStringRef peerid
, CFErrorRef
*error
) {
1343 SOSCircleAssertStable(circle
);
1344 SOSPeerInfoRef found
= asSOSPeerInfo(CFSetGetValue(circle
->peers
, peerid
));
1348 bool SOSCircleHasActivePeer(SOSCircleRef circle
, SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
1349 if(!peerInfo
) return false;
1350 return SOSCircleHasActivePeerWithID(circle
, SOSPeerInfoGetPeerID(peerInfo
), error
);
1353 bool SOSCircleHasActiveValidPeerWithID(SOSCircleRef circle
, CFStringRef peerid
, SecKeyRef user_public_key
, CFErrorRef
*error
) {
1354 SOSCircleAssertStable(circle
);
1355 SOSPeerInfoRef found
= asSOSPeerInfo(CFSetGetValue(circle
->peers
, peerid
));
1356 return found
&& SOSPeerInfoApplicationVerify(found
, user_public_key
, NULL
);
1359 bool SOSCircleHasValidSyncingPeer(SOSCircleRef circle
, SOSPeerInfoRef peerInfo
, SecKeyRef user_public_key
, CFErrorRef
*error
) {
1360 SOSCircleAssertStable(circle
);
1361 SOSPeerInfoRef found
= asSOSPeerInfo(CFSetGetValue(circle
->peers
, peerInfo
));
1362 return found
&& !isHiddenPeer(found
) && SOSPeerInfoApplicationVerify(found
, user_public_key
, NULL
);
1365 bool SOSCircleHasActiveValidPeer(SOSCircleRef circle
, SOSPeerInfoRef peerInfo
, SecKeyRef user_public_key
, CFErrorRef
*error
) {
1366 if(!peerInfo
) return false;
1367 return SOSCircleHasActiveValidPeerWithID(circle
, SOSPeerInfoGetPeerID(peerInfo
), user_public_key
, error
);
1371 CFMutableSetRef
SOSCircleCopyPeers(SOSCircleRef circle
, CFAllocatorRef allocator
) {
1372 SOSCircleAssertStable(circle
);
1374 CFMutableSetRef result
= CFSetCreateMutableForSOSPeerInfosByID(allocator
);
1376 SOSCircleForEachPeer(circle
, ^(SOSPeerInfoRef peer
) {
1377 CFSetAddValue(result
, peer
);
1384 CFMutableSetRef
SOSCircleCopyBackupCapablePeersForView(SOSCircleRef circle
, CFAllocatorRef allocator
, SecKeyRef userPubKey
, CFStringRef viewName
) {
1385 SOSCircleAssertStable(circle
);
1387 CFMutableSetRef result
= CFSetCreateMutableForSOSPeerInfosByID(allocator
);
1389 SOSCircleForEachBackupCapablePeerForView(circle
, userPubKey
, viewName
, ^(SOSPeerInfoRef peer
) {
1390 CFSetAddValue(result
, peer
);
1396 bool SOSCircleAppendConcurringPeers(SOSCircleRef circle
, CFMutableArrayRef appendHere
, CFErrorRef
*error
) {
1397 SOSCircleForEachActivePeer(circle
, ^(SOSPeerInfoRef peer
) {
1398 CFErrorRef localError
= NULL
;
1399 if (SOSCircleVerifyPeerSigned(circle
, peer
, &localError
)) {
1400 SOSPeerInfoRef peerInfo
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
, peer
, error
);
1401 CFArrayAppendValue(appendHere
, peerInfo
);
1402 CFRelease(peerInfo
);
1403 } else if (error
!= NULL
) {
1404 secerror("Error checking concurrence: %@", localError
);
1406 CFReleaseNull(localError
);
1412 CFMutableArrayRef
SOSCircleCopyConcurringPeers(SOSCircleRef circle
, CFErrorRef
* error
) {
1413 SOSCircleAssertStable(circle
);
1415 CFMutableArrayRef concurringPeers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1417 if (!SOSCircleAppendConcurringPeers(circle
, concurringPeers
, error
))
1418 CFReleaseNull(concurringPeers
);
1420 return concurringPeers
;
1423 SOSFullPeerInfoRef
SOSCircleCopyiCloudFullPeerInfoRef(SOSCircleRef circle
, CFErrorRef
*error
) {
1424 __block SOSFullPeerInfoRef cloud_full_peer
= NULL
;
1425 __block CFErrorRef searchError
= NULL
;
1426 SOSCircleForEachActivePeer(circle
, ^(SOSPeerInfoRef peer
) {
1427 if (SOSPeerInfoIsCloudIdentity(peer
)) {
1428 if (cloud_full_peer
== NULL
) {
1430 secerror("More than one cloud identity found, first had error, trying new one.");
1432 CFReleaseNull(searchError
);
1433 cloud_full_peer
= SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault
, peer
, &searchError
);
1434 if (!cloud_full_peer
) {
1435 secnotice("icloud-identity", "Failed to make FullPeer for iCloud Identity: %@ (%@)", cloud_full_peer
, searchError
);
1438 secerror("Additional cloud identity found in circle after successful creation: %@", circle
);
1442 // If we didn't find one at all, report the error.
1443 if (cloud_full_peer
== NULL
&& searchError
== NULL
) {
1444 SOSErrorCreate(kSOSErrorNoiCloudPeer
, &searchError
, NULL
, CFSTR("No iCloud identity PeerInfo found in circle"));
1445 secnotice("icloud-identity", "No iCloud identity PeerInfo found in circle");
1448 CFTransferRetained(*error
, searchError
);
1450 CFReleaseNull(searchError
);
1451 return cloud_full_peer
;
1454 SOSFullPeerInfoRef
SOSCircleCopyiCloudFullPeerInfoVerifier(SOSCircleRef circle
, CFErrorRef
*error
) {
1455 __block CFErrorRef searchError
= NULL
;
1456 __block SOSFullPeerInfoRef cloud_full_peer
= NULL
;
1457 SOSCircleForEachActivePeer(circle
, ^(SOSPeerInfoRef peer
) {
1458 // There should only ever be one signing iCloud identity. If there are more we'll take the first one.
1459 if (!cloud_full_peer
&& SOSPeerInfoIsCloudIdentity(peer
) && SOSCircleVerifyPeerSignatureExists(circle
, peer
)) {
1460 cloud_full_peer
= SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault
, peer
, &searchError
);
1463 // If we didn't find one at all, report the error.
1464 if (cloud_full_peer
== NULL
&& searchError
== NULL
) {
1465 SOSErrorCreate(kSOSErrorNoiCloudPeer
, &searchError
, NULL
, CFSTR("No iCloud identity PeerInfo found in circle"));
1466 secnotice("icloud-identity", "No iCloud identity PeerInfo found in circle");
1469 CFTransferRetained(*error
, searchError
);
1471 CFReleaseNull(searchError
);
1472 return cloud_full_peer
;
1475 void debugDumpCircle(CFStringRef message
, SOSCircleRef circle
) {
1478 secinfo("circledebug", "%@: %@", message
, circle
);
1482 CFDataRef derdata
= SOSCircleCopyEncodedData(circle
, kCFAllocatorDefault
, &error
);
1484 CFStringRef hex
= CFDataCopyHexString(derdata
);
1485 secinfo("circledebug", "Full contents: %@", hex
);
1486 if (hex
) CFRelease(hex
);
1491 bool SOSCircleAcceptPeerFromHSA2(SOSCircleRef circle
, SecKeyRef userKey
, SOSGenCountRef gencount
, SecKeyRef pPubKey
, CFDataRef signature
, SOSFullPeerInfoRef fpi
, CFErrorRef
*error
) {
1492 SOSPeerInfoRef peerInfo
= SOSFullPeerInfoGetPeerInfo(fpi
);
1495 CFSetAddValue(circle
->peers
, peerInfo
);
1497 // Gen sign first, then add signature from our approver - remember gensign removes all existing sigs.
1498 res
= SOSCircleGenerationSignWithGenCount(circle
, userKey
, fpi
, gencount
, error
);
1500 secnotice("circleOps", "Failed to regenerate circle with new gen count: %@", error
? *error
: NULL
);
1503 res
= SOSCircleSetSignature(circle
, pPubKey
, signature
, error
);
1505 secnotice("circleOps", "Failed to set signature: %@", error
? *error
: NULL
);
1508 res
= SOSCircleVerify(circle
, pPubKey
, error
);
1510 secnotice("circleOps", "Circle failed to validate after peer signature: %@", error
? *error
: NULL
);
1513 secnotice("circleOps", "Circle accepted successfully");
1520 ccstatus: Not in Circle (1)
1521 Account user public is trusted
1522 Generation Count: [2016-05-19 15:53 4]
1526 static inline void logPeerInfo(char *category
, SOSCircleRef circle
, SecKeyRef pubKey
, CFStringRef myPID
, SOSPeerInfoRef peer
) {
1528 if (SOSCircleVerifyPeerSignatureExists(circle
, peer
)) {
1531 SOSPeerInfoLogState(category
, peer
, pubKey
, myPID
, sigchr
);
1534 void SOSCircleLogState(char *category
, SOSCircleRef circle
, SecKeyRef pubKey
, CFStringRef myPID
) {
1536 CFStringRef genString
= SOSGenerationCountCopyDescription(SOSCircleGetGeneration(circle
));
1538 if(pubKey
&& SOSCircleVerifySignatureExists(circle
, pubKey
, NULL
)) {
1541 secnotice(category
, "CIRCLE: [%20@] UserSigned: %c", genString
, sigchr
);
1542 if(CFSetGetCount(circle
->peers
) == 0 )
1543 secnotice(category
, "Peers In Circle: None");
1545 secnotice(category
, "Peers In Circle:");
1546 SOSCircleForEachPeer(circle
, ^(SOSPeerInfoRef peer
) {
1547 logPeerInfo(category
, circle
, pubKey
, myPID
, peer
);
1549 SOSCircleForEachRetiredPeer(circle
, ^(SOSPeerInfoRef peer
) {
1550 logPeerInfo(category
, circle
, pubKey
, myPID
, peer
);
1552 SOSCircleForEachiCloudIdentityPeer(circle
, ^(SOSPeerInfoRef peer
) {
1553 logPeerInfo(category
, circle
, pubKey
, myPID
, peer
);
1558 if(CFSetGetCount(circle
->applicants
) == 0 )
1559 secnotice(category
, "Applicants To Circle: None");
1561 secnotice(category
, "Applicants To Circle:");
1563 SOSCircleForEachApplicant(circle
, ^(SOSPeerInfoRef peer
) {
1564 SOSPeerInfoLogState(category
, peer
, pubKey
, myPID
, 'v');
1569 if(CFSetGetCount(circle
->rejected_applicants
) == 0)
1570 secnotice(category
, "Rejected Applicants To Circle: None");
1572 secnotice(category
, "Rejected Applicants To Circle:");
1573 CFSetForEach(circle
->rejected_applicants
, ^(const void *value
) {
1574 SOSPeerInfoRef peer
= (SOSPeerInfoRef
) value
;
1575 SOSPeerInfoLogState(category
, peer
, pubKey
, myPID
, 'v');
1578 CFReleaseNull(genString
);