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