2 * Copyright (c) 2013-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@
25 #ifndef SEC_SOSAccountTesting_h
26 #define SEC_SOSAccountTesting_h
28 #include <CoreFoundation/CoreFoundation.h>
29 #include <Security/SecureObjectSync/SOSAccountPriv.h>
30 #include <Security/SecureObjectSync/SOSTransport.h>
31 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
32 #include <Security/SecureObjectSync/SOSPeerInfo.h>
33 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
34 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Expansion.h"
35 #import <Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h>
36 #import <Security/SecureObjectSync/SOSAccountTrustClassic+Identity.h>
37 #import <Security/SecureObjectSync/SOSAccountTrustClassic.h>
39 #include "SOSTestDataSource.h"
40 #include "SOSRegressionUtilities.h"
42 #include "SOSTransportTestTransports.h"
44 #include <utilities/SecCFWrappers.h>
46 // Implicit transaction helpers
49 static inline bool SOSAccountResetToOffering_wTxn(SOSAccount
* acct
, CFErrorRef
* error
)
51 __block
bool result
= false;
52 [acct performTransaction
:^(SOSAccountTransaction
* _Nonnull txn
) {
53 SecKeyRef user_key
= SOSAccountGetPrivateCredential(txn
.account
, error
);
56 result
= [acct
.trust resetToOffering
:txn key
:user_key err
:error
];
61 static inline bool SOSAccountJoinCirclesAfterRestore_wTxn(SOSAccount
* acct
, CFErrorRef
* error
)
63 __block
bool result
= false;
64 [acct performTransaction
:^(SOSAccountTransaction
* _Nonnull txn
) {
65 result
= SOSAccountJoinCirclesAfterRestore(txn
, error
);
70 static inline bool SOSAccountJoinCircles_wTxn(SOSAccount
* acct
, CFErrorRef
* error
)
72 __block
bool result
= false;
73 [acct performTransaction
:^(SOSAccountTransaction
* _Nonnull txn
) {
74 result
= SOSAccountJoinCircles(txn
, error
);
79 static inline bool SOSAccountCheckHasBeenInSync_wTxn(SOSAccount
* account
)
81 return SOSAccountHasCompletedInitialSync(account
);
84 static inline void SOSAccountPeerGotInSync_wTxn(SOSAccount
* acct
, SOSPeerInfoRef peer
)
86 [acct performTransaction
:^(SOSAccountTransaction
* _Nonnull txn
) {
87 CFMutableSetRef views
= SOSPeerInfoCopyEnabledViews(peer
);
88 SOSAccountPeerGotInSync(txn
, SOSPeerInfoGetPeerID(peer
), views
);
93 static inline bool SOSAccountSetBackupPublicKey_wTxn(SOSAccount
* acct
, CFDataRef backupKey
, CFErrorRef
* error
)
95 __block
bool result
= false;
96 [acct performTransaction
:^(SOSAccountTransaction
* _Nonnull txn
) {
97 result
= SOSAccountSetBackupPublicKey(txn
, backupKey
, error
);
102 static inline bool SOSAccountRemoveBackupPublickey_wTxn(SOSAccount
* acct
, CFErrorRef
* error
)
104 __block
bool result
= false;
105 [acct performTransaction
:^(SOSAccountTransaction
* _Nonnull txn
) {
106 result
= SOSAccountRemoveBackupPublickey(txn
, error
);
111 static inline SOSViewResultCode
SOSAccountUpdateView_wTxn(SOSAccount
* acct
, CFStringRef viewname
, SOSViewActionCode actionCode
, CFErrorRef
*error
) {
112 __block SOSViewResultCode result
= false;
113 [acct performTransaction
:^(SOSAccountTransaction
* _Nonnull txn
) {
114 result
= [acct
.trust updateView
:acct name
:viewname code
:actionCode err
:error
];
119 static inline bool SOSAccountSetMyDSID_wTxn(SOSAccount
* acct
, CFStringRef dsid
, CFErrorRef
* error
)
121 __block
bool result
= false;
122 [acct performTransaction
:^(SOSAccountTransaction
* _Nonnull txn
) {
123 result
= SOSAccountSetMyDSID(txn
, dsid
, error
);
129 // Account comparison
132 #define kAccountsAgreeTestMin 9
133 #define kAccountsAgreeTestPerPeer 1
134 #define accountsAgree(x) (kAccountsAgreeTestMin + kAccountsAgreeTestPerPeer * (x))
136 static void SOSAccountResetToTest(SOSAccount
* a
, CFStringRef accountName
) {
137 SOSUnregisterTransportKeyParameter(a
.key_transport
);
138 SOSUnregisterTransportCircle((SOSCircleStorageTransport
*)a
.circle_transport
);
139 SOSUnregisterTransportMessage((SOSMessage
*)a
.kvs_message_transport
);
140 SOSUnregisterTransportMessage((SOSMessage
*)a
.ids_message_transport
);
143 CFArrayRemoveAllValue(key_transports
, (__bridge CFTypeRef
)(a
.key_transport
));
144 if(message_transports
){
145 CFArrayRemoveAllValue(message_transports
, (__bridge CFTypeRef
)a
.ids_message_transport
);
146 CFArrayRemoveAllValue(message_transports
, (__bridge CFTypeRef
)a
.kvs_message_transport
);
148 if(circle_transports
)
149 CFArrayRemoveAllValue(circle_transports
, (__bridge CFTypeRef
)a
.circle_transport
);
151 a
.circle_transport
= nil
;
152 a
.key_transport
= nil
;
153 a
.ids_message_transport
= nil
;
154 a
.kvs_message_transport
= nil
;
156 SOSAccountEnsureFactoryCirclesTest(a
, accountName
);
160 static SOSAccount
* SOSAccountCreateBasicTest(CFAllocatorRef allocator
,
161 CFStringRef accountName
,
162 CFDictionaryRef gestalt
,
163 SOSDataSourceFactoryRef factory
) {
165 a
= SOSAccountCreate(kCFAllocatorDefault
, gestalt
, factory
);
170 static SOSAccount
* SOSAccountCreateTest(CFAllocatorRef allocator
,
171 CFStringRef accountName
,
172 CFDictionaryRef gestalt
,
173 SOSDataSourceFactoryRef factory
) {
174 SOSAccount
* a
= SOSAccountCreateBasicTest(allocator
, accountName
, gestalt
, factory
);
176 SOSAccountResetToTest(a
, accountName
);
178 SOSAccountInflateTestTransportsForCircle(a
, SOSCircleGetName([a
.trust getCircle
:NULL
]), accountName
, NULL
);
182 static SOSAccount
* SOSAccountCreateTestFromData(CFAllocatorRef allocator
,
184 CFStringRef accountName
,
185 SOSDataSourceFactoryRef factory
) {
186 SOSAccount
* a
= [SOSAccount accountFromData
:(__bridge NSData
*) data
190 CFDictionaryRef gestalt
= SOSCreatePeerGestaltFromName(accountName
);
191 a
= SOSAccountCreate(allocator
, gestalt
, factory
);
192 CFReleaseNull(gestalt
);
195 SOSAccountResetToTest(a
, accountName
);
197 SOSAccountInflateTestTransportsForCircle(a
, SOSCircleGetName([a
.trust getCircle
:NULL
]), accountName
, NULL
);
203 static inline bool SOSAccountAssertUserCredentialsAndUpdate(SOSAccount
* account
,
204 CFStringRef user_account
, CFDataRef user_password
,
207 bool success
= false;
208 success
= SOSAccountAssertUserCredentials(account
, user_account
, user_password
, error
);
209 require_quiet(success
, done
);
211 success
= SOSAccountGenerationSignatureUpdate(account
, error
);
219 static void unretired_peers_is_subset(const char* label
, CFArrayRef peers
, CFSetRef allowed_peers
)
221 CFArrayForEach(peers
, ^(const void *value
) {
222 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) value
;
224 CFErrorRef leftError
= NULL
;
225 CFErrorRef rightError
= NULL
;
227 ok(SOSPeerInfoIsRetirementTicket(pi
) || SOSPeerInfoIsCloudIdentity(pi
) || CFSetContainsValue(allowed_peers
, pi
), "Peer is allowed (%s) Peer: %@, Allowed %@", label
, pi
, allowed_peers
);
229 CFReleaseNull(leftError
);
230 CFReleaseNull(rightError
);
234 static void accounts_agree_internal(char *label
, SOSAccount
* left
, SOSAccount
* right
, bool check_peers
)
236 CFErrorRef error
= NULL
;
238 CFArrayRef leftPeers
= SOSAccountCopyActivePeers(left
, &error
);
239 ok(leftPeers
, "Left peers (%@) - %s", error
, label
);
240 CFReleaseNull(error
);
242 CFArrayRef rightPeers
= SOSAccountCopyActivePeers(right
, &error
);
243 ok(rightPeers
, "Right peers (%@) - %s", error
, label
);
244 CFReleaseNull(error
);
246 ok(CFEqual(leftPeers
, rightPeers
), "Matching peers (%s) Left: %@, Right: %@", label
, leftPeers
, rightPeers
);
249 CFMutableSetRef allowed_identities
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
251 SOSFullPeerInfoRef leftFullPeer
= [left
.trust CopyAccountIdentityPeerInfo
];
254 CFSetAddValue(allowed_identities
, SOSFullPeerInfoGetPeerInfo(leftFullPeer
));
257 SOSFullPeerInfoRef rightFullPeer
= [right
.trust CopyAccountIdentityPeerInfo
];
260 CFSetAddValue(allowed_identities
, SOSFullPeerInfoGetPeerInfo(rightFullPeer
));
262 unretired_peers_is_subset(label
, leftPeers
, allowed_identities
);
264 CFReleaseNull(allowed_identities
);
267 CFReleaseNull(leftPeers
);
268 CFReleaseNull(rightPeers
);
271 CFArrayRef leftConcurringPeers
= SOSAccountCopyConcurringPeers(left
, &error
);
272 ok(leftConcurringPeers
, "Left peers (%@) - %s", error
, label
);
274 CFArrayRef rightConcurringPeers
= SOSAccountCopyConcurringPeers(right
, &error
);
275 ok(rightConcurringPeers
, "Right peers (%@) - %s", error
, label
);
277 ok(CFEqual(leftConcurringPeers
, rightConcurringPeers
), "Matching concurring peers Left: %@, Right: %@", leftConcurringPeers
, rightConcurringPeers
);
279 CFReleaseNull(leftConcurringPeers
);
280 CFReleaseNull(rightConcurringPeers
);
283 CFArrayRef leftApplicants
= SOSAccountCopyApplicants(left
, &error
);
284 ok(leftApplicants
, "Left Applicants (%@) - %s", error
, label
);
286 CFArrayRef rightApplicants
= SOSAccountCopyApplicants(right
, &error
);
287 ok(rightApplicants
, "Left Applicants (%@) - %s", error
, label
);
289 ok(CFEqual(leftApplicants
, rightApplicants
), "Matching applicants (%s) Left: %@, Right: %@", label
, leftApplicants
, rightApplicants
);
291 CFReleaseNull(leftApplicants
);
292 CFReleaseNull(rightApplicants
);
296 static inline void accounts_agree(char *label
, SOSAccount
* left
, SOSAccount
* right
)
298 accounts_agree_internal(label
, left
, right
, true);
306 static inline CFStringRef
CFArrayCopyCompactDescription(CFArrayRef array
) {
308 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<Not an array! %@>"), array
);
310 CFMutableStringRef result
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("["));
312 __block CFStringRef separator
= CFSTR("");
313 CFArrayForEach(array
, ^(const void *value
) {
314 CFStringAppendFormat(result
, NULL
, CFSTR("%@%@"), separator
, value
);
315 separator
= CFSTR(",");
318 CFStringAppend(result
, CFSTR("]"));
320 CFReleaseSafe(separator
);
325 static inline CFStringRef
SOSAccountCopyName(SOSAccount
* account
) {
326 SOSPeerInfoRef pi
= account
.peerInfo
;
328 return pi
? CFStringCreateCopy(kCFAllocatorDefault
, SOSPeerInfoGetPeerName(pi
)) : CFStringCreateWithFormat(kCFAllocatorDefault
, 0, CFSTR("%@"), account
);
331 static inline CFStringRef
CopyChangesDescription(CFDictionaryRef changes
) {
333 CFStringRef pendingChanges
= CFDictionaryCopyCompactDescription((CFDictionaryRef
) CFDictionaryGetValue(changes
, kCFNull
));
335 CFMutableStringRef peerTable
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("["));
337 __block CFStringRef separator
= CFSTR("");
339 CFDictionaryForEach(changes
, ^(const void *key
, const void *value
) {
340 if (CFGetTypeID(key
) == SOSAccountGetTypeID()) {
341 CFStringRef accountName
= SOSAccountCopyName((__bridge SOSAccount
*)key
);
342 CFStringRef arrayDescription
= CFArrayCopyCompactDescription(value
);
344 CFStringAppendFormat(peerTable
, NULL
, CFSTR("%@%@:%@"), separator
, accountName
, arrayDescription
);
345 separator
= CFSTR(", ");
347 CFReleaseSafe(accountName
);
348 CFReleaseSafe(arrayDescription
);
352 CFStringAppend(peerTable
, CFSTR("]"));
354 CFStringRef result
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<TestChanges %@ %@>"), pendingChanges
, peerTable
);
355 CFReleaseNull(pendingChanges
);
356 CFReleaseNull(peerTable
);
361 static void CFDictionaryOverlayDictionary(CFMutableDictionaryRef target
, CFMutableDictionaryRef overlay
) {
362 CFMutableSetRef keysToRemove
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
364 CFDictionaryForEach(overlay
, ^(const void *key
, const void *value
) {
365 const void *current_value
= CFDictionaryGetValue(target
, key
);
366 if (CFEqualSafe(current_value
, value
) || (isNull(value
) && current_value
== NULL
)) {
367 CFSetAddValue(keysToRemove
, key
);
369 CFDictionarySetValue(target
, key
, value
);
373 CFSetForEach(keysToRemove
, ^(const void *value
) {
374 CFDictionaryRemoveValue(overlay
, value
);
377 CFReleaseNull(keysToRemove
);
380 static void CFArrayAppendKeys(CFMutableArrayRef keys
, CFDictionaryRef newKeysToAdd
) {
381 CFDictionaryForEach(newKeysToAdd
, ^(const void *key
, const void *value
) {
382 CFArrayAppendValue(keys
, key
);
386 static bool AddNewChanges(CFMutableDictionaryRef changesRecord
, CFMutableDictionaryRef newKeysAndValues
, SOSAccount
* sender
)
388 __block
bool changes_added
= false;
389 CFMutableDictionaryRef emptyDictionary
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
390 CFDictionaryAddValue(changesRecord
, kCFNull
, emptyDictionary
);
391 CFReleaseNull(emptyDictionary
);
393 CFDictionaryOverlayDictionary((CFMutableDictionaryRef
) CFDictionaryGetValue(changesRecord
, kCFNull
), newKeysAndValues
);
395 CFDictionaryForEach(changesRecord
, ^(const void *key
, const void *value
) {
396 if (isArray(value
) && (sender
== NULL
|| sender
!= key
)) {
397 CFArrayAppendKeys((CFMutableArrayRef
) value
, newKeysAndValues
);
398 if (CFDictionaryGetCount(newKeysAndValues
))
399 changes_added
= true;
404 secnotice("changes", "Changes from %@: %@", sender
, newKeysAndValues
);
406 CFDictionaryRemoveAllValues(newKeysAndValues
);
408 return changes_added
;
411 static bool FillAllChanges(CFMutableDictionaryRef changes
) {
412 __block
bool changed
= false;
414 CFMutableSetRef changedAccounts
= CFSetCreateMutable(kCFAllocatorDefault
, 0, NULL
);
416 CFArrayForEach(key_transports
, ^(const void *value
) {
417 CKKeyParameterTest
* tpt
= (__bridge CKKeyParameterTest
*) value
;
418 if (AddNewChanges(changes
, SOSTransportKeyParameterTestGetChanges(tpt
), SOSTransportKeyParameterTestGetAccount(tpt
))) {
420 CFSetAddValue(changedAccounts
, (__bridge CFTypeRef
)(SOSTransportKeyParameterTestGetAccount(tpt
)));
422 SOSTransportKeyParameterTestClearChanges(tpt
);
424 CFArrayForEach(circle_transports
, ^(const void *value
) {
425 SOSCircleStorageTransportTest
*tpt
= (__bridge SOSCircleStorageTransportTest
*) value
;
426 if (AddNewChanges(changes
, [tpt SOSTransportCircleTestGetChanges
], [tpt getAccount
])) {
428 CFSetAddValue(changedAccounts
, (__bridge CFTypeRef
)SOSTransportCircleTestGetAccount(tpt
));
430 SOSTransportCircleTestClearChanges(tpt
);
432 CFArrayForEach(message_transports
, ^(const void *value
) {
433 if([(__bridge SOSMessage
*)value SOSTransportMessageGetTransportType
] == kKVSTest
){
434 SOSMessageKVSTest
* tpt
= (__bridge SOSMessageKVSTest
*) value
;
435 CFDictionaryRemoveValue(SOSTransportMessageKVSTestGetChanges(tpt
), kCFNull
);
436 if (AddNewChanges(changes
, SOSTransportMessageKVSTestGetChanges(tpt
), SOSTransportMessageKVSTestGetAccount(tpt
))) {
438 CFSetAddValue(changedAccounts
, (__bridge CFTypeRef
)(SOSTransportMessageKVSTestGetAccount(tpt
)));
440 SOSTransportMessageTestClearChanges(tpt
);
442 else if([(__bridge SOSMessage
*)value SOSTransportMessageGetTransportType
] == kIDSTest
){
443 SOSMessageIDSTest
* ids
= (__bridge SOSMessageIDSTest
*) value
;
444 CFDictionaryRemoveValue(SOSTransportMessageIDSTestGetChanges(ids
), kCFNull
);
445 if (AddNewChanges(changes
, SOSTransportMessageIDSTestGetChanges(ids
), SOSTransportMessageIDSTestGetAccount(ids
))) {
447 CFSetAddValue(changedAccounts
, (__bridge CFTypeRef
)(SOSTransportMessageIDSTestGetAccount(ids
)));
449 SOSTransportMessageIDSTestClearChanges(ids
);
453 secnotice("process-changes", "Accounts with change (%@): %@", changed
? CFSTR("YES") : CFSTR("NO"), changedAccounts
);
455 CFReleaseNull(changedAccounts
);
460 static void FillChanges(CFMutableDictionaryRef changes
, SOSAccount
* forAccount
)
462 CFArrayForEach(key_transports
, ^(const void *value
) {
463 CKKeyParameterTest
* tpt
= (__bridge CKKeyParameterTest
*) value
;
464 if(CFEqualSafe((__bridge CFTypeRef
)(forAccount
), (__bridge CFTypeRef
)(SOSTransportKeyParameterTestGetAccount(tpt
)))){
465 AddNewChanges(changes
, SOSTransportKeyParameterTestGetChanges(tpt
), SOSTransportKeyParameterTestGetAccount(tpt
));
466 SOSTransportKeyParameterTestClearChanges(tpt
);
469 CFArrayForEach(circle_transports
, ^(const void *value
) {
470 SOSCircleStorageTransportTest
* tpt
= (__bridge SOSCircleStorageTransportTest
*) value
;
471 if([forAccount isEqual
: SOSTransportCircleTestGetAccount(tpt
)]){
472 AddNewChanges(changes
, [tpt SOSTransportCircleTestGetChanges
], SOSTransportCircleTestGetAccount(tpt
));
473 SOSTransportCircleTestClearChanges(tpt
);
476 CFArrayForEach(message_transports
, ^(const void *value
) {
477 if([(__bridge SOSMessage
*)value SOSTransportMessageGetTransportType
] == kKVSTest
){
478 SOSMessageKVSTest
* tpt
= (__bridge SOSMessageKVSTest
*) value
;
479 if(CFEqualSafe((__bridge CFTypeRef
)(forAccount
), (__bridge CFTypeRef
)(SOSTransportMessageKVSTestGetAccount(tpt
)))){
480 CFDictionaryRemoveValue(SOSTransportMessageKVSTestGetChanges(tpt
), kCFNull
);
481 AddNewChanges(changes
, SOSTransportMessageKVSTestGetChanges(tpt
), SOSTransportMessageKVSTestGetAccount(tpt
));
482 SOSTransportMessageTestClearChanges(tpt
);
486 SOSMessageIDSTest
* tpt
= (__bridge SOSMessageIDSTest
*) value
;
487 if(CFEqualSafe((__bridge CFTypeRef
)(forAccount
), (__bridge CFTypeRef
)(SOSTransportMessageIDSTestGetAccount(tpt
)))){
488 CFDictionaryRemoveValue(SOSTransportMessageIDSTestGetChanges(tpt
), kCFNull
);
489 AddNewChanges(changes
, SOSTransportMessageIDSTestGetChanges(tpt
), SOSTransportMessageIDSTestGetAccount(tpt
));
490 SOSTransportMessageIDSTestClearChanges(tpt
);
497 static inline void FillChangesMulti(CFMutableDictionaryRef changes
, SOSAccount
* account
, ...)
499 SOSAccount
* next_account
= account
;
501 va_start(argp
, account
);
502 while(next_account
!= NULL
) {
503 FillChanges(changes
, next_account
);
504 next_account
= va_arg(argp
, SOSAccount
*);
508 static inline CFMutableArrayRef
CFDictionaryCopyKeys(CFDictionaryRef dictionary
)
510 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
512 CFArrayAppendKeys(result
, dictionary
);
517 #define kFeedChangesToTestCount 1
518 static inline void FeedChangesTo(CFMutableDictionaryRef changes
, SOSAccount
* acct
)
520 CFDictionaryRef full_list
= (CFDictionaryRef
) CFDictionaryGetValue(changes
, kCFNull
);
522 if (!isDictionary(full_list
))
523 return; // Nothing recorded to send!
525 CFMutableArrayRef account_pending_keys
= (CFMutableArrayRef
)CFDictionaryGetValue(changes
, (__bridge CFTypeRef
)(acct
));
527 if (!isArray(account_pending_keys
)) {
528 account_pending_keys
= CFDictionaryCopyKeys(full_list
);
529 CFDictionaryAddValue(changes
, (__bridge CFTypeRef
)(acct
), account_pending_keys
);
530 CFReleaseSafe(account_pending_keys
); // The dictionary keeps it, we don't retain it here.
533 CFMutableDictionaryRef account_pending_messages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
534 CFArrayForEach(account_pending_keys
, ^(const void *value
) {
535 CFDictionaryAddValue(account_pending_messages
, value
, CFDictionaryGetValue(full_list
, value
));
538 secnotice("changes", "Changes for %@:", SOSTransportKeyParameterTestGetName((CKKeyParameterTest
*) acct
.key_transport
));
540 CFDictionaryForEach(account_pending_messages
, ^(const void *key
, const void *value
) {
541 secnotice("changes", " %@", key
);
544 if(CFDictionaryGetCount(account_pending_messages
) == 0)
547 __block CFMutableArrayRef handled
= NULL
;
548 [acct performTransaction
:^(SOSAccountTransaction
* _Nonnull txn
) {
549 __block CFErrorRef error
= NULL
;
550 ok(handled
= SOSTransportDispatchMessages(txn
, account_pending_messages
, &error
), "SOSTransportHandleMessages failed (%@)", error
);
551 CFReleaseNull(error
);
554 if (isArray(handled
)) {
555 CFArrayForEach(handled
, ^(const void *value
) {
556 CFArrayRemoveAllValue(account_pending_keys
, value
);
559 CFReleaseNull(account_pending_messages
);
560 CFReleaseNull(handled
);
563 #define kFeedChangesToMultieTestCountPer 1
565 static inline void FeedChangesToMultiV(CFMutableDictionaryRef changes
, va_list argp
)
567 SOSAccount
* account
= NULL
;
568 while((account
= va_arg(argp
, SOSAccount
*)) != NULL
) {
569 FeedChangesTo(changes
, account
);
573 static inline void FeedChangesToMulti(CFMutableDictionaryRef changes
, ...)
576 va_start(argp
, changes
);
578 FeedChangesToMultiV(changes
, argp
);
583 static inline void InjectChangeToMulti(CFMutableDictionaryRef changes
,
584 CFStringRef changeKey
, CFTypeRef changeValue
, ...)
586 CFMutableDictionaryRef changes_to_send
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
,
587 changeKey
, changeValue
,
589 AddNewChanges(changes
, changes_to_send
, NULL
);
590 CFReleaseNull(changes_to_send
);
593 va_start(argp
, changeValue
);
594 FeedChangesToMultiV(changes
, argp
);
598 static inline bool ProcessChangesOnceV(CFMutableDictionaryRef changes
, va_list argp
)
600 bool result
= FillAllChanges(changes
);
602 FeedChangesToMultiV(changes
, argp
);
608 static inline bool ProcessChangesOnce(CFMutableDictionaryRef changes
, ...)
611 va_start(argp
, changes
);
613 bool result
= ProcessChangesOnceV(changes
, argp
);
620 static inline int ProcessChangesUntilNoChange(CFMutableDictionaryRef changes
, ...)
623 va_start(argp
, changes
);
626 bool new_data
= false;
629 va_copy(argp_copy
, argp
);
631 new_data
= ProcessChangesOnceV(changes
, argp_copy
);
645 // MARK: Account creation
648 static CFStringRef
modelFromType(SOSPeerInfoDeviceClass cls
) {
650 case SOSPeerInfo_macOS
: return CFSTR("Mac Pro");
651 case SOSPeerInfo_iOS
: return CFSTR("iPhone");
652 case SOSPeerInfo_iCloud
: return CFSTR("iCloud");
653 case SOSPeerInfo_watchOS
: return CFSTR("needWatchOSDeviceName");
654 case SOSPeerInfo_tvOS
: return CFSTR("needTVOSDeviceName");
655 default: return CFSTR("GENERICOSTHING");
659 static inline SOSAccount
* CreateAccountForLocalChangesWithStartingAttributes(CFStringRef name
, CFStringRef data_source_name
, SOSPeerInfoDeviceClass devclass
, CFStringRef serial
, CFBooleanRef preferIDS
, CFBooleanRef preferIDSFragmentation
, CFBooleanRef preferIDSACKModel
, CFStringRef transportType
, CFStringRef deviceID
) {
661 SOSDataSourceFactoryRef factory
= SOSTestDataSourceFactoryCreate();
662 SOSDataSourceRef ds
= SOSTestDataSourceCreate();
663 SOSTestDataSourceFactorySetDataSource(factory
, data_source_name
, ds
);
664 SOSEngineRef engine
= SOSEngineCreate(ds
, NULL
);
667 CFMutableDictionaryRef gestalt
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
668 CFDictionaryAddValue(gestalt
, kPIUserDefinedDeviceNameKey
, name
);
669 CFDictionaryAddValue(gestalt
, kPIDeviceModelNameKey
, modelFromType(devclass
));
670 CFDictionaryAddValue(gestalt
, kPIOSVersionKey
, CFSTR("TESTRUN"));
672 CFMutableDictionaryRef testV2dict
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
673 CFDictionaryAddValue(testV2dict
, sSerialNumberKey
, serial
);
674 CFDictionaryAddValue(testV2dict
, sPreferIDS
, preferIDS
);
675 CFDictionaryAddValue(testV2dict
, sPreferIDSFragmentation
, preferIDSFragmentation
);
676 CFDictionaryAddValue(testV2dict
, sPreferIDSACKModel
, preferIDSACKModel
);
677 CFDictionaryAddValue(testV2dict
, sTransportType
, transportType
);
678 CFDictionaryAddValue(testV2dict
, sDeviceID
, deviceID
);
679 SOSAccount
* result
= SOSAccountCreateTest(kCFAllocatorDefault
, name
, gestalt
, factory
);
680 [result
.trust updateV2Dictionary
:result v2
:testV2dict
];
682 CFReleaseSafe(SOSAccountCopyUUID(result
));
684 CFReleaseNull(gestalt
);
685 CFReleaseNull(testV2dict
);
690 static CFStringRef sGestaltTest
= CFSTR("GestaltTest");
691 static CFStringRef sV2Test
= CFSTR("V2Test");
692 static inline CFDictionaryRef
SOSTestSaveStaticAccountState(SOSAccount
* account
) {
693 CFMutableDictionaryRef retval
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
694 CFDictionaryRef gestalt
= SOSAccountCopyGestalt(account
);
695 CFDictionaryRef v2dictionary
= SOSAccountCopyV2Dictionary(account
);
696 CFDictionaryAddValue(retval
, sGestaltTest
, gestalt
);
697 CFDictionaryAddValue(retval
, sV2Test
, v2dictionary
);
698 CFReleaseNull(gestalt
);
699 CFReleaseNull(v2dictionary
);
703 static inline void SOSTestRestoreAccountState(SOSAccount
* account
, CFDictionaryRef saved
) {
704 [account
.trust updateGestalt
:account newGestalt
:CFDictionaryGetValue(saved
, sGestaltTest
)];
705 [account
.trust updateV2Dictionary
:account v2
:CFDictionaryGetValue(saved
, sV2Test
)];
708 static CFStringRef
CFStringCreateRandomHexWithLength(size_t len
) {
710 CFDataRef data
= CFDataCreateWithRandomBytes(len
/2);
711 CFMutableStringRef retval
= CFStringCreateMutable(kCFAllocatorDefault
, len
);
712 CFStringAppendHexData(retval
, data
);
717 static inline SOSAccount
* CreateAccountForLocalChanges(CFStringRef name
, CFStringRef data_source_name
)
719 CFStringRef randomSerial
= CFStringCreateRandomHexWithLength(8);
720 CFStringRef randomDevID
= CFStringCreateRandomHexWithLength(16);
721 SOSAccount
* retval
= CreateAccountForLocalChangesWithStartingAttributes(name
, data_source_name
, SOSPeerInfo_iOS
, randomSerial
,
722 kCFBooleanTrue
, kCFBooleanTrue
, kCFBooleanTrue
, SOSTransportMessageTypeIDSV2
, randomDevID
);
724 CFReleaseNull(randomSerial
);
725 CFReleaseNull(randomDevID
);
729 static inline SOSAccount
* CreateAccountForLocalChangesFromData(CFDataRef flattenedData
, CFStringRef name
, CFStringRef data_source_name
)
731 SOSDataSourceFactoryRef factory
= SOSTestDataSourceFactoryCreate();
732 SOSDataSourceRef ds
= SOSTestDataSourceCreate();
733 SOSTestDataSourceFactorySetDataSource(factory
, data_source_name
, ds
);
734 SOSEngineRef engine
= SOSEngineCreate(ds
, NULL
);
737 SOSAccount
* result
= SOSAccountCreateTestFromData(kCFAllocatorDefault
, flattenedData
, name
, factory
);
744 static inline int countPeers(SOSAccount
* account
) {
745 CFErrorRef error
= NULL
;
748 peers
= SOSAccountCopyPeers(account
, &error
);
751 int retval
= (int) CFArrayGetCount(peers
);
752 CFReleaseNull(error
);
753 CFReleaseNull(peers
);
757 static inline int countActivePeers(SOSAccount
* account
) {
758 CFErrorRef error
= NULL
;
761 peers
= SOSAccountCopyActivePeers(account
, &error
);
764 int retval
= (int) CFArrayGetCount(peers
);
765 CFReleaseNull(error
);
766 CFReleaseNull(peers
);
770 static inline int countActiveValidPeers(SOSAccount
* account
) {
771 CFErrorRef error
= NULL
;
774 peers
= SOSAccountCopyActiveValidPeers(account
, &error
);
777 int retval
= (int) CFArrayGetCount(peers
);
778 CFReleaseNull(error
);
779 CFReleaseNull(peers
);
783 static inline int countApplicants(SOSAccount
* account
) {
784 CFErrorRef error
= NULL
;
785 CFArrayRef applicants
= SOSAccountCopyApplicants(account
, &error
);
788 if(applicants
) retval
= (int)CFArrayGetCount(applicants
);
789 CFReleaseNull(error
);
790 CFReleaseNull(applicants
);
795 static inline void showActiveValidPeers(SOSAccount
* account
) {
796 CFErrorRef error
= NULL
;
799 peers
= SOSAccountCopyActiveValidPeers(account
, &error
);
800 CFArrayForEach(peers
, ^(const void *value
) {
801 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) value
;
802 ok(0, "Active Valid Peer %@", pi
);
804 CFReleaseNull(peers
);
807 #define ok_or_quit(COND,MESSAGE,LABEL) ok(COND, MESSAGE); if(!(COND)) goto LABEL
809 static inline bool testAccountPersistence(SOSAccount
* account
) {
810 SOSDataSourceFactoryRef test_factory
= SOSTestDataSourceFactoryCreate();
811 SOSDataSourceRef test_source
= SOSTestDataSourceCreate();
812 SOSTestDataSourceFactorySetDataSource(test_factory
, CFSTR("TestType"), test_source
);
813 NSError
* error
= nil
;
816 SOSAccount
* reinflatedAccount
= NULL
;
817 NSData
* accountDER
= NULL
;
819 SOSAccountCheckHasBeenInSync_wTxn(account
);
821 // DER encode account to accountData - this allows checking discreet DER functions
822 size_t size
= [account
.trust getDEREncodedSize
:account err
:&error
];
824 uint8_t buffer
[size
];
825 uint8_t* start
= [account
.trust encodeToDER
:account err
:&error start
:buffer end
:buffer
+ sizeof(buffer
)];
828 ok_or_quit(start
, "successful encoding", errOut
);
829 ok_or_quit(start
== buffer
, "Used whole buffer", errOut
);
831 accountDER
= [NSData dataWithBytes
:buffer length
:size
];
832 ok_or_quit(accountDER
, "Made CFData for Account", errOut
);
835 // Re-inflate to "inflated"
836 reinflatedAccount
= [SOSAccount accountFromData
:accountDER
841 ok(reinflatedAccount
, "inflated");
842 ok(CFEqualSafe((__bridge CFTypeRef
)reinflatedAccount
, (__bridge CFTypeRef
)account
), "Compares");
844 // Repeat through SOSAccountCopyEncodedData() interface - this is the normally called combined interface
845 accountDER
= [reinflatedAccount encodedData
:&error
];
847 reinflatedAccount
= [SOSAccount accountFromData
:accountDER factory
:test_factory error
:&error
];
848 ok(reinflatedAccount
, "inflated2: %@", error
);
849 ok(CFEqual((__bridge CFTypeRef
)account
, (__bridge CFTypeRef
)reinflatedAccount
), "Compares");
858 static inline bool SOSTestStartCircleWithAccount(SOSAccount
* account
, CFMutableDictionaryRef changes
, CFStringRef cfaccount
, CFDataRef cfpassword
) {
860 if(!SOSAccountAssertUserCredentialsAndUpdate(account
, cfaccount
, cfpassword
, NULL
))
862 is(ProcessChangesUntilNoChange(changes
, account
, NULL
), 1, "updates");
863 if(!SOSAccountResetToOffering_wTxn(account
, NULL
))
865 is(ProcessChangesUntilNoChange(changes
, account
, NULL
), 1, "updates");
871 static inline bool SOSTestApproveRequest(SOSAccount
* approver
, CFIndex napplicants
) {
873 CFErrorRef error
= NULL
;
874 CFArrayRef applicants
= SOSAccountCopyApplicants(approver
, &error
);
876 ok(applicants
&& CFArrayGetCount(applicants
) == napplicants
, "See %ld applicant(s) %@ (%@)", napplicants
, applicants
, error
);
877 CFStringRef approvername
= SOSAccountCopyName(approver
);
878 ok((retval
= SOSAccountAcceptApplicants(approver
, applicants
, &error
)), "%@ accepts (%@)", approvername
, error
);
879 CFReleaseNull(error
);
880 CFReleaseNull(applicants
);
881 CFReleaseNull(approvername
);
886 #define DROP_USERKEY true
887 #define KEEP_USERKEY false
889 static inline bool SOSTestJoinWith(CFDataRef cfpassword
, CFStringRef cfaccount
, CFMutableDictionaryRef changes
, SOSAccount
* joiner
) {
890 CFErrorRef error
= NULL
;
891 // retval will return op failures, not count failures - we'll still report those from in here.
894 FeedChangesTo(changes
, joiner
);
896 ok(SOSAccountAssertUserCredentialsAndUpdate(joiner
, cfaccount
, cfpassword
, &error
), "Credential setting (%@)", error
);
897 CFReleaseNull(error
);
899 ProcessChangesUntilNoChange(changes
, joiner
, NULL
);
901 ok(retval
= SOSAccountJoinCircles_wTxn(joiner
, &error
), "Applying (%@)", error
);
902 CFReleaseNull(error
);
906 static inline bool SOSTestJoinWithApproval(CFDataRef cfpassword
, CFStringRef cfaccount
, CFMutableDictionaryRef changes
, SOSAccount
* approver
, SOSAccount
* joiner
, bool dropUserKey
, int expectedCount
, bool expectCleanup
) {
907 //CFErrorRef error = NULL;
908 // retval will return op failures, not count failures - we'll still report those from in here.
911 ok(retval
= SOSTestJoinWith(cfpassword
, cfaccount
, changes
, joiner
), "Applyication Made");
913 is(ProcessChangesUntilNoChange(changes
, approver
, joiner
, NULL
), 2, "updates");
916 if(dropUserKey
) SOSAccountPurgePrivateCredential(joiner
); // lose the userKey so we don't "fix" the ghost problem yet.
919 if(expectCleanup
) nrounds
++;
921 ok(retval
&= SOSTestApproveRequest(approver
, 1), "Accepting Request to Join");
922 is(ProcessChangesUntilNoChange(changes
, approver
, joiner
, NULL
), nrounds
, "updates");
924 accounts_agree_internal("Successful join shows same circle view", joiner
, approver
, false);
925 is(countPeers(joiner
), expectedCount
, "There should be %d valid peers", expectedCount
);
930 static inline SOSAccount
* SOSTestCreateAccountAsSerialClone(CFStringRef name
, SOSPeerInfoDeviceClass devClass
, CFStringRef serial
, CFStringRef idsID
) {
931 return CreateAccountForLocalChangesWithStartingAttributes(name
, CFSTR("TestSource"), devClass
, serial
, kCFBooleanTrue
, kCFBooleanTrue
, kCFBooleanTrue
, SOSTransportMessageTypeIDSV2
, idsID
);
934 static inline bool SOSTestMakeGhostInCircle(CFStringRef name
, SOSPeerInfoDeviceClass devClass
, CFStringRef serial
, CFStringRef idsID
,
935 CFDataRef cfpassword
, CFStringRef cfaccount
, CFMutableDictionaryRef changes
,
936 SOSAccount
* approver
, int expectedCount
) {
938 SOSAccount
* ghostAccount
= SOSTestCreateAccountAsSerialClone(name
, devClass
, serial
, idsID
);
939 ok(ghostAccount
, "Created Ghost Account");
940 require_quiet(ghostAccount
, retOut
);
941 if(!ghostAccount
) return false;
942 ok(retval
= SOSTestJoinWithApproval(cfpassword
, cfaccount
, changes
, approver
, ghostAccount
, DROP_USERKEY
, expectedCount
, true), "Ghost Joined Circle with expected result");
947 static inline bool SOSTestChangeAccountDeviceName(SOSAccount
* account
, CFStringRef name
) {
949 CFMutableDictionaryRef mygestalt
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, SOSPeerGetGestalt(account
.peerInfo
));
950 require_quiet(mygestalt
, retOut
);
951 CFDictionarySetValue(mygestalt
, kPIUserDefinedDeviceNameKey
, name
);
952 retval
= [account
.trust updateGestalt
:account newGestalt
:mygestalt
];
954 CFReleaseNull(mygestalt
);
958 static inline void SOSTestCleanup() {
959 SOSUnregisterAllTransportMessages();
960 SOSUnregisterAllTransportCircles();
961 SOSUnregisterAllTransportKeyParameters();
962 CFArrayRemoveAllValues(key_transports
);
963 CFArrayRemoveAllValues(circle_transports
);
964 CFArrayRemoveAllValues(message_transports
);