#include <stdint.h>
#include <sys/types.h>
#include <CoreFoundation/CFDate.h>
#include "SecOTRSession.h"
#include "SecOTRMath.h"
#include "SecOTRDHKey.h"
#include "SecOTRSessionPriv.h"
#include "SecOTRPackets.h"
#include "SecOTRPacketData.h"
#include "SecOTRIdentityPriv.h"
#include <utilities/SecCFWrappers.h>
#include <CoreFoundation/CFRuntime.h>
#include <CoreFoundation/CFString.h>
#include <Security/SecBasePriv.h>
#include <Security/SecRandom.h>
#include <Security/SecBase64.h>
#include <Security/SecKeyPriv.h>
#include <Security/SecureObjectSync/SOSPeerInfo.h>
#include <Security/SecureObjectSync/SOSCircle.h>
#include <Security/SecureObjectSync/SOSCloudCircle.h>
#include <Security/SecureObjectSync/SOSInternal.h>
#include <Security/SecureObjectSync/SOSUserKeygen.h>
#include <AssertMacros.h>
#include <corecrypto/cchmac.h>
#include <corecrypto/ccsha2.h>
#include <corecrypto/ccsha1.h>
#include <string.h>
#include <stdlib.h>
#include <syslog.h>
#include <os/activity.h>
#include <utilities/array_size.h>
#include <ipc/securityd_client.h>
#include <Security/SecuritydXPC.h>
CFGiblisFor(SecOTRSession);
static uint64_t setup_defaults_settings(){
Boolean keyExistsAndHasValue = false;
uint64_t seconds;
seconds = CFPreferencesGetAppIntegerValue(CFSTR("OTR"), CFSTR("com.apple.security"), &keyExistsAndHasValue);
secdebug("OTR", "Retrieving OTR default settings was success? %d value retrieved: %llu", keyExistsAndHasValue, seconds);
return keyExistsAndHasValue ? seconds : (kSecondsPerMinute * 15); }
static uint64_t SecOTRGetDefaultsWriteSeconds(void) {
static dispatch_once_t sdOnceToken;
static uint64_t seconds;
dispatch_once(&sdOnceToken, ^{
seconds = setup_defaults_settings();
});
return seconds;
}
static void SecOTRSEnableTimeToRoll(SecOTRSessionRef session){
CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
CFAbsoluteTime nextTimeToRoll = now + session->_stallSeconds;
if(session->_timeToRoll == 0 || session->_timeToRoll > nextTimeToRoll){
session->_timeToRoll = nextTimeToRoll;
}
}
static void SecOTRSExpireCachedKeysForFullKey(SecOTRSessionRef session, SecOTRFullDHKeyRef myKey)
{
for(int i = 0; i < kOTRKeyCacheSize; ++i)
{
if (0 == timingsafe_bcmp(session->_keyCache[i]._fullKeyHash, SecFDHKGetHash(myKey), CCSHA1_OUTPUT_SIZE)) {
CFDataAppendBytes(session->_macKeysToExpose, session->_keyCache[i]._receiveMacKey, sizeof(session->_keyCache[i]._receiveMacKey));
bzero(&session->_keyCache[i], sizeof(session->_keyCache[i]));
}
}
}
static void SecOTRSExpireCachedKeysForPublicKey(SecOTRSessionRef session, SecOTRPublicDHKeyRef theirKey)
{
for(int i = 0; i < kOTRKeyCacheSize; ++i)
{
if (0 == timingsafe_bcmp(session->_keyCache[i]._publicKeyHash, SecPDHKGetHash(theirKey), CCSHA1_OUTPUT_SIZE)) {
CFDataAppendBytes(session->_macKeysToExpose, session->_keyCache[i]._receiveMacKey, sizeof(session->_keyCache[i]._receiveMacKey));
bzero(&session->_keyCache[i], sizeof(session->_keyCache[i]));
}
}
}
static OSStatus SecOTRGenerateNewProposedKey(SecOTRSessionRef session)
{
SecOTRSExpireCachedKeysForFullKey(session, session->_myKey);
{
SecOTRFullDHKeyRef oldKey = session->_myKey;
session->_myKey = session->_myNextKey;
session->_myNextKey = oldKey;
}
OSStatus ret = SecFDHKNewKey(session->_myNextKey);
session->_keyID += 1;
return ret;
}
static void SecOTRSHandleProposalAcknowledge(SecOTRSessionRef session){
if(session->_missedAck){
SecOTRGenerateNewProposedKey(session);
session->_missedAck = false;
}
else{
session->_receivedAck = true;
SecOTRSEnableTimeToRoll(session);
}
}
static void SecOTRSRollIfTime(SecOTRSessionRef session){
CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
CFAbsoluteTime longestTimeToRoll = now + session->_stallSeconds;
if(session->_timeToRoll < now || session->_timeToRoll > longestTimeToRoll){
SOSOTRSRoll(session);
session->_timeToRoll = 0;
}
}
static OTRMessageType SecOTRSGetMessageType(CFDataRef message)
{
OTRMessageType type = kInvalidMessage;
CFDataRef decodedBytes = SecOTRCopyIncomingBytes(message);
const uint8_t *bytes = CFDataGetBytePtr(decodedBytes);
size_t size = CFDataGetLength(decodedBytes);
if (noErr != ReadHeader(&bytes, &size, &type)) {
uint8_t firstByte = *CFDataGetBytePtr(decodedBytes);
switch (firstByte) {
case kOddCompactDataMessage:
case kEvenCompactDataMessage:
case kOddCompactDataMessageWithHashes:
case kEvenCompactDataMessageWithHashes:
type = firstByte;
break;
default:
break;
}
}
CFReleaseNull(decodedBytes);
return type;
}
#if DEBUG
static CFStringRef SecOTRCacheElementCopyDescription(SecOTRCacheElement *keyCache){
__block CFStringRef description = NULL;
BufferPerformWithHexString(keyCache->_fullKeyHash, sizeof(keyCache->_fullKeyHash), ^(CFStringRef fullKeyHashString) {
BufferPerformWithHexString(keyCache->_publicKeyHash,sizeof(keyCache->_publicKeyHash), ^(CFStringRef publicKeyHashString) {
description = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("fkh: [%@], pkh: [%@], c: %llu tc: %llu"), fullKeyHashString, publicKeyHashString, keyCache->_counter, keyCache->_theirCounter);
});
});
return description;
}
#endif
const char *SecOTRPacketTypeString(CFDataRef message)
{
if (!message) return "NoMessage";
switch (SecOTRSGetMessageType(message)) {
case kDHMessage: return "DHMessage (0x02)";
case kDataMessage: return "DataMessage (0x03)";
case kDHKeyMessage: return "DHKeyMessage (0x0A)";
case kRevealSignatureMessage: return "RevealSignatureMessage (0x11)";
case kSignatureMessage: return "SignatureMessage (0x12)";
case kEvenCompactDataMessage: return "kEvenCompactDatamessage (0x20)";
case kOddCompactDataMessage: return "kOddCompactDataMessage (0x21)";
case kEvenCompactDataMessageWithHashes: return "kEvenCompactDatamessage (0x30)";
case kOddCompactDataMessageWithHashes: return "kOddCompactDataMessage (0x31)";
case kInvalidMessage: return "InvalidMessage (0xFF)";
default: return "UnknownMessage";
}
}
static const char *SecOTRAuthStateString(SecOTRAuthState authState)
{
switch (authState) {
case kIdle: return "Idle";
case kAwaitingDHKey: return "AwaitingDHKey";
case kAwaitingRevealSignature: return "AwaitingRevealSignature";
case kAwaitingSignature: return "AwaitingSignature";
case kDone: return "Done";
default: return "InvalidState";
}
}
static CF_RETURNS_RETAINED CFStringRef SecOTRSessionCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
SecOTRSessionRef session = (SecOTRSessionRef)cf;
CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<%s %s %s %s%s%s%s %d:%d %s%s %llu %s%s%s%s>"),
SecOTRAuthStateString(session->_state),
session->_compactAppleMessages ? "C" :"c",
session->_includeHashes ? "I" : "i",
session->_me ? "F" : "f",
session->_them ? "P" : "p",
session->_receivedDHMessage ? "D" : "d",
session->_receivedDHKeyMessage ? "K" : "k",
session->_keyID,
session->_theirKeyID,
session->_theirPreviousKey ? "P" : "p",
session->_theirKey ? "T" : "t",
session->_stallSeconds,
session->_missedAck ? "M" : "m",
session->_receivedAck ? "R" : "r",
session->_stallingTheirRoll ? "S" : "s",
(session->_timeToRoll > now && session->_timeToRoll != 0) ? "E" : "e");
}
static void SecOTRSessionDestroy(CFTypeRef cf) {
SecOTRSessionRef session = (SecOTRSessionRef)cf;
CFReleaseNull(session->_receivedDHMessage);
CFReleaseNull(session->_receivedDHKeyMessage);
CFReleaseNull(session->_me);
CFReleaseNull(session->_myKey);
CFReleaseNull(session->_myNextKey);
CFReleaseNull(session->_them);
CFReleaseNull(session->_theirKey);
CFReleaseNull(session->_theirPreviousKey);
CFReleaseNull(session->_macKeysToExpose);
dispatch_release(session->_queue);
}
static void SecOTRSessionResetInternal(SecOTRSessionRef session)
{
session->_state = kIdle;
CFReleaseNull(session->_receivedDHMessage);
CFReleaseNull(session->_receivedDHKeyMessage);
session->_keyID = 0;
CFReleaseNull(session->_myKey);
CFReleaseNull(session->_myNextKey);
session->_theirKeyID = 0;
CFReleaseNull(session->_theirKey);
CFReleaseNull(session->_theirPreviousKey);
CFReleaseNull(session->_macKeysToExpose);
session->_macKeysToExpose = CFDataCreateMutable(kCFAllocatorDefault, 0);
bzero(session->_keyCache, sizeof(session->_keyCache));
}
int SecOTRSGetKeyID(SecOTRSessionRef session){
return session->_keyID;
}
int SecOTRSGetTheirKeyID(SecOTRSessionRef session){
return session->_theirKeyID;
}
void SecOTRSessionReset(SecOTRSessionRef session)
{
dispatch_sync_f(session->_queue, session, (dispatch_function_t) SecOTRSessionResetInternal);
}
static void SecOTRPIPerformWithSerializationString(SecOTRPublicIdentityRef id, void (^action)(CFStringRef string)) {
CFMutableDataRef idData = CFDataCreateMutable(kCFAllocatorDefault, 0);
SecOTRPIAppendSerialization(id, idData, NULL);
CFDataPerformWithHexString(idData, action);
CFReleaseNull(idData);
}
SecOTRSessionRef SecOTRSessionCreateFromID(CFAllocatorRef allocator,
SecOTRFullIdentityRef myID,
SecOTRPublicIdentityRef theirID)
{
SecOTRSessionRef newID = CFTypeAllocate(SecOTRSession, struct _SecOTRSession, allocator);
(void)SecOTRGetDefaultsWriteSeconds();
newID->_queue = dispatch_queue_create("OTRSession", DISPATCH_QUEUE_SERIAL);
newID->_me = CFRetainSafe(myID);
newID->_them = CFRetainSafe(theirID);
newID->_receivedDHMessage = NULL;
newID->_receivedDHKeyMessage = NULL;
newID->_myKey = NULL;
newID->_myNextKey = NULL;
newID->_theirKey = NULL;
newID->_theirPreviousKey = NULL;
newID->_macKeysToExpose = NULL;
newID->_textOutput = false;
newID->_compactAppleMessages = false;
newID->_includeHashes = false;
newID->_timeToRoll = 0;
newID->_stallingTheirRoll = false;
newID->_stallSeconds = 0;
newID->_missedAck = true;
newID->_receivedAck = false;
SecOTRSessionResetInternal(newID);
{
SecOTRPublicIdentityRef myPublicID = SecOTRPublicIdentityCopyFromPrivate(kCFAllocatorDefault, newID->_me, NULL);
SecOTRPIPerformWithSerializationString(myPublicID, ^(CFStringRef myIDString) {
SecOTRPIPerformWithSerializationString(newID->_them, ^(CFStringRef theirIDString) {
secnotice("otr", "%@ Creating with M: %@, T: %@", newID, myIDString, theirIDString);
});
});
CFReleaseNull(myPublicID);
}
return newID;
}
SecOTRSessionRef SecOTRSessionCreateFromIDAndFlags(CFAllocatorRef allocator,
SecOTRFullIdentityRef myID,
SecOTRPublicIdentityRef theirID,
uint32_t flags)
{
uint64_t seconds = SecOTRGetDefaultsWriteSeconds();
SecOTRSessionRef newID = SecOTRSessionCreateFromID(allocator, myID, theirID);
if (flags & kSecOTRSendTextMessages) {
newID->_textOutput = true;
}
if (flags & kSecOTRUseAppleCustomMessageFormat) {
newID->_compactAppleMessages = true;
}
if(flags & kSecOTRIncludeHashesInMessages)
{
newID->_includeHashes = true;
}
if(flags & kSecOTRSlowRoll)
{
newID->_stallSeconds = seconds;
}
return newID;
}
static uint64_t constant_zero = 0;
static bool hashIsZero(uint8_t hash[CCSHA1_OUTPUT_SIZE])
{
bool isZero = true;
for(size_t byte = 0; isZero && byte < CCSHA1_OUTPUT_SIZE; ++byte)
isZero = (0 == hash[byte]);
return isZero;
}
static bool SOSOTRSCacheEntryIsEmpty(SecOTRCacheElement *element)
{
return hashIsZero(element->_fullKeyHash) && hashIsZero(element->_publicKeyHash);
}
#if DEBUG
static void WithCacheDescription(SecOTRSessionRef session, void (^operation)(CFStringRef cacheDescription)) {
CFStringRef description = NULL;
CFStringRef keyCache0Description = SecOTRCacheElementCopyDescription(&session->_keyCache[0]);
CFStringRef keyCache1Description = SecOTRCacheElementCopyDescription(&session->_keyCache[1]);
CFStringRef keyCache2Description = SecOTRCacheElementCopyDescription(&session->_keyCache[2]);
CFStringRef keyCache3Description = SecOTRCacheElementCopyDescription(&session->_keyCache[3]);
description = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("{%@, %@, %@, %@}"), keyCache0Description, keyCache1Description, keyCache2Description, keyCache3Description);
operation(description);
CFReleaseNull(keyCache0Description);
CFReleaseNull(keyCache1Description);
CFReleaseNull(keyCache2Description);
CFReleaseNull(keyCache3Description);
CFReleaseNull(description);
}
#endif
static void SecOTRSFindKeysForMessage(SecOTRSessionRef session,
SecOTRFullDHKeyRef myKey,
SecOTRPublicDHKeyRef theirKey,
bool sending,
uint8_t** messageKey, uint8_t** macKey, uint64_t **counter)
{
SecOTRCacheElement* emptyKeys = NULL;
SecOTRCacheElement* cachedKeys = NULL;
#if DEBUG
int emptyPosition = kOTRKeyCacheSize;
#endif
if ((NULL == myKey) || (NULL == theirKey)) {
if (messageKey)
*messageKey = NULL;
if (macKey)
*macKey = NULL;
if (counter)
*counter = &constant_zero;
return;
}
for(int i = 0; i < kOTRKeyCacheSize; ++i)
{
if (0 == timingsafe_bcmp(session->_keyCache[i]._fullKeyHash, SecFDHKGetHash(myKey), CCSHA1_OUTPUT_SIZE)
&& (0 == timingsafe_bcmp(session->_keyCache[i]._publicKeyHash, SecPDHKGetHash(theirKey), CCSHA1_OUTPUT_SIZE))) {
cachedKeys = &session->_keyCache[i];
#if DEBUG
secdebug("OTR","session@[%p] found key match: mk: %@, tk: %@", session, myKey, theirKey);
#endif
break;
}
if (emptyKeys == NULL && SOSOTRSCacheEntryIsEmpty(&(session->_keyCache[i]))) {
#if DEBUG
emptyPosition = i;
#endif
emptyKeys = &session->_keyCache[i];
}
}
if (cachedKeys == NULL) {
if (emptyKeys == NULL) {
#if DEBUG
WithCacheDescription(session, ^(CFStringRef cacheDescription) {
secdebug("OTR","session@[%p] Cache miss, spooky for mk: %@, tk: %@ cache: %@", session, myKey, theirKey, cacheDescription);
});
emptyPosition = 0;
#endif
emptyKeys = &session->_keyCache[0];
}
assert(emptyKeys);
memcpy(emptyKeys->_fullKeyHash, SecFDHKGetHash(myKey), CCSHA1_OUTPUT_SIZE);
memcpy(emptyKeys->_publicKeyHash, SecPDHKGetHash(theirKey), CCSHA1_OUTPUT_SIZE);
emptyKeys->_counter = 0;
emptyKeys->_theirCounter = 0;
SecOTRDHKGenerateOTRKeys(myKey, theirKey,
emptyKeys->_sendEncryptionKey, emptyKeys->_sendMacKey,
emptyKeys->_receiveEncryptionKey, emptyKeys->_receiveMacKey);
cachedKeys = emptyKeys;
#if DEBUG
WithCacheDescription(session, ^(CFStringRef cacheDescription) {
secdebug("OTR","mk %@, th: %@ session@[%p] new key cache state added key@[%d]: %@", myKey, theirKey, session, emptyPosition, cacheDescription);
});
#endif
}
if (messageKey)
*messageKey = sending ? cachedKeys->_sendEncryptionKey : cachedKeys->_receiveEncryptionKey;
if (macKey)
*macKey = sending ? cachedKeys->_sendMacKey : cachedKeys->_receiveMacKey;
if (counter)
*counter = sending ? &cachedKeys->_counter : &cachedKeys->_theirCounter;
}
SecOTRSessionRef SecOTRSessionCreateFromData(CFAllocatorRef allocator, CFDataRef data)
{
if (data == NULL)
return NULL;
SecOTRSessionRef result = NULL;
SecOTRSessionRef session = CFTypeAllocate(SecOTRSession, struct _SecOTRSession, allocator);
uint8_t numberOfKeys;
uint64_t timeToRoll;
const uint8_t *bytes = CFDataGetBytePtr(data);
size_t size = (size_t)CFDataGetLength(data);
(void)SecOTRGetDefaultsWriteSeconds();
session->_queue = dispatch_queue_create("OTRSession", DISPATCH_QUEUE_SERIAL);
session->_me = NULL;
session->_them = NULL;
session->_myKey = NULL;
session->_myNextKey = NULL;
session->_theirKey = NULL;
session->_theirPreviousKey = NULL;
session->_receivedDHMessage = NULL;
session->_receivedDHKeyMessage = NULL;
session->_textOutput = false;
session->_compactAppleMessages = false;
session->_timeToRoll = 0;
session->_stallingTheirRoll = false;
session->_stallSeconds = 0;
session->_missedAck = true;
session->_receivedAck = false;
bzero(session->_keyCache, sizeof(session->_keyCache));
uint8_t version;
require_noerr(ReadByte(&bytes, &size, &version), fail);
require(version <= 6, fail);
require_noerr(ReadLong(&bytes, &size, &session->_state), fail);
session->_me = SecOTRFullIdentityCreateFromBytes(kCFAllocatorDefault, &bytes, &size, NULL);
require(session->_me != NULL, fail);
session->_them = SecOTRPublicIdentityCreateFromBytes(kCFAllocatorDefault, &bytes, &size, NULL);
require(session->_them != NULL, fail);
require(size > sizeof(session->_r), fail);
memcpy(session->_r, bytes, sizeof(session->_r));
bytes += sizeof(session->_r);
size -= sizeof(session->_r);
{
uint8_t hasMessage = false;
ReadByte(&bytes, &size, &hasMessage);
if (hasMessage) {
session->_receivedDHMessage = CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault, &bytes, &size);
}
}
if (version >= 2) {
uint8_t hasMessage = false;
ReadByte(&bytes, &size, &hasMessage);
if (hasMessage) {
session->_receivedDHKeyMessage = CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault, &bytes, &size);
}
}
if (version < 3) {
uint8_t ready;
require_noerr(ReadByte(&bytes, &size, &ready), fail);
if (ready && session->_state == kIdle)
session->_state = kDone;
}
require_noerr(ReadLong(&bytes, &size, &session->_keyID), fail);
if (session->_keyID > 0) {
session->_myKey = SecOTRFullDHKCreateFromBytes(kCFAllocatorDefault, &bytes, &size);
require(session->_myKey != NULL, fail);
session->_myNextKey = SecOTRFullDHKCreateFromBytes(kCFAllocatorDefault, &bytes, &size);
require(session->_myNextKey != NULL, fail);
}
require_noerr(ReadByte(&bytes, &size, &numberOfKeys), fail);
require_noerr(ReadLong(&bytes, &size, &session->_theirKeyID), fail);
if (version < 5) {
if (session->_theirKeyID > 0) {
if (session->_theirKeyID > 1) {
session->_theirPreviousKey = SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault, &bytes, &size);
require(session->_theirPreviousKey != NULL, fail);
}
session->_theirKey = SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault, &bytes, &size);
require(session->_theirKey != NULL, fail);
}
}
else {
if(numberOfKeys >= 1){
if (numberOfKeys >= 2) {
session->_theirPreviousKey = SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault, &bytes, &size);
require(session->_theirPreviousKey != NULL, fail);
}
session->_theirKey = SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault, &bytes, &size);
require(session->_theirKey != NULL, fail);
}
}
uint64_t *counter;
SecOTRSFindKeysForMessage(session, session->_myKey, session->_theirKey, false, NULL, NULL, &counter);
require_noerr(ReadLongLong(&bytes, &size, counter), fail);
SecOTRSFindKeysForMessage(session, session->_myKey, session->_theirKey, true, NULL, NULL, &counter);
require_noerr(ReadLongLong(&bytes, &size, counter), fail);
SecOTRSFindKeysForMessage(session, session->_myKey, session->_theirPreviousKey, false, NULL, NULL, &counter);
require_noerr(ReadLongLong(&bytes, &size, counter), fail);
SecOTRSFindKeysForMessage(session, session->_myKey, session->_theirPreviousKey, true, NULL, NULL, &counter);
require_noerr(ReadLongLong(&bytes, &size, counter), fail);
SecOTRSFindKeysForMessage(session, session->_myNextKey, session->_theirKey, false, NULL, NULL, &counter);
require_noerr(ReadLongLong(&bytes, &size, counter), fail);
SecOTRSFindKeysForMessage(session, session->_myNextKey, session->_theirKey, true, NULL, NULL, &counter);
require_noerr(ReadLongLong(&bytes, &size, counter), fail);
SecOTRSFindKeysForMessage(session, session->_myNextKey, session->_theirPreviousKey, false, NULL, NULL, &counter);
require_noerr(ReadLongLong(&bytes, &size, counter), fail);
SecOTRSFindKeysForMessage(session, session->_myNextKey, session->_theirPreviousKey, true, NULL, NULL, &counter);
require_noerr(ReadLongLong(&bytes, &size, counter), fail);
session->_macKeysToExpose = CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault, &bytes, &size);
require(session->_macKeysToExpose != NULL, fail);
require_noerr(ReadByteAsBool(&bytes, &size, &session->_textOutput), fail);
if (version >= 4) {
require_noerr(ReadByteAsBool(&bytes, &size, &session->_compactAppleMessages), fail);
}
if (version >= 5) {
require_noerr(ReadByteAsBool(&bytes, &size, &session->_includeHashes), fail);
}
if (version >= 6) {
require_noerr(ReadLongLong(&bytes, &size, &session->_stallSeconds), fail);
require_noerr(ReadByteAsBool(&bytes, &size, &session->_stallingTheirRoll), fail);
require_noerr(ReadLongLong(&bytes, &size, &timeToRoll), fail);
require_noerr(ReadByteAsBool(&bytes, &size, &session->_missedAck), fail);
require_noerr(ReadByteAsBool(&bytes, &size, &session->_receivedAck), fail);
session->_timeToRoll = timeToRoll;
}
result = session;
session = NULL;
fail:
CFReleaseNull(session);
return result;
}
OSStatus SecOTRSAppendSerialization(SecOTRSessionRef session, CFMutableDataRef serializeInto)
{
__block OSStatus result = errSecParam;
require(session, abort);
require(serializeInto, abort);
CFIndex start = CFDataGetLength(serializeInto);
dispatch_sync(session->_queue, ^{
const uint8_t version = 6;
uint8_t numberOfKeys = 0;
CFDataAppendBytes(serializeInto, &version, sizeof(version));
AppendLong(serializeInto, session->_state);
result = (SecOTRFIAppendSerialization(session->_me, serializeInto, NULL)) ? errSecSuccess : errSecParam;
if (result == errSecSuccess) {
result = (SecOTRPIAppendSerialization(session->_them, serializeInto, NULL)) ? errSecSuccess : errSecParam;
}
if (result == errSecSuccess) {
CFDataAppendBytes(serializeInto, session->_r, sizeof(session->_r));
if (session->_receivedDHMessage == NULL) {
AppendByte(serializeInto, 0);
} else {
AppendByte(serializeInto, 1);
AppendCFDataAsDATA(serializeInto, session->_receivedDHMessage);
}
if (session->_receivedDHKeyMessage == NULL) {
AppendByte(serializeInto, 0);
} else {
AppendByte(serializeInto, 1);
AppendCFDataAsDATA(serializeInto, session->_receivedDHKeyMessage);
}
AppendLong(serializeInto, session->_keyID);
if (session->_keyID > 0) {
SecFDHKAppendSerialization(session->_myKey, serializeInto);
SecFDHKAppendSerialization(session->_myNextKey, serializeInto);
}
if(session->_theirPreviousKey != NULL)
numberOfKeys++;
if(session->_theirKey != NULL)
numberOfKeys++;
AppendByte(serializeInto, numberOfKeys);
AppendLong(serializeInto, session->_theirKeyID);
if (session->_theirPreviousKey != NULL)
SecPDHKAppendSerialization(session->_theirPreviousKey, serializeInto);
if (session->_theirKey != NULL )
SecPDHKAppendSerialization(session->_theirKey, serializeInto);
uint64_t *counter;
SecOTRSFindKeysForMessage(session, session->_myKey, session->_theirKey, false, NULL, NULL, &counter);
AppendLongLong(serializeInto, *counter);
SecOTRSFindKeysForMessage(session, session->_myKey, session->_theirKey, true, NULL, NULL, &counter);
AppendLongLong(serializeInto, *counter);
SecOTRSFindKeysForMessage(session, session->_myKey, session->_theirPreviousKey, false, NULL, NULL, &counter);
AppendLongLong(serializeInto, *counter);
SecOTRSFindKeysForMessage(session, session->_myKey, session->_theirPreviousKey, true, NULL, NULL, &counter);
AppendLongLong(serializeInto, *counter);
SecOTRSFindKeysForMessage(session, session->_myNextKey, session->_theirKey, false, NULL, NULL, &counter);
AppendLongLong(serializeInto, *counter);
SecOTRSFindKeysForMessage(session, session->_myNextKey, session->_theirKey, true, NULL, NULL, &counter);
AppendLongLong(serializeInto, *counter);
SecOTRSFindKeysForMessage(session, session->_myNextKey, session->_theirPreviousKey, false, NULL, NULL, &counter);
AppendLongLong(serializeInto, *counter);
SecOTRSFindKeysForMessage(session, session->_myNextKey, session->_theirPreviousKey, true, NULL, NULL, &counter);
AppendLongLong(serializeInto, *counter);
AppendCFDataAsDATA(serializeInto, session->_macKeysToExpose);
AppendByte(serializeInto, session->_textOutput ? 1 : 0);
AppendByte(serializeInto, session->_compactAppleMessages ? 1 : 0);
AppendByte(serializeInto, session->_includeHashes ? 1 : 0);
AppendLongLong(serializeInto, session->_stallSeconds ? session->_stallSeconds : constant_zero);
AppendByte(serializeInto, session->_stallingTheirRoll ? 1 : 0);
AppendLongLong(serializeInto, (uint64_t)session->_timeToRoll);
AppendByte(serializeInto, session->_missedAck ? 1 : 0);
AppendByte(serializeInto, session->_receivedAck ? 1 : 0);
}
});
if (result != errSecSuccess)
CFDataSetLength(serializeInto, start);
abort:
return result;
}
bool SecOTRSIsForKeys(SecOTRSessionRef session, SecKeyRef myPublic, SecKeyRef theirPublic)
{
__block bool isForKeys = false;
dispatch_sync(session->_queue, ^{
isForKeys = SecOTRFICompareToPublicKey(session->_me, myPublic) &&
SecOTRPICompareToPublicKey(session->_them, theirPublic);
});
return isForKeys;
}
bool SecOTRSGetIsReadyForMessages(SecOTRSessionRef session)
{
__block bool result;
dispatch_sync(session->_queue, ^{ result = session->_state == kDone; });
return result;
}
bool SecOTRSGetIsIdle(SecOTRSessionRef session)
{
__block bool result;
dispatch_sync(session->_queue, ^{ result = session->_state == kIdle; });
return result;
}
static void SecOTRSPrecalculateForPair(SecOTRSessionRef session,
SecOTRFullDHKeyRef myKey,
SecOTRPublicDHKeyRef theirKey)
{
if (myKey == NULL || theirKey == NULL)
return;
SecOTRSFindKeysForMessage(session, myKey, theirKey, true, NULL, NULL, NULL);
SecOTRSFindKeysForMessage(session, myKey, theirKey, false, NULL, NULL, NULL);
}
static void SecOTRSPrecalculateKeysInternal(SecOTRSessionRef session)
{
SecOTRSPrecalculateForPair(session, session->_myKey, session->_theirKey);
SecOTRSPrecalculateForPair(session, session->_myNextKey, session->_theirKey);
SecOTRSPrecalculateForPair(session, session->_myKey, session->_theirPreviousKey);
SecOTRSPrecalculateForPair(session, session->_myNextKey, session->_theirPreviousKey);
}
static void SecOTRSPrecalculateNextKeysInternal(SecOTRSessionRef session)
{
SecOTRSPrecalculateForPair(session, session->_myKey, session->_theirKey);
}
void SecOTRSPrecalculateKeys(SecOTRSessionRef session)
{
dispatch_sync_f(session->_queue, session, (dispatch_function_t) SecOTRSPrecalculateKeysInternal);
}
enum SecOTRSMessageKind SecOTRSGetMessageKind(SecOTRSessionRef session, CFDataRef message)
{
OTRMessageType type = SecOTRSGetMessageType(message);
enum SecOTRSMessageKind kind;
switch (type) {
case kDataMessage:
case kEvenCompactDataMessage:
case kOddCompactDataMessage:
case kEvenCompactDataMessageWithHashes:
case kOddCompactDataMessageWithHashes:
kind = kOTRDataPacket;
break;
case kDHMessage:
case kDHKeyMessage:
case kRevealSignatureMessage:
case kSignatureMessage:
kind = kOTRNegotiationPacket;
break;
case kInvalidMessage:
default:
kind = kOTRUnknownPacket;
break;
}
return kind;
}
static OSStatus SecOTRSSignAndProtectRaw_locked(SecOTRSessionRef session,
CFDataRef sourceMessage, CFMutableDataRef destinationMessage,
uint8_t* messageKey, uint8_t* macKey, uint64_t* counter, uint32_t theirKeyID, SecOTRPublicDHKeyRef theirKey)
{
CFIndex start = CFDataGetLength(destinationMessage);
AppendHeader(destinationMessage, kDataMessage);
AppendByte(destinationMessage, 0);
AppendLong(destinationMessage, session->_keyID);
AppendLong(destinationMessage, theirKeyID);
SecFDHKAppendPublicSerialization(session->_myNextKey, destinationMessage);
AppendLongLong(destinationMessage, ++*counter);
CFIndex sourceSize = CFDataGetLength(sourceMessage);
assert(((unsigned long)sourceSize)<=UINT32_MAX);
AppendLong(destinationMessage, (uint32_t)sourceSize);
uint8_t* encryptedDataPointer = CFDataIncreaseLengthAndGetMutableBytes(destinationMessage, sourceSize);
AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes, messageKey,
*counter,
(size_t)sourceSize, CFDataGetBytePtr(sourceMessage),
encryptedDataPointer);
CFIndex macedContentsSize = CFDataGetLength(destinationMessage) - start;
CFIndex macSize = CCSHA1_OUTPUT_SIZE;
uint8_t* macDataPointer = CFDataIncreaseLengthAndGetMutableBytes(destinationMessage, macSize);
cchmac(ccsha1_di(),
kOTRMessageMacKeyBytes, macKey,
macedContentsSize, CFDataGetBytePtr(destinationMessage) + start,
macDataPointer);
CFDataAppend(destinationMessage, session->_macKeysToExpose);
return errSecSuccess;
}
const size_t kCompactMessageMACSize = 16;
static OSStatus SecOTRSSignAndProtectCompact_locked(SecOTRSessionRef session,
CFDataRef sourceMessage, CFMutableDataRef destinationMessage,
uint8_t* messageKey, uint8_t* macKey, uint64_t* counter, uint32_t theirKeyID, SecOTRPublicDHKeyRef theirKey)
{
CFIndex start = CFDataGetLength(destinationMessage);
bool sendHashes = session->_includeHashes;
const uint8_t messageType = sendHashes ? ((theirKeyID & 0x1) ? kOddCompactDataMessageWithHashes : kEvenCompactDataMessageWithHashes)
: ((theirKeyID & 0x1) ? kOddCompactDataMessage : kEvenCompactDataMessage);
AppendByte(destinationMessage, messageType);
SecFDHKAppendCompactPublicSerialization(session->_myNextKey, destinationMessage);
AppendLongLongCompact(destinationMessage, ++*counter);
CFIndex sourceSize = CFDataGetLength(sourceMessage);
assert(((unsigned long)sourceSize)<=UINT32_MAX);
uint8_t* encryptedDataPointer = CFDataIncreaseLengthAndGetMutableBytes(destinationMessage, sourceSize);
AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes, messageKey,
*counter,
(size_t)sourceSize, CFDataGetBytePtr(sourceMessage),
encryptedDataPointer);
if (sendHashes) {
uint8_t *senderHashPtr = CFDataIncreaseLengthAndGetMutableBytes(destinationMessage, kSecDHKHashSize);
memcpy(senderHashPtr, SecFDHKGetHash(session->_myKey), kSecDHKHashSize);
uint8_t *receiverHashPtr = CFDataIncreaseLengthAndGetMutableBytes(destinationMessage, kSecDHKHashSize);
memcpy(receiverHashPtr, SecPDHKGetHash(theirKey), kSecDHKHashSize);
}
CFIndex macedContentsSize = CFDataGetLength(destinationMessage) - start;
CFIndex macSize = CCSHA1_OUTPUT_SIZE;
uint8_t mac[macSize];
cchmac(ccsha1_di(),
kOTRMessageMacKeyBytes, macKey,
macedContentsSize, CFDataGetBytePtr(destinationMessage) + start,
mac);
CFDataAppendBytes(destinationMessage, mac, kCompactMessageMACSize);
return errSecSuccess;
}
OSStatus SecOTRSSignAndProtectMessage(SecOTRSessionRef session,
CFDataRef sourceMessage,
CFMutableDataRef protectedMessage)
{
__block OSStatus result = errSecParam;
require(session, abort);
require(sourceMessage, abort);
require(protectedMessage, abort);
if(session->_state != kDone){
secdebug("OTR", "Cannot sign and protect messages, we are not done negotiating sesion[%p]", session);
require_quiet( session->_state == kDone, abort);
}
dispatch_sync(session->_queue, ^{
if (session->_myKey == NULL ||
session->_theirKey == NULL) {
return;
}
uint8_t *messageKey;
uint8_t *macKey;
uint64_t *counter;
uint32_t theirKeyID = session->_theirKeyID;
SecOTRPublicDHKeyRef theirKeyToUse = session->_theirKey;
SecOTRSRollIfTime(session);
if(session->_stallingTheirRoll && session->_theirPreviousKey){
theirKeyToUse = session->_theirPreviousKey;
theirKeyID = session->_theirKeyID - 1;
}
SecOTRSFindKeysForMessage(session, session->_myKey, theirKeyToUse,
true,
&messageKey, &macKey, &counter);
CFMutableDataRef destinationMessage = session->_textOutput || !protectedMessage ? CFDataCreateMutable(kCFAllocatorDefault, 0) : CFRetainSafe(protectedMessage);
result = session->_compactAppleMessages ? SecOTRSSignAndProtectCompact_locked(session, sourceMessage, destinationMessage, messageKey, macKey, counter, theirKeyID, theirKeyToUse)
: SecOTRSSignAndProtectRaw_locked(session, sourceMessage, destinationMessage, messageKey, macKey, counter, theirKeyID, theirKeyToUse);
if (result == errSecSuccess) {
if (session->_textOutput) {
SecOTRPrepareOutgoingBytes(destinationMessage, protectedMessage);
}
CFDataSetLength(session->_macKeysToExpose, 0);
}
CFReleaseSafe(destinationMessage);
result = errSecSuccess;
});
abort:
return result;
}
void SecOTRSKickTimeToRoll(SecOTRSessionRef session){
session->_timeToRoll = CFAbsoluteTimeGetCurrent();
}
static void SecOTRAcceptNewRemoteKey(SecOTRSessionRef session, SecOTRPublicDHKeyRef newKey)
{
if (session->_theirPreviousKey) {
SecOTRSExpireCachedKeysForPublicKey(session, session->_theirPreviousKey);
}
CFReleaseNull(session->_theirPreviousKey);
session->_theirPreviousKey = session->_theirKey;
session->_theirKey = CFRetainSafe(newKey);
session->_stallingTheirRoll = true;
session->_theirKeyID += 1;
SecOTRSEnableTimeToRoll(session);
}
OSStatus SecOTRSetupInitialRemoteKey(SecOTRSessionRef session, SecOTRPublicDHKeyRef CF_CONSUMED initialKey) {
bzero(session->_keyCache, sizeof(session->_keyCache));
CFReleaseNull(session->_theirPreviousKey);
CFAssignRetained(session->_theirKey, initialKey);
session->_theirKeyID = 1;
return errSecSuccess;
}
void SOSOTRSRoll(SecOTRSessionRef session){
session->_stallingTheirRoll = false;
if(session->_receivedAck){
SecOTRGenerateNewProposedKey(session);
session->_missedAck = false;
session->_receivedAck = false;
}
else{
session->_missedAck = true;
}
}
static OSStatus SecOTRVerifyAndExposeRaw_locked(SecOTRSessionRef session,
CFDataRef decodedBytes,
CFMutableDataRef exposedMessageContents)
{
OSStatus result = errSecDecode;
SecOTRPublicDHKeyRef newKey = NULL;
const uint8_t* bytes;
size_t size;
SecOTRFullDHKeyRef myKeyForMessage = NULL;
SecOTRPublicDHKeyRef theirKeyForMessage = NULL;
bytes = CFDataGetBytePtr(decodedBytes);
size = CFDataGetLength(decodedBytes);
const uint8_t* macDataStart = bytes;
uint32_t theirID;
uint32_t myID;
require_noerr_quiet(result = ReadAndVerifyHeader(&bytes, &size, kDataMessage), fail);
require_action_quiet(size > 0, fail, result = errSecDecode);
require_noerr_quiet(result = ReadAndVerifyByte(&bytes, &size, 0), fail);
require_noerr_quiet(result = ReadLong(&bytes, &size, &theirID), fail);
require_action_quiet(theirID == session->_theirKeyID || (theirID == (session->_theirKeyID - 1) && session->_theirPreviousKey != NULL),
fail,
result = ((theirID + 1) < session->_theirKeyID) ? errSecOTRTooOld : errSecOTRIDTooNew);
require_noerr_quiet(result = ReadLong(&bytes, &size, &myID), fail);
require_action_quiet(myID == session->_keyID || (myID == session->_keyID + 1 && session->_myNextKey != NULL),
fail,
result = (myID < session->_keyID) ? errSecOTRTooOld : errSecOTRIDTooNew);
{
uint8_t *messageKey;
uint8_t *macKey;
uint64_t *theirCounter;
myKeyForMessage = (myID == session->_keyID) ? session->_myKey : session->_myNextKey;
theirKeyForMessage = (theirID == session->_theirKeyID) ? session->_theirKey : session->_theirPreviousKey;
SecOTRSFindKeysForMessage(session, myKeyForMessage, theirKeyForMessage, false,
&messageKey, &macKey, &theirCounter);
size_t nextKeyMPISize;
const uint8_t* nextKeyMPIBytes;
require_noerr_quiet(result = SizeAndSkipMPI(&bytes, &size, &nextKeyMPIBytes, &nextKeyMPISize), fail);
uint64_t counter;
require_noerr_quiet(result = ReadLongLong(&bytes, &size, &counter), fail);
require_action_quiet(counter > *theirCounter, fail, result = errSecOTRTooOld);
size_t messageSize;
const uint8_t* messageStart;
require_noerr_quiet(result = SizeAndSkipDATA(&bytes, &size, &messageStart, &messageSize), fail);
size_t macDataSize = (bytes - macDataStart) ? (size_t)(bytes - macDataStart) : 0;
uint8_t mac[CCSHA1_OUTPUT_SIZE];
require_action_quiet(sizeof(mac) <= size, fail, result = errSecDecode);
cchmac(ccsha1_di(),
kOTRMessageMacKeyBytes, macKey,
macDataSize, macDataStart,
mac);
require_noerr_action_quiet(timingsafe_bcmp(mac, bytes, sizeof(mac)), fail, result = errSecAuthFailed);
uint8_t* dataSpace = CFDataIncreaseLengthAndGetMutableBytes(exposedMessageContents, (CFIndex)messageSize);
AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes, messageKey,
counter,
messageSize, messageStart,
dataSpace);
*theirCounter = counter;
newKey = SecOTRPublicDHKCreateFromBytes(kCFAllocatorDefault, &nextKeyMPIBytes, &nextKeyMPISize);
}
bool acceptTheirNewKey = newKey != NULL && theirID == session->_theirKeyID;
if (acceptTheirNewKey) {
SecOTRAcceptNewRemoteKey(session, newKey);
}
if (myID == (session->_keyID + 1)) {
SecOTRSHandleProposalAcknowledge(session);
}
SecOTRSRollIfTime(session);
SecOTRSPrecalculateNextKeysInternal(session);
fail:
if(result != errSecSuccess){
CFDataPerformWithHexString(decodedBytes, ^(CFStringRef decodedBytesString) {
SecOTRPublicIdentityRef myPublicID = SecOTRPublicIdentityCopyFromPrivate(kCFAllocatorDefault, session->_me, NULL);
SecOTRPIPerformWithSerializationString(myPublicID, ^(CFStringRef myIDString) {
SecOTRPIPerformWithSerializationString(session->_them, ^(CFStringRef theirIDString) {
secnotice("OTR","session[%p] failed to decrypt, session: %@, mk: %@, mpk: %@, tpk: %@, tk: %@, chose tktu: %@", session, session,
session->_myKey, session->_myNextKey, session->_theirPreviousKey, session->_theirKey, theirKeyForMessage);
secnotice("OTR","session[%p] failed to decrypt, mktu: %@, mpi: %@, tpi: %@, m: %@", session, myKeyForMessage, myIDString, theirIDString, decodedBytesString);
});
});
CFReleaseNull(myPublicID);
});
}
CFReleaseNull(newKey);
return result;
}
static OSStatus SecOTRVerifyAndExposeRawCompact_locked(SecOTRSessionRef session,
CFDataRef decodedBytes,
CFMutableDataRef exposedMessageContents)
{
SecOTRPublicDHKeyRef theirProposal = NULL;
OSStatus result = errSecDecode;
const uint8_t* bytes;
size_t size;
SecOTRPublicDHKeyRef theirKeyForMessage = NULL;
bytes = CFDataGetBytePtr(decodedBytes);
size = CFDataGetLength(decodedBytes);
SecOTRFullDHKeyRef myKeyForMessage = NULL;
const uint8_t* macDataStart = bytes;
bool useEvenKey = false;
bool useCurrentKey = false;
bool sentHashes = false;
uint64_t counter = 0;
uint8_t type_byte = 0;
require_noerr_quiet(result = ReadByte(&bytes, &size, &type_byte), fail);
require_action_quiet(type_byte == kOddCompactDataMessage || type_byte == kEvenCompactDataMessage
|| type_byte == kOddCompactDataMessageWithHashes || type_byte == kEvenCompactDataMessageWithHashes, fail, result = errSecDecode);
useEvenKey = (type_byte == kEvenCompactDataMessage || type_byte == kEvenCompactDataMessageWithHashes);
sentHashes = (type_byte == kOddCompactDataMessageWithHashes || type_byte == kEvenCompactDataMessageWithHashes);
useCurrentKey = useEvenKey ^ (session->_keyID & 1);
myKeyForMessage = useCurrentKey ? session->_myKey : session->_myNextKey;
require_action_quiet(myKeyForMessage, fail, result = errSecDecode);
theirProposal = SecOTRPublicDHKCreateFromCompactSerialization(kCFAllocatorDefault, &bytes, &size);
require_action_quiet(theirProposal, fail, result = errSecDecode);
bool proposalIsNew = !CFEqualSafe(theirProposal, session->_theirKey);
theirKeyForMessage = proposalIsNew ? session->_theirKey : session->_theirPreviousKey;
require_action_quiet(theirKeyForMessage, fail, result = errSecDecode);
uint8_t *messageKey;
uint8_t *macKey;
uint64_t *theirCounter;
SecOTRSFindKeysForMessage(session, myKeyForMessage, theirKeyForMessage, false, &messageKey, &macKey, &theirCounter);
require_noerr_quiet(result = ReadLongLongCompact(&bytes, &size, &counter), fail);
require_action_quiet(counter > *theirCounter, fail, result = errSecOTRTooOld);
size_t messageSize = size - kCompactMessageMACSize - (sentHashes ? 2 * kSecDHKHashSize : 0); const uint8_t* messageStart = bytes;
bytes += messageSize;
size -= messageSize;
if (sentHashes) {
if (memcmp(SecPDHKGetHash(theirKeyForMessage), bytes, kSecDHKHashSize) != 0) {
#if DEBUG
BufferPerformWithHexString(bytes, kSecDHKHashSize, ^(CFStringRef dataString) {
secdebug("OTR","session[%p] Sender key hash doesn't match: %@ != %@", session, theirKeyForMessage, dataString);
});
#endif
}
bytes += kSecDHKHashSize;
size -= kSecDHKHashSize;
if (memcmp(SecFDHKGetHash(myKeyForMessage), bytes, kSecDHKHashSize) != 0) {
#if DEBUG
BufferPerformWithHexString(bytes, kSecDHKHashSize, ^(CFStringRef dataString) {
secdebug("OTR","session[%p] Receiver key hash doesn't match: %@ != %@", session, myKeyForMessage, dataString);
});
#endif
}
bytes += kSecDHKHashSize;
size -= kSecDHKHashSize;
}
uint8_t mac[CCSHA1_OUTPUT_SIZE];
require_action_quiet(kCompactMessageMACSize == size, fail, result = errSecDecode);
size_t macDataSize = (size_t)(bytes - macDataStart);
cchmac(ccsha1_di(),
kOTRMessageMacKeyBytes, macKey,
macDataSize, macDataStart,
mac);
require_noerr_action_quiet(timingsafe_bcmp(mac, bytes, kCompactMessageMACSize), fail, result = errSecAuthFailed);
uint8_t* dataSpace = CFDataIncreaseLengthAndGetMutableBytes(exposedMessageContents, (CFIndex)messageSize);
AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes, messageKey,
counter,
messageSize, messageStart,
dataSpace);
*theirCounter = counter;
if (proposalIsNew) {
SecOTRAcceptNewRemoteKey(session, theirProposal);
}
if (!useCurrentKey) {
SecOTRSHandleProposalAcknowledge(session);
}
SecOTRSRollIfTime(session);
SecOTRSPrecalculateNextKeysInternal(session);
fail:
if(result != errSecSuccess){
CFDataPerformWithHexString(decodedBytes, ^(CFStringRef decodedBytesString) {
SecOTRPublicIdentityRef myPublicID = SecOTRPublicIdentityCopyFromPrivate(kCFAllocatorDefault, session->_me, NULL);
SecOTRPIPerformWithSerializationString(myPublicID, ^(CFStringRef myIDString) {
SecOTRPIPerformWithSerializationString(session->_them, ^(CFStringRef theirIDString) {
secnotice("OTR","session[%p] failed to decrypt, session: %@, mk: %@, mpk: %@, tpk: %@, tk: %@, chose tktu: %@", session, session,
session->_myKey, session->_myNextKey, session->_theirPreviousKey, session->_theirKey, theirKeyForMessage);
secnotice("OTR","session[%p] failed to decrypt, mktu: %@, mpi: %@, tpi: %@, m: %@, tP: %@, tb: %hhx", session, myKeyForMessage, myIDString, theirIDString, decodedBytesString, theirProposal, type_byte);
});
});
CFReleaseNull(myPublicID);
});
}
CFReleaseNull(theirProposal);
return result;
}
OSStatus SecOTRSVerifyAndExposeMessage(SecOTRSessionRef session,
CFDataRef incomingMessage,
CFMutableDataRef exposedMessageContents)
{
__block OSStatus result = errSecParam;
require(session, abort);
require(incomingMessage, abort);
require(exposedMessageContents, abort);
if(session->_state == kDone){
dispatch_sync(session->_queue, ^{
CFDataRef decodedBytes = SecOTRCopyIncomingBytes(incomingMessage);
OTRMessageType messageType = SecOTRSGetMessageType(decodedBytes);
switch (messageType) {
case kDataMessage:
result = SecOTRVerifyAndExposeRaw_locked(session, decodedBytes, exposedMessageContents);
break;
case kOddCompactDataMessage:
case kEvenCompactDataMessage:
case kOddCompactDataMessageWithHashes:
case kEvenCompactDataMessageWithHashes:
result = SecOTRVerifyAndExposeRawCompact_locked(session, decodedBytes, exposedMessageContents);
break;
default:
result = errSecUnsupportedFormat;
break;
}
CFReleaseSafe(decodedBytes);
});
}
else{
secnotice("OTR", "session[%p]Cannot process message:%@, session is not done negotiating, session state: %@", session, incomingMessage, session);
result = errSecOTRNotReady;
}
abort:
return result;
}
OSStatus SecOTRSEndSession(SecOTRSessionRef session,
CFMutableDataRef messageToSend)
{
return errSecUnimplemented;
}
static CFDataRef data_to_data_error_request(enum SecXPCOperation op, CFDataRef publicPeerId, CFErrorRef *error) {
__block CFDataRef result = NULL;
securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
return SecXPCDictionarySetDataOptional(message, kSecXPCPublicPeerId, publicPeerId, error);
}, ^bool(xpc_object_t response, CFErrorRef *error) {
return (result = SecXPCDictionaryCopyData(response, kSecXPCKeyResult, error));
});
return result;
}
static bool data_data_to_data_data_bool_error_request(enum SecXPCOperation op, CFDataRef sessionData, CFDataRef inputPacket, CFDataRef* outputSessionData, CFDataRef* outputPacket, bool *readyForMessages, CFErrorRef *error) {
__block CFDataRef tempOutputSessionData = NULL;
__block CFDataRef tempOutputPacket = NULL;
__block bool tempReadyForMessages = false;
bool result = securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
return SecXPCDictionarySetDataOptional(message, kSecXPCOTRSession, sessionData, error)
&& SecXPCDictionarySetDataOptional(message, kSecXPCData, inputPacket, error);
}, ^bool(xpc_object_t response, CFErrorRef *error) {
if (xpc_dictionary_get_bool(response, kSecXPCKeyResult)) {
tempOutputSessionData = SecXPCDictionaryCopyData(response, kSecXPCOTRSession, error);
tempOutputPacket = SecXPCDictionaryCopyData(response, kSecXPCData, error);
tempReadyForMessages = xpc_dictionary_get_bool(response, kSecXPCOTRReady);
return true;
} else {
return false;
}
});
*outputSessionData = tempOutputSessionData;
*outputPacket = tempOutputPacket;
*readyForMessages = tempReadyForMessages;
return result;
}
CFDataRef SecOTRSessionCreateRemote(CFDataRef publicPeerId, CFErrorRef *error) {
__block CFDataRef result;
os_activity_initiate("SecOTRSessionCreateRemote", OS_ACTIVITY_FLAG_DEFAULT, ^{
(void)SecOTRGetDefaultsWriteSeconds();
result = SECURITYD_XPC(sec_otr_session_create_remote, data_to_data_error_request, publicPeerId, error);
});
return result;
}
bool SecOTRSessionProcessPacketRemote(CFDataRef sessionData, CFDataRef inputPacket, CFDataRef* outputSessionData, CFDataRef* outputPacket, bool *readyForMessages, CFErrorRef *error) {
__block bool result;
os_activity_initiate("SecOTRSessionProcessPacketRemote", OS_ACTIVITY_FLAG_DEFAULT, ^{
result = SECURITYD_XPC(sec_otr_session_process_packet_remote, data_data_to_data_data_bool_error_request, sessionData, inputPacket, outputSessionData, outputPacket, readyForMessages, error);
});
return result;
}