Add.
[gnutls.git] / lib / x509_b64.c
blobc43329176476d79dd0f0c094452efe7904da48a7
1 /*
2 * Copyright (C) 2000, 2001, 2003, 2004, 2005 Free Software Foundation
4 * Author: Nikos Mavroyanopoulos
6 * This file is part of GNUTLS.
8 * The GNUTLS library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
21 * USA
25 /* Functions that relate to base64 encoding and decoding.
28 #include "gnutls_int.h"
29 #include "gnutls_errors.h"
30 #include <gnutls_datum.h>
31 #include <x509_b64.h>
33 static const uint8_t b64table[] =
34 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
36 static const uint8_t asciitable[128] = {
37 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
38 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
39 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
40 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
41 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
42 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
43 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
44 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
45 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
46 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
47 0xff, 0xf1, 0xff, 0xff, 0xff, 0x00, /* 0xf1 for '=' */
48 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
49 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
50 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
51 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
52 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
53 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
54 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
55 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
56 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
57 0x31, 0x32, 0x33, 0xff, 0xff, 0xff,
58 0xff, 0xff
61 inline static int
62 encode (char *result, const uint8_t * data, int left)
65 int data_len;
67 if (left > 3)
68 data_len = 3;
69 else
70 data_len = left;
72 switch (data_len)
74 case 3:
75 result[0] = b64table[(data[0] >> 2)];
76 result[1] =
77 b64table[(((((data[0] & 0x03) & 0xff) << 4) & 0xff) |
78 (data[1] >> 4))];
79 result[2] =
80 b64table[((((data[1] & 0x0f) << 2) & 0xff) | (data[2] >> 6))];
81 result[3] = b64table[(((data[2] << 2) & 0xff) >> 2)];
82 break;
83 case 2:
84 result[0] = b64table[(data[0] >> 2)];
85 result[1] =
86 b64table[(((((data[0] & 0x03) & 0xff) << 4) & 0xff) |
87 (data[1] >> 4))];
88 result[2] = b64table[(((data[1] << 4) & 0xff) >> 2)];
89 result[3] = '=';
90 break;
91 case 1:
92 result[0] = b64table[(data[0] >> 2)];
93 result[1] = b64table[(((((data[0] & 0x03) & 0xff) << 4) & 0xff))];
94 result[2] = '=';
95 result[3] = '=';
96 break;
97 default:
98 return -1;
101 return 4;
105 /* data must be 4 bytes
106 * result should be 3 bytes
108 #define TOASCII(c) (c < 127 ? asciitable[c] : 0xff)
109 inline static int
110 decode (uint8_t * result, const opaque * data)
112 uint8_t a1, a2;
113 int ret = 3;
115 a1 = TOASCII (data[0]);
116 a2 = TOASCII (data[1]);
117 if (a1 == 0xff || a2 == 0xff)
118 return -1;
119 result[0] = ((a1 << 2) & 0xff) | ((a2 >> 4) & 0xff);
121 a1 = a2;
122 a2 = TOASCII (data[2]);
123 if (a2 == 0xff)
124 return -1;
125 result[1] = ((a1 << 4) & 0xff) | ((a2 >> 2) & 0xff);
127 a1 = a2;
128 a2 = TOASCII (data[3]);
129 if (a2 == 0xff)
130 return -1;
131 result[2] = ((a1 << 6) & 0xff) | (a2 & 0xff);
133 if (data[2] == '=')
134 ret--;
136 if (data[3] == '=')
137 ret--;
138 return ret;
141 /* encodes data and puts the result into result (locally allocated)
142 * The result_size is the return value
145 _gnutls_base64_encode (const uint8_t * data, size_t data_size,
146 uint8_t ** result)
148 unsigned int i, j;
149 int ret, tmp;
150 char tmpres[4];
152 ret = B64SIZE (data_size);
154 (*result) = gnutls_malloc (ret + 1);
155 if ((*result) == NULL)
156 return GNUTLS_E_MEMORY_ERROR;
158 for (i = j = 0; i < data_size; i += 3, j += 4)
160 tmp = encode (tmpres, &data[i], data_size - i);
161 if (tmp == -1)
163 gnutls_free ((*result));
164 return GNUTLS_E_MEMORY_ERROR;
166 memcpy (&(*result)[j], tmpres, tmp);
168 (*result)[ret] = 0; /* null terminated */
170 return ret;
173 #define INCR(what, size) \
174 do { \
175 what+=size; \
176 if (what > ret) { \
177 gnutls_assert(); \
178 gnutls_free( (*result)); *result = NULL; \
179 return GNUTLS_E_INTERNAL_ERROR; \
181 } while(0)
183 /* encodes data and puts the result into result (locally allocated)
184 * The result_size (including the null terminator) is the return value.
187 _gnutls_fbase64_encode (const char *msg, const uint8_t * data,
188 int data_size, uint8_t ** result)
190 int i, ret, tmp, j;
191 char tmpres[4];
192 uint8_t *ptr;
193 uint8_t top[80];
194 uint8_t bottom[80];
195 int pos, bytes, top_len, bottom_len;
196 size_t msglen = strlen (msg);
198 if (msglen > 50)
200 gnutls_assert ();
201 return GNUTLS_E_BASE64_ENCODING_ERROR;
204 memset (bottom, 0, sizeof (bottom));
205 memset (top, 0, sizeof (top));
207 strcat (top, "-----BEGIN "); /* Flawfinder: ignore */
208 strcat (top, msg); /* Flawfinder: ignore */
209 strcat (top, "-----"); /* Flawfinder: ignore */
211 strcat (bottom, "\n-----END "); /* Flawfinder: ignore */
212 strcat (bottom, msg); /* Flawfinder: ignore */
213 strcat (bottom, "-----\n"); /* Flawfinder: ignore */
215 top_len = strlen (top);
216 bottom_len = strlen (bottom);
218 ret = B64FSIZE (msglen, data_size);
220 (*result) = gnutls_calloc (1, ret + 1);
221 if ((*result) == NULL)
223 gnutls_assert ();
224 return GNUTLS_E_MEMORY_ERROR;
227 bytes = pos = 0;
228 INCR (bytes, top_len);
229 pos = top_len;
231 strcpy (*result, top); /* Flawfinder: ignore */
233 for (i = j = 0; i < data_size; i += 3, j += 4)
236 tmp = encode (tmpres, &data[i], data_size - i);
237 if (tmp == -1)
239 gnutls_assert ();
240 gnutls_free ((*result));
241 *result = NULL;
242 return GNUTLS_E_BASE64_ENCODING_ERROR;
245 INCR (bytes, 4);
246 ptr = &(*result)[j + pos];
248 if ((j) % 64 == 0)
250 INCR (bytes, 1);
251 pos++;
252 *ptr++ = '\n';
254 *ptr++ = tmpres[0];
256 if ((j + 1) % 64 == 0)
258 INCR (bytes, 1);
259 pos++;
260 *ptr++ = '\n';
262 *ptr++ = tmpres[1];
264 if ((j + 2) % 64 == 0)
266 INCR (bytes, 1);
267 pos++;
268 *ptr++ = '\n';
270 *ptr++ = tmpres[2];
272 if ((j + 3) % 64 == 0)
274 INCR (bytes, 1);
275 pos++;
276 *ptr++ = '\n';
278 *ptr++ = tmpres[3];
281 INCR (bytes, bottom_len);
283 memcpy (&(*result)[bytes - bottom_len], bottom, bottom_len);
284 (*result)[bytes] = 0;
286 return ret + 1;
290 * gnutls_pem_base64_encode - This function will convert raw data to Base64 encoded
291 * @msg: is a message to be put in the header
292 * @data: contain the raw data
293 * @result: the place where base64 data will be copied
294 * @result_size: holds the size of the result
296 * This function will convert the given data to printable data, using the base64
297 * encoding. This is the encoding used in PEM messages. If the provided
298 * buffer is not long enough GNUTLS_E_SHORT_MEMORY_BUFFER is returned.
300 * The output string will be null terminated, although the size will not include
301 * the terminating null.
305 gnutls_pem_base64_encode (const char *msg, const gnutls_datum_t * data,
306 char *result, size_t * result_size)
308 opaque *ret;
309 int size;
311 size = _gnutls_fbase64_encode (msg, data->data, data->size, &ret);
312 if (size < 0)
313 return size;
315 if (result == NULL || *result_size < (unsigned) size)
317 gnutls_free (ret);
318 *result_size = size;
319 return GNUTLS_E_SHORT_MEMORY_BUFFER;
321 else
323 memcpy (result, ret, size);
324 gnutls_free (ret);
325 *result_size = size - 1;
328 return 0;
332 * gnutls_pem_base64_encode_alloc - This function will convert raw data to Base64 encoded
333 * @msg: is a message to be put in the encoded header
334 * @data: contains the raw data
335 * @result: will hold the newly allocated encoded data
337 * This function will convert the given data to printable data, using the base64
338 * encoding. This is the encoding used in PEM messages. This function will
339 * allocate the required memory to hold the encoded data.
341 * You should use gnutls_free() to free the returned data.
345 gnutls_pem_base64_encode_alloc (const char *msg,
346 const gnutls_datum_t * data,
347 gnutls_datum_t * result)
349 opaque *ret;
350 int size;
352 if (result == NULL)
353 return GNUTLS_E_INVALID_REQUEST;
355 size = _gnutls_fbase64_encode (msg, data->data, data->size, &ret);
356 if (size < 0)
357 return size;
359 result->data = ret;
360 result->size = size - 1;
361 return 0;
365 /* decodes data and puts the result into result (locally allocated)
366 * The result_size is the return value
369 _gnutls_base64_decode (const uint8_t * data, size_t data_size,
370 uint8_t ** result)
372 unsigned int i, j;
373 int ret, tmp, est;
374 uint8_t tmpres[3];
376 est = ((data_size * 3) / 4) + 1;
377 (*result) = gnutls_malloc (est);
378 if ((*result) == NULL)
379 return GNUTLS_E_MEMORY_ERROR;
381 ret = 0;
382 for (i = j = 0; i < data_size; i += 4, j += 3)
384 tmp = decode (tmpres, &data[i]);
385 if (tmp < 0)
387 gnutls_free (*result);
388 *result = NULL;
389 return tmp;
391 memcpy (&(*result)[j], tmpres, tmp);
392 ret += tmp;
394 return ret;
397 /* copies data to result but removes newlines and <CR>
398 * returns the size of the data copied.
400 inline static int
401 cpydata (const uint8_t * data, int data_size, uint8_t ** result)
403 int i, j;
405 (*result) = gnutls_malloc (data_size);
406 if (*result == NULL)
407 return GNUTLS_E_MEMORY_ERROR;
409 for (j = i = 0; i < data_size; i++)
411 if (data[i] == '\n' || data[i] == '\r')
412 continue;
413 (*result)[j] = data[i];
414 j++;
416 return j;
419 /* Searches the given string for ONE PEM encoded certificate, and
420 * stores it in the result.
422 * The result_size is the return value
424 #define ENDSTR "-----\n"
425 #define ENDSTR2 "-----\r"
427 _gnutls_fbase64_decode (const char *header, const opaque * data,
428 size_t data_size, uint8_t ** result)
430 int ret;
431 static const char top[] = "-----BEGIN ";
432 static const char bottom[] = "\n-----END ";
433 uint8_t *rdata;
434 int rdata_size;
435 uint8_t *kdata;
436 int kdata_size;
437 char pem_header[128];
439 _gnutls_str_cpy (pem_header, sizeof (pem_header), top);
440 if (header != NULL)
441 _gnutls_str_cat (pem_header, sizeof (pem_header), header);
443 rdata = memmem (data, data_size, pem_header, strlen (pem_header));
445 if (rdata == NULL)
447 gnutls_assert ();
448 _gnutls_debug_log ("Could not find '%s'\n", pem_header);
449 return GNUTLS_E_BASE64_DECODING_ERROR;
452 data_size -= (unsigned long int) rdata - (unsigned long int) data;
454 if (data_size < 4 + strlen (bottom))
456 gnutls_assert ();
457 return GNUTLS_E_BASE64_DECODING_ERROR;
460 kdata = memmem (rdata, data_size, ENDSTR, sizeof (ENDSTR) - 1);
461 /* allow CR as well.
463 if (kdata == NULL)
464 kdata = memmem (rdata, data_size, ENDSTR2, sizeof (ENDSTR2) - 1);
466 if (kdata == NULL)
468 gnutls_assert ();
469 _gnutls_x509_log ("Could not find '%s'\n", ENDSTR);
470 return GNUTLS_E_BASE64_DECODING_ERROR;
472 data_size -= strlen (ENDSTR);
473 data_size -= (unsigned long int) kdata - (unsigned long int) rdata;
475 rdata = kdata + strlen (ENDSTR);
477 /* position is now after the ---BEGIN--- headers */
479 kdata = memmem (rdata, data_size, bottom, strlen (bottom));
480 if (kdata == NULL)
482 gnutls_assert ();
483 return GNUTLS_E_BASE64_DECODING_ERROR;
486 /* position of kdata is before the ----END--- footer
488 rdata_size = (unsigned long int) kdata - (unsigned long int) rdata;
490 if (rdata_size < 4)
492 gnutls_assert ();
493 return GNUTLS_E_BASE64_DECODING_ERROR;
496 kdata_size = cpydata (rdata, rdata_size, &kdata);
498 if (kdata_size < 0)
500 gnutls_assert ();
501 return kdata_size;
504 if (kdata_size < 4)
506 gnutls_assert ();
507 gnutls_free (kdata);
508 return GNUTLS_E_BASE64_DECODING_ERROR;
511 if ((ret = _gnutls_base64_decode (kdata, kdata_size, result)) < 0)
513 gnutls_free (kdata);
514 gnutls_assert ();
515 return GNUTLS_E_BASE64_DECODING_ERROR;
517 gnutls_free (kdata);
519 return ret;
523 * gnutls_pem_base64_decode - This function will decode base64 encoded data
524 * @header: A null terminated string with the PEM header (eg. CERTIFICATE)
525 * @b64_data: contain the encoded data
526 * @result: the place where decoded data will be copied
527 * @result_size: holds the size of the result
529 * This function will decode the given encoded data. If the header given
530 * is non null this function will search for "-----BEGIN header" and decode
531 * only this part. Otherwise it will decode the first PEM packet found.
533 * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the buffer given is not long enough,
534 * or 0 on success.
537 gnutls_pem_base64_decode (const char *header,
538 const gnutls_datum_t * b64_data,
539 unsigned char *result, size_t * result_size)
541 opaque *ret;
542 int size;
544 size =
545 _gnutls_fbase64_decode (header, b64_data->data, b64_data->size, &ret);
546 if (size < 0)
547 return size;
549 if (result == NULL || *result_size < (unsigned) size)
551 gnutls_free (ret);
552 *result_size = size;
553 return GNUTLS_E_SHORT_MEMORY_BUFFER;
555 else
557 memcpy (result, ret, size);
558 gnutls_free (ret);
559 *result_size = size;
562 return 0;
566 * gnutls_pem_base64_decode_alloc - This function will decode base64 encoded data
567 * @header: The PEM header (eg. CERTIFICATE)
568 * @b64_data: contains the encoded data
569 * @result: the place where decoded data lie
571 * This function will decode the given encoded data. The decoded data
572 * will be allocated, and stored into result.
573 * If the header given is non null this function will search for
574 * "-----BEGIN header" and decode only this part. Otherwise it will decode the
575 * first PEM packet found.
577 * You should use gnutls_free() to free the returned data.
581 gnutls_pem_base64_decode_alloc (const char *header,
582 const gnutls_datum_t * b64_data,
583 gnutls_datum_t * result)
585 opaque *ret;
586 int size;
588 if (result == NULL)
589 return GNUTLS_E_INVALID_REQUEST;
591 size =
592 _gnutls_fbase64_decode (header, b64_data->data, b64_data->size, &ret);
593 if (size < 0)
594 return size;
596 result->data = ret;
597 result->size = size;
598 return 0;