|  | /* | 
|  | * | 
|  | *  BlueZ - Bluetooth protocol stack for Linux | 
|  | * | 
|  | *  Copyright (C) 2013-2014  Intel Corporation | 
|  | * | 
|  | * | 
|  | *  This library is free software; you can redistribute it and/or | 
|  | *  modify it under the terms of the GNU Lesser General Public | 
|  | *  License as published by the Free Software Foundation; either | 
|  | *  version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | *  This library is distributed in the hope that it will be useful, | 
|  | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | *  Lesser General Public License for more details. | 
|  | * | 
|  | *  You should have received a copy of the GNU Lesser General Public | 
|  | *  License along with this library; if not, write to the Free Software | 
|  | *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | 
|  | * | 
|  | */ | 
|  |  | 
|  | #ifdef HAVE_CONFIG_H | 
|  | #include <config.h> | 
|  | #endif | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <ctype.h> | 
|  | #include <unistd.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <errno.h> | 
|  | #include <endian.h> | 
|  | #include <stdbool.h> | 
|  | #include <sys/socket.h> | 
|  |  | 
|  | #include "lib/bluetooth.h" | 
|  | #include "lib/hci.h" | 
|  |  | 
|  | #include "src/shared/util.h" | 
|  | #include "src/shared/crypto.h" | 
|  | #include "src/shared/ecc.h" | 
|  | #include "monitor/bt.h" | 
|  | #include "bthost.h" | 
|  |  | 
|  | #define SMP_CID		0x0006 | 
|  | #define SMP_BREDR_CID	0x0007 | 
|  |  | 
|  | #define L2CAP_FC_SMP_BREDR	0x80 | 
|  |  | 
|  | #define SMP_PASSKEY_ENTRY_FAILED	0x01 | 
|  | #define SMP_OOB_NOT_AVAIL		0x02 | 
|  | #define SMP_AUTH_REQUIREMENTS		0x03 | 
|  | #define SMP_CONFIRM_FAILED		0x04 | 
|  | #define SMP_PAIRING_NOTSUPP		0x05 | 
|  | #define SMP_ENC_KEY_SIZE		0x06 | 
|  | #define SMP_CMD_NOTSUPP			0x07 | 
|  | #define SMP_UNSPECIFIED			0x08 | 
|  | #define SMP_REPEATED_ATTEMPTS		0x09 | 
|  | #define SMP_INVALID_PARAMS		0x0a | 
|  | #define SMP_DHKEY_CHECK_FAILED		0x0b | 
|  | #define SMP_NUMERIC_COMP_FAILED		0x0c | 
|  | #define SMP_BREDR_PAIRING_IN_PROGRESS	0x0d | 
|  |  | 
|  | #define DIST_ENC_KEY	0x01 | 
|  | #define DIST_ID_KEY	0x02 | 
|  | #define DIST_SIGN	0x04 | 
|  | #define DIST_LINK_KEY	0x08 | 
|  |  | 
|  | #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; | 
|  | struct bt_crypto *crypto; | 
|  | }; | 
|  |  | 
|  | struct smp_conn { | 
|  | struct smp *smp; | 
|  | uint16_t handle; | 
|  | uint8_t addr_type; | 
|  | bool out; | 
|  | bool sc; | 
|  | bool initiator; | 
|  | uint8_t method; | 
|  | uint8_t local_key_dist; | 
|  | uint8_t remote_key_dist; | 
|  | uint8_t ia[6]; | 
|  | uint8_t ia_type; | 
|  | uint8_t ra[6]; | 
|  | uint8_t ra_type; | 
|  | uint8_t tk[16]; | 
|  | uint8_t prnd[16]; | 
|  | uint8_t rrnd[16]; | 
|  | uint8_t pcnf[16]; | 
|  | uint8_t preq[7]; | 
|  | uint8_t prsp[7]; | 
|  | uint8_t ltk[16]; | 
|  |  | 
|  | uint8_t local_sk[32]; | 
|  | uint8_t local_pk[64]; | 
|  | uint8_t remote_pk[64]; | 
|  | uint8_t dhkey[32]; | 
|  | uint8_t mackey[16]; | 
|  |  | 
|  | uint8_t passkey_notify; | 
|  | uint8_t passkey_round; | 
|  | }; | 
|  |  | 
|  | 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 uint8_t key_dist(struct bthost *host) | 
|  | { | 
|  | if (!bthost_bredr_capable(host)) | 
|  | return (DIST_ENC_KEY | DIST_ID_KEY | DIST_SIGN); | 
|  |  | 
|  | return (DIST_ENC_KEY | DIST_ID_KEY | DIST_SIGN | DIST_LINK_KEY); | 
|  | } | 
|  |  | 
|  | static void smp_send(struct smp_conn *conn, uint8_t smp_cmd, const void *data, | 
|  | uint8_t len) | 
|  | { | 
|  | struct iovec iov[2]; | 
|  | uint16_t cid; | 
|  |  | 
|  | iov[0].iov_base = &smp_cmd; | 
|  | iov[0].iov_len = 1; | 
|  |  | 
|  | iov[1].iov_base = (void *) data; | 
|  | iov[1].iov_len = len; | 
|  |  | 
|  | if (conn->addr_type == BDADDR_BREDR) | 
|  | cid = SMP_BREDR_CID; | 
|  | else | 
|  | cid = SMP_CID; | 
|  |  | 
|  | bthost_send_cid_v(conn->smp->bthost, conn->handle, cid, iov, 2); | 
|  | } | 
|  |  | 
|  | static bool send_public_key(struct smp_conn *conn) | 
|  | { | 
|  | if (!ecc_make_key(conn->local_pk, conn->local_sk)) | 
|  | return false; | 
|  |  | 
|  | smp_send(conn, BT_L2CAP_SMP_PUBLIC_KEY, conn->local_pk, 64); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static void sc_dhkey_check(struct smp_conn *conn) | 
|  | { | 
|  | uint8_t io_cap[3], r[16], a[7], b[7], *local_addr, *remote_addr; | 
|  | struct bt_l2cap_smp_dhkey_check check; | 
|  |  | 
|  | memcpy(a, conn->ia, 6); | 
|  | memcpy(b, conn->ra, 6); | 
|  | a[6] = conn->ia_type; | 
|  | b[6] = conn->ra_type; | 
|  |  | 
|  | if (conn->out) { | 
|  | local_addr = a; | 
|  | remote_addr = b; | 
|  | memcpy(io_cap, &conn->preq[1], 3); | 
|  | } else { | 
|  | local_addr = b; | 
|  | remote_addr = a; | 
|  | memcpy(io_cap, &conn->prsp[1], 3); | 
|  | } | 
|  |  | 
|  | memset(r, 0, sizeof(r)); | 
|  |  | 
|  | bt_crypto_f6(conn->smp->crypto, conn->mackey, conn->prnd, conn->rrnd, | 
|  | r, io_cap, local_addr, remote_addr, check.e); | 
|  |  | 
|  | smp_send(conn, BT_L2CAP_SMP_DHKEY_CHECK, &check, sizeof(check)); | 
|  | } | 
|  |  | 
|  | static void sc_mackey_and_ltk(struct smp_conn *conn) | 
|  | { | 
|  | uint8_t *na, *nb, a[7], b[7]; | 
|  |  | 
|  | if (conn->out) { | 
|  | na = conn->prnd; | 
|  | nb = conn->rrnd; | 
|  | } else { | 
|  | na = conn->rrnd; | 
|  | nb = conn->prnd; | 
|  | } | 
|  |  | 
|  | memcpy(a, conn->ia, 6); | 
|  | memcpy(b, conn->ra, 6); | 
|  | a[6] = conn->ia_type; | 
|  | b[6] = conn->ra_type; | 
|  |  | 
|  | bt_crypto_f5(conn->smp->crypto, conn->dhkey, na, nb, a, b, | 
|  | conn->mackey, conn->ltk); | 
|  | } | 
|  |  | 
|  | static uint8_t sc_passkey_send_confirm(struct smp_conn *conn) | 
|  | { | 
|  | struct bt_l2cap_smp_pairing_confirm cfm; | 
|  | uint8_t r; | 
|  |  | 
|  | r = ((conn->passkey_notify >> conn->passkey_round) & 0x01); | 
|  | r |= 0x80; | 
|  |  | 
|  | if (!bt_crypto_f4(conn->smp->crypto, conn->local_pk, conn->remote_pk, | 
|  | conn->prnd, r, cfm.value)) | 
|  | return SMP_UNSPECIFIED; | 
|  |  | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_CONFIRM, &cfm, sizeof(cfm)); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static uint8_t sc_passkey_round(struct smp_conn *conn, uint8_t smp_op) | 
|  | { | 
|  | uint8_t cfm[16], r; | 
|  |  | 
|  | /* Ignore the PDU if we've already done 20 rounds (0 - 19) */ | 
|  | if (conn->passkey_round >= 20) | 
|  | return 0; | 
|  |  | 
|  | switch (smp_op) { | 
|  | case BT_L2CAP_SMP_PAIRING_RANDOM: | 
|  | r = ((conn->passkey_notify >> conn->passkey_round) & 0x01); | 
|  | r |= 0x80; | 
|  |  | 
|  | if (!bt_crypto_f4(conn->smp->crypto, conn->remote_pk, | 
|  | conn->local_pk, conn->rrnd, r, cfm)) | 
|  | return SMP_UNSPECIFIED; | 
|  |  | 
|  | if (memcmp(conn->pcnf, cfm, 16)) | 
|  | return SMP_CONFIRM_FAILED; | 
|  |  | 
|  | conn->passkey_round++; | 
|  |  | 
|  | if (conn->passkey_round == 20) { | 
|  | /* Generate MacKey and LTK */ | 
|  | sc_mackey_and_ltk(conn); | 
|  | } | 
|  |  | 
|  | /* The round is only complete when the initiator | 
|  | * receives pairing random. | 
|  | */ | 
|  | if (!conn->out) { | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, | 
|  | conn->prnd, sizeof(conn->prnd)); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Start the next round */ | 
|  | if (conn->passkey_round != 20) | 
|  | return sc_passkey_round(conn, 0); | 
|  |  | 
|  | /* Passkey rounds are complete - start DHKey Check */ | 
|  | sc_dhkey_check(conn); | 
|  |  | 
|  | break; | 
|  |  | 
|  | case BT_L2CAP_SMP_PAIRING_CONFIRM: | 
|  | if (conn->out) { | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, | 
|  | conn->prnd, sizeof(conn->prnd)); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return sc_passkey_send_confirm(conn); | 
|  |  | 
|  | case BT_L2CAP_SMP_PUBLIC_KEY: | 
|  | default: | 
|  | /* Initiating device starts the round */ | 
|  | if (!conn->out) | 
|  | return 0; | 
|  |  | 
|  | return sc_passkey_send_confirm(conn); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static bool verify_random(struct smp_conn *conn, const uint8_t rnd[16]) | 
|  | { | 
|  | uint8_t confirm[16]; | 
|  |  | 
|  | if (!bt_crypto_c1(conn->smp->crypto, conn->tk, conn->rrnd, conn->prsp, | 
|  | conn->preq, conn->ia_type, conn->ia, | 
|  | conn->ra_type, conn->ra, confirm)) | 
|  | return false; | 
|  |  | 
|  | if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) { | 
|  | printf("Confirmation values don't match\n"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (conn->out) { | 
|  | bt_crypto_s1(conn->smp->crypto, conn->tk, conn->rrnd, | 
|  | conn->prnd, conn->ltk); | 
|  | bthost_le_start_encrypt(conn->smp->bthost, conn->handle, | 
|  | conn->ltk); | 
|  | } else { | 
|  | bt_crypto_s1(conn->smp->crypto, conn->tk, conn->prnd, | 
|  | conn->rrnd, conn->ltk); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static void distribute_keys(struct smp_conn *conn) | 
|  | { | 
|  | uint8_t buf[16]; | 
|  |  | 
|  | if (conn->local_key_dist & DIST_ENC_KEY) { | 
|  | memset(buf, 0, sizeof(buf)); | 
|  | smp_send(conn, BT_L2CAP_SMP_ENCRYPT_INFO, buf, sizeof(buf)); | 
|  | smp_send(conn, BT_L2CAP_SMP_MASTER_IDENT, buf, 10); | 
|  | } | 
|  |  | 
|  | if (conn->local_key_dist & DIST_ID_KEY) { | 
|  | memset(buf, 0, sizeof(buf)); | 
|  | smp_send(conn, BT_L2CAP_SMP_IDENT_INFO, buf, sizeof(buf)); | 
|  |  | 
|  | memset(buf, 0, sizeof(buf)); | 
|  |  | 
|  | if (conn->out) { | 
|  | buf[0] = conn->ia_type; | 
|  | memcpy(&buf[1], conn->ia, 6); | 
|  | } else { | 
|  | buf[0] = conn->ra_type; | 
|  | memcpy(&buf[1], conn->ra, 6); | 
|  | } | 
|  |  | 
|  | smp_send(conn, BT_L2CAP_SMP_IDENT_ADDR_INFO, buf, 7); | 
|  | } | 
|  |  | 
|  | if (conn->local_key_dist & DIST_SIGN) { | 
|  | memset(buf, 0, sizeof(buf)); | 
|  | smp_send(conn, BT_L2CAP_SMP_SIGNING_INFO, buf, sizeof(buf)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void pairing_req(struct smp_conn *conn, const void *data, uint16_t len) | 
|  | { | 
|  | struct bthost *bthost = conn->smp->bthost; | 
|  | struct bt_l2cap_smp_pairing_response rsp; | 
|  |  | 
|  | memcpy(conn->preq, data, sizeof(conn->preq)); | 
|  |  | 
|  | if (conn->addr_type == BDADDR_BREDR) { | 
|  | rsp.io_capa	= 0x00; | 
|  | rsp.oob_data	= 0x00; | 
|  | rsp.auth_req	= 0x00; | 
|  | } else { | 
|  | rsp.io_capa	= bthost_get_io_capability(bthost); | 
|  | rsp.oob_data	= 0x00; | 
|  | rsp.auth_req	= bthost_get_auth_req(bthost); | 
|  | } | 
|  |  | 
|  | rsp.max_key_size	= 0x10; | 
|  | rsp.init_key_dist	= conn->preq[5] & key_dist(bthost); | 
|  | rsp.resp_key_dist	= conn->preq[6] & key_dist(bthost); | 
|  |  | 
|  | conn->prsp[0] = BT_L2CAP_SMP_PAIRING_RESPONSE; | 
|  | memcpy(&conn->prsp[1], &rsp, sizeof(rsp)); | 
|  |  | 
|  | conn->local_key_dist	= rsp.resp_key_dist; | 
|  | conn->remote_key_dist	= rsp.init_key_dist; | 
|  |  | 
|  | if (((conn->prsp[3] & 0x08) && (conn->preq[3] & 0x08)) || | 
|  | conn->addr_type == BDADDR_BREDR) { | 
|  | conn->sc = true; | 
|  | conn->local_key_dist &= ~SC_NO_DIST; | 
|  | conn->remote_key_dist &= ~SC_NO_DIST; | 
|  | } | 
|  |  | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_RESPONSE, &rsp, sizeof(rsp)); | 
|  |  | 
|  | if (conn->addr_type == BDADDR_BREDR) | 
|  | distribute_keys(conn); | 
|  | } | 
|  |  | 
|  | static void pairing_rsp(struct smp_conn *conn, const void *data, uint16_t len) | 
|  | { | 
|  | struct smp *smp = conn->smp; | 
|  | uint8_t cfm[16]; | 
|  |  | 
|  | memcpy(conn->prsp, data, sizeof(conn->prsp)); | 
|  |  | 
|  | conn->local_key_dist = conn->prsp[5]; | 
|  | conn->remote_key_dist = conn->prsp[6]; | 
|  |  | 
|  | if (conn->addr_type == BDADDR_BREDR) { | 
|  | conn->local_key_dist &= ~SC_NO_DIST; | 
|  | conn->remote_key_dist &= ~SC_NO_DIST; | 
|  | distribute_keys(conn); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (((conn->prsp[3] & 0x08) && (conn->preq[3] & 0x08)) || | 
|  | conn->addr_type == BDADDR_BREDR) { | 
|  | conn->sc = true; | 
|  | conn->local_key_dist &= ~SC_NO_DIST; | 
|  | conn->remote_key_dist &= ~SC_NO_DIST; | 
|  | if (conn->addr_type == BDADDR_BREDR) | 
|  | distribute_keys(conn); | 
|  | else | 
|  | send_public_key(conn); | 
|  | return; | 
|  | } | 
|  |  | 
|  | bt_crypto_c1(smp->crypto, conn->tk, conn->prnd, conn->prsp, | 
|  | conn->preq, conn->ia_type, conn->ia, | 
|  | conn->ra_type, conn->ra, cfm); | 
|  |  | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_CONFIRM, cfm, sizeof(cfm)); | 
|  | } | 
|  | static void sc_check_confirm(struct smp_conn *conn) | 
|  | { | 
|  | if (conn->method == REQ_PASSKEY || conn->method == DSP_PASSKEY) { | 
|  | sc_passkey_round(conn, BT_L2CAP_SMP_PAIRING_CONFIRM); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (conn->out) | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, conn->prnd, | 
|  | sizeof(conn->prnd)); | 
|  | } | 
|  |  | 
|  | static void pairing_cfm(struct smp_conn *conn, const void *data, uint16_t len) | 
|  | { | 
|  | uint8_t rsp[16]; | 
|  |  | 
|  | memcpy(conn->pcnf, data + 1, 16); | 
|  |  | 
|  | if (conn->sc) { | 
|  | sc_check_confirm(conn); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (conn->out) { | 
|  | memset(rsp, 0, sizeof(rsp)); | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, rsp, sizeof(rsp)); | 
|  | } else { | 
|  | bt_crypto_c1(conn->smp->crypto, conn->tk, conn->prnd, | 
|  | conn->prsp, conn->preq, conn->ia_type, | 
|  | conn->ia, conn->ra_type, conn->ra, rsp); | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_CONFIRM, rsp, sizeof(rsp)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static uint8_t sc_random(struct smp_conn *conn) | 
|  | { | 
|  | /* Passkey entry has special treatment */ | 
|  | if (conn->method == REQ_PASSKEY || conn->method == DSP_PASSKEY) | 
|  | return sc_passkey_round(conn, BT_L2CAP_SMP_PAIRING_RANDOM); | 
|  |  | 
|  | if (conn->out) { | 
|  | uint8_t cfm[16]; | 
|  |  | 
|  | bt_crypto_f4(conn->smp->crypto, conn->remote_pk, | 
|  | conn->local_pk, conn->rrnd, 0, cfm); | 
|  |  | 
|  | if (memcmp(conn->pcnf, cfm, 16)) | 
|  | return 0x04; /* Confirm Value Failed */ | 
|  | } else { | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, conn->prnd, 16); | 
|  | } | 
|  |  | 
|  | sc_mackey_and_ltk(conn); | 
|  |  | 
|  | if (conn->out) | 
|  | sc_dhkey_check(conn); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void pairing_rnd(struct smp_conn *conn, const void *data, uint16_t len) | 
|  | { | 
|  | uint8_t rsp[16]; | 
|  |  | 
|  | memcpy(conn->rrnd, data + 1, 16); | 
|  |  | 
|  | if (conn->sc) { | 
|  | uint8_t reason = sc_random(conn); | 
|  | if (reason) | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_FAILED, &reason, | 
|  | sizeof(reason)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!verify_random(conn, data + 1)) | 
|  | return; | 
|  |  | 
|  | if (conn->out) | 
|  | return; | 
|  |  | 
|  | memset(rsp, 0, sizeof(rsp)); | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, rsp, sizeof(rsp)); | 
|  | } | 
|  |  | 
|  | static void encrypt_info(struct smp_conn *conn, const void *data, uint16_t len) | 
|  | { | 
|  | } | 
|  |  | 
|  | static void master_ident(struct smp_conn *conn, const void *data, uint16_t len) | 
|  | { | 
|  | conn->remote_key_dist &= ~DIST_ENC_KEY; | 
|  |  | 
|  | if (conn->out && !conn->remote_key_dist) | 
|  | distribute_keys(conn); | 
|  | } | 
|  |  | 
|  | static void ident_addr_info(struct smp_conn *conn, const void *data, | 
|  | uint16_t len) | 
|  | { | 
|  | } | 
|  |  | 
|  | static void ident_info(struct smp_conn *conn, const void *data, uint16_t len) | 
|  | { | 
|  | conn->remote_key_dist &= ~DIST_ID_KEY; | 
|  |  | 
|  | if (conn->out && !conn->remote_key_dist) | 
|  | distribute_keys(conn); | 
|  | } | 
|  |  | 
|  | static void signing_info(struct smp_conn *conn, const void *data, uint16_t len) | 
|  | { | 
|  | conn->remote_key_dist &= ~DIST_SIGN; | 
|  |  | 
|  | if (conn->out && !conn->remote_key_dist) | 
|  | distribute_keys(conn); | 
|  | } | 
|  |  | 
|  | static void public_key(struct smp_conn *conn, const void *data, uint16_t len) | 
|  | { | 
|  | struct smp *smp = conn->smp; | 
|  | uint8_t buf[16]; | 
|  |  | 
|  | memcpy(conn->remote_pk, data + 1, 64); | 
|  |  | 
|  | if (!conn->out) { | 
|  | if (!send_public_key(conn)) | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!ecdh_shared_secret(conn->remote_pk, conn->local_sk, conn->dhkey)) | 
|  | return; | 
|  |  | 
|  | conn->method = sc_select_method(conn); | 
|  |  | 
|  | if (conn->method == DSP_PASSKEY || conn->method == REQ_PASSKEY) { | 
|  | sc_passkey_round(conn, BT_L2CAP_SMP_PUBLIC_KEY); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (conn->out) | 
|  | return; | 
|  |  | 
|  | if (!bt_crypto_f4(smp->crypto, conn->local_pk, conn->remote_pk, | 
|  | conn->prnd, 0, buf)) | 
|  | return; | 
|  |  | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_CONFIRM, buf, sizeof(buf)); | 
|  | } | 
|  |  | 
|  | static void dhkey_check(struct smp_conn *conn, const void *data, uint16_t len) | 
|  | { | 
|  | const struct bt_l2cap_smp_dhkey_check *cmd = data + 1; | 
|  | uint8_t a[7], b[7], *local_addr, *remote_addr; | 
|  | uint8_t io_cap[3], r[16], e[16]; | 
|  |  | 
|  | memcpy(a, &conn->ia, 6); | 
|  | memcpy(b, &conn->ra, 6); | 
|  | a[6] = conn->ia_type; | 
|  | b[6] = conn->ra_type; | 
|  |  | 
|  | if (conn->out) { | 
|  | local_addr = a; | 
|  | remote_addr = b; | 
|  | memcpy(io_cap, &conn->prsp[1], 3); | 
|  | } else { | 
|  | local_addr = b; | 
|  | remote_addr = a; | 
|  | memcpy(io_cap, &conn->preq[1], 3); | 
|  | } | 
|  |  | 
|  | memset(r, 0, sizeof(r)); | 
|  |  | 
|  | if (conn->method == REQ_PASSKEY || conn->method == DSP_PASSKEY) | 
|  | put_le32(conn->passkey_notify, r); | 
|  |  | 
|  | if (!bt_crypto_f6(conn->smp->crypto, conn->mackey, conn->rrnd, | 
|  | conn->prnd, r, io_cap, remote_addr, local_addr, e)) | 
|  | return; | 
|  |  | 
|  | if (memcmp(cmd->e, e, 16)) { | 
|  | uint8_t reason = 0x0b; /* DHKey Check Failed */ | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_FAILED, &reason, | 
|  | sizeof(reason)); | 
|  | } | 
|  |  | 
|  | if (conn->out) | 
|  | bthost_le_start_encrypt(conn->smp->bthost, conn->handle, | 
|  | conn->ltk); | 
|  | else | 
|  | sc_dhkey_check(conn); | 
|  | } | 
|  |  | 
|  | void smp_pair(void *conn_data, uint8_t io_cap, uint8_t auth_req) | 
|  | { | 
|  | struct smp_conn *conn = conn_data; | 
|  | struct bt_l2cap_smp_pairing_request req; | 
|  |  | 
|  | req.io_capa		= io_cap; | 
|  | req.oob_data		= 0x00; | 
|  | req.auth_req		= auth_req; | 
|  | req.max_key_size	= 0x10; | 
|  | req.init_key_dist	= key_dist(conn->smp->bthost); | 
|  | req.resp_key_dist	= key_dist(conn->smp->bthost); | 
|  |  | 
|  | conn->preq[0] = BT_L2CAP_SMP_PAIRING_REQUEST; | 
|  | memcpy(&conn->preq[1], &req, sizeof(req)); | 
|  |  | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_REQUEST, &req, sizeof(req)); | 
|  | } | 
|  |  | 
|  | void smp_data(void *conn_data, const void *data, uint16_t len) | 
|  | { | 
|  | struct smp_conn *conn = conn_data; | 
|  | uint8_t opcode; | 
|  |  | 
|  | if (len < 1) { | 
|  | printf("Received too small SMP PDU\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (conn->addr_type == BDADDR_BREDR) { | 
|  | printf("Received BR/EDR SMP data on LE link\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | opcode = *((const uint8_t *) data); | 
|  |  | 
|  | switch (opcode) { | 
|  | case BT_L2CAP_SMP_PAIRING_REQUEST: | 
|  | pairing_req(conn, data, len); | 
|  | break; | 
|  | case BT_L2CAP_SMP_PAIRING_RESPONSE: | 
|  | pairing_rsp(conn, data, len); | 
|  | break; | 
|  | case BT_L2CAP_SMP_PAIRING_CONFIRM: | 
|  | pairing_cfm(conn, data, len); | 
|  | break; | 
|  | case BT_L2CAP_SMP_PAIRING_RANDOM: | 
|  | pairing_rnd(conn, data, len); | 
|  | break; | 
|  | case BT_L2CAP_SMP_ENCRYPT_INFO: | 
|  | encrypt_info(conn, data, len); | 
|  | break; | 
|  | case BT_L2CAP_SMP_MASTER_IDENT: | 
|  | master_ident(conn, data, len); | 
|  | break; | 
|  | case BT_L2CAP_SMP_IDENT_ADDR_INFO: | 
|  | ident_addr_info(conn, data, len); | 
|  | break; | 
|  | case BT_L2CAP_SMP_IDENT_INFO: | 
|  | ident_info(conn, data, len); | 
|  | break; | 
|  | case BT_L2CAP_SMP_SIGNING_INFO: | 
|  | signing_info(conn, data, len); | 
|  | break; | 
|  | case BT_L2CAP_SMP_PUBLIC_KEY: | 
|  | public_key(conn, data, len); | 
|  | break; | 
|  | case BT_L2CAP_SMP_DHKEY_CHECK: | 
|  | dhkey_check(conn, data, len); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void smp_bredr_data(void *conn_data, const void *data, uint16_t len) | 
|  | { | 
|  | struct smp_conn *conn = conn_data; | 
|  | uint8_t opcode; | 
|  |  | 
|  | if (len < 1) { | 
|  | printf("Received too small SMP PDU\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (conn->addr_type != BDADDR_BREDR) { | 
|  | printf("Received LE SMP data on BR/EDR link\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | opcode = *((const uint8_t *) data); | 
|  |  | 
|  | switch (opcode) { | 
|  | case BT_L2CAP_SMP_PAIRING_REQUEST: | 
|  | pairing_req(conn, data, len); | 
|  | break; | 
|  | case BT_L2CAP_SMP_PAIRING_RESPONSE: | 
|  | pairing_rsp(conn, data, len); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | int smp_get_ltk(void *smp_data, uint64_t rand, uint16_t ediv, uint8_t *ltk) | 
|  | { | 
|  | struct smp_conn *conn = smp_data; | 
|  | static const uint8_t no_ltk[16] = { 0 }; | 
|  |  | 
|  | if (!memcmp(conn->ltk, no_ltk, 16)) | 
|  | return -ENOENT; | 
|  |  | 
|  | memcpy(ltk, conn->ltk, 16); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void smp_conn_bredr(struct smp_conn *conn, uint8_t encrypt) | 
|  | { | 
|  | struct smp *smp = conn->smp; | 
|  | struct bt_l2cap_smp_pairing_request req; | 
|  | uint64_t fixed_chan; | 
|  |  | 
|  | if (encrypt != 0x02) | 
|  | return; | 
|  |  | 
|  | conn->sc = true; | 
|  |  | 
|  | if (!conn->out) | 
|  | return; | 
|  |  | 
|  | fixed_chan = bthost_conn_get_fixed_chan(smp->bthost, conn->handle); | 
|  | if (!(fixed_chan & L2CAP_FC_SMP_BREDR)) | 
|  | return; | 
|  |  | 
|  | memset(&req, 0, sizeof(req)); | 
|  | req.max_key_size = 0x10; | 
|  | req.init_key_dist = key_dist(smp->bthost); | 
|  | req.resp_key_dist = key_dist(smp->bthost); | 
|  |  | 
|  | smp_send(conn, BT_L2CAP_SMP_PAIRING_REQUEST, &req, sizeof(req)); | 
|  | } | 
|  |  | 
|  | void smp_conn_encrypted(void *conn_data, uint8_t encrypt) | 
|  | { | 
|  | struct smp_conn *conn = conn_data; | 
|  |  | 
|  | if (!encrypt) | 
|  | return; | 
|  |  | 
|  | if (conn->addr_type == BDADDR_BREDR) { | 
|  | smp_conn_bredr(conn, encrypt); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (conn->out && conn->remote_key_dist) | 
|  | return; | 
|  |  | 
|  | distribute_keys(conn); | 
|  | } | 
|  |  | 
|  | void *smp_conn_add(void *smp_data, uint16_t handle, const uint8_t *ia, | 
|  | const uint8_t *ra, uint8_t addr_type, bool conn_init) | 
|  | { | 
|  | struct smp *smp = smp_data; | 
|  | struct smp_conn *conn; | 
|  |  | 
|  | conn = malloc(sizeof(struct smp_conn)); | 
|  | if (!conn) | 
|  | return NULL; | 
|  |  | 
|  | memset(conn, 0, sizeof(*conn)); | 
|  |  | 
|  | conn->smp = smp; | 
|  | conn->handle = handle; | 
|  | conn->addr_type = addr_type; | 
|  | conn->out = conn_init; | 
|  |  | 
|  | conn->ia_type = LE_PUBLIC_ADDRESS; | 
|  | conn->ra_type = LE_PUBLIC_ADDRESS; | 
|  | memcpy(conn->ia, ia, 6); | 
|  | memcpy(conn->ra, ra, 6); | 
|  |  | 
|  | return conn; | 
|  | } | 
|  |  | 
|  | void smp_conn_del(void *conn_data) | 
|  | { | 
|  | struct smp_conn *conn = conn_data; | 
|  |  | 
|  | free(conn); | 
|  | } | 
|  |  | 
|  | void *smp_start(struct bthost *bthost) | 
|  | { | 
|  | struct smp *smp; | 
|  |  | 
|  | smp = malloc(sizeof(struct smp)); | 
|  | if (!smp) | 
|  | return NULL; | 
|  |  | 
|  | memset(smp, 0, sizeof(*smp)); | 
|  |  | 
|  | smp->crypto = bt_crypto_new(); | 
|  | if (!smp->crypto) { | 
|  | free(smp); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | smp->bthost = bthost; | 
|  |  | 
|  | return smp; | 
|  | } | 
|  |  | 
|  | void smp_stop(void *smp_data) | 
|  | { | 
|  | struct smp *smp = smp_data; | 
|  |  | 
|  | bt_crypto_unref(smp->crypto); | 
|  |  | 
|  | free(smp); | 
|  | } |