SOSCoderStatus SOSCoderWrap(SOSCoderRef coder, CFDataRef message, CFMutableDataRef *codedMessage, CFStringRef clientId, CFErrorRef *error) {
    CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0);
    SOSCoderStatus result = kSOSCoderDataReturned;
    CFStringRef beginState = NULL;
    CFMutableDataRef encoded = NULL;
    OSStatus otrStatus = 0;

    require_action_quiet(coder->sessRef, errOut,
                         CFStringAppend(action, CFSTR("*** using null coder ***"));
                         result = nullCoder(message, codedMessage));
    beginState = CFCopyDescription(coder->sessRef);
    require_action_quiet(SecOTRSGetIsReadyForMessages(coder->sessRef), errOut,
                         CFStringAppend(action, CFSTR("not ready"));
                         result = kSOSCoderNegotiating);
    require_action_quiet(!coder->waitingForDataPacket, errOut,
                         CFStringAppend(action, CFSTR("waiting for peer to send data packet first"));
                         result = kSOSCoderNegotiating);
    require_action_quiet(encoded = CFDataCreateMutable(kCFAllocatorDefault, 0), errOut,
                         SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, NULL, error, NULL, CFSTR("%@ alloc failed"), clientId);
                         result = kSOSCoderFailure);
    require_noerr_action_quiet(otrStatus = SecOTRSSignAndProtectMessage(coder->sessRef, message, encoded), errOut,
                               SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ cannot protect message: %" PRIdOSStatus), clientId, otrStatus);
                               CFReleaseNull(encoded);
                               result = kSOSCoderFailure);
    *codedMessage = encoded;

