func checkStatus()

in Sources/PackageCollectionsSigning/Certificate/CertificatePolicy.swift [251:412]


    func checkStatus(certificate: Certificate,
                     issuer: Certificate,
                     anchorCerts: [Certificate]?,
                     httpClient: HTTPClient,
                     callbackQueue: DispatchQueue,
                     callback: @escaping (Result<Void, Error>) -> Void) {
        let wrappedCallback: (Result<Void, Error>) -> Void = { result in callbackQueue.async { callback(result) } }

        let ocspURLs = certificate.withUnsafeMutablePointer { CCryptoBoringSSL_X509_get1_ocsp($0) }
        defer { CCryptoBoringSSL_sk_OPENSSL_STRING_free(ocspURLs) }

        let ocspURLCount = CCryptoBoringSSL_sk_OPENSSL_STRING_num(ocspURLs)
        // Nothing to do if no OCSP URLs. Use `supportsOCSP` to require OCSP support if needed.
        guard ocspURLCount > 0 else { return wrappedCallback(.success(())) }

        // Construct the OCSP request
        let digest = CCryptoBoringSSL_EVP_sha1()
        let certid = certificate.withUnsafeMutablePointer { certPtr in
            issuer.withUnsafeMutablePointer { issPtr in
                OCSP_cert_to_id(digest, certPtr, issPtr)
            }
        }
        let request = OCSP_REQUEST_new()
        defer { OCSP_REQUEST_free(request) }

        guard OCSP_request_add0_id(request, certid) != nil else {
            return wrappedCallback(.failure(CertificatePolicyError.ocspSetupFailure))
        }

        // Write the request binary to memory bio
        let bio = CCryptoBoringSSL_BIO_new(CCryptoBoringSSL_BIO_s_mem())
        defer { CCryptoBoringSSL_BIO_free(bio) }
        guard i2d_OCSP_REQUEST_bio(bio, request) > 0 else {
            return wrappedCallback(.failure(CertificatePolicyError.ocspSetupFailure))
        }

        // Copy from bio to byte array then convert to Data
        var count = 0
        var out: UnsafePointer<UInt8>?
        guard CCryptoBoringSSL_BIO_mem_contents(bio, &out, &count) > 0 else {
            return wrappedCallback(.failure(CertificatePolicyError.ocspSetupFailure))
        }

        let requestData = Data(UnsafeBufferPointer(start: out, count: count))

        let results = ThreadSafeArrayStore<Result<Bool, Error>>()
        let group = DispatchGroup()

        // Query each OCSP responder and record result
        for index in 0 ..< ocspURLCount {
            guard let urlStr = CCryptoBoringSSL_sk_OPENSSL_STRING_value(ocspURLs, numericCast(index)),
                let url = String(validatingUTF8: urlStr).flatMap({ URL(string: $0) }) else {
                results.append(.failure(OCSPError.badURL))
                continue
            }

            let cacheKey = CacheKey(url: url, request: requestData)
            if let cachedResult = self.resultCache[cacheKey] {
                if cachedResult.timestamp + self.cacheTTL > DispatchTime.now() {
                    results.append(.success(cachedResult.isCertGood))
                    continue
                }
            }

            var headers = HTTPClientHeaders()
            headers.add(name: "Content-Type", value: "application/ocsp-request")
            guard let host = url.host else {
                results.append(.failure(OCSPError.badURL))
                continue
            }
            headers.add(name: "Host", value: host)

            var options = HTTPClientRequest.Options()
            options.validResponseCodes = [200]

            group.enter()
            httpClient.post(url, body: requestData, headers: headers, options: options) { result in
                defer { group.leave() }

                switch result {
                case .failure(let error):
                    results.append(.failure(error))
                case .success(let response):
                    guard let responseData = response.body else {
                        results.append(.failure(OCSPError.emptyResponseBody))
                        return
                    }

                    let bytes = responseData.copyBytes()

                    // Convert response to bio then OCSP response
                    let bio = CCryptoBoringSSL_BIO_new(CCryptoBoringSSL_BIO_s_mem())
                    defer { CCryptoBoringSSL_BIO_free(bio) }
                    guard CCryptoBoringSSL_BIO_write(bio, bytes, numericCast(bytes.count)) > 0 else {
                        results.append(.failure(OCSPError.responseConversionFailure))
                        return
                    }

                    let response = d2i_OCSP_RESPONSE_bio(bio, nil)
                    defer { OCSP_RESPONSE_free(response) }
                    
                    guard let response = response else {
                        results.append(.failure(OCSPError.responseConversionFailure))
                        return
                    }
                    
                    let basicResp = OCSP_response_get1_basic(response)
                    defer { OCSP_BASICRESP_free(basicResp) }
                    
                    guard let basicResp = basicResp else {
                        results.append(.failure(OCSPError.responseConversionFailure))
                        return
                    }

                    // This is just the OCSP response status, not the certificate's status
                    guard OCSP_response_status(response) == OCSP_RESPONSE_STATUS_SUCCESSFUL,
                        CCryptoBoringSSL_OBJ_obj2nid(response.pointee.responseBytes.pointee.responseType) == NID_id_pkix_OCSP_basic else {
                        results.append(.failure(OCSPError.badResponse))
                        return
                    }

                    let x509Store = CCryptoBoringSSL_X509_STORE_new()
                    defer { CCryptoBoringSSL_X509_STORE_free(x509Store) }

                    anchorCerts?.forEach { anchorCert in
                        _ = anchorCert.withUnsafeMutablePointer { CCryptoBoringSSL_X509_STORE_add_cert(x509Store, $0) }
                    }

                    // Verify the OCSP response to make sure we can trust it
                    guard OCSP_basic_verify(basicResp, nil, x509Store, 0) > 0 else {
                        results.append(.failure(OCSPError.responseVerificationFailure))
                        return
                    }

                    // Inspect the OCSP response
                    let basicRespData = basicResp.pointee.tbsResponseData.pointee
                    for i in 0 ..< sk_OCSP_SINGLERESP_num(basicRespData.responses) {
                        guard let singleResp = sk_OCSP_SINGLERESP_value(basicRespData.responses, numericCast(i)),
                            let certStatus = singleResp.pointee.certStatus else {
                            results.append(.failure(OCSPError.badResponse))
                            return
                        }

                        // Is the certificate in good status?
                        let isCertGood = certStatus.pointee.type == V_OCSP_CERTSTATUS_GOOD
                        results.append(.success(isCertGood))
                        self.resultCache[cacheKey] = CacheValue(isCertGood: isCertGood, timestamp: DispatchTime.now())
                        break
                    }
                }
            }
        }

        group.notify(queue: callbackQueue) {
            // Fail open: As long as no one says the cert is revoked we assume it's ok. If we receive no responses or
            // all of them are failures we'd still assume the cert is not revoked.
            guard results.compactMap({ $0.success }).first(where: { !$0 }) == nil else {
                return wrappedCallback(.failure(CertificatePolicyError.invalidCertChain))
            }
            wrappedCallback(.success(()))
        }
    }