Coverage Report

Created: 2026-03-09 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl/ssl/ech/ech_store.c
Line
Count
Source
1
/*
2
 * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the OpenSSL license (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
10
#include <openssl/ssl.h>
11
#include <openssl/ech.h>
12
#include "../ssl_local.h"
13
#include "ech_local.h"
14
#include <openssl/rand.h>
15
#include <openssl/evp.h>
16
#include <openssl/core_names.h>
17
18
/* a size for some crypto vars */
19
0
#define OSSL_ECH_CRYPTO_VAR_SIZE 2048
20
21
/*
22
 * Used for ech_bio2buf, when reading from a BIO we allocate in chunks sized
23
 * as per below, with a max number of chunks as indicated, we don't expect to
24
 * go beyond one chunk in almost all cases
25
 */
26
0
#define OSSL_ECH_BUFCHUNK 512
27
0
#define OSSL_ECH_MAXITER 32
28
29
/*
30
 * ECHConfigList input to OSSL_ECHSTORE_read_echconfiglist()
31
 * can be either binary encoded ECHConfigList or a base64
32
 * encoded ECHConfigList.
33
 */
34
0
#define OSSL_ECH_FMT_BIN 1 /* binary ECHConfigList */
35
0
#define OSSL_ECH_FMT_B64TXT 2 /* base64 ECHConfigList */
36
37
/*
38
 * Telltales we use when guessing which form of encoded input we've
39
 * been given for an RR value or ECHConfig.
40
 * We give these the EBCDIC treatment as well - why not? :-)
41
 */
42
static const char B64_alphabet[] = "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52"
43
                                   "\x53\x54\x55\x56\x57\x58\x59\x5a\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a"
44
                                   "\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x30\x31"
45
                                   "\x32\x33\x34\x35\x36\x37\x38\x39\x2b\x2f\x3d\x3b";
46
47
#ifndef TLSEXT_MINLEN_host_name
48
/* The shortest DNS name we allow, e.g. "a.bc" */
49
0
#define TLSEXT_MINLEN_host_name 4
50
#endif
51
52
/*
53
 * local functions - public APIs are at the end
54
 */
55
56
void ossl_echext_free(OSSL_ECHEXT *e)
57
0
{
58
0
    if (e == NULL)
59
0
        return;
60
0
    OPENSSL_free(e->val);
61
0
    OPENSSL_free(e);
62
0
    return;
63
0
}
64
65
OSSL_ECHEXT *ossl_echext_dup(const OSSL_ECHEXT *src)
66
0
{
67
0
    OSSL_ECHEXT *ext = OPENSSL_zalloc(sizeof(*src));
68
69
0
    if (ext == NULL)
70
0
        return NULL;
71
0
    *ext = *src;
72
0
    ext->val = NULL;
73
0
    if (ext->len != 0) {
74
0
        ext->val = OPENSSL_memdup(src->val, src->len);
75
0
        if (ext->val == NULL) {
76
0
            ossl_echext_free(ext);
77
0
            return NULL;
78
0
        }
79
0
    }
80
0
    return ext;
81
0
}
82
83
void ossl_echstore_entry_free(OSSL_ECHSTORE_ENTRY *ee)
84
0
{
85
0
    if (ee == NULL)
86
0
        return;
87
0
    OPENSSL_free(ee->public_name);
88
0
    OPENSSL_free(ee->pub);
89
0
    EVP_PKEY_free(ee->keyshare);
90
0
    OPENSSL_free(ee->encoded);
91
0
    OPENSSL_free(ee->suites);
92
0
    sk_OSSL_ECHEXT_pop_free(ee->exts, ossl_echext_free);
93
0
    OPENSSL_free(ee);
94
0
    return;
95
0
}
96
97
/*
98
 * @brief Read a buffer from an input 'till eof
99
 * @param in is the BIO input
100
 * @param buf is where to put the buffer, allocated inside here
101
 * @param len is the length of that buffer
102
 *
103
 * This is intended for small inputs, either files or buffers and
104
 * not other kinds of BIO.
105
 */
106
static int ech_bio2buf(BIO *in, unsigned char **buf, size_t *len)
107
0
{
108
0
    unsigned char *lptr = NULL, *lbuf = NULL, *tmp = NULL;
109
0
    size_t sofar = 0, readbytes = 0;
110
0
    int done = 0, brv, iter = 0;
111
112
0
    if (buf == NULL || len == NULL)
113
0
        return 0;
114
0
    sofar = OSSL_ECH_BUFCHUNK;
115
0
    lbuf = OPENSSL_zalloc(sofar);
116
0
    if (lbuf == NULL)
117
0
        return 0;
118
0
    lptr = lbuf;
119
0
    while (!BIO_eof(in) && !done && iter++ < OSSL_ECH_MAXITER) {
120
0
        brv = BIO_read_ex(in, lptr, OSSL_ECH_BUFCHUNK, &readbytes);
121
0
        if (brv != 1)
122
0
            goto err;
123
0
        if (readbytes < OSSL_ECH_BUFCHUNK) {
124
0
            done = 1;
125
0
            break;
126
0
        }
127
0
        sofar += OSSL_ECH_BUFCHUNK;
128
0
        tmp = OPENSSL_realloc(lbuf, sofar);
129
0
        if (tmp == NULL)
130
0
            goto err;
131
0
        lbuf = tmp;
132
0
        lptr = lbuf + sofar - OSSL_ECH_BUFCHUNK;
133
0
    }
134
0
    if (BIO_eof(in) && done == 1) {
135
0
        *len = sofar + readbytes - OSSL_ECH_BUFCHUNK;
136
0
        *buf = lbuf;
137
0
        return 1;
138
0
    }
139
0
err:
140
0
    OPENSSL_free(lbuf);
141
0
    return 0;
142
0
}
143
144
/*
145
 * @brief Figure out ECHConfig encoding
146
 * @param val is a buffer with the encoding
147
 * @param len is the length of that buffer
148
 * @param fmt is the detected format
149
 * @return 1 for success, 0 for error
150
 */
