QWAC validation

Hello there,

Starting from iOS 18.4, support was included for QWAC Validation and QCStatements.

Using the official QWAC Validator at: https://eidas.ec.europa.eu/efda/qwac-validation-tool

I was able to check that the domain "eidas.ec.europa.eu" has a valid QWAC certificate. However, when trying to obtain the same result using the new API, I do not obtain the same result.

Here is my sample playground code:

import Foundation
import Security
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

@MainActor
class CertificateFetcher: NSObject, URLSessionDelegate {
    
    private let url: URL
    
    init(url: URL) {
        self.url = url
        super.init()
    }
    
    func start() {
        let session = URLSession(configuration: .ephemeral, delegate: self, delegateQueue: nil)
        let task = session.dataTask(with: url) { data, response, error in
            if let error = error {
                print("Error during request: \(error)")
            } else {
                print("Request completed.")
            }
        }
        task.resume()
    }
    
    nonisolated func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        guard let trust = challenge.protectionSpace.serverTrust else {
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }
        
        if let certificates = SecTrustCopyCertificateChain(trust) as? [SecCertificate] {
            self.checkQWAC(certificates: certificates)
        }
        
        let credential = URLCredential(trust: trust)
        completionHandler(.useCredential, credential)
    }
    
    nonisolated func checkQWAC(certificates: [SecCertificate]) {
        let policy = SecPolicyCreateSSL(true, nil)
        
        var trust: SecTrust?
        guard SecTrustCreateWithCertificates(certificates as CFArray, policy, &trust) == noErr, let trust else {
            print("Unable to create SecTrust")
            return
        }
        
        var error: CFError?
        guard SecTrustEvaluateWithError(trust, &error) else {
            print("Trust evaluation failed")
            return
        }
        
        guard let result = SecTrustCopyResult(trust) as? [String : Any] else {
            print("No result dictionary")
            return
        }
        
        let qwacStatus = result[kSecTrustQWACValidation as String]
        let qcStatements = result[kSecTrustQCStatements as String]
        
        print("QWAC Status: \(String(describing: qwacStatus))")
        print("QC Statements: \(String(describing: qcStatements))")
    }
    
}

let url = URL(string: "https://eidas.ec.europa.eu/")!
let fetcher = CertificateFetcher(url: url)
fetcher.start()

Which prints:

QWAC Status: nil
QC Statements: nil
Request completed.

Am I making a mistake while using the Security framework? I would greatly appreciate any help or guidance you can provide.

Answered by DTS Engineer in 841146022
I need to confirm that

And I’m glad I did. It turns out that my theory was incorrect! Once QWAC support is operational, you should be able to access the results without specifically opting in, much like how EV works today. The issue right now is that QWAC support isn’t operational.

As to why that is, I don’t have any concrete info to share on that. My focus is on technical issues, and the answer to that question lies somewhere above layer 7 in the OSI networking model (-:

I do, however, have some concrete technical advice: While it’s fine to experiment with the current API (kSecTrustQWACValidation and kSecTrustQCStatements), don’t ship any code that uses it until QWAC is operational. This is the 21st century incarnation of classic DTS advice from the last millenium: Test what you ship!

Share and Enjoy

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

AFAICT those QWAC properties are only populated if you opt in to QWAC checking, and there’s no public API to enable that. [See my update below.] However, I need to confirm that before I send you off to file an ER. I’m off to do that now and I’ll post an update here when I know more.

Share and Enjoy

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

Thanks for checking! Looking forward to your update.

Accepted Answer
I need to confirm that

And I’m glad I did. It turns out that my theory was incorrect! Once QWAC support is operational, you should be able to access the results without specifically opting in, much like how EV works today. The issue right now is that QWAC support isn’t operational.

As to why that is, I don’t have any concrete info to share on that. My focus is on technical issues, and the answer to that question lies somewhere above layer 7 in the OSI networking model (-:

I do, however, have some concrete technical advice: While it’s fine to experiment with the current API (kSecTrustQWACValidation and kSecTrustQCStatements), don’t ship any code that uses it until QWAC is operational. This is the 21st century incarnation of classic DTS advice from the last millenium: Test what you ship!

Share and Enjoy

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

Makes sense to wait until it’s operational :D Thanks for checking!

QWAC validation
 
 
Q