Commit 06feb020 authored by Thomas Ebert Hansen's avatar Thomas Ebert Hansen Committed by Carles Cufi
Browse files

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: default avatarThomas Ebert Hansen <thoh@oticon.com>
parent 934279a4
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -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;
	}
}

+152 −0
Original line number Diff line number Diff line
@@ -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,