You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2016/05/07 02:04:58 UTC

[1/2] incubator-mynewt-core git commit: BLE host - bonding.

Repository: incubator-mynewt-core
Updated Branches:
  refs/heads/develop 78ebe68aa -> 264a67b95


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/net/nimble/host/src/test/ble_l2cap_sm_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_l2cap_sm_test.c b/net/nimble/host/src/test/ble_l2cap_sm_test.c
index f3e8bbd..409cfc9 100644
--- a/net/nimble/host/src/test/ble_l2cap_sm_test.c
+++ b/net/nimble/host/src/test/ble_l2cap_sm_test.c
@@ -31,7 +31,8 @@
 
 int ble_l2cap_sm_test_gap_event;
 int ble_l2cap_sm_test_gap_status;
-struct ble_gap_sec_params ble_l2cap_sm_test_sec_params;
+struct ble_gap_sec_state ble_l2cap_sm_test_sec_state;
+struct ble_gap_ltk_params ble_l2cap_sm_test_ltk_params;
 
 /*****************************************************************************
  * $util                                                                     *
@@ -51,21 +52,47 @@ ble_l2cap_sm_test_util_init(void)
 
     ble_l2cap_sm_test_gap_event = -1;
     ble_l2cap_sm_test_gap_status = -1;
-    memset(&ble_l2cap_sm_test_sec_params, 0xff,
-           sizeof ble_l2cap_sm_test_sec_params);
+    memset(&ble_l2cap_sm_test_sec_state, 0xff,
+           sizeof ble_l2cap_sm_test_sec_state);
 }
 
+struct ble_l2cap_sm_test_ltk_info {
+    uint8_t ltk[16];
+    unsigned authenticated:1;
+};
+
 static int
 ble_l2cap_sm_test_util_conn_cb(int event, int status,
                                struct ble_gap_conn_ctxt *ctxt, void *arg)
 {
+    struct ble_l2cap_sm_test_ltk_info *ltk_info;
+
+    ble_l2cap_sm_test_gap_event = event;
+    ble_l2cap_sm_test_gap_status = status;
+
     switch (event) {
     case BLE_GAP_EVENT_SECURITY:
-        ble_l2cap_sm_test_gap_event = event;
-        ble_l2cap_sm_test_gap_status = status;
-        ble_l2cap_sm_test_sec_params = *ctxt->sec_params;
+        ble_l2cap_sm_test_sec_state = ctxt->desc->sec_state;
         return 0;
 
+    case BLE_GAP_EVENT_LTK_REQUEST:
+        ltk_info = arg;
+        if (ltk_info == NULL) {
+            memset(ctxt->ltk_params->ltk, 0, sizeof ctxt->ltk_params->ltk);
+            ctxt->ltk_params->authenticated = 0;
+        } else {
+            memcpy(ctxt->ltk_params->ltk, ltk_info->ltk,
+                   sizeof ctxt->ltk_params->ltk);
+            ctxt->ltk_params->authenticated = ltk_info->authenticated;
+        }
+
+        ble_l2cap_sm_test_ltk_params = *ctxt->ltk_params;
+        if (ltk_info == NULL) {
+            return -1;
+        } else {
+            return 0;
+        }
+
     default:
         return 0;
     }
@@ -304,10 +331,23 @@ ble_l2cap_sm_test_util_verify_tx_lt_key_req_reply(uint16_t conn_handle,
 }
 
 static void
+ble_l2cap_sm_test_util_verify_tx_lt_key_req_neg_reply(uint16_t conn_handle)
+{
+    uint8_t param_len;
+    uint8_t *param;
+
+    param = ble_hs_test_util_verify_tx_hci(BLE_HCI_OGF_LE,
+                                           BLE_HCI_OCF_LE_LT_KEY_REQ_NEG_REPLY,
+                                           &param_len);
+    TEST_ASSERT(param_len == BLE_HCI_LT_KEY_REQ_NEG_REPLY_LEN);
+    TEST_ASSERT(le16toh(param + 0) == conn_handle);
+}
+
+static void
 ble_l2cap_sm_test_util_set_lt_key_req_reply_ack(uint8_t status,
                                                 uint16_t conn_handle)
 {
-    static uint8_t params[BLE_HCI_LT_KEY_REQ_REPLY_ACK_PARAM_LEN - 1];
+    static uint8_t params[BLE_HCI_LT_KEY_REQ_REPLY_ACK_PARAM_LEN];
 
     htole16(params, conn_handle);
     ble_hs_test_util_set_ack_params(
@@ -377,51 +417,51 @@ ble_l2cap_sm_test_util_peer_lgcy_good(
                                         ble_l2cap_sm_test_util_conn_cb,
                                         NULL);
 
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 0);
 
     /* Receive a pair request from the peer. */
     ble_l2cap_sm_test_util_rx_pair_req(conn, pair_req);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Ensure we sent the expected pair response. */
     ble_hs_test_util_tx_all();
     ble_l2cap_sm_test_util_verify_tx_pair_rsp(pair_rsp);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Receive a pair confirm from the peer. */
     ble_l2cap_sm_test_util_rx_confirm(conn, confirm_req);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Ensure we sent the expected pair confirm. */
     ble_hs_test_util_tx_all();
     ble_l2cap_sm_test_util_verify_tx_pair_confirm(confirm_rsp);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Receive a pair random from the peer. */
     ble_l2cap_sm_test_util_rx_random(conn, random_req, 0);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Ensure we sent the expected pair random. */
     ble_hs_test_util_tx_all();
     ble_l2cap_sm_test_util_verify_tx_pair_random(random_rsp);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Receive a long term key request from the controller. */
     ble_l2cap_sm_test_util_set_lt_key_req_reply_ack(0, 2);
     ble_l2cap_sm_test_util_rx_lt_key_req(2, r, ediv);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Ensure we sent the expected long term key request reply command. */
     ble_l2cap_sm_test_util_verify_tx_lt_key_req_reply(2, stk);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Receive an encryption changed event. */
@@ -433,17 +473,17 @@ ble_l2cap_sm_test_util_peer_lgcy_good(
     /* Verify that security callback was executed. */
     TEST_ASSERT(ble_l2cap_sm_test_gap_event == BLE_GAP_EVENT_SECURITY);
     TEST_ASSERT(ble_l2cap_sm_test_gap_status == 0);
-    TEST_ASSERT(ble_l2cap_sm_test_sec_params.pair_alg == pair_alg);
-    TEST_ASSERT(ble_l2cap_sm_test_sec_params.enc_enabled);
-    TEST_ASSERT(!ble_l2cap_sm_test_sec_params.authenticated);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.pair_alg == pair_alg);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.enc_enabled);
+    TEST_ASSERT(!ble_l2cap_sm_test_sec_state.authenticated);
 
     /* Verify that connection has correct security state. */
-    TEST_ASSERT(ble_l2cap_sm_test_sec_params.pair_alg ==
-                conn->bhc_sec_params.pair_alg);
-    TEST_ASSERT(ble_l2cap_sm_test_sec_params.enc_enabled ==
-                conn->bhc_sec_params.enc_enabled);
-    TEST_ASSERT(ble_l2cap_sm_test_sec_params.authenticated ==
-                conn->bhc_sec_params.authenticated);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.pair_alg ==
+                conn->bhc_sec_state.pair_alg);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.enc_enabled ==
+                conn->bhc_sec_state.enc_enabled);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.authenticated ==
+                conn->bhc_sec_state.authenticated);
 }
 
 static void
@@ -503,18 +543,18 @@ ble_l2cap_sm_test_util_peer_lgcy_fail(
     TEST_ASSERT(ble_l2cap_sm_test_gap_event == BLE_GAP_EVENT_SECURITY);
     TEST_ASSERT(ble_l2cap_sm_test_gap_status ==
                 BLE_HS_SM_US_ERR(BLE_L2CAP_SM_ERR_CONFIRM_MISMATCH));
-    TEST_ASSERT(ble_l2cap_sm_test_sec_params.pair_alg ==
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.pair_alg ==
                 BLE_L2CAP_SM_PAIR_ALG_JW);
-    TEST_ASSERT(!ble_l2cap_sm_test_sec_params.enc_enabled);
-    TEST_ASSERT(!ble_l2cap_sm_test_sec_params.authenticated);
+    TEST_ASSERT(!ble_l2cap_sm_test_sec_state.enc_enabled);
+    TEST_ASSERT(!ble_l2cap_sm_test_sec_state.authenticated);
 
     /* Verify that connection has correct security state. */
-    TEST_ASSERT(ble_l2cap_sm_test_sec_params.pair_alg ==
-                conn->bhc_sec_params.pair_alg);
-    TEST_ASSERT(ble_l2cap_sm_test_sec_params.enc_enabled ==
-                conn->bhc_sec_params.enc_enabled);
-    TEST_ASSERT(ble_l2cap_sm_test_sec_params.authenticated ==
-                conn->bhc_sec_params.authenticated);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.pair_alg ==
+                conn->bhc_sec_state.pair_alg);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.enc_enabled ==
+                conn->bhc_sec_state.enc_enabled);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.authenticated ==
+                conn->bhc_sec_state.authenticated);
 }
 
 TEST_CASE(ble_l2cap_sm_test_case_peer_lgcy_jw_good)
@@ -657,7 +697,7 @@ ble_l2cap_sm_test_util_us_lgcy_good(
                                         ble_l2cap_sm_test_util_conn_cb,
                                         NULL);
 
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 0);
 
     /* Initiate the pairing procedure. */
@@ -667,40 +707,40 @@ ble_l2cap_sm_test_util_us_lgcy_good(
     /* Ensure we sent the expected pair request. */
     ble_hs_test_util_tx_all();
     ble_l2cap_sm_test_util_verify_tx_pair_req(pair_req);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Receive a pair response from the peer. */
     ble_l2cap_sm_test_util_rx_pair_rsp(conn, pair_rsp);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Ensure we sent the expected pair confirm. */
     ble_hs_test_util_tx_all();
     ble_l2cap_sm_test_util_verify_tx_pair_confirm(confirm_req);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Receive a pair confirm from the peer. */
     ble_l2cap_sm_test_util_rx_confirm(conn, confirm_rsp);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Ensure we sent the expected pair random. */
     ble_hs_test_util_tx_all();
     ble_l2cap_sm_test_util_verify_tx_pair_random(random_req);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Receive a pair random from the peer. */
     ble_l2cap_sm_test_util_rx_random(conn, random_rsp, 0);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Ensure we sent the expected start encryption command. */
     ble_hs_test_util_tx_all();
     ble_l2cap_sm_test_util_verify_tx_start_enc(2, r, ediv, stk);
-    TEST_ASSERT(!conn->bhc_sec_params.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
     TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
 
     /* Receive an encryption changed event. */
@@ -712,17 +752,17 @@ ble_l2cap_sm_test_util_us_lgcy_good(
     /* Verify that security callback was executed. */
     TEST_ASSERT(ble_l2cap_sm_test_gap_event == BLE_GAP_EVENT_SECURITY);
     TEST_ASSERT(ble_l2cap_sm_test_gap_status == 0);
-    TEST_ASSERT(ble_l2cap_sm_test_sec_params.pair_alg == pair_alg);
-    TEST_ASSERT(ble_l2cap_sm_test_sec_params.enc_enabled);
-    TEST_ASSERT(!ble_l2cap_sm_test_sec_params.authenticated);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.pair_alg == pair_alg);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.enc_enabled);
+    TEST_ASSERT(!ble_l2cap_sm_test_sec_state.authenticated);
 
     /* Verify that connection has correct security state. */
-    TEST_ASSERT(ble_l2cap_sm_test_sec_params.pair_alg ==
-                conn->bhc_sec_params.pair_alg);
-    TEST_ASSERT(ble_l2cap_sm_test_sec_params.enc_enabled ==
-                conn->bhc_sec_params.enc_enabled);
-    TEST_ASSERT(ble_l2cap_sm_test_sec_params.authenticated ==
-                conn->bhc_sec_params.authenticated);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.pair_alg ==
+                conn->bhc_sec_state.pair_alg);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.enc_enabled ==
+                conn->bhc_sec_state.enc_enabled);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.authenticated ==
+                conn->bhc_sec_state.authenticated);
 }
 
 TEST_CASE(ble_l2cap_sm_test_case_us_lgcy_jw_good)
@@ -781,11 +821,132 @@ TEST_CASE(ble_l2cap_sm_test_case_us_lgcy_jw_good)
     );
 }
 
+static void
+ble_l2cap_sm_test_util_peer_bonding_good(uint8_t *ltk, int authenticated,
+                                         uint16_t ediv, uint64_t rand_num)
+{
+    struct ble_l2cap_sm_test_ltk_info ltk_info;
+    struct ble_hs_conn *conn;
+
+    ble_l2cap_sm_test_util_init();
+
+    memcpy(ltk_info.ltk, ltk, sizeof ltk_info.ltk);
+    ltk_info.authenticated = authenticated;
+
+    conn = ble_hs_test_util_create_conn(2, ((uint8_t[6]){1,2,3,4,5,6}),
+                                        ble_l2cap_sm_test_util_conn_cb,
+                                        &ltk_info);
+
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
+    TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 0);
+
+    /* Receive a long term key request from the controller. */
+    ble_l2cap_sm_test_util_set_lt_key_req_reply_ack(0, 2);
+    ble_l2cap_sm_test_util_rx_lt_key_req(2, rand_num, ediv);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
+    TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
+
+    /* Ensure the LTK request event got sent to the application. */
+    TEST_ASSERT(ble_l2cap_sm_test_gap_event == BLE_GAP_EVENT_LTK_REQUEST);
+    TEST_ASSERT(ble_l2cap_sm_test_gap_status == 0);
+    TEST_ASSERT(ble_l2cap_sm_test_ltk_params.ediv == ediv);
+    TEST_ASSERT(ble_l2cap_sm_test_ltk_params.rand_num == rand_num);
+    TEST_ASSERT(memcmp(ble_l2cap_sm_test_ltk_params.ltk, ltk_info.ltk,
+                       16) == 0);
+    TEST_ASSERT(ble_l2cap_sm_test_ltk_params.authenticated ==
+                ltk_info.authenticated);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
+    TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
+
+    /* Ensure we sent the expected long term key request reply command. */
+    ble_l2cap_sm_test_util_verify_tx_lt_key_req_reply(2, ltk_info.ltk);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
+    TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 1);
+
+    /* Receive an encryption changed event. */
+    ble_l2cap_sm_test_util_rx_enc_change(2, 0, 1);
+
+    /* Pairing should now be complete. */
+    TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 0);
+
+    /* Verify that security callback was executed. */
+    TEST_ASSERT(ble_l2cap_sm_test_gap_event == BLE_GAP_EVENT_SECURITY);
+    TEST_ASSERT(ble_l2cap_sm_test_gap_status == 0);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.enc_enabled);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.authenticated ==
+                ltk_info.authenticated);
+
+    /* Verify that connection has correct security state. */
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.enc_enabled);
+    TEST_ASSERT(ble_l2cap_sm_test_sec_state.authenticated ==
+                ltk_info.authenticated);
+}
+
+static void
+ble_l2cap_sm_test_util_peer_bonding_bad(uint16_t ediv, uint64_t rand_num)
+{
+    struct ble_hs_conn *conn;
+
+    ble_l2cap_sm_test_util_init();
+
+    conn = ble_hs_test_util_create_conn(2, ((uint8_t[6]){1,2,3,4,5,6}),
+                                        ble_l2cap_sm_test_util_conn_cb,
+                                        NULL);
+
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
+    TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 0);
+
+    /* Receive a long term key request from the controller. */
+    ble_l2cap_sm_test_util_set_lt_key_req_reply_ack(0, 2);
+    ble_l2cap_sm_test_util_rx_lt_key_req(2, rand_num, ediv);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
+
+    /* Ensure the LTK request event got sent to the application. */
+    TEST_ASSERT(ble_l2cap_sm_test_gap_event == BLE_GAP_EVENT_LTK_REQUEST);
+    TEST_ASSERT(ble_l2cap_sm_test_gap_status == 0);
+    TEST_ASSERT(ble_l2cap_sm_test_ltk_params.ediv == ediv);
+    TEST_ASSERT(ble_l2cap_sm_test_ltk_params.rand_num == rand_num);
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
+
+    /* Ensure we sent the expected long term key request neg reply command. */
+    ble_l2cap_sm_test_util_verify_tx_lt_key_req_neg_reply(2);
+
+    /* Ensure the security procedure was aborted. */
+    TEST_ASSERT(!conn->bhc_sec_state.enc_enabled);
+    TEST_ASSERT(!conn->bhc_sec_state.authenticated);
+    TEST_ASSERT(ble_l2cap_sm_dbg_num_procs() == 0);
+}
+
+TEST_CASE(ble_l2cap_sm_test_case_peer_bonding_good)
+{
+    /* Unauthenticated. */
+    ble_l2cap_sm_test_util_peer_bonding_good(
+        ((uint8_t[16]){ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 }),
+        0,
+        0x1234,
+        0x5678);
+
+    /* Authenticated. */
+    ble_l2cap_sm_test_util_peer_bonding_good(
+        ((uint8_t[16]){ 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 }),
+        1,
+        0x4325,
+        0x543892375);
+}
+
+TEST_CASE(ble_l2cap_sm_test_case_peer_bonding_bad)
+{
+    ble_l2cap_sm_test_util_peer_bonding_bad(0x5684, 32);
+    ble_l2cap_sm_test_util_peer_bonding_bad(54325, 65437);
+}
+
 TEST_SUITE(ble_l2cap_sm_test_suite)
 {
     ble_l2cap_sm_test_case_peer_lgcy_jw_good();
     ble_l2cap_sm_test_case_peer_lgcy_fail();
     ble_l2cap_sm_test_case_us_lgcy_jw_good();
+    ble_l2cap_sm_test_case_peer_bonding_good();
+    ble_l2cap_sm_test_case_peer_bonding_bad();
 }
 #endif
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/net/nimble/include/nimble/hci_common.h
----------------------------------------------------------------------
diff --git a/net/nimble/include/nimble/hci_common.h b/net/nimble/include/nimble/hci_common.h
index e5c3e2c..a1ae509 100644
--- a/net/nimble/include/nimble/hci_common.h
+++ b/net/nimble/include/nimble/hci_common.h
@@ -310,7 +310,11 @@
 
 /* ---  LE long term key request reply command (OCF 0x001a) */
 #define BLE_HCI_LT_KEY_REQ_REPLY_LEN        (18)
