Bluetooth: Add passkey entry support for LE SC
[cascardo/linux.git] / net / bluetooth / smp.c
index fea3782..8cfa1c1 100644 (file)
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/mgmt.h>
 
+#include "ecc.h"
 #include "smp.h"
 
 #define SMP_ALLOW_CMD(smp, code)       set_bit(code, &smp->allow_cmd)
 
+/* Keys which are not distributed with Secure Connections */
+#define SMP_SC_NO_DIST (SMP_DIST_ENC_KEY | SMP_DIST_LINK_KEY);
+
 #define SMP_TIMEOUT    msecs_to_jiffies(30000)
 
-#define AUTH_REQ_MASK   0x07
-#define KEY_DIST_MASK  0x07
+#define AUTH_REQ_MASK(dev)     (test_bit(HCI_SC_ENABLED, &(dev)->dev_flags) ? \
+                                0x1f : 0x07)
+#define KEY_DIST_MASK          0x07
+
+/* Maximum message length that can be passed to aes_cmac */
+#define CMAC_MSG_MAX   80
 
 enum {
        SMP_FLAG_TK_VALID,
@@ -44,6 +52,10 @@ enum {
        SMP_FLAG_MITM_AUTH,
        SMP_FLAG_COMPLETE,
        SMP_FLAG_INITIATOR,
+       SMP_FLAG_SC,
+       SMP_FLAG_REMOTE_PK,
+       SMP_FLAG_DEBUG_KEY,
+       SMP_FLAG_WAIT_USER,
 };
 
 struct smp_chan {
@@ -67,9 +79,43 @@ struct smp_chan {
        struct smp_ltk  *ltk;
        struct smp_ltk  *slave_ltk;
        struct smp_irk  *remote_irk;
+       u8              *link_key;
        unsigned long   flags;
+       u8              method;
+       u8              passkey_round;
+
+       /* Secure Connections variables */
+       u8                      local_pk[64];
+       u8                      local_sk[32];
+       u8                      remote_pk[64];
+       u8                      dhkey[32];
+       u8                      mackey[16];
 
        struct crypto_blkcipher *tfm_aes;
+       struct crypto_hash      *tfm_cmac;
+};
+
+/* These debug key values are defined in the SMP section of the core
+ * specification. debug_pk is the public debug key and debug_sk the
+ * private debug key.
+ */
+static const u8 debug_pk[64] = {
+               0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
+               0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
+               0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
+               0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20,
+
+               0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74,
+               0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76,
+               0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63,
+               0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc,
+};
+
+static const u8 debug_sk[32] = {
+               0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58,
+               0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a,
+               0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74,
+               0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f,
 };
 
 static inline void swap_buf(const u8 *src, u8 *dst, size_t len)
@@ -80,6 +126,190 @@ static inline void swap_buf(const u8 *src, u8 *dst, size_t len)
                dst[len - 1 - i] = src[i];
 }
 
+static int aes_cmac(struct crypto_hash *tfm, const u8 k[16], const u8 *m,
+                   size_t len, u8 mac[16])
+{
+       uint8_t tmp[16], mac_msb[16], msg_msb[CMAC_MSG_MAX];
+       struct hash_desc desc;
+       struct scatterlist sg;
+       int err;
+
+       if (len > CMAC_MSG_MAX)
+               return -EFBIG;
+
+       if (!tfm) {
+               BT_ERR("tfm %p", tfm);
+               return -EINVAL;
+       }
+
+       desc.tfm = tfm;
+       desc.flags = 0;
+
+       crypto_hash_init(&desc);
+
+       /* Swap key and message from LSB to MSB */
+       swap_buf(k, tmp, 16);
+       swap_buf(m, msg_msb, len);
+
+       BT_DBG("msg (len %zu) %*phN", len, (int) len, m);
+       BT_DBG("key %16phN", k);
+
+       err = crypto_hash_setkey(tfm, tmp, 16);
+       if (err) {
+               BT_ERR("cipher setkey failed: %d", err);
+               return err;
+       }
+
+       sg_init_one(&sg, msg_msb, len);
+
+       err = crypto_hash_update(&desc, &sg, len);
+       if (err) {
+               BT_ERR("Hash update error %d", err);
+               return err;
+       }
+
+       err = crypto_hash_final(&desc, mac_msb);
+       if (err) {
+               BT_ERR("Hash final error %d", err);
+               return err;
+       }
+
+       swap_buf(mac_msb, mac, 16);
+
+       BT_DBG("mac %16phN", mac);
+
+       return 0;
+}
+
+static int smp_f4(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32],
+                 const u8 x[16], u8 z, u8 res[16])
+{
+       u8 m[65];
+       int err;
+
+       BT_DBG("u %32phN", u);
+       BT_DBG("v %32phN", v);
+       BT_DBG("x %16phN z %02x", x, z);
+
+       m[0] = z;
+       memcpy(m + 1, v, 32);
+       memcpy(m + 33, u, 32);
+
+       err = aes_cmac(tfm_cmac, x, m, sizeof(m), res);
+       if (err)
+               return err;
+
+       BT_DBG("res %16phN", res);
+
+       return err;
+}
+
+static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16],
+                 u8 a1[7], u8 a2[7], u8 mackey[16], u8 ltk[16])
+{
+       /* The btle, salt and length "magic" values are as defined in
+        * the SMP section of the Bluetooth core specification. In ASCII
+        * the btle value ends up being 'btle'. The salt is just a
+        * random number whereas length is the value 256 in little
+        * endian format.
+        */
+       const u8 btle[4] = { 0x65, 0x6c, 0x74, 0x62 };
+       const u8 salt[16] = { 0xbe, 0x83, 0x60, 0x5a, 0xdb, 0x0b, 0x37, 0x60,
+                             0x38, 0xa5, 0xf5, 0xaa, 0x91, 0x83, 0x88, 0x6c };
+       const u8 length[2] = { 0x00, 0x01 };
+       u8 m[53], t[16];
+       int err;
+
+       BT_DBG("w %32phN", w);
+       BT_DBG("n1 %16phN n2 %16phN", n1, n2);
+       BT_DBG("a1 %7phN a2 %7phN", a1, a2);
+
+       err = aes_cmac(tfm_cmac, salt, w, 32, t);
+       if (err)
+               return err;
+
+       BT_DBG("t %16phN", t);
+
+       memcpy(m, length, 2);
+       memcpy(m + 2, a2, 7);
+       memcpy(m + 9, a1, 7);
+       memcpy(m + 16, n2, 16);
+       memcpy(m + 32, n1, 16);
+       memcpy(m + 48, btle, 4);
+
+       m[52] = 0; /* Counter */
+
+       err = aes_cmac(tfm_cmac, t, m, sizeof(m), mackey);
+       if (err)
+               return err;
+
+       BT_DBG("mackey %16phN", mackey);
+
+       m[52] = 1; /* Counter */
+
+       err = aes_cmac(tfm_cmac, t, m, sizeof(m), ltk);
+       if (err)
+               return err;
+
+       BT_DBG("ltk %16phN", ltk);
+
+       return 0;
+}
+
+static int smp_f6(struct crypto_hash *tfm_cmac, const u8 w[16],
+                 const u8 n1[16], u8 n2[16], const u8 r[16],
+                 const u8 io_cap[3], const u8 a1[7], const u8 a2[7],
+                 u8 res[16])
+{
+       u8 m[65];
+       int err;
+
+       BT_DBG("w %16phN", w);
+       BT_DBG("n1 %16phN n2 %16phN", n1, n2);
+       BT_DBG("r %16phN io_cap %3phN a1 %7phN a2 %7phN", r, io_cap, a1, a2);
+
+       memcpy(m, a2, 7);
+       memcpy(m + 7, a1, 7);
+       memcpy(m + 14, io_cap, 3);
+       memcpy(m + 17, r, 16);
+       memcpy(m + 33, n2, 16);
+       memcpy(m + 49, n1, 16);
+
+       err = aes_cmac(tfm_cmac, w, m, sizeof(m), res);
+       if (err)
+               return err;
+
+       BT_DBG("res %16phN", res);
+
+       return err;
+}
+
+static int smp_g2(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32],
+                 const u8 x[16], const u8 y[16], u32 *val)
+{
+       u8 m[80], tmp[16];
+       int err;
+
+       BT_DBG("u %32phN", u);
+       BT_DBG("v %32phN", v);
+       BT_DBG("x %16phN y %16phN", x, y);
+
+       memcpy(m, y, 16);
+       memcpy(m + 16, v, 32);
+       memcpy(m + 48, u, 32);
+
+       err = aes_cmac(tfm_cmac, x, m, sizeof(m), tmp);
+       if (err)
+               return err;
+
+       *val = get_unaligned_le32(tmp);
+       *val %= 1000000;
+
+       BT_DBG("val %06u", *val);
+
+       return 0;
+}
+
 static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
 {
        struct blkcipher_desc desc;
@@ -119,6 +349,22 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
        return err;
 }
 
