src/event/quic/ngx_event_quic_openssl_compat.c - nginx source code

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.     enum ssl_encryption_level_t   level;
  26. } ngx_quic_compat_record_t;


  27. struct ngx_quic_compat_s {
  28.     const SSL_QUIC_METHOD        *method;

  29.     enum ssl_encryption_level_t   write_level;

  30.     uint64_t                      read_record;
  31.     ngx_quic_compat_keys_t        keys;

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


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


  52. ngx_int_t
  53. ngx_quic_compat_init(ngx_conf_t *cf, SSL_CTX *ctx)
  54. {
  55.     SSL_CTX_set_keylog_callback(ctx, ngx_quic_compat_keylog_callback);

  56.     if (SSL_CTX_has_client_custom_ext(ctx, NGX_QUIC_COMPAT_SSL_TP_EXT)) {
  57.         return NGX_OK;
  58.     }

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

  73.     return NGX_OK;
  74. }


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

  87.     c = ngx_ssl_get_connection(ssl);
  88.     if (c->type != SOCK_DGRAM) {
  89.         return;
  90.     }

  91.     p = (u_char *) line;

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

  93.     n = p - start;

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

  96.     if (n == sizeof(NGX_QUIC_COMPAT_CLIENT_HANDSHAKE) - 1
  97.         && ngx_strncmp(start, NGX_QUIC_COMPAT_CLIENT_HANDSHAKE, n) == 0)
  98.     {
  99.         level = ssl_encryption_handshake;
  100.         write = 0;

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

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

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

  118.     } else {
  119.         return;
  120.     }

  121.     if (*p++ == '\0') {
  122.         return;
  123.     }

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

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

  128.     for (n = 0, start = p; *p; p++) {
  129.         ch = *p;

  130.         if (ch >= '0' && ch <= '9') {
  131.             value = ch - '0';
  132.             goto next;
  133.         }

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

  135.         if (ch >= 'a' && ch <= 'f') {
  136.             value = ch - 'a' + 10;
  137.             goto next;
  138.         }

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

  141.         return;

  142.     next:

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

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

  151.             secret[n] = (value << 4);
  152.         }
  153.     }

  154.     qc = ngx_quic_get_connection(c);
  155.     com = qc->compat;
  156.     cipher = SSL_get_current_cipher(ssl);

  157.     if (write) {
  158.         com->method->set_write_secret((SSL *) ssl, level, cipher, secret, n);
  159.         com->write_level = level;

  160.     } else {
  161.         com->method->set_read_secret((SSL *) ssl, level, cipher, secret, n);
  162.         com->read_record = 0;

  163.         (void) ngx_quic_compat_set_encryption_secret(c, &com->keys, level,
  164.                                                      cipher, secret, n);
  165.     }

  166.     ngx_explicit_memzero(secret, n);
  167. }


  168. static ngx_int_t
  169. ngx_quic_compat_set_encryption_secret(ngx_connection_t *c,
  170.     ngx_quic_compat_keys_t *keys, enum ssl_encryption_level_t level,
  171.     const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len)
  172. {
  173.     ngx_int_t            key_len;
  174.     ngx_str_t            secret_str;
  175.     ngx_uint_t           i;
  176.     ngx_quic_md_t        key;
  177.     ngx_quic_hkdf_t      seq[2];
  178.     ngx_quic_secret_t   *peer_secret;
  179.     ngx_quic_ciphers_t   ciphers;
  180.     ngx_pool_cleanup_t  *cln;

  181.     peer_secret = &keys->secret;

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

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

  184.     if (key_len == NGX_ERROR) {
  185.         ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "unexpected cipher");
  186.         return NGX_ERROR;
  187.     }

  188.     key.len = key_len;

  189.     peer_secret->iv.len = NGX_QUIC_IV_LEN;

  190.     secret_str.len = secret_len;
  191.     secret_str.data = (u_char *) secret;

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

  194.     for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) {
  195.         if (ngx_quic_hkdf_expand(&seq[i], ciphers.d, c->log) != NGX_OK) {
  196.             return NGX_ERROR;
  197.         }
  198.     }

  199.     /* register cleanup handler once */

  200.     if (peer_secret->ctx) {
  201.         ngx_quic_crypto_cleanup(peer_secret);

  202.     } else {
  203.         cln = ngx_pool_cleanup_add(c->pool, 0);
  204.         if (cln == NULL) {
  205.             return NGX_ERROR;
  206.         }

  207.         cln->handler = ngx_quic_compat_cleanup_encryption_secret;
  208.         cln->data = peer_secret;
  209.     }

  210.     if (ngx_quic_crypto_init(ciphers.c, peer_secret, &key, 1, c->log)
  211.         == NGX_ERROR)
  212.     {
  213.         return NGX_ERROR;
  214.     }

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

  216.     return NGX_OK;
  217. }


  218. static void
  219. ngx_quic_compat_cleanup_encryption_secret(void *data)
  220. {
  221.     ngx_quic_secret_t *secret = data;

  222.     ngx_quic_crypto_cleanup(secret);
  223. }


  224. static int
  225. ngx_quic_compat_add_transport_params_callback(SSL *ssl, unsigned int ext_type,
  226.     unsigned int context, const unsigned char **out, size_t *outlen, X509 *x,
  227.     size_t chainidx, int *al, void *add_arg)
  228. {
  229.     ngx_connection_t       *c;
  230.     ngx_quic_compat_t      *com;
  231.     ngx_quic_connection_t  *qc;

  232.     c = ngx_ssl_get_connection(ssl);
  233.     if (c->type != SOCK_DGRAM) {
  234.         return 0;
  235.     }

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

  238.     qc = ngx_quic_get_connection(c);
  239.     com = qc->compat;

  240.     *out = com->tp.data;
  241.     *outlen = com->tp.len;

  242.     return 1;
  243. }


  244. static int
  245. ngx_quic_compat_parse_transport_params_callback(SSL *ssl, unsigned int ext_type,
  246.     unsigned int context, const unsigned char *in, size_t inlen, X509 *x,
  247.     size_t chainidx, int *al, void *parse_arg)
  248. {
  249.     u_char                 *p;
  250.     ngx_connection_t       *c;
  251.     ngx_quic_compat_t      *com;
  252.     ngx_quic_connection_t  *qc;

  253.     c = ngx_ssl_get_connection(ssl);
  254.     if (c->type != SOCK_DGRAM) {
  255.         return 0;
  256.     }

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

  259.     qc = ngx_quic_get_connection(c);
  260.     com = qc->compat;

  261.     p = ngx_pnalloc(c->pool, inlen);
  262.     if (p == NULL) {
  263.         return 0;
  264.     }

  265.     ngx_memcpy(p, in, inlen);

  266.     com->ctp.data = p;
  267.     com->ctp.len = inlen;

  268.     return 1;
  269. }


  270. int
  271. SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method)
  272. {
  273.     BIO                    *rbio, *wbio;
  274.     ngx_connection_t       *c;
  275.     ngx_quic_compat_t      *com;
  276.     ngx_quic_connection_t  *qc;

  277.     c = ngx_ssl_get_connection(ssl);

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

  279.     qc = ngx_quic_get_connection(c);

  280.     qc->compat = ngx_pcalloc(c->pool, sizeof(ngx_quic_compat_t));
  281.     if (qc->compat == NULL) {
  282.         return 0;
  283.     }

  284.     com = qc->compat;
  285.     com->method = quic_method;

  286.     rbio = BIO_new(BIO_s_mem());
  287.     if (rbio == NULL) {
  288.         return 0;
  289.     }

  290.     wbio = BIO_new(BIO_s_null());
  291.     if (wbio == NULL) {
  292.         BIO_free(rbio);
  293.         return 0;
  294.     }

  295.     SSL_set_bio(ssl, rbio, wbio);

  296.     SSL_set_msg_callback(ssl, ngx_quic_compat_message_callback);

  297.     /* early data is not supported */
  298.     SSL_set_max_early_data(ssl, 0);

  299.     return 1;
  300. }


  301. static void
  302. ngx_quic_compat_message_callback(int write_p, int version, int content_type,
  303.     const void *buf, size_t len, SSL *ssl, void *arg)
  304. {
  305.     ngx_uint_t                    alert;
  306.     ngx_connection_t             *c;
  307.     ngx_quic_compat_t            *com;
  308.     ngx_quic_connection_t        *qc;
  309.     enum ssl_encryption_level_t   level;

  310.     if (!write_p) {
  311.         return;
  312.     }

  313.     c = ngx_ssl_get_connection(ssl);
  314.     qc = ngx_quic_get_connection(c);

  315.     if (qc == NULL) {
  316.         /* closing */
  317.         return;
  318.     }

  319.     com = qc->compat;
  320.     level = com->write_level;

  321.     switch (content_type) {

  322.     case SSL3_RT_HANDSHAKE:
  323.         ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  324.                        "quic compat tx %s len:%uz ",
  325.                        ngx_quic_level_name(level), len);

  326.         if (com->method->add_handshake_data(ssl, level, buf, len) != 1) {
  327.             goto failed;
  328.         }

  329.         break;

  330.     case SSL3_RT_ALERT:
  331.         if (len >= 2) {
  332.             alert = ((u_char *) buf)[1];

  333.             ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
  334.                            "quic compat %s alert:%ui len:%uz ",
  335.                            ngx_quic_level_name(level), alert, len);

  336.             if (com->method->send_alert(ssl, level, alert) != 1) {
  337.                 goto failed;
  338.             }
  339.         }

  340.         break;
  341.     }

  342.     return;

  343. failed:

  344.     ngx_post_event(&qc->close, &ngx_posted_events);
  345. }


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

  362.     c = ngx_ssl_get_connection(ssl);

  363.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic compat rx %s len:%uz",
  364.                    ngx_quic_level_name(level), len);

  365.     qc = ngx_quic_get_connection(c);
  366.     com = qc->compat;
  367.     rbio = SSL_get_rbio(ssl);

  368.     while (len) {
  369.         ngx_memzero(&rec, sizeof(ngx_quic_compat_record_t));

  370.         rec.type = SSL3_RT_HANDSHAKE;
  371.         rec.log = c->log;
  372.         rec.number = com->read_record++;
  373.         rec.keys = &com->keys;
  374.         rec.level = level;

  375.         if (level == ssl_encryption_initial) {
  376.             n = ngx_min(len, 65535);

  377.             rec.payload.len = n;
  378.             rec.payload.data = (u_char *) data;

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

  380.             BIO_write(rbio, out, SSL3_RT_HEADER_LENGTH);
  381.             BIO_write(rbio, data, n);

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

  388.         } else {
  389.             n = ngx_min(len, NGX_QUIC_COMPAT_RECORD_SIZE);

  390.             p = ngx_cpymem(in, data, n);
  391.             *p++ = SSL3_RT_HANDSHAKE;

  392.             rec.payload.len = p - in;
  393.             rec.payload.data = in;

  394.             res.data = out;

  395.             if (ngx_quic_compat_create_record(&rec, &res) != NGX_OK) {
  396.                 return 0;
  397.             }

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

  402.             BIO_write(rbio, res.data, res.len);
  403.         }

  404.         data += n;
  405.         len -= n;
  406.     }

  407.     return 1;
  408. }


  409. static size_t
  410. ngx_quic_compat_create_header(ngx_quic_compat_record_t *rec, u_char *out,
  411.     ngx_uint_t plain)
  412. {
  413.     u_char  type;
  414.     size_t  len;

  415.     len = rec->payload.len;

  416.     if (plain) {
  417.         type = rec->type;

  418.     } else {
  419.         type = SSL3_RT_APPLICATION_DATA;
  420.         len += NGX_QUIC_TAG_LEN;
  421.     }

  422.     out[0] = type;
  423.     out[1] = 0x03;
  424.     out[2] = 0x03;
  425.     out[3] = (len >> 8);
  426.     out[4] = len;

  427.     return 5;
  428. }


  429. static ngx_int_t
  430. ngx_quic_compat_create_record(ngx_quic_compat_record_t *rec, ngx_str_t *res)
  431. {
  432.     ngx_str_t           ad, out;
  433.     ngx_quic_secret_t  *secret;
  434.     u_char              nonce[NGX_QUIC_IV_LEN];

  435.     ad.data = res->data;
  436.     ad.len = ngx_quic_compat_create_header(rec, ad.data, 0);

  437.     out.len = rec->payload.len + NGX_QUIC_TAG_LEN;
  438.     out.data = res->data + ad.len;

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

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

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

  446.     if (ngx_quic_crypto_seal(secret, &out, nonce, &rec->payload, &ad, rec->log)
  447.         != NGX_OK)
  448.     {
  449.         return NGX_ERROR;
  450.     }

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

  452.     return NGX_OK;
  453. }


  454. int
  455. SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params,
  456.     size_t params_len)
  457. {
  458.     ngx_connection_t       *c;
  459.     ngx_quic_compat_t      *com;
  460.     ngx_quic_connection_t  *qc;

  461.     c = ngx_ssl_get_connection(ssl);
  462.     qc = ngx_quic_get_connection(c);
  463.     com = qc->compat;

  464.     com->tp.len = params_len;
  465.     com->tp.data = (u_char *) params;

  466.     return 1;
  467. }


  468. void
  469. SSL_get_peer_quic_transport_params(const SSL *ssl, const uint8_t **out_params,
  470.     size_t *out_params_len)
  471. {
  472.     ngx_connection_t       *c;
  473.     ngx_quic_compat_t      *com;
  474.     ngx_quic_connection_t  *qc;

  475.     c = ngx_ssl_get_connection(ssl);
  476.     qc = ngx_quic_get_connection(c);
  477.     com = qc->compat;

  478.     *out_params = com->ctp.data;
  479.     *out_params_len = com->ctp.len;
  480. }

  481. #endif /* NGX_QUIC_OPENSSL_COMPAT */