How to support TLS 1.3 using SwiftNIO?

I use man in the middle to capture iPhone traffic, I know SwiftNIO supports TLS 1.3. I want to know how to implement this feature. Is there any place to specify the TLS version? @lukasa

Are you using swift-nio-ssl or swift-nio-transport-services for your TLS?

In both cases, I'll note, TLS 1.3 is supported by default and does not need to be manually enabled.

1 Like

@lukasa swift-nio-ssl

So, What should I do to get the final handshake TLS version?

This is currently not exposed by swift-nio-ssl. Please file an issue to request that the information be added.

Please post the issue number here when it has been opened @yb_cherry. Thanks!

Thank you!

1 Like

Thanks!

@lukasa hi, lukasa, Does swiftnio support quic and http/3?
if not, Do you have a plan to support quic and http/3?
What is the deadline?

SwiftNIO does not support QUIC or HTTP/3 at this time. We have no immediate roadmap to do so, but we'd like to do it eventually.

Wow, that sounds great! Look forward to that day soon!

@lukasa @agnosticdev
thanks for your help about this: Get the final handshake TLS version · Issue #337 · apple/swift-nio-ssl · GitHub.

But when I ran the following code in channelRead, I got the error: NIOCore.ChannelPipelineError notFound

func channelRead(context: ChannelHandlerContext, data: NIOAny) {
    let httpResponsePart = unwrapInboundIn(data)

    // First Option, creating an EventLoopFuture to extract the TLSVersion
    let tlsVersionForChannel = context.channel.nioSSL_tlsVersion()
    switch httpResponsePart {
    ...
    case .end(_):
    	// Extract the finished TLSVersion before the channel is about to be closed.
    	// This should be the final TLSVersion used on the connection.
        tlsVersionForChannel.whenComplete { result in
            switch result {
            case .success(let tlsVersion):
                print("TLSVersion: \(tlsVersion!)")
            case .failure(let error):
                print("error: \(error.localizedDescription)")
            }
        }
        closeFuture = context.channel.close()
        promise.succeed(())
    }
}

What should I do to solve this problem?

I wonder if this is an endpoint issue?
Does the code fall into the .failure case for https://::1:4433/get ?
When I test with https://apple.com from the latest main branch I see TLSVersion: tlsv13.

var url: URL
...
url = URL(string: "https://apple.com")!

private final class HTTPResponseHandler: ChannelInboundHandler {
	...
    func channelRead(context: ChannelHandlerContext, data: NIOAny) {
        let httpResponsePart = unwrapInboundIn(data)
        let tlsVersionForChannel = context.channel.nioSSL_tlsVersion()
        switch httpResponsePart {
        case .head(let httpResponseHeader):
            ...
        case .body(var byteBuffer):
            ...
        case .end(_):
            tlsVersionForChannel.whenComplete { result in
                switch result {
                case .success(let tlsVersion):
                    print("TLSVersion: \(tlsVersion!)")
                case .failure(let error):
                    print("error: \(error.localizedDescription)")
                }
            }
            closeFuture = context.channel.close()
            promise.succeed(())
        }
    }
    ...
}

Could you please help to provide your demo project?
I'm working on a traffic grab app on iOS, I will intercept the traffic through VPN and then initiate the request again through man-in-the-middle attack. Everything works fine, but I just can't get the TLSVersion in client side.

To test this I just pulled down the main branch on SwiftNIO SSL and added the code in my previous post to the NIOSSLHTTP1Client application.

Regarding:

I'm working on a traffic grab app on iOS, I will intercept the traffic through VPN and then initiate the request again through man-in-the-middle attack.

See TN3120: Expected use cases for Network Extension packet tunnel providers. If you use this outside the context of a Network Extension, does this work as intended?

A few apps do this successfully, e.g. Charles. Notably there is a difference between intercepting the traffic through an actual VPN (which requires TCP/IP decoding) and just using the extension to inject a proxy configuration (which then only works for HTTP/HTTPS I think).

First, I think it's important to establish that this works outside the context of a Network Extension. If it does, great we have a baseline. Now, as you are mentioning, running this inside the context of a packet tunnel may add another layer to consider here and it would be up to the author to extend the existing functionality to support their use case.

When I ran this application I got the right response, like this:

>>> TLSVersion : tlsv13

But in my application, I got an error. In channelRead I can get all responses from server including head/body/end, but TLSVersion.

Yes, My application has been running stably online for a few yeas and now looks fine.