[PATCH BlueZ 10/30] mesh: Initiator side provisioning implimentation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



---
 mesh/prov-initiator.c | 651 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 651 insertions(+)
 create mode 100644 mesh/prov-initiator.c

diff --git a/mesh/prov-initiator.c b/mesh/prov-initiator.c
new file mode 100644
index 000000000..7ac618f4a
--- /dev/null
+++ b/mesh/prov-initiator.c
@@ -0,0 +1,651 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  Intel Corporation. All rights reserved.
+ *
+ *
+ *  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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <time.h>
+#include <ell/ell.h>
+
+#include "mesh/mesh-defs.h"
+#include "src/shared/ecc.h"
+
+#include "mesh/util.h"
+#include "mesh/net_keys.h"
+#include "mesh/crypto.h"
+#include "mesh/net.h"
+#include "mesh/error.h"
+#include "mesh/prov.h"
+#include "mesh/provision.h"
+#include "mesh/pb-adv.h"
+#include "mesh/mesh.h"
+#include "mesh/agent.h"
+
+/* Quick size sanity check */
+static const uint16_t expected_pdu_size[] = {
+	2,	/* PROV_INVITE */
+	12,	/* PROV_CAPS */
+	6,	/* PROV_START */
+	65,	/* PROV_PUB_KEY */
+	1,	/* PROV_INP_CMPLT */
+	17,	/* PROV_CONFIRM */
+	17,	/* PROV_RANDOM */
+	34,	/* PROV_DATA */
+	1,	/* PROV_COMPLETE */
+	2,	/* PROV_FAILED */
+};
+
+#define BEACON_TYPE_UNPROVISIONED		0x00
+
+static const uint8_t pkt_filter = MESH_AD_TYPE_PROVISION;
+
+enum int_state {
+	INT_PROV_IDLE = 0,
+	INT_PROV_INVITE_SENT,
+	INT_PROV_INVITE_ACKED,
+	INT_PROV_START_SENT,
+	INT_PROV_START_ACKED,
+	INT_PROV_KEY_SENT,
+	INT_PROV_KEY_ACKED,
+	INT_PROV_CONF_SENT,
+	INT_PROV_CONF_ACKED,
+	INT_PROV_RAND_SENT,
+	INT_PROV_RAND_ACKED,
+	INT_PROV_DATA_SENT,
+	INT_PROV_DATA_ACKED,
+};
+
+#define MAT_REMOTE_PUBLIC	0x01
+#define MAT_LOCAL_PRIVATE	0x02
+#define MAT_RAND_AUTH		0x04
+#define MAT_SECRET	(MAT_REMOTE_PUBLIC | MAT_LOCAL_PRIVATE)
+
+struct mesh_prov_initiator {
+	mesh_prov_initiator_complete_func_t cmplt;
+	prov_trans_tx_t trans_tx;
+	void *agent;
+	void *caller_data;
+	void *trans_data;
+	struct l_timeout *timeout;
+	uint32_t to_secs;
+	enum int_state	state;
+	enum trans_type transport;
+	uint8_t material;
+	uint8_t expected;
+	int8_t previous;
+	struct conf_input conf_inputs;
+	uint8_t calc_key[16];
+	uint8_t salt[16];
+	uint8_t confirm[16];
+	uint8_t s_key[16];
+	uint8_t s_nonce[13];
+	uint8_t private_key[32];
+	uint8_t secret[32];
+	uint8_t rand_auth_workspace[48];
+};
+
+static struct mesh_prov_initiator *prov = NULL;
+
+static void initiator_free()
+{
+
+	if (prov) {
+		l_timeout_remove(prov->timeout);
+	}
+
+	mesh_send_cancel(&pkt_filter, sizeof(pkt_filter));
+
+	l_free(prov);
+	prov = NULL;
+}
+
+static void int_prov_close(void *user_data, uint8_t reason)
+{
+	/* TODO: Handle Close */
+}
+
+static void int_prov_open(void *user_data, prov_trans_tx_t trans_tx,
+				void *trans_data, uint8_t transport)
+{
+	struct mesh_prov_initiator *rx_prov = user_data;
+	uint8_t invite[] = { PROV_INVITE, 30 };
+
+	/* Only one provisioning session may be open at a time */
+	if (rx_prov != prov)
+		return;
+
+	/* Only one provisioning session may be open at a time */
+	if (prov->trans_tx && prov->trans_tx != trans_tx &&
+					prov->transport != transport)
+		return;
+
+	/* We only care here if transport does *not* match */
+	if (transport != prov->transport)
+		return;
+
+	/* Always use an ephemeral key when Initiator */
+	ecc_make_key(prov->conf_inputs.prv_pub_key, prov->private_key);
+	prov->material |= MAT_LOCAL_PRIVATE;
+
+	prov->trans_tx = trans_tx;
+	prov->trans_data = trans_data;
+	prov->state = INT_PROV_INVITE_SENT;
+	prov->expected = PROV_CAPS;
+
+	prov->conf_inputs.invite.attention = invite[1];
+	prov->trans_tx(prov->trans_data, invite, sizeof(invite));
+	return;
+}
+
+static void swap_u256_bytes(uint8_t *u256)
+{
+	int i;
+
+	/* End-to-End byte reflection of 32 octet buffer */
+	for (i = 0; i < 16; i++) {
+		u256[i] ^= u256[31 - i];
+		u256[31 - i] ^= u256[i];
+		u256[i] ^= u256[31 - i];
+	}
+}
+
+static void prov_calc_secret(const uint8_t *pub, const uint8_t *priv,
+							uint8_t *secret)
+{
+	uint8_t tmp[64];
+
+	/* Convert to ECC byte order */
+	memcpy(tmp, pub, 64);
+	swap_u256_bytes(tmp);
+	swap_u256_bytes(tmp + 32);
+
+	ecdh_shared_secret(tmp, priv, secret);
+
+	/* Convert to Mesh byte order */
+	swap_u256_bytes(secret);
+}
+
+static void int_credentials(struct mesh_prov_initiator *prov)
+{
+	prov_calc_secret(prov->conf_inputs.dev_pub_key,
+			prov->private_key, prov->secret);
+
+	mesh_crypto_s1(&prov->conf_inputs,
+			sizeof(prov->conf_inputs), prov->salt);
+
+	mesh_crypto_prov_conf_key(prov->secret, prov->salt,
+			prov->calc_key);
+
+	l_getrandom(prov->rand_auth_workspace, 16);
+
+	print_packet("PublicKeyProv", prov->conf_inputs.prv_pub_key, 64);
+	print_packet("PublicKeyDev", prov->conf_inputs.dev_pub_key, 64);
+	print_packet("PrivateKeyLocal", prov->private_key, 32);
+	print_packet("ConfirmationInputs", &prov->conf_inputs,
+						sizeof(prov->conf_inputs));
+	print_packet("ECDHSecret", prov->secret, 32);
+	print_packet("LocalRandom", prov->rand_auth_workspace, 16);
+	print_packet("ConfirmationSalt", prov->salt, 16);
+	print_packet("ConfirmationKey", prov->calc_key, 16);
+}
+
+static uint8_t u16_high_bit(uint16_t mask)
+{
+	uint8_t cnt = 0;
+
+	if (!mask)
+		return 0xff;
+
+	while (mask & 0xfffe) {
+		cnt++;
+		mask >>= 1;
+	}
+
+	return cnt;
+}
+
+static uint32_t digit_mod(uint8_t power)
+{
+	uint32_t ret = 1;
+
+	while (power--)
+		ret *= 10;
+
+	return ret;
+}
+
+static void calc_local_material(const uint8_t *random)
+{
+	/* Calculate SessionKey while the data is fresh */
+	mesh_crypto_prov_prov_salt(prov->salt,
+			prov->rand_auth_workspace, random,
+			prov->salt);
+	mesh_crypto_session_key(prov->secret, prov->salt,
+			prov->s_key);
+	mesh_crypto_nonce(prov->secret, prov->salt, prov->s_nonce);
+
+	print_packet("SessionKey", prov->s_key, sizeof(prov->s_key));
+	print_packet("Nonce", prov->s_nonce, sizeof(prov->s_nonce));
+	print_packet("RandomDevice", prov->rand_auth_workspace, 16);
+}
+
+static void number_cb(void *user_data, mesh_error_t err, uint32_t number)
+{
+	struct mesh_prov_initiator *rx_prov = user_data;
+	uint8_t out[2];
+
+	if (prov != rx_prov)
+		return;
+
+	if (err) {
+		out[0] = PROV_FAILED;
+		out[1] = PROV_ERR_UNEXPECTED_ERR;
+		prov->trans_tx(prov->trans_data, out, 2);
+		return;
+	}
+
+	/* Save two copies, to generate two confirmation values */
+	l_put_be32(number, prov->rand_auth_workspace + 28);
+	l_put_be32(number, prov->rand_auth_workspace + 44);
+	prov->material |= MAT_RAND_AUTH;
+}
+
+static void static_cb(void *user_data, mesh_error_t err,
+						uint8_t *key, uint32_t len)
+{
+	struct mesh_prov_initiator *rx_prov = user_data;
+	uint8_t out[2];
+
+	if (prov != rx_prov)
+		return;
+
+	if (err || !key || len != 16) {
+		out[0] = PROV_FAILED;
+		out[1] = PROV_ERR_UNEXPECTED_ERR;
+		prov->trans_tx(prov->trans_data, out, 2);
+		return;
+	}
+
+	memcpy(prov->rand_auth_workspace + 16, key, 16);
+	memcpy(prov->rand_auth_workspace + 32, key, 16);
+	prov->material |= MAT_RAND_AUTH;
+}
+
+static void pub_key_cb(void *user_data, mesh_error_t err,
+						uint8_t *key, uint32_t len)
+{
+	struct mesh_prov_initiator *rx_prov = user_data;
+	uint8_t out[2];
+
+	if (prov != rx_prov)
+		return;
+
+	if (err || !key || len != 64) {
+		out[0] = PROV_FAILED;
+		out[1] = PROV_ERR_UNEXPECTED_ERR;
+		prov->trans_tx(prov->trans_data, out, 2);
+		return;
+	}
+
+		memcpy(prov->conf_inputs.dev_pub_key, key, 64);
+		prov->material |= MAT_REMOTE_PUBLIC;
+
+		if ((prov->material & MAT_SECRET) == MAT_SECRET)
+			int_credentials(prov);
+}
+
+static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
+{
+	struct mesh_prov_initiator *rx_prov = user_data;
+	uint8_t *out;
+	uint8_t type = *data++;
+	uint8_t fail_code[2];
+	uint32_t oob_key;
+	uint64_t mic;
+
+	if (rx_prov != prov || !prov->trans_tx)
+		return;
+
+	l_debug("Provisioning packet received type: %2.2x (%u octets)",
+								type, len);
+
+	if (type == prov->previous) {
+		l_error("Ignore repeated %2.2x packet", type);
+		return;
+	} else if (type > prov->expected || type < prov->previous) {
+		l_error("Expected %2.2x, Got:%2.2x", prov->expected, type);
+		fail_code[1] = PROV_ERR_UNEXPECTED_PDU;
+		goto failure;
+	}
+
+	if (type >= L_ARRAY_SIZE(expected_pdu_size) ||
+					len != expected_pdu_size[type]) {
+		l_error("Expected PDU size %d, Got %d (type: %2.2x)",
+			len, expected_pdu_size[type], type);
+		fail_code[1] = PROV_ERR_INVALID_FORMAT;
+		goto failure;
+	}
+
+	switch (type) {
+	case PROV_CAPS: /* Capabilities */
+		prov->state = INT_PROV_INVITE_ACKED;
+		memcpy(&prov->conf_inputs.caps, data,
+					sizeof(prov->conf_inputs.caps));
+
+		l_debug("Got Num Ele %d", data[0]);
+		l_debug("Got alg %4.4x", l_get_be16(data + 1));
+		l_debug("Got pub_type %d", data[3]);
+		l_debug("Got static_type %d", data[4]);
+		l_debug("Got output_size %d", data[5]);
+		l_debug("Got output_action %d", l_get_be16(data + 6));
+		l_debug("Got input_size %d", data[8]);
+		l_debug("Got input_action %d", l_get_be16(data + 9));
+
+		if (!(l_get_be16(data + 1) & 0x0001)) {
+			l_error("Unsupported Algorithm");
+			fail_code[1] = PROV_ERR_INVALID_FORMAT;
+			goto failure;
+		}
+
+		/* If Public Key available Out of Band, use it */
+		if (prov->conf_inputs.caps.pub_type) {
+			prov->conf_inputs.start.pub_key = 0x01;
+			prov->expected = PROV_CONFIRM;
+			/* Prompt Agent for remote Public Key */
+			mesh_agent_request_public_key(prov->agent,
+							pub_key_cb, prov);
+
+			/* Nothing else for us to do now */
+		} else
+			prov->expected = PROV_PUB_KEY;
+
+		/* Parse OOB Options, prefer static, then out, then in */
+		if (prov->conf_inputs.caps.static_type) {
+
+			prov->conf_inputs.start.auth_method = 0x01;
+
+		} else if (prov->conf_inputs.caps.output_size &&
+				prov->conf_inputs.caps.output_action) {
+
+			prov->conf_inputs.start.auth_method = 0x02;
+			prov->conf_inputs.start.auth_action =
+					u16_high_bit(l_get_be16(data + 6));
+			prov->conf_inputs.start.auth_size =
+						(data[5] > 8 ? 8 : data[5]);
+
+		} else if (prov->conf_inputs.caps.input_size &&
+				prov->conf_inputs.caps.input_action) {
+
+			prov->conf_inputs.start.auth_method = 0x03;
+			prov->conf_inputs.start.auth_action =
+					u16_high_bit(l_get_be16(data + 9));
+			prov->conf_inputs.start.auth_size =
+						(data[8] > 8 ? 8 : data[8]);
+
+		}
+
+		out = l_malloc(1 + sizeof(prov->conf_inputs.start));
+		out[0] = PROV_START;
+		memcpy(out + 1, &prov->conf_inputs.start,
+					sizeof(prov->conf_inputs.start));
+
+		prov->state = INT_PROV_START_SENT;
+		prov->trans_tx(prov->trans_data, out,
+					sizeof(prov->conf_inputs.start) + 1);
+		l_free(out);
+		break;
+
+	case PROV_PUB_KEY: /* Public Key */
+		/* If we expected Pub Key Out-Of-Band, then fail */
+		if (prov->conf_inputs.start.pub_key) {
+			fail_code[1] = PROV_ERR_INVALID_PDU;
+			goto failure;
+		}
+
+		memcpy(prov->conf_inputs.dev_pub_key, data, 64);
+		prov->material |= MAT_REMOTE_PUBLIC;
+		prov->expected = PROV_CONFIRM;
+
+		if ((prov->material & MAT_SECRET) != MAT_SECRET)
+			return;
+
+		int_credentials(prov);
+		prov->state = INT_PROV_KEY_ACKED;
+
+		prov->expected = PROV_CONFIRM;
+
+		memset(prov->rand_auth_workspace + 16, 0, 32);
+		switch (prov->conf_inputs.start.auth_method) {
+		default:
+		case 0:
+			/* Auth Type 3c - No OOB */
+			prov->material |= MAT_RAND_AUTH;
+			break;
+		case 1:
+			/* Auth Type 3c - Static OOB */
+			/* Prompt Agent for Static OOB */
+			fail_code[1] = mesh_agent_request_static(prov->agent,
+					static_cb, prov);
+
+			if (fail_code[1])
+				goto failure;
+
+			break;
+		case 2:
+			/* Auth Type 3a - Output OOB */
+			/* Prompt Agent for Output OOB */
+			if (prov->conf_inputs.start.auth_action ==
+							PROV_ACTION_OUT_ALPHA) {
+				fail_code[1] = mesh_agent_prompt_alpha(
+					prov->agent,
+					static_cb, prov);
+			} else {
+				fail_code[1] = mesh_agent_prompt_number(
+					prov->agent, true,
+					prov->conf_inputs.start.auth_action,
+					number_cb, prov);
+			}
+
+			if (fail_code[1])
+				goto failure;
+
+			break;
+
+
+		case 3:
+			/* Auth Type 3b - input OOB */
+			l_getrandom(&oob_key, sizeof(oob_key));
+			oob_key %= digit_mod(prov->conf_inputs.start.auth_size);
+
+			/* Save two copies, for two confirmation values */
+			l_put_be32(oob_key, prov->rand_auth_workspace + 28);
+			l_put_be32(oob_key, prov->rand_auth_workspace + 44);
+			prov->material |= MAT_RAND_AUTH;
+			/* Ask Agent to Display U32 */
+			if (prov->conf_inputs.start.auth_action ==
+							PROV_ACTION_IN_ALPHA) {
+				/* TODO: Construst NUL-term string to pass */
+				fail_code[1] = mesh_agent_display_string(
+					prov->agent, NULL, NULL, prov);
+			} else {
+				fail_code[1] = mesh_agent_display_number(
+					prov->agent, false,
+					prov->conf_inputs.start.auth_action,
+					oob_key, NULL, prov);
+			}
+
+			if (fail_code[1])
+				goto failure;
+
+			break;
+
+
+		}
+		break;
+
+	case PROV_INP_CMPLT: /* Provisioning Input Complete */
+		/* TODO: Cancel Agent prompt */
+		prov->expected = PROV_CONFIRM;
+		out = l_malloc(17);
+		out[0] = PROV_CONFIRM;
+		mesh_crypto_aes_cmac(prov->calc_key, prov->rand_auth_workspace,
+								32, out + 1);
+		prov->trans_tx(prov->trans_data, out, 17);
+		l_free(out);
+		break;
+
+	case PROV_CONFIRM: /* Confirmation */
+		prov->state = INT_PROV_CONF_ACKED;
+		/* RXed Device Confirmation */
+		memcpy(prov->confirm, data, 16);
+		print_packet("ConfirmationDevice", prov->confirm, 16);
+		prov->expected = PROV_RANDOM;
+		out = l_malloc(17);
+		out[0] = PROV_RANDOM;
+		memcpy(out + 1, prov->rand_auth_workspace, 16);
+		prov->trans_tx(prov->trans_data, out, 17);
+		l_free(out);
+		break;
+
+	case PROV_RANDOM: /* Random */
+		prov->state = INT_PROV_RAND_ACKED;
+
+		/* RXed Device Confirmation */
+		memcpy(prov->rand_auth_workspace + 16, data, 16);
+		print_packet("RandomDevice", data, 16);
+		calc_local_material(data);
+
+		mesh_crypto_aes_cmac(prov->calc_key,
+						prov->rand_auth_workspace + 16,
+						32, prov->rand_auth_workspace);
+
+		if (memcmp(prov->rand_auth_workspace, prov->confirm, 16)) {
+			l_error("Provisioning Failed-Confirm compare)");
+			fail_code[1] = PROV_ERR_CONFIRM_FAILED;
+			goto failure;
+		}
+
+		if (prov->state == INT_PROV_RAND_ACKED) {
+			prov->expected = PROV_COMPLETE;
+			out = l_malloc(34);
+			out[0] = PROV_DATA;
+			/* TODO: Fill Prov Data Structure */
+			/* Encrypt Prov Data */
+			mesh_crypto_aes_ccm_encrypt(prov->s_nonce, prov->s_key,
+					NULL, 0,
+					out + 1,
+					25,
+					out + 1,
+					&mic, sizeof(mic));
+			prov->trans_tx(prov->trans_data, out, 34);
+			l_free(out);
+		}
+		break;
+
+	case PROV_COMPLETE: /* Complete */
+		l_info("Provisioning Complete");
+		prov->state = INT_PROV_IDLE;
+		//mesh_prov_close(prov, 0);
+		break;
+
+	case PROV_FAILED: /* Failed */
+		l_error("Provisioning Failed (reason: %d)", data[0]);
+		//mesh_prov_close(prov, data[0]);
+		break;
+
+	default:
+		l_error("Unknown Pkt %2.2x", type);
+		fail_code[1] = PROV_ERR_UNEXPECTED_PDU;
+		goto failure;
+	}
+
+	prov->previous = type;
+	return;
+
+failure:
+	fail_code[0] = PROV_FAILED;
+	prov->trans_tx(prov->trans_data, fail_code, 2);
+	/* TODO: Call Complete Callback (Fail)*/
+}
+
+static void int_prov_ack(void *user_data, uint8_t msg_num)
+{
+	/* TODO: Handle PB-ADV Ack */
+}
+
+
+bool initiator_start(enum trans_type transport,
+		uint8_t uuid[16],
+		uint16_t max_ele,
+		uint16_t server, /* Only valid for PB-Remote */
+		uint32_t timeout, /* in seconds from mesh.conf */
+		struct mesh_agent *agent,
+		mesh_prov_initiator_complete_func_t complete_cb,
+		void *caller_data)
+{
+	bool result;
+
+	/* Invoked from Add() method in mesh-api.txt, to add a
+	 * remote unprovisioned device network.
+	 */
+
+#if defined(GATT_ENABLED)
+	/* If we support PB-GATT, that means we need to set up a GATT
+	 * service, and need the existence of bluetoothd, with it's
+	 * own LE capable controller. That is a battle for another day.
+	 * We will *always* support PB-ADV.
+	 */
+#endif
+	if (prov)
+		return false;
+
+	prov = l_new(struct mesh_prov_initiator, 1);
+	prov->to_secs = timeout;
+	prov->agent = agent;
+	prov->cmplt = complete_cb;
+	prov->caller_data = caller_data;
+	prov->previous = -1;
+
+	/* Always register for PB-ADV */
+	result = pb_adv_reg(int_prov_open, int_prov_close, int_prov_rx,
+						int_prov_ack, uuid, prov);
+
+	if (result)
+		return true;
+
+	initiator_free();
+	return false;
+}
+
+void initiator_cancel(void *user_data)
+{
+	initiator_free();
+}
-- 
2.14.5




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux