Demonstration of possible usage of the Security Manager. Security Manager deals with pairing, authentication and encryption.

SM - example usage of the Security Manager to pair and encrypt

Demonstration of possible usage of the Security Manager. Security Manager deals with pairing, authentication and encryption.

The application demonstrates usage as a central and a peripheral. The central will connect to any connectable device present. Please have one ready and advertising. Application will attempt pairing. Please authorise your peer device to pair.

Upon success it will disconnect and start advertising to demonstrate usage as a peripheral. Please scan and connect using your peer device. Upon connection grant pairing if prompted. Upon success the application will disconnect. Observe the terminal to keep track of the sequence.

Running the application

Requirements

The sample application can be seen on any BLE scanner on a smartphone. If you don't have a scanner on your phone, please install :

- nRF Master Control Panel for Android.

- LightBlue for iPhone.

Information about activity is printed over the serial connection - please have a client open. You may use:

- Tera Term

Hardware requirements are in the main readme.

Building instructions

Building instructions for all samples are in the main readme.

Revision:
17:6bf7972c4d0e
Parent:
9:674ab70e0f36
--- a/source/main.cpp	Fri Dec 14 13:15:50 2018 +0000
+++ b/source/main.cpp	Mon Jan 14 10:45:59 2019 +0000
@@ -18,6 +18,7 @@
 #include <mbed.h>
 #include "ble/BLE.h"
 #include "SecurityManager.h"
+#include "pretty_printer.h"
 
 #if MBED_CONF_APP_FILESYSTEM_SUPPORT
 #include "LittleFileSystem.h"
@@ -38,7 +39,11 @@
  *  progress.
  */
 
-static const uint8_t DEVICE_NAME[] = "SM_device";
+static const char DEVICE_NAME[] = "SM_device";
+
+/* we have to specify the disconnect call because of ambiguous overloads */
+typedef ble_error_t (Gap::*disconnect_call_t)(ble::connection_handle_t, ble::local_disconnection_reason_t);
+const static disconnect_call_t disconnect_call = &Gap::disconnect;
 
 /* for demonstration purposes we will store the peer device address
  * of the device that connects to us in the first demonstration
@@ -52,7 +57,8 @@
  *  your application is interested in.
  */
 class SMDevice : private mbed::NonCopyable<SMDevice>,
-                 public SecurityManager::EventHandler
+                 public SecurityManager::EventHandler,
+                 public ble::Gap::EventHandler
 {
 public:
     SMDevice(BLE &ble, events::EventQueue &event_queue, BLEProtocol::AddressBytes_t &peer_address) :
@@ -89,10 +95,8 @@
             makeFunctionPointer(this, &SMDevice::schedule_ble_events)
         );
 
-        /* handle timeouts, for example when connection attempts fail */
-        _ble.gap().onTimeout(
-            makeFunctionPointer(this, &SMDevice::on_timeout)
-        );
+        /* handle gap events */
+        _ble.gap().setEventHandler(this);
 
         error = _ble.init(this, &SMDevice::on_init_complete);
 
@@ -105,51 +109,6 @@
         _event_queue.dispatch_forever();
     };
 
-    /* event handler functions */
-
-    /** Respond to a pairing request. This will be called by the stack
-     * when a pairing request arrives and expects the application to
-     * call acceptPairingRequest or cancelPairingRequest */
-    virtual void pairingRequest(
-        ble::connection_handle_t connectionHandle
-    ) {
-        printf("Pairing requested - authorising\r\n");
-        _ble.securityManager().acceptPairingRequest(connectionHandle);
-    }
-
-    /** Inform the application of a successful pairing. Terminate the demonstration. */
-    virtual void pairingResult(
-        ble::connection_handle_t connectionHandle,
-        SecurityManager::SecurityCompletionStatus_t result
-    ) {
-        if (result == SecurityManager::SEC_STATUS_SUCCESS) {
-            printf("Pairing successful\r\n");
-        } else {
-            printf("Pairing failed\r\n");
-        }
-    }
-
-    /** Inform the application of change in encryption status. This will be
-     * communicated through the serial port */
-    virtual void linkEncryptionResult(
-        ble::connection_handle_t connectionHandle,
-        ble::link_encryption_t result
-    ) {
-        if (result == ble::link_encryption_t::ENCRYPTED) {
-            printf("Link ENCRYPTED\r\n");
-        } else if (result == ble::link_encryption_t::ENCRYPTED_WITH_MITM) {
-            printf("Link ENCRYPTED_WITH_MITM\r\n");
-        } else if (result == ble::link_encryption_t::NOT_ENCRYPTED) {
-            printf("Link NOT_ENCRYPTED\r\n");
-        }
-
-        /* disconnect in 2 s */
-        _event_queue.call_in(
-            2000, &_ble.gap(),
-            &Gap::disconnect, _handle, Gap::REMOTE_USER_TERMINATED_CONNECTION
-        );
-    }
-
 private:
     /** Override to start chosen activity when initialisation completes */
     virtual void start() = 0;
