LCOV - code coverage report
Current view: top level - waltz/tls - fd_tls_proto.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 517 688 75.1 %
Date: 2025-07-01 05:00:49 Functions: 22 27 81.5 %

          Line data    Source code
       1             : #include "fd_tls.h"
       2             : #include "fd_tls_proto.h"
       3             : #include "fd_tls_serde.h"
       4             : #include "fd_tls_asn1.h"
       5             : #include "../../ballet/x509/fd_x509_mock.h"
       6             : 
       7             : typedef struct fd_tls_u24 tls_u24;  /* code generator helper */
       8             : 
       9             : /* hello_retry_magic is the RFC 8446 hardcoded value of the 'random' field of a RetryHelloRequest */
      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             : long
      35             : fd_tls_decode_client_hello( fd_tls_client_hello_t * out,
      36             :                             uchar const * const     wire,
      37        6036 :                             ulong                   wire_sz ) {
      38             : 
      39        6036 :   ulong wire_laddr = (ulong)wire;
      40             : 
      41             :   /* Decode static sized part of client hello.
      42             :      (Assuming that session ID field is of a certain size) */
      43             : 
      44        6036 :   ushort legacy_version;       /* ==FD_TLS_VERSION_TLS12 */
      45        6036 :   uchar  legacy_session_id_sz; /* ==0 */
      46             : 
      47        6036 : # define FIELDS( FIELD )                            \
      48       12072 :     FIELD( 0, &legacy_version,       ushort, 1    ) \
      49       12072 :     FIELD( 1, &out->random[0],       uchar,  32UL ) \
      50       12072 :     FIELD( 2, &legacy_session_id_sz, uchar,  1    )
      51       12072 :     FD_TLS_DECODE_STATIC_BATCH( FIELDS )
      52        6036 : # undef FIELDS
      53             : 
      54        6036 :   if( FD_UNLIKELY( ( legacy_session_id_sz > 32      ) |
      55        6036 :                    ( wire_sz < legacy_session_id_sz ) ) )
      56           0 :     return -(long)FD_TLS_ALERT_DECODE_ERROR;
      57             : 
      58        6036 :   out->session_id.buf   = (void *)wire_laddr;
      59        6036 :   out->session_id.bufsz = legacy_session_id_sz;
      60        6036 :   wire_laddr += legacy_session_id_sz;
      61        6036 :   wire_sz    -= legacy_session_id_sz;
      62             : 
      63             :   /* Decode cipher suite list */
      64             : 
      65       24144 :   FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(ushort) ) {
      66        6069 :     ushort cipher_suite;
      67        6069 :     FD_TLS_DECODE_FIELD( &cipher_suite, ushort );
      68             : 
      69        6069 :     switch( cipher_suite ) {
      70        6033 :     case FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256:
      71        6033 :       out->cipher_suites.aes_128_gcm_sha256 = 1;
      72        6033 :       break;
      73          36 :     default:
      74             :       /* Ignore unsupported cipher suites ... */
      75          36 :       break;
      76        6069 :     }
      77        6069 :   }
      78        6036 :   FD_TLS_DECODE_LIST_END
      79             : 
      80             :   /* Decode next static sized part of client hello */
      81             : 
      82        6036 :   uchar  legacy_compression_method_cnt;    /* == 1  */
      83        6036 :   uchar  legacy_compression_methods[ 1 ];  /* =={0} */
      84             : 
      85        6036 : # define FIELDS( FIELD )                                  \
      86       12072 :     FIELD( 5, &legacy_compression_method_cnt, uchar,  1 ) \
      87       12072 :     FIELD( 6, &legacy_compression_methods[0], uchar,  1 )
      88       12072 :     FD_TLS_DECODE_STATIC_BATCH( FIELDS )
      89        6036 : # undef FIELDS
      90             : 
      91        6036 :   if( FD_UNLIKELY( ( legacy_compression_method_cnt != 1 )
      92        6036 :                  | ( legacy_compression_methods[0] != 0 ) ) )
      93           0 :     return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
      94             : 
      95             :   /* Read extensions */
      96             : 
      97       36249 :   FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(uchar) ) {
      98             :     /* Read extension type and length */
      99       36249 :     ushort ext_type;
     100       36249 :     ushort ext_sz;
     101       36249 : #   define FIELDS( FIELD )             \
     102       72498 :       FIELD( 0, &ext_type, ushort, 1 ) \
     103       72498 :       FIELD( 1, &ext_sz,   ushort, 1 )
     104       72498 :       FD_TLS_DECODE_STATIC_BATCH( FIELDS )
     105       36249 : #   undef FIELDS
     106             : 
     107             :     /* Bounds check extension data */
     108       36249 :     if( FD_UNLIKELY( ext_sz > wire_sz ) )
     109           0 :       return -(long)FD_TLS_ALERT_DECODE_ERROR;
     110             : 
     111             :     /* Decode extension data */
     112       36249 :     uchar const * ext_data = (uchar const *)wire_laddr;
     113       36249 :     long ext_parse_res;
     114       36249 :     switch( ext_type ) {
     115        6036 :     case FD_TLS_EXT_SUPPORTED_VERSIONS:
     116        6036 :       ext_parse_res = fd_tls_decode_ext_supported_versions( &out->supported_versions, ext_data, ext_sz );
     117        6036 :       break;
     118           3 :     case FD_TLS_EXT_SERVER_NAME:
     119           3 :       ext_parse_res = fd_tls_decode_ext_server_name( &out->server_name, ext_data, ext_sz );
     120           3 :       break;
     121        6036 :     case FD_TLS_EXT_SUPPORTED_GROUPS:
     122        6036 :       ext_parse_res = fd_tls_decode_ext_supported_groups( &out->supported_groups, ext_data, ext_sz );
     123        6036 :       break;
     124        6036 :     case FD_TLS_EXT_SIGNATURE_ALGORITHMS:
     125        6036 :       ext_parse_res = fd_tls_decode_ext_signature_algorithms( &out->signature_algorithms, ext_data, ext_sz );
     126        6036 :       break;
     127        6036 :     case FD_TLS_EXT_KEY_SHARE:
     128        6036 :       ext_parse_res = fd_tls_decode_key_share_list( &out->key_share, ext_data, ext_sz );
     129        6036 :       break;
     130           3 :     case FD_TLS_EXT_SERVER_CERT_TYPE:
     131           3 :       ext_parse_res = fd_tls_decode_ext_cert_type_list( &out->server_cert_types, ext_data, ext_sz );
     132           3 :       break;
     133           3 :     case FD_TLS_EXT_CLIENT_CERT_TYPE:
     134           3 :       ext_parse_res = fd_tls_decode_ext_cert_type_list( &out->client_cert_types, ext_data, ext_sz );
     135           3 :       break;
     136        6033 :     case FD_TLS_EXT_QUIC_TRANSPORT_PARAMS:
     137        6033 :       ext_parse_res = fd_tls_decode_ext_quic_tp( &out->quic_tp, ext_data, ext_sz );
     138        6033 :       break;
     139        6030 :     case FD_TLS_EXT_ALPN:
     140        6030 :       ext_parse_res = fd_tls_decode_ext_alpn( &out->alpn, ext_data, ext_sz );
     141        6030 :       break;
     142          33 :     default:
     143          33 :       ext_parse_res = (long)ext_sz;
     144          33 :       break;
     145       36249 :     }
     146       36249 :     if( FD_UNLIKELY( ext_parse_res<0L ) )
     147           0 :       return ext_parse_res;
     148       36249 :     if( FD_UNLIKELY( ext_parse_res != (long)ext_sz ) )
     149           0 :       return -(long)FD_TLS_ALERT_DECODE_ERROR;
     150             : 
     151             :     /* Seek to next extension */
     152       36249 :     wire_laddr += ext_sz;
     153       36249 :     wire_sz    -= ext_sz;
     154       36249 :   }
     155        6036 :   FD_TLS_DECODE_LIST_END
     156             : 
     157        6036 :   return (long)( wire_laddr - (ulong)wire );
     158        6036 : }
     159             : 
     160             : long
     161             : fd_tls_encode_client_hello( fd_tls_client_hello_t const * in,
     162             :                             uchar *                       wire,
     163        6063 :                             ulong                         wire_sz ) {
     164             : 
     165        6063 :   ulong wire_laddr = (ulong)wire;
     166             : 
     167             :   /* Encode static sized part of client hello */
     168             : 
     169        6063 :   ushort legacy_version        = FD_TLS_VERSION_TLS12;
     170        6063 :   uchar  legacy_session_id_sz  = 0;
     171        6063 :   ushort cipher_suite_sz       = 1*sizeof(ushort);
     172        6063 :   ushort cipher_suites[1]      = { FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256 };
     173        6063 :   uchar  legacy_comp_method_sz = 1;
     174        6063 :   uchar  legacy_comp_method[1] = {0};
     175             : 
     176        6063 : # define FIELDS( FIELD )                                 \
     177       12126 :     FIELD( 0, &legacy_version,            ushort, 1    ) \
     178       12126 :     FIELD( 1,  in->random,                uchar,  32UL ) \
     179       12126 :     FIELD( 2, &legacy_session_id_sz,      uchar,  1    ) \
     180       12126 :     FIELD( 3, &cipher_suite_sz,           ushort, 1    ) \
     181       12126 :     FIELD( 4,  cipher_suites,             ushort, 1    ) \
     182       12126 :     FIELD( 5, &legacy_comp_method_sz,     uchar,  1    ) \
     183       12126 :     FIELD( 6,  legacy_comp_method,        uchar,  1    )
     184       12126 :     FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
     185        6063 : # undef FIELDS
     186             : 
     187             :   /* Encode extensions */
     188             : 
     189        6063 :   ushort * extension_tot_sz = FD_TLS_SKIP_FIELD( ushort );
     190           0 :   ulong    extension_start  = wire_laddr;
     191             : 
     192        6063 :   ushort ext_supported_versions_ext_type = FD_TLS_EXT_SUPPORTED_VERSIONS;
     193        6063 :   ushort ext_supported_versions_ext_sz   = 3;
     194        6063 :   uchar  ext_supported_versions_sz       = 2;
     195        6063 :   ushort ext_supported_versions[1]       = { FD_TLS_VERSION_TLS13 };
     196             : 
     197        6063 :   ushort ext_key_share_ext_type = FD_TLS_EXT_KEY_SHARE;
     198        6063 :   ushort ext_key_share_ext_sz   = 38;
     199        6063 :   ushort ext_key_share_sz1      = 36;
     200        6063 :   ushort ext_key_share_group    = FD_TLS_GROUP_X25519;
     201        6063 :   ushort ext_key_share_sz       = 32;
     202             : 
     203        6063 :   ushort ext_supported_groups_ext_type = FD_TLS_EXT_SUPPORTED_GROUPS;
     204        6063 :   ushort ext_supported_groups_ext_sz   = 4;
     205        6063 :   ushort ext_supported_groups_sz       = 2;
     206        6063 :   ushort ext_supported_groups[1]       = { FD_TLS_GROUP_X25519 };
     207             : 
     208        6063 :   ushort ext_sigalg_ext_type = FD_TLS_EXT_SIGNATURE_ALGORITHMS;
     209        6063 :   ushort ext_sigalg_ext_sz   = 4;
     210        6063 :   ushort ext_sigalg_sz       = 2;
     211        6063 :   ushort ext_sigalg[1]       = { FD_TLS_SIGNATURE_ED25519 };
     212             : 
     213        6063 : # define FIELDS( FIELD ) \
     214       12126 :     FIELD( 0, &ext_supported_versions_ext_type,   ushort, 1    ) \
     215       12126 :     FIELD( 1, &ext_supported_versions_ext_sz,     ushort, 1    ) \
     216       12126 :     FIELD( 2, &ext_supported_versions_sz,         uchar,  1    ) \
     217       12126 :     FIELD( 3,  ext_supported_versions,            ushort, 1    ) \
     218       12126 :     FIELD( 4, &ext_key_share_ext_type,            ushort, 1    ) \
     219       12126 :     FIELD( 5, &ext_key_share_ext_sz,              ushort, 1    ) \
     220       12126 :     FIELD( 6, &ext_key_share_sz1,                 ushort, 1    ) \
     221       12126 :     FIELD( 7, &ext_key_share_group,               ushort, 1    ) \
     222       12126 :     FIELD( 8, &ext_key_share_sz,                  ushort, 1    ) \
     223       12126 :     FIELD( 9, &in->key_share.x25519[0],           uchar,  32UL ) \
     224       12126 :     FIELD(10, &ext_supported_groups_ext_type,     ushort, 1    ) \
     225       12126 :     FIELD(11, &ext_supported_groups_ext_sz,       ushort, 1    ) \
     226       12126 :     FIELD(12, &ext_supported_groups_sz,           ushort, 1    ) \
     227       12126 :     FIELD(13,  ext_supported_groups,              ushort, 1    ) \
     228       12126 :     FIELD(14, &ext_sigalg_ext_type,               ushort, 1    ) \
     229       12126 :     FIELD(15, &ext_sigalg_ext_sz,                 ushort, 1    ) \
     230       12126 :     FIELD(16, &ext_sigalg_sz,                     ushort, 1    ) \
     231       12126 :     FIELD(17,  ext_sigalg,                        ushort, 1    )
     232       12126 :     FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
     233        6063 : # undef FIELDS
     234             : 
     235             :   /* Add ALPN */
     236             : 
     237        6063 :   if( in->alpn.bufsz ) {
     238        6057 :     fd_tls_ext_hdr_t ext_hdr = { .type = FD_TLS_EXT_ALPN,
     239        6057 :                                  .sz   = (ushort)( in->alpn.bufsz+2 ) };
     240        6057 :     FD_TLS_ENCODE_SUB( fd_tls_encode_ext_hdr,  &ext_hdr  );
     241        6057 :     FD_TLS_ENCODE_SUB( fd_tls_encode_ext_alpn, &in->alpn );
     242        6057 :   }
     243             : 
     244             :   /* Add QUIC transport params */
     245             : 
     246        6063 :   if( in->quic_tp.buf ) {
     247        6057 :     ushort  quic_tp_ext_type = FD_TLS_EXT_QUIC_TRANSPORT_PARAMS;
     248        6057 :     ushort  quic_tp_ext_sz   = (ushort)in->quic_tp.bufsz;
     249        6057 : #   define FIELDS( FIELD )                    \
     250       12114 :     FIELD( 0, &quic_tp_ext_type, ushort, 1 ); \
     251       12114 :     FIELD( 1, &quic_tp_ext_sz,   ushort, 1 ); \
     252       12114 :     FIELD( 2, in->quic_tp.buf,   uchar,  in->quic_tp.bufsz );
     253       12114 :     FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
     254        6057 : # undef FIELDS
     255        6057 :   }
     256             : 
     257        6063 :   FD_STORE( ushort, extension_tot_sz, fd_ushort_bswap( (ushort)( (ulong)wire_laddr - extension_start ) ) );
     258        6063 :   return (long)( wire_laddr - (ulong)wire );
     259        6063 : }
     260             : 
     261             : long
     262             : fd_tls_decode_server_hello( fd_tls_server_hello_t * out,
     263             :                             uchar const *           wire,
     264        6033 :                             ulong                   wire_sz ) {
     265             : 
     266        6033 :   ulong wire_laddr = (ulong)wire;
     267             : 
     268             :   /* Decode static sized part of server hello */
     269             : 
     270        6033 :   ushort legacy_version;            /* ==FD_TLS_VERSION_TLS12 */
     271        6033 :   uchar  legacy_session_id_sz;      /* ==0 */
     272        6033 :   ushort cipher_suite;              /* ==FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256 */
     273        6033 :   uchar  legacy_compression_method; /* ==0 */
     274             : 
     275        6033 : # define FIELDS( FIELD )                                 \
     276       12066 :     FIELD( 0, &legacy_version,            ushort, 1    ) \
     277       12066 :     FIELD( 1, &out->random[0],            uchar,  32UL ) \
     278       12066 :     FIELD( 2, &legacy_session_id_sz,      uchar,  1    ) \
     279       12066 :     FIELD( 3, &cipher_suite,              ushort, 1    ) \
     280       12066 :     FIELD( 4, &legacy_compression_method, uchar,  1    )
     281       12066 :     FD_TLS_DECODE_STATIC_BATCH( FIELDS )
     282        6033 : # undef FIELDS
     283             : 
     284        6033 :   if( FD_UNLIKELY( ( legacy_version != FD_TLS_VERSION_TLS12 )
     285        6033 :                  | ( legacy_session_id_sz      != 0         )
     286        6033 :                  | ( legacy_compression_method != 0         ) ) )
     287           0 :     return -(long)FD_TLS_ALERT_PROTOCOL_VERSION;
     288             : 
     289        6033 :   if( FD_UNLIKELY( cipher_suite != FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256 ) )
     290           3 :     return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
     291             : 
     292             :   /* Reject HelloRetryRequest (we only support X25519) */
     293             : 
     294        6030 :   if( FD_UNLIKELY( 0==memcmp( out->random, hello_retry_magic, 32 ) ) )
     295           0 :     return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
     296             : 
     297             :   /* Read extensions */
     298             : 
     299       24120 :   FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(uchar) ) {
     300             :     /* Read extension type and length */
     301       12060 :     ushort ext_type;
     302       12060 :     ushort ext_sz;
     303       12060 : #   define FIELDS( FIELD )             \
     304       24120 :       FIELD( 0, &ext_type, ushort, 1 ) \
     305       24120 :       FIELD( 1, &ext_sz,   ushort, 1 )
     306       24120 :       FD_TLS_DECODE_STATIC_BATCH( FIELDS )
     307       12060 : #   undef FIELDS
     308             : 
     309             :     /* Bounds check extension data */
     310       12060 :     if( FD_UNLIKELY( ext_sz > wire_sz ) )
     311           0 :       return -(long)FD_TLS_ALERT_DECODE_ERROR;
     312             : 
     313       12060 :     ulong next_field = wire_laddr + ext_sz;
     314       12060 :     ulong next_sz    = wire_sz    - ext_sz;
     315             : 
     316             :     /* Decode extension data */
     317       12060 :     uchar const * ext_data = (uchar const *)wire_laddr;
     318       12060 :     long ext_parse_res;
     319       12060 :     switch( ext_type ) {
     320        6030 :     case FD_TLS_EXT_SUPPORTED_VERSIONS: {
     321        6030 :       ushort chosen_version;
     322        6030 :       FD_TLS_DECODE_FIELD( &chosen_version, ushort );
     323        6030 :       ext_parse_res = 2L;
     324        6030 :       if( FD_UNLIKELY( chosen_version!=FD_TLS_VERSION_TLS13 ) )
     325           0 :         return -(long)FD_TLS_ALERT_PROTOCOL_VERSION;
     326        6030 :       break;
     327        6030 :     }
     328        6030 :     case FD_TLS_EXT_KEY_SHARE:
     329        6030 :       ext_parse_res = fd_tls_decode_key_share( &out->key_share, ext_data, ext_sz );
     330        6030 :       break;
     331           0 :     case FD_TLS_EXT_QUIC_TRANSPORT_PARAMS:
     332             :       /* Copy transport params as-is (TODO...) */
     333           0 :       ext_parse_res = (long)ext_sz;
     334           0 :       break;
     335           0 :     default:
     336             :       /* Reject unsolicited extensions */
     337           0 :       return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
     338       12060 :     }
     339             : 
     340       12060 :     if( FD_UNLIKELY( ext_parse_res<0L ) )
     341           0 :       return ext_parse_res;
     342       12060 :     if( FD_UNLIKELY( ext_parse_res != (long)ext_sz ) )
     343           0 :       return -(long)FD_TLS_ALERT_DECODE_ERROR;
     344             : 
     345       12060 :     wire_laddr = next_field;
     346       12060 :     wire_sz    = next_sz;
     347       12060 :   }
     348        6030 :   FD_TLS_DECODE_LIST_END
     349             : 
     350             :   /* Check for required extensions */
     351             : 
     352        6030 :   if( FD_UNLIKELY( !out->key_share.has_x25519 ) )
     353           0 :     return -(long)FD_TLS_ALERT_MISSING_EXTENSION;
     354             : 
     355        6030 :   return (long)( wire_laddr - (ulong)wire );
     356        6030 : }
     357             : 
     358             : long
     359             : fd_tls_encode_server_hello( fd_tls_server_hello_t const * in,
     360             :                             uchar *                       wire,
     361        6030 :                             ulong                         wire_sz ) {
     362             : 
     363        6030 :   ulong wire_laddr = (ulong)wire;
     364             : 
     365             :   /* Encode static sized part of server hello.
     366             :      (Assuming that session ID field is of a certain size) */
     367             : 
     368        6030 :   ushort legacy_version            = FD_TLS_VERSION_TLS12;
     369        6030 :   uchar  legacy_session_id_sz      = (uchar)in->session_id.bufsz;
     370        6030 :   ushort cipher_suite              = FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256;
     371        6030 :   uchar  legacy_compression_method = 0;
     372             : 
     373        6030 : # define FIELDS( FIELD )                                 \
     374       12060 :     FIELD( 0, &legacy_version,            ushort, 1    ) \
     375       12060 :     FIELD( 1, &in->random[0],             uchar,  32UL ) \
     376       12060 :     FIELD( 2, &legacy_session_id_sz,      uchar,  1    ) \
     377       12060 :     FIELD( 3,  in->session_id.buf,        uchar,  legacy_session_id_sz ) \
     378       12060 :     FIELD( 4, &cipher_suite,              ushort, 1    ) \
     379       12060 :     FIELD( 5, &legacy_compression_method, uchar,  1    )
     380       12060 :     FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
     381        6030 : # undef FIELDS
     382             : 
     383             :   /* Encode extensions */
     384             : 
     385        6030 :   ushort * extension_tot_sz = FD_TLS_SKIP_FIELD( ushort );
     386           0 :   ulong    extension_start  = wire_laddr;
     387             : 
     388        6030 :   ushort ext_supported_versions_ext_type = FD_TLS_EXT_SUPPORTED_VERSIONS;
     389        6030 :   ushort ext_supported_versions[1]       = { FD_TLS_VERSION_TLS13 };
     390        6030 :   ushort ext_supported_versions_ext_sz   = sizeof(ext_supported_versions);
     391             : 
     392        6030 :   ushort ext_key_share_ext_type = FD_TLS_EXT_KEY_SHARE;
     393        6030 :   ushort ext_key_share_ext_sz   = sizeof(ushort) + sizeof(ushort) + 32UL;
     394        6030 :   ushort ext_key_share_group    = FD_TLS_GROUP_X25519;
     395        6030 :   ushort ext_key_share_sz       = 32UL;
     396             : 
     397        6030 : # define FIELDS( FIELD )                                         \
     398       12060 :     FIELD( 0, &ext_supported_versions_ext_type,   ushort, 1    ) \
     399       12060 :     FIELD( 1, &ext_supported_versions_ext_sz,     ushort, 1    ) \
     400       12060 :     FIELD( 2,  ext_supported_versions,            ushort, 1    ) \
     401       12060 :     FIELD( 3, &ext_key_share_ext_type,            ushort, 1    ) \
     402       12060 :     FIELD( 4, &ext_key_share_ext_sz,              ushort, 1    ) \
     403       12060 :     FIELD( 5, &ext_key_share_group,               ushort, 1    ) \
     404       12060 :     FIELD( 6, &ext_key_share_sz,                  ushort, 1    ) \
     405       12060 :     FIELD( 7, &in->key_share.x25519[0],           uchar,  32UL )
     406       12060 :     FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
     407        6030 : # undef FIELDS
     408             : 
     409        6030 :   *extension_tot_sz = fd_ushort_bswap( (ushort)( (ulong)wire_laddr - extension_start ) );
     410        6030 :   return (long)( wire_laddr - (ulong)wire );
     411        6030 : }
     412             : 
     413             : long
     414             : fd_tls_encode_hello_retry_request( fd_tls_server_hello_t const * in,
     415             :                                    uchar *                       wire,
     416           0 :                                    ulong                         wire_sz ) {
     417             : 
     418           0 :   ulong wire_laddr = (ulong)wire;
     419             : 
     420           0 :   ushort legacy_version            = FD_TLS_VERSION_TLS12;
     421           0 :   uchar  legacy_session_id_sz      = (uchar)in->session_id.bufsz;
     422           0 :   ushort cipher_suite              = FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256;
     423           0 :   uchar  legacy_compression_method = 0;
     424             : 
     425           0 : # define FIELDS( FIELD )                                 \
     426           0 :     FIELD( 0, &legacy_version,            ushort, 1    ) \
     427           0 :     FIELD( 1, hello_retry_magic,          uchar,  32UL ) \
     428           0 :     FIELD( 2, &legacy_session_id_sz,      uchar,  1    ) \
     429           0 :     FIELD( 3,  in->session_id.buf,        uchar,  legacy_session_id_sz ) \
     430           0 :     FIELD( 4, &cipher_suite,              ushort, 1    ) \
     431           0 :     FIELD( 5, &legacy_compression_method, uchar,  1    )
     432           0 :     FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
     433           0 : # undef FIELDS
     434             : 
     435             :   /* Encode extensions */
     436             : 
     437           0 :   ushort * extension_tot_sz = FD_TLS_SKIP_FIELD( ushort );
     438           0 :   ulong    extension_start  = wire_laddr;
     439             : 
     440           0 :   ushort ext_supported_versions_ext_type = FD_TLS_EXT_SUPPORTED_VERSIONS;
     441           0 :   ushort ext_supported_versions[1]       = { FD_TLS_VERSION_TLS13 };
     442           0 :   ushort ext_supported_versions_ext_sz   = sizeof(ext_supported_versions);
     443             : 
     444           0 :   ushort ext_key_share_ext_type = FD_TLS_EXT_KEY_SHARE;
     445           0 :   ushort ext_key_share_ext_sz   = sizeof(ushort);
     446           0 :   ushort ext_key_share_group    = FD_TLS_GROUP_X25519;
     447             : 
     448           0 : # define FIELDS( FIELD )                                         \
     449           0 :     FIELD( 0, &ext_supported_versions_ext_type,   ushort, 1    ) \
     450           0 :     FIELD( 1, &ext_supported_versions_ext_sz,     ushort, 1    ) \
     451           0 :     FIELD( 2,  ext_supported_versions,            ushort, 1    ) \
     452           0 :     FIELD( 3, &ext_key_share_ext_type,            ushort, 1    ) \
     453           0 :     FIELD( 4, &ext_key_share_ext_sz,              ushort, 1    ) \
     454           0 :     FIELD( 5, &ext_key_share_group,               ushort, 1    )
     455           0 :     FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
     456           0 : # undef FIELDS
     457             : 
     458           0 :   *extension_tot_sz = fd_ushort_bswap( (ushort)( (ulong)wire_laddr - extension_start ) );
     459           0 :   return (long)( wire_laddr - (ulong)wire );
     460           0 : }
     461             : 
     462             : long
     463             : fd_tls_decode_enc_ext( fd_tls_enc_ext_t * const out,
     464             :                        uchar const *      const wire,
     465        6027 :                        ulong                    wire_sz ) {
     466             : 
     467        6027 :   ulong wire_laddr = (ulong)wire;
     468             : 
     469       24108 :   FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(uchar) ) {
     470       12048 :     ushort ext_type;
     471       12048 :     ushort ext_sz;
     472       12048 : #   define FIELDS( FIELD )             \
     473       24096 :       FIELD( 0, &ext_type, ushort, 1 ) \
     474       24096 :       FIELD( 1, &ext_sz,   ushort, 1 )
     475       24096 :       FD_TLS_DECODE_STATIC_BATCH( FIELDS )
     476       12048 : #   undef FIELDS
     477             : 
     478             :     /* Bounds check extension data
     479             :        (list_stop declared by DECODE_LIST macro) */
     480       12048 :     if( FD_UNLIKELY( wire_laddr + ext_sz > list_stop ) )
     481           0 :       return -(long)FD_TLS_ALERT_DECODE_ERROR;
     482             : 
     483       12048 :     switch( ext_type ) {
     484        6024 :     case FD_TLS_EXT_ALPN: {
     485        6024 :       long res = fd_tls_decode_ext_alpn( &out->alpn, (uchar const *)wire_laddr, ext_sz );
     486        6024 :       if( FD_UNLIKELY( res<0L ) )
     487           0 :         return res;
     488        6024 :       if( FD_UNLIKELY( res!=(long)ext_sz ) )
     489           0 :         return -(long)FD_TLS_ALERT_DECODE_ERROR;
     490        6024 :       break;
     491        6024 :     }
     492        6024 :     case FD_TLS_EXT_QUIC_TRANSPORT_PARAMS:
     493        6024 :       if( FD_UNLIKELY( ext_sz > FD_TLS_EXT_QUIC_PARAMS_SZ_MAX ) )
     494           0 :         return -(long)FD_TLS_ALERT_DECODE_ERROR;
     495        6024 :       out->quic_tp.buf   = (void *)wire_laddr;
     496        6024 :       out->quic_tp.bufsz = (ushort)ext_sz;
     497        6024 :       break;
     498           0 :     case FD_TLS_EXT_SERVER_CERT_TYPE:
     499           0 :       if( FD_UNLIKELY( (ext_sz>wire_sz) | (ext_sz!=1) ) )
     500           0 :         return -(long)FD_TLS_ALERT_DECODE_ERROR;
     501           0 :       out->server_cert.cert_type = *(uchar const *)wire_laddr;
     502           0 :       break;
     503           0 :     case FD_TLS_EXT_CLIENT_CERT_TYPE:
     504           0 :       if( FD_UNLIKELY( (ext_sz>wire_sz) | (ext_sz!=1) ) )
     505           0 :         return -(long)FD_TLS_ALERT_DECODE_ERROR;
     506           0 :       out->client_cert.cert_type = *(uchar const *)wire_laddr;
     507           0 :       break;
     508           0 :     default:
     509           0 :       break;  /* TODO should we error on unknown extensions? */
     510       12048 :     }
     511             : 
     512       12048 :     wire_laddr += ext_sz;
     513       12048 :     wire_sz    -= ext_sz;
     514       12048 :   }
     515        6027 :   FD_TLS_DECODE_LIST_END
     516             : 
     517             :   /* TODO Fail if trailing bytes detected? */
     518             : 
     519        6027 :   return (long)( wire_laddr - (ulong)wire );
     520        6027 : }
     521             : 
     522             : long
     523             : fd_tls_encode_cert_x509( uchar const * x509,
     524             :                          ulong         x509_sz,
     525             :                          uchar *       wire,
     526        6027 :                          ulong         wire_sz ) {
     527             : 
     528        6027 :   ulong wire_laddr = (ulong)wire;
     529             : 
     530             :   /* TLS Record Header */
     531        6027 :   uchar msg_type = (uchar)FD_TLS_MSG_CERT;
     532             : 
     533             :   /* TLS Certificate Message header preceding X.509 data */
     534             : 
     535             :   /* All size prefixes known in advance */
     536        6027 :   fd_tls_u24_t msg_sz       = fd_uint_to_tls_u24( (uint)( x509_sz + 9UL ) );
     537        6027 :   fd_tls_u24_t cert_list_sz = fd_uint_to_tls_u24( (uint)( x509_sz + 5UL ) );
     538        6027 :   fd_tls_u24_t cert_sz      = fd_uint_to_tls_u24( (uint)( x509_sz       ) );
     539             : 
     540             :   /* zero sz certificate_request_context
     541             :      (Server certificate never has a request context) */
     542        6027 :   uchar certificate_request_context_sz = (uchar)0;
     543             : 
     544             :   /* No certificate extensions */
     545        6027 :   ushort ext_sz = (ushort)0;
     546             : 
     547        6027 : # define FIELDS( FIELD )                                            \
     548       12054 :     FIELD( 0, &msg_type,                         uchar,   1       ) \
     549       12054 :     FIELD( 1, &msg_sz,                           tls_u24, 1       ) \
     550       12054 :       FIELD( 2, &certificate_request_context_sz, uchar,   1       ) \
     551       12054 :       FIELD( 3, &cert_list_sz,                   tls_u24, 1       ) \
     552       12054 :         FIELD( 4, &cert_sz,                      tls_u24, 1       ) \
     553       12054 :         FIELD( 5, x509,                          uchar,   x509_sz ) \
     554       12054 :         FIELD( 6, &ext_sz,                       ushort,  1       )
     555       12054 :     FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
     556        6027 : # undef FIELDS
     557             : 
     558        6027 :   return (long)( wire_laddr - (ulong)wire );
     559        6027 : }
     560             : 
     561             : long
     562             : fd_tls_encode_enc_ext( fd_tls_enc_ext_t const * in,
     563             :                        uchar *                  wire,
     564        6027 :                        ulong                    wire_sz ) {
     565             : 
     566        6027 :   ulong wire_laddr = (ulong)wire;
     567             : 
     568             :   /* ALPN */
     569             : 
     570        6027 :   if( in->alpn.bufsz ) {
     571        6024 :     fd_tls_ext_hdr_t ext_hdr = { .type = FD_TLS_EXT_ALPN,
     572        6024 :                                  .sz   = (ushort)( in->alpn.bufsz+2 ) };
     573        6024 :     FD_TLS_ENCODE_SUB( fd_tls_encode_ext_hdr,  &ext_hdr  );
     574        6024 :     FD_TLS_ENCODE_SUB( fd_tls_encode_ext_alpn, &in->alpn );
     575        6024 :   }
     576             : 
     577             :   /* QUIC transport params */
     578             : 
     579        6027 :   if( in->quic_tp.buf ) {
     580        6024 :     ushort ext_type = FD_TLS_EXT_QUIC_TRANSPORT_PARAMS;
     581        6024 :     ushort ext_sz   = (ushort)in->quic_tp.bufsz;
     582        6024 : #   define FIELDS( FIELD )             \
     583       12048 :       FIELD( 0, &ext_type, ushort, 1 ) \
     584       12048 :       FIELD( 1, &ext_sz,   ushort, 1 ) \
     585       12048 :         FIELD( 2, in->quic_tp.buf, uchar, in->quic_tp.bufsz )
     586       12048 :       FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
     587        6024 : #   undef FIELDS
     588        6024 :   }
     589             : 
     590             :   /* Server certificate type */
     591             : 
     592        6027 :   if( in->server_cert.cert_type ) {
     593           0 :     ushort ext_type  = FD_TLS_EXT_SERVER_CERT_TYPE;
     594           0 :     ushort ext_sz    = 1;
     595           0 :     uchar  cert_type = (uchar)in->server_cert.cert_type;
     596           0 : #   define FIELDS( FIELD )                \
     597           0 :       FIELD( 0, &ext_type,    ushort, 1 ) \
     598           0 :       FIELD( 1, &ext_sz,      ushort, 1 ) \
     599           0 :         FIELD( 2, &cert_type, uchar,  1 )
     600           0 :       FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
     601           0 : #   undef FIELDS
     602           0 :   }
     603             : 
     604             :   /* Client certificate type */
     605             : 
     606        6027 :   if( in->client_cert.cert_type ) {
     607           0 :     ushort ext_type  = FD_TLS_EXT_CLIENT_CERT_TYPE;
     608           0 :     ushort ext_sz    = 1;
     609           0 :     uchar  cert_type = (uchar)in->client_cert.cert_type;
     610           0 : #   define FIELDS( FIELD )                \
     611           0 :       FIELD( 0, &ext_type,    ushort, 1 ) \
     612           0 :       FIELD( 1, &ext_sz,      ushort, 1 ) \
     613           0 :         FIELD( 2, &cert_type, uchar,  1 )
     614           0 :       FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
     615           0 : #   undef FIELDS
     616           0 :   }
     617             : 
     618        6027 :   return (long)( wire_laddr - (ulong)wire );
     619        6027 : }
     620             : 
     621             : long
     622             : fd_tls_encode_raw_public_key( uchar const * key,
     623             :                               uchar *       wire,
     624           0 :                               ulong         wire_sz ) {
     625             : 
     626           0 :   ulong wire_laddr = (ulong)wire;
     627             : 
     628             :   /* TLS Record Header */
     629           0 :   uchar msg_type = (uchar)FD_TLS_MSG_CERT;
     630             : 
     631             :   /* TLS Certificate Message header preceding X.509 data */
     632             : 
     633             :   /* All size prefixes known in advance */
     634           0 :   uint         rpk_sz       = sizeof(fd_asn1_ed25519_pubkey_prefix)+32UL;
     635           0 :   fd_tls_u24_t msg_sz       = fd_uint_to_tls_u24( (uint)( rpk_sz + 9UL ) );
     636           0 :   fd_tls_u24_t cert_list_sz = fd_uint_to_tls_u24( (uint)( rpk_sz + 5UL ) );
     637           0 :   fd_tls_u24_t cert_sz      = fd_uint_to_tls_u24( (uint)( rpk_sz       ) );
     638             : 
     639             :   /* zero sz certificate_request_context
     640             :      (Server certificate never has a request context) */
     641           0 :   uchar certificate_request_context_sz = (uchar)0;
     642             : 
     643             :   /* No certificate extensions */
     644           0 :   ushort ext_sz = (ushort)0;
     645             : 
     646           0 : # define FIELDS( FIELD )                                            \
     647           0 :     FIELD( 0, &msg_type,                         uchar,   1       ) \
     648           0 :     FIELD( 1, &msg_sz,                           tls_u24, 1       ) \
     649           0 :       FIELD( 2, &certificate_request_context_sz, uchar,   1       ) \
     650           0 :       FIELD( 3, &cert_list_sz,                   tls_u24, 1       ) \
     651           0 :         FIELD( 4, &cert_sz,                      tls_u24, 1       ) \
     652           0 :         FIELD( 5, fd_asn1_ed25519_pubkey_prefix, uchar,   sizeof(fd_asn1_ed25519_pubkey_prefix) ) \
     653           0 :         FIELD( 6, key,                           uchar,   32UL    ) \
     654           0 :         FIELD( 7, &ext_sz,                       ushort,  1       )
     655           0 :     FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
     656           0 : # undef FIELDS
     657             : 
     658           0 :   return (long)( wire_laddr - (ulong)wire );
     659           0 : }
     660             : 
     661             : long
     662             : fd_tls_decode_cert_verify( fd_tls_cert_verify_t * out,
     663             :                            uchar const *          wire,
     664        6027 :                            ulong                  wire_sz ) {
     665             : 
     666        6027 :   ulong wire_laddr = (ulong)wire;
     667             : 
     668        6027 :   ushort sig_sz;
     669        6027 : # define FIELDS( FIELD ) \
     670       12054 :     FIELD( 0, &out->sig_alg, ushort,  1 ) \
     671       12054 :     FIELD( 1, &sig_sz,       ushort,  1 ) \
     672       12054 :     FIELD( 2,  out->sig,     uchar,  64 )
     673       12054 :   FD_TLS_DECODE_STATIC_BATCH( FIELDS )
     674        6027 : # undef FIELDS
     675             : 
     676        6027 :   if( FD_UNLIKELY( ( out->sig_alg != FD_TLS_SIGNATURE_ED25519 )
     677        6027 :                  | (      sig_sz  != 0x40UL                   ) ) )
     678           0 :     return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;
     679             : 
     680        6027 :   return (long)( wire_laddr - (ulong)wire );
     681        6027 : }
     682             : 
     683             : long
     684             : fd_tls_encode_cert_verify( fd_tls_cert_verify_t const * in,
     685             :                            uchar *                      wire,
     686        6027 :                            ulong                        wire_sz ) {
     687             : 
     688        6027 :   ulong wire_laddr = (ulong)wire;
     689             : 
     690        6027 :   ushort sig_sz = 0x40;
     691        6027 : # define FIELDS( FIELD ) \
     692       12054 :     FIELD( 0, &in->sig_alg, ushort,  1 ) \
     693       12054 :     FIELD( 1, &sig_sz,      ushort,  1 ) \
     694       12054 :     FIELD( 2,  in->sig,     uchar,  64 )
     695       12054 :   FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
     696        6027 : # undef FIELDS
     697             : 
     698        6027 :   return (long)( wire_laddr - (ulong)wire );
     699        6027 : }
     700             : 
     701             : long
     702             : fd_tls_decode_ext_server_name( fd_tls_ext_server_name_t * out,
     703             :                                uchar const *              wire,
     704           3 :                                ulong                      wire_sz ) {
     705             : 
     706           3 :   ulong wire_laddr = (ulong)wire;
     707             : 
     708             :   /* TLS v1.3 server name lists practically always have one element. */
     709             : 
     710          12 :   FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(uchar) ) {
     711             :     /* Read type and length */
     712           3 :     uchar  name_type;
     713           3 :     ushort name_sz;
     714           3 : #   define FIELDS( FIELD )              \
     715           6 :       FIELD( 0, &name_type, uchar,  1 ) \
     716           6 :       FIELD( 1, &name_sz,   ushort, 1 )
     717           6 :       FD_TLS_DECODE_STATIC_BATCH( FIELDS )
     718           3 : #   undef FIELDS
     719             : 
     720             :     /* Bounds check name */
     721           3 :     if( FD_UNLIKELY( wire_laddr + name_sz > list_stop ) )
     722           0 :       return -(long)FD_TLS_ALERT_DECODE_ERROR;
     723             : 
     724             :     /* Decode name on first use */
     725           3 :     if( ( ( name_type == FD_TLS_SERVER_NAME_TYPE_DNS )
     726           3 :         & ( name_sz < 254                            )
     727           3 :         & ( out->host_name_len == 0                  ) ) ) {
     728           3 :       out->host_name_len = (uchar)name_sz;
     729           3 :       memcpy( out->host_name, (uchar const *)wire_laddr, name_sz );
     730           3 :       out->host_name[ name_sz ] = '\0';
     731           3 :     }
     732             : 
     733             :     /* Seek to next name */
     734           3 :     wire_laddr += name_sz;
     735           3 :     wire_sz    -= name_sz;
     736           3 :   }
     737           3 :   FD_TLS_DECODE_LIST_END
     738             : 
     739           3 :   return (long)( wire_laddr - (ulong)wire );
     740           3 : }
     741             : 
     742             : long
     743             : fd_tls_decode_ext_supported_groups( fd_tls_ext_supported_groups_t * out,
     744             :                                     uchar const *                   wire,
     745        6036 :                                     ulong                           wire_sz ) {
     746             : 
     747        6036 :   ulong wire_laddr = (ulong)wire;
     748             : 
     749       24144 :   FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(uchar) ) {
     750        6051 :     ushort group;
     751        6051 :     FD_TLS_DECODE_FIELD( &group, ushort );
     752        6051 :     switch( group ) {
     753        6036 :     case FD_TLS_GROUP_X25519:
     754        6036 :       out->x25519 = 1;
     755        6036 :       break;
     756          15 :     default:
     757             :       /* Ignore unsupported groups ... */
     758          15 :       break;
     759        6051 :     }
     760        6051 :   }
     761        6036 :   FD_TLS_DECODE_LIST_END
     762             : 
     763        6036 :   return (long)( wire_laddr - (ulong)wire );
     764        6036 : }
     765             : 
     766             : long
     767             : fd_tls_decode_ext_supported_versions( fd_tls_ext_supported_versions_t * out,
     768             :                                       uchar const *                     wire,
     769        6036 :                                       ulong                             wire_sz ) {
     770             : 
     771        6036 :   ulong wire_laddr = (ulong)wire;
     772             : 
     773       24144 :   FD_TLS_DECODE_LIST_BEGIN( uchar, alignof(ushort) ) {
     774        6036 :     ushort group;
     775        6036 :     FD_TLS_DECODE_FIELD( &group, ushort );
     776        6036 :     switch( group ) {
     777        6036 :     case FD_TLS_VERSION_TLS13:
     778        6036 :       out->tls13 = 1;
     779        6036 :       break;
     780           0 :     default:
     781             :       /* Ignore unsupported TLS versions ... */
     782           0 :       break;
     783        6036 :     }
     784        6036 :   }
     785        6036 :   FD_TLS_DECODE_LIST_END
     786             : 
     787        6036 :   return (long)( wire_laddr - (ulong)wire );
     788        6036 : }
     789             : 
     790             : long
     791             : fd_tls_decode_ext_signature_algorithms( fd_tls_ext_signature_algorithms_t * out,
     792             :                                         uchar const *                       wire,
     793        6036 :                                         ulong                               wire_sz ) {
     794             : 
     795        6036 :   ulong wire_laddr = (ulong)wire;
     796             : 
     797       24144 :   FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(ushort) ) {
     798        6093 :     ushort group;
     799        6093 :     FD_TLS_DECODE_FIELD( &group, ushort );
     800        6093 :     switch( group ) {
     801        6036 :     case FD_TLS_SIGNATURE_ED25519:
     802        6036 :       out->ed25519 = 1;
     803        6036 :       break;
     804          57 :     default:
     805             :       /* Ignore unsupported signature algorithms ... */
     806          57 :       break;
     807        6093 :     }
     808        6093 :   }
     809        6036 :   FD_TLS_DECODE_LIST_END
     810             : 
     811        6036 :   return (long)( wire_laddr - (ulong)wire );
     812        6036 : }
     813             : 
     814             : long
     815             : fd_tls_decode_key_share( fd_tls_key_share_t * out,
     816             :                          uchar const *        wire,
     817       12066 :                          ulong                wire_sz ) {
     818             : 
     819       12066 :   ulong wire_laddr = (ulong)wire;
     820             : 
     821             :   /* Read type and length */
     822       12066 :   ushort group;
     823       12066 :   ushort kex_data_sz;
     824       12066 : # define FIELDS( FIELD )                \
     825       24132 :     FIELD( 0, &group,       ushort, 1 ) \
     826       24132 :     FIELD( 1, &kex_data_sz, ushort, 1 )
     827       24132 :     FD_TLS_DECODE_STATIC_BATCH( FIELDS )
     828       12066 : # undef FIELDS
     829             : 
     830             :   /* Bounds check */
     831       12066 :   if( FD_UNLIKELY( kex_data_sz > wire_sz ) )
     832           0 :     return -(long)FD_TLS_ALERT_DECODE_ERROR;
     833             : 
     834       12066 :   switch( group ) {
     835       12066 :   case FD_TLS_GROUP_X25519:
     836       12066 :     if( FD_UNLIKELY( kex_data_sz != 32UL ) )
     837           0 :       return -(long)FD_TLS_ALERT_DECODE_ERROR;
     838       12066 :     out->has_x25519 = 1;
     839       12066 :     memcpy( out->x25519, (uchar const *)wire_laddr, 32UL );
     840       12066 :     break;
     841           0 :   default:
     842             :     /* Ignore unsupported key share groups ... */
     843           0 :     break;
     844       12066 :   }
     845             : 
     846             :   /* Seek to next group */
     847       12066 :   wire_laddr += kex_data_sz;
     848       12066 :   wire_sz    -= kex_data_sz;
     849             : 
     850       12066 :   return (long)( wire_laddr - (ulong)wire );
     851       12066 : }
     852             : 
     853             : long
     854             : fd_tls_decode_key_share_list( fd_tls_key_share_t * out,
     855             :                               uchar const *        wire,
     856        6036 :                               ulong                wire_sz ) {
     857             : 
     858        6036 :   ulong wire_laddr = (ulong)wire;
     859             : 
     860       24144 :   FD_TLS_DECODE_LIST_BEGIN( ushort, alignof(uchar) ) {
     861        6036 :     FD_TLS_DECODE_SUB( fd_tls_decode_key_share, out );
     862        6036 :   }
     863        6036 :   FD_TLS_DECODE_LIST_END
     864             : 
     865        6036 :   return (long)( wire_laddr - (ulong)wire );
     866        6036 : }
     867             : 
     868             : long
     869             : fd_tls_decode_ext_cert_type_list( fd_tls_ext_cert_type_list_t * out,
     870             :                                   uchar const *                 wire,
     871           6 :                                   ulong                         wire_sz ) {
     872             : 
     873           6 :   ulong wire_laddr = (ulong)wire;
     874             : 
     875           6 :   out->present = 1;
     876          24 :   FD_TLS_DECODE_LIST_BEGIN( uchar, alignof(uchar) ) {
     877          12 :     uchar cert_type;
     878          12 :     FD_TLS_DECODE_FIELD( &cert_type, uchar );  /* is this really a uchar? */
     879          12 :     switch( cert_type ) {
     880           6 :     case FD_TLS_CERTTYPE_X509:       out->x509 = 1;       break;
     881           6 :     case FD_TLS_CERTTYPE_RAW_PUBKEY: out->raw_pubkey = 1; break;
     882           0 :     default:
     883             :       /* Ignore unsupported cert types ... */
     884           0 :       break;
     885          12 :     }
     886          12 :   }
     887           6 :   FD_TLS_DECODE_LIST_END
     888             : 
     889           6 :   return (long)( wire_laddr - (ulong)wire );
     890           6 : }
     891             : 
     892             : long
     893             : fd_tls_encode_ext_cert_type_list( fd_tls_ext_cert_type_list_t in,
     894             :                                   uchar const *               wire,
     895           0 :                                   ulong                       wire_sz ) {
     896             : 
     897           0 :   ulong wire_laddr = (ulong)wire;
     898             : 
     899             :   /* Encode list size */
     900           0 :   uchar cnt = (uchar)fd_uchar_popcnt( in.uc );
     901           0 :   FD_TLS_ENCODE_FIELD( &cnt, uchar );
     902             : 
     903             :   /* Encode list */
     904           0 :   uchar * fields = FD_TLS_SKIP_FIELDS( uchar, cnt );
     905           0 :   if( in.x509       ) *fields++ = FD_TLS_CERTTYPE_X509;
     906           0 :   if( in.raw_pubkey ) *fields++ = FD_TLS_CERTTYPE_RAW_PUBKEY;
     907             : 
     908           0 :   return (long)( wire_laddr - (ulong)wire );
     909           0 : }
     910             : 
     911             : long
     912             : fd_tls_decode_ext_cert_type( fd_tls_ext_cert_type_t * out,
     913             :                               uchar const *           wire,
     914           0 :                               ulong                   wire_sz ) {
     915           0 :   ulong wire_laddr = (ulong)wire;
     916           0 :   FD_TLS_DECODE_FIELD( &out->cert_type, uchar );
     917           0 :   return (long)( wire_laddr - (ulong)wire );
     918           0 : }
     919             : 
     920             : long
     921             : fd_tls_encode_ext_cert_type( fd_tls_ext_cert_type_t in,
     922             :                              uchar const *          wire,
     923           0 :                              ulong                  wire_sz ) {
     924           0 :   ulong wire_laddr = (ulong)wire;
     925           0 :   FD_TLS_ENCODE_FIELD( &in.cert_type, uchar );
     926           0 :   return (long)( wire_laddr - (ulong)wire );
     927           0 : }
     928             : 
     929             : long
     930             : fd_tls_decode_ext_opaque( fd_tls_ext_opaque_t * const out,
     931             :                           uchar const *         const wire,
     932       18087 :                           ulong                       wire_sz ) {
     933       18087 :   out->buf   = wire;
     934       18087 :   out->bufsz = wire_sz;
     935       18087 :   return (long)wire_sz;
     936       18087 : }
     937             : 
     938             : long
     939             : fd_tls_decode_ext_alpn( fd_tls_ext_alpn_t * const out,
     940             :                         uchar const *       const wire,
     941       12054 :                         ulong                     wire_sz ) {
     942       12054 :   ulong wire_laddr = (ulong)wire;
     943       12054 :   ushort alpn_sz;
     944       12054 :   FD_TLS_DECODE_FIELD( &alpn_sz, ushort );
     945       12054 :   if( FD_UNLIKELY( (ulong)alpn_sz != wire_sz ) )
     946           0 :     return -(long)FD_TLS_ALERT_DECODE_ERROR;
     947       12054 :   return 2L + (long)fd_tls_decode_ext_opaque( out, (uchar const *)wire_laddr, wire_sz );
     948       12054 : }
     949             : 
     950             : long
     951             : fd_tls_encode_ext_alpn( fd_tls_ext_alpn_t const * in,
     952             :                         uchar *                   wire,
     953       12081 :                         ulong                     wire_sz ) {
     954       12081 :   ulong sz = 2UL + in->bufsz;
     955       12081 :   if( FD_UNLIKELY( sz>wire_sz ) )
     956           0 :     return -(long)FD_TLS_ALERT_INTERNAL_ERROR;
     957       12081 :   wire[0] = (uchar)( (in->bufsz >> 8)&0xFF );
     958       12081 :   wire[1] = (uchar)(  in->bufsz      &0xFF );
     959       12081 :   fd_memcpy( wire+2UL, in->buf, in->bufsz );
     960       12081 :   return (long)sz;
     961       12081 : }
     962             : 
     963             : /* fd_tls_client_handle_x509 extracts the Ed25519 subject public key
     964             :    from the certificate.  Does not validate the signature found on the
     965             :    certificate (might be self-signed).  [cert,cert+cert_sz) points to
     966             :    an ASN.1 DER serialization of the certificate.  On success, copies
     967             :    public key bits to out_pubkey and returns 0U.  On failure, returns
     968             :    positive TLS alert error code. */
     969             : 
     970             : static uint
     971             : fd_tls_client_handle_x509( uchar const *  const cert,
     972             :                            ulong          const cert_sz,
     973        6027 :                            uchar const ** const out_pubkey ) {
     974        6027 :   uchar const * pubkey = fd_x509_mock_pubkey( cert, cert_sz );
     975        6027 :   if( FD_UNLIKELY( !pubkey ) )
     976           0 :     return FD_TLS_ALERT_UNSUPPORTED_CERTIFICATE;
     977        6027 :   *out_pubkey = pubkey;
     978        6027 :   return 0U;
     979        6027 : }
     980             : 
     981             : static long
     982             : fd_tls_extract_cert_pubkey_( fd_tls_extract_cert_pubkey_res_t * res,
     983             :                              uchar const * cert_chain,
     984             :                              ulong         cert_chain_sz,
     985        6027 :                              uint          cert_type ) {
     986             : 
     987        6027 :   fd_memset( res, 0, sizeof(fd_tls_extract_cert_pubkey_res_t) );
     988             : 
     989        6027 :   ulong wire_laddr = (ulong)cert_chain;
     990        6027 :   ulong wire_sz    = cert_chain_sz;
     991             : 
     992             :   /* Skip 'opaque certificate_request_context<0..2^8-1>' */
     993        6027 :   uchar const * opaque_sz = FD_TLS_SKIP_FIELD( uchar );
     994        6027 :   uchar const * opaque    = FD_TLS_SKIP_FIELDS( uchar, *opaque_sz );
     995           0 :   (void)opaque;
     996             : 
     997             :   /* Get first entry of certificate chain
     998             :      CertificateEntry certificate_list<0..2^24-1> */
     999        6027 :   fd_tls_u24_t const * cert_list_sz_be = FD_TLS_SKIP_FIELD( fd_tls_u24_t );
    1000           0 :   fd_tls_u24_t         cert_list_sz_   = fd_tls_u24_bswap( *cert_list_sz_be );
    1001        6027 :   uint                 cert_list_sz    = fd_tls_u24_to_uint( cert_list_sz_ );
    1002        6027 :   if( FD_UNLIKELY( cert_list_sz==0U ) ) {
    1003           0 :     res->alert  = FD_TLS_ALERT_BAD_CERTIFICATE;
    1004           0 :     res->reason = FD_TLS_REASON_CERT_CHAIN_EMPTY;
    1005           0 :     return -1L;
    1006           0 :   }
    1007             : 
    1008             :   /* Get certificate size */
    1009        6027 :   fd_tls_u24_t const * cert_sz_be = FD_TLS_SKIP_FIELD( fd_tls_u24_t );
    1010           0 :   fd_tls_u24_t         cert_sz_   = fd_tls_u24_bswap( *cert_sz_be );
    1011        6027 :   uint                 cert_sz    = fd_tls_u24_to_uint( cert_sz_ );
    1012        6027 :   if( FD_UNLIKELY( cert_sz>wire_sz ) ) {
    1013           0 :     res->alert = FD_TLS_ALERT_DECODE_ERROR;
    1014           0 :     res->reason = FD_TLS_REASON_CERT_PARSE;
    1015           0 :     return -1L;
    1016           0 :   }
    1017             : 
    1018        6027 :   void * cert = (void *)wire_laddr;
    1019             : 
    1020        6027 :   switch( cert_type ) {
    1021             : 
    1022        6027 :   case FD_TLS_CERTTYPE_X509: {
    1023             : 
    1024             :     /* DER-encoded X.509 certificate */
    1025             : 
    1026        6027 :     uint x509_alert = fd_tls_client_handle_x509( cert, cert_sz, &res->pubkey );
    1027        6027 :     if( FD_UNLIKELY( x509_alert!=0U ) ) {
    1028           0 :       res->pubkey = NULL;
    1029           0 :       res->alert  = x509_alert;
    1030           0 :       res->reason = FD_TLS_REASON_X509_PARSE;
    1031           0 :       return -1L;
    1032           0 :     }
    1033             : 
    1034        6027 :     return 0L;
    1035        6027 :   }
    1036             : 
    1037           0 :   case FD_TLS_CERTTYPE_RAW_PUBKEY: {
    1038             : 
    1039             :     /* Interpret certificate entry as raw public key (RFC 7250)
    1040             :        'opaque ASN1_subjectPublicKeyInfo<1..2^24-1>' */
    1041             : 
    1042           0 :     res->pubkey = fd_ed25519_public_key_from_asn1( cert, cert_sz );
    1043           0 :     if( FD_UNLIKELY( !res->pubkey ) ) {
    1044           0 :       res->reason = FD_TLS_REASON_SPKI_PARSE;
    1045           0 :       res->alert  = FD_TLS_ALERT_BAD_CERTIFICATE;
    1046           0 :       return -1L;
    1047           0 :     }
    1048             : 
    1049           0 :     return 0L;
    1050           0 :   }
    1051             : 
    1052           0 :   default:
    1053           0 :     __builtin_unreachable();
    1054             : 
    1055        6027 :   } /* end switch */
    1056        6027 : }
    1057             : 
    1058             : fd_tls_extract_cert_pubkey_res_t
    1059             : fd_tls_extract_cert_pubkey( uchar const * cert_chain,
    1060             :                             ulong         cert_chain_sz,
    1061        6027 :                             uint          cert_type ) {
    1062        6027 :   fd_tls_extract_cert_pubkey_res_t res;
    1063        6027 :   long ret = fd_tls_extract_cert_pubkey_( &res, cert_chain, cert_chain_sz, cert_type );
    1064        6027 :   (void)ret;
    1065        6027 :   return res;
    1066        6027 : }

Generated by: LCOV version 1.14