151
static int ech_check_format(const unsigned char *val, size_t len, int *fmt)
152
0
{
153
0
    size_t span = 0;
154
0
    char *copy_with_NUL = NULL;
155
156
0
    if (fmt == NULL || len <= 4 || val == NULL)
157
0
        return 0;
158
    /* binary encoding starts with two octet length and ECH version */
159
0
    if (len == 2 + ((size_t)(val[0]) * 256 + (size_t)(val[1]))
160
0
        && val[2] == ((OSSL_ECH_RFC9849_VERSION / 256) & 0xff)
161
0
        && val[3] == ((OSSL_ECH_RFC9849_VERSION % 256) & 0xff)) {
162
0
        *fmt = OSSL_ECH_FMT_BIN;
163
0
        return 1;
164
0
    }
165
    /* ensure we always end with a NUL so strspn is safe */
166
0
    copy_with_NUL = OPENSSL_malloc(len + 1);
167
0
    if (copy_with_NUL == NULL)
168
0
        return 0;
169
0
    memcpy(copy_with_NUL, val, len);
170
0
    copy_with_NUL[len] = '\0';
171
0
    span = strspn(copy_with_NUL, B64_alphabet);
172
0
    OPENSSL_free(copy_with_NUL);
173
0
    if (len <= span) {
174
0
        *fmt = OSSL_ECH_FMT_B64TXT;
175
0
        return 1;
176
0
    }
177
0
    return 0;
178
0
}
179
180
/*
181
 * @brief helper to decode ECHConfig extensions
182
 * @param ee is the OSSL_ECHSTORE entry for these
183
 * @param exts is the binary form extensions
184
 * @return 1 for good, 0 for error
185
 */
186
static int ech_decode_echconfig_exts(OSSL_ECHSTORE_ENTRY *ee, PACKET *exts)
187
0
{
188
0
    unsigned int exttype = 0;
189
0
    size_t extlen = 0;
190
0
    unsigned char *extval = NULL;
191
0
    OSSL_ECHEXT *oe = NULL;
192
0
    PACKET ext;
193
194
    /*
195
     * reminder: exts is a two-octet length prefixed list of:
196
     * - two octet extension type
197
     * - two octet extension length (can be zero)
198
     * - length octets
199
     * we've consumed the overall length before getting here
200
     */
201
0
    while (PACKET_remaining(exts) > 0) {
202
0
        exttype = 0, extlen = 0;
203
0
        extval = NULL;
204
0
        oe = NULL;
205
0
        if (!PACKET_get_net_2(exts, &exttype) || !PACKET_get_length_prefixed_2(exts, &ext)) {
206
0
            ERR_raise(ERR_LIB_SSL, SSL_R_BAD_ECHCONFIG_EXTENSION);
207
0
            goto err;
208
0
        }
209
0
        if (PACKET_remaining(&ext) >= OSSL_ECH_MAX_ECHCONFIGEXT_LEN) {
210
0
            ERR_raise(ERR_LIB_SSL, SSL_R_BAD_ECHCONFIG_EXTENSION);
211
0
            goto err;
212
0
        }
213
0
        if (!PACKET_memdup(&ext, &extval, &extlen)) {
214
0
            ERR_raise(ERR_LIB_SSL, SSL_R_BAD_ECHCONFIG_EXTENSION);
215
0
            goto err;
216
0
        }
217
0
        oe = OPENSSL_malloc(sizeof(*oe));
218
0
        if (oe == NULL)
219
0
            goto err;
220
0
        oe->type = (uint16_t)exttype;
221
0
        oe->val = extval;
222
0
        extval = NULL; /* avoid double free */
223
0
        oe->len = (uint16_t)extlen;
224
0
        if (ee->exts == NULL)
225
0
            ee->exts = sk_OSSL_ECHEXT_new_null();
226
0
        if (ee->exts == NULL) {
227
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
228
0
            goto err;
229
0
        }
230
0
        if (!sk_OSSL_ECHEXT_push(ee->exts, oe)) {
231
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
232
0
            goto err;
233
0
        }
234
0
    }
235
0
    return 1;
236
0
err:
237
0
    sk_OSSL_ECHEXT_pop_free(ee->exts, ossl_echext_free);
238
0
    ee->exts = NULL;
239
0
    ossl_echext_free(oe);
240
0
    OPENSSL_free(extval);
241
0
    return 0;
242
0
}
243
244
/*
245
 * @brief Check entry to see if looks good or bad
246
 * @param ee is the ECHConfig to check
247
 * @return 1 for all good, 0 otherwise
248
 */
249
static int ech_final_config_checks(OSSL_ECHSTORE_ENTRY *ee)
250
0
{
251
0
    OSSL_HPKE_SUITE hpke_suite;
252
0
    int ind, num;
253
0
    int goodsuitefound = 0;
254
255
    /* check local support for some suite */
256
0
    for (ind = 0; ind != (int)ee->nsuites; ind++) {
257
        /*
258
         * suite_check says yes to the pseudo-aead for export, but we don't
259
         * want to see it here coming from outside in an encoding
260
         */
261
0
        hpke_suite = ee->suites[ind];
262
0
        if (OSSL_HPKE_suite_check(hpke_suite) == 1
263
0
            && hpke_suite.aead_id != OSSL_HPKE_AEAD_ID_EXPORTONLY) {
264
0
            goodsuitefound = 1;
265
0
            break;
266
0
        }
267
0
    }
268
0
    if (goodsuitefound == 0) {
269
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
270
0
        return 0;
271
0
    }
272
    /* check no mandatory exts (with high bit set in type) */
273
0
    num = (ee->exts == NULL ? 0 : sk_OSSL_ECHEXT_num(ee->exts));
274
0
    for (ind = 0; ind != num; ind++) {
275
0
        OSSL_ECHEXT *oe = sk_OSSL_ECHEXT_value(ee->exts, (int)ind);
276
277
0
        if (oe->type & 0x8000) {
278
0
            ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
279
0
            return 0;
280
0
        }
281
0
    }
282
    /* check public_name rules, as per spec section 4 */
283
0
    if (ee->public_name == NULL
284
0
        || ee->public_name[0] == '\0'
285
0
        || ee->public_name[0] == '.'
286
0
        || ee->public_name[strlen(ee->public_name) - 1] == '.')
287
0
        return 0;
288
0
    return 1;
289
0
}
290
291
/**
292
 * @brief decode one ECHConfig from a packet into an entry
293
 * @param rent ptr to an entry allocated within (on success)
294
 * @param pkt is the encoding
295
 * @param priv is an optional private key (NULL if absent)
296
 * @param for_retry says whether to include in a retry_config (if priv present)
297
 * @return 1 for success, 0 for error
298
 */
299
static int ech_decode_one_entry(OSSL_ECHSTORE_ENTRY **rent, PACKET *pkt,
300
    EVP_PKEY *priv, int for_retry)
