xref: /titanic_54/usr/src/lib/libkmf/libkmf/common/pem_encode.c (revision 0a44ef6d9afbfe052a7e975f55ea0d2954b62a82)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  *
21  *
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
30  * All rights reserved.
31  *
32  * This package is an SSL implementation written
33  * by Eric Young (eay@cryptsoft.com).
34  * The implementation was written so as to conform with Netscapes SSL.
35  *
36  * This library is free for commercial and non-commercial use as long as
37  * the following conditions are aheared to.  The following conditions
38  * apply to all code found in this distribution, be it the RC4, RSA,
39  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
40  * included with this distribution is covered by the same copyright terms
41  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
42  *
43  * Copyright remains Eric Young's, and as such any Copyright notices in
44  * the code are not to be removed.
45  * If this package is used in a product, Eric Young should be given attribution
46  * as the author of the parts of the library used.
47  * This can be in the form of a textual message at program startup or
48  * in documentation (online or textual) provided with the package.
49  *
50  * Redistribution and use in source and binary forms, with or without
51  * modification, are permitted provided that the following conditions
52  * are met:
53  * 1. Redistributions of source code must retain the copyright
54  *    notice, this list of conditions and the following disclaimer.
55  * 2. Redistributions in binary form must reproduce the above copyright
56  *    notice, this list of conditions and the following disclaimer in the
57  *    documentation and/or other materials provided with the distribution.
58  * 3. All advertising materials mentioning features or use of this software
59  *    must display the following acknowledgement:
60  *    "This product includes cryptographic software written by
61  *     Eric Young (eay@cryptsoft.com)"
62  *    The word 'cryptographic' can be left out if the rouines from the library
63  *    being used are not cryptographic related :-).
64  * 4. If you include any Windows specific code (or a derivative thereof) from
65  *    the apps directory (application code) you must include an acknowledgement:
66  *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
67  *
68  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
69  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
70  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
71  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
72  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
73  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
74  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
75  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
76  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
77  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
78  * SUCH DAMAGE.
79  *
80  * The licence and distribution terms for any publically available version or
81  * derivative of this code cannot be changed.  i.e. this code cannot simply be
82  * copied and put under another distribution licence
83  * [including the GNU Public Licence.]
84  */
85 
86 /* pem_encode.c - PEM encoding routines */
87 
88 #include <stdlib.h>
89 #include <strings.h>
90 #include <sys/types.h>
91 #include <kmfapi.h>
92 #include <pem_encode.h>
93 
94 static unsigned char data_bin2ascii[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
95 abcdefghijklmnopqrstuvwxyz0123456789+/";
96 
97 static unsigned char data_ascii2bin[128] = {
98 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
99 	0xFF, 0xE0, 0xF0, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF,
100 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
101 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
102 	0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
103 	0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xF2, 0xFF, 0x3F,
104 	0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
105 	0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF,
106 	0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
107 	0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
108 	0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
109 	0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
110 	0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
111 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
112 	0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
113 	0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
114 };
115 
116 #define	conv_bin2ascii(a)	(data_bin2ascii[(a)&0x3f])
117 #define	conv_ascii2bin(a)	(data_ascii2bin[(a)&0x7f])
118 
119 
120 void
121 PEM_EncodeInit(PEM_ENCODE_CTX *ctx)
122 {
123 	ctx->length = 48;
124 	ctx->num = 0;
125 	ctx->line_num = 0;
126 }
127 
128 int
129 PEM_EncodeBlock(unsigned char *t, const unsigned char *f, int dlen)
130 {
131 	int i, ret = 0;
132 	unsigned long l;
133 
134 	for (i = dlen; i > 0; i -= 3) {
135 		if (i >= 3) {
136 			l = (((unsigned long)f[0])<<16L)|
137 			    (((unsigned long)f[1])<< 8L)|f[2];
138 			*(t++) = conv_bin2ascii(l>>18L);
139 			*(t++) = conv_bin2ascii(l>>12L);
140 			*(t++) = conv_bin2ascii(l>> 6L);
141 			*(t++) = conv_bin2ascii(l);
142 		} else {
143 			l = ((unsigned long)f[0])<<16L;
144 			if (i == 2)
145 				l |= ((unsigned long)f[1]<<8L);
146 
147 			*(t++) = conv_bin2ascii(l>>18L);
148 			*(t++) = conv_bin2ascii(l>>12L);
149 			*(t++) = (i == 1)?'=':conv_bin2ascii(l>> 6L);
150 			*(t++) = '=';
151 		}
152 		ret += 4;
153 		f += 3;
154 	}
155 
156 	*t = '\0';
157 	return (ret);
158 }
159 
160 void
161 PEM_EncodeUpdate(PEM_ENCODE_CTX *ctx, unsigned char *out, int *outl,
162 	unsigned char *in, int inl)
163 {
164 	int i, j;
165 	unsigned int total = 0;
166 
167 	*outl = 0;
168 	if (inl == 0)
169 		return;
170 	if ((ctx->num+inl) < ctx->length) {
171 		(void) memcpy(&(ctx->enc_data[ctx->num]), in, inl);
172 		ctx->num += inl;
173 		return;
174 	}
175 	if (ctx->num != 0) {
176 		i = ctx->length-ctx->num;
177 		(void) memcpy(&(ctx->enc_data[ctx->num]), in, i);
178 		in += i;
179 		inl -= i;
180 		j = PEM_EncodeBlock(out, ctx->enc_data, ctx->length);
181 		ctx->num = 0;
182 		out += j;
183 		*(out++) = '\n';
184 		*out = '\0';
185 		total = j+1;
186 	}
187 
188 	while (inl >= ctx->length) {
189 		j = PEM_EncodeBlock(out, in, ctx->length);
190 		in += ctx->length;
191 		inl -= ctx->length;
192 		out += j;
193 		*(out++) = '\n';
194 		*out = '\0';
195 		total += j+1;
196 	}
197 
198 	if (inl != 0)
199 		(void) memcpy(&(ctx->enc_data[0]), in, inl);
200 	ctx->num = inl;
201 	*outl = total;
202 }
203 
204 void
205 PEM_EncodeFinal(PEM_ENCODE_CTX *ctx, unsigned char *out, int *outl)
206 {
207 	unsigned int ret = 0;
208 
209 	if (ctx->num != 0) {
210 		ret = PEM_EncodeBlock(out, ctx->enc_data, ctx->num);
211 		out[ret++] = '\n';
212 		out[ret] = '\0';
213 		ctx->num = 0;
214 	}
215 	*outl = ret;
216 }
217 
218 KMF_RETURN
219 Der2Pem(KMF_OBJECT_TYPE type, unsigned char *data,
220 	int len, unsigned char **out, int *outlen)
221 {
222 
223 
224 	int nlen, n, i, j, outl;
225 	unsigned char *buf = NULL, *p = NULL;
226 	PEM_ENCODE_CTX ctx;
227 	char *name = NULL;
228 
229 	if (data == NULL || len == 0 || out == NULL || outlen == NULL)
230 		return (KMF_ERR_BAD_PARAMETER);
231 
232 	if (type == KMF_CERT)
233 		name = PEM_STRING_X509;
234 	else if (type == KMF_CSR)
235 		name = PEM_STRING_X509_REQ;
236 	else if (type == KMF_CRL)
237 		name = PEM_STRING_X509_CRL;
238 	else
239 		return (KMF_ERR_BAD_OBJECT_TYPE);
240 
241 
242 	PEM_EncodeInit(&ctx);
243 	nlen = strlen(name);
244 
245 	buf = malloc(PEM_BUFSIZE*8);
246 	if (buf == NULL) {
247 		return (KMF_ERR_MEMORY);
248 	}
249 
250 	p = buf;
251 	(void) memcpy(p, "-----BEGIN ", 11);
252 	p += 11;
253 	(void) memcpy(p, name, nlen);
254 	p += nlen;
255 	(void) memcpy(p, "-----\n", 6);
256 	p += 6;
257 
258 	i = j = 0;
259 	while (len > 0) {
260 		n = (int)((len > (PEM_BUFSIZE*5))?(PEM_BUFSIZE*5):len);
261 		PEM_EncodeUpdate(&ctx, p, &outl, &(data[j]), n);
262 		i += outl;
263 		len -= n;
264 		j += n;
265 		p += outl;
266 	}
267 
268 	PEM_EncodeFinal(&ctx, p, &outl);
269 
270 	if (outl > 0)
271 		p += outl;
272 
273 	(void) memcpy(p, "-----END ", 9);
274 	p += 9;
275 	(void) memcpy(p, name, nlen);
276 	p += nlen;
277 	(void) memcpy(p, "-----\n", 6);
278 	p += 6;
279 
280 	*out = buf;
281 	*outlen = i+outl+nlen*2+11+6+9+6;
282 
283 	return (KMF_OK);
284 
285 }
286 
287 int
288 PEM_DecodeBlock(unsigned char *t, const unsigned char *f, int n)
289 {
290 	int i, ret = 0, a, b, c, d;
291 	unsigned long l;
292 
293 	/* trim white space from the start of the line. */
294 	while ((conv_ascii2bin(*f) == B64_WS) && (n > 0)) {
295 		f++;
296 		n--;
297 	}
298 
299 	/*
300 	 * strip off stuff at the end of the line
301 	 * ascii2bin values B64_WS, B64_EOLN, B64_EOLN and B64_EOF
302 	 */
303 	while ((n > 3) && (B64_NOT_BASE64(conv_ascii2bin(f[n-1]))))
304 		n--;
305 
306 	if (n%4 != 0) {
307 		return (-1);
308 	}
309 
310 	for (i = 0; i < n; i += 4) {
311 		a = conv_ascii2bin(*(f++));
312 		b = conv_ascii2bin(*(f++));
313 		c = conv_ascii2bin(*(f++));
314 		d = conv_ascii2bin(*(f++));
315 		if ((a & 0x80) || (b & 0x80) ||
316 			(c & 0x80) || (d & 0x80))
317 			return (-1);
318 		l = ((((unsigned long)a)<<18L)|
319 			(((unsigned long)b)<<12L)|
320 			(((unsigned long)c)<< 6L)|
321 			(((unsigned long)d)));
322 		*(t++) = (unsigned char)(l>>16L)&0xff;
323 		*(t++) = (unsigned char)(l>> 8L)&0xff;
324 		*(t++) = (unsigned char)(l)&0xff;
325 		ret += 3;
326 	}
327 	return (ret);
328 }
329 
330 void
331 PEM_DecodeInit(PEM_ENCODE_CTX *ctx)
332 {
333 	ctx->length = 30;
334 	ctx->num = 0;
335 	ctx->line_num = 0;
336 	ctx->expect_nl = 0;
337 }
338 
339 /*
340  * -1 for error
341  *  0 for last line
342  *  1 for full line
343  */
344 int
345 PEM_DecodeUpdate(PEM_ENCODE_CTX *ctx, unsigned char *out, int *outl,
346     unsigned char *in, int inl)
347 {
348 	int seof = -1, eof = 0, rv = -1, ret = 0;
349 	int i, v, tmp, n, ln, exp_nl;
350 	unsigned char *d;
351 
352 	n = ctx->num;
353 	d = ctx->enc_data;
354 	ln = ctx->line_num;
355 	exp_nl = ctx->expect_nl;
356 
357 	/* last line of input. */
358 	if ((inl == 0) || ((n == 0) && (conv_ascii2bin(in[0]) == B64_EOF))) {
359 		rv = 0;
360 		goto end;
361 	}
362 
363 	/* We parse the input data */
364 	for (i = 0; i < inl; i++) {
365 		/* If the current line is > 80 characters, scream alot */
366 		if (ln >= 80) {
367 			rv = -1;
368 			goto end;
369 		}
370 
371 		/* Get char and put it into the buffer */
372 		tmp = *(in++);
373 		v = conv_ascii2bin(tmp);
374 		/* only save the good data :-) */
375 		if (!B64_NOT_BASE64(v)) {
376 			d[n++] = tmp;
377 			ln++;
378 		} else if (v == B64_ERROR) {
379 			rv = -1;
380 			goto end;
381 		}
382 
383 		/*
384 		 * have we seen a '=' which is 'definitly' the last
385 		 * input line.  seof will point to the character that
386 		 * holds it. and eof will hold how many characters to
387 		 * chop off.
388 		 */
389 		if (tmp == '=') {
390 			if (seof == -1) seof = n;
391 			eof++;
392 		}
393 
394 		if (v == B64_CR) {
395 			ln = 0;
396 			if (exp_nl)
397 				continue;
398 		}
399 
400 		/* eoln */
401 		if (v == B64_EOLN) {
402 			ln = 0;
403 			if (exp_nl) {
404 				exp_nl = 0;
405 				continue;
406 			}
407 		}
408 		exp_nl = 0;
409 
410 		/*
411 		 * If we are at the end of input and it looks like a
412 		 * line, process it.
413 		 */
414 		if (((i+1) == inl) && (((n&3) == 0) || eof)) {
415 			v = B64_EOF;
416 			/*
417 			 * In case things were given us in really small
418 			 * records (so two '=' were given in separate
419 			 * updates), eof may contain the incorrect number
420 			 * of ending bytes to skip, so let's redo the count
421 			 */
422 			eof = 0;
423 			if (d[n-1] == '=') eof++;
424 			if (d[n-2] == '=') eof++;
425 			/* There will never be more than two '=' */
426 		}
427 
428 		if ((v == B64_EOF) || (n >= 64)) {
429 			/*
430 			 * This is needed to work correctly on 64 byte input
431 			 * lines.  We process the line and then need to
432 			 * accept the '\n'
433 			 */
434 			if ((v != B64_EOF) && (n >= 64))
435 				exp_nl = 1;
436 			if (n > 0) {
437 				v = PEM_DecodeBlock(out, d, n);
438 				if (v < 0) {
439 					rv = 0;
440 					goto end;
441 				}
442 				n = 0;
443 				ret += (v-eof);
444 			} else {
445 				eof = 1;
446 				v = 0;
447 			}
448 
449 			/*
450 			 * This is the case where we have had a short
451 			 * but valid input line
452 			 */
453 			if ((v < ctx->length) && eof) {
454 				rv = 0;
455 				goto end;
456 			} else
457 				ctx->length = v;
458 
459 			if (seof >= 0) {
460 				rv = 0;
461 				goto end;
462 			}
463 			out += v;
464 		}
465 	}
466 	rv = 1;
467 end:
468 	*outl = ret;
469 	ctx->num = n;
470 	ctx->line_num = ln;
471 	ctx->expect_nl = exp_nl;
472 	return (rv);
473 }
474 
475 int
476 PEM_DecodeFinal(PEM_ENCODE_CTX *ctx, unsigned char *out, int *outl)
477 {
478 	int i;
479 
480 	*outl = 0;
481 	if (ctx->num != 0) {
482 		i = PEM_DecodeBlock(out, ctx->enc_data, ctx->num);
483 		if (i < 0)
484 			return (-1);
485 		ctx->num = 0;
486 		*outl = i;
487 		return (1);
488 	} else
489 		return (1);
490 }
491 
492 static int
493 get_line(unsigned char *in, char *buf)
494 {
495 
496 	int i = 0;
497 	int len = 0;
498 
499 	while ((in[i] != '\n')) {
500 		buf[i] = in[i];
501 		i++;
502 		len++;
503 	}
504 
505 	return (len);
506 }
507 
508 KMF_RETURN
509 Pem2Der(unsigned char *in, int inlen,
510     unsigned char **out, int *outlen)
511 {
512 	int kmf_rv = 0;
513 	PEM_ENCODE_CTX ctx;
514 	int i, k, bl = 0;
515 	char buf[2048];
516 	char *nameB;
517 	unsigned char *dataB;
518 	int total = 0;
519 
520 	if (in == NULL || inlen == 0 || out == NULL)
521 		return (KMF_ERR_BAD_PARAMETER);
522 
523 	(void) memset(buf, 0, sizeof (buf));
524 
525 	for (;;) {
526 		/*
527 		 * get a line (ended at '\n'), which returns
528 		 * number of bytes in the line
529 		 */
530 		i = get_line(in, buf);
531 		if (i <= 0) {
532 			kmf_rv = KMF_ERR_ENCODING;
533 			goto err;
534 		}
535 
536 		while ((i >= 0) && (buf[i] <= ' ')) i--;
537 		buf[++i] = '\n';
538 		buf[++i] = '\0';
539 		total += i;
540 
541 		if (strncmp(buf, "-----BEGIN ", 11) == 0) {
542 			i = strlen(&(buf[11]));
543 			if (strncmp(&(buf[11+i-6]), "-----\n", 6) != 0) {
544 				continue;
545 			}
546 
547 			if ((nameB = malloc(i+9)) == NULL) {
548 				kmf_rv = KMF_ERR_MEMORY;
549 				goto err;
550 			}
551 
552 			(void) memcpy(nameB, &(buf[11]), i-6);
553 			nameB[i-6] = '\0';
554 			break;
555 		}
556 	}
557 
558 	bl = 0;
559 	if ((dataB = malloc(2048)) == NULL) {
560 		kmf_rv = KMF_ERR_MEMORY;
561 		goto err;
562 	}
563 
564 	dataB[0] = '\0';
565 
566 	for (;;) {
567 		(void) memset(buf, 0, 1024);
568 		i = get_line(in+total, buf);
569 
570 		if (i <= 0) break;
571 
572 		while ((i >= 0) && (buf[i] <= ' '))
573 			i--;
574 
575 		buf[++i] = '\n';
576 		buf[++i] = '\0';
577 		total += i;
578 
579 		if (buf[0] == '\n') break;
580 		if ((dataB = realloc(dataB, bl+i+9)) == NULL) {
581 			kmf_rv = KMF_ERR_MEMORY;
582 			goto err;
583 		}
584 
585 		if (strncmp(buf, "-----END ", 9) == 0) {
586 			break;
587 		}
588 
589 		(void) memcpy(&(dataB[bl]), buf, i);
590 		dataB[bl+i] = '\0';
591 		bl += i;
592 	}
593 
594 	i = strlen(nameB);
595 	if ((strncmp(buf, "-----END ", 9) != 0) ||
596 		(strncmp(nameB, &(buf[9]), i) != 0) ||
597 		(strncmp(&(buf[9+i]), "-----", 5) != 0)) {
598 		kmf_rv = KMF_ERR_ENCODING;
599 		goto err;
600 	}
601 
602 	PEM_DecodeInit(&ctx);
603 	i = PEM_DecodeUpdate(&ctx,
604 		(unsigned char *)dataB, &bl, (unsigned char *)dataB, bl);
605 
606 	if (i < 0) {
607 		kmf_rv = KMF_ERR_ENCODING;
608 		goto err;
609 	}
610 
611 	i = PEM_DecodeFinal(&ctx, (unsigned char *)&(dataB[bl]), &k);
612 	if (i < 0) {
613 		kmf_rv = KMF_ERR_ENCODING;
614 		goto err;
615 	}
616 	bl += k;
617 
618 	if (bl == 0) goto err;
619 	*out = (unsigned char *)dataB;
620 	*outlen = bl;
621 
622 err:
623 	free(nameB);
624 	if (kmf_rv != KMF_OK)
625 		free(dataB);
626 
627 	return (kmf_rv);
628 }
629