2 * Copyright (c) 2010 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include "SecEncodeTransform.h"
25 #include "SecDecodeTransform.h"
26 #include "SecCustomTransform.h"
27 #include "CoreFoundation/CoreFoundation.h"
29 #include "Utilities.h"
31 #include <malloc/malloc.h>
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
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");
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");
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};
89 static char Base64Chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
90 "abcdefghijklmnopqrstuvwxyz"
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};
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};
144 /* --------------------------------------------------------------------------
145 function: DecodeTransform
146 description: This function returns a block that implements the
148 -------------------------------------------------------------------------- */
149 static SecTransformInstanceBlock
DecodeTransform(CFStringRef name
,
150 SecTransformRef newTransform
,
151 SecTransformImplementationRef ref
)
153 SecTransformInstanceBlock instanceBlock
=
155 CFErrorRef result
= NULL
;
156 SecTransformCustomSetAttribute(ref
, kSecDecodeTypeAttribute
,
157 kSecTransformMetaAttributeRequired
, kCFBooleanTrue
);
159 SecTransformSetAttributeAction(ref
,
160 kSecTransformActionAttributeNotification
,
161 kSecDecodeTypeAttribute
,
162 ^(SecTransformStringOrAttributeRef attribute
, CFTypeRef value
)
164 if (NULL
== value
|| CFGetTypeID(value
) != CFStringGetTypeID())
166 CFErrorRef errorResult
= fancy_error(kSecTransformErrorDomain
,
167 kSecTransformErrorInvalidInput
,
168 CFSTR("Decode type was not a CFStringRef"));
169 return (CFTypeRef
)errorResult
;
171 // value is a CFStringRef
172 if (kCFCompareEqualTo
== CFStringCompare(value
, kSecBase64Encoding
, 0))
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;
179 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
,
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;
188 unsigned char *out_base
= malloc(n_chunks
* out_chunk_size
);
190 return (CFTypeRef
) GetNoMemoryError();
192 unsigned char *out_end
= out_base
+ n_chunks
* out_chunk_size
;
193 unsigned char *out
= out_base
;
194 int chunk_i
= leftover_cnt
;
196 for(; enc
< enc_end
|| !enc
; chunk_i
++) {
203 if (ch
== ' ' || ch
== '\n' || ch
== '\r') {
210 leftover
.a
[chunk_i
] = b
;
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);
232 leftover_cnt
= (chunk_i
> 0) ? chunk_i
: 0;
234 // We really shouldn't get here, but if we do we just smashed something.
238 CFDataRef ret
= CFDataCreateWithBytesNoCopy(NULL
, out_base
, out
- out_base
, kCFAllocatorMalloc
);
240 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
,
241 kSecTransformMetaAttributeValue
, ret
);
245 return (CFTypeRef
)ret
;
248 else if (kCFCompareEqualTo
== CFStringCompare(value
, kSecBase32Encoding
, 0) || kCFCompareEqualTo
== CFStringCompare(value
, kSecBase32FDEEncoding
, 0))
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
;
257 if (kCFCompareEqualTo
== CFStringCompare(value
, kSecBase32Encoding
, 0)) {
258 base32values
= Base32Vals
;
259 } else if (kCFCompareEqualTo
== CFStringCompare(value
, kSecBase32FDEEncoding
, 0)) {
260 base32values
= Base32FDEVals
;
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
);
267 SecTransformCustomSetAttribute(ref
, kSecTransformAbortAttributeName
, kSecTransformMetaAttributeValue
, bad_type
);
269 return (CFTypeRef
)bad_type
;
272 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
,
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;
281 unsigned char *out_base
= malloc(n_chunks
* out_chunk_size
);
283 return (CFTypeRef
)GetNoMemoryError();
285 unsigned char *out_end
= out_base
+ n_chunks
* out_chunk_size
;
286 unsigned char *out
= out_base
;
288 for(; enc
< enc_end
|| !d
;) {
296 b
= base32values
[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;
307 if (bits_accumulated
== full_accumulator
|| ch
== '=') {
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;
313 for(; bits_accumulated
>= 8; bits_accumulated
-= 8) {
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;
319 bits_accumulated
= 0;
326 // We really shouldn't get here, but if we do we just smashed something.
330 CFDataRef ret
= CFDataCreateWithBytesNoCopy(NULL
, out_base
, out
- out_base
, kCFAllocatorMalloc
);
332 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
,
333 kSecTransformMetaAttributeValue
, ret
);
337 return (CFTypeRef
)ret
;
340 else if (kCFCompareEqualTo
== CFStringCompare(value
, kSecZLibEncoding
, 0))
343 __block Boolean started
= FALSE
;
345 CFBooleanRef hasRatio
= (CFBooleanRef
)SecTranformCustomGetAttribute(ref
,
346 kSecCompressionRatio
, kSecTransformMetaAttributeHasOutboundConnections
);
347 Boolean ratio_connected
= (kCFBooleanTrue
== hasRatio
);
349 bzero(&zs
, sizeof(zs
));
351 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
,
357 return (CFTypeRef
)NULL
;
364 zs
.next_in
= (UInt8
*)(CFDataGetBytePtr(d
)); // we know that zlib will not 'futz' with the data
365 zs
.avail_in
= (uInt
)CFDataGetLength(d
);
373 CFIndex buf_sz
= malloc_good_size(zs
.avail_in
? zs
.avail_in
: 1024 * 4);
375 while ((d
&& zs
.avail_in
) || (d
== NULL
&& rc
!= Z_STREAM_END
)) {
376 unsigned char *buf
= malloc(buf_sz
);
378 return (CFTypeRef
)GetNoMemoryError();
382 zs
.avail_out
= (uInt
)buf_sz
;
384 rc
= inflate(&zs
, d
? Z_NO_FLUSH
: Z_FINISH
);
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
);
393 if (rc
== Z_OK
|| rc
== Z_STREAM_END
) {
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
);
400 d
= CFDataCreateWithBytesNoCopy(NULL
, buf
, buf_used
, kCFAllocatorMalloc
);
402 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
,
403 kSecTransformMetaAttributeValue
, d
);
405 } else if (rc
== Z_BUF_ERROR
) {
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.
417 buf_sz
= malloc_good_size(buf_sz
* 2);
420 CFStringRef emsg
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Zlib error#%d"), rc
);
421 CFErrorRef err
= fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidInput
, emsg
);
423 return (CFTypeRef
)err
;
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
);
436 return (CFTypeRef
)SecTransformNoData();
437 } else if (rc
== Z_STREAM_END
) {
440 return (CFTypeRef
)NULL
;
442 CFStringRef emsg
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Zlib error#%d"), rc
);
443 CFErrorRef err
= fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidInput
, emsg
);
445 return (CFTypeRef
)err
;
450 CFErrorRef bad_type
= CreateSecTransformErrorRef(kSecTransformErrorInvalidInput
, "Unsupported decode type '%@', supported types are kSecBase64Encoding, kSecBase32Encoding, and kSecGZipEncoding", value
);
452 SecTransformCustomSetAttribute(ref
, kSecTransformAbortAttributeName
, kSecTransformMetaAttributeValue
, bad_type
);
454 return (CFTypeRef
)bad_type
;
462 return Block_copy(instanceBlock
);
466 SecTransformRef
SecDecodeTransformCreate(CFTypeRef DecodeType
, CFErrorRef
* error
) {
468 static dispatch_once_t once
;
469 __block Boolean ok
= TRUE
;
470 CFErrorRef localError
= NULL
;
472 dispatch_block_t aBlock
= ^
474 ok
= SecTransformRegister(DecodeName
, &DecodeTransform
, (CFErrorRef
*)&localError
);
477 dispatch_once(&once
, aBlock
);
479 if (!ok
|| NULL
!= localError
)
488 SecTransformRef tr
= SecTransformCreate(DecodeName
, &localError
);
489 if (!tr
|| NULL
!= localError
)
491 // There might be a leak if tr is returned but localError is
492 // not NULL, but that should not happen
500 SecTransformSetAttribute(tr
, kSecDecodeTypeAttribute
, DecodeType
, &localError
);
501 if (NULL
!= localError
)
515 unsigned 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) {
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)];
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)];
538 base64
[3] = base64
[2] = '=';
539 base64
[0] = Base64Chars
[((bin
[0] >> 2) & 0x3f)];
540 base64
[1] = Base64Chars
[((bin
[0] & 0x03) << 4)];
544 base64
[0] = base64
[1] = base64
[2] = base64
[3] = '=';
553 /* --------------------------------------------------------------------------
554 function: DecodeTransform
555 description: This function returns a block that implements the
557 -------------------------------------------------------------------------- */
558 static SecTransformInstanceBlock
EncodeTransform(CFStringRef name
,
559 SecTransformRef newTransform
,
560 SecTransformImplementationRef ref
)
563 SecTransformInstanceBlock instanceBlock
=
565 CFErrorRef result
= NULL
;
566 SecTransformCustomSetAttribute(ref
, kSecEncodeTypeAttribute
,
567 kSecTransformMetaAttributeRequired
, kCFBooleanTrue
);
569 __block
int line_length
= 0, target_line_length
= 0;
571 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
,
572 kSecEncodeLineLengthAttribute
,
573 ^(SecTransformStringOrAttributeRef attribute
, CFTypeRef value
)
575 SecTransformPushbackAttribute(ref
, attribute
, value
);
579 CFTypeRef (^new_line_length
)(int out_chunk_size
, CFTypeRef value
) = ^(int out_chunk_size
, CFTypeRef value
)
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
));
588 target_line_length
= requested_length
;
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
);
595 target_line_length
-= target_line_length
% out_chunk_size
;
597 if (target_line_length
< 0) {
598 target_line_length
= 0;
604 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
,
605 kSecEncodeTypeAttribute
,
606 ^(SecTransformStringOrAttributeRef attribute
, CFTypeRef value
)
608 if (NULL
== value
|| CFGetTypeID(value
) != CFStringGetTypeID())
610 CFErrorRef errorResult
= fancy_error(kSecTransformErrorDomain
,
611 kSecTransformErrorInvalidInput
,
612 CFSTR("Encode type was not a CFStringRef"));
613 return (CFTypeRef
)errorResult
;
616 if (kCFCompareEqualTo
== CFStringCompare(value
, kSecBase64Encoding
, 0))
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;
622 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
,
623 kSecEncodeLineLengthAttribute
,
624 ^(SecTransformStringOrAttributeRef attribute
, CFTypeRef value
)
626 return new_line_length(out_chunk_size
, value
);
629 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
,
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 if (target_line_length
)
639 buf_len
+= (n_chunks
* out_chunk_size
) / target_line_length
;
641 unsigned char *out
= malloc(buf_len
);
642 unsigned char *out_end
= out
+ buf_len
, *out_base
= out
;
645 return (CFTypeRef
)GetNoMemoryError();
649 CFIndex copy_len
= in_chunk_size
- leftover_cnt
;
650 copy_len
= (copy_len
> in_len
) ? in_len
: copy_len
;
651 memcpy(leftover
.a
+ leftover_cnt
, in
, copy_len
);
653 if (copy_len
+ leftover_cnt
== in_chunk_size
|| d
== NULL
)
655 out
= encode_base64(leftover
.a
, out
, copy_len
+ leftover_cnt
);
665 leftover_cnt
+= copy_len
;
666 return (CFTypeRef
)SecTransformNoData();
670 CFIndex chunked_in_len
;
671 while (in_len
>= in_chunk_size
)
673 chunked_in_len
= in_len
- (in_len
% in_chunk_size
);
674 if (target_line_length
)
676 if (target_line_length
<= line_length
+ out_chunk_size
)
681 int max_process
= (((target_line_length
- line_length
) / out_chunk_size
) * in_chunk_size
);
682 chunked_in_len
= (chunked_in_len
< max_process
) ? chunked_in_len
: max_process
;
684 unsigned char *old_out
= out
;
685 out
= encode_base64(in
, out
, chunked_in_len
);
686 line_length
+= out
- old_out
;
687 in
+= chunked_in_len
;
688 in_len
-= chunked_in_len
;
690 leftover_cnt
= in_len
;
693 memcpy(leftover
.a
, in
, leftover_cnt
);
698 // we should never hit this, but if we do there is no recovery: we smashed past a buffer into the heap
702 CFTypeRef ret
= CFDataCreateWithBytesNoCopy(NULL
, out_base
, out
- out_base
, kCFAllocatorMalloc
);
705 SecTransformCustomSetAttribute(ref
,kSecTransformOutputAttributeName
, kSecTransformMetaAttributeValue
, ret
);
712 else if (kCFCompareEqualTo
== CFStringCompare(value
, kSecBase32Encoding
, 0) || kCFCompareEqualTo
== CFStringCompare(value
, kSecBase32FDEEncoding
, 0))
714 __block
struct { uint64_t a
[2]; } accumulator
= { .a
= {0, 0} };
715 __block
short int bits_accumulated
= 0;
716 static const short int in_chunk_size
= 5;
717 static const short int out_chunk_size
= 8;
718 char *base32alphabet
= NULL
;
720 if (kCFCompareEqualTo
== CFStringCompare(value
, kSecBase32Encoding
, 0)) {
721 base32alphabet
= "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
722 } else if (kCFCompareEqualTo
== CFStringCompare(value
, kSecBase32FDEEncoding
, 0)) {
723 base32alphabet
= "ABCDEFGH8JKLMNOPQR9TUVWXYZ234567";
726 if (NULL
== base32alphabet
) {
727 // There is only one supported type, so we don't want to mention it in an error message
728 CFErrorRef bad_type
= CreateSecTransformErrorRef(kSecTransformErrorInvalidInput
, "Unknown base32 type '%@'", value
);
730 SecTransformCustomSetAttribute(ref
, kSecTransformAbortAttributeName
, kSecTransformMetaAttributeValue
, bad_type
);
732 return (CFTypeRef
)bad_type
;
735 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
,
736 kSecEncodeLineLengthAttribute
,
737 ^(SecTransformStringOrAttributeRef attribute
, CFTypeRef value
)
739 return new_line_length(out_chunk_size
, value
);
742 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
,
746 CFIndex in_len
= d
? CFDataGetLength(d
) : 0;
747 const unsigned char *in
= d
? CFDataGetBytePtr(d
) : NULL
;
748 const unsigned char *in_end
= in
+ in_len
;
749 CFIndex n_chunks
= in_len
/ in_chunk_size
+ 3;
750 CFIndex buf_len
= n_chunks
* out_chunk_size
;
751 if (target_line_length
)
753 buf_len
+= (n_chunks
* out_chunk_size
) / target_line_length
;
755 __block
unsigned char *out
= malloc(buf_len
);
756 unsigned char *out_end
= out
+ buf_len
, *out_base
= out
;
758 return (CFTypeRef
)GetNoMemoryError();
761 void (^chunk
)(void) = ^{
762 // Grab the 5 bit (log(32)==5) values from the 80 bit accumulator. Most signifigant bits first
764 // (this could be done without the loop, which would save few cycles at the end of a stream)
765 short int shift
= 80 - bits_accumulated
;
766 for(; shift
> 0; shift
-= 8) {
767 accumulator
.a
[1] = accumulator
.a
[1] << 8 | accumulator
.a
[0] >> (64 - 8);
768 accumulator
.a
[0] = accumulator
.a
[0] << 8;
771 for(; bits_accumulated
> 0; bits_accumulated
-= 5) {
772 *out
++ = base32alphabet
[(accumulator
.a
[1] >> 11) & 0x1f];
773 accumulator
.a
[1] = 0xffff & (accumulator
.a
[1] << 5 | (accumulator
.a
[0] >> (64 - 5)));
774 accumulator
.a
[0] = accumulator
.a
[0] << 5;
775 if (++line_length
>= target_line_length
&& target_line_length
) {
780 bits_accumulated
= 0;
783 for (; in
< in_end
; in
++)
785 accumulator
.a
[1] = accumulator
.a
[1] << 8 | accumulator
.a
[0] >> (64 - 8);
786 accumulator
.a
[0] = accumulator
.a
[0] << 8 | *in
;
787 bits_accumulated
+= 8;
788 if (bits_accumulated
== 8*in_chunk_size
)
794 if (!d
&& bits_accumulated
) {
795 short int padding
= 0;
796 switch(bits_accumulated
) {
812 for(i
= 0; i
< padding
; i
++) {
818 // we should never hit this, but if we do there is no recovery: we smashed past a buffer into the heap
822 CFTypeRef ret
= NULL
;
823 if (out
- out_base
) {
824 ret
= CFDataCreateWithBytesNoCopy(NULL
, out_base
, out
- out_base
, kCFAllocatorMalloc
);
826 ret
= SecTransformNoData();
829 if (ret
!= SecTransformNoData()) {
830 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
,
831 kSecTransformMetaAttributeValue
, ret
);
839 else if (kCFCompareEqualTo
== CFStringCompare(value
, kSecZLibEncoding
, 0))
842 bzero(&zs
, sizeof(zs
));
843 __block
int clevel
= Z_DEFAULT_COMPRESSION
;
844 __block Boolean started
= FALSE
;
846 CFBooleanRef hasRatio
= (CFBooleanRef
)SecTranformCustomGetAttribute(ref
, kSecCompressionRatio
,
847 kSecTransformMetaAttributeHasOutboundConnections
);
849 Boolean ratio_connected
= (kCFBooleanTrue
== hasRatio
);
851 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
,
858 deflateInit(&zs
, clevel
);
862 zs
.next_in
= (UInt8
*)CFDataGetBytePtr(d
); // We know that xLib will not 'Futz' with the data
863 zs
.avail_in
= (uInt
)CFDataGetLength(d
);
869 int rc
= Z_BUF_ERROR
;
871 CFIndex buf_sz
= malloc_good_size(zs
.avail_in
? zs
.avail_in
: 1024 * 4);
873 while ((d
&& zs
.avail_in
) || (d
== NULL
&& rc
!= Z_STREAM_END
)) {
874 unsigned char *buf
= malloc(buf_sz
);
876 return (CFTypeRef
)GetNoMemoryError();
880 zs
.avail_out
= (uInt
)buf_sz
;
882 rc
= deflate(&zs
, d
? Z_NO_FLUSH
: Z_FINISH
);
884 CFIndex buf_used
= buf_sz
- zs
.avail_out
;
885 #ifdef DEBUG_ZLIB_MEMORY_USE
886 // It might be useful to look at these and tweak things like when we should use DataCreate vs. DataCreateWithBytesNoCopy
887 CFfprintf(stderr
, "<<zavail_in %d buf_sz %d; d %p; ", zs
.avail_in
, buf_sz
, d
);
888 CFfprintf(stderr
, "rc=%d %s", rc
, (rc
== Z_OK
) ? "Z_OK" : (rc
== Z_STREAM_END
) ? "Z_FINISH" : (rc
== Z_BUF_ERROR
) ? "Z_BUF_ERROR" : "?");
889 CFfprintf(stderr
, " (output used %d, input left %d)\n", buf_used
, zs
.avail_in
);
891 if (rc
== Z_OK
|| rc
== Z_STREAM_END
) {
893 if ((4 * buf_used
) / buf_sz
<= 1) {
894 // we would waste 25%+ of the buffer, make a smaller copy and release the original
895 d
= CFDataCreate(NULL
, buf
, buf_used
);
898 d
= CFDataCreateWithBytesNoCopy(NULL
, buf
, buf_used
, kCFAllocatorMalloc
);
900 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
,
901 kSecTransformMetaAttributeValue
, d
);
903 } else if (rc
== Z_BUF_ERROR
) {
905 buf_sz
= malloc_good_size(buf_sz
* 2);
908 CFStringRef emsg
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Zlib error#%d"), rc
);
909 CFErrorRef err
= fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidInput
, emsg
);
911 return (CFTypeRef
)err
;
914 if (ratio_connected
&& zs
.total_in
&& zs
.total_out
) {
915 float r
= (float)zs
.total_out
/ zs
.total_in
;
916 CFNumberRef ratio
= CFNumberCreate(NULL
, kCFNumberFloatType
, &r
);
917 SecTransformCustomSetAttribute(ref
, kSecCompressionRatio
,
918 kSecTransformMetaAttributeValue
, ratio
);
922 return (CFTypeRef
)SecTransformNoData();
926 return (CFTypeRef
)NULL
;
930 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
,
931 kSecEncodeLineLengthAttribute
, ^(SecTransformStringOrAttributeRef attribute
, CFTypeRef value
)
938 CFErrorRef bad_type
= CreateSecTransformErrorRef(kSecTransformErrorInvalidInput
, "Unsupported encode type '%@', supported types are kSecBase64Encoding, kSecBase32Encoding, and kSecGZipEncoding", value
);
940 SecTransformCustomSetAttribute(ref
, kSecTransformAbortAttributeName
, kSecTransformMetaAttributeValue
, bad_type
);
942 return (CFTypeRef
)bad_type
;
945 return (CFTypeRef
)value
;
951 return Block_copy(instanceBlock
);
954 SecTransformRef
SecEncodeTransformCreate(CFTypeRef EncodeType
, CFErrorRef
* error
)
957 static dispatch_once_t once
;
958 __block Boolean ok
= TRUE
;
959 CFErrorRef localError
= NULL
;
961 dispatch_block_t aBlock
= ^
963 ok
= SecTransformRegister(EncodeName
, &EncodeTransform
, (CFErrorRef
*)&localError
);
966 dispatch_once(&once
, aBlock
);
968 if (!ok
|| NULL
!= localError
)
978 SecTransformRef tr
= SecTransformCreate(EncodeName
, &localError
);
979 if (!tr
|| NULL
!= localError
)
981 // There might be a leak if tr is returned but localError is
982 // not NULL, but that should not happen
990 SecTransformSetAttribute(tr
, kSecEncodeTypeAttribute
, EncodeType
, &localError
);
991 if (NULL
!= localError
)