1/*
2 * Copyright (C) 2000-2012 Free Software Foundation, Inc.
3 *
4 * Author: Nikos Mavrogiannopoulos
5 *
6 * This file is part of GnuTLS.
7 *
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 2.1 of
11 * the License, or (at your option) any later version.
12 *
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.
17 *
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/>
20 *
21 */
22
23/* Functions that relate to base64 encoding and decoding.
24 */
25
26#include "gnutls_int.h"
27#include "gnutls_errors.h"
28#include <gnutls_datum.h>
29#include <x509_b64.h>
30#include <base64.h>
31
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)
41
42/* encodes data and puts the result into result (locally allocated)
43 * The result_size (including the null terminator) is the return value.
44 */
45int
46_gnutls_fbase64_encode (const char *msg, const uint8_t * data,
47 size_t data_size, gnutls_datum_t * result)
48{
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;
57
58 if (msg == NULL || strlen(msg) > 50)
59 {
60 gnutls_assert ();
61 return GNUTLS_E_BASE64_ENCODING_ERROR;
62 }
63
64 _gnutls_str_cpy (top, sizeof(top), "-----BEGIN ");
65 _gnutls_str_cat (top, sizeof(top), msg);
66 _gnutls_str_cat (top, sizeof(top), "-----\n");
67
68 _gnutls_str_cpy (bottom, sizeof(bottom), "-----END ");
69 _gnutls_str_cat (bottom, sizeof(bottom), msg);
70 _gnutls_str_cat (bottom, sizeof(bottom), "-----\n");
71
72 top_len = strlen (top);
73 bottom_len = strlen (bottom);
74
75 max = B64FSIZE (top_len+bottom_len, data_size);
76
77 result->data = gnutls_malloc (max + 1);
78 if (result->data == NULL)
79 {
80 gnutls_assert ();
81 return GNUTLS_E_MEMORY_ERROR;
82 }
83
84 bytes = pos = 0;
85 INCR (bytes, top_len, max);
86 pos = top_len;
87
88 memcpy (result->data, top, top_len);
89
90 for (i = 0; i < data_size; i += 48)
91 {
92 if (data_size - i < 48)
93 tmp = data_size - i;
94 else
95 tmp = 48;
96
97 base64_encode ((void*)&data[i], tmp, tmpres, sizeof(tmpres));
98 size = strlen(tmpres);
99
100 INCR (bytes, size+1, max);
101 ptr = &result->data[pos];
102
103 memcpy(ptr, tmpres, size);
104 ptr += size;
105 *ptr++ = '\n';
106
107 pos += size + 1;
108 }
109
110 INCR (bytes, bottom_len, max);
111
112 memcpy (&result->data[bytes - bottom_len], bottom, bottom_len);
113 result->data[bytes] = 0;
114 result->size = bytes;
115
116 return max + 1;
117}
118
119/**
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
125 *
126 * This function will convert the given data to printable data, using
127 * the base64 encoding. This is the encoding used in PEM messages.
128 *
129 * The output string will be null terminated, although the size will
130 * not include the terminating null.
131 *
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.
135 **/
136int
137gnutls_pem_base64_encode (const char *msg, const gnutls_datum_t * data,
138 char *result, size_t * result_size)
139{
140 gnutls_datum_t res;
141 int ret;
142
143 ret = _gnutls_fbase64_encode (msg, data->data, data->size, &res);
144 if (ret < 0)
145 return ret;
146
147 if (result == NULL || *result_size < (unsigned) res.size)
148 {
149 gnutls_free (res.data);
150 *result_size = res.size + 1;
151 return GNUTLS_E_SHORT_MEMORY_BUFFER;
152 }
153 else
154 {
155 memcpy (result, res.data, res.size);
156 gnutls_free (res.data);
157 *result_size = res.size;
158 }
159
160 return 0;
161}
162
163/**
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
168 *
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.
173 *
174 * You should use gnutls_free() to free the returned data.
175 *
176 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
177 * an error code is returned.
178 **/
179int
180gnutls_pem_base64_encode_alloc (const char *msg,
181 const gnutls_datum_t * data,
182 gnutls_datum_t * result)
183{
184 int ret;
185
186 if (result == NULL)
187 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
188
189 ret = _gnutls_fbase64_encode (msg, data->data, data->size, result);
190 if (ret < 0)
191 return gnutls_assert_val(ret);
192
193 return 0;
194}
195
196/* copies data to result but removes newlines and <CR>
197 * returns the size of the data copied.
198 */
199inline static int
200cpydata (const uint8_t * data, int data_size, gnutls_datum_t *result)
201{
202 int i, j;
203
204 result->data = gnutls_malloc (data_size+1);
205 if (result->data == NULL)
206 return GNUTLS_E_MEMORY_ERROR;
207
208 for (j = i = 0; i < data_size; i++)
209 {
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++;
216 }
217
218 result->size = j;
219 result->data[j] = 0;
220 return j;
221}
222
223/* decodes data and puts the result into result (locally allocated)
224 * The result_size is the return value
225 */
226int
227_gnutls_base64_decode (const uint8_t * data, size_t data_size,
228 gnutls_datum_t * result)
229{
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;
235
236 ret = cpydata(data, data_size, &pdata);
237 if (ret < 0)
238 {
239 gnutls_assert();
240 return ret;
241 }
242
243 est = ((data_size * 3) / 4) + 1;
244
245 result->data = gnutls_malloc (est);
246 if (result->data == NULL)
247 return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
248
249 pos = 0;
250 for (i = 0; i < pdata.size; i += 64)
251 {
252 if (pdata.size - i < 64)
253 decode_size = pdata.size - i;
254 else
255 decode_size = 64;
256
257 tmpres_size = sizeof(tmpres);
258 tmp = base64_decode ((void*)&pdata.data[i], decode_size, (void*)tmpres, &tmpres_size);
259 if (tmp == 0)
260 {
261 gnutls_assert();
262 gnutls_free (result->data);
263 result->data = NULL;
264 ret = GNUTLS_E_PARSING_ERROR;
265 goto cleanup;
266 }
267 memcpy (&result->data[pos], tmpres, tmpres_size);
268 pos += tmpres_size;
269 }
270
271 result->size = pos;
272
273 ret = pos;
274
275cleanup:
276 gnutls_free (pdata.data);
277 return ret;
278}
279
280
281/* Searches the given string for ONE PEM encoded certificate, and
282 * stores it in the result.
283 *
284 * The result_size is the return value
285 */
286#define ENDSTR "-----"
287int
288_gnutls_fbase64_decode (const char *header, const uint8_t * data,
289 size_t data_size, gnutls_datum_t* result)
290{
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];
297
298 _gnutls_str_cpy (pem_header, sizeof (pem_header), top);
299 if (header != NULL)
300 _gnutls_str_cat (pem_header, sizeof (pem_header), header);
301
302 rdata = memmem (data, data_size, pem_header, strlen (pem_header));
303
304 if (rdata == NULL)
305 {
306 gnutls_assert ();
307 _gnutls_debug_log ("Could not find '%s'\n", pem_header);
308 return GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR;
309 }
310
311 data_size -= (unsigned long int) rdata - (unsigned long int) data;
312
313 if (data_size < 4 + strlen (bottom))
314 {
315 gnutls_assert ();
316 return GNUTLS_E_BASE64_DECODING_ERROR;
317 }
318
319 kdata = memmem (rdata + 1, data_size - 1, ENDSTR, sizeof (ENDSTR) - 1);
320 /* allow CR as well.
321 */
322 if (kdata == NULL)
323 {
324 gnutls_assert ();
325 _gnutls_debug_log ("Could not find '%s'\n", ENDSTR);
326 return GNUTLS_E_BASE64_DECODING_ERROR;
327 }
328 data_size -= strlen (ENDSTR);
329 data_size -= (unsigned long int) kdata - (unsigned long int) rdata;
330
331 rdata = kdata + strlen (ENDSTR);
332
333 /* position is now after the ---BEGIN--- headers */
334
335 kdata = memmem (rdata, data_size, bottom, strlen (bottom));
336 if (kdata == NULL)
337 {
338 gnutls_assert ();
339 return GNUTLS_E_BASE64_DECODING_ERROR;
340 }
341
342 /* position of kdata is before the ----END--- footer
343 */
344 rdata_size = (unsigned long int) kdata - (unsigned long int) rdata;
345
346 if (rdata_size < 4)
347 {
348 gnutls_assert ();
349 return GNUTLS_E_BASE64_DECODING_ERROR;
350 }
351
352 if ((ret = _gnutls_base64_decode (rdata, rdata_size, result)) < 0)
353 {
354 gnutls_assert ();
355 return GNUTLS_E_BASE64_DECODING_ERROR;
356 }
357
358 return ret;
359}
360
361/**
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
367 *
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.
372 *
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.
376 **/
377int
378gnutls_pem_base64_decode (const char *header,
379 const gnutls_datum_t * b64_data,
380 unsigned char *result, size_t * result_size)
381{
382 gnutls_datum_t res;
383 int ret;
384
385 ret =
386 _gnutls_fbase64_decode (header, b64_data->data, b64_data->size, &res);
387 if (ret < 0)
388 return gnutls_assert_val(ret);
389
390 if (result == NULL || *result_size < (unsigned) res.size)
391 {
392 gnutls_free (res.data);
393 *result_size = res.size;
394 return GNUTLS_E_SHORT_MEMORY_BUFFER;
395 }
396 else
397 {
398 memcpy (result, res.data, res.size);
399 gnutls_free (res.data);
400 *result_size = res.size;
401 }
402
403 return 0;
404}
405
406/**
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
411 *
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.
417 *
418 * You should use gnutls_free() to free the returned data.
419 *
420 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
421 * an error code is returned.
422 **/
423int
424gnutls_pem_base64_decode_alloc (const char *header,
425 const gnutls_datum_t * b64_data,
426 gnutls_datum_t * result)
427{
428 int ret;
429
430 if (result == NULL)
431 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
432
433 ret =
434 _gnutls_fbase64_decode (header, b64_data->data, b64_data->size, result);
435 if (ret < 0)
436 return gnutls_assert_val(ret);
437
438 return 0;
439}
440