Implementing Eddystone EID (and AES) on the nRF52805

Hello, I'm in the process of attempting to implement Eddystone EID on the nRF52805.

It's been a bit of a nightmare, so I'm finally taking the bold step to make my first forum post!

The current sample for eddystone doesn't have any EID implementations yet... It's under 'TODO'

Here is all the documentation from Google:

 https://github.com/google/eddystone/tree/master/configuration-service

https://github.com/nrfconnect/sdk-zephyr/tree/main/samples/bluetooth/eddystone#id1

For now, I'm stuck on even just figuring out how to encrypt the beacons UUID and get the EID.

Please help Slight smile

Parents
  • Hi,

    The Eddystone sample in nRF Connect SDK (Zephyr) is very basic, and at the same time a full Eddystone beacon is quite complex. This is included in the nRF5 SDK though (Eddystone Beacon Application), so I suggest you consider that if you need it. May I ask why you are interested in Eddysone, seen as it is no longer maintained and Google discontinued support for it in 2018?

  • Hi, thank you for the response!

    I've been trying to understand that example for the past few days.

    I'm attempting to use Eddystone's EID as a convenient way to change my beacon's MAC address to prevent tracking, while still retaining the identity when the MAC address is decrypted. I suppose it doesn't actually have to be Eddystone, I'm just trying to use the existing framework to simplify what I'm trying to do.

  • Hi,

    adojang said:
    is the auth_passkey somehow related to IRK?

    No, these are completely different keys. The IRK is used only to generate and resolve resolvable random addresses. The pass key is used for MITM protection (authentication) during pairing/bonding. If you are new to BLE, I really suggest the primer I linked to in my previous post. There feature set is quite big but that will give you a good overview, also of the security features.

    adojang said:
    I found I was able to connect directly to the device using my iphone and the nrf Connect app without needing any passkey. That's mildly concering as I set it through:

    This is expected. One important thing to understand about Bluetooth security is that the security requirements are linked to specific each characteristic. Even if a device support a number of security features (bonding with legacy or LE secure connections, with or without MITM protection like passkey etc), all these features may not always be used. That is also application specific. Just as an example, iOS devices normally never use higher security level than needed. If they try to access a characteristic and access is denied, it will pair/bond, but not before that. So if you want to require pairing with a passkey you need to configure that for the specific characteristic you want to protect.

    adojang said:
    The nrf spits out error code 0x13 which indicates that [BT_HCI_ERR_REMOTE_USER_TERM_CONN] - although my client certainly did not try to disconnect.

    The log is not very ambiguous, and this is a standardized disconnect reason so I would not be too sure to rule that out. How do you know that the peer (client) did not disconnect? Do you have logs from the peer / client that shows what happened there, and perhaps why id did disconnect?

  • Thank you again for the swift reply!

    It turns out there was an issue with the version of the esp32-arduino SDK I was using which caused the disconnect error.

    I'm attempting to implement IRK using the following code, based on the beacon uart sample

    [EDIT: I have moved to the UART example]

    .PROJ File:

    #
    # Copyright (c) 2018 Nordic Semiconductor
    #
    # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
    #
    
    # Enable the UART driver
    CONFIG_UART_ASYNC_API=y
    CONFIG_NRFX_UARTE0=y
    CONFIG_SERIAL=y
    
    CONFIG_GPIO=y
    
    # Make sure printk is printing to the UART console
    CONFIG_CONSOLE=y
    CONFIG_UART_CONSOLE=y
    
    CONFIG_HEAP_MEM_POOL_SIZE=2048
    
    CONFIG_BT=y
    CONFIG_BT_PERIPHERAL=y
    CONFIG_BT_DEVICE_NAME="Nordic_UART_Service"
    CONFIG_BT_DEVICE_APPEARANCE=833
    CONFIG_BT_MAX_CONN=1
    CONFIG_BT_MAX_PAIRED=1
    
    # Enable the NUS service
    CONFIG_BT_NUS=y
    
    # Enable bonding
    CONFIG_BT_SETTINGS=y
    CONFIG_FLASH=y
    CONFIG_FLASH_PAGE_LAYOUT=y
    CONFIG_FLASH_MAP=y
    CONFIG_NVS=y
    CONFIG_SETTINGS=y
    
    # Enable DK LED and Buttons library
    CONFIG_DK_LIBRARY=y
    
    # This example requires more workqueue stack
    CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
    
    # Config logger
    CONFIG_LOG=y
    CONFIG_USE_SEGGER_RTT=y
    CONFIG_LOG_BACKEND_RTT=y
    CONFIG_LOG_BACKEND_UART=n
    
    CONFIG_ASSERT=y
    

    main.c file:

    /*
     * Copyright (c) 2018 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    /** @file
     *  @brief Nordic UART Bridge Service (NUS) sample
     */
    #include "uart_async_adapter.h"
    
    #include <zephyr/types.h>
    #include <zephyr.h>
    #include <drivers/uart.h>
    #include <usb/usb_device.h>
    
    #include <device.h>
    #include <soc.h>
    
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/uuid.h>
    #include <bluetooth/gatt.h>
    #include <bluetooth/hci.h>
    
    #include <bluetooth/services/nus.h>
    
    #include <dk_buttons_and_leds.h>
    
    #include <settings/settings.h>
    
    #include <stdio.h>
    
    #include <logging/log.h>
    
    #define LOG_MODULE_NAME peripheral_uart
    LOG_MODULE_REGISTER(LOG_MODULE_NAME);
    
    #define STACKSIZE CONFIG_BT_NUS_THREAD_STACK_SIZE
    #define PRIORITY 7
    
    #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
    #define DEVICE_NAME_LEN	(sizeof(DEVICE_NAME) - 1)
    
    #define RUN_STATUS_LED DK_LED1
    #define RUN_LED_BLINK_INTERVAL 1000
    
    #define CON_STATUS_LED DK_LED2
    
    #define KEY_PASSKEY_ACCEPT DK_BTN1_MSK
    #define KEY_PASSKEY_REJECT DK_BTN2_MSK
    
    #define UART_BUF_SIZE CONFIG_BT_NUS_UART_BUFFER_SIZE
    #define UART_WAIT_FOR_BUF_DELAY K_MSEC(50)
    #define UART_WAIT_FOR_RX CONFIG_BT_NUS_UART_RX_WAIT_TIME
    
    static K_SEM_DEFINE(ble_init_ok, 0, 1);
    
    static struct bt_conn *current_conn;
    static struct bt_conn *auth_conn;
    
    static const struct device *uart;
    static struct k_work_delayable uart_work;
    
    struct uart_data_t {
    	void *fifo_reserved;
    	uint8_t data[UART_BUF_SIZE];
    	uint16_t len;
    };
    
    static K_FIFO_DEFINE(fifo_uart_tx_data);
    static K_FIFO_DEFINE(fifo_uart_rx_data);
    
    static const struct bt_data ad[] = {
    	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
    	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
    };
    
    static const struct bt_data sd[] = {
    	BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
    };
    
    #if CONFIG_BT_NUS_UART_ASYNC_ADAPTER
    UART_ASYNC_ADAPTER_INST_DEFINE(async_adapter);
    #else
    static const struct device *const async_adapter;
    #endif
    
    static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
    {
    	ARG_UNUSED(dev);
    
    	static uint8_t *current_buf;
    	static size_t aborted_len;
    	static bool buf_release;
    	struct uart_data_t *buf;
    	static uint8_t *aborted_buf;
    
    	switch (evt->type) {
    	case UART_TX_DONE:
    		LOG_DBG("tx_done");
    		if ((evt->data.tx.len == 0) ||
    		    (!evt->data.tx.buf)) {
    			return;
    		}
    
    		if (aborted_buf) {
    			buf = CONTAINER_OF(aborted_buf, struct uart_data_t,
    					   data);
    			aborted_buf = NULL;
    			aborted_len = 0;
    		} else {
    			buf = CONTAINER_OF(evt->data.tx.buf, struct uart_data_t,
    					   data);
    		}
    
    		k_free(buf);
    
    		buf = k_fifo_get(&fifo_uart_tx_data, K_NO_WAIT);
    		if (!buf) {
    			return;
    		}
    
    		if (uart_tx(uart, buf->data, buf->len, SYS_FOREVER_MS)) {
    			LOG_WRN("Failed to send data over UART");
    		}
    
    		break;
    
    	case UART_RX_RDY:
    		LOG_DBG("rx_rdy");
    		buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data_t, data);
    		buf->len += evt->data.rx.len;
    		buf_release = false;
    
    		if (buf->len == UART_BUF_SIZE) {
    			k_fifo_put(&fifo_uart_rx_data, buf);
    		} else if ((evt->data.rx.buf[buf->len - 1] == '\n') ||
    			  (evt->data.rx.buf[buf->len - 1] == '\r')) {
    			k_fifo_put(&fifo_uart_rx_data, buf);
    			current_buf = evt->data.rx.buf;
    			buf_release = true;
    			uart_rx_disable(uart);
    		}
    
    		break;
    
    	case UART_RX_DISABLED:
    		LOG_DBG("rx_disabled");
    		buf = k_malloc(sizeof(*buf));
    		if (buf) {
    			buf->len = 0;
    		} else {
    			LOG_WRN("Not able to allocate UART receive buffer");
    			k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
    			return;
    		}
    
    		uart_rx_enable(uart, buf->data, sizeof(buf->data),
    			       UART_WAIT_FOR_RX);
    
    		break;
    
    	case UART_RX_BUF_REQUEST:
    		LOG_DBG("rx_buf_request");
    		buf = k_malloc(sizeof(*buf));
    		if (buf) {
    			buf->len = 0;
    			uart_rx_buf_rsp(uart, buf->data, sizeof(buf->data));
    		} else {
    			LOG_WRN("Not able to allocate UART receive buffer");
    		}
    
    		break;
    
    	case UART_RX_BUF_RELEASED:
    		LOG_DBG("rx_buf_released");
    		buf = CONTAINER_OF(evt->data.rx_buf.buf, struct uart_data_t,
    				   data);
    		if (buf_release && (current_buf != evt->data.rx_buf.buf)) {
    			k_free(buf);
    			buf_release = false;
    			current_buf = NULL;
    		}
    
    		break;
    
    	case UART_TX_ABORTED:
    			LOG_DBG("tx_aborted");
    			if (!aborted_buf) {
    				aborted_buf = (uint8_t *)evt->data.tx.buf;
    			}
    
    			aborted_len += evt->data.tx.len;
    			buf = CONTAINER_OF(aborted_buf, struct uart_data_t,
    					   data);
    
    			uart_tx(uart, &buf->data[aborted_len],
    				buf->len - aborted_len, SYS_FOREVER_MS);
    
    		break;
    
    	default:
    		break;
    	}
    }
    
    static void uart_work_handler(struct k_work *item)
    {
    	struct uart_data_t *buf;
    
    	buf = k_malloc(sizeof(*buf));
    	if (buf) {
    		buf->len = 0;
    	} else {
    		LOG_WRN("Not able to allocate UART receive buffer");
    		k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
    		return;
    	}
    
    	uart_rx_enable(uart, buf->data, sizeof(buf->data), UART_WAIT_FOR_RX);
    }
    
    static bool uart_test_async_api(const struct device *dev)
    {
    	const struct uart_driver_api *api =
    			(const struct uart_driver_api *)dev->api;
    
    	return (api->callback_set != NULL);
    }
    
    static int uart_init(void)
    {
    	int err;
    	int pos;
    	struct uart_data_t *rx;
    	struct uart_data_t *tx;
    
    	uart = device_get_binding(CONFIG_BT_NUS_UART_DEV);
    	if (!uart) {
    		return -ENXIO;
    	}
    
    	if (IS_ENABLED(CONFIG_USB_DEVICE_STACK)) {
    		err = usb_enable(NULL);
    		if (err) {
    			LOG_ERR("Failed to enable USB");
    			return err;
    		}
    	}
    
    	rx = k_malloc(sizeof(*rx));
    	if (rx) {
    		rx->len = 0;
    	} else {
    		return -ENOMEM;
    	}
    
    	k_work_init_delayable(&uart_work, uart_work_handler);
    
    
    	if (IS_ENABLED(CONFIG_BT_NUS_UART_ASYNC_ADAPTER) && !uart_test_async_api(uart)) {
    		/* Implement API adapter */
    		uart_async_adapter_init(async_adapter, uart);
    		uart = async_adapter;
    	}
    
    	err = uart_callback_set(uart, uart_cb, NULL);
    	if (err) {
    		LOG_ERR("Cannot initialize UART callback");
    		return err;
    	}
    
    	if (IS_ENABLED(CONFIG_UART_LINE_CTRL)) {
    		LOG_INF("Wait for DTR");
    		while (true) {
    			uint32_t dtr = 0;
    
    			uart_line_ctrl_get(uart, UART_LINE_CTRL_DTR, &dtr);
    			if (dtr) {
    				break;
    			}
    			/* Give CPU resources to low priority threads. */
    			k_sleep(K_MSEC(100));
    		}
    		LOG_INF("DTR set");
    		err = uart_line_ctrl_set(uart, UART_LINE_CTRL_DCD, 1);
    		if (err) {
    			LOG_WRN("Failed to set DCD, ret code %d", err);
    		}
    		err = uart_line_ctrl_set(uart, UART_LINE_CTRL_DSR, 1);
    		if (err) {
    			LOG_WRN("Failed to set DSR, ret code %d", err);
    		}
    	}
    
    	tx = k_malloc(sizeof(*tx));
    
    	if (tx) {
    		pos = snprintf(tx->data, sizeof(tx->data),
    			       "Starting Nordic UART service example\r\n");
    
    		if ((pos < 0) || (pos >= sizeof(tx->data))) {
    			k_free(tx);
    			LOG_ERR("snprintf returned %d", pos);
    			return -ENOMEM;
    		}
    
    		tx->len = pos;
    	} else {
    		return -ENOMEM;
    	}
    
    	err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
    	if (err) {
    		LOG_ERR("Cannot display welcome message (err: %d)", err);
    		return err;
    	}
    
    	return uart_rx_enable(uart, rx->data, sizeof(rx->data), 50);
    }
    
    static void connected(struct bt_conn *conn, uint8_t err)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	if (err) {
    		LOG_ERR("Connection failed (err %u)", err);
    		return;
    	}
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    	LOG_INF("Connected %s", log_strdup(addr));
    
    	current_conn = bt_conn_ref(conn);
    
    	dk_set_led_on(CON_STATUS_LED);
    }
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Disconnected: %s (reason %u)", log_strdup(addr), reason);
    
    	if (auth_conn) {
    		bt_conn_unref(auth_conn);
    		auth_conn = NULL;
    	}
    
    	if (current_conn) {
    		bt_conn_unref(current_conn);
    		current_conn = NULL;
    		dk_set_led_off(CON_STATUS_LED);
    	}
    }
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    static void security_changed(struct bt_conn *conn, bt_security_t level,
    			     enum bt_security_err err)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	if (!err) {
    		LOG_INF("Security changed: %s level %u", log_strdup(addr),
    			level);
    	} else {
    		LOG_WRN("Security failed: %s level %u err %d", log_strdup(addr),
    			level, err);
    	}
    }
    #endif
    
    BT_CONN_CB_DEFINE(conn_callbacks) = {
    	.connected    = connected,
    	.disconnected = disconnected,
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    	.security_changed = security_changed,
    #endif
    };
    
    #if defined(CONFIG_BT_NUS_SECURITY_ENABLED)
    static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Passkey for %s: %06u", log_strdup(addr), passkey);
    }
    
    static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	auth_conn = bt_conn_ref(conn);
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Passkey for %s: %06u", log_strdup(addr), passkey);
    	LOG_INF("Press Button 1 to confirm, Button 2 to reject.");
    }
    
    
    static void auth_cancel(struct bt_conn *conn)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Pairing cancelled: %s", log_strdup(addr));
    }
    
    
    static void pairing_complete(struct bt_conn *conn, bool bonded)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Pairing completed: %s, bonded: %d", log_strdup(addr),
    		bonded);
    }
    
    
    static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Pairing failed conn: %s, reason %d", log_strdup(addr),
    		reason);
    }
    
    
    static struct bt_conn_auth_cb conn_auth_callbacks = {
    	.passkey_display = auth_passkey_display,
    	.passkey_confirm = auth_passkey_confirm,
    	.cancel = auth_cancel,
    	.pairing_complete = pairing_complete,
    	.pairing_failed = pairing_failed
    };
    #else
    static struct bt_conn_auth_cb conn_auth_callbacks;
    #endif
    
    static void bt_receive_cb(struct bt_conn *conn, const uint8_t *const data,
    			  uint16_t len)
    {
    	int err;
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, ARRAY_SIZE(addr));
    
    	LOG_INF("Received data from: %s", log_strdup(addr));
    
    	for (uint16_t pos = 0; pos != len;) {
    		struct uart_data_t *tx = k_malloc(sizeof(*tx));
    
    		if (!tx) {
    			LOG_WRN("Not able to allocate UART send data buffer");
    			return;
    		}
    
    		/* Keep the last byte of TX buffer for potential LF char. */
    		size_t tx_data_size = sizeof(tx->data) - 1;
    
    		if ((len - pos) > tx_data_size) {
    			tx->len = tx_data_size;
    		} else {
    			tx->len = (len - pos);
    		}
    
    		memcpy(tx->data, &data[pos], tx->len);
    
    		pos += tx->len;
    
    		/* Append the LF character when the CR character triggered
    		 * transmission from the peer.
    		 */
    		if ((pos == len) && (data[len - 1] == '\r')) {
    			tx->data[tx->len] = '\n';
    			tx->len++;
    		}
    
    		err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
    		if (err) {
    			k_fifo_put(&fifo_uart_tx_data, tx);
    		}
    	}
    }
    
    static struct bt_nus_cb nus_cb = {
    	.received = bt_receive_cb,
    };
    
    void error(void)
    {
    	dk_set_leds_state(DK_ALL_LEDS_MSK, DK_NO_LEDS_MSK);
    
    	while (true) {
    		/* Spin for ever */
    		k_sleep(K_MSEC(1000));
    	}
    }
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    static void num_comp_reply(bool accept)
    {
    	if (accept) {
    		bt_conn_auth_passkey_confirm(auth_conn);
    		LOG_INF("Numeric Match, conn %p", (void *)auth_conn);
    	} else {
    		bt_conn_auth_cancel(auth_conn);
    		LOG_INF("Numeric Reject, conn %p", (void *)auth_conn);
    	}
    
    	bt_conn_unref(auth_conn);
    	auth_conn = NULL;
    }
    
    void button_changed(uint32_t button_state, uint32_t has_changed)
    {
    	uint32_t buttons = button_state & has_changed;
    
    	if (auth_conn) {
    		if (buttons & KEY_PASSKEY_ACCEPT) {
    			num_comp_reply(true);
    		}
    
    		if (buttons & KEY_PASSKEY_REJECT) {
    			num_comp_reply(false);
    		}
    	}
    }
    #endif /* CONFIG_BT_NUS_SECURITY_ENABLED */
    
    static void configure_gpio(void)
    {
    	int err;
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    	err = dk_buttons_init(button_changed);
    	if (err) {
    		LOG_ERR("Cannot init buttons (err: %d)", err);
    	}
    #endif /* CONFIG_BT_NUS_SECURITY_ENABLED */
    
    	err = dk_leds_init();
    	if (err) {
    		LOG_ERR("Cannot init LEDs (err: %d)", err);
    	}
    }
    
    void main(void)
    {
    	int blink_status = 0;
    	int err = 0;
    
    	configure_gpio();
    
    	err = uart_init();
    	if (err) {
    		error();
    	}
    
    	if (IS_ENABLED(CONFIG_BT_NUS_SECURITY_ENABLED)) {
    		bt_conn_auth_cb_register(&conn_auth_callbacks);
    	}
    
    	err = bt_enable(NULL);
    	if (err) {
    		error();
    	}
    
    	LOG_INF("Bluetooth initialized");
    
    	k_sem_give(&ble_init_ok);
    
    	if (IS_ENABLED(CONFIG_SETTINGS)) {
    		settings_load();
    	}
    
    	err = bt_nus_init(&nus_cb);
    	if (err) {
    		LOG_ERR("Failed to initialize UART service (err: %d)", err);
    		return;
    	}
    
    	err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd,
    			      ARRAY_SIZE(sd));
    	if (err) {
    		LOG_ERR("Advertising failed to start (err %d)", err);
    		return;
    	}
    
    	for (;;) {
    		dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
    		k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
    	}
    }
    
    void ble_write_thread(void)
    {
    	/* Don't go any further until BLE is initialized */
    	k_sem_take(&ble_init_ok, K_FOREVER);
    
    	for (;;) {
    		/* Wait indefinitely for data to be sent over bluetooth */
    		struct uart_data_t *buf = k_fifo_get(&fifo_uart_rx_data,
    						     K_FOREVER);
    
    		if (bt_nus_send(NULL, buf->data, buf->len)) {
    			LOG_WRN("Failed to send data over BLE connection");
    		}
    
    		k_free(buf);
    	}
    }
    
    K_THREAD_DEFINE(ble_write_thread_id, STACKSIZE, ble_write_thread, NULL, NULL,
    		NULL, PRIORITY, 0, 0);
    

    What is required to change this to RPA/IRK Authentication?

    I think it has something to do with line 594, but editing it to

    err = bt_le_adv_start(BT_LE_ADV_OPT_DIR_ADDR_RPA, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
    Does not work. Perhaps I need to create a new #define thing?
  • Hi,

    To use privacy (random resolvable address) simply add CONFIG_BT_PRIVACY=y to your prj.conf. By default the address will change every 900 seconds (15 minutes), but you can also configure that. For instance, CONFIG_BT_RPA_TIMEOUT=60 will make the address change every minute.

  • Great! I've got it complied and running.

    Is there a command or function I need to call to get the IRK key?

    I'd like to get the IRK key, then transmit it over UART to the other BLE device.

    *I'm asking this because I'm assuming that the IRK handling doesn't just happen automatically.

  • Hi,

    IRK handling is automatic in the sense that if you enable privacy (CONFIG_BT_PRIVACY=y), it is automatically generated and used by the stack. However, there are API functions you can use to mage identities (the bt_id_* functions). The simplest way here is perhaps to use bt_id_create(). Depending on the parameters, it will generate an IRK for you (and populate a buffer with it so that you can do with it what you need), or use an IRK you provide. See the API doc for more details.

Reply Children
  • I tried to make a few identities, but I think my issue lies in the parameters I call.

    I'm trying to enable RPA, but there is no BT_LE_ADV_PARAM that exists by default for it.

    I've tried to create my own, and put this in the main.c file:

    	#define BT_LE_ADV_PARAM(_options, _int_min, _int_max, _peer) \
    				((struct bt_le_adv_param[]) { \
    					BT_LE_ADV_PARAM_INIT(_options, _int_min, _int_max, _peer) \
    				})
    
    
    
    				#define BT_LE_ADV_NCONN_DIR_RPA(_peer) BT_LE_ADV_PARAM(BT_LE_ADV_OPT_DIR_ADDR_RPA, \
    						0, 0,\
    						  _peer)

    But it seems to freeze and doesn't give me an error or success message when I start BLE with:

    bt_le_adv_start(BT_LE_ADV_NCONN_DIR_RPA(peer), ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));

    The devices successfully connect when I start with:

        err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
    My current approach has nrf be connectable with a random address [This part works], then send over the IRK information with UART [Struggling with this]. Next, it disconnects, stops broadcasting, and changes to a non-connectable random resolvable address.[not working]
    Something I've also noticed is that the IRK is said to be 16 bytes, but the pointer is only a uint8_t. How do I fit all that data in there?
  • Hi,

    adojang said:
    I'm trying to enable RPA, but there is no BT_LE_ADV_PARAM that exists by default for it.

    You do not need any specific configuration of the advertiser, just enable privacy. The simplest is to take the beacon example and add "CONFIG_BT_PRIVACY=y". Then you will see that it advertises with a RPA. If you also set CONFIG_BT_RPA_TIMEOUT to a low value you can easily that the address changes every interval.

    adojang said:
    Something I've also noticed is that the IRK is said to be 16 bytes, but the pointer is only a uint8_t. How do I fit all that data in there?

    This is a pointer to an array of uint8_t. Generally in C programming, arrays are passed around as pointers to the first element. So this is how it is done in all APIs that handle arrays.

  • Thank you once again, I've learned a lot!

    I'm having trouble establishing a connection from my client (ESP32) to my peripheral (NRF) when enabling privacy. Without privacy, the uart example works great, and I can exchange information.

    My current approach is going to be to connect to the NRF while it has a non-private address, exchange the IRK key over UART, then switch the NRF device to it's privacy enabled mode, setup a new identity with that specific IRK key, and start advertising.

    On my client side, I'd like to generate a large whitelist of MAC addresses using the IRK, but I haven't been able to find the low level mathematical implementation of how to convert the IRK to a mac address. Could you point me to a resource?

  • To the math you can refer to BT core spec 5.3 page 1559 and page 3085. There are references there for digging further down. Note that this is not normally something you would need to do, as any Bluetooth stack out there should be capable of it. I cannot say how this is with Espressif so you would need to ask them for details about their devices. I would expect that they provide some tools for whitelisting IRKs though, as this is an essential feature.

  • Thanks for all the help Einar!

    I'm still struggling with the following portions:

    -IRK Generation (I'm still a little uncertain how to use pointers to create it)

    -Creating, and USING an identity with advertising.

    After the IRK key has been exchanged, the NRF will completely disconnect, and change its ble parameters. Specifically using BT_LE_ADV_NCONN [Non-connectable advertising with private address]

    I assume this allows for address resolution?

    The ESP32 will then scan, and when it finds a mac address from the correct manufacturer (I'll filter them a bit) it will do the math to confirm if the address resolves using the IRK.

Related