+static int smp_h6(struct crypto_hash *tfm_cmac, const u8 w[16],
+                 const u8 key_id[4], u8 res[16])
+{
+       int err;
+
+       BT_DBG("w %16phN key_id %4phN", w, key_id);
+
+       err = aes_cmac(tfm_cmac, w, key_id, 4, res);
+       if (err)
+               return err;
+
+       BT_DBG("res %16phN", res);
+
+       return err;
+}
+
 static int smp_ah(struct crypto_blkcipher *tfm, u8 irk[16], u8 r[3], u8 res[3])
 {
        u8 _res[16];
@@ -282,17 +528,22 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
        schedule_delayed_work(&smp->security_timer, SMP_TIMEOUT);
 }
 
-static __u8 authreq_to_seclevel(__u8 authreq)
+static u8 authreq_to_seclevel(u8 authreq)
 {
-       if (authreq & SMP_AUTH_MITM)
-               return BT_SECURITY_HIGH;
-       else
+       if (authreq & SMP_AUTH_MITM) {
+               if (authreq & SMP_AUTH_SC)
+                       return BT_SECURITY_FIPS;
+               else
+                       return BT_SECURITY_HIGH;
+       } else {
                return BT_SECURITY_MEDIUM;
+       }
 }
 
 static __u8 seclevel_to_authreq(__u8 sec_level)
 {
        switch (sec_level) {
+       case BT_SECURITY_FIPS:
        case BT_SECURITY_HIGH:
                return SMP_AUTH_MITM | SMP_AUTH_BONDING;
        case BT_SECURITY_MEDIUM:
@@ -326,13 +577,23 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
        if (test_bit(HCI_PRIVACY, &hdev->dev_flags))
                local_dist |= SMP_DIST_ID_KEY;
 
+       if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
+               if ((authreq & SMP_AUTH_SC) &&
+                   test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+                       local_dist |= SMP_DIST_LINK_KEY;
+                       remote_dist |= SMP_DIST_LINK_KEY;
+               }
+       } else {
+               authreq &= ~SMP_AUTH_SC;
+       }
+
        if (rsp == NULL) {
                req->io_capability = conn->hcon->io_capability;
                req->oob_flag = SMP_OOB_NOT_PRESENT;
                req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
                req->init_key_dist = local_dist;
                req->resp_key_dist = remote_dist;
-               req->auth_req = (authreq & AUTH_REQ_MASK);
+               req->auth_req = (authreq & AUTH_REQ_MASK(hdev));
 
                smp->remote_key_dist = remote_dist;
                return;
@@ -343,7 +604,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
        rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
        rsp->init_key_dist = req->init_key_dist & remote_dist;
        rsp->resp_key_dist = req->resp_key_dist & local_dist;
-       rsp->auth_req = (authreq & AUTH_REQ_MASK);
+       rsp->auth_req = (authreq & AUTH_REQ_MASK(hdev));
 
        smp->remote_key_dist = rsp->init_key_dist;
 }
@@ -377,24 +638,26 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
 
        kfree(smp->csrk);
        kfree(smp->slave_csrk);
+       kfree(smp->link_key);
 
        crypto_free_blkcipher(smp->tfm_aes);
+       crypto_free_hash(smp->tfm_cmac);
 
        /* If pairing failed clean up any keys we might have */
        if (!complete) {
                if (smp->ltk) {
-                       list_del(&smp->ltk->list);
-                       kfree(smp->ltk);
+                       list_del_rcu(&smp->ltk->list);
+                       kfree_rcu(smp->ltk, rcu);
                }
 
                if (smp->slave_ltk) {
-                       list_del(&smp->slave_ltk->list);
-                       kfree(smp->slave_ltk);
+                       list_del_rcu(&smp->slave_ltk->list);
+                       kfree_rcu(smp->slave_ltk, rcu);
                }
 
                if (smp->remote_irk) {
-                       list_del(&smp->remote_irk->list);
-                       kfree(smp->remote_irk);
+                       list_del_rcu(&smp->remote_irk->list);
+                       kfree_rcu(smp->remote_irk, rcu);
                }
        }
 
@@ -424,6 +687,7 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason)
 #define REQ_PASSKEY    0x02
 #define CFM_PASSKEY    0x03
 #define REQ_OOB                0x04
