This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SOLVED - Using LESec with nRF52840 and pc-ble-driver-js v2.6.1

EDIT - My solution for this is posted below as a reply and it explains the issues in the code. This is an example of how to use pc-ble-dongle-js driver to authenticate using BLE 4.2 LESec protocol. This example assumes you
1) Want to bond with the device
2) Are using LESec 
3) There is no keys / interface on the peripheral device (You may need to handle additional events with other settings)
4) Force a key size of 16 bytes (Minimum could be as few as 7)
5) Both sides are using 'long term key and master identification'
6) Both sides are using 'identity resolving keys' 


ORIGINAL POST WITH ISSUES:



Hello,

I am trying to get LESec Authentication with pc-ble-driver-js and nRF52840 dongle as a central to work with an embedded product without success. The Diffie Hellman key exchange seems to be failing. I've sniffed the connection and can see the public key being sent to the peripheral is all 0's.

Is LESec supported with this version of the driver? 
Do you have an example I can look at?
I have a feeling it deals with my call to replySecParams where I am passing null for the secKeyset, but I also get a crash if I attempt to pass in the example keyset object. This is a known issue?

pc-ble-driver-js version: 2.6.1
nRF Connect app version: 3.3.0
Bluetooth Low Energy app version: 2.4.0 (loads on default application / soft device)

Adapter interface documentation
nordicsemiconductor.github.io/.../Adapter.html

Relevant code snip-it

Nordic.prototype.pair = function(cb) {
    let secParams;

    // Security parameters
    secParams = {
        bond: true,
        mitm: false,
        lesc: true,
        keypress: false,
        io_caps: adapter.driver.BLE_GAP_IO_CAPS_NONE,
        oob: false,
        min_key_size: 16,
        max_key_size: 16,
        kdist_own: {
            enc: true,   /** Long Term Key and Master Identification. */
            id: true,    /** Identity Resolving Key and Identity Address Information. */
            sign: false,  /** Connection Signature Resolving Key. */
            link: false,  /** Derive the Link Key from the LTK. */
        },
        kdist_peer: {
            enc: true,   /** Long Term Key and Master Identification. */
            id: true,    /** Identity Resolving Key and Identity Address Information. */
            sign: false,  /** Connection Signature Resolving Key. */
            link: false,  /** Derive the Link Key from the LTK. */
        },
    };

    // Get rid of any old keys
    adapter.deleteKeys();

    // Respond to Security parameters request
    adapter.on('secParamsRequest', (device, peer_params) => {
        console.log('peer_params: ' + JSON.stringify(peer_params));

        // let secKeyset = {
        //     kdist_own: {
        //         enc: true,   /** Long Term Key and Master Identification. */
        //         id: true,    /** Identity Resolving Key and Identity Address Information. */
        //         sign: false,  /** Connection Signature Resolving Key. */
        //         link: false,  /** Derive the Link Key from the LTK. */
        //     },
        //     kdist_peer: {
        //         enc: true,   /** Long Term Key and Master Identification. */
        //         id: true,    /** Identity Resolving Key and Identity Address Information. */
        //         sign: false,  /** Connection Signature Resolving Key. */
        //         link: false,  /** Derive the Link Key from the LTK. */
        //     }
        // }
        adapter.computePublicKey();
        adapter.replySecParams(device.instanceId,
            adapter.driver.BLE_GAP_SEC_STATUS_SUCCESS,
            null, // Central device, this is null as it's been provided during 'authentication' call
            null, // Should this be null? Passing secKeyset above causes crash due to
                  // "TypeError: The provided keyset can not be parsed.
                  // function: gapReplySecurityParameters, adapter.js:2759:23"
            (err, secKeyset) => {
                console.log('secKeyset: ' + JSON.stringify(secKeyset));
                console.log("secParamsRequest Err", err);
            }
        );
    });

    // Respond to the Diffie Hellman key exchange request
    adapter.on('lescDhkeyRequest', function(device, event) {
        // Compute a public key to send
        let key = adapter.computePublicKey();
        adapter.replyLescDhkey(adapter.device.instanceId, key, function(err) {
            // Compute shared secret on success
            console.log('LescDhkey err: ' + err);
            if(!err) {
                adapter.computeSharedSecret(event);
            }
            cb();
        });
    });

    // Start authentication
    adapter.authenticate(adapter.device.instanceId, secParams, function(err) {
        console.log('Auth err: ' + err);
    });
}

  • Determined public key being passed to the peripheral as all 0's was caused from the call to replySecParams with a null secParams. Below is the updated event listener for 'secParamsRequest'

    // Respond to Security parameters request
    adapter.on('secParamsRequest', (device, peer_params) => {
        console.log('peer_params: ' + JSON.stringify(peer_params));
    
        let secKeyset = {
            keys_own: {
                enc_key: null,
                id_key: null,
                sign_key: null,
                pk: null,
            },
            keys_peer: {
                enc_key: null,
                id_key: null,
                sign_key: null,
                pk: null,
            },
        };
        secKeyset.keys_own.pk = { pk: adapter.computePublicKey() };
        adapter.replySecParams(device.instanceId,
            adapter.driver.BLE_GAP_SEC_STATUS_SUCCESS,
            null,
            secKeyset,
            (err, secKeyset) => {
                console.log('secKeyset: ' + JSON.stringify(secKeyset));
                console.log("secParamsRequest Err", err);
            }
        );
    });


    DH Auth is still failing at this point. Still investigating.

  • This reply is a full answer on the two issues in the above code. It includes the fix in the first reply.

    There were two issues in the above code.
    1) The public key being passed to the peripheral as all 0's was caused from the call to replySecParams with a null secParams. This happens in the updated event listener for 'secParamsRequest'.
    2) On the event lescDhkeyRequest, the public key was being calculated instead of the shared secret key. The correct adapter function call was to computeSharedSecret with the parameter 'event' passed into said function.

    See below for full functional code snip-it.

    Nordic.prototype.pair = function(cb) {
        let secParams;
    
        secParams = {
            bond: true,
            mitm: false,
            lesc: true,
            keypress: false,
            io_caps: adapter.driver.BLE_GAP_IO_CAPS_NONE,
            oob: false,
            min_key_size: 16,
            max_key_size: 16,
            kdist_own: {
                enc: true,   /** Long Term Key and Master Identification. */
                id: true,    /** Identity Resolving Key and Identity Address Information. */
                sign: false,  /** Connection Signature Resolving Key. */
                link: false,  /** Derive the Link Key from the LTK. */
            },
            kdist_peer: {
                enc: true,   /** Long Term Key and Master Identification. */
                id: true,    /** Identity Resolving Key and Identity Address Information. */
                sign: false,  /** Connection Signature Resolving Key. */
                link: false,  /** Derive the Link Key from the LTK. */
            },
        };
    
        // Respond to Security parameters request
        adapter.on('secParamsRequest', (device, peer_params) => {
            console.log('peer_params: ' + JSON.stringify(peer_params));
    
            let secKeyset = {
                keys_own: {
                    enc_key: null,
                    id_key: null,
                    sign_key: null,
                    pk: null,
                },
                keys_peer: {
                    enc_key: null,
                    id_key: null,
                    sign_key: null,
                    pk: null,
                },
            };
    
            // Compute our public key here (nothing else is needed)
            secKeyset.keys_own.pk = { pk: adapter.computePublicKey() };
            
            // Send the response
            adapter.replySecParams(
                device.instanceId,
                adapter.driver.BLE_GAP_SEC_STATUS_SUCCESS,
                null, // This can be null IF we are a central AND we initiated the pairing
                secKeyset,
                (err, secKeyset) => {
                    console.log('secKeyset: ' + JSON.stringify(secKeyset));
                    if(err) {
                        console.error("secParamsRequest Err", err);
                    }
                }
            );
        });
    
        // Respond to the Diffie Hellman key exchange request
        adapter.on('lescDhkeyRequest', function(device, event) {
            // Compute a shared secret key
            let key = adapter.computeSharedSecret(event);
            adapter.replyLescDhkey(
                adapter.device.instanceId,
                key,
                function(err) {
                    // Compute shared secret on success
                    if(err) {
                        console.error('LescDhkey err: ' + err);
                    }
                    cb();
                }
            );
        });
    
        // Start authentication
        adapter.authenticate(adapter.device.instanceId, secParams, function(err) {
            console.log('Auth err: ' + err);
        });
    }

Related