diff --git a/doc/reference/bluetooth/mesh/provisioning.rst b/doc/reference/bluetooth/mesh/provisioning.rst index a6ac9d69cad..c75680d5387 100644 --- a/doc/reference/bluetooth/mesh/provisioning.rst +++ b/doc/reference/bluetooth/mesh/provisioning.rst @@ -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 #include +#include +#include +#include + #include #include #include @@ -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);