Skip to content

Commit 20cb60e

Browse files
thoh-otcarlescufi
authored andcommitted
Bluetooth: controller: Fix central enc termination
Terminate connection with a MIC failure if an unexpected control PDU is received during the Encryption Start procedure. Add a greedy option to pdu_is_expected() to make sure the procedure processes all unexpected control PDU in all cases. Add unit test inspired by Bluetooth Qualification test LL/SEC/CEN/BV-14-C, Central Receiving unexpected PDU during encryption start Signed-off-by: Thomas Ebert Hansen <[email protected]>
1 parent 70e38b1 commit 20cb60e

File tree

4 files changed

+170
-2
lines changed

4 files changed

+170
-2
lines changed

subsys/bluetooth/controller/ll_sw/ull_llcp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,7 @@ void ull_cp_cte_req_set_disable(struct ll_conn *conn)
10001000

10011001
static bool pdu_is_expected(struct pdu_data *pdu, struct proc_ctx *ctx)
10021002
{
1003-
return ctx->rx_opcode == pdu->llctrl.opcode;
1003+
return (ctx->rx_opcode == pdu->llctrl.opcode || ctx->rx_greedy);
10041004
}
10051005

10061006
static bool pdu_is_unknown(struct pdu_data *pdu, struct proc_ctx *ctx)

subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,15 @@ static void lp_enc_st_wait_rx_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx
404404
/* Pause Rx data */
405405
ull_conn_pause_rx_data(conn);
406406
lp_enc_store_s(conn, ctx, pdu);
407+
408+
/* After the Central has received the LL_ENC_RSP PDU,
409+
* only PDUs related to this procedure are valid, and invalids should
410+
* result in disconnect.
411+
* to achieve this enable the greedy RX behaviour, such that
412+
* all PDU's end up in this FSM.
413+
*/
414+
ctx->rx_greedy = 1U;
415+
407416
/* Wait for LL_START_ENC_REQ */
408417
ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_START_ENC_REQ;
409418
ctx->state = LP_ENC_STATE_WAIT_RX_START_ENC_REQ;
@@ -450,6 +459,9 @@ static void lp_enc_st_wait_rx_start_enc_req(struct ll_conn *conn, struct proc_ct
450459
/* Resume possibly paused remote procedure */
451460
llcp_rr_resume(conn);
452461

462+
/* Disable the greedy behaviour */
463+
ctx->rx_greedy = 0U;
464+
453465
lp_enc_complete(conn, ctx, evt, param);
454466
break;
455467
default:
@@ -485,6 +497,9 @@ static void lp_enc_st_wait_rx_start_enc_rsp(struct ll_conn *conn, struct proc_ct
485497
/* Resume possibly paused remote procedure */
486498
llcp_rr_resume(conn);
487499

500+
/* Disable the greedy behaviour */
501+
ctx->rx_greedy = 0U;
502+
488503
lp_enc_complete(conn, ctx, evt, param);
489504
break;
490505
default:
@@ -631,7 +646,23 @@ void llcp_lp_enc_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_p
631646
break;
632647
default:
633648
/* Unknown opcode */
634-
LL_ASSERT(0);
649+
650+
/*
651+
* BLUETOOTH CORE SPECIFICATION Version 5.3
652+
* Vol 6, Part B, 5.1.3.1 Encryption Start procedure
653+
*
654+
* [...]
655+
*
656+
* If, at any time during the encryption start procedure after the Peripheral has
657+
* received the LL_ENC_REQ PDU or the Central has received the
658+
* LL_ENC_RSP PDU, the Link Layer of the Central or the Peripheral receives an
659+
* unexpected Data Physical Channel PDU from the peer Link Layer, it shall
660+
* immediately exit the Connection state, and shall transition to the Standby state.
661+
* The Host shall be notified that the link has been disconnected with the error
662+
* code Connection Terminated Due to MIC Failure (0x3D).
663+
*/
664+
665+
conn->llcp_terminate.reason_final = BT_HCI_ERR_TERM_DUE_TO_MIC_FAIL;
635666
}
636667
}
637668

subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ struct proc_ctx {
134134
/* Expected opcode to be received next */
135135
enum pdu_data_llctrl_type rx_opcode;
136136

137+
/* Greedy RX (used for central encryption) */
138+
uint8_t rx_greedy;
139+
137140
/* Last transmitted opcode used for unknown/reject */
138141
enum pdu_data_llctrl_type tx_opcode;
139142

tests/bluetooth/controller/ctrl_encrypt/src/main.c

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,138 @@ void test_encryption_start_central_loc_no_ltk_2(void)
935935
"Free CTX buffers %d", ctx_buffers_free());
936936
}
937937

