]> git.saurik.com Git - apple/security.git/blame_incremental - sec/SOSCircle/Regressions/sc-101-accountsync.c
Security-55471.14.18.tar.gz
[apple/security.git] / sec / SOSCircle / Regressions / sc-101-accountsync.c
... / ...
CommitLineData
1//
2// sc-100-devicecircle.c
3// sec
4//
5// Created by John Hurley 10/16/12.
6// Copyright 2012 Apple Inc. All rights reserved.
7//
8
9/*
10 This test is a combination of sc-75, sc_93 and sc_94 that can
11 be run on two devices.
12
13 The test will look for a circle in kvs
14 - if none exists, it will create one
15 - if one exists, it will try to join
16
17 Whenever you confirm a new peer, must start sync
18
19 Test sc-98 can be run before this to clear out kvs
20*/
21
22// Run on 2 devices:
23// /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_101_accountsync -v -- -i alice
24// /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_101_accountsync -v -- -i bob
25
26#include <SecureObjectSync/SOSEngine.h>
27#include <SecureObjectSync/SOSPeer.h>
28
29#include "SOSCircle_regressions.h"
30
31#include <corecrypto/ccsha2.h>
32#include <Security/SecRandom.h>
33
34#include <utilities/SecCFWrappers.h>
35#include <utilities/debugging.h>
36#include <utilities/iOSforOSX.h>
37#include <stdint.h>
38
39#include <AssertMacros.h>
40
41#include <stdio.h>
42#include <stdlib.h>
43#include <unistd.h>
44#include <CoreFoundation/CFDate.h>
45#include <getopt.h>
46
47#include <Security/SecKey.h>
48
49#include <SecureObjectSync/SOSFullPeerInfo.h>
50#include <SecureObjectSync/SOSPeerInfo.h>
51#include <SecureObjectSync/SOSCircle.h>
52#include <SecureObjectSync/SOSCloudCircle.h>
53#include <SecureObjectSync/SOSInternal.h>
54#include <SecureObjectSync/SOSUserKeygen.h>
55
56#include "SOSCircle_regressions.h"
57#include "SOSRegressionUtilities.h"
58#include "SOSTestDataSource.h"
59#include "SOSTestTransport.h"
60#include "SOSCloudKeychainClient.h"
61
62#include <securityd/SOSCloudCircleServer.h>
63
64#ifndef SEC_CONST_DECL
65#define SEC_CONST_DECL(k,v) CFTypeRef k = (CFTypeRef)(CFSTR(v));
66#endif
67
68//static dispatch_queue_t wait_queue = NULL;
69
70#define VALUECFNULLCHECK(msg) if (msg == NULL || CFGetTypeID(msg) == CFNullGetTypeID()) { pass("CFNull message"); return 0; }
71
72// TODO _SecServerKeychainSyncUpdate
73
74// MARK: ----- Constants -----
75
76static CFStringRef circleKey = CFSTR("Circle");
77
78struct SOSKVSTransport {
79 struct SOSTransport t;
80 CFStringRef messageKey;
81};
82
83#include <notify.h>
84#include <dispatch/dispatch.h>
85
86static void putCircleInCloud(SOSCircleRef circle, dispatch_queue_t work_queue, dispatch_group_t work_group)
87{
88 CFErrorRef error = NULL;
89 CFDataRef newCloudCircleEncoded = SOSCircleCopyEncodedData(circle, kCFAllocatorDefault, &error);
90 ok(newCloudCircleEncoded, "Encoded as: %@ [%@]", newCloudCircleEncoded, error);
91
92 // Send the circle with our application request back to cloud
93 testPutObjectInCloud(circleKey, newCloudCircleEncoded, &error, work_group, work_queue);
94 CFReleaseSafe(newCloudCircleEncoded);
95}
96
97
98// artifacts of test harness : , dispatch_queue_t work_queue, dispatch_group_t work_group)
99bool SOSAccountEstablishCircle(SOSAccountRef account, CFStringRef circleName, CFErrorRef *error, dispatch_queue_t work_queue, dispatch_group_t work_group);
100
101bool SOSAccountEstablishCircle(SOSAccountRef account, CFStringRef circleName, CFErrorRef *error, dispatch_queue_t work_queue, dispatch_group_t work_group)
102{
103 CFErrorRef localError = NULL;
104 SOSCircleRef circle = SOSAccountEnsureCircle(account, circleName, NULL);
105 CFRetain(circle);
106 SecKeyRef user_privkey = SOSAccountGetPrivateCredential(account, &localError);
107
108 SOSFullPeerInfoRef our_full_peer_info = SOSAccountGetMyFullPeerInCircleNamed(account, circleName, &localError);
109 SOSPeerInfoRef our_peer_info = SOSFullPeerInfoGetPeerInfo(our_full_peer_info);
110 CFRetain(our_peer_info);
111
112 SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(our_full_peer_info, &localError);
113 ok(device_key, "Retrieved device_key from full peer info (Error: %@)", localError);
114 CFReleaseNull(device_key);
115 CFReleaseNull(localError);
116 ok(SOSCircleRequestAdmission(circle, user_privkey, our_full_peer_info, &localError), "Requested admission (%@)", our_peer_info);
117 ok(SOSCircleAcceptRequests(circle, user_privkey, our_full_peer_info, &localError), "Accepted self");
118
119 putCircleInCloud(circle, work_queue, work_group);
120 pass("Put (new) circle in cloud: (%@)", circle);
121
122#if 0
123
124
125 SOSCircleRef oldCircle = SOSAccountFindCircle(account, SOSCircleGetName(circle));
126
127 if (!oldCircle)
128 return false; // Can't update one we don't have.
129 // TODO: Ensure we don't let circles get replayed.
130
131 CFDictionarySetValue(account->circles, SOSCircleGetName(circle), circle);
132
133//----
134 SOSCircleRef circle = SOSAccountFindCircle(our_account, circleKey);
135 CFRetain(circle);
136
137 ok(SOSCircleRequestAdmission(circle, our_full_peer_info, user_key, &localError), "Requested admission (%@)", our_peer_info);
138 ok(SOSCircleAcceptRequests(circle, our_full_peer_info, user_key, &localError), "Accepted self");
139
140 putCircleInCloud(circle, work_queue, work_group);
141
142//-----
143#endif
144 return true;
145}
146
147
148static void runTests(bool Alice, dispatch_queue_t work_queue, dispatch_group_t work_group)
149{
150 CFStringRef our_name = Alice ? CFSTR("Alice") : CFSTR("Bob");
151 CFDictionaryRef our_gestalt = SOSCreatePeerGestaltFromName(our_name);
152
153 dispatch_queue_t global_queue = dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
154
155 CFErrorRef error = NULL;
156
157 CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
158
159 CFDataRef parameters = SOSUserKeyCreateGenerateParameters(&error);
160 ok(parameters, "No parameters!");
161 ok(error == NULL, "Error: (%@)", error);
162 CFReleaseNull(error);
163
164 SecKeyRef user_privkey = SOSUserKeygen(cfpassword, parameters, &error);
165 CFReleaseNull(parameters);
166 CFReleaseNull(cfpassword);
167
168 dispatch_semaphore_t start_semaphore = dispatch_semaphore_create(0);
169
170 CFStringRef sBobReady = CFSTR("Bob-Ready");
171 CFStringRef sAliceReady = CFSTR("Alice-Ready");
172 __block CFDataRef foundNonce = NULL;
173 if (Alice) {
174 const CFIndex nonceByteCount = 10;
175 CFMutableDataRef nonce = CFDataCreateMutable(kCFAllocatorDefault, nonceByteCount);
176 CFDataSetLength(nonce, nonceByteCount);
177 SecRandomCopyBytes(kSecRandomDefault, CFDataGetLength(nonce), CFDataGetMutableBytePtr(nonce));
178
179 CloudItemsChangedBlock notification_block = ^ (CFDictionaryRef returnedValues)
180 {
181 CFTypeRef bobReadyValue = CFDictionaryGetValue(returnedValues, sBobReady);
182 if (isData(bobReadyValue) && CFEqual(bobReadyValue, nonce)) {
183 CFDictionaryRef changes = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, sAliceReady, kCFNull, sBobReady, kCFNull, NULL);
184
185 SOSCloudKeychainPutObjectsInCloud(changes, global_queue, NULL);
186
187 pass("signalling");
188 dispatch_semaphore_signal(start_semaphore);
189 CFReleaseSafe(changes);
190 }
191 CFReleaseSafe(error);
192 };
193
194 CloudKeychainReplyBlock reply_block = ^ (CFDictionaryRef returnedValues, CFErrorRef error)
195 {
196 notification_block(returnedValues);
197 };
198
199 pass("Clearing");
200
201 testClearAll(global_queue, work_group);
202
203 CFArrayRef bobKey = CFArrayCreateForCFTypes(kCFAllocatorDefault, sBobReady, NULL);
204 SOSCloudKeychainRegisterKeysAndGet(bobKey, work_queue, reply_block, notification_block);
205
206 CFStringRef description = SOSInterestListCopyDescription(bobKey);
207 pass("%@", description);
208
209 CFReleaseNull(description);
210 CFReleaseNull(bobKey);
211
212 CFDictionaryRef changes = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, sAliceReady, nonce, NULL);
213 SOSCloudKeychainPutObjectsInCloud(changes, global_queue, NULL);
214
215 description = SOSChangesCopyDescription(changes, true);
216 pass("%@", description);
217 CFReleaseNull(description);
218
219 CFReleaseNull(changes);
220 } else {
221 CloudItemsChangedBlock notification_block = ^ (CFDictionaryRef returnedValues)
222 {
223 CFTypeRef aliceReadyValue = CFDictionaryGetValue(returnedValues, sAliceReady);
224 if (isData(aliceReadyValue)) {
225 foundNonce = (CFDataRef) aliceReadyValue;
226 CFRetain(foundNonce);
227
228 pass("signalling found: %@", foundNonce);
229 dispatch_semaphore_signal(start_semaphore);
230 }
231 CFReleaseSafe(error);
232 };
233
234 CloudKeychainReplyBlock reply_block = ^ (CFDictionaryRef returnedValues, CFErrorRef error)
235 {
236 notification_block(returnedValues);
237 };
238
239 CFArrayRef aliceKey = CFArrayCreateForCFTypes(kCFAllocatorDefault, sAliceReady, NULL);
240 SOSCloudKeychainRegisterKeysAndGet(aliceKey, work_queue, reply_block, notification_block);
241
242 CFStringRef description = SOSInterestListCopyDescription(aliceKey);
243 pass("%@", description);
244 CFReleaseNull(description);
245
246 CFReleaseSafe(aliceKey);
247 }
248
249 pass("Waiting");
250 dispatch_semaphore_wait(start_semaphore, DISPATCH_TIME_FOREVER);
251 pass("Moving on");
252
253
254 __block CFArrayRef ourWork = NULL;
255 __block CFIndex current = 0;
256 __block SOSAccountRef our_account = NULL;
257 typedef CFIndex (^TestStateBlock) (SOSAccountRef account, CFErrorRef error);
258
259 SOSDataSourceFactoryRef our_data_source_factory = SOSTestDataSourceFactoryCreate();
260 SOSDataSourceRef our_data_source = SOSTestDataSourceCreate();
261 SOSTestDataSourceFactoryAddDataSource(our_data_source_factory, circleKey, our_data_source);
262
263 CloudItemsChangedBlock notification_block = ^ (CFDictionaryRef returnedValues)
264 {
265 CFStringRef changesString = SOSChangesCopyDescription(returnedValues, false);
266 pass("Got: %@", changesString);
267 CFReleaseNull(changesString);
268
269 CFErrorRef error = NULL;
270
271 SOSAccountHandleUpdates(our_account, returnedValues, &error);
272
273 TestStateBlock thingToDo = CFArrayGetValueAtIndex(ourWork, current);
274
275 if (thingToDo)
276 {
277 pass("%@ stage %d rv: %@ [error: %@]", our_name, (int)current, returnedValues, error);
278 current += thingToDo(our_account, error);
279 }
280
281 if (current < 0 || current >= CFArrayGetCount(ourWork))
282 dispatch_group_leave(work_group);
283
284 CFReleaseSafe(error);
285 };
286
287 CloudKeychainReplyBlock reply_block = ^ (CFDictionaryRef returnedValues, CFErrorRef error)
288 {
289 pass("Reply block");
290 notification_block(returnedValues);
291 };
292
293 __block bool initialConnection = !Alice;
294 SOSAccountKeyInterestBlock updateKVSKeys = ^(bool getNewKeysOnly, CFArrayRef alwaysKeys, CFArrayRef afterFirstUnlockKeys, CFArrayRef unlockedKeys) {
295 CFMutableArrayRef keys = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, alwaysKeys);
296 CFArrayAppendArray(keys, afterFirstUnlockKeys, CFRangeMake(0, CFArrayGetCount(afterFirstUnlockKeys)));
297 CFArrayAppendArray(keys, unlockedKeys, CFRangeMake(0, CFArrayGetCount(unlockedKeys)));
298
299 CFStringRef description = SOSInterestListCopyDescription(keys);
300
301 pass("%@", description);
302
303 CFReleaseNull(description);
304
305 SOSCloudKeychainRegisterKeysAndGet(keys, work_queue,
306 initialConnection ? reply_block : NULL, notification_block);
307
308 CFReleaseSafe(keys);
309 initialConnection = false;
310 };
311
312 SOSAccountDataUpdateBlock updateKVS = ^ bool (CFDictionaryRef changes, CFErrorRef *error) {
313 CFStringRef changesString = SOSChangesCopyDescription(changes, true);
314 pass("Pushing: %@", changesString);
315 CFReleaseNull(changesString);
316
317 SOSCloudKeychainPutObjectsInCloud(changes, global_queue,
318 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
319 {
320 if (error) {
321 fail("testPutObjectInCloud returned: %@", error);
322 CFRelease(error);
323 }
324 });
325 return true;
326 };
327
328
329 our_account = SOSAccountCreate(kCFAllocatorDefault, our_gestalt, our_data_source_factory, updateKVSKeys, updateKVS);
330
331 SOSFullPeerInfoRef our_full_peer_info = SOSAccountGetMyFullPeerInCircleNamed(our_account, circleKey, &error);
332 SOSPeerInfoRef our_peer_info = SOSFullPeerInfoGetPeerInfo(our_full_peer_info);
333 CFRetain(our_peer_info);
334
335 SOSAccountAddChangeBlock(our_account, ^(SOSCircleRef circle,
336 CFArrayRef peer_additions, CFArrayRef peer_removals,
337 CFArrayRef applicant_additions, CFArrayRef applicant_removals) {
338 // Should initiate syncing here!
339 bool joined = CFArrayContainsValue(peer_additions, CFRangeMake(0, CFArrayGetCount(peer_additions)), our_peer_info);
340
341 pass("Peers Changed [%s] (Add: %@, Remove: %@)", joined ? "*** I'm in ***" : "Not including me.", peer_additions, peer_removals);
342
343 if (joined) {
344 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer_info)
345 {
346 CFErrorRef error = NULL;
347
348 if (!CFEqual(peer_info, our_peer_info)) {
349 ok(SOSAccountSyncWithPeer(our_account, circle, peer_info, NULL, &error),
350 "Initiated sync with %@: [Error %@]", peer_info, error);
351 }
352 });
353 }
354 });
355
356 ok(our_peer_info, "Peer Info: %@ [error: %@]", our_peer_info, error);
357
358 //__block SOSEngineRef ourEngine;
359
360 SOSObjectRef firstObject = SOSDataSourceCreateGenericItem(our_data_source, CFSTR("1234"), CFSTR("service"));
361
362 //------------------------------------------------------------------------
363 // ALICE
364 //------------------------------------------------------------------------
365 CFArrayRef aliceWorkToDo =
366 CFArrayCreateForCFTypes(kCFAllocatorDefault,
367 ^ CFIndex (SOSAccountRef account, CFErrorRef error)
368 {
369 /*
370 When we get here, it should only be because Bob has retrieved
371 our circle and requested admission to our circle.
372 If we don't find a circleKey entry, our test setup is wrong.
373 */
374 CFErrorRef modifyError = NULL;
375 SOSAccountModifyCircle(account, circleKey, &modifyError, ^(SOSCircleRef circle) {
376 CFErrorRef localError = NULL;
377
378 ok(SOSCircleHasPeer(circle, our_peer_info, &localError), "We're a peer [Error: %@]", localError);
379 is(SOSCircleCountPeers(circle), 1, "One peer, woot");
380 is(SOSCircleCountApplicants(circle), 1, "One applicant, hope it's BOB");
381
382 ok(SOSCircleAcceptRequests(circle, user_privkey, our_full_peer_info, &localError), "Accepted peers (%@) [Error: %@]", circle, localError);
383
384 CFReleaseSafe(localError);
385 });
386 CFReleaseSafe(modifyError);
387
388 return +1;
389 },
390 ^ CFIndex (SOSAccountRef account, CFErrorRef error)
391 {
392 // He should be telling us about him and we should be responding.
393
394 CFMutableDictionaryRef ourDatabase = SOSTestDataSourceGetDatabase(our_data_source);
395
396 is(CFDictionaryGetCount(ourDatabase), 0, "Database empty, we're synced");
397
398 pass("1");
399 return +1;
400 },
401 ^ CFIndex (SOSAccountRef account, CFErrorRef error)
402 {
403 CFMutableDictionaryRef ourDatabase = SOSTestDataSourceGetDatabase(our_data_source);
404
405 is(CFDictionaryGetCount(ourDatabase), 1, "One element!");
406
407 return +1;
408 },
409 NULL);
410
411 //------------------------------------------------------------------------
412 // BOB
413 //------------------------------------------------------------------------
414
415 CFArrayRef bobWorkToDo =
416 CFArrayCreateForCFTypes(kCFAllocatorDefault,
417 ^ CFIndex (SOSAccountRef account, CFErrorRef error)
418 {
419 __block CFIndex increment = 0;
420 CFErrorRef modifyError = NULL;
421 SOSAccountModifyCircle(account, circleKey, &modifyError, ^(SOSCircleRef circle) {
422 CFErrorRef localError = NULL;
423
424 if (SOSCircleCountPeers(circle) == 1) {
425 is(SOSCircleCountApplicants(circle), 0, "No applicants");
426 ok(SOSCircleRequestAdmission(circle, user_privkey, our_full_peer_info, &localError), "Requested admission (%@) [Error: %@]", circle, localError);
427 increment = +1;
428 }
429
430 CFReleaseSafe(localError);
431 });
432 CFReleaseSafe(modifyError);
433 return increment;
434 },
435 ^ CFIndex (SOSAccountRef account, CFErrorRef error)
436 {
437 CFErrorRef modifyError = NULL;
438 SOSAccountModifyCircle(account, circleKey, &modifyError, ^(SOSCircleRef circle) {
439 CFErrorRef localError = NULL;
440
441 ok(SOSCircleHasPeer(circle, our_peer_info, &localError), "We're a peer (%@) [Error: %@]", circle, localError);
442 is(SOSCircleCountPeers(circle), 2, "One peer, hope it's Alice");
443 is(SOSCircleCountApplicants(circle), 0, "No applicants");
444
445 CFReleaseSafe(localError);
446 });
447 CFReleaseSafe(modifyError);
448
449 return +1;
450 },
451 ^ CFIndex (SOSAccountRef account, CFErrorRef error)
452 {
453 CFErrorRef localError = NULL;
454 CFMutableDictionaryRef ourDatabase = SOSTestDataSourceGetDatabase(our_data_source);
455 is(CFDictionaryGetCount(ourDatabase), 0, "Database empty, we're synced");
456
457 SOSTestDataSourceAddObject(our_data_source, firstObject, &localError);
458 CFReleaseNull(localError);
459
460 SOSAccountSyncWithAllPeers(account, &localError);
461 CFReleaseNull(localError);
462
463 pass("1");
464 return +1;
465 },
466 ^ CFIndex (SOSAccountRef account, CFErrorRef error)
467 {
468 pass("2");
469
470 CFMutableDictionaryRef ourDatabase = SOSTestDataSourceGetDatabase(our_data_source);
471 is(CFDictionaryGetCount(ourDatabase), 1, "Still one element!");
472
473 return +1;
474 },
475 NULL);
476
477 //------------------------------------------------------------------------
478 // START
479 //------------------------------------------------------------------------
480
481 ourWork = Alice ? aliceWorkToDo : bobWorkToDo;
482
483 if (Alice) {
484 /*
485 Here we create a fresh circle, add and accept ourselves
486 Then we post to the cloud and wait for a Circle changed notification
487 */
488
489 CFErrorRef modifyError = NULL;
490 SOSAccountModifyCircle(our_account, circleKey, &modifyError, ^(SOSCircleRef circle) {
491 CFErrorRef localError = NULL;
492
493 ok(SOSCircleRequestAdmission(circle, user_privkey, our_full_peer_info, &localError), "Requested admission (%@) [error: %@]", our_peer_info, localError);
494 ok(SOSCircleAcceptRequests(circle, user_privkey, our_full_peer_info, &localError), "Accepted self [Error: %@]", localError);
495
496 CFReleaseSafe(localError);
497 });
498 CFReleaseSafe(modifyError);
499
500 } else {
501 // Tell alice we're set to go:
502 if (foundNonce) {
503 CFDictionaryRef changes = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, sBobReady, foundNonce, NULL);
504 SOSCloudKeychainPutObjectsInCloud(changes, global_queue, NULL);
505 CFReleaseSafe(changes);
506 } else {
507 fail("No none found to start the handshake");
508 }
509 }
510 dispatch_group_wait(work_group, DISPATCH_TIME_FOREVER);
511
512 // We probably never get here since the program exits..
513
514 CFReleaseNull(aliceWorkToDo);
515 CFReleaseNull(bobWorkToDo);
516 CFReleaseNull(our_peer_info);
517 CFReleaseNull(foundNonce);
518
519}
520
521// MARK: ----- start of all tests -----
522static void tests(bool Alice)
523{
524 dispatch_queue_t work_queue = dispatch_queue_create("NotificationQueue", DISPATCH_QUEUE_SERIAL); //;
525 dispatch_group_t work_group = dispatch_group_create();
526
527 // Prep the group for exitting the whole shebang.
528 runTests(Alice, work_queue, work_group);
529}
530
531// define the options table for the command line
532static const struct option options[] =
533{
534 { "verbose", optional_argument, NULL, 'v' },
535 { "identity", optional_argument, NULL, 'i' },
536 { "clear", optional_argument, NULL, 'C' },
537 { }
538};
539
540static int kAliceTestCount = 32;
541static int kBobTestCount = 30;
542
543int sc_101_accountsync(int argc, char *const *argv)
544{
545 char *identity = NULL;
546 extern char *optarg;
547 int arg, argSlot;
548 bool Alice = false;
549
550 while (argSlot = -1, (arg = getopt_long(argc, (char * const *)argv, "i:vC", options, &argSlot)) != -1)
551 switch (arg)
552 {
553 case 'i':
554 identity = (char *)(optarg);
555 break;
556 case 'C': // should set up to call testClearAll
557 break;
558 default:
559 secerror("arg: %s", optarg);
560 break;
561 }
562
563 if (identity)
564 {
565 secerror("We are %s",identity);
566 if (!strcmp(identity, "alice"))
567 Alice = true;
568 }
569
570 plan_tests(Alice?kAliceTestCount:kBobTestCount);
571 tests(Alice);
572
573 return 0;
574}