Coverage Report

Created: 2026-02-14 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnutls/lib/x509_b64.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2000-2012 Free Software Foundation, Inc.
3
 * Copyright (C) 2017 Red Hat, Inc.
4
 *
5
 * Author: Nikos Mavrogiannopoulos
6
 *
7
 * This file is part of GnuTLS.
8
 *
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.
13
 *
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.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
21
 *
22
 */
23
24
/* Functions that relate to base64 encoding and decoding.
25
 */
26
27
#include "gnutls_int.h"
28
#include "errors.h"
29
#include "datum.h"
30
#include "x509_b64.h"
31
#include <nettle/base64.h>
32
33
#define INCR(what, size, max_len)                       \
34
3.31M
  do {                                            \
35
3.31M
    what += size;                           \
36
3.31M
    if (what > max_len) {                   \
37
0
      gnutls_assert();                \
38
0
      gnutls_free(result->data);      \
39
0
      result->data = NULL;            \
40
0
      return GNUTLS_E_INTERNAL_ERROR; \
41
0
    }                                       \
42
3.31M
  } while (0)
43
44
/* encodes data and puts the result into result (locally allocated)
45
 * The result_size (including the null terminator) is the return value.
46
 */
47
int _gnutls_fbase64_encode(const char *msg, const uint8_t *data,
48
         size_t data_size, gnutls_datum_t *result)
49
524k
{
50
524k
  int tmp;
51
524k
  unsigned int i;
52
524k
  uint8_t tmpres[66];
53
524k
  uint8_t *ptr;
54
524k
  char top[80];
55
524k
  char bottom[80];
56
524k
  size_t size, max, bytes;
57
524k
  int pos, top_len = 0, bottom_len = 0;
58
524k
  unsigned raw_encoding = 0;
59
60
524k
  if (msg == NULL || msg[0] == 0)
61
400
    raw_encoding = 1;
62
63
524k
  if (!raw_encoding) {
64
523k
    if (strlen(msg) > 50) {
65
0
      gnutls_assert();
66
0
      return GNUTLS_E_BASE64_ENCODING_ERROR;
67
0
    }
68
69
523k
    _gnutls_str_cpy(top, sizeof(top), "-----BEGIN ");
70
523k
    _gnutls_str_cat(top, sizeof(top), msg);
71
523k
    _gnutls_str_cat(top, sizeof(top), "-----\n");
72
73
523k
    _gnutls_str_cpy(bottom, sizeof(bottom), "-----END ");
74
523k
    _gnutls_str_cat(bottom, sizeof(bottom), msg);
75
523k
    _gnutls_str_cat(bottom, sizeof(bottom), "-----\n");
76
77
523k
    top_len = strlen(top);
78
523k
    bottom_len = strlen(bottom);
79
523k
  }
80
81
524k
  max = B64FSIZE(top_len + bottom_len, data_size);
82
83
524k
  result->data = gnutls_malloc(max + 1);
84
524k
  if (result->data == NULL) {
85
0
    gnutls_assert();
86
0
    return GNUTLS_E_MEMORY_ERROR;
87
0
  }
88
89
524k
  bytes = 0;
90
524k
  INCR(bytes, top_len, max);
91
524k
  pos = top_len;
92
93
524k
  memcpy(result->data, top, top_len);
94
95
2.78M
  for (i = 0; i < data_size; i += 48) {
96
2.26M
    if (data_size - i < 48)
97
524k
      tmp = data_size - i;
98
1.74M
    else
99
1.74M
      tmp = 48;
100
101
2.26M
    size = BASE64_ENCODE_RAW_LENGTH(tmp);
102
2.26M
    if (sizeof(tmpres) < size)
103
0
      return gnutls_assert_val(
104
2.26M
        GNUTLS_E_BASE64_ENCODING_ERROR);
105
106
2.26M
    base64_encode_raw((void *)tmpres, tmp, &data[i]);
107
108
2.26M
    INCR(bytes, size + 1, max);
109
2.26M
    ptr = &result->data[pos];
110
111
2.26M
    memcpy(ptr, tmpres, size);
112
2.26M
    ptr += size;
113
2.26M
    pos += size;
114
2.26M
    if (!raw_encoding) {
115
1.24M
      *ptr++ = '\n';
116
1.24M
      pos++;
117
1.24M
    } else {
118
1.01M
      bytes--;
119
1.01M
    }
120
2.26M
  }
121
122
524k
  INCR(bytes, bottom_len, max);
123
124
524k
  memcpy(&result->data[bytes - bottom_len], bottom, bottom_len);
125
524k
  result->data[bytes] = 0;
126
524k
  result->size = bytes;
127
128
524k
  return max + 1;
129
524k
}
130
131
/**
132
 * gnutls_pem_base64_encode:
133
 * @msg: is a message to be put in the header (may be %NULL)
134
 * @data: contain the raw data
135
 * @result: the place where base64 data will be copied
136
 * @result_size: holds the size of the result
137
 *
138
 * This function will convert the given data to printable data, using
139
 * the base64 encoding. This is the encoding used in PEM messages.
140
 *
141
 * The output string will be null terminated, although the output size will
142
 * not include the terminating null.
143
 *
144
 * Returns: On success %GNUTLS_E_SUCCESS (0) is returned,
145
 *   %GNUTLS_E_SHORT_MEMORY_BUFFER is returned if the buffer given is
146
 *   not long enough, or 0 on success.
147
 **/
