montana/Android/Внешний-аудит/08-Воспроизводимая-сборка.md
2026-05-18 22:11:45 +03:00

7.6 KiB
Raw Blame History

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 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 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. Полная последовательность сборки

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 сравнивает:

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 с нуля:

  1. Clone репозитория Montana:

    git clone https://github.com/efir369999/Montana-App.git
    cd Montana-App
    
  2. Установить toolchain:

    brew install openjdk@17
    brew install --cask android-commandlinetools
    sdkmanager "platforms;android-34" "build-tools;34.0.0"
    
  3. Прогнать сборку:

    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.