7.6 KiB
08. Воспроизводимая сборка — Montana Android v6.5.0
§1. Сборочное окружение
| Компонент | Версия | Где |
|---|---|---|
| macOS | 14.x / 15.x (Sonoma / Sequoia) | сборка |
| Homebrew | актуальная | /opt/homebrew |
| OpenJDK | 17 | /opt/homebrew/opt/openjdk@17 |
| Android SDK | platform-tools + build-tools 34.0.0 | /opt/homebrew/share/android-commandlinetools |
| Gradle | через ./gradlew (8.x) |
wrapper в проекте |
| Android Gradle Plugin | 8.x | app/build.gradle.kts |
| Kotlin | 2.0+ | через AGP |
apksigner |
34.0.0 | Android SDK build-tools |
zipalign |
34.0.0 | Android SDK build-tools |
§2. Зависимости
app/build.gradle.kts:
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar","*.jar"))))
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("androidx.webkit:webkit:1.11.0")
}
app/libs/:
libv2ray-v2tun.jar— xray-core bindings (libgojni.so wrapper)
app/src/main/jniLibs/:
arm64-v8a/libgojni.so— xray-core native (Go)arm64-v8a/libhev-socks5-tunnel.so— TUN ↔ SOCKS5 bridgearm64-v8a/libhysteria2.so— legacy artifact (не используется в v6.5.0, но included)armeabi-v7a/libhev-socks5-tunnel.sox86/libhev-socks5-tunnel.sox86_64/libhev-socks5-tunnel.so
Происхождение native libs: извлечены из APK v2rayNG v8.0.x (форк xray-core под Android). Лицензия GPL-3.0. SHA-256 verification см. §6.
§3. Assets
app/src/main/assets/:
app.html— single-page приложение (HTML + CSS + JS), включая монетные изображения base64bip39-en.txt— BIP39 EN wordlist (2048 слов, 13116 байт, SHA-256 =2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda)geoip.dat— MaxMind country DB for xray geoip routinggeosite.dat— MaxMind site categories DB for xray geosite routingjuno.jpg,juno-back.jpg— изображения монеты Юноны (front / back)symbol.jpg— символ времени (логотип Genesis)
Все assets копируются в APK как есть (без сжатия для JPEG).
§4. Полная последовательность сборки
cd /Users/kh./Python/Ничто/Montana/Android/MontanaApp
# 1. Bump version в build.gradle.kts вручную либо через sed
# versionCode = NNNNN
# versionName = "X.Y.Z"
# 2. Сборка debug APK через Gradle
export JAVA_HOME=/opt/homebrew/opt/openjdk@17
export PATH="$JAVA_HOME/bin:$PATH"
./gradlew assembleDebug
# Результат: app/build/outputs/apk/debug/app-debug.apk (~32 MB)
# Эта APK подписана debug-keystore Android SDK — пригодна для тестирования,
# но НЕ для production (другой SHA-256 fingerprint)
# 3. Production sign через Genesis keystore
SDK=/opt/homebrew/share/android-commandlinetools
APKSIGNER=$(ls $SDK/build-tools/*/apksigner | tail -1)
ZIPALIGN=$(ls $SDK/build-tools/*/zipalign | tail -1)
KEYPASS=$(cat /Users/kh./Python/Ничто/Montana/Android/keystore/.password)
# 3a. zipalign для оптимизации
"$ZIPALIGN" -p -f 4 \
app/build/outputs/apk/debug/app-debug.apk \
/tmp/montana-aligned.apk
# 3b. Sign с Genesis keystore
"$APKSIGNER" sign \
--ks /Users/kh./Python/Ничто/Montana/Android/keystore/montana.keystore \
--ks-pass "pass:$KEYPASS" \
--key-pass "pass:$KEYPASS" \
--ks-key-alias montana \
--out /Users/kh./Python/Ничто/Montana/Android/build/montana-X.Y.Z.apk \
/tmp/montana-aligned.apk
# 4. Verification
"$APKSIGNER" verify --print-certs /Users/kh./Python/Ничто/Montana/Android/build/montana-X.Y.Z.apk
# Ожидаемый SHA-256 fingerprint: 305bc99b40e6106f28c6fcc5dce4772761d2630d5aca9fee076dc0691913ce4d
§5. Reproducibility audit
Заявление: при идентичном source + identical toolchain + identical timestamps две сборки должны давать byte-identical APK.
Реальность: Android Gradle Plugin по умолчанию НЕ воспроизводим из-за:
- Время компиляции в
META-INF/MANIFEST.MF - Random salt в dex builder (можно отключить через
android.experimental.legacyTransform.forceNonIncremental=true) - Порядок entries в ZIP
Текущий status: v6.5.0 НЕ reproducible byte-exact. Two consecutive builds могут отличаться на ~1-2% (timestamps).
Closure path:
- Enable
android.useAndroidX=true+ Reproducible APK Builder plugin - Set
SOURCE_DATE_EPOCHenv var - Strip timestamps from MANIFEST.MF
Cost estimate: 1 день. Не блокер для текущего release, но обязательно для security-critical распространения.
§6. Native libraries verification
Способ: SHA-256 каждой .so файла фиксирован, в commit history. При сборке текущей версии auditor сравнивает:
cd /Users/kh./Python/Ничто/Montana/Android/MontanaApp/app/src/main/jniLibs
sha256sum arm64-v8a/*.so armeabi-v7a/*.so x86/*.so x86_64/*.so
Ожидаемые значения (приведены в приложения/нативные-библиотеки.md после первого release-tag).
Слабость: если злоумышленник подменил .so файлы при инициальном извлечении из v2rayNG → SHA-256 в commit history тоже malicious. Mitigation: cross-check с upstream v2rayNG release SHA-256 (опубликованы на GitHub releases).
§7. APK signature verification независимым аудитором
# Скачать APK
curl -O https://montana.quest/vpn/montana-v6.5.0.apk
# Verify signature
apksigner verify --print-certs montana-v6.5.0.apk
# Ожидаемый output:
# Signer #1 certificate DN: CN=Montana VPN, O=Montana Network, L=Genesis, C=RU
# Signer #1 certificate SHA-256 digest: 305bc99b40e6106f28c6fcc5dce4772761d2630d5aca9fee076dc0691913ce4d
# Signer #1 certificate SHA-1 digest: a0be58ad4d9c353eb954daf530d808ff661d9a01
Несовпадение SHA-256 fingerprint = либо APK не подписана Genesis keystore (= malicious), либо keystore был ротирован (= анонсировано отдельно в Montana/Russian/Genesis/).
§8. Сборка с нуля
Для аудитора который хочет собрать APK с нуля:
-
Clone репозитория Montana:
git clone https://github.com/efir369999/Montana-App.git cd Montana-App -
Установить toolchain:
brew install openjdk@17 brew install --cask android-commandlinetools sdkmanager "platforms;android-34" "build-tools;34.0.0" -
Прогнать сборку:
cd Montana/Android/MontanaApp ./gradlew assembleDebug -
Сравнить полученный
app-debug.apk(debug-signed) с publishedmontana-v6.5.0.apk(Genesis-signed):- Unzip обе APK
- Сравнить
classes.dex,resources.arsc,AndroidManifest.xml, native.so - Должны быть идентичны (cryptographically — диff только в META-INF/CERT.SF и META-INF/CERT.RSA)
Closure path к full reproducibility: §5.