301
0
{
302
0
    size_t ech_content_length = 0;
303
0
    unsigned int tmpi;
304
0
    const unsigned char *tmpecp = NULL;
305
0
    size_t tmpeclen = 0, test_publen = 0;
306
0
    PACKET ver_pkt, pub_pkt, cipher_suites, public_name_pkt, exts;
307
0
    uint16_t thiskemid;
308
0
    size_t suiteoctets = 0;
309
0
    unsigned int ci = 0;
310
0
    unsigned char cipher[OSSL_ECH_CIPHER_LEN], max_name_len;
311
0
    unsigned char test_pub[OSSL_ECH_CRYPTO_VAR_SIZE];
312
0
    OSSL_ECHSTORE_ENTRY *ee = NULL;
313
314
0
    if (rent == NULL) {
315
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
316
0
        return 0;
317
0
    }
318
0
    if (pkt == NULL) {
319
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
320
0
        goto err;
321
0
    }
322
0
    ee = OPENSSL_zalloc(sizeof(*ee));
323
0
    if (ee == NULL)
324
0
        goto err;
325
    /* note start of encoding so we can make a copy later */
326
0
    tmpeclen = PACKET_remaining(pkt);
327
0
    if (PACKET_peek_bytes(pkt, &tmpecp, tmpeclen) != 1
328
0
        || !PACKET_get_net_2(pkt, &tmpi)) {
329
0
        ERR_raise(ERR_LIB_SSL, SSL_R_ECH_DECODE_ERROR);
330
0
        goto err;
331
0
    }
332
0
    ee->version = (uint16_t)tmpi;
333
334
    /* grab versioned packet data */
335
0
    if (!PACKET_get_length_prefixed_2(pkt, &ver_pkt)) {
336
0
        ERR_raise(ERR_LIB_SSL, SSL_R_ECH_DECODE_ERROR);
337
0
        goto err;
338
0
    }
339
0
    ech_content_length = (unsigned int)PACKET_remaining(&ver_pkt);
340
0
    switch (ee->version) {
341
0
    case OSSL_ECH_RFC9849_VERSION:
342
0
        break;
343
0
    default:
344
        /* skip over in case we get something we can handle later */
345
0
        if (!PACKET_forward(&ver_pkt, ech_content_length)) {
346
0
            ERR_raise(ERR_LIB_SSL, SSL_R_ECH_DECODE_ERROR);
347
0
            goto err;
348
0
        }
349
        /* nothing to return but not a fail */
350
0
        ossl_echstore_entry_free(ee);
351
0
        *rent = NULL;
352
0
        return 1;
353
0
    }
354
0
    if (!PACKET_copy_bytes(&ver_pkt, &ee->config_id, 1)
355
0
        || !PACKET_get_net_2(&ver_pkt, &tmpi)
356
0
        || !PACKET_get_length_prefixed_2(&ver_pkt, &pub_pkt)
357
0
        || !PACKET_memdup(&pub_pkt, &ee->pub, &ee->pub_len)
358
0
        || !PACKET_get_length_prefixed_2(&ver_pkt, &cipher_suites)
359
0
        || (suiteoctets = PACKET_remaining(&cipher_suites)) <= 0
360
0
        || (suiteoctets % 2) == 1
361
0
        || suiteoctets / OSSL_ECH_CIPHER_LEN > UINT_MAX) {
362
0
        ERR_raise(ERR_LIB_SSL, SSL_R_ECH_DECODE_ERROR);
363
0
        goto err;
364
0
    }
365
0
    thiskemid = (uint16_t)tmpi;
366
0
    ee->nsuites = (unsigned int)(suiteoctets / OSSL_ECH_CIPHER_LEN);
367
0
    ee->suites = OPENSSL_malloc_array(ee->nsuites, sizeof(*ee->suites));
368
0
    if (ee->suites == NULL)
369
0
        goto err;
370
0
    while (PACKET_copy_bytes(&cipher_suites, cipher,
371
0
        OSSL_ECH_CIPHER_LEN)) {
372
0
        ee->suites[ci].kem_id = thiskemid;
373
0
        ee->suites[ci].kdf_id = cipher[0] << 8 | cipher[1];
374
0
        ee->suites[ci].aead_id = cipher[2] << 8 | cipher[3];
375
0
        if (ci++ >= ee->nsuites) {
376
0
            ERR_raise(ERR_LIB_SSL, SSL_R_ECH_DECODE_ERROR);
377
0
            goto err;
378
0
        }
379
0
    }
380
0
    if (PACKET_remaining(&cipher_suites) > 0
381
0
        || !PACKET_copy_bytes(&ver_pkt, &max_name_len, 1)) {
382
0
        ERR_raise(ERR_LIB_SSL, SSL_R_ECH_DECODE_ERROR);
383
0
        goto err;
384
0
    }
385
0
    ee->max_name_length = max_name_len;
386
0
    if (!PACKET_get_length_prefixed_1(&ver_pkt, &public_name_pkt)) {
387
0
        ERR_raise(ERR_LIB_SSL, SSL_R_ECH_DECODE_ERROR);
388
0
        goto err;
389
0
    }
390
0
    if (PACKET_contains_zero_byte(&public_name_pkt)
391
0
        || PACKET_remaining(&public_name_pkt) < TLSEXT_MINLEN_host_name
392
0
        || !PACKET_strndup(&public_name_pkt, &ee->public_name)) {
393
0
        ERR_raise(ERR_LIB_SSL, SSL_R_ECH_DECODE_ERROR);
394
0
        goto err;
395
0
    }
396
0
    if (!PACKET_get_length_prefixed_2(&ver_pkt, &exts)) {
397
0
        ERR_raise(ERR_LIB_SSL, SSL_R_ECH_DECODE_ERROR);
398
0
        goto err;
399
0
    }
400
0
    if (PACKET_remaining(&exts) > 0
401
0
        && ech_decode_echconfig_exts(ee, &exts) != 1) {
402
0
        ERR_raise(ERR_LIB_SSL, SSL_R_ECH_DECODE_ERROR);
403
0
        goto err;
404
0
    }
405
    /* set length of encoding of this ECHConfig */
406
0
    ee->encoded_len = PACKET_data(&ver_pkt) - tmpecp;
407
    /* copy encoded as it might get free'd if a reduce happens */
408
0
    ee->encoded = OPENSSL_memdup(tmpecp, ee->encoded_len);
409
0
    if (ee->encoded == NULL)
410
0
        goto err;
411
0
    if (priv != NULL) {
412
0
        if (EVP_PKEY_get_octet_string_param(priv,
413
0
                OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
414
0
                test_pub, OSSL_ECH_CRYPTO_VAR_SIZE,
415
0
                &test_publen)
416
0
            != 1) {
417
0
            ERR_raise(ERR_LIB_SSL, SSL_R_ECH_DECODE_ERROR);
418
0
            goto err;
419
0
        }
420
0
        if (test_publen == ee->pub_len
421
0
            && !memcmp(test_pub, ee->pub, ee->pub_len)) {
422
0
            EVP_PKEY_up_ref(priv); /* associate the private key */
423
0
            ee->keyshare = priv;
424
0
            ee->for_retry = for_retry;
425
0
        }
426
0
    }
427
0
    ee->loadtime = time(0);
428
0
    *rent = ee;
429
0
    return 1;
430
0
err:
431
0
    ossl_echstore_entry_free(ee);
432
0
    *rent = NULL;
433
0
    return 0;
434
0
}
435
436
/*
437
 * @brief decode and flatten a binary encoded ECHConfigList
438
 * @param es an OSSL_ECHSTORE
439
 * @param priv is an optional private key (NULL if absent)
440
 * @param for_retry says whether to include in a retry_config (if priv present)
441
 * @param binbuf binary encoded ECHConfigList (we hope)
442
 * @param binlen length of binbuf
443
 * @return 1 for success, 0 for error
444
 *
445
 * We may only get one ECHConfig per list, but there can be more.  We want each
446
 * element of the output to contain exactly one ECHConfig so that a client
447
 * could sensibly down select to the one they prefer later, and so that we have
448
 * the specific encoded value of that ECHConfig for inclusion in the HPKE info
449
 * parameter when finally encrypting or decrypting an inner ClientHello.
450
 *
451
 * If a private value is provided then that'll only be associated with the
452
 * relevant public value, if >1 public value was present in the ECHConfigList.
453
 */
