Coverage Report

Created: 2026-10-16 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open5gs/lib/crypt/ogs-base64.c
Line
Count
Source
1
/* Licensed to the Apache Software Foundation (ASF) under one or more
2
 * contributor license agreements.  See the NOTICE file distributed with
3
 * this work for additional information regarding copyright ownership.
4
 * The ASF licenses this file to You under the Apache License, Version 2.0
5
 * (the "License"); you may not use this file except in compliance with
6
 * the License.  You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
/*
18
 * Copyright (C) 2000-2012 Free Software Foundation, Inc.
19
 * Copyright (C) 2017 Red Hat, Inc.
20
 *
21
 * Author: Nikos Mavrogiannopoulos
22
 *
23
 * This file is part of GnuTLS.
24
 *
25
 * The GnuTLS is free software; you can redistribute it and/or
26
 * modify it under the terms of the GNU Lesser General Public License
27
 * as published by the Free Software Foundation; either version 2.1 of
28
 * the License, or (at your option) any later version.
29
 *
30
 * This library is distributed in the hope that it will be useful, but
31
 * WITHOUT ANY WARRANTY; without even the implied warranty of
32
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
33
 * Lesser General Public License for more details.
34
 *
35
 * You should have received a copy of the GNU Lesser General Public License
36
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
37
 *
38
 */
39
40
/*
41
 * Copyright (C) 2019-2026 by Sukchan Lee <acetcom@gmail.com>
42
 *
43
 * This file is part of Open5GS.
44
 *
45
 * Licensed under the Apache License, Version 2.0 (the "License");
46
 * you may not use this file except in compliance with the License.
47
 * You may obtain a copy of the License at
48
 *
49
 *   http://www.apache.org/licenses/LICENSE-2.0
50
 *
51
 * Unless required by applicable law or agreed to in writing, software
52
 * distributed under the License is distributed on an "AS IS" BASIS,
53
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
54
 * See the License for the specific language governing permissions and
55
 * limitations under the License.
56
 */
57
58
#include "ogs-crypt.h"
59
60
/* aaaack but it's fast and const should make it shared text page. */
61
static const unsigned char pr2six[256] =
62
{
63
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
65
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
66
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
67
    64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
68
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
69
    64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
70
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
71
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
72
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
73
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
74
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
75
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
76
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
77
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
78
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
79
};
80
81
static bool ogs_base64_scan(
82
        const char *in, size_t *out_nprbytes, size_t *out_decoded_size)
83
0
{
84
0
    const unsigned char *p = NULL;
85
0
    size_t nprbytes = 0, padding = 0;
86
0
    size_t decoded_size = 0;
87
88
0
    if (!in)
89
0
        return false;
90
91
0
    p = (const unsigned char *)in;
92
0
    while (*p) {
93
0
        if (pr2six[*p] <= 63) {
94
0
            if (padding)
95
0
                return false;
96
0
            nprbytes++;
97
0
        } else if (*p == '=') {
98
0
            padding++;
99
0
            if (padding > 2)
100
0
                return false;
101
0
        } else {
102
0
            return false;
103
0
        }
104
0
        p++;
105
0
    }
106
107
0
    if (!nprbytes)
108
0
        return false;
109
110
    /* A single remaining base64 character cannot produce valid output. */
111
0
    if (nprbytes % 4 == 1)
112
0
        return false;
113
114
    /* If padding exists, the encoded length including padding must be aligned. */
115
0
    if (padding && ((nprbytes + padding) % 4))
116
0
        return false;
117
118
0
    decoded_size = ((nprbytes + 3) / 4) * 3;
119
0
    decoded_size -= (4 - nprbytes) & 3;
120
121
0
    if (!decoded_size)
122
0
        return false;
123
124
0
    if (out_nprbytes)
125
0
        *out_nprbytes = nprbytes;
126
0
    if (out_decoded_size)
127
0
        *out_decoded_size = decoded_size;
128
129
0
    return true;
130
0
}
131
132
int ogs_base64_decoded_size(const char *in)
133
0
{
134
0
    size_t decoded_size = 0;
135
136
0
    if (!ogs_base64_scan(in, NULL, &decoded_size))
137
0
        return OGS_ERROR;
138
139
0
    return (int)decoded_size;
140
0
}
141
142
int ogs_base64_decode_to_buffer(
143
        uint8_t *out, size_t out_size, const char *in)
