LCOV - code coverage report
Current view: top level - waltz/tls - fd_tls_proto.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 759 794 95.6 %
Date: 2026-09-17 04:28:31 Functions: 22 22 100.0 %

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

Generated by: LCOV version 1.14