#include "SecOTR.h"
#include "SecOTRIdentityPriv.h"
#include "SecOTRSessionPriv.h"
#include <utilities/SecCFWrappers.h>
#include <stdlib.h>
#include <AssertMacros.h>
#include <Security/SecBase.h>
#include <Security/SecItem.h>
#include <Security/SecKey.h>
#include <Security/SecKeyPriv.h>
#include <Security/SecBase64.h>
#include <TargetConditionals.h>
CFStringRef sLocalErrorDomain = CFSTR("com.apple.security.otr.error");
void SecOTRCreateError(enum SecOTRError family, CFIndex errorCode, CFStringRef descriptionString, CFErrorRef previousError, CFErrorRef *newError) {
if (newError && !(*newError)) {
const void* keys[2] = {kCFErrorDescriptionKey, kCFErrorUnderlyingErrorKey};
const void* values[2] = {descriptionString, previousError};
*newError = CFErrorCreateWithUserInfoKeysAndValues(kCFAllocatorDefault, (family == secOTRErrorLocal) ? sLocalErrorDomain : kCFErrorDomainOSStatus, errorCode, keys, values, (previousError != NULL) ? 2 : 1);
} else {
CFReleaseSafe(previousError);
}
}
OSStatus insertSize(CFIndex size, uint8_t* here)
{
require(size < 0xFFFF, fail);
uint8_t bytes[] = { (size >> 8) & 0xFF, size & 0xFF };
memcpy(here, bytes, sizeof(bytes));
return errSecSuccess;
fail:
return errSecParam;
}
OSStatus appendSize(CFIndex size, CFMutableDataRef into)
{
require(size < 0xFFFF, fail);
uint8_t bytes[] = { (size >> 8) & 0xFF, size & 0xFF };
CFDataAppendBytes(into, bytes, sizeof(bytes));
return errSecSuccess;
fail:
return errSecParam;
}
OSStatus readSize(const uint8_t** data, size_t* limit, uint16_t* size)
{
require(limit != NULL, fail);
require(data != NULL, fail);
require(size != NULL, fail);
require(*limit > 1, fail);
*size = ((uint16_t)(*data)[0]) << 8 | ((uint16_t) (*data)[1]) << 0;
*limit -= 2;
*data += 2;
return errSecSuccess;
fail:
return errSecParam;
}
OSStatus appendSizeAndData(CFDataRef data, CFMutableDataRef appendTo)
{
OSStatus status = errSecNotAvailable;
require_noerr(appendSize(CFDataGetLength(data), appendTo), exit);
CFDataAppend(appendTo, data);
status = errSecSuccess;
exit:
return status;
}
OSStatus appendPublicOctetsAndSize(SecKeyRef fromKey, CFMutableDataRef appendTo)
{
OSStatus status = errSecDecode;
CFDataRef serializedKey = NULL;
require_noerr(SecKeyCopyPublicBytes(fromKey, &serializedKey), exit);
require(serializedKey, exit);
status = appendSizeAndData(serializedKey, appendTo);
exit:
CFReleaseNull(serializedKey);
return status;
}
OSStatus appendPublicOctets(SecKeyRef fromKey, CFMutableDataRef appendTo)
{
OSStatus status = errSecDecode;
CFDataRef serializedKey = NULL;
require_noerr(SecKeyCopyPublicBytes(fromKey, &serializedKey), exit);
require(serializedKey, exit);
CFDataAppend(appendTo, serializedKey);
status = errSecSuccess;
exit:
CFReleaseNull(serializedKey);
return status;
}
static SecKeyRef SecKeyCreateECPublicKey(CFAllocatorRef allocator,
const uint8_t *keyData, CFIndex keyDataLength) {
CFDataRef tempData = CFDataCreate(kCFAllocatorDefault, keyData, keyDataLength);
SecKeyRef newPublicKey = SecKeyCreateFromPublicData(kCFAllocatorDefault, kSecECDSAAlgorithmID, tempData);
CFRelease(tempData);
return newPublicKey;
}
typedef SecKeyRef (*createFunction_t)(CFAllocatorRef allocator,
const uint8_t *keyData, CFIndex keyDataLength);
static SecKeyRef CallCreateFunctionFrom(CFAllocatorRef allocator, const uint8_t** data, size_t* limit, createFunction_t create)
{
uint16_t foundLength = 0;
const uint8_t* foundData = NULL;
require(limit != NULL, fail);
require(data != NULL, fail);
require_noerr(readSize(data, limit, &foundLength), fail);
require(foundLength <= *limit, fail);
foundData = *data;
*limit -= foundLength;
*data += foundLength;
fail:
return create(allocator, foundData, foundLength);
}
SecKeyRef CreateECPublicKeyFrom(CFAllocatorRef allocator, const uint8_t** data, size_t* limit)
{
return CallCreateFunctionFrom(allocator, data, limit, &SecKeyCreateECPublicKey);
}
void SecOTRGetIncomingBytes(CFDataRef incomingMessage, CFMutableDataRef decodedBytes)
{
CFDataRef header = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("?OTR:"), kCFStringEncodingUTF8, '?');
CFDataRef footer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("."), kCFStringEncodingUTF8, '?');
CFRange headerLoc = CFDataFind(incomingMessage, header, CFRangeMake(0, CFDataGetLength(header)), 0);
CFRange footerLoc = CFDataFind(incomingMessage, footer, CFRangeMake(0, CFDataGetLength(incomingMessage)), 0);
if (kCFNotFound == headerLoc.location) {
CFDataAppend(decodedBytes, incomingMessage);
} else {
CFDataRef bodyData = CFDataCreateReferenceFromRange(kCFAllocatorDefault, incomingMessage, CFRangeMake(headerLoc.length, footerLoc.location - headerLoc.length));
size_t size = SecBase64Decode((char*)CFDataGetBytePtr(bodyData), CFDataGetLength(bodyData), NULL, 0);
uint8_t decodedByteArray[size];
SecBase64Decode((char*)CFDataGetBytePtr(bodyData), CFDataGetLength(bodyData), decodedByteArray, size);
CFDataAppendBytes(decodedBytes, decodedByteArray, size);
CFRelease(bodyData);
}
CFRelease(header);
CFRelease(footer);
}
void SecOTRPrepareOutgoingBytes(CFMutableDataRef destinationMessage, CFMutableDataRef protectedMessage)
{
CFDataRef header = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("?OTR:"), kCFStringEncodingUTF8, '?');
CFDataRef footer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("."), kCFStringEncodingUTF8, '?');
size_t base64Len = SecBase64Encode(CFDataGetBytePtr(destinationMessage), CFDataGetLength(destinationMessage), NULL, 0);
char base64Message [base64Len];
SecBase64Encode(CFDataGetBytePtr(destinationMessage), CFDataGetLength(destinationMessage), base64Message, base64Len);
CFDataRef base64Data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (uint8_t*)base64Message, base64Len, kCFAllocatorNull);
CFDataAppend(protectedMessage, header);
CFDataAppend(protectedMessage, base64Data);
CFDataAppend(protectedMessage, footer);
CFRelease(header);
CFRelease(footer);
CFRelease(base64Data);
}