]> git.saurik.com Git - apple/security.git/blob - sec/SOSCircle/Regressions/sc-100-devicecircle.c
Security-55471.14.8.tar.gz
[apple/security.git] / sec / SOSCircle / Regressions / sc-100-devicecircle.c
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_100_devicecircle -v -- -i alice
24 // /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_100_devicecircle -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
33 #include <utilities/SecCFWrappers.h>
34 #include <utilities/debugging.h>
35
36 #include <stdint.h>
37
38 #include <AssertMacros.h>
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <CoreFoundation/CFDate.h>
44 #include <getopt.h>
45
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
76 static CFStringRef circleKey = CFSTR("Circle");
77 //static CFStringRef messageKey = CFSTR("Message");
78 static CFStringRef messageFromAliceToBobKey = CFSTR("AliceToBob");
79 static CFStringRef messageFromBobToAliceKey = CFSTR("BobToAlice");
80
81 #if USE_BOB_GO
82 static CFStringRef messageGoBobGoKey = CFSTR("GoBobGo");
83 #endif
84
85 struct SOSKVSTransport {
86 struct SOSTransport t;
87 CFStringRef messageKey;
88 };
89
90 #include <notify.h>
91 #include <dispatch/dispatch.h>
92
93 static bool kvsTransportSend(CFStringRef key, CFDataRef message) {
94 __block bool success = true;
95 __block dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
96
97 CFDictionaryRef objects = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, key, message, NULL);
98
99 CFStringRef desc = SOSMessageCopyDescription(message);
100 pass("kvsTransportSend: %@ %@", key, desc);
101 CFReleaseSafe(desc);
102
103 SOSCloudKeychainPutObjectsInCloud(objects, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
104 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
105 {
106 ok(error == NULL, "Error from SOSCloudKeychainPutObjectsInCloud %@:", error);
107 if (error)
108 success = false;
109 dispatch_semaphore_signal(waitSemaphore);
110 });
111
112 dispatch_release(waitSemaphore);
113
114 return success;
115 }
116
117 static void putCircleInCloud(SOSCircleRef circle, dispatch_queue_t work_queue, dispatch_group_t work_group)
118 {
119 CFErrorRef error = NULL;
120 CFDataRef newCloudCircleEncoded = SOSCircleCopyEncodedData(circle, kCFAllocatorDefault, &error);
121 ok(newCloudCircleEncoded, "Encoded as: %@ [%@]", newCloudCircleEncoded, error);
122
123 // Send the circle with our application request back to cloud
124 testPutObjectInCloud(circleKey, newCloudCircleEncoded, &error, work_group, work_queue);
125 CFReleaseSafe(newCloudCircleEncoded);
126 }
127
128 static void mutate_inflated_circle(SOSCircleRef newCircle,
129 dispatch_queue_t work_queue, dispatch_group_t work_group,
130 void (^action)(SOSCircleRef circle))
131 {
132 //JCH action(newCircle);
133
134 putCircleInCloud(newCircle, work_queue, work_group);
135 pass("Put circle in cloud: (%@)", newCircle);
136 }
137
138 static void mutate_account_circle(SOSAccountRef account,
139 CFDataRef encodedCircle,
140 dispatch_queue_t work_queue, dispatch_group_t work_group,
141 void (^action)(SOSCircleRef circle))
142 {
143 bool mutated = false;
144
145 CFErrorRef error = NULL;
146 SKIP:
147 {
148 skip("Must be CFData!", 3, encodedCircle && (CFDataGetTypeID() == CFGetTypeID(encodedCircle)));
149 SOSCircleRef newCircle = SOSCircleCreateFromData(kCFAllocatorDefault, encodedCircle, &error);
150 ok(newCircle, "Decoded data version of circle: %@", newCircle);
151
152 SOSCircleRef accountCircle = SOSAccountFindCircle(account, SOSCircleGetName(newCircle), NULL);
153 ok(accountCircle, "Found our circle in account");
154
155 if (!CFEqual(newCircle, accountCircle)) {
156 pass("New circle and accountCircle not equal");
157
158 // JCH JCH
159 action(newCircle);
160
161 bool updated = SOSAccountUpdateCircle(account, newCircle, &error);
162 if (updated) {
163 pass("Updated account with new circle");
164 mutated = true;
165 mutate_inflated_circle(newCircle, work_queue, work_group, action);
166 }
167 }
168 }
169 pass("mutate_account_circle exit");
170 }
171
172 static void SOSCloudKeychainRegisterKeysAndGetWithNotification(CFArrayRef keysToRegister, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
173 {
174 SOSCloudKeychainRegisterKeysAndGet(keysToRegister, processQueue, replyBlock,
175 ^(CFDictionaryRef dict) { replyBlock(dict, NULL);});
176 }
177
178 #define kAlicePeerID CFSTR("alice-peer-id")
179 #define kBobPeerID CFSTR("bob-peer-id")
180
181 static void runTests(bool Alice, dispatch_queue_t work_queue, dispatch_group_t work_group)
182 {
183 SecKeyRef user_key = NULL;
184 SecKeyRef public_key = NULL;
185 CFErrorRef error = NULL;
186 CFStringRef cflabel = CFSTR("TEST_USERKEY");
187 CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
188
189 GenerateECPair(256, &public_key, &user_key);
190 SOSCCRegisterUserCredentials(cflabel, cfpassword, &error);
191 CFReleaseNull(public_key);
192 CFReleaseNull(cfpassword);
193
194 SOSDataSourceFactoryRef our_data_source_factory = SOSTestDataSourceFactoryCreate();
195 SOSDataSourceRef our_data_source = SOSTestDataSourceCreate();
196 SOSTestDataSourceFactoryAddDataSource(our_data_source_factory, circleKey, our_data_source);
197
198 CFDictionaryRef gestalt = SOSCreatePeerGestaltFromName(Alice? kAlicePeerID: kBobPeerID);
199
200 SOSAccountRef our_account = SOSAccountCreate(kCFAllocatorDefault, gestalt, our_data_source_factory, NULL, NULL);
201 SOSAccountEnsureCircle(our_account, circleKey, NULL);
202
203 SOSFullPeerInfoRef our_full_peer_info = SOSAccountGetMyFullPeerInCircleNamed(our_account, circleKey, &error);
204 __block SOSPeerInfoRef our_peer_info = SOSFullPeerInfoGetPeerInfo(our_full_peer_info);
205 CFRetain(our_peer_info);
206 pass("We are %s (%@)", Alice?"Alice":"Bob", our_peer_info);
207
208 ok(our_peer_info, "Peer Info: %@ [error: %@]", our_peer_info, error);
209
210 /// Setup ck notifications.
211
212 CFArrayRef keysToRegister = NULL;
213
214 CFStringRef message_key;
215
216 if (Alice)
217 {
218 keysToRegister = CFArrayCreateForCFTypes(kCFAllocatorDefault, circleKey, messageFromBobToAliceKey, NULL);
219 message_key = messageFromAliceToBobKey;
220 }
221 else
222 {
223 #if USE_BOB_GO
224 keysToRegister = CFArrayCreateForCFTypes(kCFAllocatorDefault, circleKey, messageFromAliceToBobKey, messageGoBobGoKey, NULL);
225 #else
226 keysToRegister = CFArrayCreateForCFTypes(kCFAllocatorDefault, circleKey, messageFromAliceToBobKey, NULL);
227 #endif
228 message_key = messageFromBobToAliceKey;
229 }
230
231 SOSPeerSendBlock transport = ^bool (CFDataRef message, CFErrorRef *error) {
232 kvsTransportSend(message_key, message);
233 return true;
234 };
235
236
237 __block CFIndex current = 0;
238 typedef CFIndex (^TestStateBlock) (CFDictionaryRef returnedValues, CFErrorRef error);
239
240 //------------------------------------------------------------------------
241 // ALICE
242 //------------------------------------------------------------------------
243 CFArrayRef aliceWorkToDo =
244 CFArrayCreateForCFTypes(kCFAllocatorDefault,
245 ^ CFIndex (CFDictionaryRef returnedValues, CFErrorRef error)
246 {
247 /*
248 We get here as the return from registerkeys.
249 Here we create a fresh circle, add and accept ourselves
250 Then we post to the cloud and wait for a Circle changed notification
251 */
252
253 (void) returnedValues; (void) error;
254
255 testClearAll(work_queue, work_group);
256
257 CFErrorRef localError = NULL;
258
259 #if USE_BOB_GO
260 testPutObjectInCloud(messageGoBobGoKey, CFSTR("Go Bob, Go!"), &localError, work_group, work_queue);
261 #endif
262
263 SOSCircleRef circle = SOSAccountFindCircle(our_account, circleKey, NULL);
264 CFRetain(circle);
265
266 ok(SOSCircleRequestAdmission(circle, SOSAccountGetPrivateCredential(our_account, &error), our_full_peer_info, &localError), "Requested admission (%@)", our_peer_info);
267 ok(SOSCircleAcceptRequests(circle, SOSAccountGetPrivateCredential(our_account, &error), our_full_peer_info, &localError), "Accepted self");
268
269 our_peer_info = SOSFullPeerInfoGetPeerInfo(our_full_peer_info);
270
271 putCircleInCloud(circle, work_queue, work_group);
272 pass("Put circle in cloud: (%@)", circle);
273
274 CFRelease(circle);
275
276 return +1;
277 },
278 ^ CFIndex (CFDictionaryRef returnedValues, CFErrorRef error)
279 {
280 __block CFIndex incrementAmount = 0; // Don't increment unless we find stuff
281
282 /*
283 When we get here, it should only be because Bob has retrieved
284 our circle and requested admission to our circle.
285 If we don't find a circleKey entry, our test setup is wrong.
286 */
287 SKIP:
288 {
289 __block CFErrorRef localError = NULL;
290 CFDataRef value = CFDictionaryGetValue(returnedValues, circleKey);
291 VALUECFNULLCHECK(value);
292 skip("cloudCircle NULL!", 5, value);
293 ok(value, "Found circle");
294
295 mutate_account_circle(our_account, value, work_queue, work_group,
296 ^ (SOSCircleRef circle)
297 {
298 ok(SOSCircleHasPeer(circle, our_peer_info, &localError), "We're a peer [error: %@]", localError);
299 CFReleaseNull(localError);
300
301 is(SOSCircleCountPeers(circle), 1, "One peer, woot");
302 is(SOSCircleCountApplicants(circle), 1, "One applicant, hope it's BOB");
303
304 CFErrorRef pkerr;
305 ok(SOSCircleAcceptRequests(circle, SOSAccountGetPrivateCredential(our_account, &pkerr), our_full_peer_info, &localError), "Accepted peers [error: %@]", localError);
306 CFReleaseNull(localError);
307
308 ok(SOSCircleSyncWithPeer(our_full_peer_info, circle, our_data_source_factory, transport, kBobPeerID, &localError), "Started sync: [error: %@]", localError);
309 CFReleaseNull(localError);
310
311 incrementAmount = 1;
312 });
313
314 CFReleaseSafe(localError);
315 }
316
317 return incrementAmount;
318 },
319 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
320 {
321 CFErrorRef localError = NULL;
322 SOSCircleRef circle = SOSAccountFindCircle(our_account, circleKey, NULL);
323 CFDataRef message = CFDictionaryGetValue(returnedValues, messageFromBobToAliceKey);
324 VALUECFNULLCHECK(message);
325
326 ok(message, "Saw response to manifest message from Bob");
327 ok(SOSCircleHandlePeerMessage(circle, our_full_peer_info, our_data_source_factory, transport, kBobPeerID, message, &localError), "handle message from bob: [error: %@]", localError);
328
329 #if TEST_EMPTY_ADD
330 // Sync again after adding an empty object
331 CFDictionaryRef object = CFDictionaryCreate(0, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
332 ok(SOSTestDataSourceAddObject(our_data_source, object, &error), "add empty object to datasource [error: %@]", error);
333
334 ok(ourEngine = SOSCircleCopyEngine(circle, our_data_source_factory, &localError), "get ourEngine [error: %@]", localError);
335
336 // Start syncing
337 SOSPeerRef bobPeer = SOSCircleCopyPeer(circle, transport, kBobPeerID, &localError);
338 ok(bobPeer, "Got bob: [error: %@]", error);
339 ok(SOSEngineSyncWithPeer(ourEngine, bobPeer, false, &localError), "tell Alice sync with peer Bob");
340 SOSPeerDispose(bobPeer);
341
342 CFReleaseNull(localError);
343 #endif
344 return 1;
345 },
346 #if TEST_EMPTY_ADD
347 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
348 {
349 CFErrorRef localError = NULL;
350 SOSCircleRef circle = SOSAccountFindCircle(our_account, circleKey);
351 CFDataRef message = CFDictionaryGetValue(returnedValues, messageFromBobToAliceKey);
352 VALUECFNULLCHECK(message);
353 ok(message, "Saw response to manifest message from Bob - 2");
354 ok(SOSCircleHandlePeerMessage(circle, our_data_source_factory, transport, kBobPeerID, message, &localError), "handle message from bob: [error: %@]", localError);
355 return 1;
356 },
357 #endif
358 NULL);
359
360 //------------------------------------------------------------------------
361 // BOB
362 //------------------------------------------------------------------------
363
364 CFArrayRef bobWorkToDo =
365 CFArrayCreateForCFTypes(kCFAllocatorDefault,
366
367 ^ CFIndex (CFDictionaryRef returnedValues, CFErrorRef error)
368 {
369 __block CFIndex incrementAmount = 0; // Don't increment unless we find stuff
370 __block CFErrorRef localError = NULL;
371
372 #if USE_BOB_GO
373 CFTypeRef goMessage = CFDictionaryGetValue(returnedValues, messageGoBobGoKey);
374 if (!goMessage) // We can discard any changes we see here; they are stale
375 return 0;
376
377 SOSCloudKeychainRemoveObjectForKey(messageGoBobGoKey, work_queue,
378 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
379 {
380 pass("ACK from messageGoBobGoKey");
381 });
382 #endif
383
384 CFTypeRef value = CFDictionaryGetValue(returnedValues, circleKey);
385 VALUECFNULLCHECK(value);
386
387 ok(value, "Found circle %@", value);
388
389 mutate_account_circle(our_account, value, work_queue, work_group,
390 ^ (SOSCircleRef circle)
391 {
392 int peerCount = SOSCircleCountPeers(circle);
393 ok(peerCount == 1, "One peer, hope it's Alice");
394 if (peerCount != 1)
395 printf("NOT One peer, hope it's Alice: saw %d peers\n", peerCount);
396 ok(SOSCircleCountApplicants(circle) == 0, "No applicants");
397 CFErrorRef pkerr;
398
399 ok(SOSCircleRequestAdmission(circle, SOSAccountGetPrivateCredential(our_account, &pkerr), our_full_peer_info, &localError), "Requested admission");
400 our_peer_info = SOSFullPeerInfoGetPeerInfo(our_full_peer_info);
401
402 incrementAmount = 1;
403 });
404
405 CFReleaseSafe(localError);
406
407 return incrementAmount;
408 },
409 ^ CFIndex (CFDictionaryRef returnedValues, CFErrorRef error)
410 {
411 __block CFIndex incrementAmount = 0; // Don't increment unless we find stuff
412 __block CFErrorRef localError = NULL;
413
414 CFDataRef value = CFDictionaryGetValue(returnedValues, circleKey);
415 VALUECFNULLCHECK(value);
416 ok(value, "Found circle");
417
418 mutate_account_circle(our_account, value, work_queue, work_group,
419 ^ (SOSCircleRef circle)
420 {
421 ok(SOSCircleHasPeer(circle, our_peer_info, &localError), "We're a peer");
422 ok(SOSCircleCountPeers(circle) == 2, "Both peers!");
423 ok(SOSCircleCountApplicants(circle) == 0, "No applicants!");
424
425 CFDataRef message;
426 ok(message = CFDictionaryGetValue(returnedValues, messageFromAliceToBobKey), "got message from alice");
427 // VALUECFNULLCHECK(message);
428 if (message == NULL || CFGetTypeID(message) == CFNullGetTypeID()) { pass("CFNull message"); return; }
429 ok(SOSCircleHandlePeerMessage(circle, our_full_peer_info, our_data_source_factory, transport, kAlicePeerID, message, &localError), "handle message from alice: %@", localError);
430
431 incrementAmount = 1;
432 });
433
434 CFReleaseSafe(localError);
435 return incrementAmount;
436
437 },
438 #if TEST_EMPTY_ADD
439 /**/
440 // this next block would normally be looking at the reply to the initial sync message.
441 // Since that one was the same we sent out, we won't get a notification for that.
442 // This block looks at the sync message of the empty object datasource
443 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
444 {
445 CFErrorRef localError = NULL;
446 SOSCircleRef circle = SOSAccountFindCircle(our_account, circleKey);
447 CFDataRef message = CFDictionaryGetValue(returnedValues, messageFromBobToAliceKey);
448 VALUECFNULLCHECK(message);
449 ok(message, "Saw response to manifest message from Alice 2");
450 ok(SOSCircleHandlePeerMessage(circle, our_data_source_factory, transport, kAlicePeerID, message, &localError), "handle message from Alice 2: %@", localError);
451 return 1;
452 },
453 #endif
454
455 NULL);
456
457 //------------------------------------------------------------------------
458 // START
459 //------------------------------------------------------------------------
460
461 CFArrayRef ourWork = Alice ? aliceWorkToDo : bobWorkToDo;
462
463 SOSCloudKeychainRegisterKeysAndGetWithNotification(keysToRegister, work_queue,
464 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
465 {
466 pass("Got key changes: %@ [error: %@]", returnedValues, error);
467
468 CFReleaseSafe(error);
469
470 TestStateBlock thingToDo = CFArrayGetValueAtIndex(ourWork, current);
471
472 if (thingToDo)
473 {
474 pass("%s stage %d rv: %@ [error: %@]", Alice?"Alice":"Bob", (int)current, returnedValues, error);
475 current += thingToDo(returnedValues, error);
476 }
477
478 if (current < 0 || current >= CFArrayGetCount(ourWork))
479 dispatch_group_leave(work_group);
480 });
481
482 dispatch_group_wait(work_group, DISPATCH_TIME_FOREVER);
483
484 // We probably never get here since the program exits..
485
486 CFReleaseNull(aliceWorkToDo);
487 CFReleaseNull(bobWorkToDo);
488 CFReleaseNull(gestalt);
489 CFReleaseNull(our_account);
490 CFReleaseNull(our_peer_info);
491 CFReleaseNull(user_key);
492 CFReleaseNull(public_key); // Should be NULL but just in case..
493
494 #if 0
495
496 if (Alice)
497 {
498 CFDictionaryRef object = CFDictionaryCreate(0, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
499 ok(SOSTestDataSourceAddObject(our_data_source, object, &error), "add empty object to datasource (error: %@)", error);
500 CFReleaseNull(error);
501 CFReleaseNull(object);
502
503 sendManifestDigest(transportX, pqrEngine, our_circle, our_peer_info);
504 ok(waitForSemaphore(), "Got ACK for manifest with object");
505 ok(handleCloudMessage(our_circle, our_data_source, t), "Got ACK for manifest with object");
506 }
507 #endif
508 }
509
510 // MARK: ----- start of all tests -----
511 static void tests(bool Alice)
512 {
513 dispatch_queue_t work_queue = dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
514 dispatch_group_t work_group = dispatch_group_create();
515
516 // Queue the work we want to do.
517 runTests(Alice, work_queue, work_group);
518
519 }
520
521 // define the options table for the command line
522 static const struct option options[] =
523 {
524 { "verbose", optional_argument, NULL, 'v' },
525 { "identity", optional_argument, NULL, 'i' },
526 { "clear", optional_argument, NULL, 'C' },
527 { }
528 };
529
530 static int kAliceTestCount = 22;
531 static int kBobTestCount = 20;
532
533 int sc_100_devicecircle(int argc, char *const *argv)
534 {
535 char *identity = NULL;
536 // extern int optind;
537 extern char *optarg;
538 int arg, argSlot;
539 bool Alice = false;
540
541 while (argSlot = -1, (arg = getopt_long(argc, (char * const *)argv, "i:vC", options, &argSlot)) != -1)
542 switch (arg)
543 {
544 case 'i':
545 identity = (char *)(optarg);
546 break;
547 case 'C': // should set up to call testClearAll
548 break;
549 default:
550 secerror("arg: %s", optarg);
551 break;
552 }
553
554 if (identity)
555 {
556 secerror("We are %s",identity);
557 if (!strcmp(identity, "alice"))
558 Alice = true;
559 }
560
561 plan_tests(Alice?kAliceTestCount:kBobTestCount);
562 tests(Alice);
563
564 return 0;
565 }