-#define BLE_HCI_LT_KEY_REQ_REPLY_ACK_PARAM_LEN (3) /* Includes status byte. */
+#define BLE_HCI_LT_KEY_REQ_REPLY_ACK_PARAM_LEN (2) /* No status byte. */
+
+/* ---  LE long term key request negative reply command (OCF 0x001b) */
+#define BLE_HCI_LT_KEY_REQ_NEG_REPLY_LEN    (2)
+#define BLE_HCI_LT_KEY_REQ_NEG_REPLY_ACK_PARAM_LEN (2)
 
 /* --- LE read supported states (OCF 0x001C) --- */
 #define BLE_HCI_RD_SUPP_STATES_RSPLEN       (8)


[2/2] incubator-mynewt-core git commit: BLE host - bonding.

Posted by cc...@apache.org.
BLE host - bonding.

This is a collection of many commits from Paul and me.  Unfortunately,
merging properly proved to be too difficult for my feeble git skills due
to the original branch coming from master rather than develop.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/264a67b9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/264a67b9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/264a67b9

Branch: refs/heads/develop
Commit: 264a67b95ef65e7a60117a22f40989ccd54ffeca
Parents: 78ebe68
Author: Christopher Collins <cc...@apache.org>
Authored: Fri May 6 19:03:52 2016 -0700
Committer: Christopher Collins <cc...@apache.org>
Committed: Fri May 6 19:03:52 2016 -0700

----------------------------------------------------------------------
 apps/bletiny/src/bletiny_priv.h              |   5 +
 apps/bletiny/src/cmd.c                       | 122 +++-
 apps/bletiny/src/main.c                      | 215 ++++--
 apps/bletiny/src/parse.c                     |  51 +-
 net/nimble/host/include/host/ble_gap.h       |  42 +-
 net/nimble/host/include/host/ble_l2cap.h     |   5 +
 net/nimble/host/include/host/host_hci.h      |   3 +-
 net/nimble/host/src/ble_att_svr.c            |  14 +-
 net/nimble/host/src/ble_gap.c                |  76 +-
 net/nimble/host/src/ble_gap_priv.h           |   6 +-
 net/nimble/host/src/ble_hs_conn_priv.h       |   2 +-
 net/nimble/host/src/ble_l2cap_sm.c           | 824 ++++++++++++++++++----
 net/nimble/host/src/ble_l2cap_sm_cmd.c       | 160 ++++-
 net/nimble/host/src/ble_l2cap_sm_priv.h      |  95 ++-
 net/nimble/host/src/host_hci_cmd.c           |  14 +
 net/nimble/host/src/test/ble_l2cap_sm_test.c | 263 +++++--
 net/nimble/include/nimble/hci_common.h       |   6 +-
 17 files changed, 1610 insertions(+), 293 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/apps/bletiny/src/bletiny_priv.h
----------------------------------------------------------------------
diff --git a/apps/bletiny/src/bletiny_priv.h b/apps/bletiny/src/bletiny_priv.h
index 84967e2..d27740f 100644
--- a/apps/bletiny/src/bletiny_priv.h
+++ b/apps/bletiny/src/bletiny_priv.h
@@ -98,12 +98,15 @@ const struct cmd_entry *parse_cmd_find(const struct cmd_entry *cmds,
 struct kv_pair *parse_kv_find(struct kv_pair *kvs, char *name);
 char *parse_arg_find(char *key);
 long parse_arg_long_bounds(char *name, long min, long max, int *out_status);
+uint64_t parse_arg_uint64_bounds(char *name, uint64_t min,
+                                 uint64_t max, int *out_status);
 long parse_arg_long(char *name, int *staus);
 uint8_t parse_arg_bool(char *name, int *status);
 uint8_t parse_arg_uint8(char *name, int *status);
 uint16_t parse_arg_uint16(char *name, int *status);
 uint16_t parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status);
 uint32_t parse_arg_uint32(char *name, int *out_status);
+uint64_t parse_arg_uint64(char *name, int *out_status);
 int parse_arg_kv(char *name, struct kv_pair *kvs);
 int parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len);
 int parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len);
@@ -164,6 +167,8 @@ int bletiny_l2cap_update(uint16_t conn_handle,
                           struct ble_l2cap_sig_update_params *params);
 int bletiny_show_rssi(uint16_t conn_handle);
 int bletiny_sec_start(uint16_t conn_handle);
+int bletiny_sec_restart(uint16_t conn_handle, uint8_t *ltk, uint16_t ediv,
+                        uint64_t rand_val, int auth);
 
 #define BLETINY_LOG_MODULE  (LOG_MODULE_PERUSER + 0)
 #define BLETINY_LOG(lvl, ...) \

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/apps/bletiny/src/cmd.c
----------------------------------------------------------------------
diff --git a/apps/bletiny/src/cmd.c b/apps/bletiny/src/cmd.c
index 458c1ca..3a1ae76 100644
--- a/apps/bletiny/src/cmd.c
+++ b/apps/bletiny/src/cmd.c
@@ -760,13 +760,9 @@ cmd_scan(int argc, char **argv)
 static int
 cmd_show_addr(int argc, char **argv)
 {
-    bletiny_lock();
-
     print_addr(g_dev_addr);
     console_printf("\n");
 
-    bletiny_unlock();
-
     return 0;
 }
 
@@ -777,8 +773,6 @@ cmd_show_chr(int argc, char **argv)
     struct bletiny_svc *svc;
     int i;
 
-    bletiny_lock();
-
     for (i = 0; i < bletiny_num_conns; i++) {
         conn = bletiny_conns + i;
 
@@ -791,8 +785,6 @@ cmd_show_chr(int argc, char **argv)
         }
     }
 
-    bletiny_unlock();
-
     return 0;
 }
 
@@ -802,8 +794,6 @@ cmd_show_conn(int argc, char **argv)
     struct bletiny_conn *conn;
     int i;
 
-    bletiny_lock();
-
     for (i = 0; i < bletiny_num_conns; i++) {
         conn = bletiny_conns + i;
 
@@ -812,8 +802,6 @@ cmd_show_conn(int argc, char **argv)
         console_printf(" addr_type=%d\n", conn->addr_type);
     }
 
-    bletiny_unlock();
-
     return 0;
 }
 
@@ -843,8 +831,6 @@ cmd_show_svc(int argc, char **argv)
     struct bletiny_svc *svc;
     int i;
 
-    bletiny_lock();
-
     for (i = 0; i < bletiny_num_conns; i++) {
         conn = bletiny_conns + i;
 
@@ -857,8 +843,6 @@ cmd_show_svc(int argc, char **argv)
         }
     }
 
-    bletiny_unlock();
-
     return 0;
 }
 
@@ -908,8 +892,70 @@ cmd_sec_start(int argc, char **argv)
     return 0;
 }
 
