summaryrefslogtreecommitdiff
path: root/emulator/smp.c
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@intel.com>2014-10-07 00:50:27 +0300
committerJohan Hedberg <johan.hedberg@intel.com>2014-12-03 16:32:49 +0200
commit3a3a0636ec4ed95b8437ed1dd352dcab98c4b1e5 (patch)
tree74ce3635e54fcbdebe58140593f61729d71ed892 /emulator/smp.c
parent504ae89fd8b88931ce193886e1d1ed01903473eb (diff)
downloadbluez-3a3a0636ec4ed95b8437ed1dd352dcab98c4b1e5.tar.gz
emulator/smp: Add initial code for selecting authentication method
Diffstat (limited to 'emulator/smp.c')
-rw-r--r--emulator/smp.c93
1 files changed, 93 insertions, 0 deletions
diff --git a/emulator/smp.c b/emulator/smp.c
index 2fe2add7b..0b9d3dc91 100644
--- a/emulator/smp.c
+++ b/emulator/smp.c
@@ -55,6 +55,14 @@
#define SC_NO_DIST (DIST_ENC_KEY | DIST_LINK_KEY)
+#define MAX_IO_CAP 0x04
+
+#define SMP_AUTH_NONE 0x00
+#define SMP_AUTH_BONDING 0x01
+#define SMP_AUTH_MITM 0x04
+#define SMP_AUTH_SC 0x08
+#define SMP_AUTH_KEYPRESS 0x10
+
struct smp {
struct bthost *bthost;
struct smp_conn *conn;
@@ -66,6 +74,8 @@ struct smp_conn {
uint16_t handle;
bool out;
bool sc;
+ bool initiator;
+ uint8_t method;
uint8_t local_key_dist;
uint8_t remote_key_dist;
uint8_t ia[6];
@@ -87,6 +97,81 @@ struct smp_conn {
uint8_t mackey[16];
};
+enum {
+ JUST_WORKS,
+ JUST_CFM,
+ REQ_PASSKEY,
+ CFM_PASSKEY,
+ REQ_OOB,
+ DSP_PASSKEY,
+ OVERLAP,
+};
+
+static const uint8_t gen_method[5][5] = {
+ { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY },
+ { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY },
+ { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY },
+ { JUST_WORKS, JUST_CFM, JUST_WORKS, JUST_WORKS, JUST_CFM },
+ { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP },
+};
+
+static const uint8_t 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 uint8_t get_auth_method(struct smp_conn *conn, uint8_t local_io,
+ uint8_t remote_io)
+{
+ /* If either side has unknown io_caps, use JUST_CFM (which gets
+ * converted later to JUST_WORKS if we're initiators.
+ */
+ if (local_io > MAX_IO_CAP || remote_io > MAX_IO_CAP)
+ return JUST_CFM;
+
+ if (conn->sc)
+ return sc_method[remote_io][local_io];
+
+ return gen_method[remote_io][local_io];
+}
+
+static uint8_t sc_select_method(struct smp_conn *conn)
+{
+ struct bt_l2cap_smp_pairing_request *local, *remote;
+ uint8_t local_mitm, remote_mitm, local_io, remote_io, method;
+
+ if (conn->out) {
+ local = (void *) &conn->preq[1];
+ remote = (void *) &conn->prsp[1];
+ } else {
+ local = (void *) &conn->prsp[1];
+ remote = (void *) &conn->preq[1];
+ }
+
+ local_io = local->io_capa;
+ remote_io = remote->io_capa;
+
+ 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(conn, local_io, remote_io);
+ else
+ method = JUST_WORKS;
+
+ /* Don't confirm locally initiated pairing attempts */
+ if (method == JUST_CFM && conn->initiator)
+ method = JUST_WORKS;
+
+ return method;
+}
+
static void smp_send(struct smp_conn *conn, uint8_t smp_cmd, const void *data,
uint8_t len)
{
@@ -396,6 +481,14 @@ static void public_key(struct smp_conn *conn, const void *data, uint16_t len)
if (!ecdh_shared_secret(conn->remote_pk, conn->local_sk, conn->dhkey))
return;
+ conn->method = sc_select_method(conn);
+
+ if (conn->method == DSP_PASSKEY) {
+ return;
+ } else if (conn->method == REQ_PASSKEY) {
+ return;
+ }
+
if (conn->out)
return;