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