feat(parameters): fix android ssl certificate issue#3061
feat(parameters): fix android ssl certificate issue#3061fedpre wants to merge 17 commits intoProvableHQ:stagingfrom
Conversation
…es, including base64, h2, http, hyper, and reqwest with version updates
…ing to a file instead of generating a Rust module
…e for curl compatibility
…irectory and using curl's cainfo() method
…g fallback to temp directory
…for direct memory access and adding fallback to file writing
…ndroid for mobile platforms
There was a problem hiding this comment.
Pull request overview
This PR addresses SSL certificate verification issues on Android and iOS by embedding Mozilla's CA certificate bundle at compile time. The solution downloads the CA bundle during the build process for mobile platforms and configures curl to use it directly from memory, avoiding filesystem access issues that Android's security model restricts.
Key Changes
- Adds a build script that downloads Mozilla's CA certificate bundle for Android/iOS builds
- Modifies curl configuration to use embedded certificates via
ssl_cainfo_blobAPI - Introduces reqwest 0.11 as a build-time dependency for downloading the CA bundle
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
| parameters/build.rs | New build script that downloads CA certificate bundle for mobile platforms and writes it to OUT_DIR |
| parameters/src/macros.rs | Adds platform-specific SSL configuration to use embedded CA bundle on Android/iOS with fallback logic |
| parameters/Cargo.toml | Adds reqwest 0.11 as build dependency with blocking and rustls-tls features |
| Cargo.lock | Adds reqwest 0.11.27 and its transitive dependencies to the lock file |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
parameters/build.rs
Outdated
| let ca_bundle_url = "https://curl.se/ca/cacert.pem"; | ||
| match download_ca_bundle(ca_bundle_url) { | ||
| Ok(contents) => { | ||
| // Write the CA bundle as a binary file that can be included with include_bytes! | ||
| // This is simpler and more efficient than generating a Rust array | ||
| fs::write(&ca_bundle_path, &contents).expect("Failed to write CA certificate bundle"); |
There was a problem hiding this comment.
The CA bundle is downloaded from an external source (https://curl.se/ca/cacert.pem) without any integrity verification (e.g., checksum validation). This creates a security risk where the build process could be compromised if the downloaded bundle is tampered with or if there's a man-in-the-middle attack during the build.
Consider adding checksum verification for the downloaded CA bundle to ensure integrity. You could:
- Store the expected SHA-256 checksum of the bundle in the code or a configuration file
- Verify the checksum after download
- Update the checksum when intentionally updating to a newer bundle version
This is particularly important since this code is handling SSL/TLS certificates that form the foundation of secure communications.
parameters/build.rs
Outdated
| // Download CA bundle for mobile platforms (Android and iOS) | ||
| let target = env::var("TARGET").unwrap_or_default(); | ||
| if target.contains("android") || target.contains("ios") { | ||
| let platform = if target.contains("android") { "Android" } else { "iOS" }; | ||
| // Download Mozilla's CA certificate bundle | ||
| println!("cargo:warning=Downloading CA certificate bundle for {platform}..."); | ||
|
|
||
| let ca_bundle_url = "https://curl.se/ca/cacert.pem"; | ||
| match download_ca_bundle(ca_bundle_url) { | ||
| Ok(contents) => { | ||
| // Write the CA bundle as a binary file that can be included with include_bytes! | ||
| // This is simpler and more efficient than generating a Rust array | ||
| fs::write(&ca_bundle_path, &contents).expect("Failed to write CA certificate bundle"); | ||
|
|
||
| println!("cargo:warning=CA certificate bundle downloaded successfully"); | ||
| println!("cargo:rerun-if-changed=build.rs"); | ||
| } | ||
| Err(e) => { | ||
| panic!( | ||
| "Failed to download CA certificate bundle: {e}\n\ | ||
| You may need to download it manually from {ca_bundle_url} and place it at {ca_bundle_path:?}" | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
The build script downloads the CA bundle during every build for mobile platforms. This is inefficient and creates several issues:
- Build reliability: Builds will fail if https://curl.se is unavailable or experiencing network issues
- Build performance: Every clean build downloads ~200KB unnecessarily
- Reproducibility: Different builds could get different versions of the CA bundle if it's updated on the server
Consider adding a mechanism to cache the downloaded bundle and only re-download when necessary (e.g., based on a version number or when explicitly requested). Alternatively, the CA bundle could be vendored in the repository with a separate script to update it when needed, ensuring deterministic builds.
…and macros. Ensure CA bundle is downloaded only for mobile targets and handle errors more gracefully.
| // On mobile platforms (Android and iOS), configure curl to use the bundled CA certificate bundle | ||
| #[cfg(any(target_os = "android", target_os = "ios"))] | ||
| { | ||
| const CA_BUNDLE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/cacert.pem")); |
There was a problem hiding this comment.
This isn't a flexible approach. It requires ANYONE building mobile to store a .pem cert with their bundle. If this is going to make it to the protocol level, we should attempt to ensure an arbitrary user of the protocol can use it without specialized knowledge like this.
For now though, this isn't the solution. What we need to do use the iOS/Android native roots of trust, packaging our own is inflexible/redundant. rustls_platform_verifier can help with fetching native roots of trust, but the latest versions of reqwest seem to use this by default, so perhaps we can simply get away with using reqwest to make these calls when android/ios is used.
@fedpre can you make this change to use reqwest and see if it works on iOS/android? If a simple switch of libaries takes care of this, this is a far better option.
@copilot code review[agent] can you verify that using reqwest instead of curl could be a potential solution here (due to its usage of rustls_platform_verifier?
There was a problem hiding this comment.
I'm not a mobile expert, but this makes a lot of sense to me, thank you @iamalwaysuncomfortable . @fedpre keen to hear if using reqwest is a practical way to resolve the issue, happy to set up a call if we need to iterate more.
I can't trigger copilot but ChatGPT-5.2 agrees, though it notes the platform verifier is only enabled by default from reqwest 0.13
There was a problem hiding this comment.
The problem is that snarkvm-parameters uses curl when fetching the parameters for local proving. Here is the Cargo.toml in snarkvm-parameters:
[target.'cfg(all(not(target_family = "wasm"), not(target_env = "sgx")))'.dependencies.curl]
version = "0.4.43"As we are not using wasm, we default curl. Hence, my workaround because iOS works fine but Android fails. I think the change needs to happen there and instead of using curl we use reqwest? Is it what we are talking about here? Just wanted to understand better your comment and implement the right solution. I'd love to have a call and go through this together for sure.
There was a problem hiding this comment.
I will go ahead and test the implementation here fedpre#2 and if successful I will open a PR. Closing this one for now as this is not the way we want to move forward with it
There was a problem hiding this comment.
Opened a new PR after testing the change successfuly #3169
Motivation
Android doesn't allow native code to check the system ssl certificate. This PR allows the certificate to be embedded at compile time and avoid the issue on Android.
This is needed on the
mobile-sdkas we usecurl-systo download proving keys when doing local proving.One of my concerns is downloading the certifcate each time. We could store it securely somewhere ourselves and then store the SHA-256 checksum so we can compute the checksum each time and make sure the file is the same and it has not being tempered with.
Test Plan
If you changed any code, please provide us with clear instructions on how you verified your changes work. Bonus points for screenshots and videos!
Documentation
If this PR adds or changes functionality, consider clarifying which docs need to be updated, e.g. on AleoNet/welcome.
Backwards compatibility
Please review backwards compatibility. Does any functionality need to be guarded by an existing or new
ConsensusVersion?