Lack of TLS 1.2 breaking apps in older Androids

I have a couple of old Android 4.x devices (4.4.2 and 4.4.4) that I’m still using. I’ve looked into running a custom ROM on them (I actually bought one of them thinking it would run LineageOS), but I can’t find anything that supports the particular set of hardware in either of these devices. I suspect there are a number of folks in this situation, who can’t afford a device running a newer Android version, and don’t know how to install a custom ROM (even if their device is supported by one).

As well as being unsupported, these old Android versions don’t support TLS 1.2. In fact, due to hardware vendor incompetence (or laziness), there are devices on newer versions of Android that don’t either. This creates problems in a number of apps in the F-Droid repo. Some don’t work at all (eg RiotX), some work inconsistently (eg AntennaPod). I suspect this is also the cause of the bug that stops F-Droid auto-updates working in some of these devices.

When I reported the AntennaPod issues on their GH repo, they said they have a workaround, but it depends on nonfree code, so it’s not available in the F-Droid versions. I presume they are using the widget described in a blog post by Ankush Gupta of Quizlet;

“… Google Play Services provides a way to do this. The solution is to use ProviderInstaller from Google Play Services to try to update the device to support the latest and greatest security protocols.”

Is there any way to create a free code implementation of this, that could be included in the F-Droid app itself, or shipped with the F-Droid builds of all the apps included in the F-Droid repo that require TLS 1.2+?

1 Like

@strypey,
Good question. I am by no means an expert, but I do have a YouTube channel where I teach people how to build custom ROMs for their phones and dabble with app building from time to time.

In theory, as you mention, if your phone is API 16+ (android 4.1+) it should have at least an outdated TLS. 1.1 and 1.2 in it. In most cases, the app maker just needs to force the app to use it, with code like this:

However, if you want the SSL library updated, you can use the code your article pointed out and call Google Play Services for a download. But, as many here are trying to be as FOSS as possible, this presents a problem.

But, as an open source alternative, you can bundle the latest openssl into your app when you build it. However, this makes the end user rely upon the app maker to constantly update their app with the latest updates to openssl. But I think that is a lot of work for the app maker and instead they would just drop old device support.

One could, in theory, make a root app that pulls a few strings to do what the play store app does in the background, but that may get complicated as you work on each version of Android. Such an app would require a lot of in depth knowledge to make work properly in every instance.

So, in my opinion, the best FOSS answer would be to have any FOSS app supporting Android 4.1-4.4 and using TLS also bundle openssl in their app, but this would be a lot of work keeping it updated.

Edit: just some thoughts from a non-expert. :smile:

Hi, the user Slinger on GitHub found an alternative for Google’s ProviderInstaller. Unfortunately, it increases the apk size noticeably and needs to be kept up-to-date. I built a little mock-up that allows to distribute the provider centrally in one app and then load the library from other apps. This is basically the same that Google does. If one central app (for example the F-Droid client) would provide the library, it would not be necessary that all apps bundle it on their own.

For detailed information and a pretty lengthy discussion, visit the Pull Request for AntennaPod: https://github.com/AntennaPod/AntennaPod/pull/4077

TLDR: AntennaPod’s F-Droid version will ship with a custom SSL stack (Conscrypt) until we find a better solution (for example using one that is provided centrally by F-Droid).

3 Likes

Do you want to write a blog post about those possibilities for publishing on f-droid.org? I think this is very much of general interest.

F-Droid also can’t to tls 1.2 on kitkat right now, though I think we’d want to do that. Have you looked on how that could be integrated into fdroid client yet?

  • reads the pr discussion now.
1 Like

Including it in an app by bundling Conscrypt is pretty easy:

implementation "org.conscrypt:conscrypt-android:2.4.0"
Security.insertProviderAt(Conscrypt.newProvider(), 1);

The problem is that every app is then forced to update the provider regularly. Also, it increases the apk size. So the idea would be to write a provider application that does nothing but bundling Conscrypt and providing a stable API that allows to use the ClassLoader. It can then be updated independently from the apps that include the provider (like AntennaPod, F-Droid). The provider app could do something like this:

