1// 2// SecOTRUtils.c 3// libSecOTR 4// 5// Created by Keith Henrickson on 6/11/12. 6// 7// 8 9#include "SecOTR.h" 10#include "SecOTRIdentityPriv.h" 11#include "SecOTRSessionPriv.h" 12#include <utilities/SecCFWrappers.h> 13#include <stdlib.h> 14 15#include <AssertMacros.h> 16 17#include <Security/SecBase.h> 18#include <Security/SecItem.h> 19#include <Security/SecKey.h> 20#include <Security/SecKeyPriv.h> 21#include <Security/SecBase64.h> 22 23#include <TargetConditionals.h> 24 25CFStringRef sLocalErrorDomain = CFSTR("com.apple.security.otr.error"); 26 27void SecOTRCreateError(enum SecOTRError family, CFIndex errorCode, CFStringRef descriptionString, CFErrorRef previousError, CFErrorRef *newError) { 28 if (newError && !(*newError)) { 29 const void* keys[2] = {kCFErrorDescriptionKey, kCFErrorUnderlyingErrorKey}; 30 const void* values[2] = {descriptionString, previousError}; 31 *newError = CFErrorCreateWithUserInfoKeysAndValues(kCFAllocatorDefault, (family == secOTRErrorLocal) ? sLocalErrorDomain : kCFErrorDomainOSStatus, errorCode, keys, values, (previousError != NULL) ? 2 : 1); 32 } else { 33 CFReleaseSafe(previousError); 34 } 35} 36 37OSStatus insertSize(CFIndex size, uint8_t* here) 38{ 39 require(size < 0xFFFF, fail); 40 41 uint8_t bytes[] = { (size >> 8) & 0xFF, size & 0xFF }; 42 memcpy(here, bytes, sizeof(bytes)); 43 44 return errSecSuccess; 45 46fail: 47 return errSecParam; 48} 49 50OSStatus appendSize(CFIndex size, CFMutableDataRef into) 51{ 52 require(size < 0xFFFF, fail); 53 54 uint8_t bytes[] = { (size >> 8) & 0xFF, size & 0xFF }; 55 CFDataAppendBytes(into, bytes, sizeof(bytes)); 56 57 return errSecSuccess; 58 59fail: 60 return errSecParam; 61} 62 63OSStatus readSize(const uint8_t** data, size_t* limit, uint16_t* size) 64{ 65 require(limit != NULL, fail); 66 require(data != NULL, fail); 67 require(size != NULL, fail); 68 require(*limit > 1, fail); 69 70 *size = ((uint16_t)(*data)[0]) << 8 | ((uint16_t) (*data)[1]) << 0; 71 72 *limit -= 2; 73 *data += 2; 74 75 return errSecSuccess; 76fail: 77 return errSecParam; 78} 79 80OSStatus appendSizeAndData(CFDataRef data, CFMutableDataRef appendTo) 81{ 82 OSStatus status = errSecNotAvailable; 83 84 require_noerr(appendSize(CFDataGetLength(data), appendTo), exit); 85 CFDataAppend(appendTo, data); 86 87 status = errSecSuccess; 88 89exit: 90 return status; 91} 92 93OSStatus appendPublicOctetsAndSize(SecKeyRef fromKey, CFMutableDataRef appendTo) 94{ 95 OSStatus status = errSecDecode; 96 CFDataRef serializedKey = NULL; 97 98 require_noerr(SecKeyCopyPublicBytes(fromKey, &serializedKey), exit); 99 require(serializedKey, exit); 100 101 status = appendSizeAndData(serializedKey, appendTo); 102 103exit: 104 CFReleaseNull(serializedKey); 105 return status; 106} 107 108OSStatus appendPublicOctets(SecKeyRef fromKey, CFMutableDataRef appendTo) 109{ 110 OSStatus status = errSecDecode; 111 CFDataRef serializedKey = NULL; 112 113 require_noerr(SecKeyCopyPublicBytes(fromKey, &serializedKey), exit); 114 require(serializedKey, exit); 115 116 CFDataAppend(appendTo, serializedKey); 117 118 status = errSecSuccess; 119 120exit: 121 CFReleaseNull(serializedKey); 122 return status; 123} 124 125 126/* Given an EC public key in encoded form return a SecKeyRef representing 127 that key. Supported encodings are kSecKeyEncodingPkcs1. */ 128static SecKeyRef SecKeyCreateECPublicKey(CFAllocatorRef allocator, 129 const uint8_t *keyData, CFIndex keyDataLength) { 130 CFDataRef tempData = CFDataCreate(kCFAllocatorDefault, keyData, keyDataLength); 131 SecKeyRef newPublicKey = SecKeyCreateFromPublicData(kCFAllocatorDefault, kSecECDSAAlgorithmID, tempData); 132 133 CFRelease(tempData); 134 return newPublicKey; 135} 136 137typedef SecKeyRef (*createFunction_t)(CFAllocatorRef allocator, 138 const uint8_t *keyData, CFIndex keyDataLength); 139 140static SecKeyRef CallCreateFunctionFrom(CFAllocatorRef allocator, const uint8_t** data, size_t* limit, createFunction_t create) 141{ 142 uint16_t foundLength = 0; 143 const uint8_t* foundData = NULL; 144 145 require(limit != NULL, fail); 146 require(data != NULL, fail); 147 148 require_noerr(readSize(data, limit, &foundLength), fail); 149 require(foundLength <= *limit, fail); 150 151 foundData = *data; 152 153 *limit -= foundLength; 154 *data += foundLength; 155 156fail: 157 158 return create(allocator, foundData, foundLength); 159} 160 161SecKeyRef CreateECPublicKeyFrom(CFAllocatorRef allocator, const uint8_t** data, size_t* limit) 162{ 163 return CallCreateFunctionFrom(allocator, data, limit, &SecKeyCreateECPublicKey); 164} 165 166void SecOTRGetIncomingBytes(CFDataRef incomingMessage, CFMutableDataRef decodedBytes) 167{ 168 CFDataRef header = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("?OTR:"), kCFStringEncodingUTF8, '?'); 169 CFDataRef footer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("."), kCFStringEncodingUTF8, '?'); 170 CFRange headerLoc = CFDataFind(incomingMessage, header, CFRangeMake(0, CFDataGetLength(header)), 0); 171 CFRange footerLoc = CFDataFind(incomingMessage, footer, CFRangeMake(0, CFDataGetLength(incomingMessage)), 0); 172 if (kCFNotFound == headerLoc.location) { 173 CFDataAppend(decodedBytes, incomingMessage); 174 } else { 175 CFDataRef bodyData = CFDataCreateReferenceFromRange(kCFAllocatorDefault, incomingMessage, CFRangeMake(headerLoc.length, footerLoc.location - headerLoc.length)); 176 size_t size = SecBase64Decode((char*)CFDataGetBytePtr(bodyData), CFDataGetLength(bodyData), NULL, 0); 177 uint8_t decodedByteArray[size]; 178 SecBase64Decode((char*)CFDataGetBytePtr(bodyData), CFDataGetLength(bodyData), decodedByteArray, size); 179 CFDataAppendBytes(decodedBytes, decodedByteArray, size); 180 CFRelease(bodyData); 181 } 182 CFRelease(header); 183 CFRelease(footer); 184} 185 186void SecOTRPrepareOutgoingBytes(CFMutableDataRef destinationMessage, CFMutableDataRef protectedMessage) 187{ 188 CFDataRef header = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("?OTR:"), kCFStringEncodingUTF8, '?'); 189 CFDataRef footer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("."), kCFStringEncodingUTF8, '?'); 190 size_t base64Len = SecBase64Encode(CFDataGetBytePtr(destinationMessage), CFDataGetLength(destinationMessage), NULL, 0); 191 char base64Message [base64Len]; 192 SecBase64Encode(CFDataGetBytePtr(destinationMessage), CFDataGetLength(destinationMessage), base64Message, base64Len); 193 CFDataRef base64Data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (uint8_t*)base64Message, base64Len, kCFAllocatorNull); 194 195 CFDataAppend(protectedMessage, header); 196 CFDataAppend(protectedMessage, base64Data); 197 CFDataAppend(protectedMessage, footer); 198 199 CFRelease(header); 200 CFRelease(footer); 201 CFRelease(base64Data); 202} 203 204 205