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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2006-2013 ARM Limited
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include <events/mbed_events.h>
00018 #include <mbed.h>
00019 #include "ble/BLE.h"
00020 #include "SecurityManager.h"
00021 #include "pretty_printer.h"
00022 
00023 #if MBED_CONF_APP_FILESYSTEM_SUPPORT
00024 #include "LittleFileSystem.h"
00025 #include "HeapBlockDevice.h"
00026 #endif //MBED_CONF_APP_FILESYSTEM_SUPPORT
00027 
00028 /** This example demonstrates all the basic setup required
00029  *  for pairing and setting up link security both as a central and peripheral
00030  *
00031  *  The example is implemented as two classes, one for the peripheral and one
00032  *  for central inheriting from a common base. They are run in sequence and
00033  *  require a peer device to connect to. During the peripheral device demonstration
00034  *  a peer device is required to connect. In the central device demonstration
00035  *  this peer device will be scanned for and connected to - therefore it should
00036  *  be advertising with the same address as when it connected.
00037  *
00038  *  During the test output is written on the serial connection to monitor its
00039  *  progress.
00040  */
00041 
00042 static const char DEVICE_NAME[] = "SM_device";
00043 
00044 /* we have to specify the disconnect call because of ambiguous overloads */
00045 typedef ble_error_t (Gap::*disconnect_call_t)(ble::connection_handle_t, ble::local_disconnection_reason_t);
00046 const static disconnect_call_t disconnect_call = &Gap::disconnect;
00047 
00048 /* for demonstration purposes we will store the peer device address
00049  * of the device that connects to us in the first demonstration
00050  * so we can use its address to reconnect to it later */
00051 static BLEProtocol::AddressBytes_t peer_address;
00052 
00053 /** Base class for both peripheral and central. The same class that provides
00054  *  the logic for the application also implements the SecurityManagerEventHandler
00055  *  which is the interface used by the Security Manager to communicate events
00056  *  back to the applications. You can provide overrides for a selection of events
00057  *  your application is interested in.
00058  */
00059 class SMDevice : private mbed::NonCopyable<SMDevice>,
00060                  public SecurityManager::EventHandler,
00061                  public ble::Gap::EventHandler
00062 {
00063 public:
00064     SMDevice(BLE &ble, events::EventQueue &event_queue, BLEProtocol::AddressBytes_t &peer_address) :
00065         _led1(LED1, 0),
00066         _ble(ble),
00067         _event_queue(event_queue),
00068         _peer_address(peer_address),
00069         _handle(0),
00070         _is_connecting(false) { };
00071 
00072     virtual ~SMDevice()
00073     {
00074         if (_ble.hasInitialized()) {
00075             _ble.shutdown();
00076         }
00077     };
00078 
00079     /** Start BLE interface initialisation */
00080     void run()
00081     {
00082         ble_error_t error;
00083 
00084         /* to show we're running we'll blink every 500ms */
00085         _event_queue.call_every(500, this, &SMDevice::blink);
00086 
00087         if (_ble.hasInitialized()) {
00088             printf("Ble instance already initialised.\r\n");
00089             return;
00090         }
00091 
00092         /* this will inform us off all events so we can schedule their handling
00093          * using our event queue */
00094         _ble.onEventsToProcess(
00095             makeFunctionPointer(this, &SMDevice::schedule_ble_events)
00096         );
00097 
00098         /* handle gap events */
00099         _ble.gap().setEventHandler(this);
00100 
00101         error = _ble.init(this, &SMDevice::on_init_complete);
00102 
00103         if (error) {
00104             printf("Error returned by BLE::init.\r\n");
00105             return;
00106         }
00107 
00108         /* this will not return until shutdown */
00109         _event_queue.dispatch_forever();
00110     };
00111 
00112 private:
00113     /** Override to start chosen activity when initialisation completes */
00114     virtual void start() = 0;
00115 
00116     /** This is called when BLE interface is initialised and starts the demonstration */
00117     void on_init_complete(BLE::InitializationCompleteCallbackContext *event)
00118     {
00119         ble_error_t error;
00120 
00121         if (event->error) {
00122             printf("Error during the initialisation\r\n");
00123             return;
00124         }
00125 
00126         /* This path will be used to store bonding information but will fallback
00127          * to storing in memory if file access fails (for example due to lack of a filesystem) */
00128         const char* db_path = "/fs/bt_sec_db";
00129         /* If the security manager is required this needs to be called before any
00130          * calls to the Security manager happen. */
00131         error = _ble.securityManager().init(
00132             true,
00133             false,
00134             SecurityManager::IO_CAPS_NONE,
00135             NULL,
00136             false,
00137             db_path
00138         );
00139 
00140         if (error) {
00141             printf("Error during init %d\r\n", error);
00142             return;
00143         }
00144 
00145         error = _ble.securityManager().preserveBondingStateOnReset(true);
00146 
00147         if (error) {
00148             printf("Error during preserveBondingStateOnReset %d\r\n", error);
00149         }
00150 
00151 #if MBED_CONF_APP_FILESYSTEM_SUPPORT
00152         /* Enable privacy so we can find the keys */
00153         error = _ble.gap().enablePrivacy(true);
00154 
00155         if (error) {
00156             printf("Error enabling privacy\r\n");
00157         }
00158 
00159         Gap::PeripheralPrivacyConfiguration_t configuration_p = {
00160             /* use_non_resolvable_random_address */ false,
00161             Gap::PeripheralPrivacyConfiguration_t::REJECT_NON_RESOLVED_ADDRESS
00162         };
00163         _ble.gap().setPeripheralPrivacyConfiguration(&configuration_p);
00164 
00165         Gap::CentralPrivacyConfiguration_t configuration_c = {
00166             /* use_non_resolvable_random_address */ false,
00167             Gap::CentralPrivacyConfiguration_t::RESOLVE_AND_FORWARD
00168         };
00169         _ble.gap().setCentralPrivacyConfiguration(&configuration_c);
00170 
00171         /* this demo switches between being master and slave */
00172         _ble.securityManager().setHintFutureRoleReversal(true);
00173 #endif
00174 
00175         /* Tell the security manager to use methods in this class to inform us
00176          * of any events. Class needs to implement SecurityManagerEventHandler. */
00177         _ble.securityManager().setSecurityManagerEventHandler(this);
00178 
00179         /* gap events also handled by this class */
00180         _ble.gap().setEventHandler(this);
00181 
00182         /* print device address */
00183         Gap::AddressType_t addr_type;
00184         Gap::Address_t addr;
00185         _ble.gap().getAddress(&addr_type, addr);
00186         print_address(addr);
00187 
00188         /* start test in 500 ms */
00189         _event_queue.call_in(500, this, &SMDevice::start);
00190     };
00191 
00192     /** Schedule processing of events from the BLE in the event queue. */
00193     void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context)
00194     {
00195         _event_queue.call(mbed::callback(&context->ble, &BLE::processEvents));
00196     };
00197 
00198     /** Blink LED to show we're running */
00199     void blink(void)
00200     {
00201         _led1 = !_led1;
00202     };
00203 
00204 private:
00205     /* Event handler */
00206 
00207     /** Respond to a pairing request. This will be called by the stack
00208      * when a pairing request arrives and expects the application to
00209      * call acceptPairingRequest or cancelPairingRequest */
00210     virtual void pairingRequest(
00211         ble::connection_handle_t connectionHandle
00212     ) {
00213         printf("Pairing requested - authorising\r\n");
00214         _ble.securityManager().acceptPairingRequest(connectionHandle);
00215     }
00216 
00217     /** Inform the application of a successful pairing. Terminate the demonstration. */
00218     virtual void pairingResult(
00219         ble::connection_handle_t connectionHandle,
00220         SecurityManager::SecurityCompletionStatus_t result
00221     ) {
00222         if (result == SecurityManager::SEC_STATUS_SUCCESS) {
00223             printf("Pairing successful\r\n");
00224         } else {
00225             printf("Pairing failed\r\n");
00226         }
00227     }
00228 
00229     /** Inform the application of change in encryption status. This will be
00230      * communicated through the serial port */
00231     virtual void linkEncryptionResult(
00232         ble::connection_handle_t connectionHandle,
00233         ble::link_encryption_t result
00234     ) {
00235         if (result == ble::link_encryption_t::ENCRYPTED) {
00236             printf("Link ENCRYPTED\r\n");
00237         } else if (result == ble::link_encryption_t::ENCRYPTED_WITH_MITM) {
00238             printf("Link ENCRYPTED_WITH_MITM\r\n");
00239         } else if (result == ble::link_encryption_t::NOT_ENCRYPTED) {
00240             printf("Link NOT_ENCRYPTED\r\n");
00241         }
00242 
00243         /* disconnect in 2 s */
00244         _event_queue.call_in(
00245             2000,
00246             &_ble.gap(),
00247             disconnect_call,
00248             _handle,
00249             ble::local_disconnection_reason_t(ble::local_disconnection_reason_t::USER_TERMINATION)
00250         );
00251     }
00252 
00253     /** This is called by Gap to notify the application we disconnected,
00254      *  in our case it ends the demonstration. */
00255     virtual void onDisconnectionComplete(const ble::DisconnectionCompleteEvent &)
00256     {
00257         printf("Diconnected\r\n");
00258         _event_queue.break_dispatch();
00259     };
00260 
00261     virtual void onAdvertisingEnd(const ble::AdvertisingEndEvent &)
00262     {
00263         printf("Advertising timed out - aborting\r\n");
00264         _event_queue.break_dispatch();
00265     }
00266 
00267     virtual void onScanTimeout(const ble::ScanTimeoutEvent &)
00268     {
00269         printf("Scan timed out - aborting\r\n");
00270         _event_queue.break_dispatch();
00271     }
00272 
00273 private:
00274     DigitalOut _led1;
00275 
00276 protected:
00277     BLE &_ble;
00278     events::EventQueue &_event_queue;
00279     BLEProtocol::AddressBytes_t &_peer_address;
00280     ble::connection_handle_t _handle;
00281     bool _is_connecting;
00282 };
00283 
00284 /** A peripheral device will advertise, accept the connection and request
00285  * a change in link security. */
00286 class SMDevicePeripheral : public SMDevice {
00287 public:
00288     SMDevicePeripheral(BLE &ble, events::EventQueue &event_queue, BLEProtocol::AddressBytes_t &peer_address)
00289         : SMDevice(ble, event_queue, peer_address) { }
00290 
00291     virtual void start()
00292     {
00293         /* Set up and start advertising */
00294         uint8_t adv_buffer[ble::LEGACY_ADVERTISING_MAX_SIZE];
00295         /* use the helper to build the payload */
00296         ble::AdvertisingDataBuilder adv_data_builder(
00297             adv_buffer
00298         );
00299 
00300         adv_data_builder.setFlags();
00301         adv_data_builder.setName(DEVICE_NAME);
00302 
00303         /* Set payload for the set */
00304         ble_error_t error = _ble.gap().setAdvertisingPayload(
00305             ble::LEGACY_ADVERTISING_HANDLE,
00306             adv_data_builder.getAdvertisingData()
00307         );
00308 
00309         if (error) {
00310             print_error(error, "Gap::setAdvertisingPayload() failed");
00311             _event_queue.break_dispatch();
00312             return;
00313         }
00314 
00315         ble::AdvertisingParameters adv_parameters(
00316             ble::advertising_type_t::CONNECTABLE_UNDIRECTED
00317         );
00318 
00319         error = _ble.gap().setAdvertisingParameters(
00320             ble::LEGACY_ADVERTISING_HANDLE,
00321             adv_parameters
00322         );
00323 
00324         if (error) {
00325             print_error(error, "Gap::setAdvertisingParameters() failed");
00326             return;
00327         }
00328 
00329         error = _ble.gap().startAdvertising(ble::LEGACY_ADVERTISING_HANDLE);
00330 
00331         if (error) {
00332             print_error(error, "Gap::startAdvertising() failed");
00333             return;
00334         }
00335 
00336         printf("Please connect to device\r\n");
00337 
00338         /** This tells the stack to generate a pairingRequest event
00339          * which will require this application to respond before pairing
00340          * can proceed. Setting it to false will automatically accept
00341          * pairing. */
00342         _ble.securityManager().setPairingRequestAuthorisation(true);
00343     };
00344 
00345     /** This is called by Gap to notify the application we connected,
00346      *  in our case it immediately requests a change in link security */
00347     virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event)
00348     {
00349         ble_error_t error;
00350 
00351         /* remember the device that connects to us now so we can connect to it
00352          * during the next demonstration */
00353         memcpy(_peer_address, event.getPeerAddress().data(), sizeof(_peer_address));
00354 
00355         printf("Connected to peer: ");
00356         print_address(event.getPeerAddress().data());
00357 
00358         _handle = event.getConnectionHandle();
00359 
00360         /* Request a change in link security. This will be done
00361          * indirectly by asking the master of the connection to
00362          * change it. Depending on circumstances different actions
00363          * may be taken by the master which will trigger events
00364          * which the applications should deal with. */
00365         error = _ble.securityManager().setLinkSecurity(
00366             _handle,
00367             SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM
00368         );
00369 
00370         if (error) {
00371             printf("Error during SM::setLinkSecurity %d\r\n", error);
00372             return;
00373         }
00374     };
00375 };
00376 
00377 /** A central device will scan, connect to a peer and request pairing. */
00378 class SMDeviceCentral : public SMDevice {
00379 public:
00380     SMDeviceCentral(BLE &ble, events::EventQueue &event_queue, BLEProtocol::AddressBytes_t &peer_address)
00381         : SMDevice(ble, event_queue, peer_address) { }
00382 
00383     virtual void start()
00384     {
00385         ble::ScanParameters params;
00386         ble_error_t error = _ble.gap().setScanParameters(params);
00387 
00388         if (error) {
00389             print_error(error, "Error in Gap::startScan %d\r\n");
00390             return;
00391         }
00392 
00393         /* start scanning, results will be handled by onAdvertisingReport */
00394         error = _ble.gap().startScan();
00395 
00396         if (error) {
00397             print_error(error, "Error in Gap::startScan %d\r\n");
00398             return;
00399         }
00400 
00401         printf("Please advertise\r\n");
00402 
00403         printf("Scanning for: ");
00404         print_address(_peer_address);
00405     }
00406 
00407 private:
00408     /* Gap::EventHandler */
00409 
00410     /** Look at scan payload to find a peer device and connect to it */
00411     virtual void onAdvertisingReport(const ble::AdvertisingReportEvent &event)
00412     {
00413         /* don't bother with analysing scan result if we're already connecting */
00414         if (_is_connecting) {
00415             return;
00416         }
00417 
00418         /* parse the advertising payload, looking for a discoverable device */
00419         if (memcmp(event.getPeerAddress().data(), _peer_address, sizeof(_peer_address)) == 0) {
00420             ble_error_t error = _ble.gap().stopScan();
00421 
00422             if (error) {
00423                 print_error(error, "Error caused by Gap::stopScan");
00424                 return;
00425             }
00426 
00427             ble::ConnectionParameters connection_params(
00428                 ble::phy_t::LE_1M,
00429                 ble::scan_interval_t(50),
00430                 ble::scan_window_t(50),
00431                 ble::conn_interval_t(50),
00432                 ble::conn_interval_t(100),
00433                 ble::slave_latency_t(0),
00434                 ble::supervision_timeout_t(100)
00435             );
00436             connection_params.setOwnAddressType(ble::own_address_type_t::RANDOM);
00437 
00438             error = _ble.gap().connect(
00439                 event.getPeerAddressType(),
00440                 event.getPeerAddress(),
00441                 connection_params
00442             );
00443 
00444             if (error) {
00445                 print_error(error, "Error caused by Gap::connect");
00446                 return;
00447             }
00448 
00449             /* we may have already scan events waiting
00450              * to be processed so we need to remember
00451              * that we are already connecting and ignore them */
00452             _is_connecting = true;
00453 
00454             return;
00455         }
00456     }
00457 
00458     /** This is called by Gap to notify the application we connected,
00459      *  in our case it immediately request pairing */
00460     virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event)
00461     {
00462         if (event.getStatus() == BLE_ERROR_NONE) {
00463             /* store the handle for future Security Manager requests */
00464             _handle = event.getConnectionHandle();
00465 
00466             printf("Connected\r\n");
00467 
00468             /* in this example the local device is the master so we request pairing */
00469             ble_error_t error = _ble.securityManager().requestPairing(_handle);
00470 
00471              if (error) {
00472                  printf("Error during SM::requestPairing %d\r\n", error);
00473                  return;
00474              }
00475 
00476             /* upon pairing success the application will disconnect */
00477         }
00478 
00479         /* failed to connect - restart scan */
00480         ble_error_t error = _ble.gap().startScan();
00481 
00482         if (error) {
00483             print_error(error, "Error in Gap::startScan %d\r\n");
00484             return;
00485         }
00486     };
00487 };
00488 
00489 
00490 #if MBED_CONF_APP_FILESYSTEM_SUPPORT
00491 bool create_filesystem()
00492 {
00493     static LittleFileSystem fs("fs");
00494 
00495     /* replace this with any physical block device your board supports (like an SD card) */
00496     static HeapBlockDevice bd(4096, 256);
00497 
00498     int err = bd.init();
00499 
00500     if (err) {
00501         return false;
00502     }
00503 
00504     err = bd.erase(0, bd.size());
00505 
00506     if (err) {
00507         return false;
00508     }
00509 
00510     err = fs.mount(&bd);
00511 
00512     if (err) {
00513         /* Reformat if we can't mount the filesystem */
00514         printf("No filesystem found, formatting...\r\n");
00515 
00516         err = fs.reformat(&bd);
00517 
00518         if (err) {
00519             return false;
00520         }
00521     }
00522 
00523     return true;
00524 }
00525 #endif //MBED_CONF_APP_FILESYSTEM_SUPPORT
00526 
00527 int main()
00528 {
00529     BLE& ble = BLE::Instance();
00530     events::EventQueue queue;
00531 
00532 #if MBED_CONF_APP_FILESYSTEM_SUPPORT
00533     /* if filesystem creation fails or there is no filesystem the security manager
00534      * will fallback to storing the security database in memory */
00535     if (!create_filesystem()) {
00536         printf("Filesystem creation failed, will use memory storage\r\n");
00537     }
00538 #endif
00539 
00540     while(1) {
00541         {
00542             printf("\r\n PERIPHERAL \r\n\r\n");
00543             SMDevicePeripheral peripheral(ble, queue, peer_address);
00544             peripheral.run();
00545         }
00546 
00547         {
00548             printf("\r\n CENTRAL \r\n\r\n");
00549             SMDeviceCentral central(ble, queue, peer_address);
00550             central.run();
00551         }
00552     }
00553 
00554     return 0;
00555 }