+++ /dev/null
-/*
- * Copyright (c) 1998-2003,2010 Apple Inc. All Rights Reserved.
- *
- * The contents of this file constitute Original Code as defined in and are
- * subject to the Apple Public Source License Version 1.2 (the 'License').
- * You may not use this file except in compliance with the License. Please
- * obtain a copy of the License at http://www.apple.com/publicsource and
- * read it before using this file.
- *
- * This Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * cuEnc64.c - encode/decode in 64-char IA5 format, per RFC 1421
- */
-
-#include "cuEnc64.h"
-#include <stdlib.h>
-#include <string.h>
-#include <stddef.h>
-
-#ifndef NULL
-#define NULL ((void *)0)
-#endif /* NULL */
-
-/*
- * map a 6-bit binary value to a printable character.
- */
-static const
-unsigned char bintoasc[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-/*
- * Map an 7-bit printable character to its corresponding binary value.
- * Any illegal characters return high bit set.
- */
-static const
-unsigned char asctobin[] =
-{
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x3e, 0x80, 0x80, 0x80, 0x3f,
- 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
- 0x3c, 0x3d, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
- 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
- 0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
- 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
- 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
- 0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80
-};
-
-/*
- * map 6 bits to a printing char
- */
-#define ENC(c) (bintoasc[((c) & 0x3f)])
-
-#define PAD '='
-
-/*
- * map one group of up to 3 bytes at inp to 4 bytes at outp.
- * Count is number of valid bytes in *inp; if less than 3, the
- * 1 or two extras must be zeros.
- */
-static void encChunk(const unsigned char *inp,
- unsigned char *outp,
- int count)
-{
- unsigned char c1, c2, c3, c4;
-
- c1 = *inp >> 2;
- c2 = ((inp[0] << 4) & 0x30) | ((inp[1] >> 4) & 0xf);
- c3 = ((inp[1] << 2) & 0x3c) | ((inp[2] >> 6) & 0x3);
- c4 = inp[2] & 0x3f;
- *outp++ = ENC(c1);
- *outp++ = ENC(c2);
- if (count == 1) {
- *outp++ = PAD;
- *outp = PAD;
- } else {
- *outp++ = ENC(c3);
- if (count == 2) {
- *outp = PAD;
- }
- else {
- *outp = ENC(c4);
- }
- }
-}
-
-/*
- * Given input buffer inbuf, length inlen, encode to 64-char IA5 format.
- * Result is fmalloc'd and returned; it is terminated by Microsoft-style
- * newline and NULL. Its length (including the trailing newline and NULL)
- * is returned in *outlen.
- */
-
-unsigned char *cuEnc64(const unsigned char *inbuf,
- unsigned inlen,
- unsigned *outlen) // RETURNED
-{
- return cuEnc64WithLines(inbuf, inlen, 0, outlen);
-}
-
-unsigned char *cuEnc64WithLines(const unsigned char *inbuf,
- unsigned inlen,
- unsigned linelen,
- unsigned *outlen)
-{
- unsigned outTextLen;
- unsigned len; // to malloc, liberal
- unsigned olen = 0; // actual output size
- unsigned char *outbuf;
- unsigned char endbuf[3];
- unsigned i;
- unsigned char *outp;
- unsigned numLines;
- unsigned thisLine;
-
- outTextLen = ((inlen + 2) / 3) * 4;
- if(linelen) {
- /*
- * linelen must be 0 mod 4 for this to work; round up...
- */
- if((linelen & 0x03) != 0) {
- linelen = (linelen + 3) & 0xfffffffc;
- }
- numLines = (outTextLen + linelen - 1)/ linelen;
- }
- else {
- numLines = 1;
- }
-
- /*
- * Total output size = encoded text size plus one newline per
- * line of output, plus trailing NULL. We always generate newlines
- * as \n; when decoding, we tolerate \r\n (Microsoft) or \n.
- */
- len = outTextLen + (2 * numLines) + 1;
- outbuf = (unsigned char*)malloc(len);
- outp = outbuf;
- thisLine = 0;
-
- while(inlen) {
- if(inlen < 3) {
- for(i=0; i<3; i++) {
- if(i < inlen) {
- endbuf[i] = inbuf[i];
- }
- else {
- endbuf[i] = 0;
- }
- }
- encChunk(endbuf, outp, inlen);
- inlen = 0;
- }
- else {
- encChunk(inbuf, outp, 3);
- inlen -= 3;
- inbuf += 3;
- }
- outp += 4;
- thisLine += 4;
- olen += 4;
- if((linelen != 0) && (thisLine >= linelen) && inlen) {
- /*
- * last trailing newline added below
- * Note we don't split 4-byte output chunks over newlines
- */
- *outp++ = '\n';
- olen++;
- thisLine = 0;
- }
- }
- *outp++ = '\n';
- *outp = '\0';
- olen += 2;
- *outlen = olen;
- return outbuf;
-}
-
-static inline int isWhite(unsigned char c)
-{
- switch(c) {
- case '\n':
- case '\r':
- case ' ':
- case '\t':
- case '\0':
- return 1;
- default:
- return 0;
- }
-}
-
-/*
- * Strip off all whitespace from a (supposedly) enc64-format string.
- * Returns a malloc'd string.
- */
-static unsigned char *stringCleanse(const unsigned char *inbuf,
- unsigned inlen,
- unsigned *outlen)
-{
- unsigned char *news; // cleansed inbuf
- unsigned newsDex; // index into news
- unsigned i;
-
- news = (unsigned char*)malloc(inlen);
- newsDex = 0;
- for(i=0; i<inlen; i++) {
- if(!isWhite(inbuf[i])) {
- news[newsDex++] = inbuf[i];
- }
- }
- *outlen = newsDex;
- return news;
-}
-
-/*
- * Given input buffer inbuf, length inlen, decode from 64-char IA5 format to
- * binary. Result is malloced and returned; its length is returned in *outlen.
- * NULL return indicates corrupted input.
- *
- * All whitespace in input is ignored.
- */
-unsigned char *cuDec64(const unsigned char *inbuf,
- unsigned inlen,
- unsigned *outlen)
-{
- unsigned char *outbuf;
- unsigned char *outp; // malloc'd outbuf size
- unsigned obuflen;
- const unsigned char *bp;
- unsigned olen = 0; // actual output size
- unsigned char c1, c2, c3, c4;
- unsigned char j;
- unsigned thisOlen;
- unsigned char *news; // cleansed inbuf
- unsigned newsLen;
-
- /*
- * Strip out all whitespace; remainder must be multiple of four
- * characters
- */
- news = stringCleanse(inbuf, inlen, &newsLen);
- if((newsLen & 0x03) != 0) {
- free(news);
- return (unsigned char*) NULL;
- }
- inlen = newsLen;
- bp = news;
-
- obuflen = (inlen / 4) * 3;
- outbuf = (unsigned char*)malloc(obuflen);
- outp = outbuf;
-
- while (inlen) {
- /*
- * Note inlen is always a multiple of four here
- */
- if (*bp & 0x80 || (c1 = asctobin[*bp]) & 0x80) {
- goto errorOut;
- }
- inlen--;
- bp++;
- if (*bp & 0x80 || (c2 = asctobin[*bp]) & 0x80){
- goto errorOut;
- }
- inlen--;
- bp++;
- if (*bp == PAD) {
- /*
- * two input bytes, one output byte
- */
- c3 = c4 = 0;
- thisOlen = 1;
- if (c2 & 0xf) {
- goto errorOut;
- }
- bp++;
- inlen--;
- if (*bp == PAD) {
- bp++;
- inlen--;
- if(inlen > 0) {
- goto errorOut;
- }
- }
- else {
- goto errorOut;
- }
- } else if (*bp & 0x80 || (c3 = asctobin[*bp]) & 0x80) {
- goto errorOut;
- } else {
- bp++;
- inlen--;
- if (*bp == PAD) {
- /*
- * Three input bytes, two output
- */
- c4 = 0;
- thisOlen = 2;
- if (c3 & 3) {
- goto errorOut;
- }
- } else if (*bp & 0x80 || (c4 = asctobin[*bp]) & 0x80) {
- goto errorOut;
- } else {
- /*
- * Normal non-pad case
- */
- thisOlen = 3;
- }
- bp++;
- inlen--;
- }
- j = (c1 << 2) | (c2 >> 4);
- *outp++ = j;
- if(thisOlen > 1) {
- j = (c2 << 4) | (c3 >> 2);
- *outp++ = j;
- if(thisOlen == 3) {
- j = (c3 << 6) | c4;
- *outp++ = j;
- }
- }
- olen += thisOlen;
- }
- free(news);
- *outlen = olen;
- return outbuf; /* normal return */
-
-errorOut:
- free(news);
- free(outbuf);
- return (unsigned char*) NULL;
-}
-
-/*
- * Determine if specified input data is valid enc64 format. Returns 1
- * if valid, 0 if not.
- * This doesn't do a full enc64 parse job; it scans for legal characters
- * and proper sync when a possible pad is found.
- */
-int cuIsValidEnc64(const unsigned char *inbuf,
- unsigned inlen)
-{
- int padChars = 0; // running count of PAD chars
- int validEncChars = 0;
- unsigned char c;
-
- /*
- * -- scan inbuf
- * -- skip whitespace
- * -- count valid chars
- * -- ensure not more than 2 PAD chars, only at end
- * -- ensure valid chars mod 4 == 0
- */
-
- while(inlen) {
- c = *inbuf++;
- inlen--;
- if(isWhite(c)) {
- continue;
- }
- if(c == PAD) {
- if(++padChars > 2) {
- return 0; // max of 2 PAD chars at end
- }
- }
- else if(padChars > 0) {
- return 0; // no normal chars after seeing PAD
- }
- else if((c & 0x80) || ((asctobin[c]) & 0x80)) {
- return 0; // invalid encoded char
- }
- validEncChars++;
- }
- if((validEncChars & 0x03) != 0) {
- return 0;
- }
- else {
- return 1;
- }
-}
-
-/*
- * Text parsing routines.
- *
- * Search incoming text for specified string. Does not assume inText is
- * NULL terminated. Returns pointer to start of found string in inText.
- */
-static const char *findStr(
- const char *inText,
- unsigned inTextLen,
- const char *str) // NULL terminated - search for this
-{
- /* probably not the hottest string search algorithm... */
- const char *cp;
- size_t srchStrLen = strlen(str);
- char c = str[0];
-
- /* last char * we can search in inText for start of str */
- const char *endCp = inText + inTextLen - srchStrLen;
-
- for(cp=inText; cp<=endCp; cp++) {
- if(*cp == c) {
- if(!memcmp(cp, str, srchStrLen)) {
- return cp;
- }
- }
- }
- return NULL;
-}
-
-/*
- * Obtain one line from current text. Returns a mallocd, NULL-terminated string
- * which caller must free(). Also returns the number of chars consumed including
- * the returned chars PLUS EOL terminators (\n and/or \r).
- *
- * ALWAYS returns a mallocd string if there is ANY data remaining per the
- * incoming inTextLen. Returns NULL if inTextLen is zero.
- */
-static const char *getLine(
- const char *inText,
- unsigned inTextLen, // RETURNED
- unsigned *consumed) // RETURNED
-
-{
- *consumed = 0;
- const char *cp = inText;
- const char *newline = NULL; // if we found a newline, this points to the first one
-
- while(inTextLen) {
- char c = *cp;
- if((c == '\r') || (c == '\n')) {
- if(newline == NULL) {
- /* first newline */
- newline = cp;
- }
- }
- else if(newline != NULL) {
- /* non newline after newline, done */
- break;
- }
- (*consumed)++;
- inTextLen--;
- cp++;
- }
- ptrdiff_t linelen;
- if(newline) {
- linelen = newline - inText;
- }
- else {
- linelen = *consumed;
- }
- char *rtn = (char *)malloc(linelen + 1);
- memmove(rtn, inText, linelen);
- rtn[linelen] = 0;
- return rtn;
-}
-
-#define UNSUPPORTED_FORMAT_ERR -25256
-
-/*
- * Given input buffer containing a PEM-encoded certificate, convert to DER
- * and return in outbuf. Result is malloced and must be freed by caller;
- * its length is returned in *outlen. Returns 0 on success.
- */
-int cuConvertPem(
- const unsigned char *inbuf,
- unsigned inlen,
- unsigned char **outbuf, // RETURNED (caller must free)
- unsigned *outlen) // RETURNED
-{
- unsigned lenToGo = (inlen) ? inlen : 0;
- const char *currCp = (inbuf) ? (const char *)inbuf : NULL;
- const char *currLine = NULL; // mallocd by getLine()
- unsigned consumed;
- int ortn = 0;
- const char *start64;
- unsigned base64Len;
- const char *end64;
- unsigned char *decData;
- unsigned decDataLen;
-
- /* search to START line, parse it to get type/format/alg */
- const char *startLine = findStr(currCp, lenToGo, "-----BEGIN");
- if(startLine != NULL) {
- /* possibly skip over leading garbage */
- consumed = (unsigned)(startLine - currCp);
- lenToGo -= consumed;
- currCp = startLine;
-
- /* get C string of START line */
- currLine = getLine(startLine, lenToGo, &consumed);
- if(currLine == NULL) {
- /* somehow got here with no data */
- // assert(lenToGo == 0);
- ortn = UNSUPPORTED_FORMAT_ERR;
- goto errOut;
- }
- // assert(consumed <= lenToGo);
- currCp += consumed;
- lenToGo -= consumed;
-
- free((void *)currLine);
- }
-
- /* Skip empty lines. */
- for( ; ; ) {
- currLine = getLine(currCp, lenToGo, &consumed);
- if(currLine == NULL) {
- /* out of data */
- ortn = UNSUPPORTED_FORMAT_ERR;
- goto errOut;
- }
- int skipThis = 0;
- size_t lineLen = strlen(currLine);
- if(lineLen == 0) {
- /* empty line */
- skipThis = 1;
- }
- free((void *)currLine);
-
- if(!skipThis) {
- /* looks like good stuff; process */
- break;
- }
- /* skip this line */
- // assert(consumed <= lenToGo);
- currCp += consumed;
- lenToGo -= consumed;
- }
- if(lenToGo == 0) {
- /* no valid base64 data */
- ortn = UNSUPPORTED_FORMAT_ERR;
- goto errOut;
- }
-
- /*
- * currCP points to start of base64 data - mark it and search for end line.
- * We skip everything after the end line.
- */
- start64 = currCp;
- base64Len = lenToGo; // if no END
- end64 = findStr(currCp, lenToGo, "-----END");
- if(end64 != NULL) {
- if(end64 == start64) {
- /* Empty, nothing between START and END */
- ortn = UNSUPPORTED_FORMAT_ERR;
- goto errOut;
- }
- base64Len = (unsigned)(end64 - start64);
- }
- /* else no END, no reason to complain about that as long as base64 decode works OK */
-
- /* Base 64 decode */
- decData = cuDec64((const unsigned char *)start64, base64Len, &decDataLen);
- if(decData == NULL) {
- /* bad base64 data */
- ortn = UNSUPPORTED_FORMAT_ERR;
- goto errOut;
- }
-
- if(outlen) {
- *outlen = decDataLen;
- }
- if(outbuf) {
- *outbuf = decData;
- }
- else {
- free((void *)decData);
- }
-
-errOut:
- return ortn;
-}