EncodeDecodeTransforms.c
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 33 const static CFStringRef DecodeName = CFSTR("com.apple.security.Decoder"); 34 const static CFStringRef EncodeName = CFSTR("com.apple.security.Encoder"); 35 // base32 & base64 are as per RFC 4648 36 const CFStringRef kSecBase64Encoding = CFSTR("base64"); 37 const 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). 42 const CFStringRef kSecBase32FDEEncoding = CFSTR("base32FDE"); 43 const CFStringRef kSecZLibEncoding = CFSTR("zlib"); 44 const CFStringRef kSecEncodeTypeAttribute = CFSTR("EncodeType"); 45 const CFStringRef kSecDecodeTypeAttribute = CFSTR("DecodeType"); 46 const CFStringRef kSecEncodeLineLengthAttribute = CFSTR("LineLength"); 47 const 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). 52 const CFStringRef kSecLineLength64 = CFSTR("64"); 53 const CFStringRef kSecLineLength76 = CFSTR("76"); 54 55 static 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 89 static char Base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 90 "abcdefghijklmnopqrstuvwxyz" 91 "0123456789" 92 "+/="; 93 94 static 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 119 static 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 -------------------------------------------------------------------------- */ 149 static 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 CFReleaseNull(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 CFReleaseNull(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 CFReleaseNull(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 CFReleaseNull(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 CFReleaseNull(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 CFReleaseNull(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 466 SecTransformRef 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 CFSafeRelease(tr); // protect against leaking tr 498 return NULL; 499 } 500 501 SecTransformSetAttribute(tr, kSecDecodeTypeAttribute, DecodeType, &localError); 502 if (NULL != localError) 503 { 504 CFReleaseNull(tr); 505 tr = NULL; 506 if (NULL != error) 507 { 508 *error = localError; 509 } 510 } 511 512 return tr; 513 } 514 515 static 516 unsigned char *encode_base64(const unsigned char *bin, unsigned char *base64, CFIndex bin_cnt) { 517 for(; bin_cnt > 0; bin_cnt -= 3, base64 += 4, bin += 3) { 518 switch (bin_cnt) 519 { 520 default: 521 case 3: 522 base64[0] = Base64Chars[((bin[0] >> 2) & 0x3f)]; 523 base64[1] = Base64Chars[((bin[0] & 0x03) << 4) | 524 ((bin[1] >> 4) & 0x0f)]; 525 base64[2] = Base64Chars[((bin[1] & 0x0f) << 2) | 526 ((bin[2] >> 6) & 0x03)]; 527 base64[3] = Base64Chars[(bin[2] & 0x3f)]; 528 break; 529 530 case 2: 531 base64[3] = '='; 532 base64[0] = Base64Chars[((bin[0] >> 2) & 0x3f)]; 533 base64[1] = Base64Chars[((bin[0] & 0x03) << 4) | 534 ((bin[1] >> 4) & 0x0f)]; 535 base64[2] = Base64Chars[((bin[1] & 0x0f) << 2)]; 536 break; 537 538 case 1: 539 base64[3] = base64[2] = '='; 540 base64[0] = Base64Chars[((bin[0] >> 2) & 0x3f)]; 541 base64[1] = Base64Chars[((bin[0] & 0x03) << 4)]; 542 break; 543 544 case 0: 545 base64[0] = base64[1] = base64[2] = base64[3] = '='; 546 break; 547 } 548 } 549 550 return base64; 551 } 552 553 554 /* -------------------------------------------------------------------------- 555 function: DecodeTransform 556 description: This function returns a block that implements the 557 Decode Transfrom 558 -------------------------------------------------------------------------- */ 559 static SecTransformInstanceBlock EncodeTransform(CFStringRef name, 560 SecTransformRef newTransform, 561 SecTransformImplementationRef ref) 562 563 { 564 SecTransformInstanceBlock instanceBlock = 565 ^{ 566 CFErrorRef result = NULL; 567 SecTransformCustomSetAttribute(ref, kSecEncodeTypeAttribute, 568 kSecTransformMetaAttributeRequired, kCFBooleanTrue); 569 570 __block int line_length = 0, target_line_length = 0; 571 572 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, 573 kSecEncodeLineLengthAttribute, 574 ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) 575 { 576 SecTransformPushbackAttribute(ref, attribute, value); 577 return value; 578 }); 579 580 CFTypeRef (^new_line_length)(int out_chunk_size, CFTypeRef value) = ^(int out_chunk_size, CFTypeRef value) 581 { 582 if (CFGetTypeID(value) == CFNumberGetTypeID()) { 583 CFNumberGetValue((CFNumberRef)value, kCFNumberIntType, &target_line_length); 584 } else if (CFGetTypeID(value) == CFStringGetTypeID()) { 585 int requested_length = CFStringGetIntValue(value); 586 if (requested_length == 0 && CFStringCompare(CFSTR("0"), value, kCFCompareAnchored)) { 587 CFErrorRef error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Could not convert '%@' to a number, please set %@ to a numeric value", kSecEncodeLineLengthAttribute, value); 588 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, error); 589 CFReleaseNull(error); 590 } else { 591 target_line_length = requested_length; 592 } 593 } else { 594 CFStringRef valueType = CFCopyTypeIDDescription(CFGetTypeID(value)); 595 CFErrorRef error = CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "%@ requires a CFNumber, but was set to a %@ (%@)", kSecEncodeLineLengthAttribute, valueType, value); 596 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, error); 597 CFReleaseNull(valueType); 598 CFReleaseNull(error); 599 } 600 target_line_length -= target_line_length % out_chunk_size; 601 602 if (target_line_length < 0) { 603 target_line_length = 0; 604 } 605 606 return value; 607 }; 608 609 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, 610 kSecEncodeTypeAttribute, 611 ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) 612 { 613 if (NULL == value || CFGetTypeID(value) != CFStringGetTypeID()) 614 { 615 CFErrorRef errorResult = fancy_error(kSecTransformErrorDomain, 616 kSecTransformErrorInvalidInput, 617 CFSTR("Encode type was not a CFStringRef")); 618 return (CFTypeRef)errorResult; 619 } 620 621 if (kCFCompareEqualTo == CFStringCompare(value, kSecBase64Encoding, 0)) 622 { 623 __block struct { unsigned char a[3]; } leftover; 624 static const short int in_chunk_size = 3, out_chunk_size = 4; 625 __block CFIndex leftover_cnt = 0; 626 627 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, 628 kSecEncodeLineLengthAttribute, 629 ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) 630 { 631 return new_line_length(out_chunk_size, value); 632 }); 633 634 SecTransformSetDataAction(ref, kSecTransformActionProcessData, 635 ^(CFTypeRef value) 636 { 637 CFDataRef d = value; 638 CFIndex in_len = d ? CFDataGetLength(d) : 0; 639 const unsigned char *in = d ? CFDataGetBytePtr(d) : NULL; 640 CFIndex n_chunks = in_len / in_chunk_size + 3; 641 CFIndex buf_len = n_chunks * out_chunk_size; 642 CFIndex line_len=0; 643 644 if (target_line_length) 645 { 646 line_len=(n_chunks * out_chunk_size) / target_line_length; 647 } 648 if ( (in_len<0) 649 || (leftover_cnt<0) 650 #if __LLP64__ 651 || (n_chunks > LONG_LONG_MAX/out_chunk_size) 652 || (buf_len > LONG_LONG_MAX-line_len) 653 #else 654 || (n_chunks > LONG_MAX/out_chunk_size) 655 || (buf_len > LONG_MAX-line_len) 656 #endif 657 || (buf_len+line_len<in_len)) 658 { 659 CFErrorRef errorResult = fancy_error(kSecTransformErrorDomain, 660 kSecTransformErrorInvalidLength, 661 CFSTR("Invalid length")); 662 return (CFTypeRef)errorResult; 663 } 664 buf_len+=line_len; 665 unsigned char *out = malloc(buf_len); 666 unsigned char *out_end = out + buf_len, *out_base = out; 667 if (!out) 668 { 669 return (CFTypeRef)GetNoMemoryError(); 670 } 671 if ((leftover_cnt) && (in_chunk_size>= leftover_cnt)) 672 { 673 CFIndex copy_len = in_chunk_size - leftover_cnt; 674 copy_len = (copy_len > in_len) ? in_len : copy_len; 675 memcpy(leftover.a + leftover_cnt, in, copy_len); 676 677 if (copy_len + leftover_cnt == in_chunk_size || d == NULL) 678 { 679 out = encode_base64(leftover.a, out, copy_len + leftover_cnt); 680 if (in) 681 { 682 in += copy_len; 683 in_len -= copy_len; 684 } 685 } 686 else 687 { 688 free(out); 689 leftover_cnt += copy_len; 690 return (CFTypeRef)SecTransformNoData(); 691 } 692 } 693 694 CFIndex chunked_in_len; 695 while (in_len >= in_chunk_size) 696 { 697 chunked_in_len = in_len - (in_len % in_chunk_size); 698 if (target_line_length) 699 { 700 if (target_line_length <= line_length + out_chunk_size) 701 { 702 *out++ = '\n'; 703 line_length = 0; 704 } 705 int max_process = (((target_line_length - line_length) / out_chunk_size) * in_chunk_size); 706 chunked_in_len = (chunked_in_len < max_process) ? chunked_in_len : max_process; 707 } 708 unsigned char *old_out = out; 709 out = encode_base64(in, out, chunked_in_len); 710 line_length += out - old_out; 711 in += chunked_in_len; 712 in_len -= chunked_in_len; 713 } 714 leftover_cnt = in_len; 715 if (leftover_cnt) 716 { 717 memcpy(leftover.a, in, leftover_cnt); 718 } 719 720 if (out > out_end) 721 { 722 // we should never hit this, but if we do there is no recovery: we smashed past a buffer into the heap 723 abort(); 724 } 725 726 CFTypeRef ret = CFDataCreateWithBytesNoCopy(NULL, out_base, out - out_base, kCFAllocatorMalloc); 727 if (!d) 728 { 729 SecTransformCustomSetAttribute(ref,kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, ret); 730 CFReleaseNull(ret); 731 ret = NULL; 732 } 733 return ret; 734 }); 735 } 736 else if (kCFCompareEqualTo == CFStringCompare(value, kSecBase32Encoding, 0) || kCFCompareEqualTo == CFStringCompare(value, kSecBase32FDEEncoding, 0)) 737 { 738 __block struct { uint64_t a[2]; } accumulator = { .a = {0, 0} }; 739 __block short int bits_accumulated = 0; 740 static const short int in_chunk_size = 5; 741 static const short int out_chunk_size = 8; 742 char *base32alphabet = NULL; 743 744 if (kCFCompareEqualTo == CFStringCompare(value, kSecBase32Encoding, 0)) { 745 base32alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; 746 } else if (kCFCompareEqualTo == CFStringCompare(value, kSecBase32FDEEncoding, 0)) { 747 base32alphabet = "ABCDEFGH8JKLMNOPQR9TUVWXYZ234567"; 748 } 749 750 if (NULL == base32alphabet) { 751 // There is only one supported type, so we don't want to mention it in an error message 752 CFErrorRef bad_type = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Unknown base32 type '%@'", value); 753 754 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, bad_type); 755 756 return (CFTypeRef)bad_type; 757 } 758 759 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, 760 kSecEncodeLineLengthAttribute, 761 ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) 762 { 763 return new_line_length(out_chunk_size, value); 764 }); 765 766 SecTransformSetDataAction(ref, kSecTransformActionProcessData, 767 ^(CFTypeRef value) 768 { 769 CFDataRef d = value; 770 CFIndex in_len = d ? CFDataGetLength(d) : 0; 771 const unsigned char *in = d ? CFDataGetBytePtr(d) : NULL; 772 const unsigned char *in_end = in + in_len; 773 CFIndex n_chunks = in_len / in_chunk_size + 3; 774 CFIndex buf_len = n_chunks * out_chunk_size; 775 if (target_line_length) 776 { 777 buf_len += (n_chunks * out_chunk_size) / target_line_length; 778 } 779 __block unsigned char *out = malloc(buf_len); 780 unsigned char *out_end = out + buf_len, *out_base = out; 781 if (!out) { 782 return (CFTypeRef)GetNoMemoryError(); 783 } 784 785 void (^chunk)(void) = ^{ 786 // Grab the 5 bit (log(32)==5) values from the 80 bit accumulator. Most signifigant bits first 787 788 // (this could be done without the loop, which would save few cycles at the end of a stream) 789 short int shift = 80 - bits_accumulated; 790 for(; shift > 0; shift -= 8) { 791 accumulator.a[1] = accumulator.a[1] << 8 | accumulator.a[0] >> (64 - 8); 792 accumulator.a[0] = accumulator.a[0] << 8; 793 } 794 795 for(; bits_accumulated > 0; bits_accumulated -= 5) { 796 *out++ = base32alphabet[(accumulator.a[1] >> 11) & 0x1f]; 797 accumulator.a[1] = 0xffff & (accumulator.a[1] << 5 | (accumulator.a[0] >> (64 - 5))); 798 accumulator.a[0] = accumulator.a[0] << 5; 799 if (++line_length >= target_line_length && target_line_length) { 800 *out++ = '\n'; 801 line_length = 0; 802 } 803 } 804 bits_accumulated = 0; 805 }; 806 807 for (; in < in_end; in++) 808 { 809 accumulator.a[1] = accumulator.a[1] << 8 | accumulator.a[0] >> (64 - 8); 810 accumulator.a[0] = accumulator.a[0] << 8 | *in; 811 bits_accumulated += 8; 812 if (bits_accumulated == 8*in_chunk_size) 813 { 814 chunk(); 815 } 816 } 817 818 if (!d && bits_accumulated) { 819 short int padding = 0; 820 switch(bits_accumulated) { 821 case 8: 822 padding = 6; 823 break; 824 case 16: 825 padding = 4; 826 break; 827 case 24: 828 padding = 3; 829 break; 830 case 32: 831 padding = 1; 832 break; 833 } 834 chunk(); 835 int i; 836 for(i = 0; i < padding; i++) { 837 *out++ = '='; 838 } 839 } 840 841 if (out > out_end) { 842 // we should never hit this, but if we do there is no recovery: we smashed past a buffer into the heap 843 abort(); 844 } 845 846 CFTypeRef ret = NULL; 847 if (out - out_base) { 848 ret = CFDataCreateWithBytesNoCopy(NULL, out_base, out - out_base, kCFAllocatorMalloc); 849 } else { 850 ret = CFRetainSafe(SecTransformNoData()); 851 } 852 if (!d) { 853 if (ret != SecTransformNoData()) { 854 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 855 kSecTransformMetaAttributeValue, ret); 856 } 857 CFSafeRelease(ret); 858 ret = NULL; 859 } 860 return ret; 861 }); 862 } 863 else if (kCFCompareEqualTo == CFStringCompare(value, kSecZLibEncoding, 0)) 864 { 865 __block z_stream zs; 866 bzero(&zs, sizeof(zs)); 867 __block int clevel = Z_DEFAULT_COMPRESSION; 868 __block Boolean started = FALSE; 869 870 CFBooleanRef hasRatio = (CFBooleanRef)SecTranformCustomGetAttribute(ref, kSecCompressionRatio, 871 kSecTransformMetaAttributeHasOutboundConnections); 872 873 Boolean ratio_connected = (kCFBooleanTrue == hasRatio); 874 875 SecTransformSetDataAction(ref, kSecTransformActionProcessData, 876 ^(CFTypeRef value) 877 { 878 CFDataRef d = value; 879 880 if (!started) { 881 started = TRUE; 882 deflateInit(&zs, clevel); 883 } 884 885 if (d) { 886 zs.next_in = (UInt8 *)CFDataGetBytePtr(d); // We know that xLib will not 'Futz' with the data 887 zs.avail_in = (uInt)CFDataGetLength(d); 888 } else { 889 zs.next_in = NULL; 890 zs.avail_in = 0; 891 } 892 893 int rc = Z_BUF_ERROR; 894 895 CFIndex buf_sz = malloc_good_size(zs.avail_in ? zs.avail_in : 1024 * 4); 896 897 while ((d && zs.avail_in) || (d == NULL && rc != Z_STREAM_END)) { 898 unsigned char *buf = malloc(buf_sz); 899 if (!buf) { 900 return (CFTypeRef)GetNoMemoryError(); 901 } 902 903 zs.next_out = buf; 904 zs.avail_out = (uInt)buf_sz; 905 906 rc = deflate(&zs, d ? Z_NO_FLUSH : Z_FINISH); 907 908 CFIndex buf_used = buf_sz - zs.avail_out; 909 #ifdef DEBUG_ZLIB_MEMORY_USE 910 // It might be useful to look at these and tweak things like when we should use DataCreate vs. DataCreateWithBytesNoCopy 911 CFfprintf(stderr, "<<zavail_in %d buf_sz %d; d %p; ", zs.avail_in, buf_sz, d); 912 CFfprintf(stderr, "rc=%d %s", rc, (rc == Z_OK) ? "Z_OK" : (rc == Z_STREAM_END) ? "Z_FINISH" : (rc == Z_BUF_ERROR) ? "Z_BUF_ERROR" : "?"); 913 CFfprintf(stderr, " (output used %d, input left %d)\n", buf_used, zs.avail_in); 914 #endif 915 if (rc == Z_OK || rc == Z_STREAM_END) { 916 CFDataRef d; 917 if ((4 * buf_used) / buf_sz <= 1) { 918 // we would waste 25%+ of the buffer, make a smaller copy and release the original 919 d = CFDataCreate(NULL, buf, buf_used); 920 free(buf); 921 } else { 922 d = CFDataCreateWithBytesNoCopy(NULL, buf, buf_used, kCFAllocatorMalloc); 923 } 924 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 925 kSecTransformMetaAttributeValue, d); 926 CFReleaseNull(d); 927 } else if (rc == Z_BUF_ERROR) { 928 free(buf); 929 buf_sz = malloc_good_size(buf_sz * 2); 930 } else { 931 free(buf); 932 CFStringRef emsg = CFStringCreateWithFormat(NULL, NULL, CFSTR("Zlib error#%d"), rc); 933 CFErrorRef err = fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidInput, emsg); 934 CFReleaseNull(emsg); 935 return (CFTypeRef)err; 936 } 937 } 938 if (ratio_connected && zs.total_in && zs.total_out) { 939 float r = (float)zs.total_out / zs.total_in; 940 CFNumberRef ratio = CFNumberCreate(NULL, kCFNumberFloatType, &r); 941 SecTransformCustomSetAttribute(ref, kSecCompressionRatio, 942 kSecTransformMetaAttributeValue, ratio); 943 CFReleaseNull(ratio); 944 } 945 if (d) { 946 return (CFTypeRef)SecTransformNoData(); 947 } else { 948 deflateEnd(&zs); 949 started = FALSE; 950 return (CFTypeRef)NULL; 951 } 952 }); 953 954 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, 955 kSecEncodeLineLengthAttribute, ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) 956 { 957 return value; 958 }); 959 } 960 else 961 { 962 CFErrorRef bad_type = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Unsupported encode type '%@', supported types are kSecBase64Encoding, kSecBase32Encoding, and kSecGZipEncoding", value); 963 964 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, bad_type); 965 966 return (CFTypeRef)bad_type; 967 } 968 969 return (CFTypeRef)value; 970 }); 971 972 return result; 973 }; 974 975 return Block_copy(instanceBlock); 976 } 977 978 SecTransformRef SecEncodeTransformCreate(CFTypeRef EncodeType, CFErrorRef* error) 979 { 980 981 static dispatch_once_t once; 982 __block Boolean ok = TRUE; 983 CFErrorRef localError = NULL; 984 985 dispatch_block_t aBlock = ^ 986 { 987 ok = SecTransformRegister(EncodeName, &EncodeTransform, (CFErrorRef*)&localError); 988 }; 989 990 dispatch_once(&once, aBlock); 991 992 if (!ok || NULL != localError) 993 { 994 if (NULL != error) 995 { 996 *error = localError; 997 } 998 999 return NULL; 1000 } 1001 1002 SecTransformRef tr = SecTransformCreate(EncodeName, &localError); 1003 if (!tr || NULL != localError) 1004 { 1005 if (NULL != error) 1006 { 1007 *error = localError; 1008 } 1009 CFSafeRelease(tr); 1010 return NULL; 1011 } 1012 1013 SecTransformSetAttribute(tr, kSecEncodeTypeAttribute, EncodeType, &localError); 1014 if (NULL != localError) 1015 { 1016 CFReleaseNull(tr); 1017 tr = NULL; 1018 if (NULL != error) 1019 { 1020 *error = localError; 1021 } 1022 } 1023 1024 return tr; 1025 }