func verify()

in Sources/PackageCollectionsSigning/Certificate/CertificatePolicy.swift [120:235]


    func verify(certChain: [Certificate],
                anchorCerts: [Certificate]? = nil,
                verifyDate: Date? = nil,
                httpClient: HTTPClient?,
                observabilityScope: ObservabilityScope,
                callbackQueue: DispatchQueue,
                callback: @escaping (Result<Void, Error>) -> Void) {
        let wrappedCallback: (Result<Void, Error>) -> Void = { result in callbackQueue.async { callback(result) } }

        guard !certChain.isEmpty else {
            return wrappedCallback(.failure(CertificatePolicyError.emptyCertChain))
        }

        // On non-Apple platforms we don't trust any of the system root certs, so if `anchorCerts`,
        // which is a combination of user-configured and SwiftPM-provided roots, is empty the trust
        // evaluation of `certChain` will always fail.
        guard let anchorCerts = anchorCerts, !anchorCerts.isEmpty else {
            return wrappedCallback(.failure(CertificatePolicyError.noTrustedRootCertsConfigured))
        }

        // Make sure certChain and underlying pointers stay in scope for sk_X509 until we are done verifying
        let error: Error? = withExtendedLifetime(certChain) {
            // Cert chain
            let x509Stack = CCryptoBoringSSL_sk_X509_new_null()
            defer { CCryptoBoringSSL_sk_X509_free(x509Stack) }

            for i in 1 ..< certChain.count {
                guard certChain[i].withUnsafeMutablePointer({ CCryptoBoringSSL_sk_X509_push(x509Stack, $0) }) > 0 else {
                    return CertificatePolicyError.trustSetupFailure
                }
            }

            // Trusted certs
            let x509Store = CCryptoBoringSSL_X509_STORE_new()
            defer { CCryptoBoringSSL_X509_STORE_free(x509Store) }

            let x509StoreCtx = CCryptoBoringSSL_X509_STORE_CTX_new()
            defer { CCryptoBoringSSL_X509_STORE_CTX_free(x509StoreCtx) }

            // !-safe since certChain cannot be empty
            guard certChain.first!.withUnsafeMutablePointer({ CCryptoBoringSSL_X509_STORE_CTX_init(x509StoreCtx, x509Store, $0, x509Stack) }) == 1 else {
                return CertificatePolicyError.trustSetupFailure
            }
            CCryptoBoringSSL_X509_STORE_CTX_set_purpose(x509StoreCtx, X509_PURPOSE_ANY)

            anchorCerts.forEach { anchorCert in
                // add_cert returns 0 for all error types, including when we add duplicate cert, so we don't check for result > 0 here.
                // If an anchor cert didn't get added, trust evaluation should fail anyway.
                _ = anchorCert.withUnsafeMutablePointer { CCryptoBoringSSL_X509_STORE_add_cert(x509Store, $0) }
            }

            var ctxFlags: CInt = 0
            if let verifyDate = verifyDate {
                CCryptoBoringSSL_X509_STORE_CTX_set_time(x509StoreCtx, 0, numericCast(Int(verifyDate.timeIntervalSince1970)))
                ctxFlags = ctxFlags | X509_V_FLAG_USE_CHECK_TIME
            }
            CCryptoBoringSSL_X509_STORE_CTX_set_flags(x509StoreCtx, numericCast(UInt(ctxFlags)))

            let verifyCallback: BoringSSLVerifyCallback = { result, ctx in
                // Success
                if result == 1 { return result }

                // Custom error handling
                let errorCode = CCryptoBoringSSL_X509_STORE_CTX_get_error(ctx)
                // Certs could have unknown critical extensions and cause them to be rejected.
                // Check if they are tolerable.
                if errorCode == X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION {
                    guard let ctx = ctx, let cert = CCryptoBoringSSL_X509_STORE_CTX_get_current_cert(ctx) else {
                        return result
                    }

                    let capacity = 100
                    let oidBuffer = UnsafeMutablePointer<Int8>.allocate(capacity: capacity)
                    defer { oidBuffer.deallocate() }

                    for i in 0 ..< CCryptoBoringSSL_X509_get_ext_count(cert) {
                        let ext = CCryptoBoringSSL_X509_get_ext(cert, numericCast(i))
                        // Skip if extension is not critical or it is supported by BoringSSL
                        if CCryptoBoringSSL_X509_EXTENSION_get_critical(ext) <= 0 || CCryptoBoringSSL_X509_supported_extension(ext) > 0 { continue }

                        // Extract OID of the critical extension
                        let extObj = CCryptoBoringSSL_X509_EXTENSION_get_object(ext)
                        guard CCryptoBoringSSL_OBJ_obj2txt(oidBuffer, numericCast(capacity), extObj, numericCast(1)) > 0,
                            let oid = String(cString: oidBuffer, encoding: .utf8),
                            supportedCriticalExtensions.contains(oid) else {
                            return result
                        }
                    }
                    // No actual unhandled critical extension found, so trust the cert chain
                    return 1
                }
                return result
            }
            CCryptoBoringSSL_X509_STORE_CTX_set_verify_cb(x509StoreCtx, verifyCallback)

            guard CCryptoBoringSSL_X509_verify_cert(x509StoreCtx) == 1 else {
                let error = CCryptoBoringSSL_X509_verify_cert_error_string(numericCast(CCryptoBoringSSL_X509_STORE_CTX_get_error(x509StoreCtx)))
                observabilityScope.emit(warning: "The certificate is invalid: \(String(describing: error.flatMap { String(cString: $0, encoding: .utf8) }))")
                return CertificatePolicyError.invalidCertChain
            }

            return nil
        }

        if let error = error {
            return wrappedCallback(.failure(error))
        }

        if certChain.count > 1, let httpClient = httpClient {
            // Whether cert chain can be trusted depends on OCSP result
            ocspClient.checkStatus(certificate: certChain[0], issuer: certChain[1], anchorCerts: anchorCerts, httpClient: httpClient,
                                   callbackQueue: callbackQueue, callback: callback)
        } else {
            wrappedCallback(.success(()))
        }
    }