Coverage Report

Created: 2025-07-18 06:25

/src/picotls/t/util.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2016,2017 DeNA Co., Ltd., Kazuho Oku, Fastly
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy
5
 * of this software and associated documentation files (the "Software"), to
6
 * deal in the Software without restriction, including without limitation the
7
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
 * sell copies of the Software, and to permit persons to whom the Software is
9
 * furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in
12
 * all copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20
 * IN THE SOFTWARE.
21
 */
22
#ifndef util_h
23
#define util_h
24
25
#ifndef _XOPEN_SOURCE
26
#define _XOPEN_SOURCE 700 /* required for glibc to use getaddrinfo, etc. */
27
#endif
28
29
#include <errno.h>
30
#include <netdb.h>
31
#include <netinet/in.h>
32
#include <stdio.h>
33
#include <string.h>
34
#include <sys/param.h>
35
#include <sys/socket.h>
36
#include <sys/types.h>
37
#include <arpa/nameser.h>
38
#include <resolv.h>
39
#include <openssl/pem.h>
40
#include "picotls/pembase64.h"
41
#include "picotls/openssl.h"
42
43
static inline void load_certificate_chain(ptls_context_t *ctx, const char *fn)
44
0
{
45
0
    if (ptls_load_certificates(ctx, (char *)fn) != 0) {
46
0
        fprintf(stderr, "failed to load certificate:%s:%s\n", fn, strerror(errno));
47
0
        exit(1);
48
0
    }
49
0
}
Unexecuted instantiation: fuzz-server-hello.c:load_certificate_chain
Unexecuted instantiation: fuzz-client-hello.c:load_certificate_chain
50
51
static inline void load_raw_public_key(ptls_iovec_t *raw_public_key, char const *cert_pem_file)
52
0
{
53
0
    size_t count;
54
0
    if (ptls_load_pem_objects(cert_pem_file, "PUBLIC KEY", raw_public_key, 1, &count) != 0) {
55
0
        fprintf(stderr, "failed to load public key:%s:%s\n", cert_pem_file, strerror(errno));
56
0
        exit(1);
57
0
    }
58
0
}
Unexecuted instantiation: fuzz-server-hello.c:load_raw_public_key
Unexecuted instantiation: fuzz-client-hello.c:load_raw_public_key
59
60
static inline void load_private_key(ptls_context_t *ctx, const char *fn)
61
0
{
62
0
    static ptls_openssl_sign_certificate_t sc;
63
0
    FILE *fp;
64
0
    EVP_PKEY *pkey;
65
0
66
0
    if ((fp = fopen(fn, "rb")) == NULL) {
67
0
        fprintf(stderr, "failed to open file:%s:%s\n", fn, strerror(errno));
68
0
        exit(1);
69
0
    }
70
0
    pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
71
0
    fclose(fp);
72
0
73
0
    if (pkey == NULL) {
74
0
        fprintf(stderr, "failed to read private key from file:%s\n", fn);
75
0
        exit(1);
76
0
    }
77
0
78
0
    ptls_openssl_init_sign_certificate(&sc, pkey);
79
0
    EVP_PKEY_free(pkey);
80
0
81
0
    ctx->sign_certificate = &sc.super;
82
0
}
Unexecuted instantiation: fuzz-server-hello.c:load_private_key
Unexecuted instantiation: fuzz-client-hello.c:load_private_key
83
84
struct st_util_save_ticket_t {
85
    ptls_save_ticket_t super;
86
    char fn[MAXPATHLEN];
87
};
88
89
static int util_save_ticket_cb(ptls_save_ticket_t *_self, ptls_t *tls, ptls_iovec_t src)
90
0
{
91
0
    struct st_util_save_ticket_t *self = (struct st_util_save_ticket_t *)_self;
92
0
    FILE *fp;
93
0
94
0
    if ((fp = fopen(self->fn, "wb")) == NULL) {
95
0
        fprintf(stderr, "failed to open file:%s:%s\n", self->fn, strerror(errno));
96
0
        return PTLS_ERROR_LIBRARY;
97
0
    }
98
0
    fwrite(src.base, 1, src.len, fp);
99
0
    fclose(fp);
100
0
101
0
    return 0;
102
0
}
Unexecuted instantiation: fuzz-server-hello.c:util_save_ticket_cb
Unexecuted instantiation: fuzz-client-hello.c:util_save_ticket_cb
103
104
static inline void setup_session_file(ptls_context_t *ctx, ptls_handshake_properties_t *hsprop, const char *fn)
105
0
{
106
0
    static struct st_util_save_ticket_t st;
107
0
    FILE *fp;
108
0
109
0
    /* setup save_ticket callback */
110
0
    strcpy(st.fn, fn);
111
0
    st.super.cb = util_save_ticket_cb;
112
0
    ctx->save_ticket = &st.super;
113
0
114
0
    /* load session ticket if possible */
115
0
    if ((fp = fopen(fn, "rb")) != NULL) {
116
0
        static uint8_t ticket[16384];
117
0
        size_t ticket_size = fread(ticket, 1, sizeof(ticket), fp);
118
0
        if (ticket_size == 0 || !feof(fp)) {
119
0
            fprintf(stderr, "failed to load ticket from file:%s\n", fn);
120
0
            exit(1);
121
0
        }
122
0
        fclose(fp);
123
0
        hsprop->client.session_ticket = ptls_iovec_init(ticket, ticket_size);
124
0
    }
125
0
}
Unexecuted instantiation: fuzz-server-hello.c:setup_session_file
Unexecuted instantiation: fuzz-client-hello.c:setup_session_file
126
127
static inline X509_STORE *init_cert_store(char const *crt_file)
128
0
{
129
0
    int ret = 0;
130
0
    X509_STORE *store = X509_STORE_new();
131
0
132
0
    if (store != NULL) {
133
0
        X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
134
0
        ret = X509_LOOKUP_load_file(lookup, crt_file, X509_FILETYPE_PEM);
135
0
        if (ret != 1) {
136
0
            fprintf(stderr, "Cannot load store (%s), ret = %d\n", crt_file, ret);
137
0
            X509_STORE_free(store);
138
0
            exit(1);
139
0
        }
140
0
    } else {
141
0
        fprintf(stderr, "Cannot get a new X509 store\n");
142
0
        exit(1);
143
0
    }
144
0
145
0
    return store;
146
0
}
Unexecuted instantiation: fuzz-server-hello.c:init_cert_store
Unexecuted instantiation: fuzz-client-hello.c:init_cert_store
147
148
static inline void setup_verify_certificate(ptls_context_t *ctx, const char *ca_file)
149
0
{
150
0
    static ptls_openssl_verify_certificate_t vc;
151
0
    ptls_openssl_init_verify_certificate(&vc, ca_file != NULL ? init_cert_store(ca_file) : NULL);
152
0
    ctx->verify_certificate = &vc.super;
153
0
}
Unexecuted instantiation: fuzz-server-hello.c:setup_verify_certificate
Unexecuted instantiation: fuzz-client-hello.c:setup_verify_certificate
154
155
static inline void setup_raw_pubkey_verify_certificate(ptls_context_t *ctx, EVP_PKEY *pubkey)
156
0
{
157
0
    static ptls_openssl_raw_pubkey_verify_certificate_t vc;
158
0
    ptls_openssl_raw_pubkey_init_verify_certificate(&vc, pubkey);
159
0
    ctx->verify_certificate = &vc.super;
160
0
}
Unexecuted instantiation: fuzz-server-hello.c:setup_raw_pubkey_verify_certificate
Unexecuted instantiation: fuzz-client-hello.c:setup_raw_pubkey_verify_certificate
161
162
struct st_util_log_event_t {
163
    ptls_log_event_t super;
164
    FILE *fp;
165
};
166
167
static void log_event_cb(ptls_log_event_t *_self, ptls_t *tls, const char *type, const char *fmt, ...)
168
0
{
169
0
    struct st_util_log_event_t *self = (struct st_util_log_event_t *)_self;
170
0
    char randomhex[PTLS_HELLO_RANDOM_SIZE * 2 + 1];
171
0
    va_list args;
172
0
173
0
    ptls_hexdump(randomhex, ptls_get_client_random(tls).base, PTLS_HELLO_RANDOM_SIZE);
174
0
    fprintf(self->fp, "%s %s ", type, randomhex);
175
0
176
0
    va_start(args, fmt);
177
0
    vfprintf(self->fp, fmt, args);
178
0
    va_end(args);
179
0
180
0
    fprintf(self->fp, "\n");
181
0
    fflush(self->fp);
182
0
}
Unexecuted instantiation: fuzz-server-hello.c:log_event_cb
Unexecuted instantiation: fuzz-client-hello.c:log_event_cb
183
184
static inline void setup_log_event(ptls_context_t *ctx, const char *fn)
185
0
{
186
0
    static struct st_util_log_event_t ls;
187
0
188
0
    if ((ls.fp = fopen(fn, "at")) == NULL) {
189
0
        fprintf(stderr, "failed to open file:%s:%s\n", fn, strerror(errno));
190
0
        exit(1);
191
0
    }
192
0
    ls.super.cb = log_event_cb;
193
0
    ctx->log_event = &ls.super;
194
0
}
Unexecuted instantiation: fuzz-server-hello.c:setup_log_event
Unexecuted instantiation: fuzz-client-hello.c:setup_log_event
195
196
/* single-entry session cache */
197
struct st_util_session_cache_t {
198
    ptls_encrypt_ticket_t super;
199
    uint8_t id[32];
200
    ptls_iovec_t data;
201
};
202
203
static int encrypt_ticket_cb(ptls_encrypt_ticket_t *_self, ptls_t *tls, int is_encrypt, ptls_buffer_t *dst, ptls_iovec_t src)
204
0
{
205
0
    struct st_util_session_cache_t *self = (struct st_util_session_cache_t *)_self;
206
0
    int ret;
207
0
208
0
    if (is_encrypt) {
209
0
210
0
        /* replace the cached entry along with a newly generated session id */
211
0
        free(self->data.base);
212
0
        if ((self->data.base = (uint8_t *)malloc(src.len)) == NULL)
213
0
            return PTLS_ERROR_NO_MEMORY;
214
0
215
0
        ptls_get_context(tls)->random_bytes(self->id, sizeof(self->id));
216
0
        memcpy(self->data.base, src.base, src.len);
217
0
        self->data.len = src.len;
218
0
219
0
        /* store the session id in buffer */
220
0
        if ((ret = ptls_buffer_reserve(dst, sizeof(self->id))) != 0)
221
0
            return ret;
222
0
        memcpy(dst->base + dst->off, self->id, sizeof(self->id));
223
0
        dst->off += sizeof(self->id);
224
0
225
0
    } else {
226
0
227
0
        /* check if session id is the one stored in cache */
228
0
        if (src.len != sizeof(self->id))
229
0
            return PTLS_ERROR_SESSION_NOT_FOUND;
230
0
        if (memcmp(self->id, src.base, sizeof(self->id)) != 0)
231
0
            return PTLS_ERROR_SESSION_NOT_FOUND;
232
0
233
0
        /* return the cached value */
234
0
        if ((ret = ptls_buffer_reserve(dst, self->data.len)) != 0)
235
0
            return ret;
236
0
        memcpy(dst->base + dst->off, self->data.base, self->data.len);
237
0
        dst->off += self->data.len;
238
0
    }
239
0
240
0
    return 0;
241
0
}
Unexecuted instantiation: fuzz-server-hello.c:encrypt_ticket_cb
Unexecuted instantiation: fuzz-client-hello.c:encrypt_ticket_cb
242
243
static inline void setup_session_cache(ptls_context_t *ctx)
244
0
{
245
0
    static struct st_util_session_cache_t sc;
246
0
247
0
    sc.super.cb = encrypt_ticket_cb;
248
0
249
0
    ctx->ticket_lifetime = 86400;
250
0
    ctx->max_early_data_size = 8192;
251
0
    ctx->encrypt_ticket = &sc.super;
252
0
}
Unexecuted instantiation: fuzz-server-hello.c:setup_session_cache
Unexecuted instantiation: fuzz-client-hello.c:setup_session_cache
253
254
static struct {
255
    ptls_iovec_t config_list;
256
    struct {
257
        struct {
258
            ptls_hpke_kem_t *kem;
259
            ptls_key_exchange_context_t *ctx;
260
        } list[16];
261
        size_t count;
262
    } keyex;
263
    struct {
264
        ptls_iovec_t configs;
265
        char *fn;
266
    } retry;
267
} ech;
268
269
static ptls_aead_context_t *ech_create_opener(ptls_ech_create_opener_t *self, ptls_hpke_kem_t **kem,
270
                                              ptls_hpke_cipher_suite_t **cipher, ptls_t *tls, uint8_t config_id,
271
                                              ptls_hpke_cipher_suite_id_t cipher_id, ptls_iovec_t enc, ptls_iovec_t info_prefix)