938+
/* +-----+ +-------+ +-----+
939+
* | UT | | LL_A | | LT |
940+
* +-----+ +-------+ +-----+
941+
* | | |
942+
* | Initiate | |
943+
* | Encryption Start Proc. | |
944+
* |--------------------------->| |
945+
* | -----------------\ | |
946+
* | | Empty Tx queue |-| |
947+
* | |----------------| | |
948+
* | | |
949+
* | | LL_ENC_REQ |
950+
* | |-------------------->|
951+
* | | |
952+
* | | LL_ENC_RSP |
953+
* | |<--------------------|
954+
* | | |
955+
* | | LL_VERSION_IND |
956+
* | |<--------------------|
957+
* | | |
958+
* | Encryption Start Proc. | |
959+
* | Complete | |
960+
* |<---------------------------| |
961+
* | | |
962+
*/
963+
void test_encryption_start_central_loc_mic(void)
964+
{
965+
uint8_t err;
966+
struct node_tx *tx;
967+
968+
const uint8_t rand[] = { RAND };
969+
const uint8_t ediv[] = { EDIV };
970+
const uint8_t ltk[] = { LTK };
971+
972+
/* Prepare expected LL_ENC_REQ */
973+
struct pdu_data_llctrl_enc_req exp_enc_req = {
974+
.rand = { RAND },
975+
.ediv = { EDIV },
976+
.skdm = { SKDM },
977+
.ivm = { IVM },
978+
};
979+
980+
/* Prepare LL_ENC_RSP */
981+
struct pdu_data_llctrl_enc_rsp enc_rsp = { .skds = { SKDS }, .ivs = { IVS } };
982+
983+
struct pdu_data_llctrl_version_ind remote_version_ind = {
984+
.version_number = 0x55,
985+
.company_id = 0xABCD,
986+
.sub_version_number = 0x1234,
987+
};
988+
989+
/* Prepare mocked call to lll_csrand_get */
990+
ztest_returns_value(lll_csrand_get, sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm));
991+
ztest_return_data(lll_csrand_get, buf, exp_enc_req.skdm);
992+
ztest_expect_value(lll_csrand_get, len, sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm));
993+
994+
/* Role */
995+
test_set_role(&conn, BT_HCI_ROLE_CENTRAL);
996+
997+
/* Connect */
998+
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
999+
1000+
/* Check state */
1001+
CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */
1002+
CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */
1003+
1004+
/* Initiate an Encryption Start Procedure */
1005+
err = ull_cp_encryption_start(&conn, rand, ediv, ltk);
1006+
zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL);
1007+
1008+
/* Prepare */
1009+
event_prepare(&conn);
1010+
1011+
/* Tx Queue should have one LL Control PDU */
1012+
lt_rx(LL_ENC_REQ, &conn, &tx, &exp_enc_req);
1013+
lt_rx_q_is_empty(&conn);
1014+
1015+
/* Check state */
1016+
CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */
1017+
CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */
1018+
1019+
/* Release Tx */
1020+
ull_cp_release_tx(&conn, tx);
1021+
1022+
/* Rx */
1023+
lt_tx(LL_ENC_RSP, &conn, &enc_rsp);
1024+
1025+
/* Rx */
1026+
lt_tx(LL_VERSION_IND, &conn, &remote_version_ind);
1027+
1028+
/* Done */
1029+
event_done(&conn);
1030+
1031+
/* Check state */
1032+
CHECK_RX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Rx paused & unenc. */
1033+
CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */
1034+
1035+
/* There should not be a host notification */
1036+
ut_rx_q_is_empty();
1037+
1038+
/**/
1039+
zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_TERM_DUE_TO_MIC_FAIL,
1040+
"Expected termination due to MIC failure");
1041+
1042+
/*
1043+
* For a 40s procedure response timeout with a connection interval of
1044+
* 7.5ms, a total of 5333.33 connection events are needed, verify that
1045+
* the state doesn't change for that many invocations.
1046+
*/
1047+
for (int n = 5334; n > 0; n--) {
1048+
/* Prepare */
1049+
event_prepare(&conn);
1050+
1051+
/* Tx Queue should NOT have a LL Control PDU */
1052+
lt_rx_q_is_empty(&conn);
1053+
1054+
/* Done */
1055+
event_done(&conn);
1056+
1057+
/* Check state */
1058+
CHECK_RX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Rx paused & unenc. */
1059+
CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */
1060+
1061+
/* There should NOT be a host notification */
1062+
ut_rx_q_is_empty();
1063+
}
1064+
1065+
/* Note that for this test the context is not released */
1066+
zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt() - 1,
1067+
"Free CTX buffers %d", ctx_buffers_free());
1068+
}
1069+
9381070
/* +-----+ +-------+ +-----+
9391071
* | UT | | LL_A | | LT |
9401072
* +-----+ +-------+ +-----+
@@ -2071,6 +2203,8 @@ void test_main(void)
20712203
unit_test_noop),
20722204
ztest_unit_test_setup_teardown(test_encryption_start_central_loc_no_ltk_2, setup,
20732205
unit_test_noop),
2206+
ztest_unit_test_setup_teardown(test_encryption_start_central_loc_mic, setup,
2207+
unit_test_noop),
20742208
ztest_unit_test_setup_teardown(test_encryption_start_periph_rem, setup,
20752209
unit_test_noop),
20762210
ztest_unit_test_setup_teardown(test_encryption_start_periph_rem_limited_memory,

0 commit comments

Comments
 (0)