454
static int ech_decode_and_flatten(OSSL_ECHSTORE *es, EVP_PKEY *priv, int for_retry,
455
    unsigned char *binbuf, size_t binblen)
456
0
{
457
0
    int rv = 0;
458
0
    size_t remaining = 0;
459
0
    PACKET opkt, pkt;
460
0
    OSSL_ECHSTORE_ENTRY *ee = NULL;
461
462
0
    if (binbuf == NULL || binblen == 0 || binblen < OSSL_ECH_MIN_ECHCONFIG_LEN
463
0
        || binblen >= OSSL_ECH_MAX_ECHCONFIG_LEN) {
464
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
465
0
        goto err;
466
0
    }
467
0
    if (PACKET_buf_init(&opkt, binbuf, binblen) != 1
468
0
        || !PACKET_get_length_prefixed_2(&opkt, &pkt)) {
469
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
470
0
        goto err;
471
0
    }
472
0
    remaining = PACKET_remaining(&pkt);
473
0
    while (remaining > 0) {
474
0
        if (ech_decode_one_entry(&ee, &pkt, priv, for_retry) != 1) {
475
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
476
0
            goto err;
477
0
        }
478
0
        remaining = PACKET_remaining(&pkt);
479
        /* if unsupported version we can skip over */
480
0
        if (ee == NULL)
481
0
            continue;
482
        /* do final checks on suites, exts, and fail if issues */
483
0
        if (ech_final_config_checks(ee) != 1)
484
0
            goto err;
485
        /* push entry into store */
486
0
        if (es->entries == NULL)
487
0
            es->entries = sk_OSSL_ECHSTORE_ENTRY_new_null();
488
0
        if (es->entries == NULL) {
489
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
490
0
            goto err;
491
0
        }
492
0
        if (!sk_OSSL_ECHSTORE_ENTRY_push(es->entries, ee)) {
493
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
494
0
            goto err;
495
0
        }
496
0
        ee = NULL;
497
0
    }
498
0
    rv = 1;
499
0
err:
500
0
    ossl_echstore_entry_free(ee);
501
0
    return rv;
502
0
}
503
504
/*
505
 * @brief check a private matches some public
506
 * @param es is the ECH store
507
 * @param priv is the private value
508
 * @return 1 if we have a match, zero otherwise
509
 */
510
static int check_priv_matches(OSSL_ECHSTORE *es, EVP_PKEY *priv)
511
0
{
512
0
    int num, ent, gotone = 0;
513
0
    OSSL_ECHSTORE_ENTRY *ee = NULL;
514
515
0
    num = (es->entries == NULL ? 0 : sk_OSSL_ECHSTORE_ENTRY_num(es->entries));
516
0
    for (ent = 0; ent != num; ent++) {
517
0
        ee = sk_OSSL_ECHSTORE_ENTRY_value(es->entries, ent);
518
0
        if (ee == NULL) {
519
0
            ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
520
0
            return 0;
521
0
        }
522
0
        if (EVP_PKEY_eq(ee->keyshare, priv)) {
523
0
            gotone = 1;
524
0
            break;
525
0
        }
526
0
    }
527
0
    return gotone;
528
0
}
529
530
/*
531
 * @brief decode input ECHConfigList and associate optional private info
532
 * @param es is the OSSL_ECHSTORE
533
 * @param in is the BIO from which we'll get the ECHConfigList
534
 * @param priv is an optional private key
535
 * @param for_retry 1 if the public related to priv ought be in retry_config
536
 */
537
static int ech_read_priv_echconfiglist(OSSL_ECHSTORE *es, BIO *in,
538
    EVP_PKEY *priv, int for_retry)