+#define DSP_PASSKEY    0x05
 #define OVERLAP                0xFF
 
 static const u8 gen_method[5][5] = {
@@ -434,6 +698,14 @@ static const u8 gen_method[5][5] = {
        { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP     },
 };
 
+static const u8 sc_method[5][5] = {
+       { JUST_WORKS,  JUST_CFM,    REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY },
+       { JUST_WORKS,  CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY },
+       { DSP_PASSKEY, DSP_PASSKEY, REQ_PASSKEY, JUST_WORKS, DSP_PASSKEY },
+       { JUST_WORKS,  JUST_CFM,    JUST_WORKS,  JUST_WORKS, JUST_CFM    },
+       { DSP_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY },
+};
+
 static u8 get_auth_method(struct smp_chan *smp, u8 local_io, u8 remote_io)
 {
        /* If either side has unknown io_caps, use JUST_CFM (which gets
@@ -443,6 +715,9 @@ static u8 get_auth_method(struct smp_chan *smp, u8 local_io, u8 remote_io)
            remote_io > SMP_IO_KEYBOARD_DISPLAY)
                return JUST_CFM;
 
+       if (test_bit(SMP_FLAG_SC, &smp->flags))
+               return sc_method[remote_io][local_io];
+
        return gen_method[remote_io][local_io];
 }
 
@@ -452,7 +727,6 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
        struct hci_conn *hcon = conn->hcon;
        struct l2cap_chan *chan = conn->smp;
        struct smp_chan *smp = chan->data;
-       u8 method;
        u32 passkey = 0;
        int ret = 0;
 
@@ -469,26 +743,28 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
         * table.
         */
        if (!(auth & SMP_AUTH_MITM))
-               method = JUST_CFM;
+               smp->method = JUST_CFM;
        else
-               method = get_auth_method(smp, local_io, remote_io);
+               smp->method = get_auth_method(smp, local_io, remote_io);
 
        /* Don't confirm locally initiated pairing attempts */
-       if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags))
-               method = JUST_WORKS;
+       if (smp->method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR,
+                                               &smp->flags))
+               smp->method = JUST_WORKS;
 
        /* Don't bother user space with no IO capabilities */
-       if (method == JUST_CFM && hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
-               method = JUST_WORKS;
+       if (smp->method == JUST_CFM &&
+           hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
+               smp->method = JUST_WORKS;
 
        /* If Just Works, Continue with Zero TK */
-       if (method == JUST_WORKS) {
+       if (smp->method == JUST_WORKS) {
                set_bit(SMP_FLAG_TK_VALID, &smp->flags);
                return 0;
        }
 
        /* Not Just Works/Confirm results in MITM Authentication */
-       if (method != JUST_CFM) {
+       if (smp->method != JUST_CFM) {
                set_bit(SMP_FLAG_MITM_AUTH, &smp->flags);
                if (hcon->pending_sec_level < BT_SECURITY_HIGH)
                        hcon->pending_sec_level = BT_SECURITY_HIGH;
@@ -497,15 +773,15 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
        /* If both devices have Keyoard-Display I/O, the master
         * Confirms and the slave Enters the passkey.
         */
-       if (method == OVERLAP) {
+       if (smp->method == OVERLAP) {
                if (hcon->role == HCI_ROLE_MASTER)
-                       method = CFM_PASSKEY;
+                       smp->method = CFM_PASSKEY;
                else
-                       method = REQ_PASSKEY;
+                       smp->method = REQ_PASSKEY;
        }
 
        /* Generate random passkey. */
-       if (method == CFM_PASSKEY) {
+       if (smp->method == CFM_PASSKEY) {
                memset(smp->tk, 0, sizeof(smp->tk));
                get_random_bytes(&passkey, sizeof(passkey));
                passkey %= 1000000;
@@ -514,12 +790,10 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
                set_bit(SMP_FLAG_TK_VALID, &smp->flags);
        }
 
-       hci_dev_lock(hcon->hdev);
-
-       if (method == REQ_PASSKEY)
+       if (smp->method == REQ_PASSKEY)
                ret = mgmt_user_passkey_request(hcon->hdev, &hcon->dst,
                                                hcon->type, hcon->dst_type);
-       else if (method == JUST_CFM)
+       else if (smp->method == JUST_CFM)
                ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst,
                                                hcon->type, hcon->dst_type,
                                                passkey, 1);
@@ -528,8 +802,6 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
                                                hcon->type, hcon->dst_type,
                                                passkey, 0);
 
-       hci_dev_unlock(hcon->hdev);
-
        return ret;
 }
 
@@ -659,8 +931,8 @@ static void smp_notify_keys(struct l2cap_conn *conn)
                 * just remove it.
                 */
                if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) {
-                       list_del(&smp->remote_irk->list);
-                       kfree(smp->remote_irk);
+                       list_del_rcu(&smp->remote_irk->list);
+                       kfree_rcu(smp->remote_irk, rcu);
                        smp->remote_irk = NULL;
                }
        }
@@ -693,6 +965,58 @@ static void smp_notify_keys(struct l2cap_conn *conn)
                bacpy(&smp->slave_ltk->bdaddr, &hcon->dst);
                mgmt_new_ltk(hdev, smp->slave_ltk, persistent);
        }
+
+       if (smp->link_key) {
+               struct link_key *key;
+               u8 type;
+
+               if (test_bit(SMP_FLAG_DEBUG_KEY, &smp->flags))
+                       type = HCI_LK_DEBUG_COMBINATION;
+               else if (hcon->sec_level == BT_SECURITY_FIPS)
+                       type = HCI_LK_AUTH_COMBINATION_P256;
+               else
+                       type = HCI_LK_UNAUTH_COMBINATION_P256;
+
+               key = hci_add_link_key(hdev, smp->conn->hcon, &hcon->dst,
+                                      smp->link_key, type, 0, &persistent);
+               if (key) {
+                       mgmt_new_link_key(hdev, key, persistent);
+
+                       /* Don't keep debug keys around if the relevant
+                        * flag is not set.
+                        */
+                       if (!test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags) &&
+                           key->type == HCI_LK_DEBUG_COMBINATION) {
+                               list_del_rcu(&key->list);
+                               kfree_rcu(key, rcu);
+                       }
+               }
+       }
+}
+
+static void sc_generate_link_key(struct smp_chan *smp)
+{
+       /* These constants are as specified in the core specification.
+        * In ASCII they spell out to 'tmp1' and 'lebr'.
+        */
+       const u8 tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 };
+       const u8 lebr[4] = { 0x72, 0x62, 0x65, 0x6c };
+
+       smp->link_key = kzalloc(16, GFP_KERNEL);
+       if (!smp->link_key)
+               return;
+
+       if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) {
+               kfree(smp->link_key);
+               smp->link_key = NULL;
+               return;
+       }
+
+       if (smp_h6(smp->tfm_cmac, smp->link_key, lebr, smp->link_key)) {
+               kfree(smp->link_key);
+               smp->link_key = NULL;
+               return;
+       }
 }
 
 static void smp_allow_key_dist(struct smp_chan *smp)
