Post

#23 Controlling TLS Cipher Suites in IDF

Previously, we covered how to achieve perfect forward secrecy guarantees in your ESP32 projects.

However, even when we did that, our TLS client on the ESP32 sent cipher suites to the server that are considered insecure because they use a Cipher Block Chaining encryption algorithm. When checking those with Cipher Suite you will see something like this:

A CBC cipher suite A weak cipher suite due to CBC algorithm

The key exchange guarantees perfect forward secrecy, which is great, but the encryption algorithm has been demonstrated to be insecure.

Key Exchange vs Encryption?

As a brief primer on cipher suites it’s helpful to understand the difference between the key exchange algorithm and the encryption algorithm. The very simple version is that key exchange is how the client and server establish a set of keys to then use for the encryption part of the process. So you can have vulnerabilities in both the key exchange and encryption if you aren’t careful. Perfect forward secrecy helps ensure that if your key exchange method is compromised (your private key is leaked) an attacker can only recover future traffic and is unable to decrypt any previous traffic they may have recorded.

The encryption algorithm also needs to be secure. In the case of CBC algorithms, several attacks have demonstrated how an attacker can decrypt traffic even without compromising your key exchange. It only requires the attacker to establish a man-in-the-middle position of analyzing the encrypted data and sending specially formatted traffic to try to figure out the keys. Some examples of this are the BEAST and POODLE attacks.

How Weak is Weak?

How weak are these CBC cipher suites really? The community is somewhat divided on this as the attacks on CBC are pretty complicated and also require a MITM compromise. That said, since CBC keeps finding ways to be hacked, the security community at large has decided to abandon it. Even the MbedTLS project is removing CBC support from the library.

How to Fix It

The easiest way to address this issue is to use TLS 1.3 as it only supports authenticated encryption, otherwise known as AEAD. It does not support any CBC cipher suites. However, what if your server doesn’t support TLS 1.3 yet? In that case, you need to control the set of cipher suites sent and exclude the CBC ones.

As an example, the Cloudflare docs have a recommended configuration of cipher suites. To support TLS 1.2 and 1.3 they recommend the following list of cipher suites:

All provide perfect forward secrecy as well as AEAD. You can click the link for each one to show the Cipher Suite report. You will find all are listed as recommended or secure. If we have our client match this recommendation we are guaranteeing perfect forward security and encryption algorithms that have no known security issues.

Controlling Cipher Suites in IDF

Unfortunately there is no combination of menuconfig settings you can use to get a cipher suite list exactly like the one recommended by Cloudflare. At least, not until MbedTLS removes the CBC cipher suites from the codebase. You can, however, manually set the cipher suites, but it requires you to go a level deeper in the TLS abstraction layer.

Currently, if you use the http example from Espressif you would use the esp_http_client_perform method and simply pass it a url with “https”. In this mode, the cipher suites will be selected for you based on a combination of defaults in the MbedTLS library and your menuconfig settings.

To control the cipher suites you need to use the esp_tls_xxx API from the esp-tls component which gives you finer grained control over aspects of the TLS connection. You lose just a little bit of the high-level abstraction of esp_http_client_perform but it lets you control exactly which cipher suites you use. Fortunately, Espressif provides an example of this API so you don’t have to start from scratch.

To use the recommended cipher suite list from our example above you use the ciphersuites_list member of the esp_tls_cfg_t struct.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static const int server_supported_ciphersuites[] = {
    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
    MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
    0
}; // list must end with 0

esp_tls_cfg_t cfg = {
    .crt_bundle_attach = esp_crt_bundle_attach,
    .ciphersuites_list = server_supported_ciphersuites,
};

You may have noticed the MbedTLS define names for the cipher suites follow a slightly different naming convention than the list provided by Cloudflare. You will have to find the MbedTLS define that matches the cipher suite you need. The list of cipher suite define names can be found at https://arm-software.github.io/CMSIS-mbedTLS/latest/ssl__ciphersuites_8h.html .

Additionally, we need to enable the CHACHA and POLY1305 algorithms in the MbedTLS settings. The sdkconfig.defaults additions for that are as follows:

1
2
3
4
5
6
7
CONFIG_MBEDTLS_POLY1305_C=y
CONFIG_MBEDTLS_CHACHA20_C=y
CONFIG_MBEDTLS_CHACHAPOLY_C=y
CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_PSK=n
CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=n
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=n
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=n

Now when we have our client connect to https://www.howsmyssl.com/a/check in this configuration we get the following response.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
  "given_cipher_suites": [
    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
    "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
    "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
    "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
    "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
    "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
  ],
  "ephemeral_keys_supported": true,
  "session_ticket_supported": true,
  "tls_compression_supported": false,
  "unknown_cipher_suite_supported": false,
  "beast_vuln": false,
  "able_to_detect_n_minus_one_splitting": false,
  "insecure_cipher_suites": {},
  "tls_version": "TLS 1.2",
  "rating": "Probably Okay"
}

We see the exact list recommended by Cloudflare and we can say our client supports only cipher suites that have perfect forward secrecy and use secure AEAD encryption algorithms. As of this writing, this implements a best practice TLS connection to the server.

After making changes to your project to guarantee PFS and use only secure cipher suites you may find that your device no longer connects to one or more of the services it needs to. In this case, the server-side of your application supports a less secure configuration. That doesn’t necessarily make the service insecure, just that it’s not following best practices and isn’t as secure as it could be.

If you control the cloud service, try to upgrade the server so it supports a more secure configuration. If you don’t control the service, reach out and ask them what their plan is to move to a more secure setup. Service providers are balancing security with maximum accessibility so they will likely always be trailing a little when it comes to implementing best security practice as their clients catch up.

Production Pointers

  • Use only known secure encryption algorithms, specifically AEAD algorithms for TLS connections
  • Use cipher suites that guarantee perfect forward secrecy
  • Stay informed on cipher suite recommendations from the security community
  • Use tools like Cipher Suite and How’s My SSL to help assess the security of your client configuration

Summary

Security has been, is, and always will be a game of cat and mouse. Researchers will come up with ever improving encryption algorithms and malicious actors will find increasingly creative ways to compromise those algorithms. For a production-ready project your job is to do everything you can to stay ahead of that curve using recommended security practices where possible.

And old parable comes to mind. If a bear is chasing you and a group of friends in the woods, you don’t have to be faster than the bear. You just have to be faster than your slowest friend. Security is similar. Attackers generally look for low-hanging fruit to exploit. It behooves you to not be the most insecure device on the market. Better yet, strive to be the fastest runner in your friend group because who knows how hungry that bear is. 😉

Join the community and get the weekly Production ESP32 newsletter. Concise, actionable content right in your inbox.

© Kevin Sidwar

Comments powered by Disqus.