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