--- /dev/null
+//
+// SecOTRUtils.c
+// libSecOTR
+//
+// Created by Keith Henrickson on 6/11/12.
+//
+//
+
+#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;
+}
+
+
+/* Given an EC public key in encoded form return a SecKeyRef representing
+ that key. Supported encodings are kSecKeyEncodingPkcs1. */
+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);
+}
+
+