@@ -217,41 +176,19 @@
          * of any events. Class needs to implement SecurityManagerEventHandler. */
         _ble.securityManager().setSecurityManagerEventHandler(this);
 
+        /* gap events also handled by this class */
+        _ble.gap().setEventHandler(this);
+
         /* print device address */
         Gap::AddressType_t addr_type;
         Gap::Address_t addr;
         _ble.gap().getAddress(&addr_type, addr);
-        printf("Device address: %02x:%02x:%02x:%02x:%02x:%02x\r\n",
-               addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);
-
-        /* when scanning we want to connect to a peer device so we need to
-         * attach callbacks that are used by Gap to notify us of events */
-        _ble.gap().onConnection(this, &SMDevice::on_connect);
-        _ble.gap().onDisconnection(this, &SMDevice::on_disconnect);
+        print_address(addr);
 
         /* start test in 500 ms */
         _event_queue.call_in(500, this, &SMDevice::start);
     };
 
-    /** This is called by Gap to notify the application we connected */
-    virtual void on_connect(const Gap::ConnectionCallbackParams_t *connection_event) = 0;
-
-    /** This is called by Gap to notify the application we disconnected,
-     *  in our case it ends the demonstration. */
-    void on_disconnect(const Gap::DisconnectionCallbackParams_t *event)
-    {
-        printf("Diconnected\r\n");
-        _event_queue.break_dispatch();
-    };
-
-    /** End demonstration unexpectedly. Called if timeout is reached during advertising,
-     * scanning or connection initiation */
-    void on_timeout(const Gap::TimeoutSource_t source)
-    {
-        printf("Unexpected timeout - aborting\r\n");
-        _event_queue.break_dispatch();
-    };
-
     /** Schedule processing of events from the BLE in the event queue. */
     void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context)
     {
@@ -265,6 +202,75 @@
     };
 
 private:
+    /* Event handler */
+
+    /** Respond to a pairing request. This will be called by the stack
+     * when a pairing request arrives and expects the application to
+     * call acceptPairingRequest or cancelPairingRequest */
+    virtual void pairingRequest(
+        ble::connection_handle_t connectionHandle
+    ) {
+        printf("Pairing requested - authorising\r\n");
+        _ble.securityManager().acceptPairingRequest(connectionHandle);
+    }
+
+    /** Inform the application of a successful pairing. Terminate the demonstration. */
+    virtual void pairingResult(
+        ble::connection_handle_t connectionHandle,
+        SecurityManager::SecurityCompletionStatus_t result
+    ) {
+        if (result == SecurityManager::SEC_STATUS_SUCCESS) {
+            printf("Pairing successful\r\n");
+        } else {
+            printf("Pairing failed\r\n");
+        }
+    }
+
+    /** Inform the application of change in encryption status. This will be
+     * communicated through the serial port */
+    virtual void linkEncryptionResult(
+        ble::connection_handle_t connectionHandle,
+        ble::link_encryption_t result
+    ) {
+        if (result == ble::link_encryption_t::ENCRYPTED) {
+            printf("Link ENCRYPTED\r\n");
+        } else if (result == ble::link_encryption_t::ENCRYPTED_WITH_MITM) {
+            printf("Link ENCRYPTED_WITH_MITM\r\n");
+        } else if (result == ble::link_encryption_t::NOT_ENCRYPTED) {
+            printf("Link NOT_ENCRYPTED\r\n");
+        }
+
+        /* disconnect in 2 s */
+        _event_queue.call_in(
+            2000,
+            &_ble.gap(),
+            disconnect_call,
+            _handle,
+            ble::local_disconnection_reason_t(ble::local_disconnection_reason_t::USER_TERMINATION)
+        );
+    }
+
+    /** This is called by Gap to notify the application we disconnected,
+     *  in our case it ends the demonstration. */
+    virtual void onDisconnectionComplete(const ble::DisconnectionCompleteEvent &)
+    {
+        printf("Diconnected\r\n");
+        _event_queue.break_dispatch();
+    };
+
+    virtual void onAdvertisingEnd(const ble::AdvertisingEndEvent &)
+    {
+        printf("Advertising timed out - aborting\r\n");
+        _event_queue.break_dispatch();
+    }
+
+    virtual void onScanTimeout(const ble::ScanTimeoutEvent &)
+    {
+        printf("Scan timed out - aborting\r\n");
+        _event_queue.break_dispatch();
+    }
+
+private:
     DigitalOut _led1;
 
 protected:
