diff --git a/doc/releases/migration-guide-4.4.rst b/doc/releases/migration-guide-4.4.rst index 554607e5f857..762082635b1c 100644 --- a/doc/releases/migration-guide-4.4.rst +++ b/doc/releases/migration-guide-4.4.rst @@ -43,6 +43,10 @@ Bluetooth Host * :c:member:`bt_conn_le_info.interval` has been deprecated. Use :c:member:`bt_conn_le_info.interval_us` instead. Note that the units have changed: ``interval`` was in units of 1.25 milliseconds, while ``interval_us`` is in microseconds. +* Legacy Bluetooth LE pairing using the passkey entry method no longer grants authenticated (MITM) + protection as of the Bluetooth Core Specification v6.2. Stored bonds that were generated using + this method will be downgraded to unauthenticated when loaded from persistent storage, resulting + in a lower security level. Networking ********** diff --git a/subsys/bluetooth/host/Kconfig b/subsys/bluetooth/host/Kconfig index 9eba33273066..e4a381e9b05a 100644 --- a/subsys/bluetooth/host/Kconfig +++ b/subsys/bluetooth/host/Kconfig @@ -1138,6 +1138,15 @@ config BT_CONN_DISABLE_SECURITY WARNING: This option enables anyone to snoop on-air traffic. Use of this feature in production is strongly discouraged. +config BT_SMP_LEGACY_PAIR_ONLY + bool "Force legacy pairing" + depends on BT_TESTING + depends on !(BT_SMP_SC_PAIR_ONLY || BT_SMP_SC_ONLY) + help + This option enforces legacy pairing. This is required for testing + legacy pairing between two Zephyr Bluetooth devices, as without this + option the devices will default to using Secure Connections pairing. + rsource "./classic/Kconfig" config BT_HCI_VS_EVT_USER diff --git a/subsys/bluetooth/host/keys.c b/subsys/bluetooth/host/keys.c index e2116f3bedeb..4707a2e0fe23 100644 --- a/subsys/bluetooth/host/keys.c +++ b/subsys/bluetooth/host/keys.c @@ -490,6 +490,18 @@ static int keys_set(const char *name, size_t len_rd, settings_read_cb read_cb, memcpy(keys->storage_start, val, len); } + /* As of Core v6.2, authenticated keys are only valid for OOB or LE SC pairing + * methods. This check ensures that keys are valid if a device is updated from a + * previous version that did not enforce this requirement. + */ + if ((keys->flags & BT_KEYS_AUTHENTICATED) && + !((keys->flags & BT_KEYS_OOB) || (keys->flags & BT_KEYS_SC))) { + LOG_WRN("The keys for %s are downgraded to unauthenticated as they no longer meet " + "authentication requirements", + bt_addr_le_str(&addr)); + keys->flags &= ~BT_KEYS_AUTHENTICATED; + } + check_and_set_id_conflict_flag(keys); LOG_DBG("Successfully restored keys for %s", bt_addr_le_str(&addr)); diff --git a/subsys/bluetooth/host/smp.c b/subsys/bluetooth/host/smp.c index 1e28a4e0c84f..11fa7a4361f3 100644 --- a/subsys/bluetooth/host/smp.c +++ b/subsys/bluetooth/host/smp.c @@ -92,21 +92,21 @@ LOG_MODULE_REGISTER(bt_smp); #if defined(CONFIG_BT_CLASSIC) #define BT_SMP_AUTH_MASK_SC 0x2f -#if defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) +#if defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) || defined(CONFIG_BT_SMP_LEGACY_PAIR_ONLY) #define BT_SMP_AUTH_DEFAULT (BT_SMP_AUTH_BONDING_FLAGS | BT_SMP_AUTH_CT2) #else #define BT_SMP_AUTH_DEFAULT (BT_SMP_AUTH_BONDING_FLAGS | BT_SMP_AUTH_CT2 |\ BT_SMP_AUTH_SC) -#endif /* CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY */ +#endif /* CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY || CONFIG_BT_SMP_LEGACY_PAIR_ONLY */ #else #define BT_SMP_AUTH_MASK_SC 0x0f -#if defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) +#if defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) || defined(CONFIG_BT_SMP_LEGACY_PAIR_ONLY) #define BT_SMP_AUTH_DEFAULT (BT_SMP_AUTH_BONDING_FLAGS) #else #define BT_SMP_AUTH_DEFAULT (BT_SMP_AUTH_BONDING_FLAGS | BT_SMP_AUTH_SC) -#endif /* CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY */ +#endif /* CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY || CONFIG_BT_SMP_LEGACY_PAIR_ONLY */ #endif /* CONFIG_BT_CLASSIC */ @@ -321,7 +321,8 @@ static struct { static bool le_sc_supported(void) { - if (IS_ENABLED(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)) { + if (IS_ENABLED(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) || + IS_ENABLED(CONFIG_BT_SMP_LEGACY_PAIR_ONLY)) { return false; } @@ -666,7 +667,9 @@ static bool update_keys_check(struct bt_smp *smp, struct bt_keys *keys) } if ((keys->flags & BT_KEYS_AUTHENTICATED) && - smp->method == JUST_WORKS) { + ((smp->method == JUST_WORKS) || + (!atomic_test_bit(smp->flags, SMP_FLAG_SC) && + (smp->method == PASSKEY_DISPLAY || smp->method == PASSKEY_INPUT)))) { return false; } @@ -2491,11 +2494,12 @@ static uint8_t legacy_request_tk(struct bt_smp *smp) * Fail if we have keys that are stronger than keys that will be * distributed in new pairing. This is to avoid replacing authenticated * keys with unauthenticated ones. - */ + */ keys = bt_keys_find_addr(conn->id, &conn->le.dst); if (keys && (keys->flags & BT_KEYS_AUTHENTICATED) && - smp->method == JUST_WORKS) { - LOG_ERR("JustWorks failed, authenticated keys present"); + (smp->method == JUST_WORKS || smp->method == PASSKEY_DISPLAY || + smp->method == PASSKEY_INPUT)) { + LOG_ERR("Pairing failed, authenticated keys present"); return BT_SMP_ERR_UNSPECIFIED; } @@ -2983,7 +2987,9 @@ static uint8_t remote_sec_level_reachable(struct bt_smp *smp) } __fallthrough; case BT_SECURITY_L3: - if (smp->method == JUST_WORKS) { + if (smp->method == JUST_WORKS || + (!atomic_test_bit(smp->flags, SMP_FLAG_SC) && + (smp->method == PASSKEY_DISPLAY || smp->method == PASSKEY_INPUT))) { return BT_SMP_ERR_AUTH_REQUIREMENTS; } @@ -6327,12 +6333,16 @@ void bt_smp_update_keys(struct bt_conn *conn) case LE_SC_OOB: case LEGACY_OOB: conn->le.keys->flags |= BT_KEYS_OOB; - /* fallthrough */ + conn->le.keys->flags |= BT_KEYS_AUTHENTICATED; + break; case PASSKEY_DISPLAY: case PASSKEY_INPUT: case PASSKEY_CONFIRM: - conn->le.keys->flags |= BT_KEYS_AUTHENTICATED; - break; + if (atomic_test_bit(smp->flags, SMP_FLAG_SC)) { + conn->le.keys->flags |= BT_KEYS_AUTHENTICATED; + break; + } + /* fallthrough */ case JUST_WORKS: default: /* unauthenticated key, clear it */