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