mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-10-17 10:57:22 +00:00
fix(nimble): Fix for BLERP attack
This commit is contained in:
@@ -11374,8 +11374,8 @@ ble_gap_reset_irk(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
ble_gap_unpair(const ble_addr_t *peer_addr)
|
||||
static int
|
||||
ble_gap_unpair_ex(const ble_addr_t *peer_addr, bool terminate)
|
||||
{
|
||||
|
||||
#if NIMBLE_BLE_SM
|
||||
@@ -11421,7 +11421,9 @@ ble_gap_unpair(const ble_addr_t *peer_addr)
|
||||
defer_add_pending = 1;
|
||||
}
|
||||
#endif
|
||||
ble_gap_terminate_with_conn(conn, BLE_ERR_REM_USER_CONN_TERM);
|
||||
if (terminate) {
|
||||
ble_gap_terminate_with_conn(conn, BLE_ERR_REM_USER_CONN_TERM);
|
||||
}
|
||||
}
|
||||
|
||||
ble_hs_unlock();
|
||||
@@ -11518,10 +11520,23 @@ ble_gap_unpair(const ble_addr_t *peer_addr)
|
||||
|
||||
return 0;
|
||||
#else
|
||||
(void)terminate;
|
||||
return BLE_HS_ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
ble_gap_unpair(const ble_addr_t *peer_addr)
|
||||
{
|
||||
return ble_gap_unpair_ex(peer_addr, true);
|
||||
}
|
||||
|
||||
int
|
||||
ble_gap_unpair_keep_conn(const ble_addr_t *peer_addr)
|
||||
{
|
||||
return ble_gap_unpair_ex(peer_addr, false);
|
||||
}
|
||||
|
||||
int
|
||||
ble_gap_unpair_oldest_peer(void)
|
||||
{
|
||||
|
||||
@@ -197,6 +197,7 @@ void ble_gap_preempt(void);
|
||||
void ble_gap_preempt_done(void);
|
||||
|
||||
int ble_gap_terminate_with_conn(struct ble_hs_conn *conn, uint8_t hci_reason);
|
||||
int ble_gap_unpair_keep_conn(const ble_addr_t *peer_addr);
|
||||
void ble_gap_reset_state(int reason);
|
||||
void ble_gap_conn_broken(uint16_t conn_handle, int reason);
|
||||
int32_t ble_gap_timer(void);
|
||||
|
||||
+252
-28
@@ -46,6 +46,7 @@
|
||||
#include "nimble/ble.h"
|
||||
#include "nimble/nimble_opt.h"
|
||||
#include "host/ble_sm.h"
|
||||
#include "host/ble_store.h"
|
||||
#include "ble_hs_priv.h"
|
||||
#include "ble_hs_resolv_priv.h"
|
||||
#include "../store/config/src/ble_store_config_priv.h"
|
||||
@@ -1022,13 +1023,20 @@ ble_sm_read_bond(uint16_t conn_handle, struct ble_store_value_sec *out_bond)
|
||||
static int
|
||||
ble_sm_chk_repeat_pairing(uint16_t conn_handle,
|
||||
ble_sm_proc_flags proc_flags,
|
||||
uint8_t key_size)
|
||||
uint8_t key_size, uint8_t *out_sm_err,
|
||||
bool *out_terminate, bool *out_keep_bond)
|
||||
{
|
||||
struct ble_gap_repeat_pairing rp;
|
||||
struct ble_store_value_sec bond;
|
||||
struct ble_hs_conn *conn;
|
||||
bool link_encrypted;
|
||||
int retried;
|
||||
int rc;
|
||||
|
||||
*out_sm_err = 0;
|
||||
*out_terminate = false;
|
||||
*out_keep_bond = false;
|
||||
|
||||
retried = 0;
|
||||
do {
|
||||
#if MYNEWT_VAL(BLE_DEFER_CONN_EVENTS)
|
||||
@@ -1056,11 +1064,27 @@ ble_sm_chk_repeat_pairing(uint16_t conn_handle,
|
||||
case 0:
|
||||
break;
|
||||
case BLE_HS_ENOENT:
|
||||
case BLE_HS_ENOTSUP:
|
||||
return 0;
|
||||
default:
|
||||
return rc;
|
||||
}
|
||||
|
||||
ble_hs_lock();
|
||||
conn = ble_hs_conn_find(conn_handle);
|
||||
link_encrypted = conn != NULL && conn->bhc_sec_state.encrypted;
|
||||
ble_hs_unlock();
|
||||
#if MYNEWT_VAL(BLE_SMP_REQUIRE_ENC_BEFORE_REPAIR)
|
||||
if (!link_encrypted) {
|
||||
*out_sm_err = BLE_SM_ERR_AUTHREQ;
|
||||
*out_terminate = true;
|
||||
*out_keep_bond = true;
|
||||
return BLE_HS_EALREADY;
|
||||
}
|
||||
#else
|
||||
(void)link_encrypted;
|
||||
#endif
|
||||
|
||||
#if MYNEWT_VAL(BLE_DEFER_CONN_EVENTS)
|
||||
/*
|
||||
* Repeat pairing needs a synchronous app decision and cannot be
|
||||
@@ -1091,6 +1115,23 @@ ble_sm_chk_repeat_pairing(uint16_t conn_handle,
|
||||
rp.new_sc = !!(proc_flags & BLE_SM_PROC_F_SC);
|
||||
rp.new_bonding = !!(proc_flags & BLE_SM_PROC_F_BONDING);
|
||||
|
||||
/*
|
||||
* A repeat pairing must not reduce the security of an existing bond.
|
||||
* Applications may still explicitly delete the bond and retry when a
|
||||
* replacement with equal or stronger security is intentional.
|
||||
*/
|
||||
#if MYNEWT_VAL(BLE_SMP_HARDENED_REPAIRING)
|
||||
if ((bond.sc && !rp.new_sc) ||
|
||||
(bond.authenticated && !rp.new_authenticated) ||
|
||||
(key_size < bond.key_size)) {
|
||||
*out_sm_err = key_size < bond.key_size ? BLE_SM_ERR_ENC_KEY_SZ
|
||||
: BLE_SM_ERR_AUTHREQ;
|
||||
*out_terminate = true;
|
||||
*out_keep_bond = true;
|
||||
return BLE_HS_EALREADY;
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = ble_gap_repeat_pairing_event(&rp);
|
||||
if (rc == BLE_GAP_REPEAT_PAIRING_RETRY) {
|
||||
if (retried) {
|
||||
@@ -1105,6 +1146,60 @@ ble_sm_chk_repeat_pairing(uint16_t conn_handle,
|
||||
return BLE_HS_EALREADY;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ble_sm_maybe_remove_bond_on_fail(uint16_t conn_handle,
|
||||
const struct ble_sm_result *res)
|
||||
{
|
||||
struct ble_hs_conn_addrs addrs;
|
||||
struct ble_hs_conn *conn;
|
||||
ble_addr_t peer;
|
||||
int is_master;
|
||||
|
||||
if (res == NULL ||
|
||||
res->keep_bond ||
|
||||
!res->enc_cb ||
|
||||
res->app_status == 0 ||
|
||||
res->app_status == BLE_HS_ENOTCONN) {
|
||||
/* Link drop is not a pairing failure. ble_sm_connection_broken()
|
||||
* still sets enc_cb with ENOTCONN while the conn object exists;
|
||||
* unpairing here would wipe IRK after a successful bond.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
#if !MYNEWT_VAL(BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL) && \
|
||||
!MYNEWT_VAL(BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL)
|
||||
return;
|
||||
#else
|
||||
ble_hs_lock();
|
||||
conn = ble_hs_conn_find(conn_handle);
|
||||
if (conn == NULL) {
|
||||
ble_hs_unlock();
|
||||
return;
|
||||
}
|
||||
is_master = !!(conn->bhc_flags & BLE_HS_CONN_F_MASTER);
|
||||
ble_hs_conn_addrs(conn, &addrs);
|
||||
peer = addrs.peer_id_addr;
|
||||
ble_hs_unlock();
|
||||
|
||||
if (is_master) {
|
||||
#if !MYNEWT_VAL(BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL)
|
||||
return;
|
||||
#endif
|
||||
} else {
|
||||
#if !MYNEWT_VAL(BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL)
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Erase the bond only; the link stays up so the application decides when
|
||||
* to disconnect, matching Bluedroid's remove-bond-on-pair-fail behaviour.
|
||||
*/
|
||||
ble_gap_unpair_keep_conn(&peer);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ble_sm_process_result(uint16_t conn_handle, struct ble_sm_result *res,
|
||||
bool tx_fail)
|
||||
@@ -1160,12 +1255,21 @@ ble_sm_process_result(uint16_t conn_handle, struct ble_sm_result *res,
|
||||
|
||||
ble_hs_unlock();
|
||||
|
||||
if (res && res->terminate) {
|
||||
ble_gap_terminate(conn_handle, BLE_ERR_AUTH_FAIL);
|
||||
}
|
||||
|
||||
/* Erase bond before BLE_RESTART_PAIR so repeat-pairing sees no bond. */
|
||||
ble_sm_maybe_remove_bond_on_fail(conn_handle, res);
|
||||
|
||||
if (proc == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
#if MYNEWT_VAL(BLE_RESTART_PAIR)
|
||||
if (res->app_status == BLE_HS_HCI_ERR(BLE_ERR_PINKEY_MISSING)) {
|
||||
if (!res->terminate &&
|
||||
!res->restore &&
|
||||
res->app_status == BLE_HS_HCI_ERR(BLE_ERR_PINKEY_MISSING)) {
|
||||
ble_hs_lock();
|
||||
conn = ble_hs_conn_find(conn_handle);
|
||||
if (conn == NULL) {
|
||||
@@ -1371,6 +1475,7 @@ ble_sm_enc_event_rx(uint16_t conn_handle, uint8_t evt_status, int encrypted)
|
||||
{
|
||||
struct ble_sm_result res;
|
||||
struct ble_sm_proc *proc;
|
||||
bool terminate_conn;
|
||||
int authenticated;
|
||||
int bonded;
|
||||
int key_size;
|
||||
@@ -1383,6 +1488,7 @@ ble_sm_enc_event_rx(uint16_t conn_handle, uint8_t evt_status, int encrypted)
|
||||
bonded = -1;
|
||||
key_size = 0;
|
||||
sc = -1;
|
||||
terminate_conn = false;
|
||||
|
||||
ble_hs_lock();
|
||||
|
||||
@@ -1423,6 +1529,26 @@ ble_sm_enc_event_rx(uint16_t conn_handle, uint8_t evt_status, int encrypted)
|
||||
sc = !!(proc->flags & BLE_SM_PROC_F_SC);
|
||||
bonded = 1;
|
||||
res.restore = 1;
|
||||
if (evt_status != 0) {
|
||||
if (evt_status == BLE_ERR_PINKEY_MISSING &&
|
||||
(proc->flags & BLE_SM_PROC_F_INITIATOR) &&
|
||||
(MYNEWT_VAL(BLE_SMP_UNBOND_ON_KEY_MISSING) ||
|
||||
MYNEWT_VAL(BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL))) {
|
||||
/*
|
||||
* Peer reports no LTK. Erase our bond (Central remove-on-
|
||||
* fail and/or unbond-on-key-missing) and leave the ACL up
|
||||
* so BLE_RESTART_PAIR can recover on this link — matches
|
||||
* historical NimBLE one-sided bond recovery used by CI.
|
||||
*/
|
||||
res.restore = 0;
|
||||
res.enc_cb = 1;
|
||||
res.app_status = BLE_HS_HCI_ERR(evt_status);
|
||||
res.keep_bond = 0;
|
||||
} else {
|
||||
terminate_conn = true;
|
||||
res.keep_bond = 1;
|
||||
}
|
||||
}
|
||||
|
||||
key_size = proc->key_size;
|
||||
break;
|
||||
@@ -1453,10 +1579,18 @@ ble_sm_enc_event_rx(uint16_t conn_handle, uint8_t evt_status, int encrypted)
|
||||
if (proc == NULL || proc->state == BLE_SM_PROC_STATE_NONE) {
|
||||
res.enc_cb = 1;
|
||||
res.app_status = BLE_HS_HCI_ERR(evt_status);
|
||||
if (proc == NULL && evt_status != 0) {
|
||||
/* No active security procedure owns this controller failure. */
|
||||
res.keep_bond = 1;
|
||||
}
|
||||
}
|
||||
|
||||
ble_hs_unlock();
|
||||
|
||||
if (terminate_conn) {
|
||||
ble_gap_terminate(conn_handle, BLE_ERR_AUTH_FAIL);
|
||||
}
|
||||
|
||||
res.bonded = (bonded == 1);
|
||||
ble_sm_process_result(conn_handle, &res, true);
|
||||
}
|
||||
@@ -1570,15 +1704,22 @@ ble_sm_ltk_start_exec(struct ble_sm_proc *proc, struct ble_sm_result *res,
|
||||
}
|
||||
}
|
||||
|
||||
struct ble_sm_ltk_restore_ctx {
|
||||
const struct ble_store_value_sec *value_sec;
|
||||
bool bond_present;
|
||||
};
|
||||
|
||||
static void
|
||||
ble_sm_ltk_restore_exec(struct ble_sm_proc *proc, struct ble_sm_result *res,
|
||||
void *arg)
|
||||
{
|
||||
struct ble_store_value_sec *value_sec;
|
||||
const struct ble_sm_ltk_restore_ctx *ctx;
|
||||
const struct ble_store_value_sec *value_sec;
|
||||
|
||||
BLE_HS_DBG_ASSERT(!(proc->flags & BLE_SM_PROC_F_INITIATOR));
|
||||
|
||||
value_sec = arg;
|
||||
ctx = arg;
|
||||
value_sec = ctx != NULL ? ctx->value_sec : NULL;
|
||||
|
||||
if (value_sec != NULL) {
|
||||
/* Store provided a key; send it to the controller. */
|
||||
@@ -1596,25 +1737,32 @@ ble_sm_ltk_restore_exec(struct ble_sm_proc *proc, struct ble_sm_result *res,
|
||||
} else {
|
||||
/* Notify the app if it provided a key and the procedure failed. */
|
||||
res->enc_cb = 1;
|
||||
res->restore = 1;
|
||||
res->terminate = 1;
|
||||
res->keep_bond = 1;
|
||||
}
|
||||
} else {
|
||||
/* Application does not have the requested key in its database. Send a
|
||||
* negative reply to the controller.
|
||||
/*
|
||||
* No LTK matched ediv/rand. If we still hold a bond for this peer,
|
||||
* treat it as a failed restore (keep bond, drop link, block
|
||||
* BLE_RESTART_PAIR). If we hold no bond, this is one-sided deletion
|
||||
* on our side — allow BLE_RESTART_PAIR so the peer can re-pair.
|
||||
*/
|
||||
int rc;
|
||||
|
||||
rc = ble_sm_ltk_req_neg_reply_tx(proc->conn_handle);
|
||||
if (rc != 0) {
|
||||
res->app_status = rc;
|
||||
res->enc_cb = 1; /* Notify application of failure, similar to reply branch */
|
||||
res->enc_cb = 1;
|
||||
} else {
|
||||
/* Neg-reply sent: do not treat as restore success. app_status=0
|
||||
* would enter ENC_RESTORE and wait for an enc-change that never
|
||||
* arrives. PINKEY_MISSING also drives BLE_RESTART_PAIR.
|
||||
*/
|
||||
res->app_status = BLE_HS_HCI_ERR(BLE_ERR_PINKEY_MISSING);
|
||||
res->enc_cb = 1;
|
||||
}
|
||||
|
||||
if (ctx != NULL && ctx->bond_present) {
|
||||
res->restore = 1;
|
||||
res->terminate = 1;
|
||||
res->keep_bond = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (res->app_status == 0) {
|
||||
@@ -1627,7 +1775,10 @@ ble_sm_ltk_restore_exec(struct ble_sm_proc *proc, struct ble_sm_result *res,
|
||||
int
|
||||
ble_sm_ltk_req_rx(const struct ble_hci_ev_le_subev_lt_key_req *ev)
|
||||
{
|
||||
struct ble_sm_ltk_restore_ctx restore_ctx;
|
||||
struct ble_store_value_sec value_sec;
|
||||
struct ble_store_value_sec bond;
|
||||
struct ble_store_key_sec key_sec;
|
||||
struct ble_hs_conn_addrs addrs;
|
||||
struct ble_sm_result res;
|
||||
struct ble_sm_proc *proc;
|
||||
@@ -1639,6 +1790,7 @@ ble_sm_ltk_req_rx(const struct ble_hci_ev_le_subev_lt_key_req *ev)
|
||||
uint16_t conn_handle = le16toh(ev->conn_handle);
|
||||
|
||||
memset(&res, 0, sizeof res);
|
||||
memset(&restore_ctx, 0, sizeof restore_ctx);
|
||||
|
||||
ble_hs_lock();
|
||||
proc = ble_sm_proc_find(conn_handle, BLE_SM_PROC_STATE_NONE, 0, NULL);
|
||||
@@ -1707,11 +1859,21 @@ ble_sm_ltk_req_rx(const struct ble_hci_ev_le_subev_lt_key_req *ev)
|
||||
peer_id_addr, &value_sec);
|
||||
if (store_rc == 0) {
|
||||
/* Send the key to the controller. */
|
||||
res.state_arg = &value_sec;
|
||||
restore_ctx.value_sec = &value_sec;
|
||||
} else {
|
||||
/* Send a nack to the controller. */
|
||||
res.state_arg = NULL;
|
||||
/*
|
||||
* A nonmatching ediv/rand does not imply that no bond exists.
|
||||
* Read the peer record here, outside the host lock, so the
|
||||
* LTK restore state can distinguish a stale request from
|
||||
* one-sided bond deletion.
|
||||
*/
|
||||
memset(&key_sec, 0, sizeof key_sec);
|
||||
key_sec.peer_addr = addrs.peer_id_addr;
|
||||
store_rc = ble_store_read_peer_sec(&key_sec, &bond);
|
||||
restore_ctx.bond_present =
|
||||
store_rc == 0 && bond.ltk_present;
|
||||
}
|
||||
res.state_arg = &restore_ctx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2100,6 +2262,9 @@ ble_sm_pair_req_rx(uint16_t conn_handle, struct os_mbuf **om,
|
||||
struct ble_hs_conn *conn;
|
||||
ble_sm_proc_flags proc_flags;
|
||||
uint8_t key_size;
|
||||
uint8_t sm_err;
|
||||
bool terminate;
|
||||
bool keep_bond;
|
||||
int rc;
|
||||
|
||||
/* Silence spurious unused-variable warnings. */
|
||||
@@ -2239,11 +2404,18 @@ ble_sm_pair_req_rx(uint16_t conn_handle, struct os_mbuf **om,
|
||||
* application an opportunity to delete the old bond.
|
||||
*/
|
||||
if (res->app_status == 0) {
|
||||
rc = ble_sm_chk_repeat_pairing(conn_handle, proc_flags, key_size);
|
||||
rc = ble_sm_chk_repeat_pairing(conn_handle, proc_flags, key_size,
|
||||
&sm_err, &terminate, &keep_bond);
|
||||
if (rc != 0) {
|
||||
/* The app indicated that the pairing request should be ignored. */
|
||||
res->app_status = rc;
|
||||
res->execute = 0;
|
||||
res->sm_err = sm_err;
|
||||
res->terminate = terminate;
|
||||
res->keep_bond = keep_bond;
|
||||
if (terminate) {
|
||||
res->enc_cb = 1;
|
||||
}
|
||||
if (rc == BLE_HS_EBUSY) {
|
||||
/*
|
||||
* CONNECT not yet delivered; send Pairing Failed so the peer
|
||||
@@ -2260,8 +2432,10 @@ ble_sm_pair_rsp_rx(uint16_t conn_handle, struct os_mbuf **om,
|
||||
struct ble_sm_result *res)
|
||||
{
|
||||
struct ble_sm_pair_cmd *rsp;
|
||||
struct ble_store_value_sec bond;
|
||||
struct ble_sm_proc *proc;
|
||||
uint8_t ioact;
|
||||
int bond_rc;
|
||||
int rc;
|
||||
|
||||
res->app_status = ble_hs_mbuf_pullup_base(om, sizeof(*rsp));
|
||||
@@ -2271,6 +2445,7 @@ ble_sm_pair_rsp_rx(uint16_t conn_handle, struct os_mbuf **om,
|
||||
}
|
||||
|
||||
rsp = (struct ble_sm_pair_cmd *)(*om)->om_data;
|
||||
bond_rc = ble_sm_read_bond(conn_handle, &bond);
|
||||
|
||||
ble_hs_lock();
|
||||
proc = ble_sm_proc_find(conn_handle, BLE_SM_PROC_STATE_PAIR, 1, NULL);
|
||||
@@ -2303,6 +2478,19 @@ ble_sm_pair_rsp_rx(uint16_t conn_handle, struct os_mbuf **om,
|
||||
res->sm_err = BLE_SM_ERR_AUTHREQ;
|
||||
res->app_status = BLE_HS_SM_US_ERR(BLE_SM_ERR_AUTHREQ);
|
||||
res->enc_cb = 1;
|
||||
} else if (MYNEWT_VAL(BLE_SMP_HARDENED_REPAIRING) &&
|
||||
bond_rc == 0 && bond.ltk_present &&
|
||||
proc->sec_req_valid &&
|
||||
((rsp->authreq &
|
||||
proc->sec_req_authreq &
|
||||
(BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC)) !=
|
||||
(proc->sec_req_authreq &
|
||||
(BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC)))) {
|
||||
res->sm_err = BLE_SM_ERR_AUTHREQ;
|
||||
res->app_status = BLE_HS_SM_US_ERR(BLE_SM_ERR_AUTHREQ);
|
||||
res->enc_cb = 1;
|
||||
res->terminate = 1;
|
||||
res->keep_bond = 1;
|
||||
} else {
|
||||
struct ble_sm_pair_cmd *req = (struct ble_sm_pair_cmd *)(proc->pair_req + 1);
|
||||
if ((rsp->init_key_dist & ~req->init_key_dist) != 0 ||
|
||||
@@ -2313,20 +2501,42 @@ ble_sm_pair_rsp_rx(uint16_t conn_handle, struct os_mbuf **om,
|
||||
res->enc_cb = 1;
|
||||
} else {
|
||||
rc = ble_sm_pair_cfg(proc);
|
||||
if (rc == 0) {
|
||||
rc = ble_sm_io_action(proc, &ioact);
|
||||
}
|
||||
if (rc != 0) {
|
||||
if (rc == 0 && bond_rc == 0 &&
|
||||
MYNEWT_VAL(BLE_SMP_HARDENED_REPAIRING) &&
|
||||
((bond.sc && !(proc->flags & BLE_SM_PROC_F_SC)) ||
|
||||
(bond.authenticated &&
|
||||
!(proc->flags & BLE_SM_PROC_F_AUTHENTICATED)) ||
|
||||
(proc->key_size < bond.key_size))) {
|
||||
res->sm_err = proc->key_size < bond.key_size ? BLE_SM_ERR_ENC_KEY_SZ
|
||||
: BLE_SM_ERR_AUTHREQ;
|
||||
res->app_status = BLE_HS_SM_US_ERR(res->sm_err);
|
||||
res->enc_cb = 1;
|
||||
res->terminate = 1;
|
||||
res->keep_bond = 1;
|
||||
} else if (rc == 0 && bond_rc != 0 &&
|
||||
bond_rc != BLE_HS_ENOENT &&
|
||||
bond_rc != BLE_HS_ENOTSUP) {
|
||||
res->sm_err = BLE_SM_ERR_UNSPECIFIED;
|
||||
res->app_status = bond_rc;
|
||||
res->enc_cb = 1;
|
||||
} else if (rc != 0) {
|
||||
res->sm_err = BLE_SM_ERR_AUTHREQ;
|
||||
res->app_status = BLE_HS_SM_US_ERR(BLE_SM_ERR_AUTHREQ);
|
||||
res->enc_cb = 1;
|
||||
} else {
|
||||
proc->state = ble_sm_state_after_pair(proc);
|
||||
if (ble_sm_ioact_state(ioact) == proc->state) {
|
||||
res->passkey_params.action = ioact;
|
||||
}
|
||||
if (ble_sm_proc_can_advance(proc)) {
|
||||
res->execute = 1;
|
||||
rc = ble_sm_io_action(proc, &ioact);
|
||||
if (rc != 0) {
|
||||
res->sm_err = BLE_SM_ERR_AUTHREQ;
|
||||
res->app_status = BLE_HS_SM_US_ERR(BLE_SM_ERR_AUTHREQ);
|
||||
res->enc_cb = 1;
|
||||
} else {
|
||||
proc->state = ble_sm_state_after_pair(proc);
|
||||
if (ble_sm_ioact_state(ioact) == proc->state) {
|
||||
res->passkey_params.action = ioact;
|
||||
}
|
||||
if (ble_sm_proc_can_advance(proc)) {
|
||||
res->execute = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2437,7 +2647,7 @@ ble_sm_sec_req_rx(uint16_t conn_handle, struct os_mbuf **om,
|
||||
|
||||
/* always check if we have keys for this peer */
|
||||
res->app_status = ble_store_read_peer_sec(&key_sec, &value_sec);
|
||||
if (res->app_status == 0) {
|
||||
if (res->app_status == 0 && value_sec.ltk_present) {
|
||||
/* if keys are present and link is already encrypted check if
|
||||
* pairing should be started for security level elevation.
|
||||
* Otherwise we first require link encryption.
|
||||
@@ -2458,9 +2668,13 @@ ble_sm_sec_req_rx(uint16_t conn_handle, struct os_mbuf **om,
|
||||
start_pairing = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (res->app_status == BLE_HS_ENOENT ||
|
||||
res->app_status == BLE_HS_ENOTSUP ||
|
||||
(res->app_status == 0 && !value_sec.ltk_present)) {
|
||||
/* no keys present, start pairing */
|
||||
start_pairing = true;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
/** Make sure a procedure isn't already in progress for this connection. */
|
||||
@@ -2475,6 +2689,16 @@ ble_sm_sec_req_rx(uint16_t conn_handle, struct os_mbuf **om,
|
||||
|
||||
if (start_pairing) {
|
||||
res->app_status = ble_sm_pair_initiate(conn_handle);
|
||||
if (res->app_status == 0) {
|
||||
ble_hs_lock();
|
||||
proc = ble_sm_proc_find(conn_handle, BLE_SM_PROC_STATE_PAIR, 1,
|
||||
NULL);
|
||||
if (proc != NULL) {
|
||||
proc->sec_req_authreq = cmd->authreq;
|
||||
proc->sec_req_valid = 1;
|
||||
}
|
||||
ble_hs_unlock();
|
||||
}
|
||||
} else {
|
||||
res->app_status = ble_sm_enc_initiate(conn_handle,
|
||||
value_sec.key_size,
|
||||
|
||||
@@ -255,6 +255,8 @@ struct ble_sm_proc {
|
||||
uint8_t state;
|
||||
uint8_t rx_key_flags;
|
||||
uint8_t key_size;
|
||||
uint8_t sec_req_authreq;
|
||||
uint8_t sec_req_valid;
|
||||
|
||||
uint8_t pair_req[sizeof(struct ble_sm_hdr) + sizeof(struct ble_sm_pair_cmd)];
|
||||
uint8_t pair_rsp[sizeof(struct ble_sm_hdr) + sizeof(struct ble_sm_pair_cmd)];
|
||||
@@ -290,6 +292,9 @@ struct ble_sm_result {
|
||||
unsigned bonded : 1;
|
||||
unsigned restore : 1;
|
||||
unsigned out_of_order : 1;
|
||||
unsigned terminate : 1;
|
||||
/* Hardened re-pair refusal: keep the stored bond despite remove-on-fail. */
|
||||
unsigned keep_bond : 1;
|
||||
};
|
||||
|
||||
#if MYNEWT_VAL(BLE_HS_DEBUG)
|
||||
|
||||
@@ -2281,11 +2281,12 @@ ble_sm_test_util_peer_lgcy_good_once_no_init(
|
||||
struct ble_sm_test_params *params,
|
||||
struct ble_hs_conn *conn,
|
||||
struct ble_sm_test_util_entity *our_entity,
|
||||
struct ble_sm_test_util_entity *peer_entity)
|
||||
struct ble_sm_test_util_entity *peer_entity,
|
||||
int expected_encrypted)
|
||||
{
|
||||
int rc;
|
||||
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 0);
|
||||
|
||||
if (params->sec_req.authreq != 0) {
|
||||
@@ -2298,13 +2299,13 @@ ble_sm_test_util_peer_lgcy_good_once_no_init(
|
||||
|
||||
/* Receive a pair request from the peer. */
|
||||
ble_sm_test_util_rx_pair_req(2, peer_entity->pair_cmd, 0);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
/* Ensure we sent the expected pair response. */
|
||||
ble_sm_test_util_verify_tx_pair_rsp(our_entity->pair_cmd);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
@@ -2313,7 +2314,7 @@ ble_sm_test_util_peer_lgcy_good_once_no_init(
|
||||
|
||||
/* Receive a pair confirm from the peer. */
|
||||
ble_sm_test_util_rx_confirm(2, peer_entity->confirms);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
@@ -2322,32 +2323,32 @@ ble_sm_test_util_peer_lgcy_good_once_no_init(
|
||||
|
||||
/* Ensure we sent the expected pair confirm. */
|
||||
ble_sm_test_util_verify_tx_pair_confirm(our_entity->confirms);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
/* Receive a pair random from the peer. */
|
||||
ble_sm_test_util_rx_random(2, peer_entity->randoms, 0);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
/* Ensure we sent the expected pair random. */
|
||||
ble_sm_test_util_verify_tx_pair_random(our_entity->randoms);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
/* Receive a long term key request from the controller. */
|
||||
ble_sm_test_util_set_lt_key_req_reply_ack(0, 2);
|
||||
ble_sm_test_util_rx_lt_key_req(2, 0, 0);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
/* Ensure we sent the expected long term key request reply command. */
|
||||
ble_sm_test_util_verify_tx_lt_key_req_reply(2, params->stk);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
@@ -2391,7 +2392,7 @@ ble_sm_test_util_peer_lgcy_good_once(struct ble_sm_test_params *params)
|
||||
|
||||
ble_sm_test_util_init_good(params, 0, &conn, &our_entity, &peer_entity);
|
||||
ble_sm_test_util_peer_lgcy_good_once_no_init(
|
||||
params, conn, &our_entity, &peer_entity);
|
||||
params, conn, &our_entity, &peer_entity, 0);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2705,13 +2706,14 @@ static void
|
||||
ble_sm_test_util_peer_sc_good_once_no_init(struct ble_sm_test_params *params,
|
||||
struct ble_hs_conn *conn,
|
||||
struct ble_sm_test_util_entity *our_entity,
|
||||
struct ble_sm_test_util_entity *peer_entity)
|
||||
struct ble_sm_test_util_entity *peer_entity,
|
||||
int expected_encrypted)
|
||||
{
|
||||
int num_iters;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 0);
|
||||
|
||||
if (params->sec_req.authreq != 0) {
|
||||
@@ -2724,25 +2726,25 @@ ble_sm_test_util_peer_sc_good_once_no_init(struct ble_sm_test_params *params,
|
||||
|
||||
/* Receive a pair request from the peer. */
|
||||
ble_sm_test_util_rx_pair_req(2, peer_entity->pair_cmd, 0);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
/* Ensure we sent the expected pair response. */
|
||||
ble_sm_test_util_verify_tx_pair_rsp(our_entity->pair_cmd);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
/* Receive a public key from the peer. */
|
||||
ble_sm_test_util_rx_public_key(2, peer_entity->public_key);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
/* Ensure we sent the expected public key. */
|
||||
ble_sm_test_util_verify_tx_public_key(our_entity->public_key);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
@@ -2765,7 +2767,7 @@ ble_sm_test_util_peer_sc_good_once_no_init(struct ble_sm_test_params *params,
|
||||
|
||||
/* Receive a pair confirm from the peer. */
|
||||
ble_sm_test_util_rx_confirm(2, peer_entity->confirms + i);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(
|
||||
2, params->passkey_info.passkey.action);
|
||||
@@ -2783,19 +2785,19 @@ ble_sm_test_util_peer_sc_good_once_no_init(struct ble_sm_test_params *params,
|
||||
|
||||
/* Ensure we sent the expected pair confirm. */
|
||||
ble_sm_test_util_verify_tx_pair_confirm(our_entity->confirms + i);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
/* Receive a pair random from the peer. */
|
||||
ble_sm_test_util_rx_random(2, peer_entity->randoms + i, 0);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
/* Ensure we sent the expected pair random. */
|
||||
ble_sm_test_util_verify_tx_pair_random(our_entity->randoms + i);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
@@ -2806,7 +2808,7 @@ ble_sm_test_util_peer_sc_good_once_no_init(struct ble_sm_test_params *params,
|
||||
|
||||
/* Receive a dhkey check from the peer. */
|
||||
ble_sm_test_util_rx_dhkey_check(2, peer_entity->dhkey_check, 0);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
@@ -2815,20 +2817,20 @@ ble_sm_test_util_peer_sc_good_once_no_init(struct ble_sm_test_params *params,
|
||||
|
||||
/* Ensure we sent the expected dhkey check. */
|
||||
ble_sm_test_util_verify_tx_dhkey_check(our_entity->dhkey_check);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
/* Receive a long term key request from the controller. */
|
||||
ble_sm_test_util_set_lt_key_req_reply_ack(0, 2);
|
||||
ble_sm_test_util_rx_lt_key_req(2, 0, 0);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
/* Ensure we sent the expected long term key request reply command. */
|
||||
ble_sm_test_util_verify_tx_lt_key_req_reply(2, params->ltk);
|
||||
TEST_ASSERT(!conn->bhc_sec_state.encrypted);
|
||||
TEST_ASSERT(conn->bhc_sec_state.encrypted == expected_encrypted);
|
||||
TEST_ASSERT(ble_sm_num_procs() == 1);
|
||||
ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
|
||||
|
||||
@@ -2872,7 +2874,7 @@ ble_sm_test_util_peer_sc_good_once(struct ble_sm_test_params *params)
|
||||
|
||||
ble_sm_test_util_init_good(params, 0, &conn, &our_entity, &peer_entity);
|
||||
ble_sm_test_util_peer_sc_good_once_no_init(
|
||||
params, conn, &our_entity, &peer_entity);
|
||||
params, conn, &our_entity, &peer_entity, 0);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -3010,6 +3012,19 @@ ble_sm_test_util_repeat_pairing(struct ble_sm_test_params *params, int sc)
|
||||
|
||||
ble_hs_test_util_prev_tx_queue_clear();
|
||||
|
||||
/* A bonded peer must not directly re-pair before old-bond encryption. */
|
||||
ble_sm_test_repeat_pairing.rc = BLE_GAP_REPEAT_PAIRING_IGNORE;
|
||||
ble_sm_test_repeat_pairing.num_calls = 0;
|
||||
ble_sm_test_util_rx_pair_req(2, peer_entity.pair_cmd, BLE_HS_EALREADY);
|
||||
TEST_ASSERT(ble_sm_test_repeat_pairing.num_calls == 0);
|
||||
TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL);
|
||||
|
||||
ble_hs_lock();
|
||||
conn = ble_hs_conn_find(2);
|
||||
TEST_ASSERT_FATAL(conn != NULL);
|
||||
conn->bhc_sec_state.encrypted = 1;
|
||||
ble_hs_unlock();
|
||||
|
||||
/* First repeat pairing event: retry;
|
||||
* Second repeat pairing event: ignore.
|
||||
*/
|
||||
@@ -3046,10 +3061,10 @@ ble_sm_test_util_repeat_pairing(struct ble_sm_test_params *params, int sc)
|
||||
*/
|
||||
if (!sc) {
|
||||
ble_sm_test_util_peer_lgcy_good_once_no_init(
|
||||
params, conn, &our_entity, &peer_entity);
|
||||
params, conn, &our_entity, &peer_entity, 1);
|
||||
} else {
|
||||
ble_sm_test_util_peer_sc_good_once_no_init(
|
||||
params, conn, &our_entity, &peer_entity);
|
||||
params, conn, &our_entity, &peer_entity, 1);
|
||||
}
|
||||
|
||||
/* Verify repeat pairing event got reported once. */
|
||||
|
||||
Reference in New Issue
Block a user