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 | */ |
45 | int |
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 | **/ |
136 | int |
137 | gnutls_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 | **/ |
179 | int |
180 | gnutls_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 | */ |
199 | inline static int |
200 | cpydata (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 | */ |
226 | int |
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 | |
275 | cleanup: |
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 "-----" |
287 | int |
288 | _gnutls_fbase64_decode (const char *, 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 [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 | **/ |
377 | int |
378 | gnutls_pem_base64_decode (const char *, |
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 | **/ |
423 | int |
424 | gnutls_pem_base64_decode_alloc (const char *, |
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 | |