@@ -737,6 +1061,14 @@ static void smp_distribute_keys(struct smp_chan *smp)
                *keydist &= req->resp_key_dist;
        }
 
+       if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+               if (*keydist & SMP_DIST_LINK_KEY)
+                       sc_generate_link_key(smp);
+
+               /* Clear the keys which are generated but not distributed */
+               *keydist &= ~SMP_SC_NO_DIST;
+       }
+
        BT_DBG("keydist 0x%x", *keydist);
 
        if (*keydist & SMP_DIST_ENC_KEY) {
@@ -848,6 +1180,14 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
                return NULL;
        }
 
+       smp->tfm_cmac = crypto_alloc_hash("cmac(aes)", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(smp->tfm_cmac)) {
+               BT_ERR("Unable to create CMAC crypto context");
+               crypto_free_blkcipher(smp->tfm_aes);
+               kfree(smp);
+               return NULL;
+       }
+
        smp->conn = conn;
        chan->data = smp;
 
@@ -860,6 +1200,205 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
        return smp;
 }
 
+static int sc_mackey_and_ltk(struct smp_chan *smp, u8 mackey[16], u8 ltk[16])
+{
+       struct hci_conn *hcon = smp->conn->hcon;
+       u8 *na, *nb, a[7], b[7];
+
+       if (hcon->out) {
+               na   = smp->prnd;
+               nb   = smp->rrnd;
+       } else {
+               na   = smp->rrnd;
+               nb   = smp->prnd;
+       }
+
+       memcpy(a, &hcon->init_addr, 6);
+       memcpy(b, &hcon->resp_addr, 6);
+       a[6] = hcon->init_addr_type;
+       b[6] = hcon->resp_addr_type;
+
+       return smp_f5(smp->tfm_cmac, smp->dhkey, na, nb, a, b, mackey, ltk);
+}
+
+static void sc_dhkey_check(struct smp_chan *smp)
+{
+       struct hci_conn *hcon = smp->conn->hcon;
+       struct smp_cmd_dhkey_check check;
+       u8 a[7], b[7], *local_addr, *remote_addr;
+       u8 io_cap[3], r[16];
+
+       memcpy(a, &hcon->init_addr, 6);
+       memcpy(b, &hcon->resp_addr, 6);
+       a[6] = hcon->init_addr_type;
+       b[6] = hcon->resp_addr_type;
+
+       if (hcon->out) {
+               local_addr = a;
+               remote_addr = b;
+               memcpy(io_cap, &smp->preq[1], 3);
+       } else {
+               local_addr = b;
+               remote_addr = a;
+               memcpy(io_cap, &smp->prsp[1], 3);
+       }
+
+       memset(r, 0, sizeof(r));
+
+       if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+               put_unaligned_le32(hcon->passkey_notify, r);
+
+       smp_f6(smp->tfm_cmac, smp->mackey, smp->prnd, smp->rrnd, r, io_cap,
+              local_addr, remote_addr, check.e);
+
+       smp_send_cmd(smp->conn, SMP_CMD_DHKEY_CHECK, sizeof(check), &check);
+}
+
+static u8 sc_passkey_send_confirm(struct smp_chan *smp)
+{
+       struct l2cap_conn *conn = smp->conn;
+       struct hci_conn *hcon = conn->hcon;
+       struct smp_cmd_pairing_confirm cfm;
+       u8 r;
+
+       r = ((hcon->passkey_notify >> smp->passkey_round) & 0x01);
+       r |= 0x80;
+
+       get_random_bytes(smp->prnd, sizeof(smp->prnd));
+
+       if (smp_f4(smp->tfm_cmac, smp->local_pk, smp->remote_pk, smp->prnd, r,
+                  cfm.confirm_val))
+               return SMP_UNSPECIFIED;
+
+       smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cfm), &cfm);
+
+       return 0;
+}
+
+static u8 sc_passkey_round(struct smp_chan *smp, u8 smp_op)
+{
+       struct l2cap_conn *conn = smp->conn;
+       struct hci_conn *hcon = conn->hcon;
+       struct hci_dev *hdev = hcon->hdev;
+       u8 cfm[16], r;
+
+       /* Ignore the PDU if we've already done 20 rounds (0 - 19) */
+       if (smp->passkey_round >= 20)
+               return 0;
+
+       switch (smp_op) {
+       case SMP_CMD_PAIRING_RANDOM:
+               r = ((hcon->passkey_notify >> smp->passkey_round) & 0x01);
+               r |= 0x80;
+
+               if (smp_f4(smp->tfm_cmac, smp->remote_pk, smp->local_pk,
+                          smp->rrnd, r, cfm))
+                       return SMP_UNSPECIFIED;
+
+               if (memcmp(smp->pcnf, cfm, 16))
+                       return SMP_CONFIRM_FAILED;
+
+               smp->passkey_round++;
+
+               if (smp->passkey_round == 20) {
+                       /* Generate MacKey and LTK */
+                       if (sc_mackey_and_ltk(smp, smp->mackey, smp->tk))
+                               return SMP_UNSPECIFIED;
+               }
+
+               /* The round is only complete when the initiator
+                * receives pairing random.
+                */
+               if (!hcon->out) {
+                       smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
+                                    sizeof(smp->prnd), smp->prnd);
+                       if (smp->passkey_round == 20) {
+                               sc_dhkey_check(smp);
+                               SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+                       } else {
+                               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+                       }
+                       return 0;
+               }
+
+               /* Start the next round */
+               if (smp->passkey_round != 20)
+                       return sc_passkey_round(smp, 0);
+
+               /* Passkey rounds are complete - start DHKey Check */
+               sc_dhkey_check(smp);
+               SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+
+               break;
+
+       case SMP_CMD_PAIRING_CONFIRM:
+               if (test_bit(SMP_FLAG_WAIT_USER, &smp->flags)) {
+                       set_bit(SMP_FLAG_CFM_PENDING, &smp->flags);
+                       return 0;
+               }
+
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
+               if (hcon->out) {
+                       smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
+                                    sizeof(smp->prnd), smp->prnd);
+                       return 0;
+               }
+
+               return sc_passkey_send_confirm(smp);
+
+       case SMP_CMD_PUBLIC_KEY:
+       default:
+               /* Initiating device starts the round */
+               if (!hcon->out)
+                       return 0;
+
+               BT_DBG("%s Starting passkey round %u", hdev->name,
+                      smp->passkey_round + 1);
+
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
+               return sc_passkey_send_confirm(smp);
+       }
+
+       return 0;
+}
+
+static int sc_user_reply(struct smp_chan *smp, u16 mgmt_op, __le32 passkey)
+{
+       struct l2cap_conn *conn = smp->conn;
+       struct hci_conn *hcon = conn->hcon;
+       u8 smp_op;
+
+       clear_bit(SMP_FLAG_WAIT_USER, &smp->flags);
+
+       switch (mgmt_op) {
+       case MGMT_OP_USER_PASSKEY_NEG_REPLY:
+               smp_failure(smp->conn, SMP_PASSKEY_ENTRY_FAILED);
+               return 0;
+       case MGMT_OP_USER_CONFIRM_NEG_REPLY:
+               smp_failure(smp->conn, SMP_NUMERIC_COMP_FAILED);
+               return 0;
+       case MGMT_OP_USER_PASSKEY_REPLY:
+               hcon->passkey_notify = le32_to_cpu(passkey);
+               smp->passkey_round = 0;
+
+               if (test_and_clear_bit(SMP_FLAG_CFM_PENDING, &smp->flags))
+                       smp_op = SMP_CMD_PAIRING_CONFIRM;
+               else
+                       smp_op = 0;
+
+               if (sc_passkey_round(smp, smp_op))
+                       return -EIO;
+
+               return 0;
+       }
+
+       sc_dhkey_check(smp);
+
+       return 0;
+}
+
 int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
 {
        struct l2cap_conn *conn = hcon->l2cap_data;
@@ -885,6 +1424,11 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
 
        smp = chan->data;
 
+       if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+               err = sc_user_reply(smp, mgmt_op, passkey);
+               goto unlock;
+       }
+
        switch (mgmt_op) {
        case MGMT_OP_USER_PASSKEY_REPLY:
                value = le32_to_cpu(passkey);
@@ -946,7 +1490,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
                return SMP_UNSPECIFIED;
 
        /* We didn't start the pairing, so match remote */
-       auth = req->auth_req & AUTH_REQ_MASK;
+       auth = req->auth_req & AUTH_REQ_MASK(hdev);
 
        if (!test_bit(HCI_BONDABLE, &hdev->dev_flags) &&
            (auth & SMP_AUTH_BONDING))
@@ -956,6 +1500,11 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        memcpy(&smp->preq[1], req, sizeof(*req));
        skb_pull(skb, sizeof(*req));
 
+       build_pairing_cmd(conn, req, &rsp, auth);
+
+       if (rsp.auth_req & SMP_AUTH_SC)
+               set_bit(SMP_FLAG_SC, &smp->flags);
+
        if (conn->hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
                sec_level = BT_SECURITY_MEDIUM;
        else
@@ -964,7 +1513,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        if (sec_level > conn->hcon->pending_sec_level)
                conn->hcon->pending_sec_level = sec_level;
 
-       /* If we need MITM check that it can be acheived */
+       /* If we need MITM check that it can be achieved */
        if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) {
                u8 method;
 
@@ -974,8 +1523,6 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
                        return SMP_AUTH_REQUIREMENTS;
        }
 
-       build_pairing_cmd(conn, req, &rsp, auth);
-
        key_size = min(req->max_key_size, rsp.max_key_size);
        if (check_enc_key_size(conn, key_size))
                return SMP_ENC_KEY_SIZE;
@@ -986,7 +1533,18 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
 
        smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
-       SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
+       clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
+
+       if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+               SMP_ALLOW_CMD(smp, SMP_CMD_PUBLIC_KEY);
+               /* Clear bits which are generated but not distributed */
+               smp->remote_key_dist &= ~SMP_SC_NO_DIST;
+               /* Wait for Public Key from Initiating Device */
+               return 0;
+       } else {
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+       }
 
        /* Request setup of TK */
        ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability);
