Bluetooth: controller: Fix periph enc termination
Terminate connection with a MIC failure if an unexpected control PDU is received during the Encryption Start procedure. Add unit test inspired by Bluetooth Qualification test LL/SEC/PER/BI-05-C, Peripheral Receiving unexpected PDU during encryption start Signed-off-by: Thomas Ebert Hansen <thoh@oticon.com>
This commit is contained in:
parent
934279a48f
commit
06feb0202c
2 changed files with 169 additions and 1 deletions
|
@ -1168,7 +1168,23 @@ void llcp_rp_enc_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_p
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* Unknown opcode */
|
/* Unknown opcode */
|
||||||
LL_ASSERT(0);
|
|
||||||
|
/*
|
||||||
|
* BLUETOOTH CORE SPECIFICATION Version 5.3
|
||||||
|
* Vol 6, Part B, 5.1.3.1 Encryption Start procedure
|
||||||
|
*
|
||||||
|
* [...]
|
||||||
|
*
|
||||||
|
* If, at any time during the encryption start procedure after the Peripheral has
|
||||||
|
* received the LL_ENC_REQ PDU or the Central has received the
|
||||||
|
* LL_ENC_RSP PDU, the Link Layer of the Central or the Peripheral receives an
|
||||||
|
* unexpected Data Physical Channel PDU from the peer Link Layer, it shall
|
||||||
|
* immediately exit the Connection state, and shall transition to the Standby state.
|
||||||
|
* The Host shall be notified that the link has been disconnected with the error
|
||||||
|
* code Connection Terminated Due to MIC Failure (0x3D).
|
||||||
|
*/
|
||||||
|
|
||||||
|
conn->llcp_terminate.reason_final = BT_HCI_ERR_TERM_DUE_TO_MIC_FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1384,6 +1384,156 @@ void test_encryption_start_periph_rem_no_ltk(void)
|
||||||
"Free CTX buffers %d", ctx_buffers_free());
|
"Free CTX buffers %d", ctx_buffers_free());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* +-----+ +-------+ +-----+
|
||||||
|
* | UT | | LL_A | | LT |
|
||||||
|
* +-----+ +-------+ +-----+
|
||||||
|
* | | |
|
||||||
|
* | | LL_ENC_REQ |
|
||||||
|
* | |<--------------------|
|
||||||
|
* | -----------------\ | |
|
||||||
|
* | | Empty Tx queue |-| |
|
||||||
|
* | |----------------| | |
|
||||||
|
* | | |
|
||||||
|
* | | LL_ENC_RSP |
|
||||||
|
* | |-------------------->|
|
||||||
|
* | | |
|
||||||
|
* | | LL_VERSION_IND |
|
||||||
|
* | |<--------------------|
|
||||||
|
* | | |
|
||||||
|
*/
|
||||||
|
void test_encryption_start_periph_rem_mic(void)
|
||||||
|
{
|
||||||
|
struct node_tx *tx;
|
||||||
|
struct node_rx_pdu *ntf;
|
||||||
|
|
||||||
|
/* Prepare LL_ENC_REQ */
|
||||||
|
struct pdu_data_llctrl_enc_req enc_req = {
|
||||||
|
.rand = { RAND },
|
||||||
|
.ediv = { EDIV },
|
||||||
|
.skdm = { SKDM },
|
||||||
|
.ivm = { IVM },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pdu_data_llctrl_enc_rsp exp_enc_rsp = {
|
||||||
|
.skds = { SKDS },
|
||||||
|
.ivs = { IVS },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pdu_data_llctrl_version_ind remote_version_ind = {
|
||||||
|
.version_number = 0x55,
|
||||||
|
.company_id = 0xABCD,
|
||||||
|
.sub_version_number = 0x1234,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Prepare mocked call to lll_csrand_get */
|
||||||
|
ztest_returns_value(lll_csrand_get, sizeof(exp_enc_rsp.skds) + sizeof(exp_enc_rsp.ivs));
|
||||||
|
ztest_return_data(lll_csrand_get, buf, exp_enc_rsp.skds);
|
||||||
|
ztest_expect_value(lll_csrand_get, len, sizeof(exp_enc_rsp.skds) + sizeof(exp_enc_rsp.ivs));
|
||||||
|
|
||||||
|
/* Role */
|
||||||
|
test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL);
|
||||||
|
|
||||||
|
/* Connect */
|
||||||
|
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
|
||||||
|
|
||||||
|
/* Check state */
|
||||||
|
CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */
|
||||||
|
CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */
|
||||||
|
|
||||||
|
/* Prepare */
|
||||||
|
event_prepare(&conn);
|
||||||
|
|
||||||
|
/* Rx */
|
||||||
|
lt_tx(LL_ENC_REQ, &conn, &enc_req);
|
||||||
|
|
||||||
|
/* Check state */
|
||||||
|
CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */
|
||||||
|
CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */
|
||||||
|
|
||||||
|
/* Done */
|
||||||
|
event_done(&conn);
|
||||||
|
|
||||||
|
/* Check state */
|
||||||
|
CHECK_RX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Rx paused & unenc. */
|
||||||
|
CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */
|
||||||
|
|
||||||
|
/* Prepare */
|
||||||
|
event_prepare(&conn);
|
||||||
|
|
||||||
|
/* Tx Queue should have one LL Control PDU */
|
||||||
|
lt_rx(LL_ENC_RSP, &conn, &tx, &exp_enc_rsp);
|
||||||
|
lt_rx_q_is_empty(&conn);
|
||||||
|
|
||||||
|
/* Check state */
|
||||||
|
CHECK_RX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Rx paused & unenc. */
|
||||||
|
CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */
|
||||||
|
|
||||||
|
/* Done */
|
||||||
|
event_done(&conn);
|
||||||
|
|
||||||
|
/* Check state */
|
||||||
|
CHECK_RX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Rx paused & unenc. */
|
||||||
|
CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */
|
||||||
|
|
||||||
|
/* Release Tx */
|
||||||
|
ull_cp_release_tx(&conn, tx);
|
||||||
|
|
||||||
|
/* There should be a host notification */
|
||||||
|
ut_rx_pdu(LL_ENC_REQ, &ntf, &enc_req);
|
||||||
|
ut_rx_q_is_empty();
|
||||||
|
|
||||||
|
/* Release Ntf */
|
||||||
|
ull_cp_release_ntf(ntf);
|
||||||
|
|
||||||
|
/* Prepare */
|
||||||
|
event_prepare(&conn);
|
||||||
|
|
||||||
|
/* Rx */
|
||||||
|
lt_tx(LL_VERSION_IND, &conn, &remote_version_ind);
|
||||||
|
|
||||||
|
/* Done */
|
||||||
|
event_done(&conn);
|
||||||
|
|
||||||
|
/* Check state */
|
||||||
|
CHECK_RX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Rx paused & unenc. */
|
||||||
|
CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */
|
||||||
|
|
||||||
|
/* There should not be a host notification */
|
||||||
|
ut_rx_q_is_empty();
|
||||||
|
|
||||||
|
/**/
|
||||||
|
zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_TERM_DUE_TO_MIC_FAIL,
|
||||||
|
"Expected termination due to MIC failure");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For a 40s procedure response timeout with a connection interval of
|
||||||
|
* 7.5ms, a total of 5333.33 connection events are needed, verify that
|
||||||
|
* the state doesn't change for that many invocations.
|
||||||
|
*/
|
||||||
|
for (int n = 5334; n > 0; n--) {
|
||||||
|
/* Prepare */
|
||||||
|
event_prepare(&conn);
|
||||||
|
|
||||||
|
/* Tx Queue should NOT have a LL Control PDU */
|
||||||
|
lt_rx_q_is_empty(&conn);
|
||||||
|
|
||||||
|
/* Done */
|
||||||
|
event_done(&conn);
|
||||||
|
|
||||||
|
/* Check state */
|
||||||
|
CHECK_RX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Rx paused & unenc. */
|
||||||
|
CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */
|
||||||
|
|
||||||
|
/* There should NOT be a host notification */
|
||||||
|
ut_rx_q_is_empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note that for this test the context is not released */
|
||||||
|
zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt() - 1,
|
||||||
|
"Free CTX buffers %d", ctx_buffers_free());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void test_encryption_pause_central_loc(void)
|
void test_encryption_pause_central_loc(void)
|
||||||
{
|
{
|
||||||
uint8_t err;
|
uint8_t err;
|
||||||
|
@ -1718,6 +1868,8 @@ void test_main(void)
|
||||||
ztest_unit_test_setup_teardown(test_encryption_start_periph_rem_limited_memory,
|
ztest_unit_test_setup_teardown(test_encryption_start_periph_rem_limited_memory,
|
||||||
setup, unit_test_noop),
|
setup, unit_test_noop),
|
||||||
ztest_unit_test_setup_teardown(test_encryption_start_periph_rem_no_ltk, setup,
|
ztest_unit_test_setup_teardown(test_encryption_start_periph_rem_no_ltk, setup,
|
||||||
|
unit_test_noop),
|
||||||
|
ztest_unit_test_setup_teardown(test_encryption_start_periph_rem_mic, setup,
|
||||||
unit_test_noop));
|
unit_test_noop));
|
||||||
|
|
||||||
ztest_test_suite(encryption_pause,
|
ztest_test_suite(encryption_pause,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue