wolfSSL / wolfssl

The wolfSSL library is a small, fast, portable implementation of TLS/SSL for embedded devices to the cloud. wolfSSL supports up to TLS 1.3 and DTLS 1.3!
https://www.wolfssl.com
GNU General Public License v2.0
2.34k stars 831 forks source link

Add a function to derive default ciphers #7376

Closed 0xg0nz0 closed 7 months ago

0xg0nz0 commented 7 months ago

Version

v5.7.0-stable

Description

There is a ton of hairy logic and #ifdef / #ifndef checking in examples related to setting the right list of default ciphers based on what is compiled into wolfSSL and a few other factors (PSK, anonymous ciphers, etc.):

Given the implications for hardening if this is done incorrectly, it feels like something that ought to be exposed as a function.

dgarske commented 7 months ago

Hi @0xg0nz0 ,

Can you explain in more detail your request?

The example client has a -e option to list available ciphers:

./examples/client/client -e
wolfSSL Entering wolfSSL_Init
wolfSSL Entering wolfCrypt_Init
TLS13-AES128-GCM-SHA256:TLS13-AES256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-CHACHA20-POLY1305:DHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305-OLD:ECDHE-ECDSA-CHACHA20-POLY1305-OLD:DHE-RSA-CHACHA20-POLY1305-OLD

That codes looks like:

static char ciphers[WOLFSSL_CIPHER_LIST_MAX_SIZE];
int ret = wolfSSL_get_ciphers(ciphers, (int)sizeof(ciphers));
if (ret == WOLFSSL_SUCCESS) {
    printf("%s\n", ciphers);
}

Thanks, David Garske, wolfSSL

0xg0nz0 commented 7 months ago

Hi David, it's probably easier to show than tell -- I'm working on a function extracted from the relevant client.c and server.c example code, and I'll share that here when completed. In short: it's not just available ciphers, but recommended ciphers to use as a reasonable default that I think is worth encapsulating as a function.

0xg0nz0 commented 7 months ago

This C++ code illustrates the problem: to just get a reasonable default list of cipher strings, even just limiting it to TLS 1.2 and TLS 1.3 recommended hardened ciphers, it's a pretty significant chunk of code that incorporates a lot of WolfSSL compilation options. There is currently a C function that gets all ciphers supported, but the subset that is a reasonable default given protocol version is as far as I can tell not supported.

const std::vector<std::string> iggy::ssl::SSLOptions::getDefaultCipherList(iggy::ssl::ProtocolVersion protocolVersion) {
    auto ciphers = std::vector<std::string>();

    // References:
    // - https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Security_Cheat_Sheet.html
    // - https://ssl-config.mozilla.org

    if (protocolVersion == iggy::ssl::ProtocolVersion::TLSV1_3) {
// sanity check to make sure TLS 1.3 is compiled in
#ifdef WOLFSSL_NO_TLS13
        throw std::runtime_error("TLS 1.3 is not supported by this build of WolfSSL");
#endif

        // recommended TLS 1.3 ciphers
#if defined(HAVE_AESGCM) && !defined(NO_AES)
        ciphers.push_back("TLS_AES_128_GCM_SHA256");
#if defined(WOLFSSL_SHA384)
        ciphers.push_back("TLS_AES_256_GCM_SHA384");
#endif
#endif
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
        ciphers.push_back("TLS_CHACHA20_POLY1305_SHA256");
#endif

    } else if (protocolVersion == iggy::ssl::ProtocolVersion::TLSV1_2) {
// sanity check to make sure TLS 1.2 with ECC support is compiled in
#ifdef WOLFSSL_NO_TLS12
        throw std::runtime_error("TLS 1.2 is not supported by this build of WolfSSL");
#endif
#if !defined(HAVE_ECC) || !defined(HAVE_SUPPORTED_CURVES)
        throw std::runtime_error("ECC + Supported Curves must be enabled to support ECDHE");
#endif

        // recommended TLS 1.2 ciphers
#if defined(HAVE_AESGCM) && !defined(NO_AES)
        ciphers.push_back("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");
#ifndef NO_RSA
        ciphers.push_back("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
#endif
#if defined(WOLFSSL_SHA384)
        ciphers.push_back("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
#ifndef NO_RSA
        ciphers.push_back("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384");
#endif
#endif
#endif
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
        ciphers.push_back("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305");
#ifndef NO_RSA
        ciphers.push_back("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305");
#endif
#endif
    } else {
        auto protocolVersionName = iggy::ssl::getProtocolVersionName(protocolVersion);
        throw std::runtime_error(fmt::format("Unsupported protocol version: {}", protocolVersionName));
    }

    if (ciphers.empty()) {
        auto protocolVersionName = iggy::ssl::getProtocolVersionName(protocolVersion);
        throw std::runtime_error(fmt::format("No ciphers available for the specified protocol version: {}", protocolVersionName));
    }
    return ciphers;
}
0xg0nz0 commented 7 months ago

For the broader context, see this PR:

https://github.com/iggy-rs/iggy-cpp-client/pull/33/files

dgarske commented 7 months ago

Hi @0xg0nz0 ,

What is your feature request? Are you looking for something like the C++ code you shared that chooses stronger cipher suites by default? Have you checked the --enable-harden-tls option (RFC9325). This disables used of weaker algorithms at build-time.

The cipher gets enabled if the algorithms are enabled at build-time, but the cipher list can be overridden at run-time if desired. The build default disable TLS prior to v1.2. The TLS version is determined by the wolfSSL_CTX_new method provided. Example: wolfSSLv23_client_method() allows downgrade and wolfTLSv1_3_client_method() forces TLS v1.3 only.

Thanks, David Garske, wolfSSL

0xg0nz0 commented 7 months ago

Hi @dgarske, yes, I'm building with the --enable-harden option already, so this is more about defaulting to the recommended cipher suites at runtime, taking into account anything disabled at compile time to ensure the default is only recommended + compiled into the library. I might be overthinking this, but I saw a lot of logic in client.c and server.c around, which is why I put in an issue. If that doesn't make sense in the C API, I am fine to close the issue and to keep that logic locally, since I am using only TLS 1.2/1.3 recommendations it's not particularly complex.

dgarske commented 7 months ago

@anhu would you mind reviewing this feature request?

anhu commented 7 months ago

Hi @0xg0nz0 ,

as @dgarske said, -e option in the example client will show you only the cipher-suites that are compiled in. So for example, if you disabled RSA, then none of the TLS 1.2 RSA cipher-suites would be listed.

....keep that logic locally

I would recommend that as the best idea. I think the issue that we are having is the idea of having general recommended cipher-suites. Each one has its own particular properties and while we can make a recommendation on a case by case basis, we cannot give general recommendations. For example, I would recommend using ECDSA, but there are some situations where RSA is the better option based on what hardware optimizations are available. There are many factors to consider.

I'm not exactly sure of your goals so I'm not sure my comment make sense in your context. Please let me know of further clarifications that might help me to better understand what you are looking for.

Warm regards, Anthony

0xg0nz0 commented 7 months ago

Thanks, @anhu, "it depends" is always a fair answer when it comes to design decisions and there is a degree of discretion, and so in that case I agree it's best to put that local to the library that knows the conditions best. I'll close this.

anhu commented 7 months ago

@0xg0nz0 ,

Thanks for your response. Can you let us know a bit about your project and goals? What are you trying to achieve? We here at wolfSSL love knowing how the community is using our software. For example, can you let us know if your project is out of personal, professional or academic interest? Are there any institutions that are associated with your work?

Any information you would be willing to share would be very much appreciated.

Warm regards, Anthony

0xg0nz0 commented 7 months ago

@0xg0nz0 ,

Thanks for your response. Can you let us know a bit about your project and goals? What are you trying to achieve? We here at wolfSSL love knowing how the community is using our software. For example, can you let us know if your project is out of personal, professional or academic interest? Are there any institutions that are associated with your work?

Any information you would be willing to share would be very much appreciated.

Warm regards, Anthony

@anhu -- it's personal interest for now, re-learning C++ (last touched it professionally 24 years ago) and so I am volunteering with the Iggy project to build their C++ client. Their Rust-based server supports HTTP, HTTP+TLS, TCP and QUIC, so that's requiring a robust set of client connectors. Since I expect Iggy C++ to be used in high-performance use cases like electronic trading (my background), I am trying to keep the library compact and efficient. Thus I made the decision after a bit of research to make the default SSL/TLS implementation based on wolfSSL rather than OpenSSL or BoringSSL, though if the C++ abstraction layer works as expected it should not be hard to support multiple.

Does that answer your question?

anhu commented 7 months ago

Yes! It does answer my question! We'd love to support your efforts the best we can. If you happen to stumble upon any problems while working with wolfSSL please feel free to send a message to support@wolfssl.com.

Warm regards, Anthony