LESC Static OOB?

I am working in NCS v2.7.0 for nRF52833 DevKits, and trying to set up a secure BLE connection to allow remote control of the device over GATT.

My devices are headless IOT devices, so there is no possibility for a true OOB data pathway and no user nearby to provide feedback, so none of the basic examples quite work. My understanding is that out of the box I would need to either use legacy static OOB which exposes me to a lack of forward secrecy, or use LESC with a static passkey which exposes me to brute force attacks.

What I would like to do is provide a fixed 16-byte random seed to an LE SC OOB data structure (bt_le_oob_sc_data.r), and have the device compute the confirm value based on the current public key. This would let me leverage ephemeral DH keys from LESC ensuring forward secrecy, and a 128-bit shared secret is a lot harder to brute force then a 6-digit static pin. I understand that true OOB is much better, but the actual alternatives available to me are much worse.

First, before I get into my mess, is there a native way to accomplish what I'm trying to do that I'm missing?

If not, what I did was hack a hook into 'smp.c' to allow retrieval of the 'sc_public_key' value:

const uint8_t *bt_smp_get_sc_public_key(void)
{
    return sc_public_key;
}


Then in my application, I added the following code to my initiator (central device) to re-calculate the confirm value on each request:

#include "crypto/bt_crypto.h"

// Hacked into the SDK smp.c to fetch the static 'sc_public_key' value
extern const uint8_t *bt_smp_get_sc_public_key(void);

static struct bt_le_oob_sc_data oob_local;
static const uint8_t static_r[16] = {
    0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
    0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE
};

static void oob_data_request(struct bt_conn *conn, struct bt_conn_oob_info *info)
{
	int err;
	
    if (info->type != BT_CONN_OOB_LE_SC) {
        printk("OOB type not LE SC, rejected\n");
        return;
    }

    printk("Central: OOB data requested\n");

	memcpy(oob_local.r, static_r, 16);
	err = bt_crypto_f4(bt_smp_get_sc_public_key(), bt_smp_get_sc_public_key(), &oob_local.r, 0, &oob_local.c);
	if (err) {
		printk("Error generating OOB confirm value: %d\n", err);
		return;
	}

	err = bt_le_oob_set_sc_data(conn, &oob_local, NULL);
	if (err) {
		printk("Error while setting OOB data: %d\n", err);
	}
}

I have basically the same thing added to the peripheral device, except argument 4 into 'bt_crypto_f4' is set to 1 instead of 0 to reflect the different role.

I think this should work, but my pairing request always end with a status 0xB (BT_SMP_ERR_DHKEY_CHECK_FAILED). Am I missing something, or is what I'm trying to do just not possible.

Thanks,
Jeremy

Parents
  • If you control both sides, why don't you just skip the standard BLE pairing step altogether and generate LTK in your own way?

    If the two devices initially share a 16 byte secret S (as I assume they do from your question), you can generate an LTK by first performing ECDH resulting in shared secret E, then let the LTK be for example HMAC(S, E). That way you get forward secrecy.

    Another way would be to use standard LESC numeric comparison. You can encrypt the 6 digits that are supposed to be shown on the display with your shared key and some AEAD cipher, send it over BLE characteristic to the other device. After decrypting it, accept if correct. Same in the other direction. Just make sure the Additional Data is different for every direction to avoid reflection attack.

Reply
  • If you control both sides, why don't you just skip the standard BLE pairing step altogether and generate LTK in your own way?

    If the two devices initially share a 16 byte secret S (as I assume they do from your question), you can generate an LTK by first performing ECDH resulting in shared secret E, then let the LTK be for example HMAC(S, E). That way you get forward secrecy.

    Another way would be to use standard LESC numeric comparison. You can encrypt the 6 digits that are supposed to be shown on the display with your shared key and some AEAD cipher, send it over BLE characteristic to the other device. After decrypting it, accept if correct. Same in the other direction. Just make sure the Additional Data is different for every direction to avoid reflection attack.

Children
Related