/******************************************************************************
 *
 *  Copyright (C) 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 <string.h>
#include "device/include/interop.h"
#include "include/bt_target.h"
#include "stack/btm/btm_int.h"
#include "stack/include/l2c_api.h"
#include "stack/smp/smp_int.h"
#include "utils/include/bt_utils.h"

#if SMP_INCLUDED == TRUE
const UINT8 smp_association_table[2][SMP_IO_CAP_MAX][SMP_IO_CAP_MAX] =
{
    /* initiator */
    {{SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY,   SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY}, /* Display Only */
        {SMP_MODEL_ENCRYPTION_ONLY,  SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY}, /* SMP_CAP_IO = 1 */
        {SMP_MODEL_KEY_NOTIF, SMP_MODEL_KEY_NOTIF,  SMP_MODEL_PASSKEY,   SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_KEY_NOTIF}, /* keyboard only */
        {SMP_MODEL_ENCRYPTION_ONLY,  SMP_MODEL_ENCRYPTION_ONLY,   SMP_MODEL_ENCRYPTION_ONLY,  SMP_MODEL_ENCRYPTION_ONLY,    SMP_MODEL_ENCRYPTION_ONLY},/* No Input No Output */
        {SMP_MODEL_KEY_NOTIF, SMP_MODEL_KEY_NOTIF,  SMP_MODEL_PASSKEY,   SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_KEY_NOTIF}}, /* keyboard display */
    /* responder */
    {{SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY,   SMP_MODEL_KEY_NOTIF, SMP_MODEL_ENCRYPTION_ONLY,    SMP_MODEL_KEY_NOTIF}, /* Display Only */
        {SMP_MODEL_ENCRYPTION_ONLY,  SMP_MODEL_ENCRYPTION_ONLY,   SMP_MODEL_KEY_NOTIF,   SMP_MODEL_ENCRYPTION_ONLY,    SMP_MODEL_KEY_NOTIF}, /* SMP_CAP_IO = 1 */
        {SMP_MODEL_PASSKEY,   SMP_MODEL_PASSKEY,    SMP_MODEL_PASSKEY,   SMP_MODEL_ENCRYPTION_ONLY,    SMP_MODEL_PASSKEY}, /* keyboard only */
        {SMP_MODEL_ENCRYPTION_ONLY,  SMP_MODEL_ENCRYPTION_ONLY,   SMP_MODEL_ENCRYPTION_ONLY,  SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_ENCRYPTION_ONLY},/* No Input No Output */
        {SMP_MODEL_PASSKEY,   SMP_MODEL_PASSKEY,    SMP_MODEL_KEY_NOTIF, SMP_MODEL_ENCRYPTION_ONLY, SMP_MODEL_PASSKEY}} /* keyboard display */
    /* display only */    /*SMP_CAP_IO = 1 */  /* keyboard only */   /* No InputOutput */  /* keyboard display */
};

#define SMP_KEY_DIST_TYPE_MAX       4
const tSMP_ACT smp_distribute_act [] =
{
    smp_generate_ltk,
    smp_send_id_info,
    smp_generate_csrk,
    smp_set_derive_link_key
};

static bool lmp_version_below(BD_ADDR 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;
}

/*******************************************************************************
** 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 key_type, BOOLEAN 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 = SMP_DEFAULT_IO_CAPS;
                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("callback_rc=%d  p_cb->cb_evt=%d",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;

                    if (p_cb->secure_connections_only_mode_required)
                    {
                        p_cb->loc_auth_req |= SMP_SC_SUPPORT_BIT;
                    }

                    if (!(p_cb->loc_auth_req & SMP_SC_SUPPORT_BIT)
                        || lmp_version_below(p_cb->pairing_bda, HCI_PROTO_VERSION_4_2)
                        || interop_match(INTEROP_DISABLE_LE_SECURE_CONNECTIONS,
                            (const bt_bdaddr_t *)&p_cb->pairing_bda))
                    {
                        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;
                    }

                    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->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_enc_size, p_cb->local_i_key, p_cb->local_r_key);

                    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 = *(UINT8 *)p_data;
    p_cb->failure = *(UINT8 *)p_data;

    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 = *(UINT8 *) p_data;
    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_LENC_KEYS   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 */
    memcpy(le_key.ltk, p_cb->ltk, BT_OCTET16_LEN);
    le_key.div =  p_cb->div;
    le_key.key_size = p_cb->loc_enc_size;
    le_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,
                            (tBTM_LE_KEY_VALUE *)&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);
}

