LCOV - code coverage report
Current view: top level - waltz/tls - fd_tls_proto.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 832 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 22 0.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             : /* 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 : }

Generated by: LCOV version 1.14