Hi everyone, I have the following question, would be great if anyone could help me understand this: How are the cipher suites for a client hello chosen, when ATS for example has only NSAllowsArbitraryLoads set to true (and no other domain dependent exceptions are set)? I‘m asking this because I got really confused by an app which whenever trying to connect to e.g. domain a.com proposed 27 ciphers in its hello, while the same app when connecting to b.com suddenly only proposed 7 cipher suites… If in its info.plist there where domain dependent exceptions this could make totally sense, but given my case I really don‘t understand why this is the case. So why can an app use different sets of cipher suites in its client hello when the only ATS exception is the one given above? Can someone tell me why this is happening or am I missing something?
Cipher Suites in Client Hello
I’m quite surprised by that result. Can you post a pair of domains so that I can try it for myself?
The one thing that might affect this is HSTS, although the last time I looked at that it didn’t change the cypher suite list but simply forced HTTP to HTTPS.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Thanks for your answer. Yes sure: this happened for the domains api-kit.snapchat.com (7 cipher suites) and api.snapkit.com (27 cipher suites).
So I tried this out and I’m not seeing a difference between the two. To start, here’s my core test code:
func test(host: String) {
let config = URLSessionConfiguration.ephemeral
config.requestCachePolicy = .reloadIgnoringLocalCacheData
let session = URLSession(configuration: config, delegate: nil, delegateQueue: .main)
print("will start task, host: \(host)")
let url = URL(string: "https://\(host)")!
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 60.0)
session.dataTask(with: request) { (data, response, error) in
if let error = error as NSError? {
print("task did fail, error \(error.domain) / \(error.code)")
return
}
let response = response as! HTTPURLResponse
let data = data!
print("task finished with status \(response.statusCode), bytes \(data.count)")
session.invalidateAndCancel()
}.resume()
print("did start task")
}
IMPORTANT This creates a session for each request, which is an anti-pattern in URLSession
code. I did this deliberately in this case because I want to ensure that the state of request N has no impact on the processing of request N+1.
I wired up two buttons to call this:
-
The first calls it with
api-kit.snapchat.com
. -
The second calls it with
api.snapkit.com
.
I put all of this in a new test app with ATS disabled:
% plutil -p Test716827.app/Info.plist
{
…
"NSAppTransportSecurity" => {
"NSAllowsArbitraryLoads" => 1
}
…
}
I ran this in the iOS 16.0 simulator and captured a packet trace for each request. In both cases the Client Hello message has the same cypher suite list:
Cipher Suites Length: 42
Cipher Suites (21 suites)
Cipher Suite: Reserved (GREASE) (0xaaaa)
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)
Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
Cipher Suite: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)
Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA (0xc008)
Cipher Suite: TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA (0xc012)
Cipher Suite: TLS_RSA_WITH_3DES_EDE_CBC_SHA (0x000a)
Note Well, not quite identical, because the GREASE
value varies, but that’s expected per RFC 8701.
Please repeat this test in your environment and let me know if you see the same results.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Hi @DTS Engineer how could you capture packet trace? could you share here?