@@ -996,11 +1554,37 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        return 0;
 }
 
+static u8 sc_send_public_key(struct smp_chan *smp)
+{
+       BT_DBG("");
+
+       while (true) {
+               /* Generate local key pair for Secure Connections */
+               if (!ecc_make_key(smp->local_pk, smp->local_sk))
+                       return SMP_UNSPECIFIED;
+
+               /* This is unlikely, but we need to check that we didn't
+                * accidentially generate a debug key.
+                */
+               if (memcmp(smp->local_sk, debug_sk, 32))
+                       break;
+       }
+
+       BT_DBG("Local Public Key X: %32phN", smp->local_pk);
+       BT_DBG("Local Public Key Y: %32phN", &smp->local_pk[32]);
+       BT_DBG("Local Private Key:  %32phN", smp->local_sk);
+
+       smp_send_cmd(smp->conn, SMP_CMD_PUBLIC_KEY, 64, smp->local_pk);
+
+       return 0;
+}
+
 static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_pairing *req, *rsp = (void *) skb->data;
        struct l2cap_chan *chan = conn->smp;
        struct smp_chan *smp = chan->data;
+       struct hci_dev *hdev = conn->hcon->hdev;
        u8 key_size, auth;
        int ret;
 
@@ -1020,9 +1604,14 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
        if (check_enc_key_size(conn, key_size))
                return SMP_ENC_KEY_SIZE;
 
