diff options
Diffstat (limited to 'contrib/epee/src')
-rw-r--r-- | contrib/epee/src/hex.cpp | 63 | ||||
-rw-r--r-- | contrib/epee/src/net_ssl.cpp | 82 |
2 files changed, 121 insertions, 24 deletions
diff --git a/contrib/epee/src/hex.cpp b/contrib/epee/src/hex.cpp index 5c8acc8be..8421dcae9 100644 --- a/contrib/epee/src/hex.cpp +++ b/contrib/epee/src/hex.cpp @@ -83,4 +83,67 @@ namespace epee { return write_hex(out, src); } + + std::vector<uint8_t> from_hex::vector(boost::string_ref src) + { + // should we include a specific character + auto include = [](char input) { + // we ignore spaces and colons + return !std::isspace(input) && input != ':'; + }; + + // the number of relevant characters to decode + auto count = std::count_if(src.begin(), src.end(), include); + + // this must be a multiple of two, otherwise we have a truncated input + if (count % 2) { + throw std::length_error{ "Invalid hexadecimal input length" }; + } + + std::vector<uint8_t> result; + result.reserve(count / 2); + + // the data to work with (std::string is always null-terminated) + auto data = src.data(); + + // convert a single hex character to an unsigned integer + auto char_to_int = [](const char *input) { + switch (std::tolower(*input)) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': return 10; + case 'b': return 11; + case 'c': return 12; + case 'd': return 13; + case 'e': return 14; + case 'f': return 15; + default: throw std::range_error{ "Invalid hexadecimal input" }; + } + }; + + // keep going until we reach the end + while (data[0] != '\0') { + // skip unwanted characters + if (!include(data[0])) { + ++data; + continue; + } + + // convert two matching characters to int + auto high = char_to_int(data++); + auto low = char_to_int(data++); + + result.push_back(high << 4 | low); + } + + return result; + } } diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp index 941799078..9a62dd3c2 100644 --- a/contrib/epee/src/net_ssl.cpp +++ b/contrib/epee/src/net_ssl.cpp @@ -154,13 +154,19 @@ bool create_ssl_certificate(std::string &pkey_buffer, std::string &cert_buffer) return success; } -ssl_context_t create_ssl_context(const std::pair<std::string, std::string> &private_key_and_certificate_path, std::list<std::string> allowed_certificates, bool allow_any_cert) +ssl_context_t create_ssl_context(const std::pair<std::string, std::string> &private_key_and_certificate_path, std::list<std::string> allowed_certificates, std::vector<std::vector<uint8_t>> allowed_fingerprints, bool allow_any_cert) { - ssl_context_t ssl_context({boost::asio::ssl::context(boost::asio::ssl::context::sslv23), std::move(allowed_certificates)}); + ssl_context_t ssl_context{boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), std::move(allowed_certificates), std::move(allowed_fingerprints)}; - // disable sslv2 - ssl_context.context.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2); - ssl_context.context.set_default_verify_paths(); + // only allow tls v1.2 and up + ssl_context.context.set_options(boost::asio::ssl::context::default_workarounds); + ssl_context.context.set_options(boost::asio::ssl::context::no_sslv2); + ssl_context.context.set_options(boost::asio::ssl::context::no_sslv3); + ssl_context.context.set_options(boost::asio::ssl::context::no_tlsv1); + ssl_context.context.set_options(boost::asio::ssl::context::no_tlsv1_1); + + // only allow a select handful of tls v1.3 and v1.2 ciphers to be used + SSL_CTX_set_cipher_list(ssl_context.context.native_handle(), "ECDHE-ECDSA-CHACHA20-POLY1305-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305"); // set options on the SSL context for added security SSL_CTX *ctx = ssl_context.context.native_handle(); @@ -179,7 +185,7 @@ ssl_context_t create_ssl_context(const std::pair<std::string, std::string> &priv #ifdef SSL_OP_NO_COMPRESSION SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION); #endif - SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); // https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices + ssl_context.context.set_default_verify_paths(); CHECK_AND_ASSERT_THROW_MES(private_key_and_certificate_path.first.empty() == private_key_and_certificate_path.second.empty(), "private key and certificate must be either both given or both empty"); if (private_key_and_certificate_path.second.empty()) @@ -225,7 +231,7 @@ bool is_ssl(const unsigned char *data, size_t len) return false; } -bool is_certificate_allowed(boost::asio::ssl::verify_context &ctx, const std::list<std::string> &allowed_certificates) +bool is_certificate_allowed(boost::asio::ssl::verify_context &ctx, const ssl_context_t &ssl_context) { X509_STORE_CTX *sctx = ctx.native_handle(); if (!sctx) @@ -240,23 +246,51 @@ bool is_certificate_allowed(boost::asio::ssl::verify_context &ctx, const std::li return false; } - BIO *bio_cert = BIO_new(BIO_s_mem()); - openssl_bio bio_cert_deleter{bio_cert}; - bool success = PEM_write_bio_X509(bio_cert, cert); - if (!success) - { - MERROR("Failed to print certificate"); - return false; + // can we check the certificate against a list of fingerprints? + if (!ssl_context.allowed_fingerprints.empty()) { + // buffer for the certificate digest and the size of the result + std::vector<uint8_t> digest(EVP_MAX_MD_SIZE); + unsigned int size{ 0 }; + + // create the digest from the certificate + if (!X509_digest(cert, EVP_sha1(), digest.data(), &size)) { + MERROR("Failed to create certificate fingerprint"); + return false; + } + + // strip unnecessary bytes from the digest + digest.resize(size); + + // is the certificate fingerprint inside the list of allowed fingerprints? + if (std::find(ssl_context.allowed_fingerprints.begin(), ssl_context.allowed_fingerprints.end(), digest) != ssl_context.allowed_fingerprints.end()) + return true; } - BUF_MEM *buf = NULL; - BIO_get_mem_ptr(bio_cert, &buf); - if (!buf || !buf->data || !buf->length) - { - MERROR("Failed to write certificate: " << ERR_get_error()); - return false; + + if (!ssl_context.allowed_certificates.empty()) { + BIO *bio_cert = BIO_new(BIO_s_mem()); + bool success = PEM_write_bio_X509(bio_cert, cert); + if (!success) + { + BIO_free(bio_cert); + MERROR("Failed to print certificate"); + return false; + } + BUF_MEM *buf = NULL; + BIO_get_mem_ptr(bio_cert, &buf); + if (!buf || !buf->data || !buf->length) + { + BIO_free(bio_cert); + MERROR("Failed to write certificate: " << ERR_get_error()); + return false; + } + std::string certificate(std::string(buf->data, buf->length)); + BIO_free(bio_cert); + if (std::find(ssl_context.allowed_certificates.begin(), ssl_context.allowed_certificates.end(), certificate) != ssl_context.allowed_certificates.end()) + return true; } - std::string certificate(std::string(buf->data, buf->length)); - return std::find(allowed_certificates.begin(), allowed_certificates.end(), certificate) != allowed_certificates.end(); + + // if either checklist is non-empty we must have failed it + return ssl_context.allowed_fingerprints.empty() && ssl_context.allowed_certificates.empty(); } bool ssl_handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket, boost::asio::ssl::stream_base::handshake_type type, const epee::net_utils::ssl_context_t &ssl_context) @@ -276,7 +310,7 @@ bool ssl_handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socke return false; } } - if (!ssl_context.allow_any_cert && !ssl_context.allowed_certificates.empty() && !is_certificate_allowed(ctx, ssl_context.allowed_certificates)) + if (!ssl_context.allow_any_cert && !is_certificate_allowed(ctx, ssl_context)) { MERROR("Certificate is not in the allowed list, connection droppped"); return false; @@ -289,7 +323,7 @@ bool ssl_handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socke socket.handshake(type, ec); if (ec) { - MERROR("handshake failed, connection dropped"); + MERROR("handshake failed, connection dropped: " << ec.message()); return false; } if (!ssl_context.allow_any_cert && !verified) |