# 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`: ```kotlin 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 bridge - `arm64-v8a/libhysteria2.so` — legacy artifact (не используется в v6.5.0, но included) - `armeabi-v7a/libhev-socks5-tunnel.so` - `x86/libhev-socks5-tunnel.so` - `x86_64/libhev-socks5-tunnel.so` **Происхождение native libs:** извлечены из APK [v2rayNG](https://github.com/2dust/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), включая монетные изображения base64 - `bip39-en.txt` — BIP39 EN wordlist (2048 слов, 13116 байт, SHA-256 = `2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda`) - `geoip.dat` — MaxMind country DB for xray geoip routing - `geosite.dat` — MaxMind site categories DB for xray geosite routing - `juno.jpg`, `juno-back.jpg` — изображения монеты Юноны (front / back) - `symbol.jpg` — символ времени (логотип Genesis) Все assets копируются в APK как есть (без сжатия для JPEG). ## §4. Полная последовательность сборки ```bash 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:** 1. Enable `android.useAndroidX=true` + Reproducible APK Builder plugin 2. Set `SOURCE_DATE_EPOCH` env var 3. Strip timestamps from MANIFEST.MF **Cost estimate:** 1 день. Не блокер для текущего release, но обязательно для security-critical распространения. ## §6. Native libraries verification **Способ:** SHA-256 каждой `.so` файла фиксирован, в commit history. При сборке текущей версии auditor сравнивает: ```bash 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 независимым аудитором ```bash # Скачать 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 с нуля: 1. Clone репозитория Montana: ```bash git clone https://github.com/efir369999/Montana-App.git cd Montana-App ``` 2. Установить toolchain: ```bash brew install openjdk@17 brew install --cask android-commandlinetools sdkmanager "platforms;android-34" "build-tools;34.0.0" ``` 3. Прогнать сборку: ```bash cd Montana/Android/MontanaApp ./gradlew assembleDebug ``` 4. Сравнить полученный `app-debug.apk` (debug-signed) с published `montana-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.