bcc347e)
SecretLink
Ручная выдача секретов по Bluetooth LE: Android-телефон — провайдер (секреты зашифрованы, выдача только после одобрения сильной биометрией); портативный Linux-бинарь — requester (в т.ч. из initramfs для ключа LUKS).
Без сети, облака и демона на Linux. Requester не хранит ничего для автоматической выдачи — каждый запрос требует одобрения на телефоне.
Как это работает
Linux (initramfs) Android (provider)
───────────────── ──────────────────
secretlink request-secret ──BLE GATT──▶ CDM wake → runtime провайдера
карточка запроса (overlay / notification / app)
сравните 6-значный код на обоих экранах
Approve → BiometricPrompt
◀── секрет, AES-256-GCM ─────────────── секрет выдан
запись в --output (mode 0600) или --output - (stdout, один write(2))
- Сопряжение (
secretlink pair): P-256 ECDH + SAS на обоих устройствах, биометрия на Android. State: приватная идентичность requester и закреплённый ключ/отпечаток провайдера. - Запрос (
secretlink request-secret): аутентифицированная сессия, свежий 6-значный код, replay protection через request tokens. - Одобрение (Android): overlay (opt-in), уведомление с full-screen intent, карточка в Requests — один runtime-источник. Approve всегда с сильной биометрией; запомненный выбор секрета не обходит одобрение.
- Выдача: секрет в сессии; файл (
--output PATH,0600) или stdout (--output -). Прогресс — stderr или--progress-fd N.
Пробуждение без открытия телефона — Companion Device Manager: Linux рекламирует wake beacon, CompanionWakeService поднимает runtime. У приложения нет INTERNET.
Статус
Проверено end-to-end на железе (Galaxy S25 / Android 16 — провайдер, ThinkBook — requester): pairing, bindings, overlay, cold-start из уведомления, in-app Approve — секрет с mode 0600. Тесты — см. Tests.
Крипто (AES-GCM, P-256, HKDF, SHA-2) зафиксировано для wire-совместимости. Формального аудита нет.
Структура репозитория
| Путь | Содержимое |
|---|---|
android/app |
Провайдер (Compose): CDM, overlay/уведомления, биометрия, секреты/клиенты |
android/secretlink-android |
BLE GATT + Keystore identity |
android/secretlink-protocol |
Wire/protocol (JVM), общие test vectors с Rust |
linux/native |
CLI requester (Rust, static musl) |
linux/luks |
Bash-агент LUKS, конверты SLV1, luks-agent.json, test_*.sh |
linux/dracut |
initramfs: модуль, юниты, install/build, DRACUT.md |
test-vectors / PROTOCOL.md / docs/CLI.md |
Векторы; wire-контракт; state + CLI |
Сборка
Linux requester
cd linux/native
cargo build --release --target x86_64-unknown-linux-musl
# → target/x86_64-unknown-linux-musl/release/secretlink
LUKS из initramfs: linux/dracut/DRACUT.md — установка, вариант [ble], сборка/подпись UKI. Bash-агент дергает request-secret; граф секретов — linux/luks/luks-agent.json.
Android provider
./gradlew :app:assembleRelease
# → android/app/build/outputs/apk/release/app-release.apk (~1.9 MB, R8)
JDK 17+, Android SDK (compileSdk 37), AGP/Gradle из корневых build-файлов. Подпись: ключи хранятся вне репозитория — укажите keystore в android/app/build.gradle.kts или соберите debug.
Linux CLI
secretlink pair-init --name my-laptop
secretlink pair # сравните SAS, одобрите на телефоне
secretlink request-secret --request-id my-request --output /tmp/secret
# --output - → секрет в stdout; --progress-fd N → прогресс на fd
secretlink check-state / show-id
secretlink ble-preflight
secretlink ble-discover / ble-check # диагностика discovery / GATT echo
secretlink ble-wake-advertise # wake beacon вручную
secretlink pair-repair-trust # trust на Android после revoke
LUKS в initramfs: bash linux/luks/secretlink-luks-agent; secretlink luks-agent — legacy.
State — --state (default: --state > SECRETLINK_STATE > /etc/secretlink/state.json if present > ./secretlink.json), root-owned, режимы 0400/0600/0444/0644 (типично /etc → 0644): приватная идентичность requester и публичный ключ/отпечаток провайдера. Подробнее: docs/CLI.md.
Нужны system D-Bus, bluetoothd, powered BLE-адаптер. Не используйте sudo по умолчанию.
request-secret по умолчанию рекламирует wake beacon; --no-wake отключает. При отозванном trusted-client на телефоне request-secret сразу даёт UNTRUSTED (CLI pair-repair-trust на protocol v2 пока недоступна).
Tests
./gradlew :secretlink-protocol:test :secretlink-android:testDebugUnitTest :app:testDebugUnitTest
cd linux/native && cargo test
# bash LUKS: linux/luks/test_*.sh
# v2 vectors: cargo run --example gen_v2_vectors (test-vectors/v2/)
Протокол закреплён общими test vectors (test-vectors/ и test-vectors/v2/) — JVM и Rust не расходятся.
Актуальные числа (после slice 9): 561 JVM (protocol 119, android 60, app 382) + 177 Rust.
Модель безопасности (кратко)
- At rest: AES-256-GCM, ключи в Android Keystore; в UI — write-only.
- P-256 identities; взаимный pin fingerprints/SPKI при pairing.
- Каждая выдача — сильная биометрия; remembered choice только предвыбирает секрет.
- Логи/
toString()без секретов; client revoke ≠ снятие CDM.
Wire: docs/PROTOCOL-V2.md (актуальный); PROTOCOL.md (v1, историческая справка).