diff --git a/pyproject.toml b/pyproject.toml index 94b67f4..e34d8a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "vpnapp" -version = "0.1.0" +version = "0.1.1" description = "Private Android/Desktop VPN and proxy client with a small subscription backend." readme = "README.md" requires-python = ">=3.12,<3.14" @@ -31,6 +31,7 @@ product = "VPN App" company = "SNW" copyright = "Copyright (C) 2026 SNW" platforms = ["android", "linux", "windows"] +build_number = 2 [tool.flet.app] path = "." diff --git a/scripts/build-android.sh b/scripts/build-android.sh index 4302f72..a491a9f 100755 --- a/scripts/build-android.sh +++ b/scripts/build-android.sh @@ -64,3 +64,4 @@ EOF fi uv run flet build apk --project vpn-app --org su.snw --artifact vpn-app-arm64 --arch arm64-v8a +./scripts/verify-android-apk.sh build/apk/vpn-app-arm64.apk 2 0.1.1 diff --git a/scripts/verify-android-apk.sh b/scripts/verify-android-apk.sh new file mode 100755 index 0000000..856e74c --- /dev/null +++ b/scripts/verify-android-apk.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +set -euo pipefail + +APK_PATH="${1:-build/apk/vpn-app-arm64.apk}" +EXPECTED_VERSION_CODE="${2:-2}" +EXPECTED_VERSION_NAME="${3:-0.1.1}" +EXPECTED_CERT_SHA256="${VPN_ANDROID_CERT_SHA256:-21d36442ec814833725cee45306daec63960fc988cd1c8705665ecf7d3b7f63d}" +ANDROID_SDK_PATH="${ANDROID_SDK_ROOT:-${ANDROID_HOME:-/home/snw/Android/sdk}}" + +if [[ ! -f "$APK_PATH" ]]; then + echo "APK not found: $APK_PATH" >&2 + exit 2 +fi + +BUILD_TOOLS_DIR="$(find "$ANDROID_SDK_PATH/build-tools" -mindepth 1 -maxdepth 1 -type d -print | sort -V | tail -n 1)" +AAPT="$BUILD_TOOLS_DIR/aapt" +APKSIGNER="$BUILD_TOOLS_DIR/apksigner" +ZIPALIGN="$BUILD_TOOLS_DIR/zipalign" + +for tool in "$AAPT" "$APKSIGNER" "$ZIPALIGN"; do + if [[ ! -x "$tool" ]]; then + echo "Required Android build tool not found: $tool" >&2 + exit 2 + fi +done + +unzip -tqq "$APK_PATH" + +BADGING="$($AAPT dump badging "$APK_PATH")" +EXPECTED_PACKAGE="package: name='su.snw.vpn_app' versionCode='$EXPECTED_VERSION_CODE' versionName='$EXPECTED_VERSION_NAME'" +if [[ "$BADGING" != *"$EXPECTED_PACKAGE"* ]]; then + echo "Unexpected package or version metadata" >&2 + echo "Expected: $EXPECTED_PACKAGE" >&2 + echo "$BADGING" | head -n 1 >&2 + exit 3 +fi + +NATIVE_CODE="$(printf '%s\n' "$BADGING" | sed -n "s/^native-code: //p")" +if [[ "$NATIVE_CODE" != "'arm64-v8a'" ]]; then + echo "Unexpected native ABI set: ${NATIVE_CODE:-none}" >&2 + exit 3 +fi + +SIGNATURE_REPORT="$($APKSIGNER verify --verbose --print-certs "$APK_PATH")" +CERT_SHA256="$(printf '%s\n' "$SIGNATURE_REPORT" | sed -n 's/^Signer #1 certificate SHA-256 digest: //p' | tr -d ':[:space:]' | tr '[:upper:]' '[:lower:]')" +if [[ "$CERT_SHA256" != "$EXPECTED_CERT_SHA256" ]]; then + echo "Unexpected signing certificate SHA-256: $CERT_SHA256" >&2 + exit 3 +fi + +$ZIPALIGN -c -P 16 -v 4 "$APK_PATH" >/dev/null + +APK_SIZE="$(stat -c '%s' "$APK_PATH")" +APK_SHA256="$(sha256sum "$APK_PATH" | cut -d ' ' -f 1)" +echo "APK verification passed" +echo " file: $APK_PATH" +echo " package: su.snw.vpn_app" +echo " version: $EXPECTED_VERSION_NAME ($EXPECTED_VERSION_CODE)" +echo " ABI: arm64-v8a" +echo " size: $APK_SIZE bytes" +echo " SHA-256: $APK_SHA256" diff --git a/uv.lock b/uv.lock index 0e2b0b3..db9fab1 100644 --- a/uv.lock +++ b/uv.lock @@ -371,7 +371,7 @@ requires-dist = [{ name = "flet", specifier = ">=0.86.0" }] [[package]] name = "vpnapp" -version = "0.1.0" +version = "0.1.1" source = { virtual = "." } dependencies = [ { name = "flet" },