539
0
{
540
0
    int rv = 0, detfmt, tdeclen = 0;
541
0
    size_t encodedlen = 0, binlen = 0;
542
0
    unsigned char *encodedval = NULL, *binbuf = NULL;
543
0
    BIO *btmp = NULL, *btmp1 = NULL;
544
545
0
    if (es == NULL || in == NULL) {
546
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
547
0
        return 0;
548
0
    }
549
0
    if (ech_bio2buf(in, &encodedval, &encodedlen) != 1) {
550
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
551
0
        return 0;
552
0
    }
553
0
    if (encodedlen >= OSSL_ECH_MAX_ECHCONFIG_LEN) { /* sanity check */
554
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
555
0
        goto err;
556
0
    }
557
0
    if (ech_check_format(encodedval, encodedlen, &detfmt) != 1) {
558
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
559
0
        goto err;
560
0
    }
561
0
    if (detfmt == OSSL_ECH_FMT_BIN) { /* copy buffer if binary format */
562
0
        binbuf = OPENSSL_memdup(encodedval, encodedlen);
563
0
        if (binbuf == NULL)
564
0
            goto err;
565
0
        binlen = encodedlen;
566
0
    }
567
0
    if (detfmt == OSSL_ECH_FMT_B64TXT) {
568
0
        btmp = BIO_new_mem_buf(encodedval, (int)encodedlen);
569
0
        if (btmp == NULL) {
570
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
571
0
            goto err;
572
0
        }
573
0
        btmp1 = BIO_new(BIO_f_base64());
574
0
        if (btmp1 == NULL) {
575
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
576
0
            goto err;
577
0
        }
578
0
        BIO_set_flags(btmp1, BIO_FLAGS_BASE64_NO_NL);
579
0
        btmp = BIO_push(btmp1, btmp);
580
        /* overestimate but good enough */
581
0
        binbuf = OPENSSL_malloc(encodedlen);
582
0
        if (binbuf == NULL)
583
0
            goto err;
584
0
        tdeclen = BIO_read(btmp, binbuf, (int)encodedlen);
585
0
        if (tdeclen <= 0) { /* need int for -1 return in failure case */
586
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
587
0
            goto err;
588
0
        }
589
0
        binlen = tdeclen;
590
0
    }
591
0
    if (ech_decode_and_flatten(es, priv, for_retry, binbuf, binlen) != 1) {
592
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
593
0
        goto err;
594
0
    }
595
0
    if (priv != NULL && check_priv_matches(es, priv) == 0)
596
0
        goto err;
597
0
    rv = 1;
598
0
err:
599
0
    BIO_free_all(btmp);
600
0
    OPENSSL_free(binbuf);
601
0
    OPENSSL_free(encodedval);
602
0
    return rv;
603
0
}
604
605
/*
606
 * API calls built around OSSL_ECHSSTORE
607
 */
608
609
OSSL_ECHSTORE *OSSL_ECHSTORE_new(OSSL_LIB_CTX *libctx, const char *propq)
610
0
{
611
0
    OSSL_ECHSTORE *es = NULL;
612
613
0
    es = OPENSSL_zalloc(sizeof(*es));
614
0
    if (es == NULL)
615
0
        return 0;
616
0
    es->libctx = libctx;
617
0
    if (propq != NULL) {
618
0
        es->propq = OPENSSL_strdup(propq);
619
0
        if (es->propq == NULL) {
620
0
            OPENSSL_free(es);
621
0
            return 0;
622
0
        }
623
0
    }
624
625
0
    return es;
626
0
}
627
628
void OSSL_ECHSTORE_free(OSSL_ECHSTORE *es)
629
0
{
630
0
    if (es == NULL)
631
0
        return;
632
0
    sk_OSSL_ECHSTORE_ENTRY_pop_free(es->entries, ossl_echstore_entry_free);
633
0
    OPENSSL_free(es->propq);
634
0
    OPENSSL_free(es);
635
0
    return;
636
0
}
637
638
int OSSL_ECHSTORE_new_config(OSSL_ECHSTORE *es,
639
    uint16_t echversion, uint8_t max_name_length,
640
    const char *public_name, OSSL_HPKE_SUITE suite)