148
int gnutls_pem_base64_encode(const char *msg, const gnutls_datum_t *data,
149
           char *result, size_t *result_size)
150
200
{
151
200
  gnutls_datum_t res;
152
200
  int ret;
153
154
200
  ret = _gnutls_fbase64_encode(msg, data->data, data->size, &res);
155
200
  if (ret < 0)
156
0
    return ret;
157
158
200
  if (result == NULL || *result_size < (unsigned)res.size) {
159
193
    gnutls_free(res.data);
160
193
    *result_size = res.size + 1;
161
193
    return GNUTLS_E_SHORT_MEMORY_BUFFER;
162
193
  } else {
163
7
    memcpy(result, res.data, res.size);
164
7
    gnutls_free(res.data);
165
7
    *result_size = res.size;
166
7
  }
167
168
7
  return 0;
169
200
}
170
171
/**
172
 * gnutls_pem_base64_encode2:
173
 * @header: is a message to be put in the encoded header (may be %NULL)
174
 * @data: contains the raw data
175
 * @result: will hold the newly allocated encoded data
176
 *
177
 * This function will convert the given data to printable data, using
178
 * the base64 encoding.  This is the encoding used in PEM messages.
179
 * This function will allocate the required memory to hold the encoded
180
 * data.
181
 *
182
 * You should use gnutls_free() to free the returned data.
183
 *
184
 * Note, that prior to GnuTLS 3.4.0 this function was available
185
 * under the name gnutls_pem_base64_encode_alloc(). There is
186
 * compatibility macro pointing to this function.
187
 *
188
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
189
 *   an error code is returned.
190
 *
191
 * Since: 3.4.0
192
 **/
193
int gnutls_pem_base64_encode2(const char *header, const gnutls_datum_t *data,
194
            gnutls_datum_t *result)
195
520k
{
196
520k
  int ret;
197
198
520k
  if (result == NULL)
199
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
200
201
520k
  ret = _gnutls_fbase64_encode(header, data->data, data->size, result);
202
520k
  if (ret < 0)
203
0
    return gnutls_assert_val(ret);
204
205
520k
  return 0;
206
520k
}
207
208
/* copies data to result but removes newlines and <CR>
209
 * returns the size of the data copied.
210
 *
211
 * It will fail with GNUTLS_E_BASE64_DECODING_ERROR if the
212
 * end-result is the empty string.
213
 */