@@ -285,39 +291,45 @@
     virtual void start()
     {
         /* Set up and start advertising */
-
-        ble_error_t error;
-        GapAdvertisingData advertising_data;
-
-        /* add advertising flags */
-        advertising_data.addFlags(GapAdvertisingData::LE_GENERAL_DISCOVERABLE
-                                  | GapAdvertisingData::BREDR_NOT_SUPPORTED);
-
-        /* add device name */
-        advertising_data.addData(
-            GapAdvertisingData::COMPLETE_LOCAL_NAME,
-            DEVICE_NAME,
-            sizeof(DEVICE_NAME)
+        uint8_t adv_buffer[ble::LEGACY_ADVERTISING_MAX_SIZE];
+        /* use the helper to build the payload */
+        ble::AdvertisingDataBuilder adv_data_builder(
+            adv_buffer
         );
 
-        error = _ble.gap().setAdvertisingPayload(advertising_data);
+        adv_data_builder.setFlags();
+        adv_data_builder.setName(DEVICE_NAME);
+
+        /* Set payload for the set */
+        ble_error_t error = _ble.gap().setAdvertisingPayload(
+            ble::LEGACY_ADVERTISING_HANDLE,
+            adv_data_builder.getAdvertisingData()
+        );
 
         if (error) {
-            printf("Error during Gap::setAdvertisingPayload\r\n");
+            print_error(error, "Gap::setAdvertisingPayload() failed");
+            _event_queue.break_dispatch();
             return;
         }
 
-        /* advertise to everyone */
-        _ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
-        /* how many milliseconds between advertisements, lower interval
-         * increases the chances of being seen at the cost of more power */
-        _ble.gap().setAdvertisingInterval(20);
-        _ble.gap().setAdvertisingTimeout(0);
+        ble::AdvertisingParameters adv_parameters(
+            ble::advertising_type_t::CONNECTABLE_UNDIRECTED
+        );
 
-        error = _ble.gap().startAdvertising();
+        error = _ble.gap().setAdvertisingParameters(
+            ble::LEGACY_ADVERTISING_HANDLE,
+            adv_parameters
+        );
 
         if (error) {
-            printf("Error during Gap::startAdvertising.\r\n");
+            print_error(error, "Gap::setAdvertisingParameters() failed");
+            return;
+        }
+
+        error = _ble.gap().startAdvertising(ble::LEGACY_ADVERTISING_HANDLE);
+
+        if (error) {
+            print_error(error, "Gap::startAdvertising() failed");
             return;
         }
 
@@ -332,20 +344,18 @@
 
     /** This is called by Gap to notify the application we connected,
      *  in our case it immediately requests a change in link security */
-    virtual void on_connect(const Gap::ConnectionCallbackParams_t *connection_event)
+    virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event)
     {
         ble_error_t error;
 
         /* remember the device that connects to us now so we can connect to it
          * during the next demonstration */
-        memcpy(_peer_address, connection_event->peerAddr, sizeof(_peer_address));
+        memcpy(_peer_address, event.getPeerAddress().data(), sizeof(_peer_address));
 
-        printf("Connected to: %02x:%02x:%02x:%02x:%02x:%02x\r\n",
-                _peer_address[5], _peer_address[4], _peer_address[3],
-                _peer_address[2], _peer_address[1], _peer_address[0]);
+        printf("Connected to peer: ");
+        print_address(event.getPeerAddress().data());
 
-        /* store the handle for future Security Manager requests */
-        _handle = connection_event->handle;
+        _handle = event.getConnectionHandle();
 
         /* Request a change in link security. This will be done
          * indirectly by asking the master of the connection to
@@ -372,45 +382,70 @@
 
     virtual void start()
     {
-        /* start scanning and attach a callback that will handle advertisements
-         * and scan requests responses */
-        ble_error_t error = _ble.gap().startScan(this, &SMDeviceCentral::on_scan);
+        ble::ScanParameters params;
+        ble_error_t error = _ble.gap().setScanParameters(params);
+
+        if (error) {
+            print_error(error, "Error in Gap::startScan %d\r\n");
+            return;
+        }
+
+        /* start scanning, results will be handled by onAdvertisingReport */
+        error = _ble.gap().startScan();
+
+        if (error) {
+            print_error(error, "Error in Gap::startScan %d\r\n");
+            return;
+        }
 
         printf("Please advertise\r\n");
 
-        printf("Scanning for: %02x:%02x:%02x:%02x:%02x:%02x\r\n",
-               _peer_address[5], _peer_address[4], _peer_address[3],
-               _peer_address[2], _peer_address[1], _peer_address[0]);
-
-        if (error) {
-            printf("Error during Gap::startScan %d\r\n", error);
-            return;
-        }
+        printf("Scanning for: ");
+        print_address(_peer_address);
     }
 
+private:
+    /* Gap::EventHandler */
+
     /** Look at scan payload to find a peer device and connect to it */
-    void on_scan(const Gap::AdvertisementCallbackParams_t *params)
+    virtual void onAdvertisingReport(const ble::AdvertisingReportEvent &event)
     {
         /* don't bother with analysing scan result if we're already connecting */
         if (_is_connecting) {
             return;
         }
 
-        /* connect to the same device that connected to us */
-        if (memcmp(params->peerAddr, _peer_address, sizeof(_peer_address)) == 0) {
+        /* parse the advertising payload, looking for a discoverable device */
+        if (memcmp(event.getPeerAddress().data(), _peer_address, sizeof(_peer_address)) == 0) {
+            ble_error_t error = _ble.gap().stopScan();
+
+            if (error) {
+                print_error(error, "Error caused by Gap::stopScan");
+                return;
+            }
 
-            ble_error_t error = _ble.gap().connect(
-                params->peerAddr, params->peerAddrType,
-                NULL, NULL
+            ble::ConnectionParameters connection_params(
+                ble::phy_t::LE_1M,
+                ble::scan_interval_t(50),
+                ble::scan_window_t(50),
+                ble::conn_interval_t(50),
+                ble::conn_interval_t(100),
+                ble::slave_latency_t(0),
+                ble::supervision_timeout_t(100)
+            );
+            connection_params.setOwnAddressType(ble::own_address_type_t::RANDOM);
+
+            error = _ble.gap().connect(
+                event.getPeerAddressType(),
+                event.getPeerAddress(),
+                connection_params
             );
 
             if (error) {
-                printf("Error during Gap::connect %d\r\n", error);
+                print_error(error, "Error caused by Gap::connect");
                 return;
             }
 
-            printf("Connecting... ");
-
             /* we may have already scan events waiting
              * to be processed so we need to remember
              * that we are already connecting and ignore them */
@@ -418,28 +453,36 @@
 
             return;
         }
-    };
+    }
 
     /** This is called by Gap to notify the application we connected,
      *  in our case it immediately request pairing */
-    virtual void on_connect(const Gap::ConnectionCallbackParams_t *connection_event)
+    virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event)
     {
-        ble_error_t error;
+        if (event.getStatus() == BLE_ERROR_NONE) {
+            /* store the handle for future Security Manager requests */
+            _handle = event.getConnectionHandle();
+
+            printf("Connected\r\n");
+
+            /* in this example the local device is the master so we request pairing */
+            ble_error_t error = _ble.securityManager().requestPairing(_handle);
 
-        /* store the handle for future Security Manager requests */
-        _handle = connection_event->handle;
+             if (error) {
+                 printf("Error during SM::requestPairing %d\r\n", error);
+                 return;
+             }
 
-        /* in this example the local device is the master so we request pairing */
-        error = _ble.securityManager().requestPairing(_handle);
+            /* upon pairing success the application will disconnect */
+        }
 
-        printf("Connected\r\n");
+        /* failed to connect - restart scan */
+        ble_error_t error = _ble.gap().startScan();
 
         if (error) {
-            printf("Error during SM::requestPairing %d\r\n", error);
+            print_error(error, "Error in Gap::startScan %d\r\n");
             return;
         }
-
-        /* upon pairing success the application will disconnect */
     };
 };