Line data Source code
1 : #include "fd_tls.h"
2 : #include "fd_tls_proto.h"
3 : #include "fd_tls_serde.h"
4 : #include "../../ballet/x509/fd_x509.h"
5 :
6 : typedef struct fd_tls_u24 tls_u24; /* code generator helper */
7 :
8 : /* RFC 8446 Section 4.1.3: "the server's value [random] will be set to the
9 : SHA-256 hash of 'HelloRetryRequest'" */
10 : static uchar const hello_retry_magic[ 32 ] =
11 : { 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
12 : 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
13 : 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E,
14 : 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C };
15 :
16 : /* Decode ClientHello (RFC 8446 Section 4.1.2) */
17 : long
18 : fd_tls_decode_client_hello( fd_tls_client_hello_t * out,
19 : uchar const * const wire,
20 0 : ulong wire_sz ) {
21 :
22 0 : ulong wire_laddr = (ulong)wire;
23 :
24 : /* Decode static sized part of client hello.
25 : (Assuming that session ID field is of a certain size) */
26 :
27 0 : ushort legacy_version; /* ==FD_TLS_VERSION_TLS12 */
28 0 : uchar legacy_session_id_sz; /* ==0 */
29 :
30 0 : # define FIELDS( FIELD ) \
31 0 : FIELD( 0, &legacy_version, ushort, 1 ) \
32 0 : FIELD( 1, &out->random[0], uchar, 32UL ) \
33 0 : FIELD( 2, &legacy_session_id_sz, uchar, 1 )
34 0 : FD_TLS_DECODE_STATIC_BATCH( FIELDS )
35 0 : # undef FIELDS
36 :
37 0 : if( FD_UNLIKELY( ( legacy_session_id_sz > 32 ) |
38 0 : ( wire_sz < legacy_session_id_sz ) ) )
39 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
40 :
41 0 : out->session_id.buf = (void *)wire_laddr;
42 0 : out->session_id.bufsz = legacy_session_id_sz;
43 0 : wire_laddr += legacy_session_id_sz;
44 0 : wire_sz -= legacy_session_id_sz;
45 :
46 : /* Decode cipher suite list */
47 :
48 0 : if( FD_UNLIKELY( wire_sz<2UL || !FD_LOAD( ushort, (void const *)wire_laddr ) ) )
49 0 : return -FD_TLS_ALERT_DECODE_ERROR;
50 0 : FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(ushort) ) {
51 0 : ushort cipher_suite;
52 0 : FD_TLS_DECODE_FIELD( &cipher_suite, ushort );
53 :
54 0 : switch( cipher_suite ) {
55 0 : case FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256:
56 0 : out->cipher_suites.aes_128_gcm_sha256 = 1;
57 0 : break;
58 0 : default:
59 : /* Ignore unsupported cipher suites ... */
60 0 : break;
61 0 : }
62 0 : }
63 0 : FD_TLS_DECODE_LIST_END
64 :
65 : /* Decode next static sized part of client hello */
66 :
67 0 : uchar legacy_compression_method_cnt; /* == 1 */
68 0 : uchar legacy_compression_methods[ 1 ]; /* =={0} */
69 :
70 0 : # define FIELDS( FIELD ) \
71 0 : FIELD( 5, &legacy_compression_method_cnt, uchar, 1 ) \
72 0 : FIELD( 6, &legacy_compression_methods[0], uchar, 1 )
73 0 : FD_TLS_DECODE_STATIC_BATCH( FIELDS )
74 0 : # undef FIELDS
75 :
76 0 : if( FD_UNLIKELY( ( legacy_compression_method_cnt != 1 )
77 0 : | ( legacy_compression_methods[0] != 0 ) ) )
78 0 : return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
79 :
80 : /* Read extensions */
81 :
82 0 : ulong seen = 0UL;
83 0 : FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(uchar) ) {
84 : /* Read extension type and length */
85 0 : ushort ext_type;
86 0 : ushort ext_sz;
87 0 : # define FIELDS( FIELD ) \
88 0 : FIELD( 0, &ext_type, ushort, 1 ) \
89 0 : FIELD( 1, &ext_sz, ushort, 1 )
90 0 : FD_TLS_DECODE_STATIC_BATCH( FIELDS )
91 0 : # undef FIELDS
92 :
93 : /* Bounds check extension data */
94 0 : if( FD_UNLIKELY( ext_sz > wire_sz ) )
95 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
96 :
97 : /* RFC 8446 Section 4.2: at most one extension of each type */
98 0 : if( ext_type<64 ) {
99 0 : if( FD_UNLIKELY( seen & (1UL<<ext_type) ) ) return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
100 0 : seen |= 1UL<<ext_type;
101 0 : }
102 :
103 : /* Decode extension data */
104 0 : uchar const * ext_data = (uchar const *)wire_laddr;
105 0 : long ext_parse_res;
106 0 : switch( ext_type ) {
107 0 : case FD_TLS_EXT_SUPPORTED_VERSIONS:
108 0 : ext_parse_res = fd_tls_decode_ext_supported_versions( &out->supported_versions, ext_data, ext_sz );
109 0 : break;
110 0 : case FD_TLS_EXT_SERVER_NAME:
111 0 : ext_parse_res = fd_tls_decode_ext_server_name( &out->server_name, ext_data, ext_sz );
112 0 : break;
113 0 : case FD_TLS_EXT_SUPPORTED_GROUPS:
114 0 : ext_parse_res = fd_tls_decode_ext_supported_groups( &out->supported_groups, ext_data, ext_sz );
115 0 : break;
116 0 : case FD_TLS_EXT_SIGNATURE_ALGORITHMS:
117 0 : ext_parse_res = fd_tls_decode_ext_signature_algorithms( &out->signature_algorithms, ext_data, ext_sz );
118 0 : break;
119 0 : case FD_TLS_EXT_SIGNATURE_ALGORITHMS_CERT:
120 0 : ext_parse_res = fd_tls_decode_ext_signature_algorithms( &out->signature_algorithms_cert, ext_data, ext_sz );
121 0 : break;
122 0 : case FD_TLS_EXT_KEY_SHARE:
123 0 : ext_parse_res = fd_tls_decode_key_share_list( &out->key_share, ext_data, ext_sz );
124 0 : break;
125 0 : case FD_TLS_EXT_QUIC_TRANSPORT_PARAMS:
126 0 : ext_parse_res = fd_tls_decode_ext_quic_tp( &out->quic_tp, ext_data, ext_sz );
127 0 : break;
128 0 : case FD_TLS_EXT_ALPN:
129 0 : ext_parse_res = fd_tls_decode_ext_alpn( &out->alpn, ext_data, ext_sz );
130 0 : break;
131 0 : default:
132 0 : ext_parse_res = (long)ext_sz;
133 0 : break;
134 0 : }
135 0 : if( FD_UNLIKELY( ext_parse_res<0L ) )
136 0 : return ext_parse_res;
137 0 : if( FD_UNLIKELY( ext_parse_res != (long)ext_sz ) )
138 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
139 :
140 : /* Seek to next extension */
141 0 : wire_laddr += ext_sz;
142 0 : wire_sz -= ext_sz;
143 0 : }
144 0 : FD_TLS_DECODE_LIST_END
145 :
146 0 : return (long)( wire_laddr - (ulong)wire );
147 0 : }
148 :
149 : long
150 : fd_tls_encode_client_hello( fd_tls_client_hello_t const * in,
151 : uchar * wire,
152 0 : ulong wire_sz ) {
153 :
154 0 : ulong wire_laddr = (ulong)wire;
155 :
156 : /* Encode static sized part of client hello */
157 :
158 0 : ushort legacy_version = FD_TLS_VERSION_TLS12;
159 0 : uchar legacy_session_id_sz = (uchar)in->session_id.bufsz;
160 0 : ushort cipher_suite_sz = 1*sizeof(ushort);
161 0 : ushort cipher_suites[1] = { FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256 };
162 0 : uchar legacy_comp_method_sz = 1;
163 0 : uchar legacy_comp_method[1] = {0};
164 :
165 0 : # define FIELDS( FIELD ) \
166 0 : FIELD( 0, &legacy_version, ushort, 1 ) \
167 0 : FIELD( 1, in->random, uchar, 32UL ) \
168 0 : FIELD( 2, &legacy_session_id_sz, uchar, 1 )
169 0 : FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
170 0 : # undef FIELDS
171 :
172 : /* Encode session_id (0 for QUIC, 32 random bytes for TCP middlebox compat) */
173 :
174 0 : if( legacy_session_id_sz ) {
175 0 : if( FD_UNLIKELY( legacy_session_id_sz > 32 ) )
176 0 : return -(long)FD_TLS_ALERT_INTERNAL_ERROR;
177 0 : if( FD_UNLIKELY( (ulong)legacy_session_id_sz > wire_sz ) )
178 0 : return -(long)FD_TLS_ALERT_INTERNAL_ERROR;
179 0 : fd_memcpy( (void *)wire_laddr, in->session_id.buf, legacy_session_id_sz );
180 0 : wire_laddr += legacy_session_id_sz;
181 0 : wire_sz -= legacy_session_id_sz;
182 0 : }
183 :
184 0 : # define FIELDS( FIELD ) \
185 0 : FIELD( 0, &cipher_suite_sz, ushort, 1 ) \
186 0 : FIELD( 1, cipher_suites, ushort, 1 ) \
187 0 : FIELD( 2, &legacy_comp_method_sz, uchar, 1 ) \
188 0 : FIELD( 3, legacy_comp_method, uchar, 1 )
189 0 : FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
190 0 : # undef FIELDS
191 :
192 : /* Encode extensions */
193 :
194 0 : ushort * extension_tot_sz = FD_TLS_SKIP_FIELD( ushort );
195 0 : ulong extension_start = wire_laddr;
196 :
197 0 : ushort ext_supported_versions_ext_type = FD_TLS_EXT_SUPPORTED_VERSIONS;
198 0 : ushort ext_supported_versions_ext_sz = 3;
199 0 : uchar ext_supported_versions_sz = 2;
200 0 : ushort ext_supported_versions[1] = { FD_TLS_VERSION_TLS13 };
201 :
202 0 : ushort ext_key_share_ext_type = FD_TLS_EXT_KEY_SHARE;
203 0 : ushort ext_key_share_ext_sz = 38;
204 0 : ushort ext_key_share_sz1 = 36;
205 0 : ushort ext_key_share_group = FD_TLS_GROUP_X25519;
206 0 : ushort ext_key_share_sz = 32;
207 :
208 0 : ushort ext_supported_groups_ext_type = FD_TLS_EXT_SUPPORTED_GROUPS;
209 0 : ushort ext_supported_groups_ext_sz = 4;
210 0 : ushort ext_supported_groups_sz = 2;
211 0 : ushort ext_supported_groups[1] = { FD_TLS_GROUP_X25519 };
212 :
213 : /* Advertise the signature algorithms the caller opted into, in
214 : descending order of preference */
215 :
216 0 : ushort ext_sigalg[5];
217 0 : ulong ext_sigalg_cnt = 0UL;
218 0 : if( in->signature_algorithms.ecdsa_secp256r1_sha256 )
219 0 : ext_sigalg[ ext_sigalg_cnt++ ] = FD_TLS_SIGNATURE_ECDSA_SECP256R1_SHA256;
220 0 : if( in->signature_algorithms.ed25519 )
221 0 : ext_sigalg[ ext_sigalg_cnt++ ] = FD_TLS_SIGNATURE_ED25519;
222 0 : if( in->signature_algorithms.rsa_pss_rsae_sha256 )
223 0 : ext_sigalg[ ext_sigalg_cnt++ ] = FD_TLS_SIGNATURE_RSA_PSS_RSAE_SHA256;
224 0 : if( in->signature_algorithms.rsa_pss_rsae_sha384 )
225 0 : ext_sigalg[ ext_sigalg_cnt++ ] = FD_TLS_SIGNATURE_RSA_PSS_RSAE_SHA384;
226 0 : if( in->signature_algorithms.rsa_pss_rsae_sha512 )
227 0 : ext_sigalg[ ext_sigalg_cnt++ ] = FD_TLS_SIGNATURE_RSA_PSS_RSAE_SHA512;
228 0 : if( FD_UNLIKELY( !ext_sigalg_cnt ) ) return -(long)FD_TLS_ALERT_INTERNAL_ERROR;
229 :
230 0 : ushort ext_sigalg_ext_type = FD_TLS_EXT_SIGNATURE_ALGORITHMS;
231 0 : ushort ext_sigalg_sz = (ushort)( 2UL*ext_sigalg_cnt );
232 0 : ushort ext_sigalg_ext_sz = (ushort)( 2U+ext_sigalg_sz );
233 :
234 0 : # define FIELDS( FIELD ) \
235 0 : FIELD( 0, &ext_supported_versions_ext_type, ushort, 1 ) \
236 0 : FIELD( 1, &ext_supported_versions_ext_sz, ushort, 1 ) \
237 0 : FIELD( 2, &ext_supported_versions_sz, uchar, 1 ) \
238 0 : FIELD( 3, ext_supported_versions, ushort, 1 ) \
239 0 : FIELD( 4, &ext_key_share_ext_type, ushort, 1 ) \
240 0 : FIELD( 5, &ext_key_share_ext_sz, ushort, 1 ) \
241 0 : FIELD( 6, &ext_key_share_sz1, ushort, 1 ) \
242 0 : FIELD( 7, &ext_key_share_group, ushort, 1 ) \
243 0 : FIELD( 8, &ext_key_share_sz, ushort, 1 ) \
244 0 : FIELD( 9, &in->key_share.x25519[0], uchar, 32UL ) \
245 0 : FIELD(10, &ext_supported_groups_ext_type, ushort, 1 ) \
246 0 : FIELD(11, &ext_supported_groups_ext_sz, ushort, 1 ) \
247 0 : FIELD(12, &ext_supported_groups_sz, ushort, 1 ) \
248 0 : FIELD(13, ext_supported_groups, ushort, 1 ) \
249 0 : FIELD(14, &ext_sigalg_ext_type, ushort, 1 ) \
250 0 : FIELD(15, &ext_sigalg_ext_sz, ushort, 1 ) \
251 0 : FIELD(16, &ext_sigalg_sz, ushort, 1 ) \
252 0 : FIELD(17, ext_sigalg, ushort, ext_sigalg_cnt )
253 0 : FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
254 0 : # undef FIELDS
255 :
256 0 : do {
257 0 : ushort schemes[6];
258 0 : ulong cnt = 0UL;
259 0 : if( in->signature_algorithms_cert.ecdsa_secp256r1_sha256 ) schemes[cnt++] = FD_TLS_SIGNATURE_ECDSA_SECP256R1_SHA256;
260 0 : if( in->signature_algorithms_cert.ecdsa_secp384r1_sha384 ) schemes[cnt++] = FD_TLS_SIGNATURE_ECDSA_SECP384R1_SHA384;
261 0 : if( in->signature_algorithms_cert.ed25519 ) schemes[cnt++] = FD_TLS_SIGNATURE_ED25519;
262 0 : if( in->signature_algorithms_cert.rsa_pkcs1_sha256 ) schemes[cnt++] = FD_TLS_SIGNATURE_RSA_PKCS1_SHA256;
263 0 : if( in->signature_algorithms_cert.rsa_pkcs1_sha384 ) schemes[cnt++] = FD_TLS_SIGNATURE_RSA_PKCS1_SHA384;
264 0 : if( in->signature_algorithms_cert.rsa_pkcs1_sha512 ) schemes[cnt++] = FD_TLS_SIGNATURE_RSA_PKCS1_SHA512;
265 0 : if( !cnt ) break;
266 0 : ushort type = FD_TLS_EXT_SIGNATURE_ALGORITHMS_CERT;
267 0 : ushort list_sz = (ushort)(2UL*cnt);
268 0 : ushort ext_sz = (ushort)(list_sz+2U);
269 0 : # define FIELDS( FIELD ) \
270 0 : FIELD( 0, &type, ushort, 1 ) \
271 0 : FIELD( 1, &ext_sz, ushort, 1 ) \
272 0 : FIELD( 2, &list_sz, ushort, 1 ) \
273 0 : FIELD( 3, schemes, ushort, cnt )
274 0 : FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
275 0 : # undef FIELDS
276 0 : } while(0);
277 :
278 : /* Add Server Name Indication (SNI) */
279 :
280 0 : if( in->server_name.host_name_len ) {
281 0 : ushort sni_name_len = in->server_name.host_name_len;
282 0 : ushort sni_list_len = (ushort)( 1 + 2 + sni_name_len ); /* name_type(1) + name_len(2) + name */
283 0 : ushort sni_ext_type = FD_TLS_EXT_SERVER_NAME;
284 0 : ushort sni_ext_sz = (ushort)( 2 + sni_list_len ); /* list_len(2) + list */
285 0 : uchar sni_name_type = FD_TLS_SERVER_NAME_TYPE_DNS;
286 0 : # define FIELDS( FIELD ) \
287 0 : FIELD( 0, &sni_ext_type, ushort, 1 ) \
288 0 : FIELD( 1, &sni_ext_sz, ushort, 1 ) \
289 0 : FIELD( 2, &sni_list_len, ushort, 1 ) \
290 0 : FIELD( 3, &sni_name_type, uchar, 1 ) \
291 0 : FIELD( 4, &sni_name_len, ushort, 1 ) \
292 0 : FIELD( 5, in->server_name.host_name, uchar, sni_name_len )
293 0 : FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
294 0 : # undef FIELDS
295 0 : }
296 :
297 : /* Add ALPN */
298 :
299 0 : if( in->alpn.bufsz ) {
300 0 : fd_tls_ext_hdr_t ext_hdr = { .type = FD_TLS_EXT_ALPN,
301 0 : .sz = (ushort)( in->alpn.bufsz+2 ) };
302 0 : FD_TLS_ENCODE_SUB( fd_tls_encode_ext_hdr, &ext_hdr );
303 0 : FD_TLS_ENCODE_SUB( fd_tls_encode_ext_alpn, &in->alpn );
304 0 : }
305 :
306 : /* Add QUIC transport params */
307 :
308 0 : if( in->quic_tp.buf ) {
309 0 : ushort quic_tp_ext_type = FD_TLS_EXT_QUIC_TRANSPORT_PARAMS;
310 0 : ushort quic_tp_ext_sz = (ushort)in->quic_tp.bufsz;
311 0 : # define FIELDS( FIELD ) \
312 0 : FIELD( 0, &quic_tp_ext_type, ushort, 1 ); \
313 0 : FIELD( 1, &quic_tp_ext_sz, ushort, 1 ); \
314 0 : FIELD( 2, in->quic_tp.buf, uchar, in->quic_tp.bufsz );
315 0 : FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
316 0 : # undef FIELDS
317 0 : }
318 :
319 0 : FD_STORE( ushort, extension_tot_sz, fd_ushort_bswap( (ushort)( (ulong)wire_laddr - extension_start ) ) );
320 0 : return (long)( wire_laddr - (ulong)wire );
321 0 : }
322 :
323 : /* Decode ServerHello (RFC 8446 Section 4.1.3) */
324 : long
325 : fd_tls_decode_server_hello( fd_tls_server_hello_t * out,
326 : uchar const * wire,
327 0 : ulong wire_sz ) {
328 :
329 0 : ulong wire_laddr = (ulong)wire;
330 :
331 : /* Decode static sized part of server hello */
332 :
333 0 : ushort legacy_version; /* ==FD_TLS_VERSION_TLS12 */
334 0 : uchar legacy_session_id_sz; /* 0 for QUIC, 0-32 for TCP */
335 0 : ushort cipher_suite; /* ==FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256 */
336 0 : uchar legacy_compression_method; /* ==0 */
337 :
338 0 : # define FIELDS( FIELD ) \
339 0 : FIELD( 0, &legacy_version, ushort, 1 ) \
340 0 : FIELD( 1, &out->random[0], uchar, 32UL ) \
341 0 : FIELD( 2, &legacy_session_id_sz, uchar, 1 )
342 0 : FD_TLS_DECODE_STATIC_BATCH( FIELDS )
343 0 : # undef FIELDS
344 :
345 : /* Skip legacy_session_id (echoed back for TCP middlebox compat) */
346 :
347 0 : if( FD_UNLIKELY( legacy_session_id_sz > 32 ) )
348 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
349 0 : if( FD_UNLIKELY( (ulong)legacy_session_id_sz > wire_sz ) )
350 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
351 0 : out->session_id.buf = (uchar const *)wire_laddr;
352 0 : out->session_id.bufsz = legacy_session_id_sz;
353 0 : wire_laddr += legacy_session_id_sz;
354 0 : wire_sz -= legacy_session_id_sz;
355 :
356 0 : # define FIELDS( FIELD ) \
357 0 : FIELD( 0, &cipher_suite, ushort, 1 ) \
358 0 : FIELD( 1, &legacy_compression_method, uchar, 1 )
359 0 : FD_TLS_DECODE_STATIC_BATCH( FIELDS )
360 0 : # undef FIELDS
361 :
362 0 : if( FD_UNLIKELY( ( legacy_version != FD_TLS_VERSION_TLS12 )
363 0 : | ( legacy_compression_method != 0 ) ) )
364 0 : return -(long)FD_TLS_ALERT_PROTOCOL_VERSION;
365 :
366 0 : out->cipher_suite = cipher_suite;
367 :
368 : /* Reject HelloRetryRequest (we only support X25519) */
369 :
370 0 : if( FD_UNLIKELY( 0==memcmp( out->random, hello_retry_magic, 32 ) ) )
371 0 : return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
372 :
373 : /* Read extensions */
374 :
375 0 : ulong seen = 0UL;
376 0 : FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(uchar) ) {
377 : /* Read extension type and length */
378 0 : ushort ext_type;
379 0 : ushort ext_sz;
380 0 : # define FIELDS( FIELD ) \
381 0 : FIELD( 0, &ext_type, ushort, 1 ) \
382 0 : FIELD( 1, &ext_sz, ushort, 1 )
383 0 : FD_TLS_DECODE_STATIC_BATCH( FIELDS )
384 0 : # undef FIELDS
385 :
386 : /* Bounds check extension data */
387 0 : if( FD_UNLIKELY( ext_sz > wire_sz ) )
388 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
389 :
390 : /* RFC 8446 Section 4.2: at most one extension of each type */
391 0 : if( ext_type<64 ) {
392 0 : if( FD_UNLIKELY( seen & (1UL<<ext_type) ) ) return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
393 0 : seen |= 1UL<<ext_type;
394 0 : }
395 :
396 0 : ulong next_field = wire_laddr + ext_sz;
397 0 : ulong next_sz = wire_sz - ext_sz;
398 :
399 : /* Decode extension data */
400 0 : uchar const * ext_data = (uchar const *)wire_laddr;
401 0 : long ext_parse_res;
402 0 : switch( ext_type ) {
403 0 : case FD_TLS_EXT_SUPPORTED_VERSIONS: {
404 0 : ushort chosen_version;
405 0 : FD_TLS_DECODE_FIELD( &chosen_version, ushort );
406 0 : ext_parse_res = 2L;
407 0 : if( FD_UNLIKELY( chosen_version!=FD_TLS_VERSION_TLS13 ) )
408 0 : return -(long)FD_TLS_ALERT_PROTOCOL_VERSION;
409 0 : break;
410 0 : }
411 0 : case FD_TLS_EXT_KEY_SHARE:
412 0 : ext_parse_res = fd_tls_decode_key_share( &out->key_share, ext_data, ext_sz );
413 0 : break;
414 0 : default:
415 : /* RFC 8446 Section 4.2: a ServerHello may only carry responses
416 : to extensions the client offered */
417 0 : return -(long)FD_TLS_ALERT_UNSUPPORTED_EXTENSION;
418 0 : }
419 :
420 0 : if( FD_UNLIKELY( ext_parse_res<0L ) )
421 0 : return ext_parse_res;
422 0 : if( FD_UNLIKELY( ext_parse_res != (long)ext_sz ) )
423 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
424 :
425 0 : wire_laddr = next_field;
426 0 : wire_sz = next_sz;
427 0 : }
428 0 : FD_TLS_DECODE_LIST_END
429 :
430 : /* Check for required extensions. Without supported_versions this
431 : is a TLS 1.2 ServerHello (RFC 8446 Section 4.1.3). */
432 :
433 0 : if( FD_UNLIKELY( !(seen & (1UL<<FD_TLS_EXT_SUPPORTED_VERSIONS)) ) )
434 0 : return -(long)FD_TLS_ALERT_PROTOCOL_VERSION;
435 0 : if( FD_UNLIKELY( !out->key_share.has_x25519 ) )
436 0 : return -(long)FD_TLS_ALERT_MISSING_EXTENSION;
437 :
438 0 : return (long)( wire_laddr - (ulong)wire );
439 0 : }
440 :
441 : long
442 : fd_tls_encode_server_hello( fd_tls_server_hello_t const * in,
443 : uchar * wire,
444 0 : ulong wire_sz ) {
445 :
446 0 : ulong wire_laddr = (ulong)wire;
447 :
448 : /* Encode static sized part of server hello.
449 : (Assuming that session ID field is of a certain size) */
450 :
451 0 : ushort legacy_version = FD_TLS_VERSION_TLS12;
452 0 : uchar legacy_session_id_sz = (uchar)in->session_id.bufsz;
453 0 : ushort cipher_suite = FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256;
454 0 : uchar legacy_compression_method = 0;
455 :
456 0 : # define FIELDS( FIELD ) \
457 0 : FIELD( 0, &legacy_version, ushort, 1 ) \
458 0 : FIELD( 1, &in->random[0], uchar, 32UL ) \
459 0 : FIELD( 2, &legacy_session_id_sz, uchar, 1 ) \
460 0 : FIELD( 3, in->session_id.buf, uchar, legacy_session_id_sz ) \
461 0 : FIELD( 4, &cipher_suite, ushort, 1 ) \
462 0 : FIELD( 5, &legacy_compression_method, uchar, 1 )
463 0 : FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
464 0 : # undef FIELDS
465 :
466 : /* Encode extensions */
467 :
468 0 : ushort * extension_tot_sz = FD_TLS_SKIP_FIELD( ushort );
469 0 : ulong extension_start = wire_laddr;
470 :
471 0 : ushort ext_supported_versions_ext_type = FD_TLS_EXT_SUPPORTED_VERSIONS;
472 0 : ushort ext_supported_versions[1] = { FD_TLS_VERSION_TLS13 };
473 0 : ushort ext_supported_versions_ext_sz = sizeof(ext_supported_versions);
474 :
475 0 : ushort ext_key_share_ext_type = FD_TLS_EXT_KEY_SHARE;
476 0 : ushort ext_key_share_ext_sz = sizeof(ushort) + sizeof(ushort) + 32UL;
477 0 : ushort ext_key_share_group = FD_TLS_GROUP_X25519;
478 0 : ushort ext_key_share_sz = 32UL;
479 :
480 0 : # define FIELDS( FIELD ) \
481 0 : FIELD( 0, &ext_supported_versions_ext_type, ushort, 1 ) \
482 0 : FIELD( 1, &ext_supported_versions_ext_sz, ushort, 1 ) \
483 0 : FIELD( 2, ext_supported_versions, ushort, 1 ) \
484 0 : FIELD( 3, &ext_key_share_ext_type, ushort, 1 ) \
485 0 : FIELD( 4, &ext_key_share_ext_sz, ushort, 1 ) \
486 0 : FIELD( 5, &ext_key_share_group, ushort, 1 ) \
487 0 : FIELD( 6, &ext_key_share_sz, ushort, 1 ) \
488 0 : FIELD( 7, &in->key_share.x25519[0], uchar, 32UL )
489 0 : FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
490 0 : # undef FIELDS
491 :
492 0 : FD_STORE( ushort, extension_tot_sz, fd_ushort_bswap( (ushort)( (ulong)wire_laddr - extension_start ) ) );
493 0 : return (long)( wire_laddr - (ulong)wire );
494 0 : }
495 :
496 : long
497 : fd_tls_encode_hello_retry_request( fd_tls_server_hello_t const * in,
498 : uchar * wire,
499 0 : ulong wire_sz ) {
500 :
501 0 : ulong wire_laddr = (ulong)wire;
502 :
503 0 : ushort legacy_version = FD_TLS_VERSION_TLS12;
504 0 : uchar legacy_session_id_sz = (uchar)in->session_id.bufsz;
505 0 : ushort cipher_suite = FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256;
506 0 : uchar legacy_compression_method = 0;
507 :
508 0 : # define FIELDS( FIELD ) \
509 0 : FIELD( 0, &legacy_version, ushort, 1 ) \
510 0 : FIELD( 1, hello_retry_magic, uchar, 32UL ) \
511 0 : FIELD( 2, &legacy_session_id_sz, uchar, 1 ) \
512 0 : FIELD( 3, in->session_id.buf, uchar, legacy_session_id_sz ) \
513 0 : FIELD( 4, &cipher_suite, ushort, 1 ) \
514 0 : FIELD( 5, &legacy_compression_method, uchar, 1 )
515 0 : FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
516 0 : # undef FIELDS
517 :
518 : /* Encode extensions */
519 :
520 0 : ushort * extension_tot_sz = FD_TLS_SKIP_FIELD( ushort );
521 0 : ulong extension_start = wire_laddr;
522 :
523 0 : ushort ext_supported_versions_ext_type = FD_TLS_EXT_SUPPORTED_VERSIONS;
524 0 : ushort ext_supported_versions[1] = { FD_TLS_VERSION_TLS13 };
525 0 : ushort ext_supported_versions_ext_sz = sizeof(ext_supported_versions);
526 :
527 0 : ushort ext_key_share_ext_type = FD_TLS_EXT_KEY_SHARE;
528 0 : ushort ext_key_share_ext_sz = sizeof(ushort);
529 0 : ushort ext_key_share_group = FD_TLS_GROUP_X25519;
530 :
531 0 : # define FIELDS( FIELD ) \
532 0 : FIELD( 0, &ext_supported_versions_ext_type, ushort, 1 ) \
533 0 : FIELD( 1, &ext_supported_versions_ext_sz, ushort, 1 ) \
534 0 : FIELD( 2, ext_supported_versions, ushort, 1 ) \
535 0 : FIELD( 3, &ext_key_share_ext_type, ushort, 1 ) \
536 0 : FIELD( 4, &ext_key_share_ext_sz, ushort, 1 ) \
537 0 : FIELD( 5, &ext_key_share_group, ushort, 1 )
538 0 : FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
539 0 : # undef FIELDS
540 :
541 0 : FD_STORE( ushort, extension_tot_sz, fd_ushort_bswap( (ushort)( (ulong)wire_laddr - extension_start ) ) );
542 0 : return (long)( wire_laddr - (ulong)wire );
543 0 : }
544 :
545 : /* Decode EncryptedExtensions (RFC 8446 Section 4.3.1) */
546 : long
547 : fd_tls_decode_enc_ext( fd_tls_enc_ext_t * const out,
548 : uchar const * const wire,
549 0 : ulong wire_sz ) {
550 :
551 0 : ulong wire_laddr = (ulong)wire;
552 :
553 0 : ulong seen = 0UL;
554 0 : FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(uchar) ) {
555 0 : ushort ext_type;
556 0 : ushort ext_sz;
557 0 : # define FIELDS( FIELD ) \
558 0 : FIELD( 0, &ext_type, ushort, 1 ) \
559 0 : FIELD( 1, &ext_sz, ushort, 1 )
560 0 : FD_TLS_DECODE_STATIC_BATCH( FIELDS )
561 0 : # undef FIELDS
562 :
563 : /* Bounds check extension data
564 : (list_stop declared by DECODE_LIST macro) */
565 0 : if( FD_UNLIKELY( wire_laddr + ext_sz > list_stop ) )
566 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
567 :
568 : /* RFC 8446 Section 4.2: at most one extension of each type */
569 0 : if( ext_type<64 ) {
570 0 : if( FD_UNLIKELY( seen & (1UL<<ext_type) ) ) return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
571 0 : seen |= 1UL<<ext_type;
572 0 : }
573 :
574 0 : switch( ext_type ) {
575 0 : case FD_TLS_EXT_SERVER_NAME:
576 0 : if( FD_UNLIKELY( ext_sz ) ) return -FD_TLS_ALERT_DECODE_ERROR;
577 0 : out->server_name = 1;
578 0 : break;
579 0 : case FD_TLS_EXT_SUPPORTED_GROUPS: {
580 : /* RFC 8446 Section 4.2.7 explicitly permits this in EE. */
581 0 : fd_tls_ext_supported_groups_t groups = {0};
582 0 : long res = fd_tls_decode_ext_supported_groups( &groups, (uchar const *)wire_laddr, ext_sz );
583 0 : if( FD_UNLIKELY( res<0L ) ) return res;
584 0 : if( FD_UNLIKELY( res!=(long)ext_sz ) ) return -FD_TLS_ALERT_DECODE_ERROR;
585 0 : break;
586 0 : }
587 0 : case FD_TLS_EXT_ALPN: {
588 0 : long res = fd_tls_decode_ext_alpn( &out->alpn, (uchar const *)wire_laddr, ext_sz );
589 0 : if( FD_UNLIKELY( res<0L ) )
590 0 : return res;
591 0 : if( FD_UNLIKELY( res!=(long)ext_sz ) )
592 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
593 0 : if( FD_UNLIKELY( out->alpn.bufsz != 1UL+out->alpn.buf[0] ) )
594 0 : return -FD_TLS_ALERT_DECODE_ERROR;
595 0 : break;
596 0 : }
597 0 : case FD_TLS_EXT_QUIC_TRANSPORT_PARAMS:
598 0 : if( FD_UNLIKELY( ext_sz > FD_TLS_EXT_QUIC_PARAMS_SZ_MAX ) )
599 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
600 0 : out->quic_tp.buf = (void *)wire_laddr;
601 0 : out->quic_tp.bufsz = (ushort)ext_sz;
602 0 : break;
603 0 : default:
604 0 : return -(long)FD_TLS_ALERT_UNSUPPORTED_EXTENSION;
605 0 : }
606 :
607 0 : wire_laddr += ext_sz;
608 0 : wire_sz -= ext_sz;
609 0 : }
610 0 : FD_TLS_DECODE_LIST_END
611 :
612 0 : return (long)( wire_laddr - (ulong)wire );
613 0 : }
614 :
615 : /* Decode CertificateRequest (RFC 8446 Section 4.3.2) */
616 : long
617 : fd_tls_decode_cert_req( fd_tls_ext_signature_algorithms_t * out,
618 : uchar const * wire,
619 0 : ulong wire_sz ) {
620 :
621 0 : ulong wire_laddr = (ulong)wire;
622 :
623 : /* certificate_request_context is empty outside of post-handshake
624 : authentication, which is not supported */
625 0 : uchar ctx_sz;
626 0 : FD_TLS_DECODE_FIELD( &ctx_sz, uchar );
627 0 : if( FD_UNLIKELY( ctx_sz ) )
628 0 : return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
629 :
630 0 : ulong seen = 0UL;
631 0 : FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(uchar) ) {
632 0 : ushort ext_type;
633 0 : ushort ext_sz;
634 0 : # define FIELDS( FIELD ) \
635 0 : FIELD( 0, &ext_type, ushort, 1 ) \
636 0 : FIELD( 1, &ext_sz, ushort, 1 )
637 0 : FD_TLS_DECODE_STATIC_BATCH( FIELDS )
638 0 : # undef FIELDS
639 :
640 0 : if( FD_UNLIKELY( ext_sz > wire_sz ) )
641 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
642 :
643 : /* RFC 8446 Section 4.2: at most one extension of each type */
644 0 : if( ext_type<64 ) {
645 0 : if( FD_UNLIKELY( seen & (1UL<<ext_type) ) ) return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
646 0 : seen |= 1UL<<ext_type;
647 0 : }
648 :
649 0 : long ext_parse_res;
650 0 : switch( ext_type ) {
651 0 : case FD_TLS_EXT_SIGNATURE_ALGORITHMS:
652 0 : ext_parse_res = fd_tls_decode_ext_signature_algorithms( out, (uchar const *)wire_laddr, ext_sz );
653 0 : break;
654 0 : default:
655 : /* Ignore everything else. certificate_authorities, oid_filters
656 : and signature_algorithms_cert do not change which certificate
657 : we send (we only have one), and extensions that RFC 8446
658 : Section 4.2 forbids here are tolerated rather than rejected
659 : with illegal_parameter. */
660 0 : ext_parse_res = (long)ext_sz;
661 0 : break;
662 0 : }
663 0 : if( FD_UNLIKELY( ext_parse_res<0L ) )
664 0 : return ext_parse_res;
665 0 : if( FD_UNLIKELY( ext_parse_res != (long)ext_sz ) )
666 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
667 :
668 0 : wire_laddr += ext_sz;
669 0 : wire_sz -= ext_sz;
670 0 : }
671 0 : FD_TLS_DECODE_LIST_END
672 :
673 : /* signature_algorithms MUST be specified */
674 0 : if( FD_UNLIKELY( !(seen & (1UL<<FD_TLS_EXT_SIGNATURE_ALGORITHMS)) ) )
675 0 : return -(long)FD_TLS_ALERT_MISSING_EXTENSION;
676 :
677 0 : return (long)( wire_laddr - (ulong)wire );
678 0 : }
679 :
680 : long
681 : fd_tls_encode_cert_x509( uchar const * x509,
682 : ulong x509_sz,
683 : uchar * wire,
684 0 : ulong wire_sz ) {
685 :
686 0 : ulong wire_laddr = (ulong)wire;
687 :
688 : /* TLS Record Header */
689 0 : uchar msg_type = (uchar)FD_TLS_MSG_CERT;
690 :
691 : /* TLS Certificate Message header preceding X.509 data */
692 :
693 : /* All size prefixes known in advance */
694 0 : fd_tls_u24_t msg_sz = fd_uint_to_tls_u24( (uint)( x509_sz + 9UL ) );
695 0 : fd_tls_u24_t cert_list_sz = fd_uint_to_tls_u24( (uint)( x509_sz + 5UL ) );
696 0 : fd_tls_u24_t cert_sz = fd_uint_to_tls_u24( (uint)( x509_sz ) );
697 :
698 : /* zero sz certificate_request_context
699 : (Server certificate never has a request context) */
700 0 : uchar certificate_request_context_sz = (uchar)0;
701 :
702 : /* No certificate extensions */
703 0 : ushort ext_sz = (ushort)0;
704 :
705 0 : # define FIELDS( FIELD ) \
706 0 : FIELD( 0, &msg_type, uchar, 1 ) \
707 0 : FIELD( 1, &msg_sz, tls_u24, 1 ) \
708 0 : FIELD( 2, &certificate_request_context_sz, uchar, 1 ) \
709 0 : FIELD( 3, &cert_list_sz, tls_u24, 1 ) \
710 0 : FIELD( 4, &cert_sz, tls_u24, 1 ) \
711 0 : FIELD( 5, x509, uchar, x509_sz ) \
712 0 : FIELD( 6, &ext_sz, ushort, 1 )
713 0 : FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
714 0 : # undef FIELDS
715 :
716 0 : return (long)( wire_laddr - (ulong)wire );
717 0 : }
718 :
719 : long
720 : fd_tls_encode_enc_ext( fd_tls_enc_ext_t const * in,
721 : uchar * wire,
722 0 : ulong wire_sz ) {
723 :
724 0 : ulong wire_laddr = (ulong)wire;
725 :
726 : /* ALPN */
727 :
728 0 : if( in->alpn.bufsz ) {
729 0 : fd_tls_ext_hdr_t ext_hdr = { .type = FD_TLS_EXT_ALPN,
730 0 : .sz = (ushort)( in->alpn.bufsz+2 ) };
731 0 : FD_TLS_ENCODE_SUB( fd_tls_encode_ext_hdr, &ext_hdr );
732 0 : FD_TLS_ENCODE_SUB( fd_tls_encode_ext_alpn, &in->alpn );
733 0 : }
734 :
735 : /* QUIC transport params */
736 :
737 0 : if( in->quic_tp.buf ) {
738 0 : ushort ext_type = FD_TLS_EXT_QUIC_TRANSPORT_PARAMS;
739 0 : ushort ext_sz = (ushort)in->quic_tp.bufsz;
740 0 : # define FIELDS( FIELD ) \
741 0 : FIELD( 0, &ext_type, ushort, 1 ) \
742 0 : FIELD( 1, &ext_sz, ushort, 1 ) \
743 0 : FIELD( 2, in->quic_tp.buf, uchar, in->quic_tp.bufsz )
744 0 : FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
745 0 : # undef FIELDS
746 0 : }
747 :
748 0 : return (long)( wire_laddr - (ulong)wire );
749 0 : }
750 :
751 : /* Decode CertificateVerify (RFC 8446 Section 4.4.3) */
752 : long
753 : fd_tls_decode_cert_verify( fd_tls_cert_verify_t * out,
754 : uchar const * wire,
755 0 : ulong wire_sz ) {
756 :
757 0 : ulong wire_laddr = (ulong)wire;
758 :
759 0 : ushort sig_sz;
760 0 : # define FIELDS( FIELD ) \
761 0 : FIELD( 0, &out->algorithm, ushort, 1 ) \
762 0 : FIELD( 1, &sig_sz, ushort, 1 )
763 0 : FD_TLS_DECODE_STATIC_BATCH( FIELDS )
764 0 : # undef FIELDS
765 :
766 : /* Validate signature algorithm and length */
767 :
768 0 : switch( out->algorithm ) {
769 0 : case FD_TLS_SIGNATURE_ED25519:
770 0 : if( FD_UNLIKELY( sig_sz != 64U ) )
771 0 : return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
772 0 : break;
773 0 : case FD_TLS_SIGNATURE_ECDSA_SECP256R1_SHA256:
774 : /* ECDSA DER-encoded signatures are variable length, max 73 */
775 0 : if( FD_UNLIKELY( sig_sz > 73U || sig_sz < 8U ) )
776 0 : return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
777 0 : break;
778 0 : case FD_TLS_SIGNATURE_RSA_PSS_RSAE_SHA256:
779 0 : case FD_TLS_SIGNATURE_RSA_PSS_RSAE_SHA384:
780 0 : case FD_TLS_SIGNATURE_RSA_PSS_RSAE_SHA512:
781 : /* Signature is the size of the modulus */
782 0 : if( FD_UNLIKELY( sig_sz > FD_RSA_MOD_SZ_MAX || sig_sz < FD_RSA_MOD_BITS_MIN/8UL ) )
783 0 : return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
784 0 : break;
785 0 : default:
786 0 : return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
787 0 : }
788 :
789 : /* Read signature bytes */
790 :
791 0 : if( FD_UNLIKELY( sig_sz > wire_sz ) )
792 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
793 0 : fd_memcpy( out->signature, (void const *)wire_laddr, sig_sz );
794 0 : out->signature_len = sig_sz;
795 0 : wire_laddr += sig_sz;
796 0 : wire_sz -= sig_sz;
797 :
798 0 : return (long)( wire_laddr - (ulong)wire );
799 0 : }
800 :
801 : long
802 : fd_tls_encode_cert_verify( fd_tls_cert_verify_t const * in,
803 : uchar * wire,
804 0 : ulong wire_sz ) {
805 :
806 0 : ulong wire_laddr = (ulong)wire;
807 :
808 0 : ushort sig_sz = in->signature_len;
809 0 : # define FIELDS( FIELD ) \
810 0 : FIELD( 0, &in->algorithm, ushort, 1 ) \
811 0 : FIELD( 1, &sig_sz, ushort, 1 )
812 0 : FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
813 0 : # undef FIELDS
814 :
815 0 : if( FD_UNLIKELY( sig_sz > wire_sz ) )
816 0 : return -(long)FD_TLS_ALERT_INTERNAL_ERROR;
817 0 : fd_memcpy( (void *)wire_laddr, in->signature, sig_sz );
818 0 : wire_laddr += sig_sz;
819 0 : wire_sz -= sig_sz;
820 :
821 0 : return (long)( wire_laddr - (ulong)wire );
822 0 : }
823 :
824 : /* Decode server_name extension (RFC 6066 Section 3) */
825 : long
826 : fd_tls_decode_ext_server_name( fd_tls_ext_server_name_t * out,
827 : uchar const * wire,
828 0 : ulong wire_sz ) {
829 :
830 0 : ulong wire_laddr = (ulong)wire;
831 :
832 : /* TLS v1.3 server name lists practically always have one element. */
833 :
834 0 : if( FD_UNLIKELY( wire_sz<2UL || !FD_LOAD( ushort, wire ) ) )
835 0 : return -FD_TLS_ALERT_DECODE_ERROR;
836 0 : uchar seen[ 32 ] = {0};
837 0 : FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(uchar) ) {
838 : /* Read type and length */
839 0 : uchar name_type;
840 0 : ushort name_sz;
841 0 : # define FIELDS( FIELD ) \
842 0 : FIELD( 0, &name_type, uchar, 1 ) \
843 0 : FIELD( 1, &name_sz, ushort, 1 )
844 0 : FD_TLS_DECODE_STATIC_BATCH( FIELDS )
845 0 : # undef FIELDS
846 :
847 : /* Bounds check name */
848 0 : if( FD_UNLIKELY( !name_sz || wire_laddr + name_sz > list_stop ) )
849 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
850 0 : uchar mask = (uchar)( 1U<<(name_type&7U) );
851 0 : if( FD_UNLIKELY( seen[ name_type>>3 ]&mask ) ) return -FD_TLS_ALERT_ILLEGAL_PARAMETER;
852 0 : seen[ name_type>>3 ] |= mask;
853 :
854 : /* Decode name on first use */
855 0 : if( ( ( name_type == FD_TLS_SERVER_NAME_TYPE_DNS )
856 0 : & ( name_sz < 254 )
857 0 : & ( out->host_name_len == 0 ) ) ) {
858 0 : out->host_name_len = (uchar)name_sz;
859 0 : memcpy( out->host_name, (uchar const *)wire_laddr, name_sz );
860 0 : out->host_name[ name_sz ] = '\0';
861 0 : }
862 :
863 : /* Seek to next name */
864 0 : wire_laddr += name_sz;
865 0 : wire_sz -= name_sz;
866 0 : }
867 0 : FD_TLS_DECODE_LIST_END
868 :
869 0 : return (long)( wire_laddr - (ulong)wire );
870 0 : }
871 :
872 : /* Decode supported_groups extension (RFC 8446 Section 4.2.7) */
873 : long
874 : fd_tls_decode_ext_supported_groups( fd_tls_ext_supported_groups_t * out,
875 : uchar const * wire,
876 0 : ulong wire_sz ) {
877 :
878 0 : ulong wire_laddr = (ulong)wire;
879 :
880 0 : if( FD_UNLIKELY( wire_sz<2UL || !FD_LOAD( ushort, wire ) ) )
881 0 : return -FD_TLS_ALERT_DECODE_ERROR;
882 0 : FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(ushort) ) {
883 0 : ushort group;
884 0 : FD_TLS_DECODE_FIELD( &group, ushort );
885 0 : switch( group ) {
886 0 : case FD_TLS_GROUP_X25519:
887 0 : out->x25519 = 1;
888 0 : break;
889 0 : default:
890 : /* Ignore unsupported groups ... */
891 0 : break;
892 0 : }
893 0 : }
894 0 : FD_TLS_DECODE_LIST_END
895 :
896 0 : return (long)( wire_laddr - (ulong)wire );
897 0 : }
898 :
899 : /* Decode supported_versions extension (RFC 8446 Section 4.2.1) */
900 : long
901 : fd_tls_decode_ext_supported_versions( fd_tls_ext_supported_versions_t * out,
902 : uchar const * wire,
903 0 : ulong wire_sz ) {
904 :
905 0 : ulong wire_laddr = (ulong)wire;
906 :
907 0 : if( FD_UNLIKELY( !wire_sz || !wire[0] ) ) return -FD_TLS_ALERT_DECODE_ERROR;
908 0 : FD_TLS_DECODE_LIST_BEGIN( uchar, alignof(ushort) ) {
909 0 : ushort group;
910 0 : FD_TLS_DECODE_FIELD( &group, ushort );
911 0 : switch( group ) {
912 0 : case FD_TLS_VERSION_TLS13:
913 0 : out->tls13 = 1;
914 0 : break;
915 0 : default:
916 : /* Ignore unsupported TLS versions ... */
917 0 : break;
918 0 : }
919 0 : }
920 0 : FD_TLS_DECODE_LIST_END
921 :
922 0 : return (long)( wire_laddr - (ulong)wire );
923 0 : }
924 :
925 : /* Decode signature_algorithms extension (RFC 8446 Section 4.2.3) */
926 : long
927 : fd_tls_decode_ext_signature_algorithms( fd_tls_ext_signature_algorithms_t * out,
928 : uchar const * wire,
929 0 : ulong wire_sz ) {
930 :
931 0 : ulong wire_laddr = (ulong)wire;
932 :
933 0 : if( FD_UNLIKELY( wire_sz<2UL || !FD_LOAD( ushort, wire ) ) )
934 0 : return -FD_TLS_ALERT_DECODE_ERROR;
935 0 : FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(ushort) ) {
936 0 : ushort group;
937 0 : FD_TLS_DECODE_FIELD( &group, ushort );
938 0 : switch( group ) {
939 0 : case FD_TLS_SIGNATURE_ED25519:
940 0 : out->ed25519 = 1;
941 0 : break;
942 0 : case FD_TLS_SIGNATURE_ECDSA_SECP256R1_SHA256:
943 0 : out->ecdsa_secp256r1_sha256 = 1;
944 0 : break;
945 0 : case FD_TLS_SIGNATURE_ECDSA_SECP384R1_SHA384:
946 0 : out->ecdsa_secp384r1_sha384 = 1;
947 0 : break;
948 0 : case FD_TLS_SIGNATURE_RSA_PKCS1_SHA256:
949 0 : out->rsa_pkcs1_sha256 = 1;
950 0 : break;
951 0 : case FD_TLS_SIGNATURE_RSA_PKCS1_SHA384:
952 0 : out->rsa_pkcs1_sha384 = 1;
953 0 : break;
954 0 : case FD_TLS_SIGNATURE_RSA_PKCS1_SHA512:
955 0 : out->rsa_pkcs1_sha512 = 1;
956 0 : break;
957 0 : case FD_TLS_SIGNATURE_RSA_PSS_RSAE_SHA256:
958 0 : out->rsa_pss_rsae_sha256 = 1;
959 0 : break;
960 0 : case FD_TLS_SIGNATURE_RSA_PSS_RSAE_SHA384:
961 0 : out->rsa_pss_rsae_sha384 = 1;
962 0 : break;
963 0 : case FD_TLS_SIGNATURE_RSA_PSS_RSAE_SHA512:
964 0 : out->rsa_pss_rsae_sha512 = 1;
965 0 : break;
966 0 : default:
967 : /* Ignore unsupported signature algorithms ... */
968 0 : break;
969 0 : }
970 0 : }
971 0 : FD_TLS_DECODE_LIST_END
972 :
973 0 : return (long)( wire_laddr - (ulong)wire );
974 0 : }
975 :
976 : long
977 : fd_tls_decode_key_share( fd_tls_key_share_t * out,
978 : uchar const * wire,
979 0 : ulong wire_sz ) {
980 :
981 0 : ulong wire_laddr = (ulong)wire;
982 :
983 : /* Read type and length */
984 0 : ushort group;
985 0 : ushort kex_data_sz;
986 0 : # define FIELDS( FIELD ) \
987 0 : FIELD( 0, &group, ushort, 1 ) \
988 0 : FIELD( 1, &kex_data_sz, ushort, 1 )
989 0 : FD_TLS_DECODE_STATIC_BATCH( FIELDS )
990 0 : # undef FIELDS
991 :
992 : /* Bounds check */
993 0 : if( FD_UNLIKELY( !kex_data_sz || kex_data_sz > wire_sz ) )
994 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
995 :
996 0 : switch( group ) {
997 0 : case FD_TLS_GROUP_X25519:
998 0 : if( FD_UNLIKELY( kex_data_sz != 32UL ) )
999 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
1000 : /* RFC 8446 Section 4.2.8: at most one KeyShareEntry per group */
1001 0 : if( FD_UNLIKELY( out->has_x25519 ) )
1002 0 : return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
1003 0 : out->has_x25519 = 1;
1004 0 : memcpy( out->x25519, (uchar const *)wire_laddr, 32UL );
1005 0 : break;
1006 0 : default:
1007 : /* Ignore unsupported key share groups ... */
1008 0 : break;
1009 0 : }
1010 :
1011 : /* Seek to next group */
1012 0 : wire_laddr += kex_data_sz;
1013 0 : wire_sz -= kex_data_sz;
1014 :
1015 0 : return (long)( wire_laddr - (ulong)wire );
1016 0 : }
1017 :
1018 : long
1019 : fd_tls_decode_key_share_list( fd_tls_key_share_t * out,
1020 : uchar const * wire,
1021 0 : ulong wire_sz ) {
1022 :
1023 0 : ulong wire_laddr = (ulong)wire;
1024 :
1025 0 : FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(uchar) ) {
1026 0 : FD_TLS_DECODE_SUB( fd_tls_decode_key_share, out );
1027 0 : }
1028 0 : FD_TLS_DECODE_LIST_END
1029 :
1030 0 : return (long)( wire_laddr - (ulong)wire );
1031 0 : }
1032 :
1033 : long
1034 : fd_tls_decode_ext_opaque( fd_tls_ext_opaque_t * const out,
1035 : uchar const * const wire,
1036 0 : ulong wire_sz ) {
1037 0 : out->buf = wire;
1038 0 : out->bufsz = wire_sz;
1039 0 : return (long)wire_sz;
1040 0 : }
1041 :
1042 : long
1043 : fd_tls_decode_ext_alpn( fd_tls_ext_alpn_t * const out,
1044 : uchar const * const wire,
1045 0 : ulong wire_sz ) {
1046 0 : ulong wire_laddr = (ulong)wire;
1047 0 : ushort alpn_sz;
1048 0 : FD_TLS_DECODE_FIELD( &alpn_sz, ushort );
1049 0 : if( FD_UNLIKELY( (ulong)alpn_sz != wire_sz ) )
1050 0 : return -(long)FD_TLS_ALERT_DECODE_ERROR;
1051 0 : if( FD_UNLIKELY( alpn_sz<2U ) ) return -FD_TLS_ALERT_DECODE_ERROR;
1052 0 : uchar const * list = (uchar const *)wire_laddr;
1053 0 : for( ulong off=0UL; off<wire_sz; ) {
1054 0 : ulong len = list[ off++ ];
1055 0 : if( FD_UNLIKELY( !len || len>wire_sz-off ) ) return -FD_TLS_ALERT_DECODE_ERROR;
1056 0 : off += len;
1057 0 : }
1058 0 : return 2L + (long)fd_tls_decode_ext_opaque( out, (uchar const *)wire_laddr, wire_sz );
1059 0 : }
1060 :
1061 : long
1062 : fd_tls_encode_ext_alpn( fd_tls_ext_alpn_t const * in,
1063 : uchar * wire,
1064 0 : ulong wire_sz ) {
1065 0 : ulong sz = 2UL + in->bufsz;
1066 0 : if( FD_UNLIKELY( sz>wire_sz ) )
1067 0 : return -(long)FD_TLS_ALERT_INTERNAL_ERROR;
1068 0 : wire[0] = (uchar)( (in->bufsz >> 8)&0xFF );
1069 0 : wire[1] = (uchar)( in->bufsz &0xFF );
1070 0 : fd_memcpy( wire+2UL, in->buf, in->bufsz );
1071 0 : return (long)sz;
1072 0 : }
1073 :
1074 : static long
1075 : fd_tls_extract_cert_pubkey_( fd_tls_extract_cert_pubkey_res_t * res,
1076 : uchar const * cert_chain,
1077 0 : ulong cert_chain_sz ) {
1078 :
1079 0 : fd_memset( res, 0, sizeof(fd_tls_extract_cert_pubkey_res_t) );
1080 :
1081 0 : ulong wire_laddr = (ulong)cert_chain;
1082 0 : ulong wire_sz = cert_chain_sz;
1083 :
1084 : /* Initial-handshake Certificate messages always have empty context. */
1085 0 : uchar const * opaque_sz = FD_TLS_SKIP_FIELD( uchar );
1086 0 : if( FD_UNLIKELY( *opaque_sz ) ) return -FD_TLS_ALERT_ILLEGAL_PARAMETER;
1087 :
1088 : /* Get first entry of certificate chain
1089 : CertificateEntry certificate_list<0..2^24-1> */
1090 0 : fd_tls_u24_t const * cert_list_sz_be = FD_TLS_SKIP_FIELD( fd_tls_u24_t );
1091 0 : fd_tls_u24_t cert_list_sz_ = fd_tls_u24_bswap( *cert_list_sz_be );
1092 0 : uint cert_list_sz = fd_tls_u24_to_uint( cert_list_sz_ );
1093 0 : if( FD_UNLIKELY( cert_list_sz!=wire_sz ) ) return -FD_TLS_ALERT_DECODE_ERROR;
1094 0 : if( FD_UNLIKELY( cert_list_sz==0U ) ) {
1095 0 : res->alert = FD_TLS_ALERT_BAD_CERTIFICATE;
1096 0 : res->reason = FD_TLS_REASON_CERT_CHAIN_EMPTY;
1097 0 : return -1L;
1098 0 : }
1099 :
1100 : /* Validate every entry before extracting the leaf key, independently
1101 : of whether the caller requests X.509 chain authentication. */
1102 0 : uchar const * cert = NULL;
1103 0 : ulong cert_sz = 0UL;
1104 0 : while( wire_sz ) {
1105 0 : fd_tls_u24_t const * sz_be = FD_TLS_SKIP_FIELD( fd_tls_u24_t );
1106 0 : ulong sz = fd_tls_u24_to_uint( fd_tls_u24_bswap( *sz_be ) );
1107 0 : if( FD_UNLIKELY( !sz || sz>wire_sz ) ) return -FD_TLS_ALERT_DECODE_ERROR;
1108 0 : if( !cert ) {
1109 0 : cert = (uchar const *)wire_laddr;
1110 0 : cert_sz = sz;
1111 0 : }
1112 0 : wire_laddr += sz;
1113 0 : wire_sz -= sz;
1114 : /* We never solicit CertificateEntry extensions (RFC 8446 Section
1115 : 4.4.2), so the extensions vector must be empty */
1116 0 : ushort const * ext_sz_be = FD_TLS_SKIP_FIELD( ushort );
1117 0 : ulong ext_sz = fd_ushort_bswap( FD_LOAD( ushort, ext_sz_be ) );
1118 0 : if( FD_UNLIKELY( ext_sz > wire_sz ) ) return -(long)FD_TLS_ALERT_DECODE_ERROR;
1119 0 : if( FD_UNLIKELY( ext_sz ) ) return -(long)FD_TLS_ALERT_UNSUPPORTED_EXTENSION;
1120 0 : }
1121 :
1122 0 : if( FD_UNLIKELY( fd_x509_extract_pubkey( cert, cert_sz, &res->pubkey,
1123 0 : &res->pubkey_len, &res->key_type ) ) ) {
1124 0 : res->pubkey = NULL;
1125 0 : res->alert = FD_TLS_ALERT_UNSUPPORTED_CERTIFICATE;
1126 0 : res->reason = FD_TLS_REASON_X509_PARSE;
1127 0 : return -1L;
1128 0 : }
1129 :
1130 0 : return 0L;
1131 0 : }
1132 :
1133 : fd_tls_extract_cert_pubkey_res_t
1134 : fd_tls_extract_cert_pubkey( uchar const * cert_chain,
1135 0 : ulong cert_chain_sz ) {
1136 0 : fd_tls_extract_cert_pubkey_res_t res;
1137 0 : long ret = fd_tls_extract_cert_pubkey_( &res, cert_chain, cert_chain_sz );
1138 0 : if( FD_UNLIKELY( ret<0L && !res.alert ) ) {
1139 0 : res.alert = (uint)(-ret);
1140 0 : res.reason = FD_TLS_REASON_CERT_PARSE;
1141 0 : }
1142 0 : return res;
1143 0 : }
|