+static int
+cmd_sec_restart(int argc, char **argv)
+{
+    uint16_t conn_handle;
+    uint16_t ediv;
+    uint64_t rand_val;
+    uint8_t ltk[16];
+    int rc;
+    int auth;
+
+    conn_handle = parse_arg_uint16("conn", &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    ediv = parse_arg_uint16("ediv", &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rand_val = parse_arg_uint64("rand", &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    auth = parse_arg_bool("auth", &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = parse_arg_byte_stream_exact_length("ltk", ltk, 16);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bletiny_sec_restart( conn_handle, ltk, ediv, rand_val, auth);
+
+    if (rc != 0) {
+        console_printf("error starting encryption; rc=%d\n", rc);
+        return rc;
+    }
+
+    return -1;
+}
+
+static int
+cmd_sec_ltk(int argc, char **argv)
+{
+    /* TODO */
+    return -1;
+}
+
+static int
+cmd_sec_request(int argc, char **argv)
+{
+    /* TODO */
+    return -1;
+}
+
 static struct cmd_entry cmd_sec_entries[] = {
     { "start", cmd_sec_start },
+    { "restart", cmd_sec_restart },
+    { "ltk", cmd_sec_ltk },
+    { "request", cmd_sec_request }
 };
 
 static int
@@ -1189,13 +1235,16 @@ cmd_set_adv_data(void)
 }
 
 static int
-cmd_set_sm_data(void) {
-    int rc;
+cmd_set_sm_data(void)
+{
     uint8_t tmp;
-    int good = 0;
+    int good;
+    int rc;
+
+    good = 0;
 
     tmp = parse_arg_bool("oob_flag", &rc);
-    if(rc == 0) {
+    if (rc == 0) {
         ble_hs_cfg.sm_oob_data_flag = tmp;
         good++;
     } else if (rc != ENOENT) {
@@ -1203,25 +1252,50 @@ cmd_set_sm_data(void) {
     }
 
     tmp = parse_arg_bool("mitm_flag", &rc);
-    if(rc == 0) {
+    if (rc == 0) {
         good++;
         ble_hs_cfg.sm_mitm = tmp;
     } else if (rc != ENOENT) {
         return rc;
     }
 
-    tmp  = parse_arg_uint8("io_capabilities", &rc);
-    if(rc == 0) {
+    tmp = parse_arg_uint8("io_capabilities", &rc);
+    if (rc == 0) {
         good++;
         ble_hs_cfg.sm_io_cap = tmp;
     } else if (rc != ENOENT) {
         return rc;
     }
 
-    if (0 == good) {
+    tmp = parse_arg_uint8("our_key_dist", &rc);
+    if (rc == 0) {
+        good++;
+        ble_hs_cfg.sm_our_key_dist = tmp;
+    } else if (rc != ENOENT) {
+        return rc;
+    }
+
+    tmp = parse_arg_uint8("their_key_dist", &rc);
+    if (rc == 0) {
+        good++;
+        ble_hs_cfg.sm_their_key_dist = tmp;
+    } else if (rc != ENOENT) {
+        return rc;
+    }
+
+    tmp = parse_arg_bool("bonding", &rc);
+    if (rc == 0) {
+        good++;
+        ble_hs_cfg.sm_bonding = tmp;
+    } else if (rc != ENOENT) {
+        return rc;
+    }
+
+    if (!good) {
         console_printf("Error: no valid settings specified\n");
         return -1;
     }
+
     return 0;
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/apps/bletiny/src/main.c
----------------------------------------------------------------------
diff --git a/apps/bletiny/src/main.c b/apps/bletiny/src/main.c
index 14c1e7c..2d55ef0 100755
--- a/apps/bletiny/src/main.c
+++ b/apps/bletiny/src/main.c
@@ -59,8 +59,6 @@
 #define SHELL_TASK_STACK_SIZE   (OS_STACK_ALIGN(288))
 static bssnz_t os_stack_t shell_stack[SHELL_TASK_STACK_SIZE];
 
-static struct os_mutex bletiny_mutex;
-
 /* Our global device address (public) */
 uint8_t g_dev_addr[BLE_DEV_ADDR_LEN];
 
@@ -98,6 +96,8 @@ struct os_mempool default_mbuf_mpool;
 #define BLETINY_MAX_DSCS               1
 #endif
 
+#define BLETINY_MAX_LTKS                4
+
 struct os_eventq bletiny_evq;
 struct os_task bletiny_task;
 bssnz_t os_stack_t bletiny_stack[BLETINY_STACK_SIZE];
@@ -130,6 +130,9 @@ uint8_t bletiny_reconnect_addr[6];
 uint8_t bletiny_pref_conn_params[8];
 uint8_t bletiny_gatt_service_changed[4];
 
+struct ble_gap_ltk_params bletiny_ltks[BLETINY_MAX_LTKS];
+int bletiny_num_ltks;
+
 #define XSTR(s) STR(s)
 #define STR(s) #s
 
@@ -139,24 +142,6 @@ uint8_t bletiny_gatt_service_changed[4];
 #define BLETINY_AUTO_DEVICE_NAME    ""
 #endif
 
-void
-bletiny_lock(void)
-{
-    int rc;
-
-    rc = os_mutex_pend(&bletiny_mutex, 0xffffffff);
-    assert(rc == 0 || rc == OS_NOT_STARTED);
-}
-
-void
-bletiny_unlock(void)
-{
-    int rc;
-
-    rc = os_mutex_release(&bletiny_mutex);
-    assert(rc == 0 || rc == OS_NOT_STARTED);
-}
-
 static void
 bletiny_print_error(char *msg, uint16_t conn_handle,
                     struct ble_gatt_error *error)
@@ -181,18 +166,60 @@ bletiny_print_conn_desc(struct ble_gap_conn_desc *desc)
     console_printf("handle=%d peer_addr_type=%d peer_addr=",
                    desc->conn_handle, desc->peer_addr_type);
     bletiny_print_bytes(desc->peer_addr, 6);
-    console_printf(" conn_itvl=%d conn_latency=%d supervision_timeout=%d",
+    console_printf(" conn_itvl=%d conn_latency=%d supervision_timeout=%d "
+                   "pair_alg=%d enc_enabled=%d authenticated=%d",
                    desc->conn_itvl, desc->conn_latency,
-                   desc->supervision_timeout);
+                   desc->supervision_timeout,
+                   desc->sec_state.pair_alg,
+                   desc->sec_state.enc_enabled,
+                   desc->sec_state.authenticated);
 }
 
 static void
-bletiny_print_sec_params(struct ble_gap_sec_params *sec_params)
+bletiny_print_passkey_action_parms(struct ble_gap_passkey_action *pkact)
 {
-    console_printf("pair_alg=%d enc_enabled=%d authenticated=%d\n",
-                   sec_params->pair_alg,
-                   sec_params->enc_enabled,
-                   sec_params->authenticated);
+    console_printf("passkey Action Request %d\n", pkact->action);
+}
+
+static void
+bletiny_print_key_exchange_parms(struct ble_gap_key_parms *key_params)
+{
+    if (key_params->is_ours) {
+        console_printf("Our Keys Sent to Peer\n");
+    } else {
+        console_printf("Keys Received from Peer\n");
+    }
+
+    if (key_params->ltk_valid) {
+        console_printf("LTK=");
+        bletiny_print_bytes(key_params->ltk, 16);
+        console_printf("\n");
+    }
+
+    if (key_params->ediv_rand_valid) {
+        console_printf("EDIV=%u, rand=%llu ",
+                        key_params->ediv,
+                        key_params->rand_val);
+        console_printf("\n");
+    }
+
+    if (key_params->addr_valid) {
+        console_printf("Addr Type=%u -- ", key_params->addr_type);
+        bletiny_print_bytes(key_params->addr, 6);
+        console_printf("\n");
+    }
+
+    if (key_params->irk_valid) {
+        console_printf("IRK=");
+        bletiny_print_bytes(key_params->irk, 16);
+        console_printf("\n");
+    }
+
+    if (key_params->csrk_valid) {
+        console_printf("CSRK=");
+        bletiny_print_bytes(key_params->csrk, 16);
+        console_printf("\n");
+    }
 }
 
 static void
@@ -726,8 +753,6 @@ static int
 bletiny_on_disc_s(uint16_t conn_handle, struct ble_gatt_error *error,
                    struct ble_gatt_service *service, void *arg)
 {
-    bletiny_lock();
-
     if (error != NULL) {
         bletiny_print_error("ERROR DISCOVERING SERVICE", conn_handle, error);
     } else if (service != NULL) {
@@ -736,8 +761,6 @@ bletiny_on_disc_s(uint16_t conn_handle, struct ble_gatt_error *error,
         console_printf("service discovery successful\n");
     }
 
-    bletiny_unlock();
-
     return 0;
 }
 
@@ -753,9 +776,7 @@ bletiny_on_disc_c(uint16_t conn_handle, struct ble_gatt_error *error,
         bletiny_print_error("ERROR DISCOVERING CHARACTERISTIC", conn_handle,
                              error);
     } else if (chr != NULL) {
-        bletiny_lock();
         bletiny_chr_add(conn_handle, svc_start_handle, chr);
-        bletiny_unlock();
     } else {
         console_printf("characteristic discovery successful\n");
     }
@@ -768,8 +789,6 @@ bletiny_on_disc_d(uint16_t conn_handle, struct ble_gatt_error *error,
                    uint16_t chr_def_handle, struct ble_gatt_dsc *dsc,
                    void *arg)
 {
-    bletiny_lock();
-
     if (error != NULL) {
         bletiny_print_error("ERROR DISCOVERING DESCRIPTOR", conn_handle,
                              error);
@@ -779,8 +798,6 @@ bletiny_on_disc_d(uint16_t conn_handle, struct ble_gatt_error *error,
         console_printf("descriptor discovery successful\n");
     }
 
-    bletiny_unlock();
-
     return 0;
 }
 
@@ -849,7 +866,7 @@ bletiny_on_write_reliable(uint16_t conn_handle, struct ble_gatt_error *error,
 
 static int
 bletiny_on_notify(uint16_t conn_handle, uint16_t attr_handle,
-                   uint8_t *attr_val, uint16_t attr_len, void *arg)
+                  uint8_t *attr_val, uint16_t attr_len, void *arg)
 {
     console_printf("received notification from conn_handle=%d attr=%d "
                    "len=%d value=", conn_handle, attr_handle, attr_len);
@@ -861,12 +878,56 @@ bletiny_on_notify(uint16_t conn_handle, uint16_t attr_handle,
 }
 
 static int
+bletiny_ltk_lookup(struct ble_gap_ltk_params *ltk_params)
+{
+    struct ble_gap_ltk_params *ltk;
+    int i;
+
+    for (i = 0; i < bletiny_num_ltks; i++) {
+        ltk = bletiny_ltks + i;
+
+        if (ltk->ediv == ltk_params->ediv &&
+            ltk->rand_num == ltk_params->rand_num) {
+
+            memcpy(ltk_params->ltk, ltk->ltk, sizeof ltk_params->ltk);
+            ltk_params->authenticated = ltk->authenticated;
+
+            return 0;
+        }
+    }
+
+    return BLE_HS_ENOENT;
+}
+
+static int
+bletiny_ltk_add(uint16_t ediv, uint64_t rand_num, uint8_t *key,
+                int authenticated)
+{
+    struct ble_gap_ltk_params *ltk;
+
+    if (bletiny_num_ltks >= BLETINY_MAX_LTKS) {
+        return BLE_HS_ENOMEM;
+    }
+
+    ltk = bletiny_ltks + bletiny_num_ltks;
+    bletiny_num_ltks++;
+
+    ltk->ediv = ediv;
+    ltk->rand_num = rand_num;
+    memcpy(ltk->ltk, key, sizeof ltk->ltk);
+    ltk->authenticated = authenticated;
+
+    return 0;
+}
+
+/* XXX: LTK clear. */
+
+static int
 bletiny_on_connect(int event, int status, struct ble_gap_conn_ctxt *ctxt,
                    void *arg)
 {
     int conn_idx;
-
-    bletiny_lock();
+    int rc;
 
     switch (event) {
     case BLE_GAP_EVENT_CONN:
@@ -893,29 +954,65 @@ bletiny_on_connect(int event, int status, struct ble_gap_conn_ctxt *ctxt,
                 bletiny_start_auto_advertise();
             }
         }
-
-        break;
+        return 0;
 
     case BLE_GAP_EVENT_CONN_UPDATED:
         console_printf("connection updated; status=%d ", status);
         bletiny_print_conn_desc(ctxt->desc);
         console_printf("\n");
-        break;
+        return 0;
 
     case BLE_GAP_EVENT_CONN_UPDATE_REQ:
         console_printf("connection update request; status=%d ", status);
         *ctxt->update.self_params = *ctxt->update.peer_params;
-        break;
+        return 0;
 
     case BLE_GAP_EVENT_SECURITY:
         console_printf("security event; status=%d ", status);
-        bletiny_print_sec_params(ctxt->sec_params);
-        break;
-    }
+        bletiny_print_conn_desc(ctxt->desc);
+        console_printf("\n");
+        return 0;
+
+    case BLE_GAP_EVENT_PASSKEY_ACTION:
+        console_printf("passkey action event; status=%d ", status);
+        bletiny_print_passkey_action_parms(ctxt->passkey_action);
+        return 0;
+
+    case BLE_GAP_EVENT_LTK_REQUEST:
+        console_printf("looking up ltk with ediv=0x%02x rand=0x%llx\n",
+                       ctxt->ltk_params->ediv, ctxt->ltk_params->rand_num);
+        rc = bletiny_ltk_lookup(ctxt->ltk_params);
+        if (rc == 0) {
+            console_printf("ltk=");
+            bletiny_print_bytes(ctxt->ltk_params->ltk,
+                                sizeof ctxt->ltk_params->ltk);
+            console_printf("\n");
+        } else {
+            console_printf("no matching ltk\n");
+        }
+        return rc;
 
-    bletiny_unlock();
+    case BLE_GAP_EVENT_KEY_EXCHANGE:
+        console_printf("key exchange event; status=%d ", status);
+        bletiny_print_key_exchange_parms(ctxt->key_params);
 
-    return 0;
+        if (ctxt->key_params->is_ours   &&
+            ctxt->key_params->ltk_valid &&
+            ctxt->key_params->ediv_rand_valid) {
+
+            rc = bletiny_ltk_add(ctxt->key_params->ediv,
+                                 ctxt->key_params->rand_val,
+                                 ctxt->key_params->ltk,
+                                 ctxt->desc->sec_state.authenticated);
+            if (rc != 0) {
+                console_printf("error persisting LTK; status=%d\n", rc);
+            }
+        }
+        return 0;
+
+    default:
+        return 0;
+    }
 }
 
 static void
@@ -1251,6 +1348,23 @@ bletiny_sec_start(uint16_t conn_handle)
     return rc;
 }
 
+int
+bletiny_sec_restart(uint16_t conn_handle,
+                    uint8_t *ltk,
+                    uint16_t ediv,
+                    uint64_t rand_val,
+                    int auth)
+{
+    #if !NIMBLE_OPT_SM
+        return BLE_HS_ENOTSUP;
+    #endif
+
+    int rc;
+
+    rc = ble_gap_encryption_initiate(conn_handle, ltk, ediv, rand_val, auth);
+    return rc;
+}
+
 /**
  * BLE test task
  *
@@ -1410,9 +1524,6 @@ main(void)
     htole16(bletiny_pref_conn_params + 4, 0);
     htole16(bletiny_pref_conn_params + 6, BSWAP16(0x100));
 
-    rc = os_mutex_init(&bletiny_mutex);
-    assert(rc == 0);
-
     /* Start the OS */
     os_start();
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/apps/bletiny/src/parse.c
----------------------------------------------------------------------
diff --git a/apps/bletiny/src/parse.c b/apps/bletiny/src/parse.c
index f668f05..c6b247f 100644
--- a/apps/bletiny/src/parse.c
+++ b/apps/bletiny/src/parse.c
@@ -121,6 +121,21 @@ parse_arg_find(char *key)
     return NULL;
 }
 
+/**
+ * Determines which number base to use when parsing the specified numeric
+ * string.  This just avoids base '0' so that numbers don't get interpreted as
+ * octal.
+ */
+static int
+parse_arg_long_base(char *sval)
+{
+    if (sval[0] == '0' && sval[1] == 'x') {
+        return 0;
+    } else {
+        return 10;
+    }
+}
+
 long
 parse_arg_long_bounds(char *name, long min, long max, int *out_status)
 {
@@ -134,7 +149,32 @@ parse_arg_long_bounds(char *name, long min, long max, int *out_status)
         return 0;
     }
 
-    lval = strtol(sval, &endptr, 0);
+    lval = strtol(sval, &endptr, parse_arg_long_base(sval));
+    if (sval[0] != '\0' && *endptr == '\0' &&
+        lval >= min && lval <= max) {
+
+        *out_status = 0;
+        return lval;
+    }
+
+    *out_status = EINVAL;
+    return 0;
+}
+
+uint64_t
+parse_arg_uint64_bounds(char *name, uint64_t min, uint64_t max, int *out_status)
+{
+    char *endptr;
+    char *sval;
+    uint64_t lval;
+
+    sval = parse_arg_find(name);
+    if (sval == NULL) {
+        *out_status = ENOENT;
+        return 0;
+    }
+
+    lval = strtoull(sval, &endptr, parse_arg_long_base(sval));
     if (sval[0] != '\0' && *endptr == '\0' &&
         lval >= min && lval <= max) {
 
@@ -173,8 +213,13 @@ parse_arg_uint16(char *name, int *out_status)
 uint32_t
 parse_arg_uint32(char *name, int *out_status)
 {
-    /* this currently doesn't work as longs are 32-bits as well */
-    return parse_arg_long_bounds(name, 0, UINT32_MAX, out_status);
+    return parse_arg_uint64_bounds(name, 0, UINT32_MAX, out_status);
+}
+
+uint64_t
+parse_arg_uint64(char *name, int *out_status)
+{
+    return parse_arg_uint64_bounds(name, 0, UINT64_MAX, out_status);
 }
 
 uint16_t

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/net/nimble/host/include/host/ble_gap.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_gap.h b/net/nimble/host/include/host/ble_gap.h
index 3e5b677..6cbe2b4 100644
--- a/net/nimble/host/include/host/ble_gap.h
+++ b/net/nimble/host/include/host/ble_gap.h
@@ -98,8 +98,17 @@ struct hci_adv_params;
 #define BLE_GAP_EVENT_ADV_FINISHED          8
 #define BLE_GAP_EVENT_SECURITY              9
 #define BLE_GAP_EVENT_PASSKEY_ACTION        10
+#define BLE_GAP_EVENT_LTK_REQUEST           11
+#define BLE_GAP_EVENT_KEY_EXCHANGE          12
+
+struct ble_gap_sec_state {
+    uint8_t pair_alg;
+    unsigned enc_enabled:1;
+    unsigned authenticated:1;
+};
 
 struct ble_gap_conn_desc {
+    struct ble_gap_sec_state sec_state;
     uint8_t peer_addr[6];
     uint16_t conn_handle;
     uint16_t conn_itvl;
@@ -128,9 +137,29 @@ struct ble_gap_upd_params {
     uint16_t max_ce_len;
 };
 
-struct ble_gap_sec_params {
-    uint8_t pair_alg;
-    unsigned enc_enabled:1;
+struct ble_gap_key_parms {
+    unsigned is_ours:1;
+    unsigned ltk_valid:1;
+    unsigned ediv_rand_valid:1;
+    unsigned irk_valid:1;
+    unsigned csrk_valid:1;
+    unsigned addr_valid:1;
+    uint16_t ediv;
+    uint64_t rand_val;
+    uint8_t addr_type;
+    uint8_t ltk[16];
+    uint8_t irk[16];
+    uint8_t csrk[16];
+    uint8_t  addr[6];
+};
+
+struct ble_gap_ltk_params {
+    /* host --> app. */
+    uint16_t ediv;
+    uint64_t rand_num;
+
+    /* app --> host. */
+    uint8_t ltk[16];
     unsigned authenticated:1;
 };
 
@@ -158,8 +187,9 @@ struct ble_gap_conn_ctxt {
             struct ble_gap_upd_params *peer_params;
         } update;
 
-        struct ble_gap_sec_params *sec_params;
         struct ble_gap_passkey_action *passkey_action;
+        struct ble_gap_ltk_params *ltk_params;
+        struct ble_gap_key_parms *key_params;
     };
 };
 
@@ -214,5 +244,7 @@ int ble_gap_wl_set(struct ble_gap_white_entry *white_list,
 int ble_gap_update_params(uint16_t conn_handle,
                           struct ble_gap_upd_params *params);
 int ble_gap_security_initiate(uint16_t conn_handle);
-
+int
+ble_gap_encryption_initiate(uint16_t conn_handle, uint8_t *ltk,
+                            uint16_t ediv, uint64_t rand_val, int auth);
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/net/nimble/host/include/host/ble_l2cap.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_l2cap.h b/net/nimble/host/include/host/ble_l2cap.h
index 44a0701..da26025 100644
--- a/net/nimble/host/include/host/ble_l2cap.h
+++ b/net/nimble/host/include/host/ble_l2cap.h
@@ -86,6 +86,11 @@ struct ble_hs_conn;
 #define BLE_L2CAP_SM_PAIR_ALG_PASSKEY       1
 #define BLE_L2CAP_SM_PAIR_ALG_OOB           2
 
+#define BLE_L2CAP_SM_KEY_DIST_ENC           0x01
+#define BLE_L2CAP_SM_KEY_DIST_ID            0x02
+#define BLE_L2CAP_SM_KEY_DIST_SIGN          0x04
+#define BLE_L2CAP_SM_KEY_DIST_LINK          0x08
+
 typedef void ble_l2cap_sig_update_fn(int status, void *arg);
 
 struct ble_l2cap_sig_update_params {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/net/nimble/host/include/host/host_hci.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/host_hci.h b/net/nimble/host/include/host/host_hci.h
index c7d1222..747f2c5 100644
--- a/net/nimble/host/include/host/host_hci.h
+++ b/net/nimble/host/include/host/host_hci.h
@@ -78,7 +78,8 @@ int host_hci_cmd_build_le_conn_update(struct hci_conn_update *hcu,
 int host_hci_cmd_le_conn_update(struct hci_conn_update *hcu);
 void host_hci_cmd_build_le_lt_key_req_reply(struct hci_lt_key_req_reply *hkr,
                                             uint8_t *dst, int dst_len);
-int host_hci_cmd_le_lt_key_req_neg_reply(uint16_t handle);
+void host_hci_cmd_build_le_lt_key_req_neg_reply(uint16_t conn_handle,
+                                                uint8_t *dst, int dst_len);
 void host_hci_cmd_build_le_conn_param_reply(struct hci_conn_param_reply *hcr,
                                             uint8_t *dst, int dst_len);
 int host_hci_cmd_le_conn_param_reply(struct hci_conn_param_reply *hcr);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/net/nimble/host/src/ble_att_svr.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_att_svr.c b/net/nimble/host/src/ble_att_svr.c
index 757cd89..6aff564 100644
--- a/net/nimble/host/src/ble_att_svr.c
+++ b/net/nimble/host/src/ble_att_svr.c
@@ -214,15 +214,15 @@ ble_att_svr_pullup_req_base(struct os_mbuf **om, int base_len,
 }
 
 static int
-ble_att_svr_get_sec_params(uint16_t conn_handle,
-                           struct ble_gap_sec_params *out_sec_params)
+ble_att_svr_get_sec_state(uint16_t conn_handle,
+                          struct ble_gap_sec_state *out_sec_state)
 {
     struct ble_hs_conn *conn;
 
     ble_hs_lock();
     conn = ble_hs_conn_find(conn_handle);
     if (conn != NULL) {
-        *out_sec_params = conn->bhc_sec_params;
+        *out_sec_state = conn->bhc_sec_state;
     }
     ble_hs_unlock();
 
@@ -238,7 +238,7 @@ ble_att_svr_check_security(uint16_t conn_handle,
                            struct ble_att_svr_entry *entry,
                            uint8_t *out_att_err)
 {
-    struct ble_gap_sec_params sec_params;
+    struct ble_gap_sec_state sec_state;
     int rc;
 
     if (!(entry->ha_flags & (HA_FLAG_ENC_REQ |
@@ -248,12 +248,12 @@ ble_att_svr_check_security(uint16_t conn_handle,
         return 0;
     }
 
-    rc = ble_att_svr_get_sec_params(conn_handle, &sec_params);
+    rc = ble_att_svr_get_sec_state(conn_handle, &sec_state);
     if (rc != 0) {
         return rc;
     }
 
-    if (entry->ha_flags & HA_FLAG_ENC_REQ && !sec_params.enc_enabled) {
+    if (entry->ha_flags & HA_FLAG_ENC_REQ && !sec_state.enc_enabled) {
         /* XXX: Check security database; if required key present, respond with
          * insufficient encryption error code.
          */
@@ -262,7 +262,7 @@ ble_att_svr_check_security(uint16_t conn_handle,
     }
 
     if (entry->ha_flags & HA_FLAG_AUTHENTICATION_REQ &&
-        !sec_params.authenticated) {
+        !sec_state.authenticated) {
 
         *out_att_err = BLE_ATT_ERR_INSUFFICIENT_AUTHENT;
         return BLE_HS_ATT_ERR(*out_att_err);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/net/nimble/host/src/ble_gap.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gap.c b/net/nimble/host/src/ble_gap.c
index 0636e05..97be06b 100644
--- a/net/nimble/host/src/ble_gap.c
+++ b/net/nimble/host/src/ble_gap.c
@@ -273,6 +273,7 @@ ble_gap_fill_conn_desc(struct ble_hs_conn *conn,
     desc->conn_itvl = conn->bhc_itvl;
     desc->conn_latency = conn->bhc_latency;
     desc->supervision_timeout = conn->bhc_supervision_timeout;
+    desc->sec_state = conn->bhc_sec_state;
 }
 
 static void
@@ -2005,6 +2006,29 @@ ble_gap_security_initiate(uint16_t conn_handle)
     rc = ble_l2cap_sm_initiate(conn_handle);
     return rc;
 }
+
+int
+ble_gap_encryption_initiate(uint16_t conn_handle,
+                            uint8_t *ltk,
+                            uint16_t ediv,
+                            uint64_t rand_val,
+                            int auth)
+{
+    ble_hs_conn_flags_t conn_flags;
+    int rc;
+
+    rc = ble_hs_atomic_conn_flags(conn_handle, &conn_flags);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (!(conn_flags & BLE_HS_CONN_F_MASTER)) {
+        return BLE_HS_EROLE;
+    }
+
+    rc = ble_l2cap_sm_sec_initiate(conn_handle, ltk, ediv, rand_val, auth);
+    return rc;
+}
 #endif
 
 void
@@ -2041,8 +2065,29 @@ ble_gap_passkey_event(uint16_t conn_handle, int status,
 }
 
 void
+ble_gap_key_exchange_event(uint16_t conn_handle,
+                           struct ble_gap_key_parms *key_params)
+{
+    struct ble_gap_conn_ctxt ctxt;
+    struct ble_gap_snapshot snap;
+    int rc;
+
+    rc = ble_gap_find_snapshot(conn_handle, &snap);
+    if (rc != 0) {
+        /* No longer connected. */
+        return;
+    }
+
+    memset(&ctxt, 0, sizeof ctxt);
+    ctxt.desc = &snap.desc;
+    ctxt.key_params = key_params;
+    ble_gap_call_conn_cb(BLE_GAP_EVENT_KEY_EXCHANGE, 0, &ctxt,
+                         snap.cb, snap.cb_arg);
+}
+
+void
 ble_gap_security_event(uint16_t conn_handle, int status,
-                       struct ble_gap_sec_params *sec_params)
+                       struct ble_gap_sec_state *sec_state)
 {
     struct ble_gap_conn_ctxt ctxt;
     struct ble_gap_snapshot snap;
@@ -2052,7 +2097,7 @@ ble_gap_security_event(uint16_t conn_handle, int status,
 
     conn = ble_hs_conn_find(conn_handle);
     if (conn != NULL) {
-        conn->bhc_sec_params = *sec_params;
+        conn->bhc_sec_state = *sec_state;
         ble_gap_conn_to_snapshot(conn, &snap);
     }
 
@@ -2065,11 +2110,36 @@ ble_gap_security_event(uint16_t conn_handle, int status,
 
     memset(&ctxt, 0, sizeof ctxt);
     ctxt.desc = &snap.desc;
-    ctxt.sec_params = sec_params;
     ble_gap_call_conn_cb(BLE_GAP_EVENT_SECURITY, status, &ctxt,
                          snap.cb, snap.cb_arg);
 }
 
+int
+ble_gap_ltk_event(uint16_t conn_handle, struct ble_gap_ltk_params *ltk_params)
+{
+    struct ble_gap_conn_ctxt ctxt;
+    struct ble_gap_snapshot snap;
+    int rc;
+
+    rc = ble_gap_find_snapshot(conn_handle, &snap);
+    if (rc != 0) {
+        /* No longer connected. */
+        return BLE_HS_ENOTCONN;
+    }
+
+    memset(&ctxt, 0, sizeof ctxt);
+    ctxt.desc = &snap.desc;
+    ctxt.ltk_params = ltk_params;
+    rc = ble_gap_call_conn_cb(BLE_GAP_EVENT_LTK_REQUEST, 0, &ctxt,
+                              snap.cb, snap.cb_arg);
+    if (rc != 0) {
+        /* No long-term key that matches the specified ediv and rand. */
+        return BLE_HS_EREJECT;
+    }
+
+    return 0;
+}
+
 /*****************************************************************************
  * $init                                                                     *
  *****************************************************************************/

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/net/nimble/host/src/ble_gap_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gap_priv.h b/net/nimble/host/src/ble_gap_priv.h
index 7776085..e84a276 100644
--- a/net/nimble/host/src/ble_gap_priv.h
+++ b/net/nimble/host/src/ble_gap_priv.h
@@ -75,9 +75,13 @@ void ble_gap_rx_param_req(struct hci_le_conn_param_req *evt);
 int ble_gap_rx_l2cap_update_req(uint16_t conn_handle,
                                 struct ble_gap_upd_params *params);
 void ble_gap_security_event(uint16_t conn_handle, int status,
-                            struct ble_gap_sec_params *sec_params);
+                            struct ble_gap_sec_state *sec_state);
 void ble_gap_passkey_event(uint16_t conn_handle, int status,
                            uint8_t passkey_action);
+void ble_gap_key_exchange_event(uint16_t conn_handle,
+                           struct ble_gap_key_parms *key_params);
+int ble_gap_ltk_event(uint16_t conn_handle,
+                      struct ble_gap_ltk_params *ltk_params);
 int ble_gap_master_in_progress(void);
 int ble_gap_slave_in_progress(void);
 int ble_gap_update_in_progress(uint16_t conn_handle);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/net/nimble/host/src/ble_hs_conn_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_conn_priv.h b/net/nimble/host/src/ble_hs_conn_priv.h
index c470c3d..aba0e94 100644
--- a/net/nimble/host/src/ble_hs_conn_priv.h
+++ b/net/nimble/host/src/ble_hs_conn_priv.h
@@ -51,7 +51,7 @@ struct ble_hs_conn {
     struct ble_att_svr_conn bhc_att_svr;
     struct ble_gatts_conn bhc_gatt_svr;
 
-    struct ble_gap_sec_params bhc_sec_params;
+    struct ble_gap_sec_state bhc_sec_state;
 
     ble_gap_conn_fn *bhc_cb;
     void *bhc_cb_arg;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/net/nimble/host/src/ble_l2cap_sm.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap_sm.c b/net/nimble/host/src/ble_l2cap_sm.c
index e6957a1..aca9260 100644
--- a/net/nimble/host/src/ble_l2cap_sm.c
+++ b/net/nimble/host/src/ble_l2cap_sm.c
@@ -57,12 +57,20 @@
 #define BLE_L2CAP_SM_PROC_STATE_RANDOM          2
 #define BLE_L2CAP_SM_PROC_STATE_LTK             3
 #define BLE_L2CAP_SM_PROC_STATE_ENC_CHANGE      4
-#define BLE_L2CAP_SM_PROC_STATE_CNT             5
+#define BLE_L2CAP_SM_PROC_STATE_KEY_EXCH        5
+#define BLE_L2CAP_SM_PROC_STATE_CNT             6
 
 #define BLE_L2CAP_SM_PROC_F_INITIATOR           0x01
 #define BLE_L2CAP_SM_PROC_F_TK_VALID            0x02
 #define BLE_L2CAP_SM_PROC_F_RX_CONFIRM          0x04
 #define BLE_L2CAP_SM_PROC_F_AUTHENTICATED       0x08
+#define BLE_L2CAP_SM_PROC_F_KEY_EXCHANGE        0x10
+
+#define BLE_L2CAP_SM_KE_F_ENC_INFO              0x01
+#define BLE_L2CAP_SM_KE_F_MASTER_IDEN           0x02
+#define BLE_L2CAP_SM_KE_F_IDEN_INFO             0x04
+#define BLE_L2CAP_SM_KE_F_ADDR_INFO             0x08
+#define BLE_L2CAP_SM_KE_F_SIGN_INFO             0x10
 
 /** Procedure timeout; 30 seconds. */
 #define BLE_L2CAP_SM_TIMEOUT_OS_TICKS           (30 * OS_TICKS_PER_SEC)
@@ -77,7 +85,7 @@ struct ble_l2cap_sm_proc {
     uint16_t conn_handle;
     uint8_t pair_alg;
     uint8_t state;
-
+    uint8_t rx_key_flags;
     /* XXX: Minimum security requirements. */
 
     struct ble_l2cap_sm_pair_cmd pair_req;
@@ -87,11 +95,15 @@ struct ble_l2cap_sm_proc {
     uint8_t randm[16];
     uint8_t rands[16];
     uint8_t ltk[16];
+
+    /* this may be temporary, but we keep the keys here for now */
+    struct ble_gap_key_parms our_keys;
+    struct ble_gap_key_parms peer_keys;
 };
 
 STAILQ_HEAD(ble_l2cap_sm_proc_list, ble_l2cap_sm_proc);
 
-typedef int ble_l2cap_sm_rx_fn(uint16_t conn_handle, uint8_t state,
+typedef int ble_l2cap_sm_rx_fn(uint16_t conn_handle, uint8_t op,
                                struct os_mbuf **om);
 
 static ble_l2cap_sm_rx_fn ble_l2cap_sm_rx_noop;
@@ -100,6 +112,7 @@ static ble_l2cap_sm_rx_fn ble_l2cap_sm_rx_pair_rsp;
 static ble_l2cap_sm_rx_fn ble_l2cap_sm_rx_pair_confirm;
 static ble_l2cap_sm_rx_fn ble_l2cap_sm_rx_pair_random;
 static ble_l2cap_sm_rx_fn ble_l2cap_sm_rx_pair_fail;
+static ble_l2cap_sm_rx_fn ble_l2cap_sm_rx_key_exchange;
 
 static ble_l2cap_sm_rx_fn * const ble_l2cap_sm_dispatch[] = {
    [BLE_L2CAP_SM_OP_PAIR_REQ] = ble_l2cap_sm_rx_pair_req,
@@ -107,11 +120,11 @@ static ble_l2cap_sm_rx_fn * const ble_l2cap_sm_dispatch[] = {
    [BLE_L2CAP_SM_OP_PAIR_CONFIRM] = ble_l2cap_sm_rx_pair_confirm,
    [BLE_L2CAP_SM_OP_PAIR_RANDOM] = ble_l2cap_sm_rx_pair_random,
    [BLE_L2CAP_SM_OP_PAIR_FAIL] = ble_l2cap_sm_rx_pair_fail,
-   [BLE_L2CAP_SM_OP_ENC_INFO] = ble_l2cap_sm_rx_noop,
-   [BLE_L2CAP_SM_OP_MASTER_ID] = ble_l2cap_sm_rx_noop,
-   [BLE_L2CAP_SM_OP_IDENTITY_INFO] = ble_l2cap_sm_rx_noop,
-   [BLE_L2CAP_SM_OP_IDENTITY_ADDR_INFO] = ble_l2cap_sm_rx_noop,
-   [BLE_L2CAP_SM_OP_SIGN_INFO] = ble_l2cap_sm_rx_noop,
+   [BLE_L2CAP_SM_OP_ENC_INFO] = ble_l2cap_sm_rx_key_exchange,
+   [BLE_L2CAP_SM_OP_MASTER_ID] = ble_l2cap_sm_rx_key_exchange,
+   [BLE_L2CAP_SM_OP_IDENTITY_INFO] = ble_l2cap_sm_rx_key_exchange,
+   [BLE_L2CAP_SM_OP_IDENTITY_ADDR_INFO] = ble_l2cap_sm_rx_key_exchange,
+   [BLE_L2CAP_SM_OP_SIGN_INFO] = ble_l2cap_sm_rx_key_exchange,
    [BLE_L2CAP_SM_OP_SEC_REQ] = ble_l2cap_sm_rx_noop,
    [BLE_L2CAP_SM_OP_PAIR_PUBLIC_KEY] = ble_l2cap_sm_rx_noop,
    [BLE_L2CAP_SM_OP_PAIR_DHKEY_CHECK] = ble_l2cap_sm_rx_noop,
@@ -130,6 +143,7 @@ static int ble_l2cap_sm_confirm_prepare_args(struct ble_l2cap_sm_proc *proc,
                                              uint8_t *rat, uint8_t *ia,
                                              uint8_t *ra);
 
+
 /*****************************************************************************
  * $debug                                                                    *
  *****************************************************************************/
@@ -142,6 +156,8 @@ static uint16_t ble_l2cap_sm_dbg_next_ediv;
 static uint8_t ble_l2cap_sm_dbg_next_ediv_set;
 static uint64_t ble_l2cap_sm_dbg_next_start_rand;
 static uint8_t ble_l2cap_sm_dbg_next_start_rand_set;
+static uint8_t ble_l2cap_sm_dbg_next_ltk[16];
+static uint8_t ble_l2cap_sm_dbg_next_ltk_set;
 
 void
 ble_l2cap_sm_dbg_set_next_pair_rand(uint8_t *next_pair_rand)
@@ -165,6 +181,14 @@ ble_l2cap_sm_dbg_set_next_start_rand(uint64_t next_start_rand)
     ble_l2cap_sm_dbg_next_start_rand_set = 1;
 }
 
+void
+ble_l2cap_sm_dbg_set_next_ltk(uint8_t *next_ltk)
+{
+    memcpy(ble_l2cap_sm_dbg_next_ltk, next_ltk,
+           sizeof ble_l2cap_sm_dbg_next_ltk);
+    ble_l2cap_sm_dbg_next_ltk_set = 1;
+}
+
 int
 ble_l2cap_sm_dbg_num_procs(void)
 {
@@ -185,6 +209,32 @@ ble_l2cap_sm_dbg_num_procs(void)
  * $misc                                                                     *
  *****************************************************************************/
 
+ static void
+ ble_l2cap_build_rx_key_exchange_state(struct ble_l2cap_sm_proc *proc)
+ {
+     uint8_t rx_key_dist;
+
+     /* if we are intiating we are waiting for the responders keys */
+     if (proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR) {
+         rx_key_dist = proc->pair_rsp.resp_key_dist;
+     } else {
+         rx_key_dist = proc->pair_rsp.init_key_dist;
+     }
+     proc->rx_key_flags = 0;
+
+     if (rx_key_dist & KEY_DIST_ENC_KEY) {
+         proc->rx_key_flags |= BLE_L2CAP_SM_KE_F_ENC_INFO |
+                               BLE_L2CAP_SM_KE_F_MASTER_IDEN;
+     }
+     if (rx_key_dist & KEY_DIST_ID_KEY) {
+         proc->rx_key_flags |= BLE_L2CAP_SM_KE_F_IDEN_INFO |
+                               BLE_L2CAP_SM_KE_F_ADDR_INFO;
+     }
+     if (rx_key_dist & KEY_DIST_SIGN) {
+         proc->rx_key_flags |= BLE_L2CAP_SM_KE_F_SIGN_INFO;
+     }
+ }
+
 static int
 ble_l2cap_sm_gen_pair_rand(uint8_t *pair_rand)
 {
@@ -250,7 +300,7 @@ ble_l2cap_sm_gen_start_rand(uint64_t *start_rand)
 }
 
 static int
-ble_l2cap_sm_gen_key(struct ble_l2cap_sm_proc *proc)
+ble_l2cap_sm_gen_stk(struct ble_l2cap_sm_proc *proc)
 {
     uint8_t key[16];
     int rc;
@@ -265,6 +315,28 @@ ble_l2cap_sm_gen_key(struct ble_l2cap_sm_proc *proc)
     return 0;
 }
 
+static int
+ble_l2cap_sm_gen_ltk(struct ble_l2cap_sm_proc *proc, uint8_t *ltk)
+{
+    int rc;
+
+#ifdef BLE_HS_DEBUG
+    if (ble_l2cap_sm_dbg_next_ltk_set) {
+        ble_l2cap_sm_dbg_next_ltk_set = 0;
+        memcpy(ltk, ble_l2cap_sm_dbg_next_ltk,
+               sizeof ble_l2cap_sm_dbg_next_ltk);
+        return 0;
+    }
+#endif
+
+    rc = ble_hci_util_rand(ltk, 16);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
 static void
 ble_l2cap_sm_proc_set_timer(struct ble_l2cap_sm_proc *proc)
 {
@@ -273,13 +345,13 @@ ble_l2cap_sm_proc_set_timer(struct ble_l2cap_sm_proc *proc)
 }
 
 static ble_l2cap_sm_rx_fn *
-ble_l2cap_sm_dispatch_get(uint8_t state)
+ble_l2cap_sm_dispatch_get(uint8_t op)
 {
-    if (state > sizeof ble_l2cap_sm_dispatch / sizeof ble_l2cap_sm_dispatch[0]) {
+    if (op > sizeof ble_l2cap_sm_dispatch / sizeof ble_l2cap_sm_dispatch[0]) {
         return NULL;
     }
 
-    return ble_l2cap_sm_dispatch[state];
+    return ble_l2cap_sm_dispatch[op];
 }
 
 /**
@@ -328,24 +400,34 @@ ble_l2cap_sm_proc_remove(struct ble_l2cap_sm_proc *proc,
 }
 
 static void
-ble_l2cap_sm_sec_params(struct ble_l2cap_sm_proc *proc,
-                        struct ble_gap_sec_params *out_sec_params,
-                        int enc_enabled)
+ble_l2cap_sm_sec_state(struct ble_l2cap_sm_proc *proc,
+                       struct ble_gap_sec_state *out_sec_state,
+                       int enc_enabled)
 {
-    out_sec_params->pair_alg = proc->pair_alg;
-    out_sec_params->enc_enabled = enc_enabled;
-    out_sec_params->authenticated =
+    out_sec_state->pair_alg = proc->pair_alg;
+    out_sec_state->enc_enabled = enc_enabled;
+    out_sec_state->authenticated =
             (proc->flags & BLE_L2CAP_SM_PROC_F_AUTHENTICATED) ? 1 : 0;
 }
 
 static void
+ble_l2cap_sm_key_exchange_events(struct ble_l2cap_sm_proc *proc) {
+
+    proc->our_keys.is_ours = 1;
+    proc->peer_keys.is_ours = 0;
+    /* TODO */
+    ble_gap_key_exchange_event(proc->conn_handle, &proc->our_keys);
+    ble_gap_key_exchange_event(proc->conn_handle, &proc->peer_keys);
+}
+
+static void
 ble_l2cap_sm_gap_event(struct ble_l2cap_sm_proc *proc, int status,
                        int enc_enabled)
 {
-    struct ble_gap_sec_params sec_params;
+    struct ble_gap_sec_state sec_state;
 
-    ble_l2cap_sm_sec_params(proc, &sec_params, enc_enabled);
-    ble_gap_security_event(proc->conn_handle, status, &sec_params);
+    ble_l2cap_sm_sec_state(proc, &sec_state, enc_enabled);
+    ble_gap_security_event(proc->conn_handle, status, &sec_state);
 }
 
 /* We must call this function when the host is unlocked because in
@@ -472,7 +554,7 @@ ble_l2cap_sm_extract_expired(struct ble_l2cap_sm_proc_list *dst_list)
 }
 
 static int
-ble_l2cap_sm_rx_noop(uint16_t conn_handle, uint8_t state, struct os_mbuf **om)
+ble_l2cap_sm_rx_noop(uint16_t conn_handle, uint8_t op, struct os_mbuf **om)
 {
     return BLE_HS_ENOTSUP;
 }
@@ -482,23 +564,16 @@ ble_l2cap_sm_rx_noop(uint16_t conn_handle, uint8_t state, struct os_mbuf **om)
  *****************************************************************************/
 
 static int
-ble_l2cap_sm_start_encrypt_tx(uint16_t conn_handle, uint8_t *ltk)
+ble_l2cap_sm_start_encrypt_tx(uint16_t conn_handle, uint16_t ediv,
+                              uint64_t rand_num, uint8_t *ltk)
 {
     struct hci_start_encrypt cmd;
     uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_LE_START_ENCRYPT_LEN];
     int rc;
 
-    rc = ble_l2cap_sm_gen_ediv(&cmd.encrypted_diversifier);
-    if (rc != 0) {
-        return rc;
-    }
-
-    rc = ble_l2cap_sm_gen_start_rand(&cmd.random_number);
-    if (rc != 0) {
-        return rc;
-    }
-
     cmd.connection_handle = conn_handle;
+    cmd.encrypted_diversifier = ediv;
+    cmd.random_number = rand_num;
     memcpy(cmd.long_term_key, ltk, sizeof cmd.long_term_key);
 
     host_hci_cmd_build_le_start_encrypt(&cmd, buf, sizeof buf);
@@ -528,7 +603,33 @@ ble_l2cap_sm_lt_key_req_reply_tx(uint16_t conn_handle, uint8_t *ltk)
     if (rc != 0) {
         return rc;
     }
-    if (ack_params_len != BLE_HCI_LT_KEY_REQ_REPLY_ACK_PARAM_LEN - 1) {
+    if (ack_params_len != BLE_HCI_LT_KEY_REQ_REPLY_ACK_PARAM_LEN) {
+        return BLE_HS_ECONTROLLER;
+    }
+
+    ack_conn_handle = TOFROMLE16(ack_conn_handle);
+    if (ack_conn_handle != conn_handle) {
+        return BLE_HS_ECONTROLLER;
+    }
+
+    return 0;
+}
+
+static int
+ble_l2cap_sm_lt_key_req_neg_reply_tx(uint16_t conn_handle)
+{
+    uint16_t ack_conn_handle;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_LT_KEY_REQ_NEG_REPLY_LEN];
+    uint8_t ack_params_len;
+    int rc;
+
+    host_hci_cmd_build_le_lt_key_req_neg_reply(conn_handle, buf, sizeof buf);
+    rc = ble_hci_cmd_tx(buf, &ack_conn_handle, sizeof ack_conn_handle,
+                        &ack_params_len);
+    if (rc != 0) {
+        return rc;
+    }
+    if (ack_params_len != BLE_HCI_LT_KEY_REQ_NEG_REPLY_ACK_PARAM_LEN) {
         return BLE_HS_ECONTROLLER;
     }
 
@@ -541,15 +642,13 @@ ble_l2cap_sm_lt_key_req_reply_tx(uint16_t conn_handle, uint8_t *ltk)
 }
 
 static int
-ble_l2cap_sm_lt_key_req_handle(struct ble_l2cap_sm_proc *proc,
-                               struct hci_le_lt_key_req *evt,
-                               uint8_t *out_sm_status)
+ble_l2cap_sm_lt_key_req_stk_handle(struct ble_l2cap_sm_proc *proc,
+                                   struct hci_le_lt_key_req *evt)
 {
     int rc;
 
     rc = ble_l2cap_sm_lt_key_req_reply_tx(proc->conn_handle, proc->ltk);
     if (rc != 0) {
-        *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
         return rc;
     }
 
@@ -639,15 +738,18 @@ ble_l2cap_sm_random_handle(struct ble_l2cap_sm_proc *proc,
     memcpy(ble_l2cap_sm_their_pair_rand(proc), cmd->value, 16);
 
     /* Generate the key. */
-    rc = ble_l2cap_sm_gen_key(proc);
+    rc = ble_l2cap_sm_gen_stk(proc);
     if (rc != 0) {
         *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
         return rc;
     }
 
     if (proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR) {
-        /* Send the start-encrypt HCI command to the controller. */
-        rc = ble_l2cap_sm_start_encrypt_tx(proc->conn_handle, proc->ltk);
+        /* Send the start-encrypt HCI command to the controller.   For
+         * short-term key generation, we always set ediv and rand to 0.
+         * (Vol. 3, part H, 2.4.4.1).
+         */
+        rc = ble_l2cap_sm_start_encrypt_tx(proc->conn_handle, 0, 0, proc->ltk);
         if (rc != 0) {
             *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
             return rc;
@@ -705,22 +807,21 @@ static const uint8_t responder_pkact[5 /*init*/ ][5 /*resp */] =
 static int
 ble_l2cap_sm_passkey_action(struct ble_l2cap_sm_proc *proc)
 {
-   /* set some default here */
     int action;
 
-    /* if both OOB set, then its OOB */
-    if(proc->pair_req.oob_data_flag && proc->pair_rsp.oob_data_flag) {
+    /* If both OOB set, then its OOB */
+    if (proc->pair_req.oob_data_flag && proc->pair_rsp.oob_data_flag) {
         action = BLE_GAP_PKACT_OOB;
     }
-    /* if neither MITM is set, then its just works */
-    else if(((proc->pair_req.authreq | proc->pair_rsp.authreq) & 0x04) == 0) {
+    /* If neither MITM is set, then its just works */
+    else if (!((proc->pair_req.authreq | proc->pair_rsp.authreq) & 0x04)) {
         action = BLE_GAP_PKACT_NONE;
     }
-    /* what to do if this in in error */
-    else if ((proc->pair_req.io_cap >= 5) || (proc->pair_rsp.io_cap >= 5)) {
+    /* What to do if this in in error */
+    /* XXX: Check this during pairing tx/rx. */
+    else if (proc->pair_req.io_cap >= 5 || proc->pair_rsp.io_cap >= 5) {
         action = BLE_GAP_PKACT_NONE;
     }
-    /* check io_cap */
     else if (proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR) {
         action = initiator_pkact[proc->pair_req.io_cap][proc->pair_rsp.io_cap];
     } else {
@@ -728,20 +829,21 @@ ble_l2cap_sm_passkey_action(struct ble_l2cap_sm_proc *proc)
     }
 
     /* set some state for the application */
-    switch(action) {
-        case BLE_GAP_PKACT_NONE:
-            proc->pair_alg = BLE_L2CAP_SM_PAIR_ALG_JW;
-            proc->flags &= ~BLE_L2CAP_SM_PROC_F_AUTHENTICATED;
-            break;
-        case BLE_GAP_PKACT_OOB:
-            proc->pair_alg = BLE_L2CAP_SM_PAIR_ALG_OOB;
-            proc->flags |= BLE_L2CAP_SM_PROC_F_AUTHENTICATED;
-            break;
-        case BLE_GAP_PKACT_INPUT:
-        case BLE_GAP_PKACT_DISP:
-            proc->pair_alg = BLE_L2CAP_SM_PAIR_ALG_PASSKEY;
-            proc->flags |= BLE_L2CAP_SM_PROC_F_AUTHENTICATED;
-            break;
+    switch (action) {
+    case BLE_GAP_PKACT_NONE:
+        proc->pair_alg = BLE_L2CAP_SM_PAIR_ALG_JW;
+        break;
+
+    case BLE_GAP_PKACT_OOB:
+        proc->pair_alg = BLE_L2CAP_SM_PAIR_ALG_OOB;
+        proc->flags |= BLE_L2CAP_SM_PROC_F_AUTHENTICATED;
+        break;
+
+    case BLE_GAP_PKACT_INPUT:
+    case BLE_GAP_PKACT_DISP:
+        proc->pair_alg = BLE_L2CAP_SM_PAIR_ALG_PASSKEY;
+        proc->flags |= BLE_L2CAP_SM_PROC_F_AUTHENTICATED;
+        break;
     }
     return action;
 }
@@ -843,10 +945,10 @@ ble_l2cap_sm_confirm_handle(struct ble_l2cap_sm_proc *proc,
         proc->state = BLE_L2CAP_SM_PROC_STATE_RANDOM;
     } else {
         proc->flags |= BLE_L2CAP_SM_PROC_F_RX_CONFIRM;
-        /* if there is no passkey action or if we already got the passkey */
-        if((ble_l2cap_sm_passkey_action(proc) == BLE_GAP_PKACT_NONE) ||
-           (proc->flags & BLE_L2CAP_SM_PROC_F_TK_VALID))
-        {
+
+        if (ble_l2cap_sm_passkey_action(proc) == BLE_GAP_PKACT_NONE ||
+            proc->flags & BLE_L2CAP_SM_PROC_F_TK_VALID) {
+
             rc = ble_l2cap_sm_confirm_go(proc);
             if (rc != 0) {
                 *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
@@ -872,10 +974,6 @@ ble_l2cap_sm_pair_go(struct ble_l2cap_sm_proc *proc)
 
     is_req = proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR;
 
-    /* when we start pairing, clear these flags */
-    proc->flags &=
-            ~(BLE_L2CAP_SM_PROC_F_TK_VALID | BLE_L2CAP_SM_PROC_F_RX_CONFIRM);
-
     cmd.io_cap = ble_hs_cfg.sm_io_cap;
     cmd.oob_data_flag = ble_hs_cfg.sm_oob_data_flag;
     cmd.authreq = (ble_hs_cfg.sm_bonding << 0)  |
@@ -912,6 +1010,22 @@ ble_l2cap_sm_pair_go(struct ble_l2cap_sm_proc *proc)
 
     return 0;
 }
+static int
+ble_l2cap_sm_check_key_exchange(struct ble_l2cap_sm_proc *proc)
+{
+    if ((proc->pair_req.authreq & proc->pair_rsp.authreq & 0x3) == 1) {
+        /* The pair response defines what shall be exchanged. */
+        if (proc->pair_rsp.init_key_dist | proc->pair_rsp.resp_key_dist) {
+            proc->flags |= BLE_L2CAP_SM_PROC_F_KEY_EXCHANGE;
+        }
+    }
+
+    /* Build the key exchange data. */
+    ble_l2cap_build_rx_key_exchange_state(proc);
+
+    /* XXX: Check for valid combinations. */
+    return 0;
+}
 
 static int
 ble_l2cap_sm_pair_req_handle(struct ble_l2cap_sm_proc *proc,
@@ -924,14 +1038,21 @@ ble_l2cap_sm_pair_req_handle(struct ble_l2cap_sm_proc *proc,
     proc->pair_req = *req;
 
     rc = ble_l2cap_sm_pair_go(proc);
+
     if (rc != 0) {
         *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
         return rc;
     }
 
+    rc = ble_l2cap_sm_check_key_exchange(proc);
+    if (rc != 0) {
+        *out_sm_status = BLE_L2CAP_SM_ERR_INVAL;
+        return rc;
+    }
+
     proc->state = BLE_L2CAP_SM_PROC_STATE_CONFIRM;
 
-    /* get the passkey action for querying the application */
+    /* Get the passkey action for querying the application. */
     *passkey_action = ble_l2cap_sm_passkey_action(proc);
 
     return 0;
@@ -947,11 +1068,16 @@ ble_l2cap_sm_pair_rsp_handle(struct ble_l2cap_sm_proc *proc,
 
     proc->pair_rsp = *rsp;
 
+    rc = ble_l2cap_sm_check_key_exchange(proc);
+    if (rc != 0) {
+        *out_sm_status = BLE_L2CAP_SM_ERR_INVAL;
+        return rc;
+    }
     proc->state = BLE_L2CAP_SM_PROC_STATE_CONFIRM;
 
-    /* if there is no passkey action to take, just continue with confirm */
+    /* If there is no passkey action to take, just continue with confirm. */
     *passkey_action = ble_l2cap_sm_passkey_action(proc);
-    if(*passkey_action == BLE_GAP_PKACT_NONE) {
+    if (*passkey_action == BLE_GAP_PKACT_NONE) {
         rc = ble_l2cap_sm_confirm_go(proc);
         if (rc != 0) {
             *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
@@ -963,17 +1089,269 @@ ble_l2cap_sm_pair_rsp_handle(struct ble_l2cap_sm_proc *proc,
 }
 
 /*****************************************************************************
+ * $key exchange                                                             *
+ *****************************************************************************/
+
+static void
+ble_l2cap_sm_enc_info_handle(struct ble_l2cap_sm_proc *proc,
+                             struct ble_l2cap_sm_enc_info *info)
+{
+    proc->rx_key_flags &= ~BLE_L2CAP_SM_KE_F_ENC_INFO;
+
+    /* Save until completion. */
+    proc->peer_keys.ltk_valid = 1;
+    memcpy(proc->peer_keys.ltk, info->ltk_le, 16);
+}
+
+static void
+ble_l2cap_sm_master_iden_handle(struct ble_l2cap_sm_proc *proc,
+                             struct ble_l2cap_sm_master_iden *info)
+{
+    proc->rx_key_flags &= ~BLE_L2CAP_SM_KE_F_MASTER_IDEN;
+    /* Save until completion. */
+    proc->peer_keys.ediv_rand_valid = 1;
+    proc->peer_keys.ediv = info->ediv;
+    proc->peer_keys.rand_val = info->rand_val;
+}
+
+static void
+ble_l2cap_sm_iden_info_handle(struct ble_l2cap_sm_proc *proc,
+                              struct ble_l2cap_sm_iden_info *info)
+{
+    proc->rx_key_flags &= ~BLE_L2CAP_SM_KE_F_IDEN_INFO;
+
+    /* Save until completion. */
+    proc->peer_keys.irk_valid = 1;
+    memcpy(proc->peer_keys.irk, info->irk_le, 16);
+}
+
+static void
+ble_l2cap_sm_iden_addr_handle(struct ble_l2cap_sm_proc *proc,
+                              struct ble_l2cap_sm_iden_addr_info *info)
+{
+    proc->rx_key_flags &= ~BLE_L2CAP_SM_KE_F_ADDR_INFO;
+
+    /* Save until completion. */
+    proc->peer_keys.addr_valid = 1;
+    proc->peer_keys.addr_type = info->addr_type;
+    memcpy(proc->peer_keys.addr, info->bd_addr_le, 6);
+}
+
+static void
+ble_l2cap_sm_signing_info_handle(struct ble_l2cap_sm_proc *proc,
+                                 struct ble_l2cap_sm_signing_info *info)
+{
+    proc->rx_key_flags &= ~BLE_L2CAP_SM_KE_F_SIGN_INFO;
+
+    /* save until completion */
+    proc->peer_keys.csrk_valid = 1;
+    memcpy(proc->peer_keys.csrk, info->sig_key_le, 16);
+}
+
+static int
+ble_l2cap_sm_key_exchange_go(struct ble_l2cap_sm_proc *proc,
+                             uint8_t *sm_status)
+{
+    struct ble_l2cap_sm_master_iden master_iden;
+    struct ble_l2cap_sm_enc_info enc_info;
+    uint8_t our_key_dist;
+    int rc;
+
+    if (proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR) {
+        our_key_dist = proc->pair_rsp.init_key_dist;
+    } else {
+        our_key_dist = proc->pair_rsp.resp_key_dist;
+    }
+
+    if (our_key_dist & BLE_L2CAP_SM_KEY_DIST_ENC) {
+        /* Send encryption information. */
+        rc = ble_l2cap_sm_gen_ltk(proc, enc_info.ltk_le);
+        if (rc != 0) {
+            *sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+            return rc;
+        }
+        rc = ble_l2cap_sm_enc_info_tx(proc->conn_handle, &enc_info);
+        if (rc != 0) {
+            *sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+            return rc;
+        }
+
+        /* Send master identification. */
+        rc = ble_l2cap_sm_gen_ediv(&master_iden.ediv);
+        if (rc != 0) {
+            *sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+            return rc;
+        }
+        rc = ble_l2cap_sm_gen_start_rand(&master_iden.rand_val);
+        if (rc != 0) {
+            *sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+            return rc;
+        }
+        rc = ble_l2cap_sm_master_iden_tx(proc->conn_handle, &master_iden);
+        if (rc != 0) {
+            *sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+            return rc;
+        }
+
+        /* copy data to pass to application */
+        proc->our_keys.is_ours = 1;
+        proc->our_keys.ltk_valid = 1;
+        proc->our_keys.ediv_rand_valid = 1;
+        proc->our_keys.rand_val = master_iden.rand_val;
+        proc->our_keys.ediv = master_iden.ediv;
+        memcpy(proc->our_keys.ltk, enc_info.ltk_le, 16);
+    }
+
+    /* XXX: Send remainining key information. */
+
+    return 0;
+}
+
+
+static int
+ble_l2cap_sm_rx_key_exchange(uint16_t conn_handle, uint8_t op,
+                               struct os_mbuf **om)
+{
+    union {
+        struct ble_l2cap_sm_enc_info enc_info;
+        struct ble_l2cap_sm_master_iden master_iden;
+        struct ble_l2cap_sm_iden_info iden_info;
+        struct ble_l2cap_sm_iden_addr_info iden_addr;
+        struct ble_l2cap_sm_signing_info signing_info;
+    } u;
+    int rc;
+    int sm_end = 0;
+    int base_len;
+    struct ble_l2cap_sm_proc *proc;
+    struct ble_l2cap_sm_proc *prev;
+    uint8_t sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+
+    switch (op)  {
+    case BLE_L2CAP_SM_OP_ENC_INFO:
+        base_len = BLE_L2CAP_SM_ENC_INFO_SZ;
+        break;
+
+    case BLE_L2CAP_SM_OP_MASTER_ID:
+        base_len = BLE_L2CAP_SM_MASTER_IDEN_SZ;
+        break;
+
+    case BLE_L2CAP_SM_OP_IDENTITY_INFO:
+        base_len = BLE_L2CAP_SM_IDEN_INFO_SZ;
+        break;
+
+    case BLE_L2CAP_SM_OP_IDENTITY_ADDR_INFO:
+        base_len = BLE_L2CAP_SM_IDEN_ADDR_INFO_SZ;
+        break;
+
+    case BLE_L2CAP_SM_OP_SIGN_INFO:
+        base_len = BLE_L2CAP_SM_SIGNING_INFO_SZ;
+        break;
+
+    default:
+        /* TODO error code */
+        return BLE_HS_ENOTSUP;
+    }
+
+    rc = ble_hs_misc_pullup_base(om, base_len);
+    if (rc != 0) {
+        return rc;
+    }
+
+    switch (op) {
+    case BLE_L2CAP_SM_OP_ENC_INFO:
+        ble_l2cap_sm_enc_info_parse((*om)->om_data, (*om)->om_len,
+                                    &u.enc_info);
+        break;
+    case BLE_L2CAP_SM_OP_MASTER_ID:
+        ble_l2cap_sm_master_iden_parse((*om)->om_data, (*om)->om_len,
+                                       &u.master_iden);
+        break;
+    case BLE_L2CAP_SM_OP_IDENTITY_INFO:
+        ble_l2cap_sm_iden_info_parse((*om)->om_data, (*om)->om_len,
+                                     &u.iden_info);
+        break;
+    case BLE_L2CAP_SM_OP_IDENTITY_ADDR_INFO:
+        ble_l2cap_sm_iden_addr_parse((*om)->om_data, (*om)->om_len,
+                                     &u.iden_addr);
+        break;
+    case BLE_L2CAP_SM_OP_SIGN_INFO:
+        ble_l2cap_sm_signing_info_parse((*om)->om_data, (*om)->om_len,
+                                        &u.signing_info);
+        break;
+    }
+
+    ble_hs_lock();
+
+    /* Check connection state; reject if not appropriate. */
+    proc = ble_l2cap_sm_proc_find(conn_handle, BLE_L2CAP_SM_PROC_STATE_KEY_EXCH,
+                                  -1, &prev);
+    if (proc != NULL) {
+        switch (op) {
+        case BLE_L2CAP_SM_OP_ENC_INFO:
+            ble_l2cap_sm_enc_info_handle(proc, &u.enc_info);
+            break;
+
+        case BLE_L2CAP_SM_OP_MASTER_ID:
+            ble_l2cap_sm_master_iden_handle(proc, &u.master_iden);
+            break;
+
+        case BLE_L2CAP_SM_OP_IDENTITY_INFO:
+            ble_l2cap_sm_iden_info_handle(proc, &u.iden_info);
+            break;
+
+        case BLE_L2CAP_SM_OP_IDENTITY_ADDR_INFO:
+            ble_l2cap_sm_iden_addr_handle(proc, &u.iden_addr);
+            break;
+
+        case BLE_L2CAP_SM_OP_SIGN_INFO:
+            ble_l2cap_sm_signing_info_handle(proc, &u.signing_info);
+            break;
+        }
+    }
+
+    /* did we finish RX keys */
+    rc = 0;
+    if (!proc->rx_key_flags) {
+        if (proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR) {
+            /* time for us to send our keys */
+            rc = ble_l2cap_sm_key_exchange_go(proc, &sm_status);
+        }
+        sm_end = 1;
+    }
+
+    if (rc != 0 || sm_end) {
+        ble_l2cap_sm_proc_remove(proc, prev);
+    }
+
+    ble_hs_unlock();
+
+    /* a successful ending of the link */
+    if (rc == 0) {
+        if (sm_end) {
+            /* TODO put error code here */
+            ble_l2cap_sm_gap_event(proc, 0, 1);
+            ble_l2cap_sm_key_exchange_events(proc);
+            ble_l2cap_sm_proc_free(proc);
+        }
+    } else {
+        rc = ble_l2cap_sm_process_status(proc, rc, sm_status, 1, 1);
+    }
+    return rc;
+}
+
+
+/*****************************************************************************
  * $rx                                                                       *
  *****************************************************************************/
 
 static int
-ble_l2cap_sm_rx_pair_req(uint16_t conn_handle, uint8_t state,
+ble_l2cap_sm_rx_pair_req(uint16_t conn_handle, uint8_t op,
                          struct os_mbuf **om)
 {
     struct ble_l2cap_sm_pair_cmd req;
     struct ble_l2cap_sm_proc *proc;
     struct ble_l2cap_sm_proc *prev;
-    uint8_t sm_status;
+    uint8_t sm_status = 0;
     uint8_t passkey_action = BLE_GAP_PKACT_NONE;
 
     int rc;
@@ -1008,7 +1386,8 @@ ble_l2cap_sm_rx_pair_req(uint16_t conn_handle, uint8_t state,
     } else {
         proc->conn_handle = conn_handle;
         proc->state = BLE_L2CAP_SM_PROC_STATE_PAIR;
-        rc = ble_l2cap_sm_pair_req_handle(proc, &req, &sm_status, &passkey_action);
+        rc = ble_l2cap_sm_pair_req_handle(proc, &req, &sm_status,
+                                          &passkey_action);
         if (rc == 0) {
             STAILQ_INSERT_HEAD(&ble_l2cap_sm_procs, proc, next);
         }
@@ -1017,7 +1396,7 @@ ble_l2cap_sm_rx_pair_req(uint16_t conn_handle, uint8_t state,
     ble_hs_unlock();
 
     /* This has to be done after the unlock */
-    if ( passkey_action != BLE_GAP_PKACT_NONE ) {
+    if (passkey_action != BLE_GAP_PKACT_NONE) {
         ble_gap_passkey_event(proc->conn_handle,sm_status, passkey_action);
     }
 
@@ -1026,13 +1405,13 @@ ble_l2cap_sm_rx_pair_req(uint16_t conn_handle, uint8_t state,
 }
 
 static int
-ble_l2cap_sm_rx_pair_rsp(uint16_t conn_handle, uint8_t state,
+ble_l2cap_sm_rx_pair_rsp(uint16_t conn_handle, uint8_t op,
                          struct os_mbuf **om)
 {
     struct ble_l2cap_sm_pair_cmd rsp;
     struct ble_l2cap_sm_proc *proc;
     struct ble_l2cap_sm_proc *prev;
-    uint8_t sm_status;
+    uint8_t sm_status = 0;
     int rc;
     uint8_t passkey_action = BLE_GAP_PKACT_NONE;
 
@@ -1053,15 +1432,16 @@ ble_l2cap_sm_rx_pair_rsp(uint16_t conn_handle, uint8_t state,
     proc = ble_l2cap_sm_proc_find(conn_handle, BLE_L2CAP_SM_PROC_STATE_PAIR, 1,
                                   &prev);
     if (proc != NULL) {
-        rc = ble_l2cap_sm_pair_rsp_handle(proc, &rsp, &sm_status, &passkey_action);
+        rc = ble_l2cap_sm_pair_rsp_handle(proc, &rsp, &sm_status,
+                                          &passkey_action);
         if (rc != 0) {
             ble_l2cap_sm_proc_remove(proc, prev);
         }
     }
     ble_hs_unlock();
 
-    /* This has to be done after the unlock */
-    if ( passkey_action != BLE_GAP_PKACT_NONE ) {
+    /* The GAP callback can only be executed after the unlock. */
+    if (passkey_action != BLE_GAP_PKACT_NONE) {
         ble_gap_passkey_event(proc->conn_handle,sm_status, passkey_action);
     }
 
@@ -1070,7 +1450,7 @@ ble_l2cap_sm_rx_pair_rsp(uint16_t conn_handle, uint8_t state,
 }
 
 static int
-ble_l2cap_sm_rx_pair_confirm(uint16_t conn_handle, uint8_t state,
+ble_l2cap_sm_rx_pair_confirm(uint16_t conn_handle, uint8_t op,
                              struct os_mbuf **om)
 {
     struct ble_l2cap_sm_pair_confirm cmd;
@@ -1097,7 +1477,6 @@ ble_l2cap_sm_rx_pair_confirm(uint16_t conn_handle, uint8_t state,
             ble_l2cap_sm_proc_remove(proc, prev);
         }
     }
-
     ble_hs_unlock();
 
     rc = ble_l2cap_sm_process_status(proc, rc, sm_status, 1, 1);
@@ -1105,7 +1484,7 @@ ble_l2cap_sm_rx_pair_confirm(uint16_t conn_handle, uint8_t state,
 }
 
 static int
-ble_l2cap_sm_rx_pair_random(uint16_t conn_handle, uint8_t state,
+ble_l2cap_sm_rx_pair_random(uint16_t conn_handle, uint8_t op,
                             struct os_mbuf **om)
 {
     struct ble_l2cap_sm_pair_random cmd;
@@ -1141,7 +1520,7 @@ ble_l2cap_sm_rx_pair_random(uint16_t conn_handle, uint8_t state,
 }
 
 static int
-ble_l2cap_sm_rx_pair_fail(uint16_t conn_handle, uint8_t state,
+ble_l2cap_sm_rx_pair_fail(uint16_t conn_handle, uint8_t op,
                           struct os_mbuf **om)
 {
     struct ble_l2cap_sm_pair_fail cmd;
@@ -1166,8 +1545,63 @@ ble_l2cap_sm_rx_pair_fail(uint16_t conn_handle, uint8_t state,
     }
     ble_hs_unlock();
 
-    rc = ble_l2cap_sm_process_status(proc,
-        BLE_HS_SM_THEM_ERR(cmd.reason), 0, 1, 0);
+    rc = ble_l2cap_sm_process_status(proc, BLE_HS_SM_THEM_ERR(cmd.reason),
+                                     0, 1, 0);
+    return rc;
+}
+
+static int
+ble_l2cap_sm_lt_key_req_ltk_handle(struct hci_le_lt_key_req *evt)
+{
+    struct ble_gap_ltk_params ltk_params;
+    struct ble_l2cap_sm_proc *proc;
+    struct ble_l2cap_sm_proc *prev;
+    int app_rc;
+    int rc;
+
+    /* Tell applicaiton to look up LTK by ediv/rand pair. */
+    ltk_params.ediv = evt->encrypted_diversifier;
+    ltk_params.rand_num = evt->random_number;
+    app_rc = ble_gap_ltk_event(evt->connection_handle, &ltk_params);
+    if (app_rc == 0) {
+        /* App provided a key; send it to the controller. */
+        rc = ble_l2cap_sm_lt_key_req_reply_tx(evt->connection_handle,
+                                              ltk_params.ltk);
+    } else {
+        /* Application does not have the requested key in its database.  Send a
+         * negative reply to the controller.
+         */
+        rc = ble_l2cap_sm_lt_key_req_neg_reply_tx(evt->connection_handle);
+    }
+
+    ble_hs_lock();
+    proc = ble_l2cap_sm_proc_find(evt->connection_handle,
+                                  BLE_L2CAP_SM_PROC_STATE_LTK, 0,
+                                  &prev);
+    if (proc == NULL) {
+        rc = BLE_HS_EUNKNOWN;
+    } else if (app_rc == 0 && rc == 0) {
+        proc->state = BLE_L2CAP_SM_PROC_STATE_ENC_CHANGE;
+        if (ltk_params.authenticated) {
+            proc->flags |= BLE_L2CAP_SM_PROC_F_AUTHENTICATED;
+        }
+    } else {
+        ble_l2cap_sm_proc_remove(proc, prev);
+    }
+    ble_hs_unlock();
+
+    /* Notify the app if it provided a key and the procedure failed. */
+    if (app_rc == 0 && rc != 0) {
+        ble_l2cap_sm_gap_event(proc, rc, 0);
+    }
+
+    /* The procedure is aborted if the app didn't provide a key or if there was
+     * a failure.
+     */
+    if (app_rc != 0 || rc != 0) {
+        ble_l2cap_sm_proc_free(proc);
+    }
+
     return rc;
 }
 
@@ -1176,23 +1610,58 @@ ble_l2cap_sm_rx_lt_key_req(struct hci_le_lt_key_req *evt)
 {
     struct ble_l2cap_sm_proc *proc;
     struct ble_l2cap_sm_proc *prev;
-    uint8_t sm_status;
+    int bonding;
     int rc;
 
+    rc = 0; /* Silence spurious gcc warning. */
+
     ble_hs_lock();
     proc = ble_l2cap_sm_proc_find(evt->connection_handle,
-                                  BLE_L2CAP_SM_PROC_STATE_LTK, 0, &prev);
+                                  BLE_L2CAP_SM_PROC_STATE_NONE, 0, &prev);
     if (proc == NULL) {
-        rc = BLE_HS_ENOTCONN;
-    } else {
-        rc = ble_l2cap_sm_lt_key_req_handle(proc, evt, &sm_status);
+        /* The peer is attempting to restore a encrypted connection via
+         * bonding.  Create a proc entry to indicate that security
+         * establishment is in progress and execute the procedure after the
+         * mutex gets unlocked.
+         */
+        bonding = 1;
+        proc = ble_l2cap_sm_proc_alloc();
+        if (proc != NULL) {
+            proc->conn_handle = evt->connection_handle;
+            proc->state = BLE_L2CAP_SM_PROC_STATE_LTK;
+            STAILQ_INSERT_HEAD(&ble_l2cap_sm_procs, proc, next);
+        }
+    } else if (proc->state == BLE_L2CAP_SM_PROC_STATE_LTK) {
+        /* Short-term key pairing just completed.  Send the short term key to
+         * the controller.
+         */
+        bonding = 0;
+        rc = ble_l2cap_sm_lt_key_req_stk_handle(proc, evt);
         if (rc != 0) {
             ble_l2cap_sm_proc_remove(proc, prev);
         }
+    } else {
+        /* The request is unexpected.  Quietly ignore it. */
+        bonding = 0;
+        proc = NULL;
+    }
+
+    if (proc != NULL) {
+        ble_l2cap_sm_proc_set_timer(proc);
     }
+
     ble_hs_unlock();
 
-    rc = ble_l2cap_sm_process_status(proc, rc, sm_status, 1, 1);
+    if (bonding) {
+        if (proc == NULL) {
+            rc = BLE_HS_ENOMEM;
+        } else {
+            rc = ble_l2cap_sm_lt_key_req_ltk_handle(evt);
+        }
+    } else if (proc != NULL) {
+        rc = ble_l2cap_sm_process_status(proc, rc, 0, 1, 0);
+    }
+
     return rc;
 }
 
@@ -1201,22 +1670,42 @@ ble_l2cap_sm_rx_encryption_change(struct hci_encrypt_change *evt)
 {
     struct ble_l2cap_sm_proc *proc;
     struct ble_l2cap_sm_proc *prev;
-    int enc_enabled;
+    int enc_enabled = 0;
+    int do_key_exchange = 0;
+    int rc = 0;
+    uint8_t sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
 
     ble_hs_lock();
     proc = ble_l2cap_sm_proc_find(evt->connection_handle,
                                   BLE_L2CAP_SM_PROC_STATE_ENC_CHANGE, -1,
                                   &prev);
+
     if (proc != NULL) {
-        ble_l2cap_sm_proc_remove(proc, prev);
+        enc_enabled = evt->encryption_enabled & 0x01; /* LE bit. */
+        do_key_exchange = (proc->flags & BLE_L2CAP_SM_PROC_F_KEY_EXCHANGE);
+    }
+
+    /* do we do a key exchange. Must be secure also */
+    if (do_key_exchange && enc_enabled) {
+        proc->state = BLE_L2CAP_SM_PROC_STATE_KEY_EXCH;
+        /* the slave starts first */
+        if (!(proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR)) {
+            rc = ble_l2cap_sm_key_exchange_go(proc, &sm_status);
+        }
+    } else {
+        if (proc != NULL) {
+            ble_l2cap_sm_proc_remove(proc, prev);
+        }
     }
+
     ble_hs_unlock();
 
-    if (proc != NULL) {
+    if (proc != NULL && do_key_exchange == 0 && rc == 0) {
         /* The pairing procedure is now complete. */
-        enc_enabled = evt->encryption_enabled & 0x01; /* LE bit. */
         ble_l2cap_sm_gap_event(proc, BLE_HS_HCI_ERR(evt->status), enc_enabled);
         ble_l2cap_sm_proc_free(proc);
+    } else if (rc) {
+        ble_l2cap_sm_process_status(proc, rc, sm_status, 1, 1);
     }
 }
 
@@ -1315,6 +1804,54 @@ done:
     return rc;
 }
 
+int
+ble_l2cap_sm_sec_initiate(uint16_t conn_handle,
+                          uint8_t *ltk,
+                          uint16_t ediv,
+                          uint64_t rand_val,
+                          int auth)
+{
+    struct ble_l2cap_sm_proc *proc;
+    int rc;
+
+    /* Make sure a pairing operation for this connection is not already in
+     * progress.
+     */
+    ble_hs_lock();
+    proc = ble_l2cap_sm_proc_find(conn_handle, BLE_L2CAP_SM_PROC_STATE_NONE,
+                                  -1, NULL);
+    if (proc != NULL) {
+        rc = BLE_HS_EALREADY;
+        goto sec_initiate_done;
+    }
+
+    proc = ble_l2cap_sm_proc_alloc();
+    if (proc == NULL) {
+        rc = BLE_HS_ENOMEM;
+        goto sec_initiate_done;
+    }
+    proc->conn_handle = conn_handle;
+    proc->state = BLE_L2CAP_SM_PROC_STATE_ENC_CHANGE;
+    proc->flags |= BLE_L2CAP_SM_PROC_F_INITIATOR;
+
+    /* use the authenticated state of the key provided */
+    if (auth) {
+        proc->flags |= BLE_L2CAP_SM_PROC_F_AUTHENTICATED;
+    }
+
+    rc = ble_l2cap_sm_start_encrypt_tx(proc->conn_handle, ediv, rand_val, ltk);
+    if (rc != 0) {
+        ble_l2cap_sm_proc_free(proc);
+        goto sec_initiate_done;
+    }
+
+    STAILQ_INSERT_HEAD(&ble_l2cap_sm_procs, proc, next);
+
+sec_initiate_done:
+    ble_hs_unlock();
+    return rc;
+}
+
 struct ble_l2cap_chan *
 ble_l2cap_sm_create_chan(void)
 {
@@ -1337,85 +1874,90 @@ int
 ble_l2cap_sm_set_tk(uint16_t conn_handle, struct passkey_action *pkey)
 {
     struct ble_l2cap_sm_proc *proc;
+    struct ble_l2cap_sm_proc *prev;
     int rc = 0;
     int sm_error = 0;
 
     ble_hs_lock();
 
-    proc = ble_l2cap_sm_proc_find(conn_handle,
-                                  BLE_L2CAP_SM_PROC_STATE_NONE, -1, NULL);
+    proc = ble_l2cap_sm_proc_find(conn_handle, BLE_L2CAP_SM_PROC_STATE_CONFIRM,
+                                  -1, &prev);
 
     if (proc == NULL) {
         rc = BLE_HS_ENOENT;
         goto set_tk_return;
     }
 
-    /* Do we already have a valid TK */
+    /* Do we already have a valid TK? */
     if (proc->flags & BLE_L2CAP_SM_PROC_F_TK_VALID) {
-        rc = BLE_L2CAP_SM_ERR_INVAL;
+        rc = BLE_HS_EALREADY;
         goto set_tk_return;
     }
 
-    /* Is the response of the right type -- must match what we asked for */
-    if ( pkey->action != ble_l2cap_sm_passkey_action(proc) ) {
-        rc = BLE_L2CAP_SM_ERR_INVAL;
+    /* Is the response of the right type?  Must match what we asked for. */
+    if (pkey->action != ble_l2cap_sm_passkey_action(proc)) {
+        rc = BLE_HS_EINVAL;
         goto set_tk_return;
     }
 
-    /* Add the passkey range */
-    switch(pkey->action) {
-        case BLE_GAP_PKACT_OOB:
-            memcpy(proc->tk, pkey->oob, 16);
-            /* only a potential error */
-            sm_error = BLE_L2CAP_SM_ERR_OOB;
-            break;
-        case BLE_GAP_PKACT_INPUT:
-        case BLE_GAP_PKACT_DISP:
-            if (pkey->passkey > 999999) {
-                /* return an error */
-                rc = BLE_L2CAP_SM_ERR_INVAL;
-                goto set_tk_return;
-            }
-            memset(proc->tk, 0, 16);
-            proc->tk[0] = (pkey->passkey >> 0) & 0xff;
-            proc->tk[1] = (pkey->passkey >> 8) & 0xff;
-            proc->tk[2] = (pkey->passkey >> 16) & 0xff;
-            proc->tk[3] = (pkey->passkey >> 24) & 0xff;
-            sm_error = BLE_L2CAP_SM_ERR_PASSKEY;
-            break;
-        default:
-            rc = BLE_L2CAP_SM_ERR_INVAL;
+    /* Add the passkey range. */
+    switch (pkey->action) {
+    case BLE_GAP_PKACT_OOB:
+        memcpy(proc->tk, pkey->oob, 16);
+        sm_error = BLE_L2CAP_SM_ERR_OOB;
+        break;
+
+    case BLE_GAP_PKACT_INPUT:
+    case BLE_GAP_PKACT_DISP:
+        sm_error = BLE_L2CAP_SM_ERR_PASSKEY;
+        if (pkey->passkey > 999999) {
+            rc = BLE_HS_EINVAL;
             goto set_tk_return;
+        }
+        memset(proc->tk, 0, 16);
+        proc->tk[0] = (pkey->passkey >> 0) & 0xff;
+        proc->tk[1] = (pkey->passkey >> 8) & 0xff;
+        proc->tk[2] = (pkey->passkey >> 16) & 0xff;
+        proc->tk[3] = (pkey->passkey >> 24) & 0xff;
+        break;
+
+    default:
+        sm_error = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+        rc = BLE_HS_EINVAL;
+        goto set_tk_return;
     }
 
     proc->flags |= BLE_L2CAP_SM_PROC_F_TK_VALID;
     rc = 0;
 
-    /* Are we in the right state. If we are the initiator, its time
-     * to send the confirm. If we are the responder, we check whether
-     * or not we received the confirm yet. All of this has to be
-     * in the confirmed state  */
-
+    /* If we are the initiator, its time to send the confirm. If we are the
+     * responder, we check whether or not we received the confirm yet. All of
+     * this has to be in the confirmed state.
+     */
     if (proc->state == BLE_L2CAP_SM_PROC_STATE_CONFIRM) {
-        if(proc->flags &
-           (BLE_L2CAP_SM_PROC_F_INITIATOR | BLE_L2CAP_SM_PROC_F_RX_CONFIRM)) {
+        if (proc->flags & (BLE_L2CAP_SM_PROC_F_INITIATOR |
+                           BLE_L2CAP_SM_PROC_F_RX_CONFIRM)) {
+
             rc = ble_l2cap_sm_confirm_go(proc);
             if (rc != 0) {
                 goto set_tk_return;
             }
 
-            /* only the initiator changes state here */
-            if (0 == (proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR)) {
+            /* Only the responder changes state here. */
+            if (!(proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR)) {
                 proc->state = BLE_L2CAP_SM_PROC_STATE_RANDOM;
             }
         }
     }
 
 set_tk_return:
+    if (proc != NULL && rc != 0) {
+        ble_l2cap_sm_proc_remove(proc, prev);
+    }
+
     ble_hs_unlock();
-    /* if we have a valid key entry and failed to confirm the key,
-     * handle this error */
-    if ((proc->flags & BLE_L2CAP_SM_PROC_F_TK_VALID) && (0 != rc)) {
+
+    if (rc != 0) {
         rc = ble_l2cap_sm_process_status(proc, rc, sm_error, 1, 1);
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/net/nimble/host/src/ble_l2cap_sm_cmd.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap_sm_cmd.c b/net/nimble/host/src/ble_l2cap_sm_cmd.c
index 7d81a07..8f4766d 100644
--- a/net/nimble/host/src/ble_l2cap_sm_cmd.c
+++ b/net/nimble/host/src/ble_l2cap_sm_cmd.c
@@ -6,7 +6,7 @@
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
- * 
+ *
  *  http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
@@ -280,4 +280,162 @@ done:
     return rc;
 }
 
+void
+ble_l2cap_sm_enc_info_parse(void *payload, int len,
+                            struct ble_l2cap_sm_enc_info *cmd)
+{
+    uint8_t *u8ptr = payload;
+    memcpy(cmd->ltk_le, u8ptr, 16);
+}
+
+int
+ble_l2cap_sm_enc_info_tx(uint16_t conn_handle,
+                         struct ble_l2cap_sm_enc_info *cmd)
+{
+    struct os_mbuf *txom;
+    int rc;
+
+    rc = ble_l2cap_sm_init_req(BLE_L2CAP_SM_ENC_INFO_SZ, &txom);
+    if (rc != 0) {
+        rc = BLE_HS_ENOMEM;
+        goto done;
+    }
+
+    txom->om_data[0] = BLE_L2CAP_SM_OP_ENC_INFO;
+    memcpy(txom->om_data + 1, cmd->ltk_le, sizeof cmd->ltk_le);
+    
+    rc = ble_l2cap_sm_tx(conn_handle, txom);
+    txom = NULL;
+
+done:
+    os_mbuf_free_chain(txom);
+    return rc;
+}
+
+void
+ble_l2cap_sm_master_iden_parse(void *payload, int len,
+                               struct ble_l2cap_sm_master_iden *cmd)
+{
+    uint8_t *u8ptr = payload;
+    cmd->ediv = le16toh(u8ptr);
+    cmd->rand_val = le64toh(u8ptr + 2);
+}
+
+int
+ble_l2cap_sm_master_iden_tx(uint16_t conn_handle,
+                            struct ble_l2cap_sm_master_iden *cmd)
+{
+    struct os_mbuf *txom;
+    int rc;
+
+    rc = ble_l2cap_sm_init_req(BLE_L2CAP_SM_MASTER_IDEN_SZ, &txom);
+    if (rc != 0) {
+        rc = BLE_HS_ENOMEM;
+        goto done;
+    }
+
+    txom->om_data[0] = BLE_L2CAP_SM_OP_MASTER_ID;
+    htole16(txom->om_data + 1, cmd->ediv);
+    htole64(txom->om_data + 3, cmd->rand_val);
+    rc = ble_l2cap_sm_tx(conn_handle, txom);
+    txom = NULL;
+
+done:
+    os_mbuf_free_chain(txom);
+    return rc;
+}
+
+void
+ble_l2cap_sm_iden_info_parse(void *payload, int len,
+                             struct ble_l2cap_sm_iden_info *cmd)
+{
+    uint8_t *u8ptr = payload;
+    memcpy(cmd->irk_le, u8ptr, 16);
+}
+
+int
+ble_l2cap_sm_iden_info_tx(uint16_t conn_handle,
+                          struct ble_l2cap_sm_iden_info *cmd)
+{
+    struct os_mbuf *txom;
+    int rc;
+
+    rc = ble_l2cap_sm_init_req(BLE_L2CAP_SM_IDEN_INFO_SZ, &txom);
+    if (rc != 0) {
+        rc = BLE_HS_ENOMEM;
+        goto done;
+    }
+
+    txom->om_data[0] = BLE_L2CAP_SM_OP_IDENTITY_INFO;
+    memcpy(txom->om_data + 1, cmd->irk_le, sizeof cmd->irk_le);
+    rc = ble_l2cap_sm_tx(conn_handle, txom);
+    txom = NULL;
+
+done:
+    os_mbuf_free_chain(txom);
+    return rc;
+}
+
+void
+ble_l2cap_sm_iden_addr_parse(void *payload, int len,
+                             struct ble_l2cap_sm_iden_addr_info *cmd) {
+    uint8_t *u8ptr = payload;
+    cmd->addr_type = *u8ptr;
+    memcpy(cmd->bd_addr_le, u8ptr + 1, 6);
+}
+
+int
+ble_l2cap_sm_iden_addr_tx(uint16_t conn_handle,
+                          struct ble_l2cap_sm_iden_addr_info *cmd)
+{
+    struct os_mbuf *txom;
+    int rc;
+
+    rc = ble_l2cap_sm_init_req(BLE_L2CAP_SM_IDEN_ADDR_INFO_SZ, &txom);
+    if (rc != 0) {
+        rc = BLE_HS_ENOMEM;
+        goto done;
+    }
+
+    txom->om_data[0] = BLE_L2CAP_SM_OP_IDENTITY_ADDR_INFO;
+    txom->om_data[1] = cmd->addr_type;
+    memcpy(txom->om_data + 2, cmd->bd_addr_le, sizeof cmd->bd_addr_le);
+    rc = ble_l2cap_sm_tx(conn_handle, txom);
+    txom = NULL;
+
+done:
+    os_mbuf_free_chain(txom);
+    return rc;
+}
+
+void
+ble_l2cap_sm_signing_info_parse(void *payload, int len,
+                               struct ble_l2cap_sm_signing_info *cmd) {
+    uint8_t *u8ptr = payload;
+    memcpy(cmd->sig_key_le, u8ptr, 16);
+}
+
+int
+ble_l2cap_sm_signing_info_tx(uint16_t conn_handle,
+                             struct ble_l2cap_sm_signing_info *cmd)
+{
+    struct os_mbuf *txom;
+    int rc;
+
+    rc = ble_l2cap_sm_init_req(BLE_L2CAP_SM_SIGNING_INFO_SZ, &txom);
+    if (rc != 0) {
+        rc = BLE_HS_ENOMEM;
+        goto done;
+    }
+
+    txom->om_data[0] = BLE_L2CAP_SM_OP_IDENTITY_INFO;
+    memcpy(txom->om_data + 1, cmd->sig_key_le, sizeof cmd->sig_key_le);
+    rc = ble_l2cap_sm_tx(conn_handle, txom);
+    txom = NULL;
+
+done:
+    os_mbuf_free_chain(txom);
+    return rc;
+}
+
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/net/nimble/host/src/ble_l2cap_sm_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap_sm_priv.h b/net/nimble/host/src/ble_l2cap_sm_priv.h
index 9ecbeaf..2552ace 100644
--- a/net/nimble/host/src/ble_l2cap_sm_priv.h
+++ b/net/nimble/host/src/ble_l2cap_sm_priv.h
@@ -6,7 +6,7 @@
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
- * 
+ *
  *  http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
@@ -20,7 +20,7 @@
 #ifndef H_BLE_L2CAP_SM_
 #define H_BLE_L2CAP_SM_
 
-struct ble_gap_sec_params;
+struct ble_gap_sec_state;
 struct hci_le_lt_key_req;
 
 #define BLE_L2CAP_SM_MTU            65
@@ -48,6 +48,13 @@ struct ble_l2cap_sm_pair_cmd {
     uint8_t resp_key_dist;
 };
 
+/* defines for the bitgs in  init_key_dist and resp_key_dist */
+#define KEY_DIST_ENC_KEY    (0x01)
+#define KEY_DIST_ID_KEY     (0x02)
+#define KEY_DIST_SIGN       (0x04)
+#define KEY_DIST_LINK       (0x08)
+#define KEY_DIST_RESERVED   (0xf0)
+
 /**
  * | Parameter                          | Size (octets)     |
  * +------------------------------------+-------------------+
@@ -81,6 +88,67 @@ struct ble_l2cap_sm_pair_fail {
     uint8_t reason;
 };
 
+/**
+ * | Parameter                          | Size (octets)     |
+ * +------------------------------------+-------------------+
+ * | (Code=0x06)                        | 1                 |
+ * | ltk                                | 16                |
+ */
+#define BLE_L2CAP_SM_ENC_INFO_SZ        16
+struct ble_l2cap_sm_enc_info {
+    uint8_t ltk_le[16];
+};
+
+/**
+ * | Parameter                          | Size (octets)     |
+ * +------------------------------------+-------------------+
+ * | (Code=0x07)                        | 1                 |
+ * | EDIV                               | 2                 |
+ * | RAND                               | 8                 |
+ */
+#define BLE_L2CAP_SM_MASTER_IDEN_SZ     10
+struct ble_l2cap_sm_master_iden {
+    uint16_t ediv;
+    uint64_t rand_val;
+};
+
+/**
+ * | Parameter                          | Size (octets)     |
+ * +------------------------------------+-------------------+
+ * | (Code=0x08)                        | 1                 |
+ * | irk                                | 16                |
+ */
+ #define BLE_L2CAP_SM_IDEN_INFO_SZ      16
+struct ble_l2cap_sm_iden_info {
+    uint8_t irk_le[16];
+};
+
+/**
+ * | Parameter                          | Size (octets)     |
+ * +------------------------------------+-------------------+
+ * | (Code=0x09)                        | 1                 |
+ * | addr_type                          | 1                 |
+ * | address                            | 6                 |
+ */
+#define BLE_L2CAP_SM_IDEN_ADDR_INFO_SZ  7
+struct ble_l2cap_sm_iden_addr_info {
+
+    uint8_t addr_type;
+    uint8_t bd_addr_le[6];
+};
+
+/**
+ * | Parameter                          | Size (octets)     |
+ * +------------------------------------+-------------------+
+ * | (Code=0x0A)                        | 1                 |
+ * | csrk                               | 16                |
+ */
+#define BLE_L2CAP_SM_SIGNING_INFO_SZ    16
+struct ble_l2cap_sm_signing_info {
+    uint8_t sig_key_le[16];
+};
+
+
 #if NIMBLE_OPT_SM
 
 #ifdef BLE_HS_DEBUG
@@ -123,11 +191,34 @@ int ble_l2cap_sm_alg_c1(uint8_t *k, uint8_t *r,
                         uint8_t *ia, uint8_t *ra,
                         uint8_t *out_enc_data);
 
+void ble_l2cap_sm_enc_info_parse(void *payload, int len,
+                                 struct ble_l2cap_sm_enc_info *cmd);
+int ble_l2cap_sm_enc_info_tx(uint16_t conn_handle,
+                             struct ble_l2cap_sm_enc_info *cmd);
+void ble_l2cap_sm_master_iden_parse(void *payload, int len,
+                                    struct ble_l2cap_sm_master_iden *cmd);
+int ble_l2cap_sm_master_iden_tx(uint16_t conn_handle,
+                                struct ble_l2cap_sm_master_iden *cmd);
+void ble_l2cap_sm_iden_info_parse(void *payload, int len,
+                                  struct ble_l2cap_sm_iden_info *cmd);
+int ble_l2cap_sm_iden_info_tx(uint16_t conn_handle,
+                              struct ble_l2cap_sm_iden_info *cmd);
+void ble_l2cap_sm_iden_addr_parse(void *payload, int len,
+                                  struct ble_l2cap_sm_iden_addr_info *cmd);
+int ble_l2cap_sm_iden_addr_tx(uint16_t conn_handle,
+                              struct ble_l2cap_sm_iden_addr_info *cmd);
+void ble_l2cap_sm_signing_info_parse(void *payload, int len,
+                                     struct ble_l2cap_sm_signing_info *cmd);
+int ble_l2cap_sm_signing_info_tx(uint16_t conn_handle,
+                                 struct ble_l2cap_sm_signing_info *cmd);
+
 void ble_l2cap_sm_rx_encryption_change(struct hci_encrypt_change *evt);
 int ble_l2cap_sm_rx_lt_key_req(struct hci_le_lt_key_req *evt);
 
 void ble_l2cap_sm_heartbeat(void);
 int ble_l2cap_sm_initiate(uint16_t conn_handle);
+int ble_l2cap_sm_sec_initiate(uint16_t conn_handle, uint8_t *ltk,
+                          uint16_t ediv, uint64_t rand_val, int auth);
 int ble_l2cap_sm_init(void);
 
 #else

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/264a67b9/net/nimble/host/src/host_hci_cmd.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/host_hci_cmd.c b/net/nimble/host/src/host_hci_cmd.c
index 16d6536..523cf35 100644
--- a/net/nimble/host/src/host_hci_cmd.c
+++ b/net/nimble/host/src/host_hci_cmd.c
@@ -809,6 +809,20 @@ host_hci_cmd_build_le_lt_key_req_reply(struct hci_lt_key_req_reply *hkr,
     host_hci_cmd_body_le_lt_key_req_reply(hkr, dst);
 }
 
+void
+host_hci_cmd_build_le_lt_key_req_neg_reply(uint16_t conn_handle,
+                                           uint8_t *dst, int dst_len)
+{
+    BLE_HS_DBG_ASSERT(
+        dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_LT_KEY_REQ_NEG_REPLY_LEN);
+
+    host_hci_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_LT_KEY_REQ_NEG_REPLY,
+                       BLE_HCI_LT_KEY_REQ_NEG_REPLY_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    htole16(dst + 0, conn_handle);
+}
+
 static void
 host_hci_cmd_body_le_conn_param_reply(struct hci_conn_param_reply *hcr,
                                       uint8_t *dst)