Coverage Report

Created: 2025-08-26 06:35

/src/h2o/deps/picotls/lib/pembase64.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2016 Christian Huitema <huitema@huitema.net>
3
 *
4
 * Permission to use, copy, modify, and distribute this software for any
5
 * purpose with or without fee is hereby granted, provided that the above
6
 * copyright notice and this permission notice appear in all copies.
7
 *
8
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
 */
16
17
/*
18
 * Manage Base64 encoding.
19
 */
20
#ifdef _WINDOWS
21
#include "wincompat.h"
22
#else
23
#include <sys/time.h>
24
#endif
25
#include <errno.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <stdio.h>
29
#include "picotls.h"
30
#include "picotls/pembase64.h"
31
32
static char ptls_base64_alphabet[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
33
                                      'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
34
                                      'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
35
                                      'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
36
37
static signed char ptls_base64_values[] = {
38
    /* 0x00 to 0x0F */
39
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
40
    /* 0x10 to 0x1F */
41
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
42
    /* 0x20 to 0x2F. '+' at 2B, '/' at 2F  */
43
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
44
    /* 0x30 to 0x3F -- digits 0 to 9 at 0x30 to 0x39*/
45
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
46
    /* 0x40 to 0x4F -- chars 'A' to 'O' at 0x41 to 0x4F */
47
    -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
48
    /* 0x50 to 0x5F -- chars 'P' to 'Z' at 0x50 to 0x5A */
49
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
50
    /* 0x60 to 0x6F -- chars 'a' to 'o' at 0x61 to 0x6F */
51
    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
52
    /* 0x70 to 0x7F -- chars 'p' to 'z' at 0x70 to 0x7A */
53
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1};
54
55
static void ptls_base64_cell(const uint8_t *data, char *text)
56
0
{
57
0
    int n[4];
58
59
0
    n[0] = data[0] >> 2;
60
0
    n[1] = ((data[0] & 3) << 4) | (data[1] >> 4);
61
0
    n[2] = ((data[1] & 15) << 2) | (data[2] >> 6);
62
0
    n[3] = data[2] & 63;
63
64
0
    for (int i = 0; i < 4; i++) {
65
0
        text[i] = ptls_base64_alphabet[n[i]];
66
0
    }
67
0
}
68
69
size_t ptls_base64_howlong(size_t data_length)
70
0
{
71
0
    return (((data_length + 2) / 3) * 4);
72
0
}
73
74
int ptls_base64_encode(const uint8_t *data, size_t data_len, char *ptls_base64_text)
75
0
{
76
0
    int l = 0;
77
0
    int lt = 0;
78
79
0
    while ((data_len - l) >= 3) {
80
0
        ptls_base64_cell(data + l, ptls_base64_text + lt);
81
0
        l += 3;
82
0
        lt += 4;
83
0
    }
84
85
0
    switch (data_len - l) {
86
0
    case 0:
87
0
        break;
88
0
    case 1:
89
0
        ptls_base64_text[lt++] = ptls_base64_alphabet[data[l] >> 2];
90
0
        ptls_base64_text[lt++] = ptls_base64_alphabet[(data[l] & 3) << 4];
91
0
        ptls_base64_text[lt++] = '=';
92
0
        ptls_base64_text[lt++] = '=';
93
0
        break;
94
0
    case 2:
95
0
        ptls_base64_text[lt++] = ptls_base64_alphabet[data[l] >> 2];
96
0
        ptls_base64_text[lt++] = ptls_base64_alphabet[((data[l] & 3) << 4) | (data[l + 1] >> 4)];
97
0
        ptls_base64_text[lt++] = ptls_base64_alphabet[((data[l + 1] & 15) << 2)];
98
0
        ptls_base64_text[lt++] = '=';
99
0
        break;
100
0
    default:
101
0
        break;
102
0
    }
103
0
    ptls_base64_text[lt++] = 0;
104
105
0
    return lt;
106
0
}
107
108
/*
109
 * Take into input a line of text, so as to work by increments.
110
 * The intermediate text of the decoding is kept in a state variable.
111
 * The decoded data is accumulated in a PTLS buffer.
112
 * The parsing is consistent with the lax definition in RFC 7468
113
 */
114
115
void ptls_base64_decode_init(ptls_base64_decode_state_t *state)
116
0
{
117
0
    state->nbc = 0;
118
0
    state->nbo = 3;
119
0
    state->v = 0;
120
0
    state->status = PTLS_BASE64_DECODE_IN_PROGRESS;
121
0
}
122
123
int ptls_base64_decode(const char *text, ptls_base64_decode_state_t *state, ptls_buffer_t *buf)
124
0
{
125
0
    int ret = 0;
126
0
    uint8_t decoded[3];
127
0
    size_t text_index = 0;
128
0
    int c;
129
0
    signed char vc;
130
131
    /* skip initial blanks */
132
0
    while (text[text_index] != 0) {
133
0
        c = text[text_index];
134
135
0
        if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
136
0
            text_index++;
137
0
        } else {
138
0
            break;
139
0
        }
140
0
    }
141
142
0
    while (text[text_index] != 0 && ret == 0 && state->status == PTLS_BASE64_DECODE_IN_PROGRESS) {
143
0
        c = text[text_index++];
144
145
0
        vc = 0 < c && c < 0x7f ? ptls_base64_values[c] : -1;
146
0
        if (vc == -1) {
147
0
            if (state->nbc == 2 && c == '=' && text[text_index] == '=') {
148
0
                state->nbc = 4;
149
0
                text_index++;
150
0
                state->nbo = 1;
151
0
                state->v <<= 12;
152
0
            } else if (state->nbc == 3 && c == '=') {
153
0
                state->nbc = 4;
154
0
                state->nbo = 2;
155
0
                state->v <<= 6;
156
0
            } else {
157
                /* Skip final blanks */
158
0
                for (--text_index; text[text_index] != 0; ++text_index) {
159
0
                    c = text[text_index];
160
0
                    if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 0x0B || c == 0x0C))
161
0
                        break;
162
0
                }
163
164
                /* Should now be at end of buffer */
165
0
                if (text[text_index] == 0) {
166
0
                    break;
167
0
                } else {
168
                    /* Not at end of buffer, signal a decoding error */
169
0
                    state->nbo = 0;
170
0
                    state->status = PTLS_BASE64_DECODE_FAILED;
171
0
                    ret = PTLS_ERROR_INCORRECT_BASE64;
172
0
                }
173
0
            }
174
0
        } else {
175
0
            state->nbc++;
176
0
            state->v <<= 6;
177
0
            state->v |= vc;
178
0
        }
