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,89 +219,66 @@ 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);
}
return bt_mesh_prov->input(input, size);
}
if (!(bt_mesh_prov->output_actions & output)) {
return -EINVAL;
err = check_output_auth(output, size);
if (err) {
return err;
}
if (size > bt_mesh_prov->output_size) {
return -EINVAL;
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 (!is_provisioner) {
err = check_input_auth(input, size);
if (err) {
return err;
}
if (size > bt_mesh_prov->input_size) {
return -EINVAL;
if (input == BT_MESH_ENTER_STRING) {
atomic_set_bit(bt_mesh_prov_link.flags, WAIT_STRING);
} else {
atomic_set_bit(bt_mesh_prov_link.flags, WAIT_NUMBER);
}
return bt_mesh_prov->input(input, size);
}
if (input == BT_MESH_ENTER_STRING) {
atomic_set_bit(bt_mesh_prov_link.flags, WAIT_STRING);
} else {
atomic_set_bit(bt_mesh_prov_link.flags, WAIT_NUMBER);
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);
}
return bt_mesh_prov->input(input, size);
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();