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))
  1. Сопряжение (secretlink pair): P-256 ECDH + SAS на обоих устройствах, биометрия на Android. State: приватная идентичность requester и закреплённый ключ/отпечаток провайдера.
  2. Запрос (secretlink request-secret): аутентифицированная сессия, свежий 6-значный код, replay protection через request tokens.
  3. Одобрение (Android): overlay (opt-in), уведомление с full-screen intent, карточка в Requests — один runtime-источник. Approve всегда с сильной биометрией; запомненный выбор секрета не обходит одобрение.
  4. Выдача: секрет в сессии; файл (--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, историческая справка).

S
Description
Manual-approval secret release over BLE: Android provider + portable Linux initramfs requester (Rust, static musl binary)
Readme
7.8 MiB
Languages
Kotlin 65.8%
Rust 26.5%
Shell 7%
Python 0.7%