272
0
{
273
0
    const uint8_t *src = ech.config_list.base, *const end = src + ech.config_list.len;
274
0
    size_t index = 0;
275
0
    int ret = 0;
276
0
277
0
    /* look for the cipher implementation; this should better be specific to each ECHConfig (as each of them may advertise different
278
0
     * set of values) */
279
0
    *cipher = NULL;
280
0
    for (size_t i = 0; ptls_openssl_hpke_cipher_suites[i] != NULL; ++i) {
281
0
        if (ptls_openssl_hpke_cipher_suites[i]->id.kdf == cipher_id.kdf &&
282
0
            ptls_openssl_hpke_cipher_suites[i]->id.aead == cipher_id.aead) {
283
0
            *cipher = ptls_openssl_hpke_cipher_suites[i];
284
0
            break;
285
0
        }
286
0
    }
287
0
    if (*cipher == NULL)
288
0
        goto Exit;
289
0
290
0
    ptls_decode_open_block(src, end, 2, {
291
0
        uint16_t version;
292
0
        if ((ret = ptls_decode16(&version, &src, end)) != 0)
293
0
            goto Exit;
294
0
        do {
295
0
            ptls_decode_open_block(src, end, 2, {
296
0
                if (src == end) {
297
0
                    ret = PTLS_ALERT_DECODE_ERROR;
298
0
                    goto Exit;
299
0
                }
300
0
                if (*src == config_id) {
301
0
                    /* this is the ECHConfig that we have been looking for */
302
0
                    if (index >= ech.keyex.count) {
303
0
                        fprintf(stderr, "ECH key missing for config %zu\n", index);
304
0
                        return NULL;
305
0
                    }
306
0
                    uint8_t *info = malloc(info_prefix.len + end - (src - 4));
307
0
                    memcpy(info, info_prefix.base, info_prefix.len);
308
0
                    memcpy(info + info_prefix.len, src - 4, end - (src - 4));
309
0
                    ptls_aead_context_t *aead;
310
0
                    ptls_hpke_setup_base_r(ech.keyex.list[index].kem, *cipher, ech.keyex.list[index].ctx, &aead, enc,
311
0
                                           ptls_iovec_init(info, info_prefix.len + end - (src - 4)));
312
0
                    free(info);
313
0
                    *kem = ech.keyex.list[index].kem;
314
0
                    return aead;
315
0
                }
316
0
                ++index;
317
0
                src = end;
318
0
            });
319
0
        } while (src != end);
320
0
    });
321
0
322
0
Exit:
323
0
    if (ret != 0)
324
0
        fprintf(stderr, "ECH decode error:%d\n", ret);
325
0
    return NULL;
326
0
}
Unexecuted instantiation: fuzz-server-hello.c:ech_create_opener
Unexecuted instantiation: fuzz-client-hello.c:ech_create_opener
327
328
static void ech_save_retry_configs(void)
329
0
{
330
0
    if (ech.retry.configs.base == NULL)
331
0
        return;
332
0
333
0
    FILE *fp;
334
0
    if ((fp = fopen(ech.retry.fn, "wt")) == NULL) {
335
0
        fprintf(stderr, "failed to write to ECH config file:%s:%s\n", ech.retry.fn, strerror(errno));
336
0
        exit(1);
337
0
    }
338
0
    fwrite(ech.retry.configs.base, 1, ech.retry.configs.len, fp);
339
0
    fclose(fp);
340
0
}
Unexecuted instantiation: fuzz-server-hello.c:ech_save_retry_configs
Unexecuted instantiation: fuzz-client-hello.c:ech_save_retry_configs
341
342
static ptls_iovec_t load_file(const char *fn)
343
0
{
344
0
    FILE *fp;
345
0
    ptls_iovec_t buf;
346
0
347
0
    if ((fp = fopen(fn, "rt")) == NULL) {
348
0
        fprintf(stderr, "failed to open file:%s:%s\n", fn, strerror(errno));
349
0
        exit(1);
350
0
    }
351
0
    buf.len = 65536;
352
0
    if ((buf.base = malloc(buf.len)) == NULL) {
353
0
        fprintf(stderr, "no memory\n");
354
0
        abort();
355
0
    }
356
0
    buf.len = fread(buf.base, 1, buf.len, fp);
357
0
    fclose(fp);
358
0
359
0
    return buf;
360
0
}
Unexecuted instantiation: fuzz-server-hello.c:load_file
Unexecuted instantiation: fuzz-client-hello.c:load_file
361
362
static void ech_setup_configs(const char *fn)
363
0
{
364
0
    ech.config_list = load_file(fn);
365
0
    ech.retry.fn = strdup(fn);
366
0
}
Unexecuted instantiation: fuzz-server-hello.c:ech_setup_configs
Unexecuted instantiation: fuzz-client-hello.c:ech_setup_configs
367
368
static void ech_setup_key(ptls_context_t *ctx, const char *fn)
369
0
{
370
0
    FILE *fp;
371
0
    EVP_PKEY *pkey;
372
0
    int ret;
373
0
374
0
    if ((fp = fopen(fn, "rt")) == NULL) {
375
0
        fprintf(stderr, "failed to open ECH private key file:%s:%s\n", fn, strerror(errno));
376
0
        exit(1);
377
0
    }
378
0
    if ((pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL) {
379
0
        fprintf(stderr, "failed to load private key from file:%s\n", fn);
380
0
        exit(1);
381
0
    }
382
0
    if ((ret = ptls_openssl_create_key_exchange(&ech.keyex.list[ech.keyex.count].ctx, pkey)) != 0) {
383
0
        fprintf(stderr, "failed to load private key from file:%s:picotls-error:%d", fn, ret);
384
0
        exit(1);
385
0
    }
386
0
    EVP_PKEY_free(pkey);
387
0
    fclose(fp);
388
0
389
0
    for (size_t i = 0; ptls_openssl_hpke_kems[i] != NULL; ++i) {
390
0
        if (ptls_openssl_hpke_kems[i]->keyex == ech.keyex.list[ech.keyex.count].ctx->algo) {
391
0
            ech.keyex.list[ech.keyex.count].kem = ptls_openssl_hpke_kems[i];
392
0
            break;
393
0
        }
394
0
    }
395
0
    if (ech.keyex.list[ech.keyex.count].kem == NULL) {
396
0
        fprintf(stderr, "kem unknown for private key:%s\n", fn);
397
0
        exit(1);
398
0
    }
399
0
400
0
    ++ech.keyex.count;
401
0
402
0
    static ptls_ech_create_opener_t opener = {.cb = ech_create_opener};
403
0
    ctx->ech.server.create_opener = &opener;
404
0
}
Unexecuted instantiation: fuzz-server-hello.c:ech_setup_key
Unexecuted instantiation: fuzz-client-hello.c:ech_setup_key
405
406
static inline int resolve_address(struct sockaddr *sa, socklen_t *salen, const char *host, const char *port, int family, int type,
407
                                  int proto)
408
0
{
409
0
    struct addrinfo hints, *res;
410
0
    int err;
411
0
412
0
    memset(&hints, 0, sizeof(hints));
413
0
    hints.ai_family = family;
414
0
    hints.ai_socktype = type;
415
0
    hints.ai_protocol = proto;
416
0
    hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV | AI_PASSIVE;
417
0
    if ((err = getaddrinfo(host, port, &hints, &res)) != 0 || res == NULL) {
418
0
        fprintf(stderr, "failed to resolve address:%s:%s:%s\n", host, port,
419
0
                err != 0 ? gai_strerror(err) : "getaddrinfo returned NULL");
420
0
        return -1;
421
0
    }
422
0
423
0
    memcpy(sa, res->ai_addr, res->ai_addrlen);
424
0
    *salen = res->ai_addrlen;
425
0
426
0
    freeaddrinfo(res);
427
0
    return 0;
428
0
}
Unexecuted instantiation: fuzz-server-hello.c:resolve_address
Unexecuted instantiation: fuzz-client-hello.c:resolve_address
429
430
static inline int normalize_txt(uint8_t *p, size_t len)
431
0
{
432
0
    uint8_t *const end = p + len, *dst = p;
433
0
434
0
    if (p == end)
435
0
        return 0;
436
0
437
0
    do {
438
0
        size_t block_len = *p++;
439
0
        if (end - p < block_len)
440
0
            return 0;
441
0
        memmove(dst, p, block_len);
442
0
        dst += block_len;
443
0
        p += block_len;
444
0
    } while (p != end);
445
0
    *dst = '\0';
446
0
447
0
    return 1;
448
0
}
Unexecuted instantiation: fuzz-server-hello.c:normalize_txt
Unexecuted instantiation: fuzz-client-hello.c:normalize_txt
449
450
/* The ptls_repeat_while_eintr macro will repeat a function call (block) if it is interrupted (EINTR) before completion. If failing
451
 * for other reason, the macro executes the exit block, such as either { break; } or { goto Fail; }.
452
 */
453
#ifdef _WINDOWS
454
#define repeat_while_eintr(expr, exit_block)                                                                                       \
455
    while ((expr) < 0) {                                                                                                           \
456
        exit_block;                                                                                                                \
457
    }
458
#else
459
#define repeat_while_eintr(expr, exit_block)                                                                                       \
460
    while ((expr) < 0) {                                                                                                           \
461
        if (errno == EINTR)                                                                                                        \
462
            continue;                                                                                                              \
463
        exit_block;                                                                                                                \
464
    }
465
#endif
466
467
#endif