/*******************************************************************************
** Function     smp_send_csrk_info
** Description  send CSRK command.
*******************************************************************************/
void smp_send_csrk_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
{
    tBTM_LE_LCSRK_KEYS  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.div = p_cb->div;
        key.sec_level = p_cb->sec_level;
        key.counter = 0; /* initialize the local counter */
        memcpy (key.csrk, p_cb->csrk, BT_OCTET16_LEN);
        btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_LCSRK, (tBTM_LE_KEY_VALUE *)&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__);
    /* send stk as LTK response */
    btm_ble_ltk_request_reply(p_cb->pairing_bda, TRUE, p_data->key.p_data);
}

/*******************************************************************************
** 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;
    tBTM_BLE_SEC_REQ_ACT sec_req_act;
    UINT8 reason;

    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)
            {
                reason = SMP_PAIR_AUTH_FAIL;
                smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
            }
            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 res= *(UINT8 *)p_data;
    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__);
    p_cb->status = *(UINT8 *)p_data;
}

/*******************************************************************************
** 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   *p = (UINT8 *)p_data;
    UINT8   reason = SMP_ENC_KEY_SIZE;
    tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (p_cb->pairing_bda);

    SMP_TRACE_DEBUG("%s", __func__);
    /* 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;

    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))
    {
        reason = SMP_INVALID_PARAMETERS;
        smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
        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__);
                reason = SMP_PAIR_AUTH_FAIL;
                smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
                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");
            reason = SMP_PAIR_AUTH_FAIL;
            smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
            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);
        }
    }
}

/*******************************************************************************
** Function     smp_proc_confirm
** Description  process pairing confirm from peer device
*******************************************************************************/
void smp_proc_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
{
    UINT8 *p = (UINT8 *)p_data;
    UINT8 reason = SMP_INVALID_PARAMETERS;

    SMP_TRACE_DEBUG("%s", __func__);

    if (smp_command_has_invalid_parameters(p_cb))
    {
        smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
        return;
    }

    if (p != NULL)
    {
        /* save the SConfirm for comparison later */
        STREAM_TO_ARRAY(p_cb->rconfirm, p, BT_OCTET16_LEN);
    }

    p_cb->flags |= SMP_PAIR_FLAGS_CMD_CONFIRM;
}

