mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Merge branch 'feat/ble_smp_repairing_hardening' into 'master'
feat(ble/bluedroid): add Kconfig options for BLE re-pairing hardening See merge request espressif/esp-idf!51221
This commit is contained in:
@@ -507,6 +507,109 @@ config BT_BLE_SMP_BOND_NVS_FLASH
|
||||
help
|
||||
This select can save SMP bonding keys to nvs flash
|
||||
|
||||
config BT_BLE_SMP_HARDENED_REPAIRING
|
||||
bool "Reject re-pairing that weakens an existing bond"
|
||||
depends on BT_BLE_SMP_ENABLE
|
||||
default y
|
||||
help
|
||||
Controls whether a peer that is already bonded may replace that bond with a
|
||||
weaker one.
|
||||
|
||||
Enabled: when a bond already exists for the peer, a new pairing procedure is
|
||||
refused if it would end up with less MITM protection, without Secure Connections
|
||||
while the stored bond used it, or with a shorter encryption key. The security
|
||||
level announced in a Security Request is also enforced on the pairing that
|
||||
follows it. The link is dropped along with the pairing so that a peer cannot keep
|
||||
retrying with different parameters. The drawback is that a peer which
|
||||
legitimately needs to re-pair at a lower level is refused for good, for instance
|
||||
one whose IO capabilities changed so that Passkey Entry became Just Works. Such a
|
||||
peer has to be unbonded by the application first, using
|
||||
esp_ble_remove_bond_device(). A refusal from this check always keeps the stored
|
||||
bond, even when BT_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL or
|
||||
BT_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL is enabled, so a peer cannot
|
||||
erase the baseline by requesting a downgrade.
|
||||
|
||||
Disabled: any peer can replace an existing bond with one at a lower security
|
||||
level, without Secure Connections, or with a shorter encryption key, and no
|
||||
authentication is needed to do so. These are the re-pairing downgrades described
|
||||
by the BLERP paper as V3, V4 and V6. Only choose this if a peer really has to
|
||||
re-pair at a lower level and the application cannot unbond it beforehand.
|
||||
|
||||
config BT_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL
|
||||
bool "Remove the stored bond when pairing fails as Central"
|
||||
depends on BT_BLE_SMP_ENABLE
|
||||
default y
|
||||
help
|
||||
Controls whether the bonding keys kept in NVS are erased when a BLE pairing
|
||||
procedure fails while the local device is the Central (link-layer master) on
|
||||
that connection. A failed encryption attempt is reported through the same
|
||||
authentication complete path and counts as a failure here. This is the role
|
||||
typically used by GATT Client applications.
|
||||
|
||||
Enabled: historical Central behaviour and the default, so applications that rely
|
||||
on a failed pairing implicitly unbonding the peer keep working after an upgrade.
|
||||
It also means every peer able to make pairing fail can drop the bond without any
|
||||
authentication, by simply sending a Pairing Failed, by letting the 30 second SMP
|
||||
timeout expire, or by disconnecting in the middle of the procedure. As Central,
|
||||
that implicit unbond is also what recovers when the peer deleted the bond on its
|
||||
side and answers LL_ENC_REQ with KEY_MISSING.
|
||||
|
||||
Disabled: the bond survives a failed pairing in the Central role and unbonding
|
||||
becomes an explicit application decision through esp_ble_remove_bond_device().
|
||||
A stale LTK then makes every following connection fail to encrypt until the
|
||||
application unbonds from the ESP_GAP_BLE_AUTH_CMPL_EVT failure callback,
|
||||
preferably after a few attempts rather than on the first one.
|
||||
|
||||
A downgrade refused by BT_BLE_SMP_HARDENED_REPAIRING never erases the bond,
|
||||
regardless of this option.
|
||||
|
||||
config BT_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL
|
||||
bool "Remove the stored bond when pairing fails as Peripheral"
|
||||
depends on BT_BLE_SMP_ENABLE
|
||||
default n
|
||||
help
|
||||
Controls whether the bonding keys kept in NVS are erased when a BLE pairing
|
||||
procedure fails while the local device is the Peripheral (link-layer slave) on
|
||||
that connection. A failed encryption attempt is reported through the same
|
||||
authentication complete path and counts as a failure here. This is the role
|
||||
typically used by GATT Server applications.
|
||||
|
||||
Disabled (default): the bond survives a failed pairing in the Peripheral role.
|
||||
Unbonding is an explicit application decision through esp_ble_remove_bond_device().
|
||||
This avoids losing the bond when the user walks away mid-pairing, when the SMP
|
||||
timeout expires, or when a peer sends Pairing Failed. Legitimate re-pairing from
|
||||
a Central that deleted its own bond is still possible through a new Pairing
|
||||
Request, as long as BT_BLE_SMP_HARDENED_REPAIRING does not refuse a downgrade.
|
||||
|
||||
Enabled: restores the historical behaviour where any pairing failure as
|
||||
Peripheral erases the NVS bond. Prefer this only if the application depends on
|
||||
that implicit unbond and cannot call esp_ble_remove_bond_device() itself.
|
||||
|
||||
A downgrade refused by BT_BLE_SMP_HARDENED_REPAIRING never erases the bond,
|
||||
regardless of this option.
|
||||
|
||||
config BT_BLE_SMP_UNBOND_ON_KEY_MISSING
|
||||
bool "Drop the local LE keys when the peer reports it has no key"
|
||||
depends on BT_BLE_SMP_ENABLE
|
||||
default n
|
||||
help
|
||||
As Central, an encryption attempt using the stored LTK fails with "PIN or Key
|
||||
Missing" when the peer has deleted the bond on its side. This option controls how
|
||||
the stack reacts to that specific error.
|
||||
|
||||
Enabled: the local LE keys are discarded and the link is kept up, so the
|
||||
application can re-pair straight away and recovers without user interaction. The
|
||||
problem is that an impersonating device can report the same error, so a peer can
|
||||
strip the stored security level without any authentication and then re-pair at a
|
||||
weaker level. That is exactly the re-pairing downgrade the BLERP paper describes,
|
||||
which is why this is not the default.
|
||||
|
||||
Disabled: the link is dropped and the keys are kept, so no peer can drop the bond
|
||||
by refusing to encrypt. The problem is that the same stale LTK is then used again
|
||||
on every following connection and encryption keeps failing, which never recovers
|
||||
on its own. The application has to call esp_ble_remove_bond_device() from the
|
||||
ESP_GAP_BLE_AUTH_CMPL_EVT failure callback to get out of it.
|
||||
|
||||
config BT_BLE_PERIPH_PSEUDO_ADDR_BOND
|
||||
bool "Peripheral dual local-identity bond isolation (pseudo address)"
|
||||
depends on BT_BLE_SMP_ENABLE && BT_BLE_50_EXTEND_ADV_EN
|
||||
|
||||
@@ -5054,6 +5054,18 @@ static UINT8 bta_dm_ble_smp_cback (tBTM_LE_EVT event, BD_ADDR bda, tBTM_LE_EVT_D
|
||||
bdcpy(sec_event.auth_cmpl.bd_addr, bda);
|
||||
#if BLE_INCLUDED == TRUE
|
||||
BTM_ReadDevInfo(bda, &sec_event.auth_cmpl.dev_type, &sec_event.auth_cmpl.addr_type);
|
||||
{
|
||||
/* BTM_GetRole() is BR/EDR-only; take the LE role from the ACL or sec record. */
|
||||
tACL_CONN *p_acl = btm_bda_to_acl(bda, BT_TRANSPORT_LE);
|
||||
if (p_acl != NULL) {
|
||||
sec_event.auth_cmpl.is_central = (p_acl->link_role == HCI_ROLE_MASTER);
|
||||
} else {
|
||||
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bda);
|
||||
if (p_dev_rec != NULL) {
|
||||
sec_event.auth_cmpl.is_central = p_dev_rec->role_master;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
p_name = BTM_SecReadDevName(bda);
|
||||
if (p_name != NULL) {
|
||||
@@ -5062,11 +5074,38 @@ static UINT8 bta_dm_ble_smp_cback (tBTM_LE_EVT event, BD_ADDR bda, tBTM_LE_EVT_D
|
||||
sec_event.auth_cmpl.bd_name[0] = '\0';
|
||||
}
|
||||
if (p_data->complt.reason != 0) {
|
||||
BOOLEAN remove_bond = FALSE;
|
||||
|
||||
sec_event.auth_cmpl.fail_reason = BTA_DM_AUTH_CONVERT_SMP_CODE(((UINT8)p_data->complt.reason));
|
||||
/* delete this device entry from Sec Dev DB */
|
||||
APPL_TRACE_WARNING("%s remove bond,rsn %d, BDA:0x%02X%02X%02X%02X%02X%02X", __func__, sec_event.auth_cmpl.fail_reason,
|
||||
bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
|
||||
bta_dm_remove_sec_dev_entry(bda);
|
||||
sec_event.auth_cmpl.keep_bond = p_data->complt.keep_bond;
|
||||
if (p_data->complt.keep_bond) {
|
||||
/* Local policy refused the procedure (hardened re-pairing). The bond is
|
||||
what the check protects, so leave the Sec Dev DB and NVS alone. */
|
||||
APPL_TRACE_WARNING("%s keep bond after local refusal,rsn %d, BDA:0x%02X%02X%02X%02X%02X%02X",
|
||||
__func__, sec_event.auth_cmpl.fail_reason,
|
||||
bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
|
||||
} else if (sec_event.auth_cmpl.is_central) {
|
||||
#if (BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL == TRUE)
|
||||
remove_bond = TRUE;
|
||||
#endif
|
||||
} else {
|
||||
#if (BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL == TRUE)
|
||||
remove_bond = TRUE;
|
||||
#endif
|
||||
}
|
||||
if (remove_bond) {
|
||||
/* delete this device entry from Sec Dev DB */
|
||||
APPL_TRACE_WARNING("%s remove bond,rsn %d, role=%s, BDA:0x%02X%02X%02X%02X%02X%02X",
|
||||
__func__, sec_event.auth_cmpl.fail_reason,
|
||||
sec_event.auth_cmpl.is_central ? "central" : "peripheral",
|
||||
bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
|
||||
bta_dm_remove_sec_dev_entry(bda);
|
||||
} else if (!p_data->complt.keep_bond) {
|
||||
APPL_TRACE_WARNING("%s keep bond after pairing fail,rsn %d, role=%s, BDA:0x%02X%02X%02X%02X%02X%02X",
|
||||
__func__, sec_event.auth_cmpl.fail_reason,
|
||||
sec_event.auth_cmpl.is_central ? "central" : "peripheral",
|
||||
bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
|
||||
}
|
||||
} else {
|
||||
sec_event.auth_cmpl.success = TRUE;
|
||||
if (!p_data->complt.smp_over_br) {
|
||||
|
||||
@@ -749,6 +749,12 @@ typedef struct {
|
||||
tBT_DEVICE_TYPE dev_type;
|
||||
UINT8 auth_mode;
|
||||
BOOLEAN sc_support; /* Denotes if peer device supported secure connection while bonding. */
|
||||
/* TRUE when the stack refused the procedure on purpose (hardened re-pairing)
|
||||
and the existing bond must not be erased. */
|
||||
BOOLEAN keep_bond;
|
||||
/* TRUE when the local device was Central (HCI master) for this LE procedure.
|
||||
Used with REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL / _AS_PERIPHERAL. */
|
||||
BOOLEAN is_central;
|
||||
} tBTA_DM_AUTH_CMPL;
|
||||
|
||||
|
||||
|
||||
@@ -168,6 +168,8 @@ void btc_dm_get_ble_local_keys(tBTA_DM_BLE_LOCAL_KEY_MASK *p_key_mask, BT_OCTET1
|
||||
}
|
||||
|
||||
|
||||
#if (BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL == TRUE) || \
|
||||
(BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL == TRUE)
|
||||
static void btc_dm_remove_ble_bonding_keys(void)
|
||||
{
|
||||
bt_bdaddr_t bd_addr;
|
||||
@@ -183,6 +185,16 @@ static void btc_dm_remove_ble_bonding_keys(void)
|
||||
btc_storage_remove_ble_bonding_keys(&bd_addr);
|
||||
}
|
||||
|
||||
/* Role-specific erase policy. keep_bond (hardened re-pairing refusal) always wins. */
|
||||
static BOOLEAN btc_dm_ble_should_remove_bond_on_fail(BOOLEAN is_central)
|
||||
{
|
||||
if (is_central) {
|
||||
return (BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL == TRUE);
|
||||
}
|
||||
return (BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL == TRUE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if BLE_SMP_BOND_NVS_FLASH
|
||||
static void btc_dm_save_ble_bonding_keys(void)
|
||||
{
|
||||
@@ -284,24 +296,51 @@ static void btc_dm_ble_auth_cmpl_evt (tBTA_DM_AUTH_CMPL *p_auth_cmpl)
|
||||
#endif
|
||||
} else {
|
||||
/*Map the HCI fail reason to bt status */
|
||||
BOOLEAN remove_bond = FALSE;
|
||||
|
||||
switch (p_auth_cmpl->fail_reason) {
|
||||
case BTA_DM_AUTH_SMP_PAIR_AUTH_FAIL:
|
||||
case BTA_DM_AUTH_SMP_CONFIRM_VALUE_FAIL:
|
||||
btc_dm_remove_ble_bonding_keys();
|
||||
status = BT_STATUS_AUTH_FAILURE;
|
||||
remove_bond = TRUE;
|
||||
break;
|
||||
case BTA_DM_AUTH_SMP_PAIR_NOT_SUPPORT:
|
||||
status = BT_STATUS_AUTH_REJECTED;
|
||||
break;
|
||||
default:
|
||||
BTC_TRACE_WARNING ("%s, remove bond in flash bd_addr: %08x%04x", __func__,
|
||||
BTC_TRACE_WARNING ("%s, pairing failed for bd_addr: %08x%04x", __func__,
|
||||
(p_auth_cmpl->bd_addr[0] << 24) + (p_auth_cmpl->bd_addr[1] << 16) + (p_auth_cmpl->bd_addr[2] << 8) + p_auth_cmpl->bd_addr[3],
|
||||
(p_auth_cmpl->bd_addr[4] << 8) + p_auth_cmpl->bd_addr[5]);
|
||||
btc_dm_remove_ble_bonding_keys();
|
||||
status = BT_STATUS_FAIL;
|
||||
remove_bond = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (p_auth_cmpl->keep_bond) {
|
||||
/* Hardened re-pairing refused this procedure; never erase the baseline. */
|
||||
BTC_TRACE_WARNING("%s, keeping bond after local refusal, fail_reason=%d, role=%s, bd_addr: %08x%04x",
|
||||
__func__, p_auth_cmpl->fail_reason,
|
||||
p_auth_cmpl->is_central ? "central" : "peripheral",
|
||||
(p_auth_cmpl->bd_addr[0] << 24) + (p_auth_cmpl->bd_addr[1] << 16) + (p_auth_cmpl->bd_addr[2] << 8) + p_auth_cmpl->bd_addr[3],
|
||||
(p_auth_cmpl->bd_addr[4] << 8) + p_auth_cmpl->bd_addr[5]);
|
||||
#if (BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL == TRUE) || \
|
||||
(BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL == TRUE)
|
||||
} else if (remove_bond && btc_dm_ble_should_remove_bond_on_fail(p_auth_cmpl->is_central)) {
|
||||
BTC_TRACE_WARNING("remove NVS bond, rsn %d, role=%s, bd_addr: %08x%04x",
|
||||
p_auth_cmpl->fail_reason,
|
||||
p_auth_cmpl->is_central ? "central" : "peripheral",
|
||||
(p_auth_cmpl->bd_addr[0] << 24) + (p_auth_cmpl->bd_addr[1] << 16) + (p_auth_cmpl->bd_addr[2] << 8) + p_auth_cmpl->bd_addr[3],
|
||||
(p_auth_cmpl->bd_addr[4] << 8) + p_auth_cmpl->bd_addr[5]);
|
||||
btc_dm_remove_ble_bonding_keys();
|
||||
#endif
|
||||
} else if (remove_bond) {
|
||||
BTC_TRACE_WARNING("%s, keeping bond after pairing fail, role=%s, unbond with esp_ble_remove_bond_device(), bd_addr: %08x%04x",
|
||||
__func__,
|
||||
p_auth_cmpl->is_central ? "central" : "peripheral",
|
||||
(p_auth_cmpl->bd_addr[0] << 24) + (p_auth_cmpl->bd_addr[1] << 16) + (p_auth_cmpl->bd_addr[2] << 8) + p_auth_cmpl->bd_addr[3],
|
||||
(p_auth_cmpl->bd_addr[4] << 8) + p_auth_cmpl->bd_addr[5]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if (CONFIG_BT_STACK_NO_LOG)
|
||||
|
||||
@@ -244,6 +244,30 @@
|
||||
#define UC_BT_BLE_PERIPH_PSEUDO_ADDR_BOND FALSE
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_SMP_HARDENED_REPAIRING
|
||||
#define UC_BT_BLE_SMP_HARDENED_REPAIRING CONFIG_BT_BLE_SMP_HARDENED_REPAIRING
|
||||
#else
|
||||
#define UC_BT_BLE_SMP_HARDENED_REPAIRING FALSE
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL
|
||||
#define UC_BT_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL CONFIG_BT_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL
|
||||
#else
|
||||
#define UC_BT_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL FALSE
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL
|
||||
#define UC_BT_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL CONFIG_BT_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL
|
||||
#else
|
||||
#define UC_BT_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL FALSE
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_SMP_UNBOND_ON_KEY_MISSING
|
||||
#define UC_BT_BLE_SMP_UNBOND_ON_KEY_MISSING CONFIG_BT_BLE_SMP_UNBOND_ON_KEY_MISSING
|
||||
#else
|
||||
#define UC_BT_BLE_SMP_UNBOND_ON_KEY_MISSING FALSE
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_42_FEATURES_SUPPORTED
|
||||
#define UC_BT_BLE_42_FEATURES_SUPPORTED CONFIG_BT_BLE_42_FEATURES_SUPPORTED
|
||||
#else
|
||||
|
||||
@@ -269,6 +269,34 @@
|
||||
#define BLE_PERIPH_PSEUDO_ADDR_BOND FALSE
|
||||
#endif
|
||||
|
||||
/* Refuse a re-pairing that would end up weaker than the bond it replaces. */
|
||||
#if (UC_BT_BLE_SMP_HARDENED_REPAIRING == TRUE)
|
||||
#define BLE_SMP_HARDENED_REPAIRING TRUE
|
||||
#else
|
||||
#define BLE_SMP_HARDENED_REPAIRING FALSE
|
||||
#endif
|
||||
|
||||
/* Erase the NVS bond when pairing/encryption fails while local device is Central. */
|
||||
#if (UC_BT_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL == TRUE)
|
||||
#define BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL TRUE
|
||||
#else
|
||||
#define BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL FALSE
|
||||
#endif
|
||||
|
||||
/* Erase the NVS bond when pairing/encryption fails while local device is Peripheral. */
|
||||
#if (UC_BT_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL == TRUE)
|
||||
#define BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL TRUE
|
||||
#else
|
||||
#define BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL FALSE
|
||||
#endif
|
||||
|
||||
/* Discard the local LE keys when the peer rejects encryption with "PIN or Key Missing". */
|
||||
#if (UC_BT_BLE_SMP_UNBOND_ON_KEY_MISSING == TRUE)
|
||||
#define BLE_SMP_UNBOND_ON_KEY_MISSING TRUE
|
||||
#else
|
||||
#define BLE_SMP_UNBOND_ON_KEY_MISSING FALSE
|
||||
#endif
|
||||
|
||||
#if (UC_BT_BLE_ENABLED ==TRUE)
|
||||
#if (UC_BT_BLE_42_FEATURES_SUPPORTED == TRUE || BLE_50_FEATURE_SUPPORT == FALSE)
|
||||
#define BLE_42_FEATURE_SUPPORT TRUE
|
||||
|
||||
@@ -4251,12 +4251,36 @@ void btm_sec_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable)
|
||||
}
|
||||
|
||||
if (p_acl && p_acl->transport == BT_TRANSPORT_LE) {
|
||||
if (status == HCI_ERR_KEY_MISSING || status == HCI_ERR_AUTH_FAILURE ||
|
||||
status == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE) {
|
||||
p_dev_rec->sec_flags &= ~ (BTM_SEC_LE_LINK_KEY_KNOWN);
|
||||
BOOLEAN disconnect = (status != HCI_SUCCESS);
|
||||
BD_ADDR pseudo_addr;
|
||||
|
||||
/* btm_ble_link_encrypted() runs the SMP state machine and the upper layer
|
||||
callbacks, which may retire p_dev_rec, so keep our own copy of the address. */
|
||||
memcpy(pseudo_addr, p_dev_rec->ble.pseudo_addr, BD_ADDR_LEN);
|
||||
|
||||
#if (BLE_SMP_UNBOND_ON_KEY_MISSING == TRUE)
|
||||
/* The peer answered our LL_ENC_REQ saying it no longer holds the LTK. Forget our
|
||||
copy and keep the link up so the application can pair again right away. This is
|
||||
unauthenticated, an impersonator can report the same error to strip the stored
|
||||
security level, which is why the option defaults to disabled. */
|
||||
if (disconnect && status == HCI_ERR_KEY_MISSING && p_acl->link_role == BTM_ROLE_MASTER) {
|
||||
BTM_TRACE_WARNING("%s peer reports no LTK, dropping the local LE keys\n", __func__);
|
||||
p_dev_rec->sec_flags &= ~(BTM_SEC_LE_LINK_KEY_KNOWN);
|
||||
p_dev_rec->ble.key_type = BTM_LE_KEY_NONE;
|
||||
disconnect = FALSE;
|
||||
}
|
||||
#endif ///BLE_SMP_UNBOND_ON_KEY_MISSING == TRUE
|
||||
|
||||
btm_ble_link_encrypted(pseudo_addr, encr_enable);
|
||||
|
||||
/* A peer that refuses to encrypt must not be able to drop our bond, otherwise it
|
||||
can strip the stored security level and then re-pair at a weaker one. Tear the
|
||||
link down instead and keep the keys until the application unbonds. */
|
||||
if (disconnect && BTM_IsAclConnectionUp(pseudo_addr, BT_TRANSPORT_LE)) {
|
||||
BTM_TRACE_WARNING("%s LE encryption failed (status 0x%02x), disconnecting\n",
|
||||
__func__, status);
|
||||
btm_remove_acl(pseudo_addr, BT_TRANSPORT_LE);
|
||||
}
|
||||
btm_ble_link_encrypted(p_dev_rec->ble.pseudo_addr, encr_enable);
|
||||
return;
|
||||
} else {
|
||||
/* BR/EDR connection, update the encryption key size to be 16 as always */
|
||||
|
||||
@@ -1817,6 +1817,9 @@ typedef struct {
|
||||
BOOLEAN is_pair_cancel;
|
||||
BOOLEAN smp_over_br;
|
||||
tSMP_AUTH_REQ auth_mode;
|
||||
/* Mirrors tSMP_CMPL.keep_bond; layouts must stay identical because
|
||||
btm_proc_smp_cback casts tSMP_EVT_DATA to tBTM_LE_EVT_DATA. */
|
||||
BOOLEAN keep_bond;
|
||||
} tBTM_LE_COMPLT;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -226,6 +226,9 @@ typedef struct {
|
||||
BOOLEAN is_pair_cancel;
|
||||
BOOLEAN smp_over_br;
|
||||
tSMP_AUTH_REQ auth_mode;
|
||||
/* TRUE when the local stack refused the procedure (e.g. hardened re-pairing)
|
||||
and the stored bond must be kept regardless of REMOVE_BOND_ON_PAIR_FAIL_AS_*. */
|
||||
BOOLEAN keep_bond;
|
||||
} tSMP_CMPL;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -338,6 +338,12 @@ typedef struct {
|
||||
UINT32 static_passkey;
|
||||
BOOLEAN accept_specified_sec_auth;
|
||||
tSMP_AUTH_REQ origin_loc_auth_req;
|
||||
#if (BLE_INCLUDED == TRUE && BLE_SMP_HARDENED_REPAIRING == TRUE)
|
||||
BOOLEAN sec_req_rcvd; /* peer asked for security through a Security Request */
|
||||
tSMP_AUTH_REQ sec_req_auth_req; /* AuthReq of that Security Request, peer_auth_req gets
|
||||
overwritten by the Pairing Response that follows */
|
||||
BOOLEAN keep_bond_on_fail; /* set when smp_repairing_is_allowed() refuses the procedure */
|
||||
#endif
|
||||
} tSMP_CB;
|
||||
|
||||
/* Server Action functions are of this type */
|
||||
@@ -487,6 +493,9 @@ extern BOOLEAN smp_encrypt_data (UINT8 *key, UINT8 key_len,
|
||||
UINT8 *plain_text, UINT8 pt_len,
|
||||
tSMP_ENC *p_out);
|
||||
extern BOOLEAN smp_command_has_invalid_parameters(tSMP_CB *p_cb);
|
||||
#if (BLE_INCLUDED == TRUE && BLE_SMP_HARDENED_REPAIRING == TRUE)
|
||||
extern BOOLEAN smp_repairing_is_allowed(tSMP_CB *p_cb, UINT8 *p_reason);
|
||||
#endif
|
||||
extern void smp_reject_unexpected_pairing_command(BD_ADDR bd_addr);
|
||||
extern tSMP_ASSO_MODEL smp_select_association_model(tSMP_CB *p_cb);
|
||||
extern void smp_reverse_array(UINT8 *arr, UINT8 len);
|
||||
|
||||
@@ -260,13 +260,9 @@ void smp_send_pair_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
|
||||
{
|
||||
SMP_TRACE_DEBUG("%s", __func__);
|
||||
|
||||
#if (BLE_INCLUDED == TRUE)
|
||||
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (p_cb->pairing_bda);
|
||||
/* erase all keys when master sends pairing req*/
|
||||
if (p_dev_rec) {
|
||||
btm_sec_clear_ble_keys(p_dev_rec);
|
||||
}
|
||||
#endif ///BLE_INCLUDED == TRUE
|
||||
/* Any existing bond is replaced only once the new pairing is authenticated,
|
||||
see smp_check_auth_req(). */
|
||||
|
||||
/* do not manipulate the key, let app decide,
|
||||
leave out to BTM to mandate key distribution for bonding case */
|
||||
smp_send_cmd(SMP_OPCODE_PAIRING_REQ, p_cb);
|
||||
@@ -485,6 +481,13 @@ void smp_proc_sec_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
|
||||
reason = SMP_PAIR_AUTH_FAIL;
|
||||
smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
|
||||
} else {
|
||||
#if (BLE_INCLUDED == TRUE && BLE_SMP_HARDENED_REPAIRING == TRUE)
|
||||
/* Remember what the peer asked for here: peer_auth_req is overwritten by the
|
||||
Pairing Response that follows, and smp_repairing_is_allowed() has to compare
|
||||
the two to catch a peer that announces a high level and then downgrades. */
|
||||
p_cb->sec_req_rcvd = TRUE;
|
||||
p_cb->sec_req_auth_req = auth_req;
|
||||
#endif
|
||||
/* initialize local i/r key to be default keys */
|
||||
p_cb->peer_auth_req = auth_req;
|
||||
p_cb->local_r_key = p_cb->local_i_key = SMP_SEC_DEFAULT_KEY ;
|
||||
@@ -574,13 +577,11 @@ void smp_proc_pair_cmd(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
|
||||
{
|
||||
UINT8 *p = (UINT8 *)p_data;
|
||||
UINT8 reason = SMP_ENC_KEY_SIZE;
|
||||
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (p_cb->pairing_bda);
|
||||
|
||||
SMP_TRACE_DEBUG("%s", __func__);
|
||||
/* erase all keys if it is slave proc pairing req*/
|
||||
if (p_dev_rec && (p_cb->role == HCI_ROLE_SLAVE)) {
|
||||
btm_sec_clear_ble_keys(p_dev_rec);
|
||||
}
|
||||
|
||||
/* Any existing bond is replaced only once the new pairing is authenticated, see
|
||||
smp_check_auth_req(). An unauthenticated Pairing Request must not drop the bond. */
|
||||
|
||||
p_cb->flags |= SMP_PAIR_FLAG_ENC_AFTER_PAIR;
|
||||
|
||||
@@ -596,6 +597,7 @@ void smp_proc_pair_cmd(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
|
||||
smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
|
||||
return;
|
||||
}
|
||||
|
||||
p_cb->accept_specified_sec_auth = bta_dm_co_ble_get_accept_auth_enable();
|
||||
p_cb->origin_loc_auth_req = bta_dm_co_ble_get_auth_req();
|
||||
if (p_cb->role == HCI_ROLE_SLAVE) {
|
||||
@@ -625,6 +627,15 @@ void smp_proc_pair_cmd(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
|
||||
auth |= SMP_AUTH_GEN_BOND;
|
||||
}
|
||||
p_cb->auth_mode = auth;
|
||||
#if (BLE_INCLUDED == TRUE && BLE_SMP_HARDENED_REPAIRING == TRUE)
|
||||
if (!smp_repairing_is_allowed(p_cb, &reason)) {
|
||||
if (BTM_IsAclConnectionUp(p_cb->pairing_bda, BT_TRANSPORT_LE)) {
|
||||
btm_remove_acl (p_cb->pairing_bda, BT_TRANSPORT_LE);
|
||||
}
|
||||
smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (p_cb->accept_specified_sec_auth) {
|
||||
if ((auth & p_cb->origin_loc_auth_req) != p_cb->origin_loc_auth_req ) {
|
||||
SMP_TRACE_ERROR("%s pairing failed - slave requires auth is 0x%x but peer auth is 0x%x local auth is 0x%x",
|
||||
@@ -664,6 +675,15 @@ void smp_proc_pair_cmd(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
|
||||
auth |= SMP_AUTH_GEN_BOND;
|
||||
}
|
||||
p_cb->auth_mode = auth;
|
||||
#if (BLE_INCLUDED == TRUE && BLE_SMP_HARDENED_REPAIRING == TRUE)
|
||||
if (!smp_repairing_is_allowed(p_cb, &reason)) {
|
||||
if (BTM_IsAclConnectionUp(p_cb->pairing_bda, BT_TRANSPORT_LE)) {
|
||||
btm_remove_acl (p_cb->pairing_bda, BT_TRANSPORT_LE);
|
||||
}
|
||||
smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (p_cb->accept_specified_sec_auth) {
|
||||
if ((auth & p_cb->origin_loc_auth_req) != p_cb->origin_loc_auth_req ) {
|
||||
SMP_TRACE_ERROR("%s pairing failed - master requires auth is 0x%x but peer auth is 0x%x local auth is 0x%x",
|
||||
@@ -1364,6 +1384,22 @@ void smp_check_auth_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
|
||||
"(i-initiator r-responder)\n",
|
||||
__func__, enc_enable, p_cb->local_i_key, p_cb->local_r_key);
|
||||
if (enc_enable == 1) {
|
||||
#if (BLE_INCLUDED == TRUE)
|
||||
/* The new pairing has been authenticated and the link is now encrypted with the
|
||||
freshly generated key, so the previous bond can be dropped. Doing it here rather
|
||||
than when the Pairing Request is exchanged keeps the old key in place for a
|
||||
pairing that never completes. */
|
||||
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(p_cb->pairing_bda);
|
||||
if (p_dev_rec) {
|
||||
if (p_dev_rec->ble.key_type & (BTM_LE_KEY_PENC | BTM_LE_KEY_LENC)) {
|
||||
SMP_TRACE_DEBUG("LE replace bond after enc, BDA:0x%02X%02X%02X%02X%02X%02X",
|
||||
p_cb->pairing_bda[0], p_cb->pairing_bda[1], p_cb->pairing_bda[2],
|
||||
p_cb->pairing_bda[3], p_cb->pairing_bda[4], p_cb->pairing_bda[5]);
|
||||
}
|
||||
btm_sec_clear_ble_keys(p_dev_rec);
|
||||
}
|
||||
#endif ///BLE_INCLUDED == TRUE
|
||||
|
||||
if (p_cb->le_secure_connections_mode_is_used) {
|
||||
/* In LE SC mode LTK is used instead of STK and has to be always saved */
|
||||
p_cb->local_i_key |= SMP_SEC_KEY_TYPE_ENC;
|
||||
@@ -1587,6 +1623,15 @@ void smp_process_io_response(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
|
||||
auth |= SMP_AUTH_GEN_BOND;
|
||||
}
|
||||
p_cb->auth_mode = auth;
|
||||
#if (BLE_INCLUDED == TRUE && BLE_SMP_HARDENED_REPAIRING == TRUE)
|
||||
if (!smp_repairing_is_allowed(p_cb, &reason)) {
|
||||
if (BTM_IsAclConnectionUp(p_cb->pairing_bda, BT_TRANSPORT_LE)) {
|
||||
btm_remove_acl (p_cb->pairing_bda, BT_TRANSPORT_LE);
|
||||
}
|
||||
smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (p_cb->accept_specified_sec_auth) {
|
||||
if ((auth & p_cb->origin_loc_auth_req) != p_cb->origin_loc_auth_req ) {
|
||||
SMP_TRACE_ERROR("pairing failed - slave requires auth is 0x%x but peer auth is 0x%x local auth is 0x%x",
|
||||
|
||||
@@ -1044,6 +1044,10 @@ void smp_proc_pairing_cmpl(tSMP_CB *p_cb)
|
||||
evt_data.cmplt.reason = p_cb->status;
|
||||
evt_data.cmplt.smp_over_br = p_cb->smp_over_br;
|
||||
evt_data.cmplt.auth_mode = 0;
|
||||
#if (BLE_INCLUDED == TRUE && BLE_SMP_HARDENED_REPAIRING == TRUE)
|
||||
/* Copied before smp_reset_control_value() zeros the control block. */
|
||||
evt_data.cmplt.keep_bond = p_cb->keep_bond_on_fail;
|
||||
#endif
|
||||
#if (BLE_INCLUDED == TRUE)
|
||||
tBTM_SEC_DEV_REC *p_rec = btm_find_dev (p_cb->pairing_bda);
|
||||
if (p_cb->status == SMP_SUCCESS) {
|
||||
@@ -1285,6 +1289,104 @@ BOOLEAN smp_parameter_unconditionally_invalid(tSMP_CB *p_cb)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#if (BLE_INCLUDED == TRUE && BLE_SMP_HARDENED_REPAIRING == TRUE)
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function smp_repairing_is_allowed
|
||||
**
|
||||
** Description Called once the association model of a pairing procedure is known.
|
||||
** First pairing (no stored bond) is always allowed. On re-pairing,
|
||||
** refuses when the peer drops AuthReq bits it announced in a Security
|
||||
** Request, when the association model would weaken MITM/SC relative to
|
||||
** the stored bond, or when the encryption key would get shorter.
|
||||
**
|
||||
** Returns TRUE when the pairing may continue. Otherwise FALSE, and *p_reason
|
||||
** holds the SMP failure code to report to the peer. Also sets
|
||||
** p_cb->keep_bond_on_fail so BTA/BTC keep the stored bond even when
|
||||
** REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL / _AS_PERIPHERAL is enabled. The
|
||||
** caller should drop the link so the peer cannot retry with other
|
||||
** parameters until one gets through.
|
||||
**
|
||||
*******************************************************************************/
|
||||
BOOLEAN smp_repairing_is_allowed(tSMP_CB *p_cb, UINT8 *p_reason)
|
||||
{
|
||||
const UINT16 level_bits = SMP_AUTH_YN_BIT | SMP_SC_SUPPORT_BIT;
|
||||
tBTM_SEC_DEV_REC *p_dev_rec;
|
||||
UINT16 new_auth = p_cb->auth_mode;
|
||||
UINT16 sec_req_level;
|
||||
UINT16 old_auth;
|
||||
UINT8 new_key_size;
|
||||
|
||||
p_dev_rec = btm_find_dev(p_cb->pairing_bda);
|
||||
if (p_dev_rec == NULL ||
|
||||
!(p_dev_rec->ble.key_type & (BTM_LE_KEY_PENC | BTM_LE_KEY_LENC))) {
|
||||
/* First pairing with this peer, there is nothing to downgrade. A Security
|
||||
Request that outruns what IO capabilities can deliver must not block it. */
|
||||
SMP_TRACE_DEBUG("LE first pairing, skip downgrade check, BDA:0x%02X%02X%02X%02X%02X%02X",
|
||||
p_cb->pairing_bda[0], p_cb->pairing_bda[1], p_cb->pairing_bda[2],
|
||||
p_cb->pairing_bda[3], p_cb->pairing_bda[4], p_cb->pairing_bda[5]);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* A Security Request carries no authentication, but the pairing command that
|
||||
follows must still claim the level it announced, otherwise the peer can
|
||||
advertise a high level to force a re-pairing and then drop those bits in the
|
||||
Pairing Response. Compare against peer_auth_req (the pairing command), not
|
||||
against the association-model result: IO capabilities that force Just Works
|
||||
are a local limitation, not a peer AuthReq downgrade. Only bits this side
|
||||
also asked for are enforced. */
|
||||
sec_req_level = p_cb->sec_req_auth_req & p_cb->loc_auth_req & level_bits;
|
||||
|
||||
if (p_cb->sec_req_rcvd &&
|
||||
((p_cb->peer_auth_req & sec_req_level) != sec_req_level)) {
|
||||
SMP_TRACE_ERROR("LE re-pair refuse: SR 0x%02x pair 0x%02x loc 0x%02x, BDA:0x%02X%02X%02X%02X%02X%02X",
|
||||
p_cb->sec_req_auth_req, p_cb->peer_auth_req, p_cb->loc_auth_req,
|
||||
p_cb->pairing_bda[0], p_cb->pairing_bda[1], p_cb->pairing_bda[2],
|
||||
p_cb->pairing_bda[3], p_cb->pairing_bda[4], p_cb->pairing_bda[5]);
|
||||
*p_reason = SMP_PAIR_AUTH_FAIL;
|
||||
/* Keep the existing bond: this is a local policy refusal, not a peer that
|
||||
proved the stored keys are gone. BTC must not erase NVS on this path. */
|
||||
p_cb->keep_bond_on_fail = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
old_auth = p_dev_rec->ble.auth_mode;
|
||||
/* Bonds created before auth_mode was recorded, or restored from an older NVS layout,
|
||||
only carry the security level. */
|
||||
if (p_dev_rec->ble.keys.sec_level >= SMP_SEC_AUTHENTICATED) {
|
||||
old_auth |= SMP_AUTH_YN_BIT;
|
||||
}
|
||||
|
||||
if ((new_auth & old_auth & level_bits) != (old_auth & level_bits)) {
|
||||
SMP_TRACE_ERROR("LE re-pair refuse: auth 0x%02x < bonded 0x%02x, BDA:0x%02X%02X%02X%02X%02X%02X",
|
||||
new_auth, old_auth,
|
||||
p_cb->pairing_bda[0], p_cb->pairing_bda[1], p_cb->pairing_bda[2],
|
||||
p_cb->pairing_bda[3], p_cb->pairing_bda[4], p_cb->pairing_bda[5]);
|
||||
*p_reason = SMP_PAIR_AUTH_FAIL;
|
||||
p_cb->keep_bond_on_fail = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
new_key_size = (p_cb->loc_enc_size < p_cb->peer_enc_size) ? p_cb->loc_enc_size
|
||||
: p_cb->peer_enc_size;
|
||||
if (new_key_size < p_dev_rec->ble.keys.key_size) {
|
||||
SMP_TRACE_ERROR("LE re-pair refuse: key size %d < bonded %d, BDA:0x%02X%02X%02X%02X%02X%02X",
|
||||
new_key_size, p_dev_rec->ble.keys.key_size,
|
||||
p_cb->pairing_bda[0], p_cb->pairing_bda[1], p_cb->pairing_bda[2],
|
||||
p_cb->pairing_bda[3], p_cb->pairing_bda[4], p_cb->pairing_bda[5]);
|
||||
*p_reason = SMP_ENC_KEY_SIZE;
|
||||
p_cb->keep_bond_on_fail = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
SMP_TRACE_DEBUG("LE re-pair allowed: auth 0x%02x->0x%02x key %d->%d, BDA:0x%02X%02X%02X%02X%02X%02X",
|
||||
old_auth, new_auth, p_dev_rec->ble.keys.key_size, new_key_size,
|
||||
p_cb->pairing_bda[0], p_cb->pairing_bda[1], p_cb->pairing_bda[2],
|
||||
p_cb->pairing_bda[3], p_cb->pairing_bda[4], p_cb->pairing_bda[5]);
|
||||
return TRUE;
|
||||
}
|
||||
#endif ///BLE_INCLUDED == TRUE && BLE_SMP_HARDENED_REPAIRING == TRUE
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function smp_reject_unexpected_pairing_command
|
||||
|
||||
Reference in New Issue
Block a user