http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/c5901fcc/net/nimble/host/test/src/ble_sm_test_util.c ---------------------------------------------------------------------- diff --cc net/nimble/host/test/src/ble_sm_test_util.c index 51dde82,0000000..aab2bc8 mode 100644,000000..100644 --- a/net/nimble/host/test/src/ble_sm_test_util.c +++ b/net/nimble/host/test/src/ble_sm_test_util.c @@@ -1,2410 -1,0 +1,2414 @@@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <stddef.h> +#include <string.h> +#include <errno.h> +#include "testutil/testutil.h" +#include "nimble/hci_common.h" +#include "nimble/nimble_opt.h" +#include "host/ble_sm.h" +#include "host/ble_hs_test.h" +#include "host/ble_hs_id.h" +#include "ble_hs_test_util.h" +#include "ble_sm_test_util.h" + +int ble_sm_test_gap_event_type; +int ble_sm_test_gap_status; +struct ble_gap_sec_state ble_sm_test_sec_state; ++static struct ble_gap_passkey_params ble_sm_test_ioact; + +int ble_sm_test_store_obj_type; +union ble_store_key ble_sm_test_store_key; +union ble_store_value ble_sm_test_store_value; + +static ble_store_read_fn ble_sm_test_util_store_read; +static ble_store_write_fn ble_sm_test_util_store_write; + +struct ble_sm_test_util_entity { + uint8_t addr_type; + uint8_t id_addr_type; + uint8_t *id_addr; + uint8_t *rpa; + + struct ble_sm_pair_cmd *pair_cmd; + struct ble_sm_pair_confirm *confirms; + struct ble_sm_pair_random *randoms; + struct ble_sm_id_info *id_info; + struct ble_sm_id_addr_info *id_addr_info; + struct ble_sm_sign_info *sign_info; + uint8_t *ltk; + + uint8_t key_dist; + + /*** Secure connections fields. */ + struct ble_sm_public_key *public_key; + struct ble_sm_dhkey_check *dhkey_check; + + /*** Legacy fields. */ + struct ble_sm_enc_info *enc_info; + struct ble_sm_master_id *master_id; + uint64_t rand_num; + uint16_t ediv; +}; + +#define BLE_SM_TEST_UTIL_HCI_HDR(handle, pb, len) \ + ((struct hci_data_hdr) { \ + .hdh_handle_pb_bc = ((handle) << 0) | \ + ((pb) << 12), \ + .hdh_len = (len) \ + }) + +static int +ble_sm_test_util_store_read(int obj_type, union ble_store_key *key, + union ble_store_value *val) +{ + ble_sm_test_store_obj_type = obj_type; + ble_sm_test_store_key = *key; + + return ble_hs_test_util_store_read(obj_type, key, val); +} + +static int +ble_sm_test_util_store_write(int obj_type, union ble_store_value *val) +{ + ble_sm_test_store_obj_type = obj_type; + ble_sm_test_store_value = *val; + + return ble_hs_test_util_store_write(obj_type, val); +} + +void +ble_sm_test_util_init(void) +{ + ble_hs_test_util_init(); + ble_hs_test_util_store_init(10, 10, 10); + ble_hs_cfg.store_read_cb = ble_sm_test_util_store_read; + ble_hs_cfg.store_write_cb = ble_sm_test_util_store_write; + + ble_sm_test_store_obj_type = -1; + ble_sm_test_gap_event_type = -1; + ble_sm_test_gap_status = -1; + ++ memset(&ble_sm_test_ioact, 0, sizeof ble_sm_test_ioact); + memset(&ble_sm_test_sec_state, 0xff, sizeof ble_sm_test_sec_state); +} + +static void +ble_sm_test_util_params_to_entity(struct ble_sm_test_params *params, + int initiator, + struct ble_sm_test_util_entity *out_entity) +{ + int sc; + + memset(out_entity, 0, sizeof *out_entity); + + sc = params->pair_req.authreq & BLE_SM_PAIR_AUTHREQ_SC && + params->pair_rsp.authreq & BLE_SM_PAIR_AUTHREQ_SC; + + if (initiator) { + out_entity->key_dist = params->pair_rsp.init_key_dist; + + out_entity->addr_type = params->init_addr_type; + out_entity->id_addr = params->init_id_addr; + out_entity->rpa = params->init_rpa; + + out_entity->pair_cmd = ¶ms->pair_req; + out_entity->confirms = params->confirm_req; + out_entity->randoms = params->random_req; + out_entity->id_info = ¶ms->id_info_rsp; + out_entity->id_addr_info = ¶ms->id_addr_info_rsp; + out_entity->sign_info = ¶ms->sign_info_rsp; + + if (sc) { + out_entity->ltk = params->ltk; + out_entity->public_key = ¶ms->public_key_req; + out_entity->dhkey_check = ¶ms->dhkey_check_req; + } else { + out_entity->enc_info = ¶ms->enc_info_rsp; + out_entity->master_id = ¶ms->master_id_rsp; + if (out_entity->key_dist & BLE_SM_PAIR_KEY_DIST_ENC) { + out_entity->rand_num = params->master_id_rsp.rand_val; + out_entity->ediv = params->master_id_rsp.ediv; + out_entity->ltk = params->enc_info_rsp.ltk; + } + } + } else { + out_entity->key_dist = params->pair_rsp.resp_key_dist; + + out_entity->addr_type = params->resp_addr_type; + out_entity->id_addr = params->resp_id_addr; + out_entity->rpa = params->resp_rpa; + + out_entity->pair_cmd = ¶ms->pair_rsp; + out_entity->confirms = params->confirm_rsp; + out_entity->randoms = params->random_rsp; + out_entity->id_info = ¶ms->id_info_req; + out_entity->id_addr_info = ¶ms->id_addr_info_req; + out_entity->sign_info = ¶ms->sign_info_req; + + if (sc) { + out_entity->ltk = params->ltk; + out_entity->public_key = ¶ms->public_key_rsp; + out_entity->dhkey_check = ¶ms->dhkey_check_rsp; + } else { + out_entity->enc_info = ¶ms->enc_info_req; + out_entity->master_id = ¶ms->master_id_req; + if (out_entity->key_dist & BLE_SM_PAIR_KEY_DIST_ENC) { + out_entity->rand_num = params->master_id_req.rand_val; + out_entity->ediv = params->master_id_req.ediv; + out_entity->ltk = params->enc_info_req.ltk; + } + } + } + + out_entity->id_addr_type = + ble_hs_misc_addr_type_to_id(out_entity->addr_type); +} + +static void +ble_sm_test_util_params_to_entities(struct ble_sm_test_params *params, + int we_are_initiator, + struct ble_sm_test_util_entity *out_us, + struct ble_sm_test_util_entity *out_peer) +{ + ble_sm_test_util_params_to_entity(params, we_are_initiator, out_us); + ble_sm_test_util_params_to_entity(params, !we_are_initiator, out_peer); +} + +static void +ble_sm_test_util_init_good(struct ble_sm_test_params *params, + int we_are_initiator, + struct ble_hs_conn **out_conn, + struct ble_sm_test_util_entity *out_us, + struct ble_sm_test_util_entity *out_peer) +{ + struct ble_hs_conn *conn; + + ble_sm_test_util_init(); + + ble_sm_test_util_params_to_entities(params, we_are_initiator, + out_us, out_peer); + + ble_hs_cfg.sm_io_cap = out_us->pair_cmd->io_cap; + ble_hs_cfg.sm_oob_data_flag = out_us->pair_cmd->oob_data_flag; + ble_hs_cfg.sm_bonding = !!(out_us->pair_cmd->authreq & + BLE_SM_PAIR_AUTHREQ_BOND); + ble_hs_cfg.sm_mitm = !!(out_us->pair_cmd->authreq & + BLE_SM_PAIR_AUTHREQ_MITM); + ble_hs_cfg.sm_sc = !!(out_us->pair_cmd->authreq & + BLE_SM_PAIR_AUTHREQ_SC); + ble_hs_cfg.sm_keypress = !!(out_us->pair_cmd->authreq & + BLE_SM_PAIR_AUTHREQ_KEYPRESS); + + if (we_are_initiator) { + ble_hs_cfg.sm_our_key_dist = out_us->pair_cmd->init_key_dist; + ble_hs_cfg.sm_their_key_dist = out_us->pair_cmd->resp_key_dist; + } else { + ble_hs_cfg.sm_our_key_dist = out_us->pair_cmd->resp_key_dist; + ble_hs_cfg.sm_their_key_dist = out_us->pair_cmd->init_key_dist; + } + + ble_hs_id_set_pub(out_us->id_addr); + ble_sm_dbg_set_next_pair_rand(out_us->randoms[0].value); + ble_sm_dbg_set_next_ediv(out_us->ediv); + ble_sm_dbg_set_next_master_id_rand(out_us->rand_num); + ble_sm_dbg_set_next_ltk(out_us->ltk); + ble_hs_test_util_set_our_irk(out_us->id_info->irk, 0, 0); + ble_sm_dbg_set_next_csrk(out_us->sign_info->sig_key); + + if (out_us->public_key != NULL) { + ble_sm_dbg_set_sc_keys(out_us->public_key->x, params->our_priv_key); + } + + ble_hs_test_util_create_rpa_conn(2, out_us->addr_type, out_us->rpa, + out_peer->addr_type, + out_peer->id_addr, out_peer->rpa, + ble_sm_test_util_conn_cb, + NULL); + + /* This test code and modifies the connection object after unlocking + * the host mutex. It is not OK for real code to do this, but this test + * can assume the connection list is unchanging. + */ + ble_hs_lock(); + conn = ble_hs_conn_find(2); + TEST_ASSERT_FATAL(conn != NULL); + ble_hs_unlock(); + + if (!we_are_initiator) { + /* Peer is the initiator so we must be the slave. */ + conn->bhc_flags &= ~BLE_HS_CONN_F_MASTER; + } + + if (out_conn != NULL) { + *out_conn = conn; + } +} + - struct ble_gap_passkey_params ble_sm_test_ioact; - +int +ble_sm_test_util_conn_cb(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_ENC_CHANGE: + ble_sm_test_gap_status = event->enc_change.status; + + rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); + TEST_ASSERT_FATAL(rc == 0); + ble_sm_test_sec_state = desc.sec_state; - rc = 0; + break; + + case BLE_GAP_EVENT_PASSKEY_ACTION: + ble_sm_test_ioact = event->passkey.params; + break; + + default: + return 0; + } + + ble_sm_test_gap_event_type = event->type; + - return rc; ++ return 0; +} + +static void +ble_sm_test_util_rx_pair_cmd(uint16_t conn_handle, uint8_t op, + struct ble_sm_pair_cmd *cmd, + int rx_status) +{ + struct hci_data_hdr hci_hdr; + struct os_mbuf *om; + void *v; + int payload_len; + int rc; + + hci_hdr = BLE_SM_TEST_UTIL_HCI_HDR( + 2, BLE_HCI_PB_FIRST_FLUSH, + BLE_L2CAP_HDR_SZ + BLE_SM_HDR_SZ + BLE_SM_PAIR_CMD_SZ); + + om = ble_hs_mbuf_l2cap_pkt(); + TEST_ASSERT_FATAL(om != NULL); + + payload_len = BLE_SM_HDR_SZ + BLE_SM_PAIR_CMD_SZ; + + v = os_mbuf_extend(om, payload_len); + TEST_ASSERT_FATAL(v != NULL); + + ble_sm_pair_cmd_write(v, payload_len, op == BLE_SM_OP_PAIR_REQ, + cmd); + + rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SM, + &hci_hdr, om); + TEST_ASSERT(rc == rx_status); +} + +static void +ble_sm_test_util_rx_pair_req(uint16_t conn_handle, + struct ble_sm_pair_cmd *req, + int rx_status) +{ + ble_sm_test_util_rx_pair_cmd(conn_handle, BLE_SM_OP_PAIR_REQ, + req, rx_status); +} + +static void +ble_sm_test_util_rx_pair_rsp(uint16_t conn_handle, struct ble_sm_pair_cmd *rsp, + int rx_status) +{ + ble_sm_test_util_rx_pair_cmd(conn_handle, BLE_SM_OP_PAIR_RSP, + rsp, rx_status); +} + +static void +ble_sm_test_util_rx_confirm(uint16_t conn_handle, + struct ble_sm_pair_confirm *cmd) +{ + struct hci_data_hdr hci_hdr; + struct os_mbuf *om; + void *v; + int payload_len; + int rc; + + hci_hdr = BLE_SM_TEST_UTIL_HCI_HDR( + 2, BLE_HCI_PB_FIRST_FLUSH, + BLE_L2CAP_HDR_SZ + BLE_SM_HDR_SZ + BLE_SM_PAIR_CONFIRM_SZ); + + om = ble_hs_mbuf_l2cap_pkt(); + TEST_ASSERT_FATAL(om != NULL); + + payload_len = BLE_SM_HDR_SZ + BLE_SM_PAIR_CONFIRM_SZ; + + v = os_mbuf_extend(om, payload_len); + TEST_ASSERT_FATAL(v != NULL); + + ble_sm_pair_confirm_write(v, payload_len, cmd); + + rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SM, + &hci_hdr, om); + TEST_ASSERT_FATAL(rc == 0); +} + +static void +ble_sm_test_util_rx_random(uint16_t conn_handle, + struct ble_sm_pair_random *cmd, + int exp_status) +{ + struct hci_data_hdr hci_hdr; + struct os_mbuf *om; + void *v; + int payload_len; + int rc; + + hci_hdr = BLE_SM_TEST_UTIL_HCI_HDR( + 2, BLE_HCI_PB_FIRST_FLUSH, + BLE_L2CAP_HDR_SZ + BLE_SM_HDR_SZ + BLE_SM_PAIR_RANDOM_SZ); + + om = ble_hs_mbuf_l2cap_pkt(); + TEST_ASSERT_FATAL(om != NULL); + + payload_len = BLE_SM_HDR_SZ + BLE_SM_PAIR_RANDOM_SZ; + + v = os_mbuf_extend(om, payload_len); + TEST_ASSERT_FATAL(v != NULL); + + ble_sm_pair_random_write(v, payload_len, cmd); + + rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SM, + &hci_hdr, om); + TEST_ASSERT_FATAL(rc == exp_status); +} + +void +ble_sm_test_util_rx_sec_req(uint16_t conn_handle, struct ble_sm_sec_req *cmd, + int exp_status) +{ + struct hci_data_hdr hci_hdr; + struct os_mbuf *om; + void *v; + int payload_len; + int rc; + + hci_hdr = BLE_SM_TEST_UTIL_HCI_HDR( + 2, BLE_HCI_PB_FIRST_FLUSH, + BLE_L2CAP_HDR_SZ + BLE_SM_HDR_SZ + BLE_SM_SEC_REQ_SZ); + + om = ble_hs_mbuf_l2cap_pkt(); + TEST_ASSERT_FATAL(om != NULL); + + payload_len = BLE_SM_HDR_SZ + BLE_SM_SEC_REQ_SZ; + + v = os_mbuf_extend(om, payload_len); + TEST_ASSERT_FATAL(v != NULL); + + ble_sm_sec_req_write(v, payload_len, cmd); + + rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SM, + &hci_hdr, om); + TEST_ASSERT_FATAL(rc == exp_status); +} + +static void +ble_sm_test_util_rx_public_key(uint16_t conn_handle, + struct ble_sm_public_key *cmd) +{ + struct hci_data_hdr hci_hdr; + struct os_mbuf *om; + void *v; + int payload_len; + int rc; + + hci_hdr = BLE_SM_TEST_UTIL_HCI_HDR( + 2, BLE_HCI_PB_FIRST_FLUSH, + BLE_L2CAP_HDR_SZ + BLE_SM_HDR_SZ + BLE_SM_PUBLIC_KEY_SZ); + + om = ble_hs_mbuf_l2cap_pkt(); + TEST_ASSERT_FATAL(om != NULL); + + payload_len = BLE_SM_HDR_SZ + BLE_SM_PUBLIC_KEY_SZ; + + v = os_mbuf_extend(om, payload_len); + TEST_ASSERT_FATAL(v != NULL); + + ble_sm_public_key_write(v, payload_len, cmd); + + rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SM, + &hci_hdr, om); + TEST_ASSERT_FATAL(rc == 0); +} + +static void +ble_sm_test_util_rx_dhkey_check(uint16_t conn_handle, + struct ble_sm_dhkey_check *cmd, + int exp_status) +{ + struct hci_data_hdr hci_hdr; + struct os_mbuf *om; + void *v; + int payload_len; + int rc; + + hci_hdr = BLE_SM_TEST_UTIL_HCI_HDR( + 2, BLE_HCI_PB_FIRST_FLUSH, + BLE_L2CAP_HDR_SZ + BLE_SM_HDR_SZ + BLE_SM_DHKEY_CHECK_SZ); + + om = ble_hs_mbuf_l2cap_pkt(); + TEST_ASSERT_FATAL(om != NULL); + + payload_len = BLE_SM_HDR_SZ + BLE_SM_DHKEY_CHECK_SZ; + + v = os_mbuf_extend(om, payload_len); + TEST_ASSERT_FATAL(v != NULL); + + ble_sm_dhkey_check_write(v, payload_len, cmd); + + rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SM, + &hci_hdr, om); + TEST_ASSERT_FATAL(rc == exp_status); +} + +static void +ble_sm_test_util_rx_enc_info(uint16_t conn_handle, + struct ble_sm_enc_info *cmd, + int exp_status) +{ + struct hci_data_hdr hci_hdr; + struct os_mbuf *om; + void *v; + int payload_len; + int rc; + + hci_hdr = BLE_SM_TEST_UTIL_HCI_HDR( + 2, BLE_HCI_PB_FIRST_FLUSH, + BLE_L2CAP_HDR_SZ + BLE_SM_HDR_SZ + BLE_SM_ENC_INFO_SZ); + + om = ble_hs_mbuf_l2cap_pkt(); + TEST_ASSERT_FATAL(om != NULL); + + payload_len = BLE_SM_HDR_SZ + BLE_SM_ENC_INFO_SZ; + + v = os_mbuf_extend(om, payload_len); + TEST_ASSERT_FATAL(v != NULL); + + ble_sm_enc_info_write(v, payload_len, cmd); + + rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SM, + &hci_hdr, om); + TEST_ASSERT_FATAL(rc == exp_status); +} + +static void +ble_sm_test_util_rx_master_id(uint16_t conn_handle, + struct ble_sm_master_id *cmd, + int exp_status) +{ + struct hci_data_hdr hci_hdr; + struct os_mbuf *om; + void *v; + int payload_len; + int rc; + + hci_hdr = BLE_SM_TEST_UTIL_HCI_HDR( + 2, BLE_HCI_PB_FIRST_FLUSH, + BLE_L2CAP_HDR_SZ + BLE_SM_HDR_SZ + BLE_SM_MASTER_ID_SZ); + + om = ble_hs_mbuf_l2cap_pkt(); + TEST_ASSERT_FATAL(om != NULL); + + payload_len = BLE_SM_HDR_SZ + BLE_SM_MASTER_ID_SZ; + + v = os_mbuf_extend(om, payload_len); + TEST_ASSERT_FATAL(v != NULL); + + ble_sm_master_id_write(v, payload_len, cmd); + + rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SM, + &hci_hdr, om); + TEST_ASSERT_FATAL(rc == exp_status); +} + +static void +ble_sm_test_util_rx_id_info(uint16_t conn_handle, + struct ble_sm_id_info *cmd, + int exp_status) +{ + struct hci_data_hdr hci_hdr; + struct os_mbuf *om; + void *v; + int payload_len; + int rc; + + hci_hdr = BLE_SM_TEST_UTIL_HCI_HDR( + 2, BLE_HCI_PB_FIRST_FLUSH, + BLE_L2CAP_HDR_SZ + BLE_SM_HDR_SZ + BLE_SM_ID_INFO_SZ); + + om = ble_hs_mbuf_l2cap_pkt(); + TEST_ASSERT_FATAL(om != NULL); + + payload_len = BLE_SM_HDR_SZ + BLE_SM_ID_INFO_SZ; + + v = os_mbuf_extend(om, payload_len); + TEST_ASSERT_FATAL(v != NULL); + + ble_sm_id_info_write(v, payload_len, cmd); + + rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SM, + &hci_hdr, om); + TEST_ASSERT_FATAL(rc == exp_status); +} + +static void +ble_sm_test_util_rx_id_addr_info(uint16_t conn_handle, + struct ble_sm_id_addr_info *cmd, + int exp_status) +{ + struct hci_data_hdr hci_hdr; + struct os_mbuf *om; + void *v; + int payload_len; + int rc; + + hci_hdr = BLE_SM_TEST_UTIL_HCI_HDR( + 2, BLE_HCI_PB_FIRST_FLUSH, + BLE_L2CAP_HDR_SZ + BLE_SM_HDR_SZ + BLE_SM_ID_ADDR_INFO_SZ); + + om = ble_hs_mbuf_l2cap_pkt(); + TEST_ASSERT_FATAL(om != NULL); + + payload_len = BLE_SM_HDR_SZ + BLE_SM_ID_ADDR_INFO_SZ; + + v = os_mbuf_extend(om, payload_len); + TEST_ASSERT_FATAL(v != NULL); + + ble_sm_id_addr_info_write(v, payload_len, cmd); + + rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SM, + &hci_hdr, om); + TEST_ASSERT_FATAL(rc == exp_status); +} + +static void +ble_sm_test_util_rx_sign_info(uint16_t conn_handle, + struct ble_sm_sign_info *cmd, + int exp_status) +{ + struct hci_data_hdr hci_hdr; + struct os_mbuf *om; + void *v; + int payload_len; + int rc; + + hci_hdr = BLE_SM_TEST_UTIL_HCI_HDR( + 2, BLE_HCI_PB_FIRST_FLUSH, + BLE_L2CAP_HDR_SZ + BLE_SM_HDR_SZ + BLE_SM_SIGN_INFO_SZ); + + om = ble_hs_mbuf_l2cap_pkt(); + TEST_ASSERT_FATAL(om != NULL); + + payload_len = BLE_SM_HDR_SZ + BLE_SM_SIGN_INFO_SZ; + + v = os_mbuf_extend(om, payload_len); + TEST_ASSERT_FATAL(v != NULL); + + ble_sm_sign_info_write(v, payload_len, cmd); + + rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SM, + &hci_hdr, om); + TEST_ASSERT_FATAL(rc == exp_status); +} + +static struct os_mbuf * +ble_sm_test_util_verify_tx_hdr(uint8_t sm_op, uint16_t payload_len) +{ + struct os_mbuf *om; + + om = ble_hs_test_util_prev_tx_dequeue_pullup(); + TEST_ASSERT_FATAL(om != NULL); + + TEST_ASSERT(OS_MBUF_PKTLEN(om) == BLE_SM_HDR_SZ + payload_len); + TEST_ASSERT_FATAL(om->om_data[0] == sm_op); + + om->om_data += BLE_SM_HDR_SZ; + om->om_len -= BLE_SM_HDR_SZ; + + return om; +} + +static void +ble_sm_test_util_verify_tx_pair_cmd( + uint8_t op, + struct ble_sm_pair_cmd *exp_cmd) +{ + struct ble_sm_pair_cmd cmd; + struct os_mbuf *om; + + om = ble_sm_test_util_verify_tx_hdr(op, BLE_SM_PAIR_CMD_SZ); + ble_sm_pair_cmd_parse(om->om_data, om->om_len, &cmd); + + TEST_ASSERT(cmd.io_cap == exp_cmd->io_cap); + TEST_ASSERT(cmd.oob_data_flag == exp_cmd->oob_data_flag); + TEST_ASSERT(cmd.authreq == exp_cmd->authreq); + TEST_ASSERT(cmd.max_enc_key_size == exp_cmd->max_enc_key_size); + TEST_ASSERT(cmd.init_key_dist == exp_cmd->init_key_dist); + TEST_ASSERT(cmd.resp_key_dist == exp_cmd->resp_key_dist); +} + +static void +ble_sm_test_util_verify_tx_pair_req( + struct ble_sm_pair_cmd *exp_req) +{ + ble_sm_test_util_verify_tx_pair_cmd(BLE_SM_OP_PAIR_REQ, + exp_req); +} + +static void +ble_sm_test_util_verify_tx_pair_rsp( + struct ble_sm_pair_cmd *exp_rsp) +{ + ble_sm_test_util_verify_tx_pair_cmd(BLE_SM_OP_PAIR_RSP, + exp_rsp); +} + +static void +ble_sm_test_util_verify_tx_pair_confirm( + struct ble_sm_pair_confirm *exp_cmd) +{ + struct ble_sm_pair_confirm cmd; + struct os_mbuf *om; + + om = ble_sm_test_util_verify_tx_hdr(BLE_SM_OP_PAIR_CONFIRM, + BLE_SM_PAIR_CONFIRM_SZ); + ble_sm_pair_confirm_parse(om->om_data, om->om_len, &cmd); + + TEST_ASSERT(memcmp(cmd.value, exp_cmd->value, 16) == 0); +} + +static void +ble_sm_test_util_verify_tx_pair_random( + struct ble_sm_pair_random *exp_cmd) +{ + struct ble_sm_pair_random cmd; + struct os_mbuf *om; + + om = ble_sm_test_util_verify_tx_hdr(BLE_SM_OP_PAIR_RANDOM, + BLE_SM_PAIR_RANDOM_SZ); + ble_sm_pair_random_parse(om->om_data, om->om_len, &cmd); + + TEST_ASSERT(memcmp(cmd.value, exp_cmd->value, 16) == 0); +} + +static void +ble_sm_test_util_verify_tx_public_key( + struct ble_sm_public_key *exp_cmd) +{ + struct ble_sm_public_key cmd; + struct os_mbuf *om; + + ble_hs_test_util_tx_all(); + + om = ble_sm_test_util_verify_tx_hdr(BLE_SM_OP_PAIR_PUBLIC_KEY, + BLE_SM_PUBLIC_KEY_SZ); + ble_sm_public_key_parse(om->om_data, om->om_len, &cmd); + + TEST_ASSERT(memcmp(cmd.x, exp_cmd->x, sizeof cmd.x) == 0); + TEST_ASSERT(memcmp(cmd.y, exp_cmd->y, sizeof cmd.y) == 0); +} + +static void +ble_sm_test_util_verify_tx_dhkey_check( + struct ble_sm_dhkey_check *exp_cmd) +{ + struct ble_sm_dhkey_check cmd; + struct os_mbuf *om; + + om = ble_sm_test_util_verify_tx_hdr(BLE_SM_OP_PAIR_DHKEY_CHECK, + BLE_SM_DHKEY_CHECK_SZ); + ble_sm_dhkey_check_parse(om->om_data, om->om_len, &cmd); + + TEST_ASSERT(memcmp(cmd.value, exp_cmd->value, 16) == 0); +} + +static void +ble_sm_test_util_verify_tx_enc_info(struct ble_sm_enc_info *exp_cmd) +{ + struct ble_sm_enc_info cmd; + struct os_mbuf *om; + + ble_hs_test_util_tx_all(); + om = ble_sm_test_util_verify_tx_hdr(BLE_SM_OP_ENC_INFO, + BLE_SM_ENC_INFO_SZ); + ble_sm_enc_info_parse(om->om_data, om->om_len, &cmd); + + TEST_ASSERT(memcmp(cmd.ltk, exp_cmd->ltk, 16) == 0); + + /* Ensure LTK is sent in little endian. */ + TEST_ASSERT(memcmp(om->om_data, cmd.ltk, 16) == 0); +} + +static void +ble_sm_test_util_verify_tx_master_id(struct ble_sm_master_id *exp_cmd) +{ + struct ble_sm_master_id cmd; + struct os_mbuf *om; + + ble_hs_test_util_tx_all(); + om = ble_sm_test_util_verify_tx_hdr(BLE_SM_OP_MASTER_ID, + BLE_SM_MASTER_ID_SZ); + ble_sm_master_id_parse(om->om_data, om->om_len, &cmd); + + TEST_ASSERT(cmd.ediv == exp_cmd->ediv); + TEST_ASSERT(cmd.rand_val == exp_cmd->rand_val); +} + +static void +ble_sm_test_util_verify_tx_id_info(struct ble_sm_id_info *exp_cmd) +{ + struct ble_sm_id_info cmd; + struct os_mbuf *om; + + ble_hs_test_util_tx_all(); + om = ble_sm_test_util_verify_tx_hdr(BLE_SM_OP_IDENTITY_INFO, + BLE_SM_ID_INFO_SZ); + ble_sm_id_info_parse(om->om_data, om->om_len, &cmd); + + TEST_ASSERT(memcmp(cmd.irk, exp_cmd->irk, 16) == 0); + + /* Ensure IRK is sent in little endian. */ + TEST_ASSERT(memcmp(om->om_data, cmd.irk, 16) == 0); +} + +static void +ble_sm_test_util_verify_tx_id_addr_info(struct ble_sm_id_addr_info *exp_cmd) +{ + struct ble_sm_id_addr_info cmd; + struct os_mbuf *om; + const uint8_t *our_id_addr; + int rc; + + ble_hs_lock(); + rc = ble_hs_id_addr(exp_cmd->addr_type, &our_id_addr, NULL); + ble_hs_unlock(); + + TEST_ASSERT_FATAL(rc == 0); + + ble_hs_test_util_tx_all(); + om = ble_sm_test_util_verify_tx_hdr(BLE_SM_OP_IDENTITY_ADDR_INFO, + BLE_SM_ID_ADDR_INFO_SZ); + ble_sm_id_addr_info_parse(om->om_data, om->om_len, &cmd); + + TEST_ASSERT(cmd.addr_type == exp_cmd->addr_type); + TEST_ASSERT(memcmp(cmd.bd_addr, exp_cmd->bd_addr, 6) == 0); + TEST_ASSERT(memcmp(cmd.bd_addr, our_id_addr, 6) == 0); +} + +static void +ble_sm_test_util_verify_tx_sign_info(struct ble_sm_sign_info *exp_cmd) +{ + struct ble_sm_sign_info cmd; + struct os_mbuf *om; + + ble_hs_test_util_tx_all(); + om = ble_sm_test_util_verify_tx_hdr(BLE_SM_OP_SIGN_INFO, + BLE_SM_ID_INFO_SZ); + ble_sm_sign_info_parse(om->om_data, om->om_len, &cmd); + + TEST_ASSERT(memcmp(cmd.sig_key, exp_cmd->sig_key, 16) == 0); + + /* Ensure CSRK is sent in little endian. */ + TEST_ASSERT(memcmp(om->om_data, cmd.sig_key, 16) == 0); +} + +static void +ble_sm_test_util_verify_tx_sec_req(struct ble_sm_sec_req *exp_cmd) +{ + struct ble_sm_sec_req cmd; + struct os_mbuf *om; + + ble_hs_test_util_tx_all(); + + om = ble_sm_test_util_verify_tx_hdr(BLE_SM_OP_SEC_REQ, BLE_SM_SEC_REQ_SZ); + ble_sm_sec_req_parse(om->om_data, om->om_len, &cmd); + + TEST_ASSERT(cmd.authreq == exp_cmd->authreq); +} + +void +ble_sm_test_util_verify_tx_pair_fail( + struct ble_sm_pair_fail *exp_cmd) +{ + struct ble_sm_pair_fail cmd; + struct os_mbuf *om; + + om = ble_sm_test_util_verify_tx_hdr(BLE_SM_OP_PAIR_FAIL, + BLE_SM_PAIR_FAIL_SZ); + ble_sm_pair_fail_parse(om->om_data, om->om_len, &cmd); + + TEST_ASSERT(cmd.reason == exp_cmd->reason); +} + +static void +ble_sm_test_util_rx_lt_key_req(uint16_t conn_handle, uint64_t r, uint16_t ediv) +{ + struct hci_le_lt_key_req evt; + int rc; + + evt.subevent_code = BLE_HCI_LE_SUBEV_LT_KEY_REQ; + evt.connection_handle = conn_handle; + evt.random_number = r; + evt.encrypted_diversifier = ediv; + + rc = ble_sm_ltk_req_rx(&evt); + TEST_ASSERT_FATAL(rc == 0); +} + +static void +ble_sm_test_util_verify_tx_lt_key_req_reply(uint16_t conn_handle, uint8_t *stk) +{ + uint8_t param_len; + uint8_t *param; + + param = ble_hs_test_util_verify_tx_hci(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_LT_KEY_REQ_REPLY, + ¶m_len); + TEST_ASSERT(param_len == BLE_HCI_LT_KEY_REQ_REPLY_LEN); + TEST_ASSERT(le16toh(param + 0) == conn_handle); + TEST_ASSERT(memcmp(param + 2, stk, 16) == 0); +} + +static void +ble_sm_test_util_verify_tx_lt_key_req_neg_reply(uint16_t conn_handle) +{ + uint8_t param_len; + uint8_t *param; + + param = ble_hs_test_util_verify_tx_hci(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_LT_KEY_REQ_NEG_REPLY, + ¶m_len); + TEST_ASSERT(param_len == BLE_HCI_LT_KEY_REQ_NEG_REPLY_LEN); + TEST_ASSERT(le16toh(param + 0) == conn_handle); +} + +static void +ble_sm_test_util_set_lt_key_req_neg_reply_ack(uint8_t status, + uint16_t conn_handle) +{ + static uint8_t params[BLE_HCI_LT_KEY_REQ_NEG_REPLY_ACK_PARAM_LEN]; + + htole16(params, conn_handle); + ble_hs_test_util_set_ack_params( + ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_LT_KEY_REQ_NEG_REPLY), + status, params, sizeof params); +} + +static void +ble_sm_test_util_set_lt_key_req_reply_ack(uint8_t status, uint16_t conn_handle) +{ + static uint8_t params[BLE_HCI_LT_KEY_REQ_REPLY_ACK_PARAM_LEN]; + + htole16(params, conn_handle); + ble_hs_test_util_set_ack_params( + ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_LT_KEY_REQ_REPLY), + status, params, sizeof params); +} + +static void +ble_sm_test_util_rx_enc_change(uint16_t conn_handle, uint8_t status, + uint8_t encryption_enabled) +{ + struct hci_encrypt_change evt; + + evt.status = status; + evt.encryption_enabled = encryption_enabled; + evt.connection_handle = conn_handle; + + ble_sm_enc_change_rx(&evt); +} + +static void +ble_sm_test_util_verify_tx_start_enc(uint16_t conn_handle, + uint64_t random_number, + uint16_t ediv, + uint8_t *ltk) +{ + uint8_t param_len; + uint8_t *param; + + param = ble_hs_test_util_verify_tx_hci(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_START_ENCRYPT, + ¶m_len); + TEST_ASSERT(param_len == BLE_HCI_LE_START_ENCRYPT_LEN); + TEST_ASSERT(le16toh(param + 0) == conn_handle); + TEST_ASSERT(le64toh(param + 2) == random_number); + TEST_ASSERT(le16toh(param + 10) == ediv); + TEST_ASSERT(memcmp(param + 12, ltk, 16) == 0); +} + +static void +ble_sm_test_util_verify_tx_add_resolve_list(uint8_t peer_id_addr_type, + uint8_t *peer_id_addr, + uint8_t *peer_irk, + uint8_t *our_irk) +{ + uint8_t param_len; + uint8_t *param; + + param = ble_hs_test_util_verify_tx_hci(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_ADD_RESOLV_LIST, + ¶m_len); + TEST_ASSERT(param_len == BLE_HCI_ADD_TO_RESOLV_LIST_LEN); + TEST_ASSERT(param[0] == peer_id_addr_type); + TEST_ASSERT(memcmp(param + 1, peer_id_addr, 6) == 0); + + /* Ensure IRKs are sent in little endian. */ + TEST_ASSERT(memcmp(param + 7, peer_irk, 16) == 0); + TEST_ASSERT(memcmp(param + 23, our_irk, 16) == 0); +} + +void +ble_sm_test_util_io_inject(struct ble_sm_test_passkey_info *passkey_info, + uint8_t cur_sm_state) +{ + uint8_t io_sm_state; + int rc; + + io_sm_state = ble_sm_ioact_state(passkey_info->passkey.action); + if (io_sm_state != cur_sm_state) { ++ TEST_ASSERT(ble_sm_test_ioact.action == BLE_SM_IOACT_NONE); + return; + } + ++ TEST_ASSERT(ble_sm_test_ioact.action == passkey_info->passkey.action); ++ + if (passkey_info->passkey.action == BLE_SM_IOACT_NUMCMP) { + TEST_ASSERT(ble_sm_test_ioact.numcmp == passkey_info->exp_numcmp); + } + + rc = ble_sm_inject_io(2, &passkey_info->passkey); + TEST_ASSERT(rc == 0); ++ ++ ble_sm_test_ioact.action = BLE_SM_IOACT_NONE; +} + +void +ble_sm_test_util_io_inject_bad(uint16_t conn_handle, uint8_t correct_io_act) +{ + struct ble_sm_proc *proc; + struct ble_sm_io io; + uint8_t io_sm_state; + int already_injected; + int rc; + int i; + + /* Lock mutex to prevent thread-safety assert from failing. */ + ble_hs_lock(); + proc = ble_sm_proc_find(conn_handle, BLE_SM_PROC_STATE_NONE, -1, NULL); + ble_hs_unlock(); + + TEST_ASSERT_FATAL(proc != NULL); + + io_sm_state = ble_sm_ioact_state(correct_io_act); + + for (i = 1; i < BLE_SM_IOACT_MAX_PLUS_ONE; i++) { + if (io_sm_state != proc->state || + i != correct_io_act || + proc->flags & BLE_SM_PROC_F_IO_INJECTED) { + + already_injected = proc->flags & BLE_SM_PROC_F_IO_INJECTED; + + io.action = i; + rc = ble_sm_inject_io(conn_handle, &io); + + if (already_injected) { + TEST_ASSERT(rc == BLE_HS_EALREADY); + } else { + TEST_ASSERT(rc == BLE_HS_EINVAL); + } + } + } +} + +void +ble_sm_test_util_io_check_pre(struct ble_sm_test_passkey_info *passkey_info, + uint8_t cur_sm_state) +{ + uint8_t io_sm_state; + int rc; + + io_sm_state = ble_sm_ioact_state(passkey_info->passkey.action); + if (io_sm_state != cur_sm_state) { + return; + } + + if (!passkey_info->io_before_rx) { + return; + } + + if (passkey_info->passkey.action == BLE_SM_IOACT_NUMCMP) { + TEST_ASSERT(ble_sm_test_ioact.numcmp == passkey_info->exp_numcmp); + } + + rc = ble_sm_inject_io(2, &passkey_info->passkey); + TEST_ASSERT(rc == 0); +} + +void +ble_sm_test_util_io_check_post(struct ble_sm_test_passkey_info *passkey_info, + uint8_t cur_sm_state) +{ + uint8_t io_sm_state; + int rc; + + io_sm_state = ble_sm_ioact_state(passkey_info->passkey.action); + if (io_sm_state != cur_sm_state) { + return; + } + + if (passkey_info->io_before_rx) { + return; + } + + if (passkey_info->passkey.action == BLE_SM_IOACT_NUMCMP) { + TEST_ASSERT(ble_sm_test_ioact.numcmp == passkey_info->exp_numcmp); + } + + /* Ensure response not sent until user performs IO. */ + ble_hs_test_util_tx_all(); + TEST_ASSERT(ble_hs_test_util_prev_tx_queue_sz() == 0); + + rc = ble_sm_inject_io(2, &passkey_info->passkey); + TEST_ASSERT_FATAL(rc == 0); +} + +static void +ble_sm_test_util_verify_persist(struct ble_sm_test_params *params, + int we_are_initiator) +{ + struct ble_sm_test_util_entity peer_entity; + struct ble_sm_test_util_entity our_entity; + struct ble_store_value_sec value_sec; + struct ble_store_key_sec key_sec; + int csrk_expected; + int ltk_expected; + int peer_irk_expected; + int our_irk_expected; + int bonding; + int sc; + int rc; + + ble_sm_test_util_params_to_entities(params, we_are_initiator, + &our_entity, &peer_entity); + + sc = params->pair_req.authreq & BLE_SM_PAIR_AUTHREQ_SC && + params->pair_rsp.authreq & BLE_SM_PAIR_AUTHREQ_SC; + + bonding = params->pair_req.authreq & BLE_SM_PAIR_AUTHREQ_BOND && + params->pair_rsp.authreq & BLE_SM_PAIR_AUTHREQ_BOND; + + memset(&key_sec, 0, sizeof key_sec); + key_sec.peer_addr_type = BLE_STORE_ADDR_TYPE_NONE; + + rc = ble_store_read_peer_sec(&key_sec, &value_sec); + if (!bonding) { + TEST_ASSERT(rc == BLE_HS_ENOENT); + peer_irk_expected = 0; + } else { + TEST_ASSERT_FATAL(rc == 0); + + ltk_expected = + sc || !!(peer_entity.key_dist & BLE_SM_PAIR_KEY_DIST_ENC); + peer_irk_expected = + !!(peer_entity.key_dist & BLE_SM_PAIR_KEY_DIST_ID); + csrk_expected = + !!(peer_entity.key_dist & BLE_SM_PAIR_KEY_DIST_SIGN); + + TEST_ASSERT(value_sec.peer_addr_type == peer_entity.id_addr_type); + TEST_ASSERT(memcmp(value_sec.peer_addr, peer_entity.id_addr, 6) == 0); + TEST_ASSERT(value_sec.ediv == peer_entity.ediv); + TEST_ASSERT(value_sec.rand_num == peer_entity.rand_num); + TEST_ASSERT(value_sec.authenticated == params->authenticated); + + TEST_ASSERT(value_sec.ltk_present == ltk_expected); + TEST_ASSERT(memcmp(value_sec.ltk, peer_entity.ltk, 16) == 0); + + TEST_ASSERT(value_sec.irk_present == peer_irk_expected); + if (peer_irk_expected) { + TEST_ASSERT(memcmp(value_sec.irk, + peer_entity.id_info->irk, 16) == 0); + } + + TEST_ASSERT(value_sec.csrk_present == csrk_expected); + if (csrk_expected) { + TEST_ASSERT(memcmp(value_sec.csrk, + peer_entity.sign_info->sig_key, 16) == 0); + } + } + + rc = ble_store_read_our_sec(&key_sec, &value_sec); + if (!bonding) { + TEST_ASSERT(rc == BLE_HS_ENOENT); + } else { + TEST_ASSERT_FATAL(rc == 0); + + ltk_expected = + sc || !!(our_entity.key_dist & BLE_SM_PAIR_KEY_DIST_ENC); + our_irk_expected = + !!(our_entity.key_dist & BLE_SM_PAIR_KEY_DIST_ID); + csrk_expected = + !!(our_entity.key_dist & BLE_SM_PAIR_KEY_DIST_SIGN); + + TEST_ASSERT(value_sec.peer_addr_type == peer_entity.id_addr_type); + TEST_ASSERT(memcmp(value_sec.peer_addr, peer_entity.id_addr, 6) == 0); + TEST_ASSERT(value_sec.ediv == our_entity.ediv); + TEST_ASSERT(value_sec.rand_num == our_entity.rand_num); + TEST_ASSERT(value_sec.authenticated == params->authenticated); + + TEST_ASSERT(value_sec.ltk_present == ltk_expected); + TEST_ASSERT(memcmp(value_sec.ltk, our_entity.ltk, 16) == 0); + + TEST_ASSERT(value_sec.irk_present == our_irk_expected); + if (our_irk_expected) { + TEST_ASSERT(memcmp(value_sec.irk, + our_entity.id_info->irk, 16) == 0); + } + + TEST_ASSERT(value_sec.csrk_present == csrk_expected); + if (csrk_expected) { + TEST_ASSERT(memcmp(value_sec.csrk, + our_entity.sign_info->sig_key, 16) == 0); + } + } + + /* Verify no other keys were persisted. */ + key_sec.idx++; + rc = ble_store_read_our_sec(&key_sec, &value_sec); + TEST_ASSERT_FATAL(rc == BLE_HS_ENOENT); + rc = ble_store_read_peer_sec(&key_sec, &value_sec); + TEST_ASSERT_FATAL(rc == BLE_HS_ENOENT); + + /* Verify we sent the peer's IRK to the controller. */ + if (peer_irk_expected) { + ble_sm_test_util_verify_tx_add_resolve_list(peer_entity.id_addr_type, + peer_entity.id_addr, + peer_entity.id_info->irk, + our_entity.id_info->irk); + } +} + +static void +ble_sm_test_util_peer_bonding_good(int send_enc_req, + uint8_t our_addr_type, + uint8_t *our_rpa, + uint8_t peer_addr_type, + uint8_t *peer_id_addr, + uint8_t *peer_rpa, + uint8_t *ltk, int authenticated, + uint16_t ediv, uint64_t rand_num) +{ + struct ble_hs_conn *conn; + int rc; + + ble_hs_test_util_create_rpa_conn(2, our_addr_type, our_rpa, peer_addr_type, + peer_id_addr, peer_rpa, + ble_sm_test_util_conn_cb, NULL); + + /* This test inspects and modifies the connection object after unlocking + * the host mutex. It is not OK for real code to do this, but this test + * can assume the connection list is unchanging. + */ + ble_hs_lock(); + conn = ble_hs_conn_find(2); + TEST_ASSERT_FATAL(conn != NULL); + conn->bhc_flags &= ~BLE_HS_CONN_F_MASTER; + ble_hs_unlock(); + + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + if (send_enc_req) { + rc = ble_sm_slave_initiate(2); + TEST_ASSERT(rc == 0); + } + + /* Receive a long term key request from the controller. */ + ble_sm_test_util_set_lt_key_req_reply_ack(0, 2); + ble_sm_test_util_rx_lt_key_req(2, rand_num, ediv); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + + /* Ensure the LTK request event got sent to the application. */ + TEST_ASSERT(ble_sm_test_store_obj_type == + BLE_STORE_OBJ_TYPE_OUR_SEC); + TEST_ASSERT(ble_sm_test_store_key.sec.peer_addr_type == + ble_hs_misc_addr_type_to_id(peer_addr_type)); + TEST_ASSERT(ble_sm_test_store_key.sec.ediv_rand_present); + TEST_ASSERT(ble_sm_test_store_key.sec.ediv == ediv); + TEST_ASSERT(ble_sm_test_store_key.sec.rand_num == rand_num); + + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, BLE_SM_IOACT_NONE); + + /* Ensure we sent the expected long term key request reply command. */ + ble_sm_test_util_verify_tx_lt_key_req_reply(2, ltk); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, BLE_SM_IOACT_NONE); + + /* Receive an encryption changed event. */ + ble_sm_test_util_rx_enc_change(2, 0, 1); + + /* Pairing should now be complete. */ + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Verify that security callback was executed. */ + TEST_ASSERT(ble_sm_test_gap_event_type == BLE_GAP_EVENT_ENC_CHANGE); + TEST_ASSERT(ble_sm_test_gap_status == 0); + TEST_ASSERT(ble_sm_test_sec_state.encrypted); + TEST_ASSERT(ble_sm_test_sec_state.authenticated == + authenticated); + + /* Verify that connection has correct security state. */ + TEST_ASSERT(ble_sm_test_sec_state.encrypted); + TEST_ASSERT(ble_sm_test_sec_state.authenticated == + authenticated); + + ble_hs_test_util_conn_disconnect(2); +} + +void +ble_sm_test_util_peer_bonding_bad(uint16_t ediv, uint64_t rand_num) +{ + struct ble_hs_conn *conn; + + ble_sm_test_util_init(); + + ble_hs_test_util_create_conn(2, ((uint8_t[6]){1,2,3,4,5,6}), + ble_sm_test_util_conn_cb, + NULL); + + /* This test inspects and modifies the connection object after unlocking + * the host mutex. It is not OK for real code to do this, but this test + * can assume the connection list is unchanging. + */ + ble_hs_lock(); + conn = ble_hs_conn_find(2); + TEST_ASSERT_FATAL(conn != NULL); + conn->bhc_flags &= ~BLE_HS_CONN_F_MASTER; + ble_hs_unlock(); + + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Receive a long term key request from the controller. */ + ble_sm_test_util_set_lt_key_req_neg_reply_ack(0, 2); + ble_sm_test_util_rx_lt_key_req(2, rand_num, ediv); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + + /* Ensure the LTK request event got sent to the application. */ + TEST_ASSERT(ble_sm_test_store_obj_type == + BLE_STORE_OBJ_TYPE_OUR_SEC); + TEST_ASSERT(ble_sm_test_store_key.sec.ediv_rand_present); + TEST_ASSERT(ble_sm_test_store_key.sec.ediv == ediv); + TEST_ASSERT(ble_sm_test_store_key.sec.rand_num == rand_num); + + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + + /* Ensure we sent the expected long term key request neg reply command. */ + ble_sm_test_util_verify_tx_lt_key_req_neg_reply(2); + + /* Ensure the security procedure was aborted. */ + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(!conn->bhc_sec_state.authenticated); + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); +} + +/** + * @param send_enc_req Whether this procedure is initiated by a slave + * security request; + * 1: Peer sends a security request at start. + * 0: No security request; we initiate. + */ +static void +ble_sm_test_util_us_bonding_good(int send_enc_req, uint8_t our_addr_type, + uint8_t *our_rpa, + uint8_t peer_addr_type, + uint8_t *peer_id_addr, uint8_t *peer_rpa, + uint8_t *ltk, int authenticated, + uint16_t ediv, uint64_t rand_num) +{ + struct ble_sm_sec_req sec_req; + struct ble_hs_conn *conn; + + ble_hs_test_util_create_rpa_conn(2, our_addr_type, our_rpa, + peer_addr_type, peer_id_addr, + peer_rpa, ble_sm_test_util_conn_cb, NULL); + + /* This test inspects and modifies the connection object after unlocking + * the host mutex. It is not OK for real code to do this, but this test + * can assume the connection list is unchanging. + */ + ble_hs_lock(); + conn = ble_hs_conn_find(2); + TEST_ASSERT_FATAL(conn != NULL); + ble_hs_unlock(); + + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + ble_hs_test_util_set_ack( + ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_START_ENCRYPT), + 0); + + if (send_enc_req) { + sec_req.authreq = 0; + sec_req.authreq |= BLE_SM_PAIR_AUTHREQ_BOND; + if (authenticated) { + sec_req.authreq |= BLE_SM_PAIR_AUTHREQ_MITM; + } + ble_sm_test_util_rx_sec_req(2, &sec_req, 0); + } else { + ble_gap_security_initiate(2); + } + + /* Ensure we sent the expected start encryption command. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_start_enc(2, rand_num, ediv, ltk); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, BLE_SM_IOACT_NONE); + + /* Receive an encryption changed event. */ + ble_sm_test_util_rx_enc_change(2, 0, 1); + + /* Pairing should now be complete. */ + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Verify that security callback was executed. */ + TEST_ASSERT(ble_sm_test_gap_event_type == BLE_GAP_EVENT_ENC_CHANGE); + TEST_ASSERT(ble_sm_test_gap_status == 0); + TEST_ASSERT(ble_sm_test_sec_state.encrypted); + TEST_ASSERT(ble_sm_test_sec_state.authenticated == + authenticated); + + /* Verify that connection has correct security state. */ + TEST_ASSERT(ble_sm_test_sec_state.encrypted); + TEST_ASSERT(ble_sm_test_sec_state.authenticated == + authenticated); + + ble_hs_test_util_conn_disconnect(2); +} + +void +ble_sm_test_util_peer_fail_inval( + int we_are_master, + uint8_t *init_id_addr, + uint8_t *resp_addr, + struct ble_sm_pair_cmd *pair_req, + struct ble_sm_pair_fail *pair_fail) +{ + struct ble_hs_conn *conn; + + ble_sm_test_util_init(); + ble_hs_id_set_pub(resp_addr); + + ble_hs_test_util_create_conn(2, init_id_addr, ble_sm_test_util_conn_cb, + NULL); + + /* This test inspects and modifies the connection object after unlocking + * the host mutex. It is not OK for real code to do this, but this test + * can assume the connection list is unchanging. + */ + ble_hs_lock(); + conn = ble_hs_conn_find(2); + TEST_ASSERT_FATAL(conn != NULL); + ble_hs_unlock(); + + if (!we_are_master) { + conn->bhc_flags &= ~BLE_HS_CONN_F_MASTER; + } + + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Receive a pair request from the peer. */ + ble_sm_test_util_rx_pair_req(2, pair_req, + BLE_HS_SM_US_ERR(pair_fail->reason)); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Ensure we sent the expected pair fail. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_fail(pair_fail); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Verify that security callback was not executed. */ + TEST_ASSERT(ble_sm_test_gap_event_type == -1); + TEST_ASSERT(ble_sm_test_gap_status == -1); + + /* Verify that connection has correct security state. */ + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(!conn->bhc_sec_state.authenticated); +} + +void +ble_sm_test_util_peer_lgcy_fail_confirm( + uint8_t *init_id_addr, + uint8_t *resp_addr, + struct ble_sm_pair_cmd *pair_req, + struct ble_sm_pair_cmd *pair_rsp, + struct ble_sm_pair_confirm *confirm_req, + struct ble_sm_pair_confirm *confirm_rsp, + struct ble_sm_pair_random *random_req, + struct ble_sm_pair_random *random_rsp, + struct ble_sm_pair_fail *fail_rsp) +{ + struct ble_hs_conn *conn; + + ble_sm_test_util_init(); + ble_hs_id_set_pub(resp_addr); + ble_sm_dbg_set_next_pair_rand(random_rsp->value); + + if (pair_rsp->authreq & BLE_SM_PAIR_AUTHREQ_SC) { + ble_hs_cfg.sm_sc = 1; + } else { + ble_hs_cfg.sm_sc = 0; + } + + ble_hs_test_util_create_conn(2, init_id_addr, ble_sm_test_util_conn_cb, + NULL); + + /* This test inspects and modifies the connection object after unlocking + * the host mutex. It is not OK for real code to do this, but this test + * can assume the connection list is unchanging. + */ + ble_hs_lock(); + conn = ble_hs_conn_find(2); + TEST_ASSERT_FATAL(conn != NULL); + ble_hs_unlock(); + + /* Peer is the initiator so we must be the slave. */ + conn->bhc_flags &= ~BLE_HS_CONN_F_MASTER; + + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Receive a pair request from the peer. */ + ble_sm_test_util_rx_pair_req(2, pair_req, 0); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, BLE_SM_IOACT_NONE); + + /* Ensure we sent the expected pair response. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_rsp(pair_rsp); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, BLE_SM_IOACT_NONE); + + /* Receive a pair confirm from the peer. */ + ble_sm_test_util_rx_confirm(2, confirm_req); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, BLE_SM_IOACT_NONE); + + /* Ensure we sent the expected pair confirm. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_confirm(confirm_rsp); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, BLE_SM_IOACT_NONE); + + /* Receive a pair random from the peer. */ + ble_sm_test_util_rx_random( + 2, random_req, BLE_HS_SM_US_ERR(BLE_SM_ERR_CONFIRM_MISMATCH)); + + /* Ensure we sent the expected pair fail. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_fail(fail_rsp); + + /* The proc should now be freed. */ + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Verify that security callback was executed. */ + TEST_ASSERT(ble_sm_test_gap_event_type == BLE_GAP_EVENT_ENC_CHANGE); + TEST_ASSERT(ble_sm_test_gap_status == + BLE_HS_SM_US_ERR(BLE_SM_ERR_CONFIRM_MISMATCH)); + TEST_ASSERT(!ble_sm_test_sec_state.encrypted); + TEST_ASSERT(!ble_sm_test_sec_state.authenticated); + + /* Verify that connection has correct security state. */ + TEST_ASSERT(ble_sm_test_sec_state.encrypted == + conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_test_sec_state.authenticated == + conn->bhc_sec_state.authenticated); +} + +static void +ble_sm_test_util_bonding_all(struct ble_sm_test_params *params, + int we_are_original_initiator) +{ + struct ble_sm_test_util_entity peer_entity; + struct ble_sm_test_util_entity our_entity; + int sc; + + if (!(params->pair_req.authreq & BLE_SM_PAIR_AUTHREQ_BOND) || + !(params->pair_rsp.authreq & BLE_SM_PAIR_AUTHREQ_BOND)) { + + /* Bonding not performed. */ + return; + } + + sc = params->pair_req.authreq & BLE_SM_PAIR_AUTHREQ_SC && + params->pair_rsp.authreq & BLE_SM_PAIR_AUTHREQ_SC; + + ble_sm_test_util_params_to_entities(params, we_are_original_initiator, + &our_entity, &peer_entity); + + if (sc || peer_entity.key_dist & BLE_SM_PAIR_KEY_DIST_ENC) { + /* We are master; we initiate procedure. */ + ble_sm_test_util_us_bonding_good(0, our_entity.addr_type, + our_entity.rpa, + peer_entity.addr_type, + peer_entity.id_addr, + peer_entity.rpa, + peer_entity.ltk, + params->authenticated, + peer_entity.ediv, + peer_entity.rand_num); + + /* We are master; peer initiates procedure via security request. */ + ble_sm_test_util_us_bonding_good(1, our_entity.addr_type, + our_entity.rpa, + peer_entity.addr_type, + peer_entity.id_addr, + peer_entity.rpa, + peer_entity.ltk, + params->authenticated, + peer_entity.ediv, + peer_entity.rand_num); + } + + if (sc || our_entity.key_dist & BLE_SM_PAIR_KEY_DIST_ENC) { + /* Peer is master; peer initiates procedure. */ + ble_sm_test_util_peer_bonding_good(0, our_entity.addr_type, + our_entity.rpa, + peer_entity.addr_type, + peer_entity.id_addr, + peer_entity.rpa, + our_entity.ltk, + params->authenticated, + our_entity.ediv, + our_entity.rand_num); + + /* Peer is master; we initiate procedure via security request. */ + ble_sm_test_util_peer_bonding_good(1, our_entity.addr_type, + our_entity.rpa, + peer_entity.addr_type, + peer_entity.id_addr, + peer_entity.rpa, + our_entity.ltk, + params->authenticated, + our_entity.ediv, + our_entity.rand_num); + } +} + +static void +ble_sm_test_util_rx_keys(struct ble_sm_test_params *params, + int we_are_initiator) +{ + struct ble_sm_id_addr_info *peer_id_addr_info; + struct ble_sm_sign_info *peer_sign_info; + struct ble_sm_master_id *peer_master_id; + struct ble_sm_enc_info *peer_enc_info; + struct ble_sm_id_info *peer_id_info; + uint8_t peer_key_dist; + int sc; + + if (we_are_initiator) { + peer_key_dist = params->pair_rsp.resp_key_dist; + peer_id_addr_info = ¶ms->id_addr_info_req; + peer_sign_info = ¶ms->sign_info_req; + peer_master_id = ¶ms->master_id_req; + peer_enc_info = ¶ms->enc_info_req; + peer_id_info = ¶ms->id_info_req; + } else { + peer_key_dist = params->pair_rsp.init_key_dist; + peer_id_addr_info = ¶ms->id_addr_info_rsp; + peer_sign_info = ¶ms->sign_info_rsp; + peer_master_id = ¶ms->master_id_rsp; + peer_enc_info = ¶ms->enc_info_rsp; + peer_id_info = ¶ms->id_info_rsp; + } + + sc = params->pair_req.authreq & BLE_SM_PAIR_AUTHREQ_SC && + params->pair_rsp.authreq & BLE_SM_PAIR_AUTHREQ_SC; + + /* Receive key material from peer. */ + if (!sc && (peer_key_dist & BLE_SM_PAIR_KEY_DIST_ENC)) { + ble_sm_test_util_rx_enc_info(2, peer_enc_info, 0); + ble_sm_test_util_rx_master_id(2, peer_master_id, 0); + } + if (peer_key_dist & BLE_SM_PAIR_KEY_DIST_ID) { + ble_hs_test_util_set_ack( + ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_ADD_RESOLV_LIST), 0); + ble_sm_test_util_rx_id_info(2, peer_id_info, 0); + ble_sm_test_util_rx_id_addr_info(2, peer_id_addr_info, 0); + } + if (peer_key_dist & BLE_SM_PAIR_KEY_DIST_SIGN) { + ble_sm_test_util_rx_sign_info(2, peer_sign_info, 0); + } +} + +static void +ble_sm_test_util_verify_tx_keys(struct ble_sm_test_params *params, + int we_are_initiator) +{ + struct ble_sm_id_addr_info *our_id_addr_info; + struct ble_sm_sign_info *our_sign_info; + struct ble_sm_master_id *our_master_id; + struct ble_sm_enc_info *our_enc_info; + struct ble_sm_id_info *our_id_info; + uint8_t our_key_dist; + int sc; + + if (we_are_initiator) { + our_key_dist = params->pair_rsp.init_key_dist; + our_id_addr_info = ¶ms->id_addr_info_rsp; + our_sign_info = ¶ms->sign_info_rsp; + our_master_id = ¶ms->master_id_rsp; + our_enc_info = ¶ms->enc_info_rsp; + our_id_info = ¶ms->id_info_rsp; + } else { + our_key_dist = params->pair_rsp.resp_key_dist; + our_id_addr_info = ¶ms->id_addr_info_req; + our_sign_info = ¶ms->sign_info_req; + our_master_id = ¶ms->master_id_req; + our_enc_info = ¶ms->enc_info_req; + our_id_info = ¶ms->id_info_req; + } + + sc = params->pair_req.authreq & BLE_SM_PAIR_AUTHREQ_SC && + params->pair_rsp.authreq & BLE_SM_PAIR_AUTHREQ_SC; + + if (!sc && our_key_dist & BLE_SM_PAIR_KEY_DIST_ENC) { + ble_sm_test_util_verify_tx_enc_info(our_enc_info); + ble_sm_test_util_verify_tx_master_id(our_master_id); + } + if (our_key_dist & BLE_SM_PAIR_KEY_DIST_ID) { + ble_sm_test_util_verify_tx_id_info(our_id_info); + ble_sm_test_util_verify_tx_id_addr_info(our_id_addr_info); + } + if (our_key_dist & BLE_SM_PAIR_KEY_DIST_SIGN) { + ble_sm_test_util_verify_tx_sign_info(our_sign_info); + } +} + +static void +ble_sm_test_util_us_lgcy_good_once(struct ble_sm_test_params *params) +{ + struct ble_sm_test_util_entity peer_entity; + struct ble_sm_test_util_entity our_entity; + struct ble_hs_conn *conn; + int rc; + + ble_sm_test_util_init_good(params, 1, &conn, &our_entity, &peer_entity); + + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + ble_hs_test_util_set_ack( + ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_START_ENCRYPT), 0); + if (params->sec_req.authreq != 0) { + ble_sm_test_util_rx_sec_req(2, ¶ms->sec_req, 0); + } else { + /* Initiate the pairing procedure. */ + rc = ble_gap_security_initiate(2); + TEST_ASSERT_FATAL(rc == 0); + } + + /* Ensure we sent the expected pair request. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_req(our_entity.pair_cmd); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive a pair response from the peer. */ + ble_sm_test_util_rx_pair_rsp(2, peer_entity.pair_cmd, 0); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + ble_sm_test_util_io_inject(¶ms->passkey_info, + BLE_SM_PROC_STATE_CONFIRM); + + /* Ensure we sent the expected pair confirm. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_confirm(our_entity.confirms); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive a pair confirm from the peer. */ + ble_sm_test_util_rx_confirm(2, peer_entity.confirms); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Ensure we sent the expected pair random. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_random(our_entity.randoms); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive a pair random from the peer. */ + ble_sm_test_util_rx_random(2, peer_entity.randoms, 0); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Ensure we sent the expected start encryption command. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_start_enc(2, 0, 0, params->stk); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive an encryption changed event. */ + ble_sm_test_util_rx_enc_change(2, 0, 1); + + /* Receive key material from peer. */ + ble_sm_test_util_rx_keys(params, 1); + + /* Verify key material gets sent to peer. */ + ble_sm_test_util_verify_tx_keys(params, 1); + + /* Pairing should now be complete. */ + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Verify that security callback was executed. */ + TEST_ASSERT(ble_sm_test_gap_event_type == BLE_GAP_EVENT_ENC_CHANGE); + TEST_ASSERT(ble_sm_test_gap_status == 0); + TEST_ASSERT(ble_sm_test_sec_state.encrypted); + TEST_ASSERT(ble_sm_test_sec_state.authenticated == params->authenticated); + + /* Verify that connection has correct security state. */ + TEST_ASSERT(ble_sm_test_sec_state.encrypted == + conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_test_sec_state.authenticated == + conn->bhc_sec_state.authenticated); + + /* Verify the appropriate security material was persisted. */ + ble_sm_test_util_verify_persist(params, 1); + + ble_hs_test_util_conn_disconnect(2); +} + +void +ble_sm_test_util_us_lgcy_good(struct ble_sm_test_params *params) +{ + /*** We are master. */ + + /* We initiate pairing. */ + params->sec_req.authreq = 0; + ble_sm_test_util_us_lgcy_good_once(params); + + /* Peer initiates with security request. */ + params->sec_req.authreq = params->pair_rsp.authreq; + ble_sm_test_util_us_lgcy_good_once(params); + + /* Verify link can be restored via the encryption procedure. */ + ble_sm_test_util_bonding_all(params, 1); +} + +static void +ble_sm_test_util_peer_lgcy_good_once(struct ble_sm_test_params *params) +{ + struct ble_sm_test_util_entity peer_entity; + struct ble_sm_test_util_entity our_entity; + struct ble_hs_conn *conn; + int rc; + + ble_sm_test_util_init_good(params, 0, &conn, &our_entity, &peer_entity); + + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + if (params->sec_req.authreq != 0) { + rc = ble_sm_slave_initiate(2); + TEST_ASSERT(rc == 0); + + /* Ensure we sent the expected security request. */ + ble_sm_test_util_verify_tx_sec_req(¶ms->sec_req); + } + + /* Receive a pair request from the peer. */ + ble_sm_test_util_rx_pair_req(2, peer_entity.pair_cmd, 0); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Ensure we sent the expected pair response. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_rsp(our_entity.pair_cmd); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + ble_sm_test_util_io_check_pre(¶ms->passkey_info, + BLE_SM_PROC_STATE_CONFIRM); + + /* Receive a pair confirm from the peer. */ + ble_sm_test_util_rx_confirm(2, peer_entity.confirms); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + ble_sm_test_util_io_check_post(¶ms->passkey_info, + BLE_SM_PROC_STATE_CONFIRM); + + /* Ensure we sent the expected pair confirm. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_confirm(our_entity.confirms); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive a pair random from the peer. */ + ble_sm_test_util_rx_random(2, peer_entity.randoms, 0); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Ensure we sent the expected pair random. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_random(our_entity.randoms); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive a long term key request from the controller. */ + ble_sm_test_util_set_lt_key_req_reply_ack(0, 2); + ble_sm_test_util_rx_lt_key_req(2, 0, 0); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Ensure we sent the expected long term key request reply command. */ + ble_sm_test_util_verify_tx_lt_key_req_reply(2, params->stk); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive an encryption changed event. */ + ble_sm_test_util_rx_enc_change(2, 0, 1); + + /* Verify key material gets sent to peer. */ + ble_sm_test_util_verify_tx_keys(params, 0); + + /* Receive key material from peer. */ + ble_sm_test_util_rx_keys(params, 0); + + /* Pairing should now be complete. */ + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Verify that security callback was executed. */ + TEST_ASSERT(ble_sm_test_gap_event_type == BLE_GAP_EVENT_ENC_CHANGE); + TEST_ASSERT(ble_sm_test_gap_status == 0); + TEST_ASSERT(ble_sm_test_sec_state.encrypted); + TEST_ASSERT(ble_sm_test_sec_state.authenticated == + params->authenticated); + + /* Verify that connection has correct security state. */ + TEST_ASSERT(ble_sm_test_sec_state.encrypted == + conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_test_sec_state.authenticated == + conn->bhc_sec_state.authenticated); + + /* Verify the appropriate security material was persisted. */ + ble_sm_test_util_verify_persist(params, 0); + + ble_hs_test_util_conn_disconnect(2); +} + +void +ble_sm_test_util_peer_lgcy_good(struct ble_sm_test_params *params) +{ + /*** Peer is master. */ + + /* Peer performs IO first; peer initiates pairing. */ + params->passkey_info.io_before_rx = 0; + params->sec_req.authreq = 0; + ble_sm_test_util_peer_lgcy_good_once(params); + + /* Peer performs IO first; we initiate with security request. */ + params->passkey_info.io_before_rx = 0; + params->sec_req.authreq = params->pair_rsp.authreq; + ble_sm_test_util_peer_lgcy_good_once(params); + + /* We perform IO first; peer initiates pairing. */ + params->passkey_info.io_before_rx = 1; + params->sec_req.authreq = 0; + ble_sm_test_util_peer_lgcy_good_once(params); + + /* We perform IO first; we initiate with security request. */ + params->passkey_info.io_before_rx = 1; + params->sec_req.authreq = params->pair_rsp.authreq; + ble_sm_test_util_peer_lgcy_good_once(params); + + /* Verify link can be restored via the encryption procedure. */ + ble_sm_test_util_bonding_all(params, 0); +} + +static void +ble_sm_test_util_us_sc_good_once(struct ble_sm_test_params *params) +{ + struct ble_sm_test_util_entity peer_entity; + struct ble_sm_test_util_entity our_entity; + struct ble_hs_conn *conn; + int num_iters; + int rc; + int i; + + ble_sm_test_util_init_good(params, 1, &conn, &our_entity, &peer_entity); + + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + ble_hs_test_util_set_ack( + ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_START_ENCRYPT), 0); + if (params->sec_req.authreq != 0) { + ble_sm_test_util_rx_sec_req(2, ¶ms->sec_req, 0); + } else { + /* Initiate the pairing procedure. */ + rc = ble_gap_security_initiate(2); + TEST_ASSERT_FATAL(rc == 0); + } + + /* Ensure we sent the expected pair request. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_req(our_entity.pair_cmd); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive a pair response from the peer. */ + ble_sm_test_util_rx_pair_rsp(2, peer_entity.pair_cmd, 0); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Ensure we sent the expected public key. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_public_key(our_entity.public_key); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive a public key from the peer. */ + ble_sm_test_util_rx_public_key(2, peer_entity.public_key); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + switch (params->pair_alg) { + case BLE_SM_PAIR_ALG_PASSKEY: + num_iters = 20; + break; + + default: + num_iters = 1; + break; + } + + ble_sm_test_util_io_inject(¶ms->passkey_info, + BLE_SM_PROC_STATE_CONFIRM); + + for (i = 0; i < num_iters; i++) { + if (params->pair_alg != BLE_SM_PAIR_ALG_JW && + params->pair_alg != BLE_SM_PAIR_ALG_NUMCMP) { + + if (i < num_iters - 1) { + ble_sm_dbg_set_next_pair_rand( + our_entity.randoms[i + 1].value); + } + + /* Ensure we sent the expected pair confirm. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_confirm(our_entity.confirms + i); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad( + 2, params->passkey_info.passkey.action); + } + + /* Receive a pair confirm from the peer. */ + ble_sm_test_util_rx_confirm(2, peer_entity.confirms + i); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Ensure we sent the expected pair random. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_random(our_entity.randoms + i); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive a pair random from the peer. */ + ble_sm_test_util_rx_random(2, peer_entity.randoms + i, 0); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + } + + ble_sm_test_util_io_inject(¶ms->passkey_info, + BLE_SM_PROC_STATE_DHKEY_CHECK); + + /* Ensure we sent the expected dhkey check. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_dhkey_check(our_entity.dhkey_check); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive a dhkey check from the peer. */ + ble_sm_test_util_rx_dhkey_check(2, peer_entity.dhkey_check, 0); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Ensure we sent the expected start encryption command. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_start_enc(2, 0, 0, params->ltk); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive an encryption changed event. */ + ble_sm_test_util_rx_enc_change(2, 0, 1); + + /* Receive key material from peer. */ + ble_sm_test_util_rx_keys(params, 1); + + /* Verify key material gets sent to peer. */ + ble_sm_test_util_verify_tx_keys(params, 1); + + /* Pairing should now be complete. */ + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Verify that security callback was executed. */ + TEST_ASSERT(ble_sm_test_gap_event_type == BLE_GAP_EVENT_ENC_CHANGE); + TEST_ASSERT(ble_sm_test_gap_status == 0); + TEST_ASSERT(ble_sm_test_sec_state.encrypted); + TEST_ASSERT(ble_sm_test_sec_state.authenticated == + params->authenticated); + + /* Verify that connection has correct security state. */ + TEST_ASSERT(ble_sm_test_sec_state.encrypted == + conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_test_sec_state.authenticated == + conn->bhc_sec_state.authenticated); + + /* Verify the appropriate security material was persisted. */ + ble_sm_test_util_verify_persist(params, 1); + + ble_hs_test_util_conn_disconnect(2); +} + +void +ble_sm_test_util_us_sc_good(struct ble_sm_test_params *params) +{ + /*** We are master. */ + + /* We initiate pairing. */ + params->passkey_info.io_before_rx = 0; + params->sec_req.authreq = 0; + ble_sm_test_util_us_sc_good_once(params); + + /* Peer initiates with security request. */ + params->passkey_info.io_before_rx = 0; + params->sec_req.authreq = params->pair_rsp.authreq; + ble_sm_test_util_us_sc_good_once(params); + + /* Verify link can be restored via the encryption procedure. */ + ble_sm_test_util_bonding_all(params, 1); +} + +static void +ble_sm_test_util_peer_sc_good_once(struct ble_sm_test_params *params) +{ + struct ble_sm_test_util_entity peer_entity; + struct ble_sm_test_util_entity our_entity; + struct ble_hs_conn *conn; + int num_iters; + int rc; + int i; + + ble_sm_test_util_init_good(params, 0, &conn, &our_entity, &peer_entity); + + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + if (params->sec_req.authreq != 0) { + rc = ble_sm_slave_initiate(2); + TEST_ASSERT(rc == 0); + + /* Ensure we sent the expected security request. */ + ble_sm_test_util_verify_tx_sec_req(¶ms->sec_req); + } + + /* Receive a pair request from the peer. */ + ble_sm_test_util_rx_pair_req(2, peer_entity.pair_cmd, 0); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Ensure we sent the expected pair response. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_rsp(our_entity.pair_cmd); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive a public key from the peer. */ + ble_sm_test_util_rx_public_key(2, peer_entity.public_key); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Ensure we sent the expected public key. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_public_key(our_entity.public_key); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + switch (params->pair_alg) { + case BLE_SM_PAIR_ALG_PASSKEY: + num_iters = 20; + break; + + default: + num_iters = 1; + break; + } + + ble_sm_test_util_io_check_pre(¶ms->passkey_info, + BLE_SM_PROC_STATE_CONFIRM); + + for (i = 0; i < num_iters; i++) { + if (params->pair_alg != BLE_SM_PAIR_ALG_JW && + params->pair_alg != BLE_SM_PAIR_ALG_NUMCMP) { + + /* Receive a pair confirm from the peer. */ + ble_sm_test_util_rx_confirm(2, peer_entity.confirms + i); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad( + 2, params->passkey_info.passkey.action); + + if (i < num_iters - 1) { + ble_sm_dbg_set_next_pair_rand( + our_entity.randoms[i + 1].value); + } + } + + if (i == 0) { + ble_sm_test_util_io_check_post(¶ms->passkey_info, + BLE_SM_PROC_STATE_CONFIRM); + } + + /* Ensure we sent the expected pair confirm. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_confirm(our_entity.confirms + i); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive a pair random from the peer. */ + ble_sm_test_util_rx_random(2, peer_entity.randoms + i, 0); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Ensure we sent the expected pair random. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_random(our_entity.randoms + i); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + } + + ble_sm_test_util_io_check_pre(¶ms->passkey_info, + BLE_SM_PROC_STATE_DHKEY_CHECK); + + /* Receive a dhkey check from the peer. */ + ble_sm_test_util_rx_dhkey_check(2, peer_entity.dhkey_check, 0); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + ble_sm_test_util_io_check_post(¶ms->passkey_info, + BLE_SM_PROC_STATE_DHKEY_CHECK); + + /* Ensure we sent the expected dhkey check. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_dhkey_check(our_entity.dhkey_check); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive a long term key request from the controller. */ + ble_sm_test_util_set_lt_key_req_reply_ack(0, 2); + ble_sm_test_util_rx_lt_key_req(2, 0, 0); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Ensure we sent the expected long term key request reply command. */ + ble_sm_test_util_verify_tx_lt_key_req_reply(2, params->ltk); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive an encryption changed event. */ + ble_sm_test_util_rx_enc_change(2, 0, 1); + + /* Verify key material gets sent to peer. */ + ble_sm_test_util_verify_tx_keys(params, 0); + + /* Receive key material from peer. */ + ble_sm_test_util_rx_keys(params, 0); + + /* Pairing should now be complete. */ + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Verify that security callback was executed. */ + TEST_ASSERT(ble_sm_test_gap_event_type == BLE_GAP_EVENT_ENC_CHANGE); + TEST_ASSERT(ble_sm_test_gap_status == 0); + TEST_ASSERT(ble_sm_test_sec_state.encrypted); + TEST_ASSERT(ble_sm_test_sec_state.authenticated == + params->authenticated); + + /* Verify that connection has correct security state. */ + TEST_ASSERT(ble_sm_test_sec_state.encrypted == + conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_test_sec_state.authenticated == + conn->bhc_sec_state.authenticated); + + /* Verify the appropriate security material was persisted. */ + ble_sm_test_util_verify_persist(params, 0); + + ble_hs_test_util_conn_disconnect(2); +} + +void +ble_sm_test_util_peer_sc_good(struct ble_sm_test_params *params) +{ + /*** Peer is master. */ + + /* Peer performs IO first; peer initiates pairing. */ + params->passkey_info.io_before_rx = 0; + params->sec_req.authreq = 0; + ble_sm_test_util_peer_sc_good_once(params); + + /* Peer performs IO first; we initiate with security request. */ + params->passkey_info.io_before_rx = 0; + params->sec_req.authreq = params->pair_req.authreq; + ble_sm_test_util_peer_sc_good_once(params); + + /* We perform IO first; peer initiates pairing. */ + params->passkey_info.io_before_rx = 1; + params->sec_req.authreq = 0; + ble_sm_test_util_peer_sc_good_once(params); + + /* We perform IO first; we initiate with security request. */ + params->passkey_info.io_before_rx = 1; + params->sec_req.authreq = params->pair_req.authreq; + ble_sm_test_util_peer_sc_good_once(params); + + /* Verify link can be restored via the encryption procedure. */ + ble_sm_test_util_bonding_all(params, 0); +} + +void +ble_sm_test_util_us_fail_inval(struct ble_sm_test_params *params) +{ + struct ble_hs_conn *conn; + int rc; + + ble_sm_test_util_init(); + ble_hs_id_set_pub(params->resp_id_addr); + + ble_sm_dbg_set_next_pair_rand(((uint8_t[16]){0})); + + ble_hs_test_util_create_conn(2, params->init_id_addr, + ble_sm_test_util_conn_cb, + NULL); + + /* This test inspects and modifies the connection object after unlocking + * the host mutex. It is not OK for real code to do this, but this test + * can assume the connection list is unchanging. + */ + ble_hs_lock(); + conn = ble_hs_conn_find(2); + TEST_ASSERT_FATAL(conn != NULL); + ble_hs_unlock(); + + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Initiate the pairing procedure. */ + rc = ble_hs_test_util_security_initiate(2, 0); + TEST_ASSERT_FATAL(rc == 0); + + /* Ensure we sent the expected pair request. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_req(¶ms->pair_req); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 1); + ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action); + + /* Receive a pair response from the peer. */ + ble_sm_test_util_rx_pair_rsp( + 2, ¶ms->pair_rsp, BLE_HS_SM_US_ERR(BLE_SM_ERR_INVAL)); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Ensure we sent the expected pair fail. */ + ble_hs_test_util_tx_all(); + ble_sm_test_util_verify_tx_pair_fail(¶ms->pair_fail); + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSERT(ble_sm_dbg_num_procs() == 0); + + /* Verify that security callback was not executed. */ + TEST_ASSERT(ble_sm_test_gap_event_type == -1); + TEST_ASSERT(ble_sm_test_gap_status == -1); + + /* Verify that connection has correct security state. */ + TEST_ASSERT(!conn->bhc_sec_state.encrypted); + TEST_ASSE
<TRUNCATED> http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/c5901fcc/net/nimble/transport/ram/src/ble_hci_ram.c ----------------------------------------------------------------------