641
0
{
642
0
    size_t pnlen = 0, publen = OSSL_ECH_CRYPTO_VAR_SIZE;
643
0
    unsigned char pub[OSSL_ECH_CRYPTO_VAR_SIZE];
644
0
    int rv = 0;
645
0
    unsigned char *bp = NULL;
646
0
    size_t bblen = 0;
647
0
    EVP_PKEY *privp = NULL;
648
0
    uint8_t config_id = 0;
649
0
    WPACKET epkt;
650
0
    BUF_MEM *epkt_mem = NULL;
651
0
    OSSL_ECHSTORE_ENTRY *ee = NULL;
652
653
    /* basic checks */
654
0
    if (es == NULL) {
655
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
656
0
        return 0;
657
0
    }
658
0
    pnlen = (public_name == NULL ? 0 : strlen(public_name));
659
0
    if (pnlen == 0 || pnlen > OSSL_ECH_MAX_PUBLICNAME) {
660
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
661
0
        return 0;
662
0
    }
663
    /* this used have more versions and will again in future */
664
0
    switch (echversion) {
665
0
    case OSSL_ECH_RFC9849_VERSION:
666
0
        break;
667
0
    default:
668
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
669
0
        return 0;
670
0
    }
671
    /*
672
     *   Reminder, for draft-13 we want this:
673
     *
674
     *   opaque HpkePublicKey<1..2^16-1>;
675
     *   uint16 HpkeKemId;  // Defined in I-D.irtf-cfrg-hpke
676
     *   uint16 HpkeKdfId;  // Defined in I-D.irtf-cfrg-hpke
677
     *   uint16 HpkeAeadId; // Defined in I-D.irtf-cfrg-hpke
678
     *   struct {
679
     *       HpkeKdfId kdf_id;
680
     *       HpkeAeadId aead_id;
681
     *   } HpkeSymmetricCipherSuite;
682
     *   struct {
683
     *       uint8 config_id;
684
     *       HpkeKemId kem_id;
685
     *       HpkePublicKey public_key;
686
     *       HpkeSymmetricCipherSuite cipher_suites<4..2^16-4>;
687
     *   } HpkeKeyConfig;
688
     *   struct {
689
     *       HpkeKeyConfig key_config;
690
     *       uint8 maximum_name_length;
691
     *       opaque public_name<1..255>;
692
     *       Extension extensions<0..2^16-1>;
693
     *   } ECHConfigContents;
694
     *   struct {
695
     *       uint16 version;
696
     *       uint16 length;
697
     *       select (ECHConfig.version) {
698
     *         case 0xfe0d: ECHConfigContents contents;
699
     *       }
700
     *   } ECHConfig;
701
     *   ECHConfig ECHConfigList<1..2^16-1>;
702
     */
703
0
    if ((epkt_mem = BUF_MEM_new()) == NULL
704
0
        || !BUF_MEM_grow(epkt_mem, OSSL_ECH_MAX_ECHCONFIG_LEN)
705
0
        || !WPACKET_init(&epkt, epkt_mem)) {
706
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
707
0
        goto err_no_epkt;
708
0
    }
709
    /* random config_id */
710
0
    if (RAND_bytes_ex(es->libctx, (unsigned char *)&config_id, 1, 0) <= 0) {
711
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
712
0
        goto err;
713
0
    }
714
    /* key pair */
715
0
    if (OSSL_HPKE_keygen(suite, pub, &publen, &privp, NULL, 0,
716
0
            es->libctx, es->propq)
717
0
        != 1) {
718
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
719
0
        goto err;
720
0
    }
721
    /* config id, KEM, public, KDF, AEAD, max name len, public_name, exts */
722
0
    if ((bp = WPACKET_get_curr(&epkt)) == NULL
723
0
        || !WPACKET_start_sub_packet_u16(&epkt)
724
0
        || !WPACKET_put_bytes_u16(&epkt, echversion)
725
0
        || !WPACKET_start_sub_packet_u16(&epkt)
726
0
        || !WPACKET_put_bytes_u8(&epkt, config_id)
727
0
        || !WPACKET_put_bytes_u16(&epkt, suite.kem_id)
728
0
        || !WPACKET_start_sub_packet_u16(&epkt)
729
0
        || !WPACKET_memcpy(&epkt, pub, publen)
730
0
        || !WPACKET_close(&epkt)
731
0
        || !WPACKET_start_sub_packet_u16(&epkt)
732
0
        || !WPACKET_put_bytes_u16(&epkt, suite.kdf_id)
733
0
        || !WPACKET_put_bytes_u16(&epkt, suite.aead_id)
734
0
        || !WPACKET_close(&epkt)
735
0
        || !WPACKET_put_bytes_u8(&epkt, max_name_length)
736
0
        || !WPACKET_start_sub_packet_u8(&epkt)
737
0
        || !WPACKET_memcpy(&epkt, public_name, pnlen)
738
0
        || !WPACKET_close(&epkt)
739
0
        || !WPACKET_start_sub_packet_u16(&epkt)
740
0
        || !WPACKET_memcpy(&epkt, NULL, 0) /* no extensions */
741
0
        || !WPACKET_close(&epkt)
742
0
        || !WPACKET_close(&epkt)
743
0
        || !WPACKET_close(&epkt)) {
744
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
745
0
        goto err;
746
0
    }
747
    /* bp, bblen has encoding */
748
0
    if (!WPACKET_get_total_written(&epkt, &bblen)) {
749
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
750
0
        goto err;
751
0
    }
752
0
    if ((ee = OPENSSL_zalloc(sizeof(*ee))) == NULL)
753
0
        goto err;
754
0
    ee->suites = OPENSSL_malloc(sizeof(*ee->suites));
755
0
    if (ee->suites == NULL)
756
0
        goto err;
757
0
    ee->version = echversion;
758
0
    ee->pub_len = publen;
759
0
    ee->pub = OPENSSL_memdup(pub, publen);
760
0
    if (ee->pub == NULL)
761
0
        goto err;
762
0
    ee->nsuites = 1;
763
0
    ee->suites[0] = suite;
764
0
    ee->public_name = OPENSSL_strdup(public_name);
765
0
    if (ee->public_name == NULL)
766
0
        goto err;
767
0
    ee->max_name_length = max_name_length;
768
0
    ee->config_id = config_id;
769
0
    ee->keyshare = privp;
770
0
    privp = NULL; /* don't free twice */
771
    /* "steal" the encoding from the memory */
772
0
    ee->encoded = (unsigned char *)epkt_mem->data;
773
0
    ee->encoded_len = bblen;
774
0
    epkt_mem->data = NULL;
775
0
    epkt_mem->length = 0;
776
0
    ee->loadtime = time(0);
777
    /* push entry into store */
778
0
    if (es->entries == NULL)
779
0
        es->entries = sk_OSSL_ECHSTORE_ENTRY_new_null();
780
0
    if (es->entries == NULL) {
781
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
782
0
        goto err;
783
0
    }
784
0
    if (!sk_OSSL_ECHSTORE_ENTRY_push(es->entries, ee)) {
785
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
786
0
        goto err;
787
0
    }
788
0
    WPACKET_finish(&epkt);
789
0
    BUF_MEM_free(epkt_mem);
790
0
    return 1;
791
792
0
err:
793
0
    ossl_echstore_entry_free(ee);
794
0
    EVP_PKEY_free(privp);
795
0
    WPACKET_cleanup(&epkt);
796
0
err_no_epkt:
797
0
    BUF_MEM_free(epkt_mem);
798
0
    return rv;
799
0
}
800
801
int OSSL_ECHSTORE_write_pem(OSSL_ECHSTORE *es, int index, BIO *out)
802
0
{
803
0
    OSSL_ECHSTORE_ENTRY *ee = NULL;
804
0
    int rv = 0, num = 0, chosen = 0, doall = 0;
805
0
    WPACKET epkt; /* used if we want to merge ECHConfigs for output */
806
0
    BUF_MEM *epkt_mem = NULL;
807
0
    size_t allencoded_len;
808
809
0
    if (es == NULL) {
810
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
811
0
        return 0;
812
0
    }
813
0
    num = (es->entries == NULL ? 0 : sk_OSSL_ECHSTORE_ENTRY_num(es->entries));
814
0
    if (num <= 0) {
815
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
816
0
        return 0;
817
0
    }
818
0
    if (index >= num) {
819
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
820
0
        return 0;
821
0
    }
822
0
    if (index == OSSL_ECHSTORE_ALL)
823
0
        doall = 1;
824
0
    else if (index == OSSL_ECHSTORE_LAST)
825
0
        chosen = num - 1;
826
0
    else
827
0
        chosen = index;
828
0
    memset(&epkt, 0, sizeof(epkt));
829
0
    if (doall == 0) {
830
0
        ee = sk_OSSL_ECHSTORE_ENTRY_value(es->entries, chosen);
831
0
        if (ee == NULL || ee->encoded == NULL) {
832
0
            ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
833
0
            return 0;
834
0
        }
835
        /* private key first */
836
0
        if (ee->keyshare != NULL
837
0
            && !PEM_write_bio_PrivateKey(out, ee->keyshare, NULL, NULL, 0,
838
0
                NULL, NULL)) {
839
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
840
0
            goto err;
841
0
        }
842
0
        if (PEM_write_bio(out, PEM_STRING_ECHCONFIG, NULL,
843
0
                ee->encoded, (long)ee->encoded_len)
844
0
            <= 0) {
845
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
846
0
            goto err;
847
0
        }
848
0
    } else {
849
        /* catenate the encodings into one */
850
0
        if ((epkt_mem = BUF_MEM_new()) == NULL
851
0
            || !BUF_MEM_grow(epkt_mem, OSSL_ECH_MAX_ECHCONFIG_LEN)
852
0
            || !WPACKET_init(&epkt, epkt_mem)
853
0
            || !WPACKET_start_sub_packet_u16(&epkt)) {
854
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
855
0
            goto err;
856
0
        }
857
0
        for (chosen = 0; chosen != num; chosen++) {
858
0
            ee = sk_OSSL_ECHSTORE_ENTRY_value(es->entries, chosen);
859
0
            if (ee == NULL || ee->encoded == NULL) {
860
0
                ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
861
0
                return 0;
862
0
            }
863
0
            if (!WPACKET_memcpy(&epkt, ee->encoded, ee->encoded_len)) {
864
0
                ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
865
0
                goto err;
866
0
            }
867
0
        }
868
0
        if (!WPACKET_close(&epkt)) {
869
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
870
0
            goto err;
871
0
        }
872
0
        if (!WPACKET_get_total_written(&epkt, &allencoded_len)) {
873
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
874
0
            goto err;
875
0
        }
876
0
        if (PEM_write_bio(out, PEM_STRING_ECHCONFIG, NULL,
877
0
                (unsigned char *)epkt_mem->data,
878
0
                (long)allencoded_len)
879
0
            <= 0) {
880
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
881
0
            goto err;
882
0
        }
883
0
    }
884
0
    rv = 1;
885
0
err:
886
0
    WPACKET_cleanup(&epkt);
887
0
    BUF_MEM_free(epkt_mem);
888
0
    return rv;
889
0
}
890
891
int OSSL_ECHSTORE_read_echconfiglist(OSSL_ECHSTORE *es, BIO *in)
892
0
{
893
0
    return ech_read_priv_echconfiglist(es, in, NULL, 0);
894
0
}
895
896
int OSSL_ECHSTORE_get1_info(OSSL_ECHSTORE *es, int index, time_t *loaded_secs,
897
    char **public_name, char **echconfig,
898
    int *has_private, int *for_retry)
