diff --git a/src/ble/ble_service.c b/src/ble/ble_service.c index 44531fc..e4ecd9f 100644 --- a/src/ble/ble_service.c +++ b/src/ble/ble_service.c @@ -15,6 +15,7 @@ #include #include #include +#include #if IS_ENABLED(CONFIG_SETTINGS) #include #endif @@ -41,7 +42,14 @@ static uint16_t current_notify_mtu = 23U; #if IS_ENABLED(CONFIG_BT_SMP) #define BLE_REQUIRED_SECURITY_LEVEL BT_SECURITY_L4 +#define PAIRING_WINDOW_TIMEOUT_MS (10 * 60 * 1000) +#define PAIRING_WINDOW_GPREGRET_REG 0U +#define PAIRING_WINDOW_GPREGRET_MAGIC 0xB0U static uint32_t pairing_passkey = BT_PASSKEY_RAND; +static bool bond_peer_exists; +static bool pairing_window_open; +static bool pairing_window_limited; +static struct k_work_delayable pairing_window_timeout_work; #endif /* @@ -132,6 +140,84 @@ static void ble_log_local_identity(void) #if IS_ENABLED(CONFIG_BT_SMP) /* NUS 접근 전 보안 level 확인 */ +static bool ble_conn_is_secure(const struct bt_conn *conn); + +static void pairing_window_close(void) +{ + pairing_window_open = false; + pairing_window_limited = false; + k_work_cancel_delayable(&pairing_window_timeout_work); + DBG_CORE("[BLE] Pairing window closed\r\n"); +} + +static void pairing_window_open_set(bool limited) +{ + pairing_window_open = true; + pairing_window_limited = limited; + if (limited) + { + k_work_schedule(&pairing_window_timeout_work, K_MSEC(PAIRING_WINDOW_TIMEOUT_MS)); + DBG_CORE("[BLE] Pairing window opened (10min)\r\n"); + } + else + { + k_work_cancel_delayable(&pairing_window_timeout_work); + DBG_CORE("[BLE] Pairing window opened (factory)\r\n"); + } +} + +static void pairing_window_timeout_handler(struct k_work *work) +{ + ARG_UNUSED(work); + if (pairing_window_limited) + { + pairing_window_close(); + if (current_conn != NULL && !ble_conn_is_secure(current_conn)) + { + DBG_ERR("[BLE] Pairing window timeout -> disconnect unsecured peer\r\n"); + (void)bt_conn_disconnect(current_conn, BT_HCI_ERR_AUTH_FAIL); + } + } +} + +static void bond_check_cb(const struct bt_bond_info *info, void *user_data) +{ + bool *found = (bool *)user_data; + ARG_UNUSED(info); + *found = true; +} + +static bool ble_consume_pairing_window_reset_request(void) +{ +#if NRF_POWER_HAS_GPREGRET + if (nrf_power_gpregret_get(NRF_POWER, PAIRING_WINDOW_GPREGRET_REG) == PAIRING_WINDOW_GPREGRET_MAGIC) + { + nrf_power_gpregret_set(NRF_POWER, PAIRING_WINDOW_GPREGRET_REG, 0U); + return true; + } +#endif + return false; +} + +static void ble_pairing_window_init_from_bonds(void) +{ + bool reset_request = ble_consume_pairing_window_reset_request(); + + bond_peer_exists = false; + bt_foreach_bond(BT_ID_DEFAULT, bond_check_cb, &bond_peer_exists); + + DBG_CORE("[BLE] Bond peer exists=%u reset_window=%u\r\n", bond_peer_exists ? 1U : 0U, reset_request ? 1U : 0U); + + if (bond_peer_exists) + { + pairing_window_close(); + } + else + { + pairing_window_open_set(reset_request); + } +} + static bool ble_conn_is_secure(const struct bt_conn *conn) { return (conn != NULL) && (bt_conn_get_security(conn) >= BLE_REQUIRED_SECURITY_LEVEL); @@ -190,8 +276,16 @@ static int ble_security_configure(void) /* Just Works/passkey pairing 요청 자동 승인 */ static void auth_pairing_confirm(struct bt_conn *conn) { - int err = bt_conn_auth_pairing_confirm(conn); + int err; + if (!pairing_window_open) + { + DBG_ERR("[BLE] Pairing rejected: window closed\r\n"); + (void)bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL); + return; + } + + err = bt_conn_auth_pairing_confirm(conn); if (err) { DBG_ERR("[BLE] Pairing confirm failed err=%d\r\n", err); @@ -214,6 +308,13 @@ static uint32_t auth_app_passkey(struct bt_conn *conn) { unsigned int passkey; + if (!pairing_window_open) + { + DBG_ERR("[BLE] Passkey rejected: pairing window closed\r\n"); + (void)bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL); + return pairing_passkey; + } + ARG_UNUSED(conn); if (ble_passkey_to_uint(m_static_passkey, &passkey) == 0) @@ -241,6 +342,11 @@ static void auth_pairing_complete(struct bt_conn *conn, bool bonded) { ARG_UNUSED(conn); DBG_CORE("[BLE] Pairing complete bonded=%u\r\n", bonded ? 1U : 0U); + if (bonded) + { + bond_peer_exists = true; + pairing_window_close(); + } } /* pairing 실패 시 연결 종료 */ @@ -546,11 +652,10 @@ static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t l /* BLE security level 변경 결과 확인 */ static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) { - ARG_UNUSED(conn); - if (err) { - DBG_ERR("[BLE] Security failed level=%u err=%u\r\n", level, err); + DBG_ERR("[BLE] Security failed level=%u err=%u window=%u\r\n", level, err, pairing_window_open ? 1U : 0U); + (void)bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL); return; } @@ -624,6 +729,9 @@ int ble_service_init(ble_data_rx_cb_t rx_cb) k_work_init(&adv_restart_work, adv_restart_handler); k_work_init_delayable(&adv_timeout_work, adv_timeout_handler); k_work_init_delayable(&conn_param_update_work, conn_param_update_handler); +#if IS_ENABLED(CONFIG_BT_SMP) + k_work_init_delayable(&pairing_window_timeout_work, pairing_window_timeout_handler); +#endif ble_tx_power_init(); // BLE stack 활성화 err = bt_enable(NULL); @@ -653,6 +761,7 @@ int ble_service_init(ble_data_rx_cb_t rx_cb) { return err; } + ble_pairing_window_init_from_bonds(); #endif // NUS service 초기화 @@ -855,6 +964,13 @@ int ble_data_send(const uint8_t *data, uint16_t len) } /* BLE 연결 상태 */ +void ble_pairing_window_mark_reset_open(void) +{ +#if IS_ENABLED(CONFIG_BT_SMP) && NRF_POWER_HAS_GPREGRET + nrf_power_gpregret_set(NRF_POWER, PAIRING_WINDOW_GPREGRET_REG, PAIRING_WINDOW_GPREGRET_MAGIC); +#endif +} + bool ble_is_connected(void) { return (current_conn != NULL); diff --git a/src/ble/ble_service.h b/src/ble/ble_service.h index 8cd566b..44ff1a8 100644 --- a/src/ble/ble_service.h +++ b/src/ble/ble_service.h @@ -36,5 +36,6 @@ int ble_dfu_advertising_enable(void); bool ble_dfu_advertising_is_enabled(void); bool ble_is_connected(void); int ble_disconnect_active(void); +void ble_pairing_window_mark_reset_open(void); #endif /* BLE_SERVICE_H__ */ diff --git a/src/command/handlers/cmd_device.c b/src/command/handlers/cmd_device.c index 270cd5f..646d680 100644 --- a/src/command/handlers/cmd_device.c +++ b/src/command/handlers/cmd_device.c @@ -76,10 +76,12 @@ int cmd_msr(const uint8_t *data, uint8_t data_len) { led_set_state(LED_STATE_BOND_DELETE); bond_data_delete = true; + ble_pairing_window_mark_reset_open(); DBG_PRINTF("[CMD] msr bond data deleted\r\n"); } #else bond_data_delete = true; + ble_pairing_window_mark_reset_open(); DBG_PRINTF("[CMD] msr bond delete skipped (BT_SMP disabled)\r\n"); #endif diff --git a/src/power/power_control.c b/src/power/power_control.c index 858d2b0..b5fe45a 100644 --- a/src/power/power_control.c +++ b/src/power/power_control.c @@ -88,10 +88,12 @@ static void bond_reset_work_handler(struct k_work *work) else { bond_data_delete = true; + ble_pairing_window_mark_reset_open(); DBG_PRINTF("[BTN] BLE bond data deleted\r\n"); } #else bond_data_delete = true; + ble_pairing_window_mark_reset_open(); DBG_PRINTF("[BTN] BLE bond delete skipped (BT_SMP disabled)\r\n"); #endif