214
inline static int cpydata(const uint8_t *data, int data_size,
215
        gnutls_datum_t *result)
216
26.0k
{
217
26.0k
  int i, j;
218
219
26.0k
  result->data = gnutls_malloc(data_size + 1);
220
26.0k
  if (result->data == NULL)
221
0
    return GNUTLS_E_MEMORY_ERROR;
222
223
47.3M
  for (j = i = 0; i < data_size; i++) {
224
47.3M
    if (data[i] == '\n' || data[i] == '\r' || data[i] == ' ' ||
225
46.8M
        data[i] == '\t')
226
496k
      continue;
227
46.8M
    else if (data[i] == '-')
228
169
      break;
229
46.8M
    result->data[j] = data[i];
230
46.8M
    j++;
231
46.8M
  }
232
233
26.0k
  result->size = j;
234
26.0k
  result->data[j] = 0;
235
236
26.0k
  if (j == 0) {
237
145
    gnutls_free(result->data);
238
145
    return gnutls_assert_val(GNUTLS_E_BASE64_DECODING_ERROR);
239
145
  }
240
241
25.8k
  return j;
242
26.0k
}
243
244
/* decodes data and puts the result into result (locally allocated).
245
 * Note that encodings of zero-length strings are being rejected
246
 * with GNUTLS_E_BASE64_DECODING_ERROR.
247
 *
248
 * The result_size is the return value.
249
 */
250
int _gnutls_base64_decode(const uint8_t *data, size_t data_size,
251
        gnutls_datum_t *result)
252
26.0k
{
253
26.0k
  int ret;
254
26.0k
  size_t size;
255
26.0k
  gnutls_datum_t pdata;
256
26.0k
  struct base64_decode_ctx ctx;
257
258
26.0k
  if (data_size == 0) {
259
0
    result->data = (unsigned char *)gnutls_strdup("");
260
0
    if (result->data == NULL)
261
0
      return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
262
0
    result->size = 0;
263
0
    return 0;
264
0
  }
265
266
26.0k
  ret = cpydata(data, data_size, &pdata);
267
26.0k
  if (ret < 0) {
268
145
    gnutls_assert();
269
145
    return ret;
270
145
  }
271
272
25.8k
  base64_decode_init(&ctx);
273
274
25.8k
  size = BASE64_DECODE_LENGTH(pdata.size);
275
25.8k
  if (size == 0) {
276
0
    ret = gnutls_assert_val(GNUTLS_E_BASE64_DECODING_ERROR);
277
0
    goto cleanup;
278
0
  }
279
280
25.8k
  result->data = gnutls_malloc(size);
281
25.8k
  if (result->data == NULL) {
282
0
    ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
283
0
    goto cleanup;
284
0
  }
285
286
25.8k
  ret = base64_decode_update(&ctx, &size, result->data, pdata.size,
287
25.8k
           (void *)pdata.data);
288
25.8k
  if (ret == 0 || size == 0) {
289
171
    gnutls_assert();
290
171
    ret = GNUTLS_E_BASE64_DECODING_ERROR;
291
171
    goto fail;
292
171
  }
293
294
25.7k
  ret = base64_decode_final(&ctx);
295
25.7k
  if (ret != 1) {
296
58
    ret = gnutls_assert_val(GNUTLS_E_BASE64_DECODING_ERROR);
297
58
    goto fail;
298
58
  }
299
300
25.6k
  result->size = size;
301
302
25.6k
  ret = size;
303
25.6k
  goto cleanup;
304
305
229
fail:
306
229
  gnutls_free(result->data);
307
308
25.8k
cleanup:
309
25.8k
  gnutls_free(pdata.data);
310
25.8k
  return ret;
311
229
}
312
313
/* Searches the given string for ONE PEM encoded certificate, and
314
 * stores it in the result.
315
 *
316
 * The result_size (always non-zero) is the return value,
317
 * or a negative error code.
318
 */
