src/event/quic/ngx_event_quic_openssl_compat.c - nginx-1.31.7 nginx/ @ 939334eff

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Nginx, Inc.
  3. */


  4. #include <ngx_config.h>
  5. #include <ngx_core.h>
  6. #include <ngx_event.h>
  7. #include <ngx_event_quic_connection.h>


  8. #if (NGX_QUIC_OPENSSL_COMPAT)

  9. ‌#define NGX_QUIC_COMPAT_RECORD_SIZE          1024

  10. ‌#define NGX_QUIC_COMPAT_SSL_TP_EXT           0x39

  11. ‌#define NGX_QUIC_COMPAT_CLIENT_HANDSHAKE     "CLIENT_HANDSHAKE_TRAFFIC_SECRET"
  12. ‌#define NGX_QUIC_COMPAT_SERVER_HANDSHAKE     "SERVER_HANDSHAKE_TRAFFIC_SECRET"
  13. ‌#define NGX_QUIC_COMPAT_CLIENT_APPLICATION   "CLIENT_TRAFFIC_SECRET_0"
  14. ‌#define NGX_QUIC_COMPAT_SERVER_APPLICATION   "SERVER_TRAFFIC_SECRET_0"


  15. ‌typedef struct {
  16.     ngx_quic_secret_t             secret;
  17.     ngx_uint_t                    cipher;
  18. ‌} ngx_quic_compat_keys_t;


  19. ‌typedef struct {
  20.     ngx_log_t                    *log;

  21.     u_char                        type;
  22.     ngx_str_t                     payload;
  23.     uint64_t                      number;
  24.     ngx_quic_compat_keys_t       *keys;
  25. ‌} ngx_quic_compat_record_t;


  26. ‌struct ngx_quic_compat_s {
  27.     const SSL_QUIC_METHOD        *method;

  28.     enum ssl_encryption_level_t   write_level;

  29.     uint64_t                      read_record;
  30.     ngx_quic_compat_keys_t        keys;

  31.     ngx_str_t                     tp;
  32.     ngx_str_t                     ctp;
  33. };


  34. static void ngx_quic_compat_keylog_callback(const SSL *ssl, const char *line);
  35. static ngx_int_t ngx_quic_compat_set_encryption_secret(ngx_connection_t *c,
  36.     ngx_quic_compat_keys_t *keys, enum ssl_encryption_level_t level,
  37.     const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len);
  38. static void ngx_quic_compat_cleanup_encryption_secret(void *data);
  39. static int ngx_quic_compat_add_transport_params_callback(SSL *ssl,
  40.     unsigned int ext_type, unsigned int context, const unsigned char **out,
  41.     size_t *outlen, X509 *x, size_t chainidx, int *al, void *add_arg);
  42. static int ngx_quic_compat_parse_transport_params_callback(SSL *ssl,
  43.     unsigned int ext_type, unsigned int context, const unsigned char *in,
  44.     size_t inlen, X509 *x, size_t chainidx, int *al, void *parse_arg);
  45. static void ngx_quic_compat_message_callback(int write_p, int version,
  46.     int content_type, const void *buf, size_t len, SSL *ssl, void *arg);
  47. static size_t ngx_quic_compat_create_header(ngx_quic_compat_record_t *rec,
  48.     u_char *out, ngx_uint_t plain);
  49. static ngx_int_t ngx_quic_compat_create_record(ngx_quic_compat_record_t *rec,
  50.     ngx_str_t *res);


  51. void
  52. ‌ngx_quic_compat_keylog_init(SSL_CTX *ctx)
  53. {
  54.     SSL_CTX_set_keylog_callback(ctx, ngx_quic_compat_keylog_callback);
  55. }


  56. ngx_int_t
  57. ‌ngx_quic_compat_ext_init(ngx_conf_t *cf, SSL_CTX *ctx)
  58. {
  59.     if (SSL_CTX_has_client_custom_ext(ctx, NGX_QUIC_COMPAT_SSL_TP_EXT)) {
  60.         return NGX_OK;
  61.     }

  62.     if (SSL_CTX_add_custom_ext(ctx, NGX_QUIC_COMPAT_SSL_TP_EXT,
  63.                                SSL_EXT_CLIENT_HELLO
  64.                                |SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
  65.                                ngx_quic_compat_add_transport_params_callback,
  66.                                NULL,
  67.                                NULL,
  68.                                ngx_quic_compat_parse_transport_params_callback,
  69.                                NULL)
  70.         == 0)
  71.     {
  72.         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
  73.                       "SSL_CTX_add_custom_ext() failed");
  74.         return NGX_ERROR;
  75.     }

  76.     return NGX_OK;
  77. }


  78. static void
  79. ‌ngx_quic_compat_keylog_callback(const SSL *ssl, const char *line)
  80. {
  81.     u_char                        ch, *p, *start, value;
  82.     size_t                        n;
  83.     ngx_uint_t                    write;
  84.     const SSL_CIPHER             *cipher;
  85.     ngx_quic_compat_t            *com;
  86.     ngx_connection_t             *c;
  87.     ngx_quic_connection_t        *qc;
  88.     enum ssl_encryption_level_t   level;
  89.     u_char                        secret[EVP_MAX_MD_SIZE];

  90.     c = ngx_ssl_get_connection(ssl);
  91.     if (c->type != SOCK_DGRAM) {
  92.         return;
  93.     }

  94.     p = (u_char *) line;

  95.     for (start = p; *p && *p != ' '; p++);

  96.     n = p - start;

  97.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  98.                    "quic compat secret %*s", n, start);

  99.     if (n == sizeof(NGX_QUIC_COMPAT_CLIENT_HANDSHAKE) - 1
  100.         && ngx_strncmp(start, NGX_QUIC_COMPAT_CLIENT_HANDSHAKE, n) == 0)
  101.     {
  102.         level = ssl_encryption_handshake;
  103.         write = 0;

  104.     } else if (n == sizeof(NGX_QUIC_COMPAT_SERVER_HANDSHAKE) - 1
  105.                && ngx_strncmp(start, NGX_QUIC_COMPAT_SERVER_HANDSHAKE, n) == 0)
  106.     {
  107.         level = ssl_encryption_handshake;
  108.         write = 1;

  109.     } else if (n == sizeof(NGX_QUIC_COMPAT_CLIENT_APPLICATION) - 1
  110.                && ngx_strncmp(start, NGX_QUIC_COMPAT_CLIENT_APPLICATION, n)
  111.                   == 0)
  112.     {
  113.         level = ssl_encryption_application;
  114.         write = 0;

  115.     } else if (n == sizeof(NGX_QUIC_COMPAT_SERVER_APPLICATION) - 1
  116.                && ngx_strncmp(start, NGX_QUIC_COMPAT_SERVER_APPLICATION, n)
  117.                    == 0)
  118.     {
  119.         level = ssl_encryption_application;
  120.         write = 1;

  121.     } else {
  122.         return;
  123.     }

  124.     if (*p++ == '\0') {
  125.         return;
  126.     }

  127.     for ( /* void */ ; *p && *p != ' '; p++);

  128.     if (*p++ == '\0') {
  129.         return;
  130.     }

  131.     for (n = 0, start = p; *p; p++) {
  132.         ch = *p;

  133.         if (ch >= '0' && ch <= '9') {
  134.             value = ch - '0';
  135.             goto next;
  136.         }

  137.         ch = (u_char) (ch | 0x20);

  138.         if (ch >= 'a' && ch <= 'f') {
  139.             value = ch - 'a' + 10;
  140.             goto next;
  141.         }

  142.         ngx_log_error(NGX_LOG_EMERG, c->log, 0,
  143.                       "invalid OpenSSL QUIC secret format");

  144.         return;

  145.     next:

  146.         if ((p - start) % 2) {
  147.             secret[n++] += value;

  148.         } else {
  149.             if (n >= EVP_MAX_MD_SIZE) {
  150.                 ngx_log_error(NGX_LOG_EMERG, c->log, 0,
  151.                               "too big OpenSSL QUIC secret");
  152.                 return;
  153.             }

  154.             secret[n] = (value << 4);
  155.         }
  156.     }

  157.     qc = ngx_quic_get_connection(c);
  158.     com = qc->compat;
  159.     cipher = SSL_get_current_cipher(ssl);

  160.     if (write) {
  161.         com->method->set_write_secret((SSL *) ssl, level, cipher, secret, n);
  162.         com->write_level = level;

  163.     } else {
  164.         com->method->set_read_secret((SSL *) ssl, level, cipher, secret, n);
  165.         com->read_record = 0;

  166.         if (ngx_quic_compat_set_encryption_secret(c, &com->keys, level,
  167.                                                   cipher, secret, n)
  168.             != NGX_OK)
  169.         {
  170.             qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
  171.         }
  172.     }

  173.     ngx_explicit_memzero(secret, n);
  174. }


  175. static ngx_int_t
  176. ‌ngx_quic_compat_set_encryption_secret(ngx_connection_t *c,
  177.     ngx_quic_compat_keys_t *keys, enum ssl_encryption_level_t level,
  178.     const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len)
  179. {
  180.     ngx_int_t            key_len;
  181.     ngx_str_t            secret_str;
  182.     ngx_uint_t           i;
  183.     ngx_quic_md_t        key;
  184.     ngx_quic_hkdf_t      seq[2];
  185.     ngx_quic_secret_t   *peer_secret;
  186.     ngx_quic_ciphers_t   ciphers;
  187.     ngx_pool_cleanup_t  *cln;

  188.     peer_secret = &keys->secret;

  189.     keys->cipher = SSL_CIPHER_get_id(cipher);

  190.     key_len = ngx_quic_ciphers(keys->cipher, &ciphers);

  191.     if (key_len == NGX_ERROR) {
  192.         ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "unexpected cipher");
  193.         return NGX_ERROR;
  194.     }

  195.     key.len = key_len;

  196.     peer_secret->iv.len = NGX_QUIC_IV_LEN;

  197.     secret_str.len = secret_len;
  198.     secret_str.data = (u_char *) secret;

  199.     ngx_quic_hkdf_set(&seq[0], "tls13 key", &key, &secret_str);
  200.     ngx_quic_hkdf_set(&seq[1], "tls13 iv", &peer_secret->iv, &secret_str);

  201.     for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) {
  202.         if (ngx_quic_hkdf_expand(&seq[i], ciphers.d, c->log) != NGX_OK) {
  203.             return NGX_ERROR;
  204.         }
  205.     }

  206.     /* register cleanup handler once */

  207.     if (peer_secret->ctx) {
  208.         ngx_quic_crypto_cleanup(peer_secret);

  209.     } else {
  210.         cln = ngx_pool_cleanup_add(c->pool, 0);
  211.         if (cln == NULL) {
  212.             return NGX_ERROR;
  213.         }

  214.         cln->handler = ngx_quic_compat_cleanup_encryption_secret;
  215.         cln->data = peer_secret;
  216.     }

  217.     if (ngx_quic_crypto_init(ciphers.c, peer_secret, &key, 1, c->log)
  218.         == NGX_ERROR)
  219.     {
  220.         return NGX_ERROR;
  221.     }

  222.     ngx_explicit_memzero(key.data, key.len);

  223.     return NGX_OK;
  224. }


  225. static void
  226. ‌ngx_quic_compat_cleanup_encryption_secret(void *data)
  227. {
  228.     ngx_quic_secret_t *secret = data;

  229.     ngx_quic_crypto_cleanup(secret);
  230. }


  231. static int
  232. ‌ngx_quic_compat_add_transport_params_callback(SSL *ssl, unsigned int ext_type,
  233.     unsigned int context, const unsigned char **out, size_t *outlen, X509 *x,
  234.     size_t chainidx, int *al, void *add_arg)
  235. {
  236.     ngx_connection_t       *c;
  237.     ngx_quic_compat_t      *com;
  238.     ngx_quic_connection_t  *qc;

  239.     c = ngx_ssl_get_connection(ssl);
  240.     if (c->type != SOCK_DGRAM) {
  241.         return 0;
  242.     }

  243.     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
  244.                    "quic compat add transport params");

  245.     qc = ngx_quic_get_connection(c);
  246.     com = qc->compat;

  247.     *out = com->tp.data;
  248.     *outlen = com->tp.len;

  249.     return 1;
  250. }


  251. static int
  252. ‌ngx_quic_compat_parse_transport_params_callback(SSL *ssl, unsigned int ext_type,
  253.     unsigned int context, const unsigned char *in, size_t inlen, X509 *x,
  254.     size_t chainidx, int *al, void *parse_arg)
  255. {
  256.     u_char                 *p;
  257.     ngx_connection_t       *c;
  258.     ngx_quic_compat_t      *com;
  259.     ngx_quic_connection_t  *qc;

  260.     c = ngx_ssl_get_connection(ssl);
  261.     if (c->type != SOCK_DGRAM) {
  262.         return 1;
  263.     }

  264.     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
  265.                    "quic compat parse transport params");

  266.     qc = ngx_quic_get_connection(c);
  267.     com = qc->compat;

  268.     p = ngx_pnalloc(c->pool, inlen);
  269.     if (p == NULL) {
  270.         return 0;
  271.     }

  272.     ngx_memcpy(p, in, inlen);

  273.     com->ctp.data = p;
  274.     com->ctp.len = inlen;

  275.     return 1;
  276. }


  277. int
  278. ‌SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method)
  279. {
  280.     BIO                    *rbio, *wbio;
  281.     ngx_connection_t       *c;
  282.     ngx_quic_compat_t      *com;
  283.     ngx_quic_connection_t  *qc;

  284.     c = ngx_ssl_get_connection(ssl);

  285.     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic compat set method");

  286.     qc = ngx_quic_get_connection(c);

  287.     qc->compat = ngx_pcalloc(c->pool, sizeof(ngx_quic_compat_t));
  288.     if (qc->compat == NULL) {
  289.         return 0;
  290.     }

  291.     com = qc->compat;
  292.     com->method = quic_method;

  293.     rbio = BIO_new(BIO_s_mem());
  294.     if (rbio == NULL) {
  295.         return 0;
  296.     }

  297.     wbio = BIO_new(BIO_s_null());
  298.     if (wbio == NULL) {
  299.         BIO_free(rbio);
  300.         return 0;
  301.     }

  302.     SSL_set_bio(ssl, rbio, wbio);

  303.     SSL_set_msg_callback(ssl, ngx_quic_compat_message_callback);

  304.     /* early data is not supported */
  305.     SSL_set_max_early_data(ssl, 0);

  306.     return 1;
  307. }


  308. static void
  309. ‌ngx_quic_compat_message_callback(int write_p, int version, int content_type,
  310.     const void *buf, size_t len, SSL *ssl, void *arg)
  311. {
  312.     ngx_uint_t                    alert;
  313.     ngx_connection_t             *c;
  314.     ngx_quic_compat_t            *com;
  315.     ngx_quic_connection_t        *qc;
  316.     enum ssl_encryption_level_t   level;

  317.     if (!write_p) {
  318.         return;
  319.     }

  320.     c = ngx_ssl_get_connection(ssl);
  321.     qc = ngx_quic_get_connection(c);

  322.     if (qc == NULL) {
  323.         /* closing */
  324.         return;
  325.     }

  326.     com = qc->compat;
  327.     level = com->write_level;

  328.     switch (content_type) {

  329.     case SSL3_RT_HANDSHAKE:
  330.         ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  331.                        "quic compat tx level:%d len:%uz", level, len);

  332.         if (com->method->add_handshake_data(ssl, level, buf, len) != 1) {
  333.             return;
  334.         }

  335.         break;

  336.     case SSL3_RT_ALERT:
  337.         if (len >= 2) {
  338.             alert = ((u_char *) buf)[1];

  339.             ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
  340.                            "quic compat level:%d alert:%ui len:%uz",
  341.                            level, alert, len);

  342.             if (com->method->send_alert(ssl, level, alert) != 1) {
  343.                 return;
  344.             }
  345.         }

  346.         break;
  347.     }

  348.     return;
  349. }


  350. int
  351. ‌SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
  352.     const uint8_t *data, size_t len)
  353. {
  354.     BIO                       *rbio;
  355.     size_t                     n;
  356.     u_char                    *p;
  357.     ngx_str_t                  res;
  358.     ngx_connection_t          *c;
  359.     ngx_quic_compat_t         *com;
  360.     ngx_quic_connection_t     *qc;
  361.     ngx_quic_compat_record_t   rec;
  362.     u_char                     in[NGX_QUIC_COMPAT_RECORD_SIZE + 1];
  363.     u_char                     out[NGX_QUIC_COMPAT_RECORD_SIZE + 1
  364.                                    + SSL3_RT_HEADER_LENGTH
  365.                                    + NGX_QUIC_TAG_LEN];

  366.     c = ngx_ssl_get_connection(ssl);

  367.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  368.                    "quic compat rx level:%d len:%uz", level, len);

  369.     qc = ngx_quic_get_connection(c);
  370.     com = qc->compat;
  371.     rbio = SSL_get_rbio(ssl);

  372.     while (len) {
  373.         ngx_memzero(&rec, sizeof(ngx_quic_compat_record_t));

  374.         rec.type = SSL3_RT_HANDSHAKE;
  375.         rec.log = c->log;
  376.         rec.number = com->read_record++;
  377.         rec.keys = &com->keys;

  378.         if (level == ssl_encryption_initial) {
  379.             n = ngx_min(len, 65535);

  380.             rec.payload.len = n;
  381.             rec.payload.data = (u_char *) data;

  382.             ngx_quic_compat_create_header(&rec, out, 1);

  383.             BIO_write(rbio, out, SSL3_RT_HEADER_LENGTH);
  384.             BIO_write(rbio, data, n);

  385. #if defined(NGX_QUIC_DEBUG_CRYPTO) && defined(NGX_QUIC_DEBUG_PACKETS)
  386.             ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
  387.                            "quic compat record len:%uz %*xs%*xs",
  388.                            n + SSL3_RT_HEADER_LENGTH,
  389.                            (size_t) SSL3_RT_HEADER_LENGTH, out, n, data);
  390. #endif

  391.         } else {
  392.             n = ngx_min(len, NGX_QUIC_COMPAT_RECORD_SIZE);

  393.             p = ngx_cpymem(in, data, n);
  394.             *p++ = SSL3_RT_HANDSHAKE;

  395.             rec.payload.len = p - in;
  396.             rec.payload.data = in;

  397.             res.data = out;

  398.             if (ngx_quic_compat_create_record(&rec, &res) != NGX_OK) {
  399.                 return 0;
  400.             }

  401. #if defined(NGX_QUIC_DEBUG_CRYPTO) && defined(NGX_QUIC_DEBUG_PACKETS)
  402.             ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  403.                            "quic compat record len:%uz %xV", res.len, &res);
  404. #endif

  405.             BIO_write(rbio, res.data, res.len);
  406.         }

  407.         data += n;
  408.         len -= n;
  409.     }

  410.     return 1;
  411. }


  412. static size_t
  413. ‌ngx_quic_compat_create_header(ngx_quic_compat_record_t *rec, u_char *out,
  414.     ngx_uint_t plain)
  415. {
  416.     u_char  type;
  417.     size_t  len;

  418.     len = rec->payload.len;

  419.     if (plain) {
  420.         type = rec->type;

  421.     } else {
  422.         type = SSL3_RT_APPLICATION_DATA;
  423.         len += NGX_QUIC_TAG_LEN;
  424.     }

  425.     out[0] = type;
  426.     out[1] = 0x03;
  427.     out[2] = 0x03;
  428.     out[3] = (len >> 8);
  429.     out[4] = len;

  430.     return 5;
  431. }


  432. static ngx_int_t
  433. ‌ngx_quic_compat_create_record(ngx_quic_compat_record_t *rec, ngx_str_t *res)
  434. {
  435.     ngx_str_t           ad, out;
  436.     ngx_quic_secret_t  *secret;
  437.     u_char              nonce[NGX_QUIC_IV_LEN];

  438.     ad.data = res->data;
  439.     ad.len = ngx_quic_compat_create_header(rec, ad.data, 0);

  440.     out.len = rec->payload.len + NGX_QUIC_TAG_LEN;
  441.     out.data = res->data + ad.len;

  442. #ifdef NGX_QUIC_DEBUG_CRYPTO
  443.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, rec->log, 0,
  444.                    "quic compat ad len:%uz %xV", ad.len, &ad);
  445. #endif

  446.     secret = &rec->keys->secret;

  447.     if (secret->ctx == NULL) {
  448.         return NGX_ERROR;
  449.     }

  450.     ngx_memcpy(nonce, secret->iv.data, secret->iv.len);
  451.     ngx_quic_compute_nonce(nonce, sizeof(nonce), rec->number);

  452.     if (ngx_quic_crypto_seal(secret, &out, nonce, &rec->payload, &ad, rec->log)
  453.         != NGX_OK)
  454.     {
  455.         return NGX_ERROR;
  456.     }

  457.     res->len = ad.len + out.len;

  458.     return NGX_OK;
  459. }


  460. int
  461. ‌SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params,
  462.     size_t params_len)
  463. {
  464.     ngx_connection_t       *c;
  465.     ngx_quic_compat_t      *com;
  466.     ngx_quic_connection_t  *qc;

  467.     c = ngx_ssl_get_connection(ssl);
  468.     qc = ngx_quic_get_connection(c);
  469.     com = qc->compat;

  470.     com->tp.len = params_len;
  471.     com->tp.data = (u_char *) params;

  472.     return 1;
  473. }


  474. void
  475. ‌SSL_get_peer_quic_transport_params(const SSL *ssl, const uint8_t **out_params,
  476.     size_t *out_params_len)
  477. {
  478.     ngx_connection_t       *c;
  479.     ngx_quic_compat_t      *com;
  480.     ngx_quic_connection_t  *qc;

  481.     c = ngx_ssl_get_connection(ssl);
  482.     qc = ngx_quic_get_connection(c);
  483.     com = qc->compat;

  484.     *out_params = com->ctp.data;
  485.     *out_params_len = com->ctp.len;
  486. }

  487. #endif /* NGX_QUIC_OPENSSL_COMPAT */