Found while triaging an unrelated CI failure on #1070. Two separate problems in the same code path, one reliability and one security.
What happened
The iOS e2e job failed at pod install:
[QuickCrypto] ⬇️ Downloading libsodium source...
curl: (6) Could not resolve host: download.libsodium.org
[!] Invalid `QuickCrypto.podspec` file: Failed to download libsodium.
A single DNS blip on a GitHub runner red-lights the whole build. The host was fine again minutes later, so it was transient — but there's no retry, so any hiccup at download.libsodium.org fails every pod install with SODIUM_ENABLED=1.
The security part
QuickCrypto.podspec:77 downloads the libsodium source tarball and compiles it, with no checksum, no signature check, and no pinned hash:
system("curl -sSfL ... -o ios/libsodium.tar.gz https://download.libsodium.org/libsodium/releases/libsodium-#{sodium_version}-stable.tar.gz") || raise(...)
system("tar -xzf ios/libsodium.tar.gz -C ios") || raise(...)
Whatever that host returns gets extracted and built into a cryptography library. TLS covers the transport, but nothing pins what we expect to receive — a compromised mirror, a hijacked domain, or a maintainer-side republish under the same URL would be compiled silently.
We already do this correctly for OpenSSL in the same file — download, verify a pinned SHA-256, fail loudly on mismatch. libsodium should meet the same bar, since it's the one we build from source rather than consume as a verified binary.
A second, weaker copy
prepare_command (line 95) downloads the same tarball again with:
curl -L -o ios/libsodium.tar.gz https://download.libsodium.org/...
Note -L without -f: on an HTTP error, curl writes the error page to libsodium.tar.gz and exits 0, so the failure surfaces later as a confusing tar error instead of a download error.
The version is also hardcoded as a literal 1.0.22 here while line 68 uses sodium_version, so the two can drift.
Suggested fix
- Pin a SHA-256 for the libsodium tarball and verify it, mirroring the OpenSSL block.
- Add
--retry 3 --retry-all-errors to both fetches.
- Add
-f to the prepare_command curl.
- Use a single version constant in both places.
Optionally verify libsodium's minisign signature instead of a pinned hash, though a pinned hash is simpler and stronger against republishing.
Scope
Only affects SODIUM_ENABLED=1 builds. Not a regression — this predates #1070, which doesn't touch these lines.
Found while triaging an unrelated CI failure on #1070. Two separate problems in the same code path, one reliability and one security.
What happened
The iOS e2e job failed at
pod install:A single DNS blip on a GitHub runner red-lights the whole build. The host was fine again minutes later, so it was transient — but there's no retry, so any hiccup at
download.libsodium.orgfails everypod installwithSODIUM_ENABLED=1.The security part
QuickCrypto.podspec:77downloads the libsodium source tarball and compiles it, with no checksum, no signature check, and no pinned hash:Whatever that host returns gets extracted and built into a cryptography library. TLS covers the transport, but nothing pins what we expect to receive — a compromised mirror, a hijacked domain, or a maintainer-side republish under the same URL would be compiled silently.
We already do this correctly for OpenSSL in the same file — download, verify a pinned SHA-256, fail loudly on mismatch. libsodium should meet the same bar, since it's the one we build from source rather than consume as a verified binary.
A second, weaker copy
prepare_command(line 95) downloads the same tarball again with:Note
-Lwithout-f: on an HTTP error, curl writes the error page tolibsodium.tar.gzand exits 0, so the failure surfaces later as a confusingtarerror instead of a download error.The version is also hardcoded as a literal
1.0.22here while line 68 usessodium_version, so the two can drift.Suggested fix
--retry 3 --retry-all-errorsto both fetches.-fto theprepare_commandcurl.Optionally verify libsodium's minisign signature instead of a pinned hash, though a pinned hash is simpler and stronger against republishing.
Scope
Only affects
SODIUM_ENABLED=1builds. Not a regression — this predates #1070, which doesn't touch these lines.