montana/Android/Внешний-аудит/08-Воспроизводимая-сборка.md

174 lines
7.6 KiB
Markdown
Raw Normal View History

2026-05-18 22:11:45 +03:00
# 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.