From 70e38b1cbb1595276b8fcabfbbbcb362c2e3b08d Mon Sep 17 00:00:00 2001 From: Thomas Ebert Hansen Date: Tue, 26 Apr 2022 09:34:27 +0200 Subject: [PATCH] Bluetooth: controller: Fix central enc reject Add support for both LL_REJECT_IND and LL_REJECT_EXT_IND when waiting for the the response to the LL_ENC_REQ and LL_START_ENC_REQ. Add unit test to test both LL_REJECT_IND and LL_REJECT_EXT_IND as responses to LL_ENC_REQ. Signed-off-by: Thomas Ebert Hansen --- .../bluetooth/controller/ll_sw/ull_llcp_enc.c | 39 +++- .../controller/ctrl_encrypt/src/main.c | 203 ++++++++++++++++++ 2 files changed, 239 insertions(+), 3 deletions(-) diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c index 474514177f7..95bebf8b8c2 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c @@ -377,6 +377,23 @@ static void lp_enc_store_s(struct ll_conn *conn, struct proc_ctx *ctx, struct pd memcpy(&conn->lll.ccm_rx.iv[4], pdu->llctrl.enc_rsp.ivs, sizeof(pdu->llctrl.enc_rsp.ivs)); } +static inline uint8_t reject_error_code(struct pdu_data *pdu) +{ + if (pdu->llctrl.opcode == PDU_DATA_LLCTRL_TYPE_REJECT_IND) { + return pdu->llctrl.reject_ind.error_code; +#if defined(CONFIG_BT_CTLR_EXT_REJ_IND) + } else if (pdu->llctrl.opcode == PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND) { + return pdu->llctrl.reject_ext_ind.error_code; +#endif /* CONFIG_BT_CTLR_EXT_REJ_IND */ + } else { + /* Called with an invalid PDU */ + LL_ASSERT(0); + + /* Keep compiler happy */ + return BT_HCI_ERR_UNSPECIFIED; + } +} + static void lp_enc_st_wait_rx_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) { @@ -391,6 +408,21 @@ static void lp_enc_st_wait_rx_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_START_ENC_REQ; ctx->state = LP_ENC_STATE_WAIT_RX_START_ENC_REQ; break; + case LP_ENC_EVT_REJECT: + /* Encryption is not supported by the Link Layer of the Peripheral */ + + /* Resume Tx data */ + llcp_tx_resume_data(conn, LLCP_TX_QUEUE_PAUSE_DATA_ENCRYPTION); + + /* Store the error reason */ + ctx->data.enc.error = reject_error_code(pdu); + + /* Resume possibly paused remote procedure */ + llcp_rr_resume(conn); + + /* Complete the procedure */ + lp_enc_complete(conn, ctx, evt, param); + break; default: /* Ignore other evts */ break; @@ -411,9 +443,10 @@ static void lp_enc_st_wait_rx_start_enc_req(struct ll_conn *conn, struct proc_ct llcp_tx_resume_data(conn, LLCP_TX_QUEUE_PAUSE_DATA_ENCRYPTION); /* Resume Rx data */ ull_conn_resume_rx_data(conn); - ctx->data.enc.error = (pdu->llctrl.opcode == PDU_DATA_LLCTRL_TYPE_REJECT_IND) ? - pdu->llctrl.reject_ind.error_code : - pdu->llctrl.reject_ext_ind.error_code; + + /* Store the error reason */ + ctx->data.enc.error = reject_error_code(pdu); + /* Resume possibly paused remote procedure */ llcp_rr_resume(conn); diff --git a/tests/bluetooth/controller/ctrl_encrypt/src/main.c b/tests/bluetooth/controller/ctrl_encrypt/src/main.c index 0a9acf9212e..0debba9e316 100644 --- a/tests/bluetooth/controller/ctrl_encrypt/src/main.c +++ b/tests/bluetooth/controller/ctrl_encrypt/src/main.c @@ -519,6 +519,205 @@ void test_encryption_start_central_loc_limited_memory(void) "Free CTX buffers %d", ctx_buffers_free()); } +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Initiate | | + * | Encryption Start Proc. | | + * |--------------------------->| | + * | -----------------\ | | + * | | Empty Tx queue |-| | + * | |----------------| | | + * | | | + * | | LL_ENC_REQ | + * | |-------------------->| + * | | | + * | | LL_REJECT_EXT_IND | + * | |<--------------------| + * | | | + * | Encryption Start Proc. | | + * | Complete | | + * |<---------------------------| | + * | | | + */ +void test_encryption_start_central_loc_reject_ext(void) +{ + uint8_t err; + struct node_tx *tx; + struct node_rx_pdu *ntf; + + const uint8_t rand[] = { RAND }; + const uint8_t ediv[] = { EDIV }; + const uint8_t ltk[] = { LTK }; + + /* Prepare expected LL_ENC_REQ */ + struct pdu_data_llctrl_enc_req exp_enc_req = { + .rand = { RAND }, + .ediv = { EDIV }, + .skdm = { SKDM }, + .ivm = { IVM }, + }; + + /* Prepare mocked call to lll_csrand_get */ + ztest_returns_value(lll_csrand_get, sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm)); + ztest_return_data(lll_csrand_get, buf, exp_enc_req.skdm); + ztest_expect_value(lll_csrand_get, len, sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm)); + + struct pdu_data_llctrl_reject_ind reject_ind = { .error_code = + BT_HCI_ERR_UNSUPP_REMOTE_FEATURE }; + + struct pdu_data_llctrl_reject_ext_ind reject_ext_ind = { + .reject_opcode = PDU_DATA_LLCTRL_TYPE_ENC_REQ, + .error_code = BT_HCI_ERR_UNSUPP_REMOTE_FEATURE + }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* 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. */ + + /* Initiate an Encryption Start Procedure */ + err = ull_cp_encryption_start(&conn, rand, ediv, ltk); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_ENC_REQ, &conn, &tx, &exp_enc_req); + lt_rx_q_is_empty(&conn); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Rx */ + lt_tx(LL_REJECT_EXT_IND, &conn, &reject_ext_ind); + + /* Done */ + event_done(&conn); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */ + + /* There should be one host notification */ + ut_rx_pdu(LL_REJECT_IND, &ntf, &reject_ind); + ut_rx_q_is_empty(); + + /* Release Ntf */ + ull_cp_release_ntf(ntf); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Initiate | | + * | Encryption Start Proc. | | + * |--------------------------->| | + * | -----------------\ | | + * | | Empty Tx queue |-| | + * | |----------------| | | + * | | | + * | | LL_ENC_REQ | + * | |-------------------->| + * | | | + * | | LL_REJECT_IND | + * | |<--------------------| + * | | | + * | Encryption Start Proc. | | + * | Complete | | + * |<---------------------------| | + * | | | + */ +void test_encryption_start_central_loc_reject(void) +{ + uint8_t err; + struct node_tx *tx; + struct node_rx_pdu *ntf; + + const uint8_t rand[] = { RAND }; + const uint8_t ediv[] = { EDIV }; + const uint8_t ltk[] = { LTK }; + + /* Prepare expected LL_ENC_REQ */ + struct pdu_data_llctrl_enc_req exp_enc_req = { + .rand = { RAND }, + .ediv = { EDIV }, + .skdm = { SKDM }, + .ivm = { IVM }, + }; + + /* Prepare mocked call to lll_csrand_get */ + ztest_returns_value(lll_csrand_get, sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm)); + ztest_return_data(lll_csrand_get, buf, exp_enc_req.skdm); + ztest_expect_value(lll_csrand_get, len, sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm)); + + struct pdu_data_llctrl_reject_ind reject_ind = { .error_code = + BT_HCI_ERR_UNSUPP_REMOTE_FEATURE }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* 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. */ + + /* Initiate an Encryption Start Procedure */ + err = ull_cp_encryption_start(&conn, rand, ediv, ltk); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_ENC_REQ, &conn, &tx, &exp_enc_req); + lt_rx_q_is_empty(&conn); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Rx */ + lt_tx(LL_REJECT_IND, &conn, &reject_ind); + + /* Done */ + event_done(&conn); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */ + + /* There should be one host notification */ + ut_rx_pdu(LL_REJECT_IND, &ntf, &reject_ind); + ut_rx_q_is_empty(); + + /* Release Ntf */ + ull_cp_release_ntf(ntf); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + /* +-----+ +-------+ +-----+ * | UT | | LL_A | | LT | * +-----+ +-------+ +-----+ @@ -1864,6 +2063,10 @@ void test_main(void) unit_test_noop), ztest_unit_test_setup_teardown(test_encryption_start_central_loc_limited_memory, setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_encryption_start_central_loc_reject_ext, setup, + unit_test_noop), + ztest_unit_test_setup_teardown(test_encryption_start_central_loc_reject, setup, + unit_test_noop), ztest_unit_test_setup_teardown(test_encryption_start_central_loc_no_ltk, setup, unit_test_noop), ztest_unit_test_setup_teardown(test_encryption_start_central_loc_no_ltk_2, setup,