/* Bluetooth Mesh */ /* * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include "adaptation.h" #include "mesh_crypto.h" #include "net/buf.h" #include "mesh_net.h" #include "adv.h" #include "mesh.h" #include "access.h" #include "foundation.h" #include "proxy.h" #include "prov.h" #include "mesh_settings.h" #define LOG_TAG "[MESH-prov]" #define LOG_INFO_ENABLE #define LOG_DEBUG_ENABLE #define LOG_WARN_ENABLE #define LOG_ERROR_ENABLE #define LOG_DUMP_ENABLE #include "mesh_log.h" #if MESH_RAM_AND_CODE_MAP_DETAIL #ifdef SUPPORT_MS_EXTENSIONS #pragma bss_seg(".ble_mesh_prov_bss") #pragma data_seg(".ble_mesh_prov_data") #pragma const_seg(".ble_mesh_prov_const") #pragma code_seg(".ble_mesh_prov_code") #endif #else /* MESH_RAM_AND_CODE_MAP_DETAIL */ #pragma bss_seg(".ble_mesh_bss") #pragma data_seg(".ble_mesh_data") #pragma const_seg(".ble_mesh_const") #pragma code_seg(".ble_mesh_code") #endif /* MESH_RAM_AND_CODE_MAP_DETAIL */ /* 3 transmissions, 20ms interval */ #define PROV_XMIT BT_MESH_TRANSMIT(2, 20) #define AUTH_METHOD_NO_OOB 0x00 #define AUTH_METHOD_STATIC 0x01 #define AUTH_METHOD_OUTPUT 0x02 #define AUTH_METHOD_INPUT 0x03 #define OUTPUT_OOB_BLINK 0x00 #define OUTPUT_OOB_BEEP 0x01 #define OUTPUT_OOB_VIBRATE 0x02 #define OUTPUT_OOB_NUMBER 0x03 #define OUTPUT_OOB_STRING 0x04 #define INPUT_OOB_PUSH 0x00 #define INPUT_OOB_TWIST 0x01 #define INPUT_OOB_NUMBER 0x02 #define INPUT_OOB_STRING 0x03 #define PUB_KEY_NO_OOB 0x00 #define PUB_KEY_OOB 0x01 #define PROV_ERR_NONE 0x00 #define PROV_ERR_NVAL_PDU 0x01 #define PROV_ERR_NVAL_FMT 0x02 #define PROV_ERR_UNEXP_PDU 0x03 #define PROV_ERR_CFM_FAILED 0x04 #define PROV_ERR_RESOURCES 0x05 #define PROV_ERR_DECRYPT 0x06 #define PROV_ERR_UNEXP_ERR 0x07 #define PROV_ERR_ADDR 0x08 #define PROV_INVITE 0x00 #define PROV_CAPABILITIES 0x01 #define PROV_START 0x02 #define PROV_PUB_KEY 0x03 #define PROV_INPUT_COMPLETE 0x04 #define PROV_CONFIRM 0x05 #define PROV_RANDOM 0x06 #define PROV_DATA 0x07 #define PROV_COMPLETE 0x08 #define PROV_FAILED 0x09 #define PROV_NO_PDU 0xff #define PROV_ALG_P256 0x00 #define GPCF(gpc) (gpc & 0x03) #define GPC_START(last_seg) (((last_seg) << 2) | 0x00) #define GPC_ACK 0x01 #define GPC_CONT(seg_id) (((seg_id) << 2) | 0x02) #define GPC_CTL(op) (((op) << 2) | 0x03) #define START_PAYLOAD_MAX 20 #define CONT_PAYLOAD_MAX 23 #define START_LAST_SEG(gpc) (gpc >> 2) #define CONT_SEG_INDEX(gpc) (gpc >> 2) #define BEARER_CTL(gpc) (gpc >> 2) #define LINK_OPEN 0x00 #define LINK_ACK 0x01 #define LINK_CLOSE 0x02 #define CLOSE_REASON_SUCCESS 0x00 #define CLOSE_REASON_TIMEOUT 0x01 #define CLOSE_REASON_FAILED 0x02 #define XACT_SEG_DATA(_seg) (&link.rx.buf->data[20 + ((_seg - 1) * 23)]) #define XACT_SEG_RECV(_seg) (link.rx.seg &= ~(1 << (_seg))) #define XACT_NVAL 0xff enum { WAIT_PUB_KEY, /* Waiting for local PubKey to be generated */ LINK_ACTIVE, /* Link has been opened */ LINK_ACK_RECVD, /* Ack for link has been received */ LINK_CLOSING, /* Link is closing down */ SEND_PUB_KEY, /* Waiting to send PubKey */ WAIT_NUMBER, /* Waiting for number input from user */ WAIT_STRING, /* Waiting for string input from user */ NOTIFY_INPUT_COMPLETE, /* Notify that input has been completed. */ LINK_INVALID, /* Error occurred during provisioning */ PROVISIONER, /* The link was opened as provisioner */ NUM_FLAGS, }; #if defined(CONFIG_BT_MESH_PROVISIONER) #define PROVISIONER_LINK 1 #else #define PROVISIONER_LINK 0 #endif struct provisioner_link { struct bt_mesh_cdb_node *node; u16_t addr; u16_t net_idx; u8_t attention_duration; u8_t uuid[16]; }; struct prov_link { ATOMIC_DEFINE(flags, NUM_FLAGS); #if defined(CONFIG_BT_MESH_PB_GATT) struct bt_conn *conn; /* GATT connection */ #endif struct provisioner_link provisioner[PROVISIONER_LINK]; u8_t dhkey[32]; /* Calculated DHKey */ u8_t expect; /* Next expected PDU */ u8_t oob_method; u8_t oob_action; u8_t oob_size; u8_t conf[16]; /* Remote Confirmation */ u8_t rand[16]; /* Local Random */ u8_t auth[16]; /* Authentication Value */ u8_t conf_salt[16]; /* ConfirmationSalt */ u8_t conf_key[16]; /* ConfirmationKey */ u8_t conf_inputs[145]; /* ConfirmationInputs */ u8_t prov_salt[16]; /* Provisioning Salt */ #if defined(CONFIG_BT_MESH_PB_ADV) u32_t id; /* Link ID */ struct { u8_t id; /* Transaction ID */ u8_t prev_id; /* Previous Transaction ID */ u8_t seg; /* Bit-field of unreceived segments */ u8_t last_seg; /* Last segment (to check length) */ u8_t fcs; /* Expected FCS value */ struct net_buf_simple *buf; } rx; struct { /* Start timestamp of the transaction */ s64_t start; /* Transaction id*/ u8_t id; /* Pending outgoing buffer(s) */ struct net_buf *buf[3]; /* Retransmit timer */ struct k_delayed_work retransmit; } tx; #endif struct k_delayed_work prot_timer; }; struct prov_rx { u32_t link_id; u8_t xact_id; u8_t gpc; }; /* #define RETRANSMIT_TIMEOUT K_MSEC(500) */ #define RETRANSMIT_TIMEOUT K_MSEC(config_bt_mesh_prov_retransmit_timeout) #define BUF_TIMEOUT K_MSEC(400) /* #define CLOSING_TIMEOUT K_SECONDS(3) */ #define CLOSING_TIMEOUT K_SECONDS(config_bt_mesh_prov_link_close_timeout) /* #define TRANSACTION_TIMEOUT K_SECONDS(30) */ #define TRANSACTION_TIMEOUT K_SECONDS(config_bt_mesh_prov_transaction_timeout) /* #define PROTOCOL_TIMEOUT K_SECONDS(60) */ #define PROTOCOL_TIMEOUT K_SECONDS(config_bt_mesh_prov_protocol_timeout) #if defined(CONFIG_BT_MESH_PB_GATT) #define PROV_BUF_HEADROOM 5 #else #define PROV_BUF_HEADROOM 0 NET_BUF_SIMPLE_DEFINE_STATIC(rx_buf, 65); #endif #define PROV_BUF(name, len) \ NET_BUF_SIMPLE_DEFINE(name, PROV_BUF_HEADROOM + len) static struct prov_link link; static const struct bt_mesh_prov *prov; static void pub_key_ready(const u8_t *pkey); static int reset_state(void) { static struct bt_pub_key_cb pub_key_cb = { .func = pub_key_ready, }; int err; k_delayed_work_cancel(&link.prot_timer); /* Disable Attention Timer if it was set */ if (link.conf_inputs[0]) { bt_mesh_attention(NULL, 0); } #if CONFIG_BT_MESH_PROVISIONER if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && link.provisioner->node != NULL) { bt_mesh_cdb_node_del(link.provisioner->node, false); } #endif /* CONFIG_BT_MESH_PROVISIONER */ #if defined(CONFIG_BT_MESH_PB_GATT) if (link.conn) { bt_conn_unref(link.conn); } #endif #if defined(CONFIG_BT_MESH_PB_ADV) /* Clear everything except the retransmit and protocol timer * delayed work objects. */ (void)memset(&link, 0, offsetof(struct prov_link, tx.retransmit)); link.rx.prev_id = XACT_NVAL; #if defined(CONFIG_BT_MESH_PB_GATT) link.rx.buf = bt_mesh_proxy_get_buf(); #else net_buf_simple_reset(&rx_buf); link.rx.buf = &rx_buf; #endif /* PB_GATT */ #else /* !PB_ADV */ /* Clear everything except the protocol timer (k_delayed_work) */ (void)memset(&link, 0, offsetof(struct prov_link, prot_timer)); #endif /* PB_ADV */ err = bt_pub_key_gen(&pub_key_cb); if (err) { BT_ERR("Failed to generate public key (%d)", err); return err; } return 0; } #if defined(CONFIG_BT_MESH_PB_ADV) static void buf_sent(int err, void *user_data) { if (!link.tx.buf[0]) { return; } k_delayed_work_submit(&link.tx.retransmit, RETRANSMIT_TIMEOUT); } static struct bt_mesh_send_cb buf_sent_cb = { .end = buf_sent, }; static void free_segments(void) { int i; for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { struct net_buf *buf = link.tx.buf[i]; if (!buf) { break; } link.tx.buf[i] = NULL; /* Mark as canceled */ BT_MESH_ADV(buf)->busy = 0U; net_buf_unref(buf); } } static void prov_clear_tx(void) { BT_INFO("--func=%s", __FUNCTION__); BT_DBG(""); k_delayed_work_cancel(&link.tx.retransmit); free_segments(); } static void reset_adv_link(void) { prov_clear_tx(); if (prov->link_close) { prov->link_close(BT_MESH_PROV_ADV); } reset_state(); } static struct net_buf *adv_buf_create(void) { struct net_buf *buf; buf = bt_mesh_adv_create(BT_MESH_ADV_PROV, PROV_XMIT, BUF_TIMEOUT); if (!buf) { BT_ERR("Out of provisioning buffers"); return NULL; } return buf; } static u8_t pending_ack = XACT_NVAL; static void ack_complete(u16_t duration, int err, void *user_data) { BT_DBG("xact %u complete", (u8_t)pending_ack); pending_ack = XACT_NVAL; } static void gen_prov_ack_send(u8_t xact_id) { BT_INFO("--func=%s", __FUNCTION__); static const struct bt_mesh_send_cb cb = { .start = ack_complete, }; const struct bt_mesh_send_cb *complete; struct net_buf *buf; BT_DBG("xact_id %u", xact_id); if (pending_ack == xact_id) { BT_DBG("Not sending duplicate ack"); return; } buf = adv_buf_create(); if (!buf) { return; } printf("buf->ref:%d", buf->ref); if (pending_ack == XACT_NVAL) { pending_ack = xact_id; complete = &cb; } else { complete = NULL; } net_buf_add_be32(buf, link.id); net_buf_add_u8(buf, xact_id); net_buf_add_u8(buf, GPC_ACK); bt_mesh_adv_send(buf, complete, NULL); net_buf_unref(buf); } static void send_reliable(void) { int i; link.tx.start = k_uptime_get(); for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { struct net_buf *buf = link.tx.buf[i]; if (!buf) { break; } if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) { bt_mesh_adv_send(buf, NULL, NULL); } else { bt_mesh_adv_send(buf, &buf_sent_cb, NULL); } } } static int bearer_ctl_send(u8_t op, const void *data, u8_t data_len) { struct net_buf *buf; BT_DBG("op 0x%02x data_len %u", op, data_len); prov_clear_tx(); buf = adv_buf_create(); if (!buf) { return -ENOBUFS; } net_buf_add_be32(buf, link.id); /* Transaction ID, always 0 for Bearer messages */ net_buf_add_u8(buf, 0x00); net_buf_add_u8(buf, GPC_CTL(op)); net_buf_add_mem(buf, data, data_len); BT_DBG("cache ctl tx buf 0x%x", buf); link.tx.buf[0] = buf; send_reliable(); return 0; } static u8_t last_seg(u8_t len) { if (len <= START_PAYLOAD_MAX) { return 0; } len -= START_PAYLOAD_MAX; return 1 + (len / CONT_PAYLOAD_MAX); } static inline u8_t next_transaction_id(void) { if (atomic_test_bit(link.flags, PROVISIONER)) { if (link.tx.id != 0x7F) { link.tx.id++; } else { link.tx.id = 0; } } else { if (link.tx.id != 0U && link.tx.id != 0xFF) { link.tx.id++; } else { link.tx.id = 0x80; } } return link.tx.id; } static int prov_send_adv(struct net_buf_simple *msg) { struct net_buf *start, *buf; u8_t seg_len, seg_id; u8_t xact_id; BT_DBG("len %u: %s", msg->len, bt_hex(msg->data, msg->len)); prov_clear_tx(); start = adv_buf_create(); if (!start) { return -ENOBUFS; } xact_id = next_transaction_id(); net_buf_add_be32(start, link.id); net_buf_add_u8(start, xact_id); net_buf_add_u8(start, GPC_START(last_seg(msg->len))); net_buf_add_be16(start, msg->len); net_buf_add_u8(start, bt_mesh_fcs_calc(msg->data, msg->len)); BT_DBG("cache prov_send_adv tx buf 0x%x", start); link.tx.buf[0] = start; seg_len = MIN(msg->len, START_PAYLOAD_MAX); BT_DBG("seg 0 len %u: %s", seg_len, bt_hex(msg->data, seg_len)); net_buf_add_mem(start, msg->data, seg_len); net_buf_simple_pull(msg, seg_len); buf = start; for (seg_id = 1U; msg->len > 0; seg_id++) { if (seg_id >= ARRAY_SIZE(link.tx.buf)) { BT_ERR("Too big message"); free_segments(); return -E2BIG; } buf = adv_buf_create(); if (!buf) { free_segments(); return -ENOBUFS; } BT_DBG("cache prov_send_adv seg tx buf 0x%x", buf); link.tx.buf[seg_id] = buf; seg_len = MIN(msg->len, CONT_PAYLOAD_MAX); BT_DBG("seg_id %u len %u: %s", seg_id, seg_len, bt_hex(msg->data, seg_len)); net_buf_add_be32(buf, link.id); net_buf_add_u8(buf, xact_id); net_buf_add_u8(buf, GPC_CONT(seg_id)); net_buf_add_mem(buf, msg->data, seg_len); net_buf_simple_pull(msg, seg_len); } send_reliable(); return 0; } #endif /* CONFIG_BT_MESH_PB_ADV */ #if defined(CONFIG_BT_MESH_PB_GATT) static int prov_send_gatt(struct net_buf_simple *msg) { if (!link.conn) { return -ENOTCONN; } return bt_mesh_proxy_send(link.conn, BT_MESH_PROXY_PROV, msg); } #endif /* CONFIG_BT_MESH_PB_GATT */ static inline int prov_send(struct net_buf_simple *buf) { k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); #if defined(CONFIG_BT_MESH_PB_GATT) if (link.conn) { return prov_send_gatt(buf); } #endif #if defined(CONFIG_BT_MESH_PB_ADV) return prov_send_adv(buf); #else return 0; #endif } static void prov_buf_init(struct net_buf_simple *buf, u8_t type) { net_buf_simple_reserve(buf, PROV_BUF_HEADROOM); net_buf_simple_add_u8(buf, type); } static void prov_send_fail_msg(u8_t err) { PROV_BUF(buf, 2); prov_buf_init(&buf, PROV_FAILED); net_buf_simple_add_u8(&buf, err); if (prov_send(&buf)) { BT_ERR("Failed to send Provisioning Failed message"); } atomic_set_bit(link.flags, LINK_INVALID); } static void prov_invite(const u8_t *data) { PROV_BUF(buf, 12); BT_DBG("Attention Duration: %u seconds", data[0]); if (data[0]) { bt_mesh_attention(NULL, data[0]); } link.conf_inputs[0] = data[0]; BT_DBG("send PROV_CAPABILITIES"); prov_buf_init(&buf, PROV_CAPABILITIES); /* Number of Elements supported */ net_buf_simple_add_u8(&buf, bt_mesh_elem_count()); /* 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); /* Static OOB Type */ net_buf_simple_add_u8(&buf, prov->static_val ? BIT(0) : 0x00); /* Output OOB Size */ net_buf_simple_add_u8(&buf, prov->output_size); /* Output OOB Action */ net_buf_simple_add_be16(&buf, prov->output_actions); /* Input OOB Size */ net_buf_simple_add_u8(&buf, prov->input_size); /* Input OOB Action */ net_buf_simple_add_be16(&buf, prov->input_actions); memcpy(&link.conf_inputs[1], &buf.data[1], 11); #if 0 BT_DBG("Send Capabilities"); BT_DBG("Elements: %u", buf.data[1]); BT_DBG("Algorithms: %u", buf.data[2]); BT_DBG("Public Key Type: 0x%02x", buf.data[4]); BT_DBG("Static OOB Type: 0x%02x", buf.data[5]); BT_DBG("Output OOB Size: %u", buf.data[6]); BT_DBG("Output OOB Action: 0x%04x", buf.data[7]); BT_DBG("Input OOB Size: %u", buf.data[9]); BT_DBG("Input OOB Action: 0x%04x", buf.data[10]); #endif /* */ if (prov_send(&buf)) { BT_ERR("Failed to send capabilities"); return; } link.expect = PROV_START; } #if defined(CONFIG_BT_MESH_PB_ADV) static void send_invite(void) { PROV_BUF(inv, 2); BT_DBG(""); prov_buf_init(&inv, PROV_INVITE); net_buf_simple_add_u8(&inv, link.provisioner->attention_duration); link.conf_inputs[0] = link.provisioner->attention_duration; if (prov_send(&inv)) { BT_ERR("Failed to send invite"); return; } link.expect = PROV_CAPABILITIES; } #endif static void send_start(void) { PROV_BUF(start, 6); BT_DBG(""); prov_buf_init(&start, PROV_START); net_buf_simple_add_u8(&start, PROV_ALG_P256); net_buf_simple_add_u8(&start, PUB_KEY_NO_OOB); net_buf_simple_add_u8(&start, AUTH_METHOD_NO_OOB); memset(link.auth, 0, sizeof(link.auth)); net_buf_simple_add_u8(&start, 0); /* Auth Action */ net_buf_simple_add_u8(&start, 0); /* Auth Size */ memcpy(&link.conf_inputs[12], &start.data[1], 5); if (prov_send(&start)) { BT_ERR("Failed to send start"); } } #if CONFIG_BT_MESH_PROVISIONER static void prov_capabilities(const u8_t *data) { u16_t algorithms, output_action, input_action; if (!IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) { return; } BT_DBG("Elements: %u", data[0]); algorithms = sys_get_be16(&data[1]); BT_DBG("Algorithms: %u", algorithms); BT_DBG("Public Key Type: 0x%02x", data[3]); BT_DBG("Static OOB Type: 0x%02x", data[4]); BT_DBG("Output OOB Size: %u", data[5]); output_action = sys_get_be16(&data[6]); BT_DBG("Output OOB Action: 0x%04x", output_action); BT_DBG("Input OOB Size: %u", data[8]); input_action = sys_get_be16(&data[9]); BT_DBG("Input OOB Action: 0x%04x", input_action); if (data[0] == 0) { BT_ERR("Invalid number of elements"); prov_send_fail_msg(PROV_ERR_NVAL_FMT); return; } link.provisioner->node = bt_mesh_cdb_node_alloc(link.provisioner->uuid, link.provisioner->addr, data[0], link.provisioner->net_idx); if (link.provisioner->node == NULL) { prov_send_fail_msg(PROV_ERR_RESOURCES); return; } memcpy(&link.conf_inputs[1], data, 11); atomic_set_bit(link.flags, SEND_PUB_KEY); send_start(); } #else static void prov_capabilities(const u8_t *data) { } #endif /* CONFIG_BT_MESH_PROVISIONER */ static bt_mesh_output_action_t output_action(u8_t action) { switch (action) { case OUTPUT_OOB_BLINK: return BT_MESH_BLINK; case OUTPUT_OOB_BEEP: return BT_MESH_BEEP; case OUTPUT_OOB_VIBRATE: return BT_MESH_VIBRATE; case OUTPUT_OOB_NUMBER: return BT_MESH_DISPLAY_NUMBER; case OUTPUT_OOB_STRING: return BT_MESH_DISPLAY_STRING; default: return BT_MESH_NO_OUTPUT; } } static bt_mesh_input_action_t input_action(u8_t action) { switch (action) { case INPUT_OOB_PUSH: return BT_MESH_PUSH; case INPUT_OOB_TWIST: return BT_MESH_TWIST; case INPUT_OOB_NUMBER: return BT_MESH_ENTER_NUMBER; case INPUT_OOB_STRING: return BT_MESH_ENTER_STRING; default: return BT_MESH_NO_INPUT; } } static int prov_auth(u8_t method, u8_t action, u8_t size) { bt_mesh_output_action_t output; bt_mesh_input_action_t input; switch (method) { case AUTH_METHOD_NO_OOB: if (action || size) { return -EINVAL; } (void)memset(link.auth, 0, sizeof(link.auth)); return 0; case AUTH_METHOD_STATIC: if (action || size) { return -EINVAL; } memcpy(link.auth + 16 - prov->static_val_len, prov->static_val, prov->static_val_len); (void)memset(link.auth, 0, sizeof(link.auth) - prov->static_val_len); return 0; case AUTH_METHOD_OUTPUT: output = output_action(action); if (!output) { return -EINVAL; } if (!(prov->output_actions & output)) { return -EINVAL; } if (size > prov->output_size) { return -EINVAL; } atomic_set_bit(link.flags, NOTIFY_INPUT_COMPLETE); if (output == BT_MESH_DISPLAY_STRING) { unsigned char str[9]; u8_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(link.auth, str, size); (void)memset(link.auth + size, 0, sizeof(link.auth) - size); return prov->output_string((char *)str); } else { u32_t div[8] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 }; u32_t num; bt_rand(&num, sizeof(num)); num %= div[size - 1]; sys_put_be32(num, &link.auth[12]); (void)memset(link.auth, 0, 12); return prov->output_number(output, num); } case AUTH_METHOD_INPUT: input = input_action(action); if (!input) { return -EINVAL; } if (!(prov->input_actions & input)) { return -EINVAL; } if (size > prov->input_size) { return -EINVAL; } if (input == BT_MESH_ENTER_STRING) { atomic_set_bit(link.flags, WAIT_STRING); } else { atomic_set_bit(link.flags, WAIT_NUMBER); } return prov->input(input, size); default: return -EINVAL; } } static void prov_start(const u8_t *data) { BT_DBG("Algorithm: 0x%02x", data[0]); BT_DBG("Public Key: 0x%02x", data[1]); BT_DBG("Auth Method: 0x%02x", data[2]); BT_DBG("Auth Action: 0x%02x", data[3]); BT_DBG("Auth Size: 0x%02x", data[4]); if (data[0] != PROV_ALG_P256) { BT_ERR("Unknown algorithm 0x%02x", data[0]); prov_send_fail_msg(PROV_ERR_NVAL_FMT); return; } if (data[1] != PUB_KEY_NO_OOB) { BT_ERR("Invalid public key type: 0x%02x", data[1]); prov_send_fail_msg(PROV_ERR_NVAL_FMT); return; } memcpy(&link.conf_inputs[12], data, 5); link.expect = PROV_PUB_KEY; if (prov_auth(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]); prov_send_fail_msg(PROV_ERR_NVAL_FMT); } } static void send_confirm(void) { PROV_BUF(cfm, 17); BT_DBG("ConfInputs[0] %s", bt_hex(link.conf_inputs, 64)); BT_DBG("ConfInputs[64] %s", bt_hex(&link.conf_inputs[64], 64)); BT_DBG("ConfInputs[128] %s", bt_hex(&link.conf_inputs[128], 17)); if (bt_mesh_prov_conf_salt(link.conf_inputs, link.conf_salt)) { BT_ERR("Unable to generate confirmation salt"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } BT_DBG("ConfirmationSalt: %s", bt_hex(link.conf_salt, 16)); if (bt_mesh_prov_conf_key(link.dhkey, link.conf_salt, link.conf_key)) { BT_ERR("Unable to generate confirmation key"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } BT_DBG("ConfirmationKey: %s", bt_hex(link.conf_key, 16)); if (bt_rand(link.rand, 16)) { BT_ERR("Unable to generate random number"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } BT_DBG("LocalRandom: %s", bt_hex(link.rand, 16)); prov_buf_init(&cfm, PROV_CONFIRM); if (bt_mesh_prov_conf(link.conf_key, link.rand, link.auth, net_buf_simple_add(&cfm, 16))) { BT_ERR("Unable to generate confirmation value"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } if (prov_send(&cfm)) { BT_ERR("Failed to send Provisioning Confirm"); return; } if (atomic_test_bit(link.flags, PROVISIONER)) { link.expect = PROV_CONFIRM; } else { link.expect = PROV_RANDOM; } } static void send_input_complete(void) { PROV_BUF(buf, 1); prov_buf_init(&buf, PROV_INPUT_COMPLETE); if (prov_send(&buf)) { BT_ERR("Failed to send Provisioning Input Complete"); } link.expect = PROV_CONFIRM; } int bt_mesh_input_number(u32_t num) { BT_DBG("%u", num); if (!atomic_test_and_clear_bit(link.flags, WAIT_NUMBER)) { return -EINVAL; } sys_put_be32(num, &link.auth[12]); send_input_complete(); return 0; } int bt_mesh_input_string(const char *str) { BT_DBG("%s", str); if (!atomic_test_and_clear_bit(link.flags, WAIT_STRING)) { return -EINVAL; } strncpy((char *)link.auth, str, prov->input_size); send_input_complete(); return 0; } static void send_pub_key(void) { PROV_BUF(buf, 65); const u8_t *key; key = bt_pub_key_get(); if (!key) { BT_ERR("No public key available"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } BT_DBG("Local Public Key: %s", bt_hex(key, 64)); prov_buf_init(&buf, PROV_PUB_KEY); /* Swap X and Y halves independently to big-endian */ sys_memcpy_swap(net_buf_simple_add(&buf, 32), key, 32); sys_memcpy_swap(net_buf_simple_add(&buf, 32), &key[32], 32); if (atomic_test_bit(link.flags, PROVISIONER)) { /* PublicKeyProvisioner */ memcpy(&link.conf_inputs[17], &buf.data[1], 64); } else { /* PublicKeyRemote */ memcpy(&link.conf_inputs[81], &buf.data[1], 64); } if (prov_send(&buf)) { BT_ERR("Failed to send Public Key"); return; } if (atomic_test_bit(link.flags, PROVISIONER)) { link.expect = PROV_PUB_KEY; } else { if (atomic_test_bit(link.flags, WAIT_NUMBER) || atomic_test_bit(link.flags, WAIT_STRING)) { link.expect = PROV_NO_PDU; /* Wait for input */ } else { link.expect = PROV_CONFIRM; } } } static void prov_dh_key_cb(const u8_t dhkey[32]) { BT_DBG("%p", dhkey); if (!dhkey) { BT_ERR("DHKey generation failed"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } sys_memcpy_swap(link.dhkey, dhkey, 32); BT_DBG("DHkey: %s", bt_hex(link.dhkey, 32)); if (atomic_test_bit(link.flags, PROVISIONER)) { send_confirm(); } else { send_pub_key(); } } static void prov_dh_key_gen(void) { u8_t remote_pk_le[64], *remote_pk; if (atomic_test_bit(link.flags, PROVISIONER)) { remote_pk = &link.conf_inputs[81]; } else { remote_pk = &link.conf_inputs[17]; } /* 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. */ sys_memcpy_swap(remote_pk_le, remote_pk, 32); sys_memcpy_swap(&remote_pk_le[32], &remote_pk[32], 32); if (bt_dh_key_gen(remote_pk_le, prov_dh_key_cb)) { BT_ERR("Failed to generate DHKey"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); } } static void prov_pub_key(const u8_t *data) { BT_DBG("Remote Public Key: %s", bt_hex(data, 64)); if (atomic_test_bit(link.flags, PROVISIONER)) { /* PublicKeyDevice */ memcpy(&link.conf_inputs[81], data, 64); #if defined(CONFIG_BT_MESH_PB_ADV) prov_clear_tx(); #endif } else { /* PublicKeyProvisioner */ memcpy(&link.conf_inputs[17], data, 64); if (!bt_pub_key_get()) { /* Clear retransmit timer */ #if defined(CONFIG_BT_MESH_PB_ADV) prov_clear_tx(); #endif atomic_set_bit(link.flags, WAIT_PUB_KEY); BT_WARN("Waiting for local public key"); return; } } prov_dh_key_gen(); } static void pub_key_ready(const u8_t *pkey) { if (!pkey) { BT_WARN("Public key not available"); return; } BT_DBG("Local public key ready"); if (atomic_test_and_clear_bit(link.flags, WAIT_PUB_KEY)) { if (atomic_test_bit(link.flags, PROVISIONER)) { send_pub_key(); } else { prov_dh_key_gen(); } } } static void notify_input_complete(void) { if (atomic_test_and_clear_bit(link.flags, NOTIFY_INPUT_COMPLETE) && prov->input_complete) { prov->input_complete(); } } static void prov_input_complete(const u8_t *data) { BT_DBG(""); notify_input_complete(); } #if CONFIG_BT_MESH_PROVISIONER static void send_prov_data(void) { PROV_BUF(pdu, 34); struct bt_mesh_cdb_subnet *sub; u8_t session_key[16]; u8_t nonce[13]; int err; err = bt_mesh_session_key(link.dhkey, link.prov_salt, session_key); if (err) { BT_ERR("Unable to generate session key"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } BT_DBG("SessionKey: %s", bt_hex(session_key, 16)); err = bt_mesh_prov_nonce(link.dhkey, link.prov_salt, nonce); if (err) { BT_ERR("Unable to generate session nonce"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } BT_DBG("Nonce: %s", bt_hex(nonce, 13)); err = bt_mesh_dev_key(link.dhkey, link.prov_salt, link.provisioner->node->dev_key); if (err) { BT_ERR("Unable to generate device key"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } BT_DBG("DevKey: %s", bt_hex(link.provisioner->node->dev_key, 16)); sub = bt_mesh_cdb_subnet_get(link.provisioner->node->net_idx); if (sub == NULL) { BT_ERR("No subnet with net_idx %u", link.provisioner->node->net_idx); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } prov_buf_init(&pdu, PROV_DATA); net_buf_simple_add_mem(&pdu, sub->keys[sub->kr_flag].net_key, 16); net_buf_simple_add_be16(&pdu, link.provisioner->node->net_idx); net_buf_simple_add_u8(&pdu, bt_mesh_cdb_subnet_flags(sub)); net_buf_simple_add_be32(&pdu, bt_mesh_cdb.iv_index); net_buf_simple_add_be16(&pdu, link.provisioner->node->addr); net_buf_simple_add(&pdu, 8); /* For MIC */ BT_DBG("net_idx %u, iv_index 0x%08x, addr 0x%04x", link.provisioner->node->net_idx, bt_mesh.iv_index, link.provisioner->node->addr); err = bt_mesh_prov_encrypt(session_key, nonce, &pdu.data[1], &pdu.data[1]); if (err) { BT_ERR("Unable to encrypt provisioning data"); prov_send_fail_msg(PROV_ERR_DECRYPT); return; } if (prov_send(&pdu)) { BT_ERR("Failed to send Provisioning Data"); return; } link.expect = PROV_COMPLETE; } static void prov_complete(const u8_t *data) { if (!IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) { return; } struct bt_mesh_cdb_node *node = link.provisioner->node; #if defined(CONFIG_BT_MESH_PB_ADV) u8_t reason = CLOSE_REASON_SUCCESS; #endif BT_DBG("key %s, net_idx %u, num_elem %u, addr 0x%04x", bt_hex(node->dev_key, 16), node->net_idx, node->num_elem, node->addr); if (IS_ENABLED(CONFIG_BT_SETTINGS)) { bt_mesh_store_cdb_node(node); } link.provisioner->node = NULL; link.expect = PROV_NO_PDU; atomic_set_bit(link.flags, LINK_CLOSING); #if defined(CONFIG_BT_MESH_PB_ADV) bearer_ctl_send(LINK_CLOSE, &reason, sizeof(reason)); #endif if (prov->node_added) { prov->node_added(node->net_idx, node->uuid, node->addr, node->num_elem, node->dev_key); } /* * According to mesh profile spec (5.3.1.4.3), the close message should * be restransmitted at least three times. Retransmit the LINK_CLOSE * message until CLOSING_TIMEOUT has elapsed instead of resetting the * link here. */ } #else static void send_prov_data(void) { } static void prov_complete(const u8_t *data) { } #endif /* CONFIG_BT_MESH_PROVISIONER */ static void send_random(void) { PROV_BUF(rnd, 17); prov_buf_init(&rnd, PROV_RANDOM); net_buf_simple_add_mem(&rnd, link.rand, 16); if (prov_send(&rnd)) { BT_ERR("Failed to send Provisioning Random"); return; } if (atomic_test_bit(link.flags, PROVISIONER)) { link.expect = PROV_RANDOM; } else { link.expect = PROV_DATA; } } static void prov_random(const u8_t *data) { u8_t conf_verify[16]; const u8_t *prov_rand, *dev_rand; BT_DBG("Remote Random: %s", bt_hex(data, 16)); if (bt_mesh_prov_conf(link.conf_key, data, link.auth, conf_verify)) { BT_ERR("Unable to calculate confirmation verification"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } if (memcmp(conf_verify, link.conf, 16)) { BT_ERR("Invalid confirmation value"); BT_DBG("Received: %s", bt_hex(link.conf, 16)); BT_DBG("Calculated: %s", bt_hex(conf_verify, 16)); prov_send_fail_msg(PROV_ERR_CFM_FAILED); return; } if (atomic_test_bit(link.flags, PROVISIONER)) { prov_rand = link.rand; dev_rand = data; } else { prov_rand = data; dev_rand = link.rand; } if (bt_mesh_prov_salt(link.conf_salt, prov_rand, dev_rand, link.prov_salt)) { BT_ERR("Failed to generate provisioning salt"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } BT_DBG("ProvisioningSalt: %s", bt_hex(link.prov_salt, 16)); if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && atomic_test_bit(link.flags, PROVISIONER)) { send_prov_data(); } else { send_random(); } } static void prov_confirm(const u8_t *data) { BT_DBG("Remote Confirm: %s", bt_hex(data, 16)); memcpy(link.conf, data, 16); notify_input_complete(); if (atomic_test_bit(link.flags, PROVISIONER)) { send_random(); } else { send_confirm(); } } static inline bool is_pb_gatt(void) { #if defined(CONFIG_BT_MESH_PB_GATT) return !!link.conn; #else return false; #endif } static void prov_data(const u8_t *data) { PROV_BUF(msg, 1); u8_t session_key[16]; u8_t nonce[13]; u8_t dev_key[16]; u8_t pdu[25]; u8_t flags; u32_t iv_index; u16_t addr; u16_t net_idx; int err; bool identity_enable; BT_DBG(""); err = bt_mesh_session_key(link.dhkey, link.prov_salt, session_key); if (err) { BT_ERR("Unable to generate session key"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } BT_DBG("SessionKey: %s", bt_hex(session_key, 16)); err = bt_mesh_prov_nonce(link.dhkey, link.prov_salt, nonce); if (err) { BT_ERR("Unable to generate session nonce"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } BT_DBG("Nonce: %s", bt_hex(nonce, 13)); err = bt_mesh_prov_decrypt(session_key, nonce, data, pdu); if (err) { BT_ERR("Unable to decrypt provisioning data"); prov_send_fail_msg(PROV_ERR_DECRYPT); return; } err = bt_mesh_dev_key(link.dhkey, link.prov_salt, dev_key); if (err) { BT_ERR("Unable to generate device key"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); return; } BT_DBG("Dev key: %s", bt_hex(dev_key, 16)); net_idx = sys_get_be16(&pdu[16]); flags = pdu[18]; iv_index = sys_get_be32(&pdu[19]); addr = sys_get_be16(&pdu[23]); BT_DBG("net_idx %u iv_index 0x%08x, addr 0x%04x", net_idx, iv_index, addr); prov_buf_init(&msg, PROV_COMPLETE); if (prov_send(&msg)) { BT_ERR("Failed to send Provisioning Complete"); return; } /* Ignore any further PDUs on this link */ link.expect = PROV_NO_PDU; /* Store info, since bt_mesh_provision() will end up clearing it */ if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && BT_MESH_FEATURES_IS_SUPPORT(BT_MESH_FEAT_PROXY)) { identity_enable = is_pb_gatt(); } else { identity_enable = false; } err = bt_mesh_provision(pdu, net_idx, flags, iv_index, addr, dev_key); if (err) { BT_ERR("Failed to provision (err %d)", err); return; } /* After PB-GATT provisioning we should start advertising * using Node Identity. */ if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && BT_MESH_FEATURES_IS_SUPPORT(BT_MESH_FEAT_PROXY) && identity_enable) { bt_mesh_proxy_identity_enable(); } } static void prov_failed(const u8_t *data) { BT_WARN("Error: 0x%02x", data[0]); } static const struct { void (*func)(const u8_t *data); u16_t len; } prov_handlers[] = { { prov_invite, 1 }, { prov_capabilities, 11 }, { prov_start, 5, }, { prov_pub_key, 64 }, { prov_input_complete, 0 }, { prov_confirm, 16 }, { prov_random, 16 }, { prov_data, 33 }, { prov_complete, 0 }, { prov_failed, 1 }, }; #if defined(CONFIG_BT_MESH_PB_ADV) static void (*prov_giving_up_callback)(void); void mesh_prov_giving_up_callback_register(void (*handler)(void)) { prov_giving_up_callback = handler; } static void prov_retransmit(struct k_work *work) { int i, timeout; BT_DBG(""); BT_INFO("--func=%s", __FUNCTION__); if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { BT_WARN("Link not active"); return; } if (atomic_test_bit(link.flags, LINK_CLOSING)) { timeout = CLOSING_TIMEOUT; } else { timeout = TRANSACTION_TIMEOUT; } if (k_uptime_get() - link.tx.start > timeout) { BT_WARN("Giving up transaction"); reset_adv_link(); if (prov_giving_up_callback) { /* prov_giving_up_callback(); */ } return; } for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { struct net_buf *buf = link.tx.buf[i]; if (!buf) { break; } if (BT_MESH_ADV(buf)->busy) { continue; } BT_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len)); if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) { bt_mesh_adv_send(buf, NULL, NULL); } else { bt_mesh_adv_send(buf, &buf_sent_cb, NULL); } } } extern int bt_mesh_pb_adv_open(const u8_t uuid[16], u16_t net_idx, u16_t addr, u8_t attention_duration); void link_start(struct net_buf_simple *buf) { printf("Link start"); u8_t uuid[16]; int i; for (i = 0; i < 16; i++) { uuid[i] = net_buf_simple_pull_u8(buf); } struct bt_mesh_cdb_subnet *sub = bt_mesh_cdb_subnet_get(0); if (sub == NULL) { bt_mesh_cdb_subnet_alloc(0); } bt_mesh_pb_adv_open(uuid, 0, BT_MESH_ADDR_UNASSIGNED, 0); } static void link_open(struct prov_rx *rx, struct net_buf_simple *buf) { BT_DBG("len %u", buf->len); if (buf->len < 16) { BT_ERR("Too short bearer open message (len %u)", buf->len); return; } if (atomic_test_bit(link.flags, LINK_ACTIVE)) { /* Send another link ack if the provisioner missed the last */ if (link.id == rx->link_id && link.expect == PROV_INVITE) { BT_DBG("Resending link ack"); bearer_ctl_send(LINK_ACK, NULL, 0); } else { BT_WARN("Ignoring bearer open: link already active"); } return; } if (memcmp(buf->data, prov->uuid, 16)) { BT_DBG("Bearer open message not for us"); return; } if (prov->link_open) { prov->link_open(BT_MESH_PROV_ADV); } link.id = rx->link_id; atomic_set_bit(link.flags, LINK_ACTIVE); net_buf_simple_reset(link.rx.buf); BT_DBG("send LINK_ACK"); bearer_ctl_send(LINK_ACK, NULL, 0); link.expect = PROV_INVITE; } static void link_ack(struct prov_rx *rx, struct net_buf_simple *buf) { BT_DBG("len %u", buf->len); if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && atomic_test_bit(link.flags, PROVISIONER)) { if (atomic_test_and_set_bit(link.flags, LINK_ACK_RECVD)) { return; } prov_clear_tx(); if (prov->link_open) { prov->link_open(BT_MESH_PROV_ADV); } send_invite(); } } static void link_close(struct prov_rx *rx, struct net_buf_simple *buf) { BT_DBG("len %u", buf->len); reset_adv_link(); } static void gen_prov_ctl(struct prov_rx *rx, struct net_buf_simple *buf) { BT_DBG("op 0x%02x len %u", BEARER_CTL(rx->gpc), buf->len); switch (BEARER_CTL(rx->gpc)) { case LINK_OPEN: BT_DBG("LINK_OPEN"); link_open(rx, buf); break; case LINK_ACK: BT_DBG("LINK_ACK"); if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { return; } link_ack(rx, buf); break; case LINK_CLOSE: BT_DBG("LINK_CLOSE"); if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { return; } link_close(rx, buf); break; default: BT_ERR("Unknown bearer opcode: 0x%02x", BEARER_CTL(rx->gpc)); return; } } static const char *const prov_handlers_name_table[] = { STR(prov_invite), STR(prov_capabilities), STR(prov_start), STR(prov_pub_key), STR(prov_input_complete), STR(prov_confirm), STR(prov_random), STR(prov_data), STR(prov_complete), STR(prov_failed), }; static void prov_msg_recv(void) { u8_t type = link.rx.buf->data[0]; BT_DBG("type 0x%02x len %u", type, link.rx.buf->len); k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); if (!bt_mesh_fcs_check(link.rx.buf, link.rx.fcs)) { BT_ERR("Incorrect FCS"); return; } BT_DBG("send prov_msg_recv ACK"); gen_prov_ack_send(link.rx.id); link.rx.prev_id = link.rx.id; link.rx.id = 0U; if (atomic_test_bit(link.flags, LINK_INVALID)) { BT_WARN("Unexpected msg 0x%02x on invalidated link", type); prov_send_fail_msg(PROV_ERR_UNEXP_PDU); return; } if (type != PROV_FAILED && type != link.expect) { BT_WARN("Unexpected msg 0x%02x != 0x%02x", type, link.expect); prov_send_fail_msg(PROV_ERR_UNEXP_PDU); return; } if (type >= ARRAY_SIZE(prov_handlers)) { BT_ERR("Unknown provisioning PDU type 0x%02x", type); prov_send_fail_msg(PROV_ERR_NVAL_PDU); return; } if (1 + prov_handlers[type].len != link.rx.buf->len) { BT_ERR("Invalid length %u for type 0x%02x", link.rx.buf->len, type); prov_send_fail_msg(PROV_ERR_NVAL_FMT); return; } BT_DBG("\nprov_msg_recv RX \"%s\"\n", prov_handlers_name_table[type]); prov_handlers[type].func(&link.rx.buf->data[1]); } static void gen_prov_cont(struct prov_rx *rx, struct net_buf_simple *buf) { u8_t seg = CONT_SEG_INDEX(rx->gpc); BT_DBG("len %u, seg_index %u", buf->len, seg); if (!link.rx.seg && link.rx.prev_id == rx->xact_id) { BT_WARN("Resending ack"); gen_prov_ack_send(rx->xact_id); return; } if (rx->xact_id != link.rx.id) { BT_WARN("Data for unknown transaction (%u != %u)", rx->xact_id, link.rx.id); return; } if (seg > link.rx.last_seg) { BT_ERR("Invalid segment index %u", seg); prov_send_fail_msg(PROV_ERR_NVAL_FMT); return; } else if (seg == link.rx.last_seg) { u8_t expect_len; expect_len = (link.rx.buf->len - 20U - ((link.rx.last_seg - 1) * 23U)); if (expect_len != buf->len) { BT_ERR("Incorrect last seg len: %u != %u", expect_len, buf->len); prov_send_fail_msg(PROV_ERR_NVAL_FMT); return; } } if (!(link.rx.seg & BIT(seg))) { BT_WARN("Ignoring already received segment"); return; } memcpy(XACT_SEG_DATA(seg), buf->data, buf->len); XACT_SEG_RECV(seg); if (!link.rx.seg) { prov_msg_recv(); } } static void gen_prov_ack(struct prov_rx *rx, struct net_buf_simple *buf) { BT_DBG("len %u", buf->len); if (!link.tx.buf[0]) { return; } if (rx->xact_id == link.tx.id) { /* Don't clear resending of LINK_CLOSE messages */ if (!atomic_test_bit(link.flags, LINK_CLOSING)) { prov_clear_tx(); } /* Send the PubKey when the the Start message is ACK'ed */ if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && atomic_test_and_clear_bit(link.flags, SEND_PUB_KEY)) { if (!bt_pub_key_get()) { atomic_set_bit(link.flags, WAIT_PUB_KEY); BT_WARN("Waiting for local public key"); } else { send_pub_key(); } } } } static void gen_prov_start(struct prov_rx *rx, struct net_buf_simple *buf) { if (link.rx.seg) { BT_WARN("Got Start while there are unreceived segments"); return; } if (link.rx.prev_id == rx->xact_id) { BT_WARN("Resending ack"); gen_prov_ack_send(rx->xact_id); return; } link.rx.buf->len = net_buf_simple_pull_be16(buf); link.rx.id = rx->xact_id; link.rx.fcs = net_buf_simple_pull_u8(buf); BT_DBG("len %u last_seg %u total_len %u fcs 0x%02x", buf->len, START_LAST_SEG(rx->gpc), link.rx.buf->len, link.rx.fcs); if (link.rx.buf->len < 1) { BT_ERR("Ignoring zero-length provisioning PDU"); prov_send_fail_msg(PROV_ERR_NVAL_FMT); return; } if (link.rx.buf->len > link.rx.buf->size) { BT_ERR("Too large provisioning PDU (%u bytes)", link.rx.buf->len); prov_send_fail_msg(PROV_ERR_NVAL_FMT); return; } if (START_LAST_SEG(rx->gpc) > 0 && link.rx.buf->len <= 20U) { BT_ERR("Too small total length for multi-segment PDU"); prov_send_fail_msg(PROV_ERR_NVAL_FMT); return; } link.rx.seg = (1 << (START_LAST_SEG(rx->gpc) + 1)) - 1; link.rx.last_seg = START_LAST_SEG(rx->gpc); memcpy(link.rx.buf->data, buf->data, buf->len); XACT_SEG_RECV(0); if (!link.rx.seg) { prov_msg_recv(); } } static const struct { void (*func)(struct prov_rx *rx, struct net_buf_simple *buf); bool require_link; u8_t min_len; } gen_prov[] = { { gen_prov_start, true, 3 }, { gen_prov_ack, true, 0 }, { gen_prov_cont, true, 0 }, { gen_prov_ctl, false, 0 }, }; static const char *const gen_prov_name_table[] = { STR(gen_prov_start), STR(gen_prov_ack), STR(gen_prov_cont), STR(gen_prov_ctl), }; static void gen_prov_recv(struct prov_rx *rx, struct net_buf_simple *buf) { if (buf->len < gen_prov[GPCF(rx->gpc)].min_len) { BT_ERR("Too short GPC message type %u", GPCF(rx->gpc)); return; } if (!atomic_test_bit(link.flags, LINK_ACTIVE) && gen_prov[GPCF(rx->gpc)].require_link) { BT_DBG("Ignoring message that requires active link"); return; } BT_DBG("\ngen_prov_recv RX \"%s\"\n", gen_prov_name_table[GPCF(rx->gpc)]); gen_prov[GPCF(rx->gpc)].func(rx, buf); } void bt_mesh_pb_adv_recv(struct net_buf_simple *buf) { struct prov_rx rx; if (!bt_prov_active() && bt_mesh_is_provisioned()) { BT_DBG("Ignoring provisioning PDU - already provisioned"); return; } if (buf->len < 6) { BT_WARN("Too short provisioning packet (len %u)", buf->len); return; } rx.link_id = net_buf_simple_pull_be32(buf); rx.xact_id = net_buf_simple_pull_u8(buf); rx.gpc = net_buf_simple_pull_u8(buf); BT_DBG("link_id 0x%08x xact_id %u", rx.link_id, rx.xact_id); if (atomic_test_bit(link.flags, LINK_ACTIVE) && link.id != rx.link_id) { BT_DBG("Ignoring mesh beacon for unknown link"); return; } gen_prov_recv(&rx, buf); } int bt_mesh_pb_adv_open(const u8_t uuid[16], u16_t net_idx, u16_t addr, u8_t attention_duration) { BT_DBG("uuid %s", bt_hex(uuid, 16)); if (atomic_test_and_set_bit(link.flags, LINK_ACTIVE)) { return -EBUSY; } atomic_set_bit(link.flags, PROVISIONER); bt_rand(&link.id, sizeof(link.id)); link.tx.id = 0x7F; memcpy(link.provisioner->uuid, uuid, 16); link.provisioner->addr = addr; link.provisioner->net_idx = net_idx; link.provisioner->attention_duration = attention_duration; net_buf_simple_reset(link.rx.buf); bearer_ctl_send(LINK_OPEN, uuid, 16); return 0; } #endif /* CONFIG_BT_MESH_PB_ADV */ #if defined(CONFIG_BT_MESH_PB_GATT) int bt_mesh_pb_gatt_recv(struct bt_conn *conn, struct net_buf_simple *buf) { u8_t type; BT_INFO("--func=%s", __FUNCTION__); BT_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len)); if (link.conn != conn) { BT_WARN("Data for unexpected connection"); return -ENOTCONN; } if (buf->len < 1) { BT_WARN("Too short provisioning packet (len %u)", buf->len); return -EINVAL; } type = net_buf_simple_pull_u8(buf); BT_DBG("type 0x%02x", type); if (type != PROV_FAILED && type != link.expect) { BT_WARN("Unexpected msg 0x%02x != 0x%02x", type, link.expect); prov_send_fail_msg(PROV_ERR_UNEXP_PDU); return -EINVAL; } if (type >= ARRAY_SIZE(prov_handlers)) { BT_ERR("Unknown provisioning PDU type 0x%02x", type); return -EINVAL; } if (prov_handlers[type].len != buf->len) { BT_ERR("Invalid length %u for type 0x%02x", buf->len, type); return -EINVAL; } prov_handlers[type].func(buf->data); return 0; } int bt_mesh_pb_gatt_open(struct bt_conn *conn) { BT_DBG("conn %p", conn); if (atomic_test_and_set_bit(link.flags, LINK_ACTIVE)) { return -EBUSY; } link.conn = bt_conn_ref(conn); link.expect = PROV_INVITE; if (prov->link_open) { prov->link_open(BT_MESH_PROV_GATT); } return 0; } int bt_mesh_pb_gatt_close(struct bt_conn *conn) { BT_DBG("conn %p", conn); if (link.conn != conn) { BT_ERR("Not connected"); return -ENOTCONN; } if (prov->link_close) { prov->link_close(BT_MESH_PROV_GATT); } return reset_state(); } #endif /* CONFIG_BT_MESH_PB_GATT */ const struct bt_mesh_prov *bt_mesh_prov_get(void) { return prov; } bool bt_prov_active(void) { return atomic_test_bit(link.flags, LINK_ACTIVE); } static void protocol_timeout(struct k_work *work) { BT_DBG("Protocol timeout"); #if defined(CONFIG_BT_MESH_PB_GATT) if (link.conn) { bt_mesh_pb_gatt_close(link.conn); return; } #endif atomic_set_bit(link.flags, LINK_CLOSING); #if defined(CONFIG_BT_MESH_PB_ADV) u8_t reason = CLOSE_REASON_TIMEOUT; link.rx.seg = 0U; bearer_ctl_send(LINK_CLOSE, &reason, sizeof(reason)); /* reset_state(); */ #endif if (prov_giving_up_callback) { prov_giving_up_callback(); } } int bt_mesh_prov_init(const struct bt_mesh_prov *prov_info) { BT_INFO("--func=%s", __FUNCTION__); if (!prov_info) { BT_ERR("No provisioning context provided"); return -EINVAL; } k_delayed_work_init(&link.prot_timer, protocol_timeout); prov = prov_info; #if defined(CONFIG_BT_MESH_PB_ADV) k_delayed_work_init(&link.tx.retransmit, prov_retransmit); #endif return reset_state(); } void bt_mesh_prov_complete(u16_t net_idx, u16_t addr) { if (prov->complete) { prov->complete(net_idx, addr); } } void bt_mesh_prov_reset(void) { if (prov->reset) { prov->reset(); } }