1/* 2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25#include <SecureObjectSync/SOSEngine.h> 26#include <SecureObjectSync/SOSPeer.h> 27 28#include "SOSCircle_regressions.h" 29 30#include <corecrypto/ccsha2.h> 31#include <Security/SecBase64.h> 32 33#include <utilities/SecCFWrappers.h> 34 35#include <stdint.h> 36#include "SOSTestDataSource.h" 37 38#if 1 39static int kTestTestCount = 0; 40#else 41static int kTestTestCount = 61; 42 43static void tests(void) 44{ 45 CFErrorRef error = NULL; 46 47 /* Transport. */ 48 __block unsigned msg_count = 0; 49 uint8_t msg_digest_buffer[CCSHA256_OUTPUT_SIZE]; 50 uint8_t *msg_digest = msg_digest_buffer; 51 52 SOSPeerSendBlock sendBlock = ^bool (CFDataRef message, CFErrorRef *error) { 53 size_t msglen = CFDataGetLength(message); 54 const uint8_t *msg = CFDataGetBytePtr(message); 55 const struct ccdigest_info *sha256 = ccsha256_di(); 56 if (msg_count++ == 0) { 57 /* message n=0 */ 58 ccdigest(sha256, msglen, msg, msg_digest); 59 } else { 60 /* message n=n+1 */ 61 ccdigest_di_decl(sha256, sha256_ctx); 62 ccdigest_init(sha256, sha256_ctx); 63 ccdigest_update(sha256, sha256_ctx, sizeof(msg_digest_buffer), msg_digest); 64 ccdigest_update(sha256, sha256_ctx, msglen, msg); 65 ccdigest_final(sha256, sha256_ctx, msg_digest); 66 } 67 size_t encmaxlen = SecBase64Encode(msg, msglen, NULL, 0); 68 CFMutableDataRef encoded = CFDataCreateMutable(NULL, encmaxlen); 69 CFDataSetLength(encoded, encmaxlen); 70 SecBase64Result rc; 71 char *enc = (char *)CFDataGetMutableBytePtr(encoded); 72#ifndef NDEBUG 73 size_t enclen = 74#endif 75 SecBase64Encode2(msg, msglen, 76 enc, 77 encmaxlen, kSecB64_F_LINE_LEN_USE_PARAM, 78 64, &rc); 79 assert(enclen < INT32_MAX); 80// printf("=== BEGIN SOSMESSAGE ===\n%.*s\n=== END SOSMESSAGE ===\n", (int)enclen, enc); 81 CFRelease(encoded); 82 return true; 83 }; 84 85 /* Create peer test. */ 86 CFStringRef peer_id = CFSTR("peer 70"); 87 SOSPeerRef peer; 88 ok(peer = SOSPeerCreateSimple(peer_id, kSOSPeerVersion, &error), 89 "create peer: %@", error); 90 CFReleaseNull(error); 91 92 /* DataSource */ 93 SOSDataSourceRef ds = SOSTestDataSourceCreate(); 94 95 /* Create engine test. */ 96 SOSEngineRef engine; 97 ok(engine = SOSEngineCreate(ds, &error), "create engine: %@", error); 98 CFReleaseNull(error); 99 100 /* Make sure the engine did not yet send a any messages to the peer. */ 101 unsigned expected_msg_count = 0; 102 is(msg_count, expected_msg_count, "Engine sent %d/%d messages", 103 msg_count, expected_msg_count); 104 105 /* Test passing peer messages to the engine. */ 106 CFDataRef message; 107 108 /* Hand an empty message to the engine for handeling. */ 109 message = CFDataCreate(NULL, NULL, 0); 110 ok(false == SOSEngineHandleMessage(engine, peer, message, &error), 111 "handle empty message: %@", error); 112 ok(false == SOSPeerSendMessageIfNeeded(engine, peer, &error, sendBlock), 113 "send response: %@", error); 114 CFReleaseNull(error); 115 CFReleaseNull(message); 116 /* Make sure the engine did not yet send a response to the peer. */ 117 is(msg_count, expected_msg_count, "Engine sent %d/%d messages", 118 msg_count, expected_msg_count); 119 120 121 /* Create manifest digest message. */ 122 SKIP: { 123 skip("Create manifest digest message failed", 2, 124 ok(message = SOSEngineCreateManifestDigestMessage(engine, peer, 125 &error), 126 "create manifest digest message: %@", error)); 127 CFReleaseNull(error); 128 /* Pass manifest digest message to the engine for handeling. */ 129 skip("", 1, 130 ok(SOSEngineHandleMessage(engine, peer, message, &error), 131 "handle manifest digest message: %@", error)); 132 /* Make sure the engine sent a response to the peer. */ 133 expected_msg_count++; 134 is(msg_count, expected_msg_count, "Engine sent %d/%d messages", 135 msg_count, expected_msg_count); 136 } 137 CFReleaseNull(message); 138 CFReleaseNull(error); 139 140 /* Create manifest message. */ 141 SKIP: { 142 skip("Create manifest message failed", 2, 143 ok(message = SOSEngineCreateManifestMessage(engine, peer, &error), 144 "create manifest message: %@", error)); 145 CFReleaseNull(error); 146 /* Pass manifest message to the engine for handeling. */ 147 skip("", 1, 148 ok(SOSEngineHandleMessage(engine, peer, message, &error), 149 "handle manifest message: %@", error)); 150 /* Make sure the engine sent a response to the peer. */ 151 expected_msg_count++; 152 is(msg_count, expected_msg_count, "Engine sent %d/%d messages", 153 msg_count, expected_msg_count); 154 } 155 CFReleaseNull(message); 156 CFReleaseNull(error); 157 158 /* Create manifest and objects message. */ 159 SKIP: { 160 skip("Create manifest and objects message failed", 2, 161 ok(message = SOSEngineCreateManifestAndObjectsMessage(engine, peer, 162 &error), 163 "create manifest and objects message: %@", error)); 164 CFReleaseNull(error); 165 /* Pass manifest and objects message to the engine for handeling. */ 166 skip("", 1, 167 ok(SOSEngineHandleMessage(engine, peer, message, &error), 168 "handle manifest and objects message: %@", error)); 169 /* Make sure the engine sent a response to the peer. */ 170 expected_msg_count++; 171 is(msg_count, expected_msg_count, "Engine sent %d/%d messages", 172 msg_count, expected_msg_count); 173 } 174 CFReleaseNull(message); 175 CFReleaseNull(error); 176 177 /* Clean up. */ 178 CFReleaseSafe(peer); 179 SOSEngineDispose(engine); 180} 181 182static CFStringRef SOSMessageCopyDigestHex(CFDataRef message) { 183 uint8_t digest[CCSHA1_OUTPUT_SIZE]; 184 ccdigest(ccsha1_di(), CFDataGetLength(message), CFDataGetBytePtr(message), digest); 185 CFMutableStringRef hex = CFStringCreateMutable(0, 2 * sizeof(digest)); 186 for (unsigned int ix = 0; ix < sizeof(digest); ++ix) { 187 CFStringAppendFormat(hex, 0, CFSTR("%02X"), digest[ix]); 188 } 189 return hex; 190} 191 192static void testsync(const char *name, void (^aliceInit)(SOSDataSourceRef ds), void (^bobInit)(SOSDataSourceRef ds), CFStringRef msg, ...) { 193 CFErrorRef error = NULL; 194 195 /* Setup Alice engine, dataSource and peer for Alice to talk to Bob */ 196 SOSDataSourceRef aliceDataSource = SOSTestDataSourceCreate(); 197 SOSEngineRef aliceEngine; 198 ok(aliceEngine = SOSEngineCreate(aliceDataSource, &error), "create alice engine: %@", error); 199 CFReleaseNull(error); 200 CFStringRef bobID = CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFSTR("Bob-%s"), name); 201 202 __block CFDataRef queued_message = NULL; 203 204 SOSPeerSendBlock enqueueMessage = ^bool (CFDataRef message, CFErrorRef *error) { 205 if (queued_message) 206 fail("We already had an unproccessed message"); 207 208 queued_message = (CFDataRef) CFRetain(message); 209 return true; 210 }; 211 212 CFDataRef (^dequeueMessage)() = ^CFDataRef () { 213 CFDataRef result = queued_message; 214 queued_message = NULL; 215 216 return result; 217 }; 218 219 SOSPeerRef bobPeer; 220 ok(bobPeer = SOSPeerCreateSimple(bobID, kSOSPeerVersion, &error, enqueueMessage), 221 "create peer: %@", error); 222 223 /* Setup Bob engine, dataSource and peer for Bob to talk to Alice */ 224 SOSDataSourceRef bobDataSource = SOSTestDataSourceCreate(); 225 SOSEngineRef bobEngine; 226 ok(bobEngine = SOSEngineCreate(bobDataSource, &error), "create bob engine: %@", error); 227 CFReleaseNull(error); 228 CFStringRef aliceID = CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFSTR("Alice-%s"), name); 229 230 SOSPeerRef alicePeer; 231 ok(alicePeer = SOSPeerCreateSimple(aliceID, kSOSPeerVersion, &error, enqueueMessage), 232 "create peer: %@", error); 233 CFReleaseNull(error); 234 235 /* Now call provided setup blocks to populate the dataSources with 236 interesting stuff. */ 237 aliceInit(aliceDataSource); 238 bobInit(bobDataSource); 239 240 CFDataRef message; 241 242 va_list msgs; 243 va_start(msgs, msg); 244 245 int msg_index = 0; 246 bool alice = false; 247 for (;;) { 248 message = dequeueMessage(); 249 msg_index++; 250 /* We are expecting a message and msg is it's digest. */ 251 if (message) { 252 CFStringRef messageDigestStr = SOSMessageCopyDigestHex(message); 253 if (msg) { 254 bool handeled = SOSEngineHandleMessage(alice ? aliceEngine : bobEngine, alice ? bobPeer : alicePeer, message, &error); 255 if (!CFEqual(messageDigestStr, msg)) { 256 if (handeled) { 257 fail("%s %s received message [%d] digest %@ != %@ %@", name, alice ? "Alice" : "Bob", msg_index, messageDigestStr, msg, message); 258 } else { 259 fail("%s %s failed to handle message [%d] digest %@ != %@ %@: %@", name, alice ? "Alice" : "Bob", msg_index, messageDigestStr, msg, message, error); 260 CFReleaseNull(error); 261 } 262 } else if (handeled) { 263 pass("%s %s handled message [%d] %@", name, alice ? "Alice" : "Bob", msg_index, message); 264 } else { 265 fail("%s %s failed to handle message [%d] %@: %@", name, alice ? "Alice" : "Bob", msg_index, message, error); 266 CFReleaseNull(error); 267 } 268 } else { 269 fail("%s %s sent extra message [%d] with digest %@: %@", name, alice ? "Bob" : "Alice", msg_index, messageDigestStr, message); 270 } 271 CFRelease(messageDigestStr); 272 CFRelease(message); 273 } else { 274 if (msg) { 275 fail("%s %s expected message [%d] with digest %@, none received", name, alice ? "Alice" : "Bob", msg_index, msg); 276 } else { 277 /* Compare alice and bobs dataSources databases. */ 278 ok(CFEqual(SOSTestDataSourceGetDatabase(aliceDataSource), SOSTestDataSourceGetDatabase(bobDataSource)), "%s Alice and Bob are in sync", name); 279 } 280 } 281 282 if (msg) { 283 alice = !alice; 284 msg = va_arg(msgs, CFStringRef); 285 } else 286 break; 287 } 288 289 va_end(msgs); 290 291 292 293 294 SOSEngineDispose(aliceEngine); // Also disposes aliceDataSource 295 CFReleaseSafe(alicePeer); 296 CFReleaseSafe(aliceID); 297 298 SOSEngineDispose(bobEngine); // Also disposes bobDataSource 299 CFReleaseSafe(bobPeer); 300 CFReleaseSafe(bobID); 301} 302 303#if 0 304static void synctests(void) { 305 // Sync between 2 empty dataSources 306 testsync("empty", 307 ^ (SOSDataSourceRef dataSource) {}, 308 ^ (SOSDataSourceRef dataSource) {}, 309 CFSTR("2AF312E092D67308A0083DFFBF2B6B754B967864"), 310 CFSTR("2AF312E092D67308A0083DFFBF2B6B754B967864"), 311 CFSTR("2AF312E092D67308A0083DFFBF2B6B754B967864"), 312 NULL); 313 314 // Sync a dataSource with one object to an empty dataSource 315 testsync("alice1", 316 ^ (SOSDataSourceRef dataSource) { 317 CFErrorRef error = NULL; 318 SOSObjectRef object = SOSDataSourceCreateGenericItem(dataSource, CFSTR("test_account"), CFSTR("test service")); 319 ok(dataSource->add(dataSource, object, &error), "dataSource added object %@", error); 320 CFReleaseSafe(object); 321 CFReleaseNull(error); 322 }, 323 ^ (SOSDataSourceRef dataSource) {}, 324 CFSTR("ADAA3ACE75ED516CB91893413EE9CC9ED04CA47B"), 325 CFSTR("147B6C509908CC4A9FC4263973A842104A64CE01"), 326 CFSTR("019B494F3C06B48BB02C280AF1E19AD861A7003C"), 327 CFSTR("ADAA3ACE75ED516CB91893413EE9CC9ED04CA47B"), 328 NULL); 329 330 // Sync a dataSource with one object to another dataSource with the same object 331 testsync("alice1bob1", 332 ^ (SOSDataSourceRef dataSource) { 333 CFErrorRef error = NULL; 334 SOSObjectRef object = SOSDataSourceCreateGenericItem(dataSource, CFSTR("test_account"), CFSTR("test service")); 335 ok(dataSource->add(dataSource, object, &error), "dataSource added object %@", error); 336 CFReleaseSafe(object); 337 CFReleaseNull(error); 338 }, 339 ^ (SOSDataSourceRef dataSource) { 340 CFErrorRef error = NULL; 341 SOSObjectRef object = SOSDataSourceCreateGenericItem(dataSource, CFSTR("test_account"), CFSTR("test service")); 342 ok(dataSource->add(dataSource, object, &error), "dataSource added object %@", error); 343 CFReleaseSafe(object); 344 CFReleaseNull(error); 345 }, 346 CFSTR("ADAA3ACE75ED516CB91893413EE9CC9ED04CA47B"), 347 CFSTR("ADAA3ACE75ED516CB91893413EE9CC9ED04CA47B"), 348 CFSTR("ADAA3ACE75ED516CB91893413EE9CC9ED04CA47B"), 349 NULL); 350 351 // Sync a dataSource with one object to another dataSource with the same object 352 testsync("alice1bob2", 353 ^ (SOSDataSourceRef dataSource) { 354 CFErrorRef error = NULL; 355 SOSObjectRef object = SOSDataSourceCreateGenericItem(dataSource, CFSTR("test_account"), CFSTR("test service")); 356 ok(dataSource->add(dataSource, object, &error), "dataSource added object %@", error); 357 CFReleaseSafe(object); 358 CFReleaseNull(error); 359 }, 360 ^ (SOSDataSourceRef dataSource) { 361 CFErrorRef error = NULL; 362 SOSObjectRef object = SOSDataSourceCreateGenericItem(dataSource, CFSTR("test_account"), CFSTR("test service")); 363 ok(dataSource->add(dataSource, object, &error), "dataSource added object %@", error); 364 CFReleaseSafe(object); 365 object = SOSDataSourceCreateGenericItem(dataSource, CFSTR("account1"), CFSTR("service1")); 366 ok(dataSource->add(dataSource, object, &error), "dataSource added object %@", error); 367 CFReleaseSafe(object); 368 CFReleaseNull(error); 369 }, 370 CFSTR("ADAA3ACE75ED516CB91893413EE9CC9ED04CA47B"), 371 CFSTR("D4049A1063CFBF7CAF8424E13DE3CE926FF5856C"), 372 CFSTR("9624EA855BBED6B668868BB723443E804D04F6A1"), 373 CFSTR("063E097CCD4FEB7F3610ED12B3DA828467314846"), 374 CFSTR("D1B3944E3084425F41B2C2EA0BE82170E10AA37D"), 375 NULL); 376 377} 378#endif 379 380#endif 381 382int sc_70_engine(int argc, char *const *argv) 383{ 384 plan_tests(kTestTestCount); 385 386 //tests(); 387 //synctests(); 388 //testsync(NULL, NULL, NULL, NULL); 389 390 return 0; 391} 392