Bezpieczeństwo BLE
Komunikacja bezprzewodowa, jakkolwiek wygodna by była, niesie ze sobą ryzyko podsłuchu. Każdy w zasięgu transmisji może się dowiedzieć, jakie parametry wysyłamy do płytki. Ten problem możemy rozwiązać, szyfrując transmitowane dane. Komunikację radiową wciąż będzie można przechwycić, ale zarejestrowane dane będą miały sens jedynie dla naszego urządzenia i smartfona.
Poza szyfrowaniem istotne jest także uwierzytelnianie, które potwierdza tożsamość urządzeń. Chroni ono przed atakami typu man-in-the-middle (MITM), w których ktoś próbuje podszyć się pod urządzenie, z którym się komunikujemy.
Całe szczęście standard BLE sam w sobie oferuje już mechanizmy zapewniające ochronę danych, a stos BLE w Zephyrze całkowicie je implementuje. Nasza rola ograniczy się więc jedynie do odpowiedniej konfiguracji i użycia tych mechanizmów.
Z punktu widzenia kodu projektu w BLE mamy 4 poziomy bezpieczeństwa połączenia:
- Level 1 (BT_SECURITY_L1): Brak szyfrowania i uwierzytelnienia.
- Level 2 (BT_SECURITY_L2): Jedynie szyfrowanie, brak uwierzytelnienia.
- Level 3 (BT_SECURITY_L3): Szyfrowanie i uwierzytelnienie.
- Level 4 (BT_SECURITY_L4): Zaawansowane bezpieczeństwo, w tym LE Secure Connections i bezpieczna wymiana kluczy przy użyciu algorytmu ECDH.
Można się spotkać jeszcze z poziomem 0, ale jest on używany w tradycyjnym Bluetooth, więc nas nie dotyczy.
Oto jak, w przybliżeniu, wygląda proces łączenia dwóch urządzeń:
- Po włączeniu nasza płytka rozpoczyna rozgłaszanie. Odbywa się to bez szyfrowania.
- Smartfon wykrywa płytkę podczas skanowania.
- Na życzenie użytkownika rozpoczyna się łączenie.
- W procesie parowania następuje wymiana kluczy, po uprzednim uwierzytelnieniu, jeśli jest ono wymagane.
Nasze urządzenie dotychczas zezwalało od razu na połączenie, ale w przypadku bezpieczniejszego podejścia wymagany jest proces parowania. W jego trakcie obie strony wymienią klucze szyfrujące, pozwalające na kodowanie i dekodowanie wiadomości.
Testowanie bezpieczeństwa BLE w projekcie
Dodajmy wyświetlenie poziomu bezpieczeństwa połączenia z telefonem zaraz po wysłaniu nowej wartości z aplikacji nRF Connect. Zmieniony początek funkcji przedstawia listing 1.
static ssize_t write_chr_cb(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
const void *buf, uint16_t len,
uint16_t offset, uint8_t flags) {
LOG_HEXDUMP_INF(buf, len, „Received buffer:”);
LOG_DBG(„BT_SECURITY_L%d”, bt_conn_get_security(conn));
Listing 1. Zmiana w pliku bt_led_svc.c
Warto zauważyć, że wyświetlenie tej informacji odbywa się poprzez makro LOG_DBG a nie LOG_INF ponieważ nie jest ona dla nas jakoś specjalnie istotna. W przyszłości, aby nie zaśmiecać loga, będziemy mogli przestawić poziom logowania z LOG_LEVEL_DBG na LOG_LEVEL_INF i odfiltrować mniej istotne wiadomości. Polecamy zrobić to już w pliku main.c – zmienić ustawienia logowania w linijce 8 z LOG_MODULE_REGISTER(main, LOG_LEVEL_INF) na LOG_MODULE_REGISTER(main, LOG_LEVEL_WRN). Pozbędziemy się w ten sposób z loga niepotrzebnych już wiadomości „Tick”.
Ustalanie stopnia zabezpieczenia danych odbywa się na poziomie definicji charakterystyki. Zmieniając BT_GATT_PERM_WRITE na BT_GATT_PERM_WRITE_ENCRYPT, zablokujemy możliwość zapisu charakterystyki, jeśli połączenie BLE ma poziom bezpieczeństwa bez szyfrowania (Level 1), czyli taki, jaki mamy obecnie.
Sprawdźmy zatem, czy zabezpieczenia BLE działają. Listing 2 zawiera definicje serwisu, w którym podniesiono wymagany poziom zabezpieczeń charakterystyki odpowiedzialnej za częstotliwość migania diody.