src/event/quic/ngx_event_quic_openssl_compat.c - nginx

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. ngx_int_t
  52. ngx_quic_compat_init(ngx_conf_t *cf, SSL_CTX *ctx)
  53. {
  54.     SSL_CTX_set_keylog_callback(ctx, ngx_quic_compat_keylog_callback);

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

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

  72.     return NGX_OK;
  73. }


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

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

  90.     p = (u_char *) line;

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

  92.     n = p - start;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  140.         return;

  141.     next:

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

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

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

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

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

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

  162.         if (ngx_quic_compat_set_encryption_secret(c, &com->keys, level,
  163.                                                   cipher, secret, n)
  164.             != NGX_OK)
  165.         {
  166.             qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
  167.         }
  168.     }

  169.     ngx_explicit_memzero(secret, n);
  170. }


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

  184.     peer_secret = &keys->secret;

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

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

  187.     if (key_len == NGX_ERROR) {
  188.         ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "unexpected cipher");
  189.         return NGX_ERROR;
  190.     }

  191.     key.len = key_len;

  192.     peer_secret->iv.len = NGX_QUIC_IV_LEN;

  193.     secret_str.len = secret_len;
  194.     secret_str.data = (u_char *) secret;

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

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

  202.     /* register cleanup handler once */

  203.     if (peer_secret->ctx) {
  204.         ngx_quic_crypto_cleanup(peer_secret);

  205.     } else {
  206.         cln = ngx_pool_cleanup_add(c->pool, 0);
  207.         if (cln == NULL) {
  208.             return NGX_ERROR;
  209.         }

  210.         cln->handler = ngx_quic_compat_cleanup_encryption_secret;
  211.         cln->data = peer_secret;
  212.     }

  213.     if (ngx_quic_crypto_init(ciphers.c, peer_secret, &key, 1, c->log)
  214.         == NGX_ERROR)
  215.     {
  216.         return NGX_ERROR;
  217.     }

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

  219.     return NGX_OK;
  220. }


  221. static void
  222. ngx_quic_compat_cleanup_encryption_secret(void *data)
  223. {
  224.     ngx_quic_secret_t *secret = data;

  225.     ngx_quic_crypto_cleanup(secret);
  226. }


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

  235.     c = ngx_ssl_get_connection(ssl);
  236.     if (c->type != SOCK_DGRAM) {
  237.         return 0;
  238.     }

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

  241.     qc = ngx_quic_get_connection(c);
  242.     com = qc->compat;

  243.     *out = com->tp.data;
  244.     *outlen = com->tp.len;

  245.     return 1;
  246. }


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

  256.     c = ngx_ssl_get_connection(ssl);
  257.     if (c->type != SOCK_DGRAM) {
  258.         return 0;
  259.     }

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

  262.     qc = ngx_quic_get_connection(c);
  263.     com = qc->compat;

  264.     p = ngx_pnalloc(c->pool, inlen);
  265.     if (p == NULL) {
  266.         return 0;
  267.     }

  268.     ngx_memcpy(p, in, inlen);

  269.     com->ctp.data = p;
  270.     com->ctp.len = inlen;

  271.     return 1;
  272. }


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

  280.     c = ngx_ssl_get_connection(ssl);

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

  282.     qc = ngx_quic_get_connection(c);

  283.     qc->compat = ngx_pcalloc(c->pool, sizeof(ngx_quic_compat_t));
  284.     if (qc->compat == NULL) {
  285.         return 0;
  286.     }

  287.     com = qc->compat;
  288.     com->method = quic_method;

  289.     rbio = BIO_new(BIO_s_mem());
  290.     if (rbio == NULL) {
  291.         return 0;
  292.     }

  293.     wbio = BIO_new(BIO_s_null());
  294.     if (wbio == NULL) {
  295.         BIO_free(rbio);
  296.         return 0;
  297.     }

  298.     SSL_set_bio(ssl, rbio, wbio);

  299.     SSL_set_msg_callback(ssl, ngx_quic_compat_message_callback);

  300.     /* early data is not supported */
  301.     SSL_set_max_early_data(ssl, 0);

  302.     return 1;
  303. }


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

  313.     if (!write_p) {
  314.         return;
  315.     }

  316.     c = ngx_ssl_get_connection(ssl);
  317.     qc = ngx_quic_get_connection(c);

  318.     if (qc == NULL) {
  319.         /* closing */
  320.         return;
  321.     }

  322.     com = qc->compat;
  323.     level = com->write_level;

  324.     switch (content_type) {

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

  328.         if (com->method->add_handshake_data(ssl, level, buf, len) != 1) {
  329.             return;
  330.         }

  331.         break;

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

  335.             ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
  336.                            "quic compat level:%d alert:%ui len:%uz",
  337.                            level, alert, len);

  338.             if (com->method->send_alert(ssl, level, alert) != 1) {
  339.                 return;
  340.             }
  341.         }

  342.         break;
  343.     }

  344.     return;
  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,
  364.                    "quic compat rx level:%d len:%uz", 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.         if (level == ssl_encryption_initial) {
  375.             n = ngx_min(len, 65535);

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

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

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

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

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

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

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

  393.             res.data = out;

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

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

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

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

  406.     return 1;
  407. }


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

  414.     len = rec->payload.len;

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

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

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

  426.     return 5;
  427. }


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

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

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

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

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

  443.     if (secret->ctx == NULL) {
  444.         return NGX_ERROR;
  445.     }

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

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

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

  454.     return NGX_OK;
  455. }


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

  463.     c = ngx_ssl_get_connection(ssl);
  464.     qc = ngx_quic_get_connection(c);
  465.     com = qc->compat;

  466.     com->tp.len = params_len;
  467.     com->tp.data = (u_char *) params;

  468.     return 1;
  469. }


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

  477.     c = ngx_ssl_get_connection(ssl);
  478.     qc = ngx_quic_get_connection(c);
  479.     com = qc->compat;

  480.     *out_params = com->ctp.data;
  481.     *out_params_len = com->ctp.len;
  482. }

  483. #endif /* NGX_QUIC_OPENSSL_COMPAT */