]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_x509_tp/lib/cuEnc64.c
Security-58286.1.32.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 if(obuflen == 0) {
264 free(news);
265 return NULL;
266 }
267 outbuf = (unsigned char*)malloc(obuflen);
268 outp = outbuf;
269
270 while (inlen) {
271 /*
272 * Note inlen is always a multiple of four here
273 */
274 if (*bp & 0x80 || (c1 = asctobin[*bp]) & 0x80) {
275 goto errorOut;
276 }
277 inlen--;
278 bp++;
279 if (*bp & 0x80 || (c2 = asctobin[*bp]) & 0x80){
280 goto errorOut;
281 }
282 inlen--;
283 bp++;
284 if (*bp == PAD) {
285 /*
286 * two input bytes, one output byte
287 */
288 c3 = c4 = 0;
289 thisOlen = 1;
290 if (c2 & 0xf) {
291 goto errorOut;
292 }
293 bp++;
294 inlen--;
295 if (*bp == PAD) {
296 bp++;
297 inlen--;
298 if(inlen > 0) {
299 goto errorOut;
300 }
301 }
302 else {
303 goto errorOut;
304 }
305 } else if (*bp & 0x80 || (c3 = asctobin[*bp]) & 0x80) {
306 goto errorOut;
307 } else {
308 bp++;
309 inlen--;
310 if (*bp == PAD) {
311 /*
312 * Three input bytes, two output
313 */
314 c4 = 0;
315 thisOlen = 2;
316 if (c3 & 3) {
317 goto errorOut;
318 }
319 } else if (*bp & 0x80 || (c4 = asctobin[*bp]) & 0x80) {
320 goto errorOut;
321 } else {
322 /*
323 * Normal non-pad case
324 */
325 thisOlen = 3;
326 }
327 bp++;
328 inlen--;
329 }
330 j = (c1 << 2) | (c2 >> 4);
331 *outp++ = j;
332 if(thisOlen > 1) {
333 j = (c2 << 4) | (c3 >> 2);
334 *outp++ = j;
335 if(thisOlen == 3) {
336 j = (c3 << 6) | c4;
337 *outp++ = j;
338 }
339 }
340 olen += thisOlen;
341 }
342 free(news);
343 *outlen = olen;
344 return outbuf; /* normal return */
345
346 errorOut:
347 free(news);
348 free(outbuf);
349 return (unsigned char*) NULL;
350 }
351
352 /*
353 * Determine if specified input data is valid enc64 format. Returns 1
354 * if valid, 0 if not.
355 * This doesn't do a full enc64 parse job; it scans for legal characters
356 * and proper sync when a possible pad is found.
357 */
358 int cuIsValidEnc64(const unsigned char *inbuf,
359 unsigned inlen)
360 {
361 int padChars = 0; // running count of PAD chars
362 int validEncChars = 0;
363 unsigned char c;
364
365 /*
366 * -- scan inbuf
367 * -- skip whitespace
368 * -- count valid chars
369 * -- ensure not more than 2 PAD chars, only at end
370 * -- ensure valid chars mod 4 == 0
371 */
372
373 while(inlen) {
374 c = *inbuf++;
375 inlen--;
376 if(isWhite(c)) {
377 continue;
378 }
379 if(c == PAD) {
380 if(++padChars > 2) {
381 return 0; // max of 2 PAD chars at end
382 }
383 }
384 else if(padChars > 0) {
385 return 0; // no normal chars after seeing PAD
386 }
387 else if((c & 0x80) || ((asctobin[c]) & 0x80)) {
388 return 0; // invalid encoded char
389 }
390 validEncChars++;
391 }
392 if((validEncChars & 0x03) != 0) {
393 return 0;
394 }
395 else {
396 return 1;
397 }
398 }
399
400 /*
401 * Text parsing routines.
402 *
403 * Search incoming text for specified string. Does not assume inText is
404 * NULL terminated. Returns pointer to start of found string in inText.
405 */
406 static const char *findStr(
407 const char *inText,
408 unsigned inTextLen,
409 const char *str) // NULL terminated - search for this
410 {
411 /* probably not the hottest string search algorithm... */
412 const char *cp;
413 size_t srchStrLen = strlen(str);
414 char c = str[0];
415
416 if(!inText) {
417 return NULL;
418 }
419
420 /* last char * we can search in inText for start of str */
421 const char *endCp = inText + inTextLen - srchStrLen;
422
423 for(cp=inText; cp<=endCp; cp++) {
424 if(*cp == c) {
425 if(!memcmp(cp, str, srchStrLen)) {
426 return cp;
427 }
428 }
429 }
430 return NULL;
431 }
432
433 /*
434 * Obtain one line from current text. Returns a mallocd, NULL-terminated string
435 * which caller must free(). Also returns the number of chars consumed including
436 * the returned chars PLUS EOL terminators (\n and/or \r).
437 *
438 * ALWAYS returns a mallocd string if there is ANY data remaining per the
439 * incoming inTextLen. Returns NULL if inTextLen is zero.
440 */
441 static const char *getLine(
442 const char *inText,
443 unsigned inTextLen, // RETURNED
444 unsigned *consumed) // RETURNED
445
446 {
447 *consumed = 0;
448 const char *cp = inText;
449 const char *newline = NULL; // if we found a newline, this points to the first one
450
451 if(!inText) {
452 return NULL;
453 }
454
455 while(inTextLen) {
456 char c = *cp;
457 if((c == '\r') || (c == '\n')) {
458 if(newline == NULL) {
459 /* first newline */
460 newline = cp;
461 }
462 }
463 else if(newline != NULL) {
464 /* non newline after newline, done */
465 break;
466 }
467 (*consumed)++;
468 inTextLen--;
469 cp++;
470 }
471 ptrdiff_t linelen;
472 if(newline) {
473 linelen = newline - inText;
474 }
475 else {
476 linelen = *consumed;
477 }
478 char *rtn = (char *)malloc(linelen + 1);
479 memmove(rtn, inText, linelen);
480 rtn[linelen] = 0;
481 return rtn;
482 }
483
484 #define UNSUPPORTED_FORMAT_ERR -25256
485
486 /*
487 * Given input buffer containing a PEM-encoded certificate, convert to DER
488 * and return in outbuf. Result is malloced and must be freed by caller;
489 * its length is returned in *outlen. Returns 0 on success.
490 */
491 int cuConvertPem(
492 const unsigned char *inbuf,
493 unsigned inlen,
494 unsigned char **outbuf, // RETURNED (caller must free)
495 unsigned *outlen) // RETURNED
496 {
497 unsigned lenToGo = (inlen) ? inlen : 0;
498 const char *currCp = (inbuf) ? (const char *)inbuf : NULL;
499 const char *currLine = NULL; // mallocd by getLine()
500 unsigned consumed;
501 int ortn = 0;
502 const char *start64;
503 unsigned base64Len;
504 const char *end64;
505 unsigned char *decData;
506 unsigned decDataLen;
507
508 /* search to START line, parse it to get type/format/alg */
509 const char *startLine = findStr(currCp, lenToGo, "-----BEGIN");
510 if(startLine != NULL) {
511 /* possibly skip over leading garbage */
512 consumed = (unsigned)(startLine - currCp);
513 lenToGo -= consumed;
514 currCp = startLine;
515
516 /* get C string of START line */
517 currLine = getLine(startLine, lenToGo, &consumed);
518 if(currLine == NULL) {
519 /* somehow got here with no data */
520 // assert(lenToGo == 0);
521 ortn = UNSUPPORTED_FORMAT_ERR;
522 goto errOut;
523 }
524 // assert(consumed <= lenToGo);
525 currCp += consumed;
526 lenToGo -= consumed;
527
528 free((void *)currLine);
529 }
530
531 /* Skip empty lines. */
532 for( ; ; ) {
533 currLine = getLine(currCp, lenToGo, &consumed);
534 if(currLine == NULL) {
535 /* out of data */
536 ortn = UNSUPPORTED_FORMAT_ERR;
537 goto errOut;
538 }
539 int skipThis = 0;
540 size_t lineLen = strlen(currLine);
541 if(lineLen == 0) {
542 /* empty line */
543 skipThis = 1;
544 }
545 free((void *)currLine);
546
547 if(!skipThis) {
548 /* looks like good stuff; process */
549 break;
550 }
551 /* skip this line */
552 // assert(consumed <= lenToGo);
553 currCp += consumed;
554 lenToGo -= consumed;
555 }
556 if(lenToGo == 0) {
557 /* no valid base64 data */
558 ortn = UNSUPPORTED_FORMAT_ERR;
559 goto errOut;
560 }
561
562 /*
563 * currCP points to start of base64 data - mark it and search for end line.
564 * We skip everything after the end line.
565 */
566 start64 = currCp;
567 base64Len = lenToGo; // if no END
568 end64 = findStr(currCp, lenToGo, "-----END");
569 if(end64 != NULL) {
570 if(end64 == start64) {
571 /* Empty, nothing between START and END */
572 ortn = UNSUPPORTED_FORMAT_ERR;
573 goto errOut;
574 }
575 base64Len = (unsigned)(end64 - start64);
576 }
577 /* else no END, no reason to complain about that as long as base64 decode works OK */
578
579 /* Base 64 decode */
580 decData = cuDec64((const unsigned char *)start64, base64Len, &decDataLen);
581 if(decData == NULL) {
582 /* bad base64 data */
583 ortn = UNSUPPORTED_FORMAT_ERR;
584 goto errOut;
585 }
586
587 if(outlen) {
588 *outlen = decDataLen;
589 }
590 if(outbuf) {
591 *outbuf = decData;
592 }
593 else {
594 free((void *)decData);
595 }
596
597 errOut:
598 return ortn;
599 }