From 06feb0202ce8879334397d8f538d72c5a12b88a9 Mon Sep 17 00:00:00 2001 From: Thomas Ebert Hansen Date: Tue, 26 Apr 2022 09:28:07 +0200 Subject: [PATCH] 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 --- .../bluetooth/controller/ll_sw/ull_llcp_enc.c | 18 ++- .../controller/ctrl_encrypt/src/main.c | 152 ++++++++++++++++++ 2 files changed, 169 insertions(+), 1 deletion(-) diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c index 8e95523c8b7..93ce902b3f4 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c @@ -1168,7 +1168,23 @@ void llcp_rp_enc_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_p break; default: /* 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; } } diff --git a/tests/bluetooth/controller/ctrl_encrypt/src/main.c b/tests/bluetooth/controller/ctrl_encrypt/src/main.c index 2073e2e5fb9..25882413590 100644 --- a/tests/bluetooth/controller/ctrl_encrypt/src/main.c +++ b/tests/bluetooth/controller/ctrl_encrypt/src/main.c @@ -1384,6 +1384,156 @@ void test_encryption_start_periph_rem_no_ltk(void) "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) { uint8_t err; @@ -1718,6 +1868,8 @@ void test_main(void) ztest_unit_test_setup_teardown(test_encryption_start_periph_rem_limited_memory, setup, unit_test_noop), 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)); ztest_test_suite(encryption_pause,