BLE relay — модель угроз

Что может сделать злоумышленник, зная исходники прошивки, но не имея доступа к секретам устройства.

Поверхность атаки

Прошивка публична → известен формат пакетов, длины, алгоритмы (X25519, HMAC-SHA256, HKDF). Эфир BLE открыт → можно слушать advertise и GATT-writes. Никаких секретов в прошивке и в эфире не лежит.

Все ключи хранятся:

Чего атакующий не может сделать

АтакаЗащита
Подделать FIREHMAC-SHA256. Без owner_secret или guest_key — 2128 перебор, неосуществимо.
Подделать TIME writeТот же HMAC поверх bleId + epochMs.
Replay sniffed advertiseCounter монотонный, при повторе/понижении HMAC не сходится.
Replay sniffed fireNonce-ring инвалидирует использованные nonce.
Откатить часы ESP назадRatchet — любой TIME < ratchet отвергается. Ratchet в NVS, переживает power-off.
Использовать просроченный bundleПосле работы ESP ratchet ≥ последнее настоящее время. validUntil просроченного бандла < ratchet → fire fails по TTL.
Сделать +1 год и обнулить всехCap +24ч на одну гостевую TIME-запись. Хозяин unlimited (он доверенный).
Pair'нуться вторым «хозяином» поверхПосле pair ESP сохраняет owner_secret в NVS и больше в pair-mode не входит без factory-reset.

Чего атакующий может — и насколько это больно

1. Drift времени вперёд (DoS легитимных гостей)

Условие: есть валидный (не просроченный) guest-bundle.

Действие: каждый раз когда в зоне — пишет TIME с прыжком +24ч.

Лимит: +24ч за сессию, и нужен fresh fire (если sync был <1ч назад). После 10 заходов ratchet ушёл на 10 дней вперёд → у других легитимных гостей токены кажутся протухшими.

Самовредительство: его собственный токен тоже считается с фейкового времени → быстрее «протухнет» по нему же.

Лечение: хозяин приходит, пишет реальное время — ratchet не двинется (нельзя назад), но новые гостевые токены выпускаются с актуальным validUntil → они валидны для дальнейших гостей. Восстановление требует только обновления валидности на сервере.

Серьёзность: временный DoS до прихода хозяина. Не катастрофа.

2. MITM во время pair

Условие: физически в радиусе ESP в 90-секундном окне после нажатия кнопки pair хозяином.

Действие: подменить свой X25519 public key в pair-handshake.

Результат: ESP спарится с атакующим, не с хозяином. Хозяин думает что спарился, но его телефон не сможет ничего открыть.

Защита сейчас: только окно времени + физическое присутствие хозяина (он сам видит, что pair-индикатор отвалился, и повторяет процедуру).

Усиление: добавить простой verification code (4 цифры на дисплее ESP / в Serial), который хозяин подтверждает в приложении. Сейчас этого нет.

Серьёзность: окно атаки узкое, требует одновременно быть рядом и в нужные 90 секунд. На практике мало реализуемо.

3. Физический factory-reset

Условие: физический доступ к ESP, удержание кнопки long-press.

Действие: NVS стирается, owner_secret удаляется, ESP уходит в pair-mode.

Результат: атакующий может перепарить ESP под себя. Хозяин лишается доступа.

Защита: физическая установка ESP в защищённое место (внутри коробки, под крышкой).

Серьёзность: аналог «оторвать домофон с двери». Не криптографическая проблема.

4. Извлечение owner_secret через UART/JTAG

Условие: физический доступ + flash dump (или secure boot/flash encryption отключены — а они сейчас отключены).

Действие: прочитать NVS с owner_secret.

Результат: полная компрометация устройства, всех гостевых токенов.

Защита: включить ESP32 secure boot + flash encryption (efuse, необратимо). Сейчас НЕ сделано.

Серьёзность: для бытового применения приемлемо. Для серьёзных объектов — надо secure boot.

5. GATT connection squatting (DoS)

Условие: в BLE-радиусе.

Действие: подключаться и не отключаться. NimBLE имеет ограниченное число одновременных соединений.

Результат: легитимные клиенты не могут подключиться пока атакующий держит слот.

Защита сейчас: только таймауты NimBLE.

Усиление: добавить idle-таймаут (если клиент не сделал валидный fire за 5 секунд — disconnect).

Серьёзность: временный DoS пока атакующий в эфире.

6. Connection-spoofing (тычки невалидными writes)

Действие: засыпать ESP мусорными writes.

Результат: ESP их режет, пишет в Serial. Никаких state-изменений. После ухода атакующего — норма.

Сводно

Стандартное усиление для production:

  1. Secure boot + flash encryption (защита от JTAG/dump).
  2. Verification code в pair-flow (защита от MITM).
  3. Idle-таймаут GATT-соединения (защита от squatting).

Это не критично для текущего use-case, но если будут серьёзные объекты — добавлю.