/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 | } |