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: 170 179 95.0 %
Date: 2025-01-08 12:08:44 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             : 
     132             : /* encrypt a packet
     133             : 
     134             :    uses the keys in keys to encrypt the packet "pkt" with header "hdr"
     135             :    (of length pkt_sz, and hdr_sz respectively) into out.
     136             : 
     137             :    out should have enough space to contain the full output with extra space
     138             :    for a full block which depends on the cipher
     139             : 
     140             :    *out_sz is used to determine the amount of buffer space left at *out
     141             :      if enough space is not available, the function fails and returns
     142             :      FD_QUIC_FAILED
     143             :    *out_sz is also set to the number of bytes written into *out at the end
     144             : 
     145             :    args
     146             :      out               the destination for the encrypted output
     147             :      out_sz            a pointer to the size of the buffer (on input) and the size of
     148             :                          the written bytes (on output)
     149             :      hdr               the input header bytes
     150             :      hdr_sz            the size of the header in bytes
     151             :      pkt               the input packet bytes
     152             :      pkt_sz            the size of the packet in bytes (frames after packet number not including MAC tag)
     153             :      keys              a pointer to the keys to use
     154             :      pkt_number        needed to create the nonce used in encryption
     155             :                          likely points to the packet number within "hdr"
     156             :      pkt_number_sz     the size of the packet number in bytes
     157             :      */
     158             : 
     159             : int
     160             : fd_quic_crypto_encrypt(
     161             :     uchar *                        const out,
     162             :     ulong *                        const out_sz,
     163             :     uchar const *                  const hdr,
     164             :     ulong                          const hdr_sz,
     165             :     uchar const *                  const pkt,
     166             :     ulong                          const pkt_sz,
     167             :     fd_quic_crypto_keys_t const *  const pkt_keys,
     168             :     fd_quic_crypto_keys_t const *  const hp_keys,
     169    20357072 :     ulong                          const pkt_number ) {
     170             : 
     171             : 
     172             :   /* ensure we have enough space in the output buffer
     173             :      most space used by cipher:
     174             :        header bytes (just XORed)
     175             :        input bytes (encrypted)
     176             :        tag bytes */
     177             : 
     178             :   /* bound on the bytes needed for cipher output */
     179    20357072 :   ulong cipher_out_bound = hdr_sz + pkt_sz + FD_QUIC_CRYPTO_TAG_SZ;
     180             : 
     181    20357072 :   if( FD_UNLIKELY( *out_sz < cipher_out_bound ) ) {
     182           0 :     FD_DEBUG( FD_LOG_WARNING(( "fd_quic_crypto_encrypt: output buffer not big enough" )) );
     183           0 :     return FD_QUIC_FAILED;
     184           0 :   }
     185             : 
     186    20357072 :   if( FD_UNLIKELY( ( hdr_sz < 4 ) | ( hdr_sz > INT_MAX ) ) ) {
     187           0 :     FD_DEBUG( FD_LOG_WARNING(( "fd_quic_crypto_encrypt: packet header size out of bounds" )) );
     188           0 :     return FD_QUIC_FAILED;
     189           0 :   }
     190             : 
     191             :   /* bounds check */
     192    20357072 :   if( FD_UNLIKELY( pkt_sz > INT_MAX ) ) return FD_QUIC_FAILED;
     193             : 
     194             :   /* copy the header into the output */
     195    20357072 :   fd_memcpy( out, hdr, hdr_sz );
     196             : 
     197             :   /* first byte needed in a couple of places */
     198    20357072 :   uchar first = out[0];
     199    20357072 :   ulong pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
     200    20357072 :   uchar const * pkt_number_ptr = out + hdr_sz - pkt_number_sz;
     201             : 
     202             :   // nonce is quic-iv XORed with packet-number
     203             :   // packet number is 1-4 bytes, so only XOR last pkt_number_sz bytes
     204    20357072 :   uchar nonce[FD_QUIC_NONCE_SZ] = {0};
     205    20357072 :   uint nonce_tmp = FD_QUIC_NONCE_SZ - 4;
     206    20357072 :   uchar const * quic_iv = pkt_keys->iv;
     207    20357072 :   memcpy( nonce, quic_iv, nonce_tmp );
     208   101785360 :   for( uint k = 0; k < 4; ++k ) {
     209    81428288 :     uint j = nonce_tmp + k;
     210    81428288 :     nonce[j] = (uchar)( quic_iv[j] ^ ( (uchar)( (pkt_number>>( (3u - k) * 8u ))&0xFF ) ) );
     211    81428288 :   }
     212             : 
     213             :   // Initial packets cipher uses AEAD_AES_128_GCM with keys derived from the Destination Connection ID field of the
     214             :   // first Initial packet sent by the client; see rfc9001 Section 5.2.
     215             : 
     216    20357072 :   fd_aes_gcm_t pkt_cipher[1];
     217    20357072 :   fd_aes_128_gcm_init( pkt_cipher, pkt_keys->pkt_key, nonce );
     218             : 
     219             :   /* cipher_text is start of encrypted packet bytes, which starts after the header */
     220    20357072 :   uchar * cipher_text = out + hdr_sz;
     221    20357072 :   uchar * tag         = cipher_text + pkt_sz;
     222    20357072 :   uchar * pkt_end     = tag + FD_QUIC_CRYPTO_TAG_SZ;
     223             : 
     224    20357072 :   fd_aes_gcm_encrypt( pkt_cipher, cipher_text, pkt, pkt_sz, hdr, hdr_sz, tag );
     225             : 
     226    20357072 :   *out_sz = (ulong)( pkt_end - out );
     227             : 
     228             :   /* Header protection */
     229             : 
     230             :   /* sample start is defined as 4 bytes after the start of the packet number
     231             :      so shorter packet numbers means sample starts later in the cipher text */
     232    20357072 :   uchar const * sample = pkt_number_ptr + 4;
     233             : 
     234    20357072 :   fd_aes_key_t ecb[1];
     235    20357072 :   fd_aes_set_encrypt_key( hp_keys->hp_key, 128, ecb );
     236    20357072 :   uchar hp_cipher[16];
     237    20357072 :   fd_aes_encrypt( sample, hp_cipher, ecb );
     238             : 
     239             :   /* hp_cipher is mask */
     240    20357072 :   uchar const * mask = hp_cipher;
     241             : 
     242    20357072 :   uchar long_hdr = first & 0x80u; /* long header? */
     243    20357072 :   out[0] ^= (uchar)( mask[0] & ( long_hdr ? 0x0fu : 0x1fu ) );
     244             : 
     245    20357072 :   ulong pkt_number_off = hdr_sz - pkt_number_sz;
     246             : 
     247   101785360 :   for( ulong j = 0; j < pkt_number_sz; ++j ) {
     248    81428288 :     out[pkt_number_off + j] ^= mask[1+j];
     249    81428288 :   }
     250             : 
     251    20357072 :   return FD_QUIC_SUCCESS;
     252    20357072 : }
     253             : 
     254             : int
     255             : fd_quic_crypto_decrypt(
     256             :     uchar *                       buf,
     257             :     ulong                         buf_sz,
     258             :     ulong                         pkt_number_off,
     259             :     ulong                         pkt_number,
     260    20351246 :     fd_quic_crypto_keys_t const * keys ) {
     261             : 
     262    20351246 :   if( FD_UNLIKELY( ( pkt_number_off >= buf_sz      ) |
     263    20351246 :                    ( buf_sz < FD_QUIC_SHORTEST_PKT ) ) ) {
     264         105 :     FD_DEBUG( FD_LOG_WARNING( ( "fd_quic_crypto_decrypt: cipher text buffer too small" ) ) );
     265         105 :     return FD_QUIC_FAILED;
     266         105 :   }
     267             : 
     268             :   /* Derive header size */
     269    20351141 :   uint    first         = buf[0];
     270    20351141 :   ulong   pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
     271    20351141 :   uchar * hdr           = buf;
     272    20351141 :   ulong   hdr_sz        = pkt_number_off + pkt_number_sz;
     273             : 
     274             :   /* calculate nonce for decryption
     275             :      nonce is quic-iv XORed with *reconstructed* packet-number
     276             :      packet number is 1-4 bytes, so only XOR last pkt_number_sz bytes */
     277    20351141 :   uchar nonce[FD_QUIC_NONCE_SZ] = {0};
     278    20351141 :   uint nonce_tmp = FD_QUIC_NONCE_SZ - 4;
     279    20351141 :   uchar const * quic_iv = keys->iv;
     280    20351141 :   fd_memcpy( nonce, quic_iv, nonce_tmp );
     281   101755705 :   for( uint k = 0; k < 4; ++k ) {
     282    81404564 :     uint j = nonce_tmp + k;
     283    81404564 :     nonce[j] = (uchar)( quic_iv[j] ^ ( (uchar)( (pkt_number>>( (3u - k) * 8u ))&0xFF ) ) );
     284    81404564 :   }
     285             : 
     286    20351141 :   if( FD_UNLIKELY( ( buf_sz < hdr_sz ) |
     287    20351141 :                    ( buf_sz < hdr_sz+FD_QUIC_CRYPTO_TAG_SZ ) ) )
     288           3 :     return FD_QUIC_FAILED;
     289             : 
     290             :   /* Derive offsets
     291             : 
     292             :      +----------+ <-- buf
     293             :      | Header   |
     294             :      +----------+ <-- out
     295             :      | Payload  |
     296             :      +----------+ <-- gcm_tag
     297             :      | GCM Tag  |
     298             :      +----------+ <-- buf_end */
     299             : 
     300    20351138 :   uchar * const out     = buf     + hdr_sz;
     301    20351138 :   uchar * const buf_end = buf     + buf_sz;
     302    20351138 :   uchar * const gcm_tag = buf_end - FD_QUIC_CRYPTO_TAG_SZ;
     303    20351138 :   ulong   const gcm_sz  = (ulong)( gcm_tag - out );
     304             : 
     305    20351138 :   fd_aes_gcm_t pkt_cipher[1];
     306    20351138 :   fd_aes_128_gcm_init( pkt_cipher, keys->pkt_key, nonce );
     307             : 
     308    20351138 :   int decrypt_ok =
     309    20351138 :    fd_aes_gcm_decrypt( pkt_cipher,
     310    20351138 :                             out /* ciphertext */, out /* plaintext */,
     311    20351138 :                             gcm_sz,      /* size of plaintext */
     312    20351138 :                             hdr, hdr_sz, /* associated data */
     313    20351138 :                             gcm_tag      /* auth tag */ );
     314    20351138 :   if( FD_UNLIKELY( !decrypt_ok ) ) {
     315     6000135 :    FD_DEBUG( FD_LOG_WARNING(( "fd_aes_gcm_decrypt failed" )) );
     316     6000135 :    return FD_QUIC_FAILED;
     317     6000135 :   }
     318             : 
     319    14351003 :   return FD_QUIC_SUCCESS;
     320    20351138 : }
     321             : 
     322             : 
     323             : int
     324             : fd_quic_crypto_decrypt_hdr(
     325             :     uchar *                        buf,
     326             :     ulong                          buf_sz,
     327             :     ulong                          pkt_number_off,
     328    20351249 :     fd_quic_crypto_keys_t const *  keys ) {
     329             : 
     330             :   /* bounds checks */
     331    20351249 :   if( FD_UNLIKELY( ( buf_sz < FD_QUIC_CRYPTO_TAG_SZ ) |
     332    20351249 :                    ( pkt_number_off >= buf_sz       ) ) ) {
     333           6 :     FD_DEBUG( FD_LOG_WARNING(( "decrypt hdr: bounds checks failed" )) );
     334           6 :     return FD_QUIC_FAILED;
     335           6 :   }
     336             : 
     337    20351243 :   uint          first      = buf[0]; /* first byte */
     338    20351243 :   uint          long_hdr   = first & 0x80u;  /* long header? (this bit is not encrypted) */
     339    20351243 :   ulong         sample_off = pkt_number_off + 4;
     340             : 
     341    20351243 :   if( FD_UNLIKELY( sample_off + FD_QUIC_HP_SAMPLE_SZ > buf_sz ) ) {
     342           6 :     FD_DEBUG( FD_LOG_WARNING(( "decrypt hdr: not enough bytes for a sample" )) );
     343           6 :     return FD_QUIC_FAILED;
     344           6 :   }
     345             : 
     346    20351237 :   uchar * sample = buf + sample_off;
     347             : 
     348             :   /* TODO this is hardcoded to AES-128 */
     349    20351237 :   uchar hp_cipher[16];
     350    20351237 :   fd_aes_key_t ecb[1];
     351    20351237 :   fd_aes_set_encrypt_key( keys->hp_key, 128, ecb );
     352    20351237 :   fd_aes_encrypt( sample, hp_cipher, ecb );
     353             : 
     354             :   /* hp_cipher is mask */
     355    20351237 :   uchar const * mask = hp_cipher;
     356             : 
     357             :   /* undo first byte mask */
     358    20351237 :   first  ^= (uint)mask[0] & ( long_hdr ? 0x0fu : 0x1fu );
     359    20351237 :   buf[0]  = (uchar)first;
     360             : 
     361             :   /* now we can calculate the actual packet number size */
     362    20351237 :   ulong pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
     363    20351237 :   if( pkt_number_off+pkt_number_sz > buf_sz ) {
     364           0 :     FD_DEBUG( FD_LOG_WARNING(( "decrypt hdr: not enough bytes for packet number" )) );
     365           0 :     return FD_QUIC_FAILED;
     366           0 :   }
     367             : 
     368             :   /* undo packet number encryption */
     369    92155765 :   for( ulong j = 0u; j < pkt_number_sz; ++j ) {
     370    71804528 :     buf[ pkt_number_off + j ] ^= mask[ 1u+j ];
     371    71804528 :   }
     372             : 
     373    20351237 :   return FD_QUIC_SUCCESS;
     374    20351237 : }

Generated by: LCOV version 1.14