| /* |
| * Copyright (c) 2020 Demant |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/types.h> |
| #include <zephyr/ztest.h> |
| |
| #include <zephyr/fff.h> |
| |
| DEFINE_FFF_GLOBALS; |
| |
| #include <zephyr/bluetooth/hci.h> |
| #include <zephyr/sys/byteorder.h> |
| #include <zephyr/sys/slist.h> |
| #include <zephyr/sys/util.h> |
| #include "hal/ccm.h" |
| |
| #include "util/util.h" |
| #include "util/mem.h" |
| #include "util/memq.h" |
| #include "util/dbuf.h" |
| |
| #include "pdu_df.h" |
| #include "lll/pdu_vendor.h" |
| #include "pdu.h" |
| #include "ll.h" |
| #include "ll_feat.h" |
| #include "ll_settings.h" |
| |
| #include "lll.h" |
| #include "lll/lll_df_types.h" |
| #include "lll_conn.h" |
| #include "lll_conn_iso.h" |
| |
| #include "ull_tx_queue.h" |
| |
| #include "isoal.h" |
| #include "ull_iso_types.h" |
| #include "ull_conn_iso_types.h" |
| |
| #include "ull_internal.h" |
| #include "ull_conn_types.h" |
| #include "ull_llcp.h" |
| #include "ull_conn_internal.h" |
| #include "ull_llcp_internal.h" |
| |
| #include "helper_pdu.h" |
| #include "helper_util.h" |
| |
| /* Tx/Rx pause flag */ |
| #define RESUMED 0U |
| #define PAUSED 1U |
| |
| /* Tx/Rx encryption flag */ |
| #define UNENCRYPTED 0U |
| #define ENCRYPTED 1U |
| |
| /* Check Rx Pause and Encryption state */ |
| #define CHECK_RX_PE_STATE(_conn, _pause, _enc) \ |
| do { \ |
| zassert_equal(_conn.pause_rx_data, _pause, "Rx Data pause state is wrong.");\ |
| zassert_equal(_conn.lll.enc_rx, _enc, "Rx Encryption state is wrong."); \ |
| } while (0) |
| |
| /* Check Tx Pause and Encryption state */ |
| #define CHECK_TX_PE_STATE(_conn, _pause, _enc) \ |
| do { \ |
| zassert_equal(_conn.tx_q.pause_data, _pause, "Tx Data pause state is wrong.");\ |
| zassert_equal(_conn.lll.enc_tx, _enc, "Tx Encryption state is wrong."); \ |
| } while (0) |
| |
| /* CCM direction flag */ |
| #define CCM_DIR_M_TO_S 1U |
| #define CCM_DIR_S_TO_M 0U |
| |
| /* Check Rx CCM state */ |
| #define CHECK_RX_CCM_STATE(_conn, _sk_be, _iv, _cnt, _dir) \ |
| do { \ |
| zassert_mem_equal(_conn.lll.ccm_rx.key, _sk_be, sizeof(_sk_be), \ |
| "CCM Rx SK not equal to expected SK"); \ |
| zassert_mem_equal(_conn.lll.ccm_rx.iv, _iv, sizeof(_iv), \ |
| "CCM Rx IV not equal to (IVm | IVs)"); \ |
| zassert_equal(_conn.lll.ccm_rx.counter, _cnt, "CCM Rx Counter is wrong"); \ |
| zassert_equal(_conn.lll.ccm_rx.direction, _dir, "CCM Rx Direction is wrong");\ |
| } while (0) |
| |
| /* Check Tx CCM state */ |
| #define CHECK_TX_CCM_STATE(_conn, _sk_be, _iv, _cnt, _dir) \ |
| do { \ |
| zassert_mem_equal(_conn.lll.ccm_tx.key, _sk_be, sizeof(_sk_be), \ |
| "CCM Tx SK not equal to expected SK"); \ |
| zassert_mem_equal(_conn.lll.ccm_tx.iv, _iv, sizeof(_iv), \ |
| "CCM Tx IV not equal to (IVm | IVs)"); \ |
| zassert_equal(_conn.lll.ccm_tx.counter, _cnt, "CCM Tx Counter is wrong"); \ |
| zassert_equal(_conn.lll.ccm_tx.direction, _dir, "CCM Tx Direction is wrong");\ |
| } while (0) |
| |
| static struct ll_conn conn; |
| |
| /* void ecb_encrypt(uint8_t const *const key_le, uint8_t const *const clear_text_le, |
| * uint8_t *const cipher_text_le, uint8_t *const cipher_text_be); |
| */ |
| FAKE_VOID_FUNC(ecb_encrypt, uint8_t const *const, uint8_t const *const, |
| uint8_t *const, uint8_t *const); |
| |
| struct { |
| /* In */ |
| uint8_t key_le[16]; |
| uint8_t clear_text_le[16]; |
| |
| /* Out */ |
| uint8_t cipher_text_le[16]; |
| uint8_t cipher_text_be[16]; |
| } ecb_encrypt_custom_fake_context; |
| |
| void ecb_encrypt_custom_fake(uint8_t const *const key_le, uint8_t const *const clear_text_le, |
| uint8_t *const cipher_text_le, uint8_t *const cipher_text_be) |
| { |
| zassert_mem_equal(key_le, ecb_encrypt_custom_fake_context.key_le, 16); |
| zassert_mem_equal(clear_text_le, ecb_encrypt_custom_fake_context.clear_text_le, 16); |
| |
| if (cipher_text_le) { |
| memcpy(cipher_text_le, ecb_encrypt_custom_fake_context.cipher_text_le, 16); |
| } |
| |
| if (cipher_text_be) { |
| memcpy(cipher_text_be, ecb_encrypt_custom_fake_context.cipher_text_be, 16); |
| } |
| } |
| |
| |
| /* int lll_csrand_get(void *buf, size_t len); */ |
| FAKE_VALUE_FUNC(int, lll_csrand_get, void *, size_t); |
| |
| struct { |
| /* In */ |
| void *buf; |
| size_t len; |
| } lll_csrand_get_custom_fake_context; |
| |
| int lll_csrand_get_custom_fake(void *buf, size_t len) |
| { |
| zassert_equal(len, lll_csrand_get_custom_fake_context.len); |
| memcpy(buf, lll_csrand_get_custom_fake_context.buf, len); |
| return lll_csrand_get_fake.return_val; |
| } |
| |
| /* struct ll_conn_iso_stream * |
| * ll_conn_iso_stream_get_by_acl(struct ll_conn *conn, uint16_t *cis_iter); |
| */ |
| FAKE_VALUE_FUNC(struct ll_conn_iso_stream *, ll_conn_iso_stream_get_by_acl, |
| struct ll_conn *, uint16_t *); |
| |
| static void enc_setup(void *data) |
| { |
| test_setup(&conn); |
| |
| /* Fake that a Feature exchange proceudre has been executed */ |
| conn.llcp.fex.valid = 1U; |
| conn.llcp.fex.features_used |= LL_FEAT_BIT_EXT_REJ_IND; |
| |
| /* Reset and setup ecb_encrypt fake */ |
| RESET_FAKE(ecb_encrypt); |
| memset(&ecb_encrypt_custom_fake_context, 0, sizeof(ecb_encrypt_custom_fake_context)); |
| ecb_encrypt_fake.custom_fake = ecb_encrypt_custom_fake; |
| |
| /* Reset and setup lll_csrand_get fake */ |
| RESET_FAKE(lll_csrand_get); |
| memset(&lll_csrand_get_custom_fake_context, 0, sizeof(lll_csrand_get_custom_fake_context)); |
| lll_csrand_get_fake.custom_fake = lll_csrand_get_custom_fake; |
| |
| /* Reset ll_conn_iso_stream_get_by_acl fake */ |
| RESET_FAKE(ll_conn_iso_stream_get_by_acl); |
| } |
| |
| /* BLUETOOTH CORE SPECIFICATION Version 5.2 | Vol 6, Part C |
| * 1 ENCRYPTION SAMPLE DATA |
| */ |
| #define RAND 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x90 |
| #define EDIV 0x24, 0x74 |
| #define LTK \ |
| 0x4C, 0x68, 0x38, 0x41, 0x39, 0xF5, 0x74, 0xD8, 0x36, 0xBC, 0xF3, 0x4E, 0x9D, 0xFB, 0x01,\ |
| 0xBF |
| #define SKDM 0xAC, 0xBD, 0xCE, 0xDF, 0xE0, 0xF1, 0x02, 0x13 |
| #define SKDS 0x02, 0x13, 0x24, 0x35, 0x46, 0x57, 0x68, 0x79 |
| #define IVM 0xBA, 0xDC, 0xAB, 0x24 |
| #define IVS 0xDE, 0xAF, 0xBA, 0xBE |
| |
| #define SK_BE \ |
| 0x66, 0xC6, 0xC2, 0x27, 0x8E, 0x3B, 0x8E, 0x05, 0x3E, 0x7E, 0xA3, 0x26, 0x52, 0x1B, 0xAD,\ |
| 0x99 |
| /* +-----+ +-------+ +-----+ |
| * | UT | | LL_A | | LT | |
| * +-----+ +-------+ +-----+ |
| * | | | |
| * | Initiate | | |
| * | Encryption Start Proc. | | |
| * |--------------------------->| | |
| * | -----------------\ | | |
| * | | Empty Tx queue |-| | |
| * | |----------------| | | |
| * | | | |
| * | | LL_ENC_REQ | |
| * | |-------------------->| |
| * | | | |
| * | | LL_ENC_RSP | |
| * | |<--------------------| |
| * | | | |
| * | | LL_START_ENC_REQ | |
| * | |<--------------------| |
| * | ----------------\ | | |
| * | | Tx Encryption |-| | |
| * | | Rx Decryption | | | |
| * | |---------------| | | |
| * | | | |
| * | | LL_START_ENC_RSP | |
| * | |-------------------->| |
| * | | | |
| * | | LL_START_ENC_RSP | |
| * | |<--------------------| |
| * | | | |
| * | Encryption Start Proc. | | |
| * | Complete | | |
| * |<---------------------------| | |
| * | | | |
| */ |
| ZTEST(encryption_start, test_encryption_start_central_loc) |
| { |
| 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 }; |
| const uint8_t skd[] = { SKDM, SKDS }; |
| const uint8_t sk_be[] = { SK_BE }; |
| const uint8_t iv[] = { IVM, IVS }; |
| |
| /* Prepare expected LL_ENC_REQ */ |
| struct pdu_data_llctrl_enc_req exp_enc_req = { |
| .rand = { RAND }, |
| .ediv = { EDIV }, |
| .skdm = { SKDM }, |
| .ivm = { IVM }, |
| }; |
| |
| /* Prepare LL_ENC_RSP */ |
| struct pdu_data_llctrl_enc_rsp enc_rsp = { .skds = { SKDS }, .ivs = { IVS } }; |
| |
| /* Prepare mocked call to lll_csrand_get */ |
| lll_csrand_get_fake.return_val = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); |
| lll_csrand_get_custom_fake_context.buf = exp_enc_req.skdm; |
| lll_csrand_get_custom_fake_context.len = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); |
| |
| /* Prepare mocked call to ecb_encrypt */ |
| memcpy(ecb_encrypt_custom_fake_context.key_le, ltk, 16); |
| memcpy(ecb_encrypt_custom_fake_context.clear_text_le, skd, 16); |
| memcpy(ecb_encrypt_custom_fake_context.cipher_text_be, sk_be, 16); |
| |
| /* 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); |
| |
| /* 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_ENC_RSP, &conn, &enc_rsp); |
| |
| /* Rx */ |
| lt_tx(LL_START_ENC_REQ, &conn, NULL); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Rx paused & enc. */ |
| CHECK_TX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Tx paused & enc. */ |
| |
| /* CCM Tx/Rx SK should match SK */ |
| /* CCM Tx/Rx IV should match the IV */ |
| /* CCM Tx/Rx Counter should be zero */ |
| /* CCM Rx Direction should be S->M */ |
| /* CCM Tx Direction should be M->S */ |
| CHECK_RX_CCM_STATE(conn, sk_be, iv, 0U, CCM_DIR_S_TO_M); |
| CHECK_TX_CCM_STATE(conn, sk_be, iv, 0U, CCM_DIR_M_TO_S); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Tx Queue should have one LL Control PDU */ |
| lt_rx(LL_START_ENC_RSP, &conn, &tx, NULL); |
| lt_rx_q_is_empty(&conn); |
| |
| /* Release Tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Rx paused & enc. */ |
| CHECK_TX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Tx paused & enc. */ |
| |
| /* Rx */ |
| lt_tx(LL_START_ENC_RSP, &conn, NULL); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Rx enc. */ |
| CHECK_TX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Tx enc. */ |
| |
| /* There should be one host notification */ |
| ut_rx_pdu(LL_START_ENC_RSP, &ntf, NULL); |
| ut_rx_q_is_empty(); |
| |
| /* Release Ntf */ |
| release_ntf(ntf); |
| |
| zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), |
| "Free CTX buffers %d", llcp_ctx_buffers_free()); |
| } |
| |
| /* +-----+ +-------+ +-----+ |
| * | UT | | LL_A | | LT | |
| * +-----+ +-------+ +-----+ |
| * | -----------------\ | | |
| * | | Reserver all |-| | |
| * | | Tx/Ntf buffers | | | |
| * | |----------------| | | |
| * | | | |
| * | Initiate | | |
| * | Encryption Start Proc. | | |
| * |--------------------------->| | |
| * | -----------------\ | | |
| * | | Empty Tx queue |-| | |
| * | |----------------| | | |
| * | | | |
| * | | LL_ENC_REQ | |
| * | |-------------------->| |
| * | | | |
| * | | LL_ENC_RSP | |
| * | |<--------------------| |
| * | | | |
| * | | LL_START_ENC_REQ | |
| * | |<--------------------| |
| * | ----------------\ | | |
| * | | Tx Encryption |-| | |
| * | | Rx Decryption | | | |
| * | |---------------| | | |
| * | | | |
| * | | LL_START_ENC_RSP | |
| * | |-------------------->| |
| * | | | |
| * | | LL_START_ENC_RSP | |
| * | |<--------------------| |
| * | | | |
| * | Encryption Start Proc. | | |
| * | Complete | | |
| * |<---------------------------| | |
| * | | | |
| */ |
| ZTEST(encryption_start, test_encryption_start_central_loc_limited_memory) |
| { |
| uint8_t err; |
| struct node_tx *tx; |
| struct node_rx_pdu *ntf; |
| struct proc_ctx *ctx = NULL; |
| |
| const uint8_t rand[] = { RAND }; |
| const uint8_t ediv[] = { EDIV }; |
| const uint8_t ltk[] = { LTK }; |
| const uint8_t skd[] = { SKDM, SKDS }; |
| const uint8_t sk_be[] = { SK_BE }; |
| const uint8_t iv[] = { IVM, IVS }; |
| |
| /* Prepare expected LL_ENC_REQ */ |
| struct pdu_data_llctrl_enc_req exp_enc_req = { |
| .rand = { RAND }, |
| .ediv = { EDIV }, |
| .skdm = { SKDM }, |
| .ivm = { IVM }, |
| }; |
| |
| /* Prepare LL_ENC_RSP */ |
| struct pdu_data_llctrl_enc_rsp enc_rsp = { .skds = { SKDS }, .ivs = { IVS } }; |
| |
| /* Prepare mocked call to lll_csrand_get */ |
| lll_csrand_get_fake.return_val = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); |
| lll_csrand_get_custom_fake_context.buf = exp_enc_req.skdm; |
| lll_csrand_get_custom_fake_context.len = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); |
| |
| /* Prepare mocked call to ecb_encrypt */ |
| memcpy(ecb_encrypt_custom_fake_context.key_le, ltk, 16); |
| memcpy(ecb_encrypt_custom_fake_context.clear_text_le, skd, 16); |
| memcpy(ecb_encrypt_custom_fake_context.cipher_text_be, sk_be, 16); |
| |
| /* 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. */ |
| |
| /* Allocate dummy procedure used to steal all buffers */ |
| ctx = llcp_create_local_procedure(PROC_VERSION_EXCHANGE); |
| |
| /* Steal all tx buffers */ |
| while (llcp_tx_alloc_peek(&conn, ctx)) { |
| tx = llcp_tx_alloc(&conn, ctx); |
| zassert_not_null(tx, NULL); |
| } |
| |
| /* Dummy remove, as above loop might queue up ctx */ |
| llcp_tx_alloc_unpeek(ctx); |
| |
| /* Initiate an Encryption Start Procedure */ |
| err = ull_cp_encryption_start(&conn, rand, ediv, ltk); |
| zassert_equal(err, BT_HCI_ERR_SUCCESS); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Tx Queue should have no LL Control PDU */ |
| 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. */ |
| |
| /* Done */ |
| event_done(&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); |
| |
| /* 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. */ |
| |
| /* Rx */ |
| lt_tx(LL_ENC_RSP, &conn, &enc_rsp); |
| |
| /* Rx */ |
| lt_tx(LL_START_ENC_REQ, &conn, NULL); |
| |
| /* 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. */ |
| |
| /* Tx Queue should have no LL Control PDU */ |
| lt_rx_q_is_empty(&conn); |
| |
| /* Release Tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Tx Queue should have no LL Control PDU */ |
| lt_rx(LL_START_ENC_RSP, &conn, &tx, NULL); |
| lt_rx_q_is_empty(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Rx paused & enc. */ |
| CHECK_TX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Tx paused & enc. */ |
| |
| /* CCM Tx/Rx SK should match SK */ |
| /* CCM Tx/Rx IV should match the IV */ |
| /* CCM Tx/Rx Counter should be zero */ |
| /* CCM Tx Direction should be M->S */ |
| /* CCM Rx Direction should be S->M */ |
| CHECK_RX_CCM_STATE(conn, sk_be, iv, 0U, CCM_DIR_S_TO_M); |
| CHECK_TX_CCM_STATE(conn, sk_be, iv, 0U, CCM_DIR_M_TO_S); |
| |
| /* Release Tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* Rx */ |
| lt_tx(LL_START_ENC_RSP, &conn, NULL); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Rx enc. */ |
| CHECK_TX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Tx enc. */ |
| |
| /* There should be one host notification */ |
| ut_rx_pdu(LL_START_ENC_RSP, &ntf, NULL); |
| ut_rx_q_is_empty(); |
| |
| /* Release Ntf */ |
| release_ntf(ntf); |
| |
| /* Tx Encryption should be enabled */ |
| zassert_equal(conn.lll.enc_tx, 1U); |
| |
| /* Rx Decryption should be enabled */ |
| zassert_equal(conn.lll.enc_rx, 1U); |
| |
| /* Release dummy procedure */ |
| llcp_proc_ctx_release(ctx); |
| |
| zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), |
| "Free CTX buffers %d", llcp_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 | | |
| * |<---------------------------| | |
| * | | | |
| */ |
| ZTEST(encryption_start, test_encryption_start_central_loc_reject_ext) |
| { |
| 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 */ |
| lll_csrand_get_fake.return_val = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); |
| lll_csrand_get_custom_fake_context.buf = exp_enc_req.skdm; |
| lll_csrand_get_custom_fake_context.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); |
| |
| /* 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 */ |
| release_ntf(ntf); |
| |
| zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), |
| "Free CTX buffers %d", llcp_ctx_buffers_free()); |
| } |
| |
| /* +-----+ +-------+ +-----+ |
| * | UT | | LL_A | | LT | |
| * +-----+ +-------+ +-----+ |
| * | | | |
| * | Initiate | | |
| * | Encryption Start Proc. | | |
| * |--------------------------->| | |
| * | -----------------\ | | |
| * | | Empty Tx queue |-| | |
| * | |----------------| | | |
| * | | | |
| * | | LL_ENC_REQ | |
| * | |-------------------->| |
| * | | | |
| * | | LL_REJECT_IND | |
| * | |<--------------------| |
| * | | | |
| * | Encryption Start Proc. | | |
| * | Complete | | |
| * |<---------------------------| | |
| * | | | |
| */ |
| ZTEST(encryption_start, test_encryption_start_central_loc_reject) |
| { |
| 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 */ |
| lll_csrand_get_fake.return_val = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); |
| lll_csrand_get_custom_fake_context.buf = exp_enc_req.skdm; |
| lll_csrand_get_custom_fake_context.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); |
| |
| /* 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 */ |
| release_ntf(ntf); |
| |
| zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), |
| "Free CTX buffers %d", llcp_ctx_buffers_free()); |
| } |
| |
| /* +-----+ +-------+ +-----+ |
| * | UT | | LL_A | | LT | |
| * +-----+ +-------+ +-----+ |
| * | | | |
| * | Initiate | | |
| * | Encryption Start Proc. | | |
| * |--------------------------->| | |
| * | -----------------\ | | |
| * | | Empty Tx queue |-| | |
| * | |----------------| | | |
| * | | | |
| * | | LL_ENC_REQ | |
| * | |-------------------->| |
| * | | | |
| * | | LL_ENC_RSP | |
| * | |<--------------------| |
| * | | | |
| * | | LL_REJECT_EXT_IND | |
| * | |<--------------------| |
| * | | | |
| * | Encryption Start Proc. | | |
| * | Complete | | |
| * |<---------------------------| | |
| * | | | |
| */ |
| ZTEST(encryption_start, test_encryption_start_central_loc_no_ltk) |
| { |
| 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 LL_ENC_RSP */ |
| struct pdu_data_llctrl_enc_rsp enc_rsp = { .skds = { SKDS }, .ivs = { IVS } }; |
| |
| /* Prepare mocked call to lll_csrand_get */ |
| lll_csrand_get_fake.return_val = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); |
| lll_csrand_get_custom_fake_context.buf = exp_enc_req.skdm; |
| lll_csrand_get_custom_fake_context.len = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); |
| |
| struct pdu_data_llctrl_reject_ind reject_ind = { .error_code = |
| BT_HCI_ERR_PIN_OR_KEY_MISSING }; |
| |
| struct pdu_data_llctrl_reject_ext_ind reject_ext_ind = { |
| .reject_opcode = PDU_DATA_LLCTRL_TYPE_ENC_REQ, |
| .error_code = BT_HCI_ERR_PIN_OR_KEY_MISSING |
| }; |
| |
| /* 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); |
| |
| /* 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_ENC_RSP, &conn, &enc_rsp); |
| |
| /* 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 */ |
| release_ntf(ntf); |
| |
| zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), |
| "Free CTX buffers %d", llcp_ctx_buffers_free()); |
| } |
| |
| /* +-----+ +-------+ +-----+ |
| * | UT | | LL_A | | LT | |
| * +-----+ +-------+ +-----+ |
| * | | | |
| * | Initiate | | |
| * | Encryption Start Proc. | | |
| * |--------------------------->| | |
| * | -----------------\ | | |
| * | | Empty Tx queue |-| | |
| * | |----------------| | | |
| * | | | |
| * | | LL_ENC_REQ | |
| * | |-------------------->| |
| * | | | |
| * | | LL_ENC_RSP | |
| * | |<--------------------| |
| * | | | |
| * | | LL_REJECT_IND | |
| * | |<--------------------| |
| * | | | |
| * | Encryption Start Proc. | | |
| * | Complete | | |
| * |<---------------------------| | |
| * | | | |
| */ |
| ZTEST(encryption_start, test_encryption_start_central_loc_no_ltk_2) |
| { |
| 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 LL_ENC_RSP */ |
| struct pdu_data_llctrl_enc_rsp enc_rsp = { .skds = { SKDS }, .ivs = { IVS } }; |
| |
| /* Prepare mocked call to lll_csrand_get */ |
| lll_csrand_get_fake.return_val = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); |
| lll_csrand_get_custom_fake_context.buf = exp_enc_req.skdm; |
| lll_csrand_get_custom_fake_context.len = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); |
| |
| struct pdu_data_llctrl_reject_ind reject_ind = { .error_code = |
| BT_HCI_ERR_PIN_OR_KEY_MISSING }; |
| |
| /* 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); |
| |
| /* 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_ENC_RSP, &conn, &enc_rsp); |
| |
| /* 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 */ |
| release_ntf(ntf); |
| |
| zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), |
| "Free CTX buffers %d", llcp_ctx_buffers_free()); |
| } |
| |
| /* +-----+ +-------+ +-----+ |
| * | UT | | LL_A | | LT | |
| * +-----+ +-------+ +-----+ |
| * | | | |
| * | Initiate | | |
| * | Encryption Start Proc. | | |
| * |--------------------------->| | |
| * | -----------------\ | | |
| * | | Empty Tx queue |-| | |
| * | |----------------| | | |
| * | | | |
| * | | LL_ENC_REQ | |
| * | |-------------------->| |
| * | | | |
| * | | LL_ENC_RSP | |
| * | |<--------------------| |
| * | | | |
| * | | LL_VERSION_IND | |
| * | |<--------------------| |
| * | | | |
| * | Encryption Start Proc. | | |
| * | Complete | | |
| * |<---------------------------| | |
| * | | | |
| */ |
| ZTEST(encryption_start, test_encryption_start_central_loc_mic) |
| { |
| uint8_t err; |
| struct node_tx *tx; |
| |
| 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 LL_ENC_RSP */ |
| struct pdu_data_llctrl_enc_rsp 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 */ |
| lll_csrand_get_fake.return_val = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); |
| lll_csrand_get_custom_fake_context.buf = exp_enc_req.skdm; |
| lll_csrand_get_custom_fake_context.len = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); |
| |
| /* 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); |
| |
| /* 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_ENC_RSP, &conn, &enc_rsp); |
| |
| /* 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(llcp_ctx_buffers_free(), test_ctx_buffers_cnt() - 1, |
| "Free CTX buffers %d", llcp_ctx_buffers_free()); |
| } |
| |
| /* +-----+ +-------+ +-----+ |
| * | UT | | LL_A | | LT | |
| * +-----+ +-------+ +-----+ |
| * | | | |
| * | | LL_ENC_REQ | |
| * | |<--------------------| |
| * | -----------------\ | | |
| * | | Empty Tx queue |-| | |
| * | |----------------| | | |
| * | | | |
| * | | LL_ENC_RSP | |
| * | |-------------------->| |
| * | | | |
| * | LTK Request | | |
| * |<----------------------| | |
| * | | | |
| * | LTK Request Reply | | |
| * |---------------------->| | |
| * | | | |
| * | | LL_START_ENC_REQ | |
| * | |-------------------->| |
| * | ----------------\ | | |
| * | | Rx Decryption |-| | |
| * | |---------------| | | |
| * | | | |
| * | | LL_START_ENC_RSP | |
| * | |<--------------------| |
| * | | | |
| * | Encryption Change | | |
| * |<----------------------| | |
| * | | | |
| * | | LL_START_ENC_RSP | |
| * | |-------------------->| |
| * | ----------------\ | | |
| * | | Tx Encryption |-| | |
| * | |---------------| | | |
| * | | | |
| */ |
| ZTEST(encryption_start, test_encryption_start_periph_rem) |
| { |
| struct node_tx *tx; |
| struct node_rx_pdu *ntf; |
| |
| const uint8_t ltk[] = { LTK }; |
| const uint8_t skd[] = { SKDM, SKDS }; |
| const uint8_t sk_be[] = { SK_BE }; |
| const uint8_t iv[] = { IVM, IVS }; |
| |
| /* 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 }, |
| }; |
| |
| /* Prepare mocked call to lll_csrand_get */ |
| lll_csrand_get_fake.return_val = sizeof(exp_enc_rsp.skds) + sizeof(exp_enc_rsp.ivs); |
| lll_csrand_get_custom_fake_context.buf = exp_enc_rsp.skds; |
| lll_csrand_get_custom_fake_context.len = sizeof(exp_enc_rsp.skds) + sizeof(exp_enc_rsp.ivs); |
| |
| /* Prepare mocked call to ecb_encrypt */ |
| memcpy(ecb_encrypt_custom_fake_context.key_le, ltk, 16); |
| memcpy(ecb_encrypt_custom_fake_context.clear_text_le, skd, 16); |
| memcpy(ecb_encrypt_custom_fake_context.cipher_text_be, sk_be, 16); |
| |
| /* 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); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ |
| CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */ |
| |
| /* Rx */ |
| lt_tx(LL_ENC_REQ, &conn, &enc_req); |
| |
| /* 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); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Rx paused & unenc. */ |
| CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ |
| |
| /* Tx Queue should have one LL Control PDU */ |
| lt_rx(LL_ENC_RSP, &conn, &tx, &exp_enc_rsp); |
| 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. */ |
| |
| /* 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 */ |
| release_ntf(ntf); |
| |
| /* LTK request reply */ |
| ull_cp_ltk_req_reply(&conn, ltk); |
| |
| /* 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_START_ENC_REQ, &conn, &tx, NULL); |
| lt_rx_q_is_empty(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Rx paused & enc. */ |
| CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Rx paused & enc. */ |
| CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ |
| |
| /* Release Tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* CCM Rx SK should match SK */ |
| /* CCM Rx IV should match the IV */ |
| /* CCM Rx Counter should be zero */ |
| /* CCM Rx Direction should be M->S */ |
| CHECK_RX_CCM_STATE(conn, sk_be, iv, 0U, CCM_DIR_M_TO_S); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Rx paused & enc. */ |
| CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ |
| |
| /* Rx */ |
| lt_tx(LL_START_ENC_RSP, &conn, NULL); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Rx enc. */ |
| CHECK_TX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Tx enc. */ |
| |
| /* There should be a host notification */ |
| ut_rx_pdu(LL_START_ENC_RSP, &ntf, NULL); |
| ut_rx_q_is_empty(); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Tx Queue should have one LL Control PDU */ |
| lt_rx(LL_START_ENC_RSP, &conn, &tx, NULL); |
| lt_rx_q_is_empty(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Rx enc. */ |
| CHECK_TX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Tx enc. */ |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Rx enc. */ |
| CHECK_TX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Tx enc. */ |
| |
| /* Release Tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* CCM Tx SK should match SK */ |
| /* CCM Tx IV should match the IV */ |
| /* CCM Tx Counter should be zero */ |
| /* CCM Tx Direction should be S->M */ |
| CHECK_TX_CCM_STATE(conn, sk_be, iv, 0U, CCM_DIR_S_TO_M); |
| |
| zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), |
| "Free CTX buffers %d", llcp_ctx_buffers_free()); |
| } |
| |
| /* +-----+ +-------+ +-----+ |
| * | UT | | LL_A | | LT | |
| * +-----+ +-------+ +-----+ |
| * | -----------------\ | | |
| * | | Reserver all |-| | |
| * | | Tx/Ntf buffers | | | |
| * | |----------------| | | |
| * | | | |
| * | | LL_ENC_REQ | |
| * | |<--------------------| |
| * | -----------------\ | | |
| * | | Empty Tx queue |-| | |
| * | |----------------| | | |
| * | | | |
| * | | LL_ENC_RSP | |
| * | |-------------------->| |
| * | | | |
| * | LTK Request | | |
| * |<----------------------| | |
| * | | | |
| * | LTK Request Reply | | |
| * |---------------------->| | |
| * | | | |
| * | | LL_START_ENC_REQ | |
| * | |-------------------->| |
| * | ----------------\ | | |
| * | | Rx Decryption |-| | |
| * | |---------------| | | |
| * | | | |
| * | | LL_START_ENC_RSP | |
| * | |<--------------------| |
| * | | | |
| * | Encryption Change | | |
| * |<----------------------| | |
| * | | | |
| * | | LL_START_ENC_RSP | |
| * | |-------------------->| |
| * | ----------------\ | | |
| * | | Tx Encryption |-| | |
| * | |---------------| | | |
| * | | | |
| */ |
| ZTEST(encryption_start, test_encryption_start_periph_rem_limited_memory) |
| { |
| struct node_tx *tx; |
| struct node_rx_pdu *ntf; |
| struct proc_ctx *ctx = NULL; |
| |
| const uint8_t ltk[] = { LTK }; |
| const uint8_t skd[] = { SKDM, SKDS }; |
| const uint8_t sk_be[] = { SK_BE }; |
| const uint8_t iv[] = { IVM, IVS }; |
| |
| /* 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 }, |
| }; |
| |
| /* Prepare mocked call to lll_csrand_get */ |
| lll_csrand_get_fake.return_val = sizeof(exp_enc_rsp.skds) + sizeof(exp_enc_rsp.ivs); |
| lll_csrand_get_custom_fake_context.buf = exp_enc_rsp.skds; |
| lll_csrand_get_custom_fake_context.len = sizeof(exp_enc_rsp.skds) + sizeof(exp_enc_rsp.ivs); |
| |
| /* Prepare mocked call to ecb_encrypt */ |
| memcpy(ecb_encrypt_custom_fake_context.key_le, ltk, 16); |
| memcpy(ecb_encrypt_custom_fake_context.clear_text_le, skd, 16); |
| memcpy(ecb_encrypt_custom_fake_context.cipher_text_be, sk_be, 16); |
| |
| /* Role */ |
| test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); |
| |
| /* Connect */ |
| ull_cp_state_set(&conn, ULL_CP_CONNECTED); |
| |
| /* Allocate dummy procedure used to steal all buffers */ |
| ctx = llcp_create_local_procedure(PROC_VERSION_EXCHANGE); |
| |
| /* Steal all tx buffers */ |
| while (llcp_tx_alloc_peek(&conn, ctx)) { |
| tx = llcp_tx_alloc(&conn, ctx); |
| zassert_not_null(tx, NULL); |
| } |
| |
| /* Dummy remove, as above loop might queue up ctx */ |
| llcp_tx_alloc_unpeek(ctx); |
| |
| /* 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); |
| |
| /* Tx Queue should not have a LL Control PDU */ |
| lt_rx_q_is_empty(&conn); |
| |
| /* 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. */ |
| |
| /* Release tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* 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. */ |
| |
| /* There should be one host notification */ |
| ut_rx_pdu(LL_ENC_REQ, &ntf, &enc_req); |
| ut_rx_q_is_empty(); |
| |
| /* Release ntf */ |
| release_ntf(ntf); |
| |
| /* 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. */ |
| |
| /* LTK request reply */ |
| ull_cp_ltk_req_reply(&conn, ltk); |
| |
| /* 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 not have one LL Control PDU */ |
| 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); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Tx Queue should have one LL Control PDU */ |
| lt_rx(LL_START_ENC_REQ, &conn, &tx, NULL); |
| lt_rx_q_is_empty(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Rx paused & enc. */ |
| CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Rx paused & enc. */ |
| CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ |
| |
| /* CCM Rx SK should match SK */ |
| /* CCM Rx IV should match the IV */ |
| /* CCM Rx Counter should be zero */ |
| /* CCM Rx Direction should be M->S */ |
| CHECK_RX_CCM_STATE(conn, sk_be, iv, 0U, CCM_DIR_M_TO_S); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Rx */ |
| lt_tx(LL_START_ENC_RSP, &conn, NULL); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Rx paused & enc. */ |
| CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Rx paused & enc. */ |
| CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ |
| |
| /* There should be one host notification */ |
| ut_rx_pdu(LL_START_ENC_RSP, &ntf, NULL); |
| ut_rx_q_is_empty(); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Tx Queue should not have a LL Control PDU */ |
| lt_rx_q_is_empty(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Rx paused & enc. */ |
| CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, PAUSED, ENCRYPTED); /* Rx paused & enc. */ |
| CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ |
| |
| /* Release tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Tx Queue should have one LL Control PDU */ |
| lt_rx(LL_START_ENC_RSP, &conn, &tx, NULL); |
| lt_rx_q_is_empty(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Rx enc. */ |
| CHECK_TX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Tx enc. */ |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Rx enc. */ |
| CHECK_TX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Tx enc. */ |
| |
| /* CCM Tx SK should match SK */ |
| /* CCM Tx IV should match the IV */ |
| /* CCM Tx Counter should be zero */ |
| /* CCM Tx Direction should be S->M */ |
| CHECK_TX_CCM_STATE(conn, sk_be, iv, 0U, CCM_DIR_S_TO_M); |
| |
| /* Release dummy procedure */ |
| llcp_proc_ctx_release(ctx); |
| |
| zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), |
| "Free CTX buffers %d", llcp_ctx_buffers_free()); |
| } |
| |
| /* +-----+ +-------+ +-----+ |
| * | UT | | LL_A | | LT | |
| * +-----+ +-------+ +-----+ |
| * | | | |
| * | | LL_ENC_REQ | |
| * | |<--------------------| |
| * | -----------------\ | | |
| * | | Empty Tx queue |-| | |
| * | |----------------| | | |
| * | | | |
| * | | LL_ENC_RSP | |
| * | |-------------------->| |
| * | | | |
| * | LTK Request | | |
| * |<----------------------| | |
| * | | | |
| * | LTK Request Reply | | |
| * |---------------------->| | |
| * | | | |
| * | | LL_REJECT_EXT_IND | |
| * | |-------------------->| |
| */ |
| ZTEST(encryption_start, test_encryption_start_periph_rem_no_ltk) |
| { |
| 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_reject_ext_ind reject_ext_ind = { |
| .reject_opcode = PDU_DATA_LLCTRL_TYPE_ENC_REQ, |
| .error_code = BT_HCI_ERR_PIN_OR_KEY_MISSING |
| }; |
| |
| /* Prepare mocked call to lll_csrand_get */ |
| lll_csrand_get_fake.return_val = sizeof(exp_enc_rsp.skds) + sizeof(exp_enc_rsp.ivs); |
| lll_csrand_get_custom_fake_context.buf = exp_enc_rsp.skds; |
| lll_csrand_get_custom_fake_context.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 */ |
| release_ntf(ntf); |
| |
| /* LTK request reply */ |
| ull_cp_ltk_req_neq_reply(&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_REJECT_EXT_IND, &conn, &tx, &reject_ext_ind); |
| lt_rx_q_is_empty(&conn); |
| |
| /* 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, RESUMED, UNENCRYPTED); /* Rx unenc. */ |
| CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */ |
| |
| /* Release Tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* There should not be a host notification */ |
| ut_rx_q_is_empty(); |
| |
| /* All contexts should be released until now. This is a side-effect of a call to |
| * ull_cp_tx_ntf that internall calls rr_check_done and lr_check_done. |
| */ |
| zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), |
| "Free CTX buffers %d", llcp_ctx_buffers_free()); |
| } |
| |
| /* +-----+ +-------+ +-----+ |
| * | UT | | LL_A | | LT | |
| * +-----+ +-------+ +-----+ |
| * | | | |
| * | | LL_ENC_REQ | |
| * | |<--------------------| |
| * | -----------------\ | | |
| * | | Empty Tx queue |-| | |
| * | |----------------| | | |
| * | | | |
| * | | LL_ENC_RSP | |
| * | |-------------------->| |
| * | | | |
| * | | LL_VERSION_IND | |
| * | |<--------------------| |
| * | | | |
| */ |
| ZTEST(encryption_start, test_encryption_start_periph_rem_mic) |
| { |
| 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 */ |
| lll_csrand_get_fake.return_val = sizeof(exp_enc_rsp.skds) + sizeof(exp_enc_rsp.ivs); |
| lll_csrand_get_custom_fake_context.buf = exp_enc_rsp.skds; |
| lll_csrand_get_custom_fake_context.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 */ |
| 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(llcp_ctx_buffers_free(), test_ctx_buffers_cnt() - 1, |
| "Free CTX buffers %d", llcp_ctx_buffers_free()); |
| } |
| |
| |
| ZTEST(encryption_pause, test_encryption_pause_central_loc) |
| { |
| 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 }; |
| const uint8_t skd[] = { SKDM, SKDS }; |
| const uint8_t sk_be[] = { SK_BE }; |
| const uint8_t iv[] = { IVM, IVS }; |
| |
| /* Prepare expected LL_ENC_REQ */ |
| struct pdu_data_llctrl_enc_req exp_enc_req = { |
| .rand = { RAND }, |
| .ediv = { EDIV }, |
| .skdm = { SKDM }, |
| .ivm = { IVM }, |
| }; |
| |
| /* Prepare LL_ENC_RSP */ |
| struct pdu_data_llctrl_enc_rsp enc_rsp = { .skds = { SKDS }, .ivs = { IVS } }; |
| |
| /* Prepare mocked call to lll_csrand_get */ |
| lll_csrand_get_fake.return_val = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); |
| lll_csrand_get_custom_fake_context.buf = exp_enc_req.skdm; |
| lll_csrand_get_custom_fake_context.len = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); |
| |
| /* Prepare mocked call to ecb_encrypt */ |
| memcpy(ecb_encrypt_custom_fake_context.key_le, ltk, 16); |
| memcpy(ecb_encrypt_custom_fake_context.clear_text_le, skd, 16); |
| memcpy(ecb_encrypt_custom_fake_context.cipher_text_be, sk_be, 16); |
| |
| /* Role */ |
| test_set_role(&conn, BT_HCI_ROLE_CENTRAL); |
| |
| /* Connect */ |
| ull_cp_state_set(&conn, ULL_CP_CONNECTED); |
| |
| /* Fake that encryption is already active */ |
| conn.lll.enc_rx = 1U; |
| conn.lll.enc_tx = 1U; |
| |
| /**** ENCRYPTED ****/ |
| |
| /* Initiate an Encryption Pause Procedure */ |
| err = ull_cp_encryption_pause(&conn, rand, ediv, ltk); |
| zassert_equal(err, BT_HCI_ERR_SUCCESS); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Tx Queue should have one LL Control PDU */ |
| lt_rx(LL_PAUSE_ENC_REQ, &conn, &tx, NULL); |
| lt_rx_q_is_empty(&conn); |
| |
| /* Release Tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* Rx */ |
| lt_tx(LL_PAUSE_ENC_RSP, &conn, NULL); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Tx Queue should have one LL Control PDU */ |
| lt_rx(LL_PAUSE_ENC_RSP, &conn, &tx, NULL); |
| |
| /* Release Tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* Tx Encryption should be disabled */ |
| zassert_equal(conn.lll.enc_tx, 0U); |
| |
| /* Rx Decryption should be disabled */ |
| zassert_equal(conn.lll.enc_rx, 0U); |
| |
| /**** UNENCRYPTED ****/ |
| |
| /* Tx Queue should have one LL Control PDU */ |
| lt_rx(LL_ENC_REQ, &conn, &tx, &exp_enc_req); |
| lt_rx_q_is_empty(&conn); |
| |
| /* Release Tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* Rx */ |
| lt_tx(LL_ENC_RSP, &conn, &enc_rsp); |
| |
| /* Rx */ |
| lt_tx(LL_START_ENC_REQ, &conn, NULL); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Tx Queue should have one LL Control PDU */ |
| lt_rx(LL_START_ENC_RSP, &conn, &tx, NULL); |
| lt_rx_q_is_empty(&conn); |
| |
| /* CCM Tx/Rx SK should match SK */ |
| /* CCM Tx/Rx IV should match the IV */ |
| /* CCM Tx/Rx Counter should be zero */ |
| /* CCM Rx Direction should be S->M */ |
| /* CCM Tx Direction should be M->S */ |
| CHECK_RX_CCM_STATE(conn, sk_be, iv, 0U, CCM_DIR_S_TO_M); |
| CHECK_TX_CCM_STATE(conn, sk_be, iv, 0U, CCM_DIR_M_TO_S); |
| |
| /* Tx Encryption should be enabled */ |
| zassert_equal(conn.lll.enc_tx, 1U); |
| |
| /* Rx Decryption should be enabled */ |
| zassert_equal(conn.lll.enc_rx, 1U); |
| |
| /* Release Tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* Rx */ |
| lt_tx(LL_START_ENC_RSP, &conn, NULL); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* There should be one host notification */ |
| ut_rx_node(NODE_ENC_REFRESH, &ntf, NULL); |
| ut_rx_q_is_empty(); |
| |
| /* Release Ntf */ |
| release_ntf(ntf); |
| |
| /* Tx Encryption should be enabled */ |
| zassert_equal(conn.lll.enc_tx, 1U); |
| |
| /* Rx Decryption should be enabled */ |
| zassert_equal(conn.lll.enc_rx, 1U); |
| |
| zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), |
| "Free CTX buffers %d", llcp_ctx_buffers_free()); |
| } |
| |
| ZTEST(encryption_pause, test_encryption_pause_periph_rem) |
| { |
| struct node_tx *tx; |
| struct node_rx_pdu *ntf; |
| |
| const uint8_t ltk[] = { LTK }; |
| const uint8_t skd[] = { SKDM, SKDS }; |
| const uint8_t sk_be[] = { SK_BE }; |
| const uint8_t iv[] = { IVM, IVS }; |
| |
| /* 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 }, |
| }; |
| |
| /* Prepare mocked call to lll_csrand_get */ |
| lll_csrand_get_fake.return_val = sizeof(exp_enc_rsp.skds) + sizeof(exp_enc_rsp.ivs); |
| lll_csrand_get_custom_fake_context.buf = exp_enc_rsp.skds; |
| lll_csrand_get_custom_fake_context.len = sizeof(exp_enc_rsp.skds) + sizeof(exp_enc_rsp.ivs); |
| |
| /* Prepare mocked call to ecb_encrypt */ |
| memcpy(ecb_encrypt_custom_fake_context.key_le, ltk, 16); |
| memcpy(ecb_encrypt_custom_fake_context.clear_text_le, skd, 16); |
| memcpy(ecb_encrypt_custom_fake_context.cipher_text_be, sk_be, 16); |
| |
| /* Role */ |
| test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); |
| |
| /* Connect */ |
| ull_cp_state_set(&conn, ULL_CP_CONNECTED); |
| |
| /* Fake that encryption is already active */ |
| conn.lll.enc_rx = 1U; |
| conn.lll.enc_tx = 1U; |
| |
| /**** ENCRYPTED ****/ |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Rx */ |
| lt_tx(LL_PAUSE_ENC_REQ, &conn, NULL); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Tx Queue should have one LL Control PDU */ |
| lt_rx(LL_PAUSE_ENC_RSP, &conn, &tx, NULL); |
| lt_rx_q_is_empty(&conn); |
| |
| /* Rx Decryption should be disabled */ |
| zassert_equal(conn.lll.enc_rx, 0U); |
| |
| /* Rx */ |
| lt_tx(LL_PAUSE_ENC_RSP, &conn, NULL); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Release Tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* Tx Encryption should be disabled */ |
| zassert_equal(conn.lll.enc_tx, 0U); |
| |
| /**** UNENCRYPTED ****/ |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Rx */ |
| lt_tx(LL_ENC_REQ, &conn, &enc_req); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* 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); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* 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 */ |
| release_ntf(ntf); |
| |
| /* LTK request reply */ |
| ull_cp_ltk_req_reply(&conn, ltk); |
| |
| /* 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_START_ENC_REQ, &conn, &tx, NULL); |
| lt_rx_q_is_empty(&conn); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Release Tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* CCM Rx SK should match SK */ |
| /* CCM Rx IV should match the IV */ |
| /* CCM Rx Counter should be zero */ |
| /* CCM Rx Direction should be M->S */ |
| CHECK_RX_CCM_STATE(conn, sk_be, iv, 0U, CCM_DIR_M_TO_S); |
| |
| /* Rx Decryption should be enabled */ |
| zassert_equal(conn.lll.enc_rx, 1U); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Rx */ |
| lt_tx(LL_START_ENC_RSP, &conn, NULL); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* There should be a host notification */ |
| ut_rx_node(NODE_ENC_REFRESH, &ntf, NULL); |
| ut_rx_q_is_empty(); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Tx Queue should have one LL Control PDU */ |
| lt_rx(LL_START_ENC_RSP, &conn, &tx, NULL); |
| lt_rx_q_is_empty(&conn); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Release Tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /* CCM Tx SK should match SK */ |
| /* CCM Tx IV should match the IV */ |
| /* CCM Tx Counter should be zero */ |
| /* CCM Tx Direction should be S->M */ |
| CHECK_TX_CCM_STATE(conn, sk_be, iv, 0U, CCM_DIR_S_TO_M); |
| |
| /* Tx Encryption should be enabled */ |
| zassert_equal(conn.lll.enc_tx, 1U); |
| |
| zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), |
| "Free CTX buffers %d", llcp_ctx_buffers_free()); |
| } |
| |
| /* +-----+ +-------+ +-----+ |
| * | UT | | LL_A | | LT | |
| * +-----+ +-------+ +-----+ |
| * | | | |
| * /------------------------------------------------------\ |
| * | Encrypted & CIS Established | |
| * \------------------------------------------------------/ |
| * | | | |
| * | Initiate | | |
| * | Encryption Pause Proc. | | |
| * |--------------------------->| | |
| * | | | |
| * | Command Disallowed | | |
| * |<---------------------------| | |
| * | | | |
| * | | LL_PAUSE_ENC_REQ | |
| * | |<--------------------| |
| * | | | |
| * | | LL_REJECT_EXT_IND | |
| * | |-------------------->| |
| * | | | |
| */ |
| ZTEST(encryption_pause, test_encryption_pause_periph_rem_invalid) |
| { |
| uint8_t err; |
| |
| struct node_tx *tx; |
| struct node_rx_pdu *ntf; |
| struct ll_conn_iso_stream cis = { 0 }; |
| |
| const uint8_t rand[] = { RAND }; |
| const uint8_t ediv[] = { EDIV }; |
| const uint8_t ltk[] = { LTK }; |
| |
| struct pdu_data_llctrl_reject_ext_ind reject_ext_ind = { |
| .reject_opcode = PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ, |
| .error_code = BT_HCI_ERR_LMP_PDU_NOT_ALLOWED |
| }; |
| |
| /* Prepare mocked call to ll_conn_iso_stream_get_by_acl() */ |
| ll_conn_iso_stream_get_by_acl_fake.return_val = &cis; |
| |
| /* Role */ |
| test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); |
| |
| /* Connect */ |
| ull_cp_state_set(&conn, ULL_CP_CONNECTED); |
| |
| /* Fake that encryption is already active */ |
| conn.lll.enc_rx = 1U; |
| conn.lll.enc_tx = 1U; |
| |
| /**** ENCRYPTED ****/ |
| |
| /* Initiate an Encryption Pause Procedure */ |
| err = ull_cp_encryption_pause(&conn, rand, ediv, ltk); |
| zassert_equal(err, BT_HCI_ERR_CMD_DISALLOWED); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Rx enc. */ |
| CHECK_TX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Tx enc. */ |
| |
| /**** *****/ |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Rx */ |
| lt_tx(LL_PAUSE_ENC_REQ, &conn, NULL); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Prepare */ |
| event_prepare(&conn); |
| |
| /* Tx Queue should have one LL Control PDU */ |
| lt_rx(LL_REJECT_EXT_IND, &conn, &tx, &reject_ext_ind); |
| lt_rx_q_is_empty(&conn); |
| |
| /* Done */ |
| event_done(&conn); |
| |
| /* Check state */ |
| CHECK_RX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Rx enc. */ |
| CHECK_TX_PE_STATE(conn, RESUMED, ENCRYPTED); /* Tx enc. */ |
| |
| /* Release Tx */ |
| ull_cp_release_tx(&conn, tx); |
| |
| /**** *****/ |
| |
| zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), |
| "Free CTX buffers %d", llcp_ctx_buffers_free()); |
| } |
| |
| |
| ZTEST_SUITE(encryption_start, NULL, NULL, enc_setup, NULL, NULL); |
| ZTEST_SUITE(encryption_pause, NULL, NULL, enc_setup, NULL, NULL); |