Skip to content

WIP: Firestore: Use Apple's SecTrust to validate gRPC TLS certificates instead of hardcoded certs - #16946

Draft
dconeybe wants to merge 31 commits into
mainfrom
dconeybe/firestore/applecerts
Draft

dconeybe wants to merge 31 commits into
mainfrom
dconeybe/firestore/applecerts

Conversation

@dconeybe

@dconeybe dconeybe commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

This PR migrates Firestore's gRPC TLS server certificate and hostname verification on Apple platforms from a static embedded root certificate bundle to Apple's Security.framework (SecTrust), enabling automatic OS root store updates, enterprise MDM trust, and pushed Over-The-Air (trustd) CA revocations while reducing SDK binary size. This change applies only to Apple platforms (iOS, macOS, Mac Catalyst, tvOS, and visionOS) and does not change anything for Windows and Linux targets.

Background & Motivation

Previously, Firestore validated gRPC TLS server connections using a hardcoded PEM root certificate bundle (grpc_root_certificates_generated.cc) compiled directly into the SDK binary. Because an embedded trust store remains static until an app ships a new SDK version, responding to a CA compromise required regenerating the embedded bundle and publishing an emergency SDK update—as done in the quick fix #16930 to mitigate the October 2026 compromised eMudhra certificate.

This PR is the long-term follow-up to #16930. Instead of relying on hardcoded certificates, Firestore on Apple platforms now delegates TLS certificate chain and hostname validation to Apple's native Security.framework (SecPolicyCreateSSL and SecTrustEvaluateWithError). This aligns Firestore's gRPC transport with URLSession and the rest of firebase-ios-sdk, automatically inheriting OS root store updates, enterprise/MDM root certificates, Certificate Transparency enforcement, and Apple's background Over-The-Air (trustd) pushed certificate revocations without requiring an SDK release, while also excluding the ~3.3 MB generated certificate source file from Apple builds.

Platform Scope: This fix only applies to Apple platforms (e.g., iOS, macOS, Mac Catalyst, tvOS, and visionOS). It does not change anything for Windows and Linux targets, which continue to validate TLS connections using the embedded root certificate bundle.

