LCOV - code coverage report
Current view: top level - waltz/quic/crypto - fd_quic_crypto_suites.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 158 167 94.6 %
Date: 2025-03-20 12:08:36 Functions: 8 8 100.0 %

          Line data    Source code
       1             : #include "fd_quic_crypto_suites.h"
       2             : #include "../fd_quic.h"
       3             : 
       4             : #include "../../../ballet/aes/fd_aes_base.h"
       5             : #include "../../../ballet/aes/fd_aes_gcm.h"
       6             : #include "../../../ballet/hmac/fd_hmac.h"
       7             : #include "../templ/fd_quic_parse_util.h"
       8             : 
       9             : /* FD_QUIC_CRYPTO_V1_INITIAL_SALT is the salt to the initial secret
      10             :    HKDF in QUIC v1. */
      11             : 
      12             : static uchar const FD_QUIC_CRYPTO_V1_INITIAL_SALT[ 20UL ] = {
      13             :     0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3,
      14             :     0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad,
      15             :     0xcc, 0xbb, 0x7f, 0x0a };
      16             : 
      17             : static inline void
      18             : fd_quic_hkdf_extract( void *       output,
      19             :                       void const * salt,    ulong salt_sz,
      20       12294 :                       void const * conn_id, ulong conn_id_sz ) {
      21       12294 :   fd_hmac_sha256( conn_id, conn_id_sz, salt, salt_sz, output );
      22       12294 : }
      23             : 
      24             : static inline void
      25             : fd_quic_hkdf_expand_label( uchar *       out,
      26             :                            ulong         out_sz,
      27             :                            uchar const   secret[ 32 ],
      28             :                            char const *  label,
      29      315468 :                            ulong         label_sz ) {
      30      315468 :   fd_tls_hkdf_expand_label( out, out_sz, secret, label, label_sz, NULL, 0UL );
      31      315468 : }
      32             : 
      33             : void
      34             : fd_quic_gen_initial_secrets(
      35             :     fd_quic_crypto_secrets_t * secrets,
      36             :     uchar const *              conn_id,
      37             :     ulong                      conn_id_sz,
      38       12294 :     int                        is_server ) {
      39             :   /* Initial Packets
      40             :      from rfc:
      41             :      initial_salt = 0x38762cf7f55934b34d179ae6a4c80cadccbb7f0a */
      42       12294 :   uchar const * initial_salt    = FD_QUIC_CRYPTO_V1_INITIAL_SALT;
      43       12294 :   ulong         initial_salt_sz = sizeof(FD_QUIC_CRYPTO_V1_INITIAL_SALT);
      44       12294 :   fd_quic_hkdf_extract( secrets->initial_secret,
      45       12294 :                         initial_salt,            initial_salt_sz,
      46       12294 :                         conn_id,                 conn_id_sz );
      47             : 
      48       12294 :   uchar * read_secret   = secrets->secret[0][0];
      49       12294 :   uchar * write_secret  = secrets->secret[0][1];
      50       12294 :   uchar * client_secret = is_server ? read_secret  : write_secret;
      51       12294 :   uchar * server_secret = is_server ? write_secret : read_secret;
      52             : 
      53       12294 :   fd_quic_hkdf_expand_label(
      54       12294 :       client_secret, FD_QUIC_SECRET_SZ,
      55       12294 :       secrets->initial_secret,
      56       12294 :       FD_QUIC_CRYPTO_LABEL_CLIENT_IN,
      57       12294 :       FD_QUIC_CRYPTO_LABEL_CLIENT_IN_LEN );
      58             : 
      59       12294 :   fd_quic_hkdf_expand_label(
      60       12294 :       server_secret, FD_QUIC_SECRET_SZ,
      61       12294 :       secrets->initial_secret,
      62       12294 :       FD_QUIC_CRYPTO_LABEL_SERVER_IN,
      63       12294 :       FD_QUIC_CRYPTO_LABEL_SERVER_IN_LEN );
      64       12294 : }
      65             : 
      66             : void
      67             : fd_quic_key_update_derive( fd_quic_crypto_secrets_t * secrets,
      68       12126 :                            fd_quic_crypto_keys_t      new_keys[2] ) {
      69             :   /* Defined as:
      70             :      application_traffic_secret_N+1 =
      71             :            HKDF-Expand-Label(application_traffic_secret_N,
      72             :                              "traffic upd", "", Hash.length) */
      73       12126 :   uint enc_level = fd_quic_enc_level_appdata_id;
      74             : 
      75       36378 :   for( ulong j=0UL; j<2UL; j++ ) {
      76       24252 :     fd_quic_hkdf_expand_label(
      77       24252 :         secrets->new_secret[j], FD_QUIC_SECRET_SZ,
      78       24252 :         secrets->secret[enc_level][j],
      79       24252 :         FD_QUIC_CRYPTO_LABEL_KEY_UPDATE, FD_QUIC_CRYPTO_LABEL_KEY_UPDATE_LEN );
      80       24252 :   }
      81             : 
      82       36378 :   for( ulong j=0UL; j<2UL; j++ ) {
      83       24252 :     fd_quic_hkdf_expand_label(
      84       24252 :         new_keys[j].pkt_key, FD_AES_128_KEY_SZ,
      85       24252 :         secrets->new_secret[j],
      86       24252 :         FD_QUIC_CRYPTO_LABEL_QUIC_KEY,
      87       24252 :         FD_QUIC_CRYPTO_LABEL_QUIC_KEY_LEN );
      88             : 
      89       24252 :     fd_quic_hkdf_expand_label(
      90       24252 :         new_keys[j].iv, FD_AES_GCM_IV_SZ,
      91       24252 :         secrets->new_secret[j],
      92       24252 :         FD_QUIC_CRYPTO_LABEL_QUIC_IV,
      93       24252 :         FD_QUIC_CRYPTO_LABEL_QUIC_IV_LEN );
      94       24252 :   }
      95       12126 : }
      96             : 
      97             : 
      98             : void
      99             : fd_quic_gen_keys(
     100             :     fd_quic_crypto_keys_t * keys,
     101       72708 :     uchar const             secret[ 32 ] ) {
     102             : 
     103             :   /* quic key */
     104             : 
     105             :   /* output length passed with "quic hp" and "quic key" must be the key size from
     106             :      the current cipher */
     107       72708 :   fd_quic_hkdf_expand_label(
     108       72708 :       keys->pkt_key, FD_AES_128_KEY_SZ,
     109       72708 :       secret,
     110       72708 :       FD_QUIC_CRYPTO_LABEL_QUIC_KEY,
     111       72708 :       FD_QUIC_CRYPTO_LABEL_QUIC_KEY_LEN );
     112             : 
     113             :   /* quic hp */
     114             : 
     115             :   /* output length passed with "quic hp" and "quic key" must be the key size from
     116             :      the current cipher */
     117       72708 :   fd_quic_hkdf_expand_label(
     118       72708 :       keys->hp_key, FD_AES_128_KEY_SZ,
     119       72708 :       secret,
     120       72708 :       FD_QUIC_CRYPTO_LABEL_QUIC_HP,
     121       72708 :       FD_QUIC_CRYPTO_LABEL_QUIC_HP_LEN );
     122             : 
     123             :   /* quic iv */
     124       72708 :   fd_quic_hkdf_expand_label(
     125       72708 :       keys->iv, FD_AES_GCM_IV_SZ,
     126       72708 :       secret,
     127       72708 :       FD_QUIC_CRYPTO_LABEL_QUIC_IV,
     128       72708 :       FD_QUIC_CRYPTO_LABEL_QUIC_IV_LEN );
     129       72708 : }
     130             : 
     131             : /* encrypt a packet
     132             : 
     133             :    uses the keys in keys to encrypt the packet "pkt" with header "hdr"
     134             :    (of length pkt_sz, and hdr_sz respectively) into out.
     135             : 
     136             :    out should have enough space to contain the full output with extra space
     137             :    for a full block which depends on the cipher
     138             : 
     139             :    *out_sz is used to determine the amount of buffer space left at *out
     140             :      if enough space is not available, the function fails and returns
     141             :      FD_QUIC_FAILED
     142             :    *out_sz is also set to the number of bytes written into *out at the end
     143             : 
     144             :    args
     145             :      out               the destination for the encrypted output
     146             :      out_sz            a pointer to the size of the buffer (on input) and the size of
     147             :                          the written bytes (on output)
     148             :      hdr               the input header bytes
     149             :      hdr_sz            the size of the header in bytes
     150             :      pkt               the input packet bytes
     151             :      pkt_sz            the size of the packet in bytes (frames after packet number not including MAC tag)
     152             :      keys              a pointer to the keys to use
     153             :      pkt_number        needed to create the nonce used in encryption
     154             :                          likely points to the packet number within "hdr"
     155             :      pkt_number_sz     the size of the packet number in bytes
     156             :      */
     157             : 
     158             : int
     159             : fd_quic_crypto_encrypt(
     160             :     uchar *                        const out,
     161             :     ulong *                        const out_sz,
     162             :     uchar const *                  const hdr,
     163             :     ulong                          const hdr_sz,
     164             :     uchar const *                  const pkt,
     165             :     ulong                          const pkt_sz,
     166             :     fd_quic_crypto_keys_t const *  const pkt_keys,
     167             :     fd_quic_crypto_keys_t const *  const hp_keys,
     168    19846030 :     ulong                          const pkt_number ) {
     169             : 
     170             : 
     171             :   /* ensure we have enough space in the output buffer
     172             :      most space used by cipher:
     173             :        header bytes (just XORed)
     174             :        input bytes (encrypted)
     175             :        tag bytes */
     176             : 
     177             :   /* bound on the bytes needed for cipher output */
     178    19846030 :   ulong cipher_out_bound = hdr_sz + pkt_sz + FD_QUIC_CRYPTO_TAG_SZ;
     179             : 
     180    19846030 :   if( FD_UNLIKELY( *out_sz < cipher_out_bound ) ) {
     181           0 :     FD_DEBUG( FD_LOG_WARNING(( "fd_quic_crypto_encrypt: output buffer not big enough" )) );
     182           0 :     return FD_QUIC_FAILED;
     183           0 :   }
     184             : 
     185    19846030 :   if( FD_UNLIKELY( ( hdr_sz < 4 ) | ( hdr_sz > INT_MAX ) ) ) {
     186           0 :     FD_DEBUG( FD_LOG_WARNING(( "fd_quic_crypto_encrypt: packet header size out of bounds" )) );
     187           0 :     return FD_QUIC_FAILED;
     188           0 :   }
     189             : 
     190             :   /* bounds check */
     191    19846030 :   if( FD_UNLIKELY( pkt_sz > INT_MAX ) ) return FD_QUIC_FAILED;
     192             : 
     193             :   /* copy the header into the output */
     194    19846030 :   fd_memcpy( out, hdr, hdr_sz );
     195             : 
     196             :   /* first byte needed in a couple of places */
     197    19846030 :   uchar first = out[0];
     198    19846030 :   ulong pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
     199    19846030 :   uchar const * pkt_number_ptr = out + hdr_sz - pkt_number_sz;
     200             : 
     201    19846030 :   uchar nonce[FD_QUIC_NONCE_SZ] = {0};
     202    19846030 :   fd_quic_get_nonce( nonce, pkt_keys->iv, pkt_number );
     203             : 
     204             :   // Initial packets cipher uses AEAD_AES_128_GCM with keys derived from the Destination Connection ID field of the
     205             :   // first Initial packet sent by the client; see rfc9001 Section 5.2.
     206    19846030 :   fd_aes_gcm_t pkt_cipher[1];
     207    19846030 :   fd_aes_128_gcm_init( pkt_cipher, pkt_keys->pkt_key, nonce );
     208             : 
     209             :   /* cipher_text is start of encrypted packet bytes, which starts after the header */
     210    19846030 :   uchar * cipher_text = out + hdr_sz;
     211    19846030 :   uchar * tag         = cipher_text + pkt_sz;
     212    19846030 :   uchar * pkt_end     = tag + FD_QUIC_CRYPTO_TAG_SZ;
     213             : 
     214    19846030 :   fd_aes_gcm_encrypt( pkt_cipher, cipher_text, pkt, pkt_sz, hdr, hdr_sz, tag );
     215             : 
     216    19846030 :   *out_sz = (ulong)( pkt_end - out );
     217             : 
     218             :   /* Header protection */
     219             : 
     220             :   /* sample start is defined as 4 bytes after the start of the packet number
     221             :      so shorter packet numbers means sample starts later in the cipher text */
     222    19846030 :   uchar const * sample = pkt_number_ptr + 4;
     223             : 
     224    19846030 :   fd_aes_key_t ecb[1];
     225    19846030 :   fd_aes_set_encrypt_key( hp_keys->hp_key, 128, ecb );
     226    19846030 :   uchar hp_cipher[16];
     227    19846030 :   fd_aes_encrypt( sample, hp_cipher, ecb );
     228             : 
     229             :   /* hp_cipher is mask */
     230    19846030 :   uchar const * mask = hp_cipher;
     231             : 
     232    19846030 :   uchar long_hdr = first & 0x80u; /* long header? */
     233    19846030 :   out[0] ^= (uchar)( mask[0] & ( long_hdr ? 0x0fu : 0x1fu ) );
     234             : 
     235    19846030 :   ulong pkt_number_off = hdr_sz - pkt_number_sz;
     236             : 
     237    99230138 :   for( ulong j = 0; j < pkt_number_sz; ++j ) {
     238    79384108 :     out[pkt_number_off + j] ^= mask[1+j];
     239    79384108 :   }
     240             : 
     241    19846030 :   return FD_QUIC_SUCCESS;
     242    19846030 : }
     243             : 
     244             : int
     245             : fd_quic_crypto_decrypt(
     246             :     uchar *                       buf,
     247             :     ulong                         buf_sz,
     248             :     ulong                         pkt_number_off,
     249             :     ulong                         pkt_number,
     250    19840204 :     fd_quic_crypto_keys_t const * keys ) {
     251             : 
     252    19840204 :   if( FD_UNLIKELY( ( pkt_number_off >= buf_sz      ) |
     253    19840204 :                    ( buf_sz < FD_QUIC_SHORTEST_PKT ) ) ) {
     254         105 :     FD_DEBUG( FD_LOG_WARNING( ( "fd_quic_crypto_decrypt: cipher text buffer too small" ) ) );
     255         105 :     return FD_QUIC_FAILED;
     256         105 :   }
     257             : 
     258             :   /* Derive header size */
     259    19840099 :   uint    first         = buf[0];
     260    19840099 :   ulong   pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
     261    19840099 :   uchar * hdr           = buf;
     262    19840099 :   ulong   hdr_sz        = pkt_number_off + pkt_number_sz;
     263             : 
     264             :   /* calculate nonce for decryption
     265             :      nonce is quic-iv XORed with *reconstructed* packet-number
     266             :      packet number is 1-4 bytes, so only XOR last pkt_number_sz bytes */
     267    19840099 :   uchar nonce[FD_QUIC_NONCE_SZ] = {0};
     268    19840099 :   fd_quic_get_nonce( nonce, keys->iv, pkt_number );
     269             : 
     270    19840099 :   if( FD_UNLIKELY( ( buf_sz < hdr_sz ) |
     271    19840099 :                    ( buf_sz < hdr_sz+FD_QUIC_CRYPTO_TAG_SZ ) ) )
     272           3 :     return FD_QUIC_FAILED;
     273             : 
     274             :   /* Derive offsets
     275             : 
     276             :      +----------+ <-- buf
     277             :      | Header   |
     278             :      +----------+ <-- out
     279             :      | Payload  |
     280             :      +----------+ <-- gcm_tag
     281             :      | GCM Tag  |
     282             :      +----------+ <-- buf_end */
     283             : 
     284    19840096 :   uchar * const out     = buf     + hdr_sz;
     285    19840096 :   uchar * const buf_end = buf     + buf_sz;
     286    19840096 :   uchar * const gcm_tag = buf_end - FD_QUIC_CRYPTO_TAG_SZ;
     287    19840096 :   ulong   const gcm_sz  = (ulong)( gcm_tag - out );
     288             : 
     289    19840096 :   fd_aes_gcm_t pkt_cipher[1];
     290    19840096 :   fd_aes_128_gcm_init( pkt_cipher, keys->pkt_key, nonce );
     291             : 
     292    19840096 :   int decrypt_ok =
     293    19840096 :    fd_aes_gcm_decrypt( pkt_cipher,
     294    19840096 :                             out /* ciphertext */, out /* plaintext */,
     295    19840096 :                             gcm_sz,      /* size of plaintext */
     296    19840096 :                             hdr, hdr_sz, /* associated data */
     297    19840096 :                             gcm_tag      /* auth tag */ );
     298    19840096 :   if( FD_UNLIKELY( !decrypt_ok ) ) {
     299     6000135 :    FD_DEBUG( FD_LOG_WARNING(( "fd_aes_gcm_decrypt failed" )) );
     300     6000135 :    return FD_QUIC_FAILED;
     301     6000135 :   }
     302             : 
     303    13839961 :   return FD_QUIC_SUCCESS;
     304    19840096 : }
     305             : 
     306             : 
     307             : int
     308             : fd_quic_crypto_decrypt_hdr(
     309             :     uchar *                        buf,
     310             :     ulong                          buf_sz,
     311             :     ulong                          pkt_number_off,
     312    19840207 :     fd_quic_crypto_keys_t const *  keys ) {
     313             : 
     314             :   /* bounds checks */
     315    19840207 :   if( FD_UNLIKELY( ( buf_sz < FD_QUIC_CRYPTO_TAG_SZ ) |
     316    19840207 :                    ( pkt_number_off >= buf_sz       ) ) ) {
     317           6 :     FD_DEBUG( FD_LOG_WARNING(( "decrypt hdr: bounds checks failed" )) );
     318           6 :     return FD_QUIC_FAILED;
     319           6 :   }
     320             : 
     321    19840201 :   uint          first      = buf[0]; /* first byte */
     322    19840201 :   uint          long_hdr   = first & 0x80u;  /* long header? (this bit is not encrypted) */
     323    19840201 :   ulong         sample_off = pkt_number_off + 4;
     324             : 
     325    19840201 :   if( FD_UNLIKELY( sample_off + FD_QUIC_HP_SAMPLE_SZ > buf_sz ) ) {
     326           6 :     FD_DEBUG( FD_LOG_WARNING(( "decrypt hdr: not enough bytes for a sample" )) );
     327           6 :     return FD_QUIC_FAILED;
     328           6 :   }
     329             : 
     330    19840195 :   uchar * sample = buf + sample_off;
     331             : 
     332             :   /* TODO this is hardcoded to AES-128 */
     333    19840195 :   uchar hp_cipher[16];
     334    19840195 :   fd_aes_key_t ecb[1];
     335    19840195 :   fd_aes_set_encrypt_key( keys->hp_key, 128, ecb );
     336    19840195 :   fd_aes_encrypt( sample, hp_cipher, ecb );
     337             : 
     338             :   /* hp_cipher is mask */
     339    19840195 :   uchar const * mask = hp_cipher;
     340             : 
     341             :   /* undo first byte mask */
     342    19840195 :   first  ^= (uint)mask[0] & ( long_hdr ? 0x0fu : 0x1fu );
     343    19840195 :   buf[0]  = (uchar)first;
     344             : 
     345             :   /* now we can calculate the actual packet number size */
     346    19840195 :   ulong pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
     347    19840195 :   if( pkt_number_off+pkt_number_sz > buf_sz ) {
     348           0 :     FD_DEBUG( FD_LOG_WARNING(( "decrypt hdr: not enough bytes for packet number" )) );
     349           0 :     return FD_QUIC_FAILED;
     350           0 :   }
     351             : 
     352             :   /* undo packet number encryption */
     353    89600543 :   for( ulong j = 0u; j < pkt_number_sz; ++j ) {
     354    69760348 :     buf[ pkt_number_off + j ] ^= mask[ 1u+j ];
     355    69760348 :   }
     356             : 
     357    19840195 :   return FD_QUIC_SUCCESS;
     358    19840195 : }

Generated by: LCOV version 1.14