179
180
0
        if (ret == 0 && state->nbc == 4) {
181
            /* Convert to up to 3 octets */
182
0
            for (int j = 0; j < state->nbo; j++) {
183
0
                decoded[j] = (uint8_t)(state->v >> (8 * (2 - j)));
184
0
            }
185
186
0
            ret = ptls_buffer__do_pushv(buf, decoded, state->nbo);
187
188
0
            if (ret == 0) {
189
                /* test for fin or continuation */
190
0
                if (state->nbo < 3) {
191
                    /* Check that there are only trainling blanks on this line */
192
0
                    while (text[text_index] != 0) {
193
0
                        c = text[text_index++];
194
195
0
                        if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 0x0B || c == 0x0C) {
196
0
                            continue;
197
0
                        }
198
0
                    }
199
0
                    if (text[text_index] == 0) {
200
0
                        state->status = PTLS_BASE64_DECODE_DONE;
201
0
                    } else {
202
0
                        state->status = PTLS_BASE64_DECODE_FAILED;
203
0
                        ret = PTLS_ERROR_INCORRECT_BASE64;
204
0
                    }
205
0
                    break;
206
0
                } else {
207
0
                    state->v = 0;
208
0
                    state->nbo = 3;
209
0
                    state->nbc = 0;
210
0
                }
211
0
            }
212
0
        }
213
0
    }
214
0
    return ret;
215
0
}
216
217
/*
218
 * Reading a PEM file, to get an object:
219
 *
220
 * - Find first object, get the object name.
221
 * - If object label is what the application expects, parse, else skip to end.
222
 *
223
 * The following labels are defined in RFC 7468:
224
 *
225
 * Sec. Label                  ASN.1 Type              Reference Module
226
 * ----+----------------------+-----------------------+---------+----------
227
 * 5  CERTIFICATE            Certificate             [RFC5280] id-pkix1-e
228
 * 6  X509 CRL               CertificateList         [RFC5280] id-pkix1-e
229
 * 7  CERTIFICATE REQUEST    CertificationRequest    [RFC2986] id-pkcs10
230
 * 8  PKCS7                  ContentInfo             [RFC2315] id-pkcs7*
231
 * 9  CMS                    ContentInfo             [RFC5652] id-cms2004
232
 * 10 PRIVATE KEY            PrivateKeyInfo ::=      [RFC5208] id-pkcs8
233
 *                           OneAsymmetricKey        [RFC5958] id-aKPV1
234
 * 11 ENCRYPTED PRIVATE KEY  EncryptedPrivateKeyInfo [RFC5958] id-aKPV1
235
 * 12 ATTRIBUTE CERTIFICATE  AttributeCertificate    [RFC5755] id-acv2
236
 * 13 PUBLIC KEY             SubjectPublicKeyInfo    [RFC5280] id-pkix1-e
237
 */
