Bluetooth: Add support for LE SC DHKey check PDU
[cascardo/linux.git] / net / bluetooth / smp.c
index 3808ade..f59f051 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(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,
        SMP_FLAG_CFM_PENDING,
@@ -46,6 +53,7 @@ enum {
        SMP_FLAG_COMPLETE,
        SMP_FLAG_INITIATOR,
        SMP_FLAG_SC,
+       SMP_FLAG_REMOTE_PK,
 };
 
 struct smp_chan {
@@ -71,7 +79,15 @@ struct smp_chan {
        struct smp_irk  *remote_irk;
        unsigned long   flags;
 
+       /* 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;
 };
 
 static inline void swap_buf(const u8 *src, u8 *dst, size_t len)
@@ -82,6 +98,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;
@@ -284,17 +484,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:
@@ -328,6 +533,16 @@ 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;
@@ -381,6 +596,7 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
        kfree(smp->slave_csrk);
 
        crypto_free_blkcipher(smp->tfm_aes);
+       crypto_free_hash(smp->tfm_cmac);
 
        /* If pairing failed clean up any keys we might have */
        if (!complete) {
@@ -846,6 +1062,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;
 
@@ -858,6 +1082,69 @@ 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 int sc_user_reply(struct smp_chan *smp, u16 mgmt_op, __le32 passkey)
+{
+       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];
+
+       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;
+       }
+
+       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);
+       }
+
+       memcpy(r, &passkey, sizeof(passkey));
+       memset(r + sizeof(passkey), 0, sizeof(r) - sizeof(passkey));
+
+       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);
+
+       return 0;
+}
+
 int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
 {
        struct l2cap_conn *conn = hcon->l2cap_data;
@@ -883,6 +1170,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);
@@ -987,7 +1279,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);
@@ -997,6 +1300,23 @@ 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("");
+
+       /* Generate local key pair for Secure Connections */
+       if (!ecc_make_key(smp->local_pk, smp->local_sk))
+               return SMP_UNSPECIFIED;
+
+       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;
@@ -1026,6 +1346,8 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
 
        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) {
@@ -1047,6 +1369,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);
@@ -1062,6 +1391,25 @@ 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 (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;
@@ -1075,6 +1423,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);
@@ -1094,6 +1445,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);
 
@@ -1103,7 +1458,51 @@ 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);
+
+       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;
+
+       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;
+
+       return 0;
 }
 
 static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
@@ -1111,8 +1510,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;
 
@@ -1145,8 +1543,7 @@ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
         */
        if (key_pref == SMP_USE_LTK &&
            test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
-           hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
-                                hcon->role))
+           hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role))
                return false;
 
        if (hcon->sec_level >= sec_level)
@@ -1255,6 +1652,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.
         */
@@ -1442,6 +1842,110 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
        return 0;
 }
 
+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 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);
+
+       /* 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];
+       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));
+
+       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;
+
+       smp->ltk = hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
+                              SMP_LTK_P256, 0, 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;
+}
+
 static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb)
 {
        struct l2cap_conn *conn = chan->conn;
@@ -1526,6 +2030,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;