Highlights

  • Apple Security.framework (SecTrust) TLS Verification: Added AppleSecTrustCertificateVerifier (grpc::experimental::ExternalCertificateVerifier) and CreateAppleTlsCredentials in grpc_tls_credentials_provider_apple.{h,mm} to configure grpc::experimental::TlsCredentials with set_verify_server_certs(false) (bypassing BoringSSL's static X509_STORE while keeping its cryptographic TLS handshake) and evaluate the peer certificate chain and target hostname via SecPolicyCreateSSL and SecTrustEvaluateWithError.
  • Platform Credentials Provider Seam & Dead Test-Hook Cleanup: Extracted default TLS channel credentials creation from GrpcConnection::CreateChannel into a link-time CreateDefaultTlsCredentials() factory (grpc_tls_credentials_provider.h), backed by grpc_tls_credentials_provider_apple.mm on Apple platforms and grpc_tls_credentials_provider_embedded.cc (LoadGrpcRootCertificate()) on Windows and Linux. Removed the unreferenced GrpcConnection::UseTestCertificate() and HostConfigMap machinery from grpc_connection.{h,cc} in favor of a minimal thread-safe InsecureHosts set and optional CFArrayRef anchor_certs parameters on the internal Apple helpers.
  • Build System Updates & Binary Size Reduction: Linked Security.framework and excluded grpc_root_certificates_generated.cc, grpc_root_certificate_finder_generated.cc, and grpc_tls_credentials_provider_embedded.cc on Apple builds across CocoaPods (FirebaseFirestoreInternal.podspec), Swift Package Manager (Package.swift), and CMake (core/CMakeLists.txt).
  • Fail-Closed Input, Memory, & Log Hardening: Validated target authorities via Foundation's NSURLComponents and POSIX inet_pton, parsed PEM chains fail-closed using NSScanner with strict RFC 7468 ASCII whitespace and NSData (options:0), snapshotted custom trust anchors immutably via CFArrayCreateCopy, used util::MakeString for null-safe NSString conversions, and escaped rejected target names with absl::CHexEscape to prevent log injection.
  • Comprehensive Unit & Loopback TLS Test Suite: Added self-contained Apple unit tests and end-to-end in-process localhost grpc::Server TLS loopback tests in grpc_tls_credentials_provider_apple_test.mm using dynamically generated 3-tier P-256 X.509v3 certificate chains to cover DNS/IPv4/IPv6 SAN matching, SslTargetNameOverride, malformed target and PEM rejection, expired/not-yet-valid certificates, untrusted roots, and missing intermediates.

Alternatives & Design Decisions

  • Default SecPolicyCreateSSL Policy vs. Explicit Revocation/Offline Flags: Passing SecPolicyCreateSSL(/*server=*/true, cf_host) directly to SecTrustCreateWithCertificates (without kSecRevocationNetworkAccessDisabled or SecTrustSetNetworkFetchAllowed(trust, false)) matches URLSession and Apple's Security.framework guidance. It enforces Apple's Valid Over-The-Air (trustd) revocation database, system/MDM trust settings, and Certificate Transparency, while preserving trustd's ability to fetch missing intermediate CA certificates via Authority Information Access (AIA) if a custom FIRFirestoreSettings.host endpoint serves an incomplete chain.
  • Defense-in-Depth Hostname Verification & OS Authority Parsing: Rather than disabling gRPC's per-RPC host check via the doc-deprecated TlsCredentialsOptions::set_check_call_host(false) setter or maintaining handwritten URI/IPv6 character-loop parsers, this PR keeps gRPC's default check_call_host(true) (ClientAuthFilter -> SslCheckCallHost) active alongside handshake-time SecPolicyCreateSSL(true, cf_host) and delegates authority parsing in ExtractTlsHostName to Foundation's NSURLComponents and POSIX inet_pton(AF_INET6, ...). Handshake-time SecPolicyCreateSSL enforces Apple's RFC 9525 / HT210176 SAN and wildcard rules, while per-RPC check_call_host(true) verifies the HTTP/2 :authority header before attaching Firebase Auth and App Check bearer tokens.
  • Fail-Closed Foundation PEM Decoding vs. BoringSSL PEM_read_bio_X509 or Handwritten Parsers: CreateCertificateChainFromPem uses Foundation's NSScanner, strict RFC 7468 ASCII whitespace (" \t\r\n"), and [[NSData alloc] initWithBase64EncodedString:options:0] rather than custom Base64 loops or BoringSSL's PEM_read_bio_X509. Unlike a PEM_read_bio_X509 loop—which silently skips non-CERTIFICATE PEM blocks, ignores pre-header text, and returns PEM_R_NO_START_LINE on both clean EOF and a trailing block missing its -----BEGIN line (risking silent chain truncation)—the NSScanner + strict NSData approach fails closed on extraneous text, unclosed/corrupted trailing blocks, and non-ASCII whitespace, uses ARC-managed memory, and avoids exposing direct BoringSSL-GRPC header dependencies across CMake, CocoaPods, and SwiftPM.
  • Per-Channel Verifier Allocation for Subchannel Pool Isolation: CreateAppleTlsCredentials allocates a fresh AppleSecTrustCertificateVerifier instance per channel credentials object instead of sharing a singleton verifier. Because gRPC's TlsCredentials::cmp_impl compares credentials by verifier pointer identity, sharing a singleton verifier caused GlobalSubchannelPool to pool subchannels across independent GrpcConnection channels—preventing GrpcConnection::RegisterConnectivityMonitor's grpc_channel_.reset() from immediately dropping dead TCP sockets during Wi-Fi/cellular handovers when an in-flight call or secondary Firestore instance still held a reference.
Changelog
  • CHANGELOG.md
    • Added an # Unreleased [changed] entry documenting the migration to Security.framework (SecTrust) TLS server certificate verification on Apple platforms.
  • FirebaseFirestoreInternal.podspec
    • Excluded grpc_root_certificate_finder_generated.cc, grpc_root_certificates_generated.cc, and grpc_tls_credentials_provider_embedded.cc from Apple pod builds.
    • Linked the Security framework on iOS, macOS, and tvOS.
  • Package.swift
    • Excluded grpc_root_certificate_finder_generated.cc, grpc_root_certificates_generated.cc, and grpc_tls_credentials_provider_embedded.cc from the FirebaseFirestoreInternalWrapper target.
    • Linked the Security framework in Firestore SwiftPM targets.
  • core/CMakeLists.txt
    • Added grpc_tls_credentials_provider_apple.*, excluded embedded root certificate sources, and linked -framework Security when APPLE is enabled.
  • project.pbxproj
    • Added grpc_tls_credentials_provider_apple_test.mm to the Firestore unit test targets in Xcode.
  • grpc_connection.h
    • Removed the unused GrpcConnection::UseTestCertificate declaration and unused header includes.
  • grpc_connection.cc
    • Replaced direct LoadGrpcRootCertificate() and CreateSslCredentials calls in GrpcConnection::CreateChannel with CreateDefaultTlsCredentials().
    • Removed GrpcConnection::UseTestCertificate, HostConfig, and HostConfigMap, replacing them with a thread-safe InsecureHosts set for GrpcConnection::UseInsecureChannel.
  • grpc_tls_credentials_provider.h
    • New header declaring the platform-neutral CreateDefaultTlsCredentials() factory.
  • grpc_tls_credentials_provider_embedded.cc
    • New non-Apple implementation of CreateDefaultTlsCredentials() backed by LoadGrpcRootCertificate() and grpc::SslCredentials for Windows and Linux targets.
  • grpc_tls_credentials_provider_apple.h
    • New Apple-only internal header declaring VerifyAppleTlsCertificateChain and CreateAppleTlsCredentials with optional custom CFArrayRef anchor_certs support for deterministic testing.
  • grpc_tls_credentials_provider_apple.mm
    • New Apple-only implementation of CreateDefaultTlsCredentials(), CreateAppleTlsCredentials, VerifyAppleTlsCertificateChain, AppleSecTrustCertificateVerifier, ExtractTlsHostName, and CreateCertificateChainFromPem using Security.framework and Foundation.
  • test/unit/remote/CMakeLists.txt
    • Appended Objective-C++ (*.mm) test sources to firestore_remote_test on Apple builds.
  • grpc_tls_credentials_provider_apple_test.mm
    • New Apple unit and localhost gRPC TLS loopback test suite with an in-memory 3-tier P-256 X.509v3 certificate generator covering authority extraction, fail-closed PEM parsing, SecTrust chain evaluation, and end-to-end gRPC TLS handshakes.

Remove the unreferenced Hexa-era GrpcConnection::UseTestCertificate() hook and replace HostConfig/HostConfigMap with a minimal thread-safe InsecureHosts set before introducing platform TLS credential providers.
Extract CreateDefaultTlsCredentials() into grpc_tls_credentials_provider.{h,_embedded.cc} so Apple and non-Apple platforms can supply link-time TLS credential implementations without #if __APPLE__ conditionals in grpc_connection.cc.
Delegate Apple gRPC server TLS certificate and hostname verification to Security.framework (SecPolicyCreateSSL and SecTrustEvaluateWithError) via grpc::experimental::ExternalCertificateVerifier instead of BoringSSL's static embedded root store. Link Security.framework and exclude embedded root certificate sources on Apple builds across CMake, CocoaPods, and SwiftPM.
Add comprehensive Apple Security.framework unit tests and end-to-end localhost gRPC TLS loopback tests covering hostname extraction, fail-closed PEM parsing, SecTrust chain verification, and TlsCredentials handshakes using in-memory P-256 X.509v3 test certificates.
…n changelog

Document the migration to Security.framework SecTrust gRPC TLS server verification on Apple platforms in Firestore CHANGELOG.md.
Document each architectural, policy, execution-mode, testing, and changelog question, the options considered, and the selected design in Firestore/DECISIONS.md.
@dconeybe dconeybe self-assigned this Oct 23, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

note to self: the AI conversation ID is: `0516c0fc-eba6-45aa-8b77-cffc6175ed08`
@dconeybe dconeybe changed the title WIP: experiment using Apple's SecTrust to validate gRPC TLS certificates in Firestore WIP: Firestore: Use Apple's SecTrust to validate gRPC TLS certificates instead of hardcoded certs Oct 27, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant