documented fix
[gnutls.git] / lib / x509_b64.c
blob8b43011ad09983925740e2f595ab760f38ce0a1a
1 /*
2 * Copyright (C) 2000-2012 Free Software Foundation, Inc.
4 * Author: Nikos Mavrogiannopoulos
6 * This file is part of GnuTLS.
8 * The GnuTLS 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 3 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 License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>
23 /* Functions that relate to base64 encoding and decoding.
26 #include "gnutls_int.h"
27 #include "gnutls_errors.h"
28 #include <gnutls_datum.h>
29 #include <x509_b64.h>
30 #include <base64.h>
32 #define INCR(what, size, max_len) \
33 do { \
34 what+=size; \
35 if (what > max_len) { \
36 gnutls_assert(); \
37 gnutls_free( result->data); result->data = NULL; \
38 return GNUTLS_E_INTERNAL_ERROR; \
39 } \
40 } while(0)
42 /* encodes data and puts the result into result (locally allocated)
43 * The result_size (including the null terminator) is the return value.
45 int
46 _gnutls_fbase64_encode (const char *msg, const uint8_t * data,
47 size_t data_size, gnutls_datum_t * result)
49 int tmp;
50 unsigned int i;
51 char tmpres[66];
52 uint8_t *ptr;
53 char top[80];
54 char bottom[80];
55 size_t size, max, bytes;
56 int pos, top_len, bottom_len;
58 if (msg == NULL || strlen(msg) > 50)
60 gnutls_assert ();
61 return GNUTLS_E_BASE64_ENCODING_ERROR;
64 _gnutls_str_cpy (top, sizeof(top), "-----BEGIN ");
65 _gnutls_str_cat (top, sizeof(top), msg);
66 _gnutls_str_cat (top, sizeof(top), "-----\n");
68 _gnutls_str_cpy (bottom, sizeof(bottom), "-----END ");
69 _gnutls_str_cat (bottom, sizeof(bottom), msg);
70 _gnutls_str_cat (bottom, sizeof(bottom), "-----\n");
72 top_len = strlen (top);
73 bottom_len = strlen (bottom);
75 max = B64FSIZE (top_len+bottom_len, data_size);
77 result->data = gnutls_malloc (max + 1);
78 if (result->data == NULL)
80 gnutls_assert ();
81 return GNUTLS_E_MEMORY_ERROR;
84 bytes = pos = 0;
85 INCR (bytes, top_len, max);
86 pos = top_len;
88 memcpy (result->data, top, top_len);
90 for (i = 0; i < data_size; i += 48)
92 if (data_size - i < 48)
93 tmp = data_size - i;
94 else
95 tmp = 48;
97 base64_encode ((void*)&data[i], tmp, tmpres, sizeof(tmpres));
98 size = strlen(tmpres);
100 INCR (bytes, size+1, max);
101 ptr = &result->data[pos];
103 memcpy(ptr, tmpres, size);
104 ptr += size;
105 *ptr++ = '\n';
107 pos += size + 1;
110 INCR (bytes, bottom_len, max);
112 memcpy (&result->data[bytes - bottom_len], bottom, bottom_len);
113 result->data[bytes] = 0;
114 result->size = bytes;
116 return max + 1;
120 * gnutls_pem_base64_encode:
121 * @msg: is a message to be put in the header
122 * @data: contain the raw data
123 * @result: the place where base64 data will be copied
124 * @result_size: holds the size of the result
126 * This function will convert the given data to printable data, using
127 * the base64 encoding. This is the encoding used in PEM messages.
129 * The output string will be null terminated, although the size will
130 * not include the terminating null.
132 * Returns: On success %GNUTLS_E_SUCCESS (0) is returned,
133 * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned if the buffer given is
134 * not long enough, or 0 on success.
137 gnutls_pem_base64_encode (const char *msg, const gnutls_datum_t * data,
138 char *result, size_t * result_size)
140 gnutls_datum_t res;
141 int ret;
143 ret = _gnutls_fbase64_encode (msg, data->data, data->size, &res);
144 if (ret < 0)
145 return ret;
147 if (result == NULL || *result_size < (unsigned) res.size)
149 gnutls_free (res.data);
150 *result_size = res.size + 1;
151 return GNUTLS_E_SHORT_MEMORY_BUFFER;
153 else
155 memcpy (result, res.data, res.size);
156 gnutls_free (res.data);
157 *result_size = res.size;
160 return 0;
164 * gnutls_pem_base64_encode_alloc:
165 * @msg: is a message to be put in the encoded header
166 * @data: contains the raw data
167 * @result: will hold the newly allocated encoded data
169 * This function will convert the given data to printable data, using
170 * the base64 encoding. This is the encoding used in PEM messages.
171 * This function will allocate the required memory to hold the encoded
172 * data.
174 * You should use gnutls_free() to free the returned data.
176 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
177 * an error code is returned.
180 gnutls_pem_base64_encode_alloc (const char *msg,
181 const gnutls_datum_t * data,
182 gnutls_datum_t * result)
184 int ret;
186 if (result == NULL)
187 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
189 ret = _gnutls_fbase64_encode (msg, data->data, data->size, result);
190 if (ret < 0)
191 return gnutls_assert_val(ret);
193 return 0;
196 /* copies data to result but removes newlines and <CR>
197 * returns the size of the data copied.
199 inline static int
200 cpydata (const uint8_t * data, int data_size, gnutls_datum_t *result)
202 int i, j;
204 result->data = gnutls_malloc (data_size);
205 if (result->data == NULL)
206 return GNUTLS_E_MEMORY_ERROR;
208 for (j = i = 0; i < data_size; i++)
210 if (data[i] == '\n' || data[i] == '\r' || data[i] == ' '
211 || data[i] == '\t')
212 continue;
213 else if (data[i] == '-') break;
214 result->data[j] = data[i];
215 j++;
218 result->size = j;
219 result->data[j] = 0;
220 return j;
223 /* decodes data and puts the result into result (locally allocated)
224 * The result_size is the return value
227 _gnutls_base64_decode (const uint8_t * data, size_t data_size,
228 gnutls_datum_t * result)
230 unsigned int i;
231 int pos, tmp, est, ret;
232 uint8_t tmpres[48];
233 size_t tmpres_size, decode_size;
234 gnutls_datum_t pdata;
236 ret = cpydata(data, data_size, &pdata);
237 if (ret < 0)
239 gnutls_assert();
240 return ret;
243 est = ((data_size * 3) / 4) + 1;
245 result->data = gnutls_malloc (est);
246 if (result->data == NULL)
247 return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
249 pos = 0;
250 for (i = 0; i < pdata.size; i += 64)
252 if (pdata.size - i < 64)
253 decode_size = pdata.size - i;
254 else
255 decode_size = 64;
257 tmpres_size = sizeof(tmpres);
258 tmp = base64_decode ((void*)&pdata.data[i], decode_size, (void*)tmpres, &tmpres_size);
259 if (tmp == 0)
261 gnutls_assert();
262 gnutls_free (result->data);
263 result->data = NULL;
264 ret = GNUTLS_E_PARSING_ERROR;
265 goto cleanup;
267 memcpy (&result->data[pos], tmpres, tmpres_size);
268 pos += tmpres_size;
271 result->size = pos;
273 ret = pos;
275 cleanup:
276 gnutls_free (pdata.data);
277 return ret;
281 /* Searches the given string for ONE PEM encoded certificate, and
282 * stores it in the result.
284 * The result_size is the return value
286 #define ENDSTR "-----"
288 _gnutls_fbase64_decode (const char *header, const uint8_t * data,
289 size_t data_size, gnutls_datum_t* result)
291 int ret;
292 static const char top[] = "-----BEGIN ";
293 static const char bottom[] = "-----END ";
294 uint8_t *rdata, *kdata;
295 int rdata_size;
296 char pem_header[128];
298 _gnutls_str_cpy (pem_header, sizeof (pem_header), top);
299 if (header != NULL)
300 _gnutls_str_cat (pem_header, sizeof (pem_header), header);
302 rdata = memmem (data, data_size, pem_header, strlen (pem_header));
304 if (rdata == NULL)
306 gnutls_assert ();
307 _gnutls_debug_log ("Could not find '%s'\n", pem_header);
308 return GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR;
311 data_size -= (unsigned long int) rdata - (unsigned long int) data;
313 if (data_size < 4 + strlen (bottom))
315 gnutls_assert ();
316 return GNUTLS_E_BASE64_DECODING_ERROR;
319 kdata = memmem (rdata + 1, data_size - 1, ENDSTR, sizeof (ENDSTR) - 1);
320 /* allow CR as well.
322 if (kdata == NULL)
324 gnutls_assert ();
325 _gnutls_debug_log ("Could not find '%s'\n", ENDSTR);
326 return GNUTLS_E_BASE64_DECODING_ERROR;
328 data_size -= strlen (ENDSTR);
329 data_size -= (unsigned long int) kdata - (unsigned long int) rdata;
331 rdata = kdata + strlen (ENDSTR);
333 /* position is now after the ---BEGIN--- headers */
335 kdata = memmem (rdata, data_size, bottom, strlen (bottom));
336 if (kdata == NULL)
338 gnutls_assert ();
339 return GNUTLS_E_BASE64_DECODING_ERROR;
342 /* position of kdata is before the ----END--- footer
344 rdata_size = (unsigned long int) kdata - (unsigned long int) rdata;
346 if (rdata_size < 4)
348 gnutls_assert ();
349 return GNUTLS_E_BASE64_DECODING_ERROR;
352 if ((ret = _gnutls_base64_decode (rdata, rdata_size, result)) < 0)
354 gnutls_assert ();
355 return GNUTLS_E_BASE64_DECODING_ERROR;
358 return ret;
362 * gnutls_pem_base64_decode:
363 * @header: A null terminated string with the PEM header (eg. CERTIFICATE)
364 * @b64_data: contain the encoded data
365 * @result: the place where decoded data will be copied
366 * @result_size: holds the size of the result
368 * This function will decode the given encoded data. If the header
369 * given is non null this function will search for "-----BEGIN header"
370 * and decode only this part. Otherwise it will decode the first PEM
371 * packet found.
373 * Returns: On success %GNUTLS_E_SUCCESS (0) is returned,
374 * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned if the buffer given is
375 * not long enough, or 0 on success.
378 gnutls_pem_base64_decode (const char *header,
379 const gnutls_datum_t * b64_data,
380 unsigned char *result, size_t * result_size)
382 gnutls_datum_t res;
383 int ret;
385 ret =
386 _gnutls_fbase64_decode (header, b64_data->data, b64_data->size, &res);
387 if (ret < 0)
388 return gnutls_assert_val(ret);
390 if (result == NULL || *result_size < (unsigned) res.size)
392 gnutls_free (res.data);
393 *result_size = res.size;
394 return GNUTLS_E_SHORT_MEMORY_BUFFER;
396 else
398 memcpy (result, res.data, res.size);
399 gnutls_free (res.data);
400 *result_size = res.size;
403 return 0;
407 * gnutls_pem_base64_decode_alloc:
408 * @header: The PEM header (eg. CERTIFICATE)
409 * @b64_data: contains the encoded data
410 * @result: the place where decoded data lie
412 * This function will decode the given encoded data. The decoded data
413 * will be allocated, and stored into result. If the header given is
414 * non null this function will search for "-----BEGIN header" and
415 * decode only this part. Otherwise it will decode the first PEM
416 * packet found.
418 * You should use gnutls_free() to free the returned data.
420 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
421 * an error code is returned.
424 gnutls_pem_base64_decode_alloc (const char *header,
425 const gnutls_datum_t * b64_data,
426 gnutls_datum_t * result)
428 int ret;
430 if (result == NULL)
431 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
433 ret =
434 _gnutls_fbase64_decode (header, b64_data->data, b64_data->size, result);
435 if (ret < 0)
436 return gnutls_assert_val(ret);
438 return 0;