]>
git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_x509_tp/lib/cuEnc64.c
2 * Copyright (c) 1998-2003,2010-2012 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
27 #define NULL ((void *)0)
31 * map a 6-bit binary value to a printable character.
34 unsigned char bintoasc
[] =
35 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
38 * Map an 7-bit printable character to its corresponding binary value.
39 * Any illegal characters return high bit set.
42 unsigned char asctobin
[] =
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
63 * map 6 bits to a printing char
65 #define ENC(c) (bintoasc[((c) & 0x3f)])
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.
74 static void encChunk(const unsigned char *inp
,
78 unsigned char c1
, c2
, c3
, c4
;
81 c2
= ((inp
[0] << 4) & 0x30) | ((inp
[1] >> 4) & 0xf);
82 c3
= ((inp
[1] << 2) & 0x3c) | ((inp
[2] >> 6) & 0x3);
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.
107 unsigned char *cuEnc64(const unsigned char *inbuf
,
109 unsigned *outlen
) // RETURNED
111 return cuEnc64WithLines(inbuf
, inlen
, 0, outlen
);
114 unsigned char *cuEnc64WithLines(const unsigned char *inbuf
,
120 unsigned len
; // to malloc, liberal
121 unsigned olen
= 0; // actual output size
122 unsigned char *outbuf
;
123 unsigned char endbuf
[3];
129 outTextLen
= ((inlen
+ 2) / 3) * 4;
132 * linelen must be 0 mod 4 for this to work; round up...
134 if((linelen
& 0x03) != 0) {
135 linelen
= (linelen
+ 3) & 0xfffffffc;
137 numLines
= (outTextLen
+ linelen
- 1)/ linelen
;
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.
148 len
= outTextLen
+ (2 * numLines
) + 1;
149 outbuf
= (unsigned char*)malloc(len
);
157 endbuf
[i
] = inbuf
[i
];
163 encChunk(endbuf
, outp
, inlen
);
167 encChunk(inbuf
, outp
, 3);
174 if((linelen
!= 0) && (thisLine
>= linelen
) && inlen
) {
176 * last trailing newline added below
177 * Note we don't split 4-byte output chunks over newlines
191 static inline int isWhite(unsigned char c
)
206 * Strip off all whitespace from a (supposedly) enc64-format string.
207 * Returns a malloc'd string.
209 static unsigned char *stringCleanse(const unsigned char *inbuf
,
213 unsigned char *news
; // cleansed inbuf
214 unsigned newsDex
; // index into news
217 news
= (unsigned char*)malloc(inlen
);
219 for(i
=0; i
<inlen
; i
++) {
220 if(!isWhite(inbuf
[i
])) {
221 news
[newsDex
++] = inbuf
[i
];
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.
233 * All whitespace in input is ignored.
235 unsigned char *cuDec64(const unsigned char *inbuf
,
239 unsigned char *outbuf
;
240 unsigned char *outp
; // malloc'd outbuf size
242 const unsigned char *bp
;
243 unsigned olen
= 0; // actual output size
244 unsigned char c1
, c2
, c3
, c4
;
247 unsigned char *news
; // cleansed inbuf
251 * Strip out all whitespace; remainder must be multiple of four
254 news
= stringCleanse(inbuf
, inlen
, &newsLen
);
255 if((newsLen
& 0x03) != 0) {
257 return (unsigned char*) NULL
;
262 obuflen
= (inlen
/ 4) * 3;
263 outbuf
= (unsigned char*)malloc(obuflen
);
268 * Note inlen is always a multiple of four here
270 if (*bp
& 0x80 || (c1
= asctobin
[*bp
]) & 0x80) {
275 if (*bp
& 0x80 || (c2
= asctobin
[*bp
]) & 0x80){
282 * two input bytes, one output byte
301 } else if (*bp
& 0x80 || (c3
= asctobin
[*bp
]) & 0x80) {
308 * Three input bytes, two output
315 } else if (*bp
& 0x80 || (c4
= asctobin
[*bp
]) & 0x80) {
319 * Normal non-pad case
326 j
= (c1
<< 2) | (c2
>> 4);
329 j
= (c2
<< 4) | (c3
>> 2);
340 return outbuf
; /* normal return */
345 return (unsigned char*) NULL
;
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.
354 int cuIsValidEnc64(const unsigned char *inbuf
,
357 int padChars
= 0; // running count of PAD chars
358 int validEncChars
= 0;
364 * -- count valid chars
365 * -- ensure not more than 2 PAD chars, only at end
366 * -- ensure valid chars mod 4 == 0
377 return 0; // max of 2 PAD chars at end
380 else if(padChars
> 0) {
381 return 0; // no normal chars after seeing PAD
383 else if((c
& 0x80) || ((asctobin
[c
]) & 0x80)) {
384 return 0; // invalid encoded char
388 if((validEncChars
& 0x03) != 0) {
397 * Text parsing routines.
399 * Search incoming text for specified string. Does not assume inText is
400 * NULL terminated. Returns pointer to start of found string in inText.
402 static const char *findStr(
405 const char *str
) // NULL terminated - search for this
407 /* probably not the hottest string search algorithm... */
409 size_t srchStrLen
= strlen(str
);
412 /* last char * we can search in inText for start of str */
413 const char *endCp
= inText
+ inTextLen
- srchStrLen
;
415 for(cp
=inText
; cp
<=endCp
; cp
++) {
417 if(!memcmp(cp
, str
, srchStrLen
)) {
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).
430 * ALWAYS returns a mallocd string if there is ANY data remaining per the
431 * incoming inTextLen. Returns NULL if inTextLen is zero.
433 static const char *getLine(
435 unsigned inTextLen
, // RETURNED
436 unsigned *consumed
) // RETURNED
440 const char *cp
= inText
;
441 const char *newline
= NULL
; // if we found a newline, this points to the first one
445 if((c
== '\r') || (c
== '\n')) {
446 if(newline
== NULL
) {
451 else if(newline
!= NULL
) {
452 /* non newline after newline, done */
461 linelen
= newline
- inText
;
466 char *rtn
= (char *)malloc(linelen
+ 1);
467 memmove(rtn
, inText
, linelen
);
472 #define UNSUPPORTED_FORMAT_ERR -25256
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.
480 const unsigned char *inbuf
,
482 unsigned char **outbuf
, // RETURNED (caller must free)
483 unsigned *outlen
) // RETURNED
485 unsigned lenToGo
= (inlen
) ? inlen
: 0;
486 const char *currCp
= (inbuf
) ? (const char *)inbuf
: NULL
;
487 const char *currLine
= NULL
; // mallocd by getLine()
493 unsigned char *decData
;
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
);
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
;
512 // assert(consumed <= lenToGo);
516 free((void *)currLine
);
519 /* Skip empty lines. */
521 currLine
= getLine(currCp
, lenToGo
, &consumed
);
522 if(currLine
== NULL
) {
524 ortn
= UNSUPPORTED_FORMAT_ERR
;
528 size_t lineLen
= strlen(currLine
);
533 free((void *)currLine
);
536 /* looks like good stuff; process */
540 // assert(consumed <= lenToGo);
545 /* no valid base64 data */
546 ortn
= UNSUPPORTED_FORMAT_ERR
;
551 * currCP points to start of base64 data - mark it and search for end line.
552 * We skip everything after the end line.
555 base64Len
= lenToGo
; // if no END
556 end64
= findStr(currCp
, lenToGo
, "-----END");
558 if(end64
== start64
) {
559 /* Empty, nothing between START and END */
560 ortn
= UNSUPPORTED_FORMAT_ERR
;
563 base64Len
= (unsigned)(end64
- start64
);
565 /* else no END, no reason to complain about that as long as base64 decode works OK */
568 decData
= cuDec64((const unsigned char *)start64
, base64Len
, &decDataLen
);
569 if(decData
== NULL
) {
570 /* bad base64 data */
571 ortn
= UNSUPPORTED_FORMAT_ERR
;
576 *outlen
= decDataLen
;
582 free((void *)decData
);