]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_cdsa_utils/lib/cuEnc64.c
Security-59306.101.1.tar.gz
[apple/security.git] / OSX / libsecurity_cdsa_utils / lib / cuEnc64.c
1 /*
2 * Copyright (c) 1998-2003,2011,2014 Apple Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please
7 * obtain a copy of the License at http://www.apple.com/publicsource and
8 * read it before using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
12 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
13 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
15 * Please see the License for the specific language governing rights and
16 * limitations under the License.
17 *
18 * cuEnc64.c - encode/decode in 64-char IA5 format, per RFC 1421
19 */
20
21 #include "cuEnc64.h"
22 #include <stdlib.h>
23
24 #ifndef NULL
25 #define NULL ((void *)0)
26 #endif /* NULL */
27
28 /*
29 * map a 6-bit binary value to a printable character.
30 */
31 static const
32 unsigned char bintoasc[] =
33 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
34
35 /*
36 * Map an 7-bit printable character to its corresponding binary value.
37 * Any illegal characters return high bit set.
38 */
39 static const
40 unsigned char asctobin[] =
41 {
42 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
43 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
44 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
45 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
46 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
47 0x80, 0x80, 0x80, 0x3e, 0x80, 0x80, 0x80, 0x3f,
48 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
49 0x3c, 0x3d, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
50 0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
51 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
52 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
53 0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80,
54 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
55 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
56 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
57 0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80
58 };
59
60 /*
61 * map 6 bits to a printing char
62 */
63 #define ENC(c) (bintoasc[((c) & 0x3f)])
64
65 #define PAD '='
66
67 /*
68 * map one group of up to 3 bytes at inp to 4 bytes at outp.
69 * Count is number of valid bytes in *inp; if less than 3, the
70 * 1 or two extras must be zeros.
71 */
72 static void encChunk(const unsigned char *inp,
73 unsigned char *outp,
74 int count)
75 {
76 unsigned char c1, c2, c3, c4;
77
78 c1 = *inp >> 2;
79 c2 = ((inp[0] << 4) & 0x30) | ((inp[1] >> 4) & 0xf);
80 c3 = ((inp[1] << 2) & 0x3c) | ((inp[2] >> 6) & 0x3);
81 c4 = inp[2] & 0x3f;
82 *outp++ = ENC(c1);
83 *outp++ = ENC(c2);
84 if (count == 1) {
85 *outp++ = PAD;
86 *outp = PAD;
87 } else {
88 *outp++ = ENC(c3);
89 if (count == 2) {
90 *outp = PAD;
91 }
92 else {
93 *outp = ENC(c4);
94 }
95 }
96 }
97
98 /*
99 * Given input buffer inbuf, length inlen, encode to 64-char IA5 format.
100 * Result is fmalloc'd and returned; it is terminated by Microsoft-style
101 * newline and NULL. Its length (including the trailing newline and NULL)
102 * is returned in *outlen.
103 */
104
105 unsigned char *cuEnc64(const unsigned char *inbuf,
106 unsigned inlen,
107 unsigned *outlen) // RETURNED
108 {
109 return cuEnc64WithLines(inbuf, inlen, 0, outlen);
110 }
111
112 unsigned char *cuEnc64WithLines(const unsigned char *inbuf,
113 unsigned inlen,
114 unsigned linelen,
115 unsigned *outlen)
116 {
117 unsigned outTextLen;
118 unsigned len; // to malloc, liberal
119 unsigned olen = 0; // actual output size
120 unsigned char *outbuf;
121 unsigned char endbuf[3];
122 unsigned i;
123 unsigned char *outp;
124 unsigned numLines;
125 unsigned thisLine;
126
127 outTextLen = ((inlen + 2) / 3) * 4;
128 if(linelen) {
129 /*
130 * linelen must be 0 mod 4 for this to work; round up...
131 */
132 if((linelen & 0x03) != 0) {
133 linelen = (linelen + 3) & 0xfffffffc;
134 }
135 numLines = (outTextLen + linelen - 1)/ linelen;
136 }
137 else {
138 numLines = 1;
139 }
140
141 /*
142 * Total output size = encoded text size plus one newline per
143 * line of output, plus trailing NULL. We always generate newlines
144 * as \n; when decoding, we tolerate \r\n (Microsoft) or \n.
145 */
146 len = outTextLen + (2 * numLines) + 1;
147 outbuf = (unsigned char*)malloc(len);
148 outp = outbuf;
149 thisLine = 0;
150
151 while(inlen) {
152 if(inlen < 3) {
153 for(i=0; i<3; i++) {
154 if(i < inlen) {
155 endbuf[i] = inbuf[i];
156 }
157 else {
158 endbuf[i] = 0;
159 }
160 }
161 encChunk(endbuf, outp, inlen);
162 inlen = 0;
163 }
164 else {
165 encChunk(inbuf, outp, 3);
166 inlen -= 3;
167 inbuf += 3;
168 }
169 outp += 4;
170 thisLine += 4;
171 olen += 4;
172 if((linelen != 0) && (thisLine >= linelen) && inlen) {
173 /*
174 * last trailing newline added below
175 * Note we don't split 4-byte output chunks over newlines
176 */
177 *outp++ = '\n';
178 olen++;
179 thisLine = 0;
180 }
181 }
182 *outp++ = '\n';
183 *outp = '\0';
184 olen += 2;
185 *outlen = olen;
186 return outbuf;
187 }
188
189 static inline int isWhite(unsigned char c)
190 {
191 switch(c) {
192 case '\n':
193 case '\r':
194 case ' ':
195 case '\t':
196 case '\0':
197 return 1;
198 default:
199 return 0;
200 }
201 }
202
203 /*
204 * Strip off all whitespace from a (supposedly) enc64-format string.
205 * Returns a malloc'd string.
206 */
207 static unsigned char *stringCleanse(const unsigned char *inbuf,
208 unsigned inlen,
209 unsigned *outlen)
210 {
211 unsigned char *news; // cleansed inbuf
212 unsigned newsDex; // index into news
213 unsigned i;
214
215 news = (unsigned char*)malloc(inlen);
216 newsDex = 0;
217 for(i=0; i<inlen; i++) {
218 if(!isWhite(inbuf[i])) {
219 news[newsDex++] = inbuf[i];
220 }
221 }
222 *outlen = newsDex;
223 return news;
224 }
225
226 /*
227 * Given input buffer inbuf, length inlen, decode from 64-char IA5 format to
228 * binary. Result is malloced and returned; its length is returned in *outlen.
229 * NULL return indicates corrupted input.
230 *
231 * All whitespace in input is ignored.
232 */
233 unsigned char *cuDec64(const unsigned char *inbuf,
234 unsigned inlen,
235 unsigned *outlen)
236 {
237 unsigned char *outbuf;
238 unsigned char *outp; // malloc'd outbuf size
239 unsigned obuflen;
240 const unsigned char *bp;
241 unsigned olen = 0; // actual output size
242 unsigned char c1, c2, c3, c4;
243 unsigned char j;
244 unsigned thisOlen;
245 unsigned char *news; // cleansed inbuf
246 unsigned newsLen;
247
248 /*
249 * Strip out all whitespace; remainder must be multiple of four
250 * characters
251 */
252 news = stringCleanse(inbuf, inlen, &newsLen);
253 if((newsLen & 0x03) != 0) {
254 free(news);
255 return (unsigned char*) NULL;
256 }
257 inlen = newsLen;
258 bp = news;
259
260 obuflen = (inlen / 4) * 3;
261 if(obuflen == 0) {
262 free(news);
263 return NULL;
264 }
265 outbuf = (unsigned char*)malloc(obuflen);
266 outp = outbuf;
267
268 while (inlen) {
269 /*
270 * Note inlen is always a multiple of four here
271 */
272 if (*bp & 0x80 || (c1 = asctobin[*bp]) & 0x80) {
273 goto errorOut;
274 }
275 inlen--;
276 bp++;
277 if (*bp & 0x80 || (c2 = asctobin[*bp]) & 0x80){
278 goto errorOut;
279 }
280 inlen--;
281 bp++;
282 if (*bp == PAD) {
283 /*
284 * two input bytes, one output byte
285 */
286 c3 = c4 = 0;
287 thisOlen = 1;
288 if (c2 & 0xf) {
289 goto errorOut;
290 }
291 bp++;
292 inlen--;
293 if (*bp == PAD) {
294 bp++;
295 inlen--;
296 if(inlen > 0) {
297 goto errorOut;
298 }
299 }
300 else {
301 goto errorOut;
302 }
303 } else if (*bp & 0x80 || (c3 = asctobin[*bp]) & 0x80) {
304 goto errorOut;
305 } else {
306 bp++;
307 inlen--;
308 if (*bp == PAD) {
309 /*
310 * Three input bytes, two output
311 */
312 c4 = 0;
313 thisOlen = 2;
314 if (c3 & 3) {
315 goto errorOut;
316 }
317 } else if (*bp & 0x80 || (c4 = asctobin[*bp]) & 0x80) {
318 goto errorOut;
319 } else {
320 /*
321 * Normal non-pad case
322 */
323 thisOlen = 3;
324 }
325 bp++;
326 inlen--;
327 }
328 j = (c1 << 2) | (c2 >> 4);
329 *outp++ = j;
330 if(thisOlen > 1) {
331 j = (c2 << 4) | (c3 >> 2);
332 *outp++ = j;
333 if(thisOlen == 3) {
334 j = (c3 << 6) | c4;
335 *outp++ = j;
336 }
337 }
338 olen += thisOlen;
339 }
340 free(news);
341 *outlen = olen;
342 return outbuf; /* normal return */
343
344 errorOut:
345 free(news);
346 free(outbuf);
347 return (unsigned char*) NULL;
348 }
349
350 /*
351 * Determine if specified input data is valid enc64 format. Returns 1
352 * if valid, 0 if not.
353 * This doesn't do a full enc64 parse job; it scans for legal characters
354 * and proper sync when a possible pad is found.
355 */
356 int cuIsValidEnc64(const unsigned char *inbuf,
357 unsigned inlen)
358 {
359 int padChars = 0; // running count of PAD chars
360 int validEncChars = 0;
361 unsigned char c;
362
363 /*
364 * -- scan inbuf
365 * -- skip whitespace
366 * -- count valid chars
367 * -- ensure not more than 2 PAD chars, only at end
368 * -- ensure valid chars mod 4 == 0
369 */
370
371 while(inlen) {
372 c = *inbuf++;
373 inlen--;
374 if(isWhite(c)) {
375 continue;
376 }
377 if(c == PAD) {
378 if(++padChars > 2) {
379 return 0; // max of 2 PAD chars at end
380 }
381 }
382 else if(padChars > 0) {
383 return 0; // no normal chars after seeing PAD
384 }
385 else if((c & 0x80) || ((asctobin[c]) & 0x80)) {
386 return 0; // invalid encoded char
387 }
388 validEncChars++;
389 }
390 if((validEncChars & 0x03) != 0) {
391 return 0;
392 }
393 else {
394 return 1;
395 }
396 }