Add `gnutls/dtls.h' to the distribution.
[gnutls.git] / lib / x509_b64.c
blob272c00de37d004cb3692275f6fa1929a516d48e7
1 /*
2 * Copyright (C) 2000, 2001, 2003, 2004, 2005, 2008, 2010 Free Software
3 * Foundation, Inc.
5 * Author: Nikos Mavrogiannopoulos
7 * This file is part of GnuTLS.
9 * The GnuTLS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1 of
12 * the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22 * USA
26 /* Functions that relate to base64 encoding and decoding.
29 #include "gnutls_int.h"
30 #include "gnutls_errors.h"
31 #include <gnutls_datum.h>
32 #include <x509_b64.h>
34 static const uint8_t b64table[] =
35 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
37 static const uint8_t asciitable[128] = {
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, 0xff, 0xff, 0xff, 0xff, 0xff,
45 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
46 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
47 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
48 0xff, 0xf1, 0xff, 0xff, 0xff, 0x00, /* 0xf1 for '=' */
49 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
50 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
51 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
52 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
53 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
54 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
55 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
56 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
57 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
58 0x31, 0x32, 0x33, 0xff, 0xff, 0xff,
59 0xff, 0xff
62 inline static int
63 encode (char *result, const uint8_t * data, int left)
66 int data_len;
68 if (left > 3)
69 data_len = 3;
70 else
71 data_len = left;
73 switch (data_len)
75 case 3:
76 result[0] = b64table[(data[0] >> 2)];
77 result[1] =
78 b64table[(((((data[0] & 0x03) & 0xff) << 4) & 0xff) |
79 (data[1] >> 4))];
80 result[2] =
81 b64table[((((data[1] & 0x0f) << 2) & 0xff) | (data[2] >> 6))];
82 result[3] = b64table[(((data[2] << 2) & 0xff) >> 2)];
83 break;
84 case 2:
85 result[0] = b64table[(data[0] >> 2)];
86 result[1] =
87 b64table[(((((data[0] & 0x03) & 0xff) << 4) & 0xff) |
88 (data[1] >> 4))];
89 result[2] = b64table[(((data[1] << 4) & 0xff) >> 2)];
90 result[3] = '=';
91 break;
92 case 1:
93 result[0] = b64table[(data[0] >> 2)];
94 result[1] = b64table[(((((data[0] & 0x03) & 0xff) << 4) & 0xff))];
95 result[2] = '=';
96 result[3] = '=';
97 break;
98 default:
99 return -1;
102 return 4;
106 /* data must be 4 bytes
107 * result should be 3 bytes
109 #define TOASCII(c) (c < 127 ? asciitable[c] : 0xff)
110 inline static int
111 decode (uint8_t * result, const opaque * data)
113 uint8_t a1, a2;
114 int ret = 3;
116 a1 = TOASCII (data[0]);
117 a2 = TOASCII (data[1]);
118 if (a1 == 0xff || a2 == 0xff)
119 return -1;
120 result[0] = ((a1 << 2) & 0xff) | ((a2 >> 4) & 0xff);
122 a1 = a2;
123 a2 = TOASCII (data[2]);
124 if (a2 == 0xff)
125 return -1;
126 result[1] = ((a1 << 4) & 0xff) | ((a2 >> 2) & 0xff);
128 a1 = a2;
129 a2 = TOASCII (data[3]);
130 if (a2 == 0xff)
131 return -1;
132 result[2] = ((a1 << 6) & 0xff) | (a2 & 0xff);
134 if (data[2] == '=')
135 ret--;
137 if (data[3] == '=')
138 ret--;
139 return ret;
142 /* encodes data and puts the result into result (locally allocated)
143 * The result_size is the return value
146 _gnutls_base64_encode (const uint8_t * data, size_t data_size,
147 uint8_t ** result)
149 unsigned int i, j;
150 int ret, tmp;
151 char tmpres[4];
153 ret = B64SIZE (data_size);
155 (*result) = gnutls_malloc (ret + 1);
156 if ((*result) == NULL)
157 return GNUTLS_E_MEMORY_ERROR;
159 for (i = j = 0; i < data_size; i += 3, j += 4)
161 tmp = encode (tmpres, &data[i], data_size - i);
162 if (tmp == -1)
164 gnutls_free ((*result));
165 return GNUTLS_E_MEMORY_ERROR;
167 memcpy (&(*result)[j], tmpres, tmp);
169 (*result)[ret] = 0; /* null terminated */
171 return ret;
174 #define INCR(what, size) \
175 do { \
176 what+=size; \
177 if (what > ret) { \
178 gnutls_assert(); \
179 gnutls_free( (*result)); *result = NULL; \
180 return GNUTLS_E_INTERNAL_ERROR; \
182 } while(0)
184 /* encodes data and puts the result into result (locally allocated)
185 * The result_size (including the null terminator) is the return value.
188 _gnutls_fbase64_encode (const char *msg, const uint8_t * data,
189 int data_size, uint8_t ** result)
191 int i, ret, tmp, j;
192 char tmpres[4];
193 uint8_t *ptr;
194 uint8_t top[80];
195 uint8_t bottom[80];
196 int pos, bytes, top_len, bottom_len;
197 size_t msglen = strlen (msg);
199 if (msglen > 50)
201 gnutls_assert ();
202 return GNUTLS_E_BASE64_ENCODING_ERROR;
205 memset (bottom, 0, sizeof (bottom));
206 memset (top, 0, sizeof (top));
208 strcat (top, "-----BEGIN "); /* Flawfinder: ignore */
209 strcat (top, msg); /* Flawfinder: ignore */
210 strcat (top, "-----"); /* Flawfinder: ignore */
212 strcat (bottom, "\n-----END "); /* Flawfinder: ignore */
213 strcat (bottom, msg); /* Flawfinder: ignore */
214 strcat (bottom, "-----\n"); /* Flawfinder: ignore */
216 top_len = strlen (top);
217 bottom_len = strlen (bottom);
219 ret = B64FSIZE (msglen, data_size);
221 (*result) = gnutls_calloc (1, ret + 1);
222 if ((*result) == NULL)
224 gnutls_assert ();
225 return GNUTLS_E_MEMORY_ERROR;
228 bytes = pos = 0;
229 INCR (bytes, top_len);
230 pos = top_len;
232 strcpy (*result, top); /* Flawfinder: ignore */
234 for (i = j = 0; i < data_size; i += 3, j += 4)
237 tmp = encode (tmpres, &data[i], data_size - i);
238 if (tmp == -1)
240 gnutls_assert ();
241 gnutls_free ((*result));
242 *result = NULL;
243 return GNUTLS_E_BASE64_ENCODING_ERROR;
246 INCR (bytes, 4);
247 ptr = &(*result)[j + pos];
249 if ((j) % 64 == 0)
251 INCR (bytes, 1);
252 pos++;
253 *ptr++ = '\n';
255 *ptr++ = tmpres[0];
257 if ((j + 1) % 64 == 0)
259 INCR (bytes, 1);
260 pos++;
261 *ptr++ = '\n';
263 *ptr++ = tmpres[1];
265 if ((j + 2) % 64 == 0)
267 INCR (bytes, 1);
268 pos++;
269 *ptr++ = '\n';
271 *ptr++ = tmpres[2];
273 if ((j + 3) % 64 == 0)
275 INCR (bytes, 1);
276 pos++;
277 *ptr++ = '\n';
279 *ptr++ = tmpres[3];
282 INCR (bytes, bottom_len);
284 memcpy (&(*result)[bytes - bottom_len], bottom, bottom_len);
285 (*result)[bytes] = 0;
287 return ret + 1;
291 * gnutls_pem_base64_encode:
292 * @msg: is a message to be put in the header
293 * @data: contain the raw data
294 * @result: the place where base64 data will be copied
295 * @result_size: holds the size of the result
297 * This function will convert the given data to printable data, using
298 * the base64 encoding. This is the encoding used in PEM messages.
300 * The output string will be null terminated, although the size will
301 * not include the terminating null.
303 * Returns: On success %GNUTLS_E_SUCCESS (0) is returned,
304 * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned if the buffer given is
305 * not long enough, or 0 on success.
308 gnutls_pem_base64_encode (const char *msg, const gnutls_datum_t * data,
309 char *result, size_t * result_size)
311 opaque *ret;
312 int size;
314 size = _gnutls_fbase64_encode (msg, data->data, data->size, &ret);
315 if (size < 0)
316 return size;
318 if (result == NULL || *result_size < (unsigned) size)
320 gnutls_free (ret);
321 *result_size = size;
322 return GNUTLS_E_SHORT_MEMORY_BUFFER;
324 else
326 memcpy (result, ret, size);
327 gnutls_free (ret);
328 *result_size = size - 1;
331 return 0;
335 * gnutls_pem_base64_encode_alloc:
336 * @msg: is a message to be put in the encoded header
337 * @data: contains the raw data
338 * @result: will hold the newly allocated encoded data
340 * This function will convert the given data to printable data, using
341 * the base64 encoding. This is the encoding used in PEM messages.
342 * This function will allocate the required memory to hold the encoded
343 * data.
345 * You should use gnutls_free() to free the returned data.
347 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
348 * an error code is returned.
351 gnutls_pem_base64_encode_alloc (const char *msg,
352 const gnutls_datum_t * data,
353 gnutls_datum_t * result)
355 opaque *ret;
356 int size;
358 if (result == NULL)
359 return GNUTLS_E_INVALID_REQUEST;
361 size = _gnutls_fbase64_encode (msg, data->data, data->size, &ret);
362 if (size < 0)
363 return size;
365 result->data = ret;
366 result->size = size - 1;
367 return 0;
371 /* decodes data and puts the result into result (locally allocated)
372 * The result_size is the return value
375 _gnutls_base64_decode (const uint8_t * data, size_t data_size,
376 uint8_t ** result)
378 unsigned int i, j;
379 int ret, tmp, est;
380 uint8_t tmpres[3];
382 est = ((data_size * 3) / 4) + 1;
383 (*result) = gnutls_malloc (est);
384 if ((*result) == NULL)
385 return GNUTLS_E_MEMORY_ERROR;
387 ret = 0;
388 for (i = j = 0; i < data_size; i += 4, j += 3)
390 tmp = decode (tmpres, &data[i]);
391 if (tmp < 0)
393 gnutls_free (*result);
394 *result = NULL;
395 return tmp;
397 memcpy (&(*result)[j], tmpres, tmp);
398 ret += tmp;
400 return ret;
403 /* copies data to result but removes newlines and <CR>
404 * returns the size of the data copied.
406 inline static int
407 cpydata (const uint8_t * data, int data_size, uint8_t ** result)
409 int i, j;
411 (*result) = gnutls_malloc (data_size);
412 if (*result == NULL)
413 return GNUTLS_E_MEMORY_ERROR;
415 for (j = i = 0; i < data_size; i++)
417 if (data[i] == '\n' || data[i] == '\r' || data[i] == ' '
418 || data[i] == '\t')
419 continue;
420 (*result)[j] = data[i];
421 j++;
423 return j;
426 /* Searches the given string for ONE PEM encoded certificate, and
427 * stores it in the result.
429 * The result_size is the return value
431 #define ENDSTR "-----"
433 _gnutls_fbase64_decode (const char *header, const opaque * data,
434 size_t data_size, uint8_t ** result)
436 int ret;
437 static const char top[] = "-----BEGIN ";
438 static const char bottom[] = "-----END ";
439 uint8_t *rdata;
440 int rdata_size;
441 uint8_t *kdata;
442 int kdata_size;
443 char pem_header[128];
445 _gnutls_str_cpy (pem_header, sizeof (pem_header), top);
446 if (header != NULL)
447 _gnutls_str_cat (pem_header, sizeof (pem_header), header);
449 rdata = memmem (data, data_size, pem_header, strlen (pem_header));
451 if (rdata == NULL)
453 gnutls_assert ();
454 _gnutls_debug_log ("Could not find '%s'\n", pem_header);
455 return GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR;
458 data_size -= (unsigned long int) rdata - (unsigned long int) data;
460 if (data_size < 4 + strlen (bottom))
462 gnutls_assert ();
463 return GNUTLS_E_BASE64_DECODING_ERROR;
466 kdata = memmem (rdata + 1, data_size - 1, ENDSTR, sizeof (ENDSTR) - 1);
467 /* allow CR as well.
469 if (kdata == NULL)
471 gnutls_assert ();
472 _gnutls_debug_log ("Could not find '%s'\n", ENDSTR);
473 return GNUTLS_E_BASE64_DECODING_ERROR;
475 data_size -= strlen (ENDSTR);
476 data_size -= (unsigned long int) kdata - (unsigned long int) rdata;
478 rdata = kdata + strlen (ENDSTR);
480 /* position is now after the ---BEGIN--- headers */
482 kdata = memmem (rdata, data_size, bottom, strlen (bottom));
483 if (kdata == NULL)
485 gnutls_assert ();
486 return GNUTLS_E_BASE64_DECODING_ERROR;
489 /* position of kdata is before the ----END--- footer
491 rdata_size = (unsigned long int) kdata - (unsigned long int) rdata;
493 if (rdata_size < 4)
495 gnutls_assert ();
496 return GNUTLS_E_BASE64_DECODING_ERROR;
499 kdata_size = cpydata (rdata, rdata_size, &kdata);
501 if (kdata_size < 0)
503 gnutls_assert ();
504 return kdata_size;
507 if (kdata_size < 4)
509 gnutls_assert ();
510 gnutls_free (kdata);
511 return GNUTLS_E_BASE64_DECODING_ERROR;
514 if ((ret = _gnutls_base64_decode (kdata, kdata_size, result)) < 0)
516 gnutls_free (kdata);
517 gnutls_assert ();
518 return GNUTLS_E_BASE64_DECODING_ERROR;
520 gnutls_free (kdata);
522 return ret;
526 * gnutls_pem_base64_decode:
527 * @header: A null terminated string with the PEM header (eg. CERTIFICATE)
528 * @b64_data: contain the encoded data
529 * @result: the place where decoded data will be copied
530 * @result_size: holds the size of the result
532 * This function will decode the given encoded data. If the header
533 * given is non null this function will search for "-----BEGIN header"
534 * and decode only this part. Otherwise it will decode the first PEM
535 * packet found.
537 * Returns: On success %GNUTLS_E_SUCCESS (0) is returned,
538 * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned if the buffer given is
539 * not long enough, or 0 on success.
542 gnutls_pem_base64_decode (const char *header,
543 const gnutls_datum_t * b64_data,
544 unsigned char *result, size_t * result_size)
546 opaque *ret;
547 int size;
549 size =
550 _gnutls_fbase64_decode (header, b64_data->data, b64_data->size, &ret);
551 if (size < 0)
552 return size;
554 if (result == NULL || *result_size < (unsigned) size)
556 gnutls_free (ret);
557 *result_size = size;
558 return GNUTLS_E_SHORT_MEMORY_BUFFER;
560 else
562 memcpy (result, ret, size);
563 gnutls_free (ret);
564 *result_size = size;
567 return 0;
571 * gnutls_pem_base64_decode_alloc:
572 * @header: The PEM header (eg. CERTIFICATE)
573 * @b64_data: contains the encoded data
574 * @result: the place where decoded data lie
576 * This function will decode the given encoded data. The decoded data
577 * will be allocated, and stored into result. If the header given is
578 * non null this function will search for "-----BEGIN header" and
579 * decode only this part. Otherwise it will decode the first PEM
580 * packet found.
582 * You should use gnutls_free() to free the returned data.
584 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
585 * an error code is returned.
588 gnutls_pem_base64_decode_alloc (const char *header,
589 const gnutls_datum_t * b64_data,
590 gnutls_datum_t * result)
592 opaque *ret;
593 int size;
595 if (result == NULL)
596 return GNUTLS_E_INVALID_REQUEST;
598 size =
599 _gnutls_fbase64_decode (header, b64_data->data, b64_data->size, &ret);
600 if (size < 0)
601 return size;
603 result->data = ret;
604 result->size = size;
605 return 0;