test: Bluetooth: Mesh: add provisioning with OOB auth tests

BubbleSim tests that check provisioning with all possible (from spec)
OOB authentications

Signed-off-by: Aleksandr Khromykh <aleksandr.khromykh@nordicsemi.no>
This commit is contained in:
Aleksandr Khromykh 2021-09-06 09:40:55 +02:00 committed by Christopher Friedt
parent 6f2516d9a7
commit 68d8a263b6
2 changed files with 226 additions and 1 deletions

View file

@ -3,9 +3,12 @@
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <stdlib.h>
#include "mesh_test.h" #include "mesh_test.h"
#include "mesh/net.h" #include "mesh/net.h"
#include "argparse.h" #include "argparse.h"
#include <bs_pc_backchannel.h>
#include <sys/byteorder.h> #include <sys/byteorder.h>
@ -21,6 +24,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#define PROV_MULTI_COUNT 3 #define PROV_MULTI_COUNT 3
#define PROV_REPROV_COUNT 10 #define PROV_REPROV_COUNT 10
#define WAIT_TIME 60 /*seconds*/
enum test_flags { enum test_flags {
IS_PROVISIONER, IS_PROVISIONER,
@ -28,7 +32,33 @@ enum test_flags {
TEST_FLAGS, TEST_FLAGS,
}; };
static uint8_t static_key1[] = {0x6E, 0x6F, 0x72, 0x64, 0x69, 0x63, 0x5F,
0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x31};
static uint8_t static_key2[] = {0x6E, 0x6F, 0x72, 0x64, 0x69, 0x63, 0x5F};
static struct {
const uint8_t *static_val;
uint8_t static_val_len;
uint8_t output_size;
uint16_t output_actions;
uint8_t input_size;
uint16_t input_actions;
} oob_auth_test_vector[] = {
{static_key1, sizeof(static_key1), 0, 0, 0, 0},
{static_key2, sizeof(static_key2), 0, 0, 0, 0},
{NULL, 0, 3, BT_MESH_BLINK, 0, 0},
{NULL, 0, 5, BT_MESH_BEEP, 0, 0},
{NULL, 0, 6, BT_MESH_VIBRATE, 0, 0},
{NULL, 0, 7, BT_MESH_DISPLAY_NUMBER, 0, 0},
{NULL, 0, 8, BT_MESH_DISPLAY_STRING, 0, 0},
{NULL, 0, 0, 0, 4, BT_MESH_PUSH},
{NULL, 0, 0, 0, 5, BT_MESH_TWIST},
{NULL, 0, 0, 0, 8, BT_MESH_ENTER_NUMBER},
{NULL, 0, 0, 0, 7, BT_MESH_ENTER_STRING},
};
static ATOMIC_DEFINE(flags, TEST_FLAGS); static ATOMIC_DEFINE(flags, TEST_FLAGS);
extern const struct bt_mesh_comp comp; extern const struct bt_mesh_comp comp;
extern const uint8_t test_net_key[16]; extern const uint8_t test_net_key[16];
extern const uint8_t test_app_key[16]; extern const uint8_t test_app_key[16];
@ -40,7 +70,11 @@ static uint16_t current_dev_addr;
static const uint8_t dev_key[16] = { 0x01, 0x02, 0x03, 0x04, 0x05 }; static const uint8_t dev_key[16] = { 0x01, 0x02, 0x03, 0x04, 0x05 };
static uint8_t dev_uuid[16] = { 0x6c, 0x69, 0x6e, 0x67, 0x61, 0x6f }; static uint8_t dev_uuid[16] = { 0x6c, 0x69, 0x6e, 0x67, 0x61, 0x6f };
#define WAIT_TIME 60 /*seconds*/ /* Delayed work to avoid requesting OOB info before generation of this. */
static struct k_work_delayable oob_timer;
static void delayed_input(struct k_work *work);
static uint *oob_channel_id;
static void test_device_init(void) static void test_device_init(void)
{ {
@ -48,12 +82,21 @@ static void test_device_init(void)
dev_uuid[6] = '0' + get_device_nbr(); dev_uuid[6] = '0' + get_device_nbr();
bt_mesh_test_cfg_set(NULL, WAIT_TIME); bt_mesh_test_cfg_set(NULL, WAIT_TIME);
k_work_init_delayable(&oob_timer, delayed_input);
} }
static void test_provisioner_init(void) static void test_provisioner_init(void)
{ {
atomic_set_bit(flags, IS_PROVISIONER); atomic_set_bit(flags, IS_PROVISIONER);
bt_mesh_test_cfg_set(NULL, WAIT_TIME); bt_mesh_test_cfg_set(NULL, WAIT_TIME);
k_work_init_delayable(&oob_timer, delayed_input);
}
static void test_terminate(void)
{
if (oob_channel_id) {
bs_clean_back_channels();
}
} }
static void unprovisioned_beacon(uint8_t uuid[16], static void unprovisioned_beacon(uint8_t uuid[16],
@ -87,14 +130,117 @@ static void prov_reset(void)
ASSERT_OK(bt_mesh_prov_enable(BT_MESH_PROV_ADV)); ASSERT_OK(bt_mesh_prov_enable(BT_MESH_PROV_ADV));
} }
static bt_mesh_input_action_t gact;
static uint8_t gsize;
static int input(bt_mesh_input_action_t act, uint8_t size)
{
/* The test system requests the input OOB data earlier than
* the output OOB is generated. Need to release context here
* to allow output OOB creation. OOB will be inserted later
* after the delay.
*/
gact = act;
gsize = size;
k_work_reschedule(&oob_timer, K_SECONDS(1));
return 0;
}
static void delayed_input(struct k_work *work)
{
char oob_str[16];
uint32_t oob_number;
int size = bs_bc_is_msg_received(*oob_channel_id);
if (size <= 0) {
FAIL("OOB data is not gotten");
}
switch (gact) {
case BT_MESH_PUSH:
case BT_MESH_TWIST:
case BT_MESH_ENTER_NUMBER:
ASSERT_TRUE(size == sizeof(uint32_t));
bs_bc_receive_msg(*oob_channel_id, (uint8_t *)&oob_number, size);
ASSERT_OK(bt_mesh_input_number(oob_number));
break;
case BT_MESH_ENTER_STRING:
bs_bc_receive_msg(*oob_channel_id, (uint8_t *)oob_str, size);
ASSERT_OK(bt_mesh_input_string(oob_str));
break;
default:
FAIL("Unknown input action %u (size %u) requested!", gact, gsize);
}
}
static void prov_input_complete(void)
{
LOG_INF("Input OOB data completed");
}
static int output_number(bt_mesh_output_action_t action, uint32_t number);
static int output_string(const char *str);
static void capabilities(const struct bt_mesh_dev_capabilities *cap);
static struct bt_mesh_prov prov = { static struct bt_mesh_prov prov = {
.uuid = dev_uuid, .uuid = dev_uuid,
.unprovisioned_beacon = unprovisioned_beacon, .unprovisioned_beacon = unprovisioned_beacon,
.complete = prov_complete, .complete = prov_complete,
.node_added = prov_node_added, .node_added = prov_node_added,
.output_number = output_number,
.output_string = output_string,
.input = input,
.input_complete = prov_input_complete,
.capabilities = capabilities,
.reset = prov_reset, .reset = prov_reset,
}; };
static int output_number(bt_mesh_output_action_t action, uint32_t number)
{
LOG_INF("OOB Number: %u", number);
bs_bc_send_msg(*oob_channel_id, (uint8_t *)&number, sizeof(uint32_t));
return 0;
}
static int output_string(const char *str)
{
LOG_INF("OOB String: %s", str);
bs_bc_send_msg(*oob_channel_id, (uint8_t *)str, strlen(str) + 1);
return 0;
}
static void capabilities(const struct bt_mesh_dev_capabilities *cap)
{
if (cap->static_oob) {
LOG_INF("Static OOB authentication");
ASSERT_OK(bt_mesh_auth_method_set_static(prov.static_val, prov.static_val_len));
} else if (cap->output_actions) {
LOG_INF("Output OOB authentication");
ASSERT_OK(bt_mesh_auth_method_set_output(prov.output_actions, prov.output_size));
} else if (cap->input_actions) {
LOG_INF("Input OOB authentication");
ASSERT_OK(bt_mesh_auth_method_set_input(prov.input_actions, prov.input_size));
} else if (!oob_channel_id) {
bt_mesh_auth_method_set_none();
} else {
FAIL("No OOB in capability frame");
}
}
static void oob_auth_set(int test_step)
{
ASSERT_TRUE(test_step < ARRAY_SIZE(oob_auth_test_vector));
prov.static_val = oob_auth_test_vector[test_step].static_val;
prov.static_val_len = oob_auth_test_vector[test_step].static_val_len;
prov.output_size = oob_auth_test_vector[test_step].output_size;
prov.output_actions = oob_auth_test_vector[test_step].output_actions;
prov.input_size = oob_auth_test_vector[test_step].input_size;
prov.input_actions = oob_auth_test_vector[test_step].input_actions;
}
/** @brief Verify that this device pb-adv provision. /** @brief Verify that this device pb-adv provision.
*/ */
static void test_device_pb_adv_no_oob(void) static void test_device_pb_adv_no_oob(void)
@ -151,6 +297,71 @@ static void test_provisioner_pb_adv_no_oob(void)
PASS(); PASS();
} }
static void test_device_pb_adv_oob_auth(void)
{
int err = 0;
k_sem_init(&prov_sem, 0, 1);
oob_channel_id = bs_open_back_channel(get_device_nbr(),
(uint[]){(get_device_nbr() + 1) % 2}, (uint[]){0}, 1);
if (!oob_channel_id) {
FAIL("Can't open OOB interface (err %d)\n", err);
}
bt_mesh_device_setup(&prov, &comp);
for (int i = 0; i < ARRAY_SIZE(oob_auth_test_vector); i++) {
oob_auth_set(i);
err = bt_mesh_prov_enable(BT_MESH_PROV_ADV);
ASSERT_OK(err, "Device PB-ADV Enable failed (err %d)", err);
/* Keep a long timeout so the prov multi case has time to finish: */
ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(40)),
"Device provision fail");
/* Delay to complete procedure with Provisioning Complete PDU frame. */
k_sleep(K_SECONDS(1));
bt_mesh_reset();
}
PASS();
}
static void test_provisioner_pb_adv_oob_auth(void)
{
int err = 0;
k_sem_init(&prov_sem, 0, 1);
oob_channel_id = bs_open_back_channel(get_device_nbr(),
(uint[]){(get_device_nbr() + 1) % 2}, (uint[]){0}, 1);
if (!oob_channel_id) {
FAIL("Can't open OOB interface (err %d)\n", err);
}
bt_mesh_device_setup(&prov, &comp);
err = bt_mesh_cdb_create(test_net_key);
ASSERT_OK(err, "Failed to create CDB (err %d)\n", err);
err = bt_mesh_provision(test_net_key, 0, 0, 0, 0x0001, dev_key);
ASSERT_OK(err, "Provisioning failed (err %d)", err);
for (int i = 0; i < ARRAY_SIZE(oob_auth_test_vector); i++) {
oob_auth_set(i);
ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(40)),
"Provisioner provision fail");
bt_mesh_cdb_node_del(bt_mesh_cdb_node_get(prov_addr - 1), true);
}
PASS();
}
/** @brief Verify that the provisioner can provision multiple devices in a row /** @brief Verify that the provisioner can provision multiple devices in a row
*/ */
static void test_provisioner_pb_adv_multi(void) static void test_provisioner_pb_adv_multi(void)
@ -288,13 +499,17 @@ static void test_provisioner_pb_adv_reprovision(void)
.test_post_init_f = test_##role##_init, \ .test_post_init_f = test_##role##_init, \
.test_tick_f = bt_mesh_test_timeout, \ .test_tick_f = bt_mesh_test_timeout, \
.test_main_f = test_##role##_##name, \ .test_main_f = test_##role##_##name, \
.test_delete_f = test_terminate \
} }
static const struct bst_test_instance test_connect[] = { static const struct bst_test_instance test_connect[] = {
TEST_CASE(device, pb_adv_no_oob, TEST_CASE(device, pb_adv_no_oob,
"Device: pb-adv provisioning use no-oob method"), "Device: pb-adv provisioning use no-oob method"),
TEST_CASE(device, pb_adv_oob_auth,
"Device: pb-adv provisioning use oob authentication"),
TEST_CASE(device, pb_adv_reprovision, TEST_CASE(device, pb_adv_reprovision,
"Device: pb-adv provisioning, reprovision"), "Device: pb-adv provisioning, reprovision"),
TEST_CASE(provisioner, pb_adv_no_oob, TEST_CASE(provisioner, pb_adv_no_oob,
"Provisioner: pb-adv provisioning use no-oob method"), "Provisioner: pb-adv provisioning use no-oob method"),
TEST_CASE(provisioner, pb_adv_multi, TEST_CASE(provisioner, pb_adv_multi,
@ -303,6 +518,8 @@ static const struct bst_test_instance test_connect[] = {
"Provisioner: effect on ivu_duration when IV Update flag is set to zero"), "Provisioner: effect on ivu_duration when IV Update flag is set to zero"),
TEST_CASE(provisioner, iv_update_flag_one, TEST_CASE(provisioner, iv_update_flag_one,
"Provisioner: effect on ivu_duration when IV Update flag is set to one"), "Provisioner: effect on ivu_duration when IV Update flag is set to one"),
TEST_CASE(provisioner, pb_adv_oob_auth,
"Provisioner: pb-adv provisioning use oob authentication"),
TEST_CASE(provisioner, pb_adv_reprovision, TEST_CASE(provisioner, pb_adv_reprovision,
"Provisioner: pb-adv provisioning, resetting and reprovisioning multiple times."), "Provisioner: pb-adv provisioning, resetting and reprovisioning multiple times."),

View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Copyright 2021 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh
RunTest mesh_provision_pb_adv_oob_auth \
prov_device_pb_adv_oob_auth prov_provisioner_pb_adv_oob_auth