diff options
Diffstat (limited to 'examples/tls_server_context_wolfssl.cc')
-rw-r--r-- | examples/tls_server_context_wolfssl.cc | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/examples/tls_server_context_wolfssl.cc b/examples/tls_server_context_wolfssl.cc new file mode 100644 index 0000000..ed09b72 --- /dev/null +++ b/examples/tls_server_context_wolfssl.cc @@ -0,0 +1,284 @@ +/* + * ngtcp2 + * + * Copyright (c) 2020 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "tls_server_context_wolfssl.h" + +#include <iostream> +#include <fstream> +#include <limits> + +#include <ngtcp2/ngtcp2_crypto_wolfssl.h> + +#include "server_base.h" +#include "template.h" + +extern Config config; + +TLSServerContext::TLSServerContext() : ssl_ctx_{nullptr} {} + +TLSServerContext::~TLSServerContext() { + if (ssl_ctx_) { + wolfSSL_CTX_free(ssl_ctx_); + } +} + +WOLFSSL_CTX *TLSServerContext::get_native_handle() const { return ssl_ctx_; } + +namespace { +int alpn_select_proto_h3_cb(WOLFSSL *ssl, const unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg) { + auto conn_ref = + static_cast<ngtcp2_crypto_conn_ref *>(wolfSSL_get_app_data(ssl)); + auto h = static_cast<HandlerBase *>(conn_ref->user_data); + const uint8_t *alpn; + size_t alpnlen; + // This should be the negotiated version, but we have not set the + // negotiated version when this callback is called. + auto version = ngtcp2_conn_get_client_chosen_version(h->conn()); + + switch (version) { + case QUIC_VER_DRAFT29: + alpn = H3_ALPN_DRAFT29; + alpnlen = str_size(H3_ALPN_DRAFT29); + break; + case QUIC_VER_DRAFT30: + alpn = H3_ALPN_DRAFT30; + alpnlen = str_size(H3_ALPN_DRAFT30); + break; + case QUIC_VER_DRAFT31: + alpn = H3_ALPN_DRAFT31; + alpnlen = str_size(H3_ALPN_DRAFT31); + break; + case QUIC_VER_DRAFT32: + alpn = H3_ALPN_DRAFT32; + alpnlen = str_size(H3_ALPN_DRAFT32); + break; + case NGTCP2_PROTO_VER_V1: + case NGTCP2_PROTO_VER_V2_DRAFT: + alpn = H3_ALPN_V1; + alpnlen = str_size(H3_ALPN_V1); + break; + default: + if (!config.quiet) { + std::cerr << "Unexpected quic protocol version: " << std::hex << "0x" + << version << std::dec << std::endl; + } + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + for (auto p = in, end = in + inlen; p + alpnlen <= end; p += *p + 1) { + if (std::equal(alpn, alpn + alpnlen, p)) { + *out = p + 1; + *outlen = *p; + return SSL_TLSEXT_ERR_OK; + } + } + + if (!config.quiet) { + std::cerr << "Client did not present ALPN " << &alpn[1] << std::endl; + } + + return SSL_TLSEXT_ERR_ALERT_FATAL; +} +} // namespace + +namespace { +int alpn_select_proto_hq_cb(WOLFSSL *ssl, const unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg) { + auto conn_ref = + static_cast<ngtcp2_crypto_conn_ref *>(wolfSSL_get_app_data(ssl)); + auto h = static_cast<HandlerBase *>(conn_ref->user_data); + const uint8_t *alpn; + size_t alpnlen; + // This should be the negotiated version, but we have not set the + // negotiated version when this callback is called. + auto version = ngtcp2_conn_get_client_chosen_version(h->conn()); + + switch (version) { + case QUIC_VER_DRAFT29: + alpn = HQ_ALPN_DRAFT29; + alpnlen = str_size(HQ_ALPN_DRAFT29); + break; + case QUIC_VER_DRAFT30: + alpn = HQ_ALPN_DRAFT30; + alpnlen = str_size(HQ_ALPN_DRAFT30); + break; + case QUIC_VER_DRAFT31: + alpn = HQ_ALPN_DRAFT31; + alpnlen = str_size(HQ_ALPN_DRAFT31); + break; + case QUIC_VER_DRAFT32: + alpn = HQ_ALPN_DRAFT32; + alpnlen = str_size(HQ_ALPN_DRAFT32); + break; + case NGTCP2_PROTO_VER_V1: + case NGTCP2_PROTO_VER_V2_DRAFT: + alpn = HQ_ALPN_V1; + alpnlen = str_size(HQ_ALPN_V1); + break; + default: + if (!config.quiet) { + std::cerr << "Unexpected quic protocol version: " << std::hex << "0x" + << version << std::dec << std::endl; + } + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + for (auto p = in, end = in + inlen; p + alpnlen <= end; p += *p + 1) { + if (std::equal(alpn, alpn + alpnlen, p)) { + *out = p + 1; + *outlen = *p; + return SSL_TLSEXT_ERR_OK; + } + } + + if (!config.quiet) { + std::cerr << "Client did not present ALPN " << &alpn[1] << std::endl; + } + + return SSL_TLSEXT_ERR_ALERT_FATAL; +} +} // namespace + +namespace { +int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { + // We don't verify the client certificate. Just request it for the + // testing purpose. + return 1; +} +} // namespace + +int TLSServerContext::init(const char *private_key_file, const char *cert_file, + AppProtocol app_proto) { + constexpr static unsigned char sid_ctx[] = "ngtcp2 server"; + +#if defined(DEBUG_WOLFSSL) + if (!config.quiet) { + /*wolfSSL_Debugging_ON();*/ + } +#endif + + ssl_ctx_ = wolfSSL_CTX_new(wolfTLSv1_3_server_method()); + if (!ssl_ctx_) { + std::cerr << "wolfSSL_CTX_new: " + << wolfSSL_ERR_error_string(wolfSSL_ERR_get_error(), nullptr) + << std::endl; + return -1; + } + + if (ngtcp2_crypto_wolfssl_configure_server_context(ssl_ctx_) != 0) { + std::cerr << "ngtcp2_crypto_wolfssl_configure_server_context failed" + << std::endl; + return -1; + } + +#ifdef WOLFSSL_EARLY_DATA + wolfSSL_CTX_set_max_early_data(ssl_ctx_, UINT32_MAX); +#endif + + constexpr auto ssl_opts = + (WOLFSSL_OP_ALL & ~WOLFSSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | + WOLFSSL_OP_SINGLE_ECDH_USE | WOLFSSL_OP_CIPHER_SERVER_PREFERENCE; + + wolfSSL_CTX_set_options(ssl_ctx_, ssl_opts); + + if (wolfSSL_CTX_set_cipher_list(ssl_ctx_, config.ciphers) != 1) { + std::cerr << "wolfSSL_CTX_set_cipher_list: " + << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return -1; + } + + if (wolfSSL_CTX_set1_curves_list(ssl_ctx_, + const_cast<char *>(config.groups)) != 1) { + std::cerr << "wolfSSL_CTX_set1_curves_list(" << config.groups << ") failed" + << std::endl; + return -1; + } + + wolfSSL_CTX_set_mode(ssl_ctx_, SSL_MODE_RELEASE_BUFFERS); + + switch (app_proto) { + case AppProtocol::H3: + wolfSSL_CTX_set_alpn_select_cb(ssl_ctx_, alpn_select_proto_h3_cb, nullptr); + break; + case AppProtocol::HQ: + wolfSSL_CTX_set_alpn_select_cb(ssl_ctx_, alpn_select_proto_hq_cb, nullptr); + break; + } + + wolfSSL_CTX_set_default_verify_paths(ssl_ctx_); + + if (wolfSSL_CTX_use_PrivateKey_file(ssl_ctx_, private_key_file, + SSL_FILETYPE_PEM) != 1) { + std::cerr << "wolfSSL_CTX_use_PrivateKey_file: " + << wolfSSL_ERR_error_string(wolfSSL_ERR_get_error(), nullptr) + << std::endl; + return -1; + } + + if (wolfSSL_CTX_use_certificate_chain_file(ssl_ctx_, cert_file) != 1) { + std::cerr << "wolfSSL_CTX_use_certificate_chain_file: " + << wolfSSL_ERR_error_string(wolfSSL_ERR_get_error(), nullptr) + << std::endl; + return -1; + } + + if (wolfSSL_CTX_check_private_key(ssl_ctx_) != 1) { + std::cerr << "wolfSSL_CTX_check_private_key: " + << wolfSSL_ERR_error_string(wolfSSL_ERR_get_error(), nullptr) + << std::endl; + return -1; + } + + wolfSSL_CTX_set_session_id_context(ssl_ctx_, sid_ctx, sizeof(sid_ctx) - 1); + + if (config.verify_client) { + wolfSSL_CTX_set_verify(ssl_ctx_, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_CLIENT_ONCE | + WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, + verify_cb); + } + + return 0; +} + +extern std::ofstream keylog_file; + +#ifdef HAVE_SECRET_CALLBACK +namespace { +void keylog_callback(const WOLFSSL *ssl, const char *line) { + keylog_file.write(line, strlen(line)); + keylog_file.put('\n'); + keylog_file.flush(); +} +} // namespace +#endif + +void TLSServerContext::enable_keylog() { +#ifdef HAVE_SECRET_CALLBACK + wolfSSL_CTX_set_keylog_callback(ssl_ctx_, keylog_callback); +#endif +} |