]> git.saurik.com Git - apple/security.git/blobdiff - sec/SOSCircle/SecureObjectSync/SOSEngine.c
Security-57031.1.35.tar.gz
[apple/security.git] / sec / SOSCircle / SecureObjectSync / SOSEngine.c
diff --git a/sec/SOSCircle/SecureObjectSync/SOSEngine.c b/sec/SOSCircle/SecureObjectSync/SOSEngine.c
deleted file mode 100644 (file)
index aab5bff..0000000
+++ /dev/null
@@ -1,932 +0,0 @@
-/*
- * Created by Michael Brouwer on 7/17/12.
- * Copyright 2012 Apple Inc. All Rights Reserved.
- */
-
-/*
- * SOSEngine.c -  Implementation of a secure object syncing engine
- */
-
-#include <SecureObjectSync/SOSEngine.h>
-#include <SecureObjectSync/SOSPeer.h>
-#include <SecureObjectSync/SOSPeerInfo.h>
-#include <corecrypto/ccder.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <utilities/SecCFError.h>
-#include <utilities/SecCFRelease.h>
-#include <utilities/SecCFWrappers.h>
-#include <utilities/der_plist.h>
-#include <utilities/der_plist_internal.h>
-#include <utilities/debugging.h>
-#include <utilities/iCloudKeychainTrace.h>
-#include <AssertMacros.h>
-#include <CoreFoundation/CoreFoundation.h>
-#include <SecItemServer.h>
-#include <SecItemPriv.h>
-
-/* DataSource helper macros and functions. */
-
-// TODO: Change to create with DER.
-#define SOSObjectCreateWithPropertyList(dataSource, plist, error) (dataSource->createWithPropertyList(dataSource, plist, error))
-
-#define SOSObjectCopyPropertyList(dataSource, object, error) (dataSource->copyPropertyList(object, error))
-#define SOSObjectCopyDigest(dataSource, object, error) (dataSource->copyDigest(object, error))
-#define SOSObjectCopyPrimaryKey(dataSource, object, error) (dataSource->copyPrimaryKey(object, error))
-#define SOSObjectCopyMergedObject(dataSource, object1, object2, error) (dataSource->copyMergedObject(object1, object2, error))
-
-#define kSOSMaxObjectPerMessage (500)
-
-static CFArrayRef SOSDataSourceCopyObjectArray(SOSDataSourceRef data_source, SOSManifestRef manifest, CFErrorRef *error) {
-    CFMutableArrayRef objects = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
-
-    // Delta sync by only sending a max of kSOSMaxObjectPerMessage objects at a time.
-    SOSManifestRef toSend = NULL;
-    if (SOSManifestGetCount(manifest) > kSOSMaxObjectPerMessage) {
-        toSend = SOSManifestCreateWithBytes(SOSManifestGetBytePtr(manifest), kSOSMaxObjectPerMessage * SOSDigestSize, error);
-    } else {
-        toSend = manifest;
-        CFRetain(toSend);
-    }
-
-    if (!data_source->foreach_object(data_source, toSend, error, ^bool (SOSObjectRef object, CFErrorRef *localError) {
-        CFDictionaryRef plist = SOSObjectCopyPropertyList(data_source, object, localError);
-        if (plist) {
-            CFArrayAppendValue(objects, plist);
-            CFRelease(plist);
-        }
-        return plist;
-    })) {
-        CFReleaseNull(objects);
-    }
-    CFRetainSafe(toSend);
-    return objects;
-}
-
-static CFDataRef SOSDataSourceCopyManifestDigest(SOSDataSourceRef ds, CFErrorRef *error) {
-    CFMutableDataRef manifestDigest = CFDataCreateMutable(0, SOSDigestSize);
-    CFDataSetLength(manifestDigest, SOSDigestSize);
-    if (!ds->get_manifest_digest(ds, CFDataGetMutableBytePtr(manifestDigest), error))
-        CFReleaseNull(manifestDigest);
-
-    return manifestDigest;
-}
-
-static SOSManifestRef SOSDataSourceCopyManifest(SOSDataSourceRef ds, CFErrorRef *error) {
-    return ds->copy_manifest(ds, error);
-}
-
-static void SOSDataSourceRelease(SOSDataSourceRef ds) {
-    ds->release(ds);
-}
-
-
-/* SOSEngine implementation. */
-
-static CFStringRef sErrorDomain = CFSTR("com.apple.security.sos.engine.error");
-
-static bool SOSEngineCreateError(CFIndex errorCode, CFStringRef descriptionString, CFErrorRef previousError, CFErrorRef *newError) {
-    SecCFCreateError(errorCode, descriptionString, sErrorDomain, previousError, newError);
-    return true;
-}
-
-struct __OpaqueSOSEngine {
-    SOSDataSourceRef dataSource;
-};
-
-SOSEngineRef SOSEngineCreate(SOSDataSourceRef dataSource, CFErrorRef *error) {
-    SOSEngineRef engine = calloc(1, sizeof(struct __OpaqueSOSEngine));
-    engine->dataSource = dataSource;
-
-    return engine;
-}
-
-void SOSEngineDispose(SOSEngineRef engine) {
-    SOSDataSourceRelease(engine->dataSource);
-    free(engine);
-}
-
-/* SOSEngine. */
-enum SOSMessageType {
-    SOSManifestInvalidMessageType = 0,
-    SOSManifestDigestMessageType = 1,
-    SOSManifestMessageType = 2,
-    SOSManifestDeltaAndObjectsMessageType = 3,
-};
-
-/* H(): SHA1 hash function.
- M: Manifest of peer p
- MSG: H(M).
- SOSPeerMessage := SEQUENCE {
- messageType INTEGER (manifestDigest, manifest, manifestDeltaAndObjects)
- version INTEGER OPTIONAL default v0
- content ANY defined by messageType
- }
- ManifestDigest := OCTECT STRING (length 20)
- Manifest := OCTECT STRING (length 20 * number of entries)
- Value := CHOICE {
- bool Boolean
- number INTEGER
- string UTF8String
- data OCTECT STRING
- date GENERAL TIME
- dictionary Object
- array Array
- }
- KVPair := SEQUENCE {
- key UTF8String
- value Value
- }
- Array := SEQUENCE of Value
- Dictionary := SET of KVPair
- Object := SEQUENCE {
- [0] conflict OCTECT STRING OPTIONAL
- [1] change OCTECT STRING OPTIONAL
- object Dictionary
- ManifestDeltaAndObjects := SEQUENCE {
- manfestDigest ManifestDigest
- removals Manifest
- additions Manifest
- addedObjects SEQUENCE of Object
- }
- manifestDigest content = OCTECT STRING
- manifest content := OCTECT STRING
- manifestDeltaAndObjects := SEQUENCE {
- manfestDigest ManifestDigest
- }
- */
-
-
-/* ManifestDigest message */
-static size_t der_sizeof_manifest_digest_message(void) {
-    return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
-                        (ccder_sizeof_uint64(SOSManifestDigestMessageType) +
-                         ccder_sizeof_raw_octet_string(SOSDigestSize)));
-}
-
-static uint8_t *der_encode_manifest_digest_message(const uint8_t digest[SOSDigestSize], const uint8_t *der, uint8_t *der_end) {
-    return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
-           ccder_encode_uint64(SOSManifestDigestMessageType, der,
-           ccder_encode_raw_octet_string(SOSDigestSize, digest, der, der_end)));
-}
-
-/* This message is sent to each peer that joins a circle and can also be sent
- as a form of ACK to confirm that the local peer is in sync with the peer
- this is beig sent to. */
-CFDataRef SOSEngineCreateManifestDigestMessage(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
-    /* TODO: avoid copying the digest here by inlining der_encode_manifest_digest_message(). */
-
-    uint8_t digest[SOSDigestSize];
-    if (!engine->dataSource->get_manifest_digest(engine->dataSource, &digest[0], error)) {
-        return NULL;
-    }
-    
-    size_t der_size = der_sizeof_manifest_digest_message();
-    CFMutableDataRef message = CFDataCreateMutable(NULL, der_size);
-    if (message == NULL) {
-       return NULL;
-    }
-    CFDataSetLength(message, der_size);
-    uint8_t *der_end = CFDataGetMutableBytePtr(message);
-    const uint8_t *der = der_end;
-    der_end += der_size;
-
-    der_end = der_encode_manifest_digest_message(digest, der, der_end);
-    assert(der == der_end);
-
-    return message;
-}
-
-
-/* Manifest message */
-static size_t der_sizeof_manifest_message(SOSManifestRef manifest) {
-    return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
-                        (ccder_sizeof_uint64(SOSManifestMessageType) +
-                         ccder_sizeof_raw_octet_string(SOSManifestGetSize(manifest))));
-}
-
-static uint8_t *der_encode_manifest_message(SOSManifestRef manifest, const uint8_t *der, uint8_t *der_end) {
-    return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
-           ccder_encode_uint64(SOSManifestMessageType, der,
-           ccder_encode_raw_octet_string(SOSManifestGetSize(manifest),
-                                         SOSManifestGetBytePtr(manifest), der, der_end)));
-}
-
-/* This message is sent in response to a manifestDigest if our manifestDigest
- differs from that of the received manifestDigest, or in response to a
- manifestAndObjects message if the manifestDigest in the received message
- doesn't match our own manifestDigest. */
-CFDataRef SOSEngineCreateManifestMessage(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
-    SOSManifestRef manifest = SOSDataSourceCopyManifest(engine->dataSource, error);
-    if (!manifest)
-        return NULL;
-
-    size_t der_size = der_sizeof_manifest_message(manifest);
-    CFMutableDataRef message = CFDataCreateMutable(NULL, der_size);
-    CFDataSetLength(message, der_size);
-    uint8_t *der_end = CFDataGetMutableBytePtr(message);
-    const uint8_t *der = der_end;
-    der_end += der_size;
-
-    der_end = der_encode_manifest_message(manifest, der, der_end);
-    assert(der == der_end);
-
-    return message;
-}
-
-
-/* ManifestDeltaAndObjects message */
-static size_t der_sizeof_manifest_and_objects_message(SOSManifestRef removals, SOSManifestRef additions, CFArrayRef objects, CFErrorRef *error) {
-    size_t objects_size = der_sizeof_plist(objects, error);
-    if (objects_size == 0)
-        return objects_size;
-
-    return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
-                        (ccder_sizeof_uint64(SOSManifestDeltaAndObjectsMessageType) +
-                         ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
-                         (ccder_sizeof_raw_octet_string(SOSDigestSize) +
-                          ccder_sizeof_raw_octet_string(SOSManifestGetSize(removals)) +
-                          ccder_sizeof_raw_octet_string(SOSManifestGetSize(additions)) +
-                          objects_size))));
-}
-
-static uint8_t *der_encode_manifest_and_objects_message(CFDataRef digest, SOSManifestRef removals, SOSManifestRef additions, CFArrayRef objects, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) {
-    assert(CFDataGetLength(digest) == SOSDigestSize);
-    return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
-           ccder_encode_uint64(SOSManifestDeltaAndObjectsMessageType, der,
-           ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
-           ccder_encode_raw_octet_string(SOSDigestSize, CFDataGetBytePtr(digest), der,
-           ccder_encode_raw_octet_string(SOSManifestGetSize(removals), SOSManifestGetBytePtr(removals), der,
-           ccder_encode_raw_octet_string(SOSManifestGetSize(additions), SOSManifestGetBytePtr(additions), der,
-           der_encode_plist(objects, error, der, der_end)))))));
-}
-
-/* This message is sent in response to a local change that needs to be
- propagated to our peers or in response to a manifest or manifestDigest
- message from a peer that is not in sync with us yet. */
-CFDataRef SOSEngineCreateManifestAndObjectsMessage(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
-    /* Assumptions:
-       peer has a manifest that corresponds to peers real manifest.
-       we send everything in our datasource that's not in peers manifest already to peer.
-     */
-    CFMutableDataRef message = NULL;
-    SOSManifestRef manifest, peerManifest, additions, removals;
-
-retry:
-    manifest = SOSDataSourceCopyManifest(engine->dataSource, error);
-    if (!manifest)
-        goto errOut4;
-    
-    peerManifest = SOSPeerCopyManifest(peer, error);
-    if (!peerManifest)
-        goto errOut3;
-
-    if (!SOSManifestDiff(manifest, peerManifest, &additions, &removals, error))
-        goto errOut2;
-
-    CFErrorRef localError = NULL;
-    CFArrayRef objects = SOSDataSourceCopyObjectArray(engine->dataSource, additions, &localError);
-    if (!objects) {
-        if(SecErrorGetOSStatus(localError)==errSecDecode) {
-            secnotice("engine", "Corrupted item found: %@", localError);
-            CFReleaseNull(manifest);
-            CFReleaseNull(additions);
-            CFReleaseNull(removals);
-            CFReleaseNull(peerManifest);
-            CFReleaseNull(localError);
-            goto retry;
-        }
-        if(error && *error==NULL)
-            *error=localError;
-        else
-            CFReleaseNull(localError);
-        goto errOut1;
-    }
-
-    size_t der_size = der_sizeof_manifest_and_objects_message(removals, additions, objects, error);
-    if (der_size == 0)
-        goto errOut0;
-
-    /* TODO: avoid copying the digest here by inlining der_encode_manifest_and_objects_message(). */
-    CFDataRef peerDigest = SOSPeerCopyManifestDigest(peer, error);
-    if (!peerDigest)
-        goto errOut0;
-
-    message = CFDataCreateMutable(NULL, der_size);
-    CFDataSetLength(message, der_size);
-    uint8_t *der_end = CFDataGetMutableBytePtr(message);
-    const uint8_t *der = der_end;
-    der_end += der_size;
-
-    der_end = der_encode_manifest_and_objects_message(peerDigest, removals, additions, objects, error, der, der_end);
-    assert(der == der_end);
-    if (der_end == NULL) {
-        CFReleaseNull(message);
-        goto errOut_;
-    }
-
-    /* Record the peers new manifest assuming that peer will accept all the
-       changes we are about to send them. */
-    SOSPeerSetManifest(peer, manifest, error);
-
-errOut_:
-    CFRelease(peerDigest);
-errOut0:
-    CFRelease(objects);
-errOut1:
-    SOSManifestDispose(removals);
-    SOSManifestDispose(additions);
-errOut2:
-    SOSManifestDispose(peerManifest);
-errOut3:
-    SOSManifestDispose(manifest);
-errOut4:
-
-    return message;
-}
-
-static const uint8_t *der_decode_msg_type(enum SOSMessageType *msg_type,
-                                          const uint8_t *der,
-                                          const uint8_t *der_end,
-                                          CFErrorRef *error) {
-    const uint8_t *body_end;
-    der = ccder_decode_sequence_tl(&body_end, der, der_end);
-    if (!der)
-        return NULL;
-
-    if (body_end != der_end) {
-        SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Trailing garbage at end of message"), NULL, error);
-        return NULL;
-    }
-
-    uint64_t msgType;
-    der = ccder_decode_uint64(&msgType, der, der_end);
-    if (msgType < 1 || msgType > SOSManifestDeltaAndObjectsMessageType) {
-        SecCFCreateErrorWithFormat(kSOSEngineInvalidMessageError, sErrorDomain,
-                                   NULL, error, NULL,
-                                   CFSTR("Bad message type: %llu"), msgType);
-        return NULL;
-    }
-    *msg_type = (enum SOSMessageType)msgType;
-    return der;
-}
-
-static const uint8_t *
-der_decode_manifest_digest(CFDataRef *digest, CFErrorRef *error,
-                           const uint8_t *der, const uint8_t *der_end) {
-    require_quiet(der, errOut);
-    size_t len;
-    der = ccder_decode_tl(CCDER_OCTET_STRING, &len, der, der_end);
-    require_action_quiet(der, errOut, SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Failed to find string"), NULL, error));
-    require_action_quiet(len == SOSDigestSize, errOut, SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Invalid digest size"), NULL, error));
-
-    *digest = CFDataCreate(0, der, len);
-    require_action_quiet(*digest, errOut, SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Failed to create digest"), NULL, error));
-
-    der += len;
-    require_action_quiet(der, errOut, CFReleaseNull(*digest); SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Failed to find string"), NULL, error));
-
-    return der;
-
-errOut:
-    return NULL;
-}
-
-static const uint8_t *
-der_decode_manifest(SOSManifestRef *manifest, CFErrorRef *error,
-                    const uint8_t *der, const uint8_t *der_end) {
-    if (!der)
-        goto errOut;
-    size_t len;
-    der = ccder_decode_tl(CCDER_OCTET_STRING, &len, der, der_end);
-    if (!der) {
-        SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Failed to decode manifest"), NULL, error);
-        goto errOut;
-    }
-    if (len % SOSDigestSize != 0) {
-        SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("manifest not a multiple of digest size"), NULL, error);
-        goto errOut;
-    }
-    *manifest = SOSManifestCreateWithBytes(der, len, error);
-    if (!*manifest)
-        goto errOut;
-
-    return der += len;
-
-errOut:
-    return NULL;
-}
-
-static const uint8_t *
-der_decode_manifest_digest_message(CFDataRef *digest, CFErrorRef *error,
-                                   const uint8_t *der, const uint8_t *der_end) {
-    der = der_decode_manifest_digest(digest, error, der, der_end);
-    if (der && der != der_end) {
-        SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Trailing garbage after digest"), NULL, error);
-        CFReleaseNull(*digest);
-        der = NULL;
-    }
-    return der;
-}
-
-static const uint8_t *
-der_decode_manifest_message(SOSManifestRef *manifest, CFErrorRef *error,
-                            const uint8_t *der, const uint8_t *der_end) {
-    der = der_decode_manifest(manifest, error, der, der_end);
-    if (der && der != der_end) {
-        SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Trailing garbage after manifest"), NULL, error);
-        SOSManifestDispose(*manifest);
-        *manifest = NULL;
-        der = NULL;
-    }
-    return der;
-}
-
-static const uint8_t *
-der_decode_manifest_and_objects_message(CFDataRef *peerManifestDigest,
-                                        SOSManifestRef *removals,
-                                        SOSManifestRef *additions,
-                                        CFArrayRef *objects,
-                                        CFErrorRef *error, const uint8_t *der,
-                                        const uint8_t *der_end) {
-    const uint8_t *body_end;
-    der = ccder_decode_sequence_tl(&body_end, der, der_end);
-    if (!der) {
-        SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Failed to decode top level sequence"), NULL, error);
-        goto errOut;
-    }
-
-    if (body_end != der_end) {
-        SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Trailing garbage at end of message"), NULL, error);
-        goto errOut;
-    }
-
-    der = der_decode_manifest_digest(peerManifestDigest, error, der, der_end);
-    if (!der)
-        goto errOut;
-    der = der_decode_manifest(removals, error, der, der_end);
-    if (!der)
-        goto errOut1;
-    der = der_decode_manifest(additions, error, der, der_end);
-    if (!der)
-        goto errOut2;
-
-    CFPropertyListRef pl;
-    der = der_decode_plist(0, 0, &pl, error, der, der_end);
-    if (!der)
-        goto errOut3;
-
-    if (der != der_end) {
-        SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Trailing garbage at end of message body"), NULL, error);
-        goto errOut4;
-    }
-
-    // TODO Check that objects is in fact an array. */
-    if (CFArrayGetTypeID() != CFGetTypeID(pl)) {
-        SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("objects is not an array"), NULL, error);
-        goto errOut4;
-    }
-    *objects = pl;
-
-    return der;
-
-errOut4:
-    CFRelease(pl);
-errOut3:
-    CFRelease(additions);
-errOut2:
-    CFRelease(removals);
-errOut1:
-    CFRelease(peerManifestDigest);
-errOut:
-    return NULL;
-}
-
-
-#if 0
-enum SOSMessageType SOSMessageGetType(CFDataRef message) {
-    const uint8_t *der = CFDataGetBytePtr(message);
-    const uint8_t *der_end = der + CFDataGetLength(message);
-    enum SOSMessageType msg_type;
-    der_decode_msg_type(&msg_type, der, der_end, NULL);
-    if (!der) {
-        return SOSManifestInvalidMessageType;
-    }
-
-    return msg_type;
-}
-#endif
-
-/* H(): SHA1 hash function.
- M: Manifest of peer p
- MSG: H(M) */
-static CFDataRef SOSEngineCopyManifestDigestReply(SOSEngineRef engine,
-                                                  SOSPeerRef peer,
-                                                  CFDataRef digest,
-                                                  CFErrorRef *error) {
-    CFDataRef reply = NULL;
-    CFDataRef peerDigest = SOSPeerCopyManifestDigest(peer, NULL);
-    CFDataRef manifestDigest = SOSDataSourceCopyManifestDigest(engine->dataSource, error);
-    if (manifestDigest) {
-        if (CFEqual(manifestDigest, digest)) {
-            /* Our dataSources manifest and that of the peer are equal, we are in sync. */
-            if (peerDigest && CFEqual(peerDigest, digest)) {
-                /* The last known digest we had for peer already matched the digest peer
-                   sent us, so this message is redundant, consider it an ack of our last
-                   message to peer. */
-                reply = CFDataCreate(kCFAllocatorDefault, NULL, 0);
-            } else {
-                /* Our peer just sent us a manifest digest that matches our own, but the digest
-                   we have for the peer (if any) doesn't match that.   Peer must have the same
-                   manifest we do, so record that. */
-                SOSManifestRef manifest = SOSDataSourceCopyManifest(engine->dataSource, error);
-                if (manifest) {
-                    bool ok = SOSPeerSetManifest(peer, manifest, error);
-                    SOSManifestDispose(manifest);
-                    if (ok) {
-                        /* Since we got lucky and happen to have the same digest as our peer, we
-                         send back an ack to ensure our peer ends up knowning our manifest as well. */
-                        reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
-                    }
-                }
-            }
-        } else if (peerDigest && CFEqual(peerDigest, digest)) {
-            /* We know peer's current manifest is correct (the computed digest
-               matches the passed in one) but peer and our dataSource
-               are not in sync.  Send the deltas to peer. */
-            reply = SOSEngineCreateManifestAndObjectsMessage(engine, peer, error);
-        } else {
-            /* Our peer has no digest yet, or the manifestDigest peer just sent
-               us doesn't match the digest of the manifest we think peer has.
-               We need to get peer to tell us their manifest, to do so we sent
-               it ours and hope it responds with deltas. */
-            reply = SOSEngineCreateManifestMessage(engine, peer, error);
-        }
-        CFRelease(manifestDigest);
-    }
-    CFReleaseSafe(peerDigest);
-    return reply;
-}
-
-/* M: Manifest of peer p
- MSG: M */
-static CFDataRef SOSEngineCopyManifestReply(SOSEngineRef engine, SOSPeerRef peer,
-                                            SOSManifestRef manifest,
-                                            CFErrorRef *error) {
-    CFDataRef reply = NULL;
-    // Peer just told us what his manifest was.  Let's roll with it.
-    SOSPeerSetManifest(peer, manifest, error);
-    CFDataRef peerManifestDigest = SOSPeerCopyManifestDigest(peer, error);
-    if (peerManifestDigest) {
-        CFDataRef manifestDigest = SOSDataSourceCopyManifestDigest(engine->dataSource, error);
-        if (manifestDigest) {
-            if (CFEqual(peerManifestDigest, manifestDigest)) {
-                /* We're in sync, optionally send peer an ack. */
-                reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
-            } else {
-                /* Send peer the objects it is missing from our manifest. */
-                reply = SOSEngineCreateManifestAndObjectsMessage(engine, peer, error);
-            }
-            CFRelease(manifestDigest);
-        }
-        CFRelease(peerManifestDigest);
-    }
-    return reply;
-}
-
-static bool SOSEngineProccesObjects(SOSEngineRef engine,
-                                    SOSPeerRef peer,
-                                    CFDataRef digest,
-                                    SOSManifestRef removals,
-                                    SOSManifestRef additions,
-                                    CFArrayRef objects,
-                                    CFErrorRef *error) {
-    __block bool result = true;
-    CFArrayForEach(objects, ^(const void *value) {
-        SOSObjectRef ob = SOSObjectCreateWithPropertyList(engine->dataSource, value, error);
-        if (ob) {
-            SOSMergeResult mr = engine->dataSource->add(engine->dataSource, ob, error);
-            if (!mr) {
-                result = false;
-                // assertion failure, duplicate object added during transaction, that wasn't explicitly listed in removal list.
-                // treat as conflict?
-                // oa =  ds->lookup(pkb);
-                // ds->choose_between(oa, ob)
-                // TODO: This is needed is we want to allow conflicts with other circles.
-                SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed, 1);
-                secerror("assertion failure, add failed: %@",
-                         error ? *error : (CFErrorRef)CFSTR("error is null"));
-            }
-            CFRelease(ob);
-        }
-    });
-    return result;
-}
-
-/* H(): SHA1 hash function.
- L: Manifest of local peer.
- M: Manifest of peer p.
- M-L: Manifest of entries in M but not in L
- L-M: Manifest of entries in L but not in M
- O(M): Objects in manifest M
- MSG: H(L) || L-M || M-L || O(M-L)  */
-static CFDataRef SOSEngineCopyManifestAndObjectsReply(SOSEngineRef engine,
-                                                      SOSPeerRef peer,
-                                                      CFDataRef digest,
-                                                      SOSManifestRef removals,
-                                                      SOSManifestRef additions,
-                                                      CFArrayRef objects,
-                                                      CFErrorRef *error) {
-    CFDataRef reply = NULL;
-    CFMutableDataRef manifestDigest = (CFMutableDataRef)SOSDataSourceCopyManifestDigest(engine->dataSource, error);
-    if (manifestDigest) {
-        SOSManifestRef manifest = SOSDataSourceCopyManifest(engine->dataSource, error);
-
-        /* Always proccess the objects after we snapshot our manifest. */
-        if (!SOSEngineProccesObjects(engine, peer, digest, removals, additions, objects, error)) {
-            secerror("peer: %@ SOSEngineProccesObjects(): %@", SOSPeerGetID(peer), *error);
-        }
-
-        if (CFEqual(manifestDigest, digest)) {
-            SOSManifestRef peerManifest = NULL;
-            if (manifest) {
-                peerManifest = SOSManifestCreateWithPatch(manifest, removals, additions, error);
-            }
-            if (peerManifest) {
-                if (SOSPeerSetManifest(peer, peerManifest, error)) {
-                    /* Now proccess the objects. */
-                    if (!SOSEngineProccesObjects(engine, peer, digest, removals, additions, objects, error)) {
-                        secerror("peer: %@ SOSEngineProccesObjects(): %@", SOSPeerGetID(peer), *error);
-                    }
-
-                    CFDataRef peerDigest = SOSPeerCopyManifestDigest(peer, error);
-                    if (peerDigest) {
-                        /* Depending on whether after proccess objects we still have objects that need to be sent back to peer we respond with our digestManifest or with a manifestAndObjectsMessage. */
-                        if (engine->dataSource->get_manifest_digest(engine->dataSource, CFDataGetMutableBytePtr(manifestDigest), error)) {
-                            if (CFEqual(manifestDigest, peerDigest)) {
-                                reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
-                            } else {
-                                reply = SOSEngineCreateManifestAndObjectsMessage(engine, peer, error);
-                            }
-                        }
-                        CFRelease(peerDigest);
-                    }
-                }
-                CFRelease(peerManifest);
-            } else {
-                secerror("Received peer: %@ sent bad message: %@", SOSPeerGetID(peer), *error);
-                /* We failed to compute peer's digest, let's tell him ours again and hope for a retransmission. */
-                /* TODO: Perhaps this should be sent by the top level whenever an error occurs during parsing. */
-                reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
-            }
-        } else {
-            /* ds->manifestDigest != msg->manigestDigest => We received deltas
-               against a manifest we don't have respond with our current
-               manifest to get back in sync. */
-            reply = SOSEngineCreateManifestMessage(engine, peer, error);
-        }
-        CFReleaseSafe(manifest);
-        CFRelease(manifestDigest);
-    }
-    return reply;
-}
-
-/* Handle incoming message from peer p.  Return false if there was an error, true otherwise. */
-bool SOSEngineHandleMessage(SOSEngineRef engine, SOSPeerRef peer,
-                            CFDataRef message, CFErrorRef *error) {
-    CFDataRef reply = NULL;
-    SOSManifestRef oldPeerManifest = SOSPeerCopyManifest(peer, NULL);
-    const uint8_t *der = CFDataGetBytePtr(message);
-    const uint8_t *der_end = der + CFDataGetLength(message);
-    enum SOSMessageType msgType;
-
-    der = der_decode_msg_type(&msgType, der, der_end, error);
-    if (der) switch (msgType) {
-        case SOSManifestDigestMessageType:
-        {
-            CFDataRef digest = NULL; // Make the static analyzer happy by NULL and Release safe
-            der = der_decode_manifest_digest_message(&digest, error, der, der_end);
-            if (der) {
-                reply = SOSEngineCopyManifestDigestReply(engine, peer, digest, error);
-            }
-            CFReleaseSafe(digest);
-            break;
-        }
-        case SOSManifestMessageType:
-        {
-            SOSManifestRef manifest;
-            der = der_decode_manifest_message(&manifest, error, der, der_end);
-            if (der) {
-                reply = SOSEngineCopyManifestReply(engine, peer, manifest, error);
-                SOSManifestDispose(manifest);
-            }
-            break;
-        }
-        case SOSManifestDeltaAndObjectsMessageType:
-        {
-            CFDataRef peerManifestDigest;
-            SOSManifestRef removals;
-            SOSManifestRef additions;
-            CFArrayRef objects;
-            der = der_decode_manifest_and_objects_message(&peerManifestDigest, &removals, &additions, &objects, error, der, der_end);
-            if (der) {
-                reply = SOSEngineCopyManifestAndObjectsReply(engine, peer, peerManifestDigest, removals, additions, objects, error);
-                CFRelease(peerManifestDigest);
-                SOSManifestDispose(removals);
-                SOSManifestDispose(additions);
-                CFRelease(objects);
-            }
-            break;
-        }
-        default:
-            SecCFCreateErrorWithFormat(kSOSEngineInvalidMessageError, sErrorDomain,
-                                       NULL, error, NULL, CFSTR("Invalid message type %d"), msgType);
-            break;
-    }
-
-    bool ok = reply;
-    if (reply && CFDataGetLength(reply)) {
-        ok = SOSPeerSendMessage(peer, reply, error);
-        if (!ok)
-            SOSPeerSetManifest(peer, oldPeerManifest, NULL);
-    }
-    secnotice("engine", "%@", SOSPeerGetID(peer));
-    CFReleaseSafe(oldPeerManifest);
-    CFReleaseSafe(reply);
-    return ok;
-}
-
-bool SOSEngineSyncWithPeer(SOSEngineRef engine, SOSPeerRef peer, bool force,
-                           CFErrorRef *error) {
-    CFDataRef reply = NULL;
-    SOSManifestRef oldPeerManifest = SOSPeerCopyManifest(peer, NULL);
-    bool ok = true;
-    require_quiet(SOSPeerCanSendMessage(peer), exit);
-    CFDataRef peerDigest = SOSPeerCopyManifestDigest(peer, NULL);
-    CFMutableDataRef manifestDigest = CFDataCreateMutable(0, SOSDigestSize);
-    CFDataSetLength(manifestDigest, SOSDigestSize);
-    if (engine->dataSource->get_manifest_digest(engine->dataSource, CFDataGetMutableBytePtr(manifestDigest), error)) {
-        if (peerDigest) {
-            if (CFEqual(peerDigest, manifestDigest)) {
-                /* We are in sync with peer already. */
-                if (force) {
-                    /* If we are at the end of the OTR handshake, we have to send
-                       something to our peer no matter what to break the symmmetry.  */
-                    reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
-                } else {
-                    reply = CFDataCreate(kCFAllocatorDefault, NULL, 0);
-                }
-            } else {
-                /* We have have a digest for peer's manifest and it doesn't
-                   match our current digest, so send deltas to peer. */
-                reply = SOSEngineCreateManifestAndObjectsMessage(engine, peer, error);
-            }
-        } else {
-            /* We have no digest for peer yet, send our manifest digest to peer,
-               it should respond with it's manifest so we can sync. */
-            reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
-        }
-    }
-    CFRelease(manifestDigest);
-    CFReleaseSafe(peerDigest);
-
-    ok = ok && reply;
-    if (ok && CFDataGetLength(reply)) {
-        ok = SOSPeerSendMessage(peer, reply, error);
-        if (!ok)
-            SOSPeerSetManifest(peer, oldPeerManifest, NULL);
-    }
-
-exit:
-    secnotice("engine", "%@", SOSPeerGetID(peer));
-    CFReleaseSafe(oldPeerManifest);
-    CFReleaseSafe(reply);
-    return ok;
-}
-
-#if 0
-static void appendObject(CFMutableStringRef desc, CFDictionaryRef object) {
-    __block bool needComma = false;
-    CFDictionaryForEach(object, ^(const void *key, const void *value) {
-        if (needComma)
-            CFStringAppend(desc, CFSTR(","));
-        else
-            needComma = true;
-
-        CFStringAppend(desc, key);
-        CFStringAppend(desc, CFSTR("="));
-        if (CFEqual(CFSTR("data"), key)) {
-            CFStringAppend(desc, CFSTR("<?>"));
-        } else if (isData(value)) {
-            CFStringAppendHexData(desc, value);
-        } else {
-            CFStringAppendFormat(desc, 0, CFSTR("%@"), value);
-        }
-    });
-}
-#endif
-
-static void appendObjects(CFMutableStringRef desc, CFArrayRef objects) {
-    __block bool needComma = false;
-    CFArrayForEach(objects, ^(const void *value) {
-        if (needComma)
-            CFStringAppend(desc, CFSTR(","));
-        else
-            needComma = true;
-
-        SecItemServerAppendItemDescription(desc, value);
-    });
-}
-
-CFStringRef SOSMessageCopyDescription(CFDataRef message) {
-    if (!message)
-        return CFSTR("<NULL>");
-
-    CFMutableStringRef desc = CFStringCreateMutable(0, 0);
-    const uint8_t *der = CFDataGetBytePtr(message);
-    const uint8_t *der_end = der + CFDataGetLength(message);
-    enum SOSMessageType msgType;
-
-    CFStringAppend(desc, CFSTR("<Msg"));
-    der = der_decode_msg_type(&msgType, der, der_end, 0);
-    if (der) switch (msgType) {
-        case SOSManifestDigestMessageType:
-        {
-            CFStringAppend(desc, CFSTR("ManifestDigest digest: "));
-            CFDataRef digest = NULL;
-            der = der_decode_manifest_digest_message(&digest, 0, der, der_end);
-            if (der) {
-                CFStringAppendHexData(desc, digest);
-            }
-            CFReleaseNull(digest);
-
-            break;
-        }
-        case SOSManifestMessageType:
-        {
-            CFStringAppend(desc, CFSTR("Manifest"));
-
-            SOSManifestRef manifest;
-            der = der_decode_manifest_message(&manifest, 0, der, der_end);
-            if (der) {
-                CFStringRef mfdesc = SOSManifestCopyDescription(manifest);
-                if (mfdesc) {
-                    CFStringAppendFormat(desc, 0, CFSTR(" manifest: %@"), mfdesc);
-                    CFRelease(mfdesc);
-                }
-                SOSManifestDispose(manifest);
-            }
-            break;
-        }
-        case SOSManifestDeltaAndObjectsMessageType:
-        {
-            CFStringAppend(desc, CFSTR("ManifestDeltaAndObjects digest:"));
-
-            CFDataRef peerManifestDigest;
-            SOSManifestRef removals;
-            SOSManifestRef additions;
-            CFArrayRef objects;
-            der = der_decode_manifest_and_objects_message(&peerManifestDigest, &removals, &additions, &objects, 0, der, der_end);
-            if (der) {
-                CFStringAppendHexData(desc, peerManifestDigest);
-                CFStringRef remdesc = SOSManifestCopyDescription(removals);
-                if (remdesc) {
-                    CFStringAppendFormat(desc, 0, CFSTR(" removals: %@"), remdesc);
-                    CFRelease(remdesc);
-                }
-                CFStringRef adddesc = SOSManifestCopyDescription(additions);
-                if (adddesc) {
-                    CFStringAppendFormat(desc, 0, CFSTR(" additions: %@"), adddesc);
-                    CFRelease(adddesc);
-                }
-                CFStringAppendFormat(desc, 0, CFSTR(" objects: "));
-                appendObjects(desc, objects);
-
-                CFRelease(peerManifestDigest);
-                SOSManifestDispose(removals);
-                SOSManifestDispose(additions);
-                CFRelease(objects);
-            }
-            break;
-        }
-        default:
-            CFStringAppendFormat(desc, 0, CFSTR("InvalidType: %d"), msgType);
-            break;
-    }
-
-    CFStringAppend(desc, CFSTR(">"));
-
-    return desc;
-}