144
0
{
145
0
    const unsigned char *bufin = NULL;
146
0
    unsigned char *bufout = NULL;
147
0
    size_t nprbytes = 0, decoded_size = 0;
148
149
0
    if (!out || !out_size)
150
0
        return OGS_ERROR;
151
152
0
    if (!ogs_base64_scan(in, &nprbytes, &decoded_size))
153
0
        return OGS_ERROR;
154
155
0
    if (out_size < decoded_size)
156
0
        return OGS_ERROR;
157
158
0
    bufout = out;
159
0
    bufin = (const unsigned char *)in;
160
161
0
    while (nprbytes > 4) {
162
0
        *(bufout++) =
163
0
            (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
164
0
        *(bufout++) =
165
0
            (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
166
0
        *(bufout++) =
167
0
            (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
168
0
        bufin += 4;
169
0
        nprbytes -= 4;
170
0
    }
171
172
0
    if (nprbytes > 1) {
173
0
        *(bufout++) =
174
0
            (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
175
0
    }
176
0
    if (nprbytes > 2) {
177
0
        *(bufout++) =
178
0
            (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
179
0
    }
180
0
    if (nprbytes > 3) {
181
0
        *(bufout++) =
182
0
            (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
183
0
    }
184
185
0
    return (int)decoded_size;
186
0
}
187
188
int ogs_base64_decode_to_string(char *out, size_t out_size, const char *in)
189
0
{
190
0
    int len;
191
192
0
    if (!out || out_size <= 1)
193
0
        return OGS_ERROR;
194
195
0
    len = ogs_base64_decode_to_buffer((uint8_t *)out, out_size - 1, in);
196
0
    if (len <= 0)
197
0
        return OGS_ERROR;
198
199
0
    out[len] = '\0';
200
0
    return len;
201
0
}
202
203
static const char basis_64[] =
204
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
205
206
int ogs_base64_encoded_size(size_t in_size)
207
0
{
208
0
    size_t encoded_size;
209
210
0
    if (in_size > (SIZE_MAX - 1) / 4 * 3)
211
0
        return OGS_ERROR;
212
213
0
    encoded_size = ((in_size + 2) / 3 * 4) + 1;
214
0
    return (int)encoded_size;
215
0
}
216
217
int ogs_base64_encode_from_buffer(
218
        char *out, size_t out_size, const uint8_t *in, size_t in_size)
219
0
{
220
0
    size_t i;
221
0
    int needed;
222
0
    char *p = NULL;
223
224
0
    if (!out || !out_size || (!in && in_size))
225
0
        return OGS_ERROR;
226
227
0
    needed = ogs_base64_encoded_size(in_size);
228
0
    if (needed <= 0 || out_size < (size_t)needed)
229
0
        return OGS_ERROR;
230
231
0
    p = out;
232
0
    for (i = 0; i + 2 < in_size; i += 3) {
233
0
        *p++ = basis_64[(in[i] >> 2) & 0x3F];
234
0
        *p++ = basis_64[((in[i] & 0x3) << 4) |
235
0
                        ((int) (in[i + 1] & 0xF0) >> 4)];
236
0
        *p++ = basis_64[((in[i + 1] & 0xF) << 2) |
237
0
                        ((int) (in[i + 2] & 0xC0) >> 6)];
238
0
        *p++ = basis_64[in[i + 2] & 0x3F];
239
0
    }
240
241
0
    if (i < in_size) {
242
0
        *p++ = basis_64[(in[i] >> 2) & 0x3F];
243
0
        if (i == (in_size - 1)) {
244
0
            *p++ = basis_64[((in[i] & 0x3) << 4)];
245
0
            *p++ = '=';
246
0
        } else {
247
0
            *p++ = basis_64[((in[i] & 0x3) << 4) |
248
0
                            ((int) (in[i + 1] & 0xF0) >> 4)];
249
0
            *p++ = basis_64[((in[i + 1] & 0xF) << 2)];
250
0
        }
251
0
        *p++ = '=';
252
0
    }
253
254
0
    *p++ = '\0';
255
0
    return (int)(p - out);
256
0
}
257
258
int ogs_base64_encode_from_string(
259
        char *out, size_t out_size, const char *in)
260
0
{
261
0
    if (!in)
262
0
        return OGS_ERROR;
263
264
0
    return ogs_base64_encode_from_buffer(
265
0
            out, out_size, (const uint8_t *)in, strlen(in));
266
0
}
267
268
/* copies data to result but removes newlines and <CR>
269
 * returns the size of the data copied.
270
 *
271
 * It will fail with GNUTLS_E_BASE64_DECODING_ERROR if the
272
 * end-result is the empty string.
273
 */
274
inline static int
275
cpydata(const uint8_t *data, int data_size, uint8_t **result, int *result_size)
276
0
{
277
0
  int i, j;
278
279
0
  *result = ogs_malloc(data_size + 1);
280
0
  if (*result == NULL) {
281
0
        ogs_error("ogs_malloc failed[%d]", data_size+1);
282
0
        return OGS_ERROR;
283
0
    }
284
285
0
  for (j = i = 0; i < data_size; i++) {
286
0
    if (data[i] == '\n' || data[i] == '\r' || data[i] == ' '
287
0
        || data[i] == '\t')
288
0
      continue;
289
0
    else if (data[i] == '-')
290
0
      break;
291
0
    (*result)[j] = data[i];
292
0
    j++;
293
0
  }
294
295
0
  *result_size = j;
296
0
  (*result)[j] = 0;
297
298
0
  if (j==0) {
299
0
        ogs_free(*result);
300
0
        ogs_error("No Data");
301
0
    return OGS_ERROR;
302
0
  }
303
304
0
  return j;
305
0
}
306
307
0
#define MEMSUB(x,y) ((ssize_t)((ptrdiff_t)x-(ptrdiff_t)y))
308
0
#define ENDSTR "-----"
309
int ogs_fbase64_decode(const char *header,
310
        const uint8_t *data, size_t data_size, ogs_datum_t *result)
311
0
{
312
0
    static const char top[] = "-----BEGIN ";
313
0
    static const char bottom[] = "-----END ";
314
0
    uint8_t *rdata, *kdata;
315
0
    int rdata_size, decoded_size;
316
0
#define MAX_PEM_HEADER_LEN 128
317
0
    char pem_header[MAX_PEM_HEADER_LEN];
318
319
0
    uint8_t *bufcoded;
320
0
    int bufcoded_len;
321
0
    char *p, *last;
322
323
0
    ogs_assert(header);
324
0
    ogs_assert(data);
325
0
    ogs_assert(data_size);
326
0
    ogs_assert(result);
327
328
0
    memset(result, 0, sizeof(*result));
329
330
0
    p = pem_header;
331
0
    last = pem_header + MAX_PEM_HEADER_LEN;
332
333
0
    p = ogs_slprintf(p, last, "%s", top);
334
0
    if (header != NULL)
335
0
        p = ogs_slprintf(p, last, "%s", header);
336
337
0
    rdata = memmem(data, data_size, pem_header, strlen(pem_header));
338
0
    if (rdata == NULL) {
339
0
        ogs_error("Cound not find [%s]", pem_header);
340
0
        ogs_log_hexdump(OGS_LOG_ERROR, data, data_size);
341
0
        return OGS_ERROR;
342
0
    }
343
344
0
    data_size -= MEMSUB(rdata, data);
345
346
0
    if (data_size < 4 + strlen(bottom)) {
347
0
        ogs_error("Not enough data (%d < 4 + %d)",
348
0
                (int)data_size, (int)strlen(bottom));
349
0
        ogs_log_hexdump(OGS_LOG_ERROR, data, data_size);
350
0
        return OGS_ERROR;
351
0
    }
352
353
0
    kdata =
354
0
        memmem(rdata + 1, data_size - 1, ENDSTR, sizeof(ENDSTR) - 1);
355
    /* allow CR as well.
356
     */
357
0
    if (kdata == NULL) {
358
0
        ogs_error("Cound not find [%s]", ENDSTR);
359
0
        ogs_log_hexdump(OGS_LOG_ERROR, data, data_size);
360
0
        return OGS_ERROR;
361
0
    }
362
0
    data_size -= strlen(ENDSTR);
363
0
    data_size -= MEMSUB(kdata, rdata);
364
365
0
    rdata = kdata + strlen(ENDSTR);
366
367
    /* position is now after the ---BEGIN--- headers */
368
369
0
    kdata = memmem(rdata, data_size, bottom, strlen(bottom));
370
0
    if (kdata == NULL) {
371
0
        ogs_error("Cound not find [%s]", bottom);
372
0
        ogs_log_hexdump(OGS_LOG_ERROR, data, data_size);
373
0
        return OGS_ERROR;
374
0
    }
375
376
    /* position of kdata is before the ----END--- footer
377
     */
378
0
    rdata_size = MEMSUB(kdata, rdata);
379
380
0
    if (rdata_size < 4) {
381
0
        ogs_error("Not enough data [%d]", rdata_size);
382
0
        ogs_log_hexdump(OGS_LOG_ERROR, data, data_size);
383
0
        return OGS_ERROR;
384
0
    }
385
386
0
    if (cpydata(rdata, rdata_size, &bufcoded, &bufcoded_len) == OGS_ERROR) {
387
0
        ogs_error("cpydata() failed");
388
0
        ogs_log_hexdump(OGS_LOG_ERROR, rdata, rdata_size);
389
0
        return OGS_ERROR;
390
0
    }
391
392
0
    decoded_size = ogs_base64_decoded_size((const char *)bufcoded);
393
0
    if (decoded_size <= 0) {
394
0
        ogs_error("ogs_base64_decoded_size() failed");
395
0
        ogs_log_hexdump(OGS_LOG_ERROR, bufcoded, bufcoded_len);
396
0
        ogs_free(bufcoded);
397
0
        return OGS_ERROR;
398
0
    }
399
400
0
    result->data = ogs_calloc(1, decoded_size);
401
0
    if (result->data == NULL) {
402
0
        ogs_error("ogs_calloc() failed [%d]", decoded_size);
403
0
        ogs_free(bufcoded);
404
0
        return OGS_ERROR;
405
0
    }
406
407
0
    result->size = ogs_base64_decode_to_buffer(
408
0
            result->data, decoded_size, (const char *)bufcoded);
409
0
    if (result->size <= 0) {
410
0
        ogs_error("ogs_base64_decode_to_buffer() failed");
411
0
        ogs_log_hexdump(OGS_LOG_ERROR, bufcoded, bufcoded_len);
412
0
        ogs_free(result->data);
413
0
        result->data = NULL;
414
0
        ogs_free(bufcoded);
415
0
        return OGS_ERROR;
416
0
    }
417
418
0
    ogs_free(bufcoded);
419
420
0
    return OGS_OK;
421
0
}
422
423
int ogs_pem_decode_curve25519_key(const char *filename, uint8_t *key)
424
0
{
425
0
    int rv;
426
427
0
    uint8_t buf[OGS_HUGE_LEN];
428
0
    size_t bytes_read;
429
0
    ogs_datum_t result;
430
431
0
#define CURVE25519_HEADER_LEN 16
432
0
    const char *_header = "302e0201 00300506 032b656e 04220420";
433
0
    uint8_t header[CURVE25519_HEADER_LEN];
434
435
0
    ogs_assert(filename);
436
0
    ogs_assert(key);
437
438
0
    rv = ogs_file_read_full(filename, buf, OGS_HUGE_LEN, &bytes_read);
439
0
    if (rv != OGS_OK) {
440
0
        ogs_error("ogs_file_read_full[%s] failed", filename);
441
0
        return OGS_ERROR;
442
0
    }
443
444
0
    rv = ogs_fbase64_decode("PRIVATE KEY", buf, bytes_read, &result);
445
0
    if (rv != OGS_OK) {
446
0
        ogs_error("ogs_fbase64_decode[%s] failed", filename);
447
0
        ogs_log_hexdump(OGS_LOG_ERROR, result.data, result.size);
448
0
        return OGS_ERROR;
449
0
    }
450
451
0
    rv = OGS_OK;
452
453
0
    if (result.size != 48) {
454
0
        ogs_error("Invalid size [%d]", (int)result.size);
455
0
        rv = OGS_ERROR;
456
0
        goto cleanup;
457
0
    }
458
459
0
    if (memcmp(ogs_hex_from_string(_header, header, sizeof(header)),
460
0
                result.data, CURVE25519_HEADER_LEN) != 0) {
461
0
        ogs_error("Invalid header [%d]", (int)result.size);
462
0
        ogs_log_hexdump(OGS_LOG_FATAL, result.data, result.size);
463
0
        rv = OGS_ERROR;
464
0
        goto cleanup;
465
0
    }
466
467
0
    memcpy(key, result.data+CURVE25519_HEADER_LEN, OGS_ECCKEY_LEN);
468
469
0
cleanup:
470
0
    if (result.data)
471
0
        ogs_free(result.data);
472
473
0
    return rv;
474
0
}
475
476
int ogs_pem_decode_secp256r1_key(const char *filename, uint8_t *key)
477
0
{
478
0
    int rv;
479
480
0
    uint8_t buf[OGS_HUGE_LEN];
481
0
    size_t bytes_read;
482
0
    ogs_datum_t result;
483
484
0
#define SECP256R1_HEADER_LEN 7
485
0
    const char *_header = "30770201 010420";
486
0
    uint8_t header[SECP256R1_HEADER_LEN];
487
488
0
    ogs_assert(filename);
489
0
    ogs_assert(key);
490
491
0
    rv = ogs_file_read_full(filename, buf, OGS_HUGE_LEN, &bytes_read);
492
0
    if (rv != OGS_OK) {
493
0
        ogs_error("ogs_file_read_full[%s] failed", filename);
494
0
        return OGS_ERROR;
495
0
    }
496
497
0
    rv = ogs_fbase64_decode("EC PRIVATE KEY", buf, bytes_read, &result);
498
0
    if (rv != OGS_OK) {
499
0
        ogs_error("ogs_fbase64_decode[%s] failed", filename);
500
0
        ogs_log_hexdump(OGS_LOG_ERROR, result.data, result.size);
501
0
        return OGS_ERROR;
502
0
    }
503
504
0
    rv = OGS_OK;
505
506
0
    if (result.size != 121) {
507
0
        ogs_error("Invalid size [%d]", (int)result.size);
508
0
        rv = OGS_ERROR;
509
0
        goto cleanup;
510
0
    }
511
512
0
    if (memcmp(ogs_hex_from_string(_header, header, sizeof(header)),
513
0
                result.data, SECP256R1_HEADER_LEN) != 0) {
514
0
        ogs_error("Invalid header [%d]", (int)result.size);
515
0
        ogs_log_hexdump(OGS_LOG_FATAL, result.data, result.size);
516
0
        rv = OGS_ERROR;
517
0
        goto cleanup;
518
0
    }
519
520
0
    memcpy(key, result.data+SECP256R1_HEADER_LEN, OGS_ECCKEY_LEN);
521
522
0
cleanup:
523
0
    if (result.data)
524
0
        ogs_free(result.data);
525
526
0
    return rv;
527
0
}