1/* 2 * Copyright (c) 2010-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#include "SecEncodeTransform.h" 25#include "SecDecodeTransform.h" 26#include "SecCustomTransform.h" 27#include "CoreFoundation/CoreFoundation.h" 28#include "misc.h" 29#include "Utilities.h" 30#include <zlib.h> 31#include <malloc/malloc.h> 32 33const static CFStringRef DecodeName = CFSTR("com.apple.security.Decoder"); 34const static CFStringRef EncodeName = CFSTR("com.apple.security.Encoder"); 35// base32 & base64 are as per RFC 4648 36const CFStringRef kSecBase64Encoding = CFSTR("base64"); 37const CFStringRef kSecBase32Encoding = CFSTR("base32"); 38// kSecBase32FDEEncoding is SPI (8436055), it avoids I and O, and uses 8 and 9. 39// Not good for number form dislexics, but avoids the appearance of a conflict 40// between 0 and O or 1 and I (note: 0 and 1 are not used anyway, so there is 41// no conflict). 42const CFStringRef kSecBase32FDEEncoding = CFSTR("base32FDE"); 43const CFStringRef kSecZLibEncoding = CFSTR("zlib"); 44const CFStringRef kSecEncodeTypeAttribute = CFSTR("EncodeType"); 45const CFStringRef kSecDecodeTypeAttribute = CFSTR("DecodeType"); 46const CFStringRef kSecEncodeLineLengthAttribute = CFSTR("LineLength"); 47const CFStringRef kSecCompressionRatio = CFSTR("CompressionRatio"); 48 49// There is no way to initialize a const CFNumberRef, so these 50// either need to be non-const, or they need to be a CF type 51// with a const constructor (CFStringRef). 52const CFStringRef kSecLineLength64 = CFSTR("64"); 53const CFStringRef kSecLineLength76 = CFSTR("76"); 54 55static unsigned char Base64Vals[] = 56{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 57 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 58 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 59 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 60 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 61 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, 62 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 63 0x3c, 0x3d, 0xff, 0xff, 0xff, 0x40, 0xff, 0xff, 64 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 65 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 66 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 67 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 68 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 69 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 70 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 71 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 72 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 73 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 74 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 75 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 76 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 77 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 78 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 79 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 80 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 81 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 82 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 83 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 84 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 85 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 86 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 87 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 88 89static char Base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 90"abcdefghijklmnopqrstuvwxyz" 91"0123456789" 92"+/="; 93 94static unsigned char Base32Vals[] = {0xff, 0xff, 0xff, 95 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 96 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 97 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 98 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 99 0xff, 0xff, 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0xff, 0xff, 100 0xff, 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 101 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 102 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 103 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 104 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 105 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 106 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 107 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 108 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 109 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 110 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 111 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 112 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 113 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 114 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 115 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 116 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 117 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 118 119static unsigned char Base32FDEVals[] = {0xff, 0xff, 0xff, 120 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 121 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 122 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 123 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 124 0xff, 0xff, 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x08, 0x12, 125 0xff, 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 126 0x04, 0x05, 0x06, 0x07, 0xff, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 127 0x0f, 0x10, 0x11, 0xff, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 128 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 129 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 130 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 131 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 132 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 133 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 134 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 135 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 136 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 137 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 138 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 139 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 140 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 141 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 142 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 143 144/* -------------------------------------------------------------------------- 145 function: DecodeTransform 146 description: This function returns a block that implements the 147 Decode Transfrom 148 -------------------------------------------------------------------------- */ 149static SecTransformInstanceBlock DecodeTransform(CFStringRef name, 150 SecTransformRef newTransform, 151 SecTransformImplementationRef ref) 152{ 153 SecTransformInstanceBlock instanceBlock = 154 ^{ 155 CFErrorRef result = NULL; 156 SecTransformCustomSetAttribute(ref, kSecDecodeTypeAttribute, 157 kSecTransformMetaAttributeRequired, kCFBooleanTrue); 158 159 SecTransformSetAttributeAction(ref, 160 kSecTransformActionAttributeNotification, 161 kSecDecodeTypeAttribute, 162 ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) 163 { 164 if (NULL == value || CFGetTypeID(value) != CFStringGetTypeID()) 165 { 166 CFErrorRef errorResult = fancy_error(kSecTransformErrorDomain, 167 kSecTransformErrorInvalidInput, 168 CFSTR("Decode type was not a CFStringRef")); 169 return (CFTypeRef)errorResult; 170 } 171 // value is a CFStringRef 172 if (kCFCompareEqualTo == CFStringCompare(value, kSecBase64Encoding, 0)) 173 { 174 __block struct { unsigned char a[4]; } leftover; 175 static const short int in_chunk_size = 4; 176 static const short int out_chunk_size = 3; 177 __block int leftover_cnt = 0; 178 179 SecTransformSetDataAction(ref, kSecTransformActionProcessData, 180 ^(CFTypeRef value) 181 { 182 CFDataRef d = value; 183 CFIndex enc_cnt = d ? CFDataGetLength(d) : 0; 184 const unsigned char *enc = d ? CFDataGetBytePtr(d) : NULL; 185 const unsigned char *enc_end = enc + enc_cnt; 186 long n_chunks = (leftover_cnt + enc_cnt) / out_chunk_size + 1; 187 188 unsigned char *out_base = malloc(n_chunks * out_chunk_size); 189 if (!out_base) { 190 return (CFTypeRef) GetNoMemoryError(); 191 } 192 unsigned char *out_end = out_base + n_chunks * out_chunk_size; 193 unsigned char *out = out_base; 194 int chunk_i = leftover_cnt; 195 196 for(; enc < enc_end || !enc; chunk_i++) { 197 unsigned char ch, b; 198 if (enc) { 199 ch = *enc++; 200 } else { 201 ch = '='; 202 } 203 if (ch == ' ' || ch == '\n' || ch == '\r') { 204 chunk_i -= 1; 205 continue; 206 } 207 208 b = Base64Vals[ch]; 209 if (b != 0xff) { 210 leftover.a[chunk_i] = b; 211 } 212 213 if (chunk_i == in_chunk_size-1 || ch == '=') { 214 *out = (leftover.a[0] & 0x3f) << 2; 215 *out++ |= ((leftover.a[1] & 0x3f) >> 4); 216 *out = (leftover.a[1] & 0x0f) << 4; 217 *out++ |= (leftover.a[2] & 0x3f) >> 2; 218 *out = (leftover.a[2] & 0x03) << 6; 219 *out++ |= (leftover.a[3] & 0x3f); 220 221 out -= 3 - chunk_i; 222 if (ch == '=') { 223 if (chunk_i != 0) { 224 out--; 225 } 226 chunk_i = -1; 227 break; 228 } 229 chunk_i = -1; 230 } 231 } 232 leftover_cnt = (chunk_i > 0) ? chunk_i : 0; 233 if (out > out_end) { 234 // We really shouldn't get here, but if we do we just smashed something. 235 abort(); 236 } 237 238 CFDataRef ret = CFDataCreateWithBytesNoCopy(NULL, out_base, out - out_base, kCFAllocatorMalloc); 239 if (!d) { 240 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 241 kSecTransformMetaAttributeValue, ret); 242 CFRelease(ret); 243 ret = NULL; 244 } 245 return (CFTypeRef)ret; 246 }); 247 } 248 else if (kCFCompareEqualTo == CFStringCompare(value, kSecBase32Encoding, 0) || kCFCompareEqualTo == CFStringCompare(value, kSecBase32FDEEncoding, 0)) 249 { 250 __block struct { uint64_t a[2]; } accumulator = { .a = {0, 0}}; 251 __block short int bits_accumulated = 0; 252 //static const short int in_chunk_size = 5, out_chunk_size = 8; 253 static const short int out_chunk_size = 8; 254 const short int full_accumulator = 80; 255 unsigned char *base32values = NULL; 256 257 if (kCFCompareEqualTo == CFStringCompare(value, kSecBase32Encoding, 0)) { 258 base32values = Base32Vals; 259 } else if (kCFCompareEqualTo == CFStringCompare(value, kSecBase32FDEEncoding, 0)) { 260 base32values = Base32FDEVals; 261 } 262 263 if (NULL == base32values) { 264 // There is only one supported type, so we don't want to mention it in an error message 265 CFErrorRef bad_type = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Unknown base32 type '%@'", value); 266 267 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, bad_type); 268 269 return (CFTypeRef)bad_type; 270 } 271 272 SecTransformSetDataAction(ref, kSecTransformActionProcessData, 273 ^(CFTypeRef value) 274 { 275 CFDataRef d = value; 276 CFIndex enc_cnt = d ? CFDataGetLength(d) : 0; 277 const unsigned char *enc = d ? CFDataGetBytePtr(d) : NULL; 278 const unsigned char *enc_end = enc + enc_cnt; 279 long n_chunks = (bits_accumulated/8 + enc_cnt) / out_chunk_size + 1; 280 281 unsigned char *out_base = malloc(n_chunks * out_chunk_size); 282 if (!out_base) { 283 return (CFTypeRef)GetNoMemoryError(); 284 } 285 unsigned char *out_end = out_base + n_chunks * out_chunk_size; 286 unsigned char *out = out_base; 287 288 for(; enc < enc_end || !d;) { 289 unsigned char ch, b; 290 if (enc) { 291 ch = *enc++; 292 } else { 293 ch = '='; 294 } 295 296 b = base32values[ch]; 297 if (b == 0xff) { 298 continue; 299 } 300 301 if (ch != '=') { 302 // 5 new low order bits 303 accumulator.a[1] = accumulator.a[1] << 5 | (0x1f & (accumulator.a[0] >> (64 -5))); 304 accumulator.a[0] = accumulator.a[0] << 5 | b; 305 bits_accumulated += 5; 306 } 307 if (bits_accumulated == full_accumulator || ch == '=') { 308 short shifted = 0; 309 for(; shifted + bits_accumulated < full_accumulator; shifted += 5) { 310 accumulator.a[1] = accumulator.a[1] << 5 | (0x1f & accumulator.a[0] >> (64 -5)); 311 accumulator.a[0] = accumulator.a[0] << 5; 312 } 313 for(; bits_accumulated >= 8; bits_accumulated -= 8) { 314 // Get 8 high bits 315 *out++ = accumulator.a[1] >> (80 - 64 - 8); 316 accumulator.a[1] = (accumulator.a[1] << 8 | accumulator.a[0] >> (64 - 8)) & 0xffff; 317 accumulator.a[0] = accumulator.a[0] << 8; 318 } 319 bits_accumulated = 0; 320 if (ch == '=') { 321 break; 322 } 323 } 324 } 325 if (out > out_end) { 326 // We really shouldn't get here, but if we do we just smashed something. 327 abort(); 328 } 329 330 CFDataRef ret = CFDataCreateWithBytesNoCopy(NULL, out_base, out - out_base, kCFAllocatorMalloc); 331 if (!d) { 332 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 333 kSecTransformMetaAttributeValue, ret); 334 CFRelease(ret); 335 ret = NULL; 336 } 337 return (CFTypeRef)ret; 338 }); 339 } 340 else if (kCFCompareEqualTo == CFStringCompare(value, kSecZLibEncoding, 0)) 341 { 342 __block z_stream zs; 343 __block Boolean started = FALSE; 344 345 CFBooleanRef hasRatio = (CFBooleanRef)SecTranformCustomGetAttribute(ref, 346 kSecCompressionRatio, kSecTransformMetaAttributeHasOutboundConnections); 347 Boolean ratio_connected = (kCFBooleanTrue == hasRatio); 348 349 bzero(&zs, sizeof(zs)); 350 351 SecTransformSetDataAction(ref, kSecTransformActionProcessData, 352 ^(CFTypeRef value) 353 { 354 CFDataRef d = value; 355 if (!started) { 356 if (!d) { 357 return (CFTypeRef)NULL; 358 } 359 started = TRUE; 360 inflateInit(&zs); 361 } 362 363 if (d) { 364 zs.next_in = (UInt8 *)(CFDataGetBytePtr(d)); // we know that zlib will not 'futz' with the data 365 zs.avail_in = (uInt)CFDataGetLength(d); 366 } else { 367 zs.next_in = NULL; 368 zs.avail_in = 0; 369 } 370 371 int rc = Z_OK; 372 373 CFIndex buf_sz = malloc_good_size(zs.avail_in ? zs.avail_in : 1024 * 4); 374 375 while ((d && zs.avail_in) || (d == NULL && rc != Z_STREAM_END)) { 376 unsigned char *buf = malloc(buf_sz); 377 if (!buf) { 378 return (CFTypeRef)GetNoMemoryError(); 379 } 380 381 zs.next_out = buf; 382 zs.avail_out = (uInt)buf_sz; 383 384 rc = inflate(&zs, d ? Z_NO_FLUSH : Z_FINISH); 385 386 CFIndex buf_used = buf_sz - zs.avail_out; 387#ifdef DEBUG_ZLIB_MEMORY_USE 388 // It might be useful to look at these and tweak things like when we should use DataCreate vs. DataCreateWithBytesNoCopy 389 CFfprintf(stderr, ">>zavail_in %d buf_sz %d; d %p; ", zs.avail_in, buf_sz, d); 390 CFfprintf(stderr, "rc=%d %s", rc, (rc == Z_OK) ? "Z_OK" : (rc == Z_STREAM_END) ? "Z_STREAM_END" : (rc == Z_BUF_ERROR) ? "Z_BUF_ERROR" : "?"); 391 CFfprintf(stderr, " (output used %d, input left %d)\n", buf_used, zs.avail_in); 392#endif 393 if (rc == Z_OK || rc == Z_STREAM_END) { 394 CFDataRef d; 395 if ((4 * buf_used) / buf_sz <= 1) { 396 // we would waste 25%+ of the buffer, make a smaller copy and release the original 397 d = CFDataCreate(NULL, buf, buf_used); 398 free(buf); 399 } else { 400 d = CFDataCreateWithBytesNoCopy(NULL, buf, buf_used, kCFAllocatorMalloc); 401 } 402 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 403 kSecTransformMetaAttributeValue, d); 404 CFRelease(d); 405 } else if (rc == Z_BUF_ERROR) { 406 free(buf); 407 if ((int)buf_sz > (1 << Z_BEST_COMPRESSION) && 0 == zs.avail_in) { 408 // zlib has an odd convention about EOF and Z_BUF_ERROR, see http://www.zlib.net/zlib_how.html 409 // Z_BUF_ERROR can mean "you don't have a big enough output buffer, please enlarge", or "the input buffer is 410 // empty, please get more data". So if we get Z_BUF_ERROR, and there are 0 bytes of input, and the output 411 // buffer is larger the the maximum number of bytes a single symbol can decode to (2^compression level, which 412 // is at most Z_BEST_COMPRESSION) we KNOW the complaint isn't about the output buffer, but the input 413 // buffer and we are free to go. NOTE: we will only hit this if we are at the end of the stream, and the prior 414 // data chunk was already entirely decoded. 415 rc = Z_STREAM_END; 416 } 417 buf_sz = malloc_good_size(buf_sz * 2); 418 } else { 419 free(buf); 420 CFStringRef emsg = CFStringCreateWithFormat(NULL, NULL, CFSTR("Zlib error#%d"), rc); 421 CFErrorRef err = fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidInput, emsg); 422 CFRelease(emsg); 423 return (CFTypeRef)err; 424 } 425 } 426 427 if (ratio_connected && zs.total_in && zs.total_out) { 428 float r = (float)zs.total_in / zs.total_out; 429 CFNumberRef ratio = CFNumberCreate(NULL, kCFNumberFloatType, &r); 430 SecTransformCustomSetAttribute(ref, kSecCompressionRatio, 431 kSecTransformMetaAttributeValue, ratio); 432 CFRelease(ratio); 433 } 434 435 if (rc == Z_OK) { 436 return (CFTypeRef)SecTransformNoData(); 437 } else if (rc == Z_STREAM_END) { 438 inflateEnd(&zs); 439 started = FALSE; 440 return (CFTypeRef)NULL; 441 } 442 CFStringRef emsg = CFStringCreateWithFormat(NULL, NULL, CFSTR("Zlib error#%d"), rc); 443 CFErrorRef err = fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidInput, emsg); 444 CFRelease(emsg); 445 return (CFTypeRef)err; 446 }); 447 } 448 else 449 { 450 CFErrorRef bad_type = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Unsupported decode type '%@', supported types are kSecBase64Encoding, kSecBase32Encoding, and kSecGZipEncoding", value); 451 452 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, bad_type); 453 454 return (CFTypeRef)bad_type; 455 } 456 return value; 457 }); 458 459 return result; 460 }; 461 462 return Block_copy(instanceBlock); 463} 464 465 466SecTransformRef SecDecodeTransformCreate(CFTypeRef DecodeType, CFErrorRef* error) { 467 468 static dispatch_once_t once; 469 __block Boolean ok = TRUE; 470 CFErrorRef localError = NULL; 471 472 dispatch_block_t aBlock = ^ 473 { 474 ok = SecTransformRegister(DecodeName, &DecodeTransform, (CFErrorRef*)&localError); 475 }; 476 477 dispatch_once(&once, aBlock); 478 479 if (!ok || NULL != localError) 480 { 481 if (NULL != error) 482 { 483 *error = localError; 484 } 485 return NULL; 486 } 487 488 SecTransformRef tr = SecTransformCreate(DecodeName, &localError); 489 if (!tr || NULL != localError) 490 { 491 // There might be a leak if tr is returned but localError is 492 // not NULL, but that should not happen 493 if (NULL != error) 494 { 495 *error = localError; 496 } 497 return NULL; 498 } 499 500 SecTransformSetAttribute(tr, kSecDecodeTypeAttribute, DecodeType, &localError); 501 if (NULL != localError) 502 { 503 CFRelease(tr); 504 tr = NULL; 505 if (NULL != error) 506 { 507 *error = localError; 508 } 509 } 510 511 return tr; 512} 513 514static 515unsigned char *encode_base64(const unsigned char *bin, unsigned char *base64, CFIndex bin_cnt) { 516 for(; bin_cnt > 0; bin_cnt -= 3, base64 += 4, bin += 3) { 517 switch (bin_cnt) 518 { 519 default: 520 case 3: 521 base64[0] = Base64Chars[((bin[0] >> 2) & 0x3f)]; 522 base64[1] = Base64Chars[((bin[0] & 0x03) << 4) | 523 ((bin[1] >> 4) & 0x0f)]; 524 base64[2] = Base64Chars[((bin[1] & 0x0f) << 2) | 525 ((bin[2] >> 6) & 0x03)]; 526 base64[3] = Base64Chars[(bin[2] & 0x3f)]; 527 break; 528 529 case 2: 530 base64[3] = '='; 531 base64[0] = Base64Chars[((bin[0] >> 2) & 0x3f)]; 532 base64[1] = Base64Chars[((bin[0] & 0x03) << 4) | 533 ((bin[1] >> 4) & 0x0f)]; 534 base64[2] = Base64Chars[((bin[1] & 0x0f) << 2)]; 535 break; 536 537 case 1: 538 base64[3] = base64[2] = '='; 539 base64[0] = Base64Chars[((bin[0] >> 2) & 0x3f)]; 540 base64[1] = Base64Chars[((bin[0] & 0x03) << 4)]; 541 break; 542 543 case 0: 544 base64[0] = base64[1] = base64[2] = base64[3] = '='; 545 break; 546 } 547 } 548 549 return base64; 550} 551 552 553/* -------------------------------------------------------------------------- 554 function: DecodeTransform 555 description: This function returns a block that implements the 556 Decode Transfrom 557 -------------------------------------------------------------------------- */ 558static SecTransformInstanceBlock EncodeTransform(CFStringRef name, 559 SecTransformRef newTransform, 560 SecTransformImplementationRef ref) 561 562{ 563 SecTransformInstanceBlock instanceBlock = 564 ^{ 565 CFErrorRef result = NULL; 566 SecTransformCustomSetAttribute(ref, kSecEncodeTypeAttribute, 567 kSecTransformMetaAttributeRequired, kCFBooleanTrue); 568 569 __block int line_length = 0, target_line_length = 0; 570 571 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, 572 kSecEncodeLineLengthAttribute, 573 ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) 574 { 575 SecTransformPushbackAttribute(ref, attribute, value); 576 return value; 577 }); 578 579 CFTypeRef (^new_line_length)(int out_chunk_size, CFTypeRef value) = ^(int out_chunk_size, CFTypeRef value) 580 { 581 if (CFGetTypeID(value) == CFNumberGetTypeID()) { 582 CFNumberGetValue((CFNumberRef)value, kCFNumberIntType, &target_line_length); 583 } else if (CFGetTypeID(value) == CFStringGetTypeID()) { 584 int requested_length = CFStringGetIntValue(value); 585 if (requested_length == 0 && CFStringCompare(CFSTR("0"), value, kCFCompareAnchored)) { 586 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Could not convert '%@' to a number, please set %@ to a numeric value", kSecEncodeLineLengthAttribute, value)); 587 } else { 588 target_line_length = requested_length; 589 } 590 } else { 591 CFStringRef valueType = CFCopyTypeIDDescription(CFGetTypeID(value)); 592 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "%@ requires a CFNumber, but was set to a %@ (%@)", kSecEncodeLineLengthAttribute, valueType, value)); 593 CFRelease(valueType); 594 } 595 target_line_length -= target_line_length % out_chunk_size; 596 597 if (target_line_length < 0) { 598 target_line_length = 0; 599 } 600 601 return value; 602 }; 603 604 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, 605 kSecEncodeTypeAttribute, 606 ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) 607 { 608 if (NULL == value || CFGetTypeID(value) != CFStringGetTypeID()) 609 { 610 CFErrorRef errorResult = fancy_error(kSecTransformErrorDomain, 611 kSecTransformErrorInvalidInput, 612 CFSTR("Encode type was not a CFStringRef")); 613 return (CFTypeRef)errorResult; 614 } 615 616 if (kCFCompareEqualTo == CFStringCompare(value, kSecBase64Encoding, 0)) 617 { 618 __block struct { unsigned char a[3]; } leftover; 619 static const short int in_chunk_size = 3, out_chunk_size = 4; 620 __block CFIndex leftover_cnt = 0; 621 622 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, 623 kSecEncodeLineLengthAttribute, 624 ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) 625 { 626 return new_line_length(out_chunk_size, value); 627 }); 628 629 SecTransformSetDataAction(ref, kSecTransformActionProcessData, 630 ^(CFTypeRef value) 631 { 632 CFDataRef d = value; 633 CFIndex in_len = d ? CFDataGetLength(d) : 0; 634 const unsigned char *in = d ? CFDataGetBytePtr(d) : NULL; 635 CFIndex n_chunks = in_len / in_chunk_size + 3; 636 CFIndex buf_len = n_chunks * out_chunk_size; 637 CFIndex line_len=0; 638 639 if (target_line_length) 640 { 641 line_len=(n_chunks * out_chunk_size) / target_line_length; 642 } 643 if ( (in_len<0) 644 || (leftover_cnt<0) 645#if __LLP64__ 646 || (n_chunks > LONG_LONG_MAX/out_chunk_size) 647 || (buf_len > LONG_LONG_MAX-line_len) 648#else 649 || (n_chunks > LONG_MAX/out_chunk_size) 650 || (buf_len > LONG_MAX-line_len) 651#endif 652 || (buf_len+line_len<in_len)) 653 { 654 CFErrorRef errorResult = fancy_error(kSecTransformErrorDomain, 655 kSecTransformErrorInvalidLength, 656 CFSTR("Invalid length")); 657 return (CFTypeRef)errorResult; 658 } 659 buf_len+=line_len; 660 unsigned char *out = malloc(buf_len); 661 unsigned char *out_end = out + buf_len, *out_base = out; 662 if (!out) 663 { 664 return (CFTypeRef)GetNoMemoryError(); 665 } 666 if ((leftover_cnt) && (in_chunk_size>= leftover_cnt)) 667 { 668 CFIndex copy_len = in_chunk_size - leftover_cnt; 669 copy_len = (copy_len > in_len) ? in_len : copy_len; 670 memcpy(leftover.a + leftover_cnt, in, copy_len); 671 672 if (copy_len + leftover_cnt == in_chunk_size || d == NULL) 673 { 674 out = encode_base64(leftover.a, out, copy_len + leftover_cnt); 675 if (in) 676 { 677 in += copy_len; 678 in_len -= copy_len; 679 } 680 } 681 else 682 { 683 free(out); 684 leftover_cnt += copy_len; 685 return (CFTypeRef)SecTransformNoData(); 686 } 687 } 688 689 CFIndex chunked_in_len; 690 while (in_len >= in_chunk_size) 691 { 692 chunked_in_len = in_len - (in_len % in_chunk_size); 693 if (target_line_length) 694 { 695 if (target_line_length <= line_length + out_chunk_size) 696 { 697 *out++ = '\n'; 698 line_length = 0; 699 } 700 int max_process = (((target_line_length - line_length) / out_chunk_size) * in_chunk_size); 701 chunked_in_len = (chunked_in_len < max_process) ? chunked_in_len : max_process; 702 } 703 unsigned char *old_out = out; 704 out = encode_base64(in, out, chunked_in_len); 705 line_length += out - old_out; 706 in += chunked_in_len; 707 in_len -= chunked_in_len; 708 } 709 leftover_cnt = in_len; 710 if (leftover_cnt) 711 { 712 memcpy(leftover.a, in, leftover_cnt); 713 } 714 715 if (out > out_end) 716 { 717 // we should never hit this, but if we do there is no recovery: we smashed past a buffer into the heap 718 abort(); 719 } 720 721 CFTypeRef ret = CFDataCreateWithBytesNoCopy(NULL, out_base, out - out_base, kCFAllocatorMalloc); 722 if (!d) 723 { 724 SecTransformCustomSetAttribute(ref,kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, ret); 725 CFRelease(ret); 726 ret = NULL; 727 } 728 return ret; 729 }); 730 } 731 else if (kCFCompareEqualTo == CFStringCompare(value, kSecBase32Encoding, 0) || kCFCompareEqualTo == CFStringCompare(value, kSecBase32FDEEncoding, 0)) 732 { 733 __block struct { uint64_t a[2]; } accumulator = { .a = {0, 0} }; 734 __block short int bits_accumulated = 0; 735 static const short int in_chunk_size = 5; 736 static const short int out_chunk_size = 8; 737 char *base32alphabet = NULL; 738 739 if (kCFCompareEqualTo == CFStringCompare(value, kSecBase32Encoding, 0)) { 740 base32alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; 741 } else if (kCFCompareEqualTo == CFStringCompare(value, kSecBase32FDEEncoding, 0)) { 742 base32alphabet = "ABCDEFGH8JKLMNOPQR9TUVWXYZ234567"; 743 } 744 745 if (NULL == base32alphabet) { 746 // There is only one supported type, so we don't want to mention it in an error message 747 CFErrorRef bad_type = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Unknown base32 type '%@'", value); 748 749 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, bad_type); 750 751 return (CFTypeRef)bad_type; 752 } 753 754 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, 755 kSecEncodeLineLengthAttribute, 756 ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) 757 { 758 return new_line_length(out_chunk_size, value); 759 }); 760 761 SecTransformSetDataAction(ref, kSecTransformActionProcessData, 762 ^(CFTypeRef value) 763 { 764 CFDataRef d = value; 765 CFIndex in_len = d ? CFDataGetLength(d) : 0; 766 const unsigned char *in = d ? CFDataGetBytePtr(d) : NULL; 767 const unsigned char *in_end = in + in_len; 768 CFIndex n_chunks = in_len / in_chunk_size + 3; 769 CFIndex buf_len = n_chunks * out_chunk_size; 770 if (target_line_length) 771 { 772 buf_len += (n_chunks * out_chunk_size) / target_line_length; 773 } 774 __block unsigned char *out = malloc(buf_len); 775 unsigned char *out_end = out + buf_len, *out_base = out; 776 if (!out) { 777 return (CFTypeRef)GetNoMemoryError(); 778 } 779 780 void (^chunk)(void) = ^{ 781 // Grab the 5 bit (log(32)==5) values from the 80 bit accumulator. Most signifigant bits first 782 783 // (this could be done without the loop, which would save few cycles at the end of a stream) 784 short int shift = 80 - bits_accumulated; 785 for(; shift > 0; shift -= 8) { 786 accumulator.a[1] = accumulator.a[1] << 8 | accumulator.a[0] >> (64 - 8); 787 accumulator.a[0] = accumulator.a[0] << 8; 788 } 789 790 for(; bits_accumulated > 0; bits_accumulated -= 5) { 791 *out++ = base32alphabet[(accumulator.a[1] >> 11) & 0x1f]; 792 accumulator.a[1] = 0xffff & (accumulator.a[1] << 5 | (accumulator.a[0] >> (64 - 5))); 793 accumulator.a[0] = accumulator.a[0] << 5; 794 if (++line_length >= target_line_length && target_line_length) { 795 *out++ = '\n'; 796 line_length = 0; 797 } 798 } 799 bits_accumulated = 0; 800 }; 801 802 for (; in < in_end; in++) 803 { 804 accumulator.a[1] = accumulator.a[1] << 8 | accumulator.a[0] >> (64 - 8); 805 accumulator.a[0] = accumulator.a[0] << 8 | *in; 806 bits_accumulated += 8; 807 if (bits_accumulated == 8*in_chunk_size) 808 { 809 chunk(); 810 } 811 } 812 813 if (!d && bits_accumulated) { 814 short int padding = 0; 815 switch(bits_accumulated) { 816 case 8: 817 padding = 6; 818 break; 819 case 16: 820 padding = 4; 821 break; 822 case 24: 823 padding = 3; 824 break; 825 case 32: 826 padding = 1; 827 break; 828 } 829 chunk(); 830 int i; 831 for(i = 0; i < padding; i++) { 832 *out++ = '='; 833 } 834 } 835 836 if (out > out_end) { 837 // we should never hit this, but if we do there is no recovery: we smashed past a buffer into the heap 838 abort(); 839 } 840 841 CFTypeRef ret = NULL; 842 if (out - out_base) { 843 ret = CFDataCreateWithBytesNoCopy(NULL, out_base, out - out_base, kCFAllocatorMalloc); 844 } else { 845 ret = SecTransformNoData(); 846 } 847 if (!d) { 848 if (ret != SecTransformNoData()) { 849 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 850 kSecTransformMetaAttributeValue, ret); 851 CFRelease(ret); 852 } 853 ret = NULL; 854 } 855 return ret; 856 }); 857 } 858 else if (kCFCompareEqualTo == CFStringCompare(value, kSecZLibEncoding, 0)) 859 { 860 __block z_stream zs; 861 bzero(&zs, sizeof(zs)); 862 __block int clevel = Z_DEFAULT_COMPRESSION; 863 __block Boolean started = FALSE; 864 865 CFBooleanRef hasRatio = (CFBooleanRef)SecTranformCustomGetAttribute(ref, kSecCompressionRatio, 866 kSecTransformMetaAttributeHasOutboundConnections); 867 868 Boolean ratio_connected = (kCFBooleanTrue == hasRatio); 869 870 SecTransformSetDataAction(ref, kSecTransformActionProcessData, 871 ^(CFTypeRef value) 872 { 873 CFDataRef d = value; 874 875 if (!started) { 876 started = TRUE; 877 deflateInit(&zs, clevel); 878 } 879 880 if (d) { 881 zs.next_in = (UInt8 *)CFDataGetBytePtr(d); // We know that xLib will not 'Futz' with the data 882 zs.avail_in = (uInt)CFDataGetLength(d); 883 } else { 884 zs.next_in = NULL; 885 zs.avail_in = 0; 886 } 887 888 int rc = Z_BUF_ERROR; 889 890 CFIndex buf_sz = malloc_good_size(zs.avail_in ? zs.avail_in : 1024 * 4); 891 892 while ((d && zs.avail_in) || (d == NULL && rc != Z_STREAM_END)) { 893 unsigned char *buf = malloc(buf_sz); 894 if (!buf) { 895 return (CFTypeRef)GetNoMemoryError(); 896 } 897 898 zs.next_out = buf; 899 zs.avail_out = (uInt)buf_sz; 900 901 rc = deflate(&zs, d ? Z_NO_FLUSH : Z_FINISH); 902 903 CFIndex buf_used = buf_sz - zs.avail_out; 904 #ifdef DEBUG_ZLIB_MEMORY_USE 905 // It might be useful to look at these and tweak things like when we should use DataCreate vs. DataCreateWithBytesNoCopy 906 CFfprintf(stderr, "<<zavail_in %d buf_sz %d; d %p; ", zs.avail_in, buf_sz, d); 907 CFfprintf(stderr, "rc=%d %s", rc, (rc == Z_OK) ? "Z_OK" : (rc == Z_STREAM_END) ? "Z_FINISH" : (rc == Z_BUF_ERROR) ? "Z_BUF_ERROR" : "?"); 908 CFfprintf(stderr, " (output used %d, input left %d)\n", buf_used, zs.avail_in); 909 #endif 910 if (rc == Z_OK || rc == Z_STREAM_END) { 911 CFDataRef d; 912 if ((4 * buf_used) / buf_sz <= 1) { 913 // we would waste 25%+ of the buffer, make a smaller copy and release the original 914 d = CFDataCreate(NULL, buf, buf_used); 915 free(buf); 916 } else { 917 d = CFDataCreateWithBytesNoCopy(NULL, buf, buf_used, kCFAllocatorMalloc); 918 } 919 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 920 kSecTransformMetaAttributeValue, d); 921 CFRelease(d); 922 } else if (rc == Z_BUF_ERROR) { 923 free(buf); 924 buf_sz = malloc_good_size(buf_sz * 2); 925 } else { 926 free(buf); 927 CFStringRef emsg = CFStringCreateWithFormat(NULL, NULL, CFSTR("Zlib error#%d"), rc); 928 CFErrorRef err = fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidInput, emsg); 929 CFRelease(emsg); 930 return (CFTypeRef)err; 931 } 932 } 933 if (ratio_connected && zs.total_in && zs.total_out) { 934 float r = (float)zs.total_out / zs.total_in; 935 CFNumberRef ratio = CFNumberCreate(NULL, kCFNumberFloatType, &r); 936 SecTransformCustomSetAttribute(ref, kSecCompressionRatio, 937 kSecTransformMetaAttributeValue, ratio); 938 CFRelease(ratio); 939 } 940 if (d) { 941 return (CFTypeRef)SecTransformNoData(); 942 } else { 943 deflateEnd(&zs); 944 started = FALSE; 945 return (CFTypeRef)NULL; 946 } 947 }); 948 949 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, 950 kSecEncodeLineLengthAttribute, ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) 951 { 952 return value; 953 }); 954 } 955 else 956 { 957 CFErrorRef bad_type = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Unsupported encode type '%@', supported types are kSecBase64Encoding, kSecBase32Encoding, and kSecGZipEncoding", value); 958 959 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, bad_type); 960 961 return (CFTypeRef)bad_type; 962 } 963 964 return (CFTypeRef)value; 965 }); 966 967 return result; 968 }; 969 970 return Block_copy(instanceBlock); 971} 972 973SecTransformRef SecEncodeTransformCreate(CFTypeRef EncodeType, CFErrorRef* error) 974{ 975 976 static dispatch_once_t once; 977 __block Boolean ok = TRUE; 978 CFErrorRef localError = NULL; 979 980 dispatch_block_t aBlock = ^ 981 { 982 ok = SecTransformRegister(EncodeName, &EncodeTransform, (CFErrorRef*)&localError); 983 }; 984 985 dispatch_once(&once, aBlock); 986 987 if (!ok || NULL != localError) 988 { 989 if (NULL != error) 990 { 991 *error = localError; 992 } 993 994 return NULL; 995 } 996 997 SecTransformRef tr = SecTransformCreate(EncodeName, &localError); 998 if (!tr || NULL != localError) 999 { 1000 // There might be a leak if tr is returned but localError is 1001 // not NULL, but that should not happen 1002 if (NULL != error) 1003 { 1004 *error = localError; 1005 } 1006 return NULL; 1007 } 1008 1009 SecTransformSetAttribute(tr, kSecEncodeTypeAttribute, EncodeType, &localError); 1010 if (NULL != localError) 1011 { 1012 CFRelease(tr); 1013 tr = NULL; 1014 if (NULL != error) 1015 { 1016 *error = localError; 1017 } 1018 } 1019 1020 return tr; 1021} 1022