899
0
{
900
0
    OSSL_ECHSTORE_ENTRY *ee = NULL;
901
0
    unsigned int j = 0;
902
0
    int num = 0;
903
0
    BIO *out = NULL;
904
0
    time_t now = time(0);
905
0
    size_t ehlen;
906
0
    unsigned char *ignore = NULL;
907
908
0
    if (es == NULL || loaded_secs == NULL || public_name == NULL
909
0
        || echconfig == NULL || has_private == NULL || for_retry == NULL) {
910
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
911
0
        return 0;
912
0
    }
913
0
    num = (es->entries == NULL ? 0 : sk_OSSL_ECHSTORE_ENTRY_num(es->entries));
914
0
    if (num == 0 || index < 0 || index >= num) {
915
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
916
0
        return 0;
917
0
    }
918
0
    ee = sk_OSSL_ECHSTORE_ENTRY_value(es->entries, index);
919
0
    if (ee == NULL) {
920
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
921
0
        return 0;
922
0
    }
923
0
    *loaded_secs = now - ee->loadtime;
924
0
    *public_name = NULL;
925
0
    *echconfig = NULL;
926
0
    if (ee->public_name != NULL) {
927
0
        *public_name = OPENSSL_strdup(ee->public_name);
928
0
        if (*public_name == NULL)
929
0
            goto err;
930
0
    }
931
0
    *has_private = (ee->keyshare == NULL ? 0 : 1);
932
0
    *for_retry = ee->for_retry;
933
    /* Now "print" the ECHConfigList */
934
0
    out = BIO_new(BIO_s_mem());
935
0
    if (out == NULL) {
936
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
937
0
        goto err;
938
0
    }
939
0
    if (ee->version != OSSL_ECH_RFC9849_VERSION) {
940
        /* just note we don't support that one today */
941
0
        BIO_printf(out, "[Unsupported version (%04x)]", ee->version);
942
0
    } else {
943
        /* version, config_id, public_name, and kem */
944
0
        BIO_printf(out, "[%04x,%02x,%s,[", ee->version, ee->config_id,
945
0
            ee->public_name != NULL ? (char *)ee->public_name : "NULL");
946
        /* ciphersuites */
947
0
        for (j = 0; j != ee->nsuites; j++) {
948
0
            BIO_printf(out, "%04x,%04x,%04x", ee->suites[j].kem_id,
949
0
                ee->suites[j].kdf_id, ee->suites[j].aead_id);
950
0
            if (j < (ee->nsuites - 1))
951
0
                BIO_printf(out, ",");
952
0
        }
953
0
        BIO_printf(out, "],");
954
        /* public key */
955
0
        for (j = 0; j != ee->pub_len; j++)
956
0
            BIO_printf(out, "%02x", ee->pub[j]);
957
        /* max name length and (only) number of extensions */
958
0
        BIO_printf(out, ",%02x,%02x]", ee->max_name_length,
959
0
            ee->exts == NULL ? 0 : sk_OSSL_ECHEXT_num(ee->exts));
960
0
    }
961
0
    ehlen = BIO_get_mem_data(out, &ignore);
962
0
    if (ehlen > INT_MAX)
963
0
        goto err;
964
0
    *echconfig = OPENSSL_malloc(ehlen + 1);
965
0
    if (*echconfig == NULL)
966
0
        goto err;
967
0
    if (BIO_read(out, *echconfig, (int)ehlen) <= 0) {
968
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
969
0
        goto err;
970
0
    }
971
0
    (*echconfig)[ehlen] = '\0';
972
0
    BIO_free(out);
973
0
    return 1;
974
0
err:
975
0
    BIO_free(out);
976
0
    OPENSSL_free(*public_name);
977
0
    *public_name = NULL;
978
0
    OPENSSL_free(*echconfig);
979
0
    *echconfig = NULL;
980
0
    return 0;
981
0
}
982
983
int OSSL_ECHSTORE_downselect(OSSL_ECHSTORE *es, int index)
984
0
{
985
0
    OSSL_ECHSTORE_ENTRY *ee = NULL;
986
0
    int i, num = 0, chosen = OSSL_ECHSTORE_ALL;
987
988
0
    if (es == NULL) {
989
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
990
0
        return 0;
991
0
    }
992
0
    num = (es->entries == NULL ? 0 : sk_OSSL_ECHSTORE_ENTRY_num(es->entries));
993
0
    if (num == 0) {
994
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
995
0
        return 0;
996
0
    }
997
0
    if (index <= OSSL_ECHSTORE_ALL) {
998
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
999
0
        return 0;
1000
0
    }
1001
0
    if (index == OSSL_ECHSTORE_LAST) {
1002
0
        chosen = num - 1;
1003
0
    } else if (index >= num) {
1004
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
1005
0
        return 0;
1006
0
    } else {
1007
0
        chosen = index;
1008
0
    }
1009
0
    for (i = num - 1; i >= 0; i--) {
1010
0
        if (i == chosen)
1011
0
            continue;
1012
0
        ee = sk_OSSL_ECHSTORE_ENTRY_value(es->entries, i);
1013
0
        ossl_echstore_entry_free(ee);
1014
0
        sk_OSSL_ECHSTORE_ENTRY_delete(es->entries, i);
1015
0
    }
1016
0
    return 1;
1017
0
}
1018
1019
int OSSL_ECHSTORE_set1_key_and_read_pem(OSSL_ECHSTORE *es, EVP_PKEY *priv,
1020
    BIO *in, int for_retry)
