__le16 max_ce_len;
} __packed;
+#define HCI_OP_LE_START_ENC 0x2019
+struct hci_cp_le_start_enc {
+ __le16 handle;
+ __u8 rand[8];
+ __le16 ediv;
+ __u8 ltk[16];
+} __packed;
+
+#define HCI_OP_LE_LTK_REPLY 0x201a
+struct hci_cp_le_ltk_reply {
+ __le16 handle;
+ __u8 ltk[16];
+} __packed;
+struct hci_rp_le_ltk_reply {
+ __u8 status;
+ __le16 handle;
+} __packed;
+
+#define HCI_OP_LE_LTK_NEG_REPLY 0x201b
+struct hci_cp_le_ltk_neg_reply {
+ __le16 handle;
+} __packed;
+struct hci_rp_le_ltk_neg_reply {
+ __u8 status;
+ __le16 handle;
+} __packed;
+
/* ---- HCI Events ---- */
#define HCI_EV_INQUIRY_COMPLETE 0x01
__u8 clk_accurancy;
} __packed;
+#define HCI_EV_LE_LTK_REQ 0x05
+struct hci_ev_le_ltk_req {
+ __le16 handle;
+ __u8 random[8];
+ __le16 ediv;
+} __packed;
+
/* Advertising report event types */
#define ADV_IND 0x00
#define ADV_DIRECT_IND 0x01
__u8 power_save;
__u16 disc_timeout;
unsigned long pend;
+ __u8 ltk[16];
__u8 remote_cap;
__u8 remote_oob;
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
u16 latency, u16 to_multiplier);
+void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
+ __u8 ltk[16]);
+void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]);
+void hci_le_ltk_neg_reply(struct hci_conn *conn);
+
#endif /* __HCI_CORE_H */
}
EXPORT_SYMBOL(hci_le_conn_update);
+void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
+ __u8 ltk[16])
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_le_start_enc cp;
+
+ BT_DBG("%p", conn);
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.handle = cpu_to_le16(conn->handle);
+ memcpy(cp.ltk, ltk, sizeof(cp.ltk));
+ cp.ediv = ediv;
+ memcpy(cp.rand, rand, sizeof(rand));
+
+ hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp);
+}
+EXPORT_SYMBOL(hci_le_start_enc);
+
+void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16])
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_le_ltk_reply cp;
+
+ BT_DBG("%p", conn);
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.handle = cpu_to_le16(conn->handle);
+ memcpy(cp.ltk, ltk, sizeof(ltk));
+
+ hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
+}
+EXPORT_SYMBOL(hci_le_ltk_reply);
+
+void hci_le_ltk_neg_reply(struct hci_conn *conn)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_le_ltk_neg_reply cp;
+
+ BT_DBG("%p", conn);
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.handle = cpu_to_le16(conn->handle);
+
+ hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(cp), &cp);
+}
+
/* Device _must_ be locked */
void hci_sco_setup(struct hci_conn *conn, __u8 status)
{
hci_dev_unlock(hdev);
}
+static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_rp_le_ltk_reply *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+ if (rp->status)
+ return;
+
+ hci_req_complete(hdev, HCI_OP_LE_LTK_REPLY, rp->status);
+}
+
+static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_rp_le_ltk_neg_reply *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+ if (rp->status)
+ return;
+
+ hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status);
+}
+
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
{
BT_DBG("%s status 0x%x", hdev->name, status);
hci_dev_unlock(hdev);
}
+static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status)
+{
+ BT_DBG("%s status 0x%x", hdev->name, status);
+}
+
static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
hci_cc_le_set_scan_enable(hdev, skb);
break;
+ case HCI_OP_LE_LTK_REPLY:
+ hci_cc_le_ltk_reply(hdev, skb);
+ break;
+
+ case HCI_OP_LE_LTK_NEG_REPLY:
+ hci_cc_le_ltk_neg_reply(hdev, skb);
+ break;
+
default:
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
break;
hci_cs_le_create_conn(hdev, ev->status);
break;
+ case HCI_OP_LE_START_ENC:
+ hci_cs_le_start_enc(hdev, ev->status);
+ break;
+
default:
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
break;
hci_dev_unlock(hdev);
}
+static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_le_ltk_req *ev = (void *) skb->data;
+ struct hci_cp_le_ltk_reply cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle));
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = cpu_to_le16(conn->handle);
+ memcpy(cp.ltk, conn->ltk, sizeof(conn->ltk));
+
+ hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
+
+ hci_dev_unlock(hdev);
+}
+
static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_le_meta *le_ev = (void *) skb->data;
hci_le_adv_report_evt(hdev, skb);
break;
+ case HCI_EV_LE_LTK_REQ:
+ hci_le_ltk_request_evt(hdev, skb);
+ break;
+
default:
break;
}
static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
{
- struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
+ struct hci_conn *hcon = conn->hcon;
+ struct crypto_blkcipher *tfm = hcon->hdev->tfm;
int ret;
u8 key[16], res[16], random[16], confirm[16], buf[128];
swap128(skb->data, random);
skb_pull(skb, sizeof(random));
+ memset(hcon->ltk, 0, sizeof(hcon->ltk));
+
if (conn->hcon->out)
ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0,
conn->src, conn->hcon->dst_type, conn->dst,
}
if (conn->hcon->out) {
+ __le16 ediv;
+ u8 rand[8];
+
smp_s1(tfm, conn->tk, random, conn->prnd, key);
+ swap128(key, hcon->ltk);
- hex_dump_to_buffer(key, sizeof(key), 16, 1, buf,
- sizeof(buf), 0);
- BT_DBG("key %s", buf);
+ memset(rand, 0, sizeof(rand));
+ ediv = 0;
+ hci_le_start_enc(hcon, ediv, rand, hcon->ltk);
} else {
u8 r[16];
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r);
smp_s1(tfm, conn->tk, conn->prnd, random, key);
+ swap128(key, hcon->ltk);
hex_dump_to_buffer(key, sizeof(key), 16, 1, buf,
sizeof(buf), 0);