android: build pyqt_builder and sip ourselves, hash pin all hostpython_prerequisites
What changed, and why it matters
This commit updates the Android build setup for the Electrum Bitcoin wallet. It switches to a newer version of the python-for-android build tool, starts building two PyQt-related packages (pyqt_builder and sip) from source instead of relying on pre-built wheels, and adds cryptographic hash checks for build-time Python tools like setuptools and pip. These are supply-chain hardening changes: they make it harder for a malicious or compromised upstream package to sneak bad code into Electrum's Android app during compilation.
Treat as a routine hardening/build-maintenance commit. Review the new python-for-android commit 0b9f7e76 for any relevant security or behavior changes, verify the pinned hashes match official PyPI releases, and ensure the source-built sip/pyqt_builder recipes produce reproducible Android builds. No urgent user action is indicated.
Security signals we found
Hash-pinning of build-time Python dependencies (setuptools, pip)
Source-build of pyqt_builder and sip instead of wheel installation
python-for-android checkout bump to newer commit
Dependency assertion updates reflecting upstream recipe changes
No explicit vulnerability fix or CVE referenced in commit
Evidence from the diff
The diff modifies Electrum’s Android build recipes. Key changes: (1) bumps the pinned python-for-android checkout from 3aa0db1e to 0b9f7e76; (2) adds HashPinnedDependency declarations for hostpython_prerequisites in pyparsing, setuptools, and pyqt_builder recipes, pinning setuptools==80.9.0 and pip==24.0 with sha256 hashes; (3) updates dependency assertions in pyqt6, pyqt_builder, and sip recipes to reflect upstream recipe changes (pyqt6 now depends on pyqt_builder, sip no longer depends on tomli, pyqt_builder depends order changed); (4) removes the empty hostpython_prerequisites list from pyqt_builder. The commit is defensive: it reduces reliance on unpinned upstream build dependencies and pre-built wheels.
Changed components
contrib/android/Dockerfilecontrib/android/p4a_recipes/pyparsing/__init__.pycontrib/android/p4a_recipes/pyqt6/__init__.pycontrib/android/p4a_recipes/pyqt_builder/__init__.pycontrib/android/p4a_recipes/setuptools/__init__.pycontrib/android/p4a_recipes/sip/__init__.pyInspect captured patch +21 / −5
diff --git a/contrib/android/Dockerfile b/contrib/android/Dockerfile
index d396fbe..8a136c2 100644
--- a/contrib/android/Dockerfile
+++ b/contrib/android/Dockerfile
@@ -235,7 +235,7 @@ RUN cd /opt \
&& /opt/venv/bin/python3 -m pip install --no-build-isolation --no-dependencies -e .
# install python-for-android
-ENV P4A_CHECKOUT_COMMIT="3aa0db1ed9b7349625c5fdeff96df4e564403541"
+ENV P4A_CHECKOUT_COMMIT="0b9f7e763866cd3533417418d4b31e7b316b624e"
# ^ from branch electrum_202602 (note: careful with force-pushing! see #8162)
RUN cd /opt \
&& git clone https://github.com/spesmilo/python-for-android \
diff --git a/contrib/android/p4a_recipes/pyparsing/__init__.py b/contrib/android/p4a_recipes/pyparsing/__init__.py
index 04f515d..c6a3e9e 100644
--- a/contrib/android/p4a_recipes/pyparsing/__init__.py
+++ b/contrib/android/p4a_recipes/pyparsing/__init__.py
@@ -1,4 +1,5 @@
from pythonforandroid.recipes.pyparsing import PyparsingRecipe
+from pythonforandroid.util import HashPinnedDependency
assert PyparsingRecipe._version == "3.0.7"
@@ -15,5 +16,12 @@ class PyparsingRecipePinned(PyparsingRecipe):
# see "PyProjectRecipe" from https://github.com/kivy/python-for-android/pull/3007
sha512sum = "1e692f4cdaa6b6e8ca2729d0a3e2ba16d978f1957c538b6de3a4220ec7d996bdbe87c41c43abab851fffa3b0498a05841373e435602917b8c095042e273badb5"
+ hostpython_prerequisites = [
+ HashPinnedDependency(package="setuptools==80.9.0",
+ hashes=['sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922']),
+ HashPinnedDependency(package="pip==24.0",
+ hashes=['sha256:ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc']),
+ ]
+
recipe = PyparsingRecipePinned()
diff --git a/contrib/android/p4a_recipes/pyqt6/__init__.py b/contrib/android/p4a_recipes/pyqt6/__init__.py
index 6a1821b..1f08245 100644
--- a/contrib/android/p4a_recipes/pyqt6/__init__.py
+++ b/contrib/android/p4a_recipes/pyqt6/__init__.py
@@ -1,13 +1,13 @@
import os
from pythonforandroid.recipes.pyqt6 import PyQt6Recipe
-from pythonforandroid.util import load_source
+from pythonforandroid.util import load_source, HashPinnedDependency
util = load_source('util', os.path.join(os.path.dirname(os.path.dirname(__file__)), 'util.py'))
assert PyQt6Recipe._version == "6.10.2"
-assert PyQt6Recipe.depends == ['qt6', 'pyjnius', 'setuptools', 'pyqt6sip', 'hostpython3', 'python3'], PyQt6Recipe.depends
+assert PyQt6Recipe.depends == ['qt6', 'pyjnius', 'setuptools', 'pyqt6sip', 'hostpython3', 'pyqt_builder', 'python3'], PyQt6Recipe.depends
assert PyQt6Recipe.python_depends == []
diff --git a/contrib/android/p4a_recipes/pyqt_builder/__init__.py b/contrib/android/p4a_recipes/pyqt_builder/__init__.py
index 516dc99..3849943 100644
--- a/contrib/android/p4a_recipes/pyqt_builder/__init__.py
+++ b/contrib/android/p4a_recipes/pyqt_builder/__init__.py
@@ -1,12 +1,14 @@
from pythonforandroid.recipes.pyqt_builder import PyQtBuilderRecipe
+from pythonforandroid.util import HashPinnedDependency
assert PyQtBuilderRecipe._version == "1.19.1"
-assert PyQtBuilderRecipe.depends == ["sip", "packaging", "python3"]
+assert PyQtBuilderRecipe.depends == ["packaging", "sip", "python3"], PyQtBuilderRecipe.depends
assert PyQtBuilderRecipe.python_depends == []
class PyQtBuilderRecipePinned(PyQtBuilderRecipe):
sha512sum = "2308c51f93c37b1d13f312e4f2475d26b22d374ef284925fead9eab4aa89b994770431aca45170ac2154b4813fff151798f113f56d4cbf6c6e544fb463104a6d"
+
recipe = PyQtBuilderRecipePinned()
diff --git a/contrib/android/p4a_recipes/setuptools/__init__.py b/contrib/android/p4a_recipes/setuptools/__init__.py
index 23c3596..d60d390 100644
--- a/contrib/android/p4a_recipes/setuptools/__init__.py
+++ b/contrib/android/p4a_recipes/setuptools/__init__.py
@@ -1,4 +1,5 @@
from pythonforandroid.recipes.setuptools import SetuptoolsRecipe
+from pythonforandroid.util import HashPinnedDependency
assert SetuptoolsRecipe._version == "80.9.0"
@@ -7,6 +8,11 @@ assert SetuptoolsRecipe.python_depends == []
class SetuptoolsRecipePinned(SetuptoolsRecipe):
+ hostpython_prerequisites = [
+ HashPinnedDependency(package='setuptools==80.9.0',
+ hashes=[]),
+ ]
+
sha512sum = "36eb1f219d29c6b9e135936bde2001ad70a971c8069cd0175d3a5325b450e6843a903d3f70043c9f534768ebeab8ab0c544b8f44456555d333f1ed72daa5c18b"
diff --git a/contrib/android/p4a_recipes/sip/__init__.py b/contrib/android/p4a_recipes/sip/__init__.py
index 417c9be..456f707 100644
--- a/contrib/android/p4a_recipes/sip/__init__.py
+++ b/contrib/android/p4a_recipes/sip/__init__.py
@@ -2,7 +2,7 @@ from pythonforandroid.recipes.sip import SipRecipe
assert SipRecipe._version == "6.15.1"
-assert SipRecipe.depends == ["setuptools", "packaging", "tomli", "python3"], SipRecipe.depends
+assert SipRecipe.depends == ["setuptools", "packaging", "python3"], SipRecipe.depends
assert SipRecipe.python_depends == []
Why this scored 28/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.