319
103k
#define ENDSTR "-----"
320
int _gnutls_fbase64_decode(const char *header, const uint8_t *data,
321
         size_t data_size, gnutls_datum_t *result)
322
26.1k
{
323
26.1k
  int ret;
324
26.1k
  static const char top[] = "-----BEGIN ";
325
26.1k
  static const char bottom[] = "-----END ";
326
26.1k
  uint8_t *rdata, *kdata;
327
26.1k
  int rdata_size;
328
26.1k
  char pem_header[128];
329
330
26.1k
  _gnutls_str_cpy(pem_header, sizeof(pem_header), top);
331
26.1k
  if (header != NULL)
332
25.5k
    _gnutls_str_cat(pem_header, sizeof(pem_header), header);
333
334
26.1k
  rdata = memmem(data, data_size, pem_header, strlen(pem_header));
335
26.1k
  if (rdata == NULL) {
336
338
    gnutls_assert();
337
338
    _gnutls_hard_log("Could not find '%s'\n", pem_header);
338
338
    return GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR;
339
338
  }
340
341
25.7k
  data_size -= MEMSUB(rdata, data);
342
343
25.7k
  if (data_size < 4 + strlen(bottom)) {
344
4
    gnutls_assert();
345
4
    return GNUTLS_E_BASE64_DECODING_ERROR;
346
4
  }
347
348
25.7k
  kdata = memmem(rdata + 1, data_size - 1, ENDSTR, sizeof(ENDSTR) - 1);
349
  /* allow CR as well.
350
   */
351
25.7k
  if (kdata == NULL) {
352
8
    gnutls_assert();
353
8
    _gnutls_hard_log("Could not find '%s'\n", ENDSTR);
354
8
    return GNUTLS_E_BASE64_DECODING_ERROR;
355
8
  }
356
25.7k
  data_size -= strlen(ENDSTR);
357
25.7k
  data_size -= MEMSUB(kdata, rdata);
358
359
25.7k
  rdata = kdata + strlen(ENDSTR);
360
361
  /* position is now after the ---BEGIN--- headers */
362
363
25.7k
  kdata = memmem(rdata, data_size, bottom, strlen(bottom));
364
25.7k
  if (kdata == NULL) {
365
10
    gnutls_assert();
366
10
    return GNUTLS_E_BASE64_DECODING_ERROR;
367
10
  }
368
369
  /* position of kdata is before the ----END--- footer 
370
   */
371
25.7k
  rdata_size = MEMSUB(kdata, rdata);
372
373
25.7k
  if (rdata_size < 4) {
374
6
    gnutls_assert();
375
6
    return GNUTLS_E_BASE64_DECODING_ERROR;
376
6
  }
377
378
25.7k
  if ((ret = _gnutls_base64_decode(rdata, rdata_size, result)) < 0) {
379
112
    gnutls_assert();
380
112
    return GNUTLS_E_BASE64_DECODING_ERROR;
381
112
  }
382
383
25.6k
  return ret;
384
25.7k
}
385
386
/**
387
 * gnutls_pem_base64_decode:
388
 * @header: A null terminated string with the PEM header (eg. CERTIFICATE)
389
 * @b64_data: contain the encoded data
390
 * @result: the place where decoded data will be copied
391
 * @result_size: holds the size of the result
392
 *
393
 * This function will decode the given encoded data.  If the header
394
 * given is non %NULL this function will search for "-----BEGIN header"
395
 * and decode only this part.  Otherwise it will decode the first PEM
396
 * packet found.
397
 *
398
 * Returns: On success %GNUTLS_E_SUCCESS (0) is returned,
399
 *   %GNUTLS_E_SHORT_MEMORY_BUFFER is returned if the buffer given is
400
 *   not long enough, or 0 on success.
401
 **/
402
int gnutls_pem_base64_decode(const char *header, const gnutls_datum_t *b64_data,
403
           unsigned char *result, size_t *result_size)
404
281
{
405
281
  gnutls_datum_t res;
406
281
  int ret;
407
408
281
  ret = _gnutls_fbase64_decode(header, b64_data->data, b64_data->size,
409
281
             &res);
410
281
  if (ret < 0)
411
239
    return gnutls_assert_val(ret);
412
413
42
  if (result == NULL || *result_size < (unsigned)res.size) {
414
27
    gnutls_free(res.data);
415
27
    *result_size = res.size;
416
27
    return GNUTLS_E_SHORT_MEMORY_BUFFER;
417
27
  } else {
418
15
    memcpy(result, res.data, res.size);
419
15
    gnutls_free(res.data);
420
15
    *result_size = res.size;
421
15
  }
422
423
15
  return 0;
424
42
}
425
426
/**
427
 * gnutls_pem_base64_decode2:
428
 * @header: The PEM header (eg. CERTIFICATE)
429
 * @b64_data: contains the encoded data
430
 * @result: the location of decoded data
431
 *
432
 * This function will decode the given encoded data. The decoded data
433
 * will be allocated, and stored into result.  If the header given is
434
 * non null this function will search for "-----BEGIN header" and
435
 * decode only this part. Otherwise it will decode the first PEM
436
 * packet found.
437
 *
438
 * You should use gnutls_free() to free the returned data.
439
 *
440
 * Note, that prior to GnuTLS 3.4.0 this function was available
441
 * under the name gnutls_pem_base64_decode_alloc(). There is
442
 * compatibility macro pointing to this function.
443
 *
444
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
445
 *   an error code is returned.
446
 *
447
 * Since: 3.4.0
448
 **/
449
int gnutls_pem_base64_decode2(const char *header,
450
            const gnutls_datum_t *b64_data,
451
            gnutls_datum_t *result)
452
281
{
453
281
  int ret;
454
455
281
  if (result == NULL)
456
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
457
458
281
  ret = _gnutls_fbase64_decode(header, b64_data->data, b64_data->size,
459
281
             result);
460
281
  if (ret < 0)
461
239
    return gnutls_assert_val(ret);
462
463
42
  return 0;
464
281
}
465
466
/**
467
 * gnutls_base64_decode2:
468
 * @base64: contains the encoded data
469
 * @result: the location of decoded data
470
 *
471
 * This function will decode the given base64 encoded data. The decoded data
472
 * will be allocated, and stored into result.
473
 *
474
 * You should use gnutls_free() to free the returned data.
475
 *
476
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
477
 *   an error code is returned.
478
 *
479
 * Since: 3.6.0
480
 **/
481
int gnutls_base64_decode2(const gnutls_datum_t *base64, gnutls_datum_t *result)
482
281
{
483
281
  int ret;
484
485
281
  ret = _gnutls_base64_decode(base64->data, base64->size, result);
486
281
  if (ret < 0) {
487
262
    return gnutls_assert_val(ret);
488
262
  }
489
490
19
  return 0;
491
281
}
492
493
/**
494
 * gnutls_base64_encode2:
495
 * @data: contains the raw data
496
 * @result: will hold the newly allocated encoded data
497
 *
498
 * This function will convert the given data to printable data, using
499
 * the base64 encoding. This function will allocate the required
500
 * memory to hold the encoded data.
501
 *
502
 * You should use gnutls_free() to free the returned data.
503
 *
504
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
505
 *   an error code is returned.
506
 *
507
 * Since: 3.6.0
508
 **/
509
int gnutls_base64_encode2(const gnutls_datum_t *data, gnutls_datum_t *result)
510
200
{
511
200
  int ret;
512
513
200
  if (result == NULL)
514
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
515
516
200
  ret = _gnutls_fbase64_encode(NULL, data->data, data->size, result);
517
200
  if (ret < 0)
518
0
    return gnutls_assert_val(ret);
519
520
200
  return 0;
521
200
}