text: /* Copyright (C) 2015 Nordic Semiconductor. All Rights Reserved.
   *
   * The information contained herein is property of Nordic Semiconductor ASA.
   * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
   *
   * Licensees are granted free, non-transferable use of the information. NO
   * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
   * the file.
   *
   */
  
  
  #include "security_dispatcher.h"
  
  #include <string.h>
  #include "ble.h"
  #include "ble_gap.h"
  #include "ble_conn_state.h"
  #include "peer_manager_types.h"
  #include "peer_database.h"
  #include "id_manager.h"
  
  
  #define MAX_REGISTRANTS 3                               /**< The number of user that can register with the module. */
  
  #define MODULE_INITIALIZED (m_smd.n_registrants > 0)    /**< Expression which is true when the module is initialized. */
  
  /**@brief Macro for verifying that the module is initialized. It will cause the function to return
   *        @ref NRF_ERROR_INVALID_STATE if not.
   */
  #define VERIFY_MODULE_INITIALIZED()     \
  do                                      \
  {                                       \
      if (!MODULE_INITIALIZED)            \
      {                                   \
          return NRF_ERROR_INVALID_STATE; \
      }                                   \
  } while(0)
  
  
  /**@brief Macro for verifying that the module is initialized. It will cause the function to return
   *        if not.
   *
   * @param[in] param  The variable to check if is NULL.
   */
  #define VERIFY_PARAM_NOT_NULL(param)    \
  do                                      \
  {                                       \
      if (param == NULL)                  \
      {                                   \
          return NRF_ERROR_NULL;          \
      }                                   \
  } while(0)
  
  
  typedef struct
  {
      smd_evt_handler_t evt_handlers[MAX_REGISTRANTS];
      uint8_t           n_registrants;
      pm_peer_id_t      pending_bond_central;
      pm_peer_id_t      pending_bond_peripheral;
  } smd_t;
  
  static smd_t m_smd;
  
  static void internal_state_reset(smd_t * smd)
  {
      memset(smd, 0, sizeof(smd_t));
      smd->pending_bond_central    = PM_PEER_ID_INVALID;
      smd->pending_bond_peripheral = PM_PEER_ID_INVALID;
  }
  
  
  /**@brief Function for storing the fact that a new bond is being made.
   *
   * @details This specifies that there is no bonding data yet stored for this peer. The role is
   *          needed because the SoftDevice supports two simultaneous bonding procedures, one per role.
   *
   * @param[in]  role     The role of the local device in the bonding procedure.
   * @param[in]  peer_id  ID of the device being bonded with.
   */
  static void pending_bond_push(uint8_t role, pm_peer_id_t peer_id)
  {
      if (role == BLE_GAP_ROLE_CENTRAL)
      {
          m_smd.pending_bond_central = peer_id;
      }
      else
      {
          m_smd.pending_bond_peripheral = peer_id;
      }
  }
  
  
  /**@brief Function for reading and clearing the record of whether a new bond is being made.
   *
   * @details If the bonding fails, and this function returns true this peer ID must be freed. The
   *          role is needed because the SoftDevice supports two simultaneous bonding procedures, one
   *          per role.
   *
   * @param[in]  role     The role of the local device in the bonding procedure.
   * @param[in]  peer_id  ID of the device being bonded with.
   *
   * @return     Whether a new bond was/is being made. I.E. whether @ref pending_bond_push has been
   *             called with the same parameters.
   */
  static bool pending_bond_pop(uint8_t role, pm_peer_id_t peer_id)
  {
      if (peer_id == PM_PEER_ID_INVALID)
      {
          return false;
      }
  
      if ((role == BLE_GAP_ROLE_CENTRAL) && (m_smd.pending_bond_central == peer_id))
      {
          m_smd.pending_bond_central = PM_PEER_ID_INVALID;
          return true;
      }
      else if ((role == BLE_GAP_ROLE_PERIPH) && (m_smd.pending_bond_peripheral == peer_id))
      {
          m_smd.pending_bond_peripheral = PM_PEER_ID_INVALID;
          return true;
      }
      else
      {
          return false;
      }
  }
  
  
  static void evt_send(smd_evt_t * p_event)
  {
      for (int i = 0; i < m_smd.n_registrants; i++)
      {
          m_smd.evt_handlers[i](p_event);
      }
  }
  
  
  static void sec_start_send(uint16_t conn_handle, pm_sec_procedure_t procedure)
  {
      smd_evt_t evt =
      {
          .evt_id = SMD_EVT_SEC_PROCEDURE_START,
          .conn_handle = conn_handle,
          .params = {.sec_procedure_start = {.procedure = procedure}}
      };
      evt_send(&evt);
  }
  
  
  /**@brief Event handler for events from the peer_database module.
   *
   * @param[in]  p_event   The event that has happened.
   */
  static void pdb_evt_handler(pdb_evt_t const * p_event)
  {
      if ((p_event->evt_id == PDB_EVT_WRITE_BUF_STORED) && (p_event->data_id == PM_PEER_DATA_ID_BONDING))
      {
          smd_evt_t evt =
          {
              .evt_id = SMD_EVT_BONDING_INFO_STORED,
              .conn_handle = im_conn_handle_get(p_event->peer_id),
              .params = {.bonding_info_stored =
              {
                  .peer_id = p_event->peer_id,
              }}
          };
          evt_send(&evt);
      }
  }
  
  
  /**@brief Function for processing the @ref BLE_GAP_EVT_SEC_PARAMS_REQUEST event from the SoftDevice.
   *
   * @param[in]  p_gap_evt  The event from the SoftDevice.
   */
  static void sec_params_request_process(ble_gap_evt_t * p_gap_evt)
  {
      smd_evt_t evt =
      {
          .evt_id = SMD_EVT_PARAMS_REQ,
          .conn_handle = p_gap_evt->conn_handle
      };
      evt_send(&evt);
      return;
  }
  
  
  /**@brief Function for processing the @ref BLE_GAP_EVT_SEC_INFO_REQUEST event from the SoftDevice.
   *
   * @param[in]  p_gap_evt  The event from the SoftDevice.
   */
  static void sec_info_request_process(ble_gap_evt_t * p_gap_evt)
  {
      ret_code_t                 err_code;
      ble_gap_enc_info_t const * p_enc_info = NULL;
      pm_peer_data_flash_t       peer_data;
      pm_peer_id_t               peer_id = im_peer_id_get_by_master_id(&p_gap_evt->params.sec_info_request.master_id);
      smd_evt_t evt;
  
      evt.conn_handle = p_gap_evt->conn_handle;
  
      if (peer_id != PM_PEER_ID_INVALID)
      {
          err_code = pdb_read_buf_get(peer_id, PM_PEER_DATA_ID_BONDING, &peer_data, NULL);
  
          if (err_code == NRF_SUCCESS)
          {
              // There is stored bonding data for this peer.
              ble_gap_enc_key_t const * p_existing_key = &peer_data.data.p_bonding_data->own_ltk;
  
              if (peer_data.data.p_bonding_data->own_role == BLE_GAP_ROLE_CENTRAL)
              {
                  // Role has changed since bonding.
                  p_existing_key = &peer_data.data.p_bonding_data->peer_ltk;
              }
  
              if (im_master_id_is_valid(&p_existing_key->master_id))
              {
                  p_enc_info = &p_existing_key->enc_info;
              }
          }
      }
  
      // All return values from the following can be safely ignored.
      err_code = sd_ble_gap_sec_info_reply(p_gap_evt->conn_handle, p_enc_info, NULL, NULL);
  
      if (err_code != NRF_SUCCESS)
      {
          evt.evt_id                        = SMD_EVT_ERROR_UNEXPECTED;
          evt.params.error_unexpected.error = err_code;
  
          evt_send(&evt);
      }
      else if (p_enc_info == NULL)
      {
          evt.evt_id                                  = SMD_EVT_LINK_ENCRYPTION_FAILED;
          evt.params.link_encryption_failed.error     = PM_SEC_ERROR_CODE_PIN_OR_KEY_MISSING;
          evt.params.link_encryption_failed.error_src = BLE_GAP_SEC_STATUS_SOURCE_LOCAL;
  
          evt_send(&evt);
      }
      else
      {
          sec_start_send(p_gap_evt->conn_handle, PM_LINK_SECURED_PROCEDURE_ENCRYPTION);
      }
      return;
  }
  
  
  /**@brief Function for processing the @ref BLE_GAP_EVT_SEC_REQUEST event from the SoftDevice.
   *
   * @param[in]  p_gap_evt  The event from the SoftDevice.
   */
  static void sec_request_process(ble_gap_evt_t * p_gap_evt)
  {
      smd_evt_t evt =
      {
          .evt_id = SMD_EVT_SLAVE_SECURITY_REQ,
          .conn_handle = p_gap_evt->conn_handle,
          .params =
          {
              .slave_security_req =
              {
                  .bond = p_gap_evt->params.sec_request.bond,
                  .mitm = p_gap_evt->params.sec_request.mitm,
              }
          }
      };
      evt_send(&evt);
      return;
  }
  
  
  /**@brief Function for processing the @ref BLE_GAP_EVT_AUTH_STATUS event from the SoftDevice, when
   *        the auth_status is success.
   *
   * @param[in]  p_gap_evt  The event from the SoftDevice.
   */
  static void auth_status_success_process(ble_gap_evt_t * p_gap_evt)
  {
      ret_code_t           err_code      = NRF_SUCCESS;
      uint8_t              role          = ble_conn_state_role(p_gap_evt->conn_handle);
      pm_peer_id_t         peer_id       = im_peer_id_get_by_conn_handle(p_gap_evt->conn_handle);
      ble_gap_sec_kdist_t  kdist_periph  = p_gap_evt->params.auth_status.kdist_periph;
      ble_gap_sec_kdist_t  kdist_central = p_gap_evt->params.auth_status.kdist_central;
      ble_gap_sec_kdist_t  kdist_own     = (role == BLE_GAP_ROLE_CENTRAL) ?  kdist_central : kdist_periph;
      ble_gap_sec_kdist_t  kdist_peer    = (role == BLE_GAP_ROLE_CENTRAL) ?  kdist_periph  : kdist_central;
  
      if (role == BLE_GAP_ROLE_INVALID)
      {
          /* Unlikely, but maybe possible? */
          return;
      }
  
      pending_bond_pop(role, peer_id);
  
      err_code = pdb_write_buf_store(peer_id, PM_PEER_DATA_ID_BONDING);
      if (err_code != NRF_SUCCESS)
      {
          /* Unexpected */
          smd_evt_t error_evt =
          {
              .evt_id      = SMD_EVT_ERROR_BONDING_INFO,
              .conn_handle = p_gap_evt->conn_handle,
              .params      =
              {
                  .error_bonding_info =
                  {
                      .peer_id        = peer_id,
                      .error          = err_code,
                  }
              }
          };
          evt_send(&error_evt);
      }
  
      smd_evt_t pairing_success_evt =
      {
          .evt_id      = SMD_EVT_PAIRING_SUCCESS,
          .conn_handle = p_gap_evt->conn_handle,
          .params      =
          {
              .pairing_success =
              {
                  .bonded        = p_gap_evt->params.auth_status.bonded,
                  .mitm          = p_gap_evt->params.auth_status.sm1_levels.lv3,
                  .kdist_own     = kdist_own,
                  .kdist_peer    = kdist_peer,
              }
          }
      };
      evt_send(&pairing_success_evt);
      return;
  }
  
  
  /**@brief Function for cleaning up after a failed pairing procedure.
   *
   * @param[in]  role     Our role in the pairing procedure. See @ref BLE_GAP_ROLES.
   * @param[in]  peer_id  The peer id used in the pairing procedure.
   */
  static void pairing_failure(uint8_t role, pm_peer_id_t peer_id)
  {
      if(pending_bond_pop(role, peer_id))
      {
          // The peer_id was created during the procedure, and should be freed, because no data is stored under it.
          pdb_peer_free(peer_id);
      }
      else
      {
          pdb_write_buf_release(peer_id, PM_PEER_DATA_ID_BONDING);
      }
  
      return;
  }
  
  
  /**@brief Function for processing the @ref BLE_GAP_EVT_DISCONNECT event from the SoftDevice.
   *
   * @param[in]  p_gap_evt  The event from the SoftDevice.
   */
  static void disconnect_process(ble_gap_evt_t * p_gap_evt)
  {
      uint8_t       role    = ble_conn_state_role(p_gap_evt->conn_handle);
      pm_peer_id_t  peer_id = im_peer_id_get_by_conn_handle(p_gap_evt->conn_handle);
  
      if (   (peer_id != PM_PEER_ID_INVALID)
          && (p_gap_evt->params.disconnected.reason == BLE_HCI_CONN_TERMINATED_DUE_TO_MIC_FAILURE)
          && (pending_bond_pop(role, peer_id)))
      {
  //        smd_evt_t     evt     =
  //        {
  //            .evt_id = SMD_EVT_PAIRING_FAIL,
  //            .conn_handle = p_gap_evt->conn_handle,
  //            .params =
  //            {
  //                .pairing_failed =
  //                {
  //                    .auth_status = ?
  //                    .error_src   = ?
  //                }
  //            }
  //        };
  
          pairing_failure(role, peer_id);
  
  //        evt_send(&evt);
      }
  }
  
  
  
  /**@brief Function for processing the @ref BLE_GAP_EVT_AUTH_STATUS event from the SoftDevice, when
   *        the auth_status is failure.
   *
   * @param[in]  p_gap_evt  The event from the SoftDevice.
   */
  static void auth_status_failure_process(ble_gap_evt_t * p_gap_evt)
  {
      uint8_t       role    = ble_conn_state_role(p_gap_evt->conn_handle);
      pm_peer_id_t  peer_id = im_peer_id_get_by_conn_handle(p_gap_evt->conn_handle);
      smd_evt_t     evt     =
      {
          .evt_id = SMD_EVT_PAIRING_FAIL,
          .conn_handle = p_gap_evt->conn_handle,
          .params =
          {
              .pairing_failed =
              {
                  .auth_status = p_gap_evt->params.auth_status.auth_status,
                  .error_src   = p_gap_evt->params.auth_status.error_src,
              }
          }
      };
  
      pairing_failure(role, peer_id);
  
      evt_send(&evt);
  }
  
  
  /**@brief Function for processing the @ref BLE_GAP_EVT_AUTH_STATUS event from the SoftDevice.
   *
   * @param[in]  p_gap_evt  The event from the SoftDevice.
   */
  static void auth_status_process(ble_gap_evt_t * p_gap_evt)
  {
      switch(p_gap_evt->params.auth_status.auth_status)
      {
          case BLE_GAP_SEC_STATUS_SUCCESS:
              auth_status_success_process(p_gap_evt);
              break;
  
          case BLE_GAP_SEC_STATUS_PDU_INVALID:
              /* Ignore, because they don't have anything to do with an ongoing procedure. */
              break;
  
          default:
              auth_status_failure_process(p_gap_evt);
              break;
      }
  }
  
  
  /**@brief Function for processing the @ref BLE_GAP_EVT_CONN_SEC_UPDATE event from the SoftDevice.
   *
   * @param[in]  p_gap_evt  The event from the SoftDevice.
   */
  static void conn_sec_update_process(ble_gap_evt_t * p_gap_evt)
  {
      smd_evt_t evt;
  
      evt.conn_handle = p_gap_evt->conn_handle;
  
      if (ble_conn_state_encrypted(p_gap_evt->conn_handle))
      {
          evt.evt_id = SMD_EVT_LINK_ENCRYPTION_UPDATE;
          evt.params.link_encryption_update.mitm_protected
                                  = ble_conn_state_mitm_protected(p_gap_evt->conn_handle);
      }
      else
      {
          evt.evt_id                                  = SMD_EVT_LINK_ENCRYPTION_FAILED;
          evt.params.link_encryption_failed.error     = PM_SEC_ERROR_CODE_PIN_OR_KEY_MISSING;
          evt.params.link_encryption_failed.error_src = BLE_GAP_SEC_STATUS_SOURCE_REMOTE;
      }
  
      evt_send(&evt);
  }
  
  
  ret_code_t smd_register(smd_evt_handler_t evt_handler)
  {
      ret_code_t err_code = NRF_SUCCESS;
      if (evt_handler == NULL)
      {
          err_code = NRF_ERROR_NULL;
      }
      else
      {
          if (!MODULE_INITIALIZED)
          {
              err_code = pdb_register(pdb_evt_handler);
              if (err_code == NRF_SUCCESS)
              {
                  internal_state_reset(&m_smd);
              }
          }
          if ((err_code == NRF_SUCCESS))
          {
              if ((m_smd.n_registrants < MAX_REGISTRANTS))
              {
                  m_smd.evt_handlers[m_smd.n_registrants++] = evt_handler;
              }
              else
              {
                  err_code = NRF_ERROR_NO_MEM;
              }
          }
      }
      return err_code;
  }
  
  
  ret_code_t smd_params_reply(uint16_t conn_handle, ble_gap_sec_params_t * p_sec_params)
  {
      VERIFY_MODULE_INITIALIZED();
  
      uint8_t              role = ble_conn_state_role(conn_handle);
      pm_peer_id_t         peer_id = PM_PEER_ID_INVALID;
      ret_code_t           err_code = NRF_SUCCESS;
      uint8_t              sec_status = BLE_GAP_SEC_STATUS_SUCCESS;
      ble_gap_sec_keyset_t sec_keyset;
  
      memset(&sec_keyset, 0, sizeof(ble_gap_sec_keyset_t));
  
      if (role == BLE_GAP_ROLE_INVALID)
      {
          return BLE_ERROR_INVALID_CONN_HANDLE;
      }
  
      if (p_sec_params == NULL)
      {
          // NULL params means reject pairing.
          sec_status = BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP;
      }
      else if (p_sec_params->bond)
      {
          // Bonding is to be performed, prepare to receive bonding data.
          pm_peer_data_t       peer_data;
  
          peer_id = im_peer_id_get_by_conn_handle(conn_handle);
  
          if (peer_id == PM_PEER_ID_INVALID)
          {
              // Peer is unknown to us, allocate a new peer ID for it.
              peer_id = pdb_peer_allocate();
              if (peer_id != PM_PEER_ID_INVALID)
              {
                  pending_bond_push(role, peer_id);
              }
              else
              {
                  err_code = NRF_ERROR_INTERNAL;
              }
          }
  
          if (err_code == NRF_SUCCESS)
          {
              // Peer ID is OK, retrieve memory buffer to receive data.
              err_code = pdb_write_buf_get(peer_id, PM_PEER_DATA_ID_BONDING, 1, &peer_data);
              if (err_code == NRF_SUCCESS)
              {
                  peer_data.data.p_bonding_data->own_role = role;
  
                  if (role == BLE_GAP_ROLE_CENTRAL)
                  {
                      sec_keyset.keys_central.p_enc_key = &peer_data.data.p_bonding_data->own_ltk;
                      sec_keyset.keys_periph.p_enc_key  = &peer_data.data.p_bonding_data->peer_ltk;
                      sec_keyset.keys_periph.p_id_key   = &peer_data.data.p_bonding_data->peer_id;
                      im_ble_addr_get(conn_handle, &peer_data.data.p_bonding_data->peer_id.id_addr_info); // Will be overwritten if ID is shared.
                  }
                  else
                  {
                      sec_keyset.keys_periph.p_enc_key  = &peer_data.data.p_bonding_data->own_ltk;
                      sec_keyset.keys_central.p_enc_key = &peer_data.data.p_bonding_data->peer_ltk;
                      sec_keyset.keys_central.p_id_key  = &peer_data.data.p_bonding_data->peer_id;
                      im_ble_addr_get(conn_handle, &peer_data.data.p_bonding_data->peer_id.id_addr_info); // Will be overwritten if ID is shared.
                  }
  
                  // Buffer is OK, reserve room in flash for the data.
                  err_code = pdb_write_buf_store_prepare(peer_id, PM_PEER_DATA_ID_BONDING);
              }
          }
      }
      else
      {
          // Pairing only, no action needed.
      }
  
      if (err_code == NRF_SUCCESS)
      {
          // Everything OK, reply to SoftDevice. If an error happened, the user is given an
          // opportunity to change the parameters and retry the call.
          if (role == BLE_GAP_ROLE_CENTRAL)
          {
              err_code = sd_ble_gap_sec_params_reply(conn_handle, sec_status, NULL, &sec_keyset);
          }
          else
          {
              err_code = sd_ble_gap_sec_params_reply(conn_handle, sec_status, p_sec_params, &sec_keyset);
  
              if ((p_sec_params != NULL) && (err_code == NRF_SUCCESS))
              {
                  pm_sec_procedure_t procedure = p_sec_params->bond
                                                 ? PM_LINK_SECURED_PROCEDURE_BONDING
                                                 : PM_LINK_SECURED_PROCEDURE_PAIRING;
                  sec_start_send(conn_handle, procedure);
              }
          }
      }
  
      if(pending_bond_pop(role, peer_id))
      {
          // New peer ID created, either report it or free it.
          if (err_code == NRF_SUCCESS)
          {
              pending_bond_push(role, peer_id);
              im_new_peer_id(conn_handle, peer_id);
          }
          else
          {
              pdb_peer_free(peer_id);
          }
      }
      return err_code;
  }
  
  
  static ret_code_t link_secure_central_existing_peer(uint16_t               conn_handle,
                                                      ble_gap_sec_params_t * p_sec_params,
                                                      bool                   force_repairing,
                                                      pm_peer_id_t           peer_id,
                                                      pm_sec_procedure_t   * procedure)
  {
      pm_peer_data_flash_t      peer_data;
      pm_peer_data_t            dummy_peer_data;
      ret_code_t                err_code;
      ble_gap_enc_key_t const * p_existing_key = NULL;
  
  
      err_code = pdb_read_buf_get(peer_id, PM_PEER_DATA_ID_BONDING, &peer_data, NULL);
  
      if (err_code == NRF_SUCCESS)
      {
          p_existing_key = &(peer_data.data.p_bonding_data->peer_ltk);
          if (peer_data.data.p_bonding_data->own_role == BLE_GAP_ROLE_PERIPH)
          {
              // Role has changed since bonding.
              p_existing_key = &(peer_data.data.p_bonding_data->own_ltk);
          }
      }
  
      if     (!force_repairing
           && (err_code == NRF_SUCCESS)
           && (p_existing_key != NULL)
           && (im_master_id_is_valid(&(p_existing_key->master_id))) /* There is an existing LTK stored. */
           && (p_existing_key->enc_info.auth >= p_sec_params->mitm)) /* The requested security is at or below the existing level. */
      {
          err_code = sd_ble_gap_encrypt(conn_handle, &(p_existing_key->master_id), &(p_existing_key->enc_info));
  
          *procedure = PM_LINK_SECURED_PROCEDURE_ENCRYPTION;
      }
      else if ((err_code == NRF_SUCCESS) || (err_code == NRF_ERROR_NOT_FOUND))
      {
          /* Re-pairing is needed, because there is no LTK available or the existing key is not
             secure enough */
          err_code = NRF_SUCCESS;
  
          if (p_sec_params->bond)
          {
              err_code = pdb_write_buf_get(peer_id, PM_PEER_DATA_ID_BONDING, 1, &dummy_peer_data);
              if (err_code == NRF_SUCCESS)
              {
                  err_code = pdb_write_buf_store_prepare(peer_id, PM_PEER_DATA_ID_BONDING);
              }
          }
  
          if (err_code == NRF_SUCCESS)
          {
              err_code = sd_ble_gap_authenticate(conn_handle, p_sec_params);
          }
  
          if (err_code != NRF_SUCCESS)
          {
              ret_code_t err_code_release = pdb_write_buf_release(peer_id, PM_PEER_DATA_ID_BONDING);
              if ((err_code_release != NRF_SUCCESS) && (err_code_release != NRF_ERROR_NOT_FOUND))
              {
                  err_code = NRF_ERROR_INTERNAL;
              }
          }
      }
  
      return err_code;
  }
  
  
  static ret_code_t link_secure_central_new_peer(uint16_t               conn_handle,
                                                 ble_gap_sec_params_t * p_sec_params)
  {
      pm_peer_id_t    peer_id = pdb_peer_allocate();
      pm_peer_data_t  dummy_peer_data;
      ret_code_t      err_code;
  
      if (peer_id != PM_PEER_ID_INVALID)
      {
          err_code = pdb_write_buf_get(peer_id, PM_PEER_DATA_ID_BONDING, 1, &dummy_peer_data);
          if (err_code == NRF_SUCCESS)
          {
              err_code = pdb_write_buf_store_prepare(peer_id, PM_PEER_DATA_ID_BONDING);
          }
  
          if (err_code == NRF_SUCCESS)
          {
              err_code = sd_ble_gap_authenticate(conn_handle, p_sec_params);
          }
  
          if (err_code != NRF_SUCCESS)
          {
              ret_code_t err_code_release = pdb_write_buf_release(peer_id, PM_PEER_DATA_ID_BONDING);
              if ((err_code_release != NRF_SUCCESS) && (err_code_release != NRF_ERROR_NOT_FOUND))
              {
                  err_code = NRF_ERROR_INTERNAL;
              }
              pdb_peer_free(peer_id);
          }
          else
          {
              pending_bond_push(BLE_GAP_ROLE_CENTRAL, peer_id);
              im_new_peer_id(conn_handle, peer_id);
          }
      }
      else
      {
          err_code = NRF_ERROR_INTERNAL;
      }
  
      return err_code;
  }
  
  
  static ret_code_t link_secure_central(uint16_t               conn_handle,
                                        ble_gap_sec_params_t * p_sec_params,
                                        bool                   force_repairing)
  {
      ret_code_t           err_code;
      pm_peer_id_t         peer_id;
  
      if (p_sec_params == NULL)
      {
          return sd_ble_gap_authenticate(conn_handle, NULL);
      }
  
      pm_sec_procedure_t procedure = p_sec_params->bond ? PM_LINK_SECURED_PROCEDURE_BONDING
                                                        : PM_LINK_SECURED_PROCEDURE_PAIRING;
  
      peer_id = im_peer_id_get_by_conn_handle(conn_handle);
  
      if (peer_id != PM_PEER_ID_INVALID)
      {
          // There is already data in flash for this peer.
          err_code = link_secure_central_existing_peer(conn_handle,
                                                       p_sec_params,
                                                       force_repairing,
                                                       peer_id,
                                                       &procedure);
      }
      else if (p_sec_params->bond)
      {
          // New peer is required.
          err_code = link_secure_central_new_peer(conn_handle, p_sec_params);
      }
      else
      {
          err_code = sd_ble_gap_authenticate(conn_handle, p_sec_params);
      }
  
      if (err_code == NRF_SUCCESS)
      {
          sec_start_send(conn_handle, procedure);
      }
  
      return err_code;
  }
  
  
  ret_code_t smd_link_secure(uint16_t               conn_handle,
                             ble_gap_sec_params_t * p_sec_params,
                             bool                   force_repairing)
  {
      VERIFY_MODULE_INITIALIZED();
  
      uint8_t role = ble_conn_state_role(conn_handle);
  
      switch (role)
      {
          case BLE_GAP_ROLE_CENTRAL:
              return link_secure_central(conn_handle,
                                         p_sec_params,
                                         force_repairing);
  
          case BLE_GAP_ROLE_PERIPH:
              VERIFY_PARAM_NOT_NULL(p_sec_params);
              return sd_ble_gap_authenticate(conn_handle, p_sec_params);
  
          default:
              return BLE_ERROR_INVALID_CONN_HANDLE;
      }
  }
  
  
  void smd_ble_evt_handler(ble_evt_t * p_ble_evt)
  {
      switch (p_ble_evt->header.evt_id)
      {
          case BLE_GAP_EVT_DISCONNECTED:
              disconnect_process(&(p_ble_evt->evt.gap_evt));
              break;
  
          case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
              sec_params_request_process(&(p_ble_evt->evt.gap_evt));
              break;
  
          case BLE_GAP_EVT_SEC_INFO_REQUEST:
              sec_info_request_process(&(p_ble_evt->evt.gap_evt));
              break;
  
          case BLE_GAP_EVT_SEC_REQUEST:
              sec_request_process(&(p_ble_evt->evt.gap_evt));
              break;
  
          case BLE_GAP_EVT_AUTH_STATUS:
              auth_status_process(&(p_ble_evt->evt.gap_evt));
              break;
  
          case BLE_GAP_EVT_CONN_SEC_UPDATE:
              conn_sec_update_process(&(p_ble_evt->evt.gap_evt));
              break;
      };
  }
  
  