1021
0
{
1022
0
    unsigned char *b64 = NULL;
1023
0
    long b64len = 0;
1024
0
    BIO *b64bio = NULL;
1025
0
    int rv = 0;
1026
0
    char *pname = NULL, *pheader = NULL;
1027
1028
    /* we allow for a NULL private key */
1029
0
    if (es == NULL || in == NULL) {
1030
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
1031
0
        return 0;
1032
0
    }
1033
0
    if (PEM_read_bio(in, &pname, &pheader, &b64, &b64len) != 1) {
1034
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
1035
0
        return 0;
1036
0
    }
1037
0
    if (pname == NULL || strcmp(pname, PEM_STRING_ECHCONFIG) != 0) {
1038
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
1039
0
        goto err;
1040
0
    }
1041
0
    b64bio = BIO_new(BIO_s_mem());
1042
0
    if (b64bio == NULL
1043
0
        || BIO_write(b64bio, b64, b64len) <= 0
1044
0
        || ech_read_priv_echconfiglist(es, b64bio, priv, for_retry) != 1) {
1045
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
1046
0
        goto err;
1047
0
    }
1048
0
    rv = 1;
1049
0
err:
1050
0
    OPENSSL_free(pname);
1051
0
    OPENSSL_free(pheader);
1052
0
    BIO_free_all(b64bio);
1053
0
    OPENSSL_free(b64);
1054
0
    return rv;
1055
0
}
1056
1057
int OSSL_ECHSTORE_read_pem(OSSL_ECHSTORE *es, BIO *in, int for_retry)
1058
0
{
1059
0
    EVP_PKEY *priv = NULL;
1060
0
    int rv = 0;
1061
0
    BIO *fbio = BIO_new(BIO_f_buffer());
1062
1063
0
    if (fbio == NULL || es == NULL || in == NULL) {
1064
0
        BIO_free_all(fbio);
1065
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
1066
0
        return 0;
1067
0
    }
1068
    /*
1069
     * Read private key then handoff to set1_key_and_read_pem.
1070
     * We allow for no private key as an option, to handle that
1071
     * the BIO_f_buffer allows us to seek back to the start.
1072
     */
1073
0
    BIO_push(fbio, in);
1074
0
    if (!PEM_read_bio_PrivateKey(fbio, &priv, NULL, NULL)
1075
0
        && BIO_seek(fbio, 0) < 0) {
1076
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
1077
0
        goto err;
1078
0
    }
1079
0
    rv = OSSL_ECHSTORE_set1_key_and_read_pem(es, priv, fbio, for_retry);
1080
0
err:
1081
0
    EVP_PKEY_free(priv);
1082
0
    BIO_pop(fbio);
1083
0
    BIO_free_all(fbio);
1084
0
    return rv;
1085
0
}
1086
1087
int OSSL_ECHSTORE_num_entries(const OSSL_ECHSTORE *es, int *numentries)
1088
0
{
1089
0
    if (es == NULL || numentries == NULL) {
1090
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
1091
0
        return 0;
1092
0
    }
1093
0
    *numentries = (es->entries == NULL ? 0 : sk_OSSL_ECHSTORE_ENTRY_num(es->entries));
1094
0
    return 1;
1095
0
}
1096
1097
int OSSL_ECHSTORE_num_keys(OSSL_ECHSTORE *es, int *numkeys)
1098
0
{
1099
0
    int i, num = 0, count = 0;
1100
0
    OSSL_ECHSTORE_ENTRY *ee = NULL;
1101
1102
0
    if (es == NULL || numkeys == NULL) {
1103
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
1104
0
        return 0;
1105
0
    }
1106
0
    num = (es->entries == NULL ? 0 : sk_OSSL_ECHSTORE_ENTRY_num(es->entries));
1107
0
    for (i = 0; i != num; i++) {
1108
0
        ee = sk_OSSL_ECHSTORE_ENTRY_value(es->entries, i);
1109
0
        if (ee == NULL) {
1110
0
            ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
1111
0
            return 0;
1112
0
        }
1113
0
        count += (ee->keyshare != NULL);
1114
0
    }
1115
0
    *numkeys = count;
1116
0
    return 1;
1117
0
}
1118
1119
int OSSL_ECHSTORE_flush_keys(OSSL_ECHSTORE *es, time_t age)
1120
0
{
1121
0
    OSSL_ECHSTORE_ENTRY *ee = NULL;
1122
0
    int i, num = 0;
1123
0
    time_t now = time(0);
1124
1125
0
    if (es == NULL) {
1126
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
1127
0
        return 0;
1128
0
    }
1129
0
    num = (es->entries == NULL ? 0 : sk_OSSL_ECHSTORE_ENTRY_num(es->entries));
1130
0
    if (num == 0) {
1131
0
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
1132
0
        return 0;
1133
0
    }
1134
0
    for (i = num - 1; i >= 0; i--) {
1135
0
        ee = sk_OSSL_ECHSTORE_ENTRY_value(es->entries, i);
1136
0
        if (ee == NULL) {
1137
0
            ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
1138
0
            return 0;
1139
0
        }
1140
0
        if (ee->keyshare != NULL && ee->loadtime + age <= now) {
1141
0
            ossl_echstore_entry_free(ee);
1142
0
            sk_OSSL_ECHSTORE_ENTRY_delete(es->entries, i);
1143
0
        }
1144
0
    }
1145
0
    return 1;
1146
0
}