Bluetooth: Mesh: Add OOB Public Key support for provisionee role

This commit allows an unprovisioned device to exchange its public key
using out-of-band techology (see MeshPRFv1.0.1, table 5.19 and section
5.4.2.3).

For in-band public key exchange, the mesh stack uses HCI commands to
generate public and private keys, and DH key. This, however, doesn't
work for OOB public key exchange since there is no command to generate
DH key with a private key provided by an application. Therefore, this
commit adds direct usage of TinyCrypto into the mesh stack for DH key
generation for OOB public key support.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
This commit is contained in:
Pavel Vasilyev 2021-06-09 11:01:57 +02:00 committed by Anas Nashif
parent ecae33740d
commit 0335d5fb01
5 changed files with 123 additions and 22 deletions

View file

@ -86,10 +86,30 @@ Public key exchange
Before the provisioning process can begin, the provisioner and the unprovisioned
device exchange public keys, either in-band or Out of Band (OOB).
In-band public key exchange is a part of the provisioning process. The Out
of Band public key exchange is application-specific. For example,
the unprovisioned device could provide its public key Out of Band by using
a QR code printed on the device packaging.
In-band public key exchange is a part of the provisioning process and always
supported by the unprovisioned device and provisioner.
If the application wants to support public key exchange via OOB, it needs to
provide public and private keys to the mesh stack. The unprovisioned device
will reflect this in its capabilities. The provisioner obtains the public key
via any available OOB mechanism (e.g. the device may advertise a packet
containing the public key or it can be encoded in a QR code printed on the
device packaging). Note that even if the unprovisioned device has specified
the public key for the Out of Band exchange, the provisioner may choose to
exchange the public key in-band if it can't retrieve the public key via OOB
mechanism. In this case, a new key pair will be generated by the mesh stack
for each Provisioning process.
To enable support of OOB public key on the unprovisioned device side,
:option:`CONFIG_BT_MESH_PROV_OOB_PUBLIC_KEY` needs to be enabled. The
application must provide public and private keys before the Provisioning
process is started by initializing pointers to
:c:member:`bt_mesh_prov.public_key_be`
and :c:member:`bt_mesh_prov.private_key_be`. The keys needs to be
provided in big-endian bytes order.
To provide the device's public key obtained via OOB,
call :c:func:`bt_mesh_prov_remote_pub_key_set` on the provisioner side.
Authentication
==============
@ -161,9 +181,6 @@ insecure in the Mesh Profile specification `erratum 16350 <https://www.bluetooth
as AuthValue may be brute forced in real time. To ensure secure provisioning, applications
should use a static OOB value and OOB public key transfer.
To provide the device's public key obtained via OOB,
call :c:func:`bt_mesh_prov_remote_pub_key_set` on the provisioner side.
API reference
*************

View file

@ -105,6 +105,23 @@ struct bt_mesh_prov {
/** Out of Band information field. */
bt_mesh_prov_oob_info_t oob_info;
/** Pointer to Public Key in big-endian for OOB public key type support.
*
* Remember to enable @option{CONFIG_BT_MESH_PROV_OOB_PUBLIC_KEY}
* when initializing this parameter.
*
* Must be used together with @ref bt_mesh_prov::private_key_be.
*/
const uint8_t *public_key_be;
/** Pointer to Private Key in big-endian for OOB public key type support.
*
* Remember to enable @option{CONFIG_BT_MESH_PROV_OOB_PUBLIC_KEY}
* when initializing this parameter.
*
* Must be used together with @ref bt_mesh_prov::public_key_be.
*/
const uint8_t *private_key_be;
/** Static OOB value */
const uint8_t *static_val;
/** Static OOB value length */

View file

@ -28,6 +28,13 @@ config BT_MESH_PROV_DEVICE
help
Enable this option to allow the device to be provisioned into a mesh network.
config BT_MESH_PROV_OOB_PUBLIC_KEY
bool "OOB Public key support"
select TINYCRYPT_ECC_DH
depends on BT_MESH_PROV_DEVICE
help
Enable this option if public key is to be exchanged via Out of Band (OOB) technology.
config BT_MESH_PB_ADV
bool "Provisioning support using the advertising bearer (PB-ADV)"
select BT_MESH_PROV

View file

@ -73,6 +73,7 @@ enum {
WAIT_CONFIRM, /* Wait for send confirm */
WAIT_AUTH, /* Wait for auth response */
OOB_STATIC_KEY, /* OOB Static Authentication */
WAIT_DH_KEY, /* Wait for DH Key */
NUM_FLAGS,
};

View file

@ -13,6 +13,10 @@
#include <sys/util.h>
#include <sys/byteorder.h>
#include <tinycrypt/constants.h>
#include <tinycrypt/ecc.h>
#include <tinycrypt/ecc_dh.h>
#include <net/buf.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
@ -92,8 +96,9 @@ static void prov_invite(const uint8_t *data)
/* Supported algorithms - FIPS P-256 Eliptic Curve */
net_buf_simple_add_be16(&buf, BIT(PROV_ALG_P256));
/* Public Key Type, Only "No OOB" Public Key is supported */
net_buf_simple_add_u8(&buf, PUB_KEY_NO_OOB);
/* Public Key Type */
net_buf_simple_add_u8(&buf,
bt_mesh_prov->public_key_be == NULL ? PUB_KEY_NO_OOB : PUB_KEY_OOB);
/* Static OOB Type */
net_buf_simple_add_u8(&buf, bt_mesh_prov->static_val ? BIT(0) : 0x00);
@ -134,12 +139,16 @@ static void prov_start(const uint8_t *data)
return;
}
if (data[1] != PUB_KEY_NO_OOB) {
if (data[1] == PUB_KEY_OOB &&
!(IS_ENABLED(CONFIG_BT_MESH_PROV_OOB_PUBLIC_KEY) &&
bt_mesh_prov->public_key_be)) {
BT_ERR("Invalid public key type: 0x%02x", data[1]);
prov_fail(PROV_ERR_NVAL_FMT);
return;
}
atomic_set_bit_to(bt_mesh_prov_link.flags, OOB_PUB_KEY, data[1] == PUB_KEY_OOB);
memcpy(&bt_mesh_prov_link.conf_inputs[12], data, 5);
bt_mesh_prov_link.expect = PROV_PUB_KEY;
@ -232,6 +241,16 @@ static void public_key_sent(int err, void *cb_data)
}
}
static void start_auth(void)
{
if (atomic_test_bit(bt_mesh_prov_link.flags, WAIT_NUMBER) ||
atomic_test_bit(bt_mesh_prov_link.flags, WAIT_STRING)) {
bt_mesh_prov_link.expect = PROV_NO_PDU; /* Wait for input */
} else {
bt_mesh_prov_link.expect = PROV_CONFIRM;
}
}
static void send_pub_key(void)
{
PROV_BUF(buf, 65);
@ -260,11 +279,18 @@ static void send_pub_key(void)
return;
}
if (atomic_test_bit(bt_mesh_prov_link.flags, WAIT_NUMBER) ||
atomic_test_bit(bt_mesh_prov_link.flags, WAIT_STRING)) {
bt_mesh_prov_link.expect = PROV_NO_PDU; /* Wait for input */
} else {
bt_mesh_prov_link.expect = PROV_CONFIRM;
start_auth();
}
static void dh_key_gen_complete(void)
{
BT_DBG("DHkey: %s", bt_hex(bt_mesh_prov_link.dhkey, 32));
if (!atomic_test_and_clear_bit(bt_mesh_prov_link.flags, WAIT_DH_KEY) &&
atomic_test_bit(bt_mesh_prov_link.flags, OOB_PUB_KEY)) {
send_confirm();
} else if (!atomic_test_bit(bt_mesh_prov_link.flags, OOB_PUB_KEY)) {
send_pub_key();
}
}
@ -280,17 +306,33 @@ static void prov_dh_key_cb(const uint8_t dhkey[32])
sys_memcpy_swap(bt_mesh_prov_link.dhkey, dhkey, 32);
BT_DBG("DHkey: %s", bt_hex(bt_mesh_prov_link.dhkey, 32));
send_pub_key();
dh_key_gen_complete();
}
static void prov_dh_key_gen(void)
{
uint8_t remote_pk_le[64], *remote_pk;
const uint8_t *remote_pk;
uint8_t remote_pk_le[64];
remote_pk = &bt_mesh_prov_link.conf_inputs[17];
if (IS_ENABLED(CONFIG_BT_MESH_PROV_OOB_PUBLIC_KEY) &&
atomic_test_bit(bt_mesh_prov_link.flags, OOB_PUB_KEY)) {
if (uECC_valid_public_key(remote_pk, &curve_secp256r1)) {
BT_ERR("Public key is not valid");
} else if (uECC_shared_secret(remote_pk, bt_mesh_prov->private_key_be,
bt_mesh_prov_link.dhkey,
&curve_secp256r1) != TC_CRYPTO_SUCCESS) {
BT_ERR("DHKey generation failed");
} else {
dh_key_gen_complete();
return;
}
prov_fail(PROV_ERR_UNEXP_ERR);
return;
}
/* Copy remote key in little-endian for bt_dh_key_gen().
* X and Y halves are swapped independently. The bt_dh_key_gen()
* will also take care of validating the remote public key.
@ -311,7 +353,21 @@ static void prov_pub_key(const uint8_t *data)
/* PublicKeyProvisioner */
memcpy(&bt_mesh_prov_link.conf_inputs[17], data, 64);
if (!bt_pub_key_get()) {
if (IS_ENABLED(CONFIG_BT_MESH_PROV_OOB_PUBLIC_KEY) &&
atomic_test_bit(bt_mesh_prov_link.flags, OOB_PUB_KEY)) {
if (!bt_mesh_prov->public_key_be || !bt_mesh_prov->private_key_be) {
BT_ERR("Public or private key is not ready");
prov_fail(PROV_ERR_UNEXP_ERR);
return;
}
/* No swap needed since user provides public key in big-endian */
memcpy(&bt_mesh_prov_link.conf_inputs[81], bt_mesh_prov->public_key_be, 64);
atomic_set_bit(bt_mesh_prov_link.flags, WAIT_DH_KEY);
start_auth();
} else if (!bt_pub_key_get()) {
/* Clear retransmit timer */
bt_mesh_prov_link.bearer->clear_tx();
atomic_set_bit(bt_mesh_prov_link.flags, WAIT_PUB_KEY);
@ -406,7 +462,9 @@ static void prov_confirm(const uint8_t *data)
notify_input_complete();
send_confirm();
if (!atomic_test_and_clear_bit(bt_mesh_prov_link.flags, WAIT_DH_KEY)) {
send_confirm();
}
}
static inline bool is_pb_gatt(void)
@ -508,7 +566,8 @@ static void prov_data(const uint8_t *data)
static void local_input_complete(void)
{
if (atomic_test_bit(bt_mesh_prov_link.flags, PUB_KEY_SENT)) {
if (atomic_test_bit(bt_mesh_prov_link.flags, PUB_KEY_SENT) ||
atomic_test_bit(bt_mesh_prov_link.flags, OOB_PUB_KEY)) {
send_input_complete();
} else {
atomic_set_bit(bt_mesh_prov_link.flags, INPUT_COMPLETE);