| /****************************************************************************** |
| * |
| * Copyright 2003-2012 Broadcom Corporation |
| * |
| * Licensed 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 <cutils/log.h> |
| #include <log/log.h> |
| #include <string.h> |
| #include "btif_common.h" |
| #include "btif_storage.h" |
| #include "device/include/interop.h" |
| #include "internal_include/bt_target.h" |
| #include "stack/btm/btm_int.h" |
| #include "stack/include/l2c_api.h" |
| #include "stack/smp/p_256_ecc_pp.h" |
| #include "stack/smp/smp_int.h" |
| #include "utils/include/bt_utils.h" |
| |
| #define SMP_KEY_DIST_TYPE_MAX 4 |
| |
| const tSMP_ACT smp_distribute_act[] = { |
| smp_generate_ltk, /* SMP_SEC_KEY_TYPE_ENC - '1' bit index */ |
| smp_send_id_info, /* SMP_SEC_KEY_TYPE_ID - '1' bit index */ |
| smp_generate_csrk, /* SMP_SEC_KEY_TYPE_CSRK - '1' bit index */ |
| smp_set_derive_link_key /* SMP_SEC_KEY_TYPE_LK - '1' bit index */ |
| }; |
| |
| static bool lmp_version_below(const RawAddress& bda, uint8_t version) { |
| tACL_CONN* acl = btm_bda_to_acl(bda, BT_TRANSPORT_LE); |
| if (acl == NULL || acl->lmp_version == 0) { |
| SMP_TRACE_WARNING("%s cannot retrieve LMP version...", __func__); |
| return false; |
| } |
| SMP_TRACE_WARNING("%s LMP version %d < %d", __func__, acl->lmp_version, |
| version); |
| return acl->lmp_version < version; |
| } |
| |
| static bool pts_test_send_authentication_complete_failure(tSMP_CB* p_cb) { |
| uint8_t reason = p_cb->cert_failure; |
| if (reason == SMP_PAIR_AUTH_FAIL || reason == SMP_PAIR_FAIL_UNKNOWN || |
| reason == SMP_PAIR_NOT_SUPPORT || reason == SMP_PASSKEY_ENTRY_FAIL || |
| reason == SMP_REPEATED_ATTEMPTS) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = reason; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return true; |
| } |
| return false; |
| } |
| |
| /******************************************************************************* |
| * Function smp_update_key_mask |
| * Description This function updates the key mask for sending or receiving. |
| ******************************************************************************/ |
| static void smp_update_key_mask(tSMP_CB* p_cb, uint8_t key_type, bool recv) { |
| SMP_TRACE_DEBUG( |
| "%s before update role=%d recv=%d local_i_key = %02x, local_r_key = %02x", |
| __func__, p_cb->role, recv, p_cb->local_i_key, p_cb->local_r_key); |
| |
| if (((p_cb->le_secure_connections_mode_is_used) || (p_cb->smp_over_br)) && |
| ((key_type == SMP_SEC_KEY_TYPE_ENC) || |
| (key_type == SMP_SEC_KEY_TYPE_LK))) { |
| /* in LE SC mode LTK, CSRK and BR/EDR LK are derived locally instead of |
| ** being exchanged with the peer */ |
| p_cb->local_i_key &= ~key_type; |
| p_cb->local_r_key &= ~key_type; |
| } else if (p_cb->role == HCI_ROLE_SLAVE) { |
| if (recv) |
| p_cb->local_i_key &= ~key_type; |
| else |
| p_cb->local_r_key &= ~key_type; |
| } else { |
| if (recv) |
| p_cb->local_r_key &= ~key_type; |
| else |
| p_cb->local_i_key &= ~key_type; |
| } |
| |
| SMP_TRACE_DEBUG("updated local_i_key = %02x, local_r_key = %02x", |
| p_cb->local_i_key, p_cb->local_r_key); |
| } |
| |
| /******************************************************************************* |
| * Function smp_send_app_cback |
| * Description notifies application about the events the application is |
| * interested in |
| ******************************************************************************/ |
| void smp_send_app_cback(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| tSMP_EVT_DATA cb_data; |
| tSMP_STATUS callback_rc; |
| SMP_TRACE_DEBUG("%s p_cb->cb_evt=%d", __func__, p_cb->cb_evt); |
| if (p_cb->p_callback && p_cb->cb_evt != 0) { |
| switch (p_cb->cb_evt) { |
| case SMP_IO_CAP_REQ_EVT: |
| cb_data.io_req.auth_req = p_cb->peer_auth_req; |
| cb_data.io_req.oob_data = SMP_OOB_NONE; |
| cb_data.io_req.io_cap = btif_storage_get_local_io_caps_ble(); |
| cb_data.io_req.max_key_size = SMP_MAX_ENC_KEY_SIZE; |
| cb_data.io_req.init_keys = p_cb->local_i_key; |
| cb_data.io_req.resp_keys = p_cb->local_r_key; |
| SMP_TRACE_WARNING("io_cap = %d", cb_data.io_req.io_cap); |
| break; |
| |
| case SMP_NC_REQ_EVT: |
| cb_data.passkey = p_data->passkey; |
| break; |
| case SMP_SC_OOB_REQ_EVT: |
| cb_data.req_oob_type = p_data->req_oob_type; |
| break; |
| case SMP_SC_LOC_OOB_DATA_UP_EVT: |
| cb_data.loc_oob_data = p_cb->sc_oob_data.loc_oob_data; |
| break; |
| |
| case SMP_BR_KEYS_REQ_EVT: |
| cb_data.io_req.auth_req = 0; |
| cb_data.io_req.oob_data = SMP_OOB_NONE; |
| cb_data.io_req.io_cap = 0; |
| cb_data.io_req.max_key_size = SMP_MAX_ENC_KEY_SIZE; |
| cb_data.io_req.init_keys = SMP_BR_SEC_DEFAULT_KEY; |
| cb_data.io_req.resp_keys = SMP_BR_SEC_DEFAULT_KEY; |
| break; |
| |
| default: |
| break; |
| } |
| |
| callback_rc = |
| (*p_cb->p_callback)(p_cb->cb_evt, p_cb->pairing_bda, &cb_data); |
| |
| SMP_TRACE_DEBUG("%s: callback_rc=%d p_cb->cb_evt=%d", __func__, |
| callback_rc, p_cb->cb_evt); |
| |
| if (callback_rc == SMP_SUCCESS) { |
| switch (p_cb->cb_evt) { |
| case SMP_IO_CAP_REQ_EVT: |
| p_cb->loc_auth_req = cb_data.io_req.auth_req; |
| p_cb->local_io_capability = cb_data.io_req.io_cap; |
| p_cb->loc_oob_flag = cb_data.io_req.oob_data; |
| p_cb->loc_enc_size = cb_data.io_req.max_key_size; |
| p_cb->local_i_key = cb_data.io_req.init_keys; |
| p_cb->local_r_key = cb_data.io_req.resp_keys; |
| |
| if (!(p_cb->loc_auth_req & SMP_AUTH_BOND)) { |
| SMP_TRACE_WARNING("Non bonding: No keys will be exchanged"); |
| p_cb->local_i_key = 0; |
| p_cb->local_r_key = 0; |
| } |
| |
| SMP_TRACE_WARNING( |
| "rcvd auth_req: 0x%02x, io_cap: %d " |
| "loc_oob_flag: %d loc_enc_size: %d, " |
| "local_i_key: 0x%02x, local_r_key: 0x%02x", |
| p_cb->loc_auth_req, p_cb->local_io_capability, p_cb->loc_oob_flag, |
| p_cb->loc_enc_size, p_cb->local_i_key, p_cb->local_r_key); |
| |
| p_cb->secure_connections_only_mode_required = |
| (btm_cb.security_mode == BTM_SEC_MODE_SC) ? true : false; |
| /* just for PTS, force SC bit */ |
| if (p_cb->secure_connections_only_mode_required) { |
| p_cb->loc_auth_req |= SMP_SC_SUPPORT_BIT; |
| } |
| |
| if (!p_cb->secure_connections_only_mode_required && |
| (!(p_cb->loc_auth_req & SMP_SC_SUPPORT_BIT) || |
| lmp_version_below(p_cb->pairing_bda, HCI_PROTO_VERSION_4_2) || |
| interop_match_addr(INTEROP_DISABLE_LE_SECURE_CONNECTIONS, |
| (const RawAddress*)&p_cb->pairing_bda))) { |
| p_cb->loc_auth_req &= ~SMP_SC_SUPPORT_BIT; |
| p_cb->loc_auth_req &= ~SMP_KP_SUPPORT_BIT; |
| p_cb->local_i_key &= ~SMP_SEC_KEY_TYPE_LK; |
| p_cb->local_r_key &= ~SMP_SEC_KEY_TYPE_LK; |
| } |
| |
| if (lmp_version_below(p_cb->pairing_bda, HCI_PROTO_VERSION_5_0)) { |
| p_cb->loc_auth_req &= ~SMP_H7_SUPPORT_BIT; |
| } |
| |
| SMP_TRACE_WARNING( |
| "set auth_req: 0x%02x, local_i_key: 0x%02x, local_r_key: 0x%02x", |
| p_cb->loc_auth_req, p_cb->local_i_key, p_cb->local_r_key); |
| |
| smp_sm_event(p_cb, SMP_IO_RSP_EVT, NULL); |
| break; |
| |
| case SMP_BR_KEYS_REQ_EVT: |
| p_cb->loc_enc_size = cb_data.io_req.max_key_size; |
| p_cb->local_i_key = cb_data.io_req.init_keys; |
| p_cb->local_r_key = cb_data.io_req.resp_keys; |
| p_cb->loc_auth_req |= SMP_H7_SUPPORT_BIT; |
| |
| p_cb->local_i_key &= ~SMP_SEC_KEY_TYPE_LK; |
| p_cb->local_r_key &= ~SMP_SEC_KEY_TYPE_LK; |
| |
| SMP_TRACE_WARNING( |
| "for SMP over BR max_key_size: 0x%02x, local_i_key: 0x%02x, " |
| "local_r_key: 0x%02x, p_cb->loc_auth_req: 0x%02x", |
| p_cb->loc_enc_size, p_cb->local_i_key, p_cb->local_r_key, |
| p_cb->loc_auth_req); |
| |
| smp_br_state_machine_event(p_cb, SMP_BR_KEYS_RSP_EVT, NULL); |
| break; |
| } |
| } |
| } |
| |
| if (!p_cb->cb_evt && p_cb->discard_sec_req) { |
| p_cb->discard_sec_req = false; |
| smp_sm_event(p_cb, SMP_DISCARD_SEC_REQ_EVT, NULL); |
| } |
| |
| SMP_TRACE_DEBUG("%s: return", __func__); |
| } |
| |
| /******************************************************************************* |
| * Function smp_send_pair_fail |
| * Description pairing failure to peer device if needed. |
| ******************************************************************************/ |
| void smp_send_pair_fail(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| p_cb->status = p_data->status; |
| p_cb->failure = p_data->status; |
| |
| SMP_TRACE_DEBUG("%s: status=%d failure=%d ", __func__, p_cb->status, |
| p_cb->failure); |
| |
| if (p_cb->status <= SMP_MAX_FAIL_RSN_PER_SPEC && |
| p_cb->status != SMP_SUCCESS) { |
| smp_send_cmd(SMP_OPCODE_PAIRING_FAILED, p_cb); |
| p_cb->wait_for_authorization_complete = true; |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_send_pair_req |
| * Description actions related to sending pairing request |
| ******************************************************************************/ |
| void smp_send_pair_req(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(p_cb->pairing_bda); |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| /* erase all keys when master sends pairing req*/ |
| if (p_dev_rec) btm_sec_clear_ble_keys(p_dev_rec); |
| /* do not manipulate the key, let app decide, |
| leave out to BTM to mandate key distribution for bonding case */ |
| smp_send_cmd(SMP_OPCODE_PAIRING_REQ, p_cb); |
| } |
| |
| /******************************************************************************* |
| * Function smp_send_pair_rsp |
| * Description actions related to sending pairing response |
| ******************************************************************************/ |
| void smp_send_pair_rsp(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| p_cb->local_i_key &= p_cb->peer_i_key; |
| p_cb->local_r_key &= p_cb->peer_r_key; |
| |
| if (smp_send_cmd(SMP_OPCODE_PAIRING_RSP, p_cb)) { |
| if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB) |
| smp_use_oob_private_key(p_cb, NULL); |
| else |
| smp_decide_association_model(p_cb, NULL); |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_send_confirm |
| * Description send confirmation to the peer |
| ******************************************************************************/ |
| void smp_send_confirm(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| smp_send_cmd(SMP_OPCODE_CONFIRM, p_cb); |
| } |
| |
| /******************************************************************************* |
| * Function smp_send_init |
| * Description process pairing initializer to slave device |
| ******************************************************************************/ |
| void smp_send_init(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| smp_send_cmd(SMP_OPCODE_INIT, p_cb); |
| } |
| |
| /******************************************************************************* |
| * Function smp_send_rand |
| * Description send pairing random to the peer |
| ******************************************************************************/ |
| void smp_send_rand(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| smp_send_cmd(SMP_OPCODE_RAND, p_cb); |
| } |
| |
| /******************************************************************************* |
| * Function smp_send_pair_public_key |
| * Description send pairing public key command to the peer |
| ******************************************************************************/ |
| void smp_send_pair_public_key(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| smp_send_cmd(SMP_OPCODE_PAIR_PUBLIC_KEY, p_cb); |
| } |
| |
| /******************************************************************************* |
| * Function SMP_SEND_COMMITMENT |
| * Description send commitment command to the peer |
| ******************************************************************************/ |
| void smp_send_commitment(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| smp_send_cmd(SMP_OPCODE_PAIR_COMMITM, p_cb); |
| } |
| |
| /******************************************************************************* |
| * Function smp_send_dhkey_check |
| * Description send DHKey Check command to the peer |
| ******************************************************************************/ |
| void smp_send_dhkey_check(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| smp_send_cmd(SMP_OPCODE_PAIR_DHKEY_CHECK, p_cb); |
| } |
| |
| /******************************************************************************* |
| * Function smp_send_keypress_notification |
| * Description send Keypress Notification command to the peer |
| ******************************************************************************/ |
| void smp_send_keypress_notification(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| p_cb->local_keypress_notification = p_data->status; |
| smp_send_cmd(SMP_OPCODE_PAIR_KEYPR_NOTIF, p_cb); |
| } |
| |
| /******************************************************************************* |
| * Function smp_send_enc_info |
| * Description send encryption information command. |
| ******************************************************************************/ |
| void smp_send_enc_info(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| tBTM_LE_KEY_VALUE le_key; |
| |
| SMP_TRACE_DEBUG("%s: p_cb->loc_enc_size = %d", __func__, p_cb->loc_enc_size); |
| smp_update_key_mask(p_cb, SMP_SEC_KEY_TYPE_ENC, false); |
| |
| smp_send_cmd(SMP_OPCODE_ENCRYPT_INFO, p_cb); |
| smp_send_cmd(SMP_OPCODE_MASTER_ID, p_cb); |
| |
| /* save the DIV and key size information when acting as slave device */ |
| le_key.lenc_key.ltk = p_cb->ltk; |
| le_key.lenc_key.div = p_cb->div; |
| le_key.lenc_key.key_size = p_cb->loc_enc_size; |
| le_key.lenc_key.sec_level = p_cb->sec_level; |
| |
| if ((p_cb->peer_auth_req & SMP_AUTH_BOND) && |
| (p_cb->loc_auth_req & SMP_AUTH_BOND)) |
| btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_LENC, &le_key, true); |
| |
| SMP_TRACE_WARNING("%s", __func__); |
| |
| smp_key_distribution(p_cb, NULL); |
| } |
| |
| /******************************************************************************* |
| * Function smp_send_id_info |
| * Description send ID information command. |
| ******************************************************************************/ |
| void smp_send_id_info(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| tBTM_LE_KEY_VALUE le_key; |
| SMP_TRACE_DEBUG("%s", __func__); |
| smp_update_key_mask(p_cb, SMP_SEC_KEY_TYPE_ID, false); |
| |
| smp_send_cmd(SMP_OPCODE_IDENTITY_INFO, p_cb); |
| smp_send_cmd(SMP_OPCODE_ID_ADDR, p_cb); |
| |
| if ((p_cb->peer_auth_req & SMP_AUTH_BOND) && |
| (p_cb->loc_auth_req & SMP_AUTH_BOND)) |
| btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_LID, &le_key, true); |
| |
| SMP_TRACE_WARNING("%s", __func__); |
| smp_key_distribution_by_transport(p_cb, NULL); |
| } |
| |
| /** send CSRK command. */ |
| void smp_send_csrk_info(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| tBTM_LE_KEY_VALUE key; |
| SMP_TRACE_DEBUG("%s", __func__); |
| smp_update_key_mask(p_cb, SMP_SEC_KEY_TYPE_CSRK, false); |
| |
| if (smp_send_cmd(SMP_OPCODE_SIGN_INFO, p_cb)) { |
| key.lcsrk_key.div = p_cb->div; |
| key.lcsrk_key.sec_level = p_cb->sec_level; |
| key.lcsrk_key.counter = 0; /* initialize the local counter */ |
| key.lcsrk_key.csrk = p_cb->csrk; |
| btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_LCSRK, &key, true); |
| } |
| |
| smp_key_distribution_by_transport(p_cb, NULL); |
| } |
| |
| /******************************************************************************* |
| * Function smp_send_ltk_reply |
| * Description send LTK reply |
| ******************************************************************************/ |
| void smp_send_ltk_reply(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| Octet16 stk; |
| memcpy(stk.data(), p_data->key.p_data, stk.size()); |
| /* send stk as LTK response */ |
| btm_ble_ltk_request_reply(p_cb->pairing_bda, true, stk); |
| } |
| |
| /******************************************************************************* |
| * Function smp_proc_sec_req |
| * Description process security request. |
| ******************************************************************************/ |
| void smp_proc_sec_req(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| tBTM_LE_AUTH_REQ auth_req = *(tBTM_LE_AUTH_REQ*)p_data->p_data; |
| tBTM_BLE_SEC_REQ_ACT sec_req_act; |
| |
| SMP_TRACE_DEBUG("%s: auth_req=0x%x", __func__, auth_req); |
| |
| p_cb->cb_evt = 0; |
| |
| btm_ble_link_sec_check(p_cb->pairing_bda, auth_req, &sec_req_act); |
| |
| SMP_TRACE_DEBUG("%s: sec_req_act=0x%x", __func__, sec_req_act); |
| |
| switch (sec_req_act) { |
| case BTM_BLE_SEC_REQ_ACT_ENCRYPT: |
| SMP_TRACE_DEBUG("%s: BTM_BLE_SEC_REQ_ACT_ENCRYPT", __func__); |
| smp_sm_event(p_cb, SMP_ENC_REQ_EVT, NULL); |
| break; |
| |
| case BTM_BLE_SEC_REQ_ACT_PAIR: |
| p_cb->secure_connections_only_mode_required = |
| (btm_cb.security_mode == BTM_SEC_MODE_SC) ? true : false; |
| |
| /* respond to non SC pairing request as failure in SC only mode */ |
| if (p_cb->secure_connections_only_mode_required && |
| (auth_req & SMP_SC_SUPPORT_BIT) == 0) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_PAIR_AUTH_FAIL; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| } else { |
| /* initialize local i/r key to be default keys */ |
| p_cb->peer_auth_req = auth_req; |
| p_cb->local_r_key = p_cb->local_i_key = SMP_SEC_DEFAULT_KEY; |
| p_cb->cb_evt = SMP_SEC_REQUEST_EVT; |
| } |
| break; |
| |
| case BTM_BLE_SEC_REQ_ACT_DISCARD: |
| p_cb->discard_sec_req = true; |
| break; |
| |
| default: |
| /* do nothing */ |
| break; |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_proc_sec_grant |
| * Description process security grant. |
| ******************************************************************************/ |
| void smp_proc_sec_grant(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t res = p_data->status; |
| SMP_TRACE_DEBUG("%s", __func__); |
| if (res != SMP_SUCCESS) { |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, p_data); |
| } else /*otherwise, start pairing */ |
| { |
| /* send IO request callback */ |
| p_cb->cb_evt = SMP_IO_CAP_REQ_EVT; |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_proc_pair_fail |
| * Description process pairing failure from peer device |
| ******************************************************************************/ |
| void smp_proc_pair_fail(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (p_cb->rcvd_cmd_len < 2) { |
| android_errorWriteLog(0x534e4554, "111214739"); |
| SMP_TRACE_WARNING("%s: rcvd_cmd_len %d too short: must be at least 2", |
| __func__, p_cb->rcvd_cmd_len); |
| p_cb->status = SMP_INVALID_PARAMETERS; |
| } else { |
| p_cb->status = p_data->status; |
| } |
| |
| /* Cancel pending auth complete timer if set */ |
| alarm_cancel(p_cb->delayed_auth_timer_ent); |
| } |
| |
| /******************************************************************************* |
| * Function smp_proc_pair_cmd |
| * Description Process the SMP pairing request/response from peer device |
| ******************************************************************************/ |
| void smp_proc_pair_cmd(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t* p = p_data->p_data; |
| tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(p_cb->pairing_bda); |
| |
| SMP_TRACE_DEBUG("%s: pairing_bda=%s", __func__, |
| p_cb->pairing_bda.ToString().c_str()); |
| |
| /* erase all keys if it is slave proc pairing req */ |
| if (p_dev_rec && (p_cb->role == HCI_ROLE_SLAVE)) |
| btm_sec_clear_ble_keys(p_dev_rec); |
| |
| p_cb->flags |= SMP_PAIR_FLAG_ENC_AFTER_PAIR; |
| |
| if (smp_command_has_invalid_length(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| android_errorWriteLog(0x534e4554, "111850706"); |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| STREAM_TO_UINT8(p_cb->peer_io_caps, p); |
| STREAM_TO_UINT8(p_cb->peer_oob_flag, p); |
| STREAM_TO_UINT8(p_cb->peer_auth_req, p); |
| STREAM_TO_UINT8(p_cb->peer_enc_size, p); |
| STREAM_TO_UINT8(p_cb->peer_i_key, p); |
| STREAM_TO_UINT8(p_cb->peer_r_key, p); |
| |
| if (smp_command_has_invalid_parameters(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| // PTS Testing failure modes |
| if (pts_test_send_authentication_complete_failure(p_cb)) return; |
| |
| if (p_cb->role == HCI_ROLE_SLAVE) { |
| if (!(p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD)) { |
| /* peer (master) started pairing sending Pairing Request */ |
| p_cb->local_i_key = p_cb->peer_i_key; |
| p_cb->local_r_key = p_cb->peer_r_key; |
| |
| p_cb->cb_evt = SMP_SEC_REQUEST_EVT; |
| } else /* update local i/r key according to pairing request */ |
| { |
| /* pairing started with this side (slave) sending Security Request */ |
| p_cb->local_i_key &= p_cb->peer_i_key; |
| p_cb->local_r_key &= p_cb->peer_r_key; |
| p_cb->selected_association_model = smp_select_association_model(p_cb); |
| |
| if (p_cb->secure_connections_only_mode_required && |
| (!(p_cb->le_secure_connections_mode_is_used) || |
| (p_cb->selected_association_model == |
| SMP_MODEL_SEC_CONN_JUSTWORKS))) { |
| SMP_TRACE_ERROR( |
| "%s: pairing failed - slave requires secure connection only mode", |
| __func__); |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_PAIR_AUTH_FAIL; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB) { |
| if (smp_request_oob_data(p_cb)) return; |
| } else { |
| smp_send_pair_rsp(p_cb, NULL); |
| } |
| } |
| } else /* Master receives pairing response */ |
| { |
| p_cb->selected_association_model = smp_select_association_model(p_cb); |
| |
| if (p_cb->secure_connections_only_mode_required && |
| (!(p_cb->le_secure_connections_mode_is_used) || |
| (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_JUSTWORKS))) { |
| SMP_TRACE_ERROR( |
| "Master requires secure connection only mode " |
| "but it can't be provided -> Master fails pairing"); |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_PAIR_AUTH_FAIL; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB) { |
| if (smp_request_oob_data(p_cb)) return; |
| } else { |
| smp_decide_association_model(p_cb, NULL); |
| } |
| } |
| } |
| |
| /** process pairing confirm from peer device */ |
| void smp_proc_confirm(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (smp_command_has_invalid_parameters(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| if (p_data) { |
| uint8_t* p = p_data->p_data; |
| if (p != NULL) { |
| /* save the SConfirm for comparison later */ |
| STREAM_TO_ARRAY(p_cb->rconfirm.data(), p, OCTET16_LEN); |
| } |
| } |
| |
| p_cb->flags |= SMP_PAIR_FLAGS_CMD_CONFIRM; |
| } |
| |
| /** process pairing initializer from peer device */ |
| void smp_proc_init(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t* p = p_data->p_data; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (smp_command_has_invalid_parameters(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| /* save the SRand for comparison */ |
| STREAM_TO_ARRAY(p_cb->rrand.data(), p, OCTET16_LEN); |
| } |
| |
| /******************************************************************************* |
| * Function smp_proc_rand |
| * Description process pairing random (nonce) from peer device |
| ******************************************************************************/ |
| void smp_proc_rand(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t* p = p_data->p_data; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (smp_command_has_invalid_parameters(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| /* save the SRand for comparison */ |
| STREAM_TO_ARRAY(p_cb->rrand.data(), p, OCTET16_LEN); |
| } |
| |
| /******************************************************************************* |
| * Function smp_process_pairing_public_key |
| * Description process pairing public key command from the peer device |
| * - saves the peer public key; |
| * - sets the flag indicating that the peer public key is received; |
| * - calls smp_wait_for_both_public_keys(...). |
| * |
| ******************************************************************************/ |
| void smp_process_pairing_public_key(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t* p = p_data->p_data; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (smp_command_has_invalid_parameters(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| STREAM_TO_ARRAY(p_cb->peer_publ_key.x, p, BT_OCTET32_LEN); |
| STREAM_TO_ARRAY(p_cb->peer_publ_key.y, p, BT_OCTET32_LEN); |
| |
| Point pt; |
| memcpy(pt.x, p_cb->peer_publ_key.x, BT_OCTET32_LEN); |
| memcpy(pt.y, p_cb->peer_publ_key.y, BT_OCTET32_LEN); |
| |
| if (!ECC_ValidatePoint(pt)) { |
| android_errorWriteLog(0x534e4554, "72377774"); |
| tSMP_INT_DATA smp; |
| smp.status = SMP_PAIR_AUTH_FAIL; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp); |
| return; |
| } |
| |
| p_cb->flags |= SMP_PAIR_FLAG_HAVE_PEER_PUBL_KEY; |
| |
| smp_wait_for_both_public_keys(p_cb, NULL); |
| } |
| |
| /******************************************************************************* |
| * Function smp_process_pairing_commitment |
| * Description process pairing commitment from peer device |
| ******************************************************************************/ |
| void smp_process_pairing_commitment(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t* p = p_data->p_data; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (smp_command_has_invalid_parameters(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| p_cb->flags |= SMP_PAIR_FLAG_HAVE_PEER_COMM; |
| |
| if (p != NULL) { |
| STREAM_TO_ARRAY(p_cb->remote_commitment.data(), p, OCTET16_LEN); |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_process_dhkey_check |
| * Description process DHKey Check from peer device |
| ******************************************************************************/ |
| void smp_process_dhkey_check(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t* p = p_data->p_data; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (smp_command_has_invalid_parameters(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| if (p != NULL) { |
| STREAM_TO_ARRAY(p_cb->remote_dhkey_check.data(), p, OCTET16_LEN); |
| } |
| |
| p_cb->flags |= SMP_PAIR_FLAG_HAVE_PEER_DHK_CHK; |
| } |
| |
| /******************************************************************************* |
| * Function smp_process_keypress_notification |
| * Description process pairing keypress notification from peer device |
| ******************************************************************************/ |
| void smp_process_keypress_notification(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t* p = p_data->p_data; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| p_cb->status = p_data->status; |
| |
| if (smp_command_has_invalid_parameters(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| if (p != NULL) { |
| STREAM_TO_UINT8(p_cb->peer_keypress_notification, p); |
| } else { |
| p_cb->peer_keypress_notification = BTM_SP_KEY_OUT_OF_RANGE; |
| } |
| p_cb->cb_evt = SMP_PEER_KEYPR_NOT_EVT; |
| } |
| |
| /******************************************************************************* |
| * Function smp_br_process_pairing_command |
| * Description Process the SMP pairing request/response from peer device via |
| * BR/EDR transport. |
| ******************************************************************************/ |
| void smp_br_process_pairing_command(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t* p = p_data->p_data; |
| tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(p_cb->pairing_bda); |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| /* rejecting BR pairing request over non-SC BR link */ |
| if (!p_dev_rec->new_encryption_key_is_p256 && p_cb->role == HCI_ROLE_SLAVE) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_XTRANS_DERIVE_NOT_ALLOW; |
| smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| /* erase all keys if it is slave proc pairing req*/ |
| if (p_dev_rec && (p_cb->role == HCI_ROLE_SLAVE)) |
| btm_sec_clear_ble_keys(p_dev_rec); |
| |
| p_cb->flags |= SMP_PAIR_FLAG_ENC_AFTER_PAIR; |
| |
| if (smp_command_has_invalid_length(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| android_errorWriteLog(0x534e4554, "111213909"); |
| smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| STREAM_TO_UINT8(p_cb->peer_io_caps, p); |
| STREAM_TO_UINT8(p_cb->peer_oob_flag, p); |
| STREAM_TO_UINT8(p_cb->peer_auth_req, p); |
| STREAM_TO_UINT8(p_cb->peer_enc_size, p); |
| STREAM_TO_UINT8(p_cb->peer_i_key, p); |
| STREAM_TO_UINT8(p_cb->peer_r_key, p); |
| |
| if (smp_command_has_invalid_parameters(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| /* peer (master) started pairing sending Pairing Request */ |
| /* or being master device always use received i/r key as keys to distribute */ |
| p_cb->local_i_key = p_cb->peer_i_key; |
| p_cb->local_r_key = p_cb->peer_r_key; |
| |
| if (p_cb->role == HCI_ROLE_SLAVE) { |
| p_dev_rec->new_encryption_key_is_p256 = false; |
| /* shortcut to skip Security Grant step */ |
| p_cb->cb_evt = SMP_BR_KEYS_REQ_EVT; |
| } else { |
| /* Master receives pairing response */ |
| SMP_TRACE_DEBUG( |
| "%s master rcvs valid PAIRING RESPONSE." |
| " Supposed to move to key distribution phase. ", |
| __func__); |
| } |
| |
| /* auth_req received via BR/EDR SM channel is set to 0, |
| but everything derived/exchanged has to be saved */ |
| p_cb->peer_auth_req |= SMP_AUTH_BOND; |
| p_cb->loc_auth_req |= SMP_AUTH_BOND; |
| } |
| |
| /******************************************************************************* |
| * Function smp_br_process_security_grant |
| * Description process security grant in case of pairing over BR/EDR transport. |
| ******************************************************************************/ |
| void smp_br_process_security_grant(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| if (p_data->status != SMP_SUCCESS) { |
| smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, p_data); |
| } else { |
| /* otherwise, start pairing; send IO request callback */ |
| p_cb->cb_evt = SMP_BR_KEYS_REQ_EVT; |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_br_check_authorization_request |
| * Description sets the SMP kes to be derived/distribute over BR/EDR transport |
| * before starting the distribution/derivation |
| ******************************************************************************/ |
| void smp_br_check_authorization_request(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s rcvs i_keys=0x%x r_keys=0x%x (i-initiator r-responder)", |
| __func__, p_cb->local_i_key, p_cb->local_r_key); |
| |
| /* In LE SC mode LK field is ignored when BR/EDR transport is used */ |
| p_cb->local_i_key &= ~SMP_SEC_KEY_TYPE_LK; |
| p_cb->local_r_key &= ~SMP_SEC_KEY_TYPE_LK; |
| |
| /* In LE SC mode only IRK, IAI, CSRK are exchanged with the peer. |
| ** Set local_r_key on master to expect only these keys. */ |
| if (p_cb->role == HCI_ROLE_MASTER) { |
| p_cb->local_r_key &= (SMP_SEC_KEY_TYPE_ID | SMP_SEC_KEY_TYPE_CSRK); |
| } |
| |
| /* Check if H7 function needs to be used for key derivation*/ |
| if ((p_cb->loc_auth_req & SMP_H7_SUPPORT_BIT) && |
| (p_cb->peer_auth_req & SMP_H7_SUPPORT_BIT)) { |
| p_cb->key_derivation_h7_used = TRUE; |
| } |
| SMP_TRACE_DEBUG("%s: use h7 = %d", __func__, p_cb->key_derivation_h7_used); |
| |
| SMP_TRACE_DEBUG( |
| "%s rcvs upgrades: i_keys=0x%x r_keys=0x%x (i-initiator r-responder)", |
| __func__, p_cb->local_i_key, p_cb->local_r_key); |
| |
| if (/*((p_cb->peer_auth_req & SMP_AUTH_BOND) || |
| (p_cb->loc_auth_req & SMP_AUTH_BOND)) &&*/ |
| (p_cb->local_i_key || p_cb->local_r_key)) { |
| smp_br_state_machine_event(p_cb, SMP_BR_BOND_REQ_EVT, NULL); |
| |
| /* if no peer key is expected, start master key distribution */ |
| if (p_cb->role == HCI_ROLE_MASTER && p_cb->local_r_key == 0) |
| smp_key_distribution_by_transport(p_cb, NULL); |
| } else { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_SUCCESS; |
| smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &smp_int_data); |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_br_select_next_key |
| * Description selects the next key to derive/send when BR/EDR transport is |
| * used. |
| ******************************************************************************/ |
| void smp_br_select_next_key(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s role=%d (0-master) r_keys=0x%x i_keys=0x%x", __func__, |
| p_cb->role, p_cb->local_r_key, p_cb->local_i_key); |
| |
| if (p_cb->role == HCI_ROLE_SLAVE || |
| (!p_cb->local_r_key && p_cb->role == HCI_ROLE_MASTER)) { |
| smp_key_pick_key(p_cb, p_data); |
| } |
| |
| if (!p_cb->local_i_key && !p_cb->local_r_key) { |
| /* state check to prevent re-entrance */ |
| if (smp_get_br_state() == SMP_BR_STATE_BOND_PENDING) { |
| if (p_cb->total_tx_unacked == 0) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_SUCCESS; |
| smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &smp_int_data); |
| } else { |
| p_cb->wait_for_authorization_complete = true; |
| } |
| } |
| } |
| } |
| |
| /** process encryption information from peer device */ |
| void smp_proc_enc_info(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t* p = p_data->p_data; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (smp_command_has_invalid_parameters(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| android_errorWriteLog(0x534e4554, "111937065"); |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| STREAM_TO_ARRAY(p_cb->ltk.data(), p, OCTET16_LEN); |
| |
| smp_key_distribution(p_cb, NULL); |
| } |
| |
| /** process master ID from slave device */ |
| void smp_proc_master_id(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t* p = p_data->p_data; |
| tBTM_LE_KEY_VALUE le_key; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (p_cb->rcvd_cmd_len < 11) { // 1(Code) + 2(EDIV) + 8(Rand) |
| android_errorWriteLog(0x534e4554, "111937027"); |
| SMP_TRACE_ERROR("%s: Invalid command length: %d, should be at least 11", |
| __func__, p_cb->rcvd_cmd_len); |
| return; |
| } |
| |
| smp_update_key_mask(p_cb, SMP_SEC_KEY_TYPE_ENC, true); |
| |
| STREAM_TO_UINT16(le_key.penc_key.ediv, p); |
| STREAM_TO_ARRAY(le_key.penc_key.rand, p, BT_OCTET8_LEN); |
| |
| /* store the encryption keys from peer device */ |
| le_key.penc_key.ltk = p_cb->ltk; |
| le_key.penc_key.sec_level = p_cb->sec_level; |
| le_key.penc_key.key_size = p_cb->loc_enc_size; |
| |
| if ((p_cb->peer_auth_req & SMP_AUTH_BOND) && |
| (p_cb->loc_auth_req & SMP_AUTH_BOND)) |
| btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_PENC, &le_key, true); |
| |
| smp_key_distribution(p_cb, NULL); |
| } |
| |
| /** process identity information from peer device */ |
| void smp_proc_id_info(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t* p = p_data->p_data; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (smp_command_has_invalid_parameters(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| android_errorWriteLog(0x534e4554, "111937065"); |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| STREAM_TO_ARRAY(p_cb->tk.data(), p, OCTET16_LEN); /* reuse TK for IRK */ |
| smp_key_distribution_by_transport(p_cb, NULL); |
| } |
| |
| /** process identity address from peer device */ |
| void smp_proc_id_addr(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t* p = p_data->p_data; |
| tBTM_LE_KEY_VALUE pid_key; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (smp_command_has_invalid_parameters(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| android_errorWriteLog(0x534e4554, "111214770"); |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| smp_update_key_mask(p_cb, SMP_SEC_KEY_TYPE_ID, true); |
| |
| STREAM_TO_UINT8(pid_key.pid_key.identity_addr_type, p); |
| STREAM_TO_BDADDR(pid_key.pid_key.identity_addr, p); |
| pid_key.pid_key.irk = p_cb->tk; |
| |
| /* to use as BD_ADDR for lk derived from ltk */ |
| p_cb->id_addr_rcvd = true; |
| p_cb->id_addr_type = pid_key.pid_key.identity_addr_type; |
| p_cb->id_addr = pid_key.pid_key.identity_addr; |
| |
| /* store the ID key from peer device */ |
| if ((p_cb->peer_auth_req & SMP_AUTH_BOND) && |
| (p_cb->loc_auth_req & SMP_AUTH_BOND)) |
| btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_PID, &pid_key, true); |
| smp_key_distribution_by_transport(p_cb, NULL); |
| } |
| |
| /* process security information from peer device */ |
| void smp_proc_srk_info(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| tBTM_LE_KEY_VALUE le_key; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (smp_command_has_invalid_parameters(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_INVALID_PARAMETERS; |
| android_errorWriteLog(0x534e4554, "111214470"); |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| smp_update_key_mask(p_cb, SMP_SEC_KEY_TYPE_CSRK, true); |
| |
| /* save CSRK to security record */ |
| le_key.pcsrk_key.sec_level = p_cb->sec_level; |
| |
| /* get peer CSRK */ |
| maybe_non_aligned_memcpy(le_key.pcsrk_key.csrk.data(), p_data->p_data, |
| OCTET16_LEN); |
| |
| /* initialize the peer counter */ |
| le_key.pcsrk_key.counter = 0; |
| |
| if ((p_cb->peer_auth_req & SMP_AUTH_BOND) && |
| (p_cb->loc_auth_req & SMP_AUTH_BOND)) |
| btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_PCSRK, &le_key, true); |
| smp_key_distribution_by_transport(p_cb, NULL); |
| } |
| |
| /******************************************************************************* |
| * Function smp_proc_compare |
| * Description process compare value |
| ******************************************************************************/ |
| void smp_proc_compare(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| if (!memcmp(p_cb->rconfirm.data(), p_data->key.p_data, OCTET16_LEN)) { |
| /* compare the max encryption key size, and save the smaller one for the |
| * link */ |
| if (p_cb->peer_enc_size < p_cb->loc_enc_size) |
| p_cb->loc_enc_size = p_cb->peer_enc_size; |
| |
| if (p_cb->role == HCI_ROLE_SLAVE) |
| smp_sm_event(p_cb, SMP_RAND_EVT, NULL); |
| else { |
| /* master device always use received i/r key as keys to distribute */ |
| p_cb->local_i_key = p_cb->peer_i_key; |
| p_cb->local_r_key = p_cb->peer_r_key; |
| |
| smp_sm_event(p_cb, SMP_ENC_REQ_EVT, NULL); |
| } |
| |
| } else { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_CONFIRM_VALUE_ERR; |
| p_cb->failure = SMP_CONFIRM_VALUE_ERR; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_proc_sl_key |
| * Description process key ready events. |
| ******************************************************************************/ |
| void smp_proc_sl_key(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t key_type = p_data->key.key_type; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| if (key_type == SMP_KEY_TYPE_TK) { |
| smp_generate_srand_mrand_confirm(p_cb, NULL); |
| } else if (key_type == SMP_KEY_TYPE_CFM) { |
| smp_set_state(SMP_STATE_WAIT_CONFIRM); |
| |
| if (p_cb->flags & SMP_PAIR_FLAGS_CMD_CONFIRM) |
| smp_sm_event(p_cb, SMP_CONFIRM_EVT, NULL); |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_start_enc |
| * Description start encryption |
| ******************************************************************************/ |
| void smp_start_enc(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| tBTM_STATUS cmd; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| if (p_data != NULL) { |
| cmd = btm_ble_start_encrypt(p_cb->pairing_bda, true, |
| (Octet16*)p_data->key.p_data); |
| } else { |
| cmd = btm_ble_start_encrypt(p_cb->pairing_bda, false, NULL); |
| } |
| |
| if (cmd != BTM_CMD_STARTED && cmd != BTM_BUSY) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_ENC_FAIL; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_proc_discard |
| * Description processing for discard security request |
| ******************************************************************************/ |
| void smp_proc_discard(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| if (!(p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD)) |
| smp_reset_control_value(p_cb); |
| } |
| |
| /******************************************************************************* |
| * Function smp_enc_cmpl |
| * Description encryption success |
| ******************************************************************************/ |
| void smp_enc_cmpl(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t enc_enable = p_data->status; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = enc_enable ? SMP_SUCCESS : SMP_ENC_FAIL; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| } |
| |
| /******************************************************************************* |
| * Function smp_check_auth_req |
| * Description check authentication request |
| ******************************************************************************/ |
| void smp_check_auth_req(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t enc_enable = p_data->status; |
| |
| SMP_TRACE_DEBUG( |
| "%s rcvs enc_enable=%d i_keys=0x%x r_keys=0x%x (i-initiator r-responder)", |
| __func__, enc_enable, p_cb->local_i_key, p_cb->local_r_key); |
| if (enc_enable == 1) { |
| if (p_cb->le_secure_connections_mode_is_used) { |
| /* In LE SC mode LTK is used instead of STK and has to be always saved */ |
| p_cb->local_i_key |= SMP_SEC_KEY_TYPE_ENC; |
| p_cb->local_r_key |= SMP_SEC_KEY_TYPE_ENC; |
| |
| /* In LE SC mode LK is derived from LTK only if both sides request it */ |
| if (!(p_cb->local_i_key & SMP_SEC_KEY_TYPE_LK) || |
| !(p_cb->local_r_key & SMP_SEC_KEY_TYPE_LK)) { |
| p_cb->local_i_key &= ~SMP_SEC_KEY_TYPE_LK; |
| p_cb->local_r_key &= ~SMP_SEC_KEY_TYPE_LK; |
| } |
| |
| /* In LE SC mode only IRK, IAI, CSRK are exchanged with the peer. |
| ** Set local_r_key on master to expect only these keys. |
| */ |
| if (p_cb->role == HCI_ROLE_MASTER) { |
| p_cb->local_r_key &= (SMP_SEC_KEY_TYPE_ID | SMP_SEC_KEY_TYPE_CSRK); |
| } |
| } else { |
| /* in legacy mode derivation of BR/EDR LK is not supported */ |
| p_cb->local_i_key &= ~SMP_SEC_KEY_TYPE_LK; |
| p_cb->local_r_key &= ~SMP_SEC_KEY_TYPE_LK; |
| } |
| SMP_TRACE_DEBUG( |
| "%s rcvs upgrades: i_keys=0x%x r_keys=0x%x (i-initiator r-responder)", |
| __func__, p_cb->local_i_key, p_cb->local_r_key); |
| |
| if (/*((p_cb->peer_auth_req & SMP_AUTH_BOND) || |
| (p_cb->loc_auth_req & SMP_AUTH_BOND)) &&*/ |
| (p_cb->local_i_key || p_cb->local_r_key)) { |
| smp_sm_event(p_cb, SMP_BOND_REQ_EVT, NULL); |
| } else { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = enc_enable ? SMP_SUCCESS : SMP_ENC_FAIL; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| } |
| } else if (enc_enable == 0) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = enc_enable ? SMP_SUCCESS : SMP_ENC_FAIL; |
| /* if failed for encryption after pairing, send callback */ |
| if (p_cb->flags & SMP_PAIR_FLAG_ENC_AFTER_PAIR) |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| /* if enc failed for old security information */ |
| /* if master device, clean up and abck to idle; slave device do nothing */ |
| else if (p_cb->role == HCI_ROLE_MASTER) { |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_key_pick_key |
| * Description Pick a key distribution function based on the key mask. |
| ******************************************************************************/ |
| void smp_key_pick_key(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t key_to_dist = |
| (p_cb->role == HCI_ROLE_SLAVE) ? p_cb->local_r_key : p_cb->local_i_key; |
| uint8_t i = 0; |
| |
| SMP_TRACE_DEBUG("%s key_to_dist=0x%x", __func__, key_to_dist); |
| while (i < SMP_KEY_DIST_TYPE_MAX) { |
| SMP_TRACE_DEBUG("key to send = %02x, i = %d", key_to_dist, i); |
| |
| if (key_to_dist & (1 << i)) { |
| SMP_TRACE_DEBUG("smp_distribute_act[%d]", i); |
| (*smp_distribute_act[i])(p_cb, p_data); |
| break; |
| } |
| i++; |
| } |
| } |
| /******************************************************************************* |
| * Function smp_key_distribution |
| * Description start key distribution if required. |
| ******************************************************************************/ |
| void smp_key_distribution(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s role=%d (0-master) r_keys=0x%x i_keys=0x%x", __func__, |
| p_cb->role, p_cb->local_r_key, p_cb->local_i_key); |
| |
| if (p_cb->role == HCI_ROLE_SLAVE || |
| (!p_cb->local_r_key && p_cb->role == HCI_ROLE_MASTER)) { |
| smp_key_pick_key(p_cb, p_data); |
| } |
| |
| if (!p_cb->local_i_key && !p_cb->local_r_key) { |
| /* state check to prevent re-entrant */ |
| if (smp_get_state() == SMP_STATE_BOND_PENDING) { |
| if (p_cb->derive_lk) { |
| smp_derive_link_key_from_long_term_key(p_cb, NULL); |
| p_cb->derive_lk = false; |
| } |
| |
| if (p_cb->total_tx_unacked == 0) { |
| /* |
| * Instead of declaring authorization complete immediately, |
| * delay the event from being sent by SMP_DELAYED_AUTH_TIMEOUT_MS. |
| * This allows the slave to send over Pairing Failed if the |
| * last key is rejected. During this waiting window, the |
| * state should remain in SMP_STATE_BOND_PENDING. |
| */ |
| if (!alarm_is_scheduled(p_cb->delayed_auth_timer_ent)) { |
| SMP_TRACE_DEBUG("%s delaying auth complete.", __func__); |
| alarm_set_on_mloop(p_cb->delayed_auth_timer_ent, |
| SMP_DELAYED_AUTH_TIMEOUT_MS, |
| smp_delayed_auth_complete_timeout, NULL); |
| } |
| } else { |
| p_cb->wait_for_authorization_complete = true; |
| } |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_decide_association_model |
| * Description This function is called to select assoc model to be used for |
| * STK generation and to start STK generation process. |
| * |
| ******************************************************************************/ |
| void smp_decide_association_model(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t int_evt = 0; |
| tSMP_INT_DATA smp_int_data; |
| |
| SMP_TRACE_DEBUG("%s Association Model = %d", __func__, |
| p_cb->selected_association_model); |
| |
| switch (p_cb->selected_association_model) { |
| case SMP_MODEL_ENCRYPTION_ONLY: /* TK = 0, go calculate Confirm */ |
| if (p_cb->role == HCI_ROLE_MASTER && |
| ((p_cb->peer_auth_req & SMP_AUTH_YN_BIT) != 0) && |
| ((p_cb->loc_auth_req & SMP_AUTH_YN_BIT) == 0)) { |
| SMP_TRACE_ERROR( |
| "IO capability does not meet authentication requirement"); |
| smp_int_data.status = SMP_PAIR_AUTH_FAIL; |
| int_evt = SMP_AUTH_CMPL_EVT; |
| } else { |
| p_cb->sec_level = SMP_SEC_UNAUTHENTICATE; |
| SMP_TRACE_EVENT("p_cb->sec_level =%d (SMP_SEC_UNAUTHENTICATE) ", |
| p_cb->sec_level); |
| |
| tSMP_KEY key; |
| key.key_type = SMP_KEY_TYPE_TK; |
| key.p_data = p_cb->tk.data(); |
| smp_int_data.key = key; |
| |
| p_cb->tk = {0}; |
| /* TK, ready */ |
| int_evt = SMP_KEY_READY_EVT; |
| } |
| break; |
| |
| case SMP_MODEL_PASSKEY: |
| p_cb->sec_level = SMP_SEC_AUTHENTICATED; |
| SMP_TRACE_EVENT("p_cb->sec_level =%d (SMP_SEC_AUTHENTICATED) ", |
| p_cb->sec_level); |
| |
| p_cb->cb_evt = SMP_PASSKEY_REQ_EVT; |
| int_evt = SMP_TK_REQ_EVT; |
| break; |
| |
| case SMP_MODEL_OOB: |
| SMP_TRACE_ERROR("Association Model = SMP_MODEL_OOB"); |
| p_cb->sec_level = SMP_SEC_AUTHENTICATED; |
| SMP_TRACE_EVENT("p_cb->sec_level =%d (SMP_SEC_AUTHENTICATED) ", |
| p_cb->sec_level); |
| |
| p_cb->cb_evt = SMP_OOB_REQ_EVT; |
| int_evt = SMP_TK_REQ_EVT; |
| break; |
| |
| case SMP_MODEL_KEY_NOTIF: |
| p_cb->sec_level = SMP_SEC_AUTHENTICATED; |
| SMP_TRACE_DEBUG("Need to generate Passkey"); |
| |
| /* generate passkey and notify application */ |
| smp_generate_passkey(p_cb, NULL); |
| break; |
| |
| case SMP_MODEL_SEC_CONN_JUSTWORKS: |
| case SMP_MODEL_SEC_CONN_NUM_COMP: |
| case SMP_MODEL_SEC_CONN_PASSKEY_ENT: |
| case SMP_MODEL_SEC_CONN_PASSKEY_DISP: |
| case SMP_MODEL_SEC_CONN_OOB: |
| int_evt = SMP_PUBL_KEY_EXCH_REQ_EVT; |
| break; |
| |
| case SMP_MODEL_OUT_OF_RANGE: |
| SMP_TRACE_ERROR("Association Model = SMP_MODEL_OUT_OF_RANGE (failed)"); |
| smp_int_data.status = SMP_UNKNOWN_IO_CAP; |
| int_evt = SMP_AUTH_CMPL_EVT; |
| break; |
| |
| default: |
| SMP_TRACE_ERROR( |
| "Association Model = %d (SOMETHING IS WRONG WITH THE CODE)", |
| p_cb->selected_association_model); |
| smp_int_data.status = SMP_UNKNOWN_IO_CAP; |
| int_evt = SMP_AUTH_CMPL_EVT; |
| } |
| |
| SMP_TRACE_EVENT("sec_level=%d ", p_cb->sec_level); |
| if (int_evt) smp_sm_event(p_cb, int_evt, &smp_int_data); |
| } |
| |
| /******************************************************************************* |
| * Function smp_process_io_response |
| * Description process IO response for a slave device. |
| ******************************************************************************/ |
| void smp_process_io_response(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| if (p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD) { |
| /* pairing started by local (slave) Security Request */ |
| smp_set_state(SMP_STATE_SEC_REQ_PENDING); |
| smp_send_cmd(SMP_OPCODE_SEC_REQ, p_cb); |
| } else /* plan to send pairing respond */ |
| { |
| /* pairing started by peer (master) Pairing Request */ |
| p_cb->selected_association_model = smp_select_association_model(p_cb); |
| |
| if (p_cb->secure_connections_only_mode_required && |
| (!(p_cb->le_secure_connections_mode_is_used) || |
| (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_JUSTWORKS))) { |
| SMP_TRACE_ERROR( |
| "Slave requires secure connection only mode " |
| "but it can't be provided -> Slave fails pairing"); |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_PAIR_AUTH_FAIL; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB) { |
| if (smp_request_oob_data(p_cb)) return; |
| } |
| |
| // PTS Testing failure modes |
| if (pts_test_send_authentication_complete_failure(p_cb)) return; |
| |
| smp_send_pair_rsp(p_cb, NULL); |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_br_process_slave_keys_response |
| * Description process application keys response for a slave device |
| * (BR/EDR transport). |
| ******************************************************************************/ |
| void smp_br_process_slave_keys_response(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| smp_br_send_pair_response(p_cb, NULL); |
| } |
| |
| /******************************************************************************* |
| * Function smp_br_send_pair_response |
| * Description actions related to sending pairing response over BR/EDR |
| * transport. |
| ******************************************************************************/ |
| void smp_br_send_pair_response(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| p_cb->local_i_key &= p_cb->peer_i_key; |
| p_cb->local_r_key &= p_cb->peer_r_key; |
| |
| smp_send_cmd(SMP_OPCODE_PAIRING_RSP, p_cb); |
| } |
| |
| /******************************************************************************* |
| * Function smp_pairing_cmpl |
| * Description This function is called to send the pairing complete |
| * callback and remove the connection if needed. |
| ******************************************************************************/ |
| void smp_pairing_cmpl(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| if (p_cb->total_tx_unacked == 0) { |
| /* process the pairing complete */ |
| smp_proc_pairing_cmpl(p_cb); |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_pair_terminate |
| * Description This function is called to send the pairing complete |
| * callback and remove the connection if needed. |
| ******************************************************************************/ |
| void smp_pair_terminate(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| p_cb->status = SMP_CONN_TOUT; |
| smp_proc_pairing_cmpl(p_cb); |
| } |
| |
| /******************************************************************************* |
| * Function smp_idle_terminate |
| * Description This function calledin idle state to determine to send |
| * authentication complete or not. |
| ******************************************************************************/ |
| void smp_idle_terminate(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| if (p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD) { |
| SMP_TRACE_DEBUG("Pairing terminated at IDLE state."); |
| p_cb->status = SMP_FAIL; |
| smp_proc_pairing_cmpl(p_cb); |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_both_have_public_keys |
| * Description The function is called when both local and peer public keys are |
| * saved. |
| * Actions: |
| * - invokes DHKey computation; |
| * - on slave side invokes sending local public key to the peer. |
| * - invokes SC phase 1 process. |
| ******************************************************************************/ |
| void smp_both_have_public_keys(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| /* invokes DHKey computation */ |
| smp_compute_dhkey(p_cb); |
| |
| /* on slave side invokes sending local public key to the peer */ |
| if (p_cb->role == HCI_ROLE_SLAVE) smp_send_pair_public_key(p_cb, NULL); |
| |
| smp_sm_event(p_cb, SMP_SC_DHKEY_CMPLT_EVT, NULL); |
| } |
| |
| /******************************************************************************* |
| * Function smp_start_secure_connection_phase1 |
| * Description Start Secure Connection phase1 i.e. invokes initialization of |
| * Secure Connection phase 1 parameters and starts building/sending |
| * to the peer messages appropriate for the role and association |
| * model. |
| ******************************************************************************/ |
| void smp_start_secure_connection_phase1(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_JUSTWORKS) { |
| p_cb->sec_level = SMP_SEC_UNAUTHENTICATE; |
| SMP_TRACE_EVENT("p_cb->sec_level =%d (SMP_SEC_UNAUTHENTICATE) ", |
| p_cb->sec_level); |
| } else { |
| p_cb->sec_level = SMP_SEC_AUTHENTICATED; |
| SMP_TRACE_EVENT("p_cb->sec_level =%d (SMP_SEC_AUTHENTICATED) ", |
| p_cb->sec_level); |
| } |
| |
| switch (p_cb->selected_association_model) { |
| case SMP_MODEL_SEC_CONN_JUSTWORKS: |
| case SMP_MODEL_SEC_CONN_NUM_COMP: |
| p_cb->local_random = {0}; |
| smp_start_nonce_generation(p_cb); |
| break; |
| case SMP_MODEL_SEC_CONN_PASSKEY_ENT: |
| /* user has to provide passkey */ |
| p_cb->cb_evt = SMP_PASSKEY_REQ_EVT; |
| smp_sm_event(p_cb, SMP_TK_REQ_EVT, NULL); |
| break; |
| case SMP_MODEL_SEC_CONN_PASSKEY_DISP: |
| /* passkey has to be provided to user */ |
| SMP_TRACE_DEBUG("Need to generate SC Passkey"); |
| smp_generate_passkey(p_cb, NULL); |
| break; |
| case SMP_MODEL_SEC_CONN_OOB: |
| /* use the available OOB information */ |
| smp_process_secure_connection_oob_data(p_cb, NULL); |
| break; |
| default: |
| SMP_TRACE_ERROR("Association Model = %d is not used in LE SC", |
| p_cb->selected_association_model); |
| break; |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_process_local_nonce |
| * Description The function processes new local nonce. |
| * |
| * Note It is supposed to be called in SC phase1. |
| ******************************************************************************/ |
| void smp_process_local_nonce(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| switch (p_cb->selected_association_model) { |
| case SMP_MODEL_SEC_CONN_JUSTWORKS: |
| case SMP_MODEL_SEC_CONN_NUM_COMP: |
| if (p_cb->role == HCI_ROLE_SLAVE) { |
| /* slave calculates and sends local commitment */ |
| smp_calculate_local_commitment(p_cb); |
| smp_send_commitment(p_cb, NULL); |
| /* slave has to wait for peer nonce */ |
| smp_set_state(SMP_STATE_WAIT_NONCE); |
| } else /* i.e. master */ |
| { |
| if (p_cb->flags & SMP_PAIR_FLAG_HAVE_PEER_COMM) { |
| /* slave commitment is already received, send local nonce, wait for |
| * remote nonce*/ |
| SMP_TRACE_DEBUG( |
| "master in assoc mode = %d " |
| "already rcvd slave commitment - race condition", |
| p_cb->selected_association_model); |
| p_cb->flags &= ~SMP_PAIR_FLAG_HAVE_PEER_COMM; |
| smp_send_rand(p_cb, NULL); |
| smp_set_state(SMP_STATE_WAIT_NONCE); |
| } |
| } |
| break; |
| case SMP_MODEL_SEC_CONN_PASSKEY_ENT: |
| case SMP_MODEL_SEC_CONN_PASSKEY_DISP: |
| smp_calculate_local_commitment(p_cb); |
| |
| if (p_cb->role == HCI_ROLE_MASTER) { |
| smp_send_commitment(p_cb, NULL); |
| } else /* slave */ |
| { |
| if (p_cb->flags & SMP_PAIR_FLAG_HAVE_PEER_COMM) { |
| /* master commitment is already received */ |
| smp_send_commitment(p_cb, NULL); |
| smp_set_state(SMP_STATE_WAIT_NONCE); |
| } |
| } |
| break; |
| case SMP_MODEL_SEC_CONN_OOB: |
| if (p_cb->role == HCI_ROLE_MASTER) { |
| smp_send_rand(p_cb, NULL); |
| } |
| |
| smp_set_state(SMP_STATE_WAIT_NONCE); |
| break; |
| default: |
| SMP_TRACE_ERROR("Association Model = %d is not used in LE SC", |
| p_cb->selected_association_model); |
| break; |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_process_peer_nonce |
| * Description The function processes newly received and saved in CB peer |
| * nonce. The actions depend on the selected association model and |
| * the role. |
| * |
| * Note It is supposed to be called in SC phase1. |
| ******************************************************************************/ |
| void smp_process_peer_nonce(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s start ", __func__); |
| |
| // PTS Testing failure modes |
| if (p_cb->cert_failure == SMP_CONFIRM_VALUE_ERR) { |
| SMP_TRACE_ERROR("%s failure case = %d", __func__, p_cb->cert_failure); |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_CONFIRM_VALUE_ERR; |
| p_cb->failure = SMP_CONFIRM_VALUE_ERR; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| // PTS Testing failure modes (for LT) |
| if ((p_cb->cert_failure == SMP_NUMERIC_COMPAR_FAIL) && |
| (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_JUSTWORKS) && |
| (p_cb->role == HCI_ROLE_SLAVE)) { |
| SMP_TRACE_ERROR("%s failure case = %d", __func__, p_cb->cert_failure); |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_NUMERIC_COMPAR_FAIL; |
| p_cb->failure = SMP_NUMERIC_COMPAR_FAIL; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| switch (p_cb->selected_association_model) { |
| case SMP_MODEL_SEC_CONN_JUSTWORKS: |
| case SMP_MODEL_SEC_CONN_NUM_COMP: |
| /* in these models only master receives commitment */ |
| if (p_cb->role == HCI_ROLE_MASTER) { |
| if (!smp_check_commitment(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_CONFIRM_VALUE_ERR; |
| p_cb->failure = SMP_CONFIRM_VALUE_ERR; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| break; |
| } |
| } else { |
| /* slave sends local nonce */ |
| smp_send_rand(p_cb, NULL); |
| } |
| |
| if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_JUSTWORKS) { |
| /* go directly to phase 2 */ |
| smp_sm_event(p_cb, SMP_SC_PHASE1_CMPLT_EVT, NULL); |
| } else /* numeric comparison */ |
| { |
| smp_set_state(SMP_STATE_WAIT_NONCE); |
| smp_sm_event(p_cb, SMP_SC_CALC_NC_EVT, NULL); |
| } |
| break; |
| case SMP_MODEL_SEC_CONN_PASSKEY_ENT: |
| case SMP_MODEL_SEC_CONN_PASSKEY_DISP: |
| if (!smp_check_commitment(p_cb) && |
| p_cb->cert_failure != SMP_NUMERIC_COMPAR_FAIL) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_CONFIRM_VALUE_ERR; |
| p_cb->failure = SMP_CONFIRM_VALUE_ERR; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| break; |
| } |
| |
| if (p_cb->role == HCI_ROLE_SLAVE) { |
| smp_send_rand(p_cb, NULL); |
| } |
| |
| if (++p_cb->round < 20) { |
| smp_set_state(SMP_STATE_SEC_CONN_PHS1_START); |
| p_cb->flags &= ~SMP_PAIR_FLAG_HAVE_PEER_COMM; |
| smp_start_nonce_generation(p_cb); |
| break; |
| } |
| |
| smp_sm_event(p_cb, SMP_SC_PHASE1_CMPLT_EVT, NULL); |
| break; |
| case SMP_MODEL_SEC_CONN_OOB: |
| if (p_cb->role == HCI_ROLE_SLAVE) { |
| smp_send_rand(p_cb, NULL); |
| } |
| |
| smp_sm_event(p_cb, SMP_SC_PHASE1_CMPLT_EVT, NULL); |
| break; |
| default: |
| SMP_TRACE_ERROR("Association Model = %d is not used in LE SC", |
| p_cb->selected_association_model); |
| break; |
| } |
| |
| SMP_TRACE_DEBUG("%s end ", __func__); |
| } |
| |
| /******************************************************************************* |
| * Function smp_match_dhkey_checks |
| * Description checks if the calculated peer DHKey Check value is the same as |
| * received from the peer DHKey check value. |
| ******************************************************************************/ |
| void smp_match_dhkey_checks(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (memcmp(p_data->key.p_data, p_cb->remote_dhkey_check.data(), |
| OCTET16_LEN)) { |
| SMP_TRACE_WARNING("dhkey chcks do no match"); |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_DHKEY_CHK_FAIL; |
| p_cb->failure = SMP_DHKEY_CHK_FAIL; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| SMP_TRACE_EVENT("dhkey chcks match"); |
| |
| /* compare the max encryption key size, and save the smaller one for the link |
| */ |
| if (p_cb->peer_enc_size < p_cb->loc_enc_size) |
| p_cb->loc_enc_size = p_cb->peer_enc_size; |
| |
| if (p_cb->role == HCI_ROLE_SLAVE) { |
| smp_sm_event(p_cb, SMP_PAIR_DHKEY_CHCK_EVT, NULL); |
| } else { |
| /* master device always use received i/r key as keys to distribute */ |
| p_cb->local_i_key = p_cb->peer_i_key; |
| p_cb->local_r_key = p_cb->peer_r_key; |
| smp_sm_event(p_cb, SMP_ENC_REQ_EVT, NULL); |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_move_to_secure_connections_phase2 |
| * Description Signal State Machine to start SC phase 2 initialization (to |
| * compute local DHKey Check value). |
| * |
| * Note SM is supposed to be in the state SMP_STATE_SEC_CONN_PHS2_START. |
| ******************************************************************************/ |
| void smp_move_to_secure_connections_phase2(tSMP_CB* p_cb, |
| tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| smp_sm_event(p_cb, SMP_SC_PHASE1_CMPLT_EVT, NULL); |
| } |
| |
| /******************************************************************************* |
| * Function smp_phase_2_dhkey_checks_are_present |
| * Description generates event if dhkey check from the peer is already |
| * received. |
| * |
| * Note It is supposed to be used on slave to prevent race condition. |
| * It is supposed to be called after slave dhkey check is |
| * calculated. |
| ******************************************************************************/ |
| void smp_phase_2_dhkey_checks_are_present(tSMP_CB* p_cb, |
| tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (p_cb->flags & SMP_PAIR_FLAG_HAVE_PEER_DHK_CHK) |
| smp_sm_event(p_cb, SMP_SC_2_DHCK_CHKS_PRES_EVT, NULL); |
| } |
| |
| /******************************************************************************* |
| * Function smp_wait_for_both_public_keys |
| * Description generates SMP_BOTH_PUBL_KEYS_RCVD_EVT event when both local and |
| * master public keys are available. |
| * |
| * Note on the slave it is used to prevent race condition. |
| * |
| ******************************************************************************/ |
| void smp_wait_for_both_public_keys(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if ((p_cb->flags & SMP_PAIR_FLAG_HAVE_PEER_PUBL_KEY) && |
| (p_cb->flags & SMP_PAIR_FLAG_HAVE_LOCAL_PUBL_KEY)) { |
| if ((p_cb->role == HCI_ROLE_SLAVE) && |
| ((p_cb->req_oob_type == SMP_OOB_LOCAL) || |
| (p_cb->req_oob_type == SMP_OOB_BOTH))) { |
| smp_set_state(SMP_STATE_PUBLIC_KEY_EXCH); |
| } |
| smp_sm_event(p_cb, SMP_BOTH_PUBL_KEYS_RCVD_EVT, NULL); |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_start_passkey_verification |
| * Description Starts SC passkey entry verification. |
| ******************************************************************************/ |
| void smp_start_passkey_verification(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| uint8_t* p = NULL; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| p = p_cb->local_random.data(); |
| UINT32_TO_STREAM(p, p_data->passkey); |
| |
| p = p_cb->peer_random.data(); |
| UINT32_TO_STREAM(p, p_data->passkey); |
| |
| p_cb->round = 0; |
| smp_start_nonce_generation(p_cb); |
| } |
| |
| /******************************************************************************* |
| * Function smp_process_secure_connection_oob_data |
| * Description Processes local/peer SC OOB data received from somewhere. |
| ******************************************************************************/ |
| void smp_process_secure_connection_oob_data(tSMP_CB* p_cb, |
| tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| tSMP_SC_OOB_DATA* p_sc_oob_data = &p_cb->sc_oob_data; |
| if (p_sc_oob_data->loc_oob_data.present) { |
| p_cb->local_random = p_sc_oob_data->loc_oob_data.randomizer; |
| } else { |
| SMP_TRACE_EVENT("%s: local OOB randomizer is absent", __func__); |
| p_cb->local_random = {0}; |
| } |
| |
| if (!p_sc_oob_data->peer_oob_data.present) { |
| SMP_TRACE_EVENT("%s: peer OOB data is absent", __func__); |
| p_cb->peer_random = {0}; |
| } else { |
| p_cb->peer_random = p_sc_oob_data->peer_oob_data.randomizer; |
| p_cb->remote_commitment = p_sc_oob_data->peer_oob_data.commitment; |
| |
| /* check commitment */ |
| if (!smp_check_commitment(p_cb)) { |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = SMP_CONFIRM_VALUE_ERR; |
| p_cb->failure = SMP_CONFIRM_VALUE_ERR; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| if (p_cb->peer_oob_flag != SMP_OOB_PRESENT) { |
| /* the peer doesn't have local randomiser */ |
| SMP_TRACE_EVENT( |
| "%s: peer didn't receive local OOB data, set local randomizer to 0", |
| __func__); |
| p_cb->local_random = {0}; |
| } |
| } |
| |
| print128(p_cb->local_random, (const uint8_t*)"local OOB randomizer"); |
| print128(p_cb->peer_random, (const uint8_t*)"peer OOB randomizer"); |
| smp_start_nonce_generation(p_cb); |
| } |
| |
| /******************************************************************************* |
| * Function smp_set_local_oob_keys |
| * Description Saves calculated private/public keys in |
| * sc_oob_data.loc_oob_data, starts nonce generation |
| * (to be saved in sc_oob_data.loc_oob_data.randomizer). |
| ******************************************************************************/ |
| void smp_set_local_oob_keys(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| memcpy(p_cb->sc_oob_data.loc_oob_data.private_key_used, p_cb->private_key, |
| BT_OCTET32_LEN); |
| p_cb->sc_oob_data.loc_oob_data.publ_key_used = p_cb->loc_publ_key; |
| smp_start_nonce_generation(p_cb); |
| } |
| |
| /******************************************************************************* |
| * Function smp_set_local_oob_random_commitment |
| * Description Saves calculated randomizer and commitment in |
| * sc_oob_data.loc_oob_data, passes sc_oob_data.loc_oob_data up |
| * for safekeeping. |
| ******************************************************************************/ |
| void smp_set_local_oob_random_commitment(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| p_cb->sc_oob_data.loc_oob_data.randomizer = p_cb->rand; |
| |
| p_cb->sc_oob_data.loc_oob_data.commitment = |
| crypto_toolbox::f4(p_cb->sc_oob_data.loc_oob_data.publ_key_used.x, |
| p_cb->sc_oob_data.loc_oob_data.publ_key_used.x, |
| p_cb->sc_oob_data.loc_oob_data.randomizer, 0); |
| |
| #if (SMP_DEBUG == TRUE) |
| uint8_t* p_print = NULL; |
| SMP_TRACE_DEBUG("local SC OOB data set:"); |
| p_print = (uint8_t*)&p_cb->sc_oob_data.loc_oob_data.addr_sent_to; |
| smp_debug_print_nbyte_little_endian(p_print, "addr_sent_to", |
| sizeof(tBLE_BD_ADDR)); |
| p_print = (uint8_t*)&p_cb->sc_oob_data.loc_oob_data.private_key_used; |
| smp_debug_print_nbyte_little_endian(p_print, "private_key_used", |
| BT_OCTET32_LEN); |
| p_print = (uint8_t*)&p_cb->sc_oob_data.loc_oob_data.publ_key_used.x; |
| smp_debug_print_nbyte_little_endian(p_print, "publ_key_used.x", |
| BT_OCTET32_LEN); |
| p_print = (uint8_t*)&p_cb->sc_oob_data.loc_oob_data.publ_key_used.y; |
| smp_debug_print_nbyte_little_endian(p_print, "publ_key_used.y", |
| BT_OCTET32_LEN); |
| p_print = (uint8_t*)&p_cb->sc_oob_data.loc_oob_data.randomizer; |
| smp_debug_print_nbyte_little_endian(p_print, "randomizer", OCTET16_LEN); |
| p_print = (uint8_t*)&p_cb->sc_oob_data.loc_oob_data.commitment; |
| smp_debug_print_nbyte_little_endian(p_print, "commitment", OCTET16_LEN); |
| SMP_TRACE_DEBUG(""); |
| #endif |
| |
| /* pass created OOB data up */ |
| p_cb->cb_evt = SMP_SC_LOC_OOB_DATA_UP_EVT; |
| smp_send_app_cback(p_cb, NULL); |
| |
| smp_cb_cleanup(p_cb); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function smp_link_encrypted |
| * |
| * Description This function is called when link is encrypted and notified |
| * to the slave device. Proceed to to send LTK, DIV and ER to |
| * master if bonding the devices. |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void smp_link_encrypted(const RawAddress& bda, uint8_t encr_enable) { |
| tSMP_CB* p_cb = &smp_cb; |
| |
| SMP_TRACE_DEBUG("%s: encr_enable=%d", __func__, encr_enable); |
| |
| if (smp_cb.pairing_bda == bda) { |
| /* encryption completed with STK, remember the key size now, could be |
| * overwritten when key exchange happens */ |
| if (p_cb->loc_enc_size != 0 && encr_enable) { |
| /* update the link encryption key size if a SMP pairing just performed */ |
| btm_ble_update_sec_key_size(bda, p_cb->loc_enc_size); |
| } |
| |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = encr_enable; |
| smp_sm_event(&smp_cb, SMP_ENCRYPTED_EVT, &smp_int_data); |
| } |
| } |
| |
| void smp_cancel_start_encryption_attempt() { |
| SMP_TRACE_ERROR("%s: Encryption request cancelled", __func__); |
| smp_sm_event(&smp_cb, SMP_DISCARD_SEC_REQ_EVT, NULL); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function smp_proc_ltk_request |
| * |
| * Description This function is called when LTK request is received from |
| * controller. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| bool smp_proc_ltk_request(const RawAddress& bda) { |
| SMP_TRACE_DEBUG("%s state = %d", __func__, smp_cb.state); |
| bool match = false; |
| |
| if (bda == smp_cb.pairing_bda) { |
| match = true; |
| } else { |
| tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bda); |
| if (p_dev_rec != NULL && p_dev_rec->ble.pseudo_addr == smp_cb.pairing_bda && |
| p_dev_rec->ble.pseudo_addr != RawAddress::kEmpty) { |
| match = true; |
| } |
| } |
| |
| if (match && smp_cb.state == SMP_STATE_ENCRYPTION_PENDING) { |
| smp_sm_event(&smp_cb, SMP_ENC_REQ_EVT, NULL); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function smp_process_secure_connection_long_term_key |
| * |
| * Description This function is called to process SC LTK. |
| * SC LTK is calculated and used instead of STK. |
| * Here SC LTK is saved in BLE DB. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void smp_process_secure_connection_long_term_key(void) { |
| tSMP_CB* p_cb = &smp_cb; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| smp_save_secure_connections_long_term_key(p_cb); |
| |
| smp_update_key_mask(p_cb, SMP_SEC_KEY_TYPE_ENC, false); |
| smp_key_distribution(p_cb, NULL); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function smp_set_derive_link_key |
| * |
| * Description This function is called to set flag that indicates that |
| * BR/EDR LK has to be derived from LTK after all keys are |
| * distributed. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void smp_set_derive_link_key(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| p_cb->derive_lk = true; |
| smp_update_key_mask(p_cb, SMP_SEC_KEY_TYPE_LK, false); |
| smp_key_distribution(p_cb, NULL); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function smp_derive_link_key_from_long_term_key |
| * |
| * Description This function is called to derive BR/EDR LK from LTK. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void smp_derive_link_key_from_long_term_key(tSMP_CB* p_cb, |
| tSMP_INT_DATA* p_data) { |
| tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| if (!smp_calculate_link_key_from_long_term_key(p_cb)) { |
| SMP_TRACE_ERROR("%s failed", __func__); |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = status; |
| smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function smp_br_process_link_key |
| * |
| * Description This function is called to process BR/EDR LK: |
| * - to derive SMP LTK from BR/EDR LK; |
| * - to save SMP LTK. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void smp_br_process_link_key(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; |
| |
| SMP_TRACE_DEBUG("%s", __func__); |
| if (!smp_calculate_long_term_key_from_link_key(p_cb)) { |
| SMP_TRACE_ERROR("%s: failed", __func__); |
| tSMP_INT_DATA smp_int_data; |
| smp_int_data.status = status; |
| smp_sm_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &smp_int_data); |
| return; |
| } |
| |
| tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(p_cb->pairing_bda); |
| if (p_dev_rec) { |
| SMP_TRACE_DEBUG("%s: dev_type = %d ", __func__, p_dev_rec->device_type); |
| p_dev_rec->device_type |= BT_DEVICE_TYPE_BLE; |
| } else { |
| SMP_TRACE_ERROR("%s failed to find Security Record", __func__); |
| } |
| |
| SMP_TRACE_DEBUG("%s: LTK derivation from LK successfully completed", |
| __func__); |
| smp_save_secure_connections_long_term_key(p_cb); |
| smp_update_key_mask(p_cb, SMP_SEC_KEY_TYPE_ENC, false); |
| smp_br_select_next_key(p_cb, NULL); |
| } |
| |
| /******************************************************************************* |
| * Function smp_key_distribution_by_transport |
| * Description depending on the transport used at the moment calls either |
| * smp_key_distribution(...) or smp_br_key_distribution(...). |
| ******************************************************************************/ |
| void smp_key_distribution_by_transport(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| if (p_cb->smp_over_br) { |
| smp_br_select_next_key(p_cb, NULL); |
| } else { |
| smp_key_distribution(p_cb, NULL); |
| } |
| } |
| |
| /******************************************************************************* |
| * Function smp_br_pairing_complete |
| * Description This function is called to send the pairing complete |
| * callback and remove the connection if needed. |
| ******************************************************************************/ |
| void smp_br_pairing_complete(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { |
| SMP_TRACE_DEBUG("%s", __func__); |
| |
| if (p_cb->total_tx_unacked == 0) { |
| /* process the pairing complete */ |
| smp_proc_pairing_cmpl(p_cb); |
| } |
| } |