public static void install() {
    Log.d(TAG, "Installing provider...");
    Security.insertProviderAt(Conscrypt.newProvider(), 1);
    Log.d(TAG, "Provider installed successfully.");
}

One could then maintain a library that does the security checks and ClassLoader handling:

Context targetContext = context.createPackageContext("com.bytehamster.providerinstaller",
        Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
ClassLoader classLoader = targetContext.getClassLoader();
Class var2 = classLoader.loadClass("com.bytehamster.providerinstaller.ProviderInstallerImpl");
Method installMethod = var2.getMethod("install", new Class[]{ });
installMethod.invoke(null);
installed = true;

Code samples are also available (and a bit more detailed) on GitHub Gist.

Main problem: By including the provider, you execute foreign code in the context of your application. How do you verify that the provider does not do malicious things? I have some ideas how to deal with this:

  1. Let a trusted application be the provider. The F-Droid application itself could bundle the provider, keep it updated and provide the stable API. Apps could then hard-code the F-Droid package name and verify its signature. The problem with this approach is that users who build F-Droid themselves or use alternatives like G-Droid could not use the updated libraries.
  2. Send an intent that allows every app to be a provider. The library could query the Android system for compatible Conscrypt providers and include one of them. The problem with this approach is that any app could reply. Therefore, it would be possible to only include the provider if it is a system app. Then the F-Droid privileged extension could bundle and provide Conscrypt. If a malicious app managed to get system status, it is too late anyway, so we do not need to check a specific signature in this case. Many F-Droid users probably do not use the privileged extension though.
  3. Provide an open-source re-implementation of Google’s provider installer library (does this infringe their TOS?) and use ClassLoader to load Google’s provider if available. If it is not available (= no Google Play services and no microG), check for a system app like in the second idea. Then the problem would only occur to users who use a system without Google services (likely a custom ROM), so they know how to install the privileged extension as system app.

I am wondering if an app that uses the ClassLoader to run another (closed-source) app’s code can be considered open-source. Using ClassLoader for Add-Ons would be a pretty nice use-case but one can not really prevent executing the code of closed-source Add-Ons.

End note: I am pretty busy with maintaining AntennaPod. I am therefore not able to maintain the library and installer app. The proof-of-concept above (also on GitHub Gist) will probably be the only code I write for this :slight_smile:

What exactly do you think I should write there? Something like this post, together with the problem statement from the first post?

3 Likes

For the reasons you stated I don’t think it’s a good fit to include this into fdroidclient itself. BUt I think instead the simplest solution would be to make a standalone app, say org.fdroid.securityprovider and just hardcode the signatur of this app whereever it is used.

Does microg implement this already actually? :thinking: Could this be used/reused somehow?

Yep, pretty much! :slight_smile:

1 Like

Yes: https://github.com/microg/android_packages_apps_GmsCore/blob/master/play-services-core/src/main/java/com/google/android/gms/common/security/ProviderInstallerImpl.java

Not sure. Conscrypt seems to work just fine with one single line of code for initialization (in the provider app).

How would you encourage users to install that app, then? This sounds like additional work for developers who include the library (user confirmation, installing, etc).

1 Like

Well, you only need to install this, if you have connectivity problems, and you likely have that in all of your apps then. googling the problem should quickly point you in the direction of installing this app might fix it. Someone could of course also include a check for this in their app, test connectivity to a TLS v1.2 only server and if that fails with not supported then recommend installing this app.

2 Likes

Text proposal below. Any opinions? Did I forget something? If you are fine with it, I will create a PR for the website.

---
layout: post
title: "Android updates and TLS connections"
author: "ByteHamster"
authorWebsite: "https://github.com/ByteHamster"
---

Android device vendors are known for their short support periods. They only provide updates for a limited time or even ship devices with old Android versions. Developers then have to deal with a fragmented distribution of Android versions. Android 4.x is still used by around 2% of the active devices on Google Play. Old phones usually still have decent performance and updating them with Custom ROMs makes them well-suited even until today. Unfortunately, not all devices support Custom ROMs and not all users want to deal with the trouble of installing one.

AntennaPod is an open-source podcast manager for Android that I maintain. AntennaPod does not rely on a centralized server but fetches the podcast feeds directly using the device. This is pretty nice for user’s privacy because nobody gets a full list of your subscriptions. Unfortunately, more and more users with old Android versions experience problems with some podcast feeds. The reason is that servers are updated to more recent TLS versions that are not available on those devices. Upgrading the TLS version of servers is definitely a good thing because it makes the communication more secure. That’s why most browsers ship their own stack that they can update independently of the device vendors. Unfortunately, all other apps that are dependent on connections to many servers out of their control, like AntennaPod, still experience issues. Users then see download errors like the following: Failure in SSL Library, usually a protocol error. This is pretty bad because it prevents users from listening to podcasts. While this is basically the device vendor’s fault, it ultimately backfires on app developers.

TLSv1.1 and TLSv1.2 are supported starting with Android 4.1 and were enabled by default in Android 5.0. There also is a number of Cipher suites and certificates that are not supported in Android 4.x. While this problem currently only affects Android 4.x users, more will follow in the future as servers are upgraded to TLSv1.3. TLSv1.3 was first shipped with Android 10, which runs on about 8% of the active devices on Google Play. You can find more details about the SSL support in Android on the SSLSocket reference page. Developers now have to deal with the fact that vendors neglect software updates.

This is a known problem, so Google published a library called ProviderInstaller. It’s pretty easy to use (ProviderInstaller.installIfNeeded(context);) and fixes all those problems instantly. Because the provider is kept up-to-date independently of the app using Google Play Services, app developers do not need to worry about this anymore.

This sounds too good to be true, doesn’t it? Well, Google’s ProviderInstaller has a pretty big drawback. The library is closed-source, so it can not be used for apps published on F-Droid. AntennaPod currently builds with two different flavors: one that is 100% free software for F-Droid and one with the ProviderInstaller library for the Google Play Store. This means that the F-Droid users are left behind. They still experience connection problems to some servers and consider this a bug in AntennaPod.

An alternative that works with F-Droid is to include the open-source library Conscrypt. Bundling Conscrypt with an app is just as easy as using Google’s ProviderInstaller but it has two major disadvantages:

  • App developers are forced to update the provider regularly. This is additional work for the maintainers and might cause trouble with apps that have a slow development cycle.
  • Bundling Conscrypt increases the apk size. In case of AntennaPod, this means that the app gets around 4 MB bigger, which is a 40% increase just for this mostly invisible change. Now consider that every app would have to do that in order to get an updated TLS stack. If every app on F-Droid that deals with networking needs to bundle additional 4 MB of TLS libraries, this would increase storage usage noticeably.

Fortunately, there is a solution for this! The idea is to write an open-source provider application (just like Google’s provider) that does nothing but bundling Conscrypt and providing a stable API that allows other apps to load it. It can then be updated independently of apps like AntennaPod or F-Droid. The provider app could simply have a class with something like this:

public static void install() {
    Security.insertProviderAt(Conscrypt.newProvider(), 1);
    Log.d(TAG, "Provider installed.");
}

We could then develop an open-source library that uses the ClassLoader to include the Conscrypt library from the provider app.

Context targetContext = context.createPackageContext("com.bytehamster.providerinstaller",
        Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
ClassLoader classLoader = targetContext.getClassLoader();
Class installClass = classLoader.loadClass("com.bytehamster.providerinstaller.ProviderInstallerImpl");
Method installMethod = installClass.getMethod("install", new Class[]{ });
installMethod.invoke(null);
installed = true;

Code samples are also available (and a bit more detailed) on GitHub Gist.

Obviously, just including code from another app is a security risk. By including the provider, you execute foreign code in the context of your application. How do you verify that the provider is not malicious? There are some ways to verify that the provider can be trusted:

  • Let a trusted application be the provider. The F-Droid application itself could bundle the provider, keep it updated and provide the stable API. Apps could then hard-code the F-Droid package name and verify its signature. The problem with this approach is that users who build F-Droid themselves or use alternatives like G-Droid could not use the updated libraries.
  • The library could query the Android system for compatible Conscrypt provider apps and include one of them. To make sure that this is secure, it would be possible to only include the provider if it is a system app. Then the F-Droid privileged extension could bundle and provide Conscrypt. Many F-Droid users probably do not use the privileged extension though, so we could still not reach all users.
  • To me, the best solution seems to have a standalone app (like org.fdroid.securityprovider) that provides Conscrypt. This was also discussed on the F-Droid Forum and originally suggested by @bubu. A trusted entity like F-Droid could build the provider and sign it with their keys. Those could then be verified by the calling apps that want to include Conscrypt.

I would love to see some movement in this direction. We should definitely explore the possibilities of a fully open-source security provider app. Unfortunately, I am pretty busy with maintaining AntennaPod. I am therefore not able to maintain the library and installer app. There is a proof-of-concept available on GitHub Gist but it misses security checks. Also, it would probably be good to add some simple ways to prompt users to install the provider on TLS failures. If you are interested in making an open-source provider possible, have a look at the F-Droid Forum.

Some notes:

  • This post was written from an app developer’s perspective. I try to make AntennaPod work best for all users, including those with Android 4.x. From a user’s perspective, you should think about whether you still want to use old Android versions like 4.x. Android 4.4 was released in 2014, which is 6 years ago. Many critical security issues have been fixed since then. Exposing such an old device to the Internet might be a pretty big security risk, independently of the TLS libraries that are included in apps.
  • Before looking into this, I did not know that you can use ClassLoader to execute other app’s code. This is pretty nice and can be used for things like plugins. I would love to see what other developers build with that.
4 Likes

@ByteHamster,

This write up looks great! I only have one comment/question:

If you hard code apps to check the signature of the trusted F-Droid “security provider app”, wouldn’t that also run back to the same problem if someone wanted to build the security app themselves?

Granted, I think this is the best solution for preventing every app developer from dropping old device support or having to bundle it themselves, just a thought about how this dependency would work for those who build them locally.

If this is the case, then bundling it with F-Droid would be simpler than making a separate app, wouldn’t it, because the end result is the same? People who build F-Droid themselves are likely to build the security app themselves also. E.g., the paranoid or enthusiast who builds it all themselves would still need to download the F-Droid security app because they can’t build it themselves or else their other apps will not work anyways.

Right? Or am I misunderstanding something (happens alot, I’m not very bright). :smiley:

2 Likes

Yes, they would run into the same problem.

The difference is that there are fewer reasons for building the provider app yourself (except trust). I build some apps myself because I (rarely) do pull requests for them. Doing lots pull requests for the provider app should not be necessary - the provider app does not have a UI and really limited functionality. Some users prefer G-Droid over F-Droid (UI, ratings, etc) – but having multiple provider apps is pretty pointless. So I think less users would build the provider app than the F-Droid client.

:joy:

1 Like

Sounds good! :slight_smile:

2 Likes

Great write-up @ByteHamster! Thanks for working on this important issue. I think your solution makes a lot of sense. I think that fdroidclient could easily include Conscrypt, since it would be nice to have there anyway. Then it could make Conscrypt available to apps via ProviderInstaller. A standlone org.fdroid.securityprovider also makes sense, there could even be both options, then have ProviderInstaller check org.fdroid.securityprovider, then org.fdroid.fdroid, then Google Play Services.

I don’t think it makes sense to include Conscrypt in F-Droid Privileged Extension. That is designed to be as minimal as possible and only handle install and uninstall commands. It has no networking at all.

3 Likes