]>
git.saurik.com Git - apple/security.git/blob - libsecurity_apple_x509_tp/lib/cuEnc64.c
4e258ee4687f566d29f0a7d6dfeea75daa83ea3f
2 * Copyright (c) 1998-2003,2010 Apple Inc. All Rights Reserved.
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.
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.
18 * cuEnc64.c - encode/decode in 64-char IA5 format, per RFC 1421
26 #define NULL ((void *)0)
30 * map a 6-bit binary value to a printable character.
33 unsigned char bintoasc
[] =
34 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
37 * Map an 7-bit printable character to its corresponding binary value.
38 * Any illegal characters return high bit set.
41 unsigned char asctobin
[] =
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
62 * map 6 bits to a printing char
64 #define ENC(c) (bintoasc[((c) & 0x3f)])
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.
73 static void encChunk(const unsigned char *inp
,
77 unsigned char c1
, c2
, c3
, c4
;
80 c2
= ((inp
[0] << 4) & 0x30) | ((inp
[1] >> 4) & 0xf);
81 c3
= ((inp
[1] << 2) & 0x3c) | ((inp
[2] >> 6) & 0x3);
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.
106 unsigned char *cuEnc64(const unsigned char *inbuf
,
108 unsigned *outlen
) // RETURNED
110 return cuEnc64WithLines(inbuf
, inlen
, 0, outlen
);
113 unsigned char *cuEnc64WithLines(const unsigned char *inbuf
,
119 unsigned len
; // to malloc, liberal
120 unsigned olen
= 0; // actual output size
121 unsigned char *outbuf
;
122 unsigned char endbuf
[3];
128 outTextLen
= ((inlen
+ 2) / 3) * 4;
131 * linelen must be 0 mod 4 for this to work; round up...
133 if((linelen
& 0x03) != 0) {
134 linelen
= (linelen
+ 3) & 0xfffffffc;
136 numLines
= (outTextLen
+ linelen
- 1)/ linelen
;
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.
147 len
= outTextLen
+ (2 * numLines
) + 1;
148 outbuf
= (unsigned char*)malloc(len
);
156 endbuf
[i
] = inbuf
[i
];
162 encChunk(endbuf
, outp
, inlen
);
166 encChunk(inbuf
, outp
, 3);
173 if((linelen
!= 0) && (thisLine
>= linelen
) && inlen
) {
175 * last trailing newline added below
176 * Note we don't split 4-byte output chunks over newlines
190 static inline int isWhite(unsigned char c
)
205 * Strip off all whitespace from a (supposedly) enc64-format string.
206 * Returns a malloc'd string.
208 static unsigned char *stringCleanse(const unsigned char *inbuf
,
212 unsigned char *news
; // cleansed inbuf
213 unsigned newsDex
; // index into news
216 news
= (unsigned char*)malloc(inlen
);
218 for(i
=0; i
<inlen
; i
++) {
219 if(!isWhite(inbuf
[i
])) {
220 news
[newsDex
++] = inbuf
[i
];
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.
232 * All whitespace in input is ignored.
234 unsigned char *cuDec64(const unsigned char *inbuf
,
238 unsigned char *outbuf
;
239 unsigned char *outp
; // malloc'd outbuf size
241 const unsigned char *bp
;
242 unsigned olen
= 0; // actual output size
243 unsigned char c1
, c2
, c3
, c4
;
246 unsigned char *news
; // cleansed inbuf
250 * Strip out all whitespace; remainder must be multiple of four
253 news
= stringCleanse(inbuf
, inlen
, &newsLen
);
254 if((newsLen
& 0x03) != 0) {
256 return (unsigned char*) NULL
;
261 obuflen
= (inlen
/ 4) * 3;
262 outbuf
= (unsigned char*)malloc(obuflen
);
267 * Note inlen is always a multiple of four here
269 if (*bp
& 0x80 || (c1
= asctobin
[*bp
]) & 0x80) {
274 if (*bp
& 0x80 || (c2
= asctobin
[*bp
]) & 0x80){
281 * two input bytes, one output byte
300 } else if (*bp
& 0x80 || (c3
= asctobin
[*bp
]) & 0x80) {
307 * Three input bytes, two output
314 } else if (*bp
& 0x80 || (c4
= asctobin
[*bp
]) & 0x80) {
318 * Normal non-pad case
325 j
= (c1
<< 2) | (c2
>> 4);
328 j
= (c2
<< 4) | (c3
>> 2);
339 return outbuf
; /* normal return */
344 return (unsigned char*) NULL
;
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.
353 int cuIsValidEnc64(const unsigned char *inbuf
,
356 int padChars
= 0; // running count of PAD chars
357 int validEncChars
= 0;
363 * -- count valid chars
364 * -- ensure not more than 2 PAD chars, only at end
365 * -- ensure valid chars mod 4 == 0
376 return 0; // max of 2 PAD chars at end
379 else if(padChars
> 0) {
380 return 0; // no normal chars after seeing PAD
382 else if((c
& 0x80) || ((asctobin
[c
]) & 0x80)) {
383 return 0; // invalid encoded char
387 if((validEncChars
& 0x03) != 0) {
396 * Text parsing routines.
398 * Search incoming text for specified string. Does not assume inText is
399 * NULL terminated. Returns pointer to start of found string in inText.
401 static const char *findStr(
404 const char *str
) // NULL terminated - search for this
406 /* probably not the hottest string search algorithm... */
408 unsigned srchStrLen
= strlen(str
);
411 /* last char * we can search in inText for start of str */
412 const char *endCp
= inText
+ inTextLen
- srchStrLen
;
414 for(cp
=inText
; cp
<=endCp
; cp
++) {
416 if(!memcmp(cp
, str
, srchStrLen
)) {
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).
429 * ALWAYS returns a mallocd string if there is ANY data remaining per the
430 * incoming inTextLen. Returns NULL if inTextLen is zero.
432 static const char *getLine(
434 unsigned inTextLen
, // RETURNED
435 unsigned *consumed
) // RETURNED
439 const char *cp
= inText
;
440 const char *newline
= NULL
; // if we found a newline, this points to the first one
444 if((c
== '\r') || (c
== '\n')) {
445 if(newline
== NULL
) {
450 else if(newline
!= NULL
) {
451 /* non newline after newline, done */
460 linelen
= newline
- inText
;
465 char *rtn
= (char *)malloc(linelen
+ 1);
466 memmove(rtn
, inText
, linelen
);
471 #define UNSUPPORTED_FORMAT_ERR -25256
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.
479 const unsigned char *inbuf
,
481 unsigned char **outbuf
, // RETURNED (caller must free)
482 unsigned *outlen
) // RETURNED
484 unsigned lenToGo
= (inlen
) ? inlen
: 0;
485 const char *currCp
= (inbuf
) ? (const char *)inbuf
: NULL
;
486 const char *currLine
= NULL
; // mallocd by getLine()
492 unsigned char *decData
;
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
;
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
;
511 // assert(consumed <= lenToGo);
515 free((void *)currLine
);
518 /* Skip empty lines. */
520 currLine
= getLine(currCp
, lenToGo
, &consumed
);
521 if(currLine
== NULL
) {
523 ortn
= UNSUPPORTED_FORMAT_ERR
;
527 unsigned lineLen
= strlen(currLine
);
532 free((void *)currLine
);
535 /* looks like good stuff; process */
539 // assert(consumed <= lenToGo);
544 /* no valid base64 data */
545 ortn
= UNSUPPORTED_FORMAT_ERR
;
550 * currCP points to start of base64 data - mark it and search for end line.
551 * We skip everything after the end line.
554 base64Len
= lenToGo
; // if no END
555 end64
= findStr(currCp
, lenToGo
, "-----END");
557 if(end64
== start64
) {
558 /* Empty, nothing between START and END */
559 ortn
= UNSUPPORTED_FORMAT_ERR
;
562 base64Len
= end64
- start64
;
564 /* else no END, no reason to complain about that as long as base64 decode works OK */
567 decData
= cuDec64((const unsigned char *)start64
, base64Len
, &decDataLen
);
568 if(decData
== NULL
) {
569 /* bad base64 data */
570 ortn
= UNSUPPORTED_FORMAT_ERR
;
575 *outlen
= decDataLen
;
581 free((void *)decData
);