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