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_helper.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 "internal/ech_helpers.h"
15
16
/* used in ECH crypto derivations (odd format for EBCDIC goodness) */
17
/* "tls ech" */
18
static const char OSSL_ECH_CONTEXT_STRING[] = "\x74\x6c\x73\x20\x65\x63\x68";
19
20
/*
21
 * Construct HPKE "info" input as per spec
22
 * encoding is the ECHconfig being used
23
 * encoding_length is the length of ECHconfig being used
24
 * info is a caller-allocated buffer for results
25
 * info_len is the buffer size on input, used-length on output
26
 * return 1 for success, zero otherwise
27
 */
28
int ossl_ech_make_enc_info(const unsigned char *encoding,
29
    size_t encoding_length,
30
    unsigned char *info, size_t *info_len)
31
0
{
32
0
    WPACKET ipkt = { 0 };
33
34
0
    if (encoding == NULL || info == NULL || info_len == NULL)
35
0
        return 0;
36
0
    if (!WPACKET_init_static_len(&ipkt, info, *info_len, 0)
37
0
        || !WPACKET_memcpy(&ipkt, OSSL_ECH_CONTEXT_STRING,
38
0
            sizeof(OSSL_ECH_CONTEXT_STRING) - 1)
39
        /*
40
         * the zero valued octet is required by the spec, section 7.1 so
41
         * a tiny bit better to add it explicitly rather than depend on
42
         * the context string being NUL terminated
43
         */
44
0
        || !WPACKET_put_bytes_u8(&ipkt, 0)
45
0
        || !WPACKET_memcpy(&ipkt, encoding, encoding_length)
46
0
        || !WPACKET_get_total_written(&ipkt, info_len)) {
47
0
        WPACKET_cleanup(&ipkt);
48
0
        return 0;
49
0
    }
50
0
    WPACKET_cleanup(&ipkt);
51
0
    return 1;
52
0
}
53
54
/*
55
 * Given a CH find the offsets of the session id, extensions and ECH
56
 * ch is the encoded client hello
57
 * ch_len is the length of ch
58
 * sessid_off returns offset of session_id length
59
 * exts_off points to offset of extensions
60
 * exts_len returns length of extensions
61
 * ech_off returns offset of ECH
62
 * echtype returns the ext type of the ECH
63
 * ech_len returns the length of the ECH
64
 * sni_off returns offset of (outer) SNI
65
 * sni_len returns the length of the SNI
66
 * inner 1 if the ECH is marked as an inner, 0 for outer
67
 * return 1 for success, other otherwise
68
 *
69
 * Offsets are set to zero if relevant thing not found.
70
 * Offsets are returned to the type or length field in question.
71
 *
72
 * Note: input here is untrusted!
73
 */
74
int ossl_ech_helper_get_ch_offsets(const unsigned char *ch, size_t ch_len,
75
    size_t *sessid_off, size_t *exts_off,
76
    size_t *exts_len,
77
    size_t *ech_off, uint16_t *echtype,
78
    size_t *ech_len, size_t *sni_off,
79
    size_t *sni_len, int *inner)
80
0
{
81
0
    unsigned int elen = 0, etype = 0, pi_tmp = 0;
82
0
    const unsigned char *pp_tmp = NULL, *chstart = NULL, *estart = NULL;
83
0
    PACKET pkt;
84
0
    int done = 0;
85
86
0
    if (ch == NULL || ch_len == 0 || sessid_off == NULL || exts_off == NULL
87
0
        || ech_off == NULL || echtype == NULL || ech_len == NULL
88
0
        || sni_off == NULL || inner == NULL)
89
0
        return 0;
90
0
    *sessid_off = *exts_off = *ech_off = *sni_off = *sni_len = *ech_len = 0;
91
0
    *echtype = 0xffff;
92
0
    if (!PACKET_buf_init(&pkt, ch, ch_len))
93
0
        return 0;
94
0
    chstart = PACKET_data(&pkt);
95
0
    if (!PACKET_get_net_2(&pkt, &pi_tmp))
96
0
        return 0;
97
    /* if we're not TLSv1.2+ then we can bail, but it's not an error */
98
0
    if (pi_tmp != TLS1_2_VERSION)
99
0
        return 1;
100
    /* chew up the packet to extensions */
101
0
    if (!PACKET_get_bytes(&pkt, &pp_tmp, SSL3_RANDOM_SIZE)
102
0
        || (*sessid_off = PACKET_data(&pkt) - chstart) == 0
103
0
        || !PACKET_get_1(&pkt, &pi_tmp) /* sessid len */
104
0
        || !PACKET_get_bytes(&pkt, &pp_tmp, pi_tmp) /* sessid */
105
0
        || !PACKET_get_net_2(&pkt, &pi_tmp) /* ciphersuite len */
106
0
        || !PACKET_get_bytes(&pkt, &pp_tmp, pi_tmp) /* suites */
107
0
        || !PACKET_get_1(&pkt, &pi_tmp) /* compression meths */
108
0
        || !PACKET_get_bytes(&pkt, &pp_tmp, pi_tmp) /* comp meths */
109
0
        || (*exts_off = PACKET_data(&pkt) - chstart) == 0
110
0
        || !PACKET_get_net_2(&pkt, &pi_tmp) /* len(extensions) */
111
0
        || (*exts_len = (size_t)pi_tmp) == 0)
112
        /*
113
         * unexpectedly, we return 1 here, as doing otherwise will
114
         * break some non-ECH test code that truncates CH messages
115
         * The same is true below when looking through extensions.
116
         * That's ok though, we'll only set those offsets we've
117
         * found.
118
         */
119
0
        return 1;
120
    /* no extensions is theoretically ok, if uninteresting */
121
0
    if (*exts_len == 0)
122
0
        return 1;
123
    /* find what we want from extensions */
124
0
    estart = PACKET_data(&pkt);
125
0
    while (PACKET_remaining(&pkt) > 0
126
0
        && (size_t)(PACKET_data(&pkt) - estart) < *exts_len
127
0
        && done < 2) {
128
0
        if (!PACKET_get_net_2(&pkt, &etype)
129
0
            || !PACKET_get_net_2(&pkt, &elen))
130
0
            return 1; /* see note above */
131
0
        if (etype == TLSEXT_TYPE_ech) {
132
0
            if (elen == 0)
133
0
                return 0;
134
0
            *ech_off = PACKET_data(&pkt) - chstart - 4;
135
0
            *echtype = etype;
136
0
            *ech_len = elen;
137
0
            done++;
138
0
        }
139
0
        if (etype == TLSEXT_TYPE_server_name) {
140
0
            *sni_off = PACKET_data(&pkt) - chstart - 4;
141
0
            *sni_len = elen;
142
0
            done++;
143
0
        }
144
0
        if (!PACKET_get_bytes(&pkt, &pp_tmp, elen))
145
0
            return 1; /* see note above */
146
0
        if (etype == TLSEXT_TYPE_ech)
147
0
            *inner = pp_tmp[0];
148
0
    }
149
0
    return 1;
150
0
}