Android and TLS1.2


(Simon) #1

This is a collection of hopefully all relevant discussions about the issues due to android support of tls1.2, which is required by Syncthing.

The original issue is:

There’s two questions regarding this:

  1. Which Android versions don’t support TLS1.2?

The issue with android seven is this report: https://github.com/syncthing/syncthing-android/issues/1255#issuecomment-466364359

The error reported there was on android 7.0:

02-22 12:15:49.246 W/SyncthingNativeCode( 8706): 2019/02/22 11:15:49 http: TLS handshake error from 127.0.0.1:......: remote error: tls: illegal parameter

02-22 12:15:49.259 W/SyncthingNativeCode( 8706): 2019/02/22 11:15:49 http: TLS handshake error from 127.0.0.1:.......: tls: client offered an unsupported, maximum protocol version of 301
  1. What exactly is the purpose of using https here?

Reset phone: Can't get past "Loading"
Is the default Android app actively maintained at the moment?
(Catfriend1) #2

To sum up Nutomic’s answer: The class SyncthingTrustManager is a clever thing (Zillode told me he originally contributed it). It loads syncthing’s web ui https cert from the flat files in the app’s private dir and verifies if the service reached at https://:8384 is really the right syncthing instance having that cert.


(Catfriend1) #3

Android 7.0 misses an elliptic curve “which google forgot to implement” so tls 1.2 is supported but with a weak cipher syncthingnative does (no longer) use since 14.5x.


#4

Which cipher is that, which would be used instead [assuming that syncthing go accepts it]? I would guess that there should be plenty of (secure) fallback options, the list of cipher suites is not that small.

On another side: Google writes that the ProviderInstaller from Google Play services can bring support for TLSv1.2 to old Android versions, even back to Android 2.3. But I suppose that requires the Google Play services being available, which may not be the case on all Androids. But it could be used, if available.


(Catfriend1) #5

From my memory I recall ECDH missing and RSA available… Would need to search again.

I know that gplay services can solve this but … honestly hate them (The app is on fdroid and it would be against their rules to include google gms). Microg doesn’t have that implementation, too.


#6

I think I found what you mean: It’s probably this: https://community.letsencrypt.org/t/warning-android-7-0-clients-not-browsers-can-only-use-curve-prime256v1/23212, https://issuetracker.google.com/issues/37122132.

So yeah, this affects both ECDHE exchanges as well as ECC certificates. The syncthing https certificates are probably ECC? Otherwise, if the certificate is not a problem a fallback to something like DHE-RSA-AES256-GCM-SHA384 or DHE-RSA-AES128-GCM-SHA256 would be totally fine, as long as >= 2048 bit numbers are used in DH and RSA.

It justs feels wrong to say “sorry, but TLS 1.2 doesn’t work on Android 7.0, an OS which is has a market share of 18.1% worldwide.”


(Catfriend1) #7

Do I understand it right that the fallback (if we would consider that) would then have to happen in the go part of syncthing?


#8

The syncthing native/go is the server in this configuration. In TLS, the client sends a list of “what it can do” and the server then responds with “what to do”, meaning it selects the cipher suite which is to be used. Generally the server should select whatever is considered most secure. If some cipher is broken on the client side, the client should not offer this suite to prevent it from being selected. If the server is unsatisfied with the list of available cipher suites send by the client, the handshake will fail.

I probably need more data on this: Is the syncthing https server restrictive with ciphers? I would assume Go allows anything that is not totally broken, as that is the usual configuration. Sometimes developers apply additional restrictions on the list of allowable ciphers to further increase security. If the syncthing server offers some kind of fallback without elliptic curves, the client just needs to support it too.

My first impression is that only the client side (Android, Java) needs a littlebit of tweaking in case elliptic curves are problematic.


(Bt90) #9

Why not use something like okhttp?

It seems to support older ciphers on earlier Android versions: https://github.com/square/okhttp/wiki/HTTPS