errOut:
    // Uber state log
    if (result == kSOSCoderFailure && error && *error)
        CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error);
    secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId, beginState,
              SecOTRPacketTypeString(encoded), action, coder->sessRef, SOSCoderString(result));
    CFReleaseSafe(beginState);
    CFRelease(action);

    return result;
}
static void sendMessages(int howMany, SecOTRSessionRef *bobSession, SecOTRSessionRef *aliceSession, bool serialize)
{
    for(int count = howMany; count > 0; --count) {
        const char* aliceToBob = "aliceToBob";
        CFDataRef rawAliceToBob = CFDataCreate(kCFAllocatorDefault, (const uint8_t*)aliceToBob, (CFIndex) strlen(aliceToBob));
        CFMutableDataRef protectedAliceToBob = CFDataCreateMutable(kCFAllocatorDefault, 0);
        CFMutableDataRef bobDecode = CFDataCreateMutable(kCFAllocatorDefault, 0);

        ok_status(SecOTRSSignAndProtectMessage(*aliceSession, rawAliceToBob, protectedAliceToBob), "encode message");
        ok_status(SecOTRSVerifyAndExposeMessage(*bobSession, protectedAliceToBob, bobDecode), "Decode message");


        if (serialize) {
            serializeAndDeserialize(bobSession);
            serializeAndDeserialize(aliceSession);
        }

        ok(CFDataGetLength(rawAliceToBob) == CFDataGetLength(bobDecode)
           && 0 == memcmp(CFDataGetBytePtr(rawAliceToBob), CFDataGetBytePtr(bobDecode), (size_t)CFDataGetLength(rawAliceToBob)), "Didn't match!");

        CFReleaseNull(rawAliceToBob);
        CFReleaseNull(protectedAliceToBob);
        CFReleaseNull(bobDecode);

        const char* bobToAlice = "i liked your silly message from me to you";
        CFDataRef rawBobToAlice = CFDataCreate(kCFAllocatorDefault, (const uint8_t*)bobToAlice, (CFIndex) strlen(bobToAlice));
        CFMutableDataRef protectedBobToAlice = CFDataCreateMutable(kCFAllocatorDefault, 0);
        CFMutableDataRef aliceDecode = CFDataCreateMutable(kCFAllocatorDefault, 0);

        ok_status(SecOTRSSignAndProtectMessage(*aliceSession, rawBobToAlice, protectedBobToAlice), "encode reply");
        ok_status(SecOTRSVerifyAndExposeMessage(*bobSession, protectedBobToAlice, aliceDecode), "decode reply");

        if (serialize) {
            serializeAndDeserialize(bobSession);
            serializeAndDeserialize(aliceSession);
        }

        ok(CFDataGetLength(rawBobToAlice) == CFDataGetLength(aliceDecode)
           && 0 == memcmp(CFDataGetBytePtr(rawBobToAlice), CFDataGetBytePtr(aliceDecode), (size_t)CFDataGetLength(rawBobToAlice)), "reply matched");

        CFReleaseNull(rawAliceToBob);
        CFReleaseNull(rawBobToAlice);
        CFReleaseNull(protectedBobToAlice);
        CFReleaseNull(protectedAliceToBob);
        CFReleaseNull(aliceDecode);

        rawAliceToBob = CFDataCreate(kCFAllocatorDefault, (const uint8_t*)aliceToBob, (CFIndex) strlen(aliceToBob));
         protectedAliceToBob = CFDataCreateMutable(kCFAllocatorDefault, 0);
         bobDecode = CFDataCreateMutable(kCFAllocatorDefault, 0);

        ok_status(SecOTRSSignAndProtectMessage(*aliceSession, rawAliceToBob, protectedAliceToBob), "encode message");
        ok_status(SecOTRSVerifyAndExposeMessage(*bobSession, protectedAliceToBob, bobDecode), "Decode message");

        if (serialize) {
            serializeAndDeserialize(bobSession);
            serializeAndDeserialize(aliceSession);
        }

        ok(CFDataGetLength(rawAliceToBob) == CFDataGetLength(bobDecode)
           && 0 == memcmp(CFDataGetBytePtr(rawAliceToBob), CFDataGetBytePtr(bobDecode), (size_t)CFDataGetLength(rawAliceToBob)), "Didn't match!");

        CFReleaseNull(rawAliceToBob);
        CFReleaseNull(protectedAliceToBob);
        CFReleaseNull(bobDecode);

         bobToAlice = "i liked your silly message from me to you";
         rawBobToAlice = CFDataCreate(kCFAllocatorDefault, (const uint8_t*)bobToAlice, (CFIndex) strlen(bobToAlice));
         protectedBobToAlice = CFDataCreateMutable(kCFAllocatorDefault, 0);
         aliceDecode = CFDataCreateMutable(kCFAllocatorDefault, 0);

        ok_status(SecOTRSSignAndProtectMessage(*aliceSession, rawBobToAlice, protectedBobToAlice), "encode reply");
        ok_status(SecOTRSVerifyAndExposeMessage(*bobSession, protectedBobToAlice, aliceDecode), "decode reply");

        if (serialize) {
            serializeAndDeserialize(bobSession);
            serializeAndDeserialize(aliceSession);
        }

        ok(CFDataGetLength(rawBobToAlice) == CFDataGetLength(aliceDecode)
           && 0 == memcmp(CFDataGetBytePtr(rawBobToAlice), CFDataGetBytePtr(aliceDecode), (size_t)CFDataGetLength(rawBobToAlice)), "reply matched");

        CFReleaseNull(rawAliceToBob);
        CFReleaseNull(rawBobToAlice);
        CFReleaseNull(protectedBobToAlice);
        CFReleaseNull(protectedAliceToBob);
        CFReleaseNull(aliceDecode);



        CFStringRef stateString = CFCopyDescription(*bobSession);
        ok(stateString, "getting state from bob");
        CFReleaseNull(stateString);

        stateString = CFCopyDescription(*aliceSession);
        ok(stateString, "getting state from alice");
        CFReleaseNull(stateString);
    }
}
static void negotiate(SecOTRSessionRef* aliceSession, SecOTRSessionRef* bobSession, bool serializeNegotiating, bool serializeMessaging, bool textMode, bool compact)
{
    const int kEmptyMessageSize = textMode ? 6 : 0;

    // Step 1: Create a start packet for each side of the transaction
    CFMutableDataRef bobStartPacket = CFDataCreateMutable(kCFAllocatorDefault, 0);
    
    ok_status(SecOTRSAppendStartPacket(*bobSession, bobStartPacket), "Bob start packet");
    
    if (serializeNegotiating)
        serializeAndDeserialize(bobSession);
    
    CFMutableDataRef aliceStartPacket = CFDataCreateMutable(kCFAllocatorDefault, 0);
    
    ok_status(SecOTRSAppendStartPacket(*aliceSession, aliceStartPacket), "Alice start packet");
    
    if (serializeNegotiating)
        serializeAndDeserialize(aliceSession);
    
    // Step 2: Exchange the start packets, forcing the DH commit messages to collide
    CFMutableDataRef aliceDHKeyResponse = CFDataCreateMutable(kCFAllocatorDefault, 0);
    
    ok_status(SecOTRSProcessPacket(*aliceSession, bobStartPacket, aliceDHKeyResponse),
              "Bob DH packet failed");
    
    if (serializeNegotiating)
        serializeAndDeserialize(aliceSession);    
    
    CFReleaseNull(bobStartPacket);
    
    CFMutableDataRef bobDHKeyResponse = CFDataCreateMutable(kCFAllocatorDefault, 0);
    
    ok_status(SecOTRSProcessPacket(*bobSession, aliceStartPacket, bobDHKeyResponse),
              "Alice DH packet failed");
    
    if (serializeNegotiating)
        serializeAndDeserialize(bobSession);
    
    CFReleaseNull(aliceStartPacket);
    
    // Step 3: With one "real" DH key message, and one replayed DH commit message, try to get a "reveal sig" out of one side
    
    CFMutableDataRef bobRevealSigResponse = CFDataCreateMutable(kCFAllocatorDefault, 0);
    
    ok_status(SecOTRSProcessPacket(*bobSession, aliceDHKeyResponse, bobRevealSigResponse),
              "Alice DH Key packet failed");
    
    if (serializeNegotiating)
        serializeAndDeserialize(bobSession);
    
    CFReleaseNull(aliceDHKeyResponse);
    
    CFMutableDataRef aliceRevealSigResponse = CFDataCreateMutable(kCFAllocatorDefault, 0);
    
    ok_status(SecOTRSProcessPacket(*aliceSession, bobDHKeyResponse, aliceRevealSigResponse),
              "Bob DH Key packet failed");
    
    if (serializeNegotiating)
        serializeAndDeserialize(aliceSession);
    
    CFReleaseNull(bobDHKeyResponse);
    
    // Step 4: Having gotten the reveal signature, now work for the signature
    
    CFMutableDataRef aliceSigResponse = CFDataCreateMutable(kCFAllocatorDefault, 0);
    
    ok_status(SecOTRSProcessPacket(*aliceSession, bobRevealSigResponse, aliceSigResponse),
              "Bob Reveal sig failed");
    
    if (serializeNegotiating)
        serializeAndDeserialize(aliceSession);
    
    CFReleaseNull(bobRevealSigResponse);
    
    CFMutableDataRef bobSigResponse = CFDataCreateMutable(kCFAllocatorDefault, 0);
    
    ok_status(SecOTRSProcessPacket(*bobSession, aliceRevealSigResponse, bobSigResponse),
              "Alice Reveal sig failed");
    
    if (serializeNegotiating)
        serializeAndDeserialize(bobSession);
    
    CFReleaseNull(aliceRevealSigResponse);
    
    // Step 5: All the messages have been sent, now deal with any replays from the collision handling
    CFMutableDataRef bobFinalResponse = CFDataCreateMutable(kCFAllocatorDefault, 0);
    
    ok_status(SecOTRSProcessPacket(*bobSession, aliceSigResponse, bobFinalResponse),
              "Alice Final Sig failed");
    
    if (serializeNegotiating)
        serializeAndDeserialize(bobSession);
    
    CFMutableDataRef aliceFinalResponse = CFDataCreateMutable(kCFAllocatorDefault, 0);
    
    ok_status(SecOTRSProcessPacket(*aliceSession, bobSigResponse, aliceFinalResponse),
              "Bob Final Sig failed");

    is(kEmptyMessageSize, CFDataGetLength(aliceFinalResponse), "Alice had nothing left to say");
    CFReleaseNull(aliceFinalResponse);
    CFReleaseNull(bobSigResponse);

    if (serializeNegotiating)
        serializeAndDeserialize(aliceSession);
    
    is(kEmptyMessageSize, CFDataGetLength(bobFinalResponse), "Bob had nothing left to say");
    ok(SecOTRSGetIsReadyForMessages(*bobSession), "Bob is ready");
    ok(SecOTRSGetIsReadyForMessages(*aliceSession), "Alice is ready");

    CFReleaseNull(aliceSigResponse);
    CFReleaseNull(bobFinalResponse);

    sendMessages(5, bobSession, aliceSession, serializeMessaging);
    
    const char* aliceToBob = "deferredMessage";
    CFDataRef rawAliceToBob = CFDataCreate(kCFAllocatorDefault, (const uint8_t*)aliceToBob, (CFIndex) strlen(aliceToBob));
    CFMutableDataRef protectedAliceToBob = CFDataCreateMutable(kCFAllocatorDefault, 0);
    CFMutableDataRef bobDecode = CFDataCreateMutable(kCFAllocatorDefault, 0);

    ok_status(SecOTRSSignAndProtectMessage(*aliceSession, rawAliceToBob, protectedAliceToBob), "encode message");


    sendMessages(1, bobSession, aliceSession, serializeMessaging);

    is(SecOTRSVerifyAndExposeMessage(*bobSession, protectedAliceToBob, bobDecode), errSecOTRTooOld, "Decode old message");

    sendMessages(1, bobSession, aliceSession, serializeMessaging);

    is(SecOTRSVerifyAndExposeMessage(*bobSession, protectedAliceToBob, bobDecode), errSecOTRTooOld, "Decode excessively old message");

    sendMessages(3, bobSession, aliceSession, serializeMessaging);

    CFReleaseNull(rawAliceToBob);
    CFReleaseNull(protectedAliceToBob);
    CFReleaseNull(bobDecode);
}