SecOTRUtils.c
1 /* 2 * Copyright (c) 2012,2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25 #include "SecOTR.h" 26 #include "SecOTRIdentityPriv.h" 27 #include "SecOTRSessionPriv.h" 28 #include <utilities/SecCFWrappers.h> 29 #include <utilities/SecBuffer.h> 30 #include <stdlib.h> 31 32 #include <AssertMacros.h> 33 34 #include <Security/SecBase.h> 35 #include <Security/SecItem.h> 36 #include <Security/SecKey.h> 37 #include <Security/SecKeyPriv.h> 38 #include <Security/SecBase64.h> 39 40 #include <TargetConditionals.h> 41 42 CFStringRef sLocalErrorDomain = CFSTR("com.apple.security.otr.error"); 43 44 bool SecOTRCreateError(enum SecOTRError family, CFIndex errorCode, CFStringRef descriptionString, CFErrorRef previousError, CFErrorRef *newError) { 45 if (newError && !(*newError)) { 46 const void* keys[2] = {kCFErrorDescriptionKey, kCFErrorUnderlyingErrorKey}; 47 const void* values[2] = {descriptionString, previousError}; 48 *newError = CFErrorCreateWithUserInfoKeysAndValues(kCFAllocatorDefault, (family == secOTRErrorLocal) ? sLocalErrorDomain : kCFErrorDomainOSStatus, errorCode, keys, values, (previousError != NULL) ? 2 : 1); 49 } else { 50 CFReleaseSafe(previousError); 51 } 52 53 return false; 54 } 55 56 OSStatus insertSize(CFIndex size, uint8_t* here) 57 { 58 require(size < 0xFFFF, fail); 59 60 uint8_t bytes[] = { (size >> 8) & 0xFF, size & 0xFF }; 61 memcpy(here, bytes, sizeof(bytes)); 62 63 return errSecSuccess; 64 65 fail: 66 return errSecParam; 67 } 68 69 OSStatus appendSize(CFIndex size, CFMutableDataRef into) 70 { 71 require(size < 0xFFFF, fail); 72 73 uint8_t bytes[] = { (size >> 8) & 0xFF, size & 0xFF }; 74 CFDataAppendBytes(into, bytes, sizeof(bytes)); 75 76 return errSecSuccess; 77 78 fail: 79 return errSecParam; 80 } 81 82 OSStatus readSize(const uint8_t** data, size_t* limit, uint16_t* size) 83 { 84 require(limit != NULL, fail); 85 require(data != NULL, fail); 86 require(size != NULL, fail); 87 require(*limit > 1, fail); 88 89 *size = ((uint16_t)(*data)[0]) << 8 | ((uint16_t) (*data)[1]) << 0; 90 91 *limit -= 2; 92 *data += 2; 93 94 return errSecSuccess; 95 fail: 96 return errSecParam; 97 } 98 99 OSStatus appendSizeAndData(CFDataRef data, CFMutableDataRef appendTo) 100 { 101 OSStatus status = errSecNotAvailable; 102 103 require_noerr(appendSize(CFDataGetLength(data), appendTo), exit); 104 CFDataAppend(appendTo, data); 105 106 status = errSecSuccess; 107 108 exit: 109 return status; 110 } 111 112 OSStatus appendPublicOctetsAndSize(SecKeyRef fromKey, CFMutableDataRef appendTo) 113 { 114 OSStatus status = errSecDecode; 115 CFDataRef serializedKey = NULL; 116 117 require_noerr(SecKeyCopyPublicBytes(fromKey, &serializedKey), exit); 118 require(serializedKey, exit); 119 120 status = appendSizeAndData(serializedKey, appendTo); 121 122 exit: 123 CFReleaseNull(serializedKey); 124 return status; 125 } 126 127 OSStatus appendPublicOctets(SecKeyRef fromKey, CFMutableDataRef appendTo) 128 { 129 OSStatus status = errSecDecode; 130 CFDataRef serializedKey = NULL; 131 132 require_noerr(SecKeyCopyPublicBytes(fromKey, &serializedKey), exit); 133 require(serializedKey, exit); 134 135 CFDataAppend(appendTo, serializedKey); 136 137 status = errSecSuccess; 138 139 exit: 140 CFReleaseNull(serializedKey); 141 return status; 142 } 143 144 145 /* Given an EC public key in encoded form return a SecKeyRef representing 146 that key. Supported encodings are kSecKeyEncodingPkcs1. */ 147 static SecKeyRef SecKeyCreateECPublicKey(CFAllocatorRef allocator, 148 const uint8_t *keyData, CFIndex keyDataLength) { 149 CFDataRef tempData = CFDataCreate(kCFAllocatorDefault, keyData, keyDataLength); 150 SecKeyRef newPublicKey = SecKeyCreateFromPublicData(kCFAllocatorDefault, kSecECDSAAlgorithmID, tempData); 151 152 CFRelease(tempData); 153 return newPublicKey; 154 } 155 156 typedef SecKeyRef (*createFunction_t)(CFAllocatorRef allocator, 157 const uint8_t *keyData, CFIndex keyDataLength); 158 159 static SecKeyRef CallCreateFunctionFrom(CFAllocatorRef allocator, const uint8_t** data, size_t* limit, createFunction_t create) 160 { 161 uint16_t foundLength = 0; 162 const uint8_t* foundData = NULL; 163 164 require(limit != NULL, fail); 165 require(data != NULL, fail); 166 167 require_noerr(readSize(data, limit, &foundLength), fail); 168 require(foundLength <= *limit, fail); 169 170 foundData = *data; 171 172 *limit -= foundLength; 173 *data += foundLength; 174 175 fail: 176 177 return create(allocator, foundData, foundLength); 178 } 179 180 SecKeyRef CreateECPublicKeyFrom(CFAllocatorRef allocator, const uint8_t** data, size_t* limit) 181 { 182 return CallCreateFunctionFrom(allocator, data, limit, &SecKeyCreateECPublicKey); 183 } 184 185 CFDataRef SecOTRCopyIncomingBytes(CFDataRef incomingMessage) 186 { 187 __block CFDataRef result = NULL; 188 189 CFDataRef header = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("?OTR:"), kCFStringEncodingUTF8, '?'); 190 CFRange headerLoc = CFDataFind(incomingMessage, header, CFRangeMake(0, CFDataGetLength(header)), 0); 191 192 if (kCFNotFound == headerLoc.location) { 193 CFRetainAssign(result, incomingMessage); 194 } else { 195 CFDataRef footer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("."), kCFStringEncodingUTF8, '?'); 196 CFRange footerLoc = CFDataFind(incomingMessage, footer, CFRangeMake(0, CFDataGetLength(incomingMessage)), 0); 197 198 CFDataRef bodyData = CFDataCreateReferenceFromRange(kCFAllocatorDefault, incomingMessage, CFRangeMake(headerLoc.length, footerLoc.location - headerLoc.length)); 199 200 size_t expectedSize = SecBase64Decode((char*)CFDataGetBytePtr(bodyData), CFDataGetLength(bodyData), NULL, 0); 201 PerformWithBuffer(expectedSize, ^(size_t size, uint8_t *decodedByteArray) { 202 size_t usedSize = SecBase64Decode((char*)CFDataGetBytePtr(bodyData), CFDataGetLength(bodyData), decodedByteArray, size); 203 if (usedSize > 0 && usedSize <= size ) { 204 result = CFDataCreate(kCFAllocatorDefault, decodedByteArray, usedSize); 205 } 206 }); 207 208 CFRelease(bodyData); 209 CFRelease(footer); 210 } 211 CFRelease(header); 212 213 return result; 214 } 215 216 void SecOTRPrepareOutgoingBytes(CFMutableDataRef destinationMessage, CFMutableDataRef protectedMessage) 217 { 218 CFDataRef header = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("?OTR:"), kCFStringEncodingUTF8, '?'); 219 CFDataRef footer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("."), kCFStringEncodingUTF8, '?'); 220 size_t base64Len = SecBase64Encode(CFDataGetBytePtr(destinationMessage), CFDataGetLength(destinationMessage), NULL, 0); 221 char base64Message [base64Len]; 222 SecBase64Encode(CFDataGetBytePtr(destinationMessage), CFDataGetLength(destinationMessage), base64Message, base64Len); 223 CFDataRef base64Data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (uint8_t*)base64Message, base64Len, kCFAllocatorNull); 224 225 CFDataAppend(protectedMessage, header); 226 CFDataAppend(protectedMessage, base64Data); 227 CFDataAppend(protectedMessage, footer); 228 229 CFRelease(header); 230 CFRelease(footer); 231 CFRelease(base64Data); 232 } 233 234