Should we be defaulting to ECC keys now?

A significantly large percentage of the global population utilizes mobile devices for internet access. The devices typically have limited update windows and are used long after those support windows end.

I don't have any current browser or os stats, but the last time I checked (2 years ago?) over 50% of web activity across Africa was through Android OS and much of it was legacy. While most of these versions have ECC support in general, releases are not necessarily compatible with the ECDSA keys. IIRC, Android 7.1 doesn't support it (but earlier and later releases generally do).

Stuff like this may not matter in terms of audience demographics, but a giant percentage of the global population interacts with the web through legacy devices that can not be upgraded.

4 Likes

This is my hesitation with upgrading to the LAG (latest and greatest) (e.g. ECDSA) and to me a great example of why such "progress" is so slow, even in a technically fast-paced world. Some of it is idealism (e.g. I want the broadest user-base possible even though most "remote" users (physically, but especially culturally) will likely never come across "my work" with the key word there in my mind being "likely" rather than "absolutely"). It's also "inclusion" in the truest sense in that technological adoption very frequently (if not almost always) creates a wall that unnecessarily and non-beneficially excludes swaths of users.

2 Likes

Cloudflare actually open sources some of their stats on Cloudflare Radar. Being a very large CDN, they do have some interesting insights. Their data is searchable by time range & location, with various categories.

For your example, Cloudflare data indicates that 65.1% of the traffic caused by humans (bots excluded) in South Africa came from a mobile operating system in the past year (https://radar.cloudflare.com/traffic/za?dateRange=52w), with Android having a market share of 87.2% (https://radar.cloudflare.com/adoption-and-usage/za?dateRange=52w). They do not generally show specific User-Agent versions, though.

I did some digging for Android's TLS version history, as I was curious if there was anything except the 7.0 bug. According to Wikipedia, Android 4.1 was the first Android version to support TLS 1.2. This is apparently due to Android 4.1 being the first version to ship OpenSSL 1.0.1*, 1.0.1c to be exact, and OpenSSL 1.0.1 is known to be the first version to support TLS 1.2. These days, Android uses BoringSSL (a fork of OpenSSL), but BoringSSL did not exist when Android 4.1 was released. Android used OpenSSL with a few patches back then. OpenSSL 1.0.1 did support ECDSA keys for both client and server mode, unless compiled with OPENSSL_NO_EC. Android 4.1 compiled OpenSSL with EC enabled. So I conclude that all Android versions that supported TLS 1.2 also supported ECDSA.

There is a bug that only affects Android 7.0 - an Android version that was superseded by 7.1 just 3 months later. Still, some manufacturers managed to ship only 7.0 to their devices and never bothered to upgrade them to 7.1 before ditching support. Devices impacted by the bug were accidentally configured to only support the P-256 (prime256v1 is the OpenSSL/BoringSSL name) elliptic curve.

I actually did some more digging (a lot more to be honest :grinning:), because I don't think the actual underlying issue has been publicly documented. I cannot say for certain that this is the issue, but looking at lots of old source code I believe the problem was that Android's BoringSSL JNI bridge library ("conscrypt") used to call a BoringSSL method SSL_CTX_set_tmp_ecdh. According to the committer that fixed the bug, "this used to be needed to allow a server to handshake with ECDHE, but is now unnecessary for BoringSSL [...]". Calling this method in the old OpenSSL days forced OpenSSL to use P-256 for ECDHE handshakes - apparently enforced by Android because ECDHE would not work properly otherwise. At some point, a BoringSSL change (this is in the very early days of the project, and I couldn't find git history of these changes) resulted in a slight divergence from OpenSSL behavior. The new BoringSSL logic, likely brought in during standard library upgrades, resulted in a manual preset of the elliptic curves in the SSL context (set by the previous CTX_set_tmp_ecdh call) to not set the default curves in the TLS ClientHello message (this was apparently done to ensure that the server does not select a curve that the client is configured to not use). This then caused BoringSSL to only advertise P-256 support in the TLS handshake (ClientHello). A conforming server then aborts the handshake if it determines that it needs P-384 support for either ECDHE or ECDSA.

The fix is known to be merged into Android 7.1.1_r6, by simply no longer calling CTX_set_tmp_ecdh. This leaves the curves unset in the SSL context object, which in turn causes BoringSSL to use the default compiled-in curves (this used to be P-256, P-384 and Android-specifically P-521 as well).

Interestingly enough, if my analysis is correct, this has some (previously unknown?) implications:

  • It is technically possible to work around the bug server-side, by ignoring the TLS client's supported elliptic curve extension. As far as I've looked through the source code, the BoringSSL version will not complain if the server's curve disagrees with the client's advertisements, as long as it's technically supported. Doing this violates TLS specification, though.
  • The bug may not affect intermediate certificates, such as the P-384 curve used by Let's Encrypts intermediates. I believe this, because AFAIK servers do not check the entire chain for elliptic curves when determining compatibility with the supported curves extension (RFC4492 says that servers should not do this, as clients may not need the chain (e.g. pinning)). If the server certificate is P-256, everything should be fine as well (P-256 will be used both for key exchange and server cert verify, chain verification is unaffected by the bug).

This is irrelevant for most people in practice, though, because Android 7.0 does not trust ISRG Root X1 out of the box - so certificate verification will fail anyway.

PS: Android 7.0 supports up to Chrome 118/119. With the help of @MikeMcQ we verified recent Chrome versions use both their own BoringSSL + own trust store (Chrome 105+ has its own verifier with custom trust store). This means that these Chrome versions are both unaffected by the 7.0 bug and trust ISRG Root X1. Only non-browser apps may be affected by trust or ECDSA issues.

8 Likes

According to those same stats, 92% in South Africa use HTTP/2 or 3, 93,5% use TLS 1.3 or QUIC, and TLS 1.0 isn't even mentioned anymore. So that's certainly not dated. If TLS 1.2 is the floor, ECC will not be an exclusion criterion anymore.

6 Likes

Probably isn't the best example.

I'd look at India or Nigeria.

5 Likes

I'm just glad @Nummer378 is on our side :sweat_smile:

6 Likes

Chrome 49, the latest version to run on Windows XP, does not support ECDSA, but it's the most recent (from 2016) & most relevant client I'm aware of that does/did not support ECDSA (edit: as it's the only one that does support TLS 1.2).

We dropped support for it a few years ago, when we moved from dual RSA+ECDSA to ECDSA-only.

2 Likes

You're right. Sorry, I've fixed my post. I was misremembering my browsers -- Firefox on Windows XP supports ECDSA.

Chrome 49 does appear to support ECDSA on Windows 7, so clearly I have my version matrices a bit messed up. I think this is an interesting enough that I would like to put together a "caniuse" type matrix here.

4 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.