]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_x509_tp/lib/cuEnc64.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_apple_x509_tp / lib / cuEnc64.c
1 /*
2 * Copyright (c) 1998-2003,2010-2012 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 #include <string.h>
24 #include <stddef.h>
25
26 #ifndef NULL
27 #define NULL ((void *)0)
28 #endif /* NULL */
29
30 /*
31 * map a 6-bit binary value to a printable character.
32 */
33 static const
34 unsigned char bintoasc[] =
35 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
36
37 /*
38 * Map an 7-bit printable character to its corresponding binary value.
39 * Any illegal characters return high bit set.
40 */
41 static const
42 unsigned char asctobin[] =
43 {
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, 0x80, 0x80, 0x80, 0x80, 0x80,
48 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
49 0x80, 0x80, 0x80, 0x3e, 0x80, 0x80, 0x80, 0x3f,
50 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
51 0x3c, 0x3d, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
52 0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
53 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
54 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
55 0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80,
56 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
57 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
58 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
59 0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80
60 };
61
62 /*
63 * map 6 bits to a printing char
64 */
65 #define ENC(c) (bintoasc[((c) & 0x3f)])
66
67 #define PAD '='
68
69 /*
70 * map one group of up to 3 bytes at inp to 4 bytes at outp.
71 * Count is number of valid bytes in *inp; if less than 3, the
72 * 1 or two extras must be zeros.
73 */
74 static void encChunk(const unsigned char *inp,
75 unsigned char *outp,
76 int count)
77 {
78 unsigned char c1, c2, c3, c4;
79
80 c1 = *inp >> 2;
81 c2 = ((inp[0] << 4) & 0x30) | ((inp[1] >> 4) & 0xf);
82 c3 = ((inp[1] << 2) & 0x3c) | ((inp[2] >> 6) & 0x3);
83 c4 = inp[2] & 0x3f;
84 *outp++ = ENC(c1);
85 *outp++ = ENC(c2);
86 if (count == 1) {
87 *outp++ = PAD;
88 *outp = PAD;
89 } else {
90 *outp++ = ENC(c3);
91 if (count == 2) {
92 *outp = PAD;
93 }
94 else {
95 *outp = ENC(c4);
96 }
97 }
98 }
99
100 /*
101 * Given input buffer inbuf, length inlen, encode to 64-char IA5 format.
102 * Result is fmalloc'd and returned; it is terminated by Microsoft-style
103 * newline and NULL. Its length (including the trailing newline and NULL)
104 * is returned in *outlen.
105 */
106
107 unsigned char *cuEnc64(const unsigned char *inbuf,
108 unsigned inlen,
109 unsigned *outlen) // RETURNED
110 {
111 return cuEnc64WithLines(inbuf, inlen, 0, outlen);
112 }
113
114 unsigned char *cuEnc64WithLines(const unsigned char *inbuf,
115 unsigned inlen,
116 unsigned linelen,
117 unsigned *outlen)
118 {
119 unsigned outTextLen;
120 unsigned len; // to malloc, liberal
121 unsigned olen = 0; // actual output size
122 unsigned char *outbuf;
123 unsigned char endbuf[3];
124 unsigned i;
125 unsigned char *outp;
126 unsigned numLines;
127 unsigned thisLine;
128
129 outTextLen = ((inlen + 2) / 3) * 4;
130 if(linelen) {
131 /*
132 * linelen must be 0 mod 4 for this to work; round up...
133 */
134 if((linelen & 0x03) != 0) {
135 linelen = (linelen + 3) & 0xfffffffc;
136 }
137 numLines = (outTextLen + linelen - 1)/ linelen;
138 }
139 else {
140 numLines = 1;
141 }
142
143 /*
144 * Total output size = encoded text size plus one newline per
145 * line of output, plus trailing NULL. We always generate newlines
146 * as \n; when decoding, we tolerate \r\n (Microsoft) or \n.
147 */
148 len = outTextLen + (2 * numLines) + 1;
149 outbuf = (unsigned char*)malloc(len);
150 outp = outbuf;
151 thisLine = 0;
152
153 while(inlen) {
154 if(inlen < 3) {
155 for(i=0; i<3; i++) {
156 if(i < inlen) {
157 endbuf[i] = inbuf[i];
158 }
159 else {
160 endbuf[i] = 0;
161 }
162 }
163 encChunk(endbuf, outp, inlen);
164 inlen = 0;
165 }
166 else {
167 encChunk(inbuf, outp, 3);
168 inlen -= 3;
169 inbuf += 3;
170 }
171 outp += 4;
172 thisLine += 4;
173 olen += 4;
174 if((linelen != 0) && (thisLine >= linelen) && inlen) {
175 /*
176 * last trailing newline added below
177 * Note we don't split 4-byte output chunks over newlines
178 */
179 *outp++ = '\n';
180 olen++;
181 thisLine = 0;
182 }
183 }
184 *outp++ = '\n';
185 *outp = '\0';
186 olen += 2;
187 *outlen = olen;
188 return outbuf;
189 }
190
191 static inline int isWhite(unsigned char c)
192 {
193 switch(c) {
194 case '\n':
195 case '\r':
196 case ' ':
197 case '\t':
198 case '\0':
199 return 1;
200 default:
201 return 0;
202 }
203 }
204
205 /*
206 * Strip off all whitespace from a (supposedly) enc64-format string.
207 * Returns a malloc'd string.
208 */
209 static unsigned char *stringCleanse(const unsigned char *inbuf,
210 unsigned inlen,
211 unsigned *outlen)
212 {
213 unsigned char *news; // cleansed inbuf
214 unsigned newsDex; // index into news
215 unsigned i;
216
217 news = (unsigned char*)malloc(inlen);
218 newsDex = 0;
219 for(i=0; i<inlen; i++) {
220 if(!isWhite(inbuf[i])) {
221 news[newsDex++] = inbuf[i];
222 }
223 }
224 *outlen = newsDex;
225 return news;
226 }
227
228 /*
229 * Given input buffer inbuf, length inlen, decode from 64-char IA5 format to
230 * binary. Result is malloced and returned; its length is returned in *outlen.
231 * NULL return indicates corrupted input.
232 *
233 * All whitespace in input is ignored.
234 */
235 unsigned char *cuDec64(const unsigned char *inbuf,
236 unsigned inlen,
237 unsigned *outlen)
238 {
239 unsigned char *outbuf;
240 unsigned char *outp; // malloc'd outbuf size
241 unsigned obuflen;
242 const unsigned char *bp;
243 unsigned olen = 0; // actual output size
244 unsigned char c1, c2, c3, c4;
245 unsigned char j;
246 unsigned thisOlen;
247 unsigned char *news; // cleansed inbuf
248 unsigned newsLen;
249
250 /*
251 * Strip out all whitespace; remainder must be multiple of four
252 * characters
253 */
254 news = stringCleanse(inbuf, inlen, &newsLen);
255 if((newsLen & 0x03) != 0) {
256 free(news);
257 return (unsigned char*) NULL;
258 }
259 inlen = newsLen;
260 bp = news;
261
262 obuflen = (inlen / 4) * 3;
263 outbuf = (unsigned char*)malloc(obuflen);
264 outp = outbuf;
265
266 while (inlen) {
267 /*
268 * Note inlen is always a multiple of four here
269 */
270 if (*bp & 0x80 || (c1 = asctobin[*bp]) & 0x80) {
271 goto errorOut;
272 }
273 inlen--;
274 bp++;
275 if (*bp & 0x80 || (c2 = asctobin[*bp]) & 0x80){
276 goto errorOut;
277 }
278 inlen--;
279 bp++;
280 if (*bp == PAD) {
281 /*
282 * two input bytes, one output byte
283 */
284 c3 = c4 = 0;
285 thisOlen = 1;
286 if (c2 & 0xf) {
287 goto errorOut;
288 }
289 bp++;
290 inlen--;
291 if (*bp == PAD) {
292 bp++;
293 inlen--;
294 if(inlen > 0) {
295 goto errorOut;
296 }
297 }
298 else {
299 goto errorOut;
300 }
301 } else if (*bp & 0x80 || (c3 = asctobin[*bp]) & 0x80) {
302 goto errorOut;
303 } else {
304 bp++;
305 inlen--;
306 if (*bp == PAD) {
307 /*
308 * Three input bytes, two output
309 */
310 c4 = 0;
311 thisOlen = 2;
312 if (c3 & 3) {
313 goto errorOut;
314 }
315 } else if (*bp & 0x80 || (c4 = asctobin[*bp]) & 0x80) {
316 goto errorOut;
317 } else {
318 /*
319 * Normal non-pad case
320 */
321 thisOlen = 3;
322 }
323 bp++;
324 inlen--;
325 }
326 j = (c1 << 2) | (c2 >> 4);
327 *outp++ = j;
328 if(thisOlen > 1) {
329 j = (c2 << 4) | (c3 >> 2);
330 *outp++ = j;
331 if(thisOlen == 3) {
332 j = (c3 << 6) | c4;
333 *outp++ = j;
334 }
335 }
336 olen += thisOlen;
337 }
338 free(news);
339 *outlen = olen;
340 return outbuf; /* normal return */
341
342 errorOut:
343 free(news);
344 free(outbuf);
345 return (unsigned char*) NULL;
346 }
347
348 /*
349 * Determine if specified input data is valid enc64 format. Returns 1
350 * if valid, 0 if not.
351 * This doesn't do a full enc64 parse job; it scans for legal characters
352 * and proper sync when a possible pad is found.
353 */
354 int cuIsValidEnc64(const unsigned char *inbuf,
355 unsigned inlen)
356 {
357 int padChars = 0; // running count of PAD chars
358 int validEncChars = 0;
359 unsigned char c;
360
361 /*
362 * -- scan inbuf
363 * -- skip whitespace
364 * -- count valid chars
365 * -- ensure not more than 2 PAD chars, only at end
366 * -- ensure valid chars mod 4 == 0
367 */
368
369 while(inlen) {
370 c = *inbuf++;
371 inlen--;
372 if(isWhite(c)) {
373 continue;
374 }
375 if(c == PAD) {
376 if(++padChars > 2) {
377 return 0; // max of 2 PAD chars at end
378 }
379 }
380 else if(padChars > 0) {
381 return 0; // no normal chars after seeing PAD
382 }
383 else if((c & 0x80) || ((asctobin[c]) & 0x80)) {
384 return 0; // invalid encoded char
385 }
386 validEncChars++;
387 }
388 if((validEncChars & 0x03) != 0) {
389 return 0;
390 }
391 else {
392 return 1;
393 }
394 }
395
396 /*
397 * Text parsing routines.
398 *
399 * Search incoming text for specified string. Does not assume inText is
400 * NULL terminated. Returns pointer to start of found string in inText.
401 */
402 static const char *findStr(
403 const char *inText,
404 unsigned inTextLen,
405 const char *str) // NULL terminated - search for this
406 {
407 /* probably not the hottest string search algorithm... */
408 const char *cp;
409 size_t srchStrLen = strlen(str);
410 char c = str[0];
411
412 /* last char * we can search in inText for start of str */
413 const char *endCp = inText + inTextLen - srchStrLen;
414
415 for(cp=inText; cp<=endCp; cp++) {
416 if(*cp == c) {
417 if(!memcmp(cp, str, srchStrLen)) {
418 return cp;
419 }
420 }
421 }
422 return NULL;
423 }
424
425 /*
426 * Obtain one line from current text. Returns a mallocd, NULL-terminated string
427 * which caller must free(). Also returns the number of chars consumed including
428 * the returned chars PLUS EOL terminators (\n and/or \r).
429 *
430 * ALWAYS returns a mallocd string if there is ANY data remaining per the
431 * incoming inTextLen. Returns NULL if inTextLen is zero.
432 */
433 static const char *getLine(
434 const char *inText,
435 unsigned inTextLen, // RETURNED
436 unsigned *consumed) // RETURNED
437
438 {
439 *consumed = 0;
440 const char *cp = inText;
441 const char *newline = NULL; // if we found a newline, this points to the first one
442
443 while(inTextLen) {
444 char c = *cp;
445 if((c == '\r') || (c == '\n')) {
446 if(newline == NULL) {
447 /* first newline */
448 newline = cp;
449 }
450 }
451 else if(newline != NULL) {
452 /* non newline after newline, done */
453 break;
454 }
455 (*consumed)++;
456 inTextLen--;
457 cp++;
458 }
459 ptrdiff_t linelen;
460 if(newline) {
461 linelen = newline - inText;
462 }
463 else {
464 linelen = *consumed;
465 }
466 char *rtn = (char *)malloc(linelen + 1);
467 memmove(rtn, inText, linelen);
468 rtn[linelen] = 0;
469 return rtn;
470 }
471
472 #define UNSUPPORTED_FORMAT_ERR -25256
473
474 /*
475 * Given input buffer containing a PEM-encoded certificate, convert to DER
476 * and return in outbuf. Result is malloced and must be freed by caller;
477 * its length is returned in *outlen. Returns 0 on success.
478 */
479 int cuConvertPem(
480 const unsigned char *inbuf,
481 unsigned inlen,
482 unsigned char **outbuf, // RETURNED (caller must free)
483 unsigned *outlen) // RETURNED
484 {
485 unsigned lenToGo = (inlen) ? inlen : 0;
486 const char *currCp = (inbuf) ? (const char *)inbuf : NULL;
487 const char *currLine = NULL; // mallocd by getLine()
488 unsigned consumed;
489 int ortn = 0;
490 const char *start64;
491 unsigned base64Len;
492 const char *end64;
493 unsigned char *decData;
494 unsigned decDataLen;
495
496 /* search to START line, parse it to get type/format/alg */
497 const char *startLine = findStr(currCp, lenToGo, "-----BEGIN");
498 if(startLine != NULL) {
499 /* possibly skip over leading garbage */
500 consumed = (unsigned)(startLine - currCp);
501 lenToGo -= consumed;
502 currCp = startLine;
503
504 /* get C string of START line */
505 currLine = getLine(startLine, lenToGo, &consumed);
506 if(currLine == NULL) {
507 /* somehow got here with no data */
508 // assert(lenToGo == 0);
509 ortn = UNSUPPORTED_FORMAT_ERR;
510 goto errOut;
511 }
512 // assert(consumed <= lenToGo);
513 currCp += consumed;
514 lenToGo -= consumed;
515
516 free((void *)currLine);
517 }
518
519 /* Skip empty lines. */
520 for( ; ; ) {
521 currLine = getLine(currCp, lenToGo, &consumed);
522 if(currLine == NULL) {
523 /* out of data */
524 ortn = UNSUPPORTED_FORMAT_ERR;
525 goto errOut;
526 }
527 int skipThis = 0;
528 size_t lineLen = strlen(currLine);
529 if(lineLen == 0) {
530 /* empty line */
531 skipThis = 1;
532 }
533 free((void *)currLine);
534
535 if(!skipThis) {
536 /* looks like good stuff; process */
537 break;
538 }
539 /* skip this line */
540 // assert(consumed <= lenToGo);
541 currCp += consumed;
542 lenToGo -= consumed;
543 }
544 if(lenToGo == 0) {
545 /* no valid base64 data */
546 ortn = UNSUPPORTED_FORMAT_ERR;
547 goto errOut;
548 }
549
550 /*
551 * currCP points to start of base64 data - mark it and search for end line.
552 * We skip everything after the end line.
553 */
554 start64 = currCp;
555 base64Len = lenToGo; // if no END
556 end64 = findStr(currCp, lenToGo, "-----END");
557 if(end64 != NULL) {
558 if(end64 == start64) {
559 /* Empty, nothing between START and END */
560 ortn = UNSUPPORTED_FORMAT_ERR;
561 goto errOut;
562 }
563 base64Len = (unsigned)(end64 - start64);
564 }
565 /* else no END, no reason to complain about that as long as base64 decode works OK */
566
567 /* Base 64 decode */
568 decData = cuDec64((const unsigned char *)start64, base64Len, &decDataLen);
569 if(decData == NULL) {
570 /* bad base64 data */
571 ortn = UNSUPPORTED_FORMAT_ERR;
572 goto errOut;
573 }
574
575 if(outlen) {
576 *outlen = decDataLen;
577 }
578 if(outbuf) {
579 *outbuf = decData;
580 }
581 else {
582 free((void *)decData);
583 }
584
585 errOut:
586 return ortn;
587 }