/ OSX / libsecurity_transform / lib / EncodeDecodeTransforms.c
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  }