Does URLSession support ticket-based TLS session resumption

My company has a server that supports ticket-based TLS session resumption (per RFC 5077). We have done Wireshark captures that show that our iOS client app, which uses URLSession for REST and WebSocket connections to the server, is not sending the TLS "session_ticket" extension in the Client Hello package that necessary to enable ticket-based resumption with the server.

Is it expected that URLSession does not support ticket-based TLS session resumption? If "yes", is there any way to tell URLSession to enable ticket-based session resumption? the lower-level API set_protocol_options_set_tls_tickets_enabled() hints that the overall TLS / HTTP stack on IOS does support ticket-based resumption, but I can't see how to use that low-level API with URLSession.

I can provide (lots) more technical details if necessary, but hopefully this is enough context to determine whether ticket-based TLS resumption is supported with URLSession.

Any tips / clarifications would be greatly appreciated.

Answered by DTS Engineer in 810036022

Focusing on the HTTP side of this right now, my understanding is that tickets are enabled on HTTP/2 but not HTTP/3. Does that match your testing?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Focusing on the HTTP side of this right now, my understanding is that tickets are enabled on HTTP/2 but not HTTP/3. Does that match your testing?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

We are using the older HTTP/1.1 protocol, but with servers that support TLS 1.2. The version of HTTP shouldn't matter since the TLS (formerly SSL) session is established before the HTTP layer of the protocol starts.

There are some security concerns about ticket-based session resumption in TLS 1.2 (search the web for "we-need-to-talk-about-session-tickets") that appear to be fixed in TLS 1.3. So perhaps the issue is that the Apple security team decided to not support ticket-based session resumption in TLS 1.2, and URLSession does not yet support TLS 1.3? ** Is there any way you could check with them? **

I will also try testing our app against a TLS 1.3 server to see if ticket-based resumption works there.

I really appreciate your help!

References I have found that do not quite answer my question:

Follow-up to Tad's question above, we have tried to use Network.framework (indirectly via the use of SwiftNIO Transport Services), which allows us to access the TLSOptions configuration. I am using the following APIs in Security.framework. https://developer.apple.com/documentation/security/sec_protocol_options_set_tls_resumption_enabled(::)

https://developer.apple.com/documentation/security/sec_protocol_options_set_tls_tickets_enabled(::)

When enabling both options above, (TLS 1.2) session resumptions work, but the session ticket is only reused once (the pattern is consistent across multiple requests in our testing). Using the same setup but setting the minimum TLS version to 1.3, session resumption does not work at all (the client never sends the pre_shared_key extension in the ClientHello packet with the necessary information for resumption). Our goal is to make session resumption work for TLS 1.3. Here is the simple HTTP Client that we are experimenting with.

final class NIOPlayerSession {
    private let bootstrap: NIOTSConnectionBootstrap
    private let tlsOptions: NWProtocolTLS.Options

    public static let shared = NIOPlayerSession()

    init() {
        self.tlsOptions = {
            let tlsOptions = NWProtocolTLS.Options()
            sec_protocol_options_set_min_tls_protocol_version(tlsOptions.securityProtocolOptions, .TLSv13)
            sec_protocol_options_set_max_tls_protocol_version(tlsOptions.securityProtocolOptions, .TLSv13)

            sec_protocol_options_set_tls_resumption_enabled(tlsOptions.securityProtocolOptions, true)
            sec_protocol_options_set_tls_tickets_enabled(tlsOptions.securityProtocolOptions, true)

            sec_protocol_options_set_verify_block(tlsOptions.securityProtocolOptions, { _, _, sec_protocol_verify_complete in
                sec_protocol_verify_complete(true)
            }, DispatchQueue.main)

            return tlsOptions
        }()

        // This is the prefered event loop group for iOS
        bootstrap = NIOTSConnectionBootstrap(group: NIOSingletons.transportServicesEventLoopGroup)
            .connectTimeout(.connectionTimeout)
            .channelOption(NIOTSChannelOptions.allowLocalEndpointReuse, value: true)
            .channelOption(NIOTSChannelOptions.waitForActivity, value: true)
            .tlsOptions(tlsOptions)
            .channelInitializer { channel in
                // 4
                channel.eventLoop.makeCompletedFuture {
                    try channel.pipeline.syncOperations.addHTTPClientHandlers()
                    try channel.pipeline.syncOperations.addHandler(HTTP1ToHTTPClientCodec())
                }
            }
    }
}

Also from the earlier answer, does the version of HTTP in use affect the outcome of TLS session resumption? Our servers only speak HTTP 1.1.

Thanks

To provide some more context, we also have the constraint that the server is connected to via IP address. According to TLS 1.3 Spec, it seems that a value for Server Name Identification (hostname) is needed for TLS session resumption to work correctly.

Sorry I didn’t get back to you sooner. TLS session resumption is a complex business and it’s taken me a while to find the time to update my experience with it for the modern world.

Also from the earlier answer, does the version of HTTP in use affect the outcome of TLS session resumption?

Yes [1]. However, you need to be careful drawing conclusion from the previous posts on this thread. Those are focused on URLSession, which means they’re using the Apple HTTP stack. You’re using SwiftNIO Transport Services, and so presumably the Async HTTP Client package, and that’s not the Apple HTTP stack.

In general, the Network framework TLS-over-TCP implementation establishes a byte stream to the remote peer, and it doesn’t care what you send over that byte stream. If you want to use an ancient HTTP version, that’s your business.

Our goal is to make session resumption work for TLS 1.3.

Why is that?

My understanding is that session resumption doesn’t get you any performance benefit when you’re using TLS 1.3 [2]. Do you need session resumption for correctness? [3]

When enabling both options above, (TLS 1.2) session resumptions work, but the session ticket is only reused once

Yeah, I’m seeing that as well. I’m gonna do some more digging on that.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] I can’t think of at least one case where this is relevant, namely that HTTP/3 requires QUIC requires TLS 1.3.

[2] Except in the 0-RTT (early data) case, but that’s the edgiest of edge cases IMO.

[3] I occasionally bump into this when dealing with folks implementing FTP, for example, this thread.

My understanding is that session resumption doesn’t get you any performance benefit when you’re using TLS 1.3

In the case of TLS 1.3, it is true that—except for early data—the number of RTTs does not decrease compared to a full handshake. However, when session resumption is used, the client does not need to receive the server certificate, which allows application data to be sent much more quickly. This becomes especially valuable in environments with unstable or low-bandwidth networks, where certificate size can have a significant impact on performance. By completely avoiding the transmission of the certificate through session resumption, these performance benefits are further amplified.

By contrast, in the case of TLS 1.2 session resumption, as noted in this thread, Apple’s current implementation appears to allow a session ticket to be reused only once—even if the lifetime hint remains valid. In principle, TLS 1.2 itself permits multiple reuses of a session ticket, but under this limitation, the intended benefit of consistently avoiding certificate transmission cannot be fully realized.

One point I would like clarification on is why sec_protocol_metadata_get_early_data_accepted exists. From my observations, when using TLS 1.3, session resumption appears not to work at all, which raises the question of why there is a need to confirm whether early data—which is inherently dependent on session resumption—was accepted.

Additionally, I would greatly appreciate your guidance on how to either:

  1. Reuse a session ticket issued under TLS 1.2 multiple times, or
  2. Enable TLS 1.3 session resumption (where a NewSessionTicket is delivered each time).
Does URLSession support ticket-based TLS session resumption
 
 
Q