Bluetooth: Add selection of the SC authentication method
This patch adds code to select the authentication method for Secure Connections based on the local and remote capabilities. A new DSP_PASSKEY method is also added for displaying the passkey - something that is not part of legacy SMP pairing. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
parent
783e057462
commit
5e3d3d9b3c
1 changed files with 69 additions and 5 deletions
|
@ -661,6 +661,7 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason)
|
||||||
#define REQ_PASSKEY 0x02
|
#define REQ_PASSKEY 0x02
|
||||||
#define CFM_PASSKEY 0x03
|
#define CFM_PASSKEY 0x03
|
||||||
#define REQ_OOB 0x04
|
#define REQ_OOB 0x04
|
||||||
|
#define DSP_PASSKEY 0x05
|
||||||
#define OVERLAP 0xFF
|
#define OVERLAP 0xFF
|
||||||
|
|
||||||
static const u8 gen_method[5][5] = {
|
static const u8 gen_method[5][5] = {
|
||||||
|
@ -671,6 +672,14 @@ static const u8 gen_method[5][5] = {
|
||||||
{ CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP },
|
{ 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)
|
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
|
/* If either side has unknown io_caps, use JUST_CFM (which gets
|
||||||
|
@ -680,6 +689,9 @@ static u8 get_auth_method(struct smp_chan *smp, u8 local_io, u8 remote_io)
|
||||||
remote_io > SMP_IO_KEYBOARD_DISPLAY)
|
remote_io > SMP_IO_KEYBOARD_DISPLAY)
|
||||||
return JUST_CFM;
|
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];
|
return gen_method[remote_io][local_io];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1305,6 +1317,11 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
memcpy(&smp->preq[1], req, sizeof(*req));
|
memcpy(&smp->preq[1], req, sizeof(*req));
|
||||||
skb_pull(skb, 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)
|
if (conn->hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
|
||||||
sec_level = BT_SECURITY_MEDIUM;
|
sec_level = BT_SECURITY_MEDIUM;
|
||||||
else
|
else
|
||||||
|
@ -1323,11 +1340,6 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
return SMP_AUTH_REQUIREMENTS;
|
return SMP_AUTH_REQUIREMENTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
build_pairing_cmd(conn, req, &rsp, auth);
|
|
||||||
|
|
||||||
if (rsp.auth_req & SMP_AUTH_SC)
|
|
||||||
set_bit(SMP_FLAG_SC, &smp->flags);
|
|
||||||
|
|
||||||
key_size = min(req->max_key_size, rsp.max_key_size);
|
key_size = min(req->max_key_size, rsp.max_key_size);
|
||||||
if (check_enc_key_size(conn, key_size))
|
if (check_enc_key_size(conn, key_size))
|
||||||
return SMP_ENC_KEY_SIZE;
|
return SMP_ENC_KEY_SIZE;
|
||||||
|
@ -1901,12 +1913,54 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
return 0;
|
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)
|
static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct smp_cmd_public_key *key = (void *) skb->data;
|
struct smp_cmd_public_key *key = (void *) skb->data;
|
||||||
struct hci_conn *hcon = conn->hcon;
|
struct hci_conn *hcon = conn->hcon;
|
||||||
struct l2cap_chan *chan = conn->smp;
|
struct l2cap_chan *chan = conn->smp;
|
||||||
struct smp_chan *smp = chan->data;
|
struct smp_chan *smp = chan->data;
|
||||||
|
struct hci_dev *hdev = hcon->hdev;
|
||||||
struct smp_cmd_pairing_confirm cfm;
|
struct smp_cmd_pairing_confirm cfm;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -1936,6 +1990,16 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
|
|
||||||
set_bit(SMP_FLAG_REMOTE_PK, &smp->flags);
|
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;
|
||||||
|
|
||||||
/* The Initiating device waits for the non-initiating device to
|
/* The Initiating device waits for the non-initiating device to
|
||||||
* send the confirm value.
|
* send the confirm value.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue