1/*
2 * Copyright (c) 2003 Apple Computer, 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
19/*
20	File:		 cuPem.h
21
22	Description: PEM encode/decode routines
23
24	Author:		 dmitch
25
26*/
27
28#include "cuPem.h"
29#include <stdlib.h>
30#include <stdio.h>
31#include <strings.h>
32#include <ctype.h>
33#include "cuEnc64.h"
34
35#define PEM_SCAN_LEN		8192
36
37/*
38 * Determine if specified blob appears to be PEM format.
39 * Returns 1 if so, 0 if not.
40 */
41int isPem(
42	const unsigned char 	*inData,
43	unsigned 				inDataLen)
44{
45	/*
46	 * 1. The entire blob must be printable ASCII.
47	 */
48	const unsigned char *cp = inData;
49	for(unsigned dex=0; dex<inDataLen; dex++, cp++) {
50		if(!isprint(*cp) && !isspace(*cp)) {
51			return 0;
52		}
53	}
54
55	/*
56	 * Search for "-----BEGIN " and "-----END".
57	 * No strnstr() on X, so copy and NULL terminate to use strstr.
58	 * First, get the first PEM_SCAN_LEN chars or inDataLen, whichever
59	 * is less.
60	 */
61	unsigned char buf[PEM_SCAN_LEN + 1];
62	unsigned len = inDataLen;
63	if(len > PEM_SCAN_LEN) {
64		len = PEM_SCAN_LEN;
65	}
66	memcpy(buf, inData, len);
67	buf[len] = '\0';
68	const char *p = strstr((const char *)buf, "-----BEGIN ");
69	if(p == NULL) {
70		return 0;
71	}
72
73	/*
74	 * Now the last PEM_SCAN_LEN chars or inDataLen, whichever is less.
75	 */
76	if(inDataLen > PEM_SCAN_LEN) {
77		memcpy(buf, inData + inDataLen - PEM_SCAN_LEN, PEM_SCAN_LEN);
78		buf[PEM_SCAN_LEN] = '\0';
79	}
80	/* else we already have whole blob in buf[] */
81	p = strstr((const char *)buf, "-----END ");
82	if(p == NULL) {
83		return 0;
84	}
85	/* success */
86	return 1;
87}
88
89int pemEncode(
90	const unsigned char 	*inData,
91	unsigned 				inDataLen,
92	unsigned char 			**outData,
93	unsigned 				*outDataLen,
94	const char 				*headerString)
95{
96	unsigned char *enc;
97	unsigned encLen;
98
99	/* First base64 encode */
100	enc = cuEnc64WithLines(inData, inDataLen, 64, &encLen);
101	if(enc == NULL) {
102		/* malloc error is actually the only known failure */
103		printf("***pemEncode: Error encoding file. Aborting.\n");
104		return -1;
105	}
106
107	/* estimate outsize - just be sloppy, way conservative */
108	size_t outSize = encLen + (2 * strlen(headerString)) + 200;
109	*outData = (unsigned char *)malloc(outSize);
110	sprintf((char *)*outData, "-----BEGIN %s-----\n%s-----END %s-----\n",
111		headerString, (char *)enc, headerString);
112	*outDataLen = (unsigned int)strlen((char *)*outData);
113
114	if((*outData)[*outDataLen - 1] == '\0') {
115		(*outDataLen)--;
116	}
117	free(enc);
118	return 0;
119}
120
121int pemDecode(
122	const unsigned char 	*inData,
123	unsigned 				inDataLen,
124	unsigned char 			**outData,
125	unsigned 				*outDataLen)
126{
127	char *cp;
128	char *curr1, *curr2;
129	char *startPem = NULL;
130	char *endPem = NULL;
131	unsigned char *out;
132	unsigned outLen;
133	int ourRtn = 0;
134	char *freeCp = NULL;
135
136	/* make the whole thing a NULL-terminated string */
137	if(inData[inDataLen - 1] != '\0') {
138		cp = freeCp = (char *)malloc(inDataLen + 1);
139		memmove(cp, inData, inDataLen);
140		cp[inDataLen] = '\0';
141		inDataLen++;
142	}
143	else {
144		/* already is */
145		cp = (char *)inData;
146	}
147
148	/* cp is start of NULL-terminated buffer, size inDataLen */
149	/* skip over everything until "-----" */
150	curr1 = strstr(cp, "-----");
151	if(curr1 == NULL) {
152		printf("***pemDecode: no terminator found\n");
153		ourRtn = -1;
154		goto abort;
155	}
156
157	/* find end of separator line, handling both flavors of terminator */
158	cp = curr1;
159	curr1 = strchr(cp, '\n');
160	curr2 = strchr(cp, '\r');
161	if((curr1 == NULL) & (curr2 == NULL)) {
162		printf("***pemDecode: Bad PEM format (1)\n");
163		ourRtn = -1;
164		goto abort;
165	}
166	if(curr1 == NULL) {
167		startPem = curr2;
168	}
169	else {
170		startPem = curr1;
171	}
172
173	/* startPem points to end of separator line */
174	/* locate ending terminator and lop it off */
175	curr1 = strstr(startPem, "-----");
176	if(curr1 == NULL) {
177		printf("***pemDecode: Bad PEM format (2)\n");
178		ourRtn = -1;
179		goto abort;
180	}
181	endPem = curr1;
182	/* endPem points to last PEM data plus one */
183
184	out = cuDec64((unsigned char *)startPem, (unsigned int)(endPem-startPem), &outLen);
185	if(out == NULL) {
186		printf("Bad PEM format (3)\n");
187		ourRtn = -1;
188		goto abort;
189	}
190	*outData = out;
191	*outDataLen = outLen;
192abort:
193	if(freeCp) {
194		free(freeCp);
195	}
196	return ourRtn;
197}
198
199