238
239
static int ptls_compare_separator_line(const char *line, const char *begin_or_end, const char *label)
240
0
{
241
0
    int ret = strncmp(line, "-----", 5);
242
0
    size_t text_index = 5;
243
244
0
    if (ret == 0) {
245
0
        size_t begin_or_end_length = strlen(begin_or_end);
246
0
        ret = strncmp(line + text_index, begin_or_end, begin_or_end_length);
247
0
        text_index += begin_or_end_length;
248
0
    }
249
250
0
    if (ret == 0) {
251
0
        ret = line[text_index] - ' ';
252
0
        text_index++;
253
0
    }
254
255
0
    if (ret == 0) {
256
0
        size_t label_length = strlen(label);
257
0
        ret = strncmp(line + text_index, label, label_length);
258
0
        text_index += label_length;
259
0
    }
260
261
0
    if (ret == 0) {
262
0
        ret = strncmp(line + text_index, "-----", 5);
263
0
    }
264
265
0
    return ret;
266
0
}
267
268
static int ptls_get_pem_object(FILE *F, const char *label, ptls_buffer_t *buf)
269
0
{
270
0
    int ret = PTLS_ERROR_PEM_LABEL_NOT_FOUND;
271
0
    char line[256];
272
0
    ptls_base64_decode_state_t state;
273
274
    /* Get the label on a line by itself */
275
0
    while (fgets(line, 256, F)) {
276
0
        if (ptls_compare_separator_line(line, "BEGIN", label) == 0) {
277
0
            ret = 0;
278
0
            ptls_base64_decode_init(&state);
279
0
            break;
280
0
        }
281
0
    }
282
    /* Get the data in the buffer */
283
0
    while (ret == 0 && fgets(line, 256, F)) {
284
0
        if (ptls_compare_separator_line(line, "END", label) == 0) {
285
0
            if (state.status == PTLS_BASE64_DECODE_DONE || (state.status == PTLS_BASE64_DECODE_IN_PROGRESS && state.nbc == 0)) {
286
0
                ret = 0;
287
0
            } else {
288
0
                ret = PTLS_ERROR_INCORRECT_BASE64;
289
0
            }
290
0
            break;
291
0
        } else {
292
0
            ret = ptls_base64_decode(line, &state, buf);
293
0
        }
294
0
    }
295
296
0
    return ret;
297
0
}
298
299
int ptls_load_pem_objects(char const *pem_fname, const char *label, ptls_iovec_t *list, size_t list_max, size_t *nb_objects)
300
0
{
301
0
    FILE *F;
302
0
    int ret = 0;
303
0
    size_t count = 0;
304
#ifdef _WINDOWS
305
    errno_t err = fopen_s(&F, pem_fname, "r");
306
    if (err != 0) {
307
        ret = -1;
308
    }
309
#else
310
0
    F = fopen(pem_fname, "r");
311
0
    if (F == NULL) {
312
0
        ret = -1;
313
0
    }
314
0
#endif
315
316
0
    *nb_objects = 0;
317
318
0
    if (ret == 0) {
319
0
        while (count < list_max) {
320
0
            ptls_buffer_t buf;
321
322
0
            ptls_buffer_init(&buf, "", 0);
323
324
0
            ret = ptls_get_pem_object(F, label, &buf);
325
326
0
            if (ret == 0) {
327
0
                if (buf.off > 0 && buf.is_allocated) {
328
0
                    list[count].base = buf.base;
329
0
                    list[count].len = buf.off;
330
0
                    count++;
331
0
                } else {
332
0
                    ptls_buffer_dispose(&buf);
333
0
                }
334
0
            } else {
335
0
                ptls_buffer_dispose(&buf);
336
0
                break;
337
0
            }
338
0
        }
339
0
    }
340
341
0
    if (ret == PTLS_ERROR_PEM_LABEL_NOT_FOUND && count > 0) {
342
0
        ret = 0;
343
0
    }
344
345
0
    *nb_objects = count;
346
347
0
    if (F != NULL) {
348
0
        fclose(F);
349
0
    }
350
351
0
    return ret;
352
0
}
353
354
0
#define PTLS_MAX_CERTS_IN_CONTEXT 16
355
356
int ptls_load_certificates(ptls_context_t *ctx, char const *cert_pem_file)
357
0
{
358
0
    int ret = 0;
359
360
0
    ctx->certificates.list = (ptls_iovec_t *)malloc(PTLS_MAX_CERTS_IN_CONTEXT * sizeof(ptls_iovec_t));
361
362
0
    if (ctx->certificates.list == NULL) {
363
0
        ret = PTLS_ERROR_NO_MEMORY;
364
0
    } else {
365
0
        ret = ptls_load_pem_objects(cert_pem_file, "CERTIFICATE", ctx->certificates.list, PTLS_MAX_CERTS_IN_CONTEXT,
366
0
                                    &ctx->certificates.count);
367
0
    }
368
369
0
    return ret;
370
0
}