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: 175 181 96.7 %
Date: 2024-11-13 11:58:15 Functions: 10 10 100.0 %

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

Generated by: LCOV version 1.14