-       auth = rsp->auth_req & AUTH_REQ_MASK;
+       auth = rsp->auth_req & AUTH_REQ_MASK(hdev);
 
-       /* If we need MITM check that it can be acheived */
+       if ((req->auth_req & SMP_AUTH_SC) && (auth & SMP_AUTH_SC))
+               set_bit(SMP_FLAG_SC, &smp->flags);
+       else if (conn->hcon->pending_sec_level > BT_SECURITY_HIGH)
+               conn->hcon->pending_sec_level = BT_SECURITY_HIGH;
+
+       /* If we need MITM check that it can be achieved */
        if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) {
                u8 method;
 
@@ -1042,6 +1631,13 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
         */
        smp->remote_key_dist &= rsp->resp_key_dist;
 
+       if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+               /* Clear bits which are generated but not distributed */
+               smp->remote_key_dist &= ~SMP_SC_NO_DIST;
+               SMP_ALLOW_CMD(smp, SMP_CMD_PUBLIC_KEY);
+               return sc_send_public_key(smp);
+       }
+
        auth |= req->auth_req;
 
        ret = tk_request(conn, 0, auth, req->io_capability, rsp->io_capability);
@@ -1057,6 +1653,28 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
        return 0;
 }
 
+static u8 sc_check_confirm(struct smp_chan *smp)
+{
+       struct l2cap_conn *conn = smp->conn;
+
+       BT_DBG("");
+
+       /* Public Key exchange must happen before any other steps */
+       if (!test_bit(SMP_FLAG_REMOTE_PK, &smp->flags))
+               return SMP_UNSPECIFIED;
+
+       if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+               return sc_passkey_round(smp, SMP_CMD_PAIRING_CONFIRM);
+
+       if (conn->hcon->out) {
+               smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
+                            smp->prnd);
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+       }
+
+       return 0;
+}
+
 static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct l2cap_chan *chan = conn->smp;
@@ -1070,6 +1688,9 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
        memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf));
        skb_pull(skb, sizeof(smp->pcnf));
 
+       if (test_bit(SMP_FLAG_SC, &smp->flags))
+               return sc_check_confirm(smp);
+
        if (conn->hcon->out) {
                smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
                             smp->prnd);
@@ -1089,6 +1710,10 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct l2cap_chan *chan = conn->smp;
        struct smp_chan *smp = chan->data;
+       struct hci_conn *hcon = conn->hcon;
+       u8 *pkax, *pkbx, *na, *nb;
+       u32 passkey;
+       int err;
 
        BT_DBG("conn %p", conn);
 
@@ -1098,7 +1723,64 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
        memcpy(smp->rrnd, skb->data, sizeof(smp->rrnd));
        skb_pull(skb, sizeof(smp->rrnd));
 
-       return smp_random(smp);
+       if (!test_bit(SMP_FLAG_SC, &smp->flags))
+               return smp_random(smp);
+
+       /* Passkey entry has special treatment */
+       if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+               return sc_passkey_round(smp, SMP_CMD_PAIRING_RANDOM);
+
+       if (hcon->out) {
+               u8 cfm[16];
+
+               err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->local_pk,
+                            smp->rrnd, 0, cfm);
+               if (err)
+                       return SMP_UNSPECIFIED;
+
+               if (memcmp(smp->pcnf, cfm, 16))
+                       return SMP_CONFIRM_FAILED;
+
+               pkax = smp->local_pk;
+               pkbx = smp->remote_pk;
+               na   = smp->prnd;
+               nb   = smp->rrnd;
+       } else {
+               smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
+                            smp->prnd);
+               SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+
+               pkax = smp->remote_pk;
+               pkbx = smp->local_pk;
+               na   = smp->rrnd;
+               nb   = smp->prnd;
+       }
+
+       /* Generate MacKey and LTK */
+       err = sc_mackey_and_ltk(smp, smp->mackey, smp->tk);
+       if (err)
+               return SMP_UNSPECIFIED;
+
+       if (smp->method == JUST_WORKS) {
+               if (hcon->out) {
+                       sc_dhkey_check(smp);
+                       SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+               }
+               return 0;
+       }
+
+       err = smp_g2(smp->tfm_cmac, pkax, pkbx, na, nb, &passkey);
+       if (err)
+               return SMP_UNSPECIFIED;
+
+       err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type,
+                                       hcon->dst_type, passkey, 0);
+       if (err)
+               return SMP_UNSPECIFIED;
+
+       set_bit(SMP_FLAG_WAIT_USER, &smp->flags);
+
+       return 0;
 }
 
 static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
@@ -1106,8 +1788,7 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
        struct smp_ltk *key;
        struct hci_conn *hcon = conn->hcon;
 
-       key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
-                                  hcon->role);
+       key = hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role);
        if (!key)
                return false;
 
@@ -1126,20 +1807,21 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
        return true;
 }
 
-bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
+bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
+                            enum smp_key_pref key_pref)
 {
        if (sec_level == BT_SECURITY_LOW)
                return true;
 
-       /* If we're encrypted with an STK always claim insufficient
-        * security. This way we allow the connection to be re-encrypted
-        * with an LTK, even if the LTK provides the same level of
-        * security. Only exception is if we don't have an LTK (e.g.
-        * because of key distribution bits).
+       /* If we're encrypted with an STK but the caller prefers using
+        * LTK claim insufficient security. This way we allow the
+        * connection to be re-encrypted with an LTK, even if the LTK
+        * provides the same level of security. Only exception is if we
+        * don't have an LTK (e.g. because of key distribution bits).
         */
-       if (test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
-           hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
-                                hcon->role))
+       if (key_pref == SMP_USE_LTK &&
+           test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
+           hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role))
                return false;
 
        if (hcon->sec_level >= sec_level)
@@ -1153,6 +1835,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
        struct smp_cmd_security_req *rp = (void *) skb->data;
        struct smp_cmd_pairing cp;
        struct hci_conn *hcon = conn->hcon;
+       struct hci_dev *hdev = hcon->hdev;
        struct smp_chan *smp;
        u8 sec_level, auth;
 
@@ -1164,14 +1847,14 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
        if (hcon->role != HCI_ROLE_MASTER)
                return SMP_CMD_NOTSUPP;
 
-       auth = rp->auth_req & AUTH_REQ_MASK;
+       auth = rp->auth_req & AUTH_REQ_MASK(hdev);
 
        if (hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
                sec_level = BT_SECURITY_MEDIUM;
        else
                sec_level = authreq_to_seclevel(auth);
 
-       if (smp_sufficient_security(hcon, sec_level))
+       if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK))
                return 0;
 
        if (sec_level > hcon->pending_sec_level)
@@ -1221,7 +1904,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
        if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags))
                return 1;
 
-       if (smp_sufficient_security(hcon, sec_level))
+       if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK))
                return 1;
 
        if (sec_level > hcon->pending_sec_level)
@@ -1247,6 +1930,9 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
 
        authreq = seclevel_to_authreq(sec_level);
 
+       if (test_bit(HCI_SC_ENABLED, &hcon->hdev->dev_flags))
+               authreq |= SMP_AUTH_SC;
+
        /* Require MITM if IO Capability allows or the security level
         * requires it.
         */
@@ -1323,7 +2009,6 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
 
        skb_pull(skb, sizeof(*rp));
 
-       hci_dev_lock(hdev);
        authenticated = (hcon->sec_level == BT_SECURITY_HIGH);
        ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, SMP_LTK,
                          authenticated, smp->tk, smp->enc_key_size,
@@ -1331,7 +2016,6 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
        smp->ltk = ltk;
        if (!(smp->remote_key_dist & KEY_DIST_MASK))
                smp_distribute_keys(smp);
-       hci_dev_unlock(hdev);
 
        return 0;
 }
@@ -1378,8 +2062,6 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
 
        skb_pull(skb, sizeof(*info));
 
