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); }
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); } }
SOSCoderStatus SOSCoderUnwrap(SOSCoderRef coder, CFDataRef codedMessage, CFMutableDataRef *message, CFStringRef clientId, CFErrorRef *error) { if(codedMessage == NULL) return kSOSCoderDataReturned; if(coder->sessRef == NULL) return nullCoder(codedMessage, message); CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0); /* This should be the "normal" case. We just use OTR to unwrap the received message. */ SOSCoderStatus result = kSOSCoderFailure; CFStringRef beginState = CFCopyDescription(coder->sessRef); enum SecOTRSMessageKind kind = SecOTRSGetMessageKind(coder->sessRef, codedMessage); switch (kind) { case kOTRNegotiationPacket: { /* If we're in here we haven't completed negotiating a session. Use SecOTRSProcessPacket() to go through the negotiation steps and immediately send a reply back if necessary using the sendBlock. This assumes the sendBlock is still available. */ CFMutableDataRef response = CFDataCreateMutable(kCFAllocatorDefault, 0); OSStatus ppstatus = errSecSuccess; if (response) { switch (ppstatus = SecOTRSProcessPacket(coder->sessRef, codedMessage, response)) { case errSecSuccess: if (CFDataGetLength(response) > 1) { CFStringAppendFormat(action, NULL, CFSTR("Sending OTR Response %s"), SecOTRPacketTypeString(response)); CFRetainAssign(coder->pendingResponse, response); result = kSOSCoderNegotiating; if (SecOTRSGetIsReadyForMessages(coder->sessRef)) { CFStringAppend(action, CFSTR(" begin waiting for data packet")); coder->waitingForDataPacket = true; } } else if(!SecOTRSGetIsReadyForMessages(coder->sessRef)) { CFStringAppend(action, CFSTR("stuck?")); result = kSOSCoderNegotiating; } else { CFStringAppend(action, CFSTR("completed negotiation")); result = kSOSCoderNegotiationCompleted; coder->waitingForDataPacket = false; } break; case errSecDecode: CFStringAppend(action, CFSTR("resending dh")); result = SOSCoderResendDH(coder, error); break; default: SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot negotiate session (%ld)"), clientId, (long)ppstatus); result = kSOSCoderFailure; break; }; } else { SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot allocate CFData"), clientId); result = kSOSCoderFailure; } CFReleaseNull(response); break; } case kOTRDataPacket: if(!SecOTRSGetIsReadyForMessages(coder->sessRef)) { CFStringAppend(action, CFSTR("not ready, resending DH packet")); SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed, 1); CFStringAppend(action, CFSTR("not ready for data; resending dh")); result = SOSCoderResendDH(coder, error); } else { if (coder->waitingForDataPacket) { CFStringAppend(action, CFSTR("got data packet we were waiting for ")); coder->waitingForDataPacket = false; } CFMutableDataRef exposed = CFDataCreateMutable(0, 0); OSStatus otrResult = SecOTRSVerifyAndExposeMessage(coder->sessRef, codedMessage, exposed); CFStringAppend(action, CFSTR("verify and expose message")); if (otrResult) { if (otrResult == errSecOTRTooOld) { CFStringAppend(action, CFSTR(" too old")); result = kSOSCoderStaleEvent; } else { SecError(otrResult, error, CFSTR("%@ Cannot expose message: %" PRIdOSStatus), clientId, otrResult); secerror("%@ Decode OTR Protected Packet: %@", clientId, error ? *error : NULL); result = kSOSCoderFailure; } } else { CFStringAppend(action, CFSTR("decoded OTR protected packet")); *message = exposed; exposed = NULL; result = kSOSCoderDataReturned; } CFReleaseNull(exposed); } break; default: secerror("%@ Unknown packet type: %@", clientId, codedMessage); SOSCreateError(kSOSErrorDecodeFailure, CFSTR("Unknown packet type"), (error != NULL) ? *error : NULL, error); result = kSOSCoderFailure; break; }; // Uber state log if (result == kSOSCoderFailure && error && *error) CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error); secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId, beginState, SecOTRPacketTypeString(codedMessage), action, coder->sessRef, SOSCoderString(result)); CFReleaseSafe(beginState); CFRelease(action); return result; }