From 9e4cd9ae78002c1de84667b6969447ce8c5de722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Kopy=C5=9Bci=C5=84ski?= Date: Fri, 8 Oct 2021 12:36:02 +0200 Subject: [PATCH] host/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. This is port of 0335d5fb0147b70f31a5f05c0d776adfff04ccc4 --- nimble/host/mesh/include/mesh/main.h | 17 ++++++ nimble/host/mesh/src/prov.h | 1 + nimble/host/mesh/src/prov_device.c | 84 +++++++++++++++++++++++----- nimble/host/mesh/syscfg.yml | 5 ++ 4 files changed, 92 insertions(+), 15 deletions(-) diff --git a/nimble/host/mesh/include/mesh/main.h b/nimble/host/mesh/include/mesh/main.h index 2bcb05c83..213950b17 100644 --- a/nimble/host/mesh/include/mesh/main.h +++ b/nimble/host/mesh/include/mesh/main.h @@ -101,6 +101,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 */ diff --git a/nimble/host/mesh/src/prov.h b/nimble/host/mesh/src/prov.h index 89f027256..d300b1d5c 100644 --- a/nimble/host/mesh/src/prov.h +++ b/nimble/host/mesh/src/prov.h @@ -75,6 +75,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, }; diff --git a/nimble/host/mesh/src/prov_device.c b/nimble/host/mesh/src/prov_device.c index 38a96e7a6..b7828fa31 100644 --- a/nimble/host/mesh/src/prov_device.c +++ b/nimble/host/mesh/src/prov_device.c @@ -76,8 +76,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); @@ -118,12 +119,16 @@ static void prov_start(const uint8_t *data) return; } - if (data[1] != PUB_KEY_NO_OOB) { + if (data[1] == PUB_KEY_OOB && + !(MYNEWT_VAL(BLE_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; @@ -216,6 +221,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) { struct os_mbuf *buf = PROV_BUF(65); @@ -244,11 +259,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(); } } @@ -264,16 +286,31 @@ 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 (MYNEWT_VAL(BLE_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() @@ -295,7 +332,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 (MYNEWT_VAL(BLE_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); @@ -390,7 +441,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) @@ -492,7 +545,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); diff --git a/nimble/host/mesh/syscfg.yml b/nimble/host/mesh/syscfg.yml index 241ea8f27..d9bb70d7b 100644 --- a/nimble/host/mesh/syscfg.yml +++ b/nimble/host/mesh/syscfg.yml @@ -23,6 +23,11 @@ syscfg.defs: BLE_MESH_PB_ADV or BLE_MESH_PB_GATT is set. value: 1 + BLE_MESH_PROV_OOB_PUBLIC_KEY: + description: > + Enable this option if public key is to be exchanged via Out of Band (OOB) technology. + value: 0 + BLE_MESH_PB_ADV: description: > Enable this option to allow the device to be provisioned over