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