/*******************************************************************************
** Function     smp_proc_init
** Description  process pairing initializer from peer device
*******************************************************************************/
void smp_proc_init(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
{
    UINT8 *p = (UINT8 *)p_data;
    UINT8 reason = SMP_INVALID_PARAMETERS;

    SMP_TRACE_DEBUG("%s", __func__);

    if (smp_command_has_invalid_parameters(p_cb))
    {
        smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
        return;
    }

    /* save the SRand for comparison */
    STREAM_TO_ARRAY(p_cb->rrand, p, BT_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 *p = (UINT8 *)p_data;
    UINT8 reason = SMP_INVALID_PARAMETERS;

    SMP_TRACE_DEBUG("%s", __func__);

    if (smp_command_has_invalid_parameters(p_cb))
    {
        smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
        return;
    }

    /* save the SRand for comparison */
    STREAM_TO_ARRAY(p_cb->rrand, p, BT_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 *p = (UINT8 *)p_data;
    UINT8 reason = SMP_INVALID_PARAMETERS;

    SMP_TRACE_DEBUG("%s", __func__);

    if (smp_command_has_invalid_parameters(p_cb))
    {
        smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
        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);
    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 *p = (UINT8 *)p_data;
    UINT8 reason = SMP_INVALID_PARAMETERS;

    SMP_TRACE_DEBUG("%s", __func__);

    if (smp_command_has_invalid_parameters(p_cb))
    {
        smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
        return;
    }

    p_cb->flags |= SMP_PAIR_FLAG_HAVE_PEER_COMM;

    if (p != NULL)
    {
        STREAM_TO_ARRAY(p_cb->remote_commitment, p, BT_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 *p = (UINT8 *)p_data;
    UINT8 reason = SMP_INVALID_PARAMETERS;

    SMP_TRACE_DEBUG("%s", __func__);

    if (smp_command_has_invalid_parameters(p_cb))
    {
        smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
        return;
    }

    if (p != NULL)
    {
        STREAM_TO_ARRAY(p_cb->remote_dhkey_check, p, BT_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 *p = (UINT8 *)p_data;
    UINT8 reason = SMP_INVALID_PARAMETERS;

    SMP_TRACE_DEBUG("%s", __func__);
    p_cb->status = *(UINT8 *)p_data;

    if (smp_command_has_invalid_parameters(p_cb))
    {
        smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
        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   *p = (UINT8 *)p_data;
    UINT8   reason = SMP_ENC_KEY_SIZE;
    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)
    {
        reason = SMP_XTRANS_DERIVE_NOT_ALLOW;
        smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &reason);
        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;

    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))
    {
        reason = SMP_INVALID_PARAMETERS;
        smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &reason);
        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)
{
    UINT8 res= *(UINT8 *)p_data;
    SMP_TRACE_DEBUG("%s", __func__);
    if (res != 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)
{
    UINT8 reason = SMP_SUCCESS;

    SMP_TRACE_DEBUG("%s rcvs i_keys=0x%x r_keys=0x%x "
                      "(i-initiator r-responder)", __FUNCTION__, 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);
    }

    SMP_TRACE_DEBUG("%s rcvs upgrades: i_keys=0x%x r_keys=0x%x "
                      "(i-initiator r-responder)", __FUNCTION__, 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
    {
        smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &reason);
    }
}

/*******************************************************************************
** 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)
{
    UINT8   reason = SMP_SUCCESS;
    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)
                smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &reason);
            else
                p_cb->wait_for_authorization_complete = TRUE;
        }
    }
}

/*******************************************************************************
** Function     smp_proc_enc_info
** Description  process encryption information from peer device
*******************************************************************************/
void smp_proc_enc_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
{
    UINT8   *p = (UINT8 *)p_data;

    SMP_TRACE_DEBUG("%s", __func__);
    STREAM_TO_ARRAY(p_cb->ltk, p, BT_OCTET16_LEN);

    smp_key_distribution(p_cb, NULL);
}
/*******************************************************************************
** Function     smp_proc_master_id
** Description  process master ID from slave device
*******************************************************************************/
void smp_proc_master_id(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
{
    UINT8   *p = (UINT8 *)p_data;
    tBTM_LE_PENC_KEYS   le_key;

    SMP_TRACE_DEBUG("%s", __func__);
    smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_ENC, TRUE);

    STREAM_TO_UINT16(le_key.ediv, p);
    STREAM_TO_ARRAY(le_key.rand, p, BT_OCTET8_LEN );

    /* store the encryption keys from peer device */
    memcpy(le_key.ltk, p_cb->ltk, BT_OCTET16_LEN);
    le_key.sec_level = p_cb->sec_level;
    le_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,
                            (tBTM_LE_KEY_VALUE *)&le_key, TRUE);

    smp_key_distribution(p_cb, NULL);
}

/*******************************************************************************
** Function     smp_proc_enc_info
** Description  process identity information from peer device
*******************************************************************************/
void smp_proc_id_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
{
    UINT8   *p = (UINT8 *)p_data;

    SMP_TRACE_DEBUG("%s", __func__);
    STREAM_TO_ARRAY (p_cb->tk, p, BT_OCTET16_LEN);   /* reuse TK for IRK */
    smp_key_distribution_by_transport(p_cb, NULL);
}

/*******************************************************************************
** Function     smp_proc_id_addr
** Description  process identity address from peer device
*******************************************************************************/
void smp_proc_id_addr(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
{
    UINT8   *p = (UINT8 *)p_data;
    tBTM_LE_PID_KEYS    pid_key;

    SMP_TRACE_DEBUG("%s", __func__);
    smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_ID, TRUE);

    STREAM_TO_UINT8(pid_key.addr_type, p);
    STREAM_TO_BDADDR(pid_key.static_addr, p);
    memcpy(pid_key.irk, p_cb->tk, BT_OCTET16_LEN);

    /* to use as BD_ADDR for lk derived from ltk */
    p_cb->id_addr_rcvd = TRUE;
    p_cb->id_addr_type = pid_key.addr_type;
    memcpy(p_cb->id_addr, pid_key.static_addr, BD_ADDR_LEN);

    /* 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,
                            (tBTM_LE_KEY_VALUE *)&pid_key, TRUE);
    smp_key_distribution_by_transport(p_cb, NULL);
}

/*******************************************************************************
** Function     smp_proc_srk_info
** Description  process security information from peer device
*******************************************************************************/
void smp_proc_srk_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
{
    tBTM_LE_PCSRK_KEYS   le_key;

    SMP_TRACE_DEBUG("%s", __func__);
    smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_CSRK, TRUE);

    /* save CSRK to security record */
    le_key.sec_level = p_cb->sec_level;
    memcpy (le_key.csrk, p_data, BT_OCTET16_LEN);   /* get peer CSRK */
    le_key.counter = 0; /* initialize the peer counter */

    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,
                            (tBTM_LE_KEY_VALUE *)&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)
{
    UINT8   reason;

    SMP_TRACE_DEBUG("%s", __func__);
    if (!memcmp(p_cb->rconfirm, p_data->key.p_data, BT_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
    {
        reason = p_cb->failure = SMP_CONFIRM_VALUE_ERR;
        smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
    }
}

/*******************************************************************************
** 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 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;
    UINT8 reason = SMP_ENC_FAIL;

    SMP_TRACE_DEBUG("%s", __func__);
    if (p_data != NULL)
        cmd = btm_ble_start_encrypt(p_cb->pairing_bda, TRUE, 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)
        smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
}

/*******************************************************************************
** 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 enc_enable = *(UINT8 *)p_data;
    UINT8 reason = enc_enable ? SMP_SUCCESS : SMP_ENC_FAIL;

    SMP_TRACE_DEBUG("%s", __func__);
    smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
}

/*******************************************************************************
** Function     smp_check_auth_req
** Description  check authentication request
*******************************************************************************/
void smp_check_auth_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
{
    UINT8 enc_enable = *(UINT8 *)p_data;
    UINT8 reason = enc_enable ? SMP_SUCCESS : SMP_ENC_FAIL;

    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
            smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
    }
    else if (enc_enable == 0)
    {
        /* 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, &reason);
        /* 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, &reason);
        }
    }
}

/*******************************************************************************
** 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   key_to_dist = (p_cb->role == HCI_ROLE_SLAVE) ? p_cb->local_r_key : p_cb->local_i_key;
    UINT8   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)
{
    UINT8   reason = SMP_SUCCESS;
    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)
                smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
            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   failure = SMP_UNKNOWN_IO_CAP;
    UINT8 int_evt = 0;
    tSMP_KEY key;
    tSMP_INT_DATA   *p = NULL;

    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");
                failure = SMP_PAIR_AUTH_FAIL;
                p = (tSMP_INT_DATA *)&failure;
                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 );

                key.key_type = SMP_KEY_TYPE_TK;
                key.p_data = p_cb->tk;
                p = (tSMP_INT_DATA *)&key;

                memset(p_cb->tk, 0, BT_OCTET16_LEN);
                /* 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)");
            p = (tSMP_INT_DATA *)&failure;
            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);
            p = (tSMP_INT_DATA *)&failure;
            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, p);
}

/*******************************************************************************
** 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)
{
    uint8_t reason = SMP_PAIR_AUTH_FAIL;

    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");
            smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
            return;
        }

        if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB)
        {
            if (smp_request_oob_data(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)
    {
        /* update connection parameter to remote preferred */
        L2CA_EnableUpdateBleConnParams(p_cb->pairing_bda, TRUE);
        /* 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_fast_conn_param
** Description  apply default connection parameter for pairing process
*******************************************************************************/
void smp_fast_conn_param(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
{
    /* disable connection parameter update */
    L2CA_EnableUpdateBleConnParams(p_cb->pairing_bda, FALSE);
}

/*******************************************************************************
** 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  The function starts 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:
            memset(p_cb->local_random, 0, BT_OCTET16_LEN);
            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)
{
    UINT8   reason;

    SMP_TRACE_DEBUG("%s start ", __func__);

    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))
                {
                    reason = p_cb->failure = SMP_CONFIRM_VALUE_ERR;
                    smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
                    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))
            {
                reason = p_cb->failure = SMP_CONFIRM_VALUE_ERR;
                smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
                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 ",__FUNCTION__);
}

/*******************************************************************************
** 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)
{
    UINT8 reason = SMP_DHKEY_CHK_FAIL;

    SMP_TRACE_DEBUG("%s", __func__);

    if (memcmp(p_data->key.p_data, p_cb->remote_dhkey_check, BT_OCTET16_LEN))
    {
        SMP_TRACE_WARNING ("dhkey chcks do no match");
        p_cb->failure = reason;
        smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
        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 *p = NULL;

    SMP_TRACE_DEBUG("%s", __func__);
    p = p_cb->local_random;
    UINT32_TO_STREAM(p, p_data->passkey);

    p = p_cb->peer_random;
    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)
    {
        memcpy(p_cb->local_random, p_sc_oob_data->loc_oob_data.randomizer,
               sizeof(p_cb->local_random));
    }
    else
    {
        SMP_TRACE_EVENT ("local OOB randomizer is absent");
        memset(p_cb->local_random, 0, sizeof (p_cb->local_random));
    }

    if (!p_sc_oob_data->peer_oob_data.present)
    {
        SMP_TRACE_EVENT ("peer OOB data is absent");
        memset(p_cb->peer_random, 0, sizeof (p_cb->peer_random));
    }
    else
    {
        memcpy(p_cb->peer_random, p_sc_oob_data->peer_oob_data.randomizer,
               sizeof(p_cb->peer_random));
        memcpy(p_cb->remote_commitment, p_sc_oob_data->peer_oob_data.commitment,
               sizeof(p_cb->remote_commitment));

        UINT8 reason = SMP_CONFIRM_VALUE_ERR;
        /* check commitment */
        if (!smp_check_commitment(p_cb))
        {
            p_cb->failure = reason;
            smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
            return;
        }

        if (p_cb->peer_oob_flag != SMP_OOB_PRESENT)
        {
            /* the peer doesn't have local randomiser */
            SMP_TRACE_EVENT ("peer didn't receive local OOB data, set local randomizer to 0");
            memset(p_cb->local_random, 0, sizeof (p_cb->local_random));
        }
    }

    print128(p_cb->local_random, (const UINT8 *)"local OOB randomizer");
    print128(p_cb->peer_random, (const UINT8 *)"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__);
    memcpy(p_cb->sc_oob_data.loc_oob_data.randomizer, p_cb->rand,
           BT_OCTET16_LEN);

    smp_calculate_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,
                     p_cb->sc_oob_data.loc_oob_data.commitment);

#if SMP_DEBUG == TRUE
    UINT8   *p_print = NULL;
    SMP_TRACE_DEBUG("local SC OOB data set:");
    p_print = (UINT8*) &p_cb->sc_oob_data.loc_oob_data.addr_sent_to;
    smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"addr_sent_to",
                                         sizeof(tBLE_BD_ADDR));
    p_print = (UINT8*) &p_cb->sc_oob_data.loc_oob_data.private_key_used;
    smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"private_key_used",
                                         BT_OCTET32_LEN);
    p_print = (UINT8*) &p_cb->sc_oob_data.loc_oob_data.publ_key_used.x;
    smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"publ_key_used.x",
                                         BT_OCTET32_LEN);
    p_print = (UINT8*) &p_cb->sc_oob_data.loc_oob_data.publ_key_used.y;
    smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"publ_key_used.y",
                                         BT_OCTET32_LEN);
    p_print = (UINT8*) &p_cb->sc_oob_data.loc_oob_data.randomizer;
    smp_debug_print_nbyte_little_endian (p_print, (const UINT8 *)"randomizer",
                                         BT_OCTET16_LEN);
    p_print = (UINT8*) &p_cb->sc_oob_data.loc_oob_data.commitment;
    smp_debug_print_nbyte_little_endian (p_print,(const UINT8 *) "commitment",
                                         BT_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
**                  slave device. Proceed to to send LTK, DIV and ER to master if
**                  bonding the devices.
**
**
** Returns          void
**
*******************************************************************************/
void smp_link_encrypted(BD_ADDR bda, UINT8 encr_enable)
{
    tSMP_CB *p_cb = &smp_cb;

    SMP_TRACE_DEBUG("%s encr_enable=%d", __func__, encr_enable);

    if (memcmp(&smp_cb.pairing_bda[0], bda, BD_ADDR_LEN) == 0)
    {
        /* encryption completed with STK, remmeber the key size now, could be overwite
        *  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);
        }

        smp_sm_event(&smp_cb, SMP_ENCRYPTED_EVT, &encr_enable);
    }
}

/*******************************************************************************
**
** Function         smp_proc_ltk_request
**
** Description      This function is called when LTK request is received from
**                  controller.
**
** Returns          void
**
*******************************************************************************/
BOOLEAN smp_proc_ltk_request(BD_ADDR bda)
{
    SMP_TRACE_DEBUG("%s state = %d",  __func__, smp_cb.state);
    BOOLEAN match = FALSE;

    if (!memcmp(bda, smp_cb.pairing_bda, BD_ADDR_LEN))
    {
        match = TRUE;
    } else {
        BD_ADDR dummy_bda = {0};
        tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bda);
        if (p_dev_rec != NULL &&
            0 == memcmp(p_dev_rec->ble.pseudo_addr, smp_cb.pairing_bda, BD_ADDR_LEN) &&
            0 != memcmp(p_dev_rec->ble.pseudo_addr, dummy_bda, BD_ADDR_LEN))
        {
            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", __FUNCTION__);
        smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status);
        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;
*8                  - 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",__FUNCTION__);
        smp_sm_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &status);
        return;
    }

    SMP_TRACE_DEBUG("%s: LTK derivation from LK successfully completed", __FUNCTION__);
    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);
    }
}

#endif
