LCOV - code coverage report
Current view: top level - waltz/tls - fd_tls.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 1047 1267 82.6 %
Date: 2026-09-17 04:28:31 Functions: 32 32 100.0 %

          Line data    Source code
       1             : #include "fd_tls.h"
       2             : #include "../../util/sanitize/fd_msan.h"
       3             : #include "fd_tls_proto.h"
       4             : #include "../../ballet/ed25519/fd_ed25519.h"
       5             : #include "../../ballet/ed25519/fd_x25519.h"
       6             : #include "../../ballet/hmac/fd_hmac.h"
       7             : #include "../../ballet/secp256r1/fd_secp256r1.h"
       8             : #include "../../ballet/x509/fd_x509.h"
       9             : #include "../../ballet/x509/fd_x509_verify.h"
      10             : 
      11             : /* Pre-generated keys */
      12             : 
      13             : char const fd_tls13_cli_sign_prefix[ 98 ] =
      14             :   "                                "  /* 32 spaces */
      15             :   "                                "  /* 32 spaces */
      16             :   "TLS 1.3, client CertificateVerify";
      17             : 
      18             : char const fd_tls13_srv_sign_prefix[ 98 ] =
      19             :   "                                "  /* 32 spaces */
      20             :   "                                "  /* 32 spaces */
      21             :   "TLS 1.3, server CertificateVerify";
      22             : 
      23             : //uchar empty_hash[ 32 ];
      24             : //fd_sha256_hash( empty_hash, NULL, 0UL );
      25             : static uchar const empty_hash[ 32 ] =
      26             :   { 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
      27             :     0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
      28             :     0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
      29             :     0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 };
      30             : 
      31             : //static uchar const psk[ 32 ] = {0};
      32             : //uchar early_secret[ 32 ];
      33             : //fd_hmac_sha256( /* data */ psk, 32UL,
      34             : //                /* salt */ NULL, 0UL,
      35             : //                early_secret );
      36             : //static uchar const early_secret[ 32 ] =
      37             : //  { 0x33, 0xad, 0x0a, 0x1c, 0x60, 0x7e, 0xc0, 0x3b,
      38             : //    0x09, 0xe6, 0xcd, 0x98, 0x93, 0x68, 0x0c, 0xe2,
      39             : //    0x10, 0xad, 0xf3, 0x00, 0xaa, 0x1f, 0x26, 0x60,
      40             : //    0xe1, 0xb2, 0x2e, 0x10, 0xf1, 0x70, 0xf9, 0x2a };
      41             : 
      42             : //uchar handshake_derived[ 32 ];
      43             : //fd_tls_hkdf_expand_label( handshake_derived,
      44             : //                          early_secret,
      45             : //                          "derived",   7UL,
      46             : //                          empty_hash, 32UL );
      47             : static uchar const handshake_derived[ 32 ] =
      48             :   { 0x6f, 0x26, 0x15, 0xa1, 0x08, 0xc7, 0x02, 0xc5,
      49             :     0x67, 0x8f, 0x54, 0xfc, 0x9d, 0xba, 0xb6, 0x97,
      50             :     0x16, 0xc0, 0x76, 0x18, 0x9c, 0x48, 0x25, 0x0c,
      51             :     0xeb, 0xea, 0xc3, 0x57, 0x6c, 0x36, 0x11, 0xba };
      52             : 
      53             : /* fd_tls_t boilerplate */
      54             : 
      55             : ulong
      56         336 : fd_tls_align( void ) {
      57         336 :   return alignof(fd_tls_t);
      58         336 : }
      59             : 
      60             : ulong
      61         336 : fd_tls_footprint( void ) {
      62         336 :   return sizeof(fd_tls_t);
      63         336 : }
      64             : 
      65             : void *
      66         336 : fd_tls_new( void * mem ) {
      67             : 
      68         336 :   if( FD_UNLIKELY( !mem ) ) {
      69           0 :     FD_LOG_WARNING(( "NULL mem" ));
      70           0 :     return NULL;
      71           0 :   }
      72             : 
      73         336 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_tls_align() ) ) ) {
      74           0 :     FD_LOG_WARNING(( "unaligned mem" ));
      75           0 :     return NULL;
      76           0 :   }
      77             : 
      78         336 :   ulong fp = fd_tls_footprint();
      79         336 :   memset( mem, 0, fp );
      80         336 :   return mem;
      81         336 : }
      82             : 
      83             : fd_tls_t *
      84         120 : fd_tls_join( void * mem ) {
      85         120 :   return (fd_tls_t *)mem;
      86         120 : }
      87             : 
      88             : void *
      89         120 : fd_tls_leave( fd_tls_t * server ) {
      90         120 :   return (void *)server;
      91         120 : }
      92             : 
      93             : void *
      94         120 : fd_tls_delete( void * mem ) {
      95         120 :   return mem;
      96         120 : }
      97             : 
      98             : /* TODO create internal state machine and integrate Tango for
      99             :         accelerating cryptographic computations (e.g. FPGA sigverify) */
     100             : 
     101             : fd_tls_estate_srv_t *
     102        7005 : fd_tls_estate_srv_new( void * mem ) {
     103             : 
     104        7005 :   fd_tls_estate_srv_t * hs = mem;
     105             : 
     106        7005 :   memset( hs, 0, sizeof(fd_tls_estate_srv_t) );
     107        7005 :   hs->base.state  = FD_TLS_HS_START;
     108        7005 :   hs->base.server = 1;
     109             : 
     110        7005 :   return hs;
     111        7005 : }
     112             : 
     113             : fd_tls_estate_cli_t *
     114        9897 : fd_tls_estate_cli_new( void * mem ) {
     115             : 
     116        9897 :   fd_tls_estate_cli_t * hs = mem;
     117             : 
     118        9897 :   memset( hs, 0, sizeof(fd_tls_estate_cli_t) );
     119        9897 :   hs->base.state = FD_TLS_HS_START;
     120             : 
     121        9897 :   return hs;
     122        9897 : }
     123             : 
     124             : void *
     125             : fd_tls_hkdf_expand_label( uchar *       out,
     126             :                           ulong         out_sz,
     127             :                           uchar const   secret[ 32 ],
     128             :                           char const *  label,
     129             :                           ulong         label_sz,
     130             :                           uchar const * context,
     131      480102 :                           ulong         context_sz ) {
     132             : 
     133      480102 : # define LABEL_BUFSZ (64UL)
     134      480102 :   FD_TEST( label_sz  <=LABEL_BUFSZ );
     135      480102 :   FD_TEST( context_sz<=LABEL_BUFSZ );
     136      480102 :   FD_TEST( out_sz    <=32UL        );
     137             : 
     138             :   /* Create HKDF info */
     139      480102 :   uchar info[ 2+1+6+LABEL_BUFSZ+1+LABEL_BUFSZ+1 ];
     140      480102 :   ulong info_sz = 0UL;
     141             : 
     142             :   /* Length of hash output */
     143      480102 :   info[0]=0; info[1]=(uchar)out_sz;
     144      480102 :   info_sz += 2UL;
     145             : 
     146             :   /* Length prefix of label */
     147      480102 :   info[ info_sz ] = (uchar)( 6UL + label_sz );
     148      480102 :   info_sz += 1UL;
     149             : 
     150             :   /* Label */
     151      480102 :   memcpy( info+info_sz, "tls13 ", 6UL );
     152      480102 :   info_sz += 6UL;
     153      480102 :   memcpy( info+info_sz, label, label_sz );
     154      480102 :   info_sz += label_sz;
     155             : 
     156             :   /* Length prefix of context */
     157      480102 :   info[ info_sz ] = (uchar)( context_sz );
     158      480102 :   info_sz += 1UL;
     159             : 
     160             :   /* Context */
     161      480102 :   fd_memcpy( info+info_sz, context, context_sz );
     162      480102 :   info_sz += context_sz;
     163             : 
     164             :   /* HKDF-Expand suffix */
     165      480102 :   info[ info_sz ] = 0x01;
     166      480102 :   info_sz += 1UL;
     167             : 
     168             :   /* Compute result of HKDF-Expand-Label */
     169      480102 :   uchar hash[ 32 ];
     170      480102 :   fd_msan_unpoison( info, info_sz );
     171      480102 :   fd_hmac_sha256( info, info_sz, secret, 32UL, hash );
     172      480102 :   fd_memcpy( out, hash, out_sz );
     173      480102 :   return out;
     174      480102 : # undef LABEL_BUFSZ
     175      480102 : }
     176             : 
     177             : static int
     178             : fd_tls_has_alpn( uchar const * list,
     179             :                  ulong const   list_sz,
     180             :                  uchar const * target,
     181        6471 :                  ulong const   target_sz ) {
     182        6471 :   if( FD_UNLIKELY( target_sz<=1UL ) ) return 0;
     183             : 
     184        6471 :   uchar const * list_end = list + list_sz;
     185        6879 :   while( list < list_end ) {
     186        6867 :     ulong ele_sz = list[0];
     187        6867 :     list += 1UL;
     188        6867 :     uchar const * ele = list;
     189        6867 :     if( FD_UNLIKELY( (long)ele_sz > list_end-list ) ) return -1;
     190        6867 :     list += ele_sz;
     191             : 
     192        6867 :     if( ele_sz==target_sz-1UL )
     193        6459 :       if( 0==memcmp( ele, target+1UL, ele_sz ) )
     194        6459 :         return 1;
     195        6867 :   }
     196          12 :   return 0;
     197        6471 : }
     198             : 
     199             : /* fd_tls_alert is a convenience function for setting a handshake
     200             :    failure alert and reason code. */
     201             : 
     202             : static inline long __attribute__((warn_unused_result))
     203             : fd_tls_alert( fd_tls_estate_base_t * hs,
     204             :               uint                   alert,
     205         441 :               ushort                 reason ) { /* hint if not yet set */
     206         441 :   if( !hs->reason ) hs->reason = reason;
     207         441 :   return -(long)alert;
     208         441 : }
     209             : 
     210             : /* fd_tls_send_cert_verify generates and sends a CertificateVerify
     211             :    message.  Returns 0L on success and negated TLS alert number on
     212             :    failure.  this is the local client or server object.  hs is the
     213             :    local handshake object.  transcript is the hash state immediately
     214             :    preceding the CertificateVerify (where last entry is Certificate).
     215             :    cs describes the negotiated cipher suite.  is_client is 1 if the
     216             :    local role is a client, 0 otherwise. */
     217             : 
     218             : static long
     219             : fd_tls_send_cert_verify( fd_tls_t const *       this,
     220             :                          fd_tls_estate_base_t * hs,
     221             :                          fd_sha256_t *          transcript,
     222       14163 :                          int                    is_client ) {
     223             : 
     224             :   /* Export current transcript hash
     225             :      And create message to be signed */
     226             : 
     227       14163 :   uchar sign_msg[ FD_TLS_CV_SIGN_SZ ];
     228       14163 :   fd_memcpy( sign_msg,
     229       14163 :              is_client ? fd_tls13_cli_sign_prefix : fd_tls13_srv_sign_prefix,
     230       14163 :              98UL );
     231             : 
     232       14163 :   fd_sha256_t transcript_clone = *transcript;
     233       14163 :   fd_sha256_fini( &transcript_clone, sign_msg+98 );
     234             : 
     235             :   /* Sign certificate */
     236             : 
     237       14163 :   uchar cert_verify_sig[ FD_ED25519_SIG_SZ ];
     238       14163 :   fd_tls_sign( &this->sign, cert_verify_sig, sign_msg );
     239             : 
     240             :   /* Create CertificateVerify message */
     241             : 
     242       14163 : # define MSG_BUFSZ (512UL)
     243       14163 :   uchar msg_buf[ MSG_BUFSZ ];
     244             : 
     245       14163 :   ulong cv_sz;
     246             : 
     247       14163 :   do {
     248       14163 :     uchar *       wire     = msg_buf;
     249       14163 :     uchar * const wire_end = msg_buf + MSG_BUFSZ;
     250             : 
     251             :     /* Leave space for message header */
     252             : 
     253       14163 :     void * hdr_ptr = wire;
     254       14163 :     wire += sizeof(fd_tls_msg_hdr_t);
     255       14163 :     fd_tls_msg_hdr_t hdr = { .type = FD_TLS_MSG_CERT_VERIFY };
     256             : 
     257             :     /* Construct CertificateVerify */
     258             : 
     259       14163 :     fd_tls_cert_verify_t cv = {
     260       14163 :       .algorithm = FD_TLS_SIGNATURE_ED25519,
     261       14163 :       .signature_len = 64,
     262       14163 :     };
     263       14163 :     fd_memcpy( cv.signature, cert_verify_sig, FD_ED25519_SIG_SZ );
     264             : 
     265             :     /* Encode CertificateVerify */
     266             : 
     267       14163 :     long encode_res = fd_tls_encode_cert_verify( &cv, wire, (ulong)(wire_end-wire) );
     268       14163 :     if( FD_UNLIKELY( encode_res<0L ) )
     269           0 :       return fd_tls_alert( hs, (uint)(-encode_res), FD_TLS_REASON_CV_ENCODE );
     270       14163 :     wire += (ulong)encode_res;
     271             : 
     272       14163 :     hdr.sz = fd_uint_to_tls_u24( (uint)encode_res );
     273       14163 :     fd_tls_encode_msg_hdr( &hdr, hdr_ptr, sizeof(fd_tls_msg_hdr_t) );
     274       14163 :     cv_sz = (ulong)(wire - msg_buf);
     275       14163 :   } while(0);
     276             : 
     277             :   /* Send CertificateVerify message */
     278             : 
     279       14163 :   if( FD_UNLIKELY( !this->sendmsg_fn(
     280       14163 :         hs,
     281       14163 :         msg_buf, cv_sz,
     282       14163 :         FD_TLS_LEVEL_HANDSHAKE,
     283       14163 :         /* flush */ 0 ) ) )
     284           0 :     return fd_tls_alert( hs, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_SENDMSG_FAIL );
     285             : 
     286             :   /* Record CertificateVerify in transcript hash */
     287             : 
     288       14163 :   fd_sha256_append( transcript, msg_buf, cv_sz );
     289             : 
     290       14163 : # undef MSG_BUFSZ
     291       14163 :   return 0L;
     292       14163 : }
     293             : 
     294             : static long fd_tls_server_hs_start           ( fd_tls_t const *, fd_tls_estate_srv_t *, uchar const *, ulong, uint );
     295             : static long fd_tls_server_hs_wait_cert       ( fd_tls_t const *, fd_tls_estate_srv_t *, uchar const *, ulong, uint );
     296             : static long fd_tls_server_hs_wait_cert_verify( fd_tls_t const *, fd_tls_estate_srv_t *, uchar const *, ulong, uint );
     297             : static long fd_tls_server_hs_wait_finished   ( fd_tls_t const *, fd_tls_estate_srv_t *, uchar const *, ulong, uint );
     298             : 
     299             : long
     300             : fd_tls_server_handshake( fd_tls_t const *      server,
     301             :                          fd_tls_estate_srv_t * handshake,
     302             :                          void const *          msg,
     303             :                          ulong                 msg_sz,
     304       27627 :                          uint                  encryption_level ) {
     305       27627 :   switch( handshake->base.state ) {
     306        7164 :   case FD_TLS_HS_START:
     307        7164 :     return fd_tls_server_hs_start           ( server, handshake, msg, msg_sz, encryption_level );
     308        6837 :   case FD_TLS_HS_WAIT_CERT:
     309        6837 :     return fd_tls_server_hs_wait_cert       ( server, handshake, msg, msg_sz, encryption_level );
     310        6813 :   case FD_TLS_HS_WAIT_CV:
     311        6813 :     return fd_tls_server_hs_wait_cert_verify( server, handshake, msg, msg_sz, encryption_level );
     312        6813 :   case FD_TLS_HS_WAIT_FINISHED:
     313        6813 :     return fd_tls_server_hs_wait_finished   ( server, handshake, msg, msg_sz, encryption_level );
     314           0 :   default:
     315           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_ILLEGAL_STATE );
     316       27627 :   }
     317       27627 : }
     318             : 
     319             : static long
     320             : fd_tls_server_hs_retry( fd_tls_t const *              server,
     321             :                         fd_tls_estate_srv_t *         handshake,
     322             :                         fd_tls_client_hello_t const * ch,
     323         192 :                         uchar const                   ch1_hash[32] ) {
     324             : 
     325         192 :   if( FD_UNLIKELY( handshake->hello_retry ) ) {
     326             :     /* Already retried but still no X25519 share */
     327           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_ILLEGAL_PARAMETER, FD_TLS_REASON_SENDMSG_FAIL );
     328           0 :   }
     329         192 :   handshake->hello_retry = 1;
     330             : 
     331             :   /* Message buffer */
     332         192 : # define MSG_BUFSZ 512UL
     333         192 :   uchar msg_buf[ MSG_BUFSZ ];
     334             : 
     335             :   /* Transcript hasher (RetryHelloRequest variation)
     336             :      https://datatracker.ietf.org/doc/html/rfc8446#section-4.4.1 */
     337         192 :   fd_sha256_t transcript; fd_sha256_init( &transcript );
     338         192 :   uchar const transcript_prefix[] = { 254, 0x00, 0x00, 32 };
     339         192 :   fd_sha256_append( &transcript, transcript_prefix, sizeof(transcript_prefix) );
     340         192 :   fd_sha256_append( &transcript, ch1_hash, 32 );
     341             : 
     342             :   /* Create HelloRetryRequest message */
     343             : 
     344         192 :   ulong server_hello_sz;
     345             : 
     346         192 :   do {
     347         192 :     uchar *       wire     = msg_buf;
     348         192 :     uchar * const wire_end = msg_buf + MSG_BUFSZ;
     349             : 
     350             :     /* Leave space for message header */
     351             : 
     352         192 :     void * hdr_ptr = wire;
     353         192 :     wire += sizeof(fd_tls_msg_hdr_t);
     354         192 :     fd_tls_msg_hdr_t hdr = { .type = FD_TLS_MSG_SERVER_HELLO };
     355             : 
     356             :     /* Construct server hello */
     357             : 
     358         192 :     fd_tls_server_hello_t sh = {
     359         192 :       .cipher_suite = FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256,
     360         192 :       .key_share    = { .has_x25519 = 1 },
     361         192 :       .session_id   = ch->session_id,
     362         192 :     };
     363         192 :     memcpy( sh.key_share.x25519, server->kex_public_key, 32UL );
     364             : 
     365             :     /* Encode server hello */
     366             : 
     367         192 :     long encode_res = fd_tls_encode_hello_retry_request( &sh, wire, (ulong)(wire_end-wire) );
     368         192 :     if( FD_UNLIKELY( encode_res<0L ) )
     369           0 :       return fd_tls_alert( &handshake->base, (uint)(-encode_res), FD_TLS_REASON_SH_ENCODE );
     370         192 :     wire += (ulong)encode_res;
     371             : 
     372         192 :     hdr.sz = fd_uint_to_tls_u24( (uint)encode_res );
     373         192 :     fd_tls_encode_msg_hdr( &hdr, hdr_ptr, sizeof(fd_tls_msg_hdr_t) );
     374         192 :     server_hello_sz = (ulong)(wire - msg_buf);
     375         192 :   } while(0);
     376             : 
     377             :   /* Call back with HelloRetryRequest */
     378             : 
     379         192 :   if( FD_UNLIKELY( !server->sendmsg_fn(
     380         192 :         handshake,
     381         192 :         msg_buf, server_hello_sz,
     382         192 :         FD_TLS_LEVEL_INITIAL,
     383         192 :         /* flush */ 1 ) ) )
     384           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_SENDMSG_FAIL );
     385             : 
     386             :   /* Record HelloRetryRequest in transcript hash */
     387             : 
     388         192 :   fd_sha256_append( &transcript, msg_buf, server_hello_sz );
     389             : 
     390             :   /* Finish up ********************************************************/
     391             : 
     392             :   /* Store transcript hash state */
     393             : 
     394         192 :   fd_tls_transcript_store( &handshake->transcript, &transcript );
     395             : 
     396             :   /* Done */
     397             : 
     398         192 :   handshake->base.state = FD_TLS_HS_START;
     399             : 
     400         192 : # undef MSG_BUFSZ
     401         192 :   return (long)0L;
     402         192 : }
     403             : 
     404             : /* fd_tls_server_hs_start is invoked in response to the initial
     405             :    ClientHello.  We send back several messages in response, including
     406             :    - the ServerHello, completing cryptographic negotiation
     407             :    - EncryptedExtensions, for further handshake data
     408             :    - Finished, completing the server's handshake message sequence */
     409             : 
     410             : static long
     411             : fd_tls_server_hs_start( fd_tls_t const *      const server,
     412             :                         fd_tls_estate_srv_t * const handshake,
     413             :                         uchar const *         const record,
     414             :                         ulong                       record_sz,
     415        7164 :                         uint                        encryption_level ) {
     416             : 
     417             :   /* Request QUIC transport params */
     418        7164 :   uchar quic_tp[ FD_TLS_EXT_QUIC_PARAMS_SZ_MAX ];
     419        7164 :   long  quic_tp_sz = -1L;
     420        7164 :   if( server->quic )
     421        6075 :     quic_tp_sz = (long)server->quic_tp_self_fn( handshake, quic_tp, FD_TLS_EXT_QUIC_PARAMS_SZ_MAX );
     422        7164 :   if( FD_UNLIKELY( quic_tp_sz > (long)FD_TLS_EXT_QUIC_PARAMS_SZ_MAX ) )
     423           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_QUIC_TP_OVERSZ );
     424             : 
     425        7164 :   if( FD_UNLIKELY( encryption_level != FD_TLS_LEVEL_INITIAL ) )
     426           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_WRONG_ENC_LVL );
     427             : 
     428        7164 :   if( FD_UNLIKELY( !server->cert_x509_sz ) )
     429           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_NO_X509 );
     430             : 
     431             :   /* Message buffer */
     432       20682 : # define MSG_BUFSZ FD_TLS_SERVER_CERT_MSG_SZ_MAX
     433        7164 :   uchar msg_buf[ MSG_BUFSZ ];
     434             : 
     435             :   /* Transcript hasher */
     436        7164 :   fd_sha256_t transcript;
     437        7164 :   if( handshake->hello_retry ) {
     438         192 :     fd_tls_transcript_load( &handshake->transcript, &transcript );
     439        6972 :   } else {
     440        6972 :     fd_sha256_init( &transcript );
     441        6972 :   }
     442             : 
     443             :   /* Read client hello ************************************************/
     444             : 
     445        7164 :   fd_tls_client_hello_t ch = {0};
     446             : 
     447        7164 :   ulong read_sz;
     448        7164 :   do {
     449        7164 :     uchar const *       wire     = record;
     450        7164 :     uchar const * const wire_end = record + record_sz;
     451             : 
     452             :     /* Decode message header */
     453             : 
     454        7164 :     fd_tls_msg_hdr_t msg_hdr = {0};
     455        7164 :     long decode_res = fd_tls_decode_msg_hdr( &msg_hdr, wire, (ulong)(wire_end-wire) );
     456        7164 :     if( FD_UNLIKELY( decode_res<0L ) )
     457           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_CH_PARSE );
     458        7164 :     wire += (ulong)decode_res;
     459             : 
     460        7164 :     if( FD_UNLIKELY( msg_hdr.type != FD_TLS_MSG_CLIENT_HELLO ) )
     461           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_CH_EXPECTED );
     462             : 
     463        7164 :     ulong msg_sz = fd_tls_u24_to_uint( msg_hdr.sz );
     464        7164 :     if( FD_UNLIKELY( msg_sz > (ulong)(wire_end-wire) ) )
     465           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_CH_PARSE );
     466             : 
     467             :     /* Decode Client Hello */
     468             : 
     469        7164 :     decode_res = fd_tls_decode_client_hello( &ch, wire, msg_sz );
     470        7164 :     if( FD_UNLIKELY( decode_res<0L ) )
     471           0 :       return fd_tls_alert( &handshake->base, (uint)(-decode_res), FD_TLS_REASON_CH_PARSE );
     472        7164 :     if( FD_UNLIKELY( (ulong)decode_res != msg_sz ) )
     473           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_CH_PARSE );
     474        7164 :     wire += (ulong)decode_res;
     475             : 
     476        7164 :     read_sz = (ulong)(wire - record);
     477        7164 :   } while(0);
     478             : 
     479             :   /* Check for cryptographic compatibility */
     480             : 
     481        7164 :   if( FD_UNLIKELY( !ch.supported_versions.tls13 ) )
     482          12 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_PROTOCOL_VERSION, FD_TLS_REASON_CH_NEG_VER );
     483        7152 :   if( FD_UNLIKELY( !ch.supported_groups.x25519 ) )
     484          12 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_HANDSHAKE_FAILURE, FD_TLS_REASON_CH_NEG_KX );
     485        7140 :   if( FD_UNLIKELY( !ch.signature_algorithms.ed25519 ) )
     486          12 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_HANDSHAKE_FAILURE, FD_TLS_REASON_CH_NEG_SIG );
     487        7128 :   if( FD_UNLIKELY( !ch.cipher_suites.aes_128_gcm_sha256 ) )
     488          15 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_HANDSHAKE_FAILURE, FD_TLS_REASON_CH_NEG_CIPHER );
     489             : 
     490             :   /* Remember client random for SSLKEYLOGFILE */
     491        7113 :   fd_memcpy( handshake->base.client_random, ch.random, 32UL );
     492             : 
     493             :   /* Detect QUIC */
     494             : 
     495        7113 :   if( server->quic ) {
     496             :     /* QUIC transport parameters are mandatory in QUIC mode */
     497        6075 :     if( FD_UNLIKELY( !ch.quic_tp.buf ) )
     498           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_MISSING_EXTENSION, FD_TLS_REASON_CH_NO_QUIC );
     499             : 
     500             :     /* Inform user of peer's QUIC transport parameters */
     501        6075 :     server->quic_tp_peer_fn( handshake, ch.quic_tp.buf, ch.quic_tp.bufsz );
     502        6075 :   }
     503             : 
     504             :   /* Reject connection if ALPN not advertised */
     505             : 
     506        7113 :   if( server->alpn[0] ) {
     507        6486 :     if( FD_UNLIKELY( !ch.alpn.bufsz ) )
     508          15 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_NO_APPLICATION_PROTOCOL, FD_TLS_REASON_NO_ALPN );
     509        6471 :     int ok = fd_tls_has_alpn( ch.alpn.buf, ch.alpn.bufsz, server->alpn, server->alpn_sz );
     510        6471 :     if( FD_UNLIKELY( ok==-1 ) )
     511           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_ALPN_PARSE );
     512        6471 :     if( FD_UNLIKELY( ok==0 ) )
     513          12 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_NO_APPLICATION_PROTOCOL, FD_TLS_REASON_ALPN_NEG );
     514        6471 :   }
     515             : 
     516             :   /* Record client hello in transcript hash */
     517             : 
     518        7086 :   fd_sha256_append( &transcript, record, read_sz );
     519             : 
     520             :   /* Retry if key share is missing */
     521             : 
     522        7086 :   if( !ch.key_share.has_x25519 ) {
     523         192 :     uchar ch1_hash[ 32 ];
     524         192 :     fd_sha256_fini( &transcript, ch1_hash );
     525         192 :     long rc = fd_tls_server_hs_retry( server, handshake, &ch, ch1_hash );
     526         192 :     /**/ rc = fd_long_if( rc>=0, (long)read_sz, rc );
     527         192 :     return rc;
     528         192 :   }
     529             : 
     530             :   /* Respond with server hello ****************************************/
     531             : 
     532             :   /* Create server random */
     533             : 
     534        6894 :   uchar server_random[ 32 ];
     535        6894 :   fd_chacha_rng_read32( server->rng, server_random );
     536             : 
     537             :   /* Create server hello message */
     538             : 
     539        6894 :   ulong server_hello_sz;
     540             : 
     541        6894 :   do {
     542        6894 :     uchar *       wire     = msg_buf;
     543        6894 :     uchar * const wire_end = msg_buf + MSG_BUFSZ;
     544             : 
     545             :     /* Leave space for message header */
     546             : 
     547        6894 :     void * hdr_ptr = wire;
     548        6894 :     wire += sizeof(fd_tls_msg_hdr_t);
     549        6894 :     fd_tls_msg_hdr_t hdr = { .type = FD_TLS_MSG_SERVER_HELLO };
     550             : 
     551             :     /* Construct server hello */
     552             : 
     553        6894 :     fd_tls_server_hello_t sh = {
     554        6894 :       .cipher_suite = FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256,
     555        6894 :       .key_share    = { .has_x25519 = 1 },
     556        6894 :       .session_id   = ch.session_id,
     557        6894 :     };
     558        6894 :     memcpy( sh.random,           server_random,            32UL );
     559        6894 :     memcpy( sh.key_share.x25519, server->kex_public_key, 32UL );
     560             : 
     561             :     /* Encode server hello */
     562             : 
     563        6894 :     long encode_res = fd_tls_encode_server_hello( &sh, wire, (ulong)(wire_end-wire) );
     564        6894 :     if( FD_UNLIKELY( encode_res<0L ) )
     565           0 :       return fd_tls_alert( &handshake->base, (uint)(-encode_res), FD_TLS_REASON_SH_ENCODE );
     566        6894 :     wire += (ulong)encode_res;
     567             : 
     568        6894 :     hdr.sz = fd_uint_to_tls_u24( (uint)encode_res );
     569        6894 :     fd_tls_encode_msg_hdr( &hdr, hdr_ptr, sizeof(fd_tls_msg_hdr_t) );
     570        6894 :     server_hello_sz = (ulong)(wire - msg_buf);
     571        6894 :   } while(0);
     572             : 
     573             :   /* Call back with server hello */
     574             : 
     575        6894 :   if( FD_UNLIKELY( !server->sendmsg_fn(
     576        6894 :         handshake,
     577        6894 :         msg_buf, server_hello_sz,
     578        6894 :         FD_TLS_LEVEL_INITIAL,
     579        6894 :         /* flush */ 1 ) ) )
     580           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_SENDMSG_FAIL );
     581             : 
     582             :   /* Record server hello in transcript hash */
     583             : 
     584        6894 :   fd_sha256_append( &transcript, msg_buf, server_hello_sz );
     585             : 
     586             :   /* Derive handshake secrets *****************************************/
     587             : 
     588             :   /* Export handshake transcript hash */
     589             : 
     590        6894 :   fd_sha256_t transcript_clone = transcript;
     591        6894 :   uchar transcript_hash[ 32 ];
     592        6894 :   fd_sha256_fini( &transcript_clone, transcript_hash );
     593             : 
     594             :   /* Derive ECDH input key material */
     595             : 
     596        6894 :   uchar _ecdh_ikm[ 32 ];
     597        6894 :   void * ecdh_ikm = fd_x25519_exchange( _ecdh_ikm,
     598        6894 :                                         server->kex_private_key,
     599        6894 :                                         ch.key_share.x25519 );
     600        6894 :   if( FD_UNLIKELY( !ecdh_ikm ) )
     601           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_HANDSHAKE_FAILURE, FD_TLS_REASON_X25519_FAIL );
     602             : 
     603             :   /* RFC 8446 Section 7.1: Handshake Secret = HKDF-Extract(
     604             :        salt = Derive-Secret(Early Secret, "derived", ""),
     605             :        IKM  = (EC)DHE shared secret)
     606             :      RFC 5869 Section 2.2: PRK = HMAC-Hash(key=salt, data=IKM) */
     607             : 
     608        6894 :   uchar handshake_secret[ 32 ];
     609        6894 :   fd_hmac_sha256( /* IKM  */ ecdh_ikm,          32UL,
     610        6894 :                   /* salt */ handshake_derived, 32UL,
     611        6894 :                   /* out  */ handshake_secret );
     612             : 
     613             :   /* RFC 8446 Section 7.1: client_handshake_traffic_secret =
     614             :        Derive-Secret(Handshake Secret, "c hs traffic",
     615             :                      ClientHello...ServerHello) */
     616             : 
     617        6894 :   uchar client_hs_secret[ 32UL ];
     618        6894 :   fd_tls_hkdf_expand_label( client_hs_secret, 32UL,
     619        6894 :                             handshake_secret,
     620        6894 :                             "c hs traffic",  12UL,
     621        6894 :                             transcript_hash, 32UL );
     622        6894 :   memcpy( handshake->client_hs_secret, client_hs_secret, 32UL );
     623             : 
     624             :   /* RFC 8446 Section 7.1: server_handshake_traffic_secret =
     625             :        Derive-Secret(Handshake Secret, "s hs traffic",
     626             :                      ClientHello...ServerHello) */
     627             : 
     628        6894 :   uchar server_hs_secret[ 32UL ];
     629        6894 :   fd_tls_hkdf_expand_label( server_hs_secret, 32UL,
     630        6894 :                             handshake_secret,
     631        6894 :                             "s hs traffic",  12UL,
     632        6894 :                             transcript_hash, 32UL );
     633             : 
     634             :   /* Call back with handshake secrets */
     635             : 
     636        6894 :   server->secrets_fn( handshake,
     637        6894 :                       /* read secret  */ client_hs_secret,
     638        6894 :                       /* write secret */ server_hs_secret,
     639        6894 :                       FD_TLS_LEVEL_HANDSHAKE );
     640             : 
     641             :   /* Derive master secret */
     642             : 
     643        6894 :   uchar master_derive[ 32 ];
     644        6894 :   fd_tls_hkdf_expand_label( master_derive, 32UL,
     645        6894 :                             handshake_secret,
     646        6894 :                             "derived",   7UL,
     647        6894 :                             empty_hash, 32UL );
     648             : 
     649             :   /* RFC 8446 Section 7.1: Master Secret = HKDF-Extract(
     650             :        salt = Derive-Secret(Handshake Secret, "derived", ""),
     651             :        IKM  = 0)
     652             :      RFC 5869 Section 2.2: PRK = HMAC-Hash(key=salt, data=IKM) */
     653             : 
     654        6894 :   static uchar const zeros[ 32 ] = {0};
     655        6894 :   uchar master_secret[ 32 ];
     656        6894 :   fd_hmac_sha256( /* IKM  */ zeros,         32UL,
     657        6894 :                   /* salt */ master_derive, 32UL,
     658        6894 :                   /* out  */ master_secret );
     659             : 
     660             :   /* Send EncryptedExtensions (EE) message ****************************/
     661             : 
     662        6894 :   ulong server_ee_sz;
     663             : 
     664        6894 :   do {
     665        6894 :     uchar *       wire     = msg_buf;
     666        6894 :     uchar * const wire_end = msg_buf + MSG_BUFSZ;
     667             : 
     668             :     /* Leave space for message header */
     669             : 
     670        6894 :     void * hdr_ptr = wire;
     671        6894 :     wire += sizeof(fd_tls_msg_hdr_t);
     672        6894 :     fd_tls_msg_hdr_t hdr = { .type = FD_TLS_MSG_ENCRYPTED_EXT };
     673             : 
     674        6894 :     ushort * ext_sz_ptr = (ushort *)wire;
     675        6894 :     wire += sizeof(ushort);
     676             : 
     677             :     /* Construct encrypted extensions */
     678             : 
     679        6894 :     fd_tls_enc_ext_t ee = {
     680        6894 :       .quic_tp = {
     681        6894 :         .buf   = (quic_tp_sz>=0L) ? quic_tp : NULL,
     682        6894 :         .bufsz = (ushort)quic_tp_sz,
     683        6894 :       },
     684        6894 :       .alpn = {
     685        6894 :         .buf   = server->alpn,
     686        6894 :         .bufsz = server->alpn_sz,
     687        6894 :       }
     688        6894 :     };
     689             : 
     690             :     /* Encode encrypted extensions */
     691             : 
     692        6894 :     long encode_res = fd_tls_encode_enc_ext( &ee, wire, (ulong)(wire_end-wire) );
     693        6894 :     if( FD_UNLIKELY( encode_res<0L ) )
     694           0 :       return fd_tls_alert( &handshake->base, (uint)(-encode_res), FD_TLS_REASON_EE_ENCODE );
     695        6894 :     wire += (ulong)encode_res;
     696             : 
     697        6894 :     *ext_sz_ptr = (ushort)fd_ushort_bswap( (ushort)encode_res );
     698        6894 :     hdr.sz = fd_uint_to_tls_u24( (uint)encode_res + (uint)sizeof(ushort) );
     699        6894 :     fd_tls_encode_msg_hdr( &hdr, hdr_ptr, 4UL );
     700        6894 :     server_ee_sz = (ulong)(wire - msg_buf);
     701        6894 :   } while(0);
     702             : 
     703             :   /* Call back with EE */
     704             : 
     705        6894 :   if( FD_UNLIKELY( !server->sendmsg_fn(
     706        6894 :         handshake,
     707        6894 :         msg_buf, server_ee_sz,
     708        6894 :         FD_TLS_LEVEL_HANDSHAKE,
     709        6894 :         /* flush */ 0 ) ) )
     710           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_SENDMSG_FAIL );
     711             : 
     712             :   /* Record EE in transcript hash */
     713             : 
     714        6894 :   fd_sha256_append( &transcript, msg_buf, server_ee_sz );
     715             : 
     716             :   /* Send CertificateRequest ******************************************/
     717             : 
     718        6894 :   static uchar const cert_req[] = {
     719        6894 :     FD_TLS_MSG_CERT_REQ,     /* msg_type */
     720        6894 :     0x00, 0x00, 0x0b,        /* msg_sz */
     721        6894 :     0x00,                    /* certificate_request_context */
     722        6894 :     0x00, 0x08,              /* extensions length prefix */
     723        6894 :     0x00, FD_TLS_EXT_SIGNATURE_ALGORITHMS,
     724             :                              /* ext type */
     725        6894 :     0x00, 0x04,              /* ext sz */
     726        6894 :     0x00, 0x02,              /* sigalg sz */
     727        6894 :     0x08, 0x07,              /* Ed25519 */
     728        6894 :   };
     729             : 
     730        6894 :   if( FD_UNLIKELY( !server->sendmsg_fn(
     731        6894 :         handshake,
     732        6894 :         cert_req, sizeof(cert_req),
     733        6894 :         FD_TLS_LEVEL_HANDSHAKE,
     734        6894 :         /* flush */ 0 ) ) )
     735           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_SENDMSG_FAIL );
     736             : 
     737             :   /* Record CertificateRequest in transcript hash */
     738             : 
     739        6894 :   fd_sha256_append( &transcript, cert_req, sizeof(cert_req) );
     740             : 
     741             :   /* Send Certificate *************************************************/
     742             : 
     743        6894 :   long cert_msg_sz = fd_tls_encode_cert_x509( server->cert_x509, server->cert_x509_sz, msg_buf, MSG_BUFSZ );
     744        6894 :   if( FD_UNLIKELY( cert_msg_sz<0L ) )
     745           0 :     return fd_tls_alert( &handshake->base, (uint)(-cert_msg_sz), FD_TLS_REASON_CERT_ENCODE );
     746             : 
     747             :   /* Send certificate message */
     748             : 
     749        6894 :   if( FD_UNLIKELY( !server->sendmsg_fn(
     750        6894 :         handshake,
     751        6894 :         msg_buf, (ulong)cert_msg_sz,
     752        6894 :         FD_TLS_LEVEL_HANDSHAKE,
     753        6894 :         /* flush */ 0 ) ) )
     754           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_SENDMSG_FAIL );
     755             : 
     756             :   /* Record Certificate message in transcript hash */
     757             : 
     758        6894 :   fd_sha256_append( &transcript, msg_buf, (ulong)cert_msg_sz );
     759             : 
     760             :   /* Send CertificateVerify *******************************************/
     761             : 
     762        6894 :   long cvfy_res = fd_tls_send_cert_verify( server, &handshake->base, &transcript, 0 );
     763        6894 :   if( FD_UNLIKELY( !!cvfy_res ) ) return cvfy_res;
     764             :   /* CertificateVerify already included in transcript hash */
     765             : 
     766             :   /* Send Finished ****************************************************/
     767             : 
     768             :   /* Create static size message layout */
     769             : 
     770        6894 :   struct __attribute__((packed)) {
     771        6894 :     fd_tls_msg_hdr_t hdr;
     772        6894 :     fd_tls_finished_t   fin;
     773        6894 :   } fin_rec;
     774        6894 :   fin_rec.hdr = (fd_tls_msg_hdr_t){
     775        6894 :     .type = FD_TLS_MSG_FINISHED,
     776        6894 :     .sz   = fd_uint_to_tls_u24( 0x20 )
     777        6894 :   };
     778        6894 :   fd_tls_msg_hdr_bswap( &fin_rec.hdr );
     779        6894 :   fd_tls_finished_bswap( &fin_rec.fin );
     780             : 
     781             :   /* Export transcript hash ClientHello..CertificateVerify */
     782             : 
     783        6894 :   transcript_clone = transcript;
     784        6894 :   fd_sha256_fini( &transcript_clone, transcript_hash );
     785             : 
     786             :   /* RFC 8446 Section 4.4.4:
     787             :        finished_key = HKDF-Expand-Label(BaseKey, "finished", "", Hash.length)
     788             :        verify_data  = HMAC(finished_key, Transcript-Hash(
     789             :                        Handshake Context, Certificate*, CertificateVerify*)) */
     790             : 
     791        6894 :   uchar finished_key[ 32 ];
     792        6894 :   fd_tls_hkdf_expand_label( finished_key, 32UL,
     793        6894 :                             server_hs_secret,
     794        6894 :                             "finished", 8UL,
     795        6894 :                             NULL,       0UL );
     796             : 
     797        6894 :   fd_hmac_sha256( /* data */ transcript_hash, 32UL,
     798        6894 :                   /* salt */ finished_key,    32UL,
     799        6894 :                   /* out  */ fin_rec.fin.verify );
     800             : 
     801             :   /* Send Finished message */
     802             : 
     803        6894 :   if( FD_UNLIKELY( !server->sendmsg_fn(
     804        6894 :         handshake,
     805        6894 :         &fin_rec, sizeof(fin_rec),
     806        6894 :         FD_TLS_LEVEL_HANDSHAKE,
     807        6894 :         /* flush */ 1 ) ) )
     808           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_SENDMSG_FAIL );
     809             : 
     810             :   /* Record Finished in transcript hash */
     811             : 
     812        6894 :   fd_sha256_append( &transcript, &fin_rec, sizeof(fin_rec) );
     813             : 
     814             :   /* Derive application secrets ***************************************/
     815             : 
     816             :   /* Export transcript hash ClientHello..ServerFinished */
     817             : 
     818        6894 :   transcript_clone = transcript;
     819        6894 :   fd_sha256_fini( &transcript_clone, transcript_hash );
     820             : 
     821             :   /* RFC 8446 Section 7.1:
     822             :        client_application_traffic_secret_0 =
     823             :          Derive-Secret(Master Secret, "c ap traffic",
     824             :                        ClientHello...server Finished) */
     825             : 
     826        6894 :   uchar client_app_secret[ 32UL ];
     827        6894 :   fd_tls_hkdf_expand_label( client_app_secret, 32UL,
     828        6894 :                             master_secret,
     829        6894 :                             "c ap traffic",  12UL,
     830        6894 :                             transcript_hash, 32UL );
     831             : 
     832             :   /* RFC 8446 Section 7.1:
     833             :        server_application_traffic_secret_0 =
     834             :          Derive-Secret(Master Secret, "s ap traffic",
     835             :                        ClientHello...server Finished) */
     836             : 
     837        6894 :   uchar server_app_secret[ 32UL ];
     838        6894 :   fd_tls_hkdf_expand_label( server_app_secret, 32UL,
     839        6894 :                             master_secret,
     840        6894 :                             "s ap traffic",  12UL,
     841        6894 :                             transcript_hash, 32UL );
     842             : 
     843             :   /* Call back with application secrets */
     844             : 
     845        6894 :   server->secrets_fn( handshake,
     846        6894 :                       /* read secret  */ client_app_secret,
     847        6894 :                       /* write secret */ server_app_secret,
     848        6894 :                       FD_TLS_LEVEL_APPLICATION );
     849             : 
     850             :   /* Finish up ********************************************************/
     851             : 
     852             :   /* Store transcript hash state */
     853             : 
     854        6894 :   fd_tls_transcript_store( &handshake->transcript, &transcript );
     855             : 
     856             :   /* Done */
     857             : 
     858        6894 :   handshake->base.state = FD_TLS_HS_WAIT_CERT;
     859             : 
     860        6894 : # undef MSG_BUFSZ
     861        6894 :   return (long)read_sz;
     862        6894 : }
     863             : 
     864             : /* fd_tls_handle_cert_chain validates a Certificate message body and
     865             :    extracts the leaf public key into *extract.  Returns the number of
     866             :    bytes consumed, or a negated alert. */
     867             : 
     868             : static long
     869             : fd_tls_handle_cert_chain( fd_tls_estate_base_t *             const base,
     870             :                           uchar const *                      const cert_chain,
     871             :                           ulong                              const cert_chain_sz,
     872       16245 :                           fd_tls_extract_cert_pubkey_res_t * const extract ) {
     873             : 
     874       16245 :   *extract = fd_tls_extract_cert_pubkey( cert_chain, cert_chain_sz );
     875             : 
     876       16245 :   if( FD_UNLIKELY( !extract->pubkey ) ) {
     877          30 :     uint   alert  = extract->alert  ? extract->alert  : FD_TLS_ALERT_DECODE_ERROR;
     878          30 :     ushort reason = extract->reason ? extract->reason : FD_TLS_REASON_CERT_PARSE;
     879          30 :     return fd_tls_alert( base, alert, reason );
     880          30 :   }
     881             : 
     882             :   /* Skip extensions */
     883             :   /* Skip remaining certificate chain */
     884             : 
     885       16215 :   return (long)cert_chain_sz;
     886       16245 : }
     887             : 
     888             : static long
     889             : fd_tls_handle_cert_verify( fd_tls_estate_base_t *    hs,
     890             :                            fd_sha256_t const *       transcript,
     891             :                            uchar const *             record,
     892             :                            ulong                     record_sz,
     893             :                            uchar const *             pubkey,
     894             :                            ulong                     pubkey_len,
     895             :                            uchar                     key_type,
     896       16197 :                            int                       is_client ) {
     897             : 
     898             :   /* Read CertificateVerify *******************************************/
     899             : 
     900       16197 :   fd_tls_cert_verify_t vfy[1] = {{0}};
     901             : 
     902       16197 :   ulong read_sz;
     903       16197 :   do {
     904       16197 :     uchar const *       wire     = record;
     905       16197 :     uchar const * const wire_end = record + record_sz;
     906             : 
     907             :     /* Decode message header */
     908             : 
     909       16197 :     fd_tls_msg_hdr_t msg_hdr = {0};
     910       16197 :     long decode_res = fd_tls_decode_msg_hdr( &msg_hdr, wire, (ulong)(wire_end-wire) );
     911       16197 :     if( FD_UNLIKELY( decode_res<0L ) )
     912           0 :       return fd_tls_alert( hs, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_CV_PARSE );
     913       16197 :     wire += (ulong)decode_res;
     914             : 
     915       16197 :     if( FD_UNLIKELY( msg_hdr.type != FD_TLS_MSG_CERT_VERIFY ) )
     916           0 :       return fd_tls_alert( hs, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_CV_EXPECTED );
     917             : 
     918       16197 :     ulong msg_sz = fd_tls_u24_to_uint( msg_hdr.sz );
     919       16197 :     if( FD_UNLIKELY( msg_sz > (ulong)(wire_end-wire) ) )
     920           0 :       return fd_tls_alert( hs, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_CV_PARSE );
     921             : 
     922             :     /* Decode CertificateVerify */
     923             : 
     924       16197 :     decode_res = fd_tls_decode_cert_verify( vfy, wire, msg_sz );
     925       16197 :     if( FD_UNLIKELY( decode_res<0L ) )
     926           9 :       return fd_tls_alert( hs, (uint)(-decode_res), FD_TLS_REASON_CV_PARSE );
     927       16188 :     if( FD_UNLIKELY( (ulong)decode_res != msg_sz ) )
     928           0 :       return fd_tls_alert( hs, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_CV_PARSE );
     929       16188 :     wire += (ulong)decode_res;
     930             : 
     931       16188 :     read_sz = (ulong)(wire - record);
     932       16188 :   } while(0);
     933             : 
     934             :   /* Verify signature *************************************************/
     935             : 
     936             :   /* Export transcript hash ClientHello..server Certificate
     937             :      And recover message that was signed */
     938             : 
     939       16188 :   uchar sign_msg[ FD_TLS_CV_SIGN_SZ ];
     940       16188 :   fd_memcpy( sign_msg,
     941       16188 :              is_client ? fd_tls13_cli_sign_prefix : fd_tls13_srv_sign_prefix,
     942       16188 :              98UL );
     943             : 
     944       16188 :   fd_sha256_t cv_transcript_clone = *transcript;
     945       16188 :   fd_sha256_fini( &cv_transcript_clone, sign_msg+98 );
     946             : 
     947             :   /* Verify certificate signature
     948             :      > If the verification fails, the receiver MUST terminate the handshake
     949             :      > with a "decrypt_error" alert. */
     950             : 
     951       16188 :   switch( vfy->algorithm ) {
     952             : 
     953       14379 :   case FD_TLS_SIGNATURE_ED25519: {
     954       14379 :     if( FD_UNLIKELY( key_type != FD_TLS_KEY_ED25519 ) )
     955           0 :       return fd_tls_alert( hs, FD_TLS_ALERT_HANDSHAKE_FAILURE, FD_TLS_REASON_CV_SIGALG );
     956       14379 :     fd_sha512_t sha512[1];
     957       14379 :     int sig_err = fd_ed25519_verify( sign_msg, FD_TLS_CV_SIGN_SZ, vfy->signature, pubkey, sha512 );
     958       14379 :     if( FD_UNLIKELY( sig_err != FD_ED25519_SUCCESS ) )
     959           0 :       return fd_tls_alert( hs, FD_TLS_ALERT_DECRYPT_ERROR, FD_TLS_REASON_ED25519_FAIL );
     960       14379 :     break;
     961       14379 :   }
     962             : 
     963       14379 :   case FD_TLS_SIGNATURE_ECDSA_SECP256R1_SHA256: {
     964        1296 :     if( FD_UNLIKELY( key_type != FD_TLS_KEY_ECDSA_P256 ) )
     965           0 :       return fd_tls_alert( hs, FD_TLS_ALERT_HANDSHAKE_FAILURE, FD_TLS_REASON_CV_SIGALG );
     966             : 
     967        1296 :     uchar raw_sig[64];
     968        1296 :     if( FD_UNLIKELY( fd_x509_decode_ecdsa_sig( vfy->signature, vfy->signature_len, raw_sig, 32 ) ) )
     969           0 :       return fd_tls_alert( hs, FD_TLS_ALERT_DECRYPT_ERROR, FD_TLS_REASON_CV_PARSE );
     970             : 
     971        1296 :     uchar compressed_pk[33];
     972        1296 :     if( FD_UNLIKELY( pubkey_len != 65 ) )
     973           0 :       return fd_tls_alert( hs, FD_TLS_ALERT_DECRYPT_ERROR, FD_TLS_REASON_CV_PARSE );
     974        1296 :     if( FD_UNLIKELY( fd_x509_ec_point_compress( pubkey, 32, compressed_pk ) ) )
     975           0 :       return fd_tls_alert( hs, FD_TLS_ALERT_DECRYPT_ERROR, FD_TLS_REASON_CV_PARSE );
     976             : 
     977        1296 :     fd_sha256_t sha256[1];
     978        1296 :     int sig_err = fd_secp256r1_verify_allow_high_s( sign_msg, 130UL, raw_sig, compressed_pk, sha256 );
     979        1296 :     if( FD_UNLIKELY( sig_err != FD_SECP256R1_SUCCESS ) )
     980           0 :       return fd_tls_alert( hs, FD_TLS_ALERT_DECRYPT_ERROR, FD_TLS_REASON_SECP256R1_FAIL );
     981        1296 :     break;
     982        1296 :   }
     983             : 
     984        1296 : #if FD_HAS_INT128
     985        1296 :   case FD_TLS_SIGNATURE_RSA_PSS_RSAE_SHA256:
     986         492 :   case FD_TLS_SIGNATURE_RSA_PSS_RSAE_SHA384:
     987         513 :   case FD_TLS_SIGNATURE_RSA_PSS_RSAE_SHA512: {
     988         513 :     if( FD_UNLIKELY( key_type != FD_TLS_KEY_RSA ) )
     989          18 :       return fd_tls_alert( hs, FD_TLS_ALERT_HANDSHAKE_FAILURE, FD_TLS_REASON_CV_SIGALG );
     990             : 
     991         495 :     fd_rsa_pubkey_t key[1];
     992         495 :     if( FD_UNLIKELY( fd_x509_decode_rsa_pubkey( pubkey, pubkey_len, key ) ) )
     993           0 :       return fd_tls_alert( hs, FD_TLS_ALERT_DECRYPT_ERROR, FD_TLS_REASON_CV_PARSE );
     994             : 
     995         495 :     int hash = vfy->algorithm==FD_TLS_SIGNATURE_RSA_PSS_RSAE_SHA256 ? FD_RSA_HASH_SHA256 :
     996         495 :                vfy->algorithm==FD_TLS_SIGNATURE_RSA_PSS_RSAE_SHA384 ? FD_RSA_HASH_SHA384 :
     997          36 :                                                                       FD_RSA_HASH_SHA512;
     998         495 :     int sig_err = fd_rsa_verify_pss( key, vfy->signature, vfy->signature_len, sign_msg, 130UL, hash );
     999         495 :     if( FD_UNLIKELY( sig_err != FD_RSA_SUCCESS ) )
    1000          45 :       return fd_tls_alert( hs, FD_TLS_ALERT_DECRYPT_ERROR, FD_TLS_REASON_RSA_FAIL );
    1001         450 :     break;
    1002         495 :   }
    1003         450 : #endif
    1004             : 
    1005         450 :   default:
    1006           0 :     return fd_tls_alert( hs, FD_TLS_ALERT_HANDSHAKE_FAILURE, FD_TLS_REASON_CV_SIGALG );
    1007       16188 :   }
    1008             : 
    1009       16125 :   return (long)read_sz;
    1010       16188 : }
    1011             : 
    1012             : static long
    1013             : fd_tls_server_hs_wait_cert( fd_tls_t const *      server,
    1014             :                             fd_tls_estate_srv_t * handshake,
    1015             :                             uchar const *   const record,
    1016             :                             ulong           const record_sz,
    1017        6837 :                             uint                  encryption_level ) {
    1018             : 
    1019        6837 :   (void)server;
    1020             : 
    1021        6837 :   if( FD_UNLIKELY( encryption_level != FD_TLS_LEVEL_HANDSHAKE ) )
    1022           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_WRONG_ENC_LVL );
    1023             : 
    1024             :   /* Read Certificate *************************************************/
    1025             : 
    1026        6837 :   ulong read_sz;
    1027        6837 :   do {
    1028        6837 :     uchar const *       wire     = record;
    1029        6837 :     uchar const * const wire_end = record + record_sz;
    1030             : 
    1031             :     /* Decode message header */
    1032             : 
    1033        6837 :     fd_tls_msg_hdr_t msg_hdr = {0};
    1034        6837 :     long decode_res = fd_tls_decode_msg_hdr( &msg_hdr, wire, (ulong)(wire_end-wire) );
    1035        6837 :     if( FD_UNLIKELY( decode_res<0L ) )
    1036           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_CERT_PARSE );
    1037        6837 :     wire += (ulong)decode_res;
    1038             : 
    1039        6837 :     if( FD_UNLIKELY( msg_hdr.type != FD_TLS_MSG_CERT ) )
    1040           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_CERT_EXPECTED );
    1041             : 
    1042        6837 :     ulong msg_sz = fd_tls_u24_to_uint( msg_hdr.sz );
    1043        6837 :     if( FD_UNLIKELY( msg_sz > (ulong)(wire_end-wire) ) )
    1044           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_CERT_CR_PARSE );
    1045             : 
    1046             :     /* Decode Certificate */
    1047             : 
    1048        6837 :     fd_tls_extract_cert_pubkey_res_t extract;
    1049        6837 :     decode_res = fd_tls_handle_cert_chain( &handshake->base, wire, msg_sz, &extract );
    1050        6837 :     if( FD_UNLIKELY( decode_res<0L ) )
    1051          24 :       return fd_tls_alert( &handshake->base, (uint)(-decode_res), FD_TLS_REASON_CERT_PARSE );
    1052        6813 :     if( FD_UNLIKELY( extract.key_type!=FD_TLS_KEY_ED25519 || extract.pubkey_len!=32UL ) )
    1053           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_UNSUPPORTED_CERTIFICATE, FD_TLS_REASON_CERT_KEY_TYPE );
    1054        6813 :     fd_memcpy( handshake->client_pubkey, extract.pubkey, 32UL );
    1055        6813 :     wire += (ulong)decode_res;
    1056             : 
    1057        6813 :     read_sz = (ulong)(wire - record);
    1058        6813 :   } while(0);
    1059             : 
    1060        6813 :   fd_sha256_t transcript;
    1061        6813 :   fd_tls_transcript_load( &handshake->transcript, &transcript );
    1062        6813 :   fd_sha256_append( &transcript, record, read_sz );
    1063        6813 :   fd_tls_transcript_store( &handshake->transcript, &transcript );
    1064             : 
    1065             :   /* Finish up ********************************************************/
    1066             : 
    1067        6813 :   handshake->base.state = FD_TLS_HS_WAIT_CV;
    1068        6813 :   return (long)read_sz;
    1069        6837 : }
    1070             : 
    1071             : static long
    1072             : fd_tls_server_hs_wait_cert_verify( fd_tls_t const *      server,
    1073             :                                    fd_tls_estate_srv_t * hs,
    1074             :                                    uchar const *   const record,
    1075             :                                    ulong           const record_sz,
    1076        6813 :                                    uint                  encryption_level ) {
    1077             : 
    1078        6813 :   (void)server;
    1079             : 
    1080        6813 :   if( FD_UNLIKELY( encryption_level != FD_TLS_LEVEL_HANDSHAKE ) )
    1081           0 :     return fd_tls_alert( &hs->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_WRONG_ENC_LVL );
    1082             : 
    1083        6813 :   fd_sha256_t transcript;
    1084        6813 :   fd_tls_transcript_load( &hs->transcript, &transcript );
    1085        6813 :   fd_sha256_t transcript_clone = transcript;
    1086             : 
    1087             :   /* Decode incoming client CertificateVerify *************************/
    1088             : 
    1089        6813 :   long res = fd_tls_handle_cert_verify( &hs->base, &transcript_clone,
    1090        6813 :                                         record, record_sz,
    1091        6813 :                                         hs->client_pubkey, 32UL, FD_TLS_KEY_ED25519, 1 );
    1092        6813 :   if( FD_UNLIKELY( res<0L ) ) return res;
    1093             : 
    1094        6813 :   fd_sha256_append( &transcript, record, (ulong)res );
    1095        6813 :   fd_tls_transcript_store( &hs->transcript, &transcript );
    1096             : 
    1097             :   /* Finish up ********************************************************/
    1098             : 
    1099        6813 :   hs->base.state = FD_TLS_HS_WAIT_FINISHED;
    1100        6813 :   return res;
    1101        6813 : }
    1102             : 
    1103             : static long
    1104             : fd_tls_server_hs_wait_finished( fd_tls_t const *      server,
    1105             :                                 fd_tls_estate_srv_t * handshake,
    1106             :                                 uchar const *   const record,
    1107             :                                 ulong                 record_sz,
    1108        6813 :                                 uint                  encryption_level )  {
    1109             : 
    1110        6813 :   (void)server;
    1111             : 
    1112        6813 :   if( FD_UNLIKELY( encryption_level != FD_TLS_LEVEL_HANDSHAKE ) )
    1113           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_WRONG_ENC_LVL );
    1114             : 
    1115             :   /* Restore state ****************************************************/
    1116             : 
    1117        6813 :   fd_sha256_t transcript;
    1118        6813 :   fd_tls_transcript_load( &handshake->transcript, &transcript );
    1119             : 
    1120             :   /* Decode incoming client "Finished" message ************************/
    1121             : 
    1122        6813 :   fd_tls_finished_t finished = {0};
    1123             : 
    1124        6813 :   ulong read_sz;
    1125        6813 :   do {
    1126        6813 :     uchar const *       wire     = record;
    1127        6813 :     uchar const * const wire_end = record + record_sz;
    1128             : 
    1129             :     /* Decode message header */
    1130             : 
    1131        6813 :     fd_tls_msg_hdr_t msg_hdr = {0};
    1132        6813 :     long decode_res = fd_tls_decode_msg_hdr( &msg_hdr, wire, (ulong)(wire_end-wire) );
    1133        6813 :     if( FD_UNLIKELY( decode_res<0L ) )
    1134           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_FINI_PARSE );
    1135        6813 :     wire += (ulong)decode_res;
    1136             : 
    1137        6813 :     if( FD_UNLIKELY( msg_hdr.type != FD_TLS_MSG_FINISHED ) )
    1138           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_FINI_EXPECTED );
    1139             : 
    1140        6813 :     if( FD_UNLIKELY( fd_tls_u24_to_uint( msg_hdr.sz ) != sizeof(fd_tls_finished_t) ) )
    1141           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_FINI_PARSE );
    1142             : 
    1143             :     /* Decode server Finished */
    1144             : 
    1145        6813 :     decode_res = fd_tls_decode_finished( &finished, wire, (ulong)(wire_end-wire) );
    1146        6813 :     if( FD_UNLIKELY( decode_res<0L ) )
    1147           0 :       return fd_tls_alert( &handshake->base, (uint)(-decode_res), FD_TLS_REASON_FINI_PARSE );
    1148        6813 :     wire += (ulong)decode_res;
    1149             : 
    1150        6813 :     read_sz = (ulong)(wire - record);
    1151        6813 :   } while(0);
    1152             : 
    1153             :   /* Check "Finished" verify data *************************************/
    1154             : 
    1155             :   /* Export transcript hash */
    1156             : 
    1157        6813 :   uchar transcript_hash[ 32 ];
    1158        6813 :   fd_sha256_fini( &transcript, transcript_hash );
    1159             : 
    1160             :   /* RFC 8446 Section 4.4.4:
    1161             :        finished_key = HKDF-Expand-Label(BaseKey, "finished", "", Hash.length)
    1162             :        verify_data  = HMAC(finished_key, Transcript-Hash(
    1163             :                        Handshake Context, Certificate*, CertificateVerify*)) */
    1164             : 
    1165        6813 :   uchar finished_key[ 32 ];
    1166        6813 :   fd_tls_hkdf_expand_label( finished_key, 32UL,
    1167        6813 :                             handshake->client_hs_secret,
    1168        6813 :                             "finished", 8UL,
    1169        6813 :                             NULL,       0UL );
    1170             : 
    1171        6813 :   uchar finished_expected[ 32 ];
    1172        6813 :   fd_hmac_sha256( /* data */ transcript_hash, 32UL,
    1173        6813 :                   /* salt */ finished_key,    32UL,
    1174        6813 :                   /* out  */ finished_expected );
    1175             : 
    1176             :   /* Verify that client and server's transcripts match */
    1177             : 
    1178        6813 :   int match = 0;
    1179      224829 :   for( ulong i=0; i<32UL; i++ )
    1180      218016 :     match |= finished.verify[i] ^ finished_expected[i];
    1181        6813 :   if( FD_UNLIKELY( match!=0 ) )
    1182           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECRYPT_ERROR, FD_TLS_REASON_FINI_FAIL );
    1183             : 
    1184             :   /* Done */
    1185             : 
    1186        6813 :   handshake->base.state = FD_TLS_HS_CONNECTED;
    1187        6813 :   return (long)read_sz;
    1188        6813 : }
    1189             : 
    1190             : static long fd_tls_client_hs_start           ( fd_tls_t const *, fd_tls_estate_cli_t *                             );
    1191             : static long fd_tls_client_hs_wait_sh         ( fd_tls_t const *, fd_tls_estate_cli_t *, uchar const *, ulong, uint );
    1192             : static long fd_tls_client_hs_wait_ee         ( fd_tls_t const *, fd_tls_estate_cli_t *, uchar const *, ulong, uint );
    1193             : static long fd_tls_client_hs_wait_cert_cr    ( fd_tls_t const *, fd_tls_estate_cli_t *, uchar const *, ulong, uint );
    1194             : static long fd_tls_client_hs_wait_cert       ( fd_tls_t const *, fd_tls_estate_cli_t *, uchar const *, ulong, uint );
    1195             : static long fd_tls_client_hs_wait_cert_verify( fd_tls_t const *, fd_tls_estate_cli_t *, uchar const *, ulong, uint );
    1196             : static long fd_tls_client_hs_wait_finished   ( fd_tls_t const *, fd_tls_estate_cli_t *, uchar const *, ulong, uint );
    1197             : 
    1198             : long
    1199             : fd_tls_client_handshake( fd_tls_t const *      client,
    1200             :                          fd_tls_estate_cli_t * handshake,
    1201             :                          void const *          record,
    1202             :                          ulong                 record_sz,
    1203       64953 :                          uint                  encryption_level ) {
    1204       64953 :   switch( handshake->base.state ) {
    1205        9747 :   case FD_TLS_HS_START:
    1206             :     /* Record argument is ignored, since ClientHello is always the first message */
    1207        9747 :     (void)record; (void)record_sz; (void)encryption_level;
    1208        9747 :     return fd_tls_client_hs_start( client, handshake );
    1209        9402 :   case FD_TLS_HS_WAIT_SH:
    1210             :     /* Incoming ServerHello */
    1211        9402 :     return fd_tls_client_hs_wait_sh( client, handshake, record, record_sz, encryption_level );
    1212        9414 :   case FD_TLS_HS_WAIT_EE:
    1213             :     /* Incoming EncryptedExtensions */
    1214        9414 :     return fd_tls_client_hs_wait_ee( client, handshake, record, record_sz, encryption_level );
    1215        9420 :   case FD_TLS_HS_WAIT_CERT_CR:
    1216             :     /* Incoming CertificateRequest or Certificate */
    1217        9420 :     return fd_tls_client_hs_wait_cert_cr( client, handshake, record, record_sz, encryption_level );
    1218        8286 :   case FD_TLS_HS_WAIT_CERT:
    1219             :     /* Incoming Certificate */
    1220        8286 :     return fd_tls_client_hs_wait_cert( client, handshake, record, record_sz, encryption_level );
    1221        9384 :   case FD_TLS_HS_WAIT_CV:
    1222             :     /* Incoming CertificateVerify */
    1223        9384 :     return fd_tls_client_hs_wait_cert_verify( client, handshake, record, record_sz, encryption_level );
    1224        9300 :   case FD_TLS_HS_WAIT_FINISHED:
    1225             :     /* Incoming Server Finished */
    1226        9300 :     return fd_tls_client_hs_wait_finished( client, handshake, record, record_sz, encryption_level );
    1227           0 :   default:
    1228           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_HANDSHAKE_FAILURE, FD_TLS_REASON_ILLEGAL_STATE );
    1229       64953 :   }
    1230       64953 : }
    1231             : 
    1232             : static long
    1233             : fd_tls_client_hs_start( fd_tls_t const * const      client,
    1234        9747 :                         fd_tls_estate_cli_t * const handshake ) {
    1235             : 
    1236        9747 :   if( FD_UNLIKELY( client->server_name_len>=sizeof(client->server_name) ) )
    1237           3 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_CH_ENCODE );
    1238             : 
    1239             :   /* Request QUIC transport params */
    1240        9744 :   uchar quic_tp[ FD_TLS_EXT_QUIC_PARAMS_SZ_MAX ];
    1241        9744 :   long  quic_tp_sz = -1L;
    1242        9744 :   if( client->quic )
    1243        6117 :     quic_tp_sz = (long)client->quic_tp_self_fn( handshake, quic_tp, FD_TLS_EXT_QUIC_PARAMS_SZ_MAX );
    1244        9744 :   if( FD_UNLIKELY( quic_tp_sz > (long)FD_TLS_EXT_QUIC_PARAMS_SZ_MAX ) )
    1245           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_QUIC_TP_OVERSZ );
    1246             : 
    1247             :   /* Message buffer (sized for session_id + SNI) */
    1248        9744 : # define MSG_BUFSZ 1024UL
    1249        9744 :   uchar msg_buf[ MSG_BUFSZ ];
    1250             : 
    1251        9744 :   fd_sha256_init( &handshake->transcript );
    1252             : 
    1253             :   /* Send ClientHello *************************************************/
    1254             : 
    1255             :   /* Create client random */
    1256             : 
    1257        9744 :   uchar client_random[ 32 ];
    1258        9744 :   fd_chacha_rng_read32( client->rng, client_random );
    1259             : 
    1260             :   /* Remember client random for SSLKEYLOGFILE */
    1261        9744 :   fd_memcpy( handshake->base.client_random, client_random, 32UL );
    1262             : 
    1263             :   /* Generate session_id for TCP middlebox compat (RFC 8446 Section 4.1.2) */
    1264             : 
    1265        9744 :   uchar session_id_buf[ 32 ];
    1266        9744 :   uchar session_id_sz = 0;
    1267        9744 :   if( !client->quic ) {
    1268        3627 :     fd_chacha_rng_read32( client->rng, session_id_buf );
    1269        3627 :     session_id_sz = 32;
    1270        3627 :     fd_memcpy( handshake->session_id, session_id_buf, 32UL );
    1271        3627 :     handshake->session_id_sz = 32;
    1272        3627 :   }
    1273             : 
    1274             :   /* Create client hello message */
    1275             : 
    1276        9744 :   ulong client_hello_sz;
    1277             : 
    1278        9744 :   do {
    1279        9744 :     uchar *       wire     = msg_buf;
    1280        9744 :     uchar * const wire_end = msg_buf + MSG_BUFSZ;
    1281             : 
    1282             :     /* Leave space for message header */
    1283             : 
    1284        9744 :     void * hdr_ptr = wire;
    1285        9744 :     wire += sizeof(fd_tls_msg_hdr_t);
    1286        9744 :     fd_tls_msg_hdr_t hdr = { .type = FD_TLS_MSG_CLIENT_HELLO };
    1287             : 
    1288             :     /* Construct client hello */
    1289             : 
    1290        9744 :     fd_tls_client_hello_t ch = {
    1291        9744 :       .supported_versions   = { .tls13=1 },
    1292        9744 :       .supported_groups     = { .x25519=1 },
    1293             :       /* TPU/QUIC peers always present Ed25519 identities.  Only the
    1294             :          TCP-based TLS client talks to CAs issuing ECDSA certs. */
    1295        9744 :       .signature_algorithms =
    1296        9744 :         { .ed25519 = 1,
    1297        9744 :           .ecdsa_secp256r1_sha256 = !client->quic,
    1298        9744 :           .rsa_pss_rsae_sha256    = !client->quic && FD_HAS_INT128,
    1299        9744 :           .rsa_pss_rsae_sha384    = !client->quic && FD_HAS_INT128,
    1300        9744 :           .rsa_pss_rsae_sha512    = !client->quic && FD_HAS_INT128 },
    1301        9744 :       .signature_algorithms_cert =
    1302        9744 :         { .ed25519                = !client->quic,
    1303        9744 :           .ecdsa_secp256r1_sha256 = !client->quic,
    1304        9744 :           .ecdsa_secp384r1_sha384 = !client->quic,
    1305        9744 :           .rsa_pkcs1_sha256       = !client->quic && FD_HAS_INT128,
    1306        9744 :           .rsa_pkcs1_sha384       = !client->quic && FD_HAS_INT128,
    1307        9744 :           .rsa_pkcs1_sha512       = !client->quic && FD_HAS_INT128 },
    1308        9744 :       .cipher_suites        = { .aes_128_gcm_sha256=1 },
    1309        9744 :       .key_share            = { .has_x25519=1 },
    1310        9744 :       .session_id = {
    1311        9744 :         .buf   = session_id_sz ? session_id_buf : NULL,
    1312        9744 :         .bufsz = session_id_sz,
    1313        9744 :       },
    1314        9744 :       .quic_tp = {
    1315        9744 :         .buf   = (quic_tp_sz>=0L) ? quic_tp            : NULL,
    1316        9744 :         .bufsz = (quic_tp_sz>=0L) ? (ushort)quic_tp_sz : 0,
    1317        9744 :       },
    1318        9744 :       .alpn = {
    1319        9744 :         .buf   = client->alpn,
    1320        9744 :         .bufsz = client->alpn_sz,
    1321        9744 :       }
    1322        9744 :     };
    1323        9744 :     memcpy( ch.random,           client_random,          32UL );
    1324        9744 :     memcpy( ch.key_share.x25519, client->kex_public_key, 32UL );
    1325             : 
    1326             :     /* Copy SNI from config if set */
    1327        9744 :     if( client->server_name_len ) {
    1328        3192 :       ch.server_name.host_name_len = client->server_name_len;
    1329        3192 :       fd_memcpy( ch.server_name.host_name, client->server_name, client->server_name_len );
    1330        3192 :       ch.server_name.host_name[ client->server_name_len ] = '\0';
    1331        3192 :     }
    1332             : 
    1333             :     /* Encode client hello */
    1334             : 
    1335        9744 :     long encode_res = fd_tls_encode_client_hello( &ch, wire, (ulong)(wire_end-wire) );
    1336        9744 :     if( FD_UNLIKELY( encode_res<0L ) )
    1337           0 :       return fd_tls_alert( &handshake->base, (uint)(-encode_res), FD_TLS_REASON_CH_ENCODE );
    1338        9744 :     wire += (ulong)encode_res;
    1339             : 
    1340        9744 :     hdr.sz = fd_uint_to_tls_u24( (uint)encode_res );
    1341        9744 :     fd_tls_encode_msg_hdr( &hdr, hdr_ptr, sizeof(fd_tls_msg_hdr_t) );
    1342        9744 :     client_hello_sz = (ulong)(wire - msg_buf);
    1343        9744 :   } while(0);
    1344             : 
    1345             :   /* Call back with client hello */
    1346             : 
    1347        9744 :   if( FD_UNLIKELY( !client->sendmsg_fn(
    1348        9744 :         handshake,
    1349        9744 :         msg_buf, client_hello_sz,
    1350        9744 :         FD_TLS_LEVEL_INITIAL,
    1351        9744 :         /* flush */ 1 ) ) )
    1352           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_SENDMSG_FAIL );
    1353             : 
    1354             :   /* Record client hello in transcript hash */
    1355             : 
    1356        9744 :   fd_sha256_append( &handshake->transcript, msg_buf, client_hello_sz );
    1357             : 
    1358             :   /* Finish up ********************************************************/
    1359             : 
    1360        9744 :   handshake->base.state = FD_TLS_HS_WAIT_SH;
    1361             : 
    1362        9744 : # undef MSG_BUFSZ
    1363        9744 :   return 0L;
    1364        9744 : }
    1365             : 
    1366             : static long
    1367             : fd_tls_client_hs_wait_sh( fd_tls_t const *      const client,
    1368             :                           fd_tls_estate_cli_t * const handshake,
    1369             :                           uchar const *         const record,
    1370             :                           ulong                 const record_sz,
    1371        9402 :                           uint                  const encryption_level ) {
    1372             : 
    1373        9402 :   if( FD_UNLIKELY( encryption_level != FD_TLS_LEVEL_INITIAL ) )
    1374           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_WRONG_ENC_LVL );
    1375             : 
    1376             :   /* Read server hello ************************************************/
    1377             : 
    1378        9402 :   fd_tls_server_hello_t sh[1] = {0};
    1379             : 
    1380        9402 :   ulong read_sz;
    1381        9402 :   do {
    1382        9402 :     uchar const *       wire     = record;
    1383        9402 :     uchar const * const wire_end = record + record_sz;
    1384             : 
    1385             :     /* Decode message header */
    1386             : 
    1387        9402 :     fd_tls_msg_hdr_t msg_hdr = {0};
    1388        9402 :     long decode_res = fd_tls_decode_msg_hdr( &msg_hdr, wire, (ulong)(wire_end-wire) );
    1389        9402 :     if( FD_UNLIKELY( decode_res<0L ) )
    1390           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_SH_PARSE );
    1391        9402 :     wire += (ulong)decode_res;
    1392             : 
    1393        9402 :     if( FD_UNLIKELY( msg_hdr.type != FD_TLS_MSG_SERVER_HELLO ) )
    1394           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_SH_EXPECTED );
    1395             : 
    1396        9402 :     ulong msg_sz = fd_tls_u24_to_uint( msg_hdr.sz );
    1397        9402 :     if( FD_UNLIKELY( msg_sz > (ulong)(wire_end-wire) ) )
    1398           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_SH_PARSE );
    1399             : 
    1400             :     /* Decode Server Hello */
    1401             : 
    1402        9402 :     decode_res = fd_tls_decode_server_hello( sh, wire, msg_sz );
    1403        9402 :     if( FD_UNLIKELY( decode_res<0L ) )
    1404           0 :       return fd_tls_alert( &handshake->base, (uint)(-decode_res), FD_TLS_REASON_SH_PARSE );
    1405        9402 :     if( FD_UNLIKELY( (ulong)decode_res != msg_sz ) )
    1406           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_SH_PARSE );
    1407        9402 :     wire += (ulong)decode_res;
    1408             : 
    1409        9402 :     read_sz = (ulong)(wire - record);
    1410        9402 :   } while(0);
    1411             : 
    1412             :   /* legacy_session_id_echo must match the session_id sent in the
    1413             :      ClientHello (RFC 8446 Section 4.1.3).  Empty in QUIC mode. */
    1414             : 
    1415        9402 :   if( FD_UNLIKELY( ( sh->session_id.bufsz != handshake->session_id_sz ) ||
    1416        9402 :                    ( 0!=memcmp( sh->session_id.buf, handshake->session_id, handshake->session_id_sz ) ) ) )
    1417           6 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_ILLEGAL_PARAMETER, FD_TLS_REASON_SH_SESSION_ID );
    1418             : 
    1419             :   /* Server must select a cipher suite offered by ClientHello. */
    1420             : 
    1421        9396 :   if( FD_UNLIKELY( sh->cipher_suite != FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256 ) )
    1422           3 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_ILLEGAL_PARAMETER, FD_TLS_REASON_SH_NEG_CIPHER );
    1423             : 
    1424             :   /* Record server hello in transcript hash */
    1425             : 
    1426        9393 :   fd_sha256_append( &handshake->transcript, record, read_sz );
    1427             : 
    1428             :   /* Derive handshake secrets *****************************************/
    1429             : 
    1430             :   /* Export handshake transcript hash */
    1431             : 
    1432        9393 :   fd_sha256_t transcript_clone = handshake->transcript;
    1433        9393 :   uchar transcript_hash[ 32 ];
    1434        9393 :   fd_sha256_fini( &transcript_clone, transcript_hash );
    1435             : 
    1436             :   /* Derive ECDH input key material */
    1437             : 
    1438        9393 :   uchar _ecdh_ikm[ 32 ];
    1439        9393 :   void * ecdh_ikm = fd_x25519_exchange( _ecdh_ikm,
    1440        9393 :                                         client->kex_private_key,
    1441        9393 :                                         sh->key_share.x25519 );
    1442        9393 :   if( FD_UNLIKELY( !ecdh_ikm ) )
    1443           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_HANDSHAKE_FAILURE, FD_TLS_REASON_X25519_FAIL );
    1444             : 
    1445             :   /* RFC 8446 Section 7.1: Handshake Secret = HKDF-Extract(
    1446             :        salt = Derive-Secret(Early Secret, "derived", ""),
    1447             :        IKM  = (EC)DHE shared secret)
    1448             :      RFC 5869 Section 2.2: PRK = HMAC-Hash(key=salt, data=IKM) */
    1449             : 
    1450        9393 :   uchar handshake_secret[ 32 ];
    1451        9393 :   fd_hmac_sha256( /* IKM  */ ecdh_ikm,          32UL,
    1452        9393 :                   /* salt */ handshake_derived, 32UL,
    1453        9393 :                   /* out  */ handshake_secret );
    1454             : 
    1455             :   /* RFC 8446 Section 7.1: client_handshake_traffic_secret =
    1456             :        Derive-Secret(Handshake Secret, "c hs traffic",
    1457             :                      ClientHello...ServerHello) */
    1458             : 
    1459        9393 :   fd_tls_hkdf_expand_label( handshake->client_hs_secret, 32UL,
    1460        9393 :                             handshake_secret,
    1461        9393 :                             "c hs traffic",  12UL,
    1462        9393 :                             transcript_hash, 32UL );
    1463             : 
    1464             :   /* RFC 8446 Section 7.1: server_handshake_traffic_secret =
    1465             :        Derive-Secret(Handshake Secret, "s hs traffic",
    1466             :                      ClientHello...ServerHello) */
    1467             : 
    1468        9393 :   fd_tls_hkdf_expand_label( handshake->server_hs_secret, 32UL,
    1469        9393 :                             handshake_secret,
    1470        9393 :                             "s hs traffic",  12UL,
    1471        9393 :                             transcript_hash, 32UL );
    1472             : 
    1473             :   /* Call back with handshake secrets */
    1474             : 
    1475        9393 :   client->secrets_fn( handshake,
    1476        9393 :                       /* read secret  */ handshake->server_hs_secret,
    1477        9393 :                       /* write secret */ handshake->client_hs_secret,
    1478        9393 :                       FD_TLS_LEVEL_HANDSHAKE );
    1479             : 
    1480             :   /* Derive master secret */
    1481             : 
    1482        9393 :   uchar master_derive[ 32 ];
    1483        9393 :   fd_tls_hkdf_expand_label( master_derive, 32UL,
    1484        9393 :                             handshake_secret,
    1485        9393 :                             "derived",  7UL,
    1486        9393 :                             empty_hash, 32UL );
    1487             : 
    1488             :   /* RFC 8446 Section 7.1: Master Secret = HKDF-Extract(
    1489             :        salt = Derive-Secret(Handshake Secret, "derived", ""),
    1490             :        IKM  = 0)
    1491             :      RFC 5869 Section 2.2: PRK = HMAC-Hash(key=salt, data=IKM) */
    1492             : 
    1493        9393 :   static uchar const zeros[ 32 ] = {0};
    1494        9393 :   fd_hmac_sha256( /* IKM  */ zeros,         32UL,
    1495        9393 :                   /* salt */ master_derive, 32UL,
    1496        9393 :                   /* out  */ handshake->master_secret );
    1497             : 
    1498             :   /* Finish up ********************************************************/
    1499             : 
    1500        9393 :   handshake->base.state = FD_TLS_HS_WAIT_EE;
    1501             : 
    1502        9393 :   return (long)read_sz;
    1503        9393 : }
    1504             : 
    1505             : static long
    1506             : fd_tls_client_hs_wait_ee( fd_tls_t const *      const client,
    1507             :                           fd_tls_estate_cli_t * const handshake,
    1508             :                           uchar const *         const record,
    1509             :                           ulong                 const record_sz,
    1510        9414 :                           uint                  const encryption_level ) {
    1511             : 
    1512        9414 :   if( FD_UNLIKELY( encryption_level != FD_TLS_LEVEL_HANDSHAKE ) )
    1513           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_WRONG_ENC_LVL );
    1514             : 
    1515             :   /* Read EncryptedExtensions (EE) message ****************************/
    1516             : 
    1517        9414 :   fd_tls_enc_ext_t ee[1] = {0};
    1518             : 
    1519        9414 :   ulong read_sz;
    1520        9414 :   do {
    1521        9414 :     uchar const *       wire     = record;
    1522        9414 :     uchar const * const wire_end = record + record_sz;
    1523             : 
    1524             :     /* Decode message header */
    1525             : 
    1526        9414 :     fd_tls_msg_hdr_t msg_hdr = {0};
    1527        9414 :     long decode_res = fd_tls_decode_msg_hdr( &msg_hdr, wire, (ulong)(wire_end-wire) );
    1528        9414 :     if( FD_UNLIKELY( decode_res<0L ) )
    1529           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_EE_PARSE );
    1530        9414 :     wire += (ulong)decode_res;
    1531             : 
    1532        9414 :     if( FD_UNLIKELY( msg_hdr.type != FD_TLS_MSG_ENCRYPTED_EXT ) )
    1533           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_EE_EXPECTED );
    1534             : 
    1535        9414 :     ulong msg_sz = fd_tls_u24_to_uint( msg_hdr.sz );
    1536        9414 :     if( FD_UNLIKELY( msg_sz > (ulong)(wire_end-wire) ) )
    1537           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_EE_PARSE );
    1538             : 
    1539             :     /* Decode EncryptedExtensions */
    1540             : 
    1541        9414 :     decode_res = fd_tls_decode_enc_ext( ee, wire, msg_sz );
    1542        9414 :     if( FD_UNLIKELY( decode_res<0L ) )
    1543           6 :       return fd_tls_alert( &handshake->base, (uint)(-decode_res), FD_TLS_REASON_EE_PARSE );
    1544        9408 :     if( FD_UNLIKELY( (ulong)decode_res != msg_sz ) )
    1545           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_EE_PARSE );
    1546        9408 :     wire += (ulong)decode_res;
    1547             : 
    1548        9408 :     read_sz = (ulong)(wire - record);
    1549        9408 :   } while(0);
    1550             : 
    1551             :   /* Record EE in transcript hash */
    1552             : 
    1553        9408 :   fd_sha256_append( &handshake->transcript, record, read_sz );
    1554             : 
    1555             :   /* QUIC mode */
    1556             : 
    1557        9408 :   if( FD_UNLIKELY( ( ee->server_name && !client->server_name_len ) ||
    1558        9408 :                    ( ee->quic_tp.buf && !client->quic            ) ||
    1559        9408 :                    ( ee->alpn.buf && !client->alpn_sz            ) ) )
    1560           9 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_UNSUPPORTED_EXTENSION, FD_TLS_REASON_EE_PARSE );
    1561             : 
    1562        9399 :   if( client->quic ) {
    1563             :     /* QUIC transport parameters are mandatory in QUIC mode */
    1564        6072 :     if( FD_UNLIKELY( !ee->quic_tp.buf ) )
    1565           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_MISSING_EXTENSION, FD_TLS_REASON_EE_NO_QUIC );
    1566             : 
    1567             :     /* Inform user of peer's QUIC transport parameters */
    1568        6072 :     client->quic_tp_peer_fn( handshake, ee->quic_tp.buf, ee->quic_tp.bufsz );
    1569        6072 :   }
    1570             : 
    1571             :   /* Check ALPN.
    1572             : 
    1573             :      In QUIC mode, ALPN is mandatory (RFC 9001 Section 8.1).
    1574             :      In TCP mode, many HTTP servers omit the ALPN extension from
    1575             :      EncryptedExtensions, permitted by RFC 8446 Section 4.2.
    1576             :      We only enforce ALPN matching when the server actually sends it. */
    1577             : 
    1578        9399 :   if( client->alpn_sz && ee->alpn.bufsz ) {
    1579        7590 :     if( FD_UNLIKELY( ee->alpn.bufsz != client->alpn_sz ||
    1580        7590 :                      0!=memcmp( ee->alpn.buf, client->alpn, client->alpn_sz ) ) )
    1581           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_HANDSHAKE_FAILURE, FD_TLS_REASON_ALPN_NEG );
    1582        7590 :     handshake->alpn_negotiated = 1;
    1583        7590 :   }
    1584        9399 :   if( client->quic && client->alpn_sz && !ee->alpn.bufsz )
    1585           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_MISSING_EXTENSION, FD_TLS_REASON_NO_ALPN );
    1586             : 
    1587             :   /* Finish up ********************************************************/
    1588             : 
    1589        9399 :   handshake->base.state = FD_TLS_HS_WAIT_CERT_CR;
    1590             : 
    1591        9399 :   return (long)read_sz;
    1592        9399 : }
    1593             : 
    1594             : static long
    1595             : fd_tls_client_handle_cert_req( fd_tls_estate_cli_t * const handshake,
    1596             :                                uchar const *         const req,
    1597        8298 :                                ulong                 const req_sz ) {
    1598             : 
    1599        8298 :   fd_tls_ext_signature_algorithms_t sigalgs = {0};
    1600        8298 :   long res = fd_tls_decode_cert_req( &sigalgs, req, req_sz );
    1601        8298 :   if( FD_UNLIKELY( res<0L ) ) return res;
    1602        8298 :   if( FD_UNLIKELY( (ulong)res != req_sz ) ) return -(long)FD_TLS_ALERT_DECODE_ERROR;
    1603             : 
    1604        8298 :   handshake->client_cert_empty = !sigalgs.ed25519;
    1605        8298 :   handshake->client_cert = 1;
    1606        8298 :   handshake->base.state  = FD_TLS_HS_WAIT_CERT;
    1607             : 
    1608        8298 :   return (long)req_sz;
    1609        8298 : }
    1610             : 
    1611             : static long
    1612             : fd_tls_client_handle_cert_chain( fd_tls_t const *      const client,
    1613             :                                  fd_tls_estate_cli_t * const hs,
    1614             :                                  uchar const *         const cert_chain,
    1615        9408 :                                  ulong                 const cert_chain_sz ) {
    1616        9408 :   fd_tls_extract_cert_pubkey_res_t extract;
    1617        9408 :   long res = fd_tls_handle_cert_chain( &hs->base, cert_chain, cert_chain_sz, &extract );
    1618        9408 :   if( FD_UNLIKELY( res < 0L ) ) return res;
    1619             : 
    1620             :   /* QUIC mode offers Ed25519 only, so a P-256 or RSA cert is not
    1621             :      acceptable. */
    1622        9402 :   if( FD_UNLIKELY( client->quic && extract.key_type!=FD_TLS_KEY_ED25519 ) )
    1623           6 :     return fd_tls_alert( &hs->base, FD_TLS_ALERT_UNSUPPORTED_CERTIFICATE, FD_TLS_REASON_CERT_KEY_TYPE );
    1624             : 
    1625        9396 :   if( FD_UNLIKELY( extract.pubkey_len > sizeof(hs->server_pubkey) ) )
    1626           0 :     return fd_tls_alert( &hs->base, FD_TLS_ALERT_UNSUPPORTED_CERTIFICATE, FD_TLS_REASON_CERT_KEY_TYPE );
    1627             : 
    1628             :   /* pubkey pinning is ...
    1629             :        ... enabled  => check that public key matches cert
    1630             :        ... disabled => update the handshake's public key value based on cert */
    1631        9396 :   if( hs->server_pubkey_pin ) {
    1632        1020 :     if( FD_UNLIKELY( extract.pubkey_len!=hs->server_pubkey_len ||
    1633        1020 :                      0!=memcmp( extract.pubkey, hs->server_pubkey, extract.pubkey_len ) ) )
    1634          12 :       return fd_tls_alert( &hs->base, FD_TLS_ALERT_HANDSHAKE_FAILURE, FD_TLS_REASON_WRONG_PUBKEY );
    1635        8376 :   } else {
    1636        8376 :     fd_memcpy( hs->server_pubkey, extract.pubkey, extract.pubkey_len );
    1637        8376 :   }
    1638        9384 :   hs->server_pubkey_len = extract.pubkey_len;
    1639        9384 :   hs->server_key_type   = extract.key_type;
    1640             : 
    1641             :   /* Verify the chain against the trust store and the SNI we sent */
    1642        9384 :   if( client->ca_store ) {
    1643        1092 :     int verify_err = fd_x509_verify_tls_cert_msg( cert_chain, cert_chain_sz, client->ca_store,
    1644        1092 :                                                   client->server_name, client->server_name_len,
    1645        1092 :                                                   fd_x509_unix_now_seconds() );
    1646        1092 :     hs->cert_verify_err = (uchar)verify_err;
    1647        1092 :     if( FD_UNLIKELY( verify_err ) ) {
    1648          84 :       FD_LOG_WARNING(( "certificate verification failed (%i) for %s", verify_err, client->server_name ));
    1649          84 :       return fd_tls_alert( &hs->base, FD_TLS_ALERT_BAD_CERTIFICATE, FD_TLS_REASON_CERT_VERIFY );
    1650          84 :     }
    1651        1092 :   }
    1652             : 
    1653        9300 :   return res;
    1654        9384 : }
    1655             : 
    1656             : static long
    1657             : fd_tls_client_hs_wait_cert_cr( fd_tls_t const *      const client,
    1658             :                                fd_tls_estate_cli_t * const handshake,
    1659             :                                uchar const *         const record,
    1660             :                                ulong                 const record_sz,
    1661        9420 :                                uint                  const encryption_level ) {
    1662             : 
    1663        9420 :   if( FD_UNLIKELY( encryption_level != FD_TLS_LEVEL_HANDSHAKE ) )
    1664           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_WRONG_ENC_LVL );
    1665             : 
    1666             :   /* Read Certificate(Request) ****************************************/
    1667             : 
    1668        9420 :   uchar next_state;
    1669             : 
    1670        9420 :   ulong read_sz;
    1671        9420 :   do {
    1672        9420 :     uchar const *       wire     = record;
    1673        9420 :     uchar const * const wire_end = record + record_sz;
    1674             : 
    1675             :     /* Decode message header */
    1676             : 
    1677        9420 :     fd_tls_msg_hdr_t msg_hdr = {0};
    1678        9420 :     long decode_res = fd_tls_decode_msg_hdr( &msg_hdr, wire, (ulong)(wire_end-wire) );
    1679        9420 :     if( FD_UNLIKELY( decode_res<0L ) )
    1680           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_CERT_CR_PARSE );
    1681        9420 :     wire += (ulong)decode_res;
    1682             : 
    1683        9420 :     ulong msg_sz = fd_tls_u24_to_uint( msg_hdr.sz );
    1684        9420 :     if( FD_UNLIKELY( msg_sz > (ulong)(wire_end-wire) ) )
    1685           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_CERT_CR_PARSE );
    1686             : 
    1687        9420 :     switch( msg_hdr.type ) {
    1688        8298 :     case FD_TLS_MSG_CERT_REQ:
    1689        8298 :       decode_res = fd_tls_client_handle_cert_req( handshake, wire, msg_sz );
    1690        8298 :       next_state = FD_TLS_HS_WAIT_CERT;
    1691        8298 :       break;
    1692        1122 :     case FD_TLS_MSG_CERT:
    1693        1122 :       decode_res = fd_tls_client_handle_cert_chain( client, handshake, wire, msg_sz );
    1694        1122 :       next_state = FD_TLS_HS_WAIT_CV;
    1695        1122 :       break;
    1696           0 :     default:
    1697           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_CERT_CR_EXPECTED );
    1698        9420 :     }
    1699             : 
    1700        9420 :     if( FD_UNLIKELY( decode_res<0L ) )
    1701         108 :       return fd_tls_alert( &handshake->base, (uint)(-decode_res), FD_TLS_REASON_CERT_CR_PARSE );
    1702        9312 :     wire += (ulong)decode_res;
    1703             : 
    1704        9312 :     read_sz = (ulong)(wire - record);
    1705        9312 :   } while(0);
    1706             : 
    1707        9312 :   fd_sha256_append( &handshake->transcript, record, read_sz );
    1708             : 
    1709             :   /* Finish up ********************************************************/
    1710             : 
    1711        9312 :   handshake->base.state = ((uchar)next_state);
    1712        9312 :   return (long)read_sz;
    1713        9420 : }
    1714             : 
    1715             : static long
    1716             : fd_tls_client_hs_wait_cert( fd_tls_t const *      const client,
    1717             :                             fd_tls_estate_cli_t * const handshake,
    1718             :                             uchar const *         const record,
    1719             :                             ulong                 const record_sz,
    1720        8286 :                             uint                  const encryption_level ) {
    1721             : 
    1722        8286 :   if( FD_UNLIKELY( encryption_level != FD_TLS_LEVEL_HANDSHAKE ) )
    1723           0 :     return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_WRONG_ENC_LVL );
    1724             : 
    1725             :   /* Read Certificate *************************************************/
    1726             : 
    1727        8286 :   ulong read_sz;
    1728        8286 :   do {
    1729        8286 :     uchar const *       wire     = record;
    1730        8286 :     uchar const * const wire_end = record + record_sz;
    1731             : 
    1732             :     /* Decode message header */
    1733             : 
    1734        8286 :     fd_tls_msg_hdr_t msg_hdr = {0};
    1735        8286 :     long decode_res = fd_tls_decode_msg_hdr( &msg_hdr, wire, (ulong)(wire_end-wire) );
    1736        8286 :     if( FD_UNLIKELY( decode_res<0L ) )
    1737           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_CERT_PARSE );
    1738        8286 :     wire += (ulong)decode_res;
    1739             : 
    1740        8286 :     if( FD_UNLIKELY( msg_hdr.type != FD_TLS_MSG_CERT ) )
    1741           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_CERT_EXPECTED );
    1742             : 
    1743        8286 :     ulong msg_sz = fd_tls_u24_to_uint( msg_hdr.sz );
    1744        8286 :     if( FD_UNLIKELY( msg_sz > (ulong)(wire_end-wire) ) )
    1745           0 :       return fd_tls_alert( &handshake->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_CERT_CR_PARSE );
    1746             : 
    1747             :     /* Decode Certificate */
    1748             : 
    1749        8286 :     decode_res = fd_tls_client_handle_cert_chain( client, handshake, wire, msg_sz );
    1750        8286 :     if( FD_UNLIKELY( decode_res<0L ) )
    1751           0 :       return fd_tls_alert( &handshake->base, (uint)(-decode_res), FD_TLS_REASON_CERT_PARSE );
    1752        8286 :     wire += (ulong)decode_res;
    1753             : 
    1754        8286 :     read_sz = (ulong)(wire - record);
    1755        8286 :   } while(0);
    1756             : 
    1757        8286 :   fd_sha256_append( &handshake->transcript, record, read_sz );
    1758             : 
    1759             :   /* Finish up ********************************************************/
    1760             : 
    1761        8286 :   handshake->base.state = (char)FD_TLS_HS_WAIT_CV;
    1762        8286 :   return (long)read_sz;
    1763        8286 : }
    1764             : 
    1765             : static long
    1766             : fd_tls_client_hs_wait_cert_verify( fd_tls_t const *      const client,
    1767             :                                    fd_tls_estate_cli_t * const hs,
    1768             :                                    uchar const *         const record,
    1769             :                                    ulong                 const record_sz,
    1770        9384 :                                    uint                  const encryption_level ) {
    1771             : 
    1772        9384 :   (void)client;
    1773             : 
    1774        9384 :   if( FD_UNLIKELY( encryption_level != FD_TLS_LEVEL_HANDSHAKE ) )
    1775           0 :     return fd_tls_alert( &hs->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_WRONG_ENC_LVL );
    1776             : 
    1777             :   /* Decode incoming server CertificateVerify *************************/
    1778             : 
    1779        9384 :   long res = fd_tls_handle_cert_verify( &hs->base, &hs->transcript,
    1780        9384 :                                         record, record_sz,
    1781        9384 :                                         hs->server_pubkey, hs->server_pubkey_len, hs->server_key_type, 0 );
    1782        9384 :   if( FD_UNLIKELY( res<0L ) ) return res;
    1783             : 
    1784        9312 :   fd_sha256_append( &hs->transcript, record, (ulong)res );
    1785             : 
    1786             :   /* Finish up ********************************************************/
    1787             : 
    1788        9312 :   hs->base.state = FD_TLS_HS_WAIT_FINISHED;
    1789        9312 :   return res;
    1790        9384 : }
    1791             : 
    1792             : static long
    1793             : fd_tls_client_hs_wait_finished( fd_tls_t const *      const client,
    1794             :                                 fd_tls_estate_cli_t * const hs,
    1795             :                                 uchar const *         const record,
    1796             :                                 ulong                 const record_sz,
    1797        9300 :                                 uint                  const encryption_level ) {
    1798             : 
    1799        9300 :   if( FD_UNLIKELY( encryption_level != FD_TLS_LEVEL_HANDSHAKE ) )
    1800           0 :     return fd_tls_alert( &hs->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_WRONG_ENC_LVL );
    1801             : 
    1802             :   /* Export transcript hash ClientHello..CertificateVerify */
    1803             : 
    1804        9300 :   fd_sha256_t transcript_clone = hs->transcript;
    1805        9300 :   uchar transcript_hash[ 32 ];
    1806        9300 :   fd_sha256_fini( &transcript_clone, transcript_hash );
    1807             : 
    1808             :   /* RFC 8446 Section 4.4.4:
    1809             :        finished_key = HKDF-Expand-Label(BaseKey, "finished", "", Hash.length)
    1810             :        verify_data  = HMAC(finished_key, Transcript-Hash(
    1811             :                        Handshake Context, Certificate*, CertificateVerify*)) */
    1812             : 
    1813        9300 :   uchar server_finished_key[ 32 ];
    1814        9300 :   fd_tls_hkdf_expand_label( server_finished_key, 32UL,
    1815        9300 :                             hs->server_hs_secret,
    1816        9300 :                             "finished", 8UL,
    1817        9300 :                             NULL,       0UL );
    1818             : 
    1819        9300 :   uchar server_finished_expected[ 32 ];
    1820        9300 :   fd_hmac_sha256( /* data */ transcript_hash,     32UL,
    1821        9300 :                   /* salt */ server_finished_key, 32UL,
    1822        9300 :                   /* out  */ server_finished_expected );
    1823             : 
    1824             :   /* Read ServerFinished **********************************************/
    1825             : 
    1826        9300 :   fd_tls_finished_t server_fin = {0};
    1827             : 
    1828        9300 :   ulong read_sz;
    1829        9300 :   do {
    1830        9300 :     uchar const *       wire     = record;
    1831        9300 :     uchar const * const wire_end = record + record_sz;
    1832             : 
    1833             :     /* Decode message header */
    1834             : 
    1835        9300 :     fd_tls_msg_hdr_t msg_hdr = {0};
    1836        9300 :     long decode_res = fd_tls_decode_msg_hdr( &msg_hdr, wire, (ulong)(wire_end-wire) );
    1837        9300 :     if( FD_UNLIKELY( decode_res<0L ) )
    1838           0 :       return fd_tls_alert( &hs->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_FINI_PARSE );
    1839        9300 :     wire += (ulong)decode_res;
    1840             : 
    1841        9300 :     if( FD_UNLIKELY( msg_hdr.type != FD_TLS_MSG_FINISHED ) )
    1842           0 :       return fd_tls_alert( &hs->base, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_FINI_EXPECTED );
    1843             : 
    1844        9300 :     if( FD_UNLIKELY( fd_tls_u24_to_uint( msg_hdr.sz ) != sizeof(fd_tls_finished_t) ) )
    1845           0 :       return fd_tls_alert( &hs->base, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_FINI_PARSE );
    1846             : 
    1847             :     /* Decode server Finished */
    1848             : 
    1849        9300 :     decode_res = fd_tls_decode_finished( &server_fin, wire, (ulong)(wire_end-wire) );
    1850        9300 :     if( FD_UNLIKELY( decode_res<0L ) )
    1851           0 :       return fd_tls_alert( &hs->base, (uint)(-decode_res), FD_TLS_REASON_FINI_PARSE );
    1852        9300 :     wire += (ulong)decode_res;
    1853             : 
    1854        9300 :     read_sz = (ulong)(wire - record);
    1855        9300 :   } while(0);
    1856             : 
    1857             :   /* Record ServerFinished */
    1858             : 
    1859        9300 :   fd_sha256_append( &hs->transcript, record, read_sz );
    1860             : 
    1861             :   /* Verify that client and server's transcripts match */
    1862             : 
    1863        9300 :   int match = 0;
    1864      306900 :   for( ulong i=0; i<32UL; i++ ) {
    1865      297600 :     match |= server_fin.verify[i] ^ server_finished_expected[i];
    1866      297600 :   }
    1867        9300 :   if( FD_UNLIKELY( match!=0 ) )
    1868           0 :     return fd_tls_alert( &hs->base, FD_TLS_ALERT_DECRYPT_ERROR, FD_TLS_REASON_FINI_FAIL );
    1869             : 
    1870             :   /* Derive application secrets ***************************************/
    1871             : 
    1872             :   /* Export transcript hash ClientHello..ServerFinished */
    1873             : 
    1874        9300 :   transcript_clone = hs->transcript;
    1875        9300 :   fd_sha256_fini( &transcript_clone, transcript_hash );
    1876             : 
    1877             :   /* RFC 8446 Section 7.1:
    1878             :        client_application_traffic_secret_0 =
    1879             :          Derive-Secret(Master Secret, "c ap traffic",
    1880             :                        ClientHello...server Finished) */
    1881             : 
    1882        9300 :   uchar client_app_secret[ 32UL ];
    1883        9300 :   fd_tls_hkdf_expand_label( client_app_secret, 32UL,
    1884        9300 :                             hs->master_secret,
    1885        9300 :                             "c ap traffic",  12UL,
    1886        9300 :                             transcript_hash, 32UL );
    1887             : 
    1888             :   /* RFC 8446 Section 7.1:
    1889             :        server_application_traffic_secret_0 =
    1890             :          Derive-Secret(Master Secret, "s ap traffic",
    1891             :                        ClientHello...server Finished) */
    1892             : 
    1893        9300 :   uchar server_app_secret[ 32UL ];
    1894        9300 :   fd_tls_hkdf_expand_label( server_app_secret, 32UL,
    1895        9300 :                                hs->master_secret,
    1896        9300 :                                "s ap traffic",  12UL,
    1897        9300 :                                transcript_hash, 32UL );
    1898             : 
    1899             :   /* Call back with application secrets */
    1900             : 
    1901        9300 :   client->secrets_fn( hs,
    1902        9300 :                       /* read secret  */ server_app_secret,
    1903        9300 :                       /* write secret */ client_app_secret,
    1904        9300 :                       FD_TLS_LEVEL_APPLICATION );
    1905             : 
    1906        9300 :   if( hs->client_cert && ( hs->client_cert_empty || !client->cert_x509_sz || !client->sign.sign_fn ) ) {
    1907             : 
    1908             :     /* RFC 8446 Section 4.4.2: a client that was asked for a certificate
    1909             :        but has none to offer sends a Certificate message with an empty
    1910             :        certificate_list and no CertificateVerify.  The server decides
    1911             :        whether to continue. */
    1912             : 
    1913        1023 :     static uchar const empty_cert[] = {
    1914        1023 :       FD_TLS_MSG_CERT,   /* msg_type */
    1915        1023 :       0x00, 0x00, 0x04,  /* msg sz */
    1916        1023 :       0x00,              /* certificate_request_context */
    1917        1023 :       0x00, 0x00, 0x00,  /* certificate_list */
    1918        1023 :     };
    1919             : 
    1920        1023 :     if( FD_UNLIKELY( !client->sendmsg_fn(
    1921        1023 :           hs,
    1922        1023 :           empty_cert, sizeof(empty_cert),
    1923        1023 :           FD_TLS_LEVEL_HANDSHAKE,
    1924        1023 :           /* flush */ 0 ) ) )
    1925           0 :       return fd_tls_alert( &hs->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_SENDMSG_FAIL );
    1926             : 
    1927        1023 :     fd_sha256_append( &hs->transcript, empty_cert, sizeof(empty_cert) );
    1928             : 
    1929        8277 :   } else if( hs->client_cert ) {
    1930             : 
    1931             :     /* Send client Certificate ****************************************/
    1932             : 
    1933             :     /* Message buffer */
    1934        7269 :     uchar msg_buf[ FD_TLS_SERVER_CERT_MSG_SZ_MAX ];
    1935        7269 :     long cert_msg_sz = fd_tls_encode_cert_x509( client->cert_x509, client->cert_x509_sz, msg_buf, sizeof(msg_buf) );
    1936        7269 :     if( FD_UNLIKELY( cert_msg_sz<0L ) )
    1937           0 :       return fd_tls_alert( &hs->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_SENDMSG_FAIL );
    1938             : 
    1939             :     /* Send certificate message */
    1940             : 
    1941        7269 :     if( FD_UNLIKELY( !client->sendmsg_fn(
    1942        7269 :           hs,
    1943        7269 :           msg_buf, (ulong)cert_msg_sz,
    1944        7269 :           FD_TLS_LEVEL_HANDSHAKE,
    1945        7269 :           /* flush */ 0 ) ) )
    1946           0 :       return fd_tls_alert( &hs->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_SENDMSG_FAIL );
    1947             : 
    1948             :     /* Record Certificate message in transcript hash */
    1949             : 
    1950        7269 :     fd_sha256_append( &hs->transcript, msg_buf, (ulong)cert_msg_sz );
    1951             : 
    1952             :     /* Send client CertificateVerify **********************************/
    1953             : 
    1954        7269 :     long cvfy_res = fd_tls_send_cert_verify( client, &hs->base, &hs->transcript, 1 );
    1955        7269 :     if( FD_UNLIKELY( !!cvfy_res ) ) return cvfy_res;
    1956             : 
    1957        7269 :   }
    1958             : 
    1959             :   /* Send client Finished *********************************************/
    1960             : 
    1961        9300 :   struct __attribute__((packed)) {
    1962        9300 :     fd_tls_msg_hdr_t hdr;
    1963        9300 :     fd_tls_finished_t   fin;
    1964        9300 :   } fin_rec;
    1965        9300 :   fin_rec.hdr = (fd_tls_msg_hdr_t){
    1966        9300 :     .type = FD_TLS_MSG_FINISHED,
    1967        9300 :     .sz   = fd_uint_to_tls_u24( 0x20 )
    1968        9300 :   };
    1969             : 
    1970        9300 :   fd_tls_msg_hdr_bswap( &fin_rec.hdr );
    1971             : 
    1972             :   /* Export transcript hash up to this point */
    1973             : 
    1974        9300 :   fd_sha256_fini( &hs->transcript, transcript_hash );
    1975             : 
    1976             :   /* RFC 8446 Section 4.4.4:
    1977             :        finished_key = HKDF-Expand-Label(BaseKey, "finished", "", Hash.length)
    1978             :        verify_data  = HMAC(finished_key, Transcript-Hash(
    1979             :                        Handshake Context, Certificate*, CertificateVerify*)) */
    1980             : 
    1981        9300 :   uchar client_finished_key[ 32 ];
    1982        9300 :   fd_tls_hkdf_expand_label( client_finished_key, 32UL,
    1983        9300 :                             hs->client_hs_secret,
    1984        9300 :                             "finished", 8UL,
    1985        9300 :                             NULL,       0UL );
    1986             : 
    1987        9300 :   fd_hmac_sha256( /* data */ transcript_hash,     32UL,
    1988        9300 :                   /* salt */ client_finished_key, 32UL,
    1989        9300 :                   /* out  */ fin_rec.fin.verify );
    1990             : 
    1991             :   /* Send client Finished message */
    1992             : 
    1993        9300 :   if( FD_UNLIKELY( !client->sendmsg_fn(
    1994        9300 :         hs,
    1995        9300 :         &fin_rec, sizeof(fin_rec),
    1996        9300 :         FD_TLS_LEVEL_HANDSHAKE,
    1997        9300 :         /* flush */ 1 ) ) )
    1998           0 :     return fd_tls_alert( &hs->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_SENDMSG_FAIL );
    1999             : 
    2000        9300 :   hs->base.state = FD_TLS_HS_CONNECTED;
    2001        9300 :   return (long)read_sz;
    2002        9300 : }
    2003             : 
    2004             : char const *
    2005         549 : fd_tls_alert_cstr( uint alert ) {
    2006         549 :   switch( alert ) {
    2007          90 :   case FD_TLS_ALERT_UNEXPECTED_MESSAGE:
    2008          90 :     return "unexpected message";
    2009          24 :   case FD_TLS_ALERT_BAD_RECORD_MAC:
    2010          24 :     return "bad record MAC";
    2011          15 :   case FD_TLS_ALERT_RECORD_OVERFLOW:
    2012          15 :     return "record overflow";
    2013         135 :   case FD_TLS_ALERT_HANDSHAKE_FAILURE:
    2014         135 :     return "handshake failure";
    2015         108 :   case FD_TLS_ALERT_BAD_CERTIFICATE:
    2016         108 :     return "bad certificate";
    2017           0 :   case FD_TLS_ALERT_UNSUPPORTED_CERTIFICATE:
    2018           0 :     return "unsupported certificate";
    2019           0 :   case FD_TLS_ALERT_CERTIFICATE_REVOKED:
    2020           0 :     return "certificate revoked";
    2021           0 :   case FD_TLS_ALERT_CERTIFICATE_EXPIRED:
    2022           0 :     return "certificate expired";
    2023           0 :   case FD_TLS_ALERT_CERTIFICATE_UNKNOWN:
    2024           0 :     return "certificate unknown";
    2025           3 :   case FD_TLS_ALERT_ILLEGAL_PARAMETER:
    2026           3 :     return "illegal parameter";
    2027          24 :   case FD_TLS_ALERT_UNKNOWN_CA:
    2028          24 :     return "unknown CA";
    2029           0 :   case FD_TLS_ALERT_ACCESS_DENIED:
    2030           0 :     return "access denied";
    2031          69 :   case FD_TLS_ALERT_DECODE_ERROR:
    2032          69 :     return "decode error";
    2033           0 :   case FD_TLS_ALERT_DECRYPT_ERROR:
    2034           0 :     return "decrypt error";
    2035          45 :   case FD_TLS_ALERT_PROTOCOL_VERSION:
    2036          45 :     return "unsupported protocol version";
    2037           0 :   case FD_TLS_ALERT_INSUFFICIENT_SECURITY:
    2038           0 :     return "insufficient security";
    2039           0 :   case FD_TLS_ALERT_INTERNAL_ERROR:
    2040           0 :     return "internal error";
    2041           0 :   case FD_TLS_ALERT_INAPPROPRIATE_FALLBACK:
    2042           0 :     return "inappropriate fallback";
    2043           0 :   case FD_TLS_ALERT_USER_CANCELED:
    2044           0 :     return "user canceled";
    2045           0 :   case FD_TLS_ALERT_MISSING_EXTENSION:
    2046           0 :     return "missing extension";
    2047           0 :   case FD_TLS_ALERT_UNSUPPORTED_EXTENSION:
    2048           0 :     return "unsupported extension";
    2049           0 :   case FD_TLS_ALERT_UNRECOGNIZED_NAME:
    2050           0 :     return "unrecognized name";
    2051           0 :   case FD_TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE:
    2052           0 :     return "bad certificate status response";
    2053           0 :   case FD_TLS_ALERT_UNKNOWN_PSK_IDENTITY:
    2054           0 :     return "unknown PSK identity";
    2055          12 :   case FD_TLS_ALERT_CERTIFICATE_REQUIRED:
    2056          12 :     return "certificate required";
    2057          24 :   case FD_TLS_ALERT_NO_APPLICATION_PROTOCOL:
    2058          24 :     return "no application protocol";
    2059           0 :   default:
    2060           0 :     FD_LOG_WARNING(( "Missing fd_tls_alert_cstr code for %u (memory corruption?)", alert ));
    2061           0 :     return "unknown alert";
    2062             :   /* TODO add the other alert codes */
    2063         549 :   }
    2064         549 : }
    2065             : 
    2066             : char const *
    2067         381 : fd_tls_reason_cstr( uint reason ) {
    2068         381 :   switch( reason ) {
    2069           0 :   case FD_TLS_REASON_ILLEGAL_STATE:
    2070           0 :     return "illegal handshake state ID (memory corruption?)";
    2071           0 :   case FD_TLS_REASON_SENDMSG_FAIL:
    2072           0 :     return "sendmsg callback failed";
    2073           0 :   case FD_TLS_REASON_WRONG_ENC_LVL:
    2074           0 :     return "wrong encryption level";
    2075           0 :   case FD_TLS_REASON_CH_EXPECTED:
    2076           0 :     return "expected ClientHello, but got other message type";
    2077           0 :   case FD_TLS_REASON_CH_PARSE:
    2078           0 :     return "failed to decode ClientHello";
    2079           0 :   case FD_TLS_REASON_CH_ENCODE:
    2080           0 :     return "failed to encode ClientHello";
    2081          12 :   case FD_TLS_REASON_CH_NEG_VER:
    2082          12 :     return "unsupported TLS version (fd_tls only supports TLS 1.3)";
    2083          12 :   case FD_TLS_REASON_CH_NEG_KX:
    2084          12 :     return "ClientHello did not advertise X25519 as a supported key exchange alg";
    2085          12 :   case FD_TLS_REASON_CH_NEG_SIG:
    2086          12 :     return "ClientHello did not advertise Ed25519 as a supported signature alg";
    2087          12 :   case FD_TLS_REASON_CH_NEG_CIPHER:
    2088          12 :     return "ClientHello did not advertise AES-128-GCM as a supported cipher suite";
    2089           0 :   case FD_TLS_REASON_CH_NO_QUIC:
    2090           0 :     return "client does not support QUIC (missing QUIC transport params)";
    2091           0 :   case FD_TLS_REASON_CH_RETRY_KS:
    2092           0 :     return "client didn't provide an X25519 key share even after a RetryHelloRequest";
    2093           0 :   case FD_TLS_REASON_X25519_FAIL:
    2094           0 :     return "X25519 key exchange failed";
    2095           0 :   case FD_TLS_REASON_NO_X509:
    2096           0 :     return "peer requested X.509 cert, but we don't have one";
    2097          12 :   case FD_TLS_REASON_WRONG_PUBKEY:
    2098          12 :     return "peer identity does not match expected public key";
    2099           0 :   case FD_TLS_REASON_ED25519_FAIL:
    2100           0 :     return "Ed25519 signature verification failed";
    2101           0 :   case FD_TLS_REASON_SECP256R1_FAIL:
    2102           0 :     return "ECDSA P-256 signature verification failed";
    2103           0 :   case FD_TLS_REASON_RSA_FAIL:
    2104           0 :     return "RSA-PSS signature verification failed";
    2105           0 :   case FD_TLS_REASON_FINI_FAIL:
    2106           0 :     return "unexpected 'Finished' data (transcript hash fail)";
    2107           0 :   case FD_TLS_REASON_QUIC_TP_OVERSZ:
    2108           0 :     return "buffer overflow in QUIC transport param handling (user bug)";
    2109           0 :   case FD_TLS_REASON_EE_NO_QUIC:
    2110           0 :     return "server does not support QUIC (missing QUIC transport params)";
    2111           0 :   case FD_TLS_REASON_X509_PARSE:
    2112           0 :     return "X.509 cert parse failed";
    2113           0 :   case FD_TLS_REASON_CV_EXPECTED:
    2114           0 :     return "expected CertificateVerify, but got other message type";
    2115           0 :   case FD_TLS_REASON_CV_SIGALG:
    2116           0 :     return "peer CertificateVerify uses a signature algorithm incompatible with its cert";
    2117           0 :   case FD_TLS_REASON_FINI_PARSE:
    2118           0 :     return "failed to parse 'Finished' message";
    2119           0 :   case FD_TLS_REASON_SH_EXPECTED:
    2120           0 :     return "expected ServerHello, but got other message type";
    2121           0 :   case FD_TLS_REASON_SH_PARSE:
    2122           0 :     return "failed to decode ServerHello";
    2123           0 :   case FD_TLS_REASON_SH_ENCODE:
    2124           0 :     return "failed to encode ServerHello";
    2125           0 :   case FD_TLS_REASON_SH_NEG_CIPHER:
    2126           0 :     return "ServerHello selected a cipher suite not offered by ClientHello";
    2127           0 :   case FD_TLS_REASON_SH_SESSION_ID:
    2128           0 :     return "ServerHello legacy_session_id_echo does not match ClientHello";
    2129           0 :   case FD_TLS_REASON_EE_EXPECTED:
    2130           0 :     return "expected EncryptedExtensions, but got other message type";
    2131           0 :   case FD_TLS_REASON_EE_PARSE:
    2132           0 :     return "failed to decode EncryptedExtensions";
    2133           0 :   case FD_TLS_REASON_EE_ENCODE:
    2134           0 :     return "failed to encode EncryptedExtensions";
    2135           0 :   case FD_TLS_REASON_CERT_KEY_TYPE:
    2136           0 :     return "unsupported certificate key type";
    2137           0 :   case FD_TLS_REASON_CERT_EXPECTED:
    2138           0 :     return "expected Certificate, but got other message type";
    2139           0 :   case FD_TLS_REASON_CERT_PARSE:
    2140           0 :    return "failed to decode Certificate";
    2141           0 :   case FD_TLS_REASON_CERT_ENCODE:
    2142           0 :     return "failed to encode Certificate";
    2143          84 :   case FD_TLS_REASON_CERT_VERIFY:
    2144          84 :     return "certificate chain failed verification";
    2145           0 :   case FD_TLS_REASON_FINI_EXPECTED:
    2146           0 :     return "expected Finished, but got other message type";
    2147           0 :   case FD_TLS_REASON_CERT_CR_EXPECTED:
    2148           0 :     return "expected Certificate or CertificateRequest, but got other message type";
    2149           0 :   case FD_TLS_REASON_CERT_CR_PARSE:
    2150           0 :     return "failed to decode Certificate or CertificateRequest";
    2151          24 :   case FD_TLS_REASON_CERT_CHAIN_EMPTY:
    2152          24 :     return "peer did not provide a certificate";
    2153           0 :   case FD_TLS_REASON_CERT_CHAIN_PARSE:
    2154           0 :     return "invalid peer cert chain";
    2155           0 :   case FD_TLS_REASON_CV_PARSE:
    2156           0 :     return "failed to decode CertificateVerify";
    2157           0 :   case FD_TLS_REASON_CV_ENCODE:
    2158           0 :     return "failed to encode CertificateVerify";
    2159           0 :   case FD_TLS_REASON_ALPN_PARSE:
    2160           0 :     return "failed to decode ALPN extension";
    2161          12 :   case FD_TLS_REASON_ALPN_NEG:
    2162          12 :     return "ALPN negotiation failed";
    2163          12 :   case FD_TLS_REASON_NO_ALPN:
    2164          12 :     return "peer did not send ALPN extension";
    2165           0 :   case FD_TLS_REASON_POST_HS_MSG:
    2166           0 :     return "unexpected post-handshake message type";
    2167           9 :   case FD_TLS_REASON_KEY_UPDATE_PARSE:
    2168           9 :     return "failed to parse KeyUpdate";
    2169          42 :   case FD_TLS_REASON_CCS:
    2170          42 :     return "unexpected ChangeCipherSpec record";
    2171          60 :   case FD_TLS_REASON_ALERT_PARSE:
    2172          60 :     return "malformed alert record";
    2173           0 :   case FD_TLS_REASON_PEER_ALERT:
    2174           0 :     return "peer sent a fatal alert";
    2175           6 :   case FD_TLS_REASON_HS_INTERLEAVED:
    2176           6 :     return "record interleaved with a fragmented handshake message";
    2177          21 :   case FD_TLS_REASON_REC_TYPE:
    2178          21 :     return "unexpected record layer content type";
    2179          15 :   case FD_TLS_REASON_REC_OVERFLOW:
    2180          15 :     return "record larger than the protocol allows";
    2181           6 :   case FD_TLS_REASON_HS_KEY_CHANGE:
    2182           6 :     return "handshake data follows a key change in the same record";
    2183          12 :   case FD_TLS_REASON_HS_MSG_SIZE:
    2184          12 :     return "handshake message length invalid";
    2185          15 :   case FD_TLS_REASON_REC_MAC:
    2186          15 :     return "record failed authentication";
    2187           3 :   case FD_TLS_REASON_REC_PADDING:
    2188           3 :     return "record has no content type byte";
    2189           0 :   default:
    2190           0 :     FD_LOG_WARNING(( "Missing fd_tls_reason_cstr code for %u (memory corruption?)", reason ));
    2191           0 :     __attribute__((fallthrough));
    2192             :   /* TODO need to add a lot more error reason codes */
    2193           0 :   case FD_TLS_REASON_NULL:
    2194           0 :     return "unknown reason";
    2195         381 :   }
    2196         381 : }

Generated by: LCOV version 1.14