Supported ciphers by my local Syncthing node according to cipherscan

prio  ciphersuite                  protocols  pfs                 curves
1     ECDHE-RSA-AES256-GCM-SHA384  TLSv1.2    ECDH,P-521,521bits  secp521r1,secp384r1,prime256v1
2     ECDHE-RSA-AES128-GCM-SHA256  TLSv1.2    ECDH,P-521,521bits  secp521r1,secp384r1,prime256v1
3     ECDHE-RSA-AES128-SHA256      TLSv1.2    ECDH,P-521,521bits  secp521r1,secp384r1,prime256v1
4     ECDHE-RSA-AES128-SHA         TLSv1.2    ECDH,P-521,521bits  secp521r1,secp384r1,prime256v1
5     ECDHE-RSA-AES256-SHA         TLSv1.2    ECDH,P-521,521bits  secp521r1,secp384r1,prime256v1
6     AES128-GCM-SHA256            TLSv1.2    None                None
7     AES256-GCM-SHA384            TLSv1.2    None                None
8     AES128-SHA256                TLSv1.2    None                None
9     AES128-SHA                   TLSv1.2    None                None
10    AES256-SHA                   TLSv1.2    None                None

Edit2: or via nmap

$ nmap --script ssl-enum-ciphers -p 8384 127.0.0.1

Starting Nmap 7.60 ( https://nmap.org ) at 2019-02-25 23:15 CET
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00010s latency).

PORT     STATE SERVICE
8384/tcp open  marathontp
| ssl-enum-ciphers:
|   TLSv1.2:
|     ciphers:
|       TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (secp521r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (secp521r1) - A
|       TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (secp521r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (secp521r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp521r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp521r1) - A
|       TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_GCM_SHA384 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|     compressors:
|       NULL
|     cipher preference: server
|_  least strength: A

Nmap done: 1 IP address (1 host up) scanned in 0.47 seconds

#10

I’m getting different results. This was taken from a pretty much clean Ubuntu with freshly installed syncthing v1.0.1.

What’s clearly visible here is that only cipher suites with ECDHE are supported. Also if I’m reading the output correctly only curve 521 is allowed, which doesn’t work on Android 7.0 due to the bug.

Edit: Using an entire library for something as “simple” as communicating with a REST API seems a littlebit over the top for me.


(Bt90) #11

Seems like my certificate is pretty old which would explain the difference.


#12

Yeah, my freshly generated certificate is also fully ECC.

This is indeed a problem, because this definetly requires ECC working on Android. So there’s either a solution to get ECC working reliably on all supported Android versions, or there must be a fallback to not use TLS for API communication.


(Bt90) #13

I agree that it should be simple but it seems to be the only sane way to support older Android versions without lowering the security standard.


(Catfriend1) #14

I didn’t try okhttp yet as the existing code in EventProcessor is a tender child because it fires an ongoing https request to read events and it has been to be carefully checked if another library can do it as well without draining much battery. Android 7 currently has its market share but some time in the future it will die out as Google might cut support for important cloud apps as they did on 4.x already.


(Simon) #15

The list of accepted ciphers can be found here: https://github.com/syncthing/syncthing/blob/master/lib/tlsutil/tlsutil.go#L30
It includes RSA ciphers. The log from the report above implicates that the advertised TLS version was 1.0 (protocol version of 301 is 1.0 according to google), so the problems seem to be more general than just a cipher suite.

I don’t think any time should be spent on 4.x - just disable TLS. Using such an outdated system has much higher security implications than someone targeting Syncthing to get control over data, that’s anyway accessible to any app with data access permissions. For 7.0 I’d try to get confirmation on the one report we have and if it really doesn’t work, just disable it as well - at least in a first step. Something more elaborate can be done later on if anyone feels like it.


(Catfriend1) #16

I’ll make the PR, expecting to do it on the weekend.


#17

I believe that there might be two entirely different problems on Android 7.0 - Altough I haven’t verified this yet.

There’s currently one user which apparently only has TLS 1.0 support. This is really strange and at this point I have no explanation for it - it may be related to a very specific setup, or it’s caused by another issue.

The second issue that may arise when TLS 1.2 is enabled on Android 7.0 is that the handshake will fail, due to incompatible cipher suites. Right now, this problem is entirely hypothetical, so it may not exist at all.

Looking at the link you send, there are indeed ciphers with RSA (some for signing only and some for signing and secret transport [no PFS]). But if my TLS understanding is correct, these ciphers can only be used when the certificate is RSA. This is likely the case for older syncthing installs, but as I showed above it is not the case for new installs - those use ECDSA and therefore the ciphers with RSA signing cannot be used.

This explains the image I posted above - only ciphers with ECDSA signing are available. This effectively kills everything that is not fully ECC with PFS.

And that constellation is, according to known bugs, unsupported on Android 7.0 because the elliptic curve used doesn’t work.

I tried to verify this in an Android emulator, but the Chrome browser inbuild has it’s own TLS stack which is not affected by the issue. And in the Syncthing-Fork app I can’t force TLS, it seems to always fall back to HTTP. Maybe @Catfriend1 can do a test build with TLS forced, which can then be tested on 7.0, to see whether this issue actually exists or if all of this was just noise.


(Bt90) #18

What about Unix sockets?


(Simon) #19

Thanks for the info.

What known reports? The one reported error I linked above refers to TLS1.0, not mismatching cipher suites. To my knowledge an independent reproduction of a/the issue on 7.0 is still outstanding.

Can’t you just test with the latest stable release (0.10.17) of the “non-fork”, that should have TLS force enabled as before.


#20

I’ve bad news: I can reproduce the issue with “unsupported, maximum protocol version” error. The recently updated syncthing app doesn’t work at all on my Android 7.0 emulator.

Right now, I’m collecting data. I will edit this post once I have some more factual information.

EDIT:

The issue with elliptic curves being problematic on Android 7.0 is documented in different places, see my links in earlier posts for examples - if you google further you find about a dozen reports with TLS handshake erors with Android 7.0.

On the Android side: I’ve installed the official/standard/non-fork Syncthing app on an Android 7.0 emulator. The app has recently been updated to bundle syncthing v1.0.1.

The following happens after install:

  1. After going through the standard setup wizard, a window appears saying something like “generating keypairs” (forgot the exact message). This window never disappears but just stays on forever. I assume that is because the app cannot communicate with the syncthing API.

  2. After forcibly stopping the app and re-opening it, a window “loading” appears and never disappears. After some time, a note reading “Syncthing is taking very long to load. Use the logs to check for any errors”.

  3. Clicking OPEN LOG shows a lot error messages, always repeating the same three messages. These are:

<Date> W/SyncthingNativeCode( 2743):<Date>http: TLS handshake error from 127.0.0.1:<increasing port number>: remote error: tls: illegal parameter
<Date> W/SyncthingNativeCode( 2743):<Date>http: TLS handshake error from 127.0.0.1:<increasing port number>: remote error: tls: client offered an unsupported, maximum protcol version of 302
<Date> W/SyncthingNativeCode( 2743):<Date>http: TLS handshake error from 127.0.0.1:<increasing port number>: remote error: tls: client offered an unsupported, maximum protcol version of 301

The messages repeat in this order. I’m still trying to figure out how to properly debug TLS on android, but my current theory is this:

  1. The Android client attempts a TLS 1.2 connection, but fails due to the discussed ECC problem.
  2. Due to TLS 1.2 failing with a cipher problem, the client attempts a reversion to TLS 1.1 [by pretending that it only supports the older standard] and then TLS 1.0, which obviously fails because the server only allows TLS 1.2. Such client-initated fallbacks are probably implemented as workarounds for version intolerance (https://timtaubert.de/blog/2016/09/tls-version-intolerance/).
  3. This causes communication between the syncthing app and the syncthing native to fail completly.