-       hci_dev_lock(hcon->hdev);
-
        /* Strictly speaking the Core Specification (4.1) allows sending
         * an empty address which would force us to rely on just the IRK
         * as "identity information". However, since such
@@ -1407,8 +2089,6 @@ distribute:
        if (!(smp->remote_key_dist & KEY_DIST_MASK))
                smp_distribute_keys(smp);
 
-       hci_dev_unlock(hcon->hdev);
-
        return 0;
 }
 
@@ -1417,7 +2097,6 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
        struct smp_cmd_sign_info *rp = (void *) skb->data;
        struct l2cap_chan *chan = conn->smp;
        struct smp_chan *smp = chan->data;
-       struct hci_dev *hdev = conn->hcon->hdev;
        struct smp_csrk *csrk;
 
        BT_DBG("conn %p", conn);
@@ -1430,7 +2109,6 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
 
        skb_pull(skb, sizeof(*rp));
 
-       hci_dev_lock(hdev);
        csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
        if (csrk) {
                csrk->master = 0x01;
@@ -1438,7 +2116,206 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
        }
        smp->csrk = csrk;
        smp_distribute_keys(smp);
-       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static u8 sc_select_method(struct smp_chan *smp)
+{
+       struct l2cap_conn *conn = smp->conn;
+       struct hci_conn *hcon = conn->hcon;
+       struct smp_cmd_pairing *local, *remote;
+       u8 local_mitm, remote_mitm, local_io, remote_io, method;
+
+       /* The preq/prsp contain the raw Pairing Request/Response PDUs
+        * which are needed as inputs to some crypto functions. To get
+        * the "struct smp_cmd_pairing" from them we need to skip the
+        * first byte which contains the opcode.
+        */
+       if (hcon->out) {
+               local = (void *) &smp->preq[1];
+               remote = (void *) &smp->prsp[1];
+       } else {
+               local = (void *) &smp->prsp[1];
+               remote = (void *) &smp->preq[1];
+       }
+
+       local_io = local->io_capability;
+       remote_io = remote->io_capability;
+
+       local_mitm = (local->auth_req & SMP_AUTH_MITM);
+       remote_mitm = (remote->auth_req & SMP_AUTH_MITM);
+
+       /* If either side wants MITM, look up the method from the table,
+        * otherwise use JUST WORKS.
+        */
+       if (local_mitm || remote_mitm)
+               method = get_auth_method(smp, local_io, remote_io);
+       else
+               method = JUST_WORKS;
+
+       /* Don't confirm locally initiated pairing attempts */
+       if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags))
+               method = JUST_WORKS;
+
+       return method;
+}
+
+static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+       struct smp_cmd_public_key *key = (void *) skb->data;
+       struct hci_conn *hcon = conn->hcon;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
+       struct hci_dev *hdev = hcon->hdev;
+       struct smp_cmd_pairing_confirm cfm;
+       int err;
+
+       BT_DBG("conn %p", conn);
+
+       if (skb->len < sizeof(*key))
+               return SMP_INVALID_PARAMS;
+
+       memcpy(smp->remote_pk, key, 64);
+
+       /* Non-initiating device sends its public key after receiving
+        * the key from the initiating device.
+        */
+       if (!hcon->out) {
+               err = sc_send_public_key(smp);
+               if (err)
+                       return err;
+       }
+
+       BT_DBG("Remote Public Key X: %32phN", smp->remote_pk);
+       BT_DBG("Remote Public Key Y: %32phN", &smp->remote_pk[32]);
+
+       if (!ecdh_shared_secret(smp->remote_pk, smp->local_sk, smp->dhkey))
+               return SMP_UNSPECIFIED;
+
+       BT_DBG("DHKey %32phN", smp->dhkey);
+
+       set_bit(SMP_FLAG_REMOTE_PK, &smp->flags);
+
+       smp->method = sc_select_method(smp);
+
+       BT_DBG("%s selected method 0x%02x", hdev->name, smp->method);
+
+       /* JUST_WORKS and JUST_CFM result in an unauthenticated key */
+       if (smp->method == JUST_WORKS || smp->method == JUST_CFM)
+               hcon->pending_sec_level = BT_SECURITY_MEDIUM;
+       else
+               hcon->pending_sec_level = BT_SECURITY_FIPS;
+
+       if (!memcmp(debug_pk, smp->remote_pk, 64))
+               set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
+
+       if (smp->method == DSP_PASSKEY) {
+               get_random_bytes(&hcon->passkey_notify,
+                                sizeof(hcon->passkey_notify));
+               hcon->passkey_notify %= 1000000;
+               hcon->passkey_entered = 0;
+               smp->passkey_round = 0;
+               if (mgmt_user_passkey_notify(hdev, &hcon->dst, hcon->type,
+                                            hcon->dst_type,
+                                            hcon->passkey_notify,
+                                            hcon->passkey_entered))
+                       return SMP_UNSPECIFIED;
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+               return sc_passkey_round(smp, SMP_CMD_PUBLIC_KEY);
+       }
+
+       if (hcon->out)
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
+       if (smp->method == REQ_PASSKEY) {
+               if (mgmt_user_passkey_request(hdev, &hcon->dst, hcon->type,
+                                             hcon->dst_type))
+                       return SMP_UNSPECIFIED;
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+               set_bit(SMP_FLAG_WAIT_USER, &smp->flags);
+               return 0;
+       }
+
+       /* The Initiating device waits for the non-initiating device to
+        * send the confirm value.
+        */
+       if (conn->hcon->out)
+               return 0;
+
+       err = smp_f4(smp->tfm_cmac, smp->local_pk, smp->remote_pk, smp->prnd,
+                    0, cfm.confirm_val);
+       if (err)
+               return SMP_UNSPECIFIED;
+
+       smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cfm), &cfm);
+       SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
+       return 0;
+}
+
+static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+       struct smp_cmd_dhkey_check *check = (void *) skb->data;
+       struct l2cap_chan *chan = conn->smp;
+       struct hci_conn *hcon = conn->hcon;
+       struct smp_chan *smp = chan->data;
+       u8 a[7], b[7], *local_addr, *remote_addr;
+       u8 io_cap[3], r[16], e[16];
+       u8 key_type, auth;
+       int err;
+
+       BT_DBG("conn %p", conn);
+
+       if (skb->len < sizeof(*check))
+               return SMP_INVALID_PARAMS;
+
+       memcpy(a, &hcon->init_addr, 6);
+       memcpy(b, &hcon->resp_addr, 6);
+       a[6] = hcon->init_addr_type;
+       b[6] = hcon->resp_addr_type;
+
+       if (hcon->out) {
+               local_addr = a;
+               remote_addr = b;
+               memcpy(io_cap, &smp->prsp[1], 3);
+       } else {
+               local_addr = b;
+               remote_addr = a;
+               memcpy(io_cap, &smp->preq[1], 3);
+       }
+
+       memset(r, 0, sizeof(r));
+
+       if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+               put_unaligned_le32(hcon->passkey_notify, r);
+
+       err = smp_f6(smp->tfm_cmac, smp->mackey, smp->rrnd, smp->prnd, r,
+                    io_cap, remote_addr, local_addr, e);
+       if (err)
+               return SMP_UNSPECIFIED;
+
+       if (memcmp(check->e, e, 16))
+               return SMP_DHKEY_CHECK_FAILED;
+
+       if (test_bit(SMP_FLAG_DEBUG_KEY, &smp->flags))
+               key_type = SMP_LTK_P256_DEBUG;
+       else
+               key_type = SMP_LTK_P256;
+
+       if (hcon->pending_sec_level == BT_SECURITY_FIPS)
+               auth = 1;
+       else
+               auth = 0;
+
+       smp->ltk = hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
+                              key_type, auth, smp->tk, smp->enc_key_size,
+                              0, 0);
+
+       if (hcon->out) {
+               hci_le_start_enc(hcon, 0, 0, smp->tk);
+               hcon->enc_key_size = smp->enc_key_size;
+       }
 
        return 0;
 }
@@ -1527,6 +2404,14 @@ static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb)
                reason = smp_cmd_sign_info(conn, skb);
                break;
 
+       case SMP_CMD_PUBLIC_KEY:
+               reason = smp_cmd_public_key(conn, skb);
+               break;
+
+       case SMP_CMD_DHKEY_CHECK:
+               reason = smp_cmd_dhkey_check(conn, skb);
+               break;
+
        default:
                BT_DBG("Unknown command code 0x%2.2x", code);
                reason = SMP_CMD_NOTSUPP;
@@ -1662,6 +2547,13 @@ static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
        chan->omtu      = pchan->omtu;
        chan->mode      = pchan->mode;
 
+       /* Other L2CAP channels may request SMP routines in order to
+        * change the security level. This means that the SMP channel
+        * lock must be considered in its own category to avoid lockdep
+        * warnings.
+        */
+       atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
+
        BT_DBG("created chan %p", chan);
 
        return chan;
@@ -1693,7 +2585,7 @@ int smp_register(struct hci_dev *hdev)
 
        BT_DBG("%s", hdev->name);
 
-       tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+       tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0);
        if (IS_ERR(tfm_aes)) {
                int err = PTR_ERR(tfm_aes);
                BT_ERR("Unable to create crypto context");
@@ -1719,6 +2611,9 @@ int smp_register(struct hci_dev *hdev)
        chan->imtu = L2CAP_DEFAULT_MTU;
        chan->ops = &smp_root_chan_ops;
 
+       /* Set correct nesting level for a parent/listening channel */
+       atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
+
        hdev->smp_data = chan;
 
        return 0;