The added functionality of this patch set allows any node with a keyring
provision a new node into the network. It does *not* implement a
user App that can store a Configuration Database, but it does provide
extensions to the python test script to test the primitives.
Brian Gix (8):
doc: Cleanup API Provisioner1 interface
mesh: Fix support for Provisioner Initiator
mesh: Add special Beacon handler for Provisioning
mesh: Expose mapping function for D-Bus errors
mesh: Expose resources needed by Management1 interface
mesh: Fix implementation of Provisioner Initiator
mesh: Implement DBus Provisioning methods
test: This extends the mesh tool to exercise Provisioning methods
doc/mesh-api.txt | 13 ++-
mesh/manager.c | 304 +++++++++++++++++++++++++++++++++++++++++++++++--
mesh/mesh-io-generic.c | 2 +-
mesh/mesh-io.c | 6 +-
mesh/mesh-io.h | 7 +-
mesh/mesh.c | 8 +-
mesh/mesh.h | 1 +
mesh/node.c | 42 +++++--
mesh/node.h | 3 +
mesh/pb-adv.c | 54 ++++++---
mesh/pb-adv.h | 3 +-
mesh/prov-acceptor.c | 2 +-
mesh/prov-initiator.c | 253 ++++++++++++++++++++++++++++++----------
mesh/provision.h | 10 +-
test/test-mesh | 115 ++++++++++++++++++-
15 files changed, 710 insertions(+), 113 deletions(-)
--
2.14.5
Provisioning uses the same AD type for Provisioning that
is also used by the Secure Network Beacon, but their
usage is distinct from each other. This change allows
us to enable/disable Unprovisioned Device Beacon capture
without disrupting SNB handling.
---
mesh/mesh-io-generic.c | 2 +-
mesh/mesh-io.c | 6 ++++--
mesh/mesh-io.h | 7 ++++---
3 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/mesh/mesh-io-generic.c b/mesh/mesh-io-generic.c
index 42aaa0947..576c5df1b 100644
--- a/mesh/mesh-io-generic.c
+++ b/mesh/mesh-io-generic.c
@@ -40,7 +40,7 @@ struct mesh_io_private {
struct l_timeout *tx_timeout;
struct l_queue *rx_regs;
struct l_queue *tx_pkts;
- uint8_t filters[3]; /* Simple filtering on AD type only */
+ uint8_t filters[4];
bool sending;
struct tx_pkt *tx;
uint16_t interval;
diff --git a/mesh/mesh-io.c b/mesh/mesh-io.c
index 62a8c0e2c..94a92e885 100644
--- a/mesh/mesh-io.c
+++ b/mesh/mesh-io.c
@@ -86,12 +86,14 @@ struct mesh_io *mesh_io_new(enum mesh_io_type type, void *opts)
if (api->set) {
uint8_t pkt = MESH_AD_TYPE_NETWORK;
- uint8_t bec = MESH_AD_TYPE_BEACON;
uint8_t prv = MESH_AD_TYPE_PROVISION;
+ uint8_t snb[2] = {MESH_AD_TYPE_BEACON, 0x01};
+ uint8_t prvb[2] = {MESH_AD_TYPE_BEACON, 0x00};
- api->set(io, 1, &bec, 1, NULL, NULL);
+ api->set(io, 1, snb, sizeof(snb), NULL, NULL);
api->set(io, 2, &prv, 1, NULL, NULL);
api->set(io, 3, &pkt, 1, NULL, NULL);
+ api->set(io, 4, prvb, sizeof(prvb), NULL, NULL);
}
if (l_queue_push_head(io_list, io))
diff --git a/mesh/mesh-io.h b/mesh/mesh-io.h
index 6585205c7..1c10779aa 100644
--- a/mesh/mesh-io.h
+++ b/mesh/mesh-io.h
@@ -19,9 +19,10 @@
struct mesh_io;
-#define MESH_IO_FILTER_BEACON 1
-#define MESH_IO_FILTER_PROV 2
-#define MESH_IO_FILTER_NET 3
+#define MESH_IO_FILTER_BEACON 1
+#define MESH_IO_FILTER_PROV 2
+#define MESH_IO_FILTER_NET 3
+#define MESH_IO_FILTER_PROV_BEACON 4
#define MESH_IO_TX_COUNT_UNLIMITED 0
--
2.14.5
---
mesh/node.c | 42 ++++++++++++++++++++++++++++++++----------
mesh/node.h | 3 +++
2 files changed, 35 insertions(+), 10 deletions(-)
diff --git a/mesh/node.c b/mesh/node.c
index 1f781cfe9..27235ef34 100644
--- a/mesh/node.c
+++ b/mesh/node.c
@@ -80,7 +80,8 @@ struct mesh_node {
struct l_queue *elements;
char *app_path;
char *owner;
- char *path;
+ char *obj_path;
+ struct mesh_agent *agent;
void *jconfig;
char *node_path;
uint32_t disc_watch;
@@ -242,14 +243,14 @@ static void free_node_dbus_resources(struct mesh_node *node)
l_free(node->app_path);
node->app_path = NULL;
- if (node->path) {
- l_dbus_object_remove_interface(dbus_get_bus(), node->path,
+ if (node->obj_path) {
+ l_dbus_object_remove_interface(dbus_get_bus(), node->obj_path,
MESH_NODE_INTERFACE);
- l_dbus_object_remove_interface(dbus_get_bus(), node->path,
+ l_dbus_object_remove_interface(dbus_get_bus(), node->obj_path,
MESH_MANAGEMENT_INTERFACE);
- l_free(node->path);
- node->path = NULL;
+ l_free(node->obj_path);
+ node->obj_path = NULL;
}
}
@@ -441,6 +442,11 @@ void node_cleanup_all(void)
l_dbus_unregister_interface(dbus_get_bus(), MESH_MANAGEMENT_INTERFACE);
}
+bool node_is_provisioner(struct mesh_node *node)
+{
+ return node->provisioner;
+}
+
bool node_is_provisioned(struct mesh_node *node)
{
return (!IS_UNASSIGNED(node->primary));
@@ -1026,14 +1032,14 @@ static bool register_node_object(struct mesh_node *node)
if (!hex2str(node->uuid, sizeof(node->uuid), uuid, sizeof(uuid)))
return false;
- node->path = l_strdup_printf(BLUEZ_MESH_PATH MESH_NODE_PATH_PREFIX
+ node->obj_path = l_strdup_printf(BLUEZ_MESH_PATH MESH_NODE_PATH_PREFIX
"%s", uuid);
- if (!l_dbus_object_add_interface(dbus_get_bus(), node->path,
+ if (!l_dbus_object_add_interface(dbus_get_bus(), node->obj_path,
MESH_NODE_INTERFACE, node))
return false;
- if (!l_dbus_object_add_interface(dbus_get_bus(), node->path,
+ if (!l_dbus_object_add_interface(dbus_get_bus(), node->obj_path,
MESH_MANAGEMENT_INTERFACE, node))
return false;
@@ -1509,6 +1515,9 @@ static void get_managed_objects_cb(struct l_dbus_message *msg, void *user_data)
&properties);
if (!agent)
goto fail;
+
+ node->agent = agent;
+
} else if (!strcmp(MESH_PROVISIONER_INTERFACE,
interface)) {
node->provisioner = true;
@@ -1736,7 +1745,7 @@ void node_build_attach_reply(struct mesh_node *node,
builder = l_dbus_message_builder_new(reply);
/* Node object path */
- l_dbus_message_builder_append_basic(builder, 'o', node->path);
+ l_dbus_message_builder_append_basic(builder, 'o', node->obj_path);
/* Array of element configurations "a*/
l_dbus_message_builder_enter_array(builder, "(ya(qa{sv}))");
@@ -2007,7 +2016,20 @@ char *node_path_get(struct mesh_node *node)
return node->node_path;
}
+const char *node_get_app_path(struct mesh_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return node->app_path;
+}
+
struct mesh_net *node_get_net(struct mesh_node *node)
{
return node->net;
}
+
+struct mesh_agent *node_get_agent(struct mesh_node *node)
+{
+ return node->agent;
+}
diff --git a/mesh/node.h b/mesh/node.h
index 142527b30..d69887ed1 100644
--- a/mesh/node.h
+++ b/mesh/node.h
@@ -42,6 +42,7 @@ struct mesh_net *node_get_net(struct mesh_node *node);
struct mesh_node *node_find_by_addr(uint16_t addr);
struct mesh_node *node_find_by_uuid(uint8_t uuid[16]);
struct mesh_node *node_find_by_token(uint64_t token);
+bool node_is_provisioner(struct mesh_node *node);
bool node_is_provisioned(struct mesh_node *node);
bool node_app_key_delete(struct mesh_net *net, uint16_t addr,
uint16_t net_idx, uint16_t idx);
@@ -82,6 +83,7 @@ uint8_t node_friend_mode_get(struct mesh_node *node);
uint32_t node_seq_cache(struct mesh_node *node);
const char *node_get_element_path(struct mesh_node *node, uint8_t ele_idx);
const char *node_get_owner(struct mesh_node *node);
+const char *node_get_app_path(struct mesh_node *node);
bool node_add_pending_local(struct mesh_node *node, void *info);
void node_attach_io_all(struct mesh_io *io);
void node_attach_io(struct mesh_node *node, struct mesh_io *io);
@@ -99,3 +101,4 @@ void node_jconfig_set(struct mesh_node *node, void *jconfig);
void *node_jconfig_get(struct mesh_node *node);
void node_path_set(struct mesh_node *node, char *path);
char *node_path_get(struct mesh_node *node);
+struct mesh_agent *node_get_agent(struct mesh_node *node);
--
2.14.5
This implements the Interface and Methods for:
UnprovisionedScan()
UnprovisionedScanCancel()
AddNode()
---
mesh/manager.c | 304 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 296 insertions(+), 8 deletions(-)
diff --git a/mesh/manager.c b/mesh/manager.c
index ca3562512..2947f4a24 100644
--- a/mesh/manager.c
+++ b/mesh/manager.c
@@ -28,19 +28,202 @@
#include "mesh/dbus.h"
#include "mesh/error.h"
#include "mesh/mesh.h"
+#include "mesh/mesh-io.h"
#include "mesh/node.h"
+#include "mesh/net.h"
#include "mesh/keyring.h"
+#include "mesh/agent.h"
+#include "mesh/provision.h"
#include "mesh/manager.h"
+struct add_data{
+ struct l_dbus_message *msg;
+ struct mesh_agent *agent;
+ struct mesh_node *node;
+ uint32_t disc_watch;
+ uint16_t primary;
+ uint16_t net_idx;
+ uint8_t num_ele;
+ uint8_t uuid[16];
+};
+
+static int8_t scan_rssi;
+static uint8_t scan_uuid[16];
+static struct mesh_node *scan_node;
+static struct l_timeout *scan_timeout;
+static struct add_data *add_pending;
+
+static void scan_cancel(struct l_timeout *timeout, void *user_data)
+{
+ struct mesh_node *node = user_data;
+ struct mesh_io *io;
+ struct mesh_net *net;
+
+ l_debug("scan_cancel");
+
+ if (scan_timeout)
+ l_timeout_remove(scan_timeout);
+
+ net = node_get_net(node);
+ io = mesh_net_get_io(net);
+ mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_PROV_BEACON);
+ scan_node = NULL;
+ scan_timeout = NULL;
+}
+
+static void free_pending_add_call()
+{
+ if (!add_pending)
+ return;
+
+ if (add_pending->disc_watch)
+ l_dbus_remove_watch(dbus_get_bus(),
+ add_pending->disc_watch);
+
+ mesh_agent_remove(add_pending->agent);
+
+ l_free(add_pending);
+ add_pending = NULL;
+}
+
+static void prov_disc_cb(struct l_dbus *bus, void *user_data)
+{
+ if (!add_pending)
+ return;
+
+ initiator_cancel(add_pending);
+ add_pending->disc_watch = 0;
+
+ free_pending_add_call();
+}
+
+static void send_add_failed(const char *owner, const char *path,
+ uint8_t status)
+{
+ struct l_dbus *dbus = dbus_get_bus();
+ struct l_dbus_message_builder *builder;
+ struct l_dbus_message *msg;
+
+ msg = l_dbus_message_new_method_call(dbus, owner, path,
+ MESH_PROVISIONER_INTERFACE,
+ "AddNodeFailed");
+
+ builder = l_dbus_message_builder_new(msg);
+ dbus_append_byte_array(builder, add_pending->uuid, 16);
+ l_dbus_message_builder_append_basic(builder, 's',
+ mesh_prov_status_str(status));
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+ l_dbus_send(dbus, msg);
+
+ free_pending_add_call();
+}
+
+static bool add_cmplt(void *user_data, uint8_t status,
+ struct mesh_prov_node_info *info)
+{
+ struct add_data *pending = user_data;
+ struct mesh_node *node = pending->node;
+ struct l_dbus *dbus = dbus_get_bus();
+ struct l_dbus_message_builder *builder;
+ struct l_dbus_message *msg;
+ bool result;
+
+ if (pending != add_pending)
+ return false;
+
+ if (status != PROV_ERR_SUCCESS) {
+ send_add_failed(node_get_owner(node), node_get_app_path(node),
+ status);
+ return false;
+ }
+
+ result = keyring_put_remote_dev_key(add_pending->node, info->unicast,
+ info->cnt, info->device_key);
+
+ if (!result) {
+ send_add_failed(node_get_owner(node), node_get_app_path(node),
+ PROV_ERR_CANT_ASSIGN_ADDR);
+ return false;
+ }
+
+ msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
+ node_get_app_path(node),
+ MESH_PROVISIONER_INTERFACE,
+ "AddNodeComplete");
+
+ builder = l_dbus_message_builder_new(msg);
+ dbus_append_byte_array(builder, add_pending->uuid, 16);
+ l_dbus_message_builder_append_basic(builder, 'q', &info->unicast);
+ l_dbus_message_builder_append_basic(builder, 'y', &info->cnt);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+
+ l_dbus_send(dbus, msg);
+
+ free_pending_add_call();
+
+ return true;
+}
+
+static void mgr_prov_data (struct l_dbus_message *reply, void *user_data)
+{
+ struct add_data *pending = user_data;
+ uint16_t net_idx;
+ uint16_t primary;
+
+ if (pending != add_pending)
+ return;
+
+ if (l_dbus_message_is_error(reply))
+ return;
+
+ if (!l_dbus_message_get_arguments(reply, "qq", &net_idx, &primary))
+ return;
+
+ add_pending->primary = primary;
+ add_pending->net_idx = net_idx;
+ initiator_prov_data(net_idx, primary, add_pending);
+}
+
+static bool add_data_get(void *user_data, uint8_t num_ele)
+{
+ struct add_data *pending = user_data;
+ struct l_dbus_message *msg;
+ struct l_dbus *dbus;
+ const char *app_path;
+ const char *sender;
+
+ if (pending != add_pending)
+ return false;
+
+ dbus = dbus_get_bus();
+ app_path = node_get_app_path(add_pending->node);
+ sender = node_get_owner(add_pending->node);
+
+ msg = l_dbus_message_new_method_call(dbus, sender, app_path,
+ MESH_PROVISIONER_INTERFACE,
+ "RequestProvData");
+
+ l_dbus_message_set_arguments(msg, "y", num_ele);
+ l_dbus_send_with_reply(dbus, msg, mgr_prov_data, add_pending, NULL);
+
+ add_pending->num_ele = num_ele;
+
+ return true;
+}
+
static struct l_dbus_message *add_node_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
+ struct mesh_node *node = user_data;
struct l_dbus_message_iter iter_uuid;
+ struct l_dbus_message *reply;
uint8_t *uuid;
uint32_t n;
- l_debug("Add node request");
+ l_debug("AddNode request");
if (!l_dbus_message_get_arguments(msg, "ay", &iter_uuid))
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
@@ -50,8 +233,47 @@ static struct l_dbus_message *add_node_call(struct l_dbus *dbus,
return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
"Bad device UUID");
- /* TODO */
- return dbus_error(msg, MESH_ERROR_NOT_IMPLEMENTED, NULL);
+ /* Allow AddNode to cancel Scanning if from the same node */
+ if (scan_node) {
+ if (scan_node != node)
+ return dbus_error(msg, MESH_ERROR_BUSY, NULL);
+
+ scan_cancel(NULL, node);
+ }
+
+ /* Invoke Prov Initiator */
+
+ add_pending = l_new(struct add_data, 1);
+ memcpy(add_pending->uuid, uuid, 16);
+ add_pending->node = node;
+ add_pending->agent = node_get_agent(node);;
+
+ if (!node_is_provisioner(node) || (add_pending->agent == NULL)) {
+ l_info("Provisioner: %d", node_is_provisioner(node));
+ l_info("Agent: %p", add_pending->agent);
+ reply = dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED,
+ "Missing Interfaces");
+ goto fail;
+ }
+
+
+ if (!initiator_start(PB_ADV, uuid, 99, 0, 60, add_pending->agent,
+ add_data_get, add_cmplt, node, add_pending)) {
+ reply = dbus_error(msg, MESH_ERROR_FAILED,
+ "Failed to start provisioning initiator");
+ goto fail;
+ }
+
+ add_pending->disc_watch = l_dbus_add_disconnect_watch(dbus,
+ node_get_owner(node),
+ prov_disc_cb, NULL, NULL);
+
+ return l_dbus_message_new_method_return(msg);
+
+fail:
+ l_free(add_pending);
+ add_pending = NULL;
+ return reply;
}
@@ -97,25 +319,91 @@ static struct l_dbus_message *delete_node_call(struct l_dbus *dbus,
return l_dbus_message_new_method_return(msg);
}
+static void prov_beacon_recv(void *user_data, struct mesh_io_recv_info *info,
+ const uint8_t *data, uint16_t len)
+{
+ struct mesh_node *node = user_data;
+ struct l_dbus_message_builder *builder;
+ struct l_dbus_message *msg;
+ struct l_dbus *dbus;
+ int16_t rssi;
+
+ if (scan_node != node || len < sizeof(scan_uuid) + 2 || data[1] != 0x00)
+ return;
+
+ if (!memcmp(data + 2, scan_uuid, sizeof(scan_uuid))) {
+ if (info->rssi <= scan_rssi)
+ return;
+ }
+
+ memcpy(scan_uuid, data + 2, sizeof(scan_uuid));
+ scan_rssi = info->rssi;
+ rssi = info->rssi;
+
+ dbus = dbus_get_bus();
+ msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
+ node_get_app_path(node),
+ MESH_PROVISIONER_INTERFACE,
+ "ScanResult");
+
+ builder = l_dbus_message_builder_new(msg);
+ l_dbus_message_builder_append_basic(builder, 'n', &rssi);
+ dbus_append_byte_array(builder, data + 2, len -2);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+
+ l_dbus_send(dbus, msg);
+}
+
static struct l_dbus_message *start_scan_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
+ struct mesh_node *node = user_data;
uint16_t duration;
+ struct mesh_io *io;
+ struct mesh_net *net;
if (!l_dbus_message_get_arguments(msg, "q", &duration))
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
- /* TODO */
- return dbus_error(msg, MESH_ERROR_NOT_IMPLEMENTED, NULL);
+ if (scan_node && scan_node != node)
+ return dbus_error(msg, MESH_ERROR_BUSY, NULL);
+
+ if (!node_is_provisioner(node))
+ return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
+
+ if (scan_timeout)
+ l_timeout_remove(scan_timeout);
+
+ memset(scan_uuid, 0, sizeof(scan_uuid));
+ scan_rssi = -128;
+ scan_timeout = NULL;
+ net = node_get_net(node);
+ io = mesh_net_get_io(net);
+ scan_node = node;
+ mesh_io_register_recv_cb(io, MESH_IO_FILTER_PROV_BEACON,
+ prov_beacon_recv, node);
+
+ if (duration)
+ scan_timeout = l_timeout_create(duration, scan_cancel,
+ node, NULL);
+
+ return l_dbus_message_new_method_return(msg);
}
static struct l_dbus_message *cancel_scan_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
- /* TODO */
- return dbus_error(msg, MESH_ERROR_NOT_IMPLEMENTED, NULL);
+ struct mesh_node *node = user_data;
+
+ if (scan_node != node)
+ return dbus_error(msg, MESH_ERROR_BUSY, NULL);
+
+ scan_cancel(NULL, node);
+
+ return l_dbus_message_new_method_return(msg);
}
static struct l_dbus_message *store_new_subnet(struct mesh_node *node,
@@ -416,7 +704,7 @@ static struct l_dbus_message *set_key_phase_call(struct l_dbus *dbus,
static void setup_management_interface(struct l_dbus_interface *iface)
{
l_dbus_interface_method(iface, "AddNode", 0, add_node_call, "", "ay",
- "", "uuid");
+ "uuid");
l_dbus_interface_method(iface, "ImportRemoteNode", 0, import_node_call,
"", "qyay", "", "primary",
"count", "dev_key");
--
2.14.5
---
mesh/mesh.c | 8 ++++----
mesh/mesh.h | 1 +
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/mesh/mesh.c b/mesh/mesh.c
index 26acfd4dc..62c80c0f3 100644
--- a/mesh/mesh.c
+++ b/mesh/mesh.c
@@ -266,7 +266,7 @@ static void prov_disc_cb(struct l_dbus *bus, void *user_data)
free_pending_join_call(true);
}
-static const char *prov_status_str(uint8_t status)
+const char *mesh_prov_status_str(uint8_t status)
{
switch (status) {
case PROV_ERR_SUCCESS:
@@ -301,7 +301,7 @@ static void send_join_failed(const char *owner, const char *path,
MESH_APPLICATION_INTERFACE,
"JoinFailed");
- l_dbus_message_set_arguments(msg, "s", prov_status_str(status));
+ l_dbus_message_set_arguments(msg, "s", mesh_prov_status_str(status));
l_dbus_send(dbus_get_bus(), msg);
free_pending_join_call(true);
@@ -316,7 +316,7 @@ static bool prov_complete_cb(void *user_data, uint8_t status,
const char *path;
const uint8_t *token;
- l_debug("Provisioning complete %s", prov_status_str(status));
+ l_debug("Provisioning complete %s", mesh_prov_status_str(status));
if (!join_pending)
return false;
@@ -342,7 +342,7 @@ static bool prov_complete_cb(void *user_data, uint8_t status,
l_dbus_message_set_arguments(msg, "t", l_get_be64(token));
- l_dbus_send(dbus_get_bus(), msg);
+ l_dbus_send(dbus, msg);
free_pending_join_call(false);
diff --git a/mesh/mesh.h b/mesh/mesh.h
index 14b1fb517..2ef77b57d 100644
--- a/mesh/mesh.h
+++ b/mesh/mesh.h
@@ -42,3 +42,4 @@ bool mesh_send_pkt(uint8_t count, uint16_t interval, uint8_t *data,
bool mesh_send_cancel(const uint8_t *filter, uint8_t len);
bool mesh_reg_prov_rx(prov_rx_cb_t cb, void *user_data);
void mesh_unreg_prov_rx(prov_rx_cb_t cb);
+const char *mesh_prov_status_str(uint8_t status);
--
2.14.5
This has had testing of baseline functionality that includes
OOB authentication type "3c" from BT Mesh v1.0 specification
---
mesh/prov-acceptor.c | 2 +-
mesh/prov-initiator.c | 253 ++++++++++++++++++++++++++++++++++++++------------
mesh/provision.h | 10 +-
3 files changed, 205 insertions(+), 60 deletions(-)
diff --git a/mesh/prov-acceptor.c b/mesh/prov-acceptor.c
index 7b79fa916..111340db3 100644
--- a/mesh/prov-acceptor.c
+++ b/mesh/prov-acceptor.c
@@ -651,7 +651,7 @@ bool acceptor_start(uint8_t num_ele, uint8_t uuid[16],
goto error_fail;
/* Always register for PB-ADV */
- result = pb_adv_reg(acp_prov_open, acp_prov_close, acp_prov_rx,
+ result = pb_adv_reg(false, acp_prov_open, acp_prov_close, acp_prov_rx,
acp_prov_ack, uuid, prov);
if (result)
diff --git a/mesh/prov-initiator.c b/mesh/prov-initiator.c
index f147c7ad8..13fd6d086 100644
--- a/mesh/prov-initiator.c
+++ b/mesh/prov-initiator.c
@@ -29,6 +29,8 @@
#include "mesh/util.h"
#include "mesh/crypto.h"
#include "mesh/net.h"
+#include "mesh/node.h"
+#include "mesh/keyring.h"
#include "mesh/prov.h"
#include "mesh/provision.h"
#include "mesh/pb-adv.h"
@@ -76,14 +78,18 @@ enum int_state {
struct mesh_prov_initiator {
mesh_prov_initiator_complete_func_t cmplt;
+ mesh_prov_initiator_data_req_func_t get_prov_data;
prov_trans_tx_t trans_tx;
void *agent;
void *caller_data;
void *trans_data;
+ struct mesh_node *node;
struct l_timeout *timeout;
uint32_t to_secs;
enum int_state state;
enum trans_type transport;
+ uint16_t net_idx;
+ uint16_t unicast;
uint8_t material;
uint8_t expected;
int8_t previous;
@@ -102,7 +108,6 @@ static struct mesh_prov_initiator *prov = NULL;
static void initiator_free(void)
{
-
if (prov)
l_timeout_remove(prov->timeout);
@@ -116,7 +121,34 @@ static void initiator_free(void)
static void int_prov_close(void *user_data, uint8_t reason)
{
- /* TODO: Handle Close */
+ struct mesh_prov_initiator *prov = user_data;
+ struct mesh_prov_node_info info;
+
+ if (reason != PROV_ERR_SUCCESS) {
+ prov->cmplt(prov->caller_data, reason, NULL);
+ initiator_free();
+ return;
+ }
+
+ memcpy(info.device_key, prov->calc_key, 16);
+ info.net_index = prov->net_idx;
+ info.unicast = prov->unicast;
+ info.cnt = prov->conf_inputs.caps.num_ele;
+
+ prov->cmplt(prov->caller_data, PROV_ERR_SUCCESS, &info);
+ initiator_free();
+}
+
+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 int_prov_open(void *user_data, prov_trans_tx_t trans_tx,
@@ -140,6 +172,8 @@ static void int_prov_open(void *user_data, prov_trans_tx_t trans_tx,
/* Always use an ephemeral key when Initiator */
ecc_make_key(prov->conf_inputs.prv_pub_key, prov->private_key);
+ swap_u256_bytes(prov->conf_inputs.prv_pub_key);
+ swap_u256_bytes(prov->conf_inputs.prv_pub_key + 32);
prov->material |= MAT_LOCAL_PRIVATE;
prov->trans_tx = trans_tx;
@@ -152,18 +186,6 @@ static void int_prov_open(void *user_data, prov_trans_tx_t trans_tx,
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)
{
@@ -241,7 +263,6 @@ static void calc_local_material(const uint8_t *random)
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, int err, uint32_t number)
@@ -307,6 +328,108 @@ static void pub_key_cb(void *user_data, int err, uint8_t *key, uint32_t len)
int_credentials(prov);
}
+static void send_pub_key(struct mesh_prov_initiator *prov)
+{
+ uint8_t out[65];
+
+ out[0] = PROV_PUB_KEY;
+ memcpy(out + 1, prov->conf_inputs.prv_pub_key, 64);
+ prov->trans_tx(prov->trans_data, out, 65);
+ prov->state = INT_PROV_KEY_SENT;
+}
+
+static void send_confirm(struct mesh_prov_initiator *prov)
+{
+ uint8_t out[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);
+ prov->state = INT_PROV_CONF_SENT;
+ prov->expected = PROV_CONFIRM;
+}
+
+static void send_random(struct mesh_prov_initiator *prov)
+{
+ uint8_t out[17];
+
+ out[0] = PROV_RANDOM;
+ memcpy(out + 1, prov->rand_auth_workspace, 16);
+ prov->trans_tx(prov->trans_data, out, 17);
+ prov->state = INT_PROV_RAND_SENT;
+ prov->expected = PROV_RANDOM;
+}
+
+void initiator_prov_data(uint16_t net_idx, uint16_t primary, void *caller_data)
+{
+ struct keyring_net_key key;
+ struct mesh_net *net;
+ uint64_t mic;
+ uint32_t iv_index;
+ uint8_t snb_flags;
+ uint8_t out[34];
+
+ if (!prov || caller_data != prov->caller_data)
+ return;
+
+ if (prov->state != INT_PROV_RAND_ACKED)
+ return;
+
+ net = node_get_net(prov->node);
+ prov->expected = PROV_COMPLETE;
+
+ /* Calculate remote device key */
+ mesh_crypto_device_key(prov->secret,
+ prov->salt,
+ prov->calc_key);
+
+ print_packet("DevKey", prov->calc_key, 16);
+
+ /* Fill Prov Data Structure */
+ if (!keyring_get_net_key(prov->node, net_idx, &key)) {
+ out[1] = PROV_ERR_UNEXPECTED_ERR;
+ goto failure;
+ }
+
+ prov->unicast = primary;
+ prov->net_idx = net_idx;
+ mesh_net_get_snb_state(net, &snb_flags, &iv_index);
+
+ out[0] = PROV_DATA;
+
+ if (key.phase == KEY_REFRESH_PHASE_TWO) {
+ memcpy(out + 1, key.new_key, 16);
+ snb_flags |= PROV_FLAG_KR;
+ } else
+ memcpy(out + 1, key.old_key, 16);
+
+ l_put_be16(net_idx, out + 1 + 16);
+ l_put_u8(snb_flags, out + 1 + 16 + 2);
+ l_put_be32(iv_index, out + 1 + 16 + 2 + 1);
+ l_put_be16(primary, out + 1 + 16 + 2 + 1 + 4);
+
+ print_packet("ProvData", out + 1, 25);
+ /* Encrypt Prov Data */
+ mesh_crypto_aes_ccm_encrypt(prov->s_nonce, prov->s_key,
+ NULL, 0,
+ out + 1,
+ 25,
+ out + 1,
+ &mic, sizeof(mic));
+ print_packet("EncData", out + 1, 25 + 8);
+ prov->trans_tx(prov->trans_data, out, 34);
+ prov->state = INT_PROV_DATA_SENT;
+ return;
+
+failure:
+ l_debug("Failing... %d", out[1]);
+ out[0] = PROV_FAILED;
+ prov->trans_tx(prov->trans_data, out, 2);
+ /* TODO: Call Complete Callback (Fail)*/
+}
+
+
static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
{
struct mesh_prov_initiator *rx_prov = user_data;
@@ -314,7 +437,6 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
uint8_t type = *data++;
uint8_t fail_code[2];
uint32_t oob_key;
- uint64_t mic;
if (rx_prov != prov || !prov->trans_tx)
return;
@@ -425,8 +547,7 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
int_credentials(prov);
prov->state = INT_PROV_KEY_ACKED;
- prov->expected = PROV_CONFIRM;
-
+ l_debug("auth_method: %d", prov->conf_inputs.start.auth_method);
memset(prov->rand_auth_workspace + 16, 0, 32);
switch (prov->conf_inputs.start.auth_method) {
default:
@@ -464,7 +585,6 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
break;
-
case 3:
/* Auth Type 3b - input OOB */
l_getrandom(&oob_key, sizeof(oob_key));
@@ -492,19 +612,16 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
break;
-
}
+
+ if (prov->material & MAT_RAND_AUTH)
+ send_confirm(prov);
+
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);
+ send_confirm(prov);
break;
case PROV_CONFIRM: /* Confirmation */
@@ -512,58 +629,46 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
/* 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);
+ send_random(prov);
break;
case PROV_RANDOM: /* Random */
prov->state = INT_PROV_RAND_ACKED;
/* RXed Device Confirmation */
+ calc_local_material(data);
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);
+ print_packet("Dev-Conf", prov->rand_auth_workspace, 16);
if (memcmp(prov->rand_auth_workspace, prov->confirm, 16)) {
- l_error("Provisioning Failed-Confirm compare)");
+ 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);
+ if (!prov->get_prov_data(prov->caller_data,
+ prov->conf_inputs.caps.num_ele)) {
+ l_error("Provisioning Failed-Data Get");
+ fail_code[1] = PROV_ERR_CANT_ASSIGN_ADDR;
+ goto failure;
}
break;
case PROV_COMPLETE: /* Complete */
l_info("Provisioning Complete");
prov->state = INT_PROV_IDLE;
- //mesh_prov_close(prov, 0);
+ int_prov_close(prov, PROV_ERR_SUCCESS);
break;
case PROV_FAILED: /* Failed */
l_error("Provisioning Failed (reason: %d)", data[0]);
- //mesh_prov_close(prov, data[0]);
+ prov->state = INT_PROV_IDLE;
+ int_prov_close(prov, data[0]);
break;
default:
@@ -572,20 +677,51 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
goto failure;
}
- prov->previous = type;
+ if (prov)
+ prov->previous = type;
+
return;
failure:
+ l_debug("Failing... %d", fail_code[1]);
fail_code[0] = PROV_FAILED;
prov->trans_tx(prov->trans_data, fail_code, 2);
- /* TODO: Call Complete Callback (Fail)*/
+ int_prov_close(prov, fail_code[1]);
}
static void int_prov_ack(void *user_data, uint8_t msg_num)
{
- /* TODO: Handle PB-ADV Ack */
-}
+ struct mesh_prov_initiator *rx_prov = user_data;
+ if (rx_prov != prov || !prov->trans_tx)
+ return;
+
+ switch(prov->state) {
+ case INT_PROV_START_SENT:
+ prov->state = INT_PROV_START_ACKED;
+ if (prov->conf_inputs.caps.pub_type == 0)
+ send_pub_key(prov);
+ break;
+
+ case INT_PROV_DATA_SENT:
+ prov->state = INT_PROV_DATA_ACKED;
+ break;
+
+ case INT_PROV_IDLE:
+ case INT_PROV_INVITE_SENT:
+ case INT_PROV_INVITE_ACKED:
+ case INT_PROV_START_ACKED:
+ case INT_PROV_KEY_SENT:
+ case INT_PROV_KEY_ACKED:
+ case INT_PROV_CONF_SENT:
+ case INT_PROV_CONF_ACKED:
+ case INT_PROV_RAND_SENT:
+ case INT_PROV_RAND_ACKED:
+ case INT_PROV_DATA_ACKED:
+ default:
+ break;
+ }
+}
bool initiator_start(enum trans_type transport,
uint8_t uuid[16],
@@ -593,8 +729,9 @@ bool initiator_start(enum trans_type transport,
uint16_t server, /* Only valid for PB-Remote */
uint32_t timeout, /* in seconds from mesh.conf */
struct mesh_agent *agent,
+ mesh_prov_initiator_data_req_func_t get_prov_data,
mesh_prov_initiator_complete_func_t complete_cb,
- void *caller_data)
+ void *node, void *caller_data)
{
bool result;
@@ -607,13 +744,15 @@ bool initiator_start(enum trans_type transport,
prov = l_new(struct mesh_prov_initiator, 1);
prov->to_secs = timeout;
+ prov->node = node;
prov->agent = agent;
prov->cmplt = complete_cb;
+ prov->get_prov_data = get_prov_data;
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,
+ result = pb_adv_reg(true, int_prov_open, int_prov_close, int_prov_rx,
int_prov_ack, uuid, prov);
if (result)
diff --git a/mesh/provision.h b/mesh/provision.h
index 6b61a45be..6670de20c 100644
--- a/mesh/provision.h
+++ b/mesh/provision.h
@@ -90,6 +90,7 @@ struct mesh_prov_node_info {
uint32_t iv_index;
uint16_t unicast;
uint16_t net_index;
+ uint8_t cnt;
uint8_t net_key[16];
uint8_t device_key[16];
uint8_t flags; /* IVU and KR bits */
@@ -99,6 +100,9 @@ typedef bool (*mesh_prov_acceptor_complete_func_t)(void *user_data,
uint8_t status,
struct mesh_prov_node_info *info);
+typedef bool (*mesh_prov_initiator_data_req_func_t)(void *user_data,
+ uint8_t num_elem);
+
typedef bool (*mesh_prov_initiator_complete_func_t)(void *user_data,
uint8_t status,
struct mesh_prov_node_info *info);
@@ -117,6 +121,8 @@ bool initiator_start(enum trans_type transport,
uint16_t server, /* Only valid for PB-Remote */
uint32_t timeout, /* in seconds from mesh.conf */
struct mesh_agent *agent,
+ mesh_prov_initiator_data_req_func_t get_prov_data,
mesh_prov_initiator_complete_func_t complete_cb,
- void *caller_data);
-void initiator_cancel(void *user_data);
+ void *node, void *caller_data);
+void initiator_prov_data(uint16_t net_idx, uint16_t primary, void *caller_data);
+void initiator_cancel(void *caller_data);
--
2.14.5
Note: This script is *not* a suitable tool for expanding into
a Mesh Provisioner. It is only intended to demonstrate the arguments
and methods required.
---
test/test-mesh | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 114 insertions(+), 1 deletion(-)
diff --git a/test/test-mesh b/test/test-mesh
index 4d515e186..b655d7b3d 100755
--- a/test/test-mesh
+++ b/test/test-mesh
@@ -24,10 +24,13 @@
# The main menu:
# token
# create
+# scan
+# add
# join
# attach
# remove
# dest
+# uuid
# app-index
# client-menu
# exit
@@ -50,6 +53,16 @@
# for the runtime of the test, and may be used in future
# attach requests.
#
+# scan
+# Scan for unprovisioned devices
+#
+# add
+# Adds a remote node to a mesh network that we have provisioning
+# authorization to. The test prompts for a remote devices
+# UUID, and supplies an Agent that will handle the interaction,
+# and provide the provisioning data which will complete to
+# process.
+#
# join
# Request provisioning of a device to become a node
# on a mesh network. The test generates device UUID
@@ -145,9 +158,11 @@ MESH_SERVICE_NAME = 'org.bluez.mesh'
DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+MESH_MGR_IFACE = 'org.bluez.mesh.Management1'
MESH_NETWORK_IFACE = 'org.bluez.mesh.Network1'
MESH_NODE_IFACE = 'org.bluez.mesh.Node1'
MESH_APPLICATION_IFACE = 'org.bluez.mesh.Application1'
+MESH_PROV_IFACE = 'org.bluez.mesh.Provisioner1'
MESH_ELEMENT_IFACE = 'org.bluez.mesh.Element1'
APP_COMPANY_ID = 0x05f1
@@ -160,6 +175,7 @@ app = None
bus = None
mainloop = None
node = None
+node_mgr = None
mesh_net = None
dst_addr = 0x0000
@@ -170,6 +186,10 @@ token = None
have_token = False
attached = False
+# Remote device UUID
+have_uuid = False
+remote_uuid = None
+
# Menu housekeeping
MAIN_MENU = 0
ON_OFF_CLIENT_MENU = 1
@@ -179,6 +199,7 @@ INPUT_TOKEN = 1
INPUT_DEST_ADDRESS = 2
INPUT_APP_KEY_INDEX = 3
INPUT_MESSAGE_PAYLOAD = 4
+INPUT_UUID = 5
menus = []
current_menu = None
@@ -226,6 +247,17 @@ def set_token(str_value):
token = numpy.uint64(input_number)
have_token = True
+def set_uuid(str_value):
+ global remote_uuid
+ global have_uuid
+
+ if len(str_value) != 32:
+ raise_error('Expected 32 digits')
+ return
+
+ remote_uuid = bytearray.fromhex(str_value)
+ have_uuid = True
+
def array_to_string(b_array):
str_value = ""
for b in b_array:
@@ -248,6 +280,18 @@ def attach(token):
reply_handler=attach_app_cb,
error_handler=attach_app_error_cb)
+def scan_cb():
+ print('Scan procedure started')
+
+def scan_error_cb(reason):
+ print('Scan procedure failed ', reason)
+
+def add_cb():
+ print('AddNode procedure started')
+
+def add_error_cb(reason):
+ print('AddNode procedure failed ', reason)
+
def join_cb():
print('Join procedure started')
@@ -306,6 +350,9 @@ def attach_app_cb(node_path, dict_array):
obj = bus.get_object(MESH_SERVICE_NAME, node_path)
+ global node_mgr
+ node_mgr = dbus.Interface(obj, MESH_MGR_IFACE)
+
global node
node = dbus.Interface(obj, MESH_NODE_IFACE)
@@ -392,6 +439,8 @@ class Application(dbus.service.Object):
'CompanyID': dbus.UInt16(APP_COMPANY_ID),
'ProductID': dbus.UInt16(APP_PRODUCT_ID),
'VersionID': dbus.UInt16(APP_VERSION_ID)
+ },
+ MESH_PROV_IFACE: {
}
}
@@ -424,6 +473,35 @@ class Application(dbus.service.Object):
def JoinFailed(self, value):
print(set_error('JoinFailed '), value)
+ @dbus.service.method(MESH_PROV_IFACE,
+ in_signature="nay", out_signature="")
+ def ScanResult(self, rssi, uuid):
+ uuid_str = array_to_string(uuid)
+ print(set_yellow('ScanResult RSSI ')
+ + set_green(format(rssi, 'd'))
+ + ' ' + uuid_str)
+
+ @dbus.service.method(MESH_PROV_IFACE,
+ in_signature="y", out_signature="qq")
+ def RequestProvData(self, count):
+ print('RequestProvData for Ele_Cnt '
+ + set_green(format(count, 'd')))
+ return dbus.Struct((dbus.UInt16(0), dbus.UInt16(678)))
+
+ @dbus.service.method(MESH_PROV_IFACE,
+ in_signature="ayqy", out_signature="")
+ def AddNodeComplete(self, uuid, unicast, count):
+ uuid_str = array_to_string(uuid)
+ print(set_yellow('AddNodeComplete of node ')
+ + set_green(format(unicast, '04x'))
+ + ' uuid ' + uuid_str)
+
+ @dbus.service.method(MESH_PROV_IFACE,
+ in_signature="ays", out_signature="")
+
+ def AddNodeFailed(self, uuid, value):
+ print(set_error('AddNodeFailed '), value)
+
class Element(dbus.service.Object):
PATH_BASE = '/example/ele'
@@ -768,6 +846,10 @@ class MainMenu(Menu):
self.__cmd_set_token),
'create': MenuItem(' - create mesh network',
self.__cmd_create),
+ 'scan': MenuItem(' - scan for near unprovisioned devs',
+ self.__cmd_scan),
+ 'add': MenuItem(' - add device to mesh network',
+ self.__cmd_add),
'join': MenuItem(' - join mesh network',
self.__cmd_join),
'attach': MenuItem(' - attach mesh node',
@@ -776,6 +858,8 @@ class MainMenu(Menu):
self.__cmd_remove),
'dest': MenuItem(' - set destination address',
self.__cmd_set_dest),
+ 'uuid': MenuItem(' - set remote uuid',
+ self.__cmd_set_uuid),
'app-index': MenuItem(' - set AppKey index',
self.__cmd_set_app_idx),
'vendor-send': MenuItem(' - send raw vendor message',
@@ -809,6 +893,12 @@ class MainMenu(Menu):
user_input = INPUT_DEST_ADDRESS
print(set_cyan('Enter 4-digit hex destination address:'))
+ def __cmd_set_uuid(self):
+ global user_input
+
+ user_input = INPUT_UUID
+ print(set_cyan('Enter 32-digit hex remote UUID:'))
+
def __cmd_set_app_idx(self):
global user_input
@@ -842,7 +932,7 @@ class MainMenu(Menu):
uuid = bytearray.fromhex("0a0102030405060708090A0B0C0D0E0F")
random.shuffle(uuid)
- uuid_str = array_to_string(uuid)
+ uuid_str = array_to_string(remote_uuid)
caps = ["out-numeric"]
oob = ["other"]
@@ -851,6 +941,26 @@ class MainMenu(Menu):
reply_handler=join_cb,
error_handler=join_error_cb)
+ def __cmd_scan(self):
+
+ print(set_yellow('Scanning...'))
+ node_mgr.UnprovisionedScan(0, reply_handler=add_cb,
+ error_handler=add_error_cb)
+
+ def __cmd_add(self):
+ global user_input
+ if agent == None:
+ print(set_error('Provisioning agent not found'))
+ return
+
+ uuid_str = array_to_string(remote_uuid)
+ caps = ["in-numeric"]
+ oob = ["other"]
+
+ print(set_yellow('Adding dev UUID ') + set_green(uuid_str))
+ node_mgr.AddNode(remote_uuid, reply_handler=add_cb,
+ error_handler=add_error_cb)
+
def __cmd_attach(self):
if have_token == False:
print(set_error('Token is not set'))
@@ -887,6 +997,8 @@ class MainMenu(Menu):
if user_input == INPUT_TOKEN:
set_token(str_value)
+ elif user_input == INPUT_UUID:
+ set_uuid(str_value)
elif user_input == INPUT_DEST_ADDRESS:
res = set_value(str_value, 4, 4)
if is_error() != True:
@@ -977,6 +1089,7 @@ def main():
mesh_net = dbus.Interface(bus.get_object(MESH_SERVICE_NAME,
"/org/bluez/mesh"),
MESH_NETWORK_IFACE)
+
mesh_net.connect_to_signal('InterfacesRemoved', interfaces_removed_cb)
app = Application(bus)
--
2.14.5
Hi Brian,
On 07/10, Brian Gix wrote:
> This has had testing of baseline functionality that includes
> OOB authentication type "3c" from BT Mesh v1.0 specification
> ---
> mesh/prov-acceptor.c | 2 +-
> mesh/prov-initiator.c | 253 ++++++++++++++++++++++++++++++++++++++------------
> mesh/provision.h | 10 +-
> 3 files changed, 205 insertions(+), 60 deletions(-)
>
> diff --git a/mesh/prov-acceptor.c b/mesh/prov-acceptor.c
> index 7b79fa916..111340db3 100644
> --- a/mesh/prov-acceptor.c
> +++ b/mesh/prov-acceptor.c
> @@ -651,7 +651,7 @@ bool acceptor_start(uint8_t num_ele, uint8_t uuid[16],
> goto error_fail;
>
> /* Always register for PB-ADV */
> - result = pb_adv_reg(acp_prov_open, acp_prov_close, acp_prov_rx,
> + result = pb_adv_reg(false, acp_prov_open, acp_prov_close, acp_prov_rx,
> acp_prov_ack, uuid, prov);
>
> if (result)
> diff --git a/mesh/prov-initiator.c b/mesh/prov-initiator.c
> index f147c7ad8..13fd6d086 100644
> --- a/mesh/prov-initiator.c
> +++ b/mesh/prov-initiator.c
> @@ -29,6 +29,8 @@
> #include "mesh/util.h"
> #include "mesh/crypto.h"
> #include "mesh/net.h"
> +#include "mesh/node.h"
> +#include "mesh/keyring.h"
> #include "mesh/prov.h"
> #include "mesh/provision.h"
> #include "mesh/pb-adv.h"
> @@ -76,14 +78,18 @@ enum int_state {
>
> struct mesh_prov_initiator {
> mesh_prov_initiator_complete_func_t cmplt;
> + mesh_prov_initiator_data_req_func_t get_prov_data;
> prov_trans_tx_t trans_tx;
> void *agent;
> void *caller_data;
> void *trans_data;
> + struct mesh_node *node;
> struct l_timeout *timeout;
> uint32_t to_secs;
> enum int_state state;
> enum trans_type transport;
> + uint16_t net_idx;
> + uint16_t unicast;
> uint8_t material;
> uint8_t expected;
> int8_t previous;
> @@ -102,7 +108,6 @@ static struct mesh_prov_initiator *prov = NULL;
>
> static void initiator_free(void)
> {
> -
> if (prov)
> l_timeout_remove(prov->timeout);
>
> @@ -116,7 +121,34 @@ static void initiator_free(void)
>
> static void int_prov_close(void *user_data, uint8_t reason)
> {
> - /* TODO: Handle Close */
> + struct mesh_prov_initiator *prov = user_data;
> + struct mesh_prov_node_info info;
> +
> + if (reason != PROV_ERR_SUCCESS) {
> + prov->cmplt(prov->caller_data, reason, NULL);
> + initiator_free();
> + return;
> + }
> +
> + memcpy(info.device_key, prov->calc_key, 16);
> + info.net_index = prov->net_idx;
> + info.unicast = prov->unicast;
> + info.cnt = prov->conf_inputs.caps.num_ele;
Please rename 'cnt' to 'num_ele' to be more consistent with the rest of
the code.
> +
> + prov->cmplt(prov->caller_data, PROV_ERR_SUCCESS, &info);
> + initiator_free();
> +}
> +
> +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 int_prov_open(void *user_data, prov_trans_tx_t trans_tx,
> @@ -140,6 +172,8 @@ static void int_prov_open(void *user_data, prov_trans_tx_t trans_tx,
>
> /* Always use an ephemeral key when Initiator */
> ecc_make_key(prov->conf_inputs.prv_pub_key, prov->private_key);
> + swap_u256_bytes(prov->conf_inputs.prv_pub_key);
> + swap_u256_bytes(prov->conf_inputs.prv_pub_key + 32);
> prov->material |= MAT_LOCAL_PRIVATE;
>
> prov->trans_tx = trans_tx;
> @@ -152,18 +186,6 @@ static void int_prov_open(void *user_data, prov_trans_tx_t trans_tx,
> 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)
> {
> @@ -241,7 +263,6 @@ static void calc_local_material(const uint8_t *random)
>
> 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, int err, uint32_t number)
> @@ -307,6 +328,108 @@ static void pub_key_cb(void *user_data, int err, uint8_t *key, uint32_t len)
> int_credentials(prov);
> }
>
> +static void send_pub_key(struct mesh_prov_initiator *prov)
> +{
> + uint8_t out[65];
> +
> + out[0] = PROV_PUB_KEY;
> + memcpy(out + 1, prov->conf_inputs.prv_pub_key, 64);
> + prov->trans_tx(prov->trans_data, out, 65);
> + prov->state = INT_PROV_KEY_SENT;
> +}
> +
> +static void send_confirm(struct mesh_prov_initiator *prov)
> +{
> + uint8_t out[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);
> + prov->state = INT_PROV_CONF_SENT;
> + prov->expected = PROV_CONFIRM;
> +}
> +
> +static void send_random(struct mesh_prov_initiator *prov)
> +{
> + uint8_t out[17];
> +
> + out[0] = PROV_RANDOM;
> + memcpy(out + 1, prov->rand_auth_workspace, 16);
> + prov->trans_tx(prov->trans_data, out, 17);
> + prov->state = INT_PROV_RAND_SENT;
> + prov->expected = PROV_RANDOM;
> +}
> +
> +void initiator_prov_data(uint16_t net_idx, uint16_t primary, void *caller_data)
> +{
> + struct keyring_net_key key;
> + struct mesh_net *net;
> + uint64_t mic;
> + uint32_t iv_index;
> + uint8_t snb_flags;
> + uint8_t out[34];
> +
> + if (!prov || caller_data != prov->caller_data)
> + return;
> +
> + if (prov->state != INT_PROV_RAND_ACKED)
> + return;
> +
> + net = node_get_net(prov->node);
> + prov->expected = PROV_COMPLETE;
> +
> + /* Calculate remote device key */
> + mesh_crypto_device_key(prov->secret,
> + prov->salt,
> + prov->calc_key);
> +
> + print_packet("DevKey", prov->calc_key, 16);
> +
> + /* Fill Prov Data Structure */
> + if (!keyring_get_net_key(prov->node, net_idx, &key)) {
> + out[1] = PROV_ERR_UNEXPECTED_ERR;
> + goto failure;
> + }
> +
> + prov->unicast = primary;
> + prov->net_idx = net_idx;
> + mesh_net_get_snb_state(net, &snb_flags, &iv_index);
> +
> + out[0] = PROV_DATA;
> +
> + if (key.phase == KEY_REFRESH_PHASE_TWO) {
> + memcpy(out + 1, key.new_key, 16);
> + snb_flags |= PROV_FLAG_KR;
> + } else
> + memcpy(out + 1, key.old_key, 16);
> +
> + l_put_be16(net_idx, out + 1 + 16);
> + l_put_u8(snb_flags, out + 1 + 16 + 2);
> + l_put_be32(iv_index, out + 1 + 16 + 2 + 1);
> + l_put_be16(primary, out + 1 + 16 + 2 + 1 + 4);
Not really related to this patchset, but a general comment about packet
building and parsing: could we please move away from calculating byte
offsets and use packed structs instead? Do you have any opinion on that?
> +
> + print_packet("ProvData", out + 1, 25);
> + /* Encrypt Prov Data */
> + mesh_crypto_aes_ccm_encrypt(prov->s_nonce, prov->s_key,
> + NULL, 0,
> + out + 1,
> + 25,
> + out + 1,
> + &mic, sizeof(mic));
> + print_packet("EncData", out + 1, 25 + 8);
> + prov->trans_tx(prov->trans_data, out, 34);
> + prov->state = INT_PROV_DATA_SENT;
> + return;
> +
> +failure:
> + l_debug("Failing... %d", out[1]);
> + out[0] = PROV_FAILED;
> + prov->trans_tx(prov->trans_data, out, 2);
> + /* TODO: Call Complete Callback (Fail)*/
> +}
> +
> +
> static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
> {
> struct mesh_prov_initiator *rx_prov = user_data;
> @@ -314,7 +437,6 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
> uint8_t type = *data++;
> uint8_t fail_code[2];
> uint32_t oob_key;
> - uint64_t mic;
>
> if (rx_prov != prov || !prov->trans_tx)
> return;
> @@ -425,8 +547,7 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
> int_credentials(prov);
> prov->state = INT_PROV_KEY_ACKED;
>
> - prov->expected = PROV_CONFIRM;
> -
> + l_debug("auth_method: %d", prov->conf_inputs.start.auth_method);
> memset(prov->rand_auth_workspace + 16, 0, 32);
> switch (prov->conf_inputs.start.auth_method) {
> default:
> @@ -464,7 +585,6 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
>
> break;
>
> -
> case 3:
> /* Auth Type 3b - input OOB */
> l_getrandom(&oob_key, sizeof(oob_key));
> @@ -492,19 +612,16 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
>
> break;
>
> -
> }
> +
> + if (prov->material & MAT_RAND_AUTH)
> + send_confirm(prov);
> +
> 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);
> + send_confirm(prov);
> break;
>
> case PROV_CONFIRM: /* Confirmation */
> @@ -512,58 +629,46 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
> /* 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);
> + send_random(prov);
> break;
>
> case PROV_RANDOM: /* Random */
> prov->state = INT_PROV_RAND_ACKED;
>
> /* RXed Device Confirmation */
> + calc_local_material(data);
> 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);
>
> + print_packet("Dev-Conf", prov->rand_auth_workspace, 16);
> if (memcmp(prov->rand_auth_workspace, prov->confirm, 16)) {
> - l_error("Provisioning Failed-Confirm compare)");
> + 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);
> + if (!prov->get_prov_data(prov->caller_data,
> + prov->conf_inputs.caps.num_ele)) {
> + l_error("Provisioning Failed-Data Get");
> + fail_code[1] = PROV_ERR_CANT_ASSIGN_ADDR;
> + goto failure;
> }
> break;
>
> case PROV_COMPLETE: /* Complete */
> l_info("Provisioning Complete");
> prov->state = INT_PROV_IDLE;
> - //mesh_prov_close(prov, 0);
> + int_prov_close(prov, PROV_ERR_SUCCESS);
> break;
>
> case PROV_FAILED: /* Failed */
> l_error("Provisioning Failed (reason: %d)", data[0]);
> - //mesh_prov_close(prov, data[0]);
> + prov->state = INT_PROV_IDLE;
> + int_prov_close(prov, data[0]);
> break;
>
> default:
> @@ -572,20 +677,51 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
> goto failure;
> }
>
> - prov->previous = type;
> + if (prov)
> + prov->previous = type;
> +
> return;
>
> failure:
> + l_debug("Failing... %d", fail_code[1]);
> fail_code[0] = PROV_FAILED;
> prov->trans_tx(prov->trans_data, fail_code, 2);
> - /* TODO: Call Complete Callback (Fail)*/
> + int_prov_close(prov, fail_code[1]);
> }
>
> static void int_prov_ack(void *user_data, uint8_t msg_num)
> {
> - /* TODO: Handle PB-ADV Ack */
> -}
> + struct mesh_prov_initiator *rx_prov = user_data;
>
> + if (rx_prov != prov || !prov->trans_tx)
> + return;
> +
> + switch(prov->state) {
ERROR: space required before the open parenthesis '('
> + case INT_PROV_START_SENT:
> + prov->state = INT_PROV_START_ACKED;
> + if (prov->conf_inputs.caps.pub_type == 0)
> + send_pub_key(prov);
> + break;
> +
> + case INT_PROV_DATA_SENT:
> + prov->state = INT_PROV_DATA_ACKED;
> + break;
> +
> + case INT_PROV_IDLE:
> + case INT_PROV_INVITE_SENT:
> + case INT_PROV_INVITE_ACKED:
> + case INT_PROV_START_ACKED:
> + case INT_PROV_KEY_SENT:
> + case INT_PROV_KEY_ACKED:
> + case INT_PROV_CONF_SENT:
> + case INT_PROV_CONF_ACKED:
> + case INT_PROV_RAND_SENT:
> + case INT_PROV_RAND_ACKED:
> + case INT_PROV_DATA_ACKED:
> + default:
> + break;
> + }
> +}
>
> bool initiator_start(enum trans_type transport,
> uint8_t uuid[16],
> @@ -593,8 +729,9 @@ bool initiator_start(enum trans_type transport,
> uint16_t server, /* Only valid for PB-Remote */
> uint32_t timeout, /* in seconds from mesh.conf */
> struct mesh_agent *agent,
> + mesh_prov_initiator_data_req_func_t get_prov_data,
> mesh_prov_initiator_complete_func_t complete_cb,
> - void *caller_data)
> + void *node, void *caller_data)
> {
> bool result;
>
> @@ -607,13 +744,15 @@ bool initiator_start(enum trans_type transport,
>
> prov = l_new(struct mesh_prov_initiator, 1);
> prov->to_secs = timeout;
> + prov->node = node;
> prov->agent = agent;
> prov->cmplt = complete_cb;
> + prov->get_prov_data = get_prov_data;
> 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,
> + result = pb_adv_reg(true, int_prov_open, int_prov_close, int_prov_rx,
> int_prov_ack, uuid, prov);
>
> if (result)
> diff --git a/mesh/provision.h b/mesh/provision.h
> index 6b61a45be..6670de20c 100644
> --- a/mesh/provision.h
> +++ b/mesh/provision.h
> @@ -90,6 +90,7 @@ struct mesh_prov_node_info {
> uint32_t iv_index;
> uint16_t unicast;
> uint16_t net_index;
> + uint8_t cnt;
> uint8_t net_key[16];
> uint8_t device_key[16];
> uint8_t flags; /* IVU and KR bits */
> @@ -99,6 +100,9 @@ typedef bool (*mesh_prov_acceptor_complete_func_t)(void *user_data,
> uint8_t status,
> struct mesh_prov_node_info *info);
>
> +typedef bool (*mesh_prov_initiator_data_req_func_t)(void *user_data,
> + uint8_t num_elem);
> +
> typedef bool (*mesh_prov_initiator_complete_func_t)(void *user_data,
> uint8_t status,
> struct mesh_prov_node_info *info);
> @@ -117,6 +121,8 @@ bool initiator_start(enum trans_type transport,
> uint16_t server, /* Only valid for PB-Remote */
> uint32_t timeout, /* in seconds from mesh.conf */
> struct mesh_agent *agent,
> + mesh_prov_initiator_data_req_func_t get_prov_data,
> mesh_prov_initiator_complete_func_t complete_cb,
> - void *caller_data);
> -void initiator_cancel(void *user_data);
> + void *node, void *caller_data);
> +void initiator_prov_data(uint16_t net_idx, uint16_t primary, void *caller_data);
> +void initiator_cancel(void *caller_data);
> --
> 2.14.5
>
--
Michał Lowas-Rzechonek <[email protected]>
Silvair http://silvair.com
Jasnogórska 44, 31-358 Krakow, POLAND
Hi Michał,
I will fix the style guide and spec alignment issues you pointed at elsewhere...
> -----Original Message-----
> > + if (key.phase == KEY_REFRESH_PHASE_TWO) {
> > + memcpy(out + 1, key.new_key, 16);
> > + snb_flags |= PROV_FLAG_KR;
> > + } else
> > + memcpy(out + 1, key.old_key, 16);
> > +
> > + l_put_be16(net_idx, out + 1 + 16);
> > + l_put_u8(snb_flags, out + 1 + 16 + 2);
> > + l_put_be32(iv_index, out + 1 + 16 + 2 + 1);
> > + l_put_be16(primary, out + 1 + 16 + 2 + 1 + 4);
>
> Not really related to this patchset, but a general comment about packet
> building and parsing: could we please move away from calculating byte offsets
> and use packed structs instead? Do you have any opinion on that?
You are probably right about this. Technically, this is a "Network Packet" (even though
we locally encrypt it before sending) and so we should treat it like any packet we
send over a socket (for instance, the HCI sockets we use to talk to controllers). This is
a vestige of my "old man engineering" where I couldn't always trust compilers across
all platforms to faithfully pack octets the same, but obviously if we are trusting the
packing for HCI, we should be trusting the packing for this. I will scrub the code for
opportunities for packing this (and other similar) structures.