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

174 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.