#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,
SMP_FLAG_COMPLETE,
SMP_FLAG_INITIATOR,
SMP_FLAG_SC,
+ SMP_FLAG_REMOTE_PK,
};
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)
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;
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:
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;
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) {
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;
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;
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);
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);
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;
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) {
*/
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);
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;
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);
{
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);
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)
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;
*/
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)
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.
*/
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;
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;