Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not possible to use WebSocketTransport exclusively on TLS 1.3 #3497

Open
fortesdev opened this issue Jan 8, 2025 · 4 comments
Open

Not possible to use WebSocketTransport exclusively on TLS 1.3 #3497

fortesdev opened this issue Jan 8, 2025 · 4 comments
Labels

Comments

@fortesdev
Copy link

fortesdev commented Jan 8, 2025

Summary

The following problem was probably always in the iOS Apollo library, and continues in the latest version (1.15.3):

It is not possible to use WebSocketTransport on TLS 1.3 (cipher TLS_AES_256_GCM_SHA384). Our implementation works just fine on endpoints that support TLS 1.2 and other ciphers.

Version

1.15.3 and older

Steps to reproduce the behavior

  1. Target an endpoint that enforces as minimum TLS version 1.3 (cipher suite TLS_AES_256_GCM_SHA384), and try a GraphQL subscription on it.
    Result: TLSv1.3 SSLHandshake failed (-9836) on real device. OSS error -9836 is errSSLPeerProtocolVersion.

  2. In the web socket client, add enabledSSLCipherSuites = [TLS_AES_256_GCM_SHA384].
    Result: FoundationStream's connect method ends with SSLSetEnabledCiphers returning -50. SSLSetEnabledCiphers is deprecated, by the way.

Some notes:

  • Exact same implementation/subscription on similar endpoint that allows TLS 1.2 (with more ciphers) do work on iOS.
  • Our Android and Web implementation with Apollo do work on that endpoint TLS 1.3.
  • Nothing helped to make it work on iOS (disableSSLCertValidation, NSAppTransportSecurity changes, etc.)

Logs

No response

Anything else?

Not sure if the certificate strength can be a factor - The new Digicert G5 root certificate is a 4096 bit one.

@fortesdev fortesdev added bug Generally incorrect behavior needs investigation labels Jan 8, 2025
@calvincestari
Copy link
Member

Hi @fortesdev, I'm a little stumped on this one. I wonder if it's related to us still targeting iOS 12 and therefore using the old deprecated security APIs? Is there some incompatibility there?

For context - the 2.0 work will allow us to jump up to iOS 15 minimum at which point we can finally move past the deprecated security APIs.

@fortesdev
Copy link
Author

Hi @calvincestari,
It is related to the framework used for sockets. As seen here:

"TLS clients using the SecureTransport APIs can’t use TLS 1.3" https://support.apple.com/en-gb/guide/security/sec100a75d12/web

A (potential) solution could be updating the transport implementation in v1.X to match current Starscream, which uses Network.framework while still being iOS 12 compatible. Current Apollo seems inspired by a very old Starscream (2018?).

https://github.com/daltoniam/Starscream

Is it feasible? We'll take a look at this refactor/replacement in the transport layer (without much hope) in our end.

@calvincestari
Copy link
Member

A (potential) solution could be updating the transport implementation in v1.X to match current Starscream, which uses Network.framework while still being iOS 12 compatible.

I do believe we looked into this a long while ago and it was not possible/feasible. I'll have to go digging to see if I can find reference about why that was though.

Current Apollo seems inspired by a very old Starscream (2018?).

That's correct. Our vendored implementation (as of release 1.16.0) is based on the last version of StarScream 3; 3.1.2.

@fortesdev
Copy link
Author

Hi @calvincestari,

I managed to make it work. Can't prepare a PR given it is too tailored to our needs and is kinda dirty still, but pretty much it is as follows:

  1. I removed Apollo/WebSocket from the project's remote dependencies.
  2. Added Apollo/WebSocket as local dependency (to rename a few things causing collisions). This probably can be avoided but I went with a quicker solution, may improve later.
  3. Added Starscream as remote dependency.
  4. Given I could avoided the few collisions, I created the following wrapper class (code for it appended in case it helps someone)
    OurNewWebSocket.txt
    ):
    public class OurNewWebSocket: WebSocket (Startscream's), WebSocketClient(Apollo's), WebSocketDelegate(Apollo's)
  5. The class above connects Starscream's events with the Apollo websocket delegate (the most important/basic events, that's it).
  6. Then I created an ApolloClient using:

/* Starscream socket part /
let pinner = FoundationSecurity(allowSelfSigned: true) // don't validate SSL certificates
let socket = OurNewWebSocket(request: urlRequest, certPinner: pinner)
socket.delegate = socket // itself will transfer Starscrean's events to Apollo's delegate
socket.connect()
/
End of starscream socket part */
ApolloClient(networkTransport: WebSocketTransport(websocket: socket), store: ApolloStore.init())

We still need to test the stability/reconnection and improve the error handling, but it works with TLS 1.3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants