Bluetooth: Mesh: Refactoring provisioning to make all OOB auth working

The current implementation has hidden dependencies that break
OOB authentication if provisioner does not have the configured
input or output fields used for device capabilities.
It didn't allow to pass several OOB authentication cases.
After refactoring provisioner behavior is independent to
provisionee settings.

Signed-off-by: Aleksandr Khromykh <aleksandr.khromykh@nordicsemi.no>
This commit is contained in:
Aleksandr Khromykh 2021-09-01 13:05:15 +02:00 committed by Christopher Friedt
commit 6f2516d9a7
4 changed files with 151 additions and 96 deletions

View file

@ -107,10 +107,98 @@ static bt_mesh_input_action_t input_action(uint8_t action)
}
}
int bt_mesh_prov_auth(uint8_t method, uint8_t action, uint8_t size)
static int check_output_auth(bt_mesh_output_action_t output, uint8_t size)
{
if (!output) {
return -EINVAL;
}
if (!(bt_mesh_prov->output_actions & output)) {
return -EINVAL;
}
if (size > bt_mesh_prov->output_size) {
return -EINVAL;
}
return 0;
}
static int check_input_auth(bt_mesh_input_action_t input, uint8_t size)
{
if (!input) {
return -EINVAL;
}
if (!(bt_mesh_prov->input_actions & input)) {
return -EINVAL;
}
if (size > bt_mesh_prov->input_size) {
return -EINVAL;
}
return 0;
}
static void get_auth_string(char *str, uint8_t size)
{
uint64_t value;
bt_rand(&value, sizeof(value));
static const char characters[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for (int i = 0; i < size; i++) {
/* pull base-36 digits: */
int idx = value % 36;
value = value / 36;
str[i] = characters[idx];
}
str[size] = '\0';
memcpy(bt_mesh_prov_link.auth, str, size);
memset(bt_mesh_prov_link.auth + size, 0,
sizeof(bt_mesh_prov_link.auth) - size);
}
static uint32_t get_auth_number(bt_mesh_output_action_t output,
bt_mesh_input_action_t input, uint8_t size)
{
const uint32_t divider[PROV_IO_OOB_SIZE_MAX] = { 10, 100, 1000, 10000,
100000, 1000000, 10000000, 100000000 };
uint32_t num = 0;
bt_rand(&num, sizeof(num));
if (output == BT_MESH_BLINK ||
output == BT_MESH_BEEP ||
output == BT_MESH_VIBRATE ||
input == BT_MESH_PUSH ||
input == BT_MESH_TWIST) {
/* According to the Bluetooth Mesh Profile
* Specification Section 5.4.2.4, blink, beep
* vibrate, push and twist should be a random integer
* between 0 and 10^size, *exclusive*:
*/
num = (num % (divider[size - 1] - 1)) + 1;
} else {
num %= divider[size - 1];
}
sys_put_be32(num, &bt_mesh_prov_link.auth[12]);
memset(bt_mesh_prov_link.auth, 0, 12);
return num;
}
int bt_mesh_prov_auth(bool is_provisioner, uint8_t method, uint8_t action, uint8_t size)
{
bt_mesh_output_action_t output;
bt_mesh_input_action_t input;
int err;
switch (method) {
case AUTH_METHOD_NO_OOB:
@ -131,80 +219,43 @@ int bt_mesh_prov_auth(uint8_t method, uint8_t action, uint8_t size)
case AUTH_METHOD_OUTPUT:
output = output_action(action);
if (!output) {
return -EINVAL;
if (is_provisioner) {
if (output == BT_MESH_DISPLAY_STRING) {
input = BT_MESH_ENTER_STRING;
atomic_set_bit(bt_mesh_prov_link.flags, WAIT_STRING);
} else {
input = BT_MESH_ENTER_NUMBER;
atomic_set_bit(bt_mesh_prov_link.flags, WAIT_NUMBER);
}
if (!(bt_mesh_prov->output_actions & output)) {
return -EINVAL;
return bt_mesh_prov->input(input, size);
}
if (size > bt_mesh_prov->output_size) {
return -EINVAL;
err = check_output_auth(output, size);
if (err) {
return err;
}
if (output == BT_MESH_DISPLAY_STRING) {
char str[9];
atomic_set_bit(bt_mesh_prov_link.flags, NOTIFY_INPUT_COMPLETE);
get_auth_string(str, size);
return bt_mesh_prov->output_string(str);
}
atomic_set_bit(bt_mesh_prov_link.flags, NOTIFY_INPUT_COMPLETE);
if (output == BT_MESH_DISPLAY_STRING) {
unsigned char str[9];
uint8_t i;
bt_rand(str, size);
/* Normalize to '0' .. '9' & 'A' .. 'Z' */
for (i = 0U; i < size; i++) {
str[i] %= 36;
if (str[i] < 10) {
str[i] += '0';
} else {
str[i] += 'A' - 10;
}
}
str[size] = '\0';
memcpy(bt_mesh_prov_link.auth, str, size);
(void)memset(bt_mesh_prov_link.auth + size, 0,
sizeof(bt_mesh_prov_link.auth) - size);
return bt_mesh_prov->output_string((char *)str);
} else {
uint32_t div[8] = { 10, 100, 1000, 10000, 100000,
1000000, 10000000, 100000000 };
uint32_t num;
bt_rand(&num, sizeof(num));
if (output == BT_MESH_BLINK ||
output == BT_MESH_BEEP ||
output == BT_MESH_VIBRATE) {
/* According to the Bluetooth Mesh Profile
* Specification Section 5.4.2.4, blink, beep
* and vibrate should be a random integer
* between 0 and 10^size, *exclusive*:
*/
num = (num % (div[size - 1] - 1)) + 1;
} else {
num %= div[size - 1];
}
sys_put_be32(num, &bt_mesh_prov_link.auth[12]);
(void)memset(bt_mesh_prov_link.auth, 0, 12);
return bt_mesh_prov->output_number(output, num);
}
return bt_mesh_prov->output_number(output,
get_auth_number(output, BT_MESH_NO_INPUT, size));
case AUTH_METHOD_INPUT:
input = input_action(action);
if (!input) {
return -EINVAL;
}
if (!(bt_mesh_prov->input_actions & input)) {
return -EINVAL;
}
if (size > bt_mesh_prov->input_size) {
return -EINVAL;
if (!is_provisioner) {
err = check_input_auth(input, size);
if (err) {
return err;
}
if (input == BT_MESH_ENTER_STRING) {
@ -214,6 +265,20 @@ int bt_mesh_prov_auth(uint8_t method, uint8_t action, uint8_t size)
}
return bt_mesh_prov->input(input, size);
}
if (input == BT_MESH_ENTER_STRING) {
char str[9];
atomic_set_bit(bt_mesh_prov_link.flags, NOTIFY_INPUT_COMPLETE);
get_auth_string(str, size);
return bt_mesh_prov->output_string(str);
}
atomic_set_bit(bt_mesh_prov_link.flags, NOTIFY_INPUT_COMPLETE);
output = BT_MESH_DISPLAY_NUMBER;
return bt_mesh_prov->output_number(output,
get_auth_number(BT_MESH_NO_OUTPUT, input, size));
default:
return -EINVAL;
@ -239,11 +304,16 @@ int bt_mesh_input_string(const char *str)
{
BT_DBG("%s", log_strdup(str));
if (strlen(str) > PROV_IO_OOB_SIZE_MAX ||
strlen(str) > bt_mesh_prov_link.oob_size) {
return -ENOTSUP;
}
if (!atomic_test_and_clear_bit(bt_mesh_prov_link.flags, WAIT_STRING)) {
return -EINVAL;
}
strncpy((char *)bt_mesh_prov_link.auth, str, bt_mesh_prov->input_size);
strcpy((char *)bt_mesh_prov_link.auth, str);
bt_mesh_prov_link.role->input_complete();

View file

@ -67,6 +67,8 @@
#define PROV_ALG_P256 0x00
#define PROV_IO_OOB_SIZE_MAX 8 /* in bytes */
#define PROV_BUF(name, len) \
NET_BUF_SIMPLE_DEFINE(name, PROV_BEARER_BUF_HEADROOM + PDU_OP_LEN + len)
@ -151,7 +153,7 @@ int bt_mesh_prov_reset_state(void (*func)(const uint8_t key[BT_PUB_KEY_LEN]));
bool bt_mesh_prov_active(void);
int bt_mesh_prov_auth(uint8_t method, uint8_t action, uint8_t size);
int bt_mesh_prov_auth(bool is_provisioner, uint8_t method, uint8_t action, uint8_t size);
int bt_mesh_pb_gatt_open(struct bt_conn *conn);
int bt_mesh_pb_gatt_close(struct bt_conn *conn);

View file

@ -150,8 +150,11 @@ static void prov_start(const uint8_t *data)
memcpy(bt_mesh_prov_link.conf_inputs.start, data, PDU_LEN_START);
bt_mesh_prov_link.expect = PROV_PUB_KEY;
bt_mesh_prov_link.oob_method = data[2];
bt_mesh_prov_link.oob_action = data[3];
bt_mesh_prov_link.oob_size = data[4];
if (bt_mesh_prov_auth(data[2], data[3], data[4]) < 0) {
if (bt_mesh_prov_auth(false, data[2], data[3], data[4]) < 0) {
BT_ERR("Invalid authentication method: 0x%02x; "
"action: 0x%02x; size: 0x%02x", data[2], data[3],
data[4]);

View file

@ -109,7 +109,6 @@ static void start_sent(int err, void *cb_data)
static void send_start(void)
{
BT_DBG("");
uint8_t method, action;
PROV_BUF(start, PDU_LEN_START);
@ -125,26 +124,6 @@ static void send_start(void)
net_buf_simple_add_u8(&start, PUB_KEY_NO_OOB);
}
if (bt_mesh_prov_link.oob_method == AUTH_METHOD_INPUT) {
method = AUTH_METHOD_OUTPUT;
if (bt_mesh_prov_link.oob_action == INPUT_OOB_STRING) {
action = OUTPUT_OOB_STRING;
} else {
action = OUTPUT_OOB_NUMBER;
}
} else if (bt_mesh_prov_link.oob_method == AUTH_METHOD_OUTPUT) {
method = AUTH_METHOD_INPUT;
if (bt_mesh_prov_link.oob_action == OUTPUT_OOB_STRING) {
action = INPUT_OOB_STRING;
} else {
action = INPUT_OOB_NUMBER;
}
} else {
method = bt_mesh_prov_link.oob_method;
action = 0x00;
}
net_buf_simple_add_u8(&start, bt_mesh_prov_link.oob_method);
net_buf_simple_add_u8(&start, bt_mesh_prov_link.oob_action);
@ -153,10 +132,11 @@ static void send_start(void)
memcpy(bt_mesh_prov_link.conf_inputs.start, &start.data[1], PDU_LEN_START);
if (bt_mesh_prov_auth(method, action, bt_mesh_prov_link.oob_size) < 0) {
if (bt_mesh_prov_auth(true, bt_mesh_prov_link.oob_method,
bt_mesh_prov_link.oob_action, bt_mesh_prov_link.oob_size) < 0) {
BT_ERR("Invalid authentication method: 0x%02x; "
"action: 0x%02x; size: 0x%02x", method,
action, bt_mesh_prov_link.oob_size);
"action: 0x%02x; size: 0x%02x", bt_mesh_prov_link.oob_method,
bt_mesh_prov_link.oob_action, bt_mesh_prov_link.oob_size);
return;
}
@ -691,7 +671,7 @@ static void prov_set_method(uint8_t method, uint8_t action, uint8_t size)
int bt_mesh_auth_method_set_input(bt_mesh_input_action_t action, uint8_t size)
{
if (!action || !size || size > 8) {
if (!action || !size || size > PROV_IO_OOB_SIZE_MAX) {
return -EINVAL;
}
@ -701,7 +681,7 @@ int bt_mesh_auth_method_set_input(bt_mesh_input_action_t action, uint8_t size)
int bt_mesh_auth_method_set_output(bt_mesh_output_action_t action, uint8_t size)
{
if (!action || !size || size > 8) {
if (!action || !size || size > PROV_IO_OOB_SIZE_MAX) {
return -EINVAL;
}