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