# HG changeset patch # User Sergey Kandaurov # Date 1582884591 -10800 # Node ID 4daf03d2bd0ab4315d8cdfa64f10c689645426de # Parent 53a5cdbe500cdc95afb286f11c170f2692311788 OpenSSL compatibility. diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -97,11 +97,15 @@ quic_set_encryption_secrets(ngx_ssl_conn enum ssl_encryption_level_t level, const uint8_t *read_secret, const uint8_t *write_secret, size_t secret_len) { + u_char *name; size_t *rlen, *wlen; uint8_t **rsec, **wsec; - const char *name; const EVP_MD *digest; - const EVP_AEAD *aead; +#ifdef OPENSSL_IS_BORINGSSL + const EVP_AEAD *evp; +#else + const EVP_CIPHER *evp; +#endif ngx_connection_t *c; c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); @@ -125,24 +129,38 @@ quic_set_encryption_secrets(ngx_ssl_conn } #endif - name = SSL_get_cipher(ssl_conn); - - if (OPENSSL_strcasecmp(name, "TLS_AES_128_GCM_SHA256") == 0) { - aead = EVP_aead_aes_128_gcm(); + name = (u_char *) SSL_get_cipher(ssl_conn); + + if (ngx_strcasecmp(name, (u_char *) "TLS_AES_128_GCM_SHA256") == 0 + || ngx_strcasecmp(name, (u_char *) "(NONE)") == 0) + { +#ifdef OPENSSL_IS_BORINGSSL + evp = EVP_aead_aes_128_gcm(); +#else + evp = EVP_aes_128_gcm(); +#endif digest = EVP_sha256(); - } else if (OPENSSL_strcasecmp(name, "TLS_AES_256_GCM_SHA384") == 0) { - aead = EVP_aead_aes_256_gcm(); + } else if (ngx_strcasecmp(name, (u_char *) "TLS_AES_256_GCM_SHA384") == 0) { +#ifdef OPENSSL_IS_BORINGSSL + evp = EVP_aead_aes_256_gcm(); +#else + evp = EVP_aes_256_gcm(); +#endif digest = EVP_sha384(); } else { return 0; } - size_t hkdfl_len; + size_t hkdfl_len, llen; uint8_t hkdfl[20]; uint8_t *p; const char *label; +#if (NGX_DEBUG) + u_char buf[512]; + size_t m; +#endif ngx_str_t *client_key, *client_iv, *client_hp; ngx_str_t *server_key, *server_iv, *server_hp; @@ -203,157 +221,229 @@ quic_set_encryption_secrets(ngx_ssl_conn // client keys - client_key->len = EVP_AEAD_key_length(aead); +#ifdef OPENSSL_IS_BORINGSSL + client_key->len = EVP_AEAD_key_length(evp); +#else + client_key->len = EVP_CIPHER_key_length(evp); +#endif client_key->data = ngx_pnalloc(c->pool, client_key->len); if (client_key->data == NULL) { return 0; } label = "tls13 quic key"; - hkdfl_len = 2 + 1 + sizeof(label) - 1 + 1; + llen = sizeof("tls13 quic key") - 1; + hkdfl_len = 2 + 1 + llen + 1; hkdfl[0] = client_key->len / 256; hkdfl[1] = client_key->len % 256; - hkdfl[2] = sizeof(label) - 1; - p = ngx_cpymem(&hkdfl[3], label, sizeof(label) - 1); + hkdfl[2] = llen; + p = ngx_cpymem(&hkdfl[3], label, llen); *p = '\0'; - if (HKDF_expand(client_key->data, client_key->len, - digest, *rsec, *rlen, - hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(client_key->data, client_key->len, + digest, *rsec, *rlen, hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, c->log, 0, - "HKDF_expand(client_key) failed"); + "ngx_hkdf_expand(client_key) failed"); return 0; } - - client_iv->len = EVP_AEAD_nonce_length(aead); + m = ngx_hex_dump(buf, client_key->data, client_key->len) - buf; + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "quic client key: %*s, len: %uz, level: %d", + m, buf, client_key->len, level); + m = ngx_hex_dump(buf, hkdfl, hkdfl_len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "hkdf: %*s, len: %uz", m, buf, hkdfl_len); + + +#ifdef OPENSSL_IS_BORINGSSL + client_iv->len = EVP_AEAD_nonce_length(evp); +#else + client_iv->len = EVP_CIPHER_iv_length(evp); +#endif client_iv->data = ngx_pnalloc(c->pool, client_iv->len); if (client_iv->data == NULL) { return 0; } label = "tls13 quic iv"; - hkdfl_len = 2 + 1 + sizeof(label) - 1 + 1; + llen = sizeof("tls13 quic iv") - 1; + hkdfl_len = 2 + 1 + llen + 1; hkdfl[0] = client_iv->len / 256; hkdfl[1] = client_iv->len % 256; - hkdfl[2] = sizeof(label) - 1; - p = ngx_cpymem(&hkdfl[3], label, sizeof(label) - 1); + hkdfl[2] = llen; + p = ngx_cpymem(&hkdfl[3], label, llen); *p = '\0'; - if (HKDF_expand(client_iv->data, client_iv->len, - digest, *rsec, *rlen, - hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(client_iv->data, client_iv->len, + digest, *rsec, *rlen, hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, c->log, 0, - "HKDF_expand(client_iv) failed"); + "ngx_hkdf_expand(client_iv) failed"); return 0; } - - client_hp->len = EVP_AEAD_key_length(aead); + m = ngx_hex_dump(buf, client_iv->data, client_iv->len) - buf; + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "quic client iv: %*s, len: %uz, level: %d", + m, buf, client_iv->len, level); + m = ngx_hex_dump(buf, hkdfl, hkdfl_len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "hkdf: %*s, len: %uz", m, buf, hkdfl_len); + + +#ifdef OPENSSL_IS_BORINGSSL + client_hp->len = EVP_AEAD_key_length(evp); +#else + client_hp->len = EVP_CIPHER_key_length(evp); +#endif client_hp->data = ngx_pnalloc(c->pool, client_hp->len); if (client_hp->data == NULL) { return 0; } label = "tls13 quic hp"; - hkdfl_len = 2 + 1 + sizeof(label) - 1 + 1; + llen = sizeof("tls13 quic hp") - 1; + hkdfl_len = 2 + 1 + llen + 1; hkdfl[0] = client_hp->len / 256; hkdfl[1] = client_hp->len % 256; - hkdfl[2] = sizeof(label) - 1; - p = ngx_cpymem(&hkdfl[3], label, sizeof(label) - 1); + hkdfl[2] = llen; + p = ngx_cpymem(&hkdfl[3], label, llen); *p = '\0'; - if (HKDF_expand(client_hp->data, client_hp->len, - digest, *rsec, *rlen, - hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(client_hp->data, client_hp->len, + digest, *rsec, *rlen, hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, c->log, 0, - "HKDF_expand(client_hp) failed"); + "ngx_hkdf_expand(client_hp) failed"); return 0; } + m = ngx_hex_dump(buf, client_hp->data, client_hp->len) - buf; + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "quic client hp: %*s, len: %uz, level: %d", + m, buf, client_hp->len, level); + m = ngx_hex_dump(buf, hkdfl, hkdfl_len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "hkdf: %*s, len: %uz", m, buf, hkdfl_len); + // server keys - server_key->len = EVP_AEAD_key_length(aead); +#ifdef OPENSSL_IS_BORINGSSL + server_key->len = EVP_AEAD_key_length(evp); +#else + server_key->len = EVP_CIPHER_key_length(evp); +#endif server_key->data = ngx_pnalloc(c->pool, server_key->len); if (server_key->data == NULL) { return 0; } label = "tls13 quic key"; - hkdfl_len = 2 + 1 + sizeof(label) - 1 + 1; + llen = sizeof("tls13 quic key") - 1; + hkdfl_len = 2 + 1 + llen + 1; hkdfl[0] = server_key->len / 256; hkdfl[1] = server_key->len % 256; - hkdfl[2] = sizeof(label) - 1; - p = ngx_cpymem(&hkdfl[3], label, sizeof(label) - 1); + hkdfl[2] = llen; + p = ngx_cpymem(&hkdfl[3], label, llen); *p = '\0'; - if (HKDF_expand(server_key->data, server_key->len, - digest, *wsec, *wlen, - hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(server_key->data, server_key->len, + digest, *wsec, *wlen, hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, c->log, 0, - "HKDF_expand(server_key) failed"); + "ngx_hkdf_expand(server_key) failed"); return 0; } - - server_iv->len = EVP_AEAD_nonce_length(aead); + m = ngx_hex_dump(buf, server_key->data, server_key->len) - buf; + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "quic server key: %*s, len: %uz, level: %d", + m, buf, server_key->len, level); + m = ngx_hex_dump(buf, hkdfl, hkdfl_len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "hkdf: %*s, len: %uz", m, buf, hkdfl_len); + + +#ifdef OPENSSL_IS_BORINGSSL + server_iv->len = EVP_AEAD_nonce_length(evp); +#else + server_iv->len = EVP_CIPHER_iv_length(evp); +#endif server_iv->data = ngx_pnalloc(c->pool, server_iv->len); if (server_iv->data == NULL) { return 0; } label = "tls13 quic iv"; - hkdfl_len = 2 + 1 + sizeof(label) - 1 + 1; + llen = sizeof("tls13 quic iv") - 1; + hkdfl_len = 2 + 1 + llen + 1; hkdfl[0] = server_iv->len / 256; hkdfl[1] = server_iv->len % 256; - hkdfl[2] = sizeof(label) - 1; - p = ngx_cpymem(&hkdfl[3], label, sizeof(label) - 1); + hkdfl[2] = llen; + p = ngx_cpymem(&hkdfl[3], label, llen); *p = '\0'; - if (HKDF_expand(server_iv->data, server_iv->len, - digest, *wsec, *wlen, - hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(server_iv->data, server_iv->len, + digest, *wsec, *wlen, hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, c->log, 0, - "HKDF_expand(server_iv) failed"); + "ngx_hkdf_expand(server_iv) failed"); return 0; } - - server_hp->len = EVP_AEAD_key_length(aead); + m = ngx_hex_dump(buf, server_iv->data, server_iv->len) - buf; + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "quic server iv: %*s, len: %uz, level: %d", + m, buf, server_iv->len, level); + m = ngx_hex_dump(buf, hkdfl, hkdfl_len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "hkdf: %*s, len: %uz", m, buf, hkdfl_len); + + +#ifdef OPENSSL_IS_BORINGSSL + server_hp->len = EVP_AEAD_key_length(evp); +#else + server_hp->len = EVP_CIPHER_key_length(evp); +#endif server_hp->data = ngx_pnalloc(c->pool, server_hp->len); if (server_hp->data == NULL) { return 0; } label = "tls13 quic hp"; - hkdfl_len = 2 + 1 + sizeof(label) - 1 + 1; + llen = sizeof("tls13 quic hp") - 1; + hkdfl_len = 2 + 1 + llen + 1; hkdfl[0] = server_hp->len / 256; hkdfl[1] = server_hp->len % 256; - hkdfl[2] = sizeof(label) - 1; - p = ngx_cpymem(&hkdfl[3], label, sizeof(label) - 1); + hkdfl[2] = llen; + p = ngx_cpymem(&hkdfl[3], label, llen); *p = '\0'; - if (HKDF_expand(server_hp->data, server_hp->len, - digest, *wsec, *wlen, - hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(server_hp->data, server_hp->len, + digest, *wsec, *wlen, hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, c->log, 0, - "HKDF_expand(server_hp) failed"); + "ngx_hkdf_expand(server_hp) failed"); return 0; } + m = ngx_hex_dump(buf, server_hp->data, server_hp->len) - buf; + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "quic server hp: %*s, len: %uz, level: %d", + m, buf, server_hp->len, level); + m = ngx_hex_dump(buf, hkdfl, hkdfl_len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "hkdf: %*s, len: %uz", m, buf, hkdfl_len); + return 1; } @@ -362,14 +452,22 @@ static int quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level, const uint8_t *data, size_t len) { - u_char buf[512], *p, *cipher, *clear, *ad; + u_char buf[512], *p, *ciphertext, *clear, *ad, *name; size_t ad_len, clear_len; ngx_int_t m; ngx_str_t *server_key, *server_iv, *server_hp; +#ifdef OPENSSL_IS_BORINGSSL + const EVP_AEAD *cipher; +#else + const EVP_CIPHER *cipher; +#endif ngx_connection_t *c; ngx_quic_connection_t *qc; c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + + ngx_ssl_handshake_log(c); + qc = c->quic; switch (level) { @@ -413,7 +511,7 @@ quic_add_handshake_data(ngx_ssl_conn_t * ngx_quic_build_int(&p, 0); clear_len = p - clear; - size_t cipher_len = clear_len + 16 /*expansion*/; + size_t ciphertext_len = clear_len + 16 /*expansion*/; ad = ngx_alloc(346 /*max header*/, c->log); if (ad == 0) { @@ -434,8 +532,10 @@ quic_add_handshake_data(ngx_ssl_conn_t * p = ngx_cpymem(p, qc->scid.data, qc->scid.len); *p++ = qc->dcid.len; p = ngx_cpymem(p, qc->dcid.data, qc->dcid.len); - ngx_quic_build_int(&p, 0); // token length - ngx_quic_build_int(&p, cipher_len); // length + if (level == ssl_encryption_initial) { + ngx_quic_build_int(&p, 0); // token length + } + ngx_quic_build_int(&p, ciphertext_len + 1); // length (inc. pnl) u_char *pnp = p; *p++ = 0; // packet number 0 @@ -446,19 +546,43 @@ quic_add_handshake_data(ngx_ssl_conn_t * "quic_add_handshake_data ad: %*s, len: %uz", m, buf, ad_len); - EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(EVP_aead_aes_128_gcm(), + + name = (u_char *) SSL_get_cipher(ssl_conn); + + if (ngx_strcasecmp(name, (u_char *) "TLS_AES_128_GCM_SHA256") == 0 + || ngx_strcasecmp(name, (u_char *) "(NONE)") == 0) + { +#ifdef OPENSSL_IS_BORINGSSL + cipher = EVP_aead_aes_128_gcm(); +#else + cipher = EVP_aes_128_gcm(); +#endif + + } else if (ngx_strcasecmp(name, (u_char *) "TLS_AES_256_GCM_SHA384") == 0) { +#ifdef OPENSSL_IS_BORINGSSL + cipher = EVP_aead_aes_256_gcm(); +#else + cipher = EVP_aes_256_gcm(); +#endif + + } else { + return 0; + } + + + ciphertext = ngx_alloc(ciphertext_len, c->log); + if (ciphertext == 0) { + return 0; + } + +#ifdef OPENSSL_IS_BORINGSSL + size_t out_len; + EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(cipher, server_key->data, server_key->len, EVP_AEAD_DEFAULT_TAG_LENGTH); - cipher = ngx_alloc(cipher_len, c->log); - if (cipher == 0) { - return 0; - } - - size_t out_len; - - if (EVP_AEAD_CTX_seal(aead, cipher, &out_len, cipher_len, + if (EVP_AEAD_CTX_seal(aead, ciphertext, &out_len, ciphertext_len, server_iv->data, server_iv->len, clear, clear_len, ad, ad_len) != 1) @@ -470,9 +594,78 @@ quic_add_handshake_data(ngx_ssl_conn_t * } EVP_AEAD_CTX_free(aead); +#else + int out_len; + EVP_CIPHER_CTX *aead; + + aead = EVP_CIPHER_CTX_new(); + if (aead == NULL) { + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "EVP_CIPHER_CTX_new() failed"); + return 0; + } + + if (EVP_EncryptInit_ex(aead, cipher, NULL, NULL, NULL) != 1) { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "EVP_EncryptInit_ex() failed"); + return 0; + } + + if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_IVLEN, server_iv->len, NULL) + == 0) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, + "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_IVLEN) failed"); + return 0; + } + + if (EVP_EncryptInit_ex(aead, NULL, NULL, server_key->data, server_iv->data) + != 1) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "EVP_EncryptInit_ex() failed"); + return 0; + } + + if (EVP_EncryptUpdate(aead, NULL, &out_len, ad, ad_len) != 1) { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "EVP_EncryptUpdate() failed"); + return 0; + } + + if (EVP_EncryptUpdate(aead, ciphertext, &out_len, clear, clear_len) != 1) { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "EVP_EncryptUpdate() failed"); + return 0; + } + + ciphertext_len = out_len; + + if (EVP_EncryptFinal_ex(aead, ciphertext + out_len, &out_len) <= 0) { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "EVP_EncryptFinal_ex failed"); + return 0; + } + + ciphertext_len += out_len; + + if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_GET_TAG, EVP_GCM_TLS_TAG_LEN, + ciphertext + clear_len) + == 0) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, + "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_GET_TAG) failed"); + return 0; + } + + EVP_CIPHER_CTX_free(aead); + + out_len = ciphertext_len + EVP_GCM_TLS_TAG_LEN; +#endif EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); - u_char *sample = cipher + 3; // pnl=0 + u_char *sample = ciphertext + 3; // pnl=0 uint8_t mask[16]; int outlen; @@ -494,11 +687,27 @@ quic_add_handshake_data(ngx_ssl_conn_t * EVP_CIPHER_CTX_free(ctx); + m = ngx_hex_dump(buf, (u_char *) sample, 16) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic_add_handshake_data sample: %*s, len: %uz", + m, buf, 16); + + m = ngx_hex_dump(buf, (u_char *) mask, 16) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic_add_handshake_data mask: %*s, len: %uz", + m, buf, 16); + + m = ngx_hex_dump(buf, (u_char *) server_hp->data, 16) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic_add_handshake_data hp_key: %*s, len: %uz", + m, buf, 16); + // header protection, pnl = 0 ad[0] ^= mask[0] & 0x0f; *pnp ^= mask[1]; -printf("cipher_len %ld out_len %ld ad_len %ld\n", cipher_len, out_len, ad_len); +printf("clear_len %ld ciphertext_len %ld out_len %ld ad_len %ld\n", +clear_len, ciphertext_len, (size_t) out_len, ad_len); u_char *packet = ngx_alloc(ad_len + out_len, c->log); if (packet == 0) { @@ -506,12 +715,12 @@ printf("cipher_len %ld out_len %ld ad_le } p = ngx_cpymem(packet, ad, ad_len); - p = ngx_cpymem(p, cipher, out_len); - - m = ngx_hex_dump(buf, (u_char *) packet, p - packet) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic_add_handshake_data packet: %*s, len: %uz", - m, buf, p - packet); + p = ngx_cpymem(p, ciphertext, out_len); + + m = ngx_hex_dump(buf, (u_char *) packet, ngx_min(256, p - packet)) - buf; + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic_add_handshake_data packet: %*s%s, len: %uz", + m, buf, len < 512 ? "" : "...", p - packet); c->send(c, packet, p - packet); diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -23,7 +23,11 @@ #include #endif #include +#ifdef OPENSSL_IS_BORINGSSL #include +#else +#include +#endif #include #ifndef OPENSSL_NO_OCSP #include diff --git a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_quic.c @@ -0,0 +1,165 @@ +#include +#include +#include + + +uint64_t +ngx_quic_parse_int(u_char **pos) +{ + u_char *p; + uint64_t value; + ngx_uint_t len; + + p = *pos; + len = 1 << ((*p & 0xc0) >> 6); + value = *p++ & 0x3f; + + while (--len) { + value = (value << 8) + *p++; + } + + *pos = p; + return value; +} + + +void +ngx_quic_build_int(u_char **pos, uint64_t value) +{ + u_char *p; + ngx_uint_t len;//, len2; + + p = *pos; + len = 0; + + while (value >> ((1 << len) * 8 - 2)) { + len++; + } + + *p = len << 6; + +// len2 = + len = (1 << len); + len--; + *p |= value >> (len * 8); + p++; + + while (len) { + *p++ = value >> ((len-- - 1) * 8); + } + + *pos = p; +// return len2; +} + + +uint64_t +ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask) +{ + u_char *p; + uint64_t value; + + p = *pos; + value = *p++ ^ *mask++; + + while (--len) { + value = (value << 8) + (*p++ ^ *mask++); + } + + *pos = p; + return value; +} + + +ngx_int_t +ngx_hkdf_extract(u_char *out_key, size_t *out_len, const EVP_MD *digest, + const u_char *secret, size_t secret_len, const u_char *salt, + size_t salt_len) +{ +#ifdef OPENSSL_IS_BORINGSSL + if (HKDF_extract(out_key, out_len, digest, secret, secret_len, salt, + salt_len) + == 0) + { + return NGX_ERROR; + } +#else + + EVP_PKEY_CTX *pctx; + + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + + if (EVP_PKEY_derive_init(pctx) <= 0) { + return NGX_ERROR; + } + + if (EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) <= 0) { + return NGX_ERROR; + } + + if (EVP_PKEY_CTX_set_hkdf_md(pctx, digest) <= 0) { + return NGX_ERROR; + } + + if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secret_len) <= 0) { + return NGX_ERROR; + } + + if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, salt_len) <= 0) { + return NGX_ERROR; + } + + if (EVP_PKEY_derive(pctx, out_key, out_len) <= 0) { + return NGX_ERROR; + } + +#endif + + return NGX_OK; +} + + +ngx_int_t +ngx_hkdf_expand(u_char *out_key, size_t out_len, const EVP_MD *digest, + const u_char *prk, size_t prk_len, const u_char *info, size_t info_len) +{ +#ifdef OPENSSL_IS_BORINGSSL + if (HKDF_expand(out_key, out_len, digest, prk, prk_len, info, info_len) + == 0) + { + return NGX_ERROR; + } +#else + + EVP_PKEY_CTX *pctx; + + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + + if (EVP_PKEY_derive_init(pctx) <= 0) { + return NGX_ERROR; + } + + if (EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) <= 0) { + return NGX_ERROR; + } + + if (EVP_PKEY_CTX_set_hkdf_md(pctx, digest) <= 0) { + return NGX_ERROR; + } + + if (EVP_PKEY_CTX_set1_hkdf_key(pctx, prk, prk_len) <= 0) { + return NGX_ERROR; + } + + if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, info_len) <= 0) { + return NGX_ERROR; + } + + if (EVP_PKEY_derive(pctx, out_key, &out_len) <= 0) { + return NGX_ERROR; + } + +#endif + + return NGX_OK; +} diff --git a/src/event/ngx_event_quic.h b/src/event/ngx_event_quic.h --- a/src/event/ngx_event_quic.h +++ b/src/event/ngx_event_quic.h @@ -52,5 +52,12 @@ uint64_t ngx_quic_parse_pn(u_char **pos, uint64_t ngx_quic_parse_int(u_char **pos); void ngx_quic_build_int(u_char **pos, uint64_t value); +ngx_int_t ngx_hkdf_extract(u_char *out_key, size_t *out_len, + const EVP_MD *digest, const u_char *secret, size_t secret_len, + const u_char *salt, size_t salt_len); +ngx_int_t ngx_hkdf_expand(u_char *out_key, size_t out_len, + const EVP_MD *digest, const u_char *prk, size_t prk_len, + const u_char *info, size_t info_len); + #endif /* _NGX_EVENT_QUIC_H_INCLUDED_ */ diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -775,16 +775,35 @@ ngx_http_quic_handshake(ngx_event_t *rev // initial secret - size_t is_len; - uint8_t is[SHA256_DIGEST_LENGTH]; - const EVP_MD *digest; + size_t is_len; + uint8_t is[SHA256_DIGEST_LENGTH]; + const EVP_MD *digest; +#ifdef OPENSSL_IS_BORINGSSL + const EVP_AEAD *cipher; +#else + const EVP_CIPHER *cipher; +#endif static const uint8_t salt[20] = "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7" "\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02"; digest = EVP_sha256(); - HKDF_extract(is, &is_len, digest, qc->dcid.data, qc->dcid.len, salt, - sizeof(salt)); + + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ + +#ifdef OPENSSL_IS_BORINGSSL + cipher = EVP_aead_aes_128_gcm(); +#else + cipher = EVP_aes_128_gcm(); +#endif + + if (ngx_hkdf_extract(is, &is_len, digest, qc->dcid.data, qc->dcid.len, + salt, sizeof(salt)) + != NGX_OK) + { + ngx_http_close_connection(c); + return; + } #if (NGX_DEBUG) if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { @@ -812,6 +831,7 @@ ngx_http_quic_handshake(ngx_event_t *rev } hkdfl_len = 2 + 1 + sizeof("tls13 client in") - 1 + 1; + bzero(hkdfl, sizeof(hkdfl)); hkdfl[0] = 0; hkdfl[1] = qc->client_in.len; hkdfl[2] = sizeof("tls13 client in") - 1; @@ -819,25 +839,39 @@ ngx_http_quic_handshake(ngx_event_t *rev sizeof("tls13 client in") - 1); *p = '\0'; - if (HKDF_expand(qc->client_in.data, qc->client_in.len, - digest, is, is_len, hkdfl, hkdfl_len) - == 0) +#if 0 + ngx_memcpy(hkdfl, "\x00\x20\x0f\x74\x6c\x73\x31\x33\x20\x63\x6c\x69\x65\x6e\x74\x20\x69\x6e\x00\x00", 20); + + m = ngx_hex_dump(buf, hkdfl, sizeof(hkdfl)) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "quic initial secret hkdf: %*s, len: %uz", + m, buf, sizeof(hkdfl)); +#endif + + if (ngx_hkdf_expand(qc->client_in.data, qc->client_in.len, + digest, is, is_len, hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(client_in) failed"); + "ngx_hkdf_expand(client_in) failed"); ngx_http_close_connection(c); return; } +#ifdef OPENSSL_IS_BORINGSSL ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic EVP key:%d tag:%d nonce:%d", - EVP_AEAD_key_length(EVP_aead_aes_128_gcm()), - EVP_AEAD_max_tag_len(EVP_aead_aes_128_gcm()), - EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm())); - - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ - - qc->client_in_key.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm()); + EVP_AEAD_key_length(cipher), + EVP_AEAD_max_tag_len(cipher), + EVP_AEAD_nonce_length(cipher)); +#endif + + +#ifdef OPENSSL_IS_BORINGSSL + qc->client_in_key.len = EVP_AEAD_key_length(cipher); +#else + qc->client_in_key.len = EVP_CIPHER_key_length(cipher); +#endif qc->client_in_key.data = ngx_pnalloc(c->pool, qc->client_in_key.len); if (qc->client_in_key.data == NULL) { ngx_http_close_connection(c); @@ -851,18 +885,22 @@ ngx_http_quic_handshake(ngx_event_t *rev sizeof("tls13 quic key") - 1); *p = '\0'; - if (HKDF_expand(qc->client_in_key.data, qc->client_in_key.len, - digest, qc->client_in.data, qc->client_in.len, - hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(qc->client_in_key.data, qc->client_in_key.len, + digest, qc->client_in.data, qc->client_in.len, + hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(client_in_key) failed"); + "ngx_hkdf_expand(client_in_key) failed"); ngx_http_close_connection(c); return; } - qc->client_in_iv.len = EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm()); +#ifdef OPENSSL_IS_BORINGSSL + qc->client_in_iv.len = EVP_AEAD_nonce_length(cipher); +#else + qc->client_in_iv.len = EVP_CIPHER_iv_length(cipher); +#endif qc->client_in_iv.data = ngx_pnalloc(c->pool, qc->client_in_iv.len); if (qc->client_in_iv.data == NULL) { ngx_http_close_connection(c); @@ -875,19 +913,24 @@ ngx_http_quic_handshake(ngx_event_t *rev p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1); *p = '\0'; - if (HKDF_expand(qc->client_in_iv.data, qc->client_in_iv.len, digest, - qc->client_in.data, qc->client_in.len, hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(qc->client_in_iv.data, qc->client_in_iv.len, + digest, qc->client_in.data, qc->client_in.len, + hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(client_in_iv) failed"); + "ngx_hkdf_expand(client_in_iv) failed"); ngx_http_close_connection(c); return; } /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ - qc->client_in_hp.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm()); +#ifdef OPENSSL_IS_BORINGSSL + qc->client_in_hp.len = EVP_AEAD_key_length(cipher); +#else + qc->client_in_hp.len = EVP_CIPHER_key_length(cipher); +#endif qc->client_in_hp.data = ngx_pnalloc(c->pool, qc->client_in_hp.len); if (qc->client_in_hp.data == NULL) { ngx_http_close_connection(c); @@ -900,12 +943,13 @@ ngx_http_quic_handshake(ngx_event_t *rev p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1); *p = '\0'; - if (HKDF_expand(qc->client_in_hp.data, qc->client_in_hp.len, digest, - qc->client_in.data, qc->client_in.len, hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(qc->client_in_hp.data, qc->client_in_hp.len, + digest, qc->client_in.data, qc->client_in.len, + hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(client_in_hp) failed"); + "ngx_hkdf_expand(client_in_hp) failed"); ngx_http_close_connection(c); return; } @@ -956,19 +1000,23 @@ ngx_http_quic_handshake(ngx_event_t *rev sizeof("tls13 server in") - 1); *p = '\0'; - if (HKDF_expand(qc->server_in.data, qc->server_in.len, - digest, is, is_len, hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(qc->server_in.data, qc->server_in.len, + digest, is, is_len, hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(server_in) failed"); + "ngx_hkdf_expand(server_in) failed"); ngx_http_close_connection(c); return; } /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ - qc->server_in_key.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm()); +#ifdef OPENSSL_IS_BORINGSSL + qc->server_in_key.len = EVP_AEAD_key_length(cipher); +#else + qc->server_in_key.len = EVP_CIPHER_key_length(cipher); +#endif qc->server_in_key.data = ngx_pnalloc(c->pool, qc->server_in_key.len); if (qc->server_in_key.data == NULL) { ngx_http_close_connection(c); @@ -982,18 +1030,22 @@ ngx_http_quic_handshake(ngx_event_t *rev sizeof("tls13 quic key") - 1); *p = '\0'; - if (HKDF_expand(qc->server_in_key.data, qc->server_in_key.len, - digest, qc->server_in.data, qc->server_in.len, - hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(qc->server_in_key.data, qc->server_in_key.len, + digest, qc->server_in.data, qc->server_in.len, + hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(server_in_key) failed"); + "ngx_hkdf_expand(server_in_key) failed"); ngx_http_close_connection(c); return; } - qc->server_in_iv.len = EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm()); +#ifdef OPENSSL_IS_BORINGSSL + qc->server_in_iv.len = EVP_AEAD_nonce_length(cipher); +#else + qc->server_in_iv.len = EVP_CIPHER_iv_length(cipher); +#endif qc->server_in_iv.data = ngx_pnalloc(c->pool, qc->server_in_iv.len); if (qc->server_in_iv.data == NULL) { ngx_http_close_connection(c); @@ -1006,19 +1058,24 @@ ngx_http_quic_handshake(ngx_event_t *rev p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1); *p = '\0'; - if (HKDF_expand(qc->server_in_iv.data, qc->server_in_iv.len, digest, - qc->server_in.data, qc->server_in.len, hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(qc->server_in_iv.data, qc->server_in_iv.len, + digest, qc->server_in.data, qc->server_in.len, + hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(server_in_iv) failed"); + "ngx_hkdf_expand(server_in_iv) failed"); ngx_http_close_connection(c); return; } /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ - qc->server_in_hp.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm()); +#ifdef OPENSSL_IS_BORINGSSL + qc->server_in_hp.len = EVP_AEAD_key_length(cipher); +#else + qc->server_in_hp.len = EVP_CIPHER_key_length(cipher); +#endif qc->server_in_hp.data = ngx_pnalloc(c->pool, qc->server_in_hp.len); if (qc->server_in_hp.data == NULL) { ngx_http_close_connection(c); @@ -1031,12 +1088,13 @@ ngx_http_quic_handshake(ngx_event_t *rev p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1); *p = '\0'; - if (HKDF_expand(qc->server_in_hp.data, qc->server_in_hp.len, digest, - qc->server_in.data, qc->server_in.len, hkdfl, hkdfl_len) - == 0) + if (ngx_hkdf_expand(qc->server_in_hp.data, qc->server_in_hp.len, + digest, qc->server_in.data, qc->server_in.len, + hkdfl, hkdfl_len) + != NGX_OK) { ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "HKDF_expand(server_in_hp) failed"); + "ngx_hkdf_expand(server_in_hp) failed"); ngx_http_close_connection(c); return; } @@ -1147,12 +1205,20 @@ ngx_http_quic_handshake(ngx_event_t *rev } #endif - EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(EVP_aead_aes_128_gcm(), + uint8_t cleartext[1600]; + size_t cleartext_len; + +#ifdef OPENSSL_IS_BORINGSSL + EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(cipher, qc->client_in_key.data, qc->client_in_key.len, EVP_AEAD_DEFAULT_TAG_LENGTH); - uint8_t cleartext[1600]; - size_t cleartext_len = sizeof(cleartext); + if (aead == NULL) { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "EVP_AEAD_CTX_new() failed"); + ngx_http_close_connection(c); + return; + } if (EVP_AEAD_CTX_open(aead, cleartext, &cleartext_len, sizeof(cleartext), nonce, qc->client_in_iv.len, ciphertext.data, @@ -1167,6 +1233,87 @@ ngx_http_quic_handshake(ngx_event_t *rev } EVP_AEAD_CTX_free(aead); +#else + int len; + u_char *tag; + EVP_CIPHER_CTX *aead; + + aead = EVP_CIPHER_CTX_new(); + if (aead == NULL) { + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_CIPHER_CTX_new() failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptInit_ex(aead, cipher, NULL, NULL, NULL) != 1) { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptInit_ex() failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_IVLEN, qc->client_in_iv.len, + NULL) + == 0) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_IVLEN) failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptInit_ex(aead, NULL, NULL, qc->client_in_key.data, nonce) + != 1) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptInit_ex() failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptUpdate(aead, NULL, &len, ad.data, ad.len) != 1) { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptUpdate() failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptUpdate(aead, cleartext, &len, ciphertext.data, + ciphertext.len - EVP_GCM_TLS_TAG_LEN) + != 1) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptUpdate() failed"); + ngx_http_close_connection(c); + return; + } + + cleartext_len = len; + tag = ciphertext.data + ciphertext.len - EVP_GCM_TLS_TAG_LEN; + + if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_TAG, EVP_GCM_TLS_TAG_LEN, + tag) + == 0) + { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, + "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_TAG) failed"); + ngx_http_close_connection(c); + return; + } + + if (EVP_DecryptFinal_ex(aead, cleartext + len, &len) <= 0) { + EVP_CIPHER_CTX_free(aead); + ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptFinal_ex failed"); + ngx_http_close_connection(c); + return; + } + + cleartext_len += len; + + EVP_CIPHER_CTX_free(aead); +#endif #if (NGX_DEBUG) if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {