Здесь приведен обзор примера GATT Server BLE для ESP32. Конфигурация безопасности позволяет серверу GATT работать в качестве подчиненного (slave) устройства, чтобы осуществить привязку (bond) с master-устройством (клиентом GATT), и установить с ним зашифрованный канал связи. Этот функционал определен в стандарте Bluetooth Specification version 4.2, и реализован стеком ESP-IDF BLE в API-функциях Security Manager Protocol (SMP).
BLE security охватывает 3 внутренне связанные друг с другом концепции: Pairing, Bonding и Encryption. Pairing заключается в обмене поддерживаемыми функциями и типами безопасности и необходимыми ключами. Дополнительно процедура pairing заботится о генерации и обмене общими ключами. Основной стандарт определяет Legacy Pairing и Secure Connections Pairing (введенный в Bluetooth 4.2), оба этих варианта поддерживаются ESP32. Как только обмен ключами завершился, устанавливается временный шифрованный линк, чтобы произошел обмен ключами short term и long term. Bonding заключается в сохранении в энергонезависимой памяти ключей, которыми прошел обмен, чтобы они при будущих соединениях не передавались повторно. И наконец, Encryption относится к шифрованию передаваемых данных с использованием подсистемы AES-128 и обмененных ключей. Также могут быть определены атрибуты сервера, чтобы можно было шифровать только сообщения записи или только сообщения чтения. В любой точке обмена данными slave-устройство может запросить начать шифрование выдачей security request другому устройству пира, которое вернет security response вызовом соответствующего API.
Здесь описывается только конфигурация безопасности. Остальной функционал сервера GATT, такой как определена в таблице служб, объясняется в документации примера GATT Server. Для лучшего понимания процессов в этом примере, рекомендуется ознакомиться с описанием pairing и генерацией ключей, описанных в секции 3.5 стандарта Bluetooth Specification Version 4.2 [Vol 3, Part H].
[Установка параметров безопасности]
ESP32 требует серию параметров безопасности, чтобы определить, как должны строиться pairing request и pairing response. Пакет Pairing Response, который строит GATT Server, включает такие поля, как возможности input/output, Secure Connections pairing, аутентифицированную защиту от атак Man-In-The-Middle (MITM), либо отсутствие требований по безопасности (см. Section 2.3.1 стандарта Bluetooth Specification Version 4.2 [Vol 3, Part H]). В этом примере соответствующая процедура выполняется в функции app_main(). Запрос pairing посылается инициатором (initiator), которым в нашем случае выступает удаленный GATT client (см. его описание выше). Сервер ESP32, реализованный в этом примере, принимает запрос и отвечает пакетом pairing response, который содержит те же параметры безопасности, чтобы оба устройства пришли к соглашению по доступным для них ресурсам и применимому pairing-алгоритму (Just Works или Passkey Entry). И команды запроса pairing, и команды response, обе имеют следующие параметры:
IO Capability: описывает, есть ли у устройства возможности ввода/вывода, такие как экран и/или клавиатура.
OOB Flag: описывает, есть ли у устройства поддержка обмена ключом пароля по побочному каналу (Out of Band passkey), например с помощью NFC или Wi-Fi, чтобы можно было по ним осуществить передачу ключей в качестве TK.
Authorization Request: показывает запрашиваемые свойства безопасности, такие как Bonding, Secure Connections (SC), защита MITM или ничего из этого (none), что должно присутствовать в пакетах Pairing Request и Pairing Response.
Maximum Encryption Key Size: максимальный размер ключа шифрования в байтах.
Initiator Key Distribution/Generation: показывает, какие ключи initiator запрашивает для распространения/генерации, или использования во время фазы Transport Specific Key Distribution. В запросе pairing эти ключи запрашиваются, в то время как в pairing response эти ключи подтверждаются для распространения.
Responder Key Distribution/Generation: показывает, какие ключи initiator у responder-а для распространения/генерации, или использования во время фазы Transport Specific Key Distribution. В запросе pairing эти ключи запрашиваются, в то время как в pairing response эти ключи подтверждаются для распространения.
В коде эти параметры определены следующим образом.
IO Capability:
// IO capability устанавливается в No Input No Output:
esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;
Возможны следующие значения для IO Capabilities:
ESP_IO_CAP_OUT 0 /*!< только отображение */
ESP_IO_CAP_IO 1 /*!< отображение Yes/No */
ESP_IO_CAP_IN 2 /*!< только клавиатура */
ESP_IO_CAP_NONE 3 /*!< NoInput, NoOutput: ни ввода, ни вывода */
ESP_IO_CAP_KBDISP 4 /*!< клавиатура и экран */
Authorization Request:
// Привязка (bonding) с устройством пира после аутентификации:
esp_ble_auth_req_t auth_req = ESP_LE_AUTH_BOND;
Возможные значения для Authorization Request это комбинация запросов Bonding, защиты MITM и Secure Connections:
ESP_LE_AUTH_NO_BOND: без bonding.
ESP_LE_AUTH_BOND: выполняется bonding.
ESP_LE_AUTH_REQ_MITM: защита MITM разрешена.
ESP_LE_AUTH_REQ_SC_ONLY: разрешены Secure Connections без bonding.
ESP_LE_AUTH_REQ_SC_BOND: разрешены Secure Connections с bonding.
ESP_LE_AUTH_REQ_SC_MITM: разрешены Secure Connections с защитой MITM Protection и без bonding.
ESP_LE_AUTH_REQ_SC_MITM_BOND: разрешены Secure Connections с защитой MITM и с bonding.
Maximum Encryption Key Size:
uint8_t key_size = 16; // размер ключа должен быть 7 .. 16 байт
Initiator Key Distribution/Generation:
uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
Инициатор распространяет ключи LTK и IRK установкой масок EncKey и IdKey.
Responder Key Distribution/Generation:
uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
Устройство responder распространяет ключи LTK и IRK установкой масок EncKey и IdKey.
Будучи определенными, параметры устанавливаются функцией esp_ble_gap_set_security_param(), которая установит тип параметра, его значение и длину:
esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t));
esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t));
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t));
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t));
Этой информации достаточно для стека BLE, чтобы он выполнил pairing-процесс, включая подтверждение pairing и генерацию ключа. Эта процедура невидима для пользователя и выполняется стеком автоматически.
[Соединение и Bonding с устройством пира]
Параметры безопасности, установленные ранее, сохраняются локально, чтобы использоваться позже, когда master-устройство подключится к slave-устройству. Каждый раз, когда удаленное устройство подключается к локальному серверу GATT, генерируется событие ESP_GATTS_CONNECT_EVT. Это событие реализовано для выполнения процесса pairing и bonding путем запуска функции esp_ble_set_encryption(), которая в качестве параметра получает адрес удаленного устройства и тип шифрования, которое будет выполняться. Затем стек BLE будет выполнять реальный процесс pairing-а в фоновом режиме. В этом примере шифрование включает защиту от атаки MITM.
case ESP_GATTS_CONNECT_EVT:
// Запускает безопасное соединение с устройством пира, когда примет
// запрос соединения, посланное устройством master.
esp_ble_set_encryption(param->connect.remote_bda, ESP_BLE_SEC_ENCRYPT_MITM);
break;
Доступны следующие типы шифрования:
ESP_BLE_SEC_NONE
ESP_BLE_SEC_ENCRYPT
ESP_BLE_SEC_ENCRYPT_NO_MITM
ESP_BLE_SEC_ENCRYPT_MITM
Различие между ESP_BLE_SEC_ENCRYPT и ESP_BLE_SEC_ENCRYPT_NO_MITM основано на факте, что предыдущее соединение могло иметь уровень безопасности, который должен быть обновлен, что требует повторного обмена ключами.
В этом примере I/O capabilities установлены в No Input No Output, так что будет применен pairing-метод Just Works, который не требует генерации случайного passkey из 6 цифр (подробности см. в таблице ниже). Пользователь может изменить этот пример, чтобы установить I/O capabilities в другие возможные варианты. Таким образом, в зависимости от I/O capabilities удаленного устройства, может быть сгенерирован passkey в устройстве ESP32, который представлен для пользователя событием ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
// Приложение получит это событие, когда IO capability имеет возможность
// вывода (Output), и устройство пира обладает возможностью ввода (Input).
// Отобразить число passkey для пользователя, чтобы он ввел его на
// устройстве пира.
ESP_LOGE(GATTS_TABLE_TAG, "The passkey Notify number:%d",
param->ble_security.key_notif.passkey);
break;
Комбинация возможностей ввода и вывода (IO capabilities), которая определяет используемый алгоритм авторизации:
|
Display Only |
Display Yes/No |
Keyboard Only |
No Input No Output |
Keyboard Display |
Display Only |
Just Works |
Passkey Entry |
Just Works |
Passkey Entry |
Display Yes/No |
Keyboard Only |
Passkey Entry |
No Input No Output |
Just Works |
Keyboard Display |
Passkey Entry |
Just Works |
Passkey Entry |
[Обмен ключами]
Когда клиент подключился к серверу и произошел pairing, происходит обмен ключами, показываемыми параметрами init_key и rsp_key. В этом примере генерируются и распространяются следующие ключи:
• LTK локального устройства (LTK означает Long Term Key)
• IRK локального устройства (IRK означает Identity Resolving Key)
• CSRK локального устройства (CSRK означает Connection Signature Resolving Key)
• LTK устройства пира
• IRK устройства пира
Обратите внимание, что только для примера здесь ключом CSRK устройства пира обмен не происходит. Для каждого сообщения обмена ключами, генерируется событие ESP_GAP_BLE_KEY_EVT, которое может использоваться для печать типа полученного ключа:
case ESP_GAP_BLE_KEY_EVT:
// Покажет для пользователю информацию ключа BLE, общего с устройством пира.
ESP_LOGI(GATTS_TABLE_TAG, "key type = %s",
esp_key_type_to_str(param->ble_security.ble_key.key_type));
break;
Когда обмен ключами прошел успешно, процесс pairing-а завершен. Это вызывает генерацию события ESP_GAP_BLE_AUTH_CMPL_EVT, которое используется для печати такой информации, как адрес удаленного устройства, типа адреса и pairing-статуса:
case ESP_GAP_BLE_AUTH_CMPL_EVT:
esp_bd_addr_t bd_addr;
memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr,
sizeof(esp_bd_addr_t));
ESP_LOGI(GATTS_TABLE_TAG, "remote BD_ADDR: %08x%04x",\
(bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) +
bd_addr[3],
(bd_addr[4] << 8) + bd_addr[5]);
ESP_LOGI(GATTS_TABLE_TAG, "address type = %d",
param->ble_security.auth_cmpl.addr_type);
ESP_LOGI(GATTS_TABLE_TAG, "pair status = %s",
param->ble_security.auth_cmpl.success ? "success" : "fail");
break;
}
[Разрешения безопасности для атрибутов]
Когда определяются атрибуты сервера, могут быть установлены различные разрешения для событий записи и чтения. Разрешениями для атрибутов могут быть:
ESP_GATT_PERM_READ: разрешение на чтение.
ESP_GATT_PERM_READ_ENCRYPTED: разрешение на чтение с шифрованием.
ESP_GATT_PERM_READ_ENC_MITM: разрешение на чтение с шифрованием и защитой MITM.
ESP_GATT_PERM_WRITE: разрешение на запись.
ESP_GATT_PERM_WRITE_ENCRYPTED: разрешение на запись с шифрованием.
ESP_GATT_PERM_WRITE_ENC_MITM: разрешение на запись с шифрованием и защитой MITM.
ESP_GATT_PERM_WRITE_SIGNED: разрешение на запись с подписью.
ESP_GATT_PERM_WRITE_SIGNED_MITM: разрешение на запись с подписью и защитой MITM.
Когда создается таблица служб, у каждого атрибута может быть разрешение на чтение или запись, с шифрованием или без. Когда у атрибута установлены разрешения на шифрование, и устройство пира не имеет требуемого разрешения по безопасности и пробует читать или записывать этот атрибут, локальный хост посылает ошибку недостаточной авторизации (Insufficient Authorization Error). В примере с разрешениями на шифрование определены следующие атрибуты:
...
// Значение характеристики Body Sensor Location:
[HRS_IDX_BOBY_SENSOR_LOC_VAL] =
{
{ESP_GATT_AUTO_RSP},
{ESP_UUID_LEN_16,
(uint8_t *)&body_sensor_location_uuid,
ESP_GATT_PERM_READ_ENCRYPTED,
sizeof(uint8_t),
sizeof(body_sensor_loc_val),
(uint8_t *)body_sensor_loc_val}
},
...
// Значение характеристики Heart Rate Control Point:
[HRS_IDX_HR_CTNL_PT_VAL] =
{
{ESP_GATT_AUTO_RSP},
{ESP_UUID_LEN_16,
(uint8_t *)&heart_rate_ctrl_point,
ESP_GATT_PERM_WRITE_ENCRYPTED|ESP_GATT_PERM_READ_ENCRYPTED,
sizeof(uint8_t),
sizeof(heart_ctrl_point),
(uint8_t *)heart_ctrl_point}
},
...
[Запросы безопасности]
Во время обмена между устройствами master и slave устройство slave может в любой момент запросить начать шифрование путем выдачи команды запроса безопасности (security request command). Эта команда приведет к генерации события ESP_GAP_BLE_SEC_REQ_EVT на master-устройстве, который ответит положительным (true) security response устройству пира, чтобы принять запрос, или отрицательным (false) security response, чтобы отклонить запрос. В этом примере такое событие используется для ответа запуском шифрования с использованием API-функции esp_ble_gap_security_rsp().
case ESP_GAP_BLE_SEC_REQ_EVT:
// Отправка положительного (true) security response устройству пира,
// чтобы принять security request. Если бы security request нельзя
// принять, то второй параметр должен быть false.
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
break;
В этом описании дан обзор реализованных аспектов безопасности сервера GATT. BLE security охватывает Pairing, Bonding и Encryption. Чтобы установить защищенный линк между устройствами master и slave, устанавливаются параметры безопасности, определяющие возможности и функции каждого устройства. Комбинация существующих возможностей взаимодействующих устройств определяет выбор подходящего метода для pairing, который затем выполнит стек BLE. Сразу после этого происходит генерация ключей и обмен ими, и начинается шифрование последующих сообщений с помощью подсистемы AES-128 и этих ключей. Эти выполняемые шаги вызывают генерацию различных событий, которые обрабатываются соответствующими хендлерами GATT и GAP. Обработчики событий могут печатать полезную информацию, такую как типы ключей, которыми произведен обмен, и pairing статус. Кроме того, назначаются разрешения для атрибутов, которые определяют необходимость шифрования для чтения и/или записи. Остальной функционал безопасности сервера GATT, такой как определение служб и характеристик, реализуется примерно так, как сделано в этом примере проекта.
См. также GitHub репозиторий [4].
Комментарии
RSS лента комментариев этой записи