Home | History | Annotate | Download | only in common
      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 (at) cryptsoft.com)
     10  * All rights reserved.
     11  *
     12  * This package is an SSL implementation written
     13  * by Eric Young